Compare commits

..

3 Commits

Author SHA1 Message Date
Marina Zhurakhinskaya
a8b2c13417 Change from St.Group to St.Widget 2012-03-26 17:10:29 -04:00
Marina Zhurakhinskaya
bd9fe20bd1 lightbox: make sure this.shown set to false when start showing 2012-03-26 17:10:28 -04:00
Marina Zhurakhinskaya
478e546a77 screenShield: add initial functionality
We are replacing the gnome-screensaver module with with a screen shield
that is part of gnome-shell.

This patch fades out the screen on idle and shows a shield with a background
image when there is activity again. The shield can be removed with a key or
button press.
2012-03-26 17:10:28 -04:00
221 changed files with 20472 additions and 24755 deletions

6
.gitignore vendored
View File

@@ -23,8 +23,6 @@ data/gnome-shell-extension-prefs.desktop.in
data/gschemas.compiled data/gschemas.compiled
data/org.gnome.shell.gschema.xml data/org.gnome.shell.gschema.xml
data/org.gnome.shell.gschema.valid data/org.gnome.shell.gschema.valid
data/org.gnome.shell.evolution.calendar.gschema.xml
data/org.gnome.shell.evolution.calendar.gschema.valid
docs/reference/*/*.args docs/reference/*/*.args
docs/reference/*/*.bak docs/reference/*/*.bak
docs/reference/*/*.hierarchy docs/reference/*/*.hierarchy
@@ -50,7 +48,6 @@ po/gnome-shell.pot
po/*.header po/*.header
po/*.sed po/*.sed
po/*.sin po/*.sin
po/.intltool-merge-cache
po/Makefile.in.in po/Makefile.in.in
po/Makevars.template po/Makevars.template
po/POTFILES po/POTFILES
@@ -63,8 +60,6 @@ src/*-enum-types.[ch]
src/*-marshal.[ch] src/*-marshal.[ch]
src/Makefile src/Makefile
src/Makefile.in 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/calendar-server/org.gnome.Shell.CalendarServer.service
src/gnome-shell src/gnome-shell
src/gnome-shell-calendar-server src/gnome-shell-calendar-server
@@ -73,7 +68,6 @@ src/gnome-shell-extension-prefs
src/gnome-shell-hotplug-sniffer src/gnome-shell-hotplug-sniffer
src/gnome-shell-jhbuild src/gnome-shell-jhbuild
src/gnome-shell-perf-helper src/gnome-shell-perf-helper
src/gnome-shell-perf-tool
src/gnome-shell-real src/gnome-shell-real
src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service
src/run-js-test src/run-js-test

223
NEWS
View File

@@ -1,214 +1,3 @@
3.5.4
=====
* Fix wrong result handling of remote calls [Florian; #678852]
* dateMenu: Fix regression that caused no date to be displayed [Colin]
* WindowTracker: Fix refcounting bug in get_app_for_window() [Giovanni; #678992]
* Show the workspace switcher for move-to-workspace keybinding
[Giovanni, Jasper; #674104, #660839, #679005]
* userMenu: Move "Power off" item to the bottom [Florian; #678887]
* Remove contacts search provider [Florian, Rui; #677442]
* network: don't ask for always-ask secrets when interaction isn't allowed
[Dan; #679091]
* PolkitAgent: Look for the right password prompt [Matthias; #675300]
* Implement extension updates [Jasper; #679099]
* userMenu: Don't disconnect account signals when disabled [Guillaume; #669112]
* Fix startup notification when opening calendar [Florian; #677907]
* networkAgent: use absolute path if configured [Clemens; #679212]
* recorder: Port to GStreamer-1.0 API [Florian; #679445]
* telepathyClient: don't add log messages on presence changes [Ana; #669508]
* lookingGlass: Don't use a signal callback on 'paint' to draw the border
[Jasper; #679464]
* Add support for inhibiting automount [Hans; #678597]
* Implemented banner support for the login screen [Matthias, Marius; #665346]
* boxpointer: Flip side if we would end outside the monitor [Rui; #678164]
* boxpointer: Change 'animate' parameter on show/hide to a bitmask
[Rui; #678337]
* Add a grayscale effect [Matthias, Jasper, Florian: #676782, #674499]
* UserMenu: show "Install Updates & Restart" when appropriate
[Giovanni; #677394, #680080]
* messageTray: don't show the message tray when a new notification is shown
[Ana; #677210]
* panel: don't break when indicator has no menu [Jean-Philippe; #678694]
* appMenu: Disable app menu during startup animations [Florian; #672322]
* autorun: Add a notification when unmounting drives [Cosimo; #676125]
* st-icon: Fix potential crash involving shadows [Jasper; #679776]
* Remove manual garbage collection on tweeners end [Cosimo; #679832]
* dash: hide tooltips when overview begins hiding [Stefano; #674241]
* Update modal dialog animation for new centered position [Florian; #674499]
* calendar: Fix grid lines in RTL locales [Florian; #679879]
* Integrate IBus with keyboard indicator [Rui; #641531]
* Move ibus status icon under keyboard [Matthias]
* gdm: port from libgdmgreeter to libgdm [Ray; #676401]
* Misc bug fixes and cleanups [Antoine, Cosimo, Giovanni, Jasper, Rico;
#678978, #672790, #679847, #679944]
Contributors:
Jean-Philippe Braun, Clemens Buchacher, Giovanni Campagna, Cosimo Cecchi,
Matthias Clasen, Hans de Goede, Guillaume Desmottes, Stefano Facchini,
Antoine Jacoutot, Rui Matos, Florian Müllner, Marius Rieder, Ana Risteska,
Jasper St. Pierre, Rico Tzschichholz, Colin Walters, Dan Williams
Translations:
Matej Urbančič [sl], Khaled Hosny [ar], Nguyễn Thái Ngọc Duy [vi],
Nilamdyuti Goswami [as], Alexander Shopov [bg], Ivaylo Valkov [bg],
Daniel Mustieles [es], Kjartan Maraas [nb,nn], Yaron Shahrabani [he],
Nilamdyuti Goswami [as], Chao-Hsiung Liao [zh_HK, zh_TW], Ihar Hrachyshka [be],
Praveen Illa [te]
3.5.3
=====
* calendar: Adapt to Evolution-Data-Server API changes [Matthew; #677402]
* messageTray: Don't show non urgent notifications while in fullscreen
[Adel; #677590]
* modalDialog: show dialogs on monitor with the mouse pointer [Tim; #642591]
* extensionSystem: Prepare for extension updating system [Jasper; #677586]
* appDisplay: Don't show apps in NoDisplay categories in the All view
[Jasper; #658176]
* st: Trigger theme updates on resolution changes [Florian; #677975]
* Always enable a11y [Bastien; #678095]
* telepathyClient: ignore invalidated channels [Guillaume; #677457]
* shell-app: Update app menu if necessary [Florian; #676238]
* Enable the Screen Reader menu item [Matthias; #663256]
* Disable unredirection when a modal operation is active [Giovanni]
* Make folks optional [Colin]
* Improve mount-operation support [Cosimo]
- Fix exception when showing password entry [#678428]
- Close the password entry on operation abort [#673787]
- autorun: Don't allow autorun for things we mount on startup [#660595]
- Turn passphrase prompt into a dialog [#674962]
- Implement org.Gtk.MountOperationHandler [#678516]
* Network menu improvements
- Sort Wifi networks by strength [Giovanni; #658946]
- Prefer wifi/3g over VPN in the panel [Cosimo; #672591]
* clock: Switch to using GnomeWallClock [Colin; #657074]
* remoteSearch: Allow to reference .desktop file for Title/Icon
[Florian; #678816]
* Fix memory leaks [Jasper, Pavel; #678079, #678406, #678737]
* Misc fixes [Florian, Giovanni, Guillaume, Jasper, Kjartan, Piotr, Rui;
#658955, #677497, #678396, #678502]
* Misc cleanups [Bastien, Florian, Jasper; #677426, #677515, #678096, #678416]
Contributors:
Matthew Barnes, Giovanni Campagna, Cosimo Cecchi, Matthias Clasen,
Guillaume Desmottes, Piotr Drąg, Adel Gadllah, Tim L, Kjartan Maraas,
Rui Matos, Florian Müllner, Bastien Nocera, Jasper St. Pierre, Colin Walters
Translations:
Matej Urbančič [sl], Yuri Kozlov [ru], Tom Tryfonidis [el],
Kjartan Maraas [nb], Žygimantas Beručka [lt], Luca Ferretti [it],
Khaled Hosny [ar], Daniel Mustieles [es], Fran Diéguez [gl], A S Alam [pa]
3.5.2
=====
* main: Move 'toggle-recording' binding into the shell [Florian; #674377]
* popupMenu: make sure to break the grab when the slider is not visible
[Stefano; #672713]
* st-theme-node-drawing: Don't use GL types [Neil; #672711]
* Mirror Evolution calendar settings into our own schema [Owen; #674424]
* shell-network-agent: don't crash if a request isn't found [Dan; #674961]
* notificationDaemon: Match app based on WM_CLASS [Jasper; #673761]
* NetworkMenu: use network-offline while loading [Giovanni; #674426]
* lookingGlass: Remove the Errors tab [Jasper; #675104]
* searchDisplay: Reset keyboard focus after displaying async results
[Rui; #675078]
* gdm: don't fail if fprintd is unavailable [Ray; #675006]
* messageTray: Fix scrolling up [Jasper; #661615]
* main: Close the recorder instead of pausing it [Rui; #675128]
* Accessibility [Alejandro]
- Use the proper label_actor for date menu on top panel [#675307]
- Set the proper role/label_actor for SearchResult.content [#672242]
- do not expose a label text if 'hidden' style class is used [#675341]
* Magnifier: Add brightness and contrast functionality [Joseph; #639851]
* theme: use a smaller border-radius for top bar [Jakub; #672430]
* placeDisplay: use new bookmark file location [Matthias; #675443]
* port all synchronous search providers to the async API [Jasper, Rui; #675328]
* NetworkAgent: disallow multiple requests for the same connection/setting
[Giovanni; #674961]
* userMenu: Update to latest mockups [Florian; #675802]
* util: Don't double-fork when spawning from Alt-F2 [Colin; #675789]
* messageTray: Make Source usable without subclassing [Jasper; #661236]
* panel: Check for appMenu button's reactivity before opening [Florian; #676316]
* Fix formatting of bluetooth passkey [Florian; #651251]
* notificationDaemon: Filter out file-transfer notifications [Jasper; #676175]
* Don't use global.log() [Jasper; #675790]
* Fix broken extension loading in some distributions [Owen, Alexandre; #670477]
* shell-app: Raise windows in reverse order to preserve the stacking
[Rui; #676371]
* Generalize gdm-mode [Florian; #676156]
* Switch string formatting to the one inside gjs [Jasper; #675479]
* extensionUtils: Support subdirectories in getCurrentExtension
[Jasper; #677001]
* panel: Refuse to add (legacy) status icons not part of the session mode
[Florian; #677058]
* Add an initial-setup mode [Matthias; #676697]
* status/keyboard: Port to the new input sources settings [Rui; #641531]
* NetworkMenu: show notifications for failed VPN connections [Giovanni; #676330]
* userMenu: Indicate progress on status changes [Florian; #659067]
* recorder: Honor "disable-save-to-disk" lockdown key [Rūdolfs; #673630]
* searchDisplay: Use the rowLimit we pass to the IconGrid [Christian; #675527]
* endSessionDialog: Factor out _updateDescription from _updateContent
[Alejandro; #674210]
* Fix empathy's appMenu freezing the shell [Alban; #676447]
* Code cleanups [Florian, Giovanni, Jasper; #672807, #672413, #676837, #676850,
#672272]
* Misc bug fixes [Alban, Florian, Giovanni, Guillaume, Jasper, Piotr, Rico,
Ron, Rui, Stefano; #659968, #672192, #673177, #673198, #674323, #675301,
#675370, #676347, #676806, #677097]
Contributors:
Alban Browaeys, Giovanni Campagna, Matthias Clasen, Guillaume Desmottes,
Piotr Drąg, Stefano Facchini, Rui Matos, Rūdolfs Mazurs, Florian Müllner,
Alejandro Piñeiro, Neil Roberts, Alexandre Rostovtsev, Joseph Scheuhammer,
Jakub Steiner, Jasper St. Pierre, Ray Strode, Owen Taylor, Rico Tzschichholz,
Colin Walters, Dan Winship, Ron Yorston
Translations:
OKANO Takayoshi [ja], Daniel Mustieles [es], Changwoo Ryu [ko],
Yaron Shahrabani [he], Fran Diéguez [gl], Jonh Wendell [pt_BR],
Kjartan Maraas [nb], Luca Ferretti [it], Tom Tryfonidis [el],
Sandeep Sheshrao Shedmake [mr], Takanori MATSUURA [ja], Dirgita [id],
Mantas Kriaučiūnas [lt], Matej Urbančič [sl], Jiro Matsuzawa [ja]
3.4.1
=====
* Fix crash that occurred when an icon theme change caused unexpected
reentrancy in the icon loading code [Jasper; #673512]
* Don't show system and other disabled users in the GDM user list
[Adel; #673784]
* Make gnome-shell-calendar-server initialize GTK+ so it can display
password prompts if needed [#673608; Owen, Rico]
* Adapt to Mutter API change for keybinding addition [Florian; #673014]
* Fix crash when an extension was installed as both a user extension
and a system extension [#673613; Jasper]
* Fix bug where chat entry could end up partially offscreen [Joost, 661944]
* Fix problem where icons weren't updating when theme was changed
[#672941; Florian]
* Look for Evolution calendar settings in GSettings, not GConf [#673610; Owen]
* Add <super>F10 for the application menu [#672909; Florian]
* Fix %Id format characters to work in translations [#673106; Cosimo]
(were already used in fa translation)
* Fix error when NetworkManager restarts [#673043; Giovanni]
* Improve efficiency of overview redraws by working around Clutter issue
[Stefano; #670636]
* Misc bug fixes [Florian, Giovanni, Jasper, Rui, Stefano;
#672592, #672641, #672719, #673187, #673233, #673656]
Contributors:
Giovanni Campagna, Cosimo Cecchi, Stefano Facchini, Adel Gadllah, Rui Matos,
Florian Müllner, Jasper St. Pierre, Owen Taylor, Rico Tzschichholz,
Joost Verdoorn
Translations:
Khaled Hosny [ar], Ihar Hrachyshka [be], Alexander Shopov [bg], Gil Forcada,
Jordi Serratosa [ca], Petr Kovar [cs], Bruce Cowan [en_GB],
Carles Ferrando [ca@valencia], Wolfgang Stöggl [de], Daniel Mustieles [es],
Arash Mousavi [fa], Bruno Brouard [fr], Fran Diéguez [gl],
Sweta Kothari [gu], Yaron Shahrabani [he], Gabor Kelemen [hu],
Shankar Prasad [kn], Žygimantas Beručka [lt], Rudolfs Mazurs [lv],
Sandeep Sheshrao Shedmake [mr], Kjartan Maraas [nb], Piotr Drąg [pl],
Yuri Myasoedov [ru], Daniel Nylander [se], Matej Urbančič [sl],
Miroslav Nikolić [sr], Sasi Bhushan, Praveen Illa [te], Yinghua Wang [zh_CN]
3.4.0 3.4.0
===== =====
* Don't crash when taking screenshots [Jasper; #672775] * Don't crash when taking screenshots [Jasper; #672775]
@@ -219,10 +8,10 @@ Contributors:
Translations: Translations:
Khaled Hosny, Abderrahim Kitouni [ar], Ihar Hrachyshka [be], Khaled Hosny, Abderrahim Kitouni [ar], Ihar Hrachyshka [be],
Alexander Shopov [bg], Marek Černocký [cs], Jiri Grönroos, Timo Jyrinki [fi], Alexander Shopov [bg], Marek Černocký [cz], Jiri Grönroos, Timo Jyrinki [fi],
Bruno Brouard [fr], Fran Diéguez [gl], Yaron Shahrabani [he], Bruno Brouard [fr], Fran Diéguez [gl], Yaron Shahrabani [he],
Gabor Kelemen [hu], Jiro Matsuzawa [ja], Kenneth Nielsen [dk], Gabor Kelemen [hu], Jiro Matsuzawa [ja], Kenneth Nielsen [dk],
Mattias Põldaru [et], Changwoo Ryu [ko], Rudolfs Mazurs [lv], Mattias Põldaru [et], Changwoo Ryu [kr], Rudolfs Mazurs [lv],
Jonh Wendell [pt_BR], Yuri Myasoedov[ru], Daniel Korostil [uk], Jonh Wendell [pt_BR], Yuri Myasoedov[ru], Daniel Korostil [uk],
Nguyễn Thái Ngọc Duy [vi], Chao-Hsiung Liao [zh_HK, zh_TW] Nguyễn Thái Ngọc Duy [vi], Chao-Hsiung Liao [zh_HK, zh_TW]
@@ -291,7 +80,7 @@ Contributors:
Translations: Translations:
Nilamdyuti Goswami [as], Ihar Hrachyshka, Kasia Bondarava [be], Nilamdyuti Goswami [as], Ihar Hrachyshka, Kasia Bondarava [be],
Alexander Shopov, Ivaylo Valkov [bg], Gil Forcada [ca], Marek Černocký [cs], Alexander Shopov, Ivaylo Valkov [bg], Gil Forcada [ca], Marek Černocký [cz],
Mario Blättermann [de], Kris Thomsen [dk], Bruce Cowan [en_GB], Mario Blättermann [de], Kris Thomsen [dk], Bruce Cowan [en_GB],
Kristjan Schmidt [eo], Daniel Mustieles [es], Mattias Põldaru [et], Kristjan Schmidt [eo], Daniel Mustieles [es], Mattias Põldaru [et],
Inaki Larranaga Murgoitio [eu], Arash Mousavi [fa], Timo Jyrinki [fi], Inaki Larranaga Murgoitio [eu], Arash Mousavi [fa], Timo Jyrinki [fi],
@@ -340,7 +129,7 @@ Contributors:
Will Thompson, Stef Walter Will Thompson, Stef Walter
Translations: Translations:
Ihar Hrachyshka [be], Marek Černocký, Adam Matoušek [cs], Ihar Hrachyshka [be], Marek Černocký, Adam Matoušek [cz],
Kenneth Nielsen [dk], Daniel Mustieles [es], Mattias Põldaru [et], Kenneth Nielsen [dk], Daniel Mustieles [es], Mattias Põldaru [et],
Fran Diéguez [gl], Yaron Shahrabani [he], Luca Ferretti [it], Fran Diéguez [gl], Yaron Shahrabani [he], Luca Ferretti [it],
Baurzhan Muftakhidinov [kk], Aurimas Černius [lt], Kjartan Maraas [nb], Baurzhan Muftakhidinov [kk], Aurimas Černius [lt], Kjartan Maraas [nb],
@@ -471,7 +260,7 @@ Contributors:
Marina Zhurakhinskaya Marina Zhurakhinskaya
Translations: Translations:
Petr Kovar [cs], Kris Thomsen [dk], Daniel Mustieles [es], Petr Kovar [cz], Kris Thomsen [dk], Daniel Mustieles [es],
Ville-Pekka Vainio [fi], Yaron Shahrabani [he], Luca Ferretti [it], Ville-Pekka Vainio [fi], Yaron Shahrabani [he], Luca Ferretti [it],
Hideki Yamane [ja], Žygimantas Beručka [lt], Jovan Naumovski [mk], Hideki Yamane [ja], Žygimantas Beručka [lt], Jovan Naumovski [mk],
Kjartan Maraas [nb], "Andreas N" [nn], Lucian Adrian Grijincu [ro], Kjartan Maraas [nb], "Andreas N" [nn], Lucian Adrian Grijincu [ro],
@@ -634,7 +423,7 @@ Contributors:
Translations: Translations:
Friedel Wolff [af], Nilamdyuti Goswami [as], Ihar Hrachyshka [be], Friedel Wolff [af], Nilamdyuti Goswami [as], Ihar Hrachyshka [be],
Ivaylo Valkov [bg], Gil Forcada [ca], Carles Ferrando [ca@valencia], Ivaylo Valkov [bg], Gil Forcada [ca], Carles Ferrando [ca@valencia],
Petr Kovar [cs], Mario Blättermann [de], Kris Thomsen [dk], Petr Kovar [cz], Mario Blättermann [de], Kris Thomsen [dk],
Tiffany Antopolski, Kristjan Schmidt [eo], Daniel Mustieles [es], Tiffany Antopolski, Kristjan Schmidt [eo], Daniel Mustieles [es],
Inaki Larranaga Murgoitio [eu], Tommi Vainikainen [fi], Bruno Brouard [fr], Inaki Larranaga Murgoitio [eu], Tommi Vainikainen [fi], Bruno Brouard [fr],
Fran Dieguez [gl], Yaron Shahrabani [he], Gabor Kelemen [hu], Fran Dieguez [gl], Yaron Shahrabani [he], Gabor Kelemen [hu],

View File

@@ -41,7 +41,7 @@
"It can be used only by extensions.gnome.org" "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_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 { typedef struct {
GDBusProxy *proxy; GDBusProxy *proxy;
@@ -163,7 +163,6 @@ NP_Initialize(NPNetscapeFuncs *pfuncs, NPPluginFuncs *plugin)
plugin->newp = NPP_New; plugin->newp = NPP_New;
plugin->destroy = NPP_Destroy; plugin->destroy = NPP_Destroy;
plugin->getvalue = NPP_GetValue; plugin->getvalue = NPP_GetValue;
plugin->setwindow = NPP_SetWindow;
return NPERR_NO_ERROR; return NPERR_NO_ERROR;
} }
@@ -225,7 +224,7 @@ NPP_New(NPMIMEType mimetype,
NULL, /* interface info */ NULL, /* interface info */
"org.gnome.Shell", "org.gnome.Shell",
"/org/gnome/Shell", "/org/gnome/Shell",
"org.gnome.Shell.Extensions", "org.gnome.Shell",
NULL, /* GCancellable */ NULL, /* GCancellable */
&error); &error);
if (!data->proxy) if (!data->proxy)
@@ -268,7 +267,6 @@ typedef struct {
NPObject parent; NPObject parent;
NPP instance; NPP instance;
GDBusProxy *proxy; GDBusProxy *proxy;
GSettings *settings;
NPObject *listener; NPObject *listener;
NPObject *restart_listener; NPObject *restart_listener;
gint signal_id; 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 * static NPObject *
plugin_object_allocate (NPP instance, plugin_object_allocate (NPP instance,
NPClass *klass) NPClass *klass)
@@ -337,7 +332,6 @@ plugin_object_allocate (NPP instance,
obj->instance = instance; obj->instance = instance;
obj->proxy = g_object_ref (data->proxy); obj->proxy = g_object_ref (data->proxy);
obj->settings = g_settings_new (SHELL_SCHEMA);
obj->signal_id = g_signal_connect (obj->proxy, "g-signal", obj->signal_id = g_signal_connect (obj->proxy, "g-signal",
G_CALLBACK (on_shell_signal), obj); G_CALLBACK (on_shell_signal), obj);
@@ -373,14 +367,39 @@ plugin_object_deallocate (NPObject *npobj)
g_slice_free (PluginObject, obj); 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 static inline gboolean
uuid_is_valid (NPString string) uuid_is_valid (const gchar *uuid)
{ {
gsize i; 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) if (c < 32 || c >= 127)
return FALSE; return FALSE;
@@ -444,73 +463,8 @@ jsonify_variant (GVariant *variant,
} }
static gboolean static gboolean
parse_args (const gchar *format_str, plugin_list_extensions (PluginObject *obj,
uint32_t argc, NPVariant *result)
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)
{ {
GError *error = NULL; GError *error = NULL;
GVariant *res; GVariant *res;
@@ -534,159 +488,91 @@ plugin_list_extensions (PluginObject *obj,
} }
static gboolean static gboolean
plugin_enable_extension (PluginObject *obj, plugin_enable_extension (PluginObject *obj,
uint32_t argc, NPString uuid,
const NPVariant *argv, gboolean enabled)
NPVariant *result)
{ {
gboolean ret; gchar *uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
gchar *uuid; if (!uuid_is_valid (uuid_str))
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)
{ {
new_uuids = g_new (const gchar *, length + 2); /* New key, NULL */ g_free (uuid_str);
memcpy (new_uuids, uuids, length * sizeof (*new_uuids)); return FALSE;
new_uuids[length] = uuid;
new_uuids[length + 1] = NULL;
} }
else
{
gsize i = 0, j = 0;
new_uuids = g_new (const gchar *, length);
for (i = 0; i < length; i ++)
{
if (g_str_equal (uuids[i], uuid))
continue;
new_uuids[j] = uuids[i];
j++;
}
new_uuids[j] = NULL;
}
ret = g_settings_set_strv (obj->settings,
ENABLED_EXTENSIONS_KEY,
new_uuids);
g_strfreev (uuids);
g_free (new_uuids);
g_free (uuid);
return ret;
}
typedef struct _AsyncClosure AsyncClosure;
struct _AsyncClosure {
PluginObject *obj;
NPObject *callback;
NPObject *errback;
};
static void
install_extension_cb (GObject *proxy,
GAsyncResult *async_res,
gpointer user_data)
{
AsyncClosure *async_closure = (AsyncClosure *) user_data;
GError *error = NULL;
GVariant *res;
NPVariant args[1];
NPVariant result = { NPVariantType_Void };
NPObject *callback;
res = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), async_res, &error);
if (res == NULL)
{
if (g_dbus_error_is_remote_error (error))
g_dbus_error_strip_remote_error (error);
STRINGZ_TO_NPVARIANT (error->message, args[0]);
callback = async_closure->errback;
}
else
{
char *string_result;
g_variant_get (res, "(&s)", &string_result);
STRINGZ_TO_NPVARIANT (string_result, args[0]);
callback = async_closure->callback;
}
funcs.invokeDefault (async_closure->obj->instance,
callback, args, 1, &result);
funcs.releasevariantvalue (&result);
funcs.releaseobject (async_closure->callback);
funcs.releaseobject (async_closure->errback);
g_slice_free (AsyncClosure, async_closure);
}
static gboolean
plugin_install_extension (PluginObject *obj,
uint32_t argc,
const NPVariant *argv,
NPVariant *result)
{
gchar *uuid;
NPObject *callback, *errback;
AsyncClosure *async_closure;
if (!parse_args ("uoo", argc, argv, &uuid, &callback, &errback))
return FALSE;
async_closure = g_slice_new (AsyncClosure);
async_closure->obj = obj;
async_closure->callback = funcs.retainobject (callback);
async_closure->errback = funcs.retainobject (errback);
g_dbus_proxy_call (obj->proxy, g_dbus_proxy_call (obj->proxy,
"InstallRemoteExtension", (enabled ? "EnableExtension" : "DisableExtension"),
g_variant_new ("(s)", uuid), g_variant_new ("(s)", uuid_str),
G_DBUS_CALL_FLAGS_NONE, G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout */ -1, /* timeout */
NULL, /* cancellable */ NULL, /* cancellable */
install_extension_cb, NULL, /* callback */
async_closure); NULL /* user_data */);
g_free (uuid); g_free (uuid_str);
return TRUE; return TRUE;
} }
static gboolean static gboolean
plugin_uninstall_extension (PluginObject *obj, plugin_install_extension (PluginObject *obj,
uint32_t argc, NPString uuid,
const NPVariant *argv, NPString version_tag)
NPVariant *result) {
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; GError *error = NULL;
GVariant *res; GVariant *res;
gchar *uuid; gchar *uuid_str;
if (!parse_args ("u", argc, argv, &uuid)) uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
return FALSE; if (!uuid_is_valid (uuid_str))
{
g_free (uuid_str);
return FALSE;
}
res = g_dbus_proxy_call_sync (obj->proxy, res = g_dbus_proxy_call_sync (obj->proxy,
"UninstallExtension", "UninstallExtension",
g_variant_new ("(s)", uuid), g_variant_new ("(s)",
uuid_str),
G_DBUS_CALL_FLAGS_NONE, G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout */ -1, /* timeout */
NULL, /* cancellable */ NULL, /* cancellable */
&error); &error);
g_free (uuid); g_free (uuid_str);
if (!res) if (!res)
{ {
@@ -699,27 +585,30 @@ plugin_uninstall_extension (PluginObject *obj,
} }
static gboolean static gboolean
plugin_get_info (PluginObject *obj, plugin_get_info (PluginObject *obj,
uint32_t argc, NPString uuid,
const NPVariant *argv, NPVariant *result)
NPVariant *result)
{ {
GError *error = NULL; GError *error = NULL;
GVariant *res; GVariant *res;
gchar *uuid; gchar *uuid_str;
if (!parse_args ("u", argc, argv, &uuid)) uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
return FALSE; if (!uuid_is_valid (uuid_str))
{
g_free (uuid_str);
return FALSE;
}
res = g_dbus_proxy_call_sync (obj->proxy, res = g_dbus_proxy_call_sync (obj->proxy,
"GetExtensionInfo", "GetExtensionInfo",
g_variant_new ("(s)", uuid), g_variant_new ("(s)", uuid_str),
G_DBUS_CALL_FLAGS_NONE, G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout */ -1, /* timeout */
NULL, /* cancellable */ NULL, /* cancellable */
&error); &error);
g_free (uuid); g_free (uuid_str);
if (!res) if (!res)
{ {
@@ -732,26 +621,31 @@ plugin_get_info (PluginObject *obj,
} }
static gboolean static gboolean
plugin_get_errors (PluginObject *obj, plugin_get_errors (PluginObject *obj,
uint32_t argc, NPString uuid,
const NPVariant *argv, NPVariant *result)
NPVariant *result)
{ {
GError *error = NULL; GError *error = NULL;
GVariant *res; GVariant *res;
gchar *uuid; gchar *uuid_str;
if (!parse_args ("u", argc, argv, &uuid)) uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
return FALSE; if (!uuid_is_valid (uuid_str))
{
g_free (uuid_str);
return FALSE;
}
res = g_dbus_proxy_call_sync (obj->proxy, res = g_dbus_proxy_call_sync (obj->proxy,
"GetExtensionErrors", "GetExtensionErrors",
g_variant_new ("(s)", uuid), g_variant_new ("(s)", uuid_str),
G_DBUS_CALL_FLAGS_NONE, G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout */ -1, /* timeout */
NULL, /* cancellable */ NULL, /* cancellable */
&error); &error);
g_free (uuid_str);
if (!res) if (!res)
{ {
g_warning ("Failed to retrieve errors: %s", error->message); g_warning ("Failed to retrieve errors: %s", error->message);
@@ -763,25 +657,29 @@ plugin_get_errors (PluginObject *obj,
} }
static gboolean static gboolean
plugin_launch_extension_prefs (PluginObject *obj, plugin_launch_extension_prefs (PluginObject *obj,
uint32_t argc, NPString uuid,
const NPVariant *argv, NPVariant *result)
NPVariant *result)
{ {
gchar *uuid; gchar *uuid_str;
if (!parse_args ("u", argc, argv, &uuid)) uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
return FALSE; if (!uuid_is_valid (uuid_str))
{
g_free (uuid_str);
return FALSE;
}
g_dbus_proxy_call (obj->proxy, g_dbus_proxy_call (obj->proxy,
"LaunchExtensionPrefs", "LaunchExtensionPrefs",
g_variant_new ("(s)", uuid), g_variant_new ("(s)", uuid_str),
G_DBUS_CALL_FLAGS_NONE, G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout */ -1, /* timeout */
NULL, /* cancellable */ NULL, /* cancellable */
NULL, /* callback */ NULL, /* callback */
NULL /* user_data */); NULL /* user_data */);
g_free (uuid_str);
return TRUE; return TRUE;
} }
@@ -835,40 +733,10 @@ plugin_get_shell_version (PluginObject *obj,
return ret; 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 static bool
plugin_object_invoke (NPObject *npobj, plugin_object_invoke (NPObject *npobj,
NPIdentifier name, NPIdentifier name,
const NPVariant *argv, const NPVariant *args,
uint32_t argc, uint32_t argc,
NPVariant *result) NPVariant *result)
{ {
@@ -880,13 +748,61 @@ plugin_object_invoke (NPObject *npobj,
VOID_TO_NPVARIANT (*result); VOID_TO_NPVARIANT (*result);
#define METHOD(x) \ if (!plugin_object_has_method (npobj, name))
if (name == x##_id) \ return FALSE;
return plugin_##x (obj, argc, argv, result);
METHODS
#undef METHOD
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 static bool
@@ -1030,12 +946,3 @@ NPP_GetValue(NPP instance,
return NPERR_NO_ERROR; 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_PREREQ(2.63)
AC_INIT([gnome-shell],[3.5.4],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell]) AC_INIT([gnome-shell],[3.4.0],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_CONFIG_HEADERS([config.h]) AC_CONFIG_HEADERS([config.h])
AC_CONFIG_SRCDIR([src/shell-global.c]) AC_CONFIG_SRCDIR([src/shell-global.c])
@@ -44,15 +44,15 @@ AC_SUBST(PYTHON)
# We need at least this, since gst_plugin_register_static() was added # 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. # 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= recorder_modules=
build_recorder=false build_recorder=false
AC_MSG_CHECKING([for GStreamer (needed for recording functionality)]) 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) AC_MSG_RESULT(yes)
build_recorder=true 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) PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes gl)
else else
AC_MSG_RESULT(no) AC_MSG_RESULT(no)
@@ -62,30 +62,28 @@ AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
CLUTTER_MIN_VERSION=1.9.16 CLUTTER_MIN_VERSION=1.9.16
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1 GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
GJS_MIN_VERSION=1.33.2 GJS_MIN_VERSION=1.29.18
MUTTER_MIN_VERSION=3.5.4 MUTTER_MIN_VERSION=3.3.92
FOLKS_MIN_VERSION=0.5.2
GTK_MIN_VERSION=3.3.9 GTK_MIN_VERSION=3.3.9
GIO_MIN_VERSION=2.31.6 GIO_MIN_VERSION=2.31.6
LIBECAL_MIN_VERSION=3.5.3 LIBECAL_MIN_VERSION=2.32.0
LIBEDATASERVER_MIN_VERSION=3.5.3 LIBEDATASERVER_MIN_VERSION=1.2.0
LIBEDATASERVERUI_MIN_VERSION=3.5.3 LIBEDATASERVERUI_MIN_VERSION=2.91.6
TELEPATHY_GLIB_MIN_VERSION=0.17.5 TELEPATHY_GLIB_MIN_VERSION=0.17.5
TELEPATHY_LOGGER_MIN_VERSION=0.2.4 TELEPATHY_LOGGER_MIN_VERSION=0.2.4
POLKIT_MIN_VERSION=0.100 POLKIT_MIN_VERSION=0.100
STARTUP_NOTIFICATION_MIN_VERSION=0.11 STARTUP_NOTIFICATION_MIN_VERSION=0.11
GCR_MIN_VERSION=3.3.90 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! # Collect more than 20 libraries for a prize!
PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
libxml-2.0 libxml-2.0
gtk+-3.0 >= $GTK_MIN_VERSION gtk+-3.0 >= $GTK_MIN_VERSION
atk-bridge-2.0 folks >= $FOLKS_MIN_VERSION
libmutter >= $MUTTER_MIN_VERSION libmutter >= $MUTTER_MIN_VERSION
gjs-internals-1.0 >= $GJS_MIN_VERSION gjs-internals-1.0 >= $GJS_MIN_VERSION
libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION libgnome-menu-3.0 $recorder_modules
$recorder_modules
gdk-x11-3.0 libsoup-2.4 gdk-x11-3.0 libsoup-2.4
gl gl
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
@@ -97,8 +95,7 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
libnm-glib libnm-util gnome-keyring-1 libnm-glib libnm-util gnome-keyring-1
gcr-3 >= $GCR_MIN_VERSION gcr-3 >= $GCR_MIN_VERSION)
gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED_VERSION)
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0) PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
@@ -106,7 +103,13 @@ PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2) PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
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]) GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
JHBUILD_TYPELIBDIR="$INTROSPECTION_TYPELIBDIR"
AC_SUBST(JHBUILD_TYPELIBDIR)
saved_CFLAGS=$CFLAGS saved_CFLAGS=$CFLAGS
saved_LIBS=$LIBS saved_LIBS=$LIBS
@@ -120,7 +123,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(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.2 x11)
PKG_CHECK_MODULES(TRAY, gtk+-3.0) PKG_CHECK_MODULES(TRAY, gtk+-3.0)
PKG_CHECK_MODULES(GVC, libpulse libpulse-mainloop-glib gobject-2.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]) AC_MSG_CHECKING([for bluetooth support])
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0], PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
@@ -236,6 +239,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) 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) 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"}" BROWSER_PLUGIN_DIR="${BROWSER_PLUGIN_DIR:-"\${libdir}/mozilla/plugins"}"
AC_ARG_VAR([BROWSER_PLUGIN_DIR],[Where to install the plugin to]) AC_ARG_VAR([BROWSER_PLUGIN_DIR],[Where to install the plugin to])
@@ -249,7 +277,6 @@ AC_CONFIG_FILES([
docs/reference/st/Makefile docs/reference/st/Makefile
docs/reference/st/st-docs.sgml docs/reference/st/st-docs.sgml
js/Makefile js/Makefile
src/calendar-server/evolution-calendar.desktop.in
src/Makefile src/Makefile
browser-plugin/Makefile browser-plugin/Makefile
tests/Makefile tests/Makefile

View File

@@ -8,7 +8,9 @@ desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
-e "s|@VERSION[@]|$(VERSION)|" \ -e "s|@VERSION[@]|$(VERSION)|" \
$< > $@ || rm $@ $< > $@ || rm $@
@INTLTOOL_DESKTOP_RULE@ # Placeholder until we add intltool
%.desktop:%.desktop.in
$(AM_V_GEN) sed s/^_// < $< > $@ || rm $@
searchprovidersdir = $(pkgdatadir)/open-search-providers searchprovidersdir = $(pkgdatadir)/open-search-providers
dist_searchproviders_DATA = \ dist_searchproviders_DATA = \
@@ -34,14 +36,15 @@ dist_theme_DATA = \
theme/dash-placeholder.svg \ theme/dash-placeholder.svg \
theme/filter-selected-ltr.svg \ theme/filter-selected-ltr.svg \
theme/filter-selected-rtl.svg \ theme/filter-selected-rtl.svg \
theme/gdm.css \
theme/gnome-shell.css \ theme/gnome-shell.css \
theme/logged-in-indicator.svg \
theme/noise-texture.png \
theme/panel-button-border.svg \ theme/panel-button-border.svg \
theme/panel-button-highlight-narrow.svg \ theme/panel-button-highlight-narrow.svg \
theme/panel-button-highlight-wide.svg \ theme/panel-button-highlight-wide.svg \
theme/process-working.svg \ theme/process-working.svg \
theme/running-indicator.svg \ theme/running-indicator.svg \
theme/scroll-hhandle.svg \
theme/scroll-vhandle.svg \
theme/source-button-border.svg \ theme/source-button-border.svg \
theme/toggle-off-us.svg \ theme/toggle-off-us.svg \
theme/toggle-off-intl.svg \ theme/toggle-off-intl.svg \
@@ -53,11 +56,6 @@ dist_theme_DATA = \
gsettings_SCHEMAS = org.gnome.shell.gschema.xml gsettings_SCHEMAS = org.gnome.shell.gschema.xml
@INTLTOOL_XML_NOMERGE_RULE@ @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@ @GSETTINGS_RULES@
# We need to compile schemas at make time # We need to compile schemas at make time
@@ -70,19 +68,24 @@ all-local: gschemas.compiled
convertdir = $(datadir)/GConf/gsettings convertdir = $(datadir)/GConf/gsettings
convert_DATA = gnome-shell-overrides.convert convert_DATA = gnome-shell-overrides.convert
shadersdir = $(pkgdatadir)/shaders
shaders_DATA = \
shaders/dim-window.glsl
EXTRA_DIST = \ EXTRA_DIST = \
gnome-shell.desktop.in.in \ gnome-shell.desktop.in.in \
gnome-shell-extension-prefs.desktop.in.in \ gnome-shell-extension-prefs.desktop.in.in \
$(introspection_DATA) \ $(introspection_DATA) \
$(menu_DATA) \ $(menu_DATA) \
$(shaders_DATA) \
$(convert_DATA) \ $(convert_DATA) \
org.gnome.shell.gschema.xml.in.in org.gnome.shell.gschema.xml.in
CLEANFILES = \ CLEANFILES = \
gnome-shell.desktop.in \ gnome-shell.desktop.in \
gnome-shell-extension-prefs.in \ gnome-shell-extension-prefs.in \
$(desktop_DATA) \ $(desktop_DATA) \
$(gsettings_SCHEMAS) \ $(gsettings_SCHEMAS) \
gschemas.compiled \ gschemas.compiled
org.gnome.shell.gschema.valid \
org.gnome.shell.gschema.xml.in

View File

@@ -61,9 +61,9 @@ value here is from the TpConnectionPresenceType enumeration.</_summary>
<_summary>Internally used to store the last session presence status for the user. The <_summary>Internally used to store the last session presence status for the user. The
value here is from the GsmPresenceStatus enumeration.</_summary> value here is from the GsmPresenceStatus enumeration.</_summary>
</key> </key>
<child name="clock" schema="org.gnome.shell.clock"/>
<child name="calendar" schema="org.gnome.shell.calendar"/> <child name="calendar" schema="org.gnome.shell.calendar"/>
<child name="recorder" schema="org.gnome.shell.recorder"/> <child name="recorder" schema="org.gnome.shell.recorder"/>
<child name="keybindings" schema="org.gnome.shell.keybindings"/>
<child name="keyboard" schema="org.gnome.shell.keyboard"/> <child name="keyboard" schema="org.gnome.shell.keyboard"/>
</schema> </schema>
@@ -78,24 +78,6 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
</key> </key>
</schema> </schema>
<schema id="org.gnome.shell.keybindings" path="/org/gnome/shell/keybindings/"
gettext-domain="@GETTEXT_PACKAGE@">
<key name="open-application-menu" type="as">
<default>["&lt;Super&gt;F10"]</default>
<_summary>Keybinding to open the application menu</_summary>
<_description>
Keybinding to open the application menu.
</_description>
</key>
<key name="toggle-recording" type="as">
<default><![CDATA[['<Control><Shift><Alt>r']]]></default>
<_summary>Keybinding to toggle the screen recorder</_summary>
<_description>
Keybinding to start/stop the builtin screen recorder.
</_description>
</key>
</schema>
<schema id="org.gnome.shell.keyboard" path="/org/gnome/shell/keyboard/" <schema id="org.gnome.shell.keyboard" path="/org/gnome/shell/keyboard/"
gettext-domain="@GETTEXT_PACKAGE@"> gettext-domain="@GETTEXT_PACKAGE@">
<key name="keyboard-type" type="s"> <key name="keyboard-type" type="s">
@@ -107,6 +89,24 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
</key> </key>
</schema> </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/" <schema id="org.gnome.shell.recorder" path="/org/gnome/shell/recorder/"
gettext-domain="@GETTEXT_PACKAGE@"> gettext-domain="@GETTEXT_PACKAGE@">
<key name="framerate" type="i"> <key name="framerate" type="i">

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

View File

@@ -9,7 +9,7 @@
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="17" width="21"
height="10" height="10"
id="svg2" id="svg2"
version="1.1" version="1.1"
@@ -66,9 +66,9 @@
<rect <rect
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none" style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none"
id="rect3796" id="rect3796"
width="7" width="3"
height="2" height="2"
x="5" x="9"
y="8" /> y="8" />
</g> </g>
</svg> </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

@@ -9,7 +9,7 @@
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="65" width="64"
height="22" height="22"
id="svg3273" id="svg3273"
version="1.1" version="1.1"

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@@ -9,7 +9,7 @@
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="65" width="64"
height="22" height="22"
id="svg3012" id="svg3012"
version="1.1" version="1.1"

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

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

View File

@@ -66,11 +66,4 @@ its dependencies to build from tarballs.</description>
<gnome:userid>marinaz</gnome:userid> <gnome:userid>marinaz</gnome:userid>
</foaf:Person> </foaf:Person>
</maintainer> </maintainer>
<maintainer>
<foaf:Person>
<foaf:name>Florian Müllner</foaf:name>
<foaf:mbox rdf:resource="mailto:fmuellner@gnome.org" />
<gnome:userid>fmuellner</gnome:userid>
</foaf:Person>
</maintainer>
</Project> </Project>

View File

@@ -6,7 +6,9 @@ misc/config.js: misc/config.js.in Makefile
[ -d $(@D) ] || $(mkdir_p) $(@D) ; \ [ -d $(@D) ] || $(mkdir_p) $(@D) ; \
sed -e "s|[@]PACKAGE_NAME@|$(PACKAGE_NAME)|g" \ sed -e "s|[@]PACKAGE_NAME@|$(PACKAGE_NAME)|g" \
-e "s|[@]PACKAGE_VERSION@|$(PACKAGE_VERSION)|g" \ -e "s|[@]PACKAGE_VERSION@|$(PACKAGE_VERSION)|g" \
-e "s|[@]GJS_VERSION@|$(GJS_VERSION)|g" \
-e "s|[@]HAVE_BLUETOOTH@|$(HAVE_BLUETOOTH)|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|[@]GETTEXT_PACKAGE@|$(GETTEXT_PACKAGE)|g" \
-e "s|[@]datadir@|$(datadir)|g" \ -e "s|[@]datadir@|$(datadir)|g" \
-e "s|[@]libexecdir@|$(libexecdir)|g" \ -e "s|[@]libexecdir@|$(libexecdir)|g" \
@@ -22,16 +24,17 @@ nobase_dist_js_DATA = \
gdm/loginDialog.js \ gdm/loginDialog.js \
gdm/powerMenu.js \ gdm/powerMenu.js \
gdm/systemd.js \ gdm/systemd.js \
gdm/util.js \
extensionPrefs/main.js \ extensionPrefs/main.js \
misc/config.js \ misc/config.js \
misc/extensionUtils.js \ misc/extensionUtils.js \
misc/fileUtils.js \ misc/fileUtils.js \
misc/format.js \
misc/gnomeSession.js \ misc/gnomeSession.js \
misc/history.js \ misc/history.js \
misc/jsParse.js \ misc/jsParse.js \
misc/modemManager.js \ misc/modemManager.js \
misc/params.js \ misc/params.js \
misc/screenSaver.js \
misc/util.js \ misc/util.js \
perf/core.js \ perf/core.js \
ui/altTab.js \ ui/altTab.js \
@@ -42,6 +45,7 @@ nobase_dist_js_DATA = \
ui/boxpointer.js \ ui/boxpointer.js \
ui/calendar.js \ ui/calendar.js \
ui/checkBox.js \ ui/checkBox.js \
ui/contactDisplay.js \
ui/ctrlAltTab.js \ ui/ctrlAltTab.js \
ui/dash.js \ ui/dash.js \
ui/dateMenu.js \ ui/dateMenu.js \
@@ -49,14 +53,13 @@ nobase_dist_js_DATA = \
ui/endSessionDialog.js \ ui/endSessionDialog.js \
ui/environment.js \ ui/environment.js \
ui/extensionSystem.js \ ui/extensionSystem.js \
ui/extensionDownloader.js \
ui/flashspot.js \ ui/flashspot.js \
ui/ibusCandidatePopup.js\
ui/iconGrid.js \ ui/iconGrid.js \
ui/keyboard.js \ ui/keyboard.js \
ui/keyringPrompt.js \ ui/keyringPrompt.js \
ui/layout.js \ ui/layout.js \
ui/lightbox.js \ ui/lightbox.js \
ui/link.js \
ui/lookingGlass.js \ ui/lookingGlass.js \
ui/magnifier.js \ ui/magnifier.js \
ui/magnifierDBus.js \ ui/magnifierDBus.js \
@@ -64,7 +67,6 @@ nobase_dist_js_DATA = \
ui/messageTray.js \ ui/messageTray.js \
ui/modalDialog.js \ ui/modalDialog.js \
ui/networkAgent.js \ ui/networkAgent.js \
ui/sessionMode.js \
ui/shellEntry.js \ ui/shellEntry.js \
ui/shellMountOperation.js \ ui/shellMountOperation.js \
ui/notificationDaemon.js \ ui/notificationDaemon.js \
@@ -81,6 +83,7 @@ nobase_dist_js_DATA = \
ui/search.js \ ui/search.js \
ui/searchDisplay.js \ ui/searchDisplay.js \
ui/shellDBus.js \ ui/shellDBus.js \
ui/statusIconDispatcher.js \
ui/status/accessibility.js \ ui/status/accessibility.js \
ui/status/keyboard.js \ ui/status/keyboard.js \
ui/status/network.js \ ui/status/network.js \
@@ -89,7 +92,6 @@ nobase_dist_js_DATA = \
ui/status/bluetooth.js \ ui/status/bluetooth.js \
ui/telepathyClient.js \ ui/telepathyClient.js \
ui/tweener.js \ ui/tweener.js \
ui/unlockDialog.js \
ui/userMenu.js \ ui/userMenu.js \
ui/viewSelector.js \ ui/viewSelector.js \
ui/wanda.js \ ui/wanda.js \

View File

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

View File

@@ -11,17 +11,10 @@ const FprintManagerIface = <interface name='net.reactivated.Fprint.Manager'>
</method> </method>
</interface>; </interface>;
const FprintManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(FprintManagerIface); const FprintManagerProxy = Gio.DBusProxy.makeProxyWrapper(FprintManagerIface);
function FprintManager() { function FprintManager() {
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.system, return new FprintManagerProxy(Gio.DBus.system,
g_interface_name: FprintManagerInfo.name, 'net.reactivated.Fprint',
g_interface_info: FprintManagerInfo, '/net/reactivated/Fprint/Manager');
g_name: 'net.reactivated.Fprint', };
g_object_path: '/net/reactivated/Fprint/Manager',
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
self.init(null);
return self;
}

View File

@@ -30,23 +30,83 @@ const Pango = imports.gi.Pango;
const Signals = imports.signals; const Signals = imports.signals;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const St = imports.gi.St; const St = imports.gi.St;
const Gdm = imports.gi.Gdm; const GdmGreeter = imports.gi.GdmGreeter;
const Batch = imports.gdm.batch; const Batch = imports.gdm.batch;
const Fprint = imports.gdm.fingerprint; const Fprint = imports.gdm.fingerprint;
const GdmUtil = imports.gdm.util;
const Lightbox = imports.ui.lightbox; const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main; const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog; const ModalDialog = imports.ui.modalDialog;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const _PASSWORD_SERVICE_NAME = 'gdm-password';
const _FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
const _FADE_ANIMATION_TIME = 0.16;
const _RESIZE_ANIMATION_TIME = 0.25; const _RESIZE_ANIMATION_TIME = 0.25;
const _SCROLL_ANIMATION_TIME = 0.5; const _SCROLL_ANIMATION_TIME = 2.0;
const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0; const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
const _LOGO_ICON_NAME_SIZE = 48; const _LOGO_ICON_NAME_SIZE = 48;
const _LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
const _FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
const _LOGO_KEY = 'logo';
let _loginDialog = null; let _loginDialog = null;
function _fadeInActor(actor) {
let hold = new Batch.Hold();
if (actor.opacity == 255 && actor.visible)
return null;
actor.show();
let [minHeight, naturalHeight] = actor.get_preferred_height(-1);
actor.opacity = 0;
actor.set_height(0);
Tweener.addTween(actor,
{ opacity: 255,
height: naturalHeight,
time: _FADE_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: function() {
actor.set_height(-1);
hold.release();
},
onCompleteScope: this
});
return hold;
}
function _fadeOutActor(actor) {
let hold = new Batch.Hold();
if (!actor.visible) {
actor.opacity = 0;
return null;
}
if (actor.opacity == 0) {
actor.hide();
return null;
}
Tweener.addTween(actor,
{ opacity: 0,
height: 0,
time: _FADE_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: function() {
actor.hide();
actor.set_height(-1);
hold.release();
},
onCompleteScope: this
});
return hold;
}
function _smoothlyResizeActor(actor, width, height) { function _smoothlyResizeActor(actor, width, height) {
let finalWidth; let finalWidth;
let finalHeight; let finalHeight;
@@ -88,37 +148,43 @@ const UserListItem = new Lang.Class({
this._userChangedId = this.user.connect('changed', this._userChangedId = this.user.connect('changed',
Lang.bind(this, this._onUserChanged)); Lang.bind(this, this._onUserChanged));
let layout = new St.BoxLayout({ vertical: false }); this._verticalBox = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-vertical-layout',
vertical: true });
this.actor = new St.Button({ style_class: 'login-dialog-user-list-item', this.actor = new St.Button({ style_class: 'login-dialog-user-list-item',
can_focus: true, can_focus: true,
child: layout, child: this._verticalBox,
reactive: true, reactive: true,
x_align: St.Align.START, x_align: St.Align.START,
x_fill: true }); x_fill: true });
let layout = new St.BoxLayout({ vertical: false });
this._verticalBox.add(layout,
{ y_fill: true,
x_fill: true,
expand: true });
this._focusBin = new St.Bin({ style_class: 'login-dialog-user-list-item-focus-bin' });
this._verticalBox.add(this._focusBin,
{ x_fill: false,
x_align: St.Align.MIDDLE,
y_fill: false,
expand: true });
this._iconBin = new St.Bin(); this._iconBin = new St.Bin();
layout.add(this._iconBin); layout.add(this._iconBin);
let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box', let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box',
vertical: true }); vertical: true });
layout.add(textLayout, { expand: true }); layout.add(textLayout,
{ y_fill: false,
y_align: St.Align.MIDDLE,
expand: true });
this._nameLabel = new St.Label({ text: this.user.get_real_name(), this._nameLabel = new St.Label({ text: this.user.get_real_name(),
style_class: 'login-dialog-user-list-item-name' }); style_class: 'login-dialog-user-list-item-name' });
textLayout.add(this._nameLabel, textLayout.add(this._nameLabel);
{ y_fill: false,
y_align: St.Align.MIDDLE,
expand: true });
this._timedLoginIndicator = new St.Bin({ style_class: 'login-dialog-timed-login-indicator',
scale_x: 0 });
textLayout.add(this._timedLoginIndicator,
{ x_fill: true,
x_align: St.Align.MIDDLE,
y_fill: false,
y_align: St.Align.END });
this._updateIcon(); this._updateIcon();
this._updateLoggedIn();
this.actor.connect('clicked', Lang.bind(this, this._onClicked)); this.actor.connect('clicked', Lang.bind(this, this._onClicked));
}, },
@@ -126,7 +192,6 @@ const UserListItem = new Lang.Class({
_onUserChanged: function() { _onUserChanged: function() {
this._nameLabel.set_text(this.user.get_real_name()); this._nameLabel.set_text(this.user.get_real_name());
this._updateIcon(); this._updateIcon();
this._updateLoggedIn();
}, },
_setIconFromFile: function(iconFile, styleClass) { _setIconFromFile: function(iconFile, styleClass) {
@@ -174,40 +239,30 @@ const UserListItem = new Lang.Class({
this._setIconFromName('avatar-default', 'login-dialog-user-list-item-icon'); this._setIconFromName('avatar-default', 'login-dialog-user-list-item-icon');
}, },
syncStyleClasses: function() {
this._updateLoggedIn();
if (global.stage.get_key_focus() == this.actor)
this.actor.add_style_pseudo_class('focus');
else
this.actor.remove_style_pseudo_class('focus');
},
_updateLoggedIn: function() {
if (this.user.is_logged_in())
this.actor.add_style_pseudo_class('logged-in');
else
this.actor.remove_style_pseudo_class('logged-in');
},
_onClicked: function() { _onClicked: function() {
this.emit('activate'); this.emit('activate');
}, },
fadeOutName: function() { fadeOutName: function() {
return GdmUtil.fadeOutActor(this._nameLabel); return _fadeOutActor(this._nameLabel);
}, },
fadeInName: function() { fadeInName: function() {
return GdmUtil.fadeInActor(this._nameLabel); return _fadeInActor(this._nameLabel);
}, },
showTimedLoginIndicator: function(time) { showFocusAnimation: function(time) {
let hold = new Batch.Hold(); let hold = new Batch.Hold();
this.hideTimedLoginIndicator(); let node = this.actor.get_theme_node();
Tweener.addTween(this._timedLoginIndicator, let padding = node.get_horizontal_padding();
{ scale_x: 1.,
let box = this._verticalBox.get_allocation_box();
Tweener.removeTweens(this._focusBin);
this._focusBin.width = 0;
Tweener.addTween(this._focusBin,
{ width: (box.x2 - box.x1 - padding),
time: time, time: time,
transition: 'linear', transition: 'linear',
onComplete: function() { onComplete: function() {
@@ -216,11 +271,6 @@ const UserListItem = new Lang.Class({
onCompleteScope: this onCompleteScope: this
}); });
return hold; return hold;
},
hideTimedLoginIndicator: function() {
Tweener.removeTweens(this._timedLoginIndicator);
this._timedLoginIndicator.scale_x = 0.;
} }
}); });
Signals.addSignalMethods(UserListItem.prototype); Signals.addSignalMethods(UserListItem.prototype);
@@ -234,10 +284,13 @@ const UserList = new Lang.Class({
Gtk.PolicyType.AUTOMATIC); Gtk.PolicyType.AUTOMATIC);
this._box = new St.BoxLayout({ vertical: true, this._box = new St.BoxLayout({ vertical: true,
style_class: 'login-dialog-user-list', style_class: 'login-dialog-user-list' });
pseudo_class: 'expanded' });
this.actor.add_actor(this._box); this.actor.add_actor(this._box,
{ x_fill: true,
y_fill: true,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
this._items = {}; this._items = {};
this.actor.connect('key-focus-in', Lang.bind(this, this._moveFocusToItems)); this.actor.connect('key-focus-in', Lang.bind(this, this._moveFocusToItems));
@@ -257,7 +310,7 @@ const UserList = new Lang.Class({
_showItem: function(item) { _showItem: function(item) {
let tasks = [function() { let tasks = [function() {
return GdmUtil.fadeInActor(item.actor); return _fadeInActor(item.actor);
}, },
function() { function() {
@@ -314,21 +367,17 @@ const UserList = new Lang.Class({
for (let userName in this._items) { for (let userName in this._items) {
let item = this._items[userName]; let item = this._items[userName];
item.actor.set_hover(false);
item.actor.reactive = false;
item.actor.can_focus = false; item.actor.can_focus = false;
item.syncStyleClasses(); item._focusBin.width = 0;
item._timedLoginIndicator.scale_x = 0.;
if (item != exception) if (item != exception)
tasks.push(function() { tasks.push(function() {
return GdmUtil.fadeOutActor(item.actor); return _fadeOutActor(item.actor);
}); });
} }
this._box.remove_style_pseudo_class('expanded');
let batch = new Batch.ConsecutiveBatch(this, let batch = new Batch.ConsecutiveBatch(this,
[function() { [function() {
return GdmUtil.fadeOutActor(this.actor.vscroll); return _fadeOutActor(this.actor.vscroll);
}, },
new Batch.ConcurrentBatch(this, tasks) new Batch.ConcurrentBatch(this, tasks)
@@ -372,16 +421,12 @@ const UserList = new Lang.Class({
for (let userName in this._items) { for (let userName in this._items) {
let item = this._items[userName]; let item = this._items[userName];
item.actor.sync_hover();
item.actor.reactive = true;
item.actor.can_focus = true; item.actor.can_focus = true;
item.syncStyleClasses();
tasks.push(function() { tasks.push(function() {
return this._showItem(item); return this._showItem(item);
}); });
} }
this._box.add_style_pseudo_class('expanded');
let batch = new Batch.ConsecutiveBatch(this, let batch = new Batch.ConsecutiveBatch(this,
[function() { [function() {
this.takeOverWhitespace(); this.takeOverWhitespace();
@@ -399,7 +444,7 @@ const UserList = new Lang.Class({
}, },
function() { function() {
return GdmUtil.fadeInActor(this.actor.vscroll); return _fadeInActor(this.actor.vscroll);
}]); }]);
return batch.run(); return batch.run();
}, },
@@ -414,7 +459,7 @@ const UserList = new Lang.Class({
Tweener.addTween (adjustment, Tweener.addTween (adjustment,
{ value: value, { value: value,
time: _SCROLL_ANIMATION_TIME, time: _SCROLL_ANIMATION_TIME,
transition: 'easeOutQuad' }); transition: 'linear' });
}, },
jumpToItem: function(item) { jumpToItem: function(item) {
@@ -443,9 +488,6 @@ const UserList = new Lang.Class({
if (user.is_system_account()) if (user.is_system_account())
return; return;
if (user.locked)
return;
let userName = user.get_user_name(); let userName = user.get_user_name();
if (!userName) if (!userName)
@@ -466,6 +508,7 @@ const UserList = new Lang.Class({
Lang.bind(this, Lang.bind(this,
function() { function() {
this.scrollToItem(item); this.scrollToItem(item);
item.showFocusAnimation(0);
})); }));
this._moveFocusToItems(); this._moveFocusToItems();
@@ -507,7 +550,10 @@ const SessionListItem = new Lang.Class({
this._box = new St.BoxLayout({ style_class: 'login-dialog-session-list-item-box' }); this._box = new St.BoxLayout({ style_class: 'login-dialog-session-list-item-box' });
this.actor.add_actor(this._box); this.actor.add_actor(this._box,
{ expand: true,
x_fill: true,
y_fill: true });
this.actor.connect('clicked', Lang.bind(this, this._onClicked)); this.actor.connect('clicked', Lang.bind(this, this._onClicked));
this._dot = new St.DrawingArea({ style_class: 'login-dialog-session-list-item-dot' }); this._dot = new St.DrawingArea({ style_class: 'login-dialog-session-list-item-dot' });
@@ -518,7 +564,10 @@ const SessionListItem = new Lang.Class({
let label = new St.Label({ style_class: 'login-dialog-session-list-item-label', let label = new St.Label({ style_class: 'login-dialog-session-list-item-label',
text: name }); text: name });
this._box.add_actor(label); this._box.add_actor(label,
{ expand: true,
x_fill: true,
y_fill: true });
}, },
setShowDot: function(show) { setShowDot: function(show) {
@@ -562,7 +611,10 @@ const SessionList = new Lang.Class({
x_fill: true, x_fill: true,
y_fill: true }); y_fill: true });
let box = new St.BoxLayout(); let box = new St.BoxLayout();
this._button.add_actor(box); this._button.add_actor(box,
{ x_fill: true,
y_fill: true,
expand: true });
this._triangle = new St.Label({ style_class: 'login-dialog-session-list-triangle', this._triangle = new St.Label({ style_class: 'login-dialog-session-list-triangle',
text: '\u25B8' }); text: '\u25B8' });
@@ -570,18 +622,30 @@ const SessionList = new Lang.Class({
let label = new St.Label({ style_class: 'login-dialog-session-list-label', let label = new St.Label({ style_class: 'login-dialog-session-list-label',
text: _("Session...") }); text: _("Session...") });
box.add_actor(label); box.add_actor(label,
{ x_fill: true,
y_fill: true,
expand: true });
this._button.connect('clicked', this._button.connect('clicked',
Lang.bind(this, this._onClicked)); Lang.bind(this, this._onClicked));
this._box.add_actor(this._button); this._box.add_actor(this._button,
{ x_fill: true,
y_fill: true,
expand: true });
this._scrollView = new St.ScrollView({ style_class: 'login-dialog-session-list-scroll-view'}); this._scrollView = new St.ScrollView({ style_class: 'login-dialog-session-list-scroll-view'});
this._scrollView.set_policy(Gtk.PolicyType.NEVER, this._scrollView.set_policy(Gtk.PolicyType.NEVER,
Gtk.PolicyType.AUTOMATIC); Gtk.PolicyType.AUTOMATIC);
this._box.add_actor(this._scrollView); this._box.add_actor(this._scrollView,
{ x_fill: true,
y_fill: true,
expand: true });
this._itemList = new St.BoxLayout({ style_class: 'login-dialog-session-item-list', this._itemList = new St.BoxLayout({ style_class: 'login-dialog-session-item-list',
vertical: true }); vertical: true });
this._scrollView.add_actor(this._itemList); this._scrollView.add_actor(this._itemList,
{ x_fill: true,
y_fill: true,
expand: true });
this._scrollView.hide(); this._scrollView.hide();
this.isOpen = false; this.isOpen = false;
this._populate(); this._populate();
@@ -634,7 +698,7 @@ const SessionList = new Lang.Class({
this._activeSessionId = null; this._activeSessionId = null;
this._items = {}; this._items = {};
let ids = Gdm.get_session_ids(); let ids = GdmGreeter.get_session_ids();
ids.sort(); ids.sort();
if (ids.length <= 1) { if (ids.length <= 1) {
@@ -646,10 +710,14 @@ const SessionList = new Lang.Class({
} }
for (let i = 0; i < ids.length; i++) { for (let i = 0; i < ids.length; i++) {
let [sessionName, sessionDescription] = Gdm.get_session_name_and_description(ids[i]); let [sessionName, sessionDescription] = GdmGreeter.get_session_name_and_description(ids[i]);
let item = new SessionListItem(ids[i], sessionName); let item = new SessionListItem(ids[i], sessionName);
this._itemList.add_actor(item.actor); this._itemList.add_actor(item.actor,
{ x_align: St.Align.START,
y_align: St.Align.START,
x_fill: true,
y_fill: true });
this._items[ids[i]] = item; this._items[ids[i]] = item;
if (!this._activeSessionId) if (!this._activeSessionId)
@@ -668,55 +736,52 @@ const LoginDialog = new Lang.Class({
Name: 'LoginDialog', Name: 'LoginDialog',
Extends: ModalDialog.ModalDialog, Extends: ModalDialog.ModalDialog,
_init: function(parentActor) { _init: function() {
this.parent({ shellReactive: true, this.parent({ shellReactive: true, styleClass: 'login-dialog' });
styleClass: 'login-dialog',
parentActor: parentActor
});
this.connect('destroy', this.connect('destroy',
Lang.bind(this, this._onDestroy)); Lang.bind(this, this._onDestroy));
this.connect('opened', this.connect('opened',
Lang.bind(this, this._onOpened)); Lang.bind(this, this._onOpened));
this._userManager = AccountsService.UserManager.get_default() this._userManager = AccountsService.UserManager.get_default()
this._greeterClient = new Gdm.Client(); this._greeterClient = new GdmGreeter.Client();
this._greeter = this._greeterClient.get_greeter_sync(null); this._greeterClient.open_connection();
this._greeter.connect('default-session-name-changed', this._greeterClient.call_start_conversation(_PASSWORD_SERVICE_NAME);
Lang.bind(this, this._onDefaultSessionChanged));
this._greeter.connect('session-opened', this._greeterClient.connect('reset',
Lang.bind(this, this._onSessionOpened)); Lang.bind(this, this._onReset));
this._greeter.connect('timed-login-requested', this._greeterClient.connect('default-session-changed',
Lang.bind(this, this._onTimedLoginRequested)); Lang.bind(this, this._onDefaultSessionChanged));
this._greeterClient.connect('info',
Lang.bind(this, this._onInfo));
this._greeterClient.connect('problem',
Lang.bind(this, this._onProblem));
this._greeterClient.connect('info-query',
Lang.bind(this, this._onInfoQuery));
this._greeterClient.connect('secret-info-query',
Lang.bind(this, this._onSecretInfoQuery));
this._greeterClient.connect('session-opened',
Lang.bind(this, this._onSessionOpened));
this._greeterClient.connect('timed-login-requested',
Lang.bind(this, this._onTimedLoginRequested));
this._greeterClient.connect('authentication-failed',
Lang.bind(this, this._onAuthenticationFailed));
this._greeterClient.connect('conversation-stopped',
Lang.bind(this, this._onConversationStopped));
this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient); this._settings = new Gio.Settings({ schema: _LOGIN_SCREEN_SCHEMA });
this._userVerifier.connect('ask-question', Lang.bind(this, this._askQuestion));
this._userVerifier.connect('verification-failed', Lang.bind(this, this._onVerificationFailed));
this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
this._userVerifier.connect('show-fingerprint-prompt', Lang.bind(this, this._showFingerprintPrompt)); this._fprintManager = new Fprint.FprintManager();
this._userVerifier.connect('hide-fingerprint-prompt', Lang.bind(this, this._hideFingerprintPrompt)); this._startFingerprintConversationIfNeeded();
this._settings.connect('changed::' + _LOGO_KEY,
this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
this._settings.connect('changed::' + GdmUtil.LOGO_KEY,
Lang.bind(this, this._updateLogo)); Lang.bind(this, this._updateLogo));
this._settings.connect('changed::' + GdmUtil.BANNER_MESSAGE_KEY,
Lang.bind(this, this._updateBanner));
this._settings.connect('changed::' + GdmUtil.BANNER_MESSAGE_TEXT_KEY,
Lang.bind(this, this._updateBanner));
this._logoBox = new St.Bin({ style_class: 'login-dialog-logo-box' }); this._logoBox = new St.Bin({ style_class: 'login-dialog-logo-box' });
this.contentLayout.add(this._logoBox); this.contentLayout.add(this._logoBox);
this._updateLogo(); this._updateLogo();
this._bannerLabel = new St.Label({ style_class: 'login-dialog-banner',
text: '' });
this.contentLayout.add(this._bannerLabel);
this._updateBanner();
this._titleLabel = new St.Label({ style_class: 'login-dialog-title', this._titleLabel = new St.Label({ style_class: 'login-dialog-title',
text: C_("title", "Sign In") }); text: C_("title", "Sign In") });
@@ -771,7 +836,7 @@ const LoginDialog = new Lang.Class({
this._sessionList = new SessionList(); this._sessionList = new SessionList();
this._sessionList.connect('session-activated', this._sessionList.connect('session-activated',
Lang.bind(this, function(list, sessionId) { Lang.bind(this, function(list, sessionId) {
this._greeter.call_select_session_sync (sessionId, null); this._greeterClient.call_select_session (sessionId);
})); }));
this._promptBox.add(this._sessionList.actor, this._promptBox.add(this._sessionList.actor,
@@ -819,9 +884,25 @@ const LoginDialog = new Lang.Class({
}, },
_startFingerprintConversationIfNeeded: function() {
this._haveFingerprintReader = false;
if (!this._settings.get_boolean(_FINGERPRINT_AUTHENTICATION_KEY))
return;
this._fprintManager.GetDefaultDeviceRemote(Gio.DBusCallFlags.NONE, Lang.bind(this,
function(device, error) {
if (!error && device)
this._haveFingerprintReader = true;
if (this._haveFingerprintReader)
this._greeterClient.call_start_conversation(_FINGERPRINT_SERVICE_NAME);
}));
},
_updateLogo: function() { _updateLogo: function() {
this._logoBox.child = null; this._logoBox.child = null;
let path = this._settings.get_string(GdmUtil.LOGO_KEY); let path = this._settings.get_string(_LOGO_KEY);
if (path) { if (path) {
let file = Gio.file_new_for_path(path); let file = Gio.file_new_for_path(path);
@@ -833,19 +914,10 @@ const LoginDialog = new Lang.Class({
}, },
_updateBanner: function() {
let enabled = this._settings.get_boolean(GdmUtil.BANNER_MESSAGE_KEY);
let text = this._settings.get_string(GdmUtil.BANNER_MESSAGE_TEXT_KEY);
if (enabled && text) {
this._bannerLabel.set_text(text);
this._fadeInBanner();
} else {
this._fadeOutBanner();
}
},
_onReset: function(client, serviceName) { _onReset: function(client, serviceName) {
this._greeterClient.call_start_conversation(_PASSWORD_SERVICE_NAME);
this._startFingerprintConversationIfNeeded();
let tasks = [this._hidePrompt, let tasks = [this._hidePrompt,
new Batch.ConcurrentBatch(this, [this._fadeInTitleLabel, new Batch.ConcurrentBatch(this, [this._fadeInTitleLabel,
@@ -875,25 +947,43 @@ const LoginDialog = new Lang.Class({
this._sessionList.setActiveSession(sessionId); this._sessionList.setActiveSession(sessionId);
}, },
_showFingerprintPrompt: function() { _onInfo: function(client, serviceName, info) {
GdmUtil.fadeInActor(this._promptFingerprintMessage); // We don't display fingerprint messages, because they
// have words like UPEK in them. Instead we use the messages
// as a cue to display our own message.
if (serviceName == _FINGERPRINT_SERVICE_NAME &&
this._haveFingerprintReader &&
(!this._promptFingerprintMessage.visible ||
this._promptFingerprintMessage.opacity != 255)) {
_fadeInActor(this._promptFingerprintMessage);
return;
}
if (serviceName != _PASSWORD_SERVICE_NAME)
return;
Main.notifyError(info);
}, },
_hideFingerprintPrompt: function() { _onProblem: function(client, serviceName, problem) {
GdmUtil.fadeOutActor(this._promptFingerprintMessage); // we don't want to show auth failed messages to
// users who haven't enrolled their fingerprint.
if (serviceName != _PASSWORD_SERVICE_NAME)
return;
Main.notifyError(problem);
}, },
cancel: function() { _onCancel: function(client) {
this._userVerifier.cancel(); this._greeterClient.call_cancel();
}, },
_fadeInPrompt: function() { _fadeInPrompt: function() {
let tasks = [function() { let tasks = [function() {
return GdmUtil.fadeInActor(this._promptLabel); return _fadeInActor(this._promptLabel);
}, },
function() { function() {
return GdmUtil.fadeInActor(this._promptEntry); return _fadeInActor(this._promptEntry);
}, },
function() { function() {
@@ -904,14 +994,14 @@ const LoginDialog = new Lang.Class({
}, },
function() { function() {
return GdmUtil.fadeInActor(this._promptBox); return _fadeInActor(this._promptBox);
}, },
function() { function() {
if (this._user && this._user.is_logged_in()) if (this._user && this._user.is_logged_in())
return null; return null;
return GdmUtil.fadeInActor(this._sessionList.actor); return _fadeInActor(this._sessionList.actor);
}, },
function() { function() {
@@ -926,14 +1016,13 @@ const LoginDialog = new Lang.Class({
_showPrompt: function() { _showPrompt: function() {
let hold = new Batch.Hold(); let hold = new Batch.Hold();
let buttons = [{ action: Lang.bind(this, this.cancel), let buttons = [{ action: Lang.bind(this, this._onCancel),
label: _("Cancel"), label: _("Cancel"),
key: Clutter.Escape }, key: Clutter.Escape },
{ action: Lang.bind(this, function() { { action: Lang.bind(this, function() {
hold.release(); hold.release();
}), }),
label: C_("button", "Sign In"), label: C_("button", "Sign In") }];
default: true }];
this._promptEntryActivateCallbackId = this._promptEntry.clutter_text.connect('activate', this._promptEntryActivateCallbackId = this._promptEntry.clutter_text.connect('activate',
Lang.bind(this, function() { Lang.bind(this, function() {
@@ -968,7 +1057,7 @@ const LoginDialog = new Lang.Class({
this.setButtons([]); this.setButtons([]);
let tasks = [function() { let tasks = [function() {
return GdmUtil.fadeOutActor(this._promptBox); return _fadeOutActor(this._promptBox);
}, },
function() { function() {
@@ -983,27 +1072,43 @@ const LoginDialog = new Lang.Class({
return batch.run(); return batch.run();
}, },
_askQuestion: function(verifier, serviceName, question, passwordChar) { _askQuestion: function(serviceName, question) {
this._promptLabel.set_text(question); this._promptLabel.set_text(question);
this._promptEntry.set_text('');
this._promptEntry.clutter_text.set_password_char(passwordChar);
let tasks = [this._showPrompt, let tasks = [this._showPrompt,
function() { function() {
let _text = this._promptEntry.get_text(); let _text = this._promptEntry.get_text();
this._promptEntry.reactive = false; this._promptEntry.reactive = false;
this._promptEntry.add_style_pseudo_class('insensitive'); this._promptEntry.add_style_pseudo_class('insensitive');
this._userVerifier.answerQuery(serviceName, _text); this._greeterClient.call_answer_query(serviceName, _text);
}]; }];
let batch = new Batch.ConsecutiveBatch(this, tasks); let batch = new Batch.ConsecutiveBatch(this, tasks);
return batch.run(); return batch.run();
}, },
_onInfoQuery: function(client, serviceName, question) {
// We only expect questions to come from the main auth service
if (serviceName != _PASSWORD_SERVICE_NAME)
return;
this._promptEntry.set_text('');
this._promptEntry.clutter_text.set_password_char('');
this._askQuestion(serviceName, question);
},
_onSecretInfoQuery: function(client, serviceName, secretQuestion) {
// We only expect secret requests to come from the main auth service
if (serviceName != _PASSWORD_SERVICE_NAME)
return;
this._promptEntry.set_text('');
this._promptEntry.clutter_text.set_password_char('\u25cf');
this._askQuestion(serviceName, secretQuestion);
},
_onSessionOpened: function(client, serviceName) { _onSessionOpened: function(client, serviceName) {
this._greeter.call_start_session_when_ready_sync(serviceName, true, null); this._greeterClient.call_start_session_when_ready(serviceName, true);
}, },
_waitForItemForUser: function(userName) { _waitForItemForUser: function(userName) {
@@ -1030,7 +1135,7 @@ const LoginDialog = new Lang.Class({
_showTimedLoginAnimation: function() { _showTimedLoginAnimation: function() {
this._timedLoginItem.actor.grab_key_focus(); this._timedLoginItem.actor.grab_key_focus();
return this._timedLoginItem.showTimedLoginIndicator(this._timedLoginAnimationTime); return this._timedLoginItem.showFocusAnimation(this._timedLoginAnimationTime);
}, },
_blockTimedLoginUntilIdle: function() { _blockTimedLoginUntilIdle: function() {
@@ -1071,6 +1176,7 @@ const LoginDialog = new Lang.Class({
// item. // item.
if (!this.is_loaded) { if (!this.is_loaded) {
this._userList.jumpToItem(this._timedLoginItem); this._userList.jumpToItem(this._timedLoginItem);
this._timedLoginItem.showFocusAnimation(0);
} }
}, },
@@ -1084,7 +1190,7 @@ const LoginDialog = new Lang.Class({
function() { function() {
this._timedLoginBatch = null; this._timedLoginBatch = null;
this._greeter.call_begin_auto_login_sync(userName, null); this._greeterClient.call_begin_auto_login(userName);
}]; }];
this._timedLoginBatch = new Batch.ConsecutiveBatch(this, tasks); this._timedLoginBatch = new Batch.ConsecutiveBatch(this, tasks);
@@ -1098,9 +1204,6 @@ const LoginDialog = new Lang.Class({
this._timedLoginBatch = null; this._timedLoginBatch = null;
} }
if (this._timedLoginItem)
this._timedLoginItem.hideTimedLoginIndicator();
let userName = this._timedLoginItem.user.get_user_name(); let userName = this._timedLoginItem.user.get_user_name();
if (userName) if (userName)
@@ -1130,8 +1233,19 @@ const LoginDialog = new Lang.Class({
})); }));
}, },
_onVerificationFailed: function() { _onAuthenticationFailed: function(client) {
this._userVerifier.cancel(); this._greeterClient.call_cancel();
},
_onConversationStopped: function(client, serviceName) {
// if the password service fails, then cancel everything.
// But if, e.g., fingerprint fails, still give
// password authentication a chance to succeed
if (serviceName == _PASSWORD_SERVICE_NAME) {
this._greeterClient.call_cancel();
} else if (serviceName == _FINGERPRINT_SERVICE_NAME) {
_fadeOutActor(this._promptFingerprintMessage);
}
}, },
_onNotListedClicked: function(user) { _onNotListedClicked: function(user) {
@@ -1152,10 +1266,7 @@ const LoginDialog = new Lang.Class({
this._fadeOutLogo]), this._fadeOutLogo]),
function() { function() {
let hold = new Batch.Hold(); this._greeterClient.call_begin_verification(_PASSWORD_SERVICE_NAME);
this._userVerifier.begin(null, hold);
return hold;
}]; }];
let batch = new Batch.ConsecutiveBatch(this, tasks); let batch = new Batch.ConsecutiveBatch(this, tasks);
@@ -1163,47 +1274,30 @@ const LoginDialog = new Lang.Class({
}, },
_fadeInLogo: function() { _fadeInLogo: function() {
return GdmUtil.fadeInActor(this._logoBox); return _fadeInActor(this._logoBox);
}, },
_fadeOutLogo: function() { _fadeOutLogo: function() {
return GdmUtil.fadeOutActor(this._logoBox); return _fadeOutActor(this._logoBox);
},
_fadeInBanner: function() {
return GdmUtil.fadeInActor(this._bannerLabel);
},
_fadeOutBanner: function() {
return GdmUtil.fadeOutActor(this._bannerLabel);
}, },
_fadeInTitleLabel: function() { _fadeInTitleLabel: function() {
return GdmUtil.fadeInActor(this._titleLabel); return _fadeInActor(this._titleLabel);
}, },
_fadeOutTitleLabel: function() { _fadeOutTitleLabel: function() {
return GdmUtil.fadeOutActor(this._titleLabel); return _fadeOutActor(this._titleLabel);
}, },
_fadeInNotListedButton: function() { _fadeInNotListedButton: function() {
return GdmUtil.fadeInActor(this._notListedButton); return _fadeInActor(this._notListedButton);
}, },
_fadeOutNotListedButton: function() { _fadeOutNotListedButton: function() {
return GdmUtil.fadeOutActor(this._notListedButton); return _fadeOutActor(this._notListedButton);
},
_beginVerificationForUser: function(userName) {
let hold = new Batch.Hold();
this._userVerifier.begin(userName, hold);
return hold;
}, },
_onUserListActivated: function(activatedItem) { _onUserListActivated: function(activatedItem) {
let userName;
let tasks = [function() { let tasks = [function() {
this._userList.actor.reactive = false; this._userList.actor.reactive = false;
return this._userList.pinInPlace(); return this._userList.pinInPlace();
@@ -1230,9 +1324,12 @@ const LoginDialog = new Lang.Class({
}, },
function() { function() {
userName = activatedItem.user.get_user_name(); let userName = activatedItem.user.get_user_name();
this._greeterClient.call_begin_verification_for_user(_PASSWORD_SERVICE_NAME,
userName);
return this._beginVerificationForUser(userName); if (this._haveFingerprintReader)
this._greeterClient.call_begin_verification_for_user(_FINGERPRINT_SERVICE_NAME, userName);
}]; }];
this._user = activatedItem.user; this._user = activatedItem.user;

View File

@@ -60,8 +60,10 @@ const PowerMenuButton = new Lang.Class({
}, },
_updateVisibility: function() { _updateVisibility: function() {
let shouldBeVisible = (this._haveSuspend || this._haveShutdown || this._haveRestart); if (!this._haveSuspend && !this._haveShutdown && !this._haveRestart)
this.actor.visible = shouldBeVisible; this.actor.hide();
else
this.actor.show();
}, },
_updateHaveShutdown: function() { _updateHaveShutdown: function() {
@@ -70,22 +72,31 @@ const PowerMenuButton = new Lang.Class({
this._systemdLoginManager.CanPowerOffRemote(Lang.bind(this, this._systemdLoginManager.CanPowerOffRemote(Lang.bind(this,
function(result, error) { function(result, error) {
if (!error) if (!error)
this._haveShutdown = result[0] != 'no'; this._haveShutdown = result != 'no';
else else
this._haveShutdown = false; this._haveShutdown = false;
this._powerOffItem.actor.visible = this._haveShutdown; if (this._haveShutdown)
this._powerOffItem.actor.show();
else
this._powerOffItem.actor.hide();
this._updateVisibility(); this._updateVisibility();
})); }));
} else { } else {
this._consoleKitManager.CanStopRemote(Lang.bind(this, this._consoleKitManager.CanStopRemote(Lang.bind(this,
function(result, error) { function(result, error) {
if (!error) if (!error)
this._haveShutdown = result[0]; this._haveShutdown = result;
else else
this._haveShutdown = false; this._haveShutdown = false;
this._powerOffItem.actor.visible = this._haveShutdown; if (this._haveShutdown) {
this._powerOffItem.actor.show();
} else {
this._powerOffItem.actor.hide();
}
this._updateVisibility(); this._updateVisibility();
})); }));
} }
@@ -97,22 +108,31 @@ const PowerMenuButton = new Lang.Class({
this._systemdLoginManager.CanRebootRemote(Lang.bind(this, this._systemdLoginManager.CanRebootRemote(Lang.bind(this,
function(result, error) { function(result, error) {
if (!error) if (!error)
this._haveRestart = result[0] != 'no'; this._haveRestart = result != 'no';
else else
this._haveRestart = false; this._haveRestart = false;
this._restartItem.actor.visible = this._haveRestart; if (this._haveRestart)
this._restartItem.actor.show();
else
this._restartItem.actor.hide();
this._updateVisibility(); this._updateVisibility();
})); }));
} else { } else {
this._consoleKitManager.CanRestartRemote(Lang.bind(this, this._consoleKitManager.CanRestartRemote(Lang.bind(this,
function(result, error) { function(result, error) {
if (!error) if (!error)
this._haveRestart = result[0]; this._haveRestart = result;
else else
this._haveRestart = false; this._haveRestart = false;
this._restartItem.actor.visible = this._haveRestart; if (this._haveRestart) {
this._restartItem.actor.show();
} else {
this._restartItem.actor.hide();
}
this._updateVisibility(); this._updateVisibility();
})); }));
} }
@@ -120,7 +140,12 @@ const PowerMenuButton = new Lang.Class({
_updateHaveSuspend: function() { _updateHaveSuspend: function() {
this._haveSuspend = this._upClient.get_can_suspend(); this._haveSuspend = this._upClient.get_can_suspend();
this._suspendItem.actor.visible = this._haveSuspend;
if (this._haveSuspend)
this._suspendItem.actor.show();
else
this._suspendItem.actor.hide();
this._updateVisibility(); this._updateVisibility();
}, },

View File

@@ -1,268 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Signals = imports.signals;
const Batch = imports.gdm.batch;
const Fprint = imports.gdm.fingerprint;
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 LOGO_KEY = 'logo';
function fadeInActor(actor) {
if (actor.opacity == 255 && actor.visible)
return null;
let hold = new Batch.Hold();
actor.show();
let [minHeight, naturalHeight] = actor.get_preferred_height(-1);
actor.opacity = 0;
actor.set_height(0);
Tweener.addTween(actor,
{ opacity: 255,
height: naturalHeight,
time: FADE_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: function() {
this.set_height(-1);
hold.release();
},
});
return hold;
}
function fadeOutActor(actor) {
if (!actor.visible || actor.opacity == 0) {
actor.opacity = 0;
actor.hide();
return null;
}
let hold = new Batch.Hold();
Tweener.addTween(actor,
{ opacity: 0,
height: 0,
time: FADE_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: function() {
this.hide();
this.set_height(-1);
hold.release();
},
});
return hold;
}
const ShellUserVerifier = new Lang.Class({
Name: 'ShellUserVerifier',
_init: function(client, params) {
params = Params.parse(params, { reauthenticationOnly: false });
this._reauthOnly = params.reauthenticationOnly;
this._client = client;
this._settings = new Gio.Settings({ schema: LOGIN_SCREEN_SCHEMA });
this._cancellable = new Gio.Cancellable();
this._fprintManager = new Fprint.FprintManager();
this._checkForFingerprintReader();
},
begin: function(userName, hold) {
this._hold = hold;
this._userName = userName;
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() {
this._cancellable.cancel();
if (this._userVerifier)
this._userVerifier.call_cancel_sync(null);
},
clear: function() {
this._cancellable.cancel();
if (this._userVerifier) {
this._userVerifier.run_dispose();
this._userVerifier = null;
}
},
answerQuery: function(serviceName, answer) {
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
},
_checkForFingerprintReader: function() {
this._haveFingerprintReader = false;
if (!this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY))
return;
this._fprintManager.GetDefaultDeviceRemote(Gio.DBusCallFlags.NONE, this._cancellable, Lang.bind(this,
function(device, error) {
if (!error && device)
this._haveFingerprintReader = true;
}));
},
_reauthenticationChannelOpened: function(client, result) {
try {
this._userVerifier = client.open_reauthentication_channel_finish(result);
this._connectSignals();
this._beginVerification();
this._hold.release();
} catch (e) {
if (this._reauthOnly) {
logError(e, 'Failed to open reauthentication channel');
this._hold.release();
this.emit('verification-failed');
return;
}
// If there's no session running, or it otherwise fails, then fall back
// to performing verification from this login session
client.get_user_verifier(this._cancellable, Lang.bind(this, this._userVerifierGot));
}
},
_userVerifierGot: function(client, result) {
this._userVerifier = client.get_user_verifier_finish(result);
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) {
obj.call_begin_verification_for_user_finish(result);
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) {
obj.call_begin_verification_for_user_finish(result);
this._hold.release();
}));
}
} else {
this._userVerifier.call_begin_verification(PASSWORD_SERVICE_NAME,
this._cancellable,
Lang.bind(this, function(obj, result) {
obj.call_begin_verification_finish(result);
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) {
this.emit('show-fingerprint-prompt');
} else if (serviceName == PASSWORD_SERVICE_NAME) {
Main.notifyError(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;
Main.notifyError(problem);
},
_onInfoQuery: function(client, serviceName, question) {
// We only expect questions to come from the main auth service
if (serviceName != PASSWORD_SERVICE_NAME)
return;
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._userVerifier.run_dispose();
this._userVerifier = null;
this._checkForFingerprintReader();
this.emit('reset');
},
_onVerificationComplete: function() {
this.emit('verification-complete');
},
_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.emit('verification-failed');
} else if (serviceName == FINGERPRINT_SERVICE_NAME) {
this.emit('hide-fingerprint-prompt');
}
},
});
Signals.addSignalMethods(ShellUserVerifier.prototype);

View File

@@ -4,8 +4,12 @@
const PACKAGE_NAME = '@PACKAGE_NAME@'; const PACKAGE_NAME = '@PACKAGE_NAME@';
/* The version of this package */ /* The version of this package */
const PACKAGE_VERSION = '@PACKAGE_VERSION@'; 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 */ /* 1 if gnome-bluetooth is available, 0 otherwise */
const HAVE_BLUETOOTH = @HAVE_BLUETOOTH@; const HAVE_BLUETOOTH = @HAVE_BLUETOOTH@;
/* The system TLS CA list */
const SHELL_SYSTEM_CA_FILE = '@SHELL_SYSTEM_CA_FILE@';
/* gettext package */ /* gettext package */
const GETTEXT_PACKAGE = '@GETTEXT_PACKAGE@'; const GETTEXT_PACKAGE = '@GETTEXT_PACKAGE@';
/* locale dir */ /* locale dir */

View File

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

View File

@@ -28,7 +28,7 @@ function deleteGFile(file) {
return file['delete'](null); return file['delete'](null);
} }
function recursivelyDeleteDir(dir, deleteParent) { function recursivelyDeleteDir(dir) {
let children = dir.enumerate_children('standard::name,standard::type', let children = dir.enumerate_children('standard::name,standard::type',
Gio.FileQueryInfoFlags.NONE, null); Gio.FileQueryInfoFlags.NONE, null);
@@ -39,29 +39,8 @@ function recursivelyDeleteDir(dir, deleteParent) {
if (type == Gio.FileType.REGULAR) if (type == Gio.FileType.REGULAR)
deleteGFile(child); deleteGFile(child);
else if (type == Gio.FileType.DIRECTORY) else if (type == Gio.FileType.DIRECTORY)
recursivelyDeleteDir(child, true); recursivelyDeleteDir(child);
} }
if (deleteParent) deleteGFile(dir);
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);
}
} }

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

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

View File

@@ -50,20 +50,9 @@ const SessionManagerIface = <interface name="org.gnome.SessionManager">
<arg type="u" direction="in" /> <arg type="u" direction="in" />
</method> </method>
<method name="Shutdown" /> <method name="Shutdown" />
<method name="Reboot" />
<method name="CanShutdown"> <method name="CanShutdown">
<arg type="b" direction="out" /> <arg type="b" direction="out" />
</method> </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>; </interface>;
var SessionManagerProxy = Gio.DBusProxy.makeProxyWrapper(SessionManagerIface); var SessionManagerProxy = Gio.DBusProxy.makeProxyWrapper(SessionManagerIface);

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 -*- // -*- 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 GLib = imports.gi.GLib;
const Shell = imports.gi.Shell;
const Main = imports.ui.main; const Main = imports.ui.main;
@@ -80,33 +83,24 @@ function spawnCommandLine(command_line) {
// this will throw an error. // this will throw an error.
function trySpawn(argv) function trySpawn(argv)
{ {
var success, pid;
try { try {
[success, pid] = GLib.spawn_async(null, argv, null, GLib.spawn_async(null, argv, null,
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD, GLib.SpawnFlags.SEARCH_PATH,
null, null); null, null);
} catch (err) { } catch (err) {
/* Rewrite the error in case of ENOENT */ if (err.code == GLib.SpawnError.G_SPAWN_ERROR_NOENT) {
if (err.matches(GLib.SpawnError, GLib.SpawnError.NOENT)) { err.message = _("Command not found");
throw new GLib.SpawnError({ code: GLib.SpawnError.NOENT, } else {
message: _("Command not found") });
} else if (err instanceof GLib.Error) {
// The exception from gjs contains an error string like: // The exception from gjs contains an error string like:
// Error invoking GLib.spawn_command_line_async: Failed to // Error invoking GLib.spawn_command_line_async: Failed to
// execute child process "foo" (No such file or directory) // execute child process "foo" (No such file or directory)
// We are only interested in the part in the parentheses. (And // We are only interested in the part in the parentheses. (And
// we can't pattern match the text, since it gets localized.) // we can't pattern match the text, since it gets localized.)
let message = err.message.replace(/.*\((.+)\)/, '$1'); err.message = err.message.replace(/.*\((.+)\)/, '$1');
throw new (err.constructor)({ code: err.code,
message: message });
} else {
throw err;
} }
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: // trySpawnCommandLine:
@@ -150,7 +144,7 @@ function killall(processName) {
// whatever... // whatever...
let argv = ['pkill', '-f', '^([^ ]*/)?' + processName + '($| )']; 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 // It might be useful to return success/failure, but we'd need
// a wrapper around WIFEXITED and WEXITSTATUS. Since none of // a wrapper around WIFEXITED and WEXITSTATUS. Since none of
// the current callers care, we don't bother. // the current callers care, we don't bother.

View File

@@ -1,7 +1,5 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const System = imports.system;
const Main = imports.ui.main; const Main = imports.ui.main;
const Scripting = imports.ui.scripting; const Scripting = imports.ui.scripting;
@@ -101,7 +99,7 @@ function run() {
Main.overview.hide(); Main.overview.hide();
yield Scripting.waitLeisure(); yield Scripting.waitLeisure();
System.gc(); global.gc();
yield Scripting.sleep(1000); yield Scripting.sleep(1000);
Scripting.collectStatistics(); Scripting.collectStatistics();
Scripting.scriptEvent('afterShowHide'); Scripting.scriptEvent('afterShowHide');

View File

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

View File

@@ -9,9 +9,7 @@ const Params = imports.misc.params;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Main = imports.ui.main; const Main = imports.ui.main;
const ShellMountOperation = imports.ui.shellMountOperation; const ShellMountOperation = imports.ui.shellMountOperation;
const GnomeSession = imports.misc.gnomeSession; const ScreenSaver = imports.misc.screenSaver;
const GNOME_SESSION_AUTOMOUNT_INHIBIT = 16;
// GSettings keys // GSettings keys
const SETTINGS_SCHEMA = 'org.gnome.desktop.media-handling'; const SETTINGS_SCHEMA = 'org.gnome.desktop.media-handling';
@@ -81,17 +79,13 @@ const AutomountManager = new Lang.Class({
_init: function() { _init: function() {
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA }); this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
this._volumeQueue = []; 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;
if (!haveSystemd()) if (!haveSystemd())
this.ckListener = new ConsoleKitManager(); this.ckListener = new ConsoleKitManager();
Main.screenShield.connect('lock-status-changed', Lang.bind(this, this._lockStatusChanged)); this._ssProxy = new ScreenSaver.ScreenSaverProxy();
this._ssProxy.connectSignal('ActiveChanged',
Lang.bind(this, this._screenSaverActiveChanged));
this._volumeMonitor = Gio.VolumeMonitor.get(); this._volumeMonitor = Gio.VolumeMonitor.get();
@@ -114,18 +108,8 @@ const AutomountManager = new Lang.Class({
Mainloop.idle_add(Lang.bind(this, this._startupMountAll)); Mainloop.idle_add(Lang.bind(this, this._startupMountAll));
}, },
_InhibitorsChanged: function(object, senderName, [inhibtor]) { _screenSaverActiveChanged: function(object, senderName, [isActive]) {
this._session.IsInhibitedRemote(GNOME_SESSION_AUTOMOUNT_INHIBIT, if (!isActive) {
Lang.bind(this,
function(result, error) {
if (!error) {
this._inhibited = result[0];
}
}));
},
_lockStatusChanged: function(shield, locked) {
if (!locked) {
this._volumeQueue.forEach(Lang.bind(this, function(volume) { this._volumeQueue.forEach(Lang.bind(this, function(volume) {
this._checkAndMountVolume(volume); this._checkAndMountVolume(volume);
})); }));
@@ -139,8 +123,7 @@ const AutomountManager = new Lang.Class({
let volumes = this._volumeMonitor.get_volumes(); let volumes = this._volumeMonitor.get_volumes();
volumes.forEach(Lang.bind(this, function(volume) { volumes.forEach(Lang.bind(this, function(volume) {
this._checkAndMountVolume(volume, { checkSession: false, this._checkAndMountVolume(volume, { checkSession: false,
useMountOp: false, useMountOp: false });
allowAutorun: false });
})); }));
return false; return false;
@@ -163,7 +146,7 @@ const AutomountManager = new Lang.Class({
if (!this.isSessionActive()) if (!this.isSessionActive())
return; return;
if (Main.screenShield.locked) if (this._ssProxy.screenSaverActive)
return; return;
global.play_theme_sound(0, 'device-added-media'); global.play_theme_sound(0, 'device-added-media');
@@ -175,7 +158,7 @@ const AutomountManager = new Lang.Class({
if (!this.isSessionActive()) if (!this.isSessionActive())
return; return;
if (Main.screenShield.locked) if (this._ssProxy.screenSaverActive)
return; return;
global.play_theme_sound(0, 'device-removed-media'); global.play_theme_sound(0, 'device-removed-media');
@@ -218,8 +201,7 @@ const AutomountManager = new Lang.Class({
_checkAndMountVolume: function(volume, params) { _checkAndMountVolume: function(volume, params) {
params = Params.parse(params, { checkSession: true, params = Params.parse(params, { checkSession: true,
useMountOp: true, useMountOp: true });
allowAutorun: true });
if (params.checkSession) { if (params.checkSession) {
// if we're not in the current ConsoleKit session, // if we're not in the current ConsoleKit session,
@@ -227,7 +209,7 @@ const AutomountManager = new Lang.Class({
if (!this.isSessionActive()) if (!this.isSessionActive())
return; return;
if (Main.screenShield.locked) { if (this._ssProxy.screenSaverActive) {
if (this._volumeQueue.indexOf(volume) == -1) if (this._volumeQueue.indexOf(volume) == -1)
this._volumeQueue.push(volume); this._volumeQueue.push(volume);
@@ -235,9 +217,6 @@ const AutomountManager = new Lang.Class({
} }
} }
if (this._inhibited)
return;
// Volume is already mounted, don't bother. // Volume is already mounted, don't bother.
if (volume.get_mount()) if (volume.get_mount())
return; return;
@@ -257,20 +236,15 @@ const AutomountManager = new Lang.Class({
if (params.useMountOp) { if (params.useMountOp) {
let operation = new ShellMountOperation.ShellMountOperation(volume); let operation = new ShellMountOperation.ShellMountOperation(volume);
this._mountVolume(volume, operation, params.allowAutorun); this._mountVolume(volume, operation.mountOp);
} else { } else {
this._mountVolume(volume, null, params.allowAutorun); this._mountVolume(volume, null);
} }
}, },
_mountVolume: function(volume, operation, allowAutorun) { _mountVolume: function(volume, operation) {
if (allowAutorun) this._allowAutorun(volume);
this._allowAutorun(volume); volume.mount(0, operation, null,
let mountOp = operation ? operation.mountOp : null;
volume._operation = operation;
volume.mount(0, mountOp, null,
Lang.bind(this, this._onVolumeMounted)); Lang.bind(this, this._onVolumeMounted));
}, },
@@ -279,19 +253,15 @@ const AutomountManager = new Lang.Class({
try { try {
volume.mount_finish(res); volume.mount_finish(res);
this._closeOperation(volume);
} catch (e) { } catch (e) {
// FIXME: we will always get G_IO_ERROR_FAILED from the gvfs udisks let string = e.toString();
// 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); // 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);
} }
}, },
@@ -303,16 +273,8 @@ const AutomountManager = new Lang.Class({
}, },
_reaskPassword: function(volume) { _reaskPassword: function(volume) {
let existingDialog = volume._operation ? volume._operation.borrowDialog() : null; let operation = new ShellMountOperation.ShellMountOperation(volume, { reaskPassword: true });
let operation = this._mountVolume(volume, operation.mountOp);
new ShellMountOperation.ShellMountOperation(volume,
{ existingDialog: existingDialog });
this._mountVolume(volume, operation);
},
_closeOperation: function(volume) {
if (volume._operation)
volume._operation.close();
}, },
_allowAutorun: function(volume) { _allowAutorun: function(volume) {

View File

@@ -23,14 +23,12 @@ const AutorunSetting = {
}; };
// misc utils // misc utils
function shouldAutorunMount(mount, forTransient) { function ignoreAutorunForMount(mount) {
let root = mount.get_root(); let root = mount.get_root();
let volume = mount.get_volume(); let volume = mount.get_volume();
if (!volume || (!volume.allowAutorun && forTransient)) if ((root.is_native() && !isMountRootHidden(root)) ||
return false; (volume && volume.allowAutorun && volume.should_automount()))
if (!root.is_native() || isMountRootHidden(root))
return false; return false;
return true; return true;
@@ -226,9 +224,11 @@ const AutorunManager = new Lang.Class({
try { try {
mount.unmount_with_operation_finish(res); mount.unmount_with_operation_finish(res);
} catch (e) { } catch (e) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED)) // FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
log('Unable to eject the mount ' + mount.get_name() // but we can't access the error code from JS.
+ ': ' + e.toString()); // See https://bugzilla.gnome.org/show_bug.cgi?id=591480
log('Unable to eject the mount ' + mount.get_name()
+ ': ' + e.toString());
} }
}, },
@@ -236,9 +236,11 @@ const AutorunManager = new Lang.Class({
try { try {
source.eject_with_operation_finish(res); source.eject_with_operation_finish(res);
} catch (e) { } catch (e) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED)) // FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
log('Unable to eject the drive ' + source.get_name() // but we can't access the error code from JS.
+ ': ' + e.toString()); // See https://bugzilla.gnome.org/show_bug.cgi?id=591480
log('Unable to eject the drive ' + source.get_name()
+ ': ' + e.toString());
} }
}, },
@@ -246,9 +248,11 @@ const AutorunManager = new Lang.Class({
try { try {
drive.stop_finish(res); drive.stop_finish(res);
} catch (e) { } catch (e) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED)) // FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
log('Unable to stop the drive ' + drive.get_name() // but we can't access the error code from JS.
+ ': ' + e.toString()); // See https://bugzilla.gnome.org/show_bug.cgi?id=591480
log('Unable to stop the drive ' + drive.get_name()
+ ': ' + e.toString());
} }
}, },
}); });
@@ -258,15 +262,16 @@ const AutorunResidentSource = new Lang.Class({
Extends: MessageTray.Source, Extends: MessageTray.Source,
_init: function() { _init: function() {
this.parent(_("Removable Devices"), 'media-removable', St.IconType.FULLCOLOR); this.parent(_("Removable Devices"));
this._mounts = []; this._mounts = [];
this._notification = new AutorunResidentNotification(this); this._notification = new AutorunResidentNotification(this);
this._setSummaryIcon(this.createNotificationIcon());
}, },
addMount: function(mount, apps) { addMount: function(mount, apps) {
if (!shouldAutorunMount(mount, false)) if (ignoreAutorunForMount(mount))
return; return;
let filtered = this._mounts.filter(function (element) { let filtered = this._mounts.filter(function (element) {
@@ -305,6 +310,12 @@ const AutorunResidentSource = new Lang.Class({
Main.messageTray.add(this); Main.messageTray.add(this);
this.pushNotification(this._notification); this.pushNotification(this._notification);
} }
},
createNotificationIcon: function() {
return new St.Icon ({ icon_name: 'media-removable',
icon_type: St.IconType.FULLCOLOR,
icon_size: this.ICON_SIZE });
} }
}); });
@@ -444,7 +455,7 @@ const AutorunTransientDispatcher = new Lang.Class({
return; return;
// if the mount doesn't want to be autorun, return // if the mount doesn't want to be autorun, return
if (!shouldAutorunMount(mount, true)) if (ignoreAutorunForMount(mount))
return; return;
let setting = this._getAutorunSettingForType(contentTypes[0]); let setting = this._getAutorunSettingForType(contentTypes[0]);
@@ -489,21 +500,22 @@ const AutorunTransientSource = new Lang.Class({
Extends: MessageTray.Source, Extends: MessageTray.Source,
_init: function(mount, apps) { _init: function(mount, apps) {
this.parent(mount.get_name());
this.mount = mount; this.mount = mount;
this.apps = apps; this.apps = apps;
this.parent(mount.get_name());
this._notification = new AutorunTransientNotification(this); this._notification = new AutorunTransientNotification(this);
this._setSummaryIcon(this.createNotificationIcon());
// add ourselves as a source, and popup the notification // add ourselves as a source, and popup the notification
Main.messageTray.add(this); Main.messageTray.add(this);
this.notify(this._notification); this.notify(this._notification);
}, },
createIcon: function(size) { createNotificationIcon: function() {
return new St.Icon({ gicon: this.mount.get_icon(), return new St.Icon({ gicon: this.mount.get_icon(),
icon_size: size }); icon_size: this.ICON_SIZE });
} }
}); });

View File

@@ -9,13 +9,6 @@ const Shell = imports.gi.Shell;
const Main = imports.ui.main; const Main = imports.ui.main;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const PopupAnimation = {
NONE: 0,
SLIDE: 1 << 0,
FADE: 1 << 1,
FULL: ~0,
};
const POPUP_ANIMATION_TIME = 0.15; 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 * An actor which displays a triangle "arrow" pointing to a given
* side. The .bin property is a container in which content can be * side. The .bin property is a container in which content can be
* placed. The arrow position may be controlled via * placed. The arrow position may be controlled via setArrowOrigin().
* 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.
* *
*/ */
const BoxPointer = new Lang.Class({ const BoxPointer = new Lang.Class({
@@ -36,7 +26,6 @@ const BoxPointer = new Lang.Class({
_init: function(arrowSide, binProperties) { _init: function(arrowSide, binProperties) {
this._arrowSide = arrowSide; this._arrowSide = arrowSide;
this._userArrowSide = arrowSide;
this._arrowOrigin = 0; this._arrowOrigin = 0;
this.actor = new St.Bin({ x_fill: true, this.actor = new St.Bin({ x_fill: true,
y_fill: true }); y_fill: true });
@@ -76,16 +65,11 @@ const BoxPointer = new Lang.Class({
show: function(animate, onComplete) { show: function(animate, onComplete) {
let themeNode = this.actor.get_theme_node(); let themeNode = this.actor.get_theme_node();
let rise = themeNode.get_length('-arrow-rise'); 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(); this.actor.show();
if (animate & PopupAnimation.SLIDE) { if (animate) {
switch (this._arrowSide) { switch (this._arrowSide) {
case St.Side.TOP: case St.Side.TOP:
this.yOffset = -rise; this.yOffset = -rise;
@@ -111,7 +95,7 @@ const BoxPointer = new Lang.Class({
if (onComplete) if (onComplete)
onComplete(); onComplete();
}), }),
time: animationTime }); time: POPUP_ANIMATION_TIME });
}, },
hide: function(animate, onComplete) { hide: function(animate, onComplete) {
@@ -119,10 +103,8 @@ const BoxPointer = new Lang.Class({
let yOffset = 0; let yOffset = 0;
let themeNode = this.actor.get_theme_node(); let themeNode = this.actor.get_theme_node();
let rise = themeNode.get_length('-arrow-rise'); 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) { switch (this._arrowSide) {
case St.Side.TOP: case St.Side.TOP:
yOffset = rise; yOffset = rise;
@@ -141,14 +123,13 @@ const BoxPointer = new Lang.Class({
this._muteInput(); this._muteInput();
Tweener.addTween(this, { opacity: fade ? 0 : 255, Tweener.addTween(this, { opacity: 0,
xOffset: xOffset, xOffset: xOffset,
yOffset: yOffset, yOffset: yOffset,
transition: 'linear', transition: 'linear',
time: animationTime, time: POPUP_ANIMATION_TIME,
onComplete: Lang.bind(this, function () { onComplete: Lang.bind(this, function () {
this.actor.hide(); this.actor.hide();
this.opacity = 0;
this.xOffset = 0; this.xOffset = 0;
this.yOffset = 0; this.yOffset = 0;
if (onComplete) if (onComplete)
@@ -218,27 +199,8 @@ const BoxPointer = new Lang.Class({
} }
this.bin.allocate(childBox, flags); this.bin.allocate(childBox, flags);
if (this._sourceActor && this._sourceActor.mapped) { if (this._sourceActor && this._sourceActor.mapped)
this._reposition(this._sourceActor, this._arrowAlignment); 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) { _drawBorder: function(area) {
@@ -252,6 +214,7 @@ const BoxPointer = new Lang.Class({
let halfBorder = borderWidth / 2; let halfBorder = borderWidth / 2;
let halfBase = Math.floor(base/2); let halfBase = Math.floor(base/2);
let borderColor = themeNode.get_color('-arrow-border-color');
let backgroundColor = themeNode.get_color('-arrow-background-color'); let backgroundColor = themeNode.get_color('-arrow-background-color');
let [width, height] = area.get_surface_size(); let [width, height] = area.get_surface_size();
@@ -262,6 +225,7 @@ const BoxPointer = new Lang.Class({
boxWidth -= rise; boxWidth -= rise;
} }
let cr = area.get_context(); let cr = area.get_context();
Clutter.cairo_set_source_color(cr, borderColor);
// Translate so that box goes from 0,0 to boxWidth,boxHeight, // Translate so that box goes from 0,0 to boxWidth,boxHeight,
// with the arrow poking out of that // with the arrow poking out of that
@@ -357,18 +321,12 @@ const BoxPointer = new Lang.Class({
Clutter.cairo_set_source_color(cr, backgroundColor); Clutter.cairo_set_source_color(cr, backgroundColor);
cr.fillPreserve(); cr.fillPreserve();
Clutter.cairo_set_source_color(cr, borderColor);
if (borderWidth > 0) { cr.setLineWidth(borderWidth);
let borderColor = themeNode.get_color('-arrow-border-color'); cr.stroke();
Clutter.cairo_set_source_color(cr, borderColor);
cr.setLineWidth(borderWidth);
cr.stroke();
}
}, },
setPosition: function(sourceActor, alignment) { setPosition: function(sourceActor, alignment) {
this._arrowSide = this._userArrowSide;
// We need to show it now to force an allocation, // We need to show it now to force an allocation,
// so that we can query the correct size. // so that we can query the correct size.
this.actor.show(); this.actor.show();
@@ -385,7 +343,11 @@ const BoxPointer = new Lang.Class({
if (!this._sourceActor) if (!this._sourceActor)
return; 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) { _reposition: function(sourceActor, alignment) {
@@ -484,39 +446,6 @@ const BoxPointer = new Lang.Class({
-(this._yPosition + this._yOffset)); -(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) { set xOffset(offset) {
this._xOffset = offset; this._xOffset = offset;
this._shiftActor(); this._shiftActor();

View File

@@ -448,7 +448,7 @@ const Calendar = new Lang.Class({
} }
// All the children after this are days, and get removed when we update the calendar // 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) { _onStyleChange: function(actor, event) {
@@ -551,7 +551,6 @@ const Calendar = new Lang.Class({
let row = 2; let row = 2;
while (true) { while (true) {
let button = new St.Button({ label: iter.getDate().toString() }); let button = new St.Button({ label: iter.getDate().toString() });
let rtl = button.get_text_direction() == Clutter.TextDirection.RTL;
if (!this._eventSource) if (!this._eventSource)
button.reactive = false; button.reactive = false;
@@ -572,10 +571,7 @@ const Calendar = new Lang.Class({
// Hack used in lieu of border-collapse - see gnome-shell.css // Hack used in lieu of border-collapse - see gnome-shell.css
if (row == 2) if (row == 2)
styleClass = 'calendar-day-top ' + styleClass; styleClass = 'calendar-day-top ' + styleClass;
if (iter.getDay() == this._weekStart)
let leftMost = rtl ? iter.getDay() == (this._weekStart + 6) % 7
: iter.getDay() == this._weekStart;
if (leftMost)
styleClass = 'calendar-day-left ' + styleClass; styleClass = 'calendar-day-left ' + styleClass;
if (_sameDay(now, iter)) if (_sameDay(now, iter))

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

@@ -133,6 +133,7 @@ const DashItemContainer = new Lang.Class({
}, },
hideLabel: function () { hideLabel: function () {
this.label.opacity = 255;
Tweener.addTween(this.label, Tweener.addTween(this.label,
{ opacity: 0, { opacity: 0,
time: DASH_ITEM_LABEL_HIDE_TIME, time: DASH_ITEM_LABEL_HIDE_TIME,
@@ -167,17 +168,7 @@ const DashItemContainer = new Lang.Class({
}); });
}, },
destroy: function() {
if (this.label)
this.label.destroy();
this.actor.destroy();
},
animateOutAndDestroy: function() { animateOutAndDestroy: function() {
if (this.label)
this.label.destroy();
if (this.child == null) { if (this.child == null) {
this.actor.destroy(); this.actor.destroy();
return; return;
@@ -458,13 +449,6 @@ const Dash = new Lang.Class({
Lang.bind(this, function() { Lang.bind(this, function() {
this._onHover(item, display) this._onHover(item, display)
})); }));
Main.overview.connect('hiding',
Lang.bind(this, function() {
this._labelShowing = false;
item.hideLabel();
}));
return item; return item;
}, },
@@ -480,7 +464,7 @@ const Dash = new Lang.Class({
})); }));
if (this._resetHoverTimeoutId > 0) { if (this._resetHoverTimeoutId > 0) {
Mainloop.source_remove(this._resetHoverTimeoutId); Mainloop.source_remove(this._resetHoverTimeoutId);
this._resetHoverTimeoutId = 0; this._resetHoverTimeoutId = 0;
} }
} }
} else { } else {
@@ -707,7 +691,7 @@ const Dash = new Lang.Class({
if (Main.overview.visible) if (Main.overview.visible)
item.animateOutAndDestroy(); item.animateOutAndDestroy();
else else
item.destroy(); item.actor.destroy();
} }
this._adjustIconSize(); this._adjustIconSize();

View File

@@ -2,7 +2,6 @@
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const GnomeDesktop = imports.gi.GnomeDesktop;
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
const Cairo = imports.cairo; const Cairo = imports.cairo;
@@ -17,6 +16,14 @@ const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu; const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
const Calendar = imports.ui.calendar; 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) function _onVertSepRepaint (area)
{ {
@@ -38,7 +45,9 @@ const DateMenuButton = new Lang.Class({
Name: 'DateMenuButton', Name: 'DateMenuButton',
Extends: PanelMenu.Button, Extends: PanelMenu.Button,
_init: function() { _init: function(params) {
params = Params.parse(params, { showEvents: true });
let item; let item;
let hbox; let hbox;
let vbox; let vbox;
@@ -53,8 +62,8 @@ const DateMenuButton = new Lang.Class({
// role ATK_ROLE_MENU like other elements of the panel. // role ATK_ROLE_MENU like other elements of the panel.
this.actor.accessible_role = Atk.Role.LABEL; this.actor.accessible_role = Atk.Role.LABEL;
this._clockDisplay = new St.Label(); this._clock = new St.Label();
this.actor.add_actor(this._clockDisplay); this.actor.add_actor(this._clock);
hbox = new St.BoxLayout({name: 'calendarArea' }); hbox = new St.BoxLayout({name: 'calendarArea' });
this.menu.addActor(hbox); this.menu.addActor(hbox);
@@ -66,11 +75,11 @@ const DateMenuButton = new Lang.Class({
// Date // Date
this._date = new St.Label(); this._date = new St.Label();
this.actor.label_actor = this._clockDisplay; this.actor.label_actor = this._date;
this._date.style_class = 'datemenu-date-label'; this._date.style_class = 'datemenu-date-label';
vbox.add(this._date); vbox.add(this._date);
if (Main.sessionMode.showCalendarEvents) { if (params.showEvents) {
this._eventSource = new Calendar.DBusEventSource(); this._eventSource = new Calendar.DBusEventSource();
this._eventList = new Calendar.EventsList(this._eventSource); this._eventList = new Calendar.EventsList(this._eventSource);
} else { } else {
@@ -101,7 +110,7 @@ const DateMenuButton = new Lang.Class({
item.actor.reparent(vbox); item.actor.reparent(vbox);
} }
if (Main.sessionMode.showCalendarEvents) { if (params.showEvents) {
// Add vertical separator // Add vertical separator
item = new St.DrawingArea({ style_class: 'calendar-vertical-separator', item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
@@ -148,29 +157,77 @@ const DateMenuButton = new Lang.Class({
// Done with hbox for calendar and event list // Done with hbox for calendar and event list
this._clock = new GnomeDesktop.WallClock(); // Track changes to clock settings
this._clock.connect('notify::clock', Lang.bind(this, this._updateClockAndDate)); 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(); this._updateClockAndDate();
}, },
_updateClockAndDate: function() { _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 /* 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"). * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
*/ */
let dateFormat = _("%A %B %e, %Y"); dateFormat = _("%A %B %e, %Y");
let displayDate = new Date();
this._date.set_text(displayDate.toLocaleFormat(dateFormat)); this._date.set_text(displayDate.toLocaleFormat(dateFormat));
Mainloop.timeout_add_seconds(1, Lang.bind(this, this._updateClockAndDate));
return false;
}, },
_onOpenCalendarActivate: function() { _onOpenCalendarActivate: function() {
this.menu.close(); this.menu.close();
let calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' }); let calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' });
let tool = calendarSettings.get_string('exec'); let tool = calendarSettings.get_string('exec');
if (tool.length == 0 || tool.substr(0, 9) == 'evolution') { if (tool.length == 0 || tool == 'evolution') {
// TODO: pass the selected day // TODO: pass the selected day
let app = Shell.AppSystem.get_default().lookup_app('evolution-calendar.desktop'); Util.spawn(['evolution', '-c', 'calendar']);
app.activate();
} else { } else {
let needTerm = calendarSettings.get_boolean('needs-term'); let needTerm = calendarSettings.get_boolean('needs-term');
if (needTerm) { if (needTerm) {

View File

@@ -31,6 +31,7 @@ const St = imports.gi.St;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const GnomeSession = imports.misc.gnomeSession; const GnomeSession = imports.misc.gnomeSession;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main; const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog; const ModalDialog = imports.ui.modalDialog;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
@@ -279,17 +280,21 @@ const EndSessionDialog = new Lang.Class({
scrollView.hide(); scrollView.hide();
this._applicationList = new St.BoxLayout({ vertical: true }); 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', this._applicationList.connect('actor-added',
Lang.bind(this, function() { Lang.bind(this, function() {
if (this._applicationList.get_n_children() == 1) if (this._applicationList.get_children().length == 1)
scrollView.show(); scrollView.show();
})); }));
this._applicationList.connect('actor-removed', this._applicationList.connect('actor-removed',
Lang.bind(this, function() { Lang.bind(this, function() {
if (this._applicationList.get_n_children() == 0) if (this._applicationList.get_children().length == 0)
scrollView.hide(); scrollView.hide();
})); }));
@@ -337,7 +342,7 @@ const EndSessionDialog = new Lang.Class({
} }
}, },
_updateDescription: function() { _updateContent: function() {
if (this.state != ModalDialog.State.OPENING && if (this.state != ModalDialog.State.OPENING &&
this.state != ModalDialog.State.OPENED) this.state != ModalDialog.State.OPENED)
return; return;
@@ -347,6 +352,17 @@ const EndSessionDialog = new Lang.Class({
let subject = dialogContent.subject; let subject = dialogContent.subject;
let description; 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) { if (this._inhibitors.length > 0) {
this._stopTimer(); this._stopTimer();
description = dialogContent.inhibitedDescription; description = dialogContent.inhibitedDescription;
@@ -379,27 +395,6 @@ const EndSessionDialog = new Lang.Class({
_setLabelText(this._descriptionLabel, description); _setLabelText(this._descriptionLabel, description);
}, },
_updateContent: function() {
if (this.state != ModalDialog.State.OPENING &&
this.state != ModalDialog.State.OPENED)
return;
let dialogContent = DialogContent[this._type];
if (this._user.is_loaded && !dialogContent.iconName) {
let iconFile = this._user.get_icon_file();
if (GLib.file_test(iconFile, GLib.FileTest.EXISTS))
this._setIconFromFile(iconFile, dialogContent.iconStyleClass);
else
this._setIconFromName('avatar-default', dialogContent.iconStyleClass);
} else if (dialogContent.iconName) {
this._setIconFromName(dialogContent.iconName,
dialogContent.iconStyleClass);
}
this._updateDescription();
},
_updateButtons: function() { _updateButtons: function() {
let dialogContent = DialogContent[this._type]; let dialogContent = DialogContent[this._type];
let buttons = [{ action: Lang.bind(this, this.cancel), let buttons = [{ action: Lang.bind(this, this.cancel),
@@ -446,7 +441,7 @@ const EndSessionDialog = new Lang.Class({
{ _secondsLeft: 0, { _secondsLeft: 0,
time: this._secondsLeft, time: this._secondsLeft,
transition: 'linear', transition: 'linear',
onUpdate: Lang.bind(this, this._updateDescription), onUpdate: Lang.bind(this, this._updateContent),
onComplete: Lang.bind(this, function() { onComplete: Lang.bind(this, function() {
let dialogContent = DialogContent[this._type]; let dialogContent = DialogContent[this._type];
let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1]; let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];

View File

@@ -39,19 +39,11 @@ function _patchContainerClass(containerClass) {
}; };
} }
function _makeLoggingFunc(func) {
return function() {
return func([].join.call(arguments, ', '));
};
}
function init() { function init() {
// Add some bindings to the global JS namespace; (gjs keeps the web // Add some bindings to the global JS namespace; (gjs keeps the web
// browser convention of having that namespace be called 'window'.) // browser convention of having that namespace be called 'window'.)
window.global = Shell.Global.get(); window.global = Shell.Global.get();
window.log = _makeLoggingFunc(window.log);
window._ = Gettext.gettext; window._ = Gettext.gettext;
window.C_ = Gettext.pgettext; window.C_ = Gettext.pgettext;
window.ngettext = Gettext.ngettext; window.ngettext = Gettext.ngettext;
@@ -90,7 +82,7 @@ function init() {
} }
// OK, now things are initialized enough that we can import shell JS // 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; const Tweener = imports.ui.tweener;
Tweener.init(); 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(uuid))
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().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(uuid))
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(uuid);
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,11 +3,19 @@
const Lang = imports.lang; const Lang = imports.lang;
const Signals = imports.signals; const Signals = imports.signals;
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const St = imports.gi.St; 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 ExtensionUtils = imports.misc.extensionUtils;
const FileUtils = imports.misc.fileUtils;
const ModalDialog = imports.ui.modalDialog;
const API_VERSION = 1;
const ExtensionState = { const ExtensionState = {
ENABLED: 1, ENABLED: 1,
@@ -22,6 +30,29 @@ const ExtensionState = {
UNINSTALLED: 99 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 // Arrays of uuids
var enabledExtensions; var enabledExtensions;
// Contains the order that extensions were enabled in. // Contains the order that extensions were enabled in.
@@ -38,6 +69,90 @@ const disconnect = Lang.bind(_signals, _signals.disconnect);
const ENABLED_EXTENSIONS_KEY = 'enabled-extensions'; const ENABLED_EXTENSIONS_KEY = 'enabled-extensions';
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) { function disableExtension(uuid) {
let extension = ExtensionUtils.extensions[uuid]; let extension = ExtensionUtils.extensions[uuid];
if (!extension) if (!extension)
@@ -63,23 +178,23 @@ function disableExtension(uuid) {
try { try {
ExtensionUtils.extensions[uuid].stateObj.disable(); ExtensionUtils.extensions[uuid].stateObj.disable();
} catch(e) { } catch(e) {
logExtensionError(uuid, e); logExtensionError(uuid, e.toString());
} }
} }
if (extension.stylesheet) { try {
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme(); extension.stateObj.disable();
theme.unload_stylesheet(extension.stylesheet.get_path()); } catch(e) {
logExtensionError(uuid, e.toString());
return;
} }
extension.stateObj.disable();
for (let i = 0; i < order.length; i++) { for (let i = 0; i < order.length; i++) {
let uuid = order[i]; let uuid = order[i];
try { try {
ExtensionUtils.extensions[uuid].stateObj.enable(); ExtensionUtils.extensions[uuid].stateObj.enable();
} catch(e) { } catch(e) {
logExtensionError(uuid, e); logExtensionError(uuid, e.toString());
} }
} }
@@ -102,77 +217,67 @@ function enableExtension(uuid) {
extensionOrder.push(uuid); extensionOrder.push(uuid);
extension.stateObj.enable(); try {
extension.stateObj.enable();
let stylesheetFile = extension.dir.get_child('stylesheet.css'); } catch(e) {
if (stylesheetFile.query_exists(null)) { logExtensionError(uuid, e.toString());
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme(); return;
theme.load_stylesheet(stylesheetFile.get_path());
extension.stylesheet = stylesheetFile;
} }
extension.state = ExtensionState.ENABLED; extension.state = ExtensionState.ENABLED;
_signals.emit('extension-state-changed', extension); _signals.emit('extension-state-changed', extension);
} }
function logExtensionError(uuid, error) { function logExtensionError(uuid, message, state) {
let extension = ExtensionUtils.extensions[uuid]; let extension = ExtensionUtils.extensions[uuid];
if (!extension) if (!extension)
return; return;
let message = '' + error;
if (error.state)
extension.state = error.state;
else
extension.state = ExtensionState.ERROR;
if (!extension.errors) if (!extension.errors)
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, _signals.emit('extension-state-changed', { uuid: uuid,
error: message, 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) {
throw new Error('extension already loaded');
}
try {
extension = ExtensionUtils.createExtensionObject(uuid, dir, type);
} catch(e) {
logExtensionError(uuid, e.message);
return;
}
// Default to error, we set success as the last step // Default to error, we set success as the last step
extension.state = ExtensionState.ERROR; extension.state = ExtensionState.ERROR;
if (ExtensionUtils.isOutOfDate(extension)) { if (ExtensionUtils.isOutOfDate(extension)) {
let error = new Error('extension is not compatible with current GNOME Shell and/or GJS version'); logExtensionError(uuid, 'extension is not compatible with current GNOME Shell and/or GJS version', ExtensionState.OUT_OF_DATE);
error.state = ExtensionState.OUT_OF_DATE; extension.state = ExtensionState.OUT_OF_DATE;
throw error; return;
} }
let enabled = enabledExtensions.indexOf(extension.uuid) != -1;
if (enabled) { if (enabled) {
initExtension(extension.uuid); initExtension(uuid);
if (extension.state == ExtensionState.DISABLED) if (extension.state == ExtensionState.DISABLED)
enableExtension(extension.uuid); enableExtension(uuid);
} else { } else {
extension.state = ExtensionState.INITIALIZED; extension.state = ExtensionState.INITIALIZED;
} }
_signals.emit('extension-state-changed', extension); _signals.emit('extension-state-changed', extension);
} global.log('Loaded extension ' + uuid);
function unloadExtension(uuid) {
let extension = ExtensionUtils.extensions[uuid];
if (!extension)
return false;
// Try to disable it -- if it's ERROR'd, we can't guarantee that,
// but it will be removed on next reboot, and hopefully nothing
// broke too much.
disableExtension(uuid);
extension.state = ExtensionState.UNINSTALLED;
_signals.emit('extension-state-changed', extension);
delete ExtensionUtils.extensions[uuid];
return true;
} }
function initExtension(uuid) { function initExtension(uuid) {
@@ -183,24 +288,64 @@ function initExtension(uuid) {
throw new Error("Extension was not properly created. Call loadExtension first"); throw new Error("Extension was not properly created. Call loadExtension first");
let extensionJs = dir.get_child('extension.js'); let extensionJs = dir.get_child('extension.js');
if (!extensionJs.query_exists(null)) if (!extensionJs.query_exists(null)) {
throw new Error('Missing extension.js'); 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 extensionModule;
let extensionState = null; 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); if (!extensionModule.init) {
extensionModule = extension.imports.extension; logExtensionError(uuid, 'missing \'init\' function');
return;
}
if (extensionModule.init) { try {
extensionState = extensionModule.init(extension); extensionState = extensionModule.init(extension);
} catch (e) {
if (stylesheetPath != null)
theme.unload_stylesheet(stylesheetPath);
logExtensionError(uuid, 'Failed to evaluate init function:' + e);
return;
} }
if (!extensionState) if (!extensionState)
extensionState = extensionModule; extensionState = extensionModule;
extension.stateObj = extensionState; 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; extension.state = ExtensionState.DISABLED;
_signals.emit('extension-loaded', uuid); _signals.emit('extension-loaded', uuid);
} }
@@ -212,11 +357,7 @@ function onEnabledExtensionsChanged() {
newEnabledExtensions.filter(function(uuid) { newEnabledExtensions.filter(function(uuid) {
return enabledExtensions.indexOf(uuid) == -1; return enabledExtensions.indexOf(uuid) == -1;
}).forEach(function(uuid) { }).forEach(function(uuid) {
try { enableExtension(uuid);
enableExtension(uuid);
} catch(e) {
logExtensionError(uuid, e);
}
}); });
// Find and disable all the newly disabled extensions: UUIDs found in the // Find and disable all the newly disabled extensions: UUIDs found in the
@@ -224,27 +365,87 @@ function onEnabledExtensionsChanged() {
enabledExtensions.filter(function(item) { enabledExtensions.filter(function(item) {
return newEnabledExtensions.indexOf(item) == -1; return newEnabledExtensions.indexOf(item) == -1;
}).forEach(function(uuid) { }).forEach(function(uuid) {
try { disableExtension(uuid);
disableExtension(uuid);
} catch(e) {
logExtensionError(uuid, e);
}
}); });
enabledExtensions = newEnabledExtensions; enabledExtensions = newEnabledExtensions;
} }
function loadExtensions() { function init() {
ExtensionUtils.init();
global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged); global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged);
enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY); enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
let finder = new ExtensionUtils.ExtensionFinder();
finder.connect('extension-found', function(signals, extension) {
try {
loadExtension(extension);
} catch(e) {
logExtensionError(extension.uuid, e);
}
});
finder.scanExtensions();
} }
function loadExtensions() {
ExtensionUtils.scanExtensions(function(uuid, dir, type) {
let enabled = enabledExtensions.indexOf(uuid) != -1;
loadExtension(dir, type, enabled);
});
}
const InstallExtensionDialog = new Lang.Class({
Name: 'InstallExtensionDialog',
Extends: ModalDialog.ModalDialog,
_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());
}
});

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

@@ -146,6 +146,11 @@ const BaseIcon = new Lang.Class({
size = found ? len : ICON_SIZE; size = found ? len : ICON_SIZE;
} }
// don't create icons unnecessarily
if (size == this.iconSize &&
this._iconBin.child)
return;
this._createIconTexture(size); this._createIconTexture(size);
} }
}); });
@@ -282,10 +287,6 @@ const IconGrid = new Lang.Class({
return this._computeLayout(rowWidth)[0]; return this._computeLayout(rowWidth)[0];
}, },
getRowLimit: function() {
return this._rowLimit;
},
_computeLayout: function (forWidth) { _computeLayout: function (forWidth) {
let nColumns = 0; let nColumns = 0;
let usedWidth = 0; let usedWidth = 0;
@@ -309,22 +310,21 @@ const IconGrid = new Lang.Class({
this._grid.queue_relayout(); this._grid.queue_relayout();
}, },
removeAll: function() { removeAll: function () {
this._grid.destroy_all_children(); this._grid.get_children().forEach(Lang.bind(this, function (child) {
child.destroy();
}));
}, },
addItem: function(actor, index) { addItem: function(actor) {
if (index !== undefined) this._grid.add_actor(actor);
this._grid.insert_child_at_index(actor, index);
else
this._grid.add_actor(actor);
}, },
getItemAtIndex: function(index) { getItemAtIndex: function(index) {
return this._grid.get_child_at_index(index); return this._grid.get_children()[index];
}, },
visibleItemsCount: function() { 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._getExtendedKeys();
this.actor._extended_keys = this._extended_keyboard; this.actor._extended_keys = this._extended_keyboard;
this._boxPointer.actor.hide(); 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.actor.fake_release();
this._boxPointer.actor.raise_top(); this._boxPointer.actor.raise_top();
this._boxPointer.setPosition(this.actor, 0.5); this._boxPointer.setPosition(this.actor, 0.5);
this._boxPointer.show(BoxPointer.PopupAnimation.FULL); this._boxPointer.show(true);
this.actor.set_hover(false); this.actor.set_hover(false);
if (!this._grabbed) { if (!this._grabbed) {
Main.pushModal(this.actor); Main.pushModal(this.actor);
@@ -186,7 +186,7 @@ const Key = new Lang.Class({
} else { } else {
if (this._grabbed) if (this._grabbed)
this._ungrab(); this._ungrab();
this._boxPointer.hide(BoxPointer.PopupAnimation.FULL); this._boxPointer.hide(true);
} }
} }
}); });
@@ -541,8 +541,16 @@ const KeyboardSource = new Lang.Class({
Extends: MessageTray.Source, Extends: MessageTray.Source,
_init: function(keyboard) { _init: function(keyboard) {
this.parent(_("Keyboard"));
this._keyboard = keyboard; this._keyboard = keyboard;
this.parent(_("Keyboard"), 'input-keyboard', St.IconType.SYMBOLIC);
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() { handleSummaryClick: function() {

View File

@@ -65,8 +65,7 @@ const KeyringDialog = new Lang.Class({
key: Clutter.Escape key: Clutter.Escape
}, },
{ label: '', { label: '',
action: Lang.bind(this, this._onContinueButton), action: Lang.bind(this, this._onContinueButton)
default: true
}] }]
this.setButtons(buttons); this.setButtons(buttons);

View File

@@ -11,6 +11,7 @@ const St = imports.gi.St;
const DND = imports.ui.dnd; const DND = imports.ui.dnd;
const Main = imports.ui.main; const Main = imports.ui.main;
const Params = imports.misc.params; const Params = imports.misc.params;
const ScreenSaver = imports.misc.screenSaver;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5; const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
@@ -32,37 +33,26 @@ const LayoutManager = new Lang.Class({
this._chrome = new Chrome(this); this._chrome = new Chrome(this);
this.screenShieldGroup = new St.Widget({ name: 'screenShieldGroup',
visible: false,
clip_to_allocation: true,
});
this.addChrome(this.screenShieldGroup);
this.panelBox = new St.BoxLayout({ name: 'panelBox', this.panelBox = new St.BoxLayout({ name: 'panelBox',
vertical: true }); vertical: true });
this.addChrome(this.panelBox, { affectsStruts: true, this.addChrome(this.panelBox, { affectsStruts: true });
trackFullscreen: true });
this.panelBox.connect('allocation-changed', this.panelBox.connect('allocation-changed',
Lang.bind(this, this._updatePanelBarriers)); Lang.bind(this, this._updatePanelBarriers));
this.trayBox = new St.BoxLayout({ name: 'trayBox' }); this.trayBox = new St.BoxLayout({ name: 'trayBox' });
this.addChrome(this.trayBox); this.addChrome(this.trayBox, { visibleInFullscreen: true });
this.trayBox.connect('allocation-changed', this.trayBox.connect('allocation-changed',
Lang.bind(this, this._updateTrayBarrier)); Lang.bind(this, this._updateTrayBarrier));
this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox', this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox',
reactive: true, reactive: true,
track_hover: true }); track_hover: true });
this.addChrome(this.keyboardBox); this.addChrome(this.keyboardBox, { visibleInFullscreen: true });
this._keyboardHeightNotifyId = 0; this._keyboardHeightNotifyId = 0;
global.screen.connect('monitors-changed', global.screen.connect('monitors-changed',
Lang.bind(this, this._monitorsChanged)); Lang.bind(this, this._monitorsChanged));
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; // This is called by Main after everything else is constructed;
@@ -155,9 +145,6 @@ const LayoutManager = new Lang.Class({
}, },
_updateBoxes: function() { _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_position(this.primaryMonitor.x, this.primaryMonitor.y);
this.panelBox.set_size(this.primaryMonitor.width, -1); this.panelBox.set_size(this.primaryMonitor.width, -1);
@@ -237,9 +224,26 @@ const LayoutManager = new Lang.Class({
return false; return false;
}, },
get currentMonitor() { get focusIndex() {
let index = global.screen.get_current_monitor(); let focusWindow = global.display.focus_window;
return Main.layoutManager.monitors[index];
if (focusWindow) {
let wrect = focusWindow.get_outer_rect();
for (let i = 0; i < this.monitors.length; i++) {
let monitor = this.monitors[i];
if (monitor.x <= wrect.x && monitor.y <= wrect.y &&
monitor.x + monitor.width > wrect.x &&
monitor.y + monitor.height > wrect.y)
return i;
}
}
return this.primaryIndex;
},
get focusMonitor() {
return this.monitors[this.focusIndex];
}, },
_startupAnimation: function() { _startupAnimation: function() {
@@ -327,10 +331,8 @@ const LayoutManager = new Lang.Class({
// the window manager struts. Changes to @actor's visibility will // the window manager struts. Changes to @actor's visibility will
// NOT affect whether or not the strut is present, however. // NOT affect whether or not the strut is present, however.
// //
// If %trackFullscreen in @params is %true, the actor's visibility // If %visibleInFullscreen in @params is %true, the actor will be
// will be bound to the presence of fullscreen windows on the same // visible even when a fullscreen window should be covering it.
// monitor (it will be hidden whenever a fullscreen window is visible,
// and shown otherwise)
addChrome: function(actor, params) { addChrome: function(actor, params) {
this._chrome.addActor(actor, params); this._chrome.addActor(actor, params);
}, },
@@ -344,8 +346,10 @@ const LayoutManager = new Lang.Class({
// struts or input region to cover specific children. // struts or input region to cover specific children.
// //
// @params can have any of the same values as in addChrome(), // @params can have any of the same values as in addChrome(),
// though some possibilities don't make sense. By default, @actor has // though some possibilities don't make sense (eg, trying to have
// the same params as its chrome ancestor. // a %visibleInFullscreen child of a non-%visibleInFullscreen
// parent). By default, @actor has the same params as its chrome
// ancestor.
trackChrome: function(actor, params) { trackChrome: function(actor, params) {
this._chrome.trackActor(actor, params); this._chrome.trackActor(actor, params);
}, },
@@ -551,7 +555,7 @@ const HotCorner = new Lang.Class({
// workspace content. // workspace content.
const defaultParams = { const defaultParams = {
trackFullscreen: false, visibleInFullscreen: false,
affectsStruts: false, affectsStruts: false,
affectsInputRegion: true affectsInputRegion: true
}; };
@@ -564,7 +568,6 @@ const Chrome = new Lang.Class({
this._monitors = []; this._monitors = [];
this._inOverview = false; this._inOverview = false;
this._isLocked = false;
this._updateRegionIdle = 0; this._updateRegionIdle = 0;
this._freezeUpdateCount = 0; this._freezeUpdateCount = 0;
@@ -579,6 +582,16 @@ const Chrome = new Lang.Class({
global.screen.connect('notify::n-workspaces', global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._queueUpdateRegions)); 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(); this._relayout();
}, },
@@ -587,8 +600,6 @@ const Chrome = new Lang.Class({
Lang.bind(this, this._overviewShowing)); Lang.bind(this, this._overviewShowing));
Main.overview.connect('hidden', Main.overview.connect('hidden',
Lang.bind(this, this._overviewHidden)); Lang.bind(this, this._overviewHidden));
Main.screenShield.connect('lock-status-changed',
Lang.bind(this, this._lockStatusChanged));
}, },
addActor: function(actor, params) { addActor: function(actor, params) {
@@ -683,18 +694,19 @@ const Chrome = new Lang.Class({
_updateVisibility: function() { _updateVisibility: function() {
for (let i = 0; i < this._trackedActors.length; i++) { for (let i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i], visible; let actorData = this._trackedActors[i], visible;
if (!actorData.trackFullscreen)
continue;
if (!actorData.isToplevel) if (!actorData.isToplevel)
continue; continue;
if (this._inOverview || this._isLocked) if (this._screenSaverActive)
visible = false;
else if (this._inOverview)
visible = true; visible = true;
else if (this.findMonitorForActor(actorData.actor).inFullscreen) else if (!actorData.visibleInFullscreen &&
this.findMonitorForActor(actorData.actor).inFullscreen)
visible = false; visible = false;
else else
visible = true; visible = true;
actorData.actor.visible = visible; Main.uiGroup.set_skip_paint(actorData.actor, !visible);
} }
}, },
@@ -710,12 +722,6 @@ const Chrome = new Lang.Class({
this._queueUpdateRegions(); this._queueUpdateRegions();
}, },
_lockStatusChanged: function(shield, locked) {
this._isLocked = locked;
this._updateVisibility();
this._queueUpdateRegions();
},
_relayout: function() { _relayout: function() {
this._monitors = this._layoutManager.monitors; this._monitors = this._layoutManager.monitors;
this._primaryMonitor = this._layoutManager.primaryMonitor; this._primaryMonitor = this._layoutManager.primaryMonitor;
@@ -725,6 +731,12 @@ const Chrome = new Lang.Class({
this._queueUpdateRegions(); this._queueUpdateRegions();
}, },
_onScreenSaverActiveChanged: function(screenSaverActive) {
this._screenSaverActive = screenSaverActive;
this._updateVisibility();
this._queueUpdateRegions();
},
_findMonitorForRect: function(x, y, w, h) { _findMonitorForRect: function(x, y, w, h) {
// First look at what monitor the center of the rectangle is at // First look at what monitor the center of the rectangle is at
let cx = x + w/2; let cx = x + w/2;
@@ -840,8 +852,6 @@ const Chrome = new Lang.Class({
for (let i = 0; i < this._monitors.length; i++) for (let i = 0; i < this._monitors.length; i++)
wasInFullscreen[i] = this._monitors[i].inFullscreen; wasInFullscreen[i] = this._monitors[i].inFullscreen;
let primaryWasInFullscreen = this._primaryMonitor.inFullscreen;
this._updateFullscreen(); this._updateFullscreen();
let changed = false; let changed = false;
@@ -851,15 +861,10 @@ const Chrome = new Lang.Class({
break; break;
} }
} }
if (changed) { if (changed) {
this._updateVisibility(); this._updateVisibility();
this._queueUpdateRegions(); this._queueUpdateRegions();
} }
if (primaryWasInFullscreen != this._primaryMonitor.inFullscreen) {
this.emit('primary-fullscreen-changed', this._primaryMonitor.inFullscreen);
}
}, },
updateRegions: function() { updateRegions: function() {
@@ -974,5 +979,3 @@ const Chrome = new Lang.Class({
return false; return false;
} }
}); });
Signals.addSignalMethods(Chrome.prototype);

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 Signals = imports.signals;
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
const System = imports.system;
const History = imports.misc.history; const History = imports.misc.history;
const ExtensionSystem = imports.ui.extensionSystem; const ExtensionSystem = imports.ui.extensionSystem;
const ExtensionUtils = imports.misc.extensionUtils; const ExtensionUtils = imports.misc.extensionUtils;
const Link = imports.ui.link;
const ShellEntry = imports.ui.shellEntry; const ShellEntry = imports.ui.shellEntry;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const Main = imports.ui.main; const Main = imports.ui.main;
const JsParse = imports.misc.jsParse; const JsParse = imports.misc.jsParse;
const CHEVRON = '>>> ';
/* Imports...feel free to add here as needed */ /* Imports...feel free to add here as needed */
var commandHeader = 'const Clutter = imports.gi.Clutter; ' + var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
'const GLib = imports.gi.GLib; ' + 'const GLib = imports.gi.GLib; ' +
@@ -40,7 +38,7 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
'const stage = global.stage; ' + 'const stage = global.stage; ' +
'const color = function(pixel) { let c= new Clutter.Color(); c.from_pixel(pixel); return c; }; ' + 'const color = function(pixel) { let c= new Clutter.Color(); c.from_pixel(pixel); return c; }; ' +
/* Special lookingGlass functions */ /* Special lookingGlass functions */
'const it = Main.lookingGlass.getIt(); ' + 'const it = Main.lookingGlass.getIt(); ' +
'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); '; 'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ';
const HISTORY_KEY = 'looking-glass-history'; const HISTORY_KEY = 'looking-glass-history';
@@ -263,8 +261,9 @@ function objectToString(o) {
const ObjLink = new Lang.Class({ const ObjLink = new Lang.Class({
Name: 'ObjLink', Name: 'ObjLink',
Extends: Link.Link,
_init: function(lookingGlass, o, title) { _init: function(o, title) {
let text; let text;
if (title) if (title)
text = title; text = title;
@@ -273,30 +272,24 @@ const ObjLink = new Lang.Class({
text = GLib.markup_escape_text(text, -1); text = GLib.markup_escape_text(text, -1);
this._obj = o; this._obj = o;
this.actor = new St.Button({ reactive: true, this.parent({ label: text });
track_hover: true,
style_class: 'shell-link',
label: text });
this.actor.get_child().single_line_mode = true; this.actor.get_child().single_line_mode = true;
this.actor.connect('clicked', Lang.bind(this, this._onClicked)); this.actor.connect('clicked', Lang.bind(this, this._onClicked));
this._lookingGlass = lookingGlass;
}, },
_onClicked: function (link) { _onClicked: function (link) {
this._lookingGlass.inspectObject(this._obj, this.actor); Main.lookingGlass.inspectObject(this._obj, this.actor);
} }
}); });
const Result = new Lang.Class({ const Result = new Lang.Class({
Name: 'Result', Name: 'Result',
_init: function(lookingGlass, command, o, index) { _init : function(command, o, index) {
this.index = index; this.index = index;
this.o = o; this.o = o;
this.actor = new St.BoxLayout({ vertical: true }); this.actor = new St.BoxLayout({ vertical: true });
this._lookingGlass = lookingGlass;
let cmdTxt = new St.Label({ text: command }); let cmdTxt = new St.Label({ text: command });
cmdTxt.clutter_text.ellipsize = Pango.EllipsizeMode.END; cmdTxt.clutter_text.ellipsize = Pango.EllipsizeMode.END;
@@ -306,7 +299,7 @@ const Result = new Lang.Class({
let resultTxt = new St.Label({ text: 'r(' + index + ') = ' }); let resultTxt = new St.Label({ text: 'r(' + index + ') = ' });
resultTxt.clutter_text.ellipsize = Pango.EllipsizeMode.END; resultTxt.clutter_text.ellipsize = Pango.EllipsizeMode.END;
box.add(resultTxt); box.add(resultTxt);
let objLink = new ObjLink(this._lookingGlass, o); let objLink = new ObjLink(o);
box.add(objLink.actor); box.add(objLink.actor);
let line = new Clutter.Rectangle({ name: 'Separator' }); let line = new Clutter.Rectangle({ name: 'Separator' });
let padBin = new St.Bin({ name: 'Separator', x_fill: true, y_fill: true }); let padBin = new St.Bin({ name: 'Separator', x_fill: true, y_fill: true });
@@ -318,18 +311,16 @@ const Result = new Lang.Class({
const WindowList = new Lang.Class({ const WindowList = new Lang.Class({
Name: 'WindowList', Name: 'WindowList',
_init: function(lookingGlass) { _init : function () {
this.actor = new St.BoxLayout({ name: 'Windows', vertical: true, style: 'spacing: 8px' }); this.actor = new St.BoxLayout({ name: 'Windows', vertical: true, style: 'spacing: 8px' });
let tracker = Shell.WindowTracker.get_default(); let tracker = Shell.WindowTracker.get_default();
this._updateId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._updateWindowList)); this._updateId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._updateWindowList));
global.display.connect('window-created', 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)); tracker.connect('tracked-windows-changed', Lang.bind(this, this._updateWindowList));
this._lookingGlass = lookingGlass;
}, },
_updateWindowList: function() { _updateWindowList: function() {
this.actor.destroy_all_children(); this.actor.get_children().forEach(function (actor) { actor.destroy(); });
let windows = global.get_window_actors(); let windows = global.get_window_actors();
let tracker = Shell.WindowTracker.get_default(); let tracker = Shell.WindowTracker.get_default();
for (let i = 0; i < windows.length; i++) { for (let i = 0; i < windows.length; i++) {
@@ -341,7 +332,7 @@ const WindowList = new Lang.Class({
} }
let box = new St.BoxLayout({ vertical: true }); let box = new St.BoxLayout({ vertical: true });
this.actor.add(box); 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 }); box.add(windowLink.actor, { x_align: St.Align.START, x_fill: false });
let propsBox = new St.BoxLayout({ vertical: true, style: 'padding-left: 6px;' }); let propsBox = new St.BoxLayout({ vertical: true, style: 'padding-left: 6px;' });
box.add(propsBox); box.add(propsBox);
@@ -352,7 +343,7 @@ const WindowList = new Lang.Class({
let propBox = new St.BoxLayout({ style: 'spacing: 6px; ' }); let propBox = new St.BoxLayout({ style: 'spacing: 6px; ' });
propsBox.add(propBox); propsBox.add(propBox);
propBox.add(new St.Label({ text: 'app: ' }), { y_fill: false }); 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(appLink.actor, { y_fill: false });
propBox.add(icon, { y_fill: false }); propBox.add(icon, { y_fill: false });
} else { } else {
@@ -366,7 +357,7 @@ Signals.addSignalMethods(WindowList.prototype);
const ObjInspector = new Lang.Class({ const ObjInspector = new Lang.Class({
Name: 'ObjInspector', Name: 'ObjInspector',
_init: function(lookingGlass) { _init : function () {
this._obj = null; this._obj = null;
this._previousObj = null; this._previousObj = null;
@@ -378,8 +369,6 @@ const ObjInspector = new Lang.Class({
style_class: 'lg-dialog', style_class: 'lg-dialog',
vertical: true }); vertical: true });
this.actor.add_actor(this._container); this.actor.add_actor(this._container);
this._lookingGlass = lookingGlass;
}, },
selectObject: function(obj, skipPrevious) { selectObject: function(obj, skipPrevious) {
@@ -389,7 +378,7 @@ const ObjInspector = new Lang.Class({
this._previousObj = null; this._previousObj = null;
this._obj = obj; 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' }); let hbox = new St.BoxLayout({ style_class: 'lg-obj-inspector-title' });
this._container.add_actor(hbox); this._container.add_actor(hbox);
@@ -411,19 +400,12 @@ const ObjInspector = new Lang.Class({
button.connect('clicked', Lang.bind(this, this.close)); button.connect('clicked', Lang.bind(this, this.close));
hbox.add(button); hbox.add(button);
if (typeof(obj) == typeof({})) { if (typeof(obj) == typeof({})) {
let properties = [];
for (let propName in obj) { for (let propName in obj) {
properties.push(propName);
}
properties.sort();
for (let i = 0; i < properties.length; i++) {
let propName = properties[i];
let valueStr; let valueStr;
let link; let link;
try { try {
let prop = obj[propName]; let prop = obj[propName];
link = new ObjLink(this._lookingGlass, prop).actor; link = new ObjLink(prop).actor;
} catch (e) { } catch (e) {
link = new St.Label({ text: '<error>' }); link = new St.Label({ text: '<error>' });
} }
@@ -468,7 +450,7 @@ const ObjInspector = new Lang.Class({
_onInsert: function() { _onInsert: function() {
let obj = this._obj; let obj = this._obj;
this.close(); this.close();
this._lookingGlass.insertObject(obj); Main.lookingGlass.insertObject(obj);
}, },
_onBack: function() { _onBack: function() {
@@ -476,36 +458,34 @@ const ObjInspector = new Lang.Class({
} }
}); });
const RedBorderEffect = new Lang.Class({ function addBorderPaintHook(actor) {
Name: 'RedBorderEffect', let signalId = actor.connect_after('paint',
Extends: Clutter.Effect, function () {
let color = new Cogl.Color();
color.init_from_4ub(0xff, 0, 0, 0xc4);
Cogl.set_source_color(color);
vfunc_paint: function() { let geom = actor.get_allocation_geometry();
let actor = this.get_actor(); let width = 2;
actor.continue_paint();
let color = new Cogl.Color(); // clockwise order
color.init_from_4ub(0xff, 0, 0, 0xc4); Cogl.rectangle(0, 0, geom.width, width);
Cogl.set_source_color(color); 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(); actor.queue_redraw();
let width = 2; return signalId;
}
// clockwise order
Cogl.rectangle(0, 0, geom.width, width);
Cogl.rectangle(geom.width - width, width,
geom.width, geom.height);
Cogl.rectangle(0, geom.height,
geom.width - width, geom.height - width);
Cogl.rectangle(0, geom.height - width,
width, width);
},
});
const Inspector = new Lang.Class({ const Inspector = new Lang.Class({
Name: 'Inspector', Name: 'Inspector',
_init: function(lookingGlass) { _init: function() {
let container = new Shell.GenericContainer({ width: 0, let container = new Shell.GenericContainer({ width: 0,
height: 0 }); height: 0 });
container.connect('allocate', Lang.bind(this, this._allocate)); container.connect('allocate', Lang.bind(this, this._allocate));
@@ -519,6 +499,9 @@ const Inspector = new Lang.Class({
this._displayText = new St.Label(); this._displayText = new St.Label();
eventHandler.add(this._displayText, { expand: true }); 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('key-press-event', Lang.bind(this, this._onKeyPressEvent));
eventHandler.connect('button-press-event', Lang.bind(this, this._onButtonPressEvent)); eventHandler.connect('button-press-event', Lang.bind(this, this._onButtonPressEvent));
eventHandler.connect('scroll-event', Lang.bind(this, this._onScrollEvent)); eventHandler.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
@@ -533,8 +516,6 @@ const Inspector = new Lang.Class({
// out, or move the pointer outside of _pointerTarget. // out, or move the pointer outside of _pointerTarget.
this._target = null; this._target = null;
this._pointerTarget = null; this._pointerTarget = null;
this._lookingGlass = lookingGlass;
}, },
_allocate: function(actor, box, flags) { _allocate: function(actor, box, flags) {
@@ -555,13 +536,18 @@ const Inspector = new Lang.Class({
}, },
_close: function() { _close: function() {
Clutter.ungrab_pointer(); Clutter.ungrab_pointer(this._eventHandler);
Clutter.ungrab_keyboard(); Clutter.ungrab_keyboard(this._eventHandler);
this._eventHandler.destroy(); this._eventHandler.destroy();
this._eventHandler = null; this._eventHandler = null;
this.emit('closed'); this.emit('closed');
}, },
_onDestroy: function() {
if (this._borderPaintTarget != null)
this._borderPaintTarget.disconnect(this._borderPaintId);
},
_onKeyPressEvent: function (actor, event) { _onKeyPressEvent: function (actor, event) {
if (event.get_key_symbol() == Clutter.Escape) if (event.get_key_symbol() == Clutter.Escape)
this._close(); this._close();
@@ -630,12 +616,56 @@ const Inspector = new Lang.Class({
this._displayText.text = ''; this._displayText.text = '';
this._displayText.text = position + ' ' + this._target; this._displayText.text = position + ' ' + this._target;
this._lookingGlass.setBorderPaintTarget(this._target); if (this._borderPaintTarget != this._target) {
if (this._borderPaintTarget != null)
this._borderPaintTarget.disconnect(this._borderPaintId);
this._borderPaintTarget = this._target;
this._borderPaintId = addBorderPaintHook(this._target);
}
} }
}); });
Signals.addSignalMethods(Inspector.prototype); Signals.addSignalMethods(Inspector.prototype);
const ErrorLog = new Lang.Class({
Name: 'ErrorLog',
_init: function() {
this.actor = new St.BoxLayout();
this.text = new St.Label();
this.actor.add(this.text);
// We need to override StLabel's default ellipsization when
// using line_wrap; otherwise ClutterText's layout is going
// to constrain both the width and height, which prevents
// scrolling.
this.text.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this.text.clutter_text.line_wrap = true;
this.actor.connect('notify::mapped', Lang.bind(this, this._renderText));
},
_formatTime: function(d){
function pad(n) { return n < 10 ? '0' + n : n; }
return d.getUTCFullYear()+'-'
+ pad(d.getUTCMonth()+1)+'-'
+ pad(d.getUTCDate())+'T'
+ pad(d.getUTCHours())+':'
+ pad(d.getUTCMinutes())+':'
+ pad(d.getUTCSeconds())+'Z';
},
_renderText: function() {
if (!this.actor.mapped)
return;
let text = this.text.text;
let stack = Main._getAndClearErrorStack();
for (let i = 0; i < stack.length; i++) {
let logItem = stack[i];
text += logItem.category + ' t=' + this._formatTime(new Date(logItem.timestamp)) + ' ' + logItem.message + '\n';
}
this.text.text = text;
}
});
const Memory = new Lang.Class({ const Memory = new Lang.Class({
Name: 'Memory', Name: 'Memory',
@@ -664,7 +694,7 @@ const Memory = new Lang.Class({
this._gcbutton = new St.Button({ label: 'Full GC', this._gcbutton = new St.Button({ label: 'Full GC',
style_class: 'lg-obj-inspector-button' }); 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, this.actor.add(this._gcbutton, { x_align: St.Align.START,
x_fill: false }); x_fill: false });
@@ -725,13 +755,13 @@ const Extensions = new Lang.Class({
let extension = actor._extension; let extension = actor._extension;
let uri = extension.dir.get_uri(); let uri = extension.dir.get_uri();
Gio.app_info_launch_default_for_uri(uri, global.create_app_launch_context()); Gio.app_info_launch_default_for_uri(uri, global.create_app_launch_context());
this._lookingGlass.close(); Main.lookingGlass.close();
}, },
_onWebPage: function (actor) { _onWebPage: function (actor) {
let extension = actor._extension; let extension = actor._extension;
Gio.app_info_launch_default_for_uri(extension.metadata.url, global.create_app_launch_context()); Gio.app_info_launch_default_for_uri(extension.metadata.url, global.create_app_launch_context());
this._lookingGlass.close(); Main.lookingGlass.close();
}, },
_onViewErrors: function (actor) { _onViewErrors: function (actor) {
@@ -795,33 +825,24 @@ const Extensions = new Lang.Class({
text: this._stateToString(extension.state) }); text: this._stateToString(extension.state) });
metaBox.add(state); metaBox.add(state);
let viewsource = new St.Button({ reactive: true, let viewsource = new Link.Link({ label: _("View Source") });
track_hover: true, viewsource.actor._extension = extension;
style_class: 'shell-link', viewsource.actor.connect('clicked', Lang.bind(this, this._onViewSource));
label: _("View Source") }); metaBox.add(viewsource.actor);
viewsource._extension = extension;
viewsource.connect('clicked', Lang.bind(this, this._onViewSource));
metaBox.add(viewsource);
if (extension.metadata.url) { if (extension.metadata.url) {
let webpage = new St.Button({ reactive: true, let webpage = new Link.Link({ label: _("Web Page") });
track_hover: true, webpage.actor._extension = extension;
style_class: 'shell-link', webpage.actor.connect('clicked', Lang.bind(this, this._onWebPage));
label: _("Web Page") }); metaBox.add(webpage.actor);
webpage._extension = extension;
webpage.connect('clicked', Lang.bind(this, this._onWebPage));
metaBox.add(webpage);
} }
let viewerrors = new St.Button({ reactive: true, let viewerrors = new Link.Link({ label: _("Show Errors") });
track_hover: true, viewerrors.actor._extension = extension;
style_class: 'shell-link', viewerrors.actor._parentBox = box;
label: _("Show Errors") }); viewerrors.actor._isShowing = false;
viewerrors._extension = extension; viewerrors.actor.connect('clicked', Lang.bind(this, this._onViewErrors));
viewerrors._parentBox = box; metaBox.add(viewerrors.actor);
viewerrors._isShowing = false;
viewerrors.connect('clicked', Lang.bind(this, this._onViewErrors));
metaBox.add(viewerrors);
return box; return box;
} }
@@ -832,7 +853,8 @@ const LookingGlass = new Lang.Class({
_init : function() { _init : function() {
this._borderPaintTarget = null; this._borderPaintTarget = null;
this._redBorderEffect = new RedBorderEffect(); this._borderPaintId = 0;
this._borderDestroyId = 0;
this._open = false; this._open = false;
@@ -862,7 +884,7 @@ const LookingGlass = new Lang.Class({
Main.layoutManager.keyboardBox.connect('allocation-changed', Main.layoutManager.keyboardBox.connect('allocation-changed',
Lang.bind(this, this._queueResize)); Lang.bind(this, this._queueResize));
this._objInspector = new ObjInspector(this); this._objInspector = new ObjInspector();
Main.uiGroup.add_actor(this._objInspector.actor); Main.uiGroup.add_actor(this._objInspector.actor);
this._objInspector.actor.hide(); this._objInspector.actor.hide();
@@ -874,7 +896,7 @@ const LookingGlass = new Lang.Class({
toolbar.add_actor(inspectIcon); toolbar.add_actor(inspectIcon);
inspectIcon.reactive = true; inspectIcon.reactive = true;
inspectIcon.connect('button-press-event', Lang.bind(this, function () { 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) { inspector.connect('target', Lang.bind(this, function(i, target, stageX, stageY) {
this._pushResult('<inspect x:' + stageX + ' y:' + stageY + '>', this._pushResult('<inspect x:' + stageX + ' y:' + stageY + '>',
target); target);
@@ -904,16 +926,23 @@ const LookingGlass = new Lang.Class({
this._entryArea = new St.BoxLayout({ name: 'EntryArea' }); this._entryArea = new St.BoxLayout({ name: 'EntryArea' });
this._evalBox.add_actor(this._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._entryArea.add(label);
this._entry = new St.Entry({ can_focus: true }); this._entry = new St.Entry({ can_focus: true });
ShellEntry.addContextMenu(this._entry); ShellEntry.addContextMenu(this._entry);
this._entryArea.add(this._entry, { expand: true }); 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); notebook.appendPage('Windows', this._windowList.actor);
this._errorLog = new ErrorLog();
notebook.appendPage('Errors', this._errorLog.actor);
this._memory = new Memory(); this._memory = new Memory();
notebook.appendPage('Memory', this._memory.actor); notebook.appendPage('Memory', this._memory.actor);
@@ -965,22 +994,23 @@ const LookingGlass = new Lang.Class({
+ 'font-family: "' + fontDesc.get_family() + '";'; + '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) { _pushResult: function(command, obj) {
let index = this._results.length + this._offset; 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._results.push(result);
this._resultsArea.add(result.actor); this._resultsArea.add(result.actor);
if (obj instanceof Clutter.Actor) if (this._borderPaintTarget != null) {
this.setBorderPaintTarget(obj); 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(); let children = this._resultsArea.get_children();
if (children.length > this._maxItems) { if (children.length > this._maxItems) {
this._results.shift(); this._results.shift();
@@ -1161,7 +1191,11 @@ const LookingGlass = new Lang.Class({
this._open = false; this._open = false;
Tweener.removeTweens(this.actor); 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); Main.popModal(this._entry);

View File

@@ -16,7 +16,6 @@ const Params = imports.misc.params;
const MOUSE_POLL_FREQUENCY = 50; const MOUSE_POLL_FREQUENCY = 50;
const CROSSHAIRS_CLIP_SIZE = [100, 100]; const CROSSHAIRS_CLIP_SIZE = [100, 100];
const NO_CHANGE = 0.0;
// Settings // Settings
const APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications'; 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 MAGNIFIER_SCHEMA = 'org.gnome.desktop.a11y.magnifier';
const SCREEN_POSITION_KEY = 'screen-position'; const SCREEN_POSITION_KEY = 'screen-position';
const MAG_FACTOR_KEY = 'mag-factor'; 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 LENS_MODE_KEY = 'lens-mode';
const CLAMP_MODE_KEY = 'scroll-at-edges'; const CLAMP_MODE_KEY = 'scroll-at-edges';
const MOUSE_TRACKING_KEY = 'mouse-tracking'; const MOUSE_TRACKING_KEY = 'mouse-tracking';
@@ -452,25 +443,6 @@ const Magnifier = new Lang.Class({
aPref = this._settings.get_enum(MOUSE_TRACKING_KEY); aPref = this._settings.get_enum(MOUSE_TRACKING_KEY);
if (aPref) if (aPref)
zoomRegion.setMouseTrackingMode(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); let showCrosshairs = this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY);
@@ -493,25 +465,6 @@ const Magnifier = new Lang.Class({
this._settings.connect('changed::' + MOUSE_TRACKING_KEY, this._settings.connect('changed::' + MOUSE_TRACKING_KEY,
Lang.bind(this, this._updateMouseTrackingMode)); 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, this._settings.connect('changed::' + SHOW_CROSS_HAIRS_KEY,
Lang.bind(this, function() { Lang.bind(this, function() {
this.setCrosshairsVisible(this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY)); this.setCrosshairsVisible(this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY));
@@ -587,47 +540,7 @@ const Magnifier = new Lang.Class({
this._settings.get_enum(MOUSE_TRACKING_KEY) 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); Signals.addSignalMethods(Magnifier.prototype);
@@ -641,10 +554,6 @@ const ZoomRegion = new Lang.Class({
this._clampScrollingAtEdges = false; this._clampScrollingAtEdges = false;
this._lensMode = false; this._lensMode = false;
this._screenPosition = GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN; 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._magView = null;
this._background = null; this._background = null;
@@ -970,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 //// //// Private methods ////
_createActors: function() { _createActors: function() {
@@ -1109,13 +917,6 @@ const ZoomRegion = new Lang.Class({
this._crossHairsActor = this._crossHairs.addToZoomRegion(this, this._mouseActor); this._crossHairsActor = this._crossHairs.addToZoomRegion(this, this._mouseActor);
else else
this._crossHairsActor = null; 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() { _destroyActors: function() {
@@ -1124,8 +925,6 @@ const ZoomRegion = new Lang.Class({
if (this._crossHairs) if (this._crossHairs)
this._crossHairs.removeFromParent(this._crossHairsActor); this._crossHairs.removeFromParent(this._crossHairsActor);
this._magShaderEffects.destroyEffects();
this._magShaderEffects = null;
this._magView.destroy(); this._magView.destroy();
this._magView = null; this._magView = null;
this._background = null; this._background = null;
@@ -1429,7 +1228,10 @@ const Crosshairs = new Lang.Class({
crosshairsActor = new Clutter.Clone({ source: this._actor }); crosshairsActor = new Clutter.Clone({ source: this._actor });
this._clones.push(crosshairsActor); this._clones.push(crosshairsActor);
} }
crosshairsActor.visible = this._actor.visible; if (this._actor.visible)
crosshairsActor.show();
else
crosshairsActor.hide();
container.add_actor(crosshairsActor); container.add_actor(crosshairsActor);
container.raise_child(magnifiedMouse, crosshairsActor); container.raise_child(magnifiedMouse, crosshairsActor);
@@ -1634,144 +1436,3 @@ const Crosshairs = new Lang.Class({
this._vertBottomHair.set_position((groupWidth - thickness) / 2, bottom); 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

@@ -18,7 +18,6 @@ const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent;
const KeyringPrompt = imports.ui.keyringPrompt; const KeyringPrompt = imports.ui.keyringPrompt;
const Environment = imports.ui.environment; const Environment = imports.ui.environment;
const ExtensionSystem = imports.ui.extensionSystem; const ExtensionSystem = imports.ui.extensionSystem;
const ExtensionDownloader = imports.ui.extensionDownloader;
const Keyboard = imports.ui.keyboard; const Keyboard = imports.ui.keyboard;
const MessageTray = imports.ui.messageTray; const MessageTray = imports.ui.messageTray;
const Overview = imports.ui.overview; const Overview = imports.ui.overview;
@@ -32,14 +31,12 @@ const NotificationDaemon = imports.ui.notificationDaemon;
const WindowAttentionHandler = imports.ui.windowAttentionHandler; const WindowAttentionHandler = imports.ui.windowAttentionHandler;
const ScreenShield = imports.ui.screenShield; const ScreenShield = imports.ui.screenShield;
const Scripting = imports.ui.scripting; const Scripting = imports.ui.scripting;
const SessionMode = imports.ui.sessionMode;
const ShellDBus = imports.ui.shellDBus; const ShellDBus = imports.ui.shellDBus;
const ShellMountOperation = imports.ui.shellMountOperation;
const TelepathyClient = imports.ui.telepathyClient; const TelepathyClient = imports.ui.telepathyClient;
const UnlockDialog = imports.ui.unlockDialog;
const WindowManager = imports.ui.windowManager; const WindowManager = imports.ui.windowManager;
const Magnifier = imports.ui.magnifier; const Magnifier = imports.ui.magnifier;
const XdndHandler = imports.ui.xdndHandler; const XdndHandler = imports.ui.xdndHandler;
const StatusIconDispatcher = imports.ui.statusIconDispatcher;
const Util = imports.misc.util; const Util = imports.misc.util;
const OVERRIDES_SCHEMA = 'org.gnome.shell.overrides'; const OVERRIDES_SCHEMA = 'org.gnome.shell.overrides';
@@ -50,6 +47,7 @@ let automountManager = null;
let autorunManager = null; let autorunManager = null;
let panel = null; let panel = null;
let hotCorners = []; let hotCorners = [];
let placesManager = null;
let overview = null; let overview = null;
let runDialog = null; let runDialog = null;
let lookingGlass = null; let lookingGlass = null;
@@ -61,76 +59,60 @@ let windowAttentionHandler = null;
let telepathyClient = null; let telepathyClient = null;
let ctrlAltTabManager = null; let ctrlAltTabManager = null;
let recorder = null; let recorder = null;
let sessionMode = null;
let shellDBusService = null; let shellDBusService = null;
let shellMountOpDBusService = null;
let screenSaverDBus = null;
let modalCount = 0; let modalCount = 0;
let modalActorFocusStack = []; let modalActorFocusStack = [];
let uiGroup = null; let uiGroup = null;
let magnifier = null; let magnifier = null;
let xdndHandler = null; let xdndHandler = null;
let statusIconDispatcher = null;
let keyboard = null; let keyboard = null;
let layoutManager = null; let layoutManager = null;
let networkAgent = null; let networkAgent = null;
let _errorLogStack = [];
let _startDate; let _startDate;
let _defaultCssStylesheet = null; let _defaultCssStylesheet = null;
let _cssStylesheet = null; let _cssStylesheet = null;
let _gdmCssStylesheet = null;
let _overridesSettings = null; let _overridesSettings = null;
let background = null; let background = null;
function createUserSession() { function _createUserSession() {
// Load the calendar server. Note that we are careful about // Load the calendar server. Note that we are careful about
// not loading any events until the user presses the clock // not loading any events until the user presses the clock
global.launch_calendar_server(); global.launch_calendar_server();
placesManager = new PlaceDisplay.PlacesManager();
telepathyClient = new TelepathyClient.Client(); telepathyClient = new TelepathyClient.Client();
automountManager = new AutomountManager.AutomountManager(); automountManager = new AutomountManager.AutomountManager();
autorunManager = new AutorunManager.AutorunManager(); autorunManager = new AutorunManager.AutorunManager();
networkAgent = new NetworkAgent.NetworkAgent(); networkAgent = new NetworkAgent.NetworkAgent();
_initRecorder();
} }
function createGDMSession() { function _createGDMSession() {
screenShield.showDialog();
}
function createGDMLoginDialog(parentActor) {
// We do this this here instead of at the top to prevent GDM // We do this this here instead of at the top to prevent GDM
// related code from getting loaded in normal user sessions // related code from getting loaded in normal user sessions
const LoginDialog = imports.gdm.loginDialog; const LoginDialog = imports.gdm.loginDialog;
let loginDialog = new LoginDialog.LoginDialog(parentActor); let loginDialog = new LoginDialog.LoginDialog();
return [loginDialog, true]; loginDialog.connect('loaded', function() {
} loginDialog.open();
});
function createSessionUnlockDialog(parentActor) {
let dialog = new UnlockDialog.UnlockDialog(parentActor);
return [dialog, false];
}
function createInitialSetupSession() {
networkAgent = new NetworkAgent.NetworkAgent();
} }
function _initRecorder() { function _initRecorder() {
let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' }); let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
let desktopLockdownSettings = new Gio.Settings({ schema: 'org.gnome.desktop.lockdown' });
let bindingSettings = new Gio.Settings({ schema: 'org.gnome.shell.keybindings' });
global.display.add_keybinding('toggle-recording', global.screen.connect('toggle-recording', function() {
bindingSettings,
Meta.KeyBindingFlags.NONE, function() {
if (recorder == null) { if (recorder == null) {
recorder = new Shell.Recorder({ stage: global.stage }); recorder = new Shell.Recorder({ stage: global.stage });
} }
if (recorder.is_recording()) { if (recorder.is_recording()) {
recorder.close(); recorder.pause();
Meta.enable_unredirect_for_screen(global.screen); Meta.enable_unredirect_for_screen(global.screen);
} else if (!desktopLockdownSettings.get_boolean('disable-save-to-disk')) { } else {
// read the parameters from GSettings always in case they have changed // read the parameters from GSettings always in case they have changed
recorder.set_framerate(recorderSettings.get_int('framerate')); recorder.set_framerate(recorderSettings.get_int('framerate'));
/* Translators: this is a filename used for screencast recording */ /* Translators: this is a filename used for screencast recording */
@@ -149,19 +131,39 @@ function _initRecorder() {
}); });
} }
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() { function start() {
// These are here so we don't break compatibility. // Monkey patch utility functions into the global proxy;
global.logError = window.log; // This is easier and faster than indirecting down into global
global.log = window.log; // if we want to call back up into JS.
global.logError = _logError;
global.log = _logDebug;
// Chain up async errors reported from C // Chain up async errors reported from C
global.connect('notify-error', function (global, msg, detail) { notifyError(msg, detail); }); global.connect('notify-error', function (global, msg, detail) { notifyError(msg, detail); });
Gio.DesktopAppInfo.set_desktop_env('GNOME'); Gio.DesktopAppInfo.set_desktop_env('GNOME');
sessionMode = new SessionMode.SessionMode();
shellDBusService = new ShellDBus.GnomeShell(); shellDBusService = new ShellDBus.GnomeShell();
shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will // Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
// also initialize ShellAppSystem first. ShellAppSystem // also initialize ShellAppSystem first. ShellAppSystem
@@ -183,6 +185,7 @@ function start() {
global.stage.no_clear_hint = true; global.stage.no_clear_hint = true;
_defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css'; _defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
_gdmCssStylesheet = global.datadir + '/theme/gdm.css';
loadTheme(); loadTheme();
// Set up stage hierarchy to group all UI actors under one container. // Set up stage hierarchy to group all UI actors under one container.
@@ -193,16 +196,9 @@ function start() {
for (let i = 0; i < children.length; i++) for (let i = 0; i < children.length; i++)
children[i].allocate_preferred_size(flags); children[i].allocate_preferred_size(flags);
}); });
uiGroup.connect('get-preferred-width', let constraint = new Clutter.BindConstraint({ source: global.stage,
function(actor, forHeight, alloc) { coordinate: Clutter.BindCoordinate.SIZE });
let width = global.stage.width; uiGroup.add_constraint(constraint);
[alloc.min_size, alloc.natural_size] = [width, width];
});
uiGroup.connect('get-preferred-height',
function(actor, forWidth, alloc) {
let height = global.stage.height;
[alloc.min_size, alloc.natural_size] = [height, height];
});
global.window_group.reparent(uiGroup); global.window_group.reparent(uiGroup);
global.overlay_group.reparent(uiGroup); global.overlay_group.reparent(uiGroup);
global.stage.add_actor(uiGroup); global.stage.add_actor(uiGroup);
@@ -210,18 +206,22 @@ function start() {
layoutManager = new Layout.LayoutManager(); layoutManager = new Layout.LayoutManager();
xdndHandler = new XdndHandler.XdndHandler(); xdndHandler = new XdndHandler.XdndHandler();
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager(); 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(); magnifier = new Magnifier.Magnifier();
screenShield = new ScreenShield.ScreenShield(); statusIconDispatcher = new StatusIconDispatcher.StatusIconDispatcher();
screenSaverDBus = new ShellDBus.ScreenSaverDBus();
panel = new Panel.Panel(); panel = new Panel.Panel();
wm = new WindowManager.WindowManager(); wm = new WindowManager.WindowManager();
messageTray = new MessageTray.MessageTray(); messageTray = new MessageTray.MessageTray();
screenShield = new ScreenShield.ScreenShield();
keyboard = new Keyboard.Keyboard(); keyboard = new Keyboard.Keyboard();
notificationDaemon = new NotificationDaemon.NotificationDaemon(); notificationDaemon = new NotificationDaemon.NotificationDaemon();
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler(); windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
sessionMode.createSession(); if (global.session_type == Shell.SessionType.USER)
_createUserSession();
else if (global.session_type == Shell.SessionType.GDM)
_createGDMSession();
panel.startStatusArea(); panel.startStatusArea();
@@ -229,29 +229,9 @@ function start() {
keyboard.init(); keyboard.init();
overview.init(); overview.init();
if (sessionMode.hasWorkspaces) if (global.session_type == Shell.SessionType.USER)
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, _initUserSession();
false, -1, 1); statusIconDispatcher.start(messageTray.actor);
if (sessionMode.allowExtensions) {
ExtensionDownloader.init();
ExtensionSystem.loadExtensions();
}
if (sessionMode.hasRunDialog) {
Meta.keybindings_set_custom_handler('panel-run-dialog', function() {
getRunDialog().open();
});
}
if (sessionMode.hasOverview) {
Meta.keybindings_set_custom_handler('panel-main-menu', function () {
overview.toggle();
});
global.display.connect('overlay-key',
Lang.bind(overview, overview.toggle));
}
// Provide the bus object for gnome-session to // Provide the bus object for gnome-session to
// initiate logouts. // initiate logouts.
@@ -267,6 +247,7 @@ function start() {
global.stage.connect('captured-event', _globalKeyPressHandler); global.stage.connect('captured-event', _globalKeyPressHandler);
_log('info', 'loaded at ' + _startDate);
log('GNOME Shell started at ' + _startDate); log('GNOME Shell started at ' + _startDate);
let perfModuleName = GLib.getenv("SHELL_PERF_MODULE"); let perfModuleName = GLib.getenv("SHELL_PERF_MODULE");
@@ -509,8 +490,8 @@ function loadTheme() {
let theme = new St.Theme ({ application_stylesheet: cssStylesheet }); let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
if (sessionMode.extraStylesheet) if (global.session_type == Shell.SessionType.GDM)
theme.load_stylesheet(sessionMode.extraStylesheet); theme.load_stylesheet(_gdmCssStylesheet);
if (previousTheme) { if (previousTheme) {
let customStylesheets = previousTheme.get_custom_stylesheets(); let customStylesheets = previousTheme.get_custom_stylesheets();
@@ -532,7 +513,6 @@ function notify(msg, details) {
messageTray.add(source); messageTray.add(source);
let notification = new MessageTray.Notification(source, msg, details); let notification = new MessageTray.Notification(source, msg, details);
notification.setTransient(true); notification.setTransient(true);
notification.setShowWhenLocked(true);
source.notify(notification); source.notify(notification);
} }
@@ -553,6 +533,59 @@ function notifyError(msg, details) {
notify(msg, details); notify(msg, details);
} }
/**
* _log:
* @category: string message type ('info', 'error')
* @msg: A message string
* ...: Any further arguments are converted into JSON notation,
* and appended to the log message, separated by spaces.
*
* Log a message into the LookingGlass error
* stream. This is primarily intended for use by the
* extension system as well as debugging.
*/
function _log(category, msg) {
let text = msg;
if (arguments.length > 2) {
text += ': ';
for (let i = 2; i < arguments.length; i++) {
text += JSON.stringify(arguments[i]);
if (i < arguments.length - 1)
text += ' ';
}
}
_errorLogStack.push({timestamp: new Date().getTime(),
category: category,
message: text });
}
function _logError(msg) {
return _log('error', msg);
}
function _logDebug(msg) {
return _log('debug', msg);
}
// Used by the error display in lookingGlass.js
function _getAndClearErrorStack() {
let errors = _errorLogStack;
_errorLogStack = [];
return errors;
}
function logStackTrace(msg) {
try {
throw new Error();
} catch (e) {
// e.stack must have at least two lines, with the first being
// logStackTrace() (which we strip off), and the second being
// our caller.
let trace = e.stack.substr(e.stack.indexOf('\n') + 1);
log(msg ? (msg + '\n' + trace) : trace);
}
}
function isWindowActorDisplayedOnWorkspace(win, workspaceIndex) { function isWindowActorDisplayedOnWorkspace(win, workspaceIndex) {
return win.get_workspace() == workspaceIndex || return win.get_workspace() == workspaceIndex ||
(win.get_meta_window() && win.get_meta_window().is_on_all_workspaces()); (win.get_meta_window() && win.get_meta_window().is_on_all_workspaces());
@@ -575,11 +608,6 @@ function _globalKeyPressHandler(actor, event) {
if (event.type() != Clutter.EventType.KEY_PRESS) if (event.type() != Clutter.EventType.KEY_PRESS)
return false; return false;
if (!sessionMode.allowKeybindingsWhenModal) {
if (modalCount > (overview.visible ? 1 : 0))
return false;
}
let symbol = event.get_key_symbol(); let symbol = event.get_key_symbol();
let keyCode = event.get_key_code(); let keyCode = event.get_key_code();
let ignoredModifiers = global.display.get_ignored_modifier_mask(); let ignoredModifiers = global.display.get_ignored_modifier_mask();
@@ -588,6 +616,11 @@ function _globalKeyPressHandler(actor, event) {
// This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType // This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType
let action = global.display.get_keybinding_action(keyCode, modifierState); 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 // This isn't a Meta.KeyBindingAction yet
if (symbol == Clutter.Super_L || symbol == Clutter.Super_R) { if (symbol == Clutter.Super_L || symbol == Clutter.Super_R) {
overview.hide(); overview.hide();
@@ -600,39 +633,28 @@ function _globalKeyPressHandler(actor, event) {
return true; 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) { switch (action) {
// left/right would effectively act as synonyms for up/down if we enabled them; // 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. // but that could be considered confusing; we also disable them in the main view.
// //
// case Meta.KeyBindingAction.WORKSPACE_LEFT: // case Meta.KeyBindingAction.WORKSPACE_LEFT:
// if (!sessionMode.hasWorkspaces)
// return false;
//
// wm.actionMoveWorkspaceLeft(); // wm.actionMoveWorkspaceLeft();
// return true; // return true;
// case Meta.KeyBindingAction.WORKSPACE_RIGHT: // case Meta.KeyBindingAction.WORKSPACE_RIGHT:
// if (!sessionMode.hasWorkspaces)
// return false;
//
// wm.actionMoveWorkspaceRight(); // wm.actionMoveWorkspaceRight();
// return true; // return true;
case Meta.KeyBindingAction.WORKSPACE_UP: case Meta.KeyBindingAction.WORKSPACE_UP:
if (!sessionMode.hasWorkspaces) wm.actionMoveWorkspaceUp();
return false;
wm.actionMoveWorkspace(Meta.MotionDirection.UP);
return true; return true;
case Meta.KeyBindingAction.WORKSPACE_DOWN: case Meta.KeyBindingAction.WORKSPACE_DOWN:
if (!sessionMode.hasWorkspaces) wm.actionMoveWorkspaceDown();
return false;
wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
return true; return true;
case Meta.KeyBindingAction.PANEL_RUN_DIALOG: case Meta.KeyBindingAction.PANEL_RUN_DIALOG:
case Meta.KeyBindingAction.COMMAND_2: case Meta.KeyBindingAction.COMMAND_2:
if (!sessionMode.hasRunDialog)
return false;
getRunDialog().open(); getRunDialog().open();
return true; return true;
case Meta.KeyBindingAction.PANEL_MAIN_MENU: case Meta.KeyBindingAction.PANEL_MAIN_MENU:
@@ -687,7 +709,6 @@ function pushModal(actor, timestamp, options) {
log('pushModal: invocation of begin_modal failed'); log('pushModal: invocation of begin_modal failed');
return false; return false;
} }
Meta.disable_unredirect_for_screen(global.screen);
} }
global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN); global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
@@ -696,7 +717,7 @@ function pushModal(actor, timestamp, options) {
let actorDestroyId = actor.connect('destroy', function() { let actorDestroyId = actor.connect('destroy', function() {
let index = _findModal(actor); let index = _findModal(actor);
if (index >= 0) if (index >= 0)
popModal(actor); modalActorFocusStack.splice(index, 1);
}); });
let curFocus = global.stage.get_key_focus(); let curFocus = global.stage.get_key_focus();
let curFocusDestroyId; let curFocusDestroyId;
@@ -768,7 +789,6 @@ function popModal(actor, timestamp) {
global.end_modal(timestamp); global.end_modal(timestamp);
global.set_stage_input_mode(Shell.StageInputMode.NORMAL); global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
Meta.enable_unredirect_for_screen(global.screen);
} }
function createLookingGlass() { function createLookingGlass() {
@@ -911,8 +931,7 @@ function initializeDeferredWork(actor, callback, props) {
function queueDeferredWork(workId) { function queueDeferredWork(workId) {
let data = _deferredWorkData[workId]; let data = _deferredWorkData[workId];
if (!data) { if (!data) {
let message = 'Invalid work id %d'.format(workId); global.logError('invalid work id ', workId);
logError(new Error(message), message);
return; return;
} }
if (_deferredWorkQueue.indexOf(workId) < 0) if (_deferredWorkQueue.indexOf(workId) < 0)

View File

@@ -394,7 +394,7 @@ Signals.addSignalMethods(FocusGrabber.prototype);
// the content area (as with addBody()). // the content area (as with addBody()).
// //
// By default, the icon shown is created by calling // By default, the icon shown is created by calling
// source.createIcon(). However, if @params contains an 'icon' // source.createNotificationIcon(). However, if @params contains an 'icon'
// parameter, the passed in icon will be used. // parameter, the passed in icon will be used.
// //
// If @params contains a 'titleMarkup', 'bannerMarkup', or // If @params contains a 'titleMarkup', 'bannerMarkup', or
@@ -420,7 +420,6 @@ const Notification = new Lang.Class({
// 'transient' is a reserved keyword in JS, so we have to use an alternate variable name // 'transient' is a reserved keyword in JS, so we have to use an alternate variable name
this.isTransient = false; this.isTransient = false;
this.expanded = false; this.expanded = false;
this.showWhenLocked = false;
this._destroyed = false; this._destroyed = false;
this._useActionIcons = false; this._useActionIcons = false;
this._customContent = false; this._customContent = false;
@@ -442,7 +441,7 @@ const Notification = new Lang.Class({
this.actor.connect('clicked', Lang.bind(this, this._onClicked)); this.actor.connect('clicked', Lang.bind(this, this._onClicked));
this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._table = new St.Table({ style_class: 'notification', this._table = new St.Table({ name: 'notification',
reactive: true }); reactive: true });
this._table.connect('style-changed', Lang.bind(this, this._styleChanged)); this._table.connect('style-changed', Lang.bind(this, this._styleChanged));
this.actor.set_child(this._table); this.actor.set_child(this._table);
@@ -494,7 +493,6 @@ const Notification = new Lang.Class({
params = Params.parse(params, { customContent: false, params = Params.parse(params, { customContent: false,
body: null, body: null,
icon: null, icon: null,
secondaryIcon: null,
titleMarkup: false, titleMarkup: false,
bannerMarkup: false, bannerMarkup: false,
bodyMarkup: false, bodyMarkup: false,
@@ -509,11 +507,6 @@ const Notification = new Lang.Class({
this._icon = null; this._icon = null;
} }
if (this._secondaryIcon && (params.secondaryIcon || params.clear)) {
this._secondaryIcon.destroy();
this._secondaryIcon = null;
}
// We always clear the content area if we don't have custom // We always clear the content area if we don't have custom
// content because it might contain the @banner that didn't // content because it might contain the @banner that didn't
// fit in the banner mode. // fit in the banner mode.
@@ -540,7 +533,7 @@ const Notification = new Lang.Class({
this._table.remove_style_class_name('multi-line-notification'); this._table.remove_style_class_name('multi-line-notification');
if (!this._icon) { if (!this._icon) {
this._icon = params.icon || this.source.createIcon(this.source.ICON_SIZE); this._icon = params.icon || this.source.createNotificationIcon();
this._table.add(this._icon, { row: 0, this._table.add(this._icon, { row: 0,
col: 0, col: 0,
x_expand: false, x_expand: false,
@@ -549,13 +542,6 @@ const Notification = new Lang.Class({
y_align: St.Align.START }); y_align: St.Align.START });
} }
if (!this._secondaryIcon) {
this._secondaryIcon = params.secondaryIcon;
if (this._secondaryIcon)
this._bannerBox.add_actor(this._secondaryIcon);
}
this.title = title; this.title = title;
title = title ? _fixMarkup(title.replace(/\n/g, ' '), params.titleMarkup) : ''; title = title ? _fixMarkup(title.replace(/\n/g, ' '), params.titleMarkup) : '';
this._titleLabel.clutter_text.set_markup('<b>' + title + '</b>'); this._titleLabel.clutter_text.set_markup('<b>' + title + '</b>');
@@ -591,7 +577,7 @@ const Notification = new Lang.Class({
if (params.body) if (params.body)
this.addBody(params.body, params.bodyMarkup); this.addBody(params.body, params.bodyMarkup);
this.updated(); this._updated();
}, },
setIconVisible: function(visible) { setIconVisible: function(visible) {
@@ -600,21 +586,19 @@ const Notification = new Lang.Class({
enableScrolling: function(enableScrolling) { enableScrolling: function(enableScrolling) {
this._scrollPolicy = enableScrolling ? Gtk.PolicyType.AUTOMATIC : Gtk.PolicyType.NEVER; this._scrollPolicy = enableScrolling ? Gtk.PolicyType.AUTOMATIC : Gtk.PolicyType.NEVER;
if (this._scrollArea) { if (this._scrollArea)
this._scrollArea.vscrollbar_policy = this._scrollPolicy; this._scrollArea.vscrollbar_policy = this._scrollPolicy;
this._scrollArea.enable_mouse_scrolling = enableScrolling;
}
}, },
_createScrollArea: function() { _createScrollArea: function() {
this._table.add_style_class_name('multi-line-notification'); this._table.add_style_class_name('multi-line-notification');
this._scrollArea = new St.ScrollView({ style_class: 'notification-scrollview', this._scrollArea = new St.ScrollView({ name: 'notification-scrollview',
vscrollbar_policy: this._scrollPolicy, vscrollbar_policy: this._scrollPolicy,
hscrollbar_policy: Gtk.PolicyType.NEVER }); hscrollbar_policy: Gtk.PolicyType.NEVER });
this._table.add(this._scrollArea, { row: 1, this._table.add(this._scrollArea, { row: 1,
col: 2 }); col: 2 });
this._updateLastColumnSettings(); this._updateLastColumnSettings();
this._contentArea = new St.BoxLayout({ style_class: 'notification-body', this._contentArea = new St.BoxLayout({ name: 'notification-body',
vertical: true }); vertical: true });
this._scrollArea.add_actor(this._contentArea); this._scrollArea.add_actor(this._contentArea);
// If we know the notification will be expandable, we need to add // If we know the notification will be expandable, we need to add
@@ -632,7 +616,7 @@ const Notification = new Lang.Class({
} }
this._contentArea.add(actor, style ? style : {}); this._contentArea.add(actor, style ? style : {});
this.updated(); this._updated();
}, },
// addBody: // addBody:
@@ -695,7 +679,7 @@ const Notification = new Lang.Class({
this._table.add_style_class_name('multi-line-notification'); this._table.add_style_class_name('multi-line-notification');
this._table.add(this._actionArea, props); this._table.add(this._actionArea, props);
this._updateLastColumnSettings(); this._updateLastColumnSettings();
this.updated(); this._updated();
}, },
_updateLastColumnSettings: function() { _updateLastColumnSettings: function() {
@@ -750,7 +734,7 @@ const Notification = new Lang.Class({
addButton: function(id, label) { addButton: function(id, label) {
if (!this._buttonBox) { if (!this._buttonBox) {
let box = new St.BoxLayout({ style_class: 'notification-actions' }); let box = new St.BoxLayout({ name: 'notification-actions' });
this.setActionArea(box, { x_expand: false, this.setActionArea(box, { x_expand: false,
y_expand: false, y_expand: false,
x_fill: false, x_fill: false,
@@ -760,7 +744,6 @@ const Notification = new Lang.Class({
} }
let button = new St.Button({ can_focus: true }); let button = new St.Button({ can_focus: true });
button._actionId = id;
if (this._useActionIcons && Gtk.IconTheme.get_default().has_icon(id)) { if (this._useActionIcons && Gtk.IconTheme.get_default().has_icon(id)) {
button.add_style_class_name('notification-icon-button'); button.add_style_class_name('notification-icon-button');
@@ -770,35 +753,14 @@ const Notification = new Lang.Class({
button.label = label; button.label = label;
} }
if (this._buttonBox.get_n_children() > 0) if (this._buttonBox.get_children().length > 0)
this._buttonFocusManager.remove_group(this._buttonBox); this._buttonFocusManager.remove_group(this._buttonBox);
this._buttonBox.add(button); this._buttonBox.add(button);
this._buttonFocusManager.add_group(this._buttonBox); this._buttonFocusManager.add_group(this._buttonBox);
button.connect('clicked', Lang.bind(this, this._onActionInvoked, id)); button.connect('clicked', Lang.bind(this, this._onActionInvoked, id));
this.updated(); this._updated();
},
// setButtonSensitive:
// @id: the action ID
// @sensitive: whether the button should be sensitive
//
// If the notification contains a button with action ID @id,
// its sensitivity will be set to @sensitive. Insensitive
// buttons cannot be clicked.
setButtonSensitive: function(id, sensitive) {
if (!this._buttonBox)
return;
let button = this._buttonBox.get_children().filter(function(b) {
return b._actionId == id;
})[0];
if (!button || button.reactive == sensitive)
return;
button.reactive = sensitive;
}, },
setUrgency: function(urgency) { setUrgency: function(urgency) {
@@ -813,14 +775,6 @@ const Notification = new Lang.Class({
this.isTransient = isTransient; this.isTransient = isTransient;
}, },
setShowWhenLocked: function(show) {
if (show && !this.isTransient) {
throw new Error('ShowWhenLocked can only be set on a transient notification');
}
this.showWhenLocked = show;
},
setUseActionIcons: function(useIcons) { setUseActionIcons: function(useIcons) {
this._useActionIcons = useIcons; this._useActionIcons = useIcons;
}, },
@@ -833,15 +787,8 @@ const Notification = new Lang.Class({
let [titleMin, titleNat] = this._titleLabel.get_preferred_width(forHeight); let [titleMin, titleNat] = this._titleLabel.get_preferred_width(forHeight);
let [bannerMin, bannerNat] = this._bannerLabel.get_preferred_width(forHeight); let [bannerMin, bannerNat] = this._bannerLabel.get_preferred_width(forHeight);
if (this._secondaryIcon) { alloc.min_size = titleMin;
let [secondaryIconMin, secondaryIconNat] = this._secondaryIcon.get_preferred_width(forHeight); alloc.natural_size = titleNat + this._spacing + bannerNat;
alloc.min_size = secondaryIconMin + this._spacing + titleMin;
alloc.natural_size = secondaryIconNat + this._spacing + titleNat + this._spacing + bannerNat;
} else {
alloc.min_size = titleMin;
alloc.natural_size = titleNat + this._spacing + bannerNat;
}
}, },
_bannerBoxGetPreferredHeight: function(actor, forWidth, alloc) { _bannerBoxGetPreferredHeight: function(actor, forWidth, alloc) {
@@ -856,42 +803,14 @@ const Notification = new Lang.Class({
let [titleMinH, titleNatH] = this._titleLabel.get_preferred_height(availWidth); let [titleMinH, titleNatH] = this._titleLabel.get_preferred_height(availWidth);
let [bannerMinW, bannerNatW] = this._bannerLabel.get_preferred_width(availWidth); let [bannerMinW, bannerNatW] = this._bannerLabel.get_preferred_width(availWidth);
let rtl = (this._titleDirection == Clutter.TextDirection.RTL);
let x = rtl ? availWidth : 0;
if (this._secondaryIcon) {
let [iconMinW, iconNatW] = this._secondaryIcon.get_preferred_width(-1);
let [iconMinH, iconNatH] = this._secondaryIcon.get_preferred_height(availWidth);
let secondaryIconBox = new Clutter.ActorBox();
let secondaryIconBoxW = Math.min(iconNatW, availWidth);
// allocate secondary icon box
if (rtl) {
secondaryIconBox.x1 = x - secondaryIconBoxW;
secondaryIconBox.x2 = x;
x = x - (secondaryIconBoxW + this._spacing);
} else {
secondaryIconBox.x1 = x;
secondaryIconBox.x2 = x + secondaryIconBoxW;
x = x + secondaryIconBoxW + this._spacing;
}
secondaryIconBox.y1 = 0;
// Using titleNatH ensures that the secondary icon is centered vertically
secondaryIconBox.y2 = titleNatH;
availWidth = availWidth - (secondaryIconBoxW + this._spacing);
this._secondaryIcon.allocate(secondaryIconBox, flags);
}
let titleBox = new Clutter.ActorBox(); let titleBox = new Clutter.ActorBox();
let titleBoxW = Math.min(titleNatW, availWidth); let titleBoxW = Math.min(titleNatW, availWidth);
if (rtl) { if (this._titleDirection == Clutter.TextDirection.RTL) {
titleBox.x1 = availWidth - titleBoxW; titleBox.x1 = availWidth - titleBoxW;
titleBox.x2 = availWidth; titleBox.x2 = availWidth;
} else { } else {
titleBox.x1 = x; titleBox.x1 = 0;
titleBox.x2 = titleBox.x1 + titleBoxW; titleBox.x2 = titleBoxW;
} }
titleBox.y1 = 0; titleBox.y1 = 0;
titleBox.y2 = titleNatH; titleBox.y2 = titleNatH;
@@ -905,7 +824,7 @@ const Notification = new Lang.Class({
} else { } else {
let bannerBox = new Clutter.ActorBox(); let bannerBox = new Clutter.ActorBox();
if (rtl) { if (this._titleDirection == Clutter.TextDirection.RTL) {
bannerBox.x1 = 0; bannerBox.x1 = 0;
bannerBox.x2 = titleBox.x1 - this._spacing; bannerBox.x2 = titleBox.x1 - this._spacing;
@@ -937,7 +856,7 @@ const Notification = new Lang.Class({
if (this._canExpandContent()) { if (this._canExpandContent()) {
this._addBannerBody(); this._addBannerBody();
this._table.add_style_class_name('multi-line-notification'); this._table.add_style_class_name('multi-line-notification');
this.updated(); this._updated();
} }
return false; return false;
})); }));
@@ -948,7 +867,7 @@ const Notification = new Lang.Class({
(!this._titleFitsInBannerMode && !this._table.has_style_class_name('multi-line-notification')); (!this._titleFitsInBannerMode && !this._table.has_style_class_name('multi-line-notification'));
}, },
updated: function() { _updated: function() {
if (this.expanded) if (this.expanded)
this.expand(false); this.expand(false);
}, },
@@ -1034,20 +953,22 @@ const Notification = new Lang.Class({
}); });
Signals.addSignalMethods(Notification.prototype); Signals.addSignalMethods(Notification.prototype);
const SourceActor = new Lang.Class({ const Source = new Lang.Class({
Name: 'SourceActor', Name: 'MessageTraySource',
_init: function(source, size) { ICON_SIZE: 24,
this._source = source;
this._size = size; _init: function(title) {
this.title = title;
this.actor = new Shell.GenericContainer(); this.actor = new Shell.GenericContainer();
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); 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('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this.actor.connect('allocate', Lang.bind(this, this._allocate)); this.actor.connect('allocate', Lang.bind(this, this._allocate));
this.actor.connect('destroy', Lang.bind(this, function() { this.actor.connect('destroy', Lang.bind(this,
this._actorDestroyed = true; function() {
})); this._actorDestroyed = true;
}));
this._actorDestroyed = false; this._actorDestroyed = false;
this._counterLabel = new St.Label(); this._counterLabel = new St.Label();
@@ -1055,24 +976,19 @@ const SourceActor = new Lang.Class({
child: this._counterLabel }); child: this._counterLabel });
this._counterBin.hide(); this._counterBin.hide();
this._iconBin = new St.Bin({ width: size, this._iconBin = new St.Bin({ width: this.ICON_SIZE,
height: size, height: this.ICON_SIZE,
x_fill: true, x_fill: true,
y_fill: true }); y_fill: true });
this.actor.add_actor(this._iconBin); this.actor.add_actor(this._iconBin);
this.actor.add_actor(this._counterBin); this.actor.add_actor(this._counterBin);
this._source.connect('count-changed', Lang.bind(this, this._updateCount)); this.isTransient = false;
this._updateCount(); this.isChat = false;
this.isMuted = false;
this._source.connect('icon-updated', Lang.bind(this, this._updateIcon)); this.notifications = [];
this._updateIcon();
},
setIcon: function(icon) {
this._iconBin.child = icon;
this._iconSet = true;
}, },
_getPreferredWidth: function (actor, forHeight, alloc) { _getPreferredWidth: function (actor, forHeight, alloc) {
@@ -1110,51 +1026,15 @@ const SourceActor = new Lang.Class({
this._counterBin.allocate(childBox, flags); this._counterBin.allocate(childBox, flags);
}, },
_updateIcon: function() {
if (this._actorDestroyed)
return;
if (!this._iconSet)
this._iconBin.child = this._source.createIcon(this._size);
},
_updateCount: function() {
if (this._actorDestroyed)
return;
this._counterBin.visible = this._source.countVisible;
this._counterLabel.set_text(this._source.count.toString());
}
});
const Source = new Lang.Class({
Name: 'MessageTraySource',
ICON_SIZE: 24,
_init: function(title, iconName, iconType) {
this.title = title;
this.iconName = iconName;
this.iconType = iconType;
this.count = 0;
this.isTransient = false;
this.isChat = false;
this.isMuted = false;
this.notifications = [];
this.mainIcon = new SourceActor(this, this.ICON_SIZE);
},
_setCount: function(count, visible) { _setCount: function(count, visible) {
if (isNaN(parseInt(count))) if (isNaN(parseInt(count)))
throw new Error("Invalid notification count: " + count); throw new Error("Invalid notification count: " + count);
this.count = count; if (this._actorDestroyed)
this.countVisible = visible; return;
this.emit('count-changed');
this._counterBin.visible = visible;
this._counterLabel.set_text(count.toString());
}, },
_updateCount: function() { _updateCount: function() {
@@ -1178,19 +1058,17 @@ const Source = new Lang.Class({
this.emit('muted-changed'); this.emit('muted-changed');
}, },
// Called to create a new icon actor. // Called to create a new icon actor (of size this.ICON_SIZE).
// Provides a sane default implementation, override if you need // Must be overridden by the subclass if you do not pass icons
// something more fancy. // explicitly to the Notification() constructor.
createIcon: function(size) { createNotificationIcon: function() {
return new St.Icon({ icon_name: this.iconName, throw new Error('no implementation of createNotificationIcon in ' + this);
icon_type: this.iconType,
icon_size: size });
}, },
// Unlike createIcon, this always returns the same actor; // Unlike createNotificationIcon, this always returns the same actor;
// there is only one summary icon actor for a Source. // there is only one summary icon actor for a Source.
getSummaryIcon: function() { getSummaryIcon: function() {
return this.mainIcon.actor; return this.actor;
}, },
pushNotification: function(notification) { pushNotification: function(notification) {
@@ -1234,18 +1112,17 @@ const Source = new Lang.Class({
return false; return false;
}, },
iconUpdated: function() {
this.emit('icon-updated');
},
//// Protected methods //// //// Protected methods ////
// The subclass must call this at least once to set the summary icon.
_setSummaryIcon: function(icon) { _setSummaryIcon: function(icon) {
this.mainIcon.setIcon(icon); if (this._iconBin.child)
this.iconUpdated(); this._iconBin.child.destroy();
this._iconBin.child = icon;
}, },
// Default implementation is to do nothing, but subclasses can override
open: function(notification) { open: function(notification) {
this.emit('opened', notification);
}, },
destroyNonResidentNotifications: function() { destroyNonResidentNotifications: function() {
@@ -1259,10 +1136,6 @@ const Source = new Lang.Class({
// Default implementation is to destroy this source, but subclasses can override // Default implementation is to destroy this source, but subclasses can override
_lastNotificationRemoved: function() { _lastNotificationRemoved: function() {
this.destroy(); this.destroy();
},
hasResidentNotification: function() {
return this.notifications.some(function(n) { return n.resident; });
} }
}); });
Signals.addSignalMethods(Source.prototype); Signals.addSignalMethods(Source.prototype);
@@ -1301,12 +1174,12 @@ const SummaryItem = new Lang.Class({
this._sourceBox.add(this._sourceTitleBin, { expand: true, y_fill: false }); this._sourceBox.add(this._sourceTitleBin, { expand: true, y_fill: false });
this.actor.child = this._sourceBox; this.actor.child = this._sourceBox;
this.notificationStackView = new St.ScrollView({ style_class: source.isChat ? '' : 'summary-notification-stack-scrollview', this.notificationStackView = new St.ScrollView({ name: source.isChat ? '' : 'summary-notification-stack-scrollview',
vscrollbar_policy: source.isChat ? Gtk.PolicyType.NEVER : Gtk.PolicyType.AUTOMATIC, vscrollbar_policy: source.isChat ? Gtk.PolicyType.NEVER : Gtk.PolicyType.AUTOMATIC,
hscrollbar_policy: Gtk.PolicyType.NEVER }); hscrollbar_policy: Gtk.PolicyType.NEVER,
this.notificationStackView.add_style_class_name('vfade'); style_class: 'vfade' });
this.notificationStack = new St.BoxLayout({ style_class: 'summary-notification-stack', this.notificationStack = new St.BoxLayout({ name: 'summary-notification-stack',
vertical: true }); vertical: true });
this.notificationStackView.add_actor(this.notificationStack); this.notificationStackView.add_actor(this.notificationStack);
this._stackedNotifications = []; this._stackedNotifications = [];
@@ -1379,7 +1252,7 @@ const SummaryItem = new Lang.Class({
}, },
prepareNotificationStackForShowing: function() { prepareNotificationStackForShowing: function() {
if (this.notificationStack.get_n_children() > 0) if (this.notificationStack.get_children().length > 0)
return; return;
for (let i = 0; i < this.source.notifications.length; i++) { for (let i = 0; i < this.source.notifications.length; i++) {
@@ -1388,6 +1261,7 @@ const SummaryItem = new Lang.Class({
}, },
doneShowingNotificationStack: function() { doneShowingNotificationStack: function() {
let notificationActors = this.notificationStack.get_children();
for (let i = 0; i < this._stackedNotifications.length; i++) { for (let i = 0; i < this._stackedNotifications.length; i++) {
let stackedNotification = this._stackedNotifications[i]; let stackedNotification = this._stackedNotifications[i];
let notification = stackedNotification.notification; let notification = stackedNotification.notification;
@@ -1417,7 +1291,7 @@ const SummaryItem = new Lang.Class({
this._stackedNotifications.push(stackedNotification); this._stackedNotifications.push(stackedNotification);
if (!this.source.isChat) if (!this.source.isChat)
notification.enableScrolling(false); notification.enableScrolling(false);
if (this.notificationStack.get_n_children() > 0) if (this.notificationStack.get_children().length > 0)
notification.setIconVisible(false); notification.setIconVisible(false);
this.notificationStack.add(notification.actor); this.notificationStack.add(notification.actor);
notification.expand(false); notification.expand(false);
@@ -1504,7 +1378,7 @@ const MessageTray = new Lang.Class({
track_hover: true }); track_hover: true });
this._summaryBoxPointer.actor.style_class = 'summary-boxpointer'; this._summaryBoxPointer.actor.style_class = 'summary-boxpointer';
this._summaryBoxPointer.actor.hide(); this._summaryBoxPointer.actor.hide();
Main.layoutManager.addChrome(this._summaryBoxPointer.actor); Main.layoutManager.addChrome(this._summaryBoxPointer.actor, { visibleInFullscreen: true });
this._summaryBoxPointerItem = null; this._summaryBoxPointerItem = null;
this._summaryBoxPointerContentUpdatedId = 0; this._summaryBoxPointerContentUpdatedId = 0;
@@ -1563,7 +1437,6 @@ const MessageTray = new Lang.Class({
this._overviewVisible = Main.overview.visible; this._overviewVisible = Main.overview.visible;
this._notificationRemoved = false; this._notificationRemoved = false;
this._reNotifyAfterHideNotification = null; this._reNotifyAfterHideNotification = null;
this._inFullscreen = false;
this._corner = new Clutter.Rectangle({ width: 1, this._corner = new Clutter.Rectangle({ width: 1,
height: 1, height: 1,
@@ -1574,12 +1447,11 @@ const MessageTray = new Lang.Class({
Main.layoutManager.trackChrome(this._corner); Main.layoutManager.trackChrome(this._corner);
Main.layoutManager.trayBox.add_actor(this.actor); Main.layoutManager.trayBox.add_actor(this.actor);
this.actor.y = 0; this.actor.y = this.actor.height;
Main.layoutManager.trackChrome(this.actor); Main.layoutManager.trackChrome(this.actor);
Main.layoutManager.trackChrome(this._notificationBin); Main.layoutManager.trackChrome(this._notificationBin);
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._setSizePosition)); Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._setSizePosition));
Main.layoutManager.connect('primary-fullscreen-changed', Lang.bind(this, this._onFullscreenChanged));
this._setSizePosition(); this._setSizePosition();
@@ -1604,9 +1476,6 @@ const MessageTray = new Lang.Class({
} }
})); }));
this._isScreenLocked = false;
Main.screenShield.connect('lock-status-changed', Lang.bind(this, this._onScreenLockStatusChanged));
this._summaryItems = []; this._summaryItems = [];
// We keep a list of new summary items that were added to the summary since the last // We keep a list of new summary items that were added to the summary since the last
// time it was shown to the user. We automatically show the summary to the user if there // time it was shown to the user. We automatically show the summary to the user if there
@@ -1713,12 +1582,6 @@ const MessageTray = new Lang.Class({
// *first* and not show the summary item until after it hides. // *first* and not show the summary item until after it hides.
// So postpone calling _updateState() a tiny bit. // So postpone calling _updateState() a tiny bit.
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() { this._updateState(); return false; })); Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() { this._updateState(); return false; }));
this.emit('summary-item-added', summaryItem);
},
getSummaryItems: function() {
return this._summaryItems;
}, },
_onSourceDestroy: function(source) { _onSourceDestroy: function(source) {
@@ -1793,11 +1656,6 @@ const MessageTray = new Lang.Class({
this._notificationQueue.splice(index, 1); this._notificationQueue.splice(index, 1);
}, },
_onScreenLockStatusChanged: function(screenShield, locked) {
this._isScreenLocked = locked;
this._updateState();
},
_lock: function() { _lock: function() {
this._locked = true; this._locked = true;
}, },
@@ -1996,7 +1854,7 @@ const MessageTray = new Lang.Class({
_onTrayHoverChanged: function() { _onTrayHoverChanged: function() {
if (this.actor.hover) { if (this.actor.hover) {
// Don't do anything if the one pixel area at the bottom is hovered over while the tray is hidden. // Don't do anything if the one pixel area at the bottom is hovered over while the tray is hidden.
if (this._trayState == State.HIDDEN && this._notificationState == State.HIDDEN) if (this._trayState == State.HIDDEN)
return; return;
// Don't do anything if this._useLongerTrayLeftTimeout is true, meaning the notification originally // Don't do anything if this._useLongerTrayLeftTimeout is true, meaning the notification originally
@@ -2067,11 +1925,6 @@ const MessageTray = new Lang.Class({
this._updateState(); this._updateState();
}, },
_onFullscreenChanged: function(obj, state) {
this._inFullscreen = state;
this._updateState();
},
_onStatusChanged: function(status) { _onStatusChanged: function(status) {
if (status == GnomeSession.PresenceStatus.BUSY) { if (status == GnomeSession.PresenceStatus.BUSY) {
// remove notification and allow the summary to be closed now // remove notification and allow the summary to be closed now
@@ -2128,36 +1981,18 @@ const MessageTray = new Lang.Class({
// at the present time. // at the present time.
_updateState: function() { _updateState: function() {
// Notifications // Notifications
let notificationQueue = this._notificationQueue.filter(Lang.bind(this, function(notification) { let notificationUrgent = this._notificationQueue.length > 0 && this._notificationQueue[0].urgency == Urgency.CRITICAL;
if (this._isScreenLocked) let notificationsPending = this._notificationQueue.length > 0 && (!this._busy || notificationUrgent);
return notification.showWhenLocked;
else
return true;
}));
let notificationUrgent = notificationQueue.length > 0 && notificationQueue[0].urgency == Urgency.CRITICAL;
// notificationsLimited is false when the screen is locked, because they go through
// different filtering, and we want to show non urgent messages at times
let notificationsLimited = (this._busy || this._inFullscreen) && !this._isScreenLocked;
let notificationsPending = notificationQueue.length > 0 && (!notificationsLimited || notificationUrgent);
let nextNotification = notificationQueue.length > 0 ? notificationQueue[0] : null;
let notificationPinned = this._pointerInTray && !this._pointerInSummary && !this._notificationRemoved; let notificationPinned = this._pointerInTray && !this._pointerInSummary && !this._notificationRemoved;
let notificationExpanded = this._notificationBin.y < - this.actor.height; let notificationExpanded = this._notificationBin.y < 0;
let notificationExpired = this._notificationTimeoutId == 0 && let notificationExpired = (this._notificationTimeoutId == 0 && !(this._notification && this._notification.urgency == Urgency.CRITICAL) && !this._pointerInTray && !this._locked && !(this._pointerInKeyboard && notificationExpanded)) || this._notificationRemoved;
!(this._notification && this._notification.urgency == Urgency.CRITICAL) &&
!this._pointerInTray &&
!this._locked &&
!(this._pointerInKeyboard && notificationExpanded);
let notificationLockedOut = this._isScreenLocked && (this._notification && !this._notification.showWhenLocked);
let notificationMustClose = this._notificationRemoved || notificationLockedOut || notificationExpired;
let canShowNotification = notificationsPending && this._summaryState == State.HIDDEN; let canShowNotification = notificationsPending && this._summaryState == State.HIDDEN;
if (this._notificationState == State.HIDDEN) { if (this._notificationState == State.HIDDEN) {
if (canShowNotification) { if (canShowNotification)
this._showNotification(nextNotification); this._showNotification();
this._notificationQueue.splice(this._notificationQueue.indexOf(nextNotification), 1);
}
} else if (this._notificationState == State.SHOWN) { } else if (this._notificationState == State.SHOWN) {
if (notificationMustClose) if (notificationExpired)
this._hideNotification(); this._hideNotification();
else if (notificationPinned && !notificationExpanded) else if (notificationPinned && !notificationExpanded)
this._expandNotification(false); this._expandNotification(false);
@@ -2178,12 +2013,12 @@ const MessageTray = new Lang.Class({
let summaryOptionalInOverview = this._overviewVisible && !this._locked && !summaryHovered; let summaryOptionalInOverview = this._overviewVisible && !this._locked && !summaryHovered;
let mustHideSummary = (notificationsPending && (notificationUrgent || summaryOptionalInOverview)) let mustHideSummary = (notificationsPending && (notificationUrgent || summaryOptionalInOverview))
|| notificationsVisible || this._isScreenLocked; || notificationsVisible;
if (this._summaryState == State.HIDDEN && !mustHideSummary) { if (this._summaryState == State.HIDDEN && !mustHideSummary) {
if (summarySummoned) { if (summarySummoned) {
this._showSummary(0); this._showSummary(0);
} else if (notificationsDone && !this._busy && !this._inFullscreen) { } else if (notificationsDone && !this._busy) {
if (this._backFromAway && this._unseenNotifications.length > 0) if (this._backFromAway && this._unseenNotifications.length > 0)
this._showSummary(LONGER_SUMMARY_TIMEOUT); this._showSummary(LONGER_SUMMARY_TIMEOUT);
else if (this._newSummaryItems.length > 0) else if (this._newSummaryItems.length > 0)
@@ -2229,7 +2064,8 @@ const MessageTray = new Lang.Class({
// Tray itself // Tray itself
let trayIsVisible = (this._trayState == State.SHOWING || let trayIsVisible = (this._trayState == State.SHOWING ||
this._trayState == State.SHOWN); this._trayState == State.SHOWN);
let trayShouldBeVisible = (this._summaryState == State.SHOWING || let trayShouldBeVisible = (!notificationsDone ||
this._summaryState == State.SHOWING ||
this._summaryState == State.SHOWN); this._summaryState == State.SHOWN);
if (!trayIsVisible && trayShouldBeVisible) if (!trayIsVisible && trayShouldBeVisible)
this._showTray(); this._showTray();
@@ -2269,7 +2105,7 @@ const MessageTray = new Lang.Class({
_hideTray: function() { _hideTray: function() {
this._tween(this.actor, '_trayState', State.HIDDEN, this._tween(this.actor, '_trayState', State.HIDDEN,
{ y: 0, { y: this.actor.height,
time: ANIMATION_TIME, time: ANIMATION_TIME,
transition: 'easeOutQuad' transition: 'easeOutQuad'
}); });
@@ -2295,8 +2131,8 @@ const MessageTray = new Lang.Class({
} }
}, },
_showNotification: function(notification) { _showNotification: function() {
this._notification = notification; this._notification = this._notificationQueue.shift();
this._unseenNotifications.push(this._notification); this._unseenNotifications.push(this._notification);
if (this._idleMonitorWatchId == 0) if (this._idleMonitorWatchId == 0)
this._idleMonitorWatchId = this.idleMonitor.add_watch(1000, this._idleMonitorWatchId = this.idleMonitor.add_watch(1000,
@@ -2306,7 +2142,7 @@ const MessageTray = new Lang.Class({
this._notificationBin.child = this._notification.actor; this._notificationBin.child = this._notification.actor;
this._notificationBin.opacity = 0; this._notificationBin.opacity = 0;
this._notificationBin.y = 0; this._notificationBin.y = this.actor.height;
this._notificationBin.show(); this._notificationBin.show();
this._updateShowingNotification(); this._updateShowingNotification();
@@ -2341,8 +2177,7 @@ const MessageTray = new Lang.Class({
// We tween all notifications to full opacity. This ensures that both new notifications and // We tween all notifications to full opacity. This ensures that both new notifications and
// notifications that might have been in the process of hiding get full opacity. // notifications that might have been in the process of hiding get full opacity.
// //
// We tween any notification showing in the banner mode to banner height // We tween any notification showing in the banner mode to banner height (this._notificationBin.y = 0).
// (this._notificationBin.y = -this.actor.height).
// This ensures that both new notifications and notifications in the banner mode that might // This ensures that both new notifications and notifications in the banner mode that might
// have been in the process of hiding are shown with the banner height. // have been in the process of hiding are shown with the banner height.
// //
@@ -2359,7 +2194,7 @@ const MessageTray = new Lang.Class({
onCompleteScope: this onCompleteScope: this
}; };
if (!this._notification.expanded) if (!this._notification.expanded)
tweenParams.y = - this.actor.height; tweenParams.y = 0;
this._tween(this._notificationBin, '_notificationState', State.SHOWN, tweenParams); this._tween(this._notificationBin, '_notificationState', State.SHOWN, tweenParams);
}, },
@@ -2448,7 +2283,7 @@ const MessageTray = new Lang.Class({
}, },
_onNotificationExpanded: function() { _onNotificationExpanded: function() {
let expandedY = - this._notificationBin.height; let expandedY = this.actor.height - this._notificationBin.height;
// Don't animate the notification to its new position if it has shrunk: // Don't animate the notification to its new position if it has shrunk:
// there will be a very visible "gap" that breaks the illusion. // there will be a very visible "gap" that breaks the illusion.
@@ -2556,23 +2391,24 @@ const MessageTray = new Lang.Class({
this._summaryBoxPointerState = State.SHOWING; this._summaryBoxPointerState = State.SHOWING;
this._clickedSummaryItem.actor.add_style_pseudo_class('selected'); this._clickedSummaryItem.actor.add_style_pseudo_class('selected');
this._summaryBoxPointer.show(BoxPointer.PopupAnimation.FULL, Lang.bind(this, function() { this._summaryBoxPointer.show(true, Lang.bind(this, function() {
this._summaryBoxPointerState = State.SHOWN; this._summaryBoxPointerState = State.SHOWN;
})); }));
}, },
_onSummaryBoxPointerContentUpdated: function() { _onSummaryBoxPointerContentUpdated: function() {
if (this._summaryBoxPointerItem.notificationStack.get_n_children() == 0) if (this._summaryBoxPointerItem.notificationStack.get_children().length == 0)
this._hideSummaryBoxPointer(); this._hideSummaryBoxPointer();
this._adjustSummaryBoxPointerPosition(); this._adjustSummaryBoxPointerPosition();
}, },
_adjustSummaryBoxPointerPosition: function() { _adjustSummaryBoxPointerPosition: function() {
// The position of the arrow origin should be the same as center of this._clickedSummaryItem.actor
if (!this._clickedSummaryItem) if (!this._clickedSummaryItem)
return; return;
this._summaryBoxPointer.setPosition(this._clickedSummaryItem.actor, 0); this._summaryBoxPointer.setPosition(this._clickedSummaryItem.actor, 0, 0.5);
}, },
_unsetClickedSummaryItem: function() { _unsetClickedSummaryItem: function() {
@@ -2595,7 +2431,7 @@ const MessageTray = new Lang.Class({
// We should be sure to hide the box pointer if all notifications in it are destroyed while // We should be sure to hide the box pointer if all notifications in it are destroyed while
// it is hiding, so that we don't show an an animation of an empty blob being hidden. // it is hiding, so that we don't show an an animation of an empty blob being hidden.
if (this._summaryBoxPointerState == State.HIDING && if (this._summaryBoxPointerState == State.HIDING &&
this._summaryBoxPointerItem.notificationStack.get_n_children() == 0) { this._summaryBoxPointerItem.notificationStack.get_children().length == 0) {
this._summaryBoxPointer.actor.hide(); this._summaryBoxPointer.actor.hide();
return; return;
} }
@@ -2610,7 +2446,7 @@ const MessageTray = new Lang.Class({
this._summaryBoxPointer.actor.hide(); this._summaryBoxPointer.actor.hide();
this._hideSummaryBoxPointerCompleted(); this._hideSummaryBoxPointerCompleted();
} else { } else {
this._summaryBoxPointer.hide(BoxPointer.PopupAnimation.FULL, Lang.bind(this, this._hideSummaryBoxPointerCompleted)); this._summaryBoxPointer.hide(true, Lang.bind(this, this._hideSummaryBoxPointerCompleted));
} }
}, },
@@ -2645,15 +2481,21 @@ const MessageTray = new Lang.Class({
this._updateState(); this._updateState();
} }
}); });
Signals.addSignalMethods(MessageTray.prototype);
const SystemNotificationSource = new Lang.Class({ const SystemNotificationSource = new Lang.Class({
Name: 'SystemNotificationSource', Name: 'SystemNotificationSource',
Extends: Source, Extends: Source,
_init: function() { _init: function() {
this.parent(_("System Information"), 'dialog-information', St.IconType.SYMBOLIC); this.parent(_("System Information"));
this.setTransient(true);
this._setSummaryIcon(this.createNotificationIcon());
},
createNotificationIcon: function() {
return new St.Icon({ icon_name: 'dialog-information',
icon_type: St.IconType.SYMBOLIC,
icon_size: this.ICON_SIZE });
}, },
open: function() { open: function() {

View File

@@ -35,9 +35,7 @@ const ModalDialog = new Lang.Class({
_init: function(params) { _init: function(params) {
params = Params.parse(params, { shellReactive: false, params = Params.parse(params, { shellReactive: false,
styleClass: null, styleClass: null });
parentActor: Main.uiGroup
});
this.state = State.CLOSED; this.state = State.CLOSED;
this._hasModal = false; this._hasModal = false;
@@ -47,7 +45,7 @@ const ModalDialog = new Lang.Class({
x: 0, x: 0,
y: 0, y: 0,
accessible_role: Atk.Role.DIALOG }); 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, let constraint = new Clutter.BindConstraint({ source: global.stage,
coordinate: Clutter.BindCoordinate.ALL }); coordinate: Clutter.BindCoordinate.ALL });
@@ -56,7 +54,7 @@ const ModalDialog = new Lang.Class({
this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy)); this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy));
this._actionKeys = {}; 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._backgroundBin = new St.Bin();
this._group.add_actor(this._backgroundBin); this._group.add_actor(this._backgroundBin);
@@ -108,10 +106,6 @@ const ModalDialog = new Lang.Class({
this._group.destroy(); this._group.destroy();
}, },
setActionKey: function(key, action) {
this._actionKeys[key] = action;
},
setButtons: function(buttons) { setButtons: function(buttons) {
let hadChildren = this._buttonLayout.get_children() > 0; let hadChildren = this._buttonLayout.get_children() > 0;
@@ -125,17 +119,11 @@ const ModalDialog = new Lang.Class({
let label = buttonInfo['label']; let label = buttonInfo['label'];
let action = buttonInfo['action']; let action = buttonInfo['action'];
let key = buttonInfo['key']; let key = buttonInfo['key'];
let isDefault = buttonInfo['default'];
if (isDefault && !key)
key = Clutter.KEY_Return;
buttonInfo.button = new St.Button({ style_class: 'modal-dialog-button', buttonInfo.button = new St.Button({ style_class: 'modal-dialog-button',
reactive: true, reactive: true,
can_focus: true, can_focus: true,
label: label }); label: label });
if (isDefault)
buttonInfo.button.add_style_pseudo_class('default');
let x_alignment; let x_alignment;
if (buttons.length == 1) if (buttons.length == 1)
@@ -179,16 +167,12 @@ const ModalDialog = new Lang.Class({
}, },
_onKeyReleaseEvent: function(object, event) { _onKeyPressEvent: function(object, keyPressEvent) {
let symbol = event.get_key_symbol(); let symbol = keyPressEvent.get_key_symbol();
let action = this._actionKeys[symbol]; let action = this._actionKeys[symbol];
if (action) { if (action)
action(); action();
return true;
}
return false;
}, },
_onGroupDestroy: function() { _onGroupDestroy: function() {
@@ -196,7 +180,7 @@ const ModalDialog = new Lang.Class({
}, },
_fadeOpen: function() { _fadeOpen: function() {
let monitor = Main.layoutManager.currentMonitor; let monitor = Main.layoutManager.focusMonitor;
this._backgroundBin.set_position(monitor.x, monitor.y); this._backgroundBin.set_position(monitor.x, monitor.y);
this._backgroundBin.set_size(monitor.width, monitor.height); this._backgroundBin.set_size(monitor.width, monitor.height);

View File

@@ -147,7 +147,7 @@ const NetworkSecretDialog = new Lang.Class({
this._okButton = { label: _("Connect"), this._okButton = { label: _("Connect"),
action: Lang.bind(this, this._onOk), action: Lang.bind(this, this._onOk),
default: true key: Clutter.KEY_Return,
}; };
this.setButtons([{ label: _("Cancel"), this.setButtons([{ label: _("Cancel"),
@@ -165,6 +165,11 @@ const NetworkSecretDialog = new Lang.Class({
} }
this._okButton.button.reactive = valid; 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() { _onOk: function() {
@@ -678,10 +683,7 @@ const NetworkAgent = new Lang.Class({
try { try {
externalUIMode = keyfile.get_boolean('GNOME', 'supports-external-ui-mode'); externalUIMode = keyfile.get_boolean('GNOME', 'supports-external-ui-mode');
} catch(e) { } // ignore errors if key does not exist } catch(e) { } // ignore errors if key does not exist
let path = binary; let path = GLib.build_filenamev([Config.LIBEXECDIR, binary]);
if (!GLib.path_is_absolute(path)) {
path = GLib.build_filenamev([Config.LIBEXECDIR, path]);
}
if (GLib.file_test(path, GLib.FileTest.IS_EXECUTABLE)) if (GLib.file_test(path, GLib.FileTest.IS_EXECUTABLE))
this._vpnBinaries[service] = { fileName: path, externalUIMode: externalUIMode }; this._vpnBinaries[service] = { fileName: path, externalUIMode: externalUIMode };

View File

@@ -1,7 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const GdkPixbuf = imports.gi.GdkPixbuf;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const Lang = imports.lang; 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({ const NotificationDaemon = new Lang.Class({
Name: 'NotificationDaemon', Name: 'NotificationDaemon',
@@ -115,19 +99,18 @@ const NotificationDaemon = new Lang.Class({
this._notifications = {}; this._notifications = {};
this._busProxy = new Bus(); this._busProxy = new Bus();
this._trayManager = new Shell.TrayManager(); Main.statusIconDispatcher.connect('message-icon-added', Lang.bind(this, this._onTrayIconAdded));
this._trayManager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded)); Main.statusIconDispatcher.connect('message-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
this._trayManager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
Shell.WindowTracker.get_default().connect('notify::focus-app', Shell.WindowTracker.get_default().connect('notify::focus-app',
Lang.bind(this, this._onFocusAppChanged)); Lang.bind(this, this._onFocusAppChanged));
Main.overview.connect('hidden', Main.overview.connect('hidden',
Lang.bind(this, this._onFocusAppChanged)); 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 // 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 // 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 // notify_notification_set_icon_from_pixbuf() from libnotify, which in turn sets
@@ -138,18 +121,20 @@ const NotificationDaemon = new Lang.Class({
// a large image. // a large image.
if (icon) { if (icon) {
if (icon.substr(0, 7) == 'file://') 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] == '/') { 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 } 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']) { } else if (hints['image-data']) {
let [width, height, rowStride, hasAlpha, let [width, height, rowStride, hasAlpha,
bitsPerSample, nChannels, data] = hints['image-data']; bitsPerSample, nChannels, data] = hints['image-data'];
return Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha, return textureCache.load_from_raw(data, hasAlpha, width, height, rowStride, size);
bitsPerSample, width, height, rowStride);
} else if (hints['image-path']) { } 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 { } else {
let stockIcon; let stockIcon;
switch (hints.urgency) { switch (hints.urgency) {
@@ -161,7 +146,9 @@ const NotificationDaemon = new Lang.Class({
stockIcon = 'gtk-dialog-error'; stockIcon = 'gtk-dialog-error';
break; 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 [appName, replacesId, icon, summary, body, actions, hints, timeout] = params;
let id; 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 // Filter out chat, presence, calls and invitation notifications from
// Empathy, since we handle that information from telepathyClient.js // Empathy, since we handle that information from telepathyClient.js
if (appName == 'Empathy' && (hints['category'] == 'im.received' || if (appName == 'Empathy' && (hints['category'] == 'im.received' ||
hints['category'] == 'x-empathy.im.room-invitation' || hints['category'] == 'x-empathy.im.room-invitation' ||
hints['category'] == 'x-empathy.call.incoming' || 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'] == 'x-empathy.im.subscription-request' ||
hints['category'] == 'presence.online' || hints['category'] == 'presence.online' ||
hints['category'] == 'presence.offline')) { hints['category'] == 'presence.offline')) {
@@ -269,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 // 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 // 'image-data' and 'image-path' are the latest name of these hints, introduced in 1.2
@@ -354,10 +341,7 @@ const NotificationDaemon = new Lang.Class({
[ndata.id, ndata.icon, ndata.summary, ndata.body, [ndata.id, ndata.icon, ndata.summary, ndata.body,
ndata.actions, ndata.hints, ndata.notification]; ndata.actions, ndata.hints, ndata.notification];
let gicon = this._iconForNotificationData(icon, hints); let iconActor = this._iconForNotificationData(icon, hints, source.ICON_SIZE);
let iconActor = new St.Icon({ gicon: gicon,
icon_type: St.IconType.FULLCOLOR,
icon_size: source.ICON_SIZE });
if (notification == null) { if (notification == null) {
notification = new MessageTray.Notification(source, summary, body, 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 // of the 'transient' hint with hints['transient'] rather than hints.transient
notification.setTransient(hints['transient'] == true); notification.setTransient(hints['transient'] == true);
let sourceGIcon = source.useNotificationIcon ? this._iconForNotificationData(icon, hints) : null; let sourceIconActor = source.useNotificationIcon ? this._iconForNotificationData(icon, hints, source.ICON_SIZE) : null;
source.processNotification(notification, sourceGIcon); source.processNotification(notification, sourceIconActor);
}, },
CloseNotification: function(id) { CloseNotification: function(id) {
@@ -499,11 +483,7 @@ const NotificationDaemon = new Lang.Class({
}, },
_onTrayIconAdded: function(o, icon) { _onTrayIconAdded: function(o, icon) {
let wmClass = icon.wm_class.toLowerCase(); let source = this._getSource(icon.title || icon.wm_class || _("Unknown"), icon.pid, null, null, icon);
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);
}, },
_onTrayIconRemoved: function(o, icon) { _onTrayIconRemoved: function(o, icon) {
@@ -554,11 +534,11 @@ const Source = new Lang.Class({
this.destroy(); this.destroy();
}, },
processNotification: function(notification, gicon) { processNotification: function(notification, icon) {
if (gicon) if (!this.app)
this._gicon = gicon; this._setApp();
if (!this.trayIcon) if (!this.app && icon)
this.iconUpdated(); this._setSummaryIcon(icon);
let tracker = Shell.WindowTracker.get_default(); let tracker = Shell.WindowTracker.get_default();
if (notification.resident && this.app && tracker.focus_app == this.app) if (notification.resident && this.app && tracker.focus_app == this.app)
@@ -598,27 +578,11 @@ const Source = new Lang.Class({
return true; return true;
}, },
_getApp: function() {
let app;
app = Shell.WindowTracker.get_default().get_app_from_pid(this.pid);
if (app != null)
return app;
if (this.trayIcon) {
app = Shell.AppSystem.get_default().lookup_wmclass(this.trayIcon.wmclass);
if (app != null)
return app;
}
return null;
},
_setApp: function() { _setApp: function() {
if (this.app) if (this.app)
return; return;
this.app = this._getApp(); this.app = Shell.WindowTracker.get_default().get_app_from_pid(this.pid);
if (!this.app) if (!this.app)
return; return;
@@ -626,7 +590,7 @@ const Source = new Lang.Class({
// notification-based icons (ie, not a trayicon) or if it was unset before // notification-based icons (ie, not a trayicon) or if it was unset before
if (!this.trayIcon) { if (!this.trayIcon) {
this.useNotificationIcon = false; this.useNotificationIcon = false;
this.iconUpdated(); this._setSummaryIcon(this.app.create_icon_texture (this.ICON_SIZE));
} }
}, },
@@ -658,20 +622,5 @@ const Source = new Lang.Class({
} }
this.parent(); 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

@@ -11,8 +11,10 @@ const Shell = imports.gi.Shell;
const Gdk = imports.gi.Gdk; const Gdk = imports.gi.Gdk;
const AppDisplay = imports.ui.appDisplay; const AppDisplay = imports.ui.appDisplay;
const ContactDisplay = imports.ui.contactDisplay;
const Dash = imports.ui.dash; const Dash = imports.ui.dash;
const DND = imports.ui.dnd; const DND = imports.ui.dnd;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main; const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray; const MessageTray = imports.ui.messageTray;
const Panel = imports.ui.panel; const Panel = imports.ui.panel;
@@ -75,7 +77,6 @@ const ShellInfo = new Lang.Class({
let notification = null; let notification = null;
if (this._source.notifications.length == 0) { if (this._source.notifications.length == 0) {
notification = new MessageTray.Notification(this._source, text, null); notification = new MessageTray.Notification(this._source, text, null);
notification.setShowWhenLocked(true);
} else { } else {
notification = this._source.notifications[0]; notification = this._source.notifications[0];
notification.update(text, null, { clear: true }); notification.update(text, null, { clear: true });
@@ -98,8 +99,10 @@ const ShellInfo = new Lang.Class({
const Overview = new Lang.Class({ const Overview = new Lang.Class({
Name: 'Overview', Name: 'Overview',
_init : function() { _init : function(params) {
this.isDummy = !Main.sessionMode.hasOverview; params = Params.parse(params, { isDummy: false });
this.isDummy = params.isDummy;
// We only have an overview in user sessions, so // We only have an overview in user sessions, so
// create a dummy overview in other cases // create a dummy overview in other cases
@@ -207,6 +210,7 @@ const Overview = new Lang.Class({
this.addSearchProvider(new AppDisplay.AppSearchProvider()); this.addSearchProvider(new AppDisplay.AppSearchProvider());
this.addSearchProvider(new AppDisplay.SettingsSearchProvider()); this.addSearchProvider(new AppDisplay.SettingsSearchProvider());
this.addSearchProvider(new PlaceDisplay.PlaceSearchProvider()); this.addSearchProvider(new PlaceDisplay.PlaceSearchProvider());
this.addSearchProvider(new ContactDisplay.ContactSearchProvider());
// Load remote search providers provided by applications // Load remote search providers provided by applications
RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider)); RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider));

View File

@@ -4,7 +4,6 @@ const Cairo = imports.cairo;
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta; const Meta = imports.gi.Meta;
@@ -14,6 +13,7 @@ const St = imports.gi.St;
const Signals = imports.signals; const Signals = imports.signals;
const Atk = imports.gi.Atk; const Atk = imports.gi.Atk;
const Config = imports.misc.config;
const CtrlAltTab = imports.ui.ctrlAltTab; const CtrlAltTab = imports.ui.ctrlAltTab;
const DND = imports.ui.dnd; const DND = imports.ui.dnd;
const Layout = imports.ui.layout; const Layout = imports.ui.layout;
@@ -31,6 +31,33 @@ const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
const ANIMATED_ICON_UPDATE_TIMEOUT = 100; const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
const SPINNER_ANIMATION_TIME = 0.2; const SPINNER_ANIMATION_TIME = 0.2;
const STANDARD_STATUS_AREA_ORDER = ['a11y', 'keyboard', 'volume', 'bluetooth', 'network', 'battery', 'userMenu'];
const STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION = {
'a11y': imports.ui.status.accessibility.ATIndicator,
'volume': imports.ui.status.volume.Indicator,
'battery': imports.ui.status.power.Indicator,
'keyboard': imports.ui.status.keyboard.XKBIndicator,
'userMenu': imports.ui.userMenu.UserMenuButton
};
if (Config.HAVE_BLUETOOTH)
STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION['bluetooth'] = imports.ui.status.bluetooth.Indicator;
try {
STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION['network'] = imports.ui.status.network.NMApplet;
} catch(e) {
log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
}
const GDM_STATUS_AREA_ORDER = ['a11y', 'display', 'keyboard', 'volume', 'battery', 'powerMenu'];
const GDM_STATUS_AREA_SHELL_IMPLEMENTATION = {
'a11y': imports.ui.status.accessibility.ATIndicator,
'volume': imports.ui.status.volume.Indicator,
'battery': imports.ui.status.power.Indicator,
'keyboard': imports.ui.status.keyboard.XKBIndicator,
'powerMenu': imports.gdm.powerMenu.PowerMenuButton
};
// To make sure the panel corners blend nicely with the panel, // To make sure the panel corners blend nicely with the panel,
// we draw background and borders the same way, e.g. drawing // we draw background and borders the same way, e.g. drawing
// them as filled shapes from the outside inwards instead of // them as filled shapes from the outside inwards instead of
@@ -346,7 +373,6 @@ const AppMenuButton = new Lang.Class({
return; return;
this._stop = true; this._stop = true;
this.actor.reactive = true;
Tweener.addTween(this._spinner.actor, Tweener.addTween(this._spinner.actor,
{ opacity: 0, { opacity: 0,
time: SPINNER_ANIMATION_TIME, time: SPINNER_ANIMATION_TIME,
@@ -361,7 +387,6 @@ const AppMenuButton = new Lang.Class({
startAnimation: function() { startAnimation: function() {
this._stop = false; this._stop = false;
this.actor.reactive = false;
this._spinner.actor.show(); this._spinner.actor.show();
}, },
@@ -467,13 +492,6 @@ const AppMenuButton = new Lang.Class({
this._sync(); this._sync();
}, },
setLockedState: function(locked) {
if (locked)
this.hide();
else
this._sync();
},
_sync: function() { _sync: function() {
let tracker = Shell.WindowTracker.get_default(); let tracker = Shell.WindowTracker.get_default();
let focusedApp = tracker.focus_app; let focusedApp = tracker.focus_app;
@@ -760,11 +778,10 @@ const PanelCorner = new Lang.Class({
return null; return null;
// Start at the back and work backward // Start at the back and work backward
let index; let index = children.length - 1;
for (index = children.length - 1; index >= 0; index--) { while (!children[index].visible && index >= 0)
if (children[index].visible) index--;
break;
}
if (index < 0) if (index < 0)
return null; return null;
@@ -785,11 +802,10 @@ const PanelCorner = new Lang.Class({
return null; return null;
// Start at the front and work forward // Start at the front and work forward
let index; let index = 0;
for (index = 0; index < children.length; index++) { while (!children[index].visible && index < children.length)
if (children[index].visible) index++;
break;
}
if (index == children.length) if (index == children.length)
return null; return null;
@@ -917,8 +933,6 @@ const Panel = new Lang.Class({
this.actor.remove_style_class_name('in-overview'); this.actor.remove_style_class_name('in-overview');
})); }));
Main.screenShield.connect('lock-status-changed', Lang.bind(this, this._onLockStateChanged));
this._menus = new PopupMenu.PopupMenuManager(this); this._menus = new PopupMenu.PopupMenuManager(this);
this._leftBox = new St.BoxLayout({ name: 'panelLeft' }); this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
@@ -947,7 +961,7 @@ const Panel = new Lang.Class({
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress)); this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
/* Button on the left side of the panel. */ /* Button on the left side of the panel. */
if (Main.sessionMode.hasOverview) { if (global.session_type == Shell.SessionType.USER) {
this._activitiesButton = new ActivitiesButton(); this._activitiesButton = new ActivitiesButton();
this._activities = this._activitiesButton.actor; this._activities = this._activitiesButton.actor;
this._leftBox.add(this._activities); this._leftBox.add(this._activities);
@@ -955,18 +969,31 @@ const Panel = new Lang.Class({
// The activities button has a pretend menu, so as to integrate // The activities button has a pretend menu, so as to integrate
// more cleanly with the rest of the panel // more cleanly with the rest of the panel
this._menus.addMenu(this._activitiesButton.menu); this._menus.addMenu(this._activitiesButton.menu);
}
if (Main.sessionMode.hasAppMenu) {
this._appMenu = new AppMenuButton(this._menus); this._appMenu = new AppMenuButton(this._menus);
this._leftBox.add(this._appMenu.actor); this._leftBox.add(this._appMenu.actor);
} }
/* center */ /* center */
this._dateMenu = new DateMenu.DateMenuButton(); 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._centerBox.add(this._dateMenu.actor, { y_fill: true });
this._menus.addMenu(this._dateMenu.menu); 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.layoutManager.panelBox.add(this.actor);
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'start-here', Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'start-here',
{ sortGroup: CtrlAltTab.SortGroup.TOP }); { sortGroup: CtrlAltTab.SortGroup.TOP });
@@ -1085,19 +1112,10 @@ const Panel = new Lang.Class({
return true; return true;
}, },
openAppMenu: function() {
let menu = this._appMenu.menu;
if (!this._appMenu.actor.reactive || menu.isOpen)
return;
menu.open();
menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
},
startStatusArea: function() { startStatusArea: function() {
for (let i = 0; i < Main.sessionMode.statusArea.order.length; i++) { for (let i = 0; i < this._status_area_order.length; i++) {
let role = Main.sessionMode.statusArea.order[i]; let role = this._status_area_order[i];
let constructor = Main.sessionMode.statusArea.implementation[role]; let constructor = this._status_area_shell_implementation[role];
if (!constructor) { if (!constructor) {
// This icon is not implemented (this is a bug) // This icon is not implemented (this is a bug)
continue; continue;
@@ -1135,27 +1153,35 @@ const Panel = new Lang.Class({
if (!position) if (!position)
position = 0; position = 0;
this._insertStatusItem(indicator.actor, position); this._insertStatusItem(indicator.actor, position);
if (indicator.menu) this._menus.addMenu(indicator.menu);
this._menus.addMenu(indicator.menu);
this._statusArea[role] = indicator; this._statusArea[role] = indicator;
let destroyId = indicator.connect('destroy', Lang.bind(this, function(emitter) { let destroyId = indicator.connect('destroy', Lang.bind(this, function(emitter) {
delete this._statusArea[role]; this._statusArea[role] = null;
emitter.disconnect(destroyId); emitter.disconnect(destroyId);
})); }));
return indicator; return indicator;
}, },
_onLockStateChanged: function(shield, locked) { _onTrayIconAdded: function(o, icon, role) {
if (this._activitiesButton) if (this._status_area_shell_implementation[role]) {
this._activitiesButton.setLockedState(locked); // This icon is legacy, and replaced by a Shell version
if (this._appMenu) // Hide it
this._appMenu.setLockedState(locked); return;
if (this._dateMenu) }
this._dateMenu.setLockedState(locked);
for (let id in this._statusArea) icon.height = PANEL_ICON_SIZE;
this._statusArea[id].setLockedState(locked); 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

@@ -145,13 +145,6 @@ const Button = new Lang.Class({
} }
}, },
setLockedState: function(locked) {
// default behaviour is to hide completely
if (locked)
this.menu.close();
this.actor.visible = !locked;
},
_onButtonPress: function(actor, event) { _onButtonPress: function(actor, event) {
if (!this.menu) if (!this.menu)
return; return;

View File

@@ -189,7 +189,7 @@ const PlacesManager = new Lang.Class({
this._volumeMonitor.connect('drive-changed', Lang.bind(this, this._updateDevices)); this._volumeMonitor.connect('drive-changed', Lang.bind(this, this._updateDevices));
this._updateDevices(); this._updateDevices();
this._bookmarksPath = GLib.build_filenamev([GLib.get_user_config_dir(), 'gtk-3.0', 'bookmarks']); this._bookmarksPath = GLib.build_filenamev([GLib.get_home_dir(), '.gtk-bookmarks']);
this._bookmarksFile = Gio.file_new_for_path(this._bookmarksPath); this._bookmarksFile = Gio.file_new_for_path(this._bookmarksPath);
this._monitor = this._bookmarksFile.monitor_file(Gio.FileMonitorFlags.NONE, null); this._monitor = this._bookmarksFile.monitor_file(Gio.FileMonitorFlags.NONE, null);
this._bookmarkTimeoutId = 0; this._bookmarkTimeoutId = 0;
@@ -365,13 +365,12 @@ const PlaceSearchProvider = new Lang.Class({
_init: function() { _init: function() {
this.parent(_("PLACES & DEVICES")); this.parent(_("PLACES & DEVICES"));
this.placesManager = new PlacesManager();
}, },
getResultMetas: function(resultIds, callback) { getResultMetas: function(resultIds) {
let metas = []; let metas = [];
for (let i = 0; i < resultIds.length; i++) { for (let i = 0; i < resultIds.length; i++) {
let placeInfo = this.placesManager.lookupPlaceById(resultIds[i]); let placeInfo = Main.placesManager.lookupPlaceById(resultIds[i]);
if (!placeInfo) if (!placeInfo)
metas.push(null); metas.push(null);
else else
@@ -382,22 +381,24 @@ const PlaceSearchProvider = new Lang.Class({
} }
}); });
} }
callback(metas); return metas;
}, },
activateResult: function(id, params) { activateResult: function(id, params) {
let placeInfo = this.placesManager.lookupPlaceById(id); let placeInfo = Main.placesManager.lookupPlaceById(id);
placeInfo.launch(params); placeInfo.launch(params);
}, },
_compareResultMeta: function (idA, idB) { _compareResultMeta: function (idA, idB) {
let infoA = this.placesManager.lookupPlaceById(idA); let infoA = Main.placesManager.lookupPlaceById(idA);
let infoB = this.placesManager.lookupPlaceById(idB); let infoB = Main.placesManager.lookupPlaceById(idB);
return infoA.name.localeCompare(infoB.name); return infoA.name.localeCompare(infoB.name);
}, },
_searchPlaces: function(places, terms) { _searchPlaces: function(places, terms) {
let multiplePrefixResults = [];
let prefixResults = []; let prefixResults = [];
let multipleSubstringResults = [];
let substringResults = []; let substringResults = [];
terms = terms.map(String.toLowerCase); terms = terms.map(String.toLowerCase);
@@ -405,26 +406,29 @@ const PlaceSearchProvider = new Lang.Class({
for (let i = 0; i < places.length; i++) { for (let i = 0; i < places.length; i++) {
let place = places[i]; let place = places[i];
let mtype = place.matchTerms(terms); let mtype = place.matchTerms(terms);
if (mtype == Search.MatchType.PREFIX) if (mtype == Search.MatchType.MULTIPLE_PREFIX)
multiplePrefixResults.push(place.id);
else if (mtype == Search.MatchType.PREFIX)
prefixResults.push(place.id); prefixResults.push(place.id);
else if (mtype == Search.MatchType.MULTIPLE_SUBSTRING)
multipleSubstringResults.push(place.id);
else if (mtype == Search.MatchType.SUBSTRING) else if (mtype == Search.MatchType.SUBSTRING)
substringResults.push(place.id); substringResults.push(place.id);
} }
prefixResults.sort(Lang.bind(this, this._compareResultMeta)); multiplePrefixResults.sort(this._compareResultMeta);
substringResults.sort(Lang.bind(this, this._compareResultMeta)); prefixResults.sort(this._compareResultMeta);
multipleSubstringResults.sort(this._compareResultMeta);
this.searchSystem.pushResults(this, prefixResults.concat(substringResults)); substringResults.sort(this._compareResultMeta);
return multiplePrefixResults.concat(prefixResults.concat(multipleSubstringResults.concat(substringResults)));
}, },
getInitialResultSet: function(terms) { getInitialResultSet: function(terms) {
let places = this.placesManager.getAllPlaces(); let places = Main.placesManager.getAllPlaces();
this._searchPlaces(places, terms); return this._searchPlaces(places, terms);
}, },
getSubsearchResultSet: function(previousResults, terms) { getSubsearchResultSet: function(previousResults, terms) {
let places = previousResults.map(Lang.bind(this, function(id) { let places = previousResults.map(function (id) { return Main.placesManager.lookupPlaceById(id); });
return this.placesManager.lookupPlaceById(id); return this._searchPlaces(places, terms);
}));
this._searchPlaces(places, terms);
} }
}); });

View File

@@ -135,17 +135,17 @@ const AuthenticationDialog = new Lang.Class({
this._onUserChanged(); 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); messageBox.add(this._passwordBox);
this._passwordLabel = new St.Label(({ style_class: 'prompt-dialog-password-label' })); 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', this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
text: "", text: "",
can_focus: true}); can_focus: true});
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true }); ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivate)); this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivate));
this._passwordBox.add(this._passwordEntry, this._passwordBox.add(this._passwordEntry,
{ expand: true }); {expand: true });
this.setInitialKeyFocus(this._passwordEntry); this.setInitialKeyFocus(this._passwordEntry);
this._passwordBox.hide(); this._passwordBox.hide();
@@ -167,7 +167,6 @@ const AuthenticationDialog = new Lang.Class({
*/ */
this._nullMessageLabel = new St.Label({ style_class: 'prompt-dialog-null-label', this._nullMessageLabel = new St.Label({ style_class: 'prompt-dialog-null-label',
text: 'abc'}); text: 'abc'});
this._nullMessageLabel.add_style_class_name('hidden');
this._nullMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; this._nullMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._nullMessageLabel.clutter_text.line_wrap = true; this._nullMessageLabel.clutter_text.line_wrap = true;
messageBox.add(this._nullMessageLabel); messageBox.add(this._nullMessageLabel);
@@ -178,8 +177,7 @@ const AuthenticationDialog = new Lang.Class({
key: Clutter.Escape key: Clutter.Escape
}, },
{ label: _("Authenticate"), { label: _("Authenticate"),
action: Lang.bind(this, this._onAuthenticateButtonPressed), action: Lang.bind(this, this._onAuthenticateButtonPressed)
default: true
}]); }]);
this._doneEmitted = false; this._doneEmitted = false;
@@ -270,7 +268,7 @@ const AuthenticationDialog = new Lang.Class({
_onSessionRequest: function(session, request, echo_on) { _onSessionRequest: function(session, request, echo_on) {
// Cheap localization trick // Cheap localization trick
if (request == 'Password:' || request == 'Password: ') if (request == 'Password:')
this._passwordLabel.set_text(_("Password:")); this._passwordLabel.set_text(_("Password:"));
else else
this._passwordLabel.set_text(request); this._passwordLabel.set_text(request);

View File

@@ -134,7 +134,12 @@ const PopupBaseMenuItem = new Lang.Class({
this.sensitive = sensitive; this.sensitive = sensitive;
this.actor.reactive = sensitive; 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); this.emit('sensitive-changed', sensitive);
}, },
@@ -541,10 +546,6 @@ const PopupSliderMenuItem = new Lang.Class({
this._slider.connect('repaint', Lang.bind(this, this._sliderRepaint)); this._slider.connect('repaint', Lang.bind(this, this._sliderRepaint));
this.actor.connect('button-press-event', Lang.bind(this, this._startDragging)); this.actor.connect('button-press-event', Lang.bind(this, this._startDragging));
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent)); this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
this.actor.connect('notify::mapped', Lang.bind(this, function() {
if (!this.actor.mapped)
this._endDragging();
}));
this._releaseId = this._motionId = 0; this._releaseId = this._motionId = 0;
this._dragging = false; this._dragging = false;
@@ -768,10 +769,12 @@ const PopupSwitchMenuItem = new Lang.Class({
this._statusLabel.text = text; this._statusLabel.text = text;
this._statusBin.child = this._statusLabel; this._statusBin.child = this._statusLabel;
this.actor.reactive = false; this.actor.reactive = false;
this.actor.can_focus = false;
this.actor.accessible_role = Atk.Role.MENU_ITEM; this.actor.accessible_role = Atk.Role.MENU_ITEM;
} else { } else {
this._statusBin.child = this._switch.actor; this._statusBin.child = this._switch.actor;
this.actor.reactive = true; this.actor.reactive = true;
this.actor.can_focus = true;
this.actor.accessible_role = Atk.Role.CHECK_MENU_ITEM; this.actor.accessible_role = Atk.Role.CHECK_MENU_ITEM;
} }
this.checkAccessibleState(); this.checkAccessibleState();
@@ -868,7 +871,6 @@ const PopupMenuBase = new Lang.Class({
this._activeMenuItem = null; this._activeMenuItem = null;
this._childMenus = []; this._childMenus = [];
this._settingsActions = { };
}, },
addAction: function(title, callback) { addAction: function(title, callback) {
@@ -882,7 +884,8 @@ const PopupMenuBase = new Lang.Class({
}, },
addSettingsAction: function(title, desktopFile) { addSettingsAction: function(title, desktopFile) {
if (!Main.sessionMode.allowSettings) // Don't allow user settings to get edited unless we're in a user session
if (global.session_type != Shell.SessionType.USER)
return null; return null;
let menuItem = this.addAction(title, function() { let menuItem = this.addAction(title, function() {
@@ -896,21 +899,11 @@ const PopupMenuBase = new Lang.Class({
Main.overview.hide(); Main.overview.hide();
app.activate(); app.activate();
}); });
this._settingsActions[desktopFile] = menuItem;
return menuItem; return menuItem;
}, },
setSettingsVisibility: function(visible) {
for (let id in this._settingsActions) {
let item = this._settingsActions[id];
item.actor.visible = visible;
}
},
isEmpty: function() { isEmpty: function() {
return this.box.get_n_children() == 0; return this.box.get_children().length == 0;
}, },
isChildMenu: function(menu) { isChildMenu: function(menu) {
@@ -946,7 +939,7 @@ const PopupMenuBase = new Lang.Class({
_connectSubMenuSignals: function(object, menu) { _connectSubMenuSignals: function(object, menu) {
object._subMenuActivateId = menu.connect('activate', Lang.bind(this, function() { object._subMenuActivateId = menu.connect('activate', Lang.bind(this, function() {
this.emit('activate'); this.emit('activate');
this.close(BoxPointer.PopupAnimation.FULL); this.close(true);
})); }));
object._subMenuActiveChangeId = menu.connect('active-changed', Lang.bind(this, function(submenu, submenuItem) { object._subMenuActiveChangeId = menu.connect('active-changed', Lang.bind(this, function(submenu, submenuItem) {
if (this._activeMenuItem && this._activeMenuItem != submenuItem) if (this._activeMenuItem && this._activeMenuItem != submenuItem)
@@ -981,7 +974,7 @@ const PopupMenuBase = new Lang.Class({
})); }));
menuItem._activateId = menuItem.connect('activate', Lang.bind(this, function (menuItem, event) { menuItem._activateId = menuItem.connect('activate', Lang.bind(this, function (menuItem, event) {
this.emit('activate', menuItem); 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 weird name is to avoid a conflict with some random property
// the menuItem may have, called destroyId // the menuItem may have, called destroyId
@@ -1054,7 +1047,7 @@ const PopupMenuBase = new Lang.Class({
menuItem._closingId = this.connect('open-state-changed', menuItem._closingId = this.connect('open-state-changed',
function(self, open) { function(self, open) {
if (!open) if (!open)
menuItem.close(BoxPointer.PopupAnimation.FADE); menuItem.close(false);
}); });
menuItem.connect('destroy', Lang.bind(this, function() { menuItem.connect('destroy', Lang.bind(this, function() {
menuItem.disconnect(menuItem._subMenuActivateId); menuItem.disconnect(menuItem._subMenuActivateId);
@@ -1071,7 +1064,7 @@ const PopupMenuBase = new Lang.Class({
this._connectItemSignals(menuItem); this._connectItemSignals(menuItem);
menuItem._closingId = this.connect('open-state-changed', function(self, open) { menuItem._closingId = this.connect('open-state-changed', function(self, open) {
if (!open) if (!open)
menuItem.menu.close(BoxPointer.PopupAnimation.FADE); menuItem.menu.close(false);
}); });
} else if (menuItem instanceof PopupSeparatorMenuItem) { } else if (menuItem instanceof PopupSeparatorMenuItem) {
this._connectItemSignals(menuItem); this._connectItemSignals(menuItem);
@@ -1158,9 +1151,9 @@ const PopupMenuBase = new Lang.Class({
toggle: function() { toggle: function() {
if (this.isOpen) if (this.isOpen)
this.close(BoxPointer.PopupAnimation.FULL); this.close(true);
else else
this.open(BoxPointer.PopupAnimation.FULL); this.open(true);
}, },
destroy: function() { destroy: function() {
@@ -1221,7 +1214,7 @@ const PopupMenu = new Lang.Class({
_onKeyPressEvent: function(actor, event) { _onKeyPressEvent: function(actor, event) {
if (event.get_key_symbol() == Clutter.Escape) { if (event.get_key_symbol() == Clutter.Escape) {
this.close(BoxPointer.PopupAnimation.FULL); this.close(true);
return true; return true;
} }
@@ -1423,7 +1416,7 @@ const PopupSubMenu = new Lang.Class({
// Move focus back to parent menu if the user types Left. // Move focus back to parent menu if the user types Left.
if (this.isOpen && event.get_key_symbol() == Clutter.KEY_Left) { if (this.isOpen && event.get_key_symbol() == Clutter.KEY_Left) {
this.close(BoxPointer.PopupAnimation.FULL); this.close(true);
this.sourceActor._delegate.setActive(true); this.sourceActor._delegate.setActive(true);
return true; return true;
} }
@@ -1505,7 +1498,7 @@ const PopupSubMenuMenuItem = new Lang.Class({
let symbol = event.get_key_symbol(); let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_Right) { 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); this.menu.actor.navigate_focus(null, Gtk.DirectionType.DOWN, false);
return true; return true;
} else if (symbol == Clutter.KEY_Left && this.menu.isOpen) { } else if (symbol == Clutter.KEY_Left && this.menu.isOpen) {
@@ -1517,7 +1510,7 @@ const PopupSubMenuMenuItem = new Lang.Class({
}, },
activate: function(event) { activate: function(event) {
this.menu.open(BoxPointer.PopupAnimation.FULL); this.menu.open(true);
}, },
_onButtonReleaseEvent: function(actor) { _onButtonReleaseEvent: function(actor) {
@@ -1544,7 +1537,7 @@ const PopupComboMenu = new Lang.Class({
_onKeyPressEvent: function(actor, event) { _onKeyPressEvent: function(actor, event) {
if (event.get_key_symbol() == Clutter.Escape) { if (event.get_key_symbol() == Clutter.Escape) {
this.close(BoxPointer.PopupAnimation.FULL); this.close(true);
return true; return true;
} }
@@ -1895,7 +1888,11 @@ const RemoteMenu = new Lang.Class({
})); }));
} }
item.actor.reactive = action.enabled; 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() { destroyId = item.connect('destroy', Lang.bind(this, function() {
item.disconnect(destroyId); item.disconnect(destroyId);
@@ -1920,7 +1917,7 @@ const RemoteMenu = new Lang.Class({
while (k0 < currentItems.length && currentItems[k0]._ignored) while (k0 < currentItems.length && currentItems[k0]._ignored)
k0++; k0++;
// find the right menu item matching the model item // 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) if (currentItems[k0]._ignored)
k0++; k0++;
} }
@@ -1930,7 +1927,7 @@ const RemoteMenu = new Lang.Class({
for (k = k0; k < currentItems.length; k++) for (k = k0; k < currentItems.length; k++)
currentItems[k].destroy(); currentItems[k].destroy();
} else { } 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(); currentItems[k].destroy();
if (currentItems[k]._ignored) if (currentItems[k]._ignored)
@@ -1961,9 +1958,8 @@ const RemoteMenu = new Lang.Class({
k++; k++;
} }
} else if (changeSignal) { } else if (changeSignal) {
let signalId = this.actionGroup.connect(changeSignal, Lang.bind(this, function(actionGroup, actionName) { let signalId = this.actionGroup.connect(changeSignal, Lang.bind(this, function() {
actionGroup.disconnect(signalId); this.actionGroup.disconnect(signalId);
if (this._actions[actionName]) return;
// force a full update // force a full update
this._modelChanged(model, 0, -1, model.get_n_items(), target); this._modelChanged(model, 0, -1, model.get_n_items(), target);
@@ -2027,7 +2023,12 @@ const RemoteMenu = new Lang.Class({
if (action.items.length) { if (action.items.length) {
for (let i = 0; i < action.items.length; i++) { for (let i = 0; i < action.items.length; i++) {
let item = action.items[i]; let item = action.items[i];
item.actor.reactive = action.enabled; 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');
} }
} }
} }
@@ -2189,11 +2190,11 @@ const PopupMenuManager = new Lang.Class({
let oldMenu = this._activeMenu; let oldMenu = this._activeMenu;
this._activeMenu = null; this._activeMenu = null;
for (let i = this._menuStack.length - 1; i >= 0; i--) for (let i = this._menuStack.length - 1; i >= 0; i--)
this._menuStack[i].close(BoxPointer.PopupAnimation.FADE); this._menuStack[i].close(false);
oldMenu.close(BoxPointer.PopupAnimation.FADE); oldMenu.close(false);
newMenu.open(BoxPointer.PopupAnimation.FADE); newMenu.open(false);
} else } else
newMenu.open(BoxPointer.PopupAnimation.FULL); newMenu.open(true);
}, },
_onMenuSourceEnter: function(menu) { _onMenuSourceEnter: function(menu) {
@@ -2308,6 +2309,6 @@ const PopupMenuManager = new Lang.Class({
_closeMenu: function() { _closeMenu: function() {
if (this._activeMenu != null) if (this._activeMenu != null)
this._activeMenu.close(BoxPointer.PopupAnimation.FULL); this._activeMenu.close(true);
} }
}); });

View File

@@ -62,25 +62,10 @@ function loadRemoteSearchProvidersFromDir(dir, addProviderCallback) {
let remoteProvider, title; let remoteProvider, title;
try { try {
let group = KEY_FILE_GROUP; let group = KEY_FILE_GROUP;
let icon = keyfile.get_string(group, 'Icon');
let busName = keyfile.get_string(group, 'BusName'); let busName = keyfile.get_string(group, 'BusName');
let objectPath = keyfile.get_string(group, 'ObjectPath'); let objectPath = keyfile.get_string(group, 'ObjectPath');
title = keyfile.get_locale_string(group, 'Title', null);
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);
}
remoteProvider = new RemoteSearchProvider(title, remoteProvider = new RemoteSearchProvider(title,
icon, icon,
@@ -106,6 +91,7 @@ const RemoteSearchProvider = new Lang.Class({
dbusName, dbusPath); dbusName, dbusPath);
this.parent(title.toUpperCase()); this.parent(title.toUpperCase());
this.async = true;
this._cancellable = new Gio.Cancellable(); this._cancellable = new Gio.Cancellable();
}, },
@@ -134,7 +120,7 @@ const RemoteSearchProvider = new Lang.Class({
this.searchSystem.pushResults(this, results[0]); this.searchSystem.pushResults(this, results[0]);
}, },
getInitialResultSet: function(terms) { getInitialResultSetAsync: function(terms) {
this._cancellable.cancel(); this._cancellable.cancel();
this._cancellable.reset(); this._cancellable.reset();
try { try {
@@ -147,7 +133,7 @@ const RemoteSearchProvider = new Lang.Class({
} }
}, },
getSubsearchResultSet: function(previousResults, newTerms) { getSubsearchResultSetAsync: function(previousResults, newTerms) {
this._cancellable.cancel(); this._cancellable.cancel();
this._cancellable.reset(); this._cancellable.reset();
try { try {
@@ -178,7 +164,7 @@ const RemoteSearchProvider = new Lang.Class({
callback(resultMetas); callback(resultMetas);
}, },
getResultMetas: function(ids, callback) { getResultMetasAsync: function(ids, callback) {
this._cancellable.cancel(); this._cancellable.cancel();
this._cancellable.reset(); this._cancellable.reset();
try { try {

View File

@@ -2,261 +2,18 @@
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const GnomeDesktop = imports.gi.GnomeDesktop;
const Lang = imports.lang; const Lang = imports.lang;
const Meta = imports.gi.Meta; const Meta = imports.gi.Meta;
const Signals = imports.signals;
const St = imports.gi.St; const St = imports.gi.St;
const GnomeSession = imports.misc.gnomeSession; const GnomeSession = imports.misc.gnomeSession;
const Lightbox = imports.ui.lightbox; const Lightbox = imports.ui.lightbox;
const LoginDialog = imports.gdm.loginDialog;
const Main = imports.ui.main; const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const Tweener = imports.ui.tweener;
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver'; const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
const LOCK_ENABLED_KEY = 'lock-enabled'; const LOCK_ENABLED_KEY = 'lock-enabled';
const CURTAIN_SLIDE_TIME = 0.8;
// fraction of screen height the arrow must reach before completing
// the slide up automatically
const ARROW_DRAG_TRESHOLD = 0.1;
// The distance in px that the lock screen will move to when pressing
// a key that has no effect in the lock screen (bumping it)
const BUMP_SIZE = 25;
const BUMP_TIME = 0.3;
const SUMMARY_ICON_SIZE = 48;
// Lightbox fading times
// STANDARD_FADE_TIME is used when the session goes idle, while
// SHORT_FADE_TIME is used when requesting lock explicitly from the user menu
const STANDARD_FADE_TIME = 10;
const SHORT_FADE_TIME = 0.8;
const Clock = new Lang.Class({
Name: 'ScreenShieldClock',
CLOCK_FORMAT_KEY: 'clock-format',
CLOCK_SHOW_SECONDS_KEY: 'clock-show-seconds',
_init: function() {
this.actor = new St.BoxLayout({ style_class: 'screen-shield-clock',
vertical: true });
this._time = new St.Label({ style_class: 'screen-shield-clock-time' });
this._date = new St.Label({ style_class: 'screen-shield-clock-date' });
this.actor.add(this._time, { x_align: St.Align.MIDDLE });
this.actor.add(this._date, { x_align: St.Align.MIDDLE });
this._wallClock = new GnomeDesktop.WallClock({ time_only: true });
this._wallClock.connect('notify::clock', Lang.bind(this, this._updateClock));
this._updateClock();
},
_updateClock: function() {
this._time.text = this._wallClock.clock;
let date = new Date();
/* Translators: This is a time format for a date in
long format */
this._date.text = date.toLocaleFormat(_("%A, %B %d"));
},
destroy: function() {
this.actor.destroy();
this._wallClock.run_dispose();
}
});
const NotificationsBox = new Lang.Class({
Name: 'NotificationsBox',
_init: function() {
this.actor = new St.BoxLayout({ vertical: true,
name: 'screenShieldNotifications',
style_class: 'screen-shield-notifications-box' });
this._residentNotificationBox = new St.BoxLayout({ vertical: true,
style_class: 'screen-shield-notifications-box' });
let scrollView = new St.ScrollView({ x_fill: false, x_align: St.Align.MIDDLE });
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.MIDDLE });
this._items = [];
Main.messageTray.getSummaryItems().forEach(Lang.bind(this, function(item) {
this._summaryItemAdded(Main.messageTray, item);
}));
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() {
if (this._residentNotificationBox.get_n_children() > 0) {
this.actor.show();
return;
}
let children = this._persistentNotificationBox.get_children()
this.actor.visible = children.some(function(a) { return a.visible; });
},
_sourceIsResident: function(source) {
return source.hasResidentNotification() && !source.isChat;
},
_makeNotificationCountText: function(source) {
if (source.isChat)
return ngettext("%d new message", "%d new messages", source.count).format(source.count);
else
return ngettext("%d new notification", "%d new notifications", source.count).format(source.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);
let label = new St.Label({ text: source.title,
style_class: 'screen-shield-notification-label' });
textBox.add(label);
let countLabel = new St.Label({ text: this._makeNotificationCountText(source),
style_class: 'screen-shield-notification-count-text' });
textBox.add(countLabel);
box.visible = source.count != 0;
return [box, countLabel];
},
_summaryItemAdded: function(tray, item) {
// Ignore transient sources
if (item.source.isTransient)
return;
let obj = {
item: item,
source: item.source,
resident: this._sourceIsResident(item.source),
contentUpdatedId: 0,
sourceDestroyId: 0,
sourceBox: null,
countLabel: null,
};
if (obj.resident) {
item.prepareNotificationStackForShowing();
this._residentNotificationBox.add(item.notificationStackView);
} else {
[obj.sourceBox, obj.countLabel] = this._makeNotificationSource(item.source);
this._persistentNotificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.MIDDLE });
}
obj.contentUpdatedId = item.connect('content-updated', Lang.bind(this, this._onItemContentUpdated));
obj.sourceCountChangedId = item.source.connect('count-changed', 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);
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
this._residentNotificationBox.remove_actor(obj.item.notificationStackView);
[obj.sourceBox, obj.countLabel] = this._makeNotificationSource(obj.source);
this._persistentNotificationBox.add(obj.sourceBox);
} else if (itemShouldBeResident && !obj.resident) {
// make into a resident item
obj.sourceBox.destroy();
obj.sourceBox = obj.countLabel = null;
obj.item.prepareNotificationStackForShowing();
this._residentNotificationBox.add(obj.item.notificationStackView);
} else {
// just update the counter
obj.countLabel.text = this._makeNotificationCountText(obj.item.source);
obj.sourceBox.visible = obj.source.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) {
this._residentNotificationBox.remove_actor(obj.item.notificationStackView);
obj.item.doneShowingNotificationStack();
} else {
obj.sourceBox.destroy();
}
obj.item.disconnect(obj.contentUpdatedId);
obj.source.disconnect(obj.sourceDestroyId);
obj.source.disconnect(obj.sourceCountChangedId);
},
});
/** /**
* To test screen shield, make sure to kill gnome-screensaver. * To test screen shield, make sure to kill gnome-screensaver.
* *
@@ -271,50 +28,7 @@ const ScreenShield = new Lang.Class({
Name: 'ScreenShield', Name: 'ScreenShield',
_init: function() { _init: function() {
this.actor = Main.layoutManager.screenShieldGroup;
this._lockScreenGroup = new St.Widget({ x_expand: true,
y_expand: true,
reactive: true,
can_focus: true,
layout_manager: new Clutter.BinLayout()
});
this._background = Meta.BackgroundActor.new_for_screen(global.screen);
this._background.add_effect(new Clutter.BlurEffect());
this._background.add_effect(new Clutter.DesaturateEffect({ factor: 0.6 }));
this._lockScreenGroup.add_actor(this._background);
this._arrow = new St.DrawingArea({ style_class: 'arrow',
reactive: 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
});
this._arrow.connect('repaint', Lang.bind(this, this._drawArrow));
this._lockScreenGroup.add_actor(this._arrow);
let action = new Clutter.DragAction({ drag_axis: Clutter.DragAxis.Y_AXIS });
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 });
this.actor.add_actor(this._lockDialogGroup);
this.actor.add_actor(this._lockScreenGroup);
this._presence = new GnomeSession.Presence(Lang.bind(this, function(proxy, error) { 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._onStatusChanged(proxy.status);
})); }));
this._presence.connectSignal('StatusChanged', Lang.bind(this, function(proxy, senderName, [status]) { this._presence.connectSignal('StatusChanged', Lang.bind(this, function(proxy, senderName, [status]) {
@@ -323,294 +37,55 @@ const ScreenShield = new Lang.Class({
this._settings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA }); this._settings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
this._isModal = false; this._group = new St.Widget({ x: 0,
this._isLocked = false; y: 0 });
this._hasLockScreen = false; Main.uiGroup.add_actor(this._group);
let constraint = new Clutter.BindConstraint({ source: global.stage,
this._lightbox = new Lightbox.Lightbox(Main.uiGroup, coordinate: Clutter.BindCoordinate.POSITION | Clutter.BindCoordinate.SIZE });
{ inhibitEvents: true, this._group.add_constraint(constraint);
fadeInTime: STANDARD_FADE_TIME, this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
fadeFactor: 1 }); this._group.connect('button-press-event', Lang.bind(this, this._onButtonPressEvent));
}, this._lightbox = new Lightbox.Lightbox(this._group,
{ inhibitEvents: true, fadeInTime: 10, fadeFactor: 1 });
_onStageKeyRelease: function(actor, event) { this._background = Meta.BackgroundActor.new_for_screen(global.screen);
if (!this._isLocked) this._background.hide();
return false; Main.uiGroup.add_actor(this._background);
if (event.get_key_symbol() == Clutter.KEY_Escape) {
this._showUnlockDialog(true);
return true;
}
this._bumpLockScreen();
return true;
},
_drawArrow: function() {
let cr = this._arrow.get_context();
let [w, h] = this._arrow.get_surface_size();
let node = this._arrow.get_theme_node();
Clutter.cairo_set_source_color(cr, node.get_foreground_color());
cr.moveTo(0, h);
cr.lineTo(w/2, 0);
cr.lineTo(w, h);
cr.fill();
},
_onDragBegin: function() {
Tweener.removeTweens(this._lockScreenGroup);
},
_onDragEnd: function(action, actor, eventX, eventY, modifiers) {
if (this._lockScreenGroup.y < -(ARROW_DRAG_TRESHOLD * global.stage.height)) {
// Complete motion automatically
this._showUnlockDialog(true);
} else {
// restore the lock screen to its original place
// try to use the same speed as the normal animation
let h = global.stage.height;
let time = CURTAIN_SLIDE_TIME * (-this._lockScreenGroup.y) / h;
Tweener.removeTweens(this._lockScreenGroup);
Tweener.addTween(this._lockScreenGroup,
{ y: 0,
time: time,
transition: 'linear',
onComplete: function() {
this.fixed_position_set = false;
}
});
}
}, },
_onStatusChanged: function(status) { _onStatusChanged: function(status) {
log ("in _onStatusChanged");
if (status == GnomeSession.PresenceStatus.IDLE) { if (status == GnomeSession.PresenceStatus.IDLE) {
if (this._dialog) { log("session gone idle");
this._dialog.cancel(); this._group.reactive = true;
if (!this._keepDialog) { Main.pushModal(this._group);
this._dialog = null; this._lightbox.show();
}
}
if (!this._isModal) {
Main.pushModal(this.actor);
this._isModal = true;
}
if (!this._isLocked)
this._lightbox.show();
} else { } else {
let lightboxWasShown = this._lightbox.shown; let lightboxWasShown = this._lightbox.shown;
log("this._lightbox.shown " + this._lightbox.shown);
this._lightbox.hide(); this._lightbox.hide();
if (lightboxWasShown && this._settings.get_boolean(LOCK_ENABLED_KEY)) {
let shouldLock = lightboxWasShown && this._settings.get_boolean(LOCK_ENABLED_KEY); this._background.show();
if (shouldLock || this._isLocked) { this._background.raise_top();
this.lock(false); } else {
} else if (this._isModal) { this._popModal();
this.unlock();
} }
} }
}, },
showDialog: function() { _popModal: function() {
this.lock(true); this._group.reactive = false;
this._showUnlockDialog(false); if (Main.isInModalStack(this._group))
Main.popModal(this._group);
this._background.hide();
}, },
_bumpLockScreen: function() { _onKeyPressEvent: function(object, keyPressEvent) {
Tweener.removeTweens(this._lockScreenGroup); log("in _onKeyPressEvent - lock is enabled: " + this._settings.get_boolean(LOCK_ENABLED_KEY));
Tweener.addTween(this._lockScreenGroup, this._popModal();
{ y: -BUMP_SIZE,
time: BUMP_TIME / 2,
transition: 'easeOutQuad',
onComplete: function() {
Tweener.addTween(this,
{ y: 0,
time: BUMP_TIME / 2,
transition: 'easeInQuad' });
}
});
}, },
_showUnlockDialog: function(animate) { _onButtonPressEvent: function(object, buttonPressEvent) {
if (animate) { log("in _onButtonPressEvent - lock is enabled: " + this._settings.get_boolean(LOCK_ENABLED_KEY));
// Tween the lock screen out of screen this._popModal();
// try to use the same speed regardless of original position
let h = global.stage.height;
let time = CURTAIN_SLIDE_TIME * (h + this._lockScreenGroup.y) / h;
Tweener.removeTweens(this._lockScreenGroup);
Tweener.addTween(this._lockScreenGroup,
{ y: -h,
time: time,
transition: 'linear',
onComplete: Lang.bind(this, this._hideLockScreen),
});
} else {
this._hideLockScreen();
}
if (!this._dialog) {
[this._dialog, this._keepDialog] = Main.sessionMode.createUnlockDialog(this._lockDialogGroup);
if (!this._dialog) {
// This session mode has no locking capabilities
this.unlock();
return;
}
this._dialog.connect('loaded', Lang.bind(this, function() {
if (!this._dialog.open()) {
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));
}
if (this._keepDialog) {
// Notify the other components that even though we are showing the
// screenshield, we're not in a locked state
// (this happens for the gdm greeter)
this._isLocked = false;
this.emit('lock-status-changed', false);
}
},
_onUnlockFailed: function() {
this._dialog.destroy();
this._dialog = null;
this._resetLockScreen(false);
},
_onUnlockSucceded: function() {
this.unlock();
},
_hideLockScreen: function() {
this._arrow.hide();
this._lockScreenGroup.hide();
},
_resetLockScreen: function(animate) {
this._lockScreenGroup.show();
this._arrow.show();
if (animate) {
this._lockScreenGroup.y = -global.screen_height;
Tweener.removeTweens(this._lockScreenGroup);
Tweener.addTween(this._lockScreenGroup,
{ y: 0,
time: SHORT_FADE_TIME,
transition: 'linear',
onComplete: function() {
this._lockScreenGroup.fixed_position_set = false;
this.emit('lock-screen-shown');
},
onCompleteScope: this
});
} else {
this._lockScreenGroup.fixed_position_set = false;
this.emit('lock-screen-shown');
}
if (!this._stageKeyHandler)
this._stageKeyHandler = global.stage.connect('key-release-event',
Lang.bind(this, this._onStageKeyRelease));
},
// Some of the actors in the lock screen are heavy in
// resources, so we only create them when needed
_prepareLockScreen: function() {
this._lockScreenContentsBox = new St.BoxLayout({ x_align: Clutter.ActorAlign.CENTER,
y_align: Clutter.ActorAlign.CENTER,
x_expand: true,
y_expand: true,
vertical: true });
this._clock = new Clock();
this._lockScreenContentsBox.add(this._clock.actor, { x_fill: true,
y_fill: true });
this._lockScreenGroup.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();
this._hasLockScreen = false;
},
get locked() {
return this._isLocked;
},
unlock: function() {
if (this._hasLockScreen)
this._clearLockScreen();
if (this._stageKeyHandler) {
global.stage.disconnect(this._stageKeyHandler);
this._stageKeyHandler = 0;
}
if (this._keepDialog) {
// The dialog must be kept alive,
// so immediately go back to it
// This will also reset _isLocked
this._showUnlockDialog(false);
return;
}
if (this._dialog) {
this._dialog.destroy();
this._dialog = null;
}
this._lightbox.hide();
Main.popModal(this.actor);
this.actor.hide();
this._isModal = false;
this._isLocked = false;
this.emit('lock-status-changed', false);
},
lock: function(animate) {
if (!this._hasLockScreen)
this._prepareLockScreen();
if (!this._isModal) {
Main.pushModal(this.actor);
this._isModal = true;
}
this._isLocked = true;
this.actor.show();
this._resetLockScreen(animate);
this.emit('lock-status-changed', true);
}, },
}); });
Signals.addSignalMethods(ScreenShield.prototype);

View File

@@ -18,7 +18,9 @@ const DISABLED_OPEN_SEARCH_PROVIDERS_KEY = 'disabled-open-search-providers';
const MatchType = { const MatchType = {
NONE: 0, NONE: 0,
SUBSTRING: 1, SUBSTRING: 1,
PREFIX: 2 MULTIPLE_SUBSTRING: 2,
PREFIX: 3,
MULTIPLE_PREFIX: 4
}; };
const SearchResultDisplay = new Lang.Class({ const SearchResultDisplay = new Lang.Class({
@@ -51,7 +53,7 @@ const SearchResultDisplay = new Lang.Class({
* Remove all results from this display. * Remove all results from this display.
*/ */
clear: function() { clear: function() {
this.actor.destroy_all_children(); this.actor.get_children().forEach(function (actor) { actor.destroy(); });
}, },
/** /**
@@ -70,8 +72,11 @@ const SearchResultDisplay = new Lang.Class({
* Subclass this object to add a new result type * Subclass this object to add a new result type
* to the search system, then call registerProvider() * to the search system, then call registerProvider()
* in SearchSystem with an instance. * in SearchSystem with an instance.
* Search is asynchronous and uses the * By default, search is synchronous and uses the
* getInitialResultSet()/getSubsearchResultSet() methods. * getInitialResultSet()/getSubsearchResultSet() methods.
* For asynchronous search, set the async property to true
* and implement getInitialResultSetAsync()/getSubsearchResultSetAsync()
* instead.
*/ */
const SearchProvider = new Lang.Class({ const SearchProvider = new Lang.Class({
Name: 'SearchProvider', Name: 'SearchProvider',
@@ -79,6 +84,7 @@ const SearchProvider = new Lang.Class({
_init: function(title) { _init: function(title) {
this.title = title; this.title = title;
this.searchSystem = null; this.searchSystem = null;
this.async = false;
}, },
/** /**
@@ -89,7 +95,7 @@ const SearchProvider = new Lang.Class({
* therefore a single term of length one or two), or when * therefore a single term of length one or two), or when
* a new term is added. * 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 * items which match the given search terms. This
* is expected to be a substring match on the metadata for a given * 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, * item. Ordering of returned results is up to the discretion of the provider,
@@ -99,9 +105,6 @@ const SearchProvider = new Lang.Class({
* description) before single matches * description) before single matches
* * Put items which match on a prefix before non-prefix substring 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 * This function should be fast; do not perform unindexed full-text searches
* or network queries. * or network queries.
*/ */
@@ -109,6 +112,18 @@ const SearchProvider = new Lang.Class({
throw new Error('Not implemented'); 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: * getSubsearchResultSet:
* @previousResults: Array of item identifiers * @previousResults: Array of item identifiers
@@ -121,26 +136,62 @@ const SearchProvider = new Lang.Class({
* *
* This allows search providers to only search through the previous * This allows search providers to only search through the previous
* result set, rather than possibly performing a full re-query. * 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) { getSubsearchResultSet: function(previousResults, newTerms) {
throw new Error('Not implemented'); 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: * getResultMetas:
* @ids: Result identifier strings * @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 * 'createIcon' (function(size) returning a Clutter.Texture) properties
* with the same number of members as @ids * with the same number of members as @ids
*/ */
getResultMetas: function(ids, callback) { getResultMetas: function(ids) {
throw new Error('Not implemented'); 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: * createResultActor:
* @resultMeta: Object with result metadata * @resultMeta: Object with result metadata
@@ -328,33 +379,42 @@ const SearchSystem = new Lang.Class({
} }
} }
let previousResultsArr = this._previousResults;
let results = []; let results = [];
this._previousTerms = terms;
this._previousResults = results;
if (isSubSearch) { if (isSubSearch) {
for (let i = 0; i < this._providers.length; i++) { for (let i = 0; i < this._providers.length; i++) {
let [provider, previousResults] = previousResultsArr[i]; let [provider, previousResults] = this._previousResults[i];
try { try {
results.push([provider, []]); if (provider.async) {
provider.getSubsearchResultSet(previousResults, terms); provider.getSubsearchResultSetAsync(previousResults, terms);
results.push([provider, []]);
} else {
let providerResults = provider.getSubsearchResultSet(previousResults, terms);
results.push([provider, providerResults]);
}
} catch (error) { } 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 { } else {
for (let i = 0; i < this._providers.length; i++) { for (let i = 0; i < this._providers.length; i++) {
let provider = this._providers[i]; let provider = this._providers[i];
try { try {
results.push([provider, []]); if (provider.async) {
provider.getInitialResultSet(terms); provider.getInitialResultSetAsync(terms);
results.push([provider, []]);
} else {
let providerResults = provider.getInitialResultSet(terms);
results.push([provider, providerResults]);
}
} catch (error) { } 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); Signals.addSignalMethods(SearchSystem.prototype);

View File

@@ -5,7 +5,6 @@ const Lang = imports.lang;
const Gtk = imports.gi.Gtk; const Gtk = imports.gi.Gtk;
const Meta = imports.gi.Meta; const Meta = imports.gi.Meta;
const St = imports.gi.St; const St = imports.gi.St;
const Atk = imports.gi.Atk;
const DND = imports.ui.dnd; const DND = imports.ui.dnd;
const IconGrid = imports.ui.iconGrid; const IconGrid = imports.ui.iconGrid;
@@ -34,13 +33,12 @@ const SearchResult = new Lang.Class({
content = new St.Bin({ style_class: 'search-result-content', content = new St.Bin({ style_class: 'search-result-content',
reactive: true, reactive: true,
can_focus: true, can_focus: true,
track_hover: true, track_hover: true });
accessible_role: Atk.Role.PUSH_BUTTON });
let icon = new IconGrid.BaseIcon(this.metaInfo['name'], let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
{ createIcon: this.metaInfo['createIcon'] }); { createIcon: this.metaInfo['createIcon'] });
content.set_child(icon.actor); content.set_child(icon.actor);
this._dragActorSource = icon.icon; this._dragActorSource = icon.icon;
content.label_actor = icon.label; this.actor.label_actor = icon.label;
} else { } else {
if (content._delegate && content._delegate.getDragActorSource) if (content._delegate && content._delegate.getDragActorSource)
this._dragActorSource = content._delegate.getDragActorSource(); this._dragActorSource = content._delegate.getDragActorSource();
@@ -121,7 +119,13 @@ const GridSearchResults = new Lang.Class({
if (results.length == 0) if (results.length == 0)
return; 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 = []; this._notDisplayedResult = [];
@@ -131,7 +135,7 @@ const GridSearchResults = new Lang.Class({
getResultsForDisplay: function() { getResultsForDisplay: function() {
let alreadyVisible = this._pendingClear ? 0 : this._grid.visibleItemsCount(); 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; - alreadyVisible;
let numResults = Math.min(this._notDisplayedResult.length, canDisplay); let numResults = Math.min(this._notDisplayedResult.length, canDisplay);
@@ -175,7 +179,8 @@ const SearchResults = new Lang.Class({
_init: function(searchSystem, openSearchSystem) { _init: function(searchSystem, openSearchSystem) {
this._searchSystem = searchSystem; 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._openSearchSystem = openSearchSystem;
this.actor = new St.BoxLayout({ name: 'searchResults', this.actor = new St.BoxLayout({ name: 'searchResults',
@@ -209,8 +214,10 @@ const SearchResults = new Lang.Class({
this._content.add(this._statusText); this._content.add(this._statusText);
this._providers = this._searchSystem.getProviders(); this._providers = this._searchSystem.getProviders();
this._providerMeta = []; this._providerMeta = [];
this._providerMetaResults = {};
for (let i = 0; i < this._providers.length; i++) { for (let i = 0; i < this._providers.length; i++) {
this.createProviderMeta(this._providers[i]); this.createProviderMeta(this._providers[i]);
this._providerMetaResults[this.providers[i].title] = [];
} }
this._searchProvidersBox = new St.BoxLayout({ style_class: 'search-providers-box' }); this._searchProvidersBox = new St.BoxLayout({ style_class: 'search-providers-box' });
this.actor.add(this._searchProvidersBox); this.actor.add(this._searchProvidersBox);
@@ -275,12 +282,16 @@ const SearchResults = new Lang.Class({
x_fill: true, x_fill: true,
y_fill: true }); y_fill: true });
providerBox.add(resultDisplayBin, { expand: 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); resultDisplayBin.set_child(resultDisplay.actor);
this._providerMeta.push({ provider: provider, this._providerMeta.push({ provider: provider,
actor: providerBox, actor: providerBox,
resultDisplay: resultDisplay }); resultDisplay: resultDisplay,
hasPendingResults: false });
this._content.add(providerBox); this._content.add(providerBox);
}, },
@@ -296,6 +307,7 @@ const SearchResults = new Lang.Class({
}, },
_clearDisplay: function() { _clearDisplay: function() {
this._visibleResultsCount = 0;
for (let i = 0; i < this._providerMeta.length; i++) { for (let i = 0; i < this._providerMeta.length; i++) {
let meta = this._providerMeta[i]; let meta = this._providerMeta[i];
meta.resultDisplay.clear(); meta.resultDisplay.clear();
@@ -323,8 +335,6 @@ const SearchResults = new Lang.Class({
doSearch: function (searchString) { doSearch: function (searchString) {
this._searchSystem.updateSearch(searchString); this._searchSystem.updateSearch(searchString);
let terms = this._searchSystem.getTerms();
this._openSearchSystem.setSearchTerms(terms);
}, },
_metaForProvider: function(provider) { _metaForProvider: function(provider) {
@@ -336,6 +346,8 @@ const SearchResults = new Lang.Class({
for (let i = 0; i < this._providerMeta.length; i++) { for (let i = 0; i < this._providerMeta.length; i++) {
let meta = this._providerMeta[i]; let meta = this._providerMeta[i];
if (meta.hasPendingResults)
return;
if (!meta.actor.visible) if (!meta.actor.visible)
continue; continue;
@@ -360,57 +372,71 @@ const SearchResults = new Lang.Class({
} }
}, },
_updateStatusText: function () { _updateCurrentResults: function(searchSystem, results) {
let haveResults = false; let terms = searchSystem.getTerms();
let [provider, providerResults] = results;
let meta = this._metaForProvider(provider);
meta.hasPendingResults = false;
this._updateProviderResults(provider, providerResults, terms);
},
for (let i = 0; i < this._providerMeta.length; ++i) _updateProviderResults: function(provider, providerResults, terms) {
if (this._providerMeta[i].resultDisplay.getFirstResult()) { let meta = this._metaForProvider(provider);
haveResults = true; if (providerResults.length == 0) {
break; this._clearDisplayForProvider(provider);
meta.resultDisplay.setResults([], []);
} else {
this._providerMetaResults[provider.title] = providerResults;
meta.resultDisplay.setResults(providerResults, terms);
let results = meta.resultDisplay.getResultsForDisplay();
if (provider.async) {
provider.getResultMetasAsync(results, Lang.bind(this,
function(metas) {
this._clearDisplayForProvider(provider);
meta.actor.show();
this._content.hide();
meta.resultDisplay.renderResults(metas);
this._maybeSetInitialSelection();
this._content.show();
}));
} else {
let metas = provider.getResultMetas(results);
this._clearDisplayForProvider(provider);
meta.actor.show();
meta.resultDisplay.renderResults(metas);
} }
}
},
if (!haveResults) { _updateResults: function(searchSystem, results) {
if (results.length == 0) {
this._statusText.set_text(_("No matching results.")); this._statusText.set_text(_("No matching results."));
this._statusText.show(); this._statusText.show();
} else { } else {
this._statusText.hide(); this._statusText.hide();
} }
},
_updateResults: function(searchSystem, results) {
let terms = searchSystem.getTerms(); let terms = searchSystem.getTerms();
let [provider, providerResults] = results; this._openSearchSystem.setSearchTerms(terms);
let meta = this._metaForProvider(provider);
if (providerResults.length == 0) { // To avoid CSS transitions causing flickering when the first search
this._clearDisplayForProvider(provider); // result stays the same, we hide the content while filling in the
meta.resultDisplay.setResults([], []); // results.
this._maybeSetInitialSelection(); this._content.hide();
this._updateStatusText();
} else {
meta.resultDisplay.setResults(providerResults, terms);
let results = meta.resultDisplay.getResultsForDisplay();
provider.getResultMetas(results, Lang.bind(this, function(metas) { for (let i = 0; i < results.length; i++) {
this._clearDisplayForProvider(provider); let [provider, providerResults] = results[i];
meta.actor.show(); let meta = this._metaForProvider(provider);
meta.hasPendingResults = provider.async;
// Hiding drops the key focus if we have it if (!meta.hasPendingResults)
let focus = global.stage.get_key_focus(); this._updateProviderResults(provider, providerResults, 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();
meta.resultDisplay.renderResults(metas);
this._maybeSetInitialSelection();
this._updateStatusText();
this._content.show();
if (this._content.contains(focus))
global.stage.set_key_focus(focus);
}));
} }
this._maybeSetInitialSelection();
this._content.show();
return true;
}, },
activateDefault: function() { activateDefault: function() {

View File

@@ -1,135 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Config = imports.misc.config;
const Main = imports.ui.main;
const Params = imports.misc.params;
const STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION = {
'a11y': imports.ui.status.accessibility.ATIndicator,
'volume': imports.ui.status.volume.Indicator,
'battery': imports.ui.status.power.Indicator,
'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
'userMenu': imports.ui.userMenu.UserMenuButton
};
if (Config.HAVE_BLUETOOTH)
STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION['bluetooth'] =
imports.ui.status.bluetooth.Indicator;
try {
STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION['network'] =
imports.ui.status.network.NMApplet;
} catch(e) {
log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
}
const DEFAULT_MODE = 'user';
const _modes = {
'gdm': { hasOverview: false,
hasAppMenu: false,
showCalendarEvents: false,
allowSettings: false,
allowExtensions: false,
allowKeybindingsWhenModal: true,
hasRunDialog: false,
hasWorkspaces: false,
createSession: Main.createGDMSession,
createUnlockDialog: Main.createGDMLoginDialog,
extraStylesheet: null,
statusArea: {
order: [
'a11y', 'display', 'keyboard',
'volume', 'battery', 'powerMenu'
],
implementation: {
'a11y': imports.ui.status.accessibility.ATIndicator,
'volume': imports.ui.status.volume.Indicator,
'battery': imports.ui.status.power.Indicator,
'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
'powerMenu': imports.gdm.powerMenu.PowerMenuButton
}
}
},
'initial-setup': { hasOverview: false,
hasAppMenu: false,
showCalendarEvents: false,
allowSettings: false,
allowExtensions: false,
allowKeybindingsWhenModal: false,
hasRunDialog: false,
hasWorkspaces: false,
createSession: Main.createInitialSetupSession,
extraStylesheet: null,
statusArea: {
order: [
'a11y', 'keyboard', 'volume'
],
implementation: {
'a11y': imports.ui.status.accessibility.ATIndicator,
'keyboard': imports.ui.status.keyboard.XKBIndicator,
'volume': imports.ui.status.volume.Indicator
}
}
},
'user': { hasOverview: true,
hasAppMenu: true,
showCalendarEvents: true,
allowSettings: true,
allowExtensions: true,
allowKeybindingsWhenModal: false,
hasRunDialog: true,
hasWorkspaces: true,
createSession: Main.createUserSession,
createUnlockDialog: Main.createSessionUnlockDialog,
extraStylesheet: null,
statusArea: {
order: [
'input-method', 'a11y', 'keyboard', 'volume', 'bluetooth',
'network', 'battery', 'userMenu'
],
implementation: STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION
}
}
};
function listModes() {
let modes = Object.getOwnPropertyNames(_modes);
for (let i = 0; i < modes.length; i++)
print(modes[i]);
}
const SessionMode = new Lang.Class({
Name: 'SessionMode',
_init: function() {
let params = _modes[global.session_mode];
params = Params.parse(params, _modes[DEFAULT_MODE]);
this._createSession = params.createSession;
delete params.createSession;
this._createUnlockDialog = params.createUnlockDialog;
delete params.createUnlockDialog;
Lang.copyProperties(params, this);
},
createSession: function() {
if (this._createSession)
this._createSession();
},
createUnlockDialog: function() {
if (this._createUnlockDialog)
return this._createUnlockDialog.apply(this, arguments);
else
return null;
},
});

View File

@@ -7,7 +7,6 @@ const Shell = imports.gi.Shell;
const Config = imports.misc.config; const Config = imports.misc.config;
const ExtensionSystem = imports.ui.extensionSystem; const ExtensionSystem = imports.ui.extensionSystem;
const ExtensionDownloader = imports.ui.extensionDownloader;
const ExtensionUtils = imports.misc.extensionUtils; const ExtensionUtils = imports.misc.extensionUtils;
const Flashspot = imports.ui.flashspot; const Flashspot = imports.ui.flashspot;
const Main = imports.ui.main; 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="b" direction="out" name="success" />
<arg type="s" direction="out" name="result" /> <arg type="s" direction="out" name="result" />
</method> </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"> <method name="ScreenshotArea">
<arg type="i" direction="in" name="x"/> <arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="y"/> <arg type="i" direction="in" name="y"/>
@@ -46,21 +56,30 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
<arg type="i" direction="in" name="width"/> <arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/> <arg type="i" direction="in" name="height"/>
</method> </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="OverviewActive" type="b" access="readwrite" />
<property name="ApiVersion" type="i" access="read" />
<property name="ShellVersion" type="s" access="read" /> <property name="ShellVersion" type="s" access="read" />
</interface>; <signal name="ExtensionStatusChanged">
<arg type="s" name="uuid"/>
const ScreenSaverIface = <interface name="org.gnome.ScreenSaver"> <arg type="i" name="state"/>
<method name="Lock"> <arg type="s" name="error"/>
</method>
<method name="GetActive">
<arg name="active" direction="out" type="b" />
</method>
<method name="SetActive">
<arg name="value" direction="in" type="u" />
</method>
<signal name="ActiveChanged">
<arg name="new_value" type="b" />
</signal> </signal>
</interface>; </interface>;
@@ -70,8 +89,8 @@ const GnomeShell = new Lang.Class({
_init: function() { _init: function() {
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellIface, this); this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell'); this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
ExtensionSystem.connect('extension-state-changed',
this._extensionsSerivce = new GnomeShellExtensions(); Lang.bind(this, this._extensionStateChanged));
}, },
/** /**
@@ -183,67 +202,6 @@ const GnomeShell = new Lang.Class({
flashspot.fire(); 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() { ListExtensions: function() {
let out = {}; let out = {};
for (let uuid in ExtensionUtils.extensions) { for (let uuid in ExtensionUtils.extensions) {
@@ -302,12 +260,26 @@ const GnomeShellExtensions = new Lang.Class({
return extension.errors; return extension.errors;
}, },
InstallRemoteExtensionAsync: function([uuid], invocation) { EnableExtension: function(uuid) {
return ExtensionDownloader.installExtension(uuid, invocation); 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) { UninstallExtension: function(uuid) {
return ExtensionDownloader.uninstallExtension(uuid); return ExtensionSystem.uninstallExtensionFromUUID(uuid);
}, },
LaunchExtensionPrefs: function(uuid) { LaunchExtensionPrefs: function(uuid) {
@@ -317,15 +289,19 @@ const GnomeShellExtensions = new Lang.Class({
['extension:///' + uuid], -1, null); ['extension:///' + uuid], -1, null);
}, },
ReloadExtension: function(uuid) { get OverviewActive() {
ExtensionSystem.unloadExtension(uuid); return Main.overview.visible;
ExtensionSystem.loadExtension(uuid);
}, },
CheckForUpdates: function() { set OverviewActive(visible) {
ExtensionDownloader.checkForUpdates(); if (visible)
Main.overview.show();
else
Main.overview.hide();
}, },
ApiVersion: ExtensionSystem.API_VERSION,
ShellVersion: Config.PACKAGE_VERSION, ShellVersion: Config.PACKAGE_VERSION,
_extensionStateChanged: function(_, newState) { _extensionStateChanged: function(_, newState) {
@@ -333,33 +309,3 @@ const GnomeShellExtensions = new Lang.Class({
GLib.Variant.new('(sis)', [newState.uuid, newState.state, newState.error])); GLib.Variant.new('(sis)', [newState.uuid, newState.state, newState.error]));
} }
}); });
const ScreenSaverDBus = new Lang.Class({
Name: 'ScreenSaverDBus',
_init: function() {
this.parent();
Main.screenShield.connect('lock-status-changed', Lang.bind(this, function(shield, locked) {
this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [locked]));
}));
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenSaverIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/ScreenSaver');
},
Lock: function() {
Main.screenShield.lock(true);
},
SetActive: function(active) {
if (active)
Main.screenShield.lock(true);
else
Main.screenShield.unlock();
},
GetActive: function() {
return Main.screenShield.locked;
}
});

View File

@@ -7,7 +7,7 @@ const Main = imports.ui.main;
const Params = imports.misc.params; const Params = imports.misc.params;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
const EntryMenu = new Lang.Class({ const _EntryMenu = new Lang.Class({
Name: 'ShellEntryMenu', Name: 'ShellEntryMenu',
Extends: PopupMenu.PopupMenu, Extends: PopupMenu.PopupMenu,
@@ -34,37 +34,18 @@ const EntryMenu = new Lang.Class({
this._pasteItem = item; this._pasteItem = item;
this._passwordItem = null; this._passwordItem = null;
if (params.isPassword) if (params.isPassword) {
this._makePasswordItem(); item = new PopupMenu.PopupMenuItem('');
item.connect('activate', Lang.bind(this,
this._onPasswordActivated));
this.addMenuItem(item);
this._passwordItem = item;
}
Main.uiGroup.add_actor(this.actor); Main.uiGroup.add_actor(this.actor);
this.actor.hide(); 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() { open: function() {
this._updatePasteItem(); this._updatePasteItem();
this._updateCopyItem(); this._updateCopyItem();
@@ -123,50 +104,50 @@ const EntryMenu = new Lang.Class({
function _setMenuAlignment(entry, stageX) { function _setMenuAlignment(entry, stageX) {
let [success, entryX, entryY] = entry.transform_stage_point(stageX, 0); let [success, entryX, entryY] = entry.transform_stage_point(stageX, 0);
if (success) if (success)
entry.menu.setSourceAlignment(entryX / entry.width); entry._menu.setSourceAlignment(entryX / entry.width);
}; };
function _onClicked(action, actor) { function _onClicked(action, actor) {
let entry = actor.menu ? actor : actor.get_parent(); let entry = actor._menu ? actor : actor.get_parent();
if (entry.menu.isOpen) { if (entry._menu.isOpen) {
entry.menu.close(); entry._menu.close();
} else if (action.get_button() == 3) { } else if (action.get_button() == 3) {
let [stageX, stageY] = action.get_coords(); let [stageX, stageY] = action.get_coords();
_setMenuAlignment(entry, stageX); _setMenuAlignment(entry, stageX);
entry.menu.open(); entry._menu.open();
} }
}; };
function _onLongPress(action, actor, state) { function _onLongPress(action, actor, state) {
let entry = actor.menu ? actor : actor.get_parent(); let entry = actor._menu ? actor : actor.get_parent();
if (state == Clutter.LongPressState.QUERY) if (state == Clutter.LongPressState.QUERY)
return action.get_button() == 1 && !entry.menu.isOpen; return action.get_button() == 1 && !entry._menu.isOpen;
if (state == Clutter.LongPressState.ACTIVATE) { if (state == Clutter.LongPressState.ACTIVATE) {
let [stageX, stageY] = action.get_coords(); let [stageX, stageY] = action.get_coords();
_setMenuAlignment(entry, stageX); _setMenuAlignment(entry, stageX);
entry.menu.open(); entry._menu.open();
} }
return false; return false;
}; };
function _onPopup(actor) { function _onPopup(actor) {
let entry = actor.menu ? actor : actor.get_parent(); let entry = actor._menu ? actor : actor.get_parent();
let [success, textX, textY, lineHeight] = entry.clutter_text.position_to_coords(-1); let [success, textX, textY, lineHeight] = entry.clutter_text.position_to_coords(-1);
if (success) if (success)
entry.menu.setSourceAlignment(textX / entry.width); entry._menu.setSourceAlignment(textX / entry.width);
entry.menu.open(); entry._menu.open();
}; };
function addContextMenu(entry, params) { function addContextMenu(entry, params) {
if (entry.menu) if (entry._menu)
return; return;
entry.menu = new EntryMenu(entry, params); entry._menu = new _EntryMenu(entry, params);
entry._menuManager = new PopupMenu.PopupMenuManager({ actor: entry }); entry._menuManager = new PopupMenu.PopupMenuManager({ actor: entry });
entry._menuManager.addMenu(entry.menu); entry._menuManager.addMenu(entry._menu);
let clickAction; let clickAction;

View File

@@ -1,21 +1,17 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Lang = imports.lang; const Lang = imports.lang;
const Signals = imports.signals; const Signals = imports.signals;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk; const Gtk = imports.gi.Gtk;
const Pango = imports.gi.Pango; const Pango = imports.gi.Pango;
const St = imports.gi.St; const St = imports.gi.St;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const CheckBox = imports.ui.checkBox;
const Main = imports.ui.main; const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray; const MessageTray = imports.ui.messageTray;
const ModalDialog = imports.ui.modalDialog; const ModalDialog = imports.ui.modalDialog;
const Params = imports.misc.params; const Params = imports.misc.params;
const ShellEntry = imports.ui.shellEntry;
const LIST_ITEM_ICON_SIZE = 48; const LIST_ITEM_ICON_SIZE = 48;
@@ -52,11 +48,6 @@ function _setLabelsForMessage(dialog, message) {
_setLabelText(dialog.descriptionLabel, labels[1]); _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({ const ListItem = new Lang.Class({
@@ -100,11 +91,11 @@ const ShellMountOperation = new Lang.Class({
Name: 'ShellMountOperation', Name: 'ShellMountOperation',
_init: function(source, params) { _init: function(source, params) {
params = Params.parse(params, { existingDialog: null }); params = Params.parse(params, { reaskPassword: false });
this._reaskPassword = params.reaskPassword;
this._dialog = null; this._dialog = null;
this._dialogId = 0;
this._existingDialog = params.existingDialog;
this._processesDialog = null; this._processesDialog = null;
this.mountOp = new Shell.MountOperation(); this.mountOp = new Shell.MountOperation();
@@ -116,177 +107,99 @@ const ShellMountOperation = new Lang.Class({
this.mountOp.connect('show-processes-2', this.mountOp.connect('show-processes-2',
Lang.bind(this, this._onShowProcesses2)); Lang.bind(this, this._onShowProcesses2));
this.mountOp.connect('aborted', this.mountOp.connect('aborted',
Lang.bind(this, this.close)); Lang.bind(this, this._onAborted));
this.mountOp.connect('show-unmount-progress',
Lang.bind(this, this._onShowUnmountProgress));
this._gicon = source.get_icon(); this._icon = new St.Icon({ gicon: source.get_icon(),
}, style_class: 'shell-mount-operation-icon' });
_closeExistingDialog: function() {
if (!this._existingDialog)
return;
this._existingDialog.close();
this._existingDialog = null;
}, },
_onAskQuestion: function(op, message, choices) { _onAskQuestion: function(op, message, choices) {
this._closeExistingDialog(); this._dialog = new ShellMountQuestionDialog(this._icon);
this._dialog = new ShellMountQuestionDialog(this._gicon);
this._dialogId = this._dialog.connect('response', Lang.bind(this, this._dialog.connect('response',
function(object, choice) { Lang.bind(this, function(object, choice) {
this.mountOp.set_choice(choice); this.mountOp.set_choice(choice);
this.mountOp.reply(Gio.MountOperationResult.HANDLED); 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.update(message, choices);
this._dialog.open(); this._dialog.open(global.get_current_time());
}, },
_onAskPassword: function(op, message, defaultUser, defaultDomain, flags) { _onAskPassword: function(op, message) {
if (this._existingDialog) { this._notificationShowing = true;
this._dialog = this._existingDialog; this._source = new ShellMountPasswordSource(message, this._icon, this._reaskPassword);
this._dialog.reaskPassword();
} else {
this._dialog = new ShellMountPasswordDialog(message, this._gicon, flags);
}
this._dialogId = this._dialog.connect('response', Lang.bind(this, this._source.connect('password-ready',
function(object, choice, password, remember) { Lang.bind(this, function(source, password) {
if (choice == -1) { this.mountOp.set_password(password);
this.mountOp.reply(Gio.MountOperationResult.ABORTED); this.mountOp.reply(Gio.MountOperationResult.HANDLED);
} else {
if (remember)
this.mountOp.set_password_save(Gio.PasswordSave.PERMANENTLY);
else
this.mountOp.set_password_save(Gio.PasswordSave.NEVER);
this.mountOp.set_password(password); this._notificationShowing = false;
this.mountOp.reply(Gio.MountOperationResult.HANDLED); this._source.destroy();
} }));
}));
this._dialog.open(); this._source.connect('destroy',
Lang.bind(this, function() {
if (!this._notificationShowing)
return;
this._notificationShowing = false;
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
}));
}, },
close: function(op) { _onAborted: function(op) {
this._closeExistingDialog(); if (!this._dialog)
this._processesDialog = null; return;
if (this._dialog) { this._dialog.close(global.get_current_time());
this._dialog.close(); this._dialog = null;
this._dialog = null;
}
if (this._notifier) {
this._notifier.done();
this._notifier = null;
}
}, },
_onShowProcesses2: function(op) { _onShowProcesses2: function(op) {
this._closeExistingDialog();
let processes = op.get_show_processes_pids(); let processes = op.get_show_processes_pids();
let choices = op.get_show_processes_choices(); let choices = op.get_show_processes_choices();
let message = op.get_show_processes_message(); let message = op.get_show_processes_message();
if (!this._processesDialog) { if (!this._processesDialog) {
this._processesDialog = new ShellProcessesDialog(this._gicon); this._processesDialog = new ShellProcessesDialog(this._icon);
this._dialog = this._processesDialog; this._dialog = this._processesDialog;
this._dialogId = this._processesDialog.connect('response', Lang.bind(this, this._processesDialog.connect('response',
function(object, choice) { Lang.bind(this, function(object, choice) {
if (choice == -1) { if (choice == -1) {
this.mountOp.reply(Gio.MountOperationResult.ABORTED); this.mountOp.reply(Gio.MountOperationResult.ABORTED);
} else { } else {
this.mountOp.set_choice(choice); this.mountOp.set_choice(choice);
this.mountOp.reply(Gio.MountOperationResult.HANDLED); this.mountOp.reply(Gio.MountOperationResult.HANDLED);
} }
this.close(); this._processesDialog.close(global.get_current_time());
})); this._dialog = null;
this._processesDialog.open(); }));
this._processesDialog.open(global.get_current_time());
} }
this._processesDialog.update(message, processes, choices); 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', St.IconType.FULLCOLOR);
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({ const ShellMountQuestionDialog = new Lang.Class({
Name: 'ShellMountQuestionDialog', Name: 'ShellMountQuestionDialog',
Extends: ModalDialog.ModalDialog, Extends: ModalDialog.ModalDialog,
_init: function(gicon) { _init: function(icon) {
this.parent({ styleClass: 'mount-question-dialog' }); this.parent({ styleClass: 'mount-question-dialog' });
let mainContentLayout = new St.BoxLayout(); let mainContentLayout = new St.BoxLayout();
this.contentLayout.add(mainContentLayout, { x_fill: true, this.contentLayout.add(mainContentLayout, { x_fill: true,
y_fill: false }); y_fill: false });
this._iconBin = new St.Bin({ child: _createIcon(gicon) }); this._iconBin = new St.Bin({ child: icon });
mainContentLayout.add(this._iconBin, mainContentLayout.add(this._iconBin,
{ x_fill: true, { x_fill: true,
y_fill: false, y_fill: false,
@@ -321,108 +234,62 @@ const ShellMountQuestionDialog = new Lang.Class({
}); });
Signals.addSignalMethods(ShellMountQuestionDialog.prototype); Signals.addSignalMethods(ShellMountQuestionDialog.prototype);
const ShellMountPasswordDialog = new Lang.Class({ const ShellMountPasswordSource = new Lang.Class({
Name: 'ShellMountPasswordDialog', Name: 'ShellMountPasswordSource',
Extends: ModalDialog.ModalDialog, Extends: MessageTray.Source,
_init: function(message, gicon, flags) { _init: function(message, icon, reaskPassword) {
let strings = message.split('\n'); 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', this._notification = new ShellMountPasswordNotification(this, strings, icon, reaskPassword);
vertical: false });
this.contentLayout.add(mainContentBox);
let icon = _createIcon(gicon); // add ourselves as a source, and popup the notification
mainContentBox.add(icon, Main.messageTray.add(this);
{ x_fill: true, this.notify(this._notification);
y_fill: false, },
x_align: St.Align.END, });
y_align: St.Align.START }); Signals.addSignalMethods(ShellMountPasswordSource.prototype);
this._messageBox = new St.BoxLayout({ style_class: 'prompt-dialog-message-layout', const ShellMountPasswordNotification = new Lang.Class({
vertical: true }); Name: 'ShellMountPasswordNotification',
mainContentBox.add(this._messageBox, Extends: MessageTray.Notification,
{ y_align: St.Align.START, expand: true, x_fill: true, y_fill: true });
let subject = new St.Label({ style_class: 'prompt-dialog-headline' }); _init: function(source, strings, icon, reaskPassword) {
this._messageBox.add(subject, this.parent(source, strings[0], null, { customContent: true, icon: icon });
{ y_fill: false,
y_align: St.Align.START }); // set the notification to transient and urgent, so that it
if (strings[0]) // expands out
subject.set_text(strings[0]); 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]) if (strings[1])
description.set_text(strings[1]); this.addBody(strings[1]);
this._passwordBox = new St.BoxLayout({ vertical: false }); if (reaskPassword) {
this._messageBox.add(this._passwordBox); 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', this.addActor(label);
text: _("Passphrase") }));
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._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 Passphrase");
this._rememberChoice.actor.checked = true;
this._messageBox.add(this._rememberChoice.actor);
} else {
this._rememberChoice = null;
} }
let buttons = [{ label: _("Cancel"), this._responseEntry = new St.Entry({ style_class: 'mount-password-entry',
action: Lang.bind(this, this._onCancelButton), can_focus: true });
key: Clutter.Escape this.setActionArea(this._responseEntry);
},
{ label: _("Unlock"),
action: Lang.bind(this, this._onUnlockButton),
default: true
}];
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() { _onEntryActivated: function() {
this._passwordEntry.set_text(''); let text = this._responseEntry.get_text();
this._errorMessageLabel.show(); if (text == '')
}, return;
_onCancelButton: function() { this.source.emit('password-ready', text);
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);
} }
}); });
@@ -430,14 +297,14 @@ const ShellProcessesDialog = new Lang.Class({
Name: 'ShellProcessesDialog', Name: 'ShellProcessesDialog',
Extends: ModalDialog.ModalDialog, Extends: ModalDialog.ModalDialog,
_init: function(gicon) { _init: function(icon) {
this.parent({ styleClass: 'show-processes-dialog' }); this.parent({ styleClass: 'show-processes-dialog' });
let mainContentLayout = new St.BoxLayout(); let mainContentLayout = new St.BoxLayout();
this.contentLayout.add(mainContentLayout, { x_fill: true, this.contentLayout.add(mainContentLayout, { x_fill: true,
y_fill: false }); y_fill: false });
this._iconBin = new St.Bin({ child: _createIcon(gicon) }); this._iconBin = new St.Bin({ child: icon });
mainContentLayout.add(this._iconBin, mainContentLayout.add(this._iconBin,
{ x_fill: true, { x_fill: true,
y_fill: false, y_fill: false,
@@ -471,17 +338,21 @@ const ShellProcessesDialog = new Lang.Class({
scrollView.hide(); scrollView.hide();
this._applicationList = new St.BoxLayout({ vertical: true }); 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', this._applicationList.connect('actor-added',
Lang.bind(this, function() { Lang.bind(this, function() {
if (this._applicationList.get_n_children() == 1) if (this._applicationList.get_children().length == 1)
scrollView.show(); scrollView.show();
})); }));
this._applicationList.connect('actor-removed', this._applicationList.connect('actor-removed',
Lang.bind(this, function() { Lang.bind(this, function() {
if (this._applicationList.get_n_children() == 0) if (this._applicationList.get_children().length == 0)
scrollView.hide(); scrollView.hide();
})); }));
}, },
@@ -515,253 +386,3 @@ const ShellProcessesDialog = new Lang.Class({
} }
}); });
Signals.addSignalMethods(ShellProcessesDialog.prototype); 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 -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const GDesktopEnums = imports.gi.GDesktopEnums;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang; 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 PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
const Util = imports.misc.util;
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard'; const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
const KEY_STICKY_KEYS_ENABLED = 'stickykeys-enable'; const KEY_STICKY_KEYS_ENABLED = 'stickykeys-enable';
@@ -48,9 +56,9 @@ const ATIndicator = new Lang.Class({
let textZoom = this._buildFontItem(); let textZoom = this._buildFontItem();
this.menu.addMenuItem(textZoom); this.menu.addMenuItem(textZoom);
let screenReader = this._buildItem(_("Screen Reader"), APPLICATIONS_SCHEMA, // let screenReader = this._buildItem(_("Screen Reader"), APPLICATIONS_SCHEMA,
'screen-reader-enabled'); // 'screen-reader-enabled');
this.menu.addMenuItem(screenReader); // this.menu.addMenuItem(screenReader);
let screenKeyboard = this._buildItem(_("Screen Keyboard"), APPLICATIONS_SCHEMA, let screenKeyboard = this._buildItem(_("Screen Keyboard"), APPLICATIONS_SCHEMA,
'screen-keyboard-enabled'); 'screen-keyboard-enabled');
@@ -75,10 +83,6 @@ const ATIndicator = new Lang.Class({
this.menu.addSettingsAction(_("Universal Access Settings"), 'gnome-universal-access-panel.desktop'); this.menu.addSettingsAction(_("Universal Access Settings"), 'gnome-universal-access-panel.desktop');
}, },
setLockedState: function(locked) {
this.menu.setSettingsVisibility(!locked);
},
_buildItemExtended: function(string, initial_value, writable, on_set) { _buildItemExtended: function(string, initial_value, writable, on_set) {
let widget = new PopupMenu.PopupSwitchMenuItem(string, initial_value); let widget = new PopupMenu.PopupSwitchMenuItem(string, initial_value);
if (!writable) if (!writable)

View File

@@ -1,11 +1,15 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Gdk = imports.gi.Gdk;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const GnomeBluetoothApplet = imports.gi.GnomeBluetoothApplet; const GnomeBluetoothApplet = imports.gi.GnomeBluetoothApplet;
const GnomeBluetooth = imports.gi.GnomeBluetooth; const Gtk = imports.gi.Gtk;
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop;
const St = imports.gi.St; const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Main = imports.ui.main; const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray; const MessageTray = imports.ui.messageTray;
@@ -32,11 +36,11 @@ const Indicator = new Lang.Class({
this._applet.connect('notify::killswitch-state', Lang.bind(this, this._updateKillswitch)); this._applet.connect('notify::killswitch-state', Lang.bind(this, this._updateKillswitch));
this._killswitch.connect('toggled', Lang.bind(this, function() { this._killswitch.connect('toggled', Lang.bind(this, function() {
let current_state = this._applet.killswitch_state; let current_state = this._applet.killswitch_state;
if (current_state != GnomeBluetooth.KillswitchState.HARD_BLOCKED && if (current_state != GnomeBluetoothApplet.KillswitchState.HARD_BLOCKED &&
current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER) { current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER) {
this._applet.killswitch_state = this._killswitch.state ? this._applet.killswitch_state = this._killswitch.state ?
GnomeBluetooth.KillswitchState.UNBLOCKED: GnomeBluetoothApplet.KillswitchState.UNBLOCKED:
GnomeBluetooth.KillswitchState.SOFT_BLOCKED; GnomeBluetoothApplet.KillswitchState.SOFT_BLOCKED;
} else } else
this._killswitch.setToggleState(false); this._killswitch.setToggleState(false);
})); }));
@@ -88,17 +92,12 @@ const Indicator = new Lang.Class({
this._applet.connect('cancel-request', Lang.bind(this, this._cancelRequest)); this._applet.connect('cancel-request', Lang.bind(this, this._cancelRequest));
}, },
setLockedState: function(locked) {
this._isLocked = locked;
this._updateKillswitch();
},
_updateKillswitch: function() { _updateKillswitch: function() {
let current_state = this._applet.killswitch_state; let current_state = this._applet.killswitch_state;
let on = current_state == GnomeBluetooth.KillswitchState.UNBLOCKED; let on = current_state == GnomeBluetoothApplet.KillswitchState.UNBLOCKED;
let has_adapter = current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER; let has_adapter = current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER;
let can_toggle = current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER && let can_toggle = current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER &&
current_state != GnomeBluetooth.KillswitchState.HARD_BLOCKED; current_state != GnomeBluetoothApplet.KillswitchState.HARD_BLOCKED;
this._killswitch.setToggleState(on); this._killswitch.setToggleState(on);
if (can_toggle) if (can_toggle)
@@ -107,7 +106,10 @@ const Indicator = new Lang.Class({
/* TRANSLATORS: this means that bluetooth was disabled by hardware rfkill */ /* TRANSLATORS: this means that bluetooth was disabled by hardware rfkill */
this._killswitch.setStatus(_("hardware disabled")); this._killswitch.setStatus(_("hardware disabled"));
this.actor.visible = !this._isLocked && has_adapter; if (has_adapter)
this.actor.show();
else
this.actor.hide();
if (on) { if (on) {
this._discoverable.actor.show(); this._discoverable.actor.show();
@@ -306,7 +308,7 @@ const Indicator = new Lang.Class({
_ensureSource: function() { _ensureSource: function() {
if (!this._source) { if (!this._source) {
this._source = new MessageTray.Source(_("Bluetooth"), 'bluetooth-active', St.IconType.SYMBOLIC); this._source = new Source();
Main.messageTray.add(this._source); Main.messageTray.add(this._source);
} }
}, },
@@ -331,6 +333,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({ const AuthNotification = new Lang.Class({
Name: 'AuthNotification', Name: 'AuthNotification',
Extends: MessageTray.Notification, Extends: MessageTray.Notification,
@@ -381,7 +412,7 @@ const ConfirmNotification = new Lang.Class({
this._applet = applet; this._applet = applet;
this._devicePath = device_path; this._devicePath = device_path;
this.addBody(_("Device %s wants to pair with this computer").format(long_name)); 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('matches', _("Matches"));
this.addButton('does-not-match', _("Does not match")); this.addButton('does-not-match', _("Does not match"));
@@ -417,8 +448,7 @@ const PinNotification = new Lang.Class({
this._entry.connect('key-release-event', Lang.bind(this, function(entry, event) { this._entry.connect('key-release-event', Lang.bind(this, function(entry, event) {
let key = event.get_key_symbol(); let key = event.get_key_symbol();
if (key == Clutter.KEY_Return) { if (key == Clutter.KEY_Return) {
if (this._canActivateOkButton()) this.emit('action-invoked', 'ok');
this.emit('action-invoked', 'ok');
return true; return true;
} else if (key == Clutter.KEY_Escape) { } else if (key == Clutter.KEY_Escape) {
this.emit('action-invoked', 'cancel'); this.emit('action-invoked', 'cancel');
@@ -431,12 +461,6 @@ const PinNotification = new Lang.Class({
this.addButton('ok', _("OK")); this.addButton('ok', _("OK"));
this.addButton('cancel', _("Cancel")); 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) { this.connect('action-invoked', Lang.bind(this, function(self, action) {
if (action == 'ok') { if (action == 'ok') {
if (this._numeric) { if (this._numeric) {
@@ -459,11 +483,6 @@ const PinNotification = new Lang.Class({
})); }));
}, },
_canActivateOkButton: function() {
// PINs have a fixed length of 6
return this._entry.clutter_text.text.length == 6;
},
grabFocus: function(lockTray) { grabFocus: function(lockTray) {
this.parent(lockTray); this.parent(lockTray);
global.stage.set_key_focus(this._entry); global.stage.set_key_focus(this._entry);

View File

@@ -1,149 +1,47 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GdkPixbuf = imports.gi.GdkPixbuf;
const Gkbd = imports.gi.Gkbd;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const GnomeDesktop = imports.gi.GnomeDesktop;
const Lang = imports.lang; const Lang = imports.lang;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const St = imports.gi.St; const St = imports.gi.St;
try {
var IBus = imports.gi.IBus;
if (!('new_async' in IBus.Bus))
throw "IBus version is too old";
const IBusCandidatePopup = imports.ui.ibusCandidatePopup;
} catch (e) {
var IBus = null;
log(e);
}
const Main = imports.ui.main; const Main = imports.ui.main;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
const PanelMenu = imports.ui.panelMenu; const PanelMenu = imports.ui.panelMenu;
const Util = imports.misc.util; const Util = imports.misc.util;
const DESKTOP_INPUT_SOURCES_SCHEMA = 'org.gnome.desktop.input-sources';
const KEY_CURRENT_INPUT_SOURCE = 'current';
const KEY_INPUT_SOURCES = 'sources';
const INPUT_SOURCE_TYPE_XKB = 'xkb';
const INPUT_SOURCE_TYPE_IBUS = 'ibus';
const IBusManager = new Lang.Class({
Name: 'IBusManager',
_init: function(readyCallback) {
if (!IBus)
return;
IBus.init();
this._readyCallback = readyCallback;
this._candidatePopup = new IBusCandidatePopup.CandidatePopup();
this._ibus = null;
this._panelService = null;
this._engines = {};
this._ready = false;
this._nameWatcherId = Gio.DBus.session.watch_name(IBus.SERVICE_IBUS,
Gio.BusNameWatcherFlags.NONE,
Lang.bind(this, this._onNameAppeared),
Lang.bind(this, this._clear));
},
_clear: function() {
if (this._panelService)
this._panelService.destroy();
if (this._ibus)
this._ibus.destroy();
this._ibus = null;
this._panelService = null;
this._candidatePopup.setPanelService(null);
this._engines = {};
this._ready = false;
},
_onNameAppeared: function() {
this._ibus = IBus.Bus.new_async();
this._ibus.connect('connected', Lang.bind(this, this._onConnected));
},
_onConnected: function() {
this._ibus.list_engines_async(-1, null, Lang.bind(this, this._initEngines));
this._ibus.request_name_async(IBus.SERVICE_PANEL,
IBus.BusNameFlag.REPLACE_EXISTING,
-1, null,
Lang.bind(this, this._initPanelService));
this._ibus.connect('disconnected', Lang.bind(this, this._clear));
},
_initEngines: function(ibus, result) {
let enginesList = this._ibus.list_engines_async_finish(result);
if (enginesList) {
for (let i = 0; i < enginesList.length; ++i) {
let name = enginesList[i].get_name();
this._engines[name] = enginesList[i];
}
} else {
this._clear();
return;
}
this._updateReadiness();
},
_initPanelService: function(ibus, result) {
let success = this._ibus.request_name_async_finish(result);
if (success) {
this._panelService = new IBus.PanelService({ connection: this._ibus.get_connection(),
object_path: IBus.PATH_PANEL });
this._candidatePopup.setPanelService(this._panelService);
} else {
this._clear();
return;
}
this._updateReadiness();
},
_updateReadiness: function() {
this._ready = (Object.keys(this._engines).length > 0 &&
this._panelService != null);
if (this._ready && this._readyCallback)
this._readyCallback();
},
getEngineDesc: function(id) {
if (!IBus || !this._ready)
return null;
return this._engines[id];
}
});
const LayoutMenuItem = new Lang.Class({ const LayoutMenuItem = new Lang.Class({
Name: 'LayoutMenuItem', Name: 'LayoutMenuItem',
Extends: PopupMenu.PopupBaseMenuItem, Extends: PopupMenu.PopupBaseMenuItem,
_init: function(displayName, shortName) { _init: function(config, id, indicator, long_name) {
this.parent(); this.parent();
this.label = new St.Label({ text: displayName }); this._config = config;
this.indicator = new St.Label({ text: shortName }); this._id = id;
this.label = new St.Label({ text: long_name });
this.indicator = indicator;
this.addActor(this.label); this.addActor(this.label);
this.addActor(this.indicator); this.addActor(this.indicator);
},
activate: function(event) {
this.parent(event);
this._config.lock_group(this._id);
} }
}); });
const InputSourceIndicator = new Lang.Class({ const XKBIndicator = new Lang.Class({
Name: 'InputSourceIndicator', Name: 'XKBIndicator',
Extends: PanelMenu.Button, Extends: PanelMenu.Button,
_init: function() { _init: function() {
this.parent(0.0, _("Keyboard")); this.parent(0.0);
this._container = new Shell.GenericContainer(); this._container = new Shell.GenericContainer();
this._container.connect('get-preferred-width', Lang.bind(this, this._containerGetPreferredWidth)); this._container.connect('get-preferred-width', Lang.bind(this, this._containerGetPreferredWidth));
@@ -152,169 +50,122 @@ const InputSourceIndicator = new Lang.Class({
this.actor.add_actor(this._container); this.actor.add_actor(this._container);
this.actor.add_style_class_name('panel-status-button'); this.actor.add_style_class_name('panel-status-button');
this._labelActors = {}; this._iconActor = new St.Icon({ icon_name: 'keyboard', icon_type: St.IconType.SYMBOLIC, style_class: 'system-status-icon' });
this._layoutItems = {}; this._container.add_actor(this._iconActor);
this._labelActors = [ ];
this._layoutItems = [ ];
this._settings = new Gio.Settings({ schema: DESKTOP_INPUT_SOURCES_SCHEMA }); this._showFlags = false;
this._settings.connect('changed::' + KEY_CURRENT_INPUT_SOURCE, Lang.bind(this, this._currentInputSourceChanged)); this._config = Gkbd.Configuration.get();
this._settings.connect('changed::' + KEY_INPUT_SOURCES, Lang.bind(this, this._inputSourcesChanged)); this._config.connect('changed', Lang.bind(this, this._syncConfig));
this._config.connect('group-changed', Lang.bind(this, this._syncGroup));
this._config.start_listen();
this._currentSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE); this._syncConfig();
this._xkbInfo = new GnomeDesktop.XkbInfo();
this._ibusManager = new IBusManager(Lang.bind(this, this._inputSourcesChanged)); if (global.session_type == Shell.SessionType.USER) {
this._inputSourcesChanged();
// re-using "allowSettings" for the keyboard layout is a bit shady,
// but at least for now it is used as "allow popping up windows
// from shell menus"; we can always add a separate sessionMode
// option if need arises.
if (Main.sessionMode.allowSettings) {
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._showLayoutItem = this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, this._showLayout)); this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, function() {
Main.overview.hide();
Util.spawn(['gkbd-keyboard-display', '-g', String(this._config.get_current_group() + 1)]);
}));
} }
this.menu.addSettingsAction(_("Region and Language Settings"), 'gnome-region-panel.desktop'); this.menu.addSettingsAction(_("Region and Language Settings"), 'gnome-region-panel.desktop');
}, },
setLockedState: function(locked) { _adjustGroupNames: function(names) {
this._showLayoutItem.actor.visible = !locked; // Disambiguate duplicate names with a subscript
this.menu.setSettingsVisibility(!locked); // This is O(N^2) to avoid sorting names
}, // but N <= 4 so who cares?
_currentInputSourceChanged: function() { for (let i = 0; i < names.length; i++) {
let nVisibleSources = Object.keys(this._layoutItems).length; let name = names[i];
if (nVisibleSources < 2) let cnt = 0;
return; for (let j = i + 1; j < names.length; j++) {
if (names[j] == name) {
let nSources = this._settings.get_value(KEY_INPUT_SOURCES).n_children(); cnt++;
let newCurrentSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE); // U+2081 SUBSCRIPT ONE
if (newCurrentSourceIndex >= nSources) names[j] = name + String.fromCharCode(0x2081 + cnt);
return;
if (!this._layoutItems[newCurrentSourceIndex]) {
// This source index is invalid as we weren't able to
// build a menu item for it, so we hide ourselves since we
// can't fix it here. *shrug*
this.menu.close();
this.actor.hide();
return;
} else {
this.actor.show();
}
if (this._layoutItems[this._currentSourceIndex]) {
this._layoutItems[this._currentSourceIndex].setShowDot(false);
this._container.set_skip_paint(this._labelActors[this._currentSourceIndex], true);
}
this._layoutItems[newCurrentSourceIndex].setShowDot(true);
this._container.set_skip_paint(this._labelActors[newCurrentSourceIndex], false);
this._currentSourceIndex = newCurrentSourceIndex;
},
_inputSourcesChanged: function() {
let sources = this._settings.get_value(KEY_INPUT_SOURCES);
let nSources = sources.n_children();
for (let i in this._layoutItems)
this._layoutItems[i].destroy();
for (let i in this._labelActors)
this._labelActors[i].destroy();
this._layoutItems = {};
this._labelActors = {};
let infos = [];
let infosByShortName = {};
for (let i = 0; i < nSources; i++) {
let info = { exists: false };
let [type, id] = sources.get_child_value(i).deep_unpack();
if (type == INPUT_SOURCE_TYPE_XKB) {
[info.exists, info.displayName, info.shortName, , ] =
this._xkbInfo.get_layout_info(id);
} else if (type == INPUT_SOURCE_TYPE_IBUS) {
let engineDesc = this._ibusManager.getEngineDesc(id);
if (engineDesc) {
info.exists = true;
info.displayName = engineDesc.get_longname();
info.shortName = engineDesc.get_symbol();
} }
} }
if (cnt != 0)
if (!info.exists) names[i] = name + '\u2081';
continue;
info.sourceIndex = i;
if (!(info.shortName in infosByShortName))
infosByShortName[info.shortName] = [];
infosByShortName[info.shortName].push(info);
infos.push(info);
} }
if (infos.length > 1) { return names;
},
_syncConfig: function() {
this._showFlags = this._config.if_flags_shown();
if (this._showFlags) {
this._container.set_skip_paint(this._iconActor, false);
} else {
this._container.set_skip_paint(this._iconActor, true);
}
let groups = this._config.get_group_names();
if (groups.length > 1) {
this.actor.show(); this.actor.show();
} else { } else {
this.menu.close(); this.menu.close();
this.actor.hide(); this.actor.hide();
} }
for (let i = 0; i < infos.length; i++) { for (let i = 0; i < this._layoutItems.length; i++)
let info = infos[i]; this._layoutItems[i].destroy();
if (infosByShortName[info.shortName].length > 1) {
let sub = infosByShortName[info.shortName].indexOf(info) + 1;
info.shortName += String.fromCharCode(0x2080 + sub);
}
let item = new LayoutMenuItem(info.displayName, info.shortName); for (let i = 0; i < this._labelActors.length; i++)
this._layoutItems[info.sourceIndex] = item; this._labelActors[i].destroy();
let short_names = this._adjustGroupNames(this._config.get_short_group_names());
this._selectedLayout = null;
this._layoutItems = [ ];
this._selectedLabel = null;
this._labelActors = [ ];
for (let i = 0; i < groups.length; i++) {
let icon_name = this._config.get_group_name(i);
let actor;
if (this._showFlags)
actor = new St.Icon({ icon_name: icon_name, icon_type: St.IconType.SYMBOLIC, style_class: 'popup-menu-icon' });
else
actor = new St.Label({ text: short_names[i] });
let item = new LayoutMenuItem(this._config, i, actor, groups[i]);
item._short_group_name = short_names[i];
item._icon_name = icon_name;
this._layoutItems.push(item);
this.menu.addMenuItem(item, i); this.menu.addMenuItem(item, i);
item.connect('activate', Lang.bind(this, function() {
this._settings.set_value(KEY_CURRENT_INPUT_SOURCE,
GLib.Variant.new_uint32(info.sourceIndex));
}));
let shortLabel = new St.Label({ text: info.shortName }); let shortLabel = new St.Label({ text: short_names[i] });
this._labelActors[info.sourceIndex] = shortLabel; this._labelActors.push(shortLabel);
this._container.add_actor(shortLabel); this._container.add_actor(shortLabel);
this._container.set_skip_paint(shortLabel, true); this._container.set_skip_paint(shortLabel, true);
} }
this._currentInputSourceChanged(); this._syncGroup();
}, },
_showLayout: function() { _syncGroup: function() {
Main.overview.hide(); let selected = this._config.get_current_group();
let sources = this._settings.get_value(KEY_INPUT_SOURCES); if (this._selectedLayout) {
let current = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE); this._selectedLayout.setShowDot(false);
let [type, id] = sources.get_child_value(current).deep_unpack(); this._selectedLayout = null;
let xkbLayout = '';
let xkbVariant = '';
if (type == INPUT_SOURCE_TYPE_XKB) {
[, , , xkbLayout, xkbVariant] = this._xkbInfo.get_layout_info(id);
} else if (type == INPUT_SOURCE_TYPE_IBUS) {
let engineDesc = this._ibusManager.getEngineDesc(id);
if (engineDesc) {
xkbLayout = engineDesc.get_layout();
xkbVariant = '';
}
} }
if (!xkbLayout || xkbLayout.length == 0) if (this._selectedLabel) {
return; this._container.set_skip_paint(this._selectedLabel, true);
this._selectedLabel = null;
}
let description = xkbLayout; let item = this._layoutItems[selected];
if (xkbVariant.length > 0) item.setShowDot(true);
description = description + '\t' + xkbVariant;
Util.spawn(['gkbd-keyboard-display', '-l', description]); this._iconActor.icon_name = item._icon_name;
this._selectedLabel = this._labelActors[selected];
this._container.set_skip_paint(this._selectedLabel, this._showFlags);
this._selectedLayout = item;
}, },
_containerGetPreferredWidth: function(container, for_height, alloc) { _containerGetPreferredWidth: function(container, for_height, alloc) {
@@ -322,11 +173,15 @@ const InputSourceIndicator = new Lang.Class({
// for the height of all children, but we ignore the results // for the height of all children, but we ignore the results
// for those we don't actually display. // for those we don't actually display.
let max_min_width = 0, max_natural_width = 0; let max_min_width = 0, max_natural_width = 0;
if (this._showFlags)
[max_min_width, max_natural_width] = this._iconActor.get_preferred_width(for_height);
for (let i in this._labelActors) { for (let i = 0; i < this._labelActors.length; i++) {
let [min_width, natural_width] = this._labelActors[i].get_preferred_width(for_height); let [min_width, natural_width] = this._labelActors[i].get_preferred_width(for_height);
max_min_width = Math.max(max_min_width, min_width); if (!this._showFlags) {
max_natural_width = Math.max(max_natural_width, natural_width); max_min_width = Math.max(max_min_width, min_width);
max_natural_width = Math.max(max_natural_width, natural_width);
}
} }
alloc.min_size = max_min_width; alloc.min_size = max_min_width;
@@ -335,11 +190,15 @@ const InputSourceIndicator = new Lang.Class({
_containerGetPreferredHeight: function(container, for_width, alloc) { _containerGetPreferredHeight: function(container, for_width, alloc) {
let max_min_height = 0, max_natural_height = 0; let max_min_height = 0, max_natural_height = 0;
if (this._showFlags)
for (let i in this._labelActors) { [max_min_height, max_natural_height] = this._iconActor.get_preferred_height(for_width);
for (let i = 0; i < this._labelActors.length; i++) {
let [min_height, natural_height] = this._labelActors[i].get_preferred_height(for_width); let [min_height, natural_height] = this._labelActors[i].get_preferred_height(for_width);
max_min_height = Math.max(max_min_height, min_height); if (!this._showFlags) {
max_natural_height = Math.max(max_natural_height, natural_height); max_min_height = Math.max(max_min_height, min_height);
max_natural_height = Math.max(max_natural_height, natural_height);
}
} }
alloc.min_size = max_min_height; alloc.min_size = max_min_height;
@@ -353,7 +212,8 @@ const InputSourceIndicator = new Lang.Class({
box.y2 -= box.y1; box.y2 -= box.y1;
box.y1 = 0; box.y1 = 0;
for (let i in this._labelActors) this._iconActor.allocate_align_fill(box, 0.5, 0, false, false, flags);
for (let i = 0; i < this._labelActors.length; i++)
this._labelActors[i].allocate_align_fill(box, 0.5, 0, false, false, flags); this._labelActors[i].allocate_align_fill(box, 0.5, 0, false, false, flags);
} }
}); });

View File

@@ -1,9 +1,12 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const ByteArray = imports.byteArray;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject; const GObject = imports.gi.GObject;
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop;
const NetworkManager = imports.gi.NetworkManager; const NetworkManager = imports.gi.NetworkManager;
const NMClient = imports.gi.NMClient; const NMClient = imports.gi.NMClient;
const Shell = imports.gi.Shell;
const Signals = imports.signals; const Signals = imports.signals;
const St = imports.gi.St; const St = imports.gi.St;
@@ -98,10 +101,11 @@ const NMNetworkMenuItem = new Lang.Class({
Name: 'NMNetworkMenuItem', Name: 'NMNetworkMenuItem',
Extends: PopupMenu.PopupBaseMenuItem, Extends: PopupMenu.PopupBaseMenuItem,
_init: function(bestAP, title, params) { _init: function(accessPoints, title, params) {
this.parent(params); this.parent(params);
this.bestAP = bestAP; accessPoints = sortAccessPoints(accessPoints);
this.bestAP = accessPoints[0];
if (!title) { if (!title) {
let ssid = this.bestAP.get_ssid(); let ssid = this.bestAP.get_ssid();
@@ -123,10 +127,24 @@ const NMNetworkMenuItem = new Lang.Class({
this.bestAP._secType != NMAccessPointSecurity.NONE) this.bestAP._secType != NMAccessPointSecurity.NONE)
this._secureIcon.icon_name = 'network-wireless-encrypted'; this._secureIcon.icon_name = 'network-wireless-encrypted';
this._icons.add_actor(this._secureIcon); this._icons.add_actor(this._secureIcon);
this._accessPoints = [ ];
for (let i = 0; i < accessPoints.length; i++) {
let ap = accessPoints[i];
// need a wrapper object here, because the access points can be shared
// between many NMNetworkMenuItems
let apObj = {
ap: ap,
updateId: ap.connect('notify::strength', Lang.bind(this, this._updated))
};
this._accessPoints.push(apObj);
}
}, },
updateBestAP: function(ap) { _updated: function(ap) {
this.bestAP = ap; if (ap.strength > this.bestAP.strength)
this.bestAP = ap;
this._signalIcon.icon_name = this._getIcon(); this._signalIcon.icon_name = this._getIcon();
}, },
@@ -135,6 +153,36 @@ const NMNetworkMenuItem = new Lang.Class({
return 'network-workgroup'; return 'network-workgroup';
else else
return 'network-wireless-signal-' + signalToIcon(this.bestAP.strength); return 'network-wireless-signal-' + signalToIcon(this.bestAP.strength);
},
updateAccessPoints: function(accessPoints) {
for (let i = 0; i < this._accessPoints.length; i++) {
let apObj = this._accessPoints[i];
apObj.ap.disconnect(apObj.updateId);
apObj.updateId = 0;
}
accessPoints = sortAccessPoints(accessPoints);
this.bestAP = accessPoints[0];
this._accessPoints = [ ];
for (let i = 0; i < accessPoints; i++) {
let ap = accessPoints[i];
let apObj = {
ap: ap,
updateId: ap.connect('notify::strength', Lang.bind(this, this._updated))
};
this._accessPoints.push(apObj);
}
},
destroy: function() {
for (let i = 0; i < this._accessPoints.length; i++) {
let apObj = this._accessPoints[i];
apObj.ap.disconnect(apObj.updateId);
apObj.updateId = 0;
}
this.parent();
} }
}); });
@@ -249,17 +297,16 @@ const NMDevice = new Lang.Class({
this._client = client; this._client = client;
this._connections = [ ]; this._connections = [ ];
for (let i = 0; i < connections.length; i++) { for (let i = 0; i < connections.length; i++) {
if (!connections[i].get_uuid()) if (!connections[i]._uuid)
continue; continue;
if (!this.connectionValid(connections[i])) if (!this.connectionValid(connections[i]))
continue; continue;
// record the connection // record the connection
let obj = { let obj = {
connection: connections[i], connection: connections[i],
name: connections[i].get_id(), name: connections[i]._name,
uuid: connections[i].get_uuid(), uuid: connections[i]._uuid,
timestamp: connections[i]._timestamp, timestamp: connections[i]._timestamp,
item: null,
}; };
this._connections.push(obj); this._connections.push(obj);
} }
@@ -354,46 +401,48 @@ const NMDevice = new Lang.Class({
}, },
checkConnection: function(connection) { checkConnection: function(connection) {
let pos = this._findConnection(connection.get_uuid()); let pos = this._findConnection(connection._uuid);
let exists = pos != -1; let exists = pos != -1;
let valid = this.connectionValid(connection); let valid = this.connectionValid(connection);
let similar = false;
if (exists) {
let existing = this._connections[pos];
// Check if connection changed name or id if (exists && !valid)
similar = existing.name == connection.get_id() &&
existing.timestamp == connection._timestamp;
}
if (exists && valid && similar) {
// Nothing to do
return;
}
if (exists)
this.removeConnection(connection); this.removeConnection(connection);
if (valid) else if (!exists && valid)
this.addConnection(connection); this.addConnection(connection);
else if (exists && valid) {
// propagate changes and update the UI
if (this._connections[pos].timestamp != connection._timestamp) {
this._connections[pos].timestamp = connection._timestamp;
this._connections.sort(this._connectionSortFunction);
this._clearSection();
this._queueCreateSection();
}
}
}, },
addConnection: function(connection) { addConnection: function(connection) {
// record the connection // record the connection
let obj = { let obj = {
connection: connection, connection: connection,
name: connection.get_id(), name: connection._name,
uuid: connection.get_uuid(), uuid: connection._uuid,
timestamp: connection._timestamp, timestamp: connection._timestamp,
item: null,
}; };
Util.insertSorted(this._connections, obj, this._connectionSortFunction); this._connections.push(obj);
this._connections.sort(this._connectionSortFunction);
this._clearSection(); this._clearSection();
this._queueCreateSection(); this._queueCreateSection();
}, },
removeConnection: function(connection) { removeConnection: function(connection) {
let pos = this._findConnection(connection.get_uuid()); if (!connection._uuid) {
log('Cannot remove a connection without an UUID');
return;
}
let pos = this._findConnection(connection._uuid);
if (pos == -1) { if (pos == -1) {
// this connection was never added, nothing to do here // this connection was never added, nothing to do here
return; return;
@@ -565,7 +614,7 @@ const NMDevice = new Lang.Class({
let title; let title;
let active = this._activeConnection._connection; let active = this._activeConnection._connection;
if (active) { if (active) {
title = active.get_id(); title = active._name;
} else { } else {
/* TRANSLATORS: this is the indication that a connection for another logged in user is active, /* TRANSLATORS: this is the indication that a connection for another logged in user is active,
and we cannot access its settings (including the name) */ and we cannot access its settings (including the name) */
@@ -658,15 +707,18 @@ const NMDeviceWired = new Lang.Class({
// the device // the device
// we can do it here because addConnection and removeConnection // we can do it here because addConnection and removeConnection
// both call _createSection at some point // both call _createSection at some point
this.section.actor.visible = this._connections.length > 1; if (this._connections.length <= 1)
this.section.actor.hide();
else
this.section.actor.show();
}, },
_createAutomaticConnection: function() { _createAutomaticConnection: function() {
let connection = new NetworkManager.Connection(); let connection = new NetworkManager.Connection();
let uuid = NetworkManager.utils_uuid_generate(); connection._uuid = NetworkManager.utils_uuid_generate();
connection.add_setting(new NetworkManager.SettingWired()); connection.add_setting(new NetworkManager.SettingWired());
connection.add_setting(new NetworkManager.SettingConnection({ connection.add_setting(new NetworkManager.SettingConnection({
uuid: uuid, uuid: connection._uuid,
id: this._autoConnectionName, id: this._autoConnectionName,
type: NetworkManager.SETTING_WIRED_SETTING_NAME, type: NetworkManager.SETTING_WIRED_SETTING_NAME,
autoconnect: true autoconnect: true
@@ -810,10 +862,10 @@ const NMDeviceBluetooth = new Lang.Class({
_createAutomaticConnection: function() { _createAutomaticConnection: function() {
let connection = new NetworkManager.Connection; let connection = new NetworkManager.Connection;
let uuid = NetworkManager.utils_uuid_generate(); connection._uuid = NetworkManager.utils_uuid_generate();
connection.add_setting(new NetworkManager.SettingBluetooth); connection.add_setting(new NetworkManager.SettingBluetooth);
connection.add_setting(new NetworkManager.SettingConnection({ connection.add_setting(new NetworkManager.SettingConnection({
uuid: uuid, uuid: connection._uuid,
id: this._autoConnectionName, id: this._autoConnectionName,
type: NetworkManager.SETTING_BLUETOOTH_SETTING_NAME, type: NetworkManager.SETTING_BLUETOOTH_SETTING_NAME,
autoconnect: false autoconnect: false
@@ -848,12 +900,12 @@ const NMDeviceVPN = new Lang.Class({
Name: 'NMDeviceVPN', Name: 'NMDeviceVPN',
Extends: NMDevice, Extends: NMDevice,
_init: function(client, device, connections) { _init: function(client) {
// Disable autoconnections // Disable autoconnections
this._autoConnectionName = null; this._autoConnectionName = null;
this.category = NMConnectionCategory.VPN; this.category = NMConnectionCategory.VPN;
this.parent(client, null, connections); this.parent(client, null, [ ]);
}, },
connectionValid: function(connection) { connectionValid: function(connection) {
@@ -865,24 +917,13 @@ const NMDeviceVPN = new Lang.Class({
}, },
get connected() { get connected() {
if (!this._activeConnection) return !!this._activeConnection;
return false;
return this._activeConnection.vpn_state == NetworkManager.VPNConnectionState.ACTIVATED;
}, },
setActiveConnection: function(activeConnection) { setActiveConnection: function(activeConnection) {
if (this._stateChangeId)
this._activeConnection.disconnect(this._stateChangeId);
this._stateChangeId = 0;
this.parent(activeConnection); this.parent(activeConnection);
if (this._activeConnection) this.emit('active-connection-changed');
this._stateChangeId = this._activeConnection.connect('vpn-state-changed',
Lang.bind(this, this._connectionStateChanged));
this.emit('state-changed');
}, },
_shouldShowConnectionList: function() { _shouldShowConnectionList: function() {
@@ -895,39 +936,7 @@ const NMDeviceVPN = new Lang.Class({
}, },
getStatusLabel: function() { getStatusLabel: function() {
if (!this._activeConnection) // Same as DISCONNECTED return null;
return null;
switch(this._activeConnection.vpn_state) {
case NetworkManager.VPNConnectionState.DISCONNECTED:
case NetworkManager.VPNConnectionState.ACTIVATED:
return null;
case NetworkManager.VPNConnectionState.PREPARE:
case NetworkManager.VPNConnectionState.CONNECT:
case NetworkManager.VPNConnectionState.IP_CONFIG_GET:
return _("connecting...");
case NetworkManager.VPNConnectionState.NEED_AUTH:
/* Translators: this is for network connections that require some kind of key or password */
return _("authentication required");
case NetworkManager.VPNConnectionState.FAILED:
return _("connection failed");
default:
log('VPN connection state invalid, is %d'.format(this.device.state));
return 'invalid';
}
},
_connectionStateChanged: function(connection, newstate, reason) {
if (newstate == NetworkManager.VPNConnectionState.FAILED) {
// FIXME: if we ever want to show something based on reason,
// we need to convert from NetworkManager.VPNConnectionStateReason
// to NetworkManager.DeviceStateReason
this.emit('activation-failed', reason);
}
// Differently from real NMDevices, there is no need to queue
// an update of the menu section, contents wouldn't change anyway
this.emit('state-changed');
} }
}); });
@@ -978,7 +987,6 @@ const NMDeviceWireless = new Lang.Class({
obj.ssidText = ssidToLabel(obj.ssid); obj.ssidText = ssidToLabel(obj.ssid);
this._networks.push(obj); this._networks.push(obj);
} }
ap._updateId = ap.connect('notify::strength', Lang.bind(this, this._onApStrengthChanged));
// Check if some connection is valid for this AP // Check if some connection is valid for this AP
for (let j = 0; j < validConnections.length; j++) { for (let j = 0; j < validConnections.length; j++) {
@@ -990,10 +998,6 @@ const NMDeviceWireless = new Lang.Class({
} }
} }
// Sort APs within each network by strength
for (let i = 0; i < this._networks.length; i++)
sortAccessPoints(this._networks[i].accessPoints);
if (this.device.active_access_point) { if (this.device.active_access_point) {
let networkPos = this._findNetwork(this.device.active_access_point); let networkPos = this._findNetwork(this.device.active_access_point);
@@ -1034,8 +1038,13 @@ const NMDeviceWireless = new Lang.Class({
}, },
setEnabled: function(enabled) { setEnabled: function(enabled) {
this.statusItem.actor.visible = enabled; if (enabled) {
this.section.actor.visible = enabled; this.statusItem.actor.show();
this.section.actor.show();
} else {
this.statusItem.actor.hide();
this.section.actor.hide();
}
}, },
activate: function() { activate: function() {
@@ -1076,7 +1085,7 @@ const NMDeviceWireless = new Lang.Class({
// the user toggles the switch and has more than one wireless device) // the user toggles the switch and has more than one wireless device)
if (this._networks.length > 0) { if (this._networks.length > 0) {
let connection = this._createAutomaticConnection(this._networks[0]); let connection = this._createAutomaticConnection(this._networks[0]);
let accessPoints = this._networks[0].accessPoints; let accessPoints = sortAccessPoints(this._networks[0].accessPoints);
this._client.add_and_activate_connection(connection, this.device, accessPoints[0].dbus_path, null); this._client.add_and_activate_connection(connection, this.device, accessPoints[0].dbus_path, null);
} }
}, },
@@ -1146,13 +1155,6 @@ const NMDeviceWireless = new Lang.Class({
else if (!oneHasConnection && twoHasConnection) else if (!oneHasConnection && twoHasConnection)
return 1; return 1;
let oneStrength = one.accessPoints[0].strength;
let twoStrength = two.accessPoints[0].strength;
// place stronger connections first
if (oneStrength != twoStrength)
return oneStrength < twoStrength ? 1 : -1;
let oneHasSecurity = one.security != NMAccessPointSecurity.NONE; let oneHasSecurity = one.security != NMAccessPointSecurity.NONE;
let twoHasSecurity = two.security != NMAccessPointSecurity.NONE; let twoHasSecurity = two.security != NMAccessPointSecurity.NONE;
@@ -1202,28 +1204,6 @@ const NMDeviceWireless = new Lang.Class({
return -1; return -1;
}, },
_onApStrengthChanged: function(ap) {
let res = this._findExistingNetwork(ap);
if (res == null) {
// Uhm... stale signal?
return;
}
let network = this._networks[res.network];
network.accessPoints.splice(res.ap, 1);
Util.insertSorted(network.accessPoints, ap, function(one, two) {
return two.strength - one.strength;
});
this._networks.splice(res.network, 1);
let newPos = Util.insertSorted(this._networks, network, Lang.bind(this, this._networkSortFunction));
if (newPos != res.network) {
this._clearSection();
this._queueCreateSection();
}
},
_accessPointAdded: function(device, accessPoint) { _accessPointAdded: function(device, accessPoint) {
if (accessPoint.get_ssid() == null) { if (accessPoint.get_ssid() == null) {
// This access point is not visible yet // This access point is not visible yet
@@ -1243,11 +1223,9 @@ const NMDeviceWireless = new Lang.Class({
return; return;
} }
Util.insertSorted(apObj.accessPoints, accessPoint, function(one, two) { apObj.accessPoints.push(accessPoint);
return two.strength - one.strength;
});
if (apObj.item) if (apObj.item)
apObj.item.updateBestAP(apObj.accessPoints[0]); apObj.item.updateAccessPoints(apObj.accessPoints);
} else { } else {
apObj = { ssid: accessPoint.get_ssid(), apObj = { ssid: accessPoint.get_ssid(),
mode: accessPoint.mode, mode: accessPoint.mode,
@@ -1258,7 +1236,6 @@ const NMDeviceWireless = new Lang.Class({
}; };
apObj.ssidText = ssidToLabel(apObj.ssid); apObj.ssidText = ssidToLabel(apObj.ssid);
} }
accessPoint._updateId = accessPoint.connect('notify::strength', Lang.bind(this, this._onApStrengthChanged));
// check if this enables new connections for this group // check if this enables new connections for this group
for (let i = 0; i < this._connections.length; i++) { for (let i = 0; i < this._connections.length; i++) {
@@ -1266,26 +1243,23 @@ const NMDeviceWireless = new Lang.Class({
if (accessPoint.connection_valid(connection) && if (accessPoint.connection_valid(connection) &&
apObj.connections.indexOf(connection) == -1) { apObj.connections.indexOf(connection) == -1) {
apObj.connections.push(connection); apObj.connections.push(connection);
// this potentially changes the order
needsupdate = true;
} }
} }
if (pos != -1) if (pos == -1 || needsupdate) {
this._networks.splice(pos, 1); if (pos != -1)
let newPos = Util.insertSorted(this._networks, apObj, this._networkSortFunction); this._networks.splice(pos, 1);
pos = Util.insertSorted(this._networks, apObj, this._networkSortFunction);
// Queue an update of the UI if we changed the order
if (newPos != pos) {
this._clearSection(); this._clearSection();
this._queueCreateSection(); this._queueCreateSection();
} }
}, },
_accessPointRemoved: function(device, accessPoint) { _accessPointRemoved: function(device, accessPoint) {
if (accessPoint._updateId) {
accessPoint.disconnect(accessPoint._updateId);
accessPoint._updateId = 0;
}
let res = this._findExistingNetwork(accessPoint); let res = this._findExistingNetwork(accessPoint);
if (res == null) { if (res == null) {
@@ -1327,30 +1301,17 @@ const NMDeviceWireless = new Lang.Class({
this._overflowItem = null; this._overflowItem = null;
} }
} }
this._networks.splice(res.network, 1); this._networks.splice(res.network, 1);
} else {
let okPrev = true, okNext = true;
if (res.network > 0) } else if (apObj.item)
okPrev = this._networkSortFunction(this._networks[res.network - 1], apObj) >= 0; apObj.item.updateAccessPoints(apObj.accessPoints);
if (res.network < this._networks.length-1)
okNext = this._networkSortFunction(this._networks[res.network + 1], apObj) <= 0;
if (!okPrev || !okNext) {
this._clearSection();
this._queueCreateSection();
} else if (apObj.item) {
apObj.item.updateBestAP(apObj.accessPoints[0]);
}
}
}, },
_createAPItem: function(connection, accessPointObj, useConnectionName) { _createAPItem: function(connection, accessPointObj, useConnectionName) {
let item = new NMNetworkMenuItem(accessPointObj.accessPoints[0], useConnectionName ? connection.get_id() : undefined); let item = new NMNetworkMenuItem(accessPointObj.accessPoints, useConnectionName ? connection._name : undefined);
item._connection = connection; item._connection = connection;
item.connect('activate', Lang.bind(this, function() { item.connect('activate', Lang.bind(this, function() {
let accessPoints = accessPointObj.accessPoints; let accessPoints = sortAccessPoints(accessPointObj.accessPoints);
for (let i = 0; i < accessPoints.length; i++) { for (let i = 0; i < accessPoints.length; i++) {
if (accessPoints[i].connection_valid(connection)) { if (accessPoints[i].connection_valid(connection)) {
this._client.activate_connection(connection, this.device, accessPoints[i].dbus_path, null); this._client.activate_connection(connection, this.device, accessPoints[i].dbus_path, null);
@@ -1370,7 +1331,9 @@ const NMDeviceWireless = new Lang.Class({
}, },
removeConnection: function(connection) { removeConnection: function(connection) {
let pos = this._findConnection(connection.get_uuid()); if (!connection._uuid)
return;
let pos = this._findConnection(connection._uuid);
if (pos == -1) { if (pos == -1) {
// removing connection that was never added // removing connection that was never added
return; return;
@@ -1384,7 +1347,7 @@ const NMDeviceWireless = new Lang.Class({
let apObj = this._networks[i]; let apObj = this._networks[i];
let connections = apObj.connections; let connections = apObj.connections;
for (let k = 0; k < connections.length; k++) { for (let k = 0; k < connections.length; k++) {
if (connections[k].get_uuid() == connection.get_uuid()) { if (connections[k]._uuid == connection._uuid) {
// remove the connection from the access point group // remove the connection from the access point group
connections.splice(k); connections.splice(k);
forceupdate = forceupdate || connections.length == 0; forceupdate = forceupdate || connections.length == 0;
@@ -1400,7 +1363,7 @@ const NMDeviceWireless = new Lang.Class({
forceupdate = true; forceupdate = true;
} else { } else {
for (let j = 0; j < items.length; j++) { for (let j = 0; j < items.length; j++) {
if (items[j]._connection.get_uuid() == connection.get_uuid()) { if (items[j]._connection._uuid == connection._uuid) {
items[j].destroy(); items[j].destroy();
break; break;
} }
@@ -1427,8 +1390,8 @@ const NMDeviceWireless = new Lang.Class({
// record the connection // record the connection
let obj = { let obj = {
connection: connection, connection: connection,
name: connection.get_id(), name: connection._name,
uuid: connection.get_uuid(), uuid: connection._uuid,
}; };
this._connections.push(obj); this._connections.push(obj);
@@ -1457,19 +1420,27 @@ const NMDeviceWireless = new Lang.Class({
}, },
_createActiveConnectionItem: function() { _createActiveConnectionItem: function() {
let title; let icon, title;
if (this._activeConnection && this._activeConnection._connection) if (this._activeConnection && this._activeConnection._connection) {
title = this._activeConnection._connection.get_id(); let connection = this._activeConnection._connection;
else if (this._activeNetwork)
title = _("Connected (private)"); this._activeConnectionItem = new NMNetworkMenuItem(this._activeNetwork.accessPoints, undefined,
{ reactive: false });
if (this._activeNetwork) else
this._activeConnectionItem = new NMNetworkMenuItem(this.device.active_access_point, undefined, this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(connection._name,
{ reactive: false }); 'network-wireless-connected',
else { reactive: false });
this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(title, } else {
'network-wireless-connected', // We cannot read the connection (due to ACL, or API incompatibility), but we still show signal if we have it
{ reactive: false }); let menuItem;
if (this._activeNetwork)
this._activeConnectionItem = new NMNetworkMenuItem(this._activeNetwork.accessPoints, undefined,
{ reactive: false });
else
this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(_("Connected (private)"),
'network-wireless-connected',
{ reactive: false });
}
this._activeConnectionItem.setShowDot(true); this._activeConnectionItem.setShowDot(true);
}, },
@@ -1509,9 +1480,9 @@ const NMDeviceWireless = new Lang.Class({
apObj.item.menu.addMenuItem(this._createAPItem(apObj.connections[i], apObj, true)); apObj.item.menu.addMenuItem(this._createAPItem(apObj.connections[i], apObj, true));
} }
} else { } else {
apObj.item = new NMNetworkMenuItem(apObj.accessPoints[0]); apObj.item = new NMNetworkMenuItem(apObj.accessPoints);
apObj.item.connect('activate', Lang.bind(this, function() { apObj.item.connect('activate', Lang.bind(this, function() {
let accessPoints = apObj.accessPoints; let accessPoints = sortAccessPoints(apObj.accessPoints);
if ( (accessPoints[0]._secType == NMAccessPointSecurity.WPA2_ENT) if ( (accessPoints[0]._secType == NMAccessPointSecurity.WPA2_ENT)
|| (accessPoints[0]._secType == NMAccessPointSecurity.WPA_ENT)) { || (accessPoints[0]._secType == NMAccessPointSecurity.WPA_ENT)) {
// 802.1x-enabled APs require further configuration, so they're // 802.1x-enabled APs require further configuration, so they're
@@ -1564,25 +1535,10 @@ const NMDeviceWireless = new Lang.Class({
const NMApplet = new Lang.Class({ const NMApplet = new Lang.Class({
Name: 'NMApplet', Name: 'NMApplet',
Extends: PanelMenu.Button, Extends: PanelMenu.SystemStatusButton,
_init: function() { _init: function() {
this.parent(0.0, _('Network')); this.parent('network-error', _("Network"));
this._box = new St.BoxLayout({ name: 'networkMenu' });
this.actor.add_actor (this._box);
this.actor.add_style_class_name('panel-status-button');
this._primaryIcon = new St.Icon({ icon_name: 'network-offline',
icon_type: St.IconType.SYMBOLIC,
style_class: 'system-status-icon' });
this._box.add_actor(this._primaryIcon);
this._secondaryIcon = new St.Icon({ icon_name: 'network-vpn',
icon_type: St.IconType.SYMBOLIC,
style_class: 'system-status-icon',
visible: false });
this._box.add_actor(this._secondaryIcon);
this._client = NMClient.Client.new(); this._client = NMClient.Client.new();
@@ -1596,16 +1552,6 @@ const NMApplet = new Lang.Class({
this.menu.addMenuItem(this._statusSection); this.menu.addMenuItem(this._statusSection);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._activeConnections = [ ];
this._connections = [ ];
this._mainConnection = null;
this._vpnConnection = null;
this._activeAccessPointUpdateId = 0;
this._activeAccessPoint = null;
this._mobileUpdateId = 0;
this._mobileUpdateDevice = null;
this._devices = { }; this._devices = { };
this._devices.wired = { this._devices.wired = {
@@ -1641,9 +1587,13 @@ const NMApplet = new Lang.Class({
this._devices.vpn = { this._devices.vpn = {
section: new PopupMenu.PopupMenuSection(), section: new PopupMenu.PopupMenuSection(),
device: this._makeWrapperDevice(NMDeviceVPN, null), device: new NMDeviceVPN(this._client),
item: new NMWiredSectionTitleMenuItem(_("VPN Connections")) item: new NMWiredSectionTitleMenuItem(_("VPN Connections"))
}; };
this._devices.vpn.device.connect('active-connection-changed', Lang.bind(this, function() {
this._devices.vpn.item.updateForDevice(this._devices.vpn.device);
}));
this._devices.vpn.item.updateForDevice(this._devices.vpn.device);
this._devices.vpn.section.addMenuItem(this._devices.vpn.item); this._devices.vpn.section.addMenuItem(this._devices.vpn.item);
this._devices.vpn.section.addMenuItem(this._devices.vpn.device.section); this._devices.vpn.section.addMenuItem(this._devices.vpn.device.section);
this._devices.vpn.section.actor.hide(); this._devices.vpn.section.actor.hide();
@@ -1651,6 +1601,15 @@ const NMApplet = new Lang.Class({
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addSettingsAction(_("Network Settings"), 'gnome-network-panel.desktop'); this.menu.addSettingsAction(_("Network Settings"), 'gnome-network-panel.desktop');
this._activeConnections = [ ];
this._connections = [ ];
this._mainConnection = null;
this._activeAccessPointUpdateId = 0;
this._activeAccessPoint = null;
this._mobileUpdateId = 0;
this._mobileUpdateDevice = null;
// Device types // Device types
this._dtypes = { }; this._dtypes = { };
this._dtypes[NetworkManager.DeviceType.ETHERNET] = NMDeviceWired; this._dtypes[NetworkManager.DeviceType.ETHERNET] = NMDeviceWired;
@@ -1691,25 +1650,9 @@ const NMApplet = new Lang.Class({
})); }));
}, },
setIcon: function(iconName) {
this._primaryIcon.icon_name = iconName;
},
setLockedState: function(locked) {
// FIXME: more design discussion is needed before we can
// expose part of this menu
if (locked)
this.menu.close();
this.actor.reactive = !locked;
},
_ensureSource: function() { _ensureSource: function() {
if (!this._source) { if (!this._source) {
this._source = new MessageTray.Source(_("Network Manager"), this._source = new NMMessageTraySource();
'network-transmit-receive',
St.IconType.SYMBOLIC);
this._source.connect('destroy', Lang.bind(this, function() { this._source.connect('destroy', Lang.bind(this, function() {
this._source = null; this._source = null;
})); }));
@@ -1730,18 +1673,6 @@ const NMApplet = new Lang.Class({
}, },
_syncSectionTitle: function(category) { _syncSectionTitle: function(category) {
if (category == NMConnectionCategory.VPN) {
// Special case VPN: it's only one device (and a fake one
// actually), and we don't show it if empty
let device = this._devices.vpn.device;
let section = this._devices.vpn.section;
let item = this._devices.vpn.item;
section.actor.visible = !device.empty;
item.updateForDevice(device);
return;
}
let devices = this._devices[category].devices; let devices = this._devices[category].devices;
let item = this._devices[category].item; let item = this._devices[category].item;
let section = this._devices[category].section; let section = this._devices[category].section;
@@ -1792,29 +1723,6 @@ const NMApplet = new Lang.Class({
this._source.notify(device._notification); this._source.notify(device._notification);
}, },
_makeWrapperDevice: function(wrapperClass, device) {
let wrapper = new wrapperClass(this._client, device, this._connections);
wrapper._activationFailedId = wrapper.connect('activation-failed', Lang.bind(this, function(device, reason) {
// XXX: nm-applet has no special text depending on reason
// but I'm not sure of this generic message
this._notifyForDevice(device, 'network-error',
_("Connection failed"),
_("Activation of network connection failed"),
MessageTray.Urgency.HIGH);
}));
wrapper._deviceStateChangedId = wrapper.connect('state-changed', Lang.bind(this, function(dev) {
this._syncSectionTitle(dev.category);
}));
wrapper._destroyId = wrapper.connect('destroy', function(wrapper) {
wrapper.disconnect(wrapper._activationFailedId);
wrapper.disconnect(wrapper._deviceStateChangedId);
wrapper.disconnect(wrapper._destroyId);
});
return wrapper;
},
_deviceAdded: function(client, device) { _deviceAdded: function(client, device) {
if (device._delegate) { if (device._delegate) {
// already seen, not adding again // already seen, not adding again
@@ -1822,8 +1730,24 @@ const NMApplet = new Lang.Class({
} }
let wrapperClass = this._dtypes[device.get_device_type()]; let wrapperClass = this._dtypes[device.get_device_type()];
if (wrapperClass) { if (wrapperClass) {
let wrapper = this._makeWrapperDevice(wrapperClass, device); let wrapper = new wrapperClass(this._client, device, this._connections);
wrapper._activationFailedId = wrapper.connect('activation-failed', Lang.bind(this, function(device, reason) {
// XXX: nm-applet has no special text depending on reason
// but I'm not sure of this generic message
this._notifyForDevice(device, 'network-error',
_("Connection failed"),
_("Activation of network connection failed"),
MessageTray.Urgency.HIGH);
}));
wrapper._deviceStateChangedId = wrapper.connect('state-changed', Lang.bind(this, function(dev) {
this._syncSectionTitle(dev.category);
}));
wrapper._destroyId = wrapper.connect('destroy', function(wrapper) {
wrapper.disconnect(wrapper._activationFailedId);
wrapper.disconnect(wrapper._deviceStateChangedId);
wrapper.disconnect(wrapper._destroyId);
});
let section = this._devices[wrapper.category].section; let section = this._devices[wrapper.category].section;
let devices = this._devices[wrapper.category].devices; let devices = this._devices[wrapper.category].devices;
@@ -1877,8 +1801,6 @@ const NMApplet = new Lang.Class({
this._activeConnections = newActiveConnections; this._activeConnections = newActiveConnections;
this._mainConnection = null; this._mainConnection = null;
this._vpnConnection = null;
let activating = null; let activating = null;
let default_ip4 = null; let default_ip4 = null;
let default_ip6 = null; let default_ip6 = null;
@@ -1912,17 +1834,17 @@ const NMApplet = new Lang.Class({
default_ip4 = a; default_ip4 = a;
if (a.default6) if (a.default6)
default_ip6 = a; default_ip6 = a;
if (a._type == 'vpn') if (a._type == 'vpn')
active_vpn = a; active_vpn = a;
else if (a.state == NetworkManager.ActiveConnectionState.ACTIVATING)
if (a.state == NetworkManager.ActiveConnectionState.ACTIVATING)
activating = a; activating = a;
if (!a._primaryDevice) { if (!a._primaryDevice) {
if (a._type != NetworkManager.SETTING_VPN_SETTING_NAME) { if (a._type != NetworkManager.SETTING_VPN_SETTING_NAME) {
// find a good device to be considered primary // find a good device to be considered primary
a._primaryDevice = null; a._primaryDevice = null;
let devices = a.get_devices() || []; let devices = a.get_devices();
for (let j = 0; j < devices.length; j++) { for (let j = 0; j < devices.length; j++) {
let d = devices[j]; let d = devices[j];
if (d._delegate) { if (d._delegate) {
@@ -1944,8 +1866,7 @@ const NMApplet = new Lang.Class({
} }
} }
this._mainConnection = activating || default_ip4 || default_ip6 || this._activeConnections[0] || null; this._mainConnection = activating || active_vpn || default_ip4 || default_ip6 || this._activeConnections[0] || null;
this._vpnConnection = active_vpn;
}, },
_notifyActivated: function(activeConnection) { _notifyActivated: function(activeConnection) {
@@ -1962,7 +1883,7 @@ const NMApplet = new Lang.Class({
let connections = this._settings.list_connections(); let connections = this._settings.list_connections();
for (let i = 0; i < connections.length; i++) { for (let i = 0; i < connections.length; i++) {
let connection = connections[i]; let connection = connections[i];
if (connection._updatedId) { if (connection._uuid) {
// connection was already seen (for example because NetworkManager was restarted) // connection was already seen (for example because NetworkManager was restarted)
continue; continue;
} }
@@ -1975,7 +1896,7 @@ const NMApplet = new Lang.Class({
}, },
_newConnection: function(settings, connection) { _newConnection: function(settings, connection) {
if (connection._updatedId) { if (connection._uuid) {
// connection was already seen // connection was already seen
return; return;
} }
@@ -1998,31 +1919,35 @@ const NMApplet = new Lang.Class({
if (section == NMConnectionCategory.VPN) { if (section == NMConnectionCategory.VPN) {
this._devices.vpn.device.removeConnection(connection); this._devices.vpn.device.removeConnection(connection);
this._syncSectionTitle(section); if (this._devices.vpn.device.empty)
this._devices.vpn.section.actor.hide();
} else if (section != NMConnectionCategory.INVALID) { } else if (section != NMConnectionCategory.INVALID) {
let devices = this._devices[section].devices; let devices = this._devices[section].devices;
for (let i = 0; i < devices.length; i++) for (let i = 0; i < devices.length; i++)
devices[i].removeConnection(connection); devices[i].removeConnection(connection);
} }
connection._uuid = null;
connection.disconnect(connection._removedId); connection.disconnect(connection._removedId);
connection.disconnect(connection._updatedId); connection.disconnect(connection._updatedId);
connection._removedId = connection._updatedId = 0;
}, },
_updateConnection: function(connection) { _updateConnection: function(connection) {
let connectionSettings = connection.get_setting_by_name(NetworkManager.SETTING_CONNECTION_SETTING_NAME); let connectionSettings = connection.get_setting_by_name(NetworkManager.SETTING_CONNECTION_SETTING_NAME);
connection._type = connectionSettings.type; connection._type = connectionSettings.type;
connection._section = this._ctypes[connection._type] || NMConnectionCategory.INVALID; connection._section = this._ctypes[connection._type] || NMConnectionCategory.INVALID;
connection._name = connectionSettings.id;
connection._uuid = connectionSettings.uuid;
connection._timestamp = connectionSettings.timestamp; connection._timestamp = connectionSettings.timestamp;
let section = connection._section; let section = connection._section;
if (section == NMConnectionCategory.INVALID) if (connection._section == NMConnectionCategory.INVALID)
return; return;
if (section == NMConnectionCategory.VPN) { if (section == NMConnectionCategory.VPN) {
this._devices.vpn.device.checkConnection(connection); this._devices.vpn.device.checkConnection(connection);
this._syncSectionTitle(section); this._devices.vpn.section.actor.show();
} else { } else {
let devices = this._devices[section].devices; let devices = this._devices[section].devices;
for (let i = 0; i < devices.length; i++) { for (let i = 0; i < devices.length; i++) {
@@ -2045,10 +1970,12 @@ const NMApplet = new Lang.Class({
this._statusSection.actor.hide(); this._statusSection.actor.hide();
this._syncSectionTitle(NMConnectionCategory.WIRED); this._syncSectionTitle('wired');
this._syncSectionTitle(NMConnectionCategory.WIRELESS); this._syncSectionTitle('wireless');
this._syncSectionTitle(NMConnectionCategory.WWAN); this._syncSectionTitle('wwan');
this._syncSectionTitle(NMConnectionCategory.VPN);
if (!this._devices.vpn.device.empty)
this._devices.vpn.section.actor.show();
}, },
_syncNMState: function() { _syncNMState: function() {
@@ -2091,6 +2018,9 @@ const NMApplet = new Lang.Class({
case NMConnectionCategory.WIRED: case NMConnectionCategory.WIRED:
this.setIcon('network-wired-acquiring'); this.setIcon('network-wired-acquiring');
break; break;
case NMConnectionCategory.VPN:
this.setIcon('network-vpn-acquiring');
break;
default: default:
// fallback to a generic connected icon // fallback to a generic connected icon
// (it could be a private connection of some other user) // (it could be a private connection of some other user)
@@ -2153,6 +2083,9 @@ const NMApplet = new Lang.Class({
this.setIcon('network-cellular-signal-' + signalToIcon(dev.mobileDevice.signal_quality)); this.setIcon('network-cellular-signal-' + signalToIcon(dev.mobileDevice.signal_quality));
hasMobileIcon = true; hasMobileIcon = true;
break; break;
case NMConnectionCategory.VPN:
this.setIcon('network-vpn');
break;
default: default:
// fallback to a generic connected icon // fallback to a generic connected icon
// (it could be a private connection of some other user) // (it could be a private connection of some other user)
@@ -2161,25 +2094,6 @@ const NMApplet = new Lang.Class({
} }
} }
// update VPN indicator
if (this._vpnConnection) {
let vpnIconName = 'network-vpn';
if (this._vpnConnection.state == NetworkManager.ActiveConnectionState.ACTIVATING)
vpnIconName = 'network-vpn-acquiring';
// only show a separate icon when we're using a wireless/3g connection
if (mc._section == NMConnectionCategory.WIRELESS ||
mc._section == NMConnectionCategory.WWAN) {
this._secondaryIcon.icon_name = vpnIconName;
this._secondaryIcon.visible = true;
} else {
this.setIcon(vpnIconName);
this._secondaryIcon.visible = false;
}
} else {
this._secondaryIcon.visible = false;
}
// cleanup stale signal connections // cleanup stale signal connections
if (!hasApIcon && this._activeAccessPointUpdateId) { if (!hasApIcon && this._activeAccessPointUpdateId) {
@@ -2194,3 +2108,18 @@ const NMApplet = new Lang.Class({
} }
} }
}); });
const NMMessageTraySource = new Lang.Class({
Name: 'NMMessageTraySource',
Extends: MessageTray.Source,
_init: function() {
this.parent(_("Network Manager"));
let icon = new St.Icon({ icon_name: 'network-transmit-receive',
icon_type: St.IconType.SYMBOLIC,
icon_size: this.ICON_SIZE
});
this._setSummaryIcon(icon);
}
});

View File

@@ -2,10 +2,14 @@
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
const St = imports.gi.St; const St = imports.gi.St;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu; const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
const Util = imports.misc.util;
const BUS_NAME = 'org.gnome.SettingsDaemon'; const BUS_NAME = 'org.gnome.SettingsDaemon';
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power'; const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
@@ -76,12 +80,6 @@ const Indicator = new Lang.Class({
this._devicesChanged(); this._devicesChanged();
}, },
setLockedState: function(locked) {
if (locked)
this.menu.close();
this.actor.reactive = !locked;
},
_readPrimaryDevice: function() { _readPrimaryDevice: function() {
this._proxy.GetPrimaryDeviceRemote(Lang.bind(this, function(result, error) { this._proxy.GetPrimaryDeviceRemote(Lang.bind(this, function(result, error) {
if (error) { if (error) {
@@ -214,7 +212,7 @@ const DeviceItem = new Lang.Class({
case UPDeviceType.COMPUTER: case UPDeviceType.COMPUTER:
return _("Computer"); return _("Computer");
default: default:
return C_("device", "Unknown"); return _("Unknown");
} }
} }
}); });

View File

@@ -2,11 +2,16 @@
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
const Gvc = imports.gi.Gvc; const Gvc = imports.gi.Gvc;
const Signals = imports.signals;
const St = imports.gi.St; const St = imports.gi.St;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu; const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
const Util = imports.misc.util;
const VOLUME_ADJUSTMENT_STEP = 0.05; /* Volume adjustment step in % */ const VOLUME_ADJUSTMENT_STEP = 0.05; /* Volume adjustment step in % */
@@ -57,10 +62,6 @@ const Indicator = new Lang.Class({
this._control.open(); this._control.open();
}, },
setLockedState: function(locked) {
this.menu.setSettingsVisibility(!locked);
},
_onScrollEvent: function(actor, event) { _onScrollEvent: function(actor, event) {
let direction = event.get_scroll_direction(); let direction = event.get_scroll_direction();
let currentVolume = this._output.volume; let currentVolume = this._output.volume;
@@ -148,9 +149,13 @@ const Indicator = new Lang.Class({
} }
} }
} }
if (showInput) {
this._inputTitle.actor.visible = showInput; this._inputTitle.actor.show();
this._inputSlider.actor.visible = showInput; this._inputSlider.actor.show();
} else {
this._inputTitle.actor.hide();
this._inputSlider.actor.hide();
}
}, },
_volumeToIcon: function(volume) { _volumeToIcon: function(volume) {

View File

@@ -0,0 +1,63 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const MessageTray = imports.ui.messageTray;
const NotificationDaemon = imports.ui.notificationDaemon;
const Util = imports.misc.util;
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': 'input-method'
};
const StatusIconDispatcher = new Lang.Class({
Name: 'StatusIconDispatcher',
_init: function() {
this._traymanager = new Shell.TrayManager();
this._traymanager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded));
this._traymanager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
// Yet-another-Ubuntu-workaround - we have to kill their
// app-indicators, so that applications fall back to normal
// status icons
// http://bugzilla.gnome.org/show_bug.cgi=id=621382
Util.killall('indicator-application-service');
},
start: function(themeWidget) {
this._traymanager.manage_stage(global.stage, themeWidget);
},
_onTrayIconAdded: function(o, icon) {
let wmClass = (icon.wm_class || 'unknown').toLowerCase();
let role = STANDARD_TRAY_ICON_IMPLEMENTATIONS[wmClass];
if (role)
this.emit('status-icon-added', icon, role);
else
this.emit('message-icon-added', icon);
},
_onTrayIconRemoved: function(o, icon) {
let wmClass = (icon.wm_class || 'unknown').toLowerCase();
let role = STANDARD_TRAY_ICON_IMPLEMENTATIONS[wmClass];
if (role)
this.emit('status-icon-removed', icon);
else
this.emit('message-icon-removed', icon);
}
});
Signals.addSignalMethods(StatusIconDispatcher.prototype);

View File

@@ -132,9 +132,6 @@ const Client = new Lang.Class({
let channel = channels[i]; let channel = channels[i];
let [targetHandle, targetHandleType] = channel.get_handle(); let [targetHandle, targetHandleType] = channel.get_handle();
if (Shell.is_channel_invalidated(channel))
continue;
/* Only observe contact text channels */ /* Only observe contact text channels */
if ((!(channel instanceof Tp.TextChannel)) || if ((!(channel instanceof Tp.TextChannel)) ||
targetHandleType != Tp.HandleType.CONTACT) targetHandleType != Tp.HandleType.CONTACT)
@@ -184,9 +181,6 @@ const Client = new Lang.Class({
continue; continue;
} }
if (Shell.is_channel_invalidated(channel))
continue;
// 'notify' will be true when coming from an actual HandleChannels // 'notify' will be true when coming from an actual HandleChannels
// call, and not when from a successful Claim call. The point is // call, and not when from a successful Claim call. The point is
// we don't want to notify for a channel we just claimed which // we don't want to notify for a channel we just claimed which
@@ -237,19 +231,12 @@ const Client = new Lang.Class({
let channel = channels[0]; let channel = channels[0];
let chanType = channel.get_channel_type(); let chanType = channel.get_channel_type();
if (Shell.is_channel_invalidated(channel)) {
Shell.decline_dispatch_op(context, 'Channel is invalidated');
return;
}
if (chanType == Tp.IFACE_CHANNEL_TYPE_TEXT) if (chanType == Tp.IFACE_CHANNEL_TYPE_TEXT)
this._approveTextChannel(account, conn, channel, dispatchOp, context); this._approveTextChannel(account, conn, channel, dispatchOp, context);
else if (chanType == Tp.IFACE_CHANNEL_TYPE_CALL) else if (chanType == Tp.IFACE_CHANNEL_TYPE_CALL)
this._approveCall(account, conn, channel, dispatchOp, context); this._approveCall(account, conn, channel, dispatchOp, context);
else if (chanType == Tp.IFACE_CHANNEL_TYPE_FILE_TRANSFER) else if (chanType == Tp.IFACE_CHANNEL_TYPE_FILE_TRANSFER)
this._approveFileTransfer(account, conn, channel, dispatchOp, context); this._approveFileTransfer(account, conn, channel, dispatchOp, context);
else
Shell.decline_dispatch_op(context, 'Unsupported channel type');
}, },
_approveTextChannel: function(account, conn, channel, dispatchOp, context) { _approveTextChannel: function(account, conn, channel, dispatchOp, context) {
@@ -378,9 +365,8 @@ const Client = new Lang.Class({
_ensureSubscriptionSource: function() { _ensureSubscriptionSource: function() {
if (this._subscriptionSource == null) { if (this._subscriptionSource == null) {
this._subscriptionSource = new MessageTray.Source(_("Subscription request"), this._subscriptionSource = new MultiNotificationSource(
'gtk-dialog-question', _("Subscription request"), 'gtk-dialog-question');
St.IconType.FULLCOLOR);
Main.messageTray.add(this._subscriptionSource); Main.messageTray.add(this._subscriptionSource);
this._subscriptionSource.connect('destroy', Lang.bind(this, function () { this._subscriptionSource.connect('destroy', Lang.bind(this, function () {
this._subscriptionSource = null; this._subscriptionSource = null;
@@ -415,9 +401,8 @@ const Client = new Lang.Class({
_ensureAccountSource: function() { _ensureAccountSource: function() {
if (this._accountSource == null) { if (this._accountSource == null) {
this._accountSource = new MessageTray.Source(_("Connection error"), this._accountSource = new MultiNotificationSource(
'gtk-dialog-error', _("Connection error"), 'gtk-dialog-error');
St.IconType.FULLCOLOR);
Main.messageTray.add(this._accountSource); Main.messageTray.add(this._accountSource);
this._accountSource.connect('destroy', Lang.bind(this, function () { this._accountSource.connect('destroy', Lang.bind(this, function () {
this._accountSource = null; this._accountSource = null;
@@ -433,13 +418,14 @@ const ChatSource = new Lang.Class({
Extends: MessageTray.Source, Extends: MessageTray.Source,
_init: function(account, conn, channel, contact, client) { _init: function(account, conn, channel, contact, client) {
this.parent(contact.get_alias());
this.isChat = true;
this._account = account; this._account = account;
this._contact = contact; this._contact = contact;
this._client = client; this._client = client;
this.parent(contact.get_alias());
this.isChat = true;
this._pendingMessages = []; this._pendingMessages = [];
this._conn = conn; this._conn = conn;
@@ -460,6 +446,8 @@ const ChatSource = new Lang.Class({
this._receivedId = this._channel.connect('message-received', Lang.bind(this, this._messageReceived)); this._receivedId = this._channel.connect('message-received', Lang.bind(this, this._messageReceived));
this._pendingId = this._channel.connect('pending-message-removed', Lang.bind(this, this._pendingRemoved)); this._pendingId = this._channel.connect('pending-message-removed', Lang.bind(this, this._pendingRemoved));
this._setSummaryIcon(this.createNotificationIcon());
this._notifyAliasId = this._contact.connect('notify::alias', Lang.bind(this, this._updateAlias)); this._notifyAliasId = this._contact.connect('notify::alias', Lang.bind(this, this._updateAlias));
this._notifyAvatarId = this._contact.connect('notify::avatar-file', Lang.bind(this, this._updateAvatarIcon)); this._notifyAvatarId = this._contact.connect('notify::avatar-file', Lang.bind(this, this._updateAvatarIcon));
this._presenceChangedId = this._contact.connect('presence-changed', Lang.bind(this, this._presenceChanged)); this._presenceChangedId = this._contact.connect('presence-changed', Lang.bind(this, this._presenceChanged));
@@ -482,9 +470,9 @@ const ChatSource = new Lang.Class({
this._notification.appendAliasChange(oldAlias, newAlias); this._notification.appendAliasChange(oldAlias, newAlias);
}, },
createIcon: function(size) { createNotificationIcon: function() {
this._iconBox = new St.Bin({ style_class: 'avatar-box' }); this._iconBox = new St.Bin({ style_class: 'avatar-box' });
this._iconBox._size = size; this._iconBox._size = this.ICON_SIZE;
let textureCache = St.TextureCache.get_default(); let textureCache = St.TextureCache.get_default();
let file = this._contact.get_avatar_file(); let file = this._contact.get_avatar_file();
@@ -500,40 +488,9 @@ const ChatSource = new Lang.Class({
return this._iconBox; return this._iconBox;
}, },
createSecondaryIcon: function() {
let iconBox = new St.Bin();
iconBox.child = new St.Icon({ style_class: 'secondary-icon',
icon_type: St.IconType.FULLCOLOR });
let presenceType = this._contact.get_presence_type();
switch (presenceType) {
case Tp.ConnectionPresenceType.AVAILABLE:
iconBox.child.icon_name = 'user-available';
break;
case Tp.ConnectionPresenceType.BUSY:
iconBox.child.icon_name = 'user-busy';
break;
case Tp.ConnectionPresenceType.OFFLINE:
iconBox.child.icon_name = 'user-offline';
break;
case Tp.ConnectionPresenceType.HIDDEN:
iconBox.child.icon_name = 'user-invisible';
break;
case Tp.ConnectionPresenceType.AWAY:
iconBox.child.icon_name = 'user-away';
break;
case Tp.ConnectionPresenceType.EXTENDED_AWAY:
iconBox.child.icon_name = 'user-idle';
break;
default:
iconBox.child.icon_name = 'user-offline';
}
return iconBox;
},
_updateAvatarIcon: function() { _updateAvatarIcon: function() {
this.iconUpdated(); this._setSummaryIcon(this.createNotificationIcon());
this._notification.update(this._notification.title, null, { customContent: true }); this._notification.update(this._notification.title, null, { customContent: true, icon: this.createNotificationIcon() });
}, },
open: function(notification) { open: function(notification) {
@@ -553,10 +510,10 @@ const ChatSource = new Lang.Class({
_getLogMessages: function() { _getLogMessages: function() {
let logManager = Tpl.LogManager.dup_singleton(); let logManager = Tpl.LogManager.dup_singleton();
let entity = Tpl.Entity.new_from_tp_contact(this._contact, Tpl.EntityType.CONTACT); let entity = Tpl.Entity.new_from_tp_contact(this._contact, Tpl.EntityType.CONTACT);
Shell.get_contact_events(logManager,
logManager.get_filtered_events_async(this._account, entity, this._account, entity,
Tpl.EventTypeMask.TEXT, SCROLLBACK_HISTORY_LINES, SCROLLBACK_HISTORY_LINES,
null, Lang.bind(this, this._displayPendingMessages)); Lang.bind(this, this._displayPendingMessages));
}, },
_displayPendingMessages: function(logManager, result) { _displayPendingMessages: function(logManager, result) {
@@ -695,14 +652,38 @@ const ChatSource = new Lang.Class({
}, },
_presenceChanged: function (contact, presence, status, message) { _presenceChanged: function (contact, presence, status, message) {
let msg, title; let msg, shouldNotify, title;
if (this._presence == presence)
return;
title = GLib.markup_escape_text(this.title, -1); title = GLib.markup_escape_text(this.title, -1);
this._notification.update(this._notification.title, null, { customContent: true, secondaryIcon: this.createSecondaryIcon() }); if (presence == Tp.ConnectionPresenceType.AVAILABLE) {
msg = _("%s is online.").format(title);
shouldNotify = (this._presence == Tp.ConnectionPresenceType.OFFLINE);
} else if (presence == Tp.ConnectionPresenceType.OFFLINE) {
presence = Tp.ConnectionPresenceType.OFFLINE;
msg = _("%s is offline.").format(title);
shouldNotify = (this._presence != Tp.ConnectionPresenceType.OFFLINE);
} else if (presence == Tp.ConnectionPresenceType.AWAY ||
presence == Tp.ConnectionPresenceType.EXTENDED_AWAY) {
msg = _("%s is away.").format(title);
shouldNotify = false;
} else if (presence == Tp.ConnectionPresenceType.BUSY) {
msg = _("%s is busy.").format(title);
shouldNotify = false;
} else
return;
this._presence = presence;
if (message) if (message)
msg += ' <i>(' + GLib.markup_escape_text(message, -1) + ')</i>'; msg += ' <i>(' + GLib.markup_escape_text(message, -1) + ')</i>';
this._notification.appendPresence(msg, shouldNotify);
if (shouldNotify)
this.notify();
}, },
_pendingRemoved: function(channel, message) { _pendingRemoved: function(channel, message) {
@@ -729,7 +710,7 @@ const ChatNotification = new Lang.Class({
Extends: MessageTray.Notification, Extends: MessageTray.Notification,
_init: function(source) { _init: function(source) {
this.parent(source, source.title, null, { customContent: true, secondaryIcon: source.createSecondaryIcon() }); this.parent(source, source.title, null, { customContent: true });
this.setResident(true); this.setResident(true);
this._responseEntry = new St.Entry({ style_class: 'chat-response', this._responseEntry = new St.Entry({ style_class: 'chat-response',
@@ -822,7 +803,7 @@ const ChatNotification = new Lang.Class({
let groups = this._contentArea.get_children(); let groups = this._contentArea.get_children();
for (let i = 0; i < groups.length; i++) { for (let i = 0; i < groups.length; i++) {
let group = groups[i]; let group = groups[i];
if (group.get_n_children() == 0) if (group.get_children().length == 0)
group.destroy(); group.destroy();
} }
}, },
@@ -872,8 +853,6 @@ const ChatNotification = new Lang.Class({
this._lastGroupActor.add(body, props.childProps); this._lastGroupActor.add(body, props.childProps);
this.updated();
let timestamp = props.timestamp; let timestamp = props.timestamp;
this._history.unshift({ actor: body, time: timestamp, this._history.unshift({ actor: body, time: timestamp,
realMessage: group != 'meta' }); realMessage: group != 'meta' });
@@ -939,6 +918,19 @@ const ChatNotification = new Lang.Class({
return false; return false;
}, },
appendPresence: function(text, asTitle) {
if (asTitle)
this.update(text, null, { customContent: true, titleMarkup: true });
else
this.update(this.source.title, null, { customContent: true });
let label = this._append({ body: text,
group: 'meta',
styles: ['chat-meta-message'] });
this._filterMessages();
},
appendAliasChange: function(oldAlias, newAlias) { appendAliasChange: function(oldAlias, newAlias) {
oldAlias = GLib.markup_escape_text(oldAlias, -1); oldAlias = GLib.markup_escape_text(oldAlias, -1);
newAlias = GLib.markup_escape_text(newAlias, -1); newAlias = GLib.markup_escape_text(newAlias, -1);
@@ -1008,10 +1000,11 @@ const ApproverSource = new Lang.Class({
Extends: MessageTray.Source, Extends: MessageTray.Source,
_init: function(dispatchOp, text, gicon) { _init: function(dispatchOp, text, gicon) {
this._gicon = gicon;
this.parent(text); this.parent(text);
this._gicon = gicon;
this._setSummaryIcon(this.createNotificationIcon());
this._dispatchOp = dispatchOp; this._dispatchOp = dispatchOp;
// Destroy the source if the channel dispatch operation is invalidated // Destroy the source if the channel dispatch operation is invalidated
@@ -1031,9 +1024,10 @@ const ApproverSource = new Lang.Class({
this.parent(); this.parent();
}, },
createIcon: function(size) { createNotificationIcon: function() {
return new St.Icon({ gicon: this._gicon, return new St.Icon({ gicon: this._gicon,
icon_size: size }); icon_type: St.IconType.FULLCOLOR,
icon_size: this.ICON_SIZE });
} }
}); });
@@ -1155,6 +1149,40 @@ const FileTransferNotification = new Lang.Class({
} }
}); });
// A notification source that can embed multiple notifications
const MultiNotificationSource = new Lang.Class({
Name: 'MultiNotificationSource',
Extends: MessageTray.Source,
_init: function(title, icon) {
this.parent(title);
this._icon = icon;
this._setSummaryIcon(this.createNotificationIcon());
this._nbNotifications = 0;
},
notify: function(notification) {
this.parent(notification);
this._nbNotifications += 1;
// Display the source while there is at least one notification
notification.connect('destroy', Lang.bind(this, function () {
this._nbNotifications -= 1;
if (this._nbNotifications == 0)
this.destroy();
}));
},
createNotificationIcon: function() {
return new St.Icon({ gicon: Gio.icon_new_for_string(this._icon),
icon_type: St.IconType.FULLCOLOR,
icon_size: this.ICON_SIZE });
}
});
// Subscription request // Subscription request
const SubscriptionRequestNotification = new Lang.Class({ const SubscriptionRequestNotification = new Lang.Class({
Name: 'SubscriptionRequestNotification', Name: 'SubscriptionRequestNotification',

View File

@@ -1,227 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const AccountsService = imports.gi.AccountsService;
const Clutter = imports.gi.Clutter;
const Gdm = imports.gi.Gdm;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Signals = imports.signals;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog;
const ShellEntry = imports.ui.shellEntry;
const Tweener = imports.ui.tweener;
const UserMenu = imports.ui.userMenu;
const Batch = imports.gdm.batch;
const GdmUtil = imports.gdm.util;
// A widget showing the user avatar and name
const UserWidget = new Lang.Class({
Name: 'UserWidget',
_init: function(user) {
this._user = user;
this.actor = new St.BoxLayout({ style_class: 'unlock-dialog-user-name-container',
vertical: false });
this._avatar = new UserMenu.UserAvatarWidget(user);
this.actor.add(this._avatar.actor,
{ x_fill: true, y_fill: true });
this._label = new St.Label({ style_class: 'login-dialog-username' });
this.actor.add(this._label,
{ expand: true,
x_fill: true,
y_align: St.Align.MIDDLE });
this._userLoadedId = this._user.connect('notify::is-loaded',
Lang.bind(this, this._updateUser));
this._userChangedId = this._user.connect('changed',
Lang.bind(this, this._updateUser));
if (this._user.is_loaded)
this._updateUser();
},
destroy: function() {
if (this._userLoadedId != 0) {
this._user.disconnect(this._userLoadedId);
this._userLoadedId = 0;
}
if (this._userChangedId != 0) {
this._user.disconnect(this._userChangedId);
this._userChangedId = 0;
}
this.actor.destroy();
},
_updateUser: function() {
if (this._user.is_loaded)
this._label.text = this._user.get_real_name();
else
this._label.text = '';
this._avatar.update();
}
});
const UnlockDialog = new Lang.Class({
Name: 'UnlockDialog',
Extends: ModalDialog.ModalDialog,
_init: function(parentActor) {
this.parent({ shellReactive: true,
styleClass: 'login-dialog',
parentActor: parentActor
});
this._userManager = AccountsService.UserManager.get_default();
this._userName = GLib.get_user_name();
this._user = this._userManager.get_user(this._userName);
this._greeterClient = new Gdm.Client();
this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient, { reauthenticationOnly: true });
this._userVerifier.connect('reset', Lang.bind(this, this._reset));
this._userVerifier.connect('ask-question', Lang.bind(this, this._onAskQuestion));
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
this._userVerifier.connect('verification-failed', Lang.bind(this, this._onVerificationFailed));
this._userVerifier.connect('show-fingerprint-prompt', Lang.bind(this, this._showFingerprintPrompt));
this._userVerifier.connect('hide-fingerprint-prompt', Lang.bind(this, this._hideFingerprintPrompt));
this._userWidget = new UserWidget(this._user);
this.contentLayout.add_actor(this._userWidget.actor);
this._promptLayout = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
vertical: false });
this._promptLabel = new St.Label({ style_class: 'login-dialog-prompt-label' });
this._promptLayout.add(this._promptLabel,
{ x_align: St.Align.START });
this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
can_focus: true });
ShellEntry.addContextMenu(this._promptEntry);
this.setInitialKeyFocus(this._promptEntry);
this._promptEntry.clutter_text.connect('activate', Lang.bind(this, this._doUnlock));
this._promptLayout.add(this._promptEntry,
{ expand: true,
x_fill: true });
this.contentLayout.add_actor(this._promptLayout);
// Translators: this message is shown below the password entry field
// to indicate the user can swipe their finger instead
this._promptFingerprintMessage = new St.Label({ text: _("(or swipe finger)"),
style_class: 'login-dialog-prompt-fingerprint-message' });
this._promptFingerprintMessage.hide();
this.contentLayout.add_actor(this._promptFingerprintMessage);
let otherUserLabel = new St.Label({ text: _("Login as another user"),
style_class: 'login-dialog-not-listed-label' });
this._otherUserButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
can_focus: true,
child: otherUserLabel,
reactive: true,
x_align: St.Align.START,
x_fill: true });
this._otherUserButton.connect('clicked', Lang.bind(this, this._otherUserClicked));
this.contentLayout.add(this._otherUserButton,
{ x_align: St.Align.START,
x_fill: false });
this._okButton = { label: _("Unlock"),
action: Lang.bind(this, this._doUnlock),
default: true };
this.setButtons([this._okButton]);
this.setActionKey(Clutter.KEY_Escape, Lang.bind(this, this._escape));
this._updateOkButton(false);
this._reset();
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
this.emit('loaded');
return false;
}));
},
_updateOkButton: function(sensitive) {
this._okButton.button.reactive = sensitive;
},
_reset: function() {
this._userVerifier.begin(this._userName, new Batch.Hold());
},
_onAskQuestion: function(verifier, serviceName, question, passwordChar) {
this._promptLabel.text = question;
this._promptEntry.text = '';
this._promptEntry.clutter_text.set_password_char(passwordChar);
this._promptEntry.menu.isPassword = passwordChar != '';
this._currentQuery = serviceName;
this._updateOkButton(true);
},
_showFingerprintPrompt: function() {
GdmUtil.fadeInActor(this._promptFingerprintMessage);
},
_hideFingerprintPrompt: function() {
GdmUtil.fadeOutActor(this._promptFingerprintMessage);
},
_doUnlock: function() {
if (!this._currentQuery)
return;
let query = this._currentQuery;
this._currentQuery = null;
this._updateOkButton(false);
this._userVerifier.answerQuery(query, this._promptEntry.text);
},
_onVerificationComplete: function() {
this._userVerifier.clear();
this.emit('unlocked');
},
_onVerificationFailed: function() {
this._userVerifier.cancel();
this.emit('failed');
},
_escape: function() {
this._onVerificationFailed();
},
_otherUserClicked: function(button, event) {
this._userManager.goto_login_session();
this._userVerifier.cancel();
this.emit('failed');
},
destroy: function() {
this._userVerifier.clear();
this.parent();
},
cancel: function() {
this._userVerifier.cancel(null);
this.destroy();
},
});

View File

@@ -1,7 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const AccountsService = imports.gi.AccountsService; const AccountsService = imports.gi.AccountsService;
const Gdm = imports.gi.Gdm;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const Lang = imports.lang; const Lang = imports.lang;
@@ -16,6 +15,7 @@ const GnomeSession = imports.misc.gnomeSession;
const Main = imports.ui.main; const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu; const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
const ScreenSaver = imports.misc.screenSaver;
const Util = imports.misc.util; const Util = imports.misc.util;
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown'; const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
@@ -40,34 +40,6 @@ const IMStatus = {
// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>. // Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
// Copyright (C) 2008,2009 Red Hat, Inc. // Copyright (C) 2008,2009 Red Hat, Inc.
const UserAvatarWidget = new Lang.Class({
Name: 'UserAvatarWidget',
_init: function(user) {
this._user = user;
this.actor = new St.Bin({ style_class: 'status-chooser-user-icon',
track_hover: true,
reactive: true });
},
update: function() {
let iconFile = this._user.get_icon_file();
if (!GLib.file_test(iconFile, GLib.FileTest.EXISTS))
iconFile = null;
if (iconFile) {
let file = Gio.File.new_for_path(iconFile);
this.actor.child = null;
this.actor.style = 'background-image: url("%s");'.format(iconFile);
} else {
this.actor.style = null;
this.actor.child = new St.Icon({ icon_name: 'avatar-default',
icon_type: St.IconType.SYMBOLIC,
icon_size: DIALOG_ICON_SIZE });
}
}
});
const IMStatusItem = new Lang.Class({ const IMStatusItem = new Lang.Class({
Name: 'IMStatusItem', Name: 'IMStatusItem',
@@ -135,11 +107,7 @@ const IMStatusChooserItem = new Lang.Class({
this.parent({ reactive: false, this.parent({ reactive: false,
style_class: 'status-chooser' }); style_class: 'status-chooser' });
this._userManager = AccountsService.UserManager.get_default(); this._iconBin = new St.Button({ style_class: 'status-chooser-user-icon' });
this._user = this._userManager.get_user(GLib.get_user_name());
this._avatar = new UserAvatarWidget(this._user);
this._iconBin = new St.Button({ child: this._avatar.actor });
this.addActor(this._iconBin); this.addActor(this._iconBin);
this._iconBin.connect('clicked', Lang.bind(this, this._iconBin.connect('clicked', Lang.bind(this,
@@ -164,7 +132,7 @@ const IMStatusChooserItem = new Lang.Class({
item = new IMStatusItem(_("Busy"), 'user-busy'); item = new IMStatusItem(_("Busy"), 'user-busy');
this._combo.addMenuItem(item, IMStatus.BUSY); this._combo.addMenuItem(item, IMStatus.BUSY);
item = new IMStatusItem(_("Invisible"), 'user-invisible'); item = new IMStatusItem(_("Hidden"), 'user-invisible');
this._combo.addMenuItem(item, IMStatus.HIDDEN); this._combo.addMenuItem(item, IMStatus.HIDDEN);
item = new IMStatusItem(_("Away"), 'user-away'); item = new IMStatusItem(_("Away"), 'user-away');
@@ -217,6 +185,10 @@ const IMStatusChooserItem = new Lang.Class({
} }
})); }));
this._userManager = AccountsService.UserManager.get_default();
this._user = this._userManager.get_user(GLib.get_user_name());
this._userLoadedId = this._user.connect('notify::is-loaded', this._userLoadedId = this._user.connect('notify::is-loaded',
Lang.bind(this, Lang.bind(this,
this._updateUser)); this._updateUser));
@@ -254,12 +226,44 @@ const IMStatusChooserItem = new Lang.Class({
}, },
_updateUser: function() { _updateUser: function() {
if (this._user.is_loaded) let iconFile = null;
if (this._user.is_loaded) {
this._name.label.set_text(this._user.get_real_name()); this._name.label.set_text(this._user.get_real_name());
else iconFile = this._user.get_icon_file();
if (!GLib.file_test(iconFile, GLib.FileTest.EXISTS))
iconFile = null;
} else {
this._name.label.set_text(""); this._name.label.set_text("");
}
this._avatar.update(); if (iconFile)
this._setIconFromFile(iconFile);
else
this._setIconFromName('avatar-default');
},
_setIconFromFile: function(iconFile) {
this._iconBin.set_style('background-image: url("' + iconFile + '");' +
'background-size: contain;');
this._iconBin.child = null;
},
_setIconFromName: function(iconName) {
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();
}
}, },
_statusForPresence: function(presence) { _statusForPresence: function(presence) {
@@ -450,6 +454,7 @@ const UserMenuButton = new Lang.Class({
this._accountMgr = Tp.AccountManager.dup(); this._accountMgr = Tp.AccountManager.dup();
this._upClient = new UPowerGlib.Client(); this._upClient = new UPowerGlib.Client();
this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy();
this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._iconBox = new St.Bin(); this._iconBox = new St.Bin();
@@ -468,20 +473,13 @@ const UserMenuButton = new Lang.Class({
style_class: 'popup-menu-icon' }); style_class: 'popup-menu-icon' });
this._idleIcon = new St.Icon({ icon_name: 'user-idle', this._idleIcon = new St.Icon({ icon_name: 'user-idle',
style_class: 'popup-menu-icon' }); style_class: 'popup-menu-icon' });
this._pendingIcon = new St.Icon({ icon_name: 'user-status-pending',
style_class: 'popup-menu-icon' });
this._accountMgr.connect('most-available-presence-changed', this._accountMgr.connect('most-available-presence-changed',
Lang.bind(this, this._updatePresenceIcon)); Lang.bind(this, this._updatePresenceIcon));
this._accountMgr.connect('account-enabled',
Lang.bind(this, this._onAccountEnabled));
this._accountMgr.connect('account-removed',
Lang.bind(this, this._onAccountRemoved));
this._accountMgr.prepare_async(null, Lang.bind(this, this._accountMgr.prepare_async(null, Lang.bind(this,
function(mgr) { function(mgr) {
let [presence, s, msg] = mgr.get_most_available_presence(); let [presence, s, msg] = mgr.get_most_available_presence();
this._updatePresenceIcon(mgr, presence, s, msg); this._updatePresenceIcon(mgr, presence, s, msg);
this._setupAccounts();
})); }));
this._name = new St.Label(); this._name = new St.Label();
@@ -499,13 +497,13 @@ const UserMenuButton = new Lang.Class({
})); }));
this._userManager.connect('notify::is-loaded', this._userManager.connect('notify::is-loaded',
Lang.bind(this, this._updateMultiUser)); Lang.bind(this, this._updateSwitchUser));
this._userManager.connect('notify::has-multiple-users', this._userManager.connect('notify::has-multiple-users',
Lang.bind(this, this._updateMultiUser)); Lang.bind(this, this._updateSwitchUser));
this._userManager.connect('user-added', this._userManager.connect('user-added',
Lang.bind(this, this._updateMultiUser)); Lang.bind(this, this._updateSwitchUser));
this._userManager.connect('user-removed', this._userManager.connect('user-removed',
Lang.bind(this, this._updateMultiUser)); Lang.bind(this, this._updateSwitchUser));
this._lockdownSettings.connect('changed::' + DISABLE_USER_SWITCH_KEY, this._lockdownSettings.connect('changed::' + DISABLE_USER_SWITCH_KEY,
Lang.bind(this, this._updateSwitchUser)); Lang.bind(this, this._updateSwitchUser));
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY, this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
@@ -517,10 +515,6 @@ const UserMenuButton = new Lang.Class({
this._updateLogout(); this._updateLogout();
this._updateLockScreen(); this._updateLockScreen();
this._updatesFile = Gio.File.new_for_path('/var/lib/PackageKit/prepared-update');
this._updatesMonitor = this._updatesFile.monitor(Gio.FileMonitorFlags.NONE, null);
this._updatesMonitor.connect('changed', Lang.bind(this, this._updateInstallUpdates));
// Whether shutdown is available or not depends on both lockdown // Whether shutdown is available or not depends on both lockdown
// settings (disable-log-out) and Polkit policy - the latter doesn't // settings (disable-log-out) and Polkit policy - the latter doesn't
// notify, so we update the menu item each time the menu opens or // notify, so we update the menu item each time the menu opens or
@@ -536,12 +530,6 @@ const UserMenuButton = new Lang.Class({
this._upClient.connect('notify::can-suspend', Lang.bind(this, this._updateSuspendOrPowerOff)); this._upClient.connect('notify::can-suspend', Lang.bind(this, this._updateSuspendOrPowerOff));
}, },
setLockedState: function(locked) {
if (locked)
this.menu.close();
this.actor.reactive = !locked;
},
_onDestroy: function() { _onDestroy: function() {
this._user.disconnect(this._userLoadedId); this._user.disconnect(this._userLoadedId);
this._user.disconnect(this._userChangedId); this._user.disconnect(this._userChangedId);
@@ -554,45 +542,37 @@ const UserMenuButton = new Lang.Class({
this._name.set_text(""); this._name.set_text("");
}, },
_updateMultiUser: function() {
this._updateSwitchUser();
this._updateLogout();
},
_updateSwitchUser: function() { _updateSwitchUser: function() {
let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY); let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY);
let multiUser = this._userManager.can_switch() && this._userManager.has_multiple_users; if (allowSwitch &&
let multiSession = Gdm.get_session_ids().length > 1; this._userManager.can_switch() &&
this._userManager.has_multiple_users)
this._loginScreenItem.label.set_text(multiUser ? _("Switch User") this._loginScreenItem.actor.show();
: _("Switch Session")); else
this._loginScreenItem.actor.visible = allowSwitch && (multiUser || multiSession); this._loginScreenItem.actor.hide();
}, },
_updateLogout: function() { _updateLogout: function() {
let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY); let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY);
let multiUser = this._userManager.has_multiple_users; if (allowLogout)
let multiSession = Gdm.get_session_ids().length > 1; this._logoutItem.actor.show();
else
this._logoutItem.actor.visible = allowLogout && (multiUser || multiSession); this._logoutItem.actor.hide();
}, },
_updateLockScreen: function() { _updateLockScreen: function() {
let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY); let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
this._lockScreenItem.actor.visible = allowLockScreen; if (allowLockScreen)
}, this._lockScreenItem.actor.show();
else
_updateInstallUpdates: function() { this._lockScreenItem.actor.hide();
let haveUpdates = this._updatesFile.query_exists(null);
this._installUpdatesItem.actor.visible = haveUpdates && this._haveShutdown;
}, },
_updateHaveShutdown: function() { _updateHaveShutdown: function() {
this._session.CanShutdownRemote(Lang.bind(this, this._session.CanShutdownRemote(Lang.bind(this,
function(result, error) { function(result, error) {
if (!error) { if (!error) {
this._haveShutdown = result[0]; this._haveShutdown = result;
this._updateInstallUpdates();
this._updateSuspendOrPowerOff(); this._updateSuspendOrPowerOff();
} }
})); }));
@@ -604,16 +584,19 @@ const UserMenuButton = new Lang.Class({
if (!this._suspendOrPowerOffItem) if (!this._suspendOrPowerOffItem)
return; return;
this._suspendOrPowerOffItem.actor.visible = this._haveShutdown || this._haveSuspend; if (!this._haveShutdown && !this._haveSuspend)
this._suspendOrPowerOffItem.actor.hide();
else
this._suspendOrPowerOffItem.actor.show();
// If we can't power off show Suspend instead // If we can't suspend show Power Off... instead
// and disable the alt key // and disable the alt key
if (!this._haveShutdown) { if (!this._haveSuspend) {
this._suspendOrPowerOffItem.updateText(_("Power Off..."), null);
} else if (!this._haveShutdown) {
this._suspendOrPowerOffItem.updateText(_("Suspend"), null); this._suspendOrPowerOffItem.updateText(_("Suspend"), null);
} else if (!this._haveSuspend) {
this._suspendOrPowerOffItem.updateText(_("Power Off"), null);
} else { } else {
this._suspendOrPowerOffItem.updateText(_("Power Off"), _("Suspend")); this._suspendOrPowerOffItem.updateText(_("Suspend"), _("Power Off..."));
} }
}, },
@@ -637,52 +620,11 @@ const UserMenuButton = new Lang.Class({
this._iconBox.child = this._offlineIcon; this._iconBox.child = this._offlineIcon;
}, },
_setupAccounts: function() {
let accounts = this._accountMgr.get_valid_accounts();
for (let i = 0; i < accounts.length; i++) {
accounts[i]._changingId = accounts[i].connect('notify::connection-status',
Lang.bind(this, this._updateChangingPresence));
}
this._updateChangingPresence();
},
_onAccountEnabled: function(accountMgr, account) {
if (!account._changingId)
account._changingId = account.connect('notify::connection-status',
Lang.bind(this, this._updateChangingPresence));
this._updateChangingPresence();
},
_onAccountRemoved: function(accountMgr, account) {
account.disconnect(account._changingId);
account._changingId = 0;
this._updateChangingPresence();
},
_updateChangingPresence: function() {
let accounts = this._accountMgr.get_valid_accounts();
let changing = false;
for (let i = 0; i < accounts.length; i++) {
if (accounts[i].connection_status == Tp.ConnectionStatus.CONNECTING) {
changing = true;
break;
}
}
if (changing) {
this._iconBox.child = this._pendingIcon;
} else {
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
this._updatePresenceIcon(this._accountMgr, presence, s, msg);
}
},
_createSubMenu: function() { _createSubMenu: function() {
let item; let item;
item = new IMStatusChooserItem(); item = new IMStatusChooserItem();
if (Main.sessionMode.allowSettings) item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
this.menu.addMenuItem(item); this.menu.addMenuItem(item);
this._statusChooser = item; this._statusChooser = item;
@@ -694,44 +636,41 @@ const UserMenuButton = new Lang.Class({
item = new PopupMenu.PopupSeparatorMenuItem(); item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item); this.menu.addMenuItem(item);
if (Main.sessionMode.allowSettings) { item = new PopupMenu.PopupMenuItem(_("Online Accounts"));
item = new PopupMenu.PopupMenuItem(_("System Settings")); item.connect('activate', Lang.bind(this, this._onOnlineAccountsActivate));
item.connect('activate', Lang.bind(this, this._onPreferencesActivate)); this.menu.addMenuItem(item);
this.menu.addMenuItem(item);
} item = new PopupMenu.PopupMenuItem(_("System Settings"));
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
this.menu.addMenuItem(item);
item = new PopupMenu.PopupSeparatorMenuItem(); item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item); this.menu.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("Lock Screen"));
item.connect('activate', Lang.bind(this, this._onLockScreenActivate));
this.menu.addMenuItem(item);
this._lockScreenItem = item;
item = new PopupMenu.PopupMenuItem(_("Switch User")); item = new PopupMenu.PopupMenuItem(_("Switch User"));
item.connect('activate', Lang.bind(this, this._onLoginScreenActivate)); item.connect('activate', Lang.bind(this, this._onLoginScreenActivate));
this.menu.addMenuItem(item); this.menu.addMenuItem(item);
this._loginScreenItem = item; this._loginScreenItem = item;
item = new PopupMenu.PopupMenuItem(_("Log Out")); item = new PopupMenu.PopupMenuItem(_("Log Out..."));
item.connect('activate', Lang.bind(this, this._onQuitSessionActivate)); item.connect('activate', Lang.bind(this, this._onQuitSessionActivate));
this.menu.addMenuItem(item); this.menu.addMenuItem(item);
this._logoutItem = item; this._logoutItem = item;
item = new PopupMenu.PopupMenuItem(_("Lock"));
item.connect('activate', Lang.bind(this, this._onLockScreenActivate));
this.menu.addMenuItem(item);
this._lockScreenItem = item;
item = new PopupMenu.PopupSeparatorMenuItem(); item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item); this.menu.addMenuItem(item);
item = new PopupMenu.PopupAlternatingMenuItem(_("Power Off"), item = new PopupMenu.PopupAlternatingMenuItem(_("Suspend"),
_("Suspend")); _("Power Off..."));
this.menu.addMenuItem(item); this.menu.addMenuItem(item);
item.connect('activate', Lang.bind(this, this._onSuspendOrPowerOffActivate));
this._suspendOrPowerOffItem = item; this._suspendOrPowerOffItem = item;
item.connect('activate', Lang.bind(this, this._onSuspendOrPowerOffActivate));
this._updateSuspendOrPowerOff(); this._updateSuspendOrPowerOff();
item = new PopupMenu.PopupMenuItem(_("Install Updates & Restart"));
item.connect('activate', Lang.bind(this, this._onInstallUpdatesActivate));
this.menu.addMenuItem(item);
this._installUpdatesItem = item;
}, },
_updatePresenceStatus: function(item, event) { _updatePresenceStatus: function(item, event) {
@@ -759,6 +698,12 @@ const UserMenuButton = new Lang.Class({
app.activate(); app.activate();
}, },
_onOnlineAccountsActivate: function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().lookup_setting('gnome-online-accounts-panel.desktop');
app.activate(-1);
},
_onPreferencesActivate: function() { _onPreferencesActivate: function() {
Main.overview.hide(); Main.overview.hide();
let app = Shell.AppSystem.get_default().lookup_app('gnome-control-center.desktop'); let app = Shell.AppSystem.get_default().lookup_app('gnome-control-center.desktop');
@@ -767,13 +712,16 @@ const UserMenuButton = new Lang.Class({
_onLockScreenActivate: function() { _onLockScreenActivate: function() {
Main.overview.hide(); Main.overview.hide();
Main.screenShield.lock(true); this._screenSaverProxy.LockRemote();
}, },
_onLoginScreenActivate: function() { _onLoginScreenActivate: function() {
Main.overview.hide(); Main.overview.hide();
Main.screenShield.lock(false); // Ensure we only move to GDM after the screensaver has activated; in some
this._userManager.goto_login_session(); // OS configurations, the X server may block event processing on VT switch
this._screenSaverProxy.SetActiveRemote(true, Lang.bind(this, function() {
this._userManager.goto_login_session();
}));
}, },
_onQuitSessionActivate: function() { _onQuitSessionActivate: function() {
@@ -781,27 +729,17 @@ const UserMenuButton = new Lang.Class({
this._session.LogoutRemote(0); this._session.LogoutRemote(0);
}, },
_onInstallUpdatesActivate: function() {
Main.overview.hide();
Util.spawn(['pkexec', '/usr/libexec/pk-trigger-offline-update']);
this._session.RebootRemote();
},
_onSuspendOrPowerOffActivate: function() { _onSuspendOrPowerOffActivate: function() {
Main.overview.hide(); Main.overview.hide();
if (this._haveShutdown && if (this._haveSuspend &&
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) { this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
this._session.ShutdownRemote(); // Ensure we only suspend after locking the screen
} else { this._screenSaverProxy.LockRemote(Lang.bind(this, function() {
let tmpId = Main.screenShield.connect('lock-screen-shown', Lang.bind(this, function() {
Main.screenShield.disconnect(tmpId);
this._upClient.suspend_sync(null); this._upClient.suspend_sync(null);
})); }));
} else {
Main.screenShield.lock(true); this._session.ShutdownRemote();
} }
} }
}); });

View File

@@ -168,35 +168,34 @@ const WandaSearchProvider = new Lang.Class({
this.parent(_("Your favorite Easter Egg")); this.parent(_("Your favorite Easter Egg"));
}, },
getResultMetas: function(fish, callback) { getResultMetas: function(fish) {
callback([{ 'id': fish[0], // there may be many fish in the sea, but return [{ 'id': fish[0], // there may be many fish in the sea, but
// only one which speaks the truth! // only one which speaks the truth!
'name': capitalize(fish[0]), 'name': capitalize(fish[0]),
'createIcon': function(iconSize) { 'createIcon': function(iconSize) {
// for DND only (maybe could be improved) // for DND only (maybe could be improved)
// DON'T use St.Icon here, it crashes the shell // DON'T use St.Icon here, it crashes the shell
// (dnd.js code assumes it can query the actor size // (dnd.js code assumes it can query the actor size
// without parenting it, while StWidget accesses // without parenting it, while StWidget accesses
// StThemeNode in get_preferred_width/height, which // StThemeNode in get_preferred_width/height, which
// triggers an assertion failure) // triggers an assertion failure)
return St.TextureCache.get_default().load_icon_name(null, return St.TextureCache.get_default().load_icon_name(null,
'face-smile', 'face-smile',
St.IconType.FULLCOLOR, St.IconType.FULLCOLOR,
iconSize); iconSize);
} }
}]); }];
}, },
getInitialResultSet: function(terms) { getInitialResultSet: function(terms) {
if (terms.join(' ') == MAGIC_FISH_KEY) { if (terms.join(' ') == MAGIC_FISH_KEY) {
this.searchSystem.pushResults(this, [ FISH_NAME ]); return [ FISH_NAME ];
} else {
this.searchSystem.pushResults(this, []);
} }
return [];
}, },
getSubsearchResultSet: function(previousResults, terms) { getSubsearchResultSet: function(previousResults, terms) {
this.getInitialResultSet(terms); return this.getInitialResultSet(terms);
}, },
activateResult: function(fish, params) { activateResult: function(fish, params) {

View File

@@ -53,10 +53,10 @@ const Source = new Lang.Class({
Extends: MessageTray.Source, Extends: MessageTray.Source,
_init: function(app, window) { _init: function(app, window) {
this.parent(app.get_name());
this._window = window; this._window = window;
this._app = app; this._app = app;
this._setSummaryIcon(this.createNotificationIcon());
this.parent(app.get_name());
this.signalIDs = []; this.signalIDs = [];
this.signalIDs.push(this._window.connect('notify::demands-attention', Lang.bind(this, function() { this.destroy(); }))); this.signalIDs.push(this._window.connect('notify::demands-attention', Lang.bind(this, function() { this.destroy(); })));
@@ -73,8 +73,8 @@ const Source = new Lang.Class({
this.signalIDs = []; this.signalIDs = [];
}, },
createIcon : function(size) { createNotificationIcon : function() {
return this._app.create_icon_texture(size); return this._app.create_icon_texture(this.ICON_SIZE);
}, },
open : function(notification) { open : function(notification) {

View File

@@ -13,40 +13,66 @@ const WorkspaceSwitcherPopup = imports.ui.workspaceSwitcherPopup;
const Main = imports.ui.main; const Main = imports.ui.main;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings';
const WINDOW_ANIMATION_TIME = 0.25; const WINDOW_ANIMATION_TIME = 0.25;
const DIM_DESATURATION = 0.6;
const DIM_BRIGHTNESS = -0.1;
const DIM_TIME = 0.500; const DIM_TIME = 0.500;
const UNDIM_TIME = 0.250; const UNDIM_TIME = 0.250;
var dimShader = undefined;
function getDimShaderSource() {
if (!dimShader)
dimShader = Shell.get_file_contents_utf8_sync(global.datadir + '/shaders/dim-window.glsl');
return dimShader;
}
function getTopInvisibleBorder(metaWindow) {
let outerRect = metaWindow.get_outer_rect();
let inputRect = metaWindow.get_input_rect();
return outerRect.y - inputRect.y;
}
const WindowDimmer = new Lang.Class({ const WindowDimmer = new Lang.Class({
Name: 'WindowDimmer', Name: 'WindowDimmer',
_init: function(actor) { _init: function(actor) {
this._desaturateEffect = new Clutter.DesaturateEffect(); if (Clutter.feature_available(Clutter.FeatureFlags.SHADERS_GLSL)) {
this._brightnessEffect = new Clutter.BrightnessContrastEffect(); this._effect = new Clutter.ShaderEffect({ shader_type: Clutter.ShaderType.FRAGMENT_SHADER });
actor.add_effect(this._desaturateEffect); this._effect.set_shader_source(getDimShaderSource());
actor.add_effect(this._brightnessEffect); } else {
this._effect = null;
}
this.actor = actor; this.actor = actor;
this._dimFactor = 0.0;
}, },
setEnabled: function(enabled) { set dimFraction(fraction) {
this._desaturateEffect.enabled = enabled; this._dimFraction = fraction;
this._brightnessEffect.enabled = enabled;
if (this._effect == null)
return;
if (!Meta.prefs_get_attach_modal_dialogs()) {
this._effect.enabled = false;
return;
}
if (fraction > 0.01) {
Shell.shader_effect_set_double_uniform(this._effect, 'height', this.actor.get_height());
Shell.shader_effect_set_double_uniform(this._effect, 'fraction', fraction);
if (!this._effect.actor)
this.actor.add_effect(this._effect);
} else {
if (this._effect.actor)
this.actor.remove_effect(this._effect);
}
}, },
set dimFactor(factor) { get dimFraction() {
this._dimFactor = factor; return this._dimFraction;
this._desaturateEffect.set_factor(factor * DIM_DESATURATION);
this._brightnessEffect.set_brightness(factor * DIM_BRIGHTNESS);
}, },
get dimFactor() { _dimFraction: 0.0
return this._dimFactor;
}
}); });
function getWindowDimmer(actor) { function getWindowDimmer(actor) {
@@ -67,7 +93,6 @@ const WindowManager = new Lang.Class({
this._unmaximizing = []; this._unmaximizing = [];
this._mapping = []; this._mapping = [];
this._destroying = []; this._destroying = [];
this._movingWindow = null;
this._dimmedWindows = []; this._dimmedWindows = [];
@@ -99,14 +124,6 @@ const WindowManager = new Lang.Class({
Lang.bind(this, this._showWorkspaceSwitcher)); Lang.bind(this, this._showWorkspaceSwitcher));
Meta.keybindings_set_custom_handler('switch-to-workspace-down', Meta.keybindings_set_custom_handler('switch-to-workspace-down',
Lang.bind(this, this._showWorkspaceSwitcher)); Lang.bind(this, this._showWorkspaceSwitcher));
Meta.keybindings_set_custom_handler('move-to-workspace-left',
Lang.bind(this, this._showWorkspaceSwitcher));
Meta.keybindings_set_custom_handler('move-to-workspace-right',
Lang.bind(this, this._showWorkspaceSwitcher));
Meta.keybindings_set_custom_handler('move-to-workspace-up',
Lang.bind(this, this._showWorkspaceSwitcher));
Meta.keybindings_set_custom_handler('move-to-workspace-down',
Lang.bind(this, this._showWorkspaceSwitcher));
Meta.keybindings_set_custom_handler('switch-windows', Meta.keybindings_set_custom_handler('switch-windows',
Lang.bind(this, this._startAppSwitcher)); Lang.bind(this, this._startAppSwitcher));
Meta.keybindings_set_custom_handler('switch-group', Meta.keybindings_set_custom_handler('switch-group',
@@ -117,18 +134,14 @@ const WindowManager = new Lang.Class({
Lang.bind(this, this._startAppSwitcher)); Lang.bind(this, this._startAppSwitcher));
Meta.keybindings_set_custom_handler('switch-panels', Meta.keybindings_set_custom_handler('switch-panels',
Lang.bind(this, this._startA11ySwitcher)); Lang.bind(this, this._startA11ySwitcher));
global.display.add_keybinding('open-application-menu',
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
Meta.KeyBindingFlags.NONE,
Lang.bind(this, this._openAppMenu));
Main.overview.connect('showing', Lang.bind(this, function() { Main.overview.connect('showing', Lang.bind(this, function() {
for (let i = 0; i < this._dimmedWindows.length; i++) for (let i = 0; i < this._dimmedWindows.length; i++)
this._undimWindow(this._dimmedWindows[i]); this._undimWindow(this._dimmedWindows[i], true);
})); }));
Main.overview.connect('hiding', Lang.bind(this, function() { Main.overview.connect('hiding', Lang.bind(this, function() {
for (let i = 0; i < this._dimmedWindows.length; i++) for (let i = 0; i < this._dimmedWindows.length; i++)
this._dimWindow(this._dimmedWindows[i]); this._dimWindow(this._dimmedWindows[i], true);
})); }));
}, },
@@ -140,14 +153,12 @@ const WindowManager = new Lang.Class({
this._animationBlockCount = Math.max(0, this._animationBlockCount - 1); this._animationBlockCount = Math.max(0, this._animationBlockCount - 1);
}, },
_shouldAnimate: function() { _shouldAnimate : function(actor) {
return !(Main.overview.visible || this._animationBlockCount > 0); if (Main.overview.visible || this._animationBlockCount > 0)
},
_shouldAnimateActor: function(actor) {
if (!this._shouldAnimate())
return false; return false;
return actor.meta_window.get_window_type() == Meta.WindowType.NORMAL; if (actor && (actor.meta_window.get_window_type() != Meta.WindowType.NORMAL))
return false;
return true;
}, },
_removeEffect : function(list, actor) { _removeEffect : function(list, actor) {
@@ -160,7 +171,7 @@ const WindowManager = new Lang.Class({
}, },
_minimizeWindow : function(shellwm, actor) { _minimizeWindow : function(shellwm, actor) {
if (!this._shouldAnimateActor(actor)) { if (!this._shouldAnimate(actor)) {
shellwm.completed_minimize(actor); shellwm.completed_minimize(actor);
return; return;
} }
@@ -244,47 +255,43 @@ const WindowManager = new Lang.Class({
window._dimmed = true; window._dimmed = true;
this._dimmedWindows.push(window); this._dimmedWindows.push(window);
if (!Main.overview.visible) if (!Main.overview.visible)
this._dimWindow(window); this._dimWindow(window, true);
} else if (!shouldDim && window._dimmed) { } else if (!shouldDim && window._dimmed) {
window._dimmed = false; window._dimmed = false;
this._dimmedWindows = this._dimmedWindows.filter(function(win) { this._dimmedWindows = this._dimmedWindows.filter(function(win) {
return win != window; return win != window;
}); });
if (!Main.overview.visible) if (!Main.overview.visible)
this._undimWindow(window); this._undimWindow(window, true);
} }
}, },
_dimWindow: function(window) { _dimWindow: function(window, animate) {
let actor = window.get_compositor_private(); let actor = window.get_compositor_private();
if (!actor) if (!actor)
return; return;
let dimmer = getWindowDimmer(actor); if (animate)
let enabled = Meta.prefs_get_attach_modal_dialogs(); Tweener.addTween(getWindowDimmer(actor),
dimmer.setEnabled(enabled); { dimFraction: 1.0,
if (!enabled) time: DIM_TIME,
return; transition: 'linear'
Tweener.addTween(dimmer, });
{ dimFactor: 1.0, else
time: DIM_TIME, getWindowDimmer(actor).dimFraction = 1.0;
transition: 'linear'
});
}, },
_undimWindow: function(window) { _undimWindow: function(window, animate) {
let actor = window.get_compositor_private(); let actor = window.get_compositor_private();
if (!actor) if (!actor)
return; return;
let dimmer = getWindowDimmer(actor); if (animate)
let enabled = Meta.prefs_get_attach_modal_dialogs(); Tweener.addTween(getWindowDimmer(actor),
dimmer.setEnabled(enabled); { dimFraction: 0.0,
if (!enabled) time: UNDIM_TIME,
return; transition: 'linear'
Tweener.addTween(dimmer, });
{ dimFactor: 0.0, else
time: UNDIM_TIME, getWindowDimmer(actor).dimFraction = 0.0;
transition: 'linear'
});
}, },
_mapWindow : function(shellwm, actor) { _mapWindow : function(shellwm, actor) {
@@ -304,12 +311,28 @@ const WindowManager = new Lang.Class({
})); }));
if (actor.meta_window.is_attached_dialog()) { if (actor.meta_window.is_attached_dialog()) {
this._checkDimming(actor.get_meta_window().get_transient_for()); this._checkDimming(actor.get_meta_window().get_transient_for());
if (this._shouldAnimate()) {
actor.set_scale(1.0, 0.0);
actor.show();
this._mapping.push(actor);
if (!this._shouldAnimate()) { Tweener.addTween(actor,
shellwm.completed_map(actor); { scale_y: 1,
time: WINDOW_ANIMATION_TIME,
transition: "easeOutQuad",
onComplete: this._mapWindowDone,
onCompleteScope: this,
onCompleteParams: [shellwm, actor],
onOverwrite: this._mapWindowOverwrite,
onOverwriteScope: this,
onOverwriteParams: [shellwm, actor]
});
return; return;
} }
} else if (!this._shouldAnimateActor(actor)) { shellwm.completed_map(actor);
return;
}
if (!this._shouldAnimate(actor)) {
shellwm.completed_map(actor); shellwm.completed_map(actor);
return; return;
} }
@@ -365,7 +388,7 @@ const WindowManager = new Lang.Class({
return; return;
} }
actor.opacity = 255; actor.set_scale(1.0, 1.0);
actor.show(); actor.show();
this._destroying.push(actor); this._destroying.push(actor);
@@ -375,7 +398,7 @@ const WindowManager = new Lang.Class({
})); }));
Tweener.addTween(actor, Tweener.addTween(actor,
{ opacity: 0, { scale_y: 0,
time: WINDOW_ANIMATION_TIME, time: WINDOW_ANIMATION_TIME,
transition: "easeOutQuad", transition: "easeOutQuad",
onComplete: this._destroyWindowDone, onComplete: this._destroyWindowDone,
@@ -437,13 +460,11 @@ const WindowManager = new Lang.Class({
this._switchData = switchData; this._switchData = switchData;
switchData.inGroup = new Clutter.Group(); switchData.inGroup = new Clutter.Group();
switchData.outGroup = new Clutter.Group(); switchData.outGroup = new Clutter.Group();
switchData.movingWindowBin = new Clutter.Group();
switchData.windows = []; switchData.windows = [];
let wgroup = global.window_group; let wgroup = global.window_group;
wgroup.add_actor(switchData.inGroup); wgroup.add_actor(switchData.inGroup);
wgroup.add_actor(switchData.outGroup); wgroup.add_actor(switchData.outGroup);
wgroup.add_actor(switchData.movingWindowBin);
for (let i = 0; i < windows.length; i++) { for (let i = 0; i < windows.length; i++) {
let window = windows[i]; let window = windows[i];
@@ -451,12 +472,7 @@ const WindowManager = new Lang.Class({
if (!window.meta_window.showing_on_its_workspace()) if (!window.meta_window.showing_on_its_workspace())
continue; continue;
if (this._movingWindow && window.meta_window == this._movingWindow) { if (window.get_workspace() == from) {
switchData.movingWindow = { window: window,
parent: window.get_parent() };
switchData.windows.push(switchData.movingWindow);
window.reparent(switchData.movingWindowBin);
} else if (window.get_workspace() == from) {
switchData.windows.push({ window: window, switchData.windows.push({ window: window,
parent: window.get_parent() }); parent: window.get_parent() });
window.reparent(switchData.outGroup); window.reparent(switchData.outGroup);
@@ -471,8 +487,6 @@ const WindowManager = new Lang.Class({
switchData.inGroup.set_position(-xDest, -yDest); switchData.inGroup.set_position(-xDest, -yDest);
switchData.inGroup.raise_top(); switchData.inGroup.raise_top();
switchData.movingWindowBin.raise_top();
Tweener.addTween(switchData.outGroup, Tweener.addTween(switchData.outGroup,
{ x: xDest, { x: xDest,
y: yDest, y: yDest,
@@ -510,10 +524,6 @@ const WindowManager = new Lang.Class({
Tweener.removeTweens(switchData.outGroup); Tweener.removeTweens(switchData.outGroup);
switchData.inGroup.destroy(); switchData.inGroup.destroy();
switchData.outGroup.destroy(); switchData.outGroup.destroy();
switchData.movingWindowBin.destroy();
if (this._movingWindow)
this._movingWindow = null;
shellwm.completed_switch_workspace(); shellwm.completed_switch_workspace();
}, },
@@ -521,7 +531,7 @@ const WindowManager = new Lang.Class({
_startAppSwitcher : function(display, screen, window, binding) { _startAppSwitcher : function(display, screen, window, binding) {
/* prevent a corner case where both popups show up at once */ /* prevent a corner case where both popups show up at once */
if (this._workspaceSwitcherPopup != null) if (this._workspaceSwitcherPopup != null)
this._workspaceSwitcherPopup.destroy(); this._workspaceSwitcherPopup.actor.hide();
let tabPopup = new AltTab.AltTabPopup(); let tabPopup = new AltTab.AltTabPopup();
@@ -537,64 +547,80 @@ const WindowManager = new Lang.Class({
Main.ctrlAltTabManager.popup(backwards, binding.get_mask()); Main.ctrlAltTabManager.popup(backwards, binding.get_mask());
}, },
_openAppMenu : function(display, screen, window, event, binding) {
Main.panel.openAppMenu();
},
_showWorkspaceSwitcher : function(display, screen, window, binding) { _showWorkspaceSwitcher : function(display, screen, window, binding) {
if (screen.n_workspaces == 1) if (screen.n_workspaces == 1)
return; return;
let [action,,,direction] = binding.get_name().split('-'); if (this._workspaceSwitcherPopup == null)
let direction = Meta.MotionDirection[direction.toUpperCase()]; this._workspaceSwitcherPopup = new WorkspaceSwitcherPopup.WorkspaceSwitcherPopup();
let newWs;
if (binding.get_name() == 'switch-to-workspace-up')
if (direction != Meta.MotionDirection.UP && this.actionMoveWorkspaceUp();
direction != Meta.MotionDirection.DOWN) else if (binding.get_name() == 'switch-to-workspace-down')
return; this.actionMoveWorkspaceDown();
// left/right would effectively act as synonyms for up/down if we enabled them;
if (action == 'switch') // but that could be considered confusing.
newWs = this.actionMoveWorkspace(direction); // else if (binding.get_name() == 'switch-to-workspace-left')
else // this.actionMoveWorkspaceLeft();
newWs = this.actionMoveWindow(window, direction); // else if (binding.get_name() == 'switch-to-workspace-right')
// this.actionMoveWorkspaceRight();
if (!Main.overview.visible) {
if (this._workspaceSwitcherPopup == null) {
this._workspaceSwitcherPopup = new WorkspaceSwitcherPopup.WorkspaceSwitcherPopup();
this._workspaceSwitcherPopup.connect('destroy', Lang.bind(this, function() {
this._workspaceSwitcherPopup = null;
}));
}
this._workspaceSwitcherPopup.display(direction, newWs.index());
}
}, },
actionMoveWorkspace: function(direction) { actionMoveWorkspaceLeft: function() {
let activeWorkspace = global.screen.get_active_workspace(); let rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL);
let toActivate = activeWorkspace.get_neighbor(direction); let activeWorkspaceIndex = global.screen.get_active_workspace_index();
let indexToActivate = activeWorkspaceIndex;
if (rtl && activeWorkspaceIndex < global.screen.n_workspaces - 1)
indexToActivate++;
else if (!rtl && activeWorkspaceIndex > 0)
indexToActivate--;
if (activeWorkspace != toActivate) if (indexToActivate != activeWorkspaceIndex)
toActivate.activate(global.get_current_time()); global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
return toActivate; if (!Main.overview.visible)
this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.UP, indexToActivate);
}, },
actionMoveWindow: function(window, direction) { actionMoveWorkspaceRight: function() {
let activeWorkspace = global.screen.get_active_workspace(); let rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL);
let toActivate = activeWorkspace.get_neighbor(direction); let activeWorkspaceIndex = global.screen.get_active_workspace_index();
let indexToActivate = activeWorkspaceIndex;
if (rtl && activeWorkspaceIndex > 0)
indexToActivate--;
else if (!rtl && activeWorkspaceIndex < global.screen.n_workspaces - 1)
indexToActivate++;
if (activeWorkspace != toActivate) { if (indexToActivate != activeWorkspaceIndex)
// This won't have any effect for "always sticky" windows global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
// (like desktop windows or docks)
this._movingWindow = window; if (!Main.overview.visible)
window.change_workspace(toActivate); this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.DOWN, indexToActivate);
global.display.clear_mouse_mode();
toActivate.activate_with_focus (window, global.get_current_time());
}
return toActivate;
}, },
actionMoveWorkspaceUp: function() {
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
let indexToActivate = activeWorkspaceIndex;
if (activeWorkspaceIndex > 0)
indexToActivate--;
if (indexToActivate != activeWorkspaceIndex)
global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
if (!Main.overview.visible)
this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.UP, indexToActivate);
},
actionMoveWorkspaceDown: function() {
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
let indexToActivate = activeWorkspaceIndex;
if (activeWorkspaceIndex < global.screen.n_workspaces - 1)
indexToActivate++;
if (indexToActivate != activeWorkspaceIndex)
global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
if (!Main.overview.visible)
this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.DOWN, indexToActivate);
}
}); });

View File

@@ -1122,6 +1122,26 @@ const Workspace = new Lang.Class({
} }
}, },
_showAllOverlays: function() {
let currentWorkspace = global.screen.get_active_workspace();
for (let i = 0; i < this._windows.length; i++) {
let clone = this._windows[i];
let overlay = this._windowOverlays[i];
this._showWindowOverlay(clone, overlay,
this.metaWorkspace == null || this.metaWorkspace == currentWorkspace);
}
},
_hideAllOverlays: function() {
for (let i = 0; i < this._windows.length; i++) {
let clone = this._windows[i];
Tweener.removeTweens(clone.actor);
let overlay = this._windowOverlays[i];
if (overlay)
overlay.hide();
}
},
_delayedWindowRepositioning: function() { _delayedWindowRepositioning: function() {
if (this._windowIsZooming) if (this._windowIsZooming)
return true; return true;
@@ -1233,7 +1253,7 @@ const Workspace = new Lang.Class({
if (!this._isMyWindow(win) || !this._isOverviewWindow(win)) if (!this._isMyWindow(win) || !this._isOverviewWindow(win))
return; return;
let [clone, overlay] = this._addWindowClone(win); let clone = this._addWindowClone(win);
if (win._overviewHint) { if (win._overviewHint) {
let x = win._overviewHint.x - this.actor.x; let x = win._overviewHint.x - this.actor.x;
@@ -1243,7 +1263,6 @@ const Workspace = new Lang.Class({
clone.actor.set_position (x, y); clone.actor.set_position (x, y);
clone.actor.set_scale (scale, scale); clone.actor.set_scale (scale, scale);
this._updateWindowOverlayPositions(clone, overlay, x, y, scale, false);
} else { } else {
// Position new windows at the top corner of the workspace rather // Position new windows at the top corner of the workspace rather
// than where they were placed for real to avoid the window // than where they were placed for real to avoid the window
@@ -1303,10 +1322,7 @@ const Workspace = new Lang.Class({
this.leavingOverview = true; this.leavingOverview = true;
for (let i = 0; i < this._windows.length; i++) { this._hideAllOverlays();
let clone = this._windows[i];
Tweener.removeTweens(clone.actor);
}
if (this._repositionWindowsId > 0) { if (this._repositionWindowsId > 0) {
Mainloop.source_remove(this._repositionWindowsId); Mainloop.source_remove(this._repositionWindowsId);
@@ -1321,10 +1337,6 @@ const Workspace = new Lang.Class({
// Position and scale the windows. // Position and scale the windows.
for (let i = 0; i < this._windows.length; i++) { for (let i = 0; i < this._windows.length; i++) {
let clone = this._windows[i]; let clone = this._windows[i];
let overlay = this._windowOverlays[i];
if (overlay)
overlay.hide();
clone.zoomFromOverview(); clone.zoomFromOverview();
@@ -1349,6 +1361,7 @@ const Workspace = new Lang.Class({
}); });
} }
} }
}, },
destroy : function() { destroy : function() {
@@ -1439,7 +1452,7 @@ const Workspace = new Lang.Class({
this._windows.push(clone); this._windows.push(clone);
this._windowOverlays.push(overlay); this._windowOverlays.push(overlay);
return [clone, overlay]; return clone;
}, },
_onShowOverlayClose: function (windowOverlay) { _onShowOverlayClose: function (windowOverlay) {

View File

@@ -3,17 +3,18 @@
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St; const St = imports.gi.St;
const Main = imports.ui.main; const Main = imports.ui.main;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const ANIMATION_TIME = 0.1; const ANIMATION_TIME = 0.1;
const DISPLAY_TIMEOUT = 600; const DISPLAY_TIMEOUT = 600;
const UP = -1;
const DOWN = 1;
const WorkspaceSwitcherPopup = new Lang.Class({ const WorkspaceSwitcherPopup = new Lang.Class({
Name: 'WorkspaceSwitcherPopup', Name: 'WorkspaceSwitcherPopup',
@@ -42,14 +43,12 @@ const WorkspaceSwitcherPopup = new Lang.Class({
this.actor.add_actor(this._container); this.actor.add_actor(this._container);
this._redisplay(); this._redraw();
this._position();
this.actor.hide(); this.actor.hide();
this._globalSignals = [];
this._globalSignals.push(global.screen.connect('workspace-added', Lang.bind(this, this._redisplay)));
this._globalSignals.push(global.screen.connect('workspace-removed', Lang.bind(this, this._redisplay)));
this._timeoutId = Mainloop.timeout_add(DISPLAY_TIMEOUT, Lang.bind(this, this._onTimeout)); this._timeoutId = Mainloop.timeout_add(DISPLAY_TIMEOUT, Lang.bind(this, this._onTimeout));
}, },
@@ -105,15 +104,15 @@ const WorkspaceSwitcherPopup = new Lang.Class({
} }
}, },
_redisplay: function() { _redraw : function(direction, activeWorkspaceIndex) {
this._list.destroy_all_children(); this._list.destroy_all_children();
for (let i = 0; i < global.screen.n_workspaces; i++) { for (let i = 0; i < global.screen.n_workspaces; i++) {
let indicator = null; let indicator = null;
if (i == this._activeWorkspaceIndex && this._direction == Meta.MotionDirection.UP) if (i == activeWorkspaceIndex && direction == UP)
indicator = new St.Bin({ style_class: 'ws-switcher-active-up' }); indicator = new St.Bin({ style_class: 'ws-switcher-active-up' });
else if(i == this._activeWorkspaceIndex && this._direction == Meta.MotionDirection.DOWN) else if(i == activeWorkspaceIndex && direction == DOWN)
indicator = new St.Bin({ style_class: 'ws-switcher-active-down' }); indicator = new St.Bin({ style_class: 'ws-switcher-active-down' });
else else
indicator = new St.Bin({ style_class: 'ws-switcher-box' }); indicator = new St.Bin({ style_class: 'ws-switcher-box' });
@@ -121,13 +120,13 @@ const WorkspaceSwitcherPopup = new Lang.Class({
this._list.add_actor(indicator); this._list.add_actor(indicator);
} }
},
_position: function() {
let primary = Main.layoutManager.primaryMonitor; let primary = Main.layoutManager.primaryMonitor;
let [containerMinHeight, containerNatHeight] = this._container.get_preferred_height(global.screen_width); this._container.x = primary.x + Math.floor((primary.width - this._container.width) / 2);
let [containerMinWidth, containerNatWidth] = this._container.get_preferred_width(containerNatHeight);
this._container.x = primary.x + Math.floor((primary.width - containerNatWidth) / 2);
this._container.y = primary.y + Main.panel.actor.height + this._container.y = primary.y + Main.panel.actor.height +
Math.floor(((primary.height - Main.panel.actor.height) - containerNatHeight) / 2); Math.floor(((primary.height - Main.panel.actor.height) - this._container.height) / 2);
}, },
_show : function() { _show : function() {
@@ -135,14 +134,12 @@ const WorkspaceSwitcherPopup = new Lang.Class({
time: ANIMATION_TIME, time: ANIMATION_TIME,
transition: 'easeOutQuad' transition: 'easeOutQuad'
}); });
this._position();
this.actor.show(); this.actor.show();
}, },
display : function(direction, activeWorkspaceIndex) { display : function(direction, activeWorkspaceIndex) {
this._direction = direction; this._redraw(direction, activeWorkspaceIndex);
this._activeWorkspaceIndex = activeWorkspaceIndex;
this._redisplay();
if (this._timeoutId != 0) if (this._timeoutId != 0)
Mainloop.source_remove(this._timeoutId); Mainloop.source_remove(this._timeoutId);
this._timeoutId = Mainloop.timeout_add(DISPLAY_TIMEOUT, Lang.bind(this, this._onTimeout)); this._timeoutId = Mainloop.timeout_add(DISPLAY_TIMEOUT, Lang.bind(this, this._onTimeout));
@@ -155,22 +152,8 @@ const WorkspaceSwitcherPopup = new Lang.Class({
Tweener.addTween(this._container, { opacity: 0.0, Tweener.addTween(this._container, { opacity: 0.0,
time: ANIMATION_TIME, time: ANIMATION_TIME,
transition: 'easeOutQuad', transition: 'easeOutQuad',
onComplete: function() { this.destroy(); }, onComplete: function() { this.actor.hide(); },
onCompleteScope: this onCompleteScope: this
}); });
},
destroy: function() {
if (this._timeoutId)
Mainloop.source_remove(this._timeoutId);
this._timeoutId = 0;
for (let i = 0; i < this._globalSignals.length; i++)
global.screen.disconnect(this._globalSignals[i]);
this.actor.destroy();
this.emit('destroy');
} }
}); });
Signals.addSignalMethods(WorkspaceSwitcherPopup.prototype);

View File

@@ -667,7 +667,7 @@ const ThumbnailsBox = new Lang.Class({
if (this._dropWorkspace != -1) if (this._dropWorkspace != -1)
return this._thumbnails[this._dropWorkspace].handleDragOverInternal(source, time); return this._thumbnails[this._dropWorkspace].handleDragOverInternal(source, time);
else if (this._dropPlaceholderPos != -1) else if (this._dropPlaceholderPos != -1)
return source.realWindow ? DND.DragMotionResult.MOVE_DROP : DND.DragMotionResult.COPY_DROP; return DND.DragMotionResult.MOVE_DROP;
else else
return DND.DragMotionResult.CONTINUE; return DND.DragMotionResult.CONTINUE;
}, },

View File

@@ -187,7 +187,7 @@ const WorkspacesView = new Lang.Class({
activeWorkspace.actor.raise_top(); activeWorkspace.actor.raise_top();
this.actor.remove_clip(); this.actor.remove_clip(this._x, this._y, this._width, this._height);
for (let w = 0; w < this._workspaces.length; w++) for (let w = 0; w < this._workspaces.length; w++)
this._workspaces[w].zoomFromOverview(); this._workspaces[w].zoomFromOverview();
@@ -529,11 +529,9 @@ const WorkspacesDisplay = new Lang.Class({
this._updateAlwaysZoom(); this._updateAlwaysZoom();
})); }));
global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._workspacesChanged));
this._switchWorkspaceNotifyId = 0; this._switchWorkspaceNotifyId = 0;
this._nWorkspacesChangedId = 0;
this._itemDragBeginId = 0; this._itemDragBeginId = 0;
this._itemDragCancelledId = 0; this._itemDragCancelledId = 0;
this._itemDragEndId = 0; this._itemDragEndId = 0;
@@ -572,6 +570,9 @@ const WorkspacesDisplay = new Lang.Class({
global.screen.connect('restacked', global.screen.connect('restacked',
Lang.bind(this, this._onRestacked)); Lang.bind(this, this._onRestacked));
if (this._nWorkspacesChangedId == 0)
this._nWorkspacesChangedId = global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._workspacesChanged));
if (this._itemDragBeginId == 0) if (this._itemDragBeginId == 0)
this._itemDragBeginId = Main.overview.connect('item-drag-begin', this._itemDragBeginId = Main.overview.connect('item-drag-begin',
Lang.bind(this, this._dragBegin)); Lang.bind(this, this._dragBegin));
@@ -843,7 +844,10 @@ const WorkspacesDisplay = new Lang.Class({
if (!primaryView) if (!primaryView)
return; return;
primaryView.actor.opacity = opacity; primaryView.actor.opacity = opacity;
primaryView.actor.visible = opacity != 0; if (opacity == 0)
primaryView.actor.hide();
else
primaryView.actor.show();
})); }));
})); }));
}, },
@@ -924,16 +928,19 @@ const WorkspacesDisplay = new Lang.Class({
}, },
_workspacesChanged: function() { _workspacesChanged: function() {
let oldNumWorkspaces = this._workspaces[0].length;
let newNumWorkspaces = global.screen.n_workspaces;
let active = global.screen.get_active_workspace_index();
if (oldNumWorkspaces == newNumWorkspaces)
return;
this._updateAlwaysZoom(); this._updateAlwaysZoom();
this._updateZoom(); this._updateZoom();
if (this._workspacesViews == null) if (this._workspacesViews == null)
return; return;
let oldNumWorkspaces = this._workspaces[0].length;
let newNumWorkspaces = global.screen.n_workspaces;
let active = global.screen.get_active_workspace_index();
let lostWorkspaces = []; let lostWorkspaces = [];
if (newNumWorkspaces > oldNumWorkspaces) { if (newNumWorkspaces > oldNumWorkspaces) {
let monitors = Main.layoutManager.monitors; let monitors = Main.layoutManager.monitors;
@@ -1048,10 +1055,10 @@ const WorkspacesDisplay = new Lang.Class({
_onScrollEvent: function (actor, event) { _onScrollEvent: function (actor, event) {
switch ( event.get_scroll_direction() ) { switch ( event.get_scroll_direction() ) {
case Clutter.ScrollDirection.UP: case Clutter.ScrollDirection.UP:
Main.wm.actionMoveWorkspace(Meta.MotionDirection.UP); Main.wm.actionMoveWorkspaceUp();
break; break;
case Clutter.ScrollDirection.DOWN: case Clutter.ScrollDirection.DOWN:
Main.wm.actionMoveWorkspace(Meta.MotionDirection.DOWN); Main.wm.actionMoveWorkspaceDown();
break; break;
} }
} }

View File

@@ -1,6 +1,6 @@
data/gnome-shell.desktop.in.in data/gnome-shell.desktop.in.in
data/gnome-shell-extension-prefs.desktop.in.in data/gnome-shell-extension-prefs.desktop.in.in
data/org.gnome.shell.gschema.xml.in.in data/org.gnome.shell.gschema.xml.in
js/extensionPrefs/main.js js/extensionPrefs/main.js
js/gdm/loginDialog.js js/gdm/loginDialog.js
js/gdm/powerMenu.js js/gdm/powerMenu.js
@@ -9,10 +9,10 @@ js/ui/appDisplay.js
js/ui/appFavorites.js js/ui/appFavorites.js
js/ui/autorunManager.js js/ui/autorunManager.js
js/ui/calendar.js js/ui/calendar.js
js/ui/contactDisplay.js
js/ui/dash.js js/ui/dash.js
js/ui/dateMenu.js js/ui/dateMenu.js
js/ui/endSessionDialog.js js/ui/endSessionDialog.js
js/ui/extensionDownloader.js
js/ui/extensionSystem.js js/ui/extensionSystem.js
js/ui/keyboard.js js/ui/keyboard.js
js/ui/keyringPrompt.js js/ui/keyringPrompt.js
@@ -37,12 +37,10 @@ js/ui/status/network.js
js/ui/status/power.js js/ui/status/power.js
js/ui/status/volume.js js/ui/status/volume.js
js/ui/telepathyClient.js js/ui/telepathyClient.js
js/ui/unlockDialog.js
js/ui/userMenu.js js/ui/userMenu.js
js/ui/viewSelector.js js/ui/viewSelector.js
js/ui/wanda.js js/ui/wanda.js
js/ui/windowAttentionHandler.js js/ui/windowAttentionHandler.js
src/calendar-server/evolution-calendar.desktop.in.in
src/gvc/gvc-mixer-control.c src/gvc/gvc-mixer-control.c
src/main.c src/main.c
src/shell-app.c src/shell-app.c

View File

@@ -1,2 +1,2 @@
data/org.gnome.shell.evolution.calendar.gschema.xml.in data/gnome-shell.desktop.in
src/calendar-server/evolution-calendar.desktop.in data/gnome-shell-extension-prefs.desktop.in

905
po/ar.po

File diff suppressed because it is too large Load Diff

978
po/as.po

File diff suppressed because it is too large Load Diff

684
po/be.po

File diff suppressed because it is too large Load Diff

674
po/bg.po

File diff suppressed because it is too large Load Diff

132
po/ca.po
View File

@@ -3,21 +3,19 @@
# This file is distributed under the same license as the gnome-shell package. # This file is distributed under the same license as the gnome-shell package.
# Siegfried-Angel Gevatter Pujals <rainct@ubuntu.com>, 2009. # Siegfried-Angel Gevatter Pujals <rainct@ubuntu.com>, 2009.
# Gil Forcada <gilforcada@guifi.net>, 2010, 2011, 2012. # Gil Forcada <gilforcada@guifi.net>, 2010, 2011, 2012.
# Jordi Serratosa <jordis@softcatala.cat>, 2012.
# #
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: HEAD\n" "Project-Id-Version: HEAD\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-" "Report-Msgid-Bugs-To: \n"
"shell&keywords=I18N+L10N&component=general\n" "POT-Creation-Date: 2012-03-19 22:35+0100\n"
"POT-Creation-Date: 2012-04-01 22:09+0000\n" "PO-Revision-Date: 2012-03-19 22:35+0100\n"
"PO-Revision-Date: 2012-04-02 00:08+0200\n"
"Last-Translator: Gil Forcada <gilforcada@guifi.net>\n" "Last-Translator: Gil Forcada <gilforcada@guifi.net>\n"
"Language-Team: català; valencià <tradgnome@softcatala.org>\n" "Language-Team: català; valencià <tradgnome@softcatala.org>\n"
"Language: ca\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bits\n" "Content-Transfer-Encoding: 8bits\n"
"Language: ca\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n" "Plural-Forms: nplurals=2; plural=n != 1;\n"
#: ../data/gnome-shell.desktop.in.in.h:1 #: ../data/gnome-shell.desktop.in.in.h:1
@@ -136,43 +134,35 @@ msgid "If true, display the ISO week date in the calendar."
msgstr "Si és «true» (cert) es mostra el número de la setmana en el calendari." msgstr "Si és «true» (cert) es mostra el número de la setmana en el calendari."
#: ../data/org.gnome.shell.gschema.xml.in.h:16 #: ../data/org.gnome.shell.gschema.xml.in.h:16
msgid "Keybinding to open the application menu"
msgstr "Vinculació per obrir el menú d'aplicació"
#: ../data/org.gnome.shell.gschema.xml.in.h:17
msgid "Keybinding to open the application menu."
msgstr "La vinculació per obrir el menú d'aplicació."
#: ../data/org.gnome.shell.gschema.xml.in.h:18
msgid "Which keyboard to use" msgid "Which keyboard to use"
msgstr "Quin tipus de teclat s'ha d'utilitzar" msgstr "Quin tipus de teclat s'ha d'utilitzar"
#: ../data/org.gnome.shell.gschema.xml.in.h:19 #: ../data/org.gnome.shell.gschema.xml.in.h:17
msgid "The type of keyboard to use." msgid "The type of keyboard to use."
msgstr "El tipus de teclat que s'utilitzarà." msgstr "El tipus de teclat que s'utilitzarà."
#: ../data/org.gnome.shell.gschema.xml.in.h:20 #: ../data/org.gnome.shell.gschema.xml.in.h:18
msgid "Show time with seconds" msgid "Show time with seconds"
msgstr "Mostra l'hora amb segons" msgstr "Mostra l'hora amb segons"
#: ../data/org.gnome.shell.gschema.xml.in.h:21 #: ../data/org.gnome.shell.gschema.xml.in.h:19
msgid "If true, display seconds in time." msgid "If true, display seconds in time."
msgstr "Si és «true» (cert) es mostren els segons." msgstr "Si és «true» (cert) es mostren els segons."
#: ../data/org.gnome.shell.gschema.xml.in.h:22 #: ../data/org.gnome.shell.gschema.xml.in.h:20
msgid "Show date in clock" msgid "Show date in clock"
msgstr "Mostra la data en el rellotge" msgstr "Mostra la data en el rellotge"
#: ../data/org.gnome.shell.gschema.xml.in.h:23 #: ../data/org.gnome.shell.gschema.xml.in.h:21
msgid "If true, display date in the clock, in addition to time." msgid "If true, display date in the clock, in addition to time."
msgstr "" msgstr ""
"Si és «true» (cert) es mostra la data en el rellotge a més a més de l'hora." "Si és «true» (cert) es mostra la data en el rellotge a més a més de l'hora."
#: ../data/org.gnome.shell.gschema.xml.in.h:24 #: ../data/org.gnome.shell.gschema.xml.in.h:22
msgid "Framerate used for recording screencasts." msgid "Framerate used for recording screencasts."
msgstr "Imatges per segon per enregistrar els screencasts." msgstr "Imatges per segon per enregistrar els screencasts."
#: ../data/org.gnome.shell.gschema.xml.in.h:25 #: ../data/org.gnome.shell.gschema.xml.in.h:23
msgid "" msgid ""
"The framerate of the resulting screencast recordered by GNOME Shell's " "The framerate of the resulting screencast recordered by GNOME Shell's "
"screencast recorder in frames-per-second." "screencast recorder in frames-per-second."
@@ -180,11 +170,11 @@ msgstr ""
"Les imatges per segon, en fotogrames per segon, de l'screencast enregistrat " "Les imatges per segon, en fotogrames per segon, de l'screencast enregistrat "
"per l'eina d'enregistrament del GNOME Shell." "per l'eina d'enregistrament del GNOME Shell."
#: ../data/org.gnome.shell.gschema.xml.in.h:26 #: ../data/org.gnome.shell.gschema.xml.in.h:24
msgid "The gstreamer pipeline used to encode the screencast" msgid "The gstreamer pipeline used to encode the screencast"
msgstr "El conducte GStreamer per enregistrar els screencasts" msgstr "El conducte GStreamer per enregistrar els screencasts"
#: ../data/org.gnome.shell.gschema.xml.in.h:28 #: ../data/org.gnome.shell.gschema.xml.in.h:26
#, no-c-format #, no-c-format
msgid "" msgid ""
"Sets the GStreamer pipeline used to encode recordings. It follows the syntax " "Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
@@ -210,11 +200,11 @@ msgstr ""
"enregistra amb el format WEBM i utilitza el còdec VP8. El %T és una variable " "enregistra amb el format WEBM i utilitza el còdec VP8. El %T és una variable "
"per estimar el nombre de fils d'execució paral·lels òptims del sistema." "per estimar el nombre de fils d'execució paral·lels òptims del sistema."
#: ../data/org.gnome.shell.gschema.xml.in.h:29 #: ../data/org.gnome.shell.gschema.xml.in.h:27
msgid "File extension used for storing the screencast" msgid "File extension used for storing the screencast"
msgstr "Extensió de fitxer per desar l'screencast" msgstr "Extensió de fitxer per desar l'screencast"
#: ../data/org.gnome.shell.gschema.xml.in.h:30 #: ../data/org.gnome.shell.gschema.xml.in.h:28
msgid "" msgid ""
"The filename for recorded screencasts will be a unique filename based on the " "The filename for recorded screencasts will be a unique filename based on the "
"current date, and use this extension. It should be changed when recording to " "current date, and use this extension. It should be changed when recording to "
@@ -515,7 +505,7 @@ msgstr "Ocupat"
#: ../js/ui/contactDisplay.js:102 #: ../js/ui/contactDisplay.js:102
msgid "Offline" msgid "Offline"
msgstr "Fora de línia" msgstr "Fora de lnia"
#: ../js/ui/contactDisplay.js:153 #: ../js/ui/contactDisplay.js:153
msgid "CONTACTS" msgid "CONTACTS"
@@ -694,51 +684,51 @@ msgstr "Contrasenya:"
msgid "Type again:" msgid "Type again:"
msgstr "Torneu a escriure-la:" msgstr "Torneu a escriure-la:"
#: ../js/ui/lookingGlass.js:732 #: ../js/ui/lookingGlass.js:725
msgid "No extensions installed" msgid "No extensions installed"
msgstr "No hi ha cap extensió instal·lada" msgstr "No hi ha cap extensió instal·lada"
#. Translators: argument is an extension UUID. #. Translators: argument is an extension UUID.
#: ../js/ui/lookingGlass.js:786 #: ../js/ui/lookingGlass.js:779
#, c-format #, c-format
msgid "%s has not emitted any errors." msgid "%s has not emitted any errors."
msgstr "%s no ha emès cap error." msgstr "%s no ha emès cap error."
#: ../js/ui/lookingGlass.js:792 #: ../js/ui/lookingGlass.js:785
msgid "Hide Errors" msgid "Hide Errors"
msgstr "Amaga els errors" msgstr "Amaga els errors"
#: ../js/ui/lookingGlass.js:796 ../js/ui/lookingGlass.js:847 #: ../js/ui/lookingGlass.js:789 ../js/ui/lookingGlass.js:840
msgid "Show Errors" msgid "Show Errors"
msgstr "Mostra els errors" msgstr "Mostra els errors"
#: ../js/ui/lookingGlass.js:805 #: ../js/ui/lookingGlass.js:798
msgid "Enabled" msgid "Enabled"
msgstr "Habilitat" msgstr "Habilitat"
#. translators: #. translators:
#. * The device has been disabled #. * The device has been disabled
#: ../js/ui/lookingGlass.js:808 ../src/gvc/gvc-mixer-control.c:1093 #: ../js/ui/lookingGlass.js:801 ../src/gvc/gvc-mixer-control.c:1093
msgid "Disabled" msgid "Disabled"
msgstr "Inhabilitat" msgstr "Inhabilitat"
#: ../js/ui/lookingGlass.js:810 #: ../js/ui/lookingGlass.js:803
msgid "Error" msgid "Error"
msgstr "Error" msgstr "Error"
#: ../js/ui/lookingGlass.js:812 #: ../js/ui/lookingGlass.js:805
msgid "Out of date" msgid "Out of date"
msgstr "Fora d'hora" msgstr "Fora d'hora"
#: ../js/ui/lookingGlass.js:814 #: ../js/ui/lookingGlass.js:807
msgid "Downloading" msgid "Downloading"
msgstr "S'està baixant" msgstr "S'està baixant"
#: ../js/ui/lookingGlass.js:835 #: ../js/ui/lookingGlass.js:828
msgid "View Source" msgid "View Source"
msgstr "Mostra el codi font" msgstr "Mostra el codi font"
#: ../js/ui/lookingGlass.js:841 #: ../js/ui/lookingGlass.js:834
msgid "Web Page" msgid "Web Page"
msgstr "Pàgina web" msgstr "Pàgina web"
@@ -806,15 +796,15 @@ msgstr "La xarxa sense fil requereix autenticació"
#: ../js/ui/networkAgent.js:330 #: ../js/ui/networkAgent.js:330
#, c-format #, c-format
msgid "" msgid ""
"Passwords or encryption keys are required to access the wireless network '%" "Passwords or encryption keys are required to access the wireless network "
"s'." "'%s'."
msgstr "" msgstr ""
"Per accedir a la xarxa sense fil «%s» calen les contrasenyes o les claus " "Per accedir a la xarxa sense fil «%s» calen les contrasenyes o les claus "
"d'encriptació." "d'encriptació."
#: ../js/ui/networkAgent.js:334 #: ../js/ui/networkAgent.js:334
msgid "Wired 802.1X authentication" msgid "Wired 802.1X authentication"
msgstr "Autenticació 802.1X amb fil" msgstr "Autenticaci 802.1X amb fil"
#: ../js/ui/networkAgent.js:336 #: ../js/ui/networkAgent.js:336
msgid "Network name: " msgid "Network name: "
@@ -822,15 +812,15 @@ msgstr "Nom de la xarxa: "
#: ../js/ui/networkAgent.js:341 #: ../js/ui/networkAgent.js:341
msgid "DSL authentication" msgid "DSL authentication"
msgstr "Autenticació DSL" msgstr "Autenticaci DSL"
#: ../js/ui/networkAgent.js:348 #: ../js/ui/networkAgent.js:348
msgid "PIN code required" msgid "PIN code required"
msgstr "Cal que introduïu el codi PIN" msgstr "Cal que introduu el codi PIN"
#: ../js/ui/networkAgent.js:349 #: ../js/ui/networkAgent.js:349
msgid "PIN code is needed for the mobile broadband device" msgid "PIN code is needed for the mobile broadband device"
msgstr "Cal que introduïu el codi PIN del dispositiu de banda ampla mòbil" msgstr "Cal que introduu el codi PIN del dispositiu de banda ampla mbil"
#: ../js/ui/networkAgent.js:350 #: ../js/ui/networkAgent.js:350
msgid "PIN: " msgid "PIN: "
@@ -838,12 +828,12 @@ msgstr "PIN: "
#: ../js/ui/networkAgent.js:356 #: ../js/ui/networkAgent.js:356
msgid "Mobile broadband network password" msgid "Mobile broadband network password"
msgstr "Contrasenya de la xarxa de banda ampla mòbil" msgstr "Contrasenya de la xarxa de banda ampla mbil"
#: ../js/ui/networkAgent.js:357 #: ../js/ui/networkAgent.js:357
#, c-format #, c-format
msgid "A password is required to connect to '%s'." msgid "A password is required to connect to '%s'."
msgstr "Cal que introduïu una contrasenya per connectar-vos a «%s»." msgstr "Cal que introduu una contrasenya per connectar-vos a «%s»."
#: ../js/ui/overview.js:90 #: ../js/ui/overview.js:90
msgid "Undo" msgid "Undo"
@@ -867,17 +857,17 @@ msgstr "Aplicacions"
msgid "Dash" msgid "Dash"
msgstr "Quadre d'aplicacions" msgstr "Quadre d'aplicacions"
#: ../js/ui/panel.js:592 #: ../js/ui/panel.js:591
msgid "Quit" msgid "Quit"
msgstr "Surt" msgstr "Surt"
#. Translators: If there is no suitable word for "Activities" #. Translators: If there is no suitable word for "Activities"
#. in your language, you can use the word for "Overview". #. in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:624 #: ../js/ui/panel.js:623
msgid "Activities" msgid "Activities"
msgstr "Activitats" msgstr "Activitats"
#: ../js/ui/panel.js:999 #: ../js/ui/panel.js:998
msgid "Top Bar" msgid "Top Bar"
msgstr "Barra superior" msgstr "Barra superior"
@@ -931,11 +921,11 @@ msgstr "toggle-switch-intl"
msgid "Please enter a command:" msgid "Please enter a command:"
msgstr "Introduïu una ordre:" msgstr "Introduïu una ordre:"
#: ../js/ui/searchDisplay.js:332 #: ../js/ui/searchDisplay.js:331
msgid "Searching..." msgid "Searching..."
msgstr "S'està cercant..." msgstr "S'està cercant..."
#: ../js/ui/searchDisplay.js:414 #: ../js/ui/searchDisplay.js:413
msgid "No matching results." msgid "No matching results."
msgstr "No s'ha trobat cap coincidència." msgstr "No s'ha trobat cap coincidència."
@@ -1078,7 +1068,7 @@ msgstr "Paràmetres de so"
#: ../js/ui/status/bluetooth.js:372 #: ../js/ui/status/bluetooth.js:372
#, c-format #, c-format
msgid "Authorization request from %s" msgid "Authorization request from %s"
msgstr "Hi ha una sol·licitud d'autorització des de %s" msgstr "Hi ha una petició d'autorització des de %s"
#: ../js/ui/status/bluetooth.js:378 #: ../js/ui/status/bluetooth.js:378
#, c-format #, c-format
@@ -1248,7 +1238,7 @@ msgstr "Paràmetres de xarxa"
#: ../js/ui/status/network.js:1739 #: ../js/ui/status/network.js:1739
msgid "Connection failed" msgid "Connection failed"
msgstr "Ha fallat la connexió" msgstr "Ha fallat la connexi"
#: ../js/ui/status/network.js:1740 #: ../js/ui/status/network.js:1740
msgid "Activation of network connection failed" msgid "Activation of network connection failed"
@@ -1377,11 +1367,11 @@ msgstr "Trucada"
#. We got the TpContact #. We got the TpContact
#: ../js/ui/telepathyClient.js:287 #: ../js/ui/telepathyClient.js:287
msgid "File Transfer" msgid "File Transfer"
msgstr "Transferència de fitxers" msgstr "Transferncia de fitxers"
#: ../js/ui/telepathyClient.js:369 #: ../js/ui/telepathyClient.js:369
msgid "Subscription request" msgid "Subscription request"
msgstr "Teniu una sol·licitud de subscripció" msgstr "Teniu una petició de subscripció"
#: ../js/ui/telepathyClient.js:405 #: ../js/ui/telepathyClient.js:405
msgid "Connection error" msgid "Connection error"
@@ -1492,7 +1482,7 @@ msgstr "En/na %s us envia %s"
#: ../js/ui/telepathyClient.js:1194 #: ../js/ui/telepathyClient.js:1194
#, c-format #, c-format
msgid "%s would like permission to see when you are online" msgid "%s would like permission to see when you are online"
msgstr "%s vol poder saber quan esteu en línia" msgstr "%s vol poder saber quan esteu en lnia"
#: ../js/ui/telepathyClient.js:1287 #: ../js/ui/telepathyClient.js:1287
msgid "Network error" msgid "Network error"
@@ -1500,7 +1490,7 @@ msgstr "Error de la xarxa"
#: ../js/ui/telepathyClient.js:1289 #: ../js/ui/telepathyClient.js:1289
msgid "Authentication failed" msgid "Authentication failed"
msgstr "Ha fallat l'autenticació" msgstr "Ha fallat l'autenticaci"
#: ../js/ui/telepathyClient.js:1291 #: ../js/ui/telepathyClient.js:1291
msgid "Encryption error" msgid "Encryption error"
@@ -1512,19 +1502,19 @@ msgstr "No s'ha proporcionat el certificat"
#: ../js/ui/telepathyClient.js:1295 #: ../js/ui/telepathyClient.js:1295
msgid "Certificate untrusted" msgid "Certificate untrusted"
msgstr "El certificat no és de confiança" msgstr "El certificat no s de confiana"
#: ../js/ui/telepathyClient.js:1297 #: ../js/ui/telepathyClient.js:1297
msgid "Certificate expired" msgid "Certificate expired"
msgstr "El certificat ha vençut" msgstr "El certificat ha venut"
#: ../js/ui/telepathyClient.js:1299 #: ../js/ui/telepathyClient.js:1299
msgid "Certificate not activated" msgid "Certificate not activated"
msgstr "El certificat no està activat" msgstr "El certificat no est activat"
#: ../js/ui/telepathyClient.js:1301 #: ../js/ui/telepathyClient.js:1301
msgid "Certificate hostname mismatch" msgid "Certificate hostname mismatch"
msgstr "No coincideix el nom de la màquina del certificat" msgstr "No coincideix el nom de la mquina del certificat"
#: ../js/ui/telepathyClient.js:1303 #: ../js/ui/telepathyClient.js:1303
msgid "Certificate fingerprint mismatch" msgid "Certificate fingerprint mismatch"
@@ -1540,33 +1530,33 @@ msgstr "S'ha establert l'estat a fora de línia"
#: ../js/ui/telepathyClient.js:1309 #: ../js/ui/telepathyClient.js:1309
msgid "Encryption is not available" msgid "Encryption is not available"
msgstr "L'encriptació no està disponible" msgstr "L'encriptaci no est disponible"
#: ../js/ui/telepathyClient.js:1311 #: ../js/ui/telepathyClient.js:1311
msgid "Certificate is invalid" msgid "Certificate is invalid"
msgstr "El certificat no és vàlid" msgstr "El certificat no s vlid"
#: ../js/ui/telepathyClient.js:1313 #: ../js/ui/telepathyClient.js:1313
msgid "Connection has been refused" msgid "Connection has been refused"
msgstr "S'ha rebutjat la connexió" msgstr "S'ha rebutjat la connexi"
#: ../js/ui/telepathyClient.js:1315 #: ../js/ui/telepathyClient.js:1315
msgid "Connection can't be established" msgid "Connection can't be established"
msgstr "No es pot establir la connexió" msgstr "No es pot establir la connexi"
#: ../js/ui/telepathyClient.js:1317 #: ../js/ui/telepathyClient.js:1317
msgid "Connection has been lost" msgid "Connection has been lost"
msgstr "S'ha perdut la connexió" msgstr "S'ha perdut la connexi"
#: ../js/ui/telepathyClient.js:1319 #: ../js/ui/telepathyClient.js:1319
msgid "This account is already connected to the server" msgid "This account is already connected to the server"
msgstr "Aquest compte ja està connectat al servidor" msgstr "Aquest compte ja est connectat al servidor"
#: ../js/ui/telepathyClient.js:1321 #: ../js/ui/telepathyClient.js:1321
msgid "" msgid ""
"Connection has been replaced by a new connection using the same resource" "Connection has been replaced by a new connection using the same resource"
msgstr "" msgstr ""
"S'ha reemplaçat la connexió per una altra de nova fent servir el mateix " "S'ha reemplaat la connexi per una altra de nova fent servir el mateix "
"recurs" "recurs"
#: ../js/ui/telepathyClient.js:1323 #: ../js/ui/telepathyClient.js:1323
@@ -1575,7 +1565,7 @@ msgstr "Ja existeix aquest compte al servidor"
#: ../js/ui/telepathyClient.js:1325 #: ../js/ui/telepathyClient.js:1325
msgid "Server is currently too busy to handle the connection" msgid "Server is currently too busy to handle the connection"
msgstr "El servidor està massa ocupat per gestionar la connexió" msgstr "El servidor est massa ocupat per gestionar la connexi"
#: ../js/ui/telepathyClient.js:1327 #: ../js/ui/telepathyClient.js:1327
msgid "Certificate has been revoked" msgid "Certificate has been revoked"
@@ -1585,8 +1575,8 @@ msgstr "S'ha revocat el certificat"
msgid "" msgid ""
"Certificate uses an insecure cipher algorithm or is cryptographically weak" "Certificate uses an insecure cipher algorithm or is cryptographically weak"
msgstr "" msgstr ""
"El certificat utilitza un algorisme criptògraf no segur o la seva fortalesa " "El certificat utilitza un algorisme criptgraf no segur o la seva fortalesa "
"criptogràfica és feble" "criptogrfica s feble"
#: ../js/ui/telepathyClient.js:1331 #: ../js/ui/telepathyClient.js:1331
msgid "" msgid ""
@@ -1594,7 +1584,7 @@ msgid ""
"chain, exceed the limits imposed by the cryptography library" "chain, exceed the limits imposed by the cryptography library"
msgstr "" msgstr ""
"La llargada del certificat del servidor o la profunditat de la cadena de " "La llargada del certificat del servidor o la profunditat de la cadena de "
"certificació excedeix els límits de la biblioteca criptogràfica" "certificaci excedeix els lmits de la biblioteca criptogrfica"
#: ../js/ui/telepathyClient.js:1333 #: ../js/ui/telepathyClient.js:1333
msgid "Internal error" msgid "Internal error"

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +1,27 @@
# Czech translation of gnome-shell. # Czech translation of gnome-shell.
# Copyright (C) 2009, 2010, 2011 the author(s) of gnome-shell. # Copyright (C) 2009, 2010, 2011 the author(s) of gnome-shell.
# This file is distributed under the same license as the gnome-shell package. # This file is distributed under the same license as the gnome-shell package.
#
# Andre Klapper <ak-47@gmx.net>, 2009. # Andre Klapper <ak-47@gmx.net>, 2009.
# Petr Kovar <pknbe@volny.cz>, 2009, 2010, 2011, 2012. # Petr Kovar <pknbe@volny.cz>, 2009, 2010, 2011.
# Adam Matoušek <adydas95@gmail.com>, 2012 # Adam Matoušek <adydas95@gmail.com>, 2012
# Marek Černocký <marek@manet.cz>, 2012. # Marek Černocký <marek@manet.cz>, 2012.
#
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: gnome-shell\n" "Project-Id-Version: gnome-shell\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&keywords=I18N+L10N&component=general\n" "shell&keywords=I18N+L10N&component=general\n"
"POT-Creation-Date: 2012-03-30 17:59+0000\n" "POT-Creation-Date: 2012-03-19 14:09+0000\n"
"PO-Revision-Date: 2012-04-16 15:34+0200\n" "PO-Revision-Date: 2012-03-22 11:28+0100\n"
"Last-Translator: Petr Kovar <pknbe@volny.cz>\n" "Last-Translator: Marek Černocký <marek@manet.cz>\n"
"Language-Team: Czech <gnome-cs-list@gnome.org>\n" "Language-Team: Czech <gnome-cs-list@gnome.org>\n"
"Language: cs\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: cs\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
"X-Generator: Virtaal 0.7.1\n" "X-Generator: Lokalize 1.2\n"
"X-Project-Style: gnome\n"
#: ../data/gnome-shell.desktop.in.in.h:1 #: ../data/gnome-shell.desktop.in.in.h:1
msgid "GNOME Shell" msgid "GNOME Shell"
@@ -133,42 +134,34 @@ msgid "If true, display the ISO week date in the calendar."
msgstr "Je-li zapnuto, zobrazovat v kalendáři čísla týdnů dle ISO." msgstr "Je-li zapnuto, zobrazovat v kalendáři čísla týdnů dle ISO."
#: ../data/org.gnome.shell.gschema.xml.in.h:16 #: ../data/org.gnome.shell.gschema.xml.in.h:16
msgid "Keybinding to open the application menu"
msgstr "Klávesová zkratka na otevření nabídky aplikace"
#: ../data/org.gnome.shell.gschema.xml.in.h:17
msgid "Keybinding to open the application menu."
msgstr "Klávesová zkratka na otevření nabídky aplikace."
#: ../data/org.gnome.shell.gschema.xml.in.h:18
msgid "Which keyboard to use" msgid "Which keyboard to use"
msgstr "Která klávesnice se má používat" msgstr "Která klávesnice se má používat"
#: ../data/org.gnome.shell.gschema.xml.in.h:19 #: ../data/org.gnome.shell.gschema.xml.in.h:17
msgid "The type of keyboard to use." msgid "The type of keyboard to use."
msgstr "Typ klávesnice, který se má používat." msgstr "Typ klávesnice, který se má používat."
#: ../data/org.gnome.shell.gschema.xml.in.h:20 #: ../data/org.gnome.shell.gschema.xml.in.h:18
msgid "Show time with seconds" msgid "Show time with seconds"
msgstr "Zobrazovat čas včetně sekund" msgstr "Zobrazovat čas včetně sekund"
#: ../data/org.gnome.shell.gschema.xml.in.h:21 #: ../data/org.gnome.shell.gschema.xml.in.h:19
msgid "If true, display seconds in time." msgid "If true, display seconds in time."
msgstr "Je-li zapnuto, zobrazovat čas včetně sekund." msgstr "Je-li zapnuto, zobrazovat čas včetně sekund."
#: ../data/org.gnome.shell.gschema.xml.in.h:22 #: ../data/org.gnome.shell.gschema.xml.in.h:20
msgid "Show date in clock" msgid "Show date in clock"
msgstr "Zobrazovat v hodinách datum" msgstr "Zobrazovat v hodinách datum"
#: ../data/org.gnome.shell.gschema.xml.in.h:23 #: ../data/org.gnome.shell.gschema.xml.in.h:21
msgid "If true, display date in the clock, in addition to time." msgid "If true, display date in the clock, in addition to time."
msgstr "Je-li zapnuto, zobrazovat v hodinách kromě času i datum." msgstr "Je-li zapnuto, zobrazovat v hodinách kromě času i datum."
#: ../data/org.gnome.shell.gschema.xml.in.h:24 #: ../data/org.gnome.shell.gschema.xml.in.h:22
msgid "Framerate used for recording screencasts." msgid "Framerate used for recording screencasts."
msgstr "Frekvence snímků při nahrávání dění na obrazovce." msgstr "Frekvence snímků při nahrávání dění na obrazovce."
#: ../data/org.gnome.shell.gschema.xml.in.h:25 #: ../data/org.gnome.shell.gschema.xml.in.h:23
msgid "" msgid ""
"The framerate of the resulting screencast recordered by GNOME Shell's " "The framerate of the resulting screencast recordered by GNOME Shell's "
"screencast recorder in frames-per-second." "screencast recorder in frames-per-second."
@@ -176,11 +169,11 @@ msgstr ""
"Frekvence snímků za sekundu výsledné nahrávky dění na obrazovce, která byla " "Frekvence snímků za sekundu výsledné nahrávky dění na obrazovce, která byla "
"nahrána záznamovým programem GNOME Shell." "nahrána záznamovým programem GNOME Shell."
#: ../data/org.gnome.shell.gschema.xml.in.h:26 #: ../data/org.gnome.shell.gschema.xml.in.h:24
msgid "The gstreamer pipeline used to encode the screencast" msgid "The gstreamer pipeline used to encode the screencast"
msgstr "Roura systému gstreamer určená ke kódování nahrávky dění na obrazovce" msgstr "Roura systému gstreamer určená ke kódování nahrávky dění na obrazovce"
#: ../data/org.gnome.shell.gschema.xml.in.h:28 #: ../data/org.gnome.shell.gschema.xml.in.h:26
#, no-c-format #, no-c-format
msgid "" msgid ""
"Sets the GStreamer pipeline used to encode recordings. It follows the syntax " "Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
@@ -204,11 +197,11 @@ msgstr ""
"%T ! queue ! webmmux“ s nahráváním do WEBM s kodekem VP8. %T je použito jako " "%T ! queue ! webmmux“ s nahráváním do WEBM s kodekem VP8. %T je použito jako "
"zástupný symbol odhadu nejvhodnějšího počtu vláken na systému." "zástupný symbol odhadu nejvhodnějšího počtu vláken na systému."
#: ../data/org.gnome.shell.gschema.xml.in.h:29 #: ../data/org.gnome.shell.gschema.xml.in.h:27
msgid "File extension used for storing the screencast" msgid "File extension used for storing the screencast"
msgstr "Přípona souboru s nahrávkou dění na obrazovce" msgstr "Přípona souboru s nahrávkou dění na obrazovce"
#: ../data/org.gnome.shell.gschema.xml.in.h:30 #: ../data/org.gnome.shell.gschema.xml.in.h:28
msgid "" msgid ""
"The filename for recorded screencasts will be a unique filename based on the " "The filename for recorded screencasts will be a unique filename based on the "
"current date, and use this extension. It should be changed when recording to " "current date, and use this extension. It should be changed when recording to "
@@ -704,51 +697,51 @@ msgstr "Heslo:"
msgid "Type again:" msgid "Type again:"
msgstr "Napište znovu:" msgstr "Napište znovu:"
#: ../js/ui/lookingGlass.js:732 #: ../js/ui/lookingGlass.js:725
msgid "No extensions installed" msgid "No extensions installed"
msgstr "Nejsou nainstalována žádná rozšíření" msgstr "Nejsou nainstalována žádná rozšíření"
#. Translators: argument is an extension UUID. #. Translators: argument is an extension UUID.
#: ../js/ui/lookingGlass.js:786 #: ../js/ui/lookingGlass.js:779
#, c-format #, c-format
msgid "%s has not emitted any errors." msgid "%s has not emitted any errors."
msgstr "Rozšíření %s nevyvolalo žádné chyby." msgstr "Rozšíření %s nevyvolalo žádné chyby."
#: ../js/ui/lookingGlass.js:792 #: ../js/ui/lookingGlass.js:785
msgid "Hide Errors" msgid "Hide Errors"
msgstr "Skrývat chyby" msgstr "Skrývat chyby"
#: ../js/ui/lookingGlass.js:796 ../js/ui/lookingGlass.js:847 #: ../js/ui/lookingGlass.js:789 ../js/ui/lookingGlass.js:840
msgid "Show Errors" msgid "Show Errors"
msgstr "Zobrazovat chyby" msgstr "Zobrazovat chyby"
#: ../js/ui/lookingGlass.js:805 #: ../js/ui/lookingGlass.js:798
msgid "Enabled" msgid "Enabled"
msgstr "Povoleno" msgstr "Povoleno"
#. translators: #. translators:
#. * The device has been disabled #. * The device has been disabled
#: ../js/ui/lookingGlass.js:808 ../src/gvc/gvc-mixer-control.c:1093 #: ../js/ui/lookingGlass.js:801 ../src/gvc/gvc-mixer-control.c:1093
msgid "Disabled" msgid "Disabled"
msgstr "Zakázáno" msgstr "Zakázáno"
#: ../js/ui/lookingGlass.js:810 #: ../js/ui/lookingGlass.js:803
msgid "Error" msgid "Error"
msgstr "Chyba" msgstr "Chyba"
#: ../js/ui/lookingGlass.js:812 #: ../js/ui/lookingGlass.js:805
msgid "Out of date" msgid "Out of date"
msgstr "Neaktuální" msgstr "Neaktuální"
#: ../js/ui/lookingGlass.js:814 #: ../js/ui/lookingGlass.js:807
msgid "Downloading" msgid "Downloading"
msgstr "Stahování" msgstr "Stahování"
#: ../js/ui/lookingGlass.js:835 #: ../js/ui/lookingGlass.js:828
msgid "View Source" msgid "View Source"
msgstr "Zobrazit zdroj" msgstr "Zobrazit zdroj"
#: ../js/ui/lookingGlass.js:841 #: ../js/ui/lookingGlass.js:834
msgid "Web Page" msgid "Web Page"
msgstr "Webová stránka" msgstr "Webová stránka"
@@ -816,8 +809,8 @@ msgstr "K bezdrátové síti je vyžadováno ověření"
#: ../js/ui/networkAgent.js:330 #: ../js/ui/networkAgent.js:330
#, c-format #, c-format
msgid "" msgid ""
"Passwords or encryption keys are required to access the wireless network '%" "Passwords or encryption keys are required to access the wireless network "
"s'." "'%s'."
msgstr "" msgstr ""
"Pro přístup k bezdrátové síti „%s“ jsou vyžadována hesla nebo šifrovací " "Pro přístup k bezdrátové síti „%s“ jsou vyžadována hesla nebo šifrovací "
"klíče." "klíče."
@@ -877,17 +870,17 @@ msgstr "Aplikace"
msgid "Dash" msgid "Dash"
msgstr "Oblíbené" msgstr "Oblíbené"
#: ../js/ui/panel.js:592 #: ../js/ui/panel.js:591
msgid "Quit" msgid "Quit"
msgstr "Ukončit" msgstr "Ukončit"
#. Translators: If there is no suitable word for "Activities" #. Translators: If there is no suitable word for "Activities"
#. in your language, you can use the word for "Overview". #. in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:624 #: ../js/ui/panel.js:623
msgid "Activities" msgid "Activities"
msgstr "Činnosti" msgstr "Činnosti"
#: ../js/ui/panel.js:999 #: ../js/ui/panel.js:998
msgid "Top Bar" msgid "Top Bar"
msgstr "Horní lišta" msgstr "Horní lišta"
@@ -941,11 +934,11 @@ msgstr "toggle-switch-intl"
msgid "Please enter a command:" msgid "Please enter a command:"
msgstr "Zadejte prosím příkaz:" msgstr "Zadejte prosím příkaz:"
#: ../js/ui/searchDisplay.js:332 #: ../js/ui/searchDisplay.js:331
msgid "Searching..." msgid "Searching..."
msgstr "Hledá se…" msgstr "Hledá se…"
#: ../js/ui/searchDisplay.js:414 #: ../js/ui/searchDisplay.js:413
msgid "No matching results." msgid "No matching results."
msgstr "Neodpovídá ani jeden z výsledků." msgstr "Neodpovídá ani jeden z výsledků."

116
po/de.po
View File

@@ -11,16 +11,15 @@
# Jakob Kramer <jakob.kramer@gmx.de>, 2010. # Jakob Kramer <jakob.kramer@gmx.de>, 2010.
# Paul Seyfert <pseyfert@mathphys.fsk.uni-heidelberg.de>, 2010, 2011. # Paul Seyfert <pseyfert@mathphys.fsk.uni-heidelberg.de>, 2010, 2011.
# Christian Kirbach <Christian.Kirbach@googlemail.com>, 2009, 2010, 2011, 2012. # Christian Kirbach <Christian.Kirbach@googlemail.com>, 2009, 2010, 2011, 2012.
# Wolfgang Stöggl <c72578@yahoo.de>, 2012.
# #
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: gnome-shell master\n" "Project-Id-Version: gnome-shell master\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&keywords=I18N+L10N&component=general\n" "shell&keywords=I18N+L10N&component=general\n"
"POT-Creation-Date: 2012-04-13 19:40+0000\n" "POT-Creation-Date: 2012-03-18 00:40+0000\n"
"PO-Revision-Date: 2012-04-14 16:04+0200\n" "PO-Revision-Date: 2012-03-18 01:04+0100\n"
"Last-Translator: Wolfgang Stoeggl <c72578@yahoo.de>\n" "Last-Translator: Christian Kirbach <Christian.Kirbach@googlemail.com>\n"
"Language-Team: Deutsch <gnome-de@gnome.org>\n" "Language-Team: Deutsch <gnome-de@gnome.org>\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@@ -143,42 +142,34 @@ msgstr ""
"Wenn dieser Wert gesetzt ist, wird der ISO-Wochentag im Kalender angezeigt." "Wenn dieser Wert gesetzt ist, wird der ISO-Wochentag im Kalender angezeigt."
#: ../data/org.gnome.shell.gschema.xml.in.h:16 #: ../data/org.gnome.shell.gschema.xml.in.h:16
msgid "Keybinding to open the application menu"
msgstr "Tastenkombination zum Öffnen des Anwendungsmenüs"
#: ../data/org.gnome.shell.gschema.xml.in.h:17
msgid "Keybinding to open the application menu."
msgstr "Tastenkombination zum Öffnen des Anwendungsmenüs."
#: ../data/org.gnome.shell.gschema.xml.in.h:18
msgid "Which keyboard to use" msgid "Which keyboard to use"
msgstr "Zu verwendende Tastatur" msgstr "Zu verwendende Tastatur"
#: ../data/org.gnome.shell.gschema.xml.in.h:19 #: ../data/org.gnome.shell.gschema.xml.in.h:17
msgid "The type of keyboard to use." msgid "The type of keyboard to use."
msgstr "Der Typ der zu verwendenden Tastatur" msgstr "Der Typ der zu verwendenden Tastatur"
#: ../data/org.gnome.shell.gschema.xml.in.h:20 #: ../data/org.gnome.shell.gschema.xml.in.h:18
msgid "Show time with seconds" msgid "Show time with seconds"
msgstr "Zeit sekundengenau anzeigen" msgstr "Zeit sekundengenau anzeigen"
#: ../data/org.gnome.shell.gschema.xml.in.h:21 #: ../data/org.gnome.shell.gschema.xml.in.h:19
msgid "If true, display seconds in time." msgid "If true, display seconds in time."
msgstr "Legt fest, ob in der Uhrzeit Sekunden angezeigt werden." msgstr "Legt fest, ob in der Uhrzeit Sekunden angezeigt werden."
#: ../data/org.gnome.shell.gschema.xml.in.h:22 #: ../data/org.gnome.shell.gschema.xml.in.h:20
msgid "Show date in clock" msgid "Show date in clock"
msgstr "Datum in der Uhr anzeigen" msgstr "Datum in der Uhr anzeigen"
#: ../data/org.gnome.shell.gschema.xml.in.h:23 #: ../data/org.gnome.shell.gschema.xml.in.h:21
msgid "If true, display date in the clock, in addition to time." msgid "If true, display date in the clock, in addition to time."
msgstr "Legt fest, ob das Datum zusätzlich zur Uhrzeit angezeigt wird." msgstr "Legt fest, ob das Datum zusätzlich zur Uhrzeit angezeigt wird."
#: ../data/org.gnome.shell.gschema.xml.in.h:24 #: ../data/org.gnome.shell.gschema.xml.in.h:22
msgid "Framerate used for recording screencasts." msgid "Framerate used for recording screencasts."
msgstr "Bildwiederholungsrate zur Aufnahme von Screencasts" msgstr "Bildwiederholungsrate zur Aufnahme von Screencasts"
#: ../data/org.gnome.shell.gschema.xml.in.h:25 #: ../data/org.gnome.shell.gschema.xml.in.h:23
msgid "" msgid ""
"The framerate of the resulting screencast recordered by GNOME Shell's " "The framerate of the resulting screencast recordered by GNOME Shell's "
"screencast recorder in frames-per-second." "screencast recorder in frames-per-second."
@@ -187,12 +178,12 @@ msgstr ""
"der GNOME-Shell aufgezeichnet werden soll, in Einzelbildern pro Sekunde." "der GNOME-Shell aufgezeichnet werden soll, in Einzelbildern pro Sekunde."
# hmm Enkodieren oder Kodieren? # hmm Enkodieren oder Kodieren?
#: ../data/org.gnome.shell.gschema.xml.in.h:26 #: ../data/org.gnome.shell.gschema.xml.in.h:24
msgid "The gstreamer pipeline used to encode the screencast" msgid "The gstreamer pipeline used to encode the screencast"
msgstr "Die GStreamer-Weiterleitung zur Enkodierung des Screencasts" msgstr "Die GStreamer-Weiterleitung zur Enkodierung des Screencasts"
# Hier blicke ich überhaupt nicht durch. # Hier blicke ich überhaupt nicht durch.
#: ../data/org.gnome.shell.gschema.xml.in.h:28 #: ../data/org.gnome.shell.gschema.xml.in.h:26
#, no-c-format #, no-c-format
msgid "" msgid ""
"Sets the GStreamer pipeline used to encode recordings. It follows the syntax " "Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
@@ -219,11 +210,11 @@ msgstr ""
"mittels des VP8-Codecs aufzeichnet. %T wird als Platzhalter für die " "mittels des VP8-Codecs aufzeichnet. %T wird als Platzhalter für die "
"vermutete optimale Thread-Anzahl auf dem System verwendet." "vermutete optimale Thread-Anzahl auf dem System verwendet."
#: ../data/org.gnome.shell.gschema.xml.in.h:29 #: ../data/org.gnome.shell.gschema.xml.in.h:27
msgid "File extension used for storing the screencast" msgid "File extension used for storing the screencast"
msgstr "Die Dateiendung zum Speichern des Screencast" msgstr "Die Dateiendung zum Speichern des Screencast"
#: ../data/org.gnome.shell.gschema.xml.in.h:30 #: ../data/org.gnome.shell.gschema.xml.in.h:28
msgid "" msgid ""
"The filename for recorded screencasts will be a unique filename based on the " "The filename for recorded screencasts will be a unique filename based on the "
"current date, and use this extension. It should be changed when recording to " "current date, and use this extension. It should be changed when recording to "
@@ -247,53 +238,53 @@ msgstr "<b>Erweiterung</b>"
msgid "Select an extension to configure using the combobox above." msgid "Select an extension to configure using the combobox above."
msgstr "Wählen Sie oben eine Erweiterung aus, die Sie konfigurieren wollen." msgstr "Wählen Sie oben eine Erweiterung aus, die Sie konfigurieren wollen."
#: ../js/gdm/loginDialog.js:627 #: ../js/gdm/loginDialog.js:624
msgid "Session..." msgid "Session..."
msgstr "Sitzung …" msgstr "Sitzung …"
#: ../js/gdm/loginDialog.js:789 #: ../js/gdm/loginDialog.js:786
msgctxt "title" msgctxt "title"
msgid "Sign In" msgid "Sign In"
msgstr "Anmelden" msgstr "Anmelden"
#. Translators: this message is shown below the password entry field #. Translators: this message is shown below the password entry field
#. to indicate the user can swipe their finger instead #. to indicate the user can swipe their finger instead
#: ../js/gdm/loginDialog.js:834 #: ../js/gdm/loginDialog.js:831
msgid "(or swipe finger)" msgid "(or swipe finger)"
msgstr "(oder benutzen Sie den Fingerabdruckleser)" msgstr "(oder benutzen Sie den Fingerabdruckleser)"
#. translators: this message is shown below the user list on the #. translators: this message is shown below the user list on the
#. login screen. It can be activated to reveal an entry for #. login screen. It can be activated to reveal an entry for
#. manually entering the username. #. manually entering the username.
#: ../js/gdm/loginDialog.js:855 #: ../js/gdm/loginDialog.js:852
msgid "Not listed?" msgid "Not listed?"
msgstr "Nicht aufgeführt?" msgstr "Nicht aufgeführt?"
#: ../js/gdm/loginDialog.js:1023 ../js/ui/endSessionDialog.js:401 #: ../js/gdm/loginDialog.js:1020 ../js/ui/endSessionDialog.js:401
#: ../js/ui/extensionSystem.js:400 ../js/ui/networkAgent.js:153 #: ../js/ui/extensionSystem.js:399 ../js/ui/networkAgent.js:153
#: ../js/ui/polkitAuthenticationAgent.js:175 ../js/ui/status/bluetooth.js:462 #: ../js/ui/polkitAuthenticationAgent.js:175 ../js/ui/status/bluetooth.js:462
msgid "Cancel" msgid "Cancel"
msgstr "Abbrechen" msgstr "Abbrechen"
#: ../js/gdm/loginDialog.js:1028 #: ../js/gdm/loginDialog.js:1025
msgctxt "button" msgctxt "button"
msgid "Sign In" msgid "Sign In"
msgstr "Anmelden" msgstr "Anmelden"
#: ../js/gdm/loginDialog.js:1380 #: ../js/gdm/loginDialog.js:1377
msgid "Login Window" msgid "Login Window"
msgstr "Anmeldefenster" msgstr "Anmeldefenster"
#: ../js/gdm/powerMenu.js:155 ../js/ui/userMenu.js:597 #: ../js/gdm/powerMenu.js:152 ../js/ui/userMenu.js:597
#: ../js/ui/userMenu.js:599 ../js/ui/userMenu.js:668 #: ../js/ui/userMenu.js:599 ../js/ui/userMenu.js:668
msgid "Suspend" msgid "Suspend"
msgstr "Bereitschaft" msgstr "Bereitschaft"
#: ../js/gdm/powerMenu.js:160 #: ../js/gdm/powerMenu.js:157
msgid "Restart" msgid "Restart"
msgstr "Neu starten" msgstr "Neu starten"
#: ../js/gdm/powerMenu.js:165 #: ../js/gdm/powerMenu.js:162
msgid "Power Off" msgid "Power Off"
msgstr "Ausschalten" msgstr "Ausschalten"
@@ -688,11 +679,11 @@ msgstr[1] "Das System wird automatisch in %d Sekunden neu gestartet."
msgid "Restarting the system." msgid "Restarting the system."
msgstr "Neustart des Systems." msgstr "Neustart des Systems."
#: ../js/ui/extensionSystem.js:404 #: ../js/ui/extensionSystem.js:403
msgid "Install" msgid "Install"
msgstr "Installieren" msgstr "Installieren"
#: ../js/ui/extensionSystem.js:408 #: ../js/ui/extensionSystem.js:407
#, c-format #, c-format
msgid "Download and install '%s' from extensions.gnome.org?" msgid "Download and install '%s' from extensions.gnome.org?"
msgstr "»%s« von extensions.gnome.org herunterladen und installieren?" msgstr "»%s« von extensions.gnome.org herunterladen und installieren?"
@@ -701,8 +692,7 @@ msgstr "»%s« von extensions.gnome.org herunterladen und installieren?"
msgid "tray" msgid "tray"
msgstr "Benachrichtigungsfeld" msgstr "Benachrichtigungsfeld"
#: ../js/ui/keyboard.js:544 ../js/ui/status/keyboard.js:44 #: ../js/ui/keyboard.js:544 ../js/ui/status/power.js:203
#: ../js/ui/status/power.js:203
msgid "Keyboard" msgid "Keyboard"
msgstr "Tastatur" msgstr "Tastatur"
@@ -714,51 +704,51 @@ msgstr "Passwort:"
msgid "Type again:" msgid "Type again:"
msgstr "Erneut eingeben:" msgstr "Erneut eingeben:"
#: ../js/ui/lookingGlass.js:732 #: ../js/ui/lookingGlass.js:725
msgid "No extensions installed" msgid "No extensions installed"
msgstr "Keine Erweiterungen installiert" msgstr "Keine Erweiterungen installiert"
#. Translators: argument is an extension UUID. #. Translators: argument is an extension UUID.
#: ../js/ui/lookingGlass.js:786 #: ../js/ui/lookingGlass.js:779
#, c-format #, c-format
msgid "%s has not emitted any errors." msgid "%s has not emitted any errors."
msgstr "%s hat keine Fehler ausgegeben." msgstr "%s hat keine Fehler ausgegeben."
#: ../js/ui/lookingGlass.js:792 #: ../js/ui/lookingGlass.js:785
msgid "Hide Errors" msgid "Hide Errors"
msgstr "Fehler verbergen" msgstr "Fehler verbergen"
#: ../js/ui/lookingGlass.js:796 ../js/ui/lookingGlass.js:847 #: ../js/ui/lookingGlass.js:789 ../js/ui/lookingGlass.js:840
msgid "Show Errors" msgid "Show Errors"
msgstr "Fehler anzeigen" msgstr "Fehler anzeigen"
#: ../js/ui/lookingGlass.js:805 #: ../js/ui/lookingGlass.js:798
msgid "Enabled" msgid "Enabled"
msgstr "Aktiviert" msgstr "Aktiviert"
#. translators: #. translators:
#. * The device has been disabled #. * The device has been disabled
#: ../js/ui/lookingGlass.js:808 ../src/gvc/gvc-mixer-control.c:1082 #: ../js/ui/lookingGlass.js:801 ../src/gvc/gvc-mixer-control.c:1093
msgid "Disabled" msgid "Disabled"
msgstr "Deaktiviert" msgstr "Deaktiviert"
#: ../js/ui/lookingGlass.js:810 #: ../js/ui/lookingGlass.js:803
msgid "Error" msgid "Error"
msgstr "Fehler" msgstr "Fehler"
#: ../js/ui/lookingGlass.js:812 #: ../js/ui/lookingGlass.js:805
msgid "Out of date" msgid "Out of date"
msgstr "Veraltet" msgstr "Veraltet"
#: ../js/ui/lookingGlass.js:814 #: ../js/ui/lookingGlass.js:807
msgid "Downloading" msgid "Downloading"
msgstr "Herunterladen" msgstr "Herunterladen"
#: ../js/ui/lookingGlass.js:835 #: ../js/ui/lookingGlass.js:828
msgid "View Source" msgid "View Source"
msgstr "Quelle zeigen" msgstr "Quelle zeigen"
#: ../js/ui/lookingGlass.js:841 #: ../js/ui/lookingGlass.js:834
msgid "Web Page" msgid "Web Page"
msgstr "Webseite" msgstr "Webseite"
@@ -869,36 +859,32 @@ msgstr "Es wird ein Passwort benötigt, um sich mit »%s« zu verbinden."
msgid "Undo" msgid "Undo"
msgstr "Rückgängig" msgstr "Rückgängig"
#: ../js/ui/overview.js:132 #: ../js/ui/overview.js:199
msgid "Overview"
msgstr "Übersicht"
#: ../js/ui/overview.js:202
msgid "Windows" msgid "Windows"
msgstr "Fenster" msgstr "Fenster"
#: ../js/ui/overview.js:205 #: ../js/ui/overview.js:202
msgid "Applications" msgid "Applications"
msgstr "Anwendungen" msgstr "Anwendungen"
# Würde ich so übernehmen, oder evtl. »Dock«. # Würde ich so übernehmen, oder evtl. »Dock«.
#. Translators: this is the name of the dock/favorites area on #. Translators: this is the name of the dock/favorites area on
#. the left of the overview #. the left of the overview
#: ../js/ui/overview.js:231 #: ../js/ui/overview.js:228
msgid "Dash" msgid "Dash"
msgstr "Dash" msgstr "Dash"
#: ../js/ui/panel.js:592 #: ../js/ui/panel.js:591
msgid "Quit" msgid "Quit"
msgstr "Beenden" msgstr "Beenden"
#. Translators: If there is no suitable word for "Activities" #. Translators: If there is no suitable word for "Activities"
#. in your language, you can use the word for "Overview". #. in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:624 #: ../js/ui/panel.js:623
msgid "Activities" msgid "Activities"
msgstr "Aktivitäten" msgstr "Aktivitäten"
#: ../js/ui/panel.js:999 #: ../js/ui/panel.js:998
msgid "Top Bar" msgid "Top Bar"
msgstr "Obere Leiste" msgstr "Obere Leiste"
@@ -952,11 +938,11 @@ msgstr "toggle-switch-intl"
msgid "Please enter a command:" msgid "Please enter a command:"
msgstr "Bitte geben Sie einen Befehl ein:" msgstr "Bitte geben Sie einen Befehl ein:"
#: ../js/ui/searchDisplay.js:332 #: ../js/ui/searchDisplay.js:331
msgid "Searching..." msgid "Searching..."
msgstr "Suche läuft …" msgstr "Suche läuft …"
#: ../js/ui/searchDisplay.js:415 #: ../js/ui/searchDisplay.js:413
msgid "No matching results." msgid "No matching results."
msgstr "Keine passenden Ergebnisse." msgstr "Keine passenden Ergebnisse."
@@ -1207,7 +1193,7 @@ msgstr "Verbindung gescheitert"
#: ../js/ui/status/network.js:585 ../js/ui/status/network.js:1505 #: ../js/ui/status/network.js:585 ../js/ui/status/network.js:1505
msgid "More..." msgid "More..."
msgstr "Mehr " msgstr "Mehr ..."
#. TRANSLATORS: this is the indication that a connection for another logged in user is active, #. TRANSLATORS: this is the indication that a connection for another logged in user is active,
#. and we cannot access its settings (including the name) #. and we cannot access its settings (including the name)
@@ -1734,7 +1720,7 @@ msgstr "»%s« ist bereit"
#. translators: #. translators:
#. * The number of sound outputs on a particular device #. * The number of sound outputs on a particular device
#: ../src/gvc/gvc-mixer-control.c:1089 #: ../src/gvc/gvc-mixer-control.c:1100
#, c-format #, c-format
msgid "%u Output" msgid "%u Output"
msgid_plural "%u Outputs" msgid_plural "%u Outputs"
@@ -1743,14 +1729,14 @@ msgstr[1] "%u Ausgänge"
#. translators: #. translators:
#. * The number of sound inputs on a particular device #. * The number of sound inputs on a particular device
#: ../src/gvc/gvc-mixer-control.c:1099 #: ../src/gvc/gvc-mixer-control.c:1110
#, c-format #, c-format
msgid "%u Input" msgid "%u Input"
msgid_plural "%u Inputs" msgid_plural "%u Inputs"
msgstr[0] "%u Eingang" msgstr[0] "%u Eingang"
msgstr[1] "%u Eingänge" msgstr[1] "%u Eingänge"
#: ../src/gvc/gvc-mixer-control.c:1397 #: ../src/gvc/gvc-mixer-control.c:1408
msgid "System Sounds" msgid "System Sounds"
msgstr "Systemklänge" msgstr "Systemklänge"

1224
po/el.po

File diff suppressed because it is too large Load Diff

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