Compare commits
197 Commits
Author | SHA1 | Date | |
---|---|---|---|
ab29ce872a | |||
57beb0ade1 | |||
e3d0b6f90f | |||
e2aa954cb5 | |||
05c285a945 | |||
27b34992c6 | |||
4886238761 | |||
42d46aed90 | |||
a622aba7eb | |||
831099cca5 | |||
b8a54faf94 | |||
80ff6ff797 | |||
2f27b94757 | |||
b2f33e2895 | |||
d1d4142052 | |||
46caf6d673 | |||
2864c360bc | |||
e5dfc6323a | |||
5bc042ba6f | |||
c63fe5ee24 | |||
ee6bc33cea | |||
5c730dc53d | |||
5a3de8d663 | |||
fad88dd517 | |||
39dd24310d | |||
b13809d0c7 | |||
87559414a3 | |||
b5b5759829 | |||
d254e2e1f2 | |||
458b0b22fc | |||
cd30128af8 | |||
d61cdd8cea | |||
0d0e545979 | |||
6c5e96c33a | |||
bae2359b54 | |||
8cbbb456f0 | |||
dfd39461cf | |||
60d8683ae7 | |||
f2cc5cf152 | |||
d4a26fbf4b | |||
025784fd83 | |||
4e89a5edde | |||
b3936ecadf | |||
2c9e6bb589 | |||
73261a4a66 | |||
3d0dd38045 | |||
8bcbf3030f | |||
1bc7edc5d8 | |||
0673720db9 | |||
730a0d4c5a | |||
9147dee0de | |||
12746a1949 | |||
bdd65fe755 | |||
21e2280825 | |||
e9d2a429eb | |||
b67dfb9edf | |||
55308917f9 | |||
1b64b09532 | |||
74dd298891 | |||
5a85fc0e55 | |||
26991988cb | |||
15563444cf | |||
3bcdba6e1d | |||
ef56a78544 | |||
049a561466 | |||
b40b19997a | |||
46505a8314 | |||
78fb102002 | |||
c6e924f788 | |||
f6508b51a2 | |||
b0ae596de8 | |||
1d311e7916 | |||
0c19f71c96 | |||
8d6ab32b9a | |||
6c1a2d531f | |||
c6e9f9742b | |||
d23aaf3cea | |||
017fde91ad | |||
31ffc5a85d | |||
62b65a25d8 | |||
882fe48d80 | |||
90a691ed25 | |||
eadb41b3bb | |||
6829590c8f | |||
29da720e6a | |||
c5932c0f07 | |||
6195386a06 | |||
0080440118 | |||
b5be62cd1b | |||
1bac40fbe3 | |||
11637bae43 | |||
301bacec9f | |||
8943b3b0e9 | |||
f59018f2d7 | |||
235cb9c505 | |||
3f328463a8 | |||
e58c82fc04 | |||
91ca86ffe4 | |||
33d4518e50 | |||
b087191d2b | |||
64baea1693 | |||
b88b743428 | |||
c606cf076d | |||
d205d7e7c2 | |||
efdd3375d0 | |||
abcca3d3bc | |||
f4d13b9801 | |||
7da39031e1 | |||
6cdb1bd60c | |||
7c108e267c | |||
5cf06fe9a7 | |||
41f6956197 | |||
417cbea79c | |||
225c807550 | |||
ff78d2655b | |||
bde15f7c61 | |||
30300f1aeb | |||
d42c3a15d6 | |||
14a65559af | |||
ba1e7bd095 | |||
951705a4b2 | |||
36bf63a5de | |||
66c4881fd2 | |||
c6b169cb33 | |||
0e753ed5a6 | |||
785969feb5 | |||
56036476a4 | |||
936b1b5638 | |||
bf3b94d654 | |||
90b4a0856f | |||
686190ce80 | |||
45495c2474 | |||
9caa88fecb | |||
25948f214e | |||
bea4faacd4 | |||
a7bd9f811b | |||
2b9561fcbb | |||
7b9c9b2f7d | |||
b47b82ed42 | |||
c4c2c11dca | |||
ce38293a0f | |||
6c4e9d23f2 | |||
4aa1fe9ca2 | |||
951fff5aa0 | |||
5ad8080cb9 | |||
e53e3cbb09 | |||
8a029f333f | |||
4debedb275 | |||
8764253861 | |||
bbdce159fa | |||
087d8e602e | |||
50aa15dec9 | |||
26580f8f2c | |||
875b6d131b | |||
77afd6782f | |||
09ab13cf04 | |||
56f312dc03 | |||
f2cbddc196 | |||
c7846e172f | |||
54afb7b25e | |||
f3cb9d0443 | |||
fcee7f2f3a | |||
faff0738eb | |||
69e26c6dee | |||
1acec65c5e | |||
8a6a3968c3 | |||
58e4870cb8 | |||
102099cadd | |||
efe6d06ddd | |||
f0d0e025dd | |||
fc2d0215f5 | |||
02af8eb824 | |||
102f2a91f9 | |||
2c649d5da5 | |||
7fa7d04ed0 | |||
aad9179373 | |||
d5b4e30eb7 | |||
34ba6f2f71 | |||
d845f40b98 | |||
80c16aa8f7 | |||
0cbaeaefed | |||
ab6a7773ce | |||
85e243982b | |||
6eb168bc68 | |||
47b55e29d4 | |||
2c243b678f | |||
a634f25d30 | |||
26df6cf35c | |||
84dde8e9bb | |||
f2c79be11a | |||
10df80b96a | |||
bbb83656bf | |||
ef49670ae4 | |||
e4df00c77d | |||
3d70662716 | |||
8d0638a187 | |||
06143396b7 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -18,6 +18,8 @@ config
|
||||
configure
|
||||
data/gnome-shell.desktop
|
||||
data/gnome-shell.desktop.in
|
||||
data/gnome-shell-extension-prefs.desktop
|
||||
data/gnome-shell-extension-prefs.desktop.in
|
||||
data/gschemas.compiled
|
||||
data/org.gnome.shell.gschema.xml
|
||||
data/org.gnome.shell.gschema.valid
|
||||
@ -62,6 +64,7 @@ src/calendar-server/org.gnome.Shell.CalendarServer.service
|
||||
src/gnome-shell
|
||||
src/gnome-shell-calendar-server
|
||||
src/gnome-shell-extension-tool
|
||||
src/gnome-shell-extension-prefs
|
||||
src/gnome-shell-hotplug-sniffer
|
||||
src/gnome-shell-jhbuild
|
||||
src/gnome-shell-perf-helper
|
||||
|
131
NEWS
131
NEWS
@ -1,3 +1,134 @@
|
||||
3.3.5
|
||||
=====
|
||||
|
||||
* Extension system: [Jasper; #668429]
|
||||
http://blog.mecheye.net/2012/02/more-extension-api-breaks/
|
||||
- Add a 'gnome-shell-extension-prefs' application for displaying extension
|
||||
preferences as provided by the extension in a prefs.js file.
|
||||
- Allow launching gnome-shell-extension-prefs from extensions.gnome.org
|
||||
throuhg the browser plugin.
|
||||
- Add ExtensionUtils.getCurrentExtension() for an extension to get a
|
||||
handle to an extension object, to get local imports or paths.
|
||||
- Add an onshellrestart callback to the browser plugin [Jasper; #668517]
|
||||
* Screenshots:
|
||||
- Move the screenshot "flash" to the shell [Cosimo; #668618]
|
||||
- Move saving screenshots to a thread [Adel Gadllah; #652952]
|
||||
- Correctly screenshot rounded decorations [Jasper; #662486]
|
||||
* Screen recorder:
|
||||
- Change the default pipeline to favor speed over quality [Owen; #669066]
|
||||
- Drop frames to keep from running the user out of memory [Owen; #669066]
|
||||
* Work around a slow implementation of glReadPixels() in the Intel drivers,
|
||||
improving performance for screenshots and the screen recorder.
|
||||
[Owen; #669065]
|
||||
* Use Keywords: field in desktop files when search for applications
|
||||
[Florian; #609702]
|
||||
* Strip debian- when matching desktop file names [Jasper; #665647]
|
||||
* Fix up various problems from CSS background size-addition
|
||||
[Florian, Jasper; #668430, #633462]
|
||||
* UI tweaks and behavior fixes
|
||||
[Florian, Giovanni, Stefano; #643867, #666197, #664622]
|
||||
* Some improvements to exported accessibility information [Alejando; #667376]
|
||||
* Don't show contacts without IM information as offline [Florian; #662685]
|
||||
* Don't change status from hidden to extended_away when going idle
|
||||
[Florian; #642408]
|
||||
* Cleanups [Emmanuele, Jasper; #662152, #669239]
|
||||
* Misc bug fixes [Cosimo, Dan, Florian, Jasper, Rui, Stefano;
|
||||
#633462, #643867, #662213, #662386, #662747, #665000, #665017, #665322,
|
||||
#667371, #667860, #668020, #668517, #668541, #669236]
|
||||
|
||||
Contributors:
|
||||
Emmanuele Bassi, Giovanni Campagna, Cosimo Cecchi, Stefano Facchini,
|
||||
Rui Matos, Florian Müllner, Alejandro Piñeiro, Jasper St. Pierre,
|
||||
Owen Taylor, Dan Winship
|
||||
|
||||
Translations:
|
||||
Daniel Mustieles [es], Timo Jyrinki [fi], Seán de Búrca [ga],
|
||||
Fran Diéguez [gl], Kjartan Maraas [nb], Wouter Bolsterlee [nl],
|
||||
Danishka Navin [si], Yaron Shahrabani [he], Matej Urbančič [sl],
|
||||
Chao-Hsiung Liao [zh_HK, zh_TW]
|
||||
|
||||
3.3.4
|
||||
=====
|
||||
* https://live.gnome.org/EveryDetailMatters
|
||||
- Add "browse" for labels for dash items - once a tooltip is
|
||||
showing, switch to other items without a delay [Seif; #666170]
|
||||
- Always scale down windows in the overview at least to 70% [Vit; #646704]
|
||||
- Fix the new-workspace drop indicator sometimes getting stuck
|
||||
[Stefano; #664201]
|
||||
- Delay rearranging windows in the overview as long as the pointer
|
||||
is over a window [Vit; #645325]
|
||||
* Add a GConf => DConf migration file for overriden Mutter settings
|
||||
[Florian; #667636]
|
||||
* When a VPN connection is active, show that as the network icon
|
||||
[Giovanni; #665115]
|
||||
* Handle the "ExtendedAway" IM status as away, not offline [Guillaume; #667813]
|
||||
* Improve the appearance of the labels in "Applications" [Alex; #642392]
|
||||
* Adjust for GTK+ and Mutter API changes for application menu [Ryan; #668118]
|
||||
* Add section label support to the application menu [Giovanni; #666681]
|
||||
* Fix screenshot methods to work again [Cosimo; #667662]
|
||||
* Fix several crashers related to updating workspace thumbnails [Owen; #667652]
|
||||
* Fix memory management error causing gnome-shell-hotplug-sniffer to crash
|
||||
[Owen; #667378]
|
||||
* Build fixes [Emmanuele, Rico; #667864]
|
||||
* Code cleanups [Adel; #668087]
|
||||
* Misc bug fixes [Colin, Florian, Giovanni, Owen, Xavier; #633028, #658817,
|
||||
#664138, #667881, #668048, #668050]
|
||||
|
||||
Contributors:
|
||||
Emmanuele Bassi, Giovanni Campagna, Cosimo Cecchi, Xavier Claessens,
|
||||
Guillaume Desmottes, Stefano Facchini, Adel Gadllah, Alex Hultman,
|
||||
Ryan Lortie, Seif Lotfy, Florian Müllner, Vit Stanislav, Owen Taylor,
|
||||
Rico Tzschichholz, Colin Walters
|
||||
|
||||
Translations:
|
||||
Ihar Hrachyshka [be], Alexander Shopov [bg], Arash Mousavi [fa],
|
||||
Jiri Grönroos, Timo Jyrinki [fi], Fran Diéguez [gl], Kjartan Maraas [nb],
|
||||
Yuri Myasoedov [ru], Matej Urbančič [sl], Nguyễn Thái Ngọc Duy [vi]
|
||||
|
||||
3.3.3
|
||||
=====
|
||||
* https://live.gnome.org/EveryDetailMatters
|
||||
- Stop flashing the window labels on actions in overview [Zan; #644861]
|
||||
- Improve the look of window captions in the overview [Marc; #664487]
|
||||
- Move dash tooltips beside the icon [Seif, Stefano; #666166]
|
||||
* Support application menus exported from applications via new GLib API
|
||||
and D-Bus protocol. [Giovanni, Colin, Matthias, Cosimo]
|
||||
* For removable device prompts, show "Open with Rhythmbox], rather
|
||||
than "Open with Rhythmbox Music Player' [Florian; #664561]
|
||||
* Switch to activating the message tray only with a hot corner rather
|
||||
than a full row of pixels, allowing mouse events to apps [Rui; #663366]
|
||||
* Fully handle the case where the workspaces-only-on-primary
|
||||
GSetting is set to false [Florian; #652580]
|
||||
* Add support for background-size CSS property to St [Quentin; #633462]
|
||||
* Port to new GJS Lang.Class framework [Giovanni; #664436]
|
||||
* Finish port to GDBus [Giovanni; #664436]
|
||||
* Stop using the deprecated Clutter default stage [Florian, Jasper; #664052]
|
||||
* Fix bugs that kept browser plugin from working in WebKit-based browser
|
||||
[Jasper; #666444]
|
||||
* Fix typo that made uninstalling extensions not work [Jasper]
|
||||
* Fix crash in browser plugin if shell is not run [Jürg]
|
||||
* Reintroduce piscine paschal ovum [Giovanni; #666606]
|
||||
* Network menu bug fixes
|
||||
Giovanni; #664124, #665194, #665680, #666429, #666614]
|
||||
* Misc bug fixes [Florian, Jasper, Jonny, Marina, Ron; #647587, #659272,
|
||||
#664138, #665261, #666020, #666243]
|
||||
* Build fixes [Owen]
|
||||
|
||||
Contributors:
|
||||
Jürg Billeter, Giovanni Campagna, Stefano Candori, Cosimo Cecchi,
|
||||
Matthias Clasen, Zan Dobersek, Quentin Glidic, Jonny Lamb, Ryan Lortie,
|
||||
Seif Lotfy, Rui Matos, Florian Müllner, Bastien Nocera, Jasper St. Pierre,
|
||||
Marc Plano-Lesay, Owen Taylor, Colin Walters, Ron Yorsten,
|
||||
Marina Zhurakhinskaya
|
||||
|
||||
Translations:
|
||||
Petr Kovar [cz], Kris Thomsen [dk], Daniel Mustieles [es],
|
||||
Ville-Pekka Vainio [fi], Yaron Shahrabani [he], Luca Ferretti [it],
|
||||
Hideki Yamane [ja], Žygimantas Beručka [lt], Jovan Naumovski [mk],
|
||||
Kjartan Maraas [nb], "Andreas N" [nn], Lucian Adrian Grijincu [ro],
|
||||
Matej Urbančič [sl], Praveen Illa [te], Muhammet Kara [tr],
|
||||
Daniel Korostil [uk], Aron Xu [zh_CN]
|
||||
|
||||
3.3.2
|
||||
=====
|
||||
* Port D-Bus usage in the shell to GDBus [Giovanni, Marc-Antoine, Florian,
|
||||
|
@ -41,7 +41,7 @@
|
||||
"It can be used only by extensions.gnome.org"
|
||||
#define PLUGIN_MIME_STRING "application/x-gnome-shell-integration::Gnome Shell Integration Dummy Content-Type";
|
||||
|
||||
#define PLUGIN_API_VERSION 1
|
||||
#define PLUGIN_API_VERSION 3
|
||||
|
||||
typedef struct {
|
||||
GDBusProxy *proxy;
|
||||
@ -262,11 +262,13 @@ NPP_Destroy(NPP instance,
|
||||
/* =================== scripting interface =================== */
|
||||
|
||||
typedef struct {
|
||||
NPObject parent;
|
||||
NPP instance;
|
||||
GDBusProxy *proxy;
|
||||
NPObject *listener;
|
||||
gint signal_id;
|
||||
NPObject parent;
|
||||
NPP instance;
|
||||
GDBusProxy *proxy;
|
||||
NPObject *listener;
|
||||
NPObject *restart_listener;
|
||||
gint signal_id;
|
||||
guint watch_name_id;
|
||||
} PluginObject;
|
||||
|
||||
static void
|
||||
@ -284,7 +286,7 @@ on_shell_signal (GDBusProxy *proxy,
|
||||
gint32 status;
|
||||
gchar *error;
|
||||
NPVariant args[3];
|
||||
NPVariant result;
|
||||
NPVariant result = { NPVariantType_Void };
|
||||
|
||||
g_variant_get (parameters, "(sis)", &uuid, &status, &error);
|
||||
STRINGZ_TO_NPVARIANT (uuid, args[0]);
|
||||
@ -300,6 +302,25 @@ on_shell_signal (GDBusProxy *proxy,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_shell_appeared (GDBusConnection *connection,
|
||||
const gchar *name,
|
||||
const gchar *name_owner,
|
||||
gpointer user_data)
|
||||
{
|
||||
PluginObject *obj = (PluginObject*) user_data;
|
||||
|
||||
if (obj->restart_listener)
|
||||
{
|
||||
NPVariant result = { NPVariantType_Void };
|
||||
|
||||
funcs.invokeDefault (obj->instance, obj->restart_listener,
|
||||
NULL, 0, &result);
|
||||
|
||||
funcs.releasevariantvalue (&result);
|
||||
}
|
||||
}
|
||||
|
||||
static NPObject *
|
||||
plugin_object_allocate (NPP instance,
|
||||
NPClass *klass)
|
||||
@ -312,6 +333,14 @@ plugin_object_allocate (NPP instance,
|
||||
obj->signal_id = g_signal_connect (obj->proxy, "g-signal",
|
||||
G_CALLBACK (on_shell_signal), obj);
|
||||
|
||||
obj->watch_name_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
|
||||
"org.gnome.Shell",
|
||||
G_BUS_NAME_WATCHER_FLAGS_NONE,
|
||||
on_shell_appeared,
|
||||
NULL,
|
||||
obj,
|
||||
NULL);
|
||||
|
||||
g_debug ("plugin object created");
|
||||
|
||||
return (NPObject*)obj;
|
||||
@ -328,6 +357,9 @@ plugin_object_deallocate (NPObject *npobj)
|
||||
if (obj->listener)
|
||||
funcs.releaseobject (obj->listener);
|
||||
|
||||
if (obj->watch_name_id)
|
||||
g_bus_unwatch_name (obj->watch_name_id);
|
||||
|
||||
g_debug ("plugin object destroyed");
|
||||
|
||||
g_slice_free (PluginObject, obj);
|
||||
@ -341,7 +373,9 @@ 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,
|
||||
@ -352,7 +386,8 @@ plugin_object_has_method (NPObject *npobj,
|
||||
name == enable_extension_id ||
|
||||
name == install_extension_id ||
|
||||
name == uninstall_extension_id ||
|
||||
name == get_errors_id);
|
||||
name == get_errors_id ||
|
||||
name == launch_extension_prefs_id);
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
@ -455,9 +490,12 @@ plugin_enable_extension (PluginObject *obj,
|
||||
NPString uuid,
|
||||
gboolean enabled)
|
||||
{
|
||||
const gchar *uuid_str = uuid.UTF8Characters;
|
||||
gchar *uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
|
||||
if (!uuid_is_valid (uuid_str))
|
||||
return FALSE;
|
||||
{
|
||||
g_free (uuid_str);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_dbus_proxy_call (obj->proxy,
|
||||
(enabled ? "EnableExtension" : "DisableExtension"),
|
||||
@ -468,6 +506,8 @@ plugin_enable_extension (PluginObject *obj,
|
||||
NULL, /* callback */
|
||||
NULL /* user_data */);
|
||||
|
||||
g_free (uuid_str);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -476,21 +516,32 @@ plugin_install_extension (PluginObject *obj,
|
||||
NPString uuid,
|
||||
NPString version_tag)
|
||||
{
|
||||
const gchar *uuid_str = uuid.UTF8Characters;
|
||||
gchar *uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
|
||||
gchar *version_tag_str;
|
||||
|
||||
if (!uuid_is_valid (uuid_str))
|
||||
return FALSE;
|
||||
{
|
||||
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.UTF8Characters),
|
||||
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;
|
||||
}
|
||||
|
||||
@ -501,11 +552,14 @@ plugin_uninstall_extension (PluginObject *obj,
|
||||
{
|
||||
GError *error = NULL;
|
||||
GVariant *res;
|
||||
const gchar *uuid_str;
|
||||
gchar *uuid_str;
|
||||
|
||||
uuid_str = uuid.UTF8Characters;
|
||||
uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
|
||||
if (!uuid_is_valid (uuid_str))
|
||||
return FALSE;
|
||||
{
|
||||
g_free (uuid_str);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
res = g_dbus_proxy_call_sync (obj->proxy,
|
||||
"UninstallExtension",
|
||||
@ -516,6 +570,8 @@ plugin_uninstall_extension (PluginObject *obj,
|
||||
NULL, /* cancellable */
|
||||
&error);
|
||||
|
||||
g_free (uuid_str);
|
||||
|
||||
if (!res)
|
||||
{
|
||||
g_warning ("Failed to uninstall extension: %s", error->message);
|
||||
@ -533,11 +589,14 @@ plugin_get_info (PluginObject *obj,
|
||||
{
|
||||
GError *error = NULL;
|
||||
GVariant *res;
|
||||
const gchar *uuid_str;
|
||||
gchar *uuid_str;
|
||||
|
||||
uuid_str = uuid.UTF8Characters;
|
||||
uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
|
||||
if (!uuid_is_valid (uuid_str))
|
||||
return FALSE;
|
||||
{
|
||||
g_free (uuid_str);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
res = g_dbus_proxy_call_sync (obj->proxy,
|
||||
"GetExtensionInfo",
|
||||
@ -547,6 +606,8 @@ plugin_get_info (PluginObject *obj,
|
||||
NULL, /* cancellable */
|
||||
&error);
|
||||
|
||||
g_free (uuid_str);
|
||||
|
||||
if (!res)
|
||||
{
|
||||
g_warning ("Failed to retrieve extension metadata: %s", error->message);
|
||||
@ -564,11 +625,14 @@ plugin_get_errors (PluginObject *obj,
|
||||
{
|
||||
GError *error = NULL;
|
||||
GVariant *res;
|
||||
const gchar *uuid_str;
|
||||
gchar *uuid_str;
|
||||
|
||||
uuid_str = uuid.UTF8Characters;
|
||||
uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
|
||||
if (!uuid_is_valid (uuid_str))
|
||||
return FALSE;
|
||||
{
|
||||
g_free (uuid_str);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
res = g_dbus_proxy_call_sync (obj->proxy,
|
||||
"GetExtensionErrors",
|
||||
@ -578,6 +642,8 @@ plugin_get_errors (PluginObject *obj,
|
||||
NULL, /* cancellable */
|
||||
&error);
|
||||
|
||||
g_free (uuid_str);
|
||||
|
||||
if (!res)
|
||||
{
|
||||
g_warning ("Failed to retrieve errors: %s", error->message);
|
||||
@ -588,6 +654,33 @@ plugin_get_errors (PluginObject *obj,
|
||||
return jsonify_variant (res, result);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_launch_extension_prefs (PluginObject *obj,
|
||||
NPString uuid,
|
||||
NPVariant *result)
|
||||
{
|
||||
gchar *uuid_str;
|
||||
|
||||
uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
|
||||
if (!uuid_is_valid (uuid_str))
|
||||
{
|
||||
g_free (uuid_str);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_dbus_proxy_call (obj->proxy,
|
||||
"LaunchExtensionPrefs",
|
||||
g_variant_new ("(s)", uuid_str),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1, /* timeout */
|
||||
NULL, /* cancellable */
|
||||
NULL, /* callback */
|
||||
NULL /* user_data */);
|
||||
|
||||
g_free (uuid_str);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int
|
||||
plugin_get_api_version (PluginObject *obj,
|
||||
NPVariant *result)
|
||||
@ -633,7 +726,8 @@ plugin_get_shell_version (PluginObject *obj,
|
||||
STRINGN_TO_NPVARIANT (buffer, length, *result);
|
||||
|
||||
out:
|
||||
g_variant_unref (res);
|
||||
if (res)
|
||||
g_variant_unref (res);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -697,6 +791,14 @@ plugin_object_invoke (NPObject *npobj,
|
||||
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;
|
||||
}
|
||||
@ -706,6 +808,7 @@ plugin_object_has_property (NPObject *npobj,
|
||||
NPIdentifier name)
|
||||
{
|
||||
return (name == onextension_changed_id ||
|
||||
name == onrestart_id ||
|
||||
name == api_version_id ||
|
||||
name == shell_version_id);
|
||||
}
|
||||
@ -732,6 +835,33 @@ plugin_object_get_property (NPObject *npobj,
|
||||
else
|
||||
NULL_TO_NPVARIANT (*result);
|
||||
}
|
||||
else if (name == onrestart_id)
|
||||
{
|
||||
if (obj->restart_listener)
|
||||
OBJECT_TO_NPVARIANT (obj->restart_listener, *result);
|
||||
else
|
||||
NULL_TO_NPVARIANT (*result);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static bool
|
||||
plugin_object_set_callback (NPObject **listener,
|
||||
const NPVariant *value)
|
||||
{
|
||||
if (!NPVARIANT_IS_OBJECT (*value) && !NPVARIANT_IS_NULL (*value))
|
||||
return FALSE;
|
||||
|
||||
if (*listener)
|
||||
funcs.releaseobject (*listener);
|
||||
*listener = NULL;
|
||||
|
||||
if (NPVARIANT_IS_OBJECT (*value))
|
||||
{
|
||||
*listener = NPVARIANT_TO_OBJECT (*value);
|
||||
funcs.retainobject (*listener);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@ -743,25 +873,13 @@ plugin_object_set_property (NPObject *npobj,
|
||||
{
|
||||
PluginObject *obj;
|
||||
|
||||
if (!plugin_object_has_property (npobj, name))
|
||||
return FALSE;
|
||||
obj = (PluginObject *)npobj;
|
||||
|
||||
if (name == onextension_changed_id)
|
||||
{
|
||||
obj = (PluginObject*) npobj;
|
||||
if (obj->listener)
|
||||
funcs.releaseobject (obj->listener);
|
||||
return plugin_object_set_callback (&obj->listener, value);
|
||||
|
||||
obj->listener = NULL;
|
||||
if (NPVARIANT_IS_OBJECT (*value))
|
||||
{
|
||||
obj->listener = NPVARIANT_TO_OBJECT (*value);
|
||||
funcs.retainobject (obj->listener);
|
||||
return TRUE;
|
||||
}
|
||||
else if (NPVARIANT_IS_NULL (*value))
|
||||
return TRUE;
|
||||
}
|
||||
if (name == onrestart_id)
|
||||
return plugin_object_set_callback (&obj->restart_listener, value);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
@ -795,7 +913,9 @@ init_methods_and_properties (void)
|
||||
install_extension_id = funcs.getstringidentifier ("installExtension");
|
||||
uninstall_extension_id = funcs.getstringidentifier ("uninstallExtension");
|
||||
get_errors_id = funcs.getstringidentifier ("getExtensionErrors");
|
||||
launch_extension_prefs_id = funcs.getstringidentifier ("launchExtensionPrefs");
|
||||
|
||||
onrestart_id = funcs.getstringidentifier ("onshellrestart");
|
||||
onextension_changed_id = funcs.getstringidentifier ("onchange");
|
||||
}
|
||||
|
||||
|
18
configure.ac
18
configure.ac
@ -1,5 +1,5 @@
|
||||
AC_PREREQ(2.63)
|
||||
AC_INIT([gnome-shell],[3.3.2],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
AC_INIT([gnome-shell],[3.3.5],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_SRCDIR([src/shell-global.c])
|
||||
@ -53,7 +53,7 @@ if $PKG_CONFIG --exists gstreamer-0.10 '>=' $GSTREAMER_MIN_VERSION ; then
|
||||
AC_MSG_RESULT(yes)
|
||||
build_recorder=true
|
||||
recorder_modules="gstreamer-0.10 gstreamer-base-0.10 x11"
|
||||
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes)
|
||||
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes gl)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
@ -63,14 +63,14 @@ AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
|
||||
CLUTTER_MIN_VERSION=1.7.5
|
||||
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
|
||||
GJS_MIN_VERSION=1.29.18
|
||||
MUTTER_MIN_VERSION=3.3.2
|
||||
MUTTER_MIN_VERSION=3.3.5
|
||||
FOLKS_MIN_VERSION=0.5.2
|
||||
GTK_MIN_VERSION=3.0.0
|
||||
GIO_MIN_VERSION=2.31.0
|
||||
GTK_MIN_VERSION=3.3.9
|
||||
GIO_MIN_VERSION=2.31.6
|
||||
LIBECAL_MIN_VERSION=2.32.0
|
||||
LIBEDATASERVER_MIN_VERSION=1.2.0
|
||||
LIBEDATASERVERUI_MIN_VERSION=2.91.6
|
||||
TELEPATHY_GLIB_MIN_VERSION=0.15.5
|
||||
TELEPATHY_GLIB_MIN_VERSION=0.15.6
|
||||
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
|
||||
POLKIT_MIN_VERSION=0.100
|
||||
STARTUP_NOTIFICATION_MIN_VERSION=0.11
|
||||
@ -84,6 +84,7 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
|
||||
gjs-internals-1.0 >= $GJS_MIN_VERSION
|
||||
libgnome-menu-3.0 $recorder_modules
|
||||
gdk-x11-3.0 libsoup-2.4
|
||||
gl
|
||||
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
|
||||
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
|
||||
libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION
|
||||
@ -116,6 +117,7 @@ AC_CHECK_FUNCS(JS_NewGlobalObject XFixesCreatePointerBarrier)
|
||||
CFLAGS=$saved_CFLAGS
|
||||
LIBS=$saved_LIBS
|
||||
|
||||
PKG_CHECK_MODULES(GNOME_SHELL_JS, glib-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 gnome-desktop-3.0 >= 2.90.0 x11)
|
||||
PKG_CHECK_MODULES(TRAY, gtk+-3.0)
|
||||
PKG_CHECK_MODULES(GVC, libpulse libpulse-mainloop-glib gobject-2.0)
|
||||
@ -197,7 +199,7 @@ if test "$enable_compile_warnings" != no ; then
|
||||
if test "$enable_compile_warnings" = error ; then
|
||||
case " $CFLAGS " in
|
||||
*[\ \ ]-Werror[\ \ ]*) ;;
|
||||
*) CFLAGS="$CFLAGS -Werror" ;;
|
||||
*) CFLAGS="$CFLAGS -Werror -Wno-error=deprecated-declarations" ;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
@ -205,7 +207,7 @@ fi
|
||||
changequote([,])dnl
|
||||
|
||||
AC_ARG_ENABLE(jhbuild-wrapper-script,
|
||||
AS_HELP_STRING([--jhbuild-wrapper-script=yes],[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)
|
||||
|
||||
AC_MSG_CHECKING([location of system Certificate Authority list])
|
||||
|
@ -1,5 +1,5 @@
|
||||
desktopdir=$(datadir)/applications
|
||||
desktop_DATA = gnome-shell.desktop
|
||||
desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
|
||||
|
||||
# We substitute in bindir so it works as an autostart
|
||||
# file when built in a non-system prefix
|
||||
@ -59,6 +59,8 @@ gschemas.compiled: $(gsettings_SCHEMAS:.xml=.valid)
|
||||
|
||||
all-local: gschemas.compiled
|
||||
|
||||
convertdir = $(datadir)/GConf/gsettings
|
||||
convert_DATA = gnome-shell-overrides.convert
|
||||
|
||||
shadersdir = $(pkgdatadir)/shaders
|
||||
shaders_DATA = \
|
||||
@ -67,12 +69,15 @@ shaders_DATA = \
|
||||
|
||||
EXTRA_DIST = \
|
||||
gnome-shell.desktop.in.in \
|
||||
gnome-shell-extension-prefs.desktop.in.in \
|
||||
$(menu_DATA) \
|
||||
$(shaders_DATA) \
|
||||
$(convert_DATA) \
|
||||
org.gnome.shell.gschema.xml.in
|
||||
|
||||
CLEANFILES = \
|
||||
gnome-shell.desktop.in \
|
||||
gnome-shell-extension-prefs.in \
|
||||
$(desktop_DATA) \
|
||||
$(gsettings_SCHEMAS) \
|
||||
gschemas.compiled
|
||||
|
12
data/gnome-shell-extension-prefs.desktop.in.in
Normal file
12
data/gnome-shell-extension-prefs.desktop.in.in
Normal file
@ -0,0 +1,12 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
_Name=GNOME Shell Extension Preferences
|
||||
_Comment=Configure GNOME Shell Extensions
|
||||
Exec=@bindir@/gnome-shell-extension-prefs %u
|
||||
X-GNOME-Bugzilla-Bugzilla=GNOME
|
||||
X-GNOME-Bugzilla-Product=gnome-shell
|
||||
X-GNOME-Bugzilla-Component=extensions
|
||||
X-GNOME-Bugzilla-Version=@VERSION@
|
||||
Categories=GNOME;GTK;
|
||||
OnlyShowIn=GNOME;
|
||||
NoDisplay=true
|
5
data/gnome-shell-overrides.convert
Normal file
5
data/gnome-shell-overrides.convert
Normal file
@ -0,0 +1,5 @@
|
||||
[org.gnome.shell.overrides]
|
||||
attach-modal-dialogs = /desktop/gnome/shell/windows/attach_modal_dialogs
|
||||
button-layout = /desktop/gnome/shell/windows/button_layout
|
||||
edge-tiling = /desktop/gnome/shell/windows/edge_tiling
|
||||
workspaces-only-on-primary = /desktop/gnome/shell/windows/workspaces_only_on_primary
|
@ -108,7 +108,7 @@
|
||||
<schema id="org.gnome.shell.recorder" path="/org/gnome/shell/recorder/"
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="framerate" type="i">
|
||||
<default>15</default>
|
||||
<default>30</default>
|
||||
<_summary>Framerate used for recording screencasts.</_summary>
|
||||
<_description>
|
||||
The framerate of the resulting screencast recordered
|
||||
@ -127,7 +127,7 @@
|
||||
take care of its own output - this might be used to send the output
|
||||
to an icecast server via shout2send or similar. When unset or set
|
||||
to an empty value, the default pipeline will be used. This is currently
|
||||
'videorate ! vp8enc quality=10 speed=2 threads=%T ! queue ! webmmux'
|
||||
'vp8enc quality=8 speed=6 threads=%T ! queue ! webmmux'
|
||||
and records to WEBM using the VP8 codec. %T is used as a placeholder
|
||||
for a guess at the optimal thread count on the system.
|
||||
</_description>
|
||||
|
@ -224,16 +224,20 @@ StTooltip StLabel {
|
||||
|
||||
.toggle-switch-us {
|
||||
background-image: url("toggle-off-us.svg");
|
||||
background-size: contain;
|
||||
}
|
||||
.toggle-switch-us:checked {
|
||||
background-image: url("toggle-on-us.svg");
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.toggle-switch-intl {
|
||||
background-image: url("toggle-off-intl.svg");
|
||||
background-size: contain;
|
||||
}
|
||||
.toggle-switch-intl:checked {
|
||||
background-image: url("toggle-on-intl.svg");
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.nm-menu-item-icons {
|
||||
@ -467,16 +471,17 @@ StTooltip StLabel {
|
||||
}
|
||||
|
||||
.window-caption {
|
||||
background: rgba(0,0,0,0.8);
|
||||
border: 1px solid rgba(128,128,128,0.40);
|
||||
border-radius: 10px;
|
||||
background: rgba(0,0,0,0.5);
|
||||
border-radius: 8px;
|
||||
font-size: 9pt;
|
||||
padding: 2px 8px;
|
||||
-shell-caption-spacing: 4px;
|
||||
font-weight: bold;
|
||||
padding: 6px 12px;
|
||||
-shell-caption-spacing: 12px;
|
||||
}
|
||||
|
||||
.window-close {
|
||||
background-image: url("close-window.svg");
|
||||
background-size: 34px;
|
||||
height: 34px;
|
||||
width: 34px;
|
||||
-shell-close-overlap: 20px;
|
||||
@ -511,6 +516,7 @@ StTooltip StLabel {
|
||||
|
||||
.placeholder {
|
||||
background-image: url("dash-placeholder.svg");
|
||||
background-size: contain;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
@ -645,6 +651,17 @@ StTooltip StLabel {
|
||||
font-size: 11pt;
|
||||
}
|
||||
|
||||
.dash-label {
|
||||
border-radius: 7px;
|
||||
padding: 4px 12px;
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
color: #ffffff;
|
||||
font-size: 0.9em;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
-x-offset: 8px;
|
||||
}
|
||||
|
||||
/* Apps */
|
||||
|
||||
.icon-grid {
|
||||
@ -717,7 +734,8 @@ StTooltip StLabel {
|
||||
border-radius: 4px;
|
||||
padding: 3px;
|
||||
border: 1px rgba(0,0,0,0);
|
||||
font-size: 7.5pt;
|
||||
font-size: 8pt;
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
transition-duration: 100;
|
||||
text-align: center;
|
||||
@ -771,6 +789,7 @@ StTooltip StLabel {
|
||||
.app-well-app.running > .overview-icon {
|
||||
text-shadow: black 0px 2px 2px;
|
||||
background-image: url("running-indicator.svg");
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.contact:selected,
|
||||
@ -1432,6 +1451,7 @@ StTooltip StLabel {
|
||||
|
||||
.summary-source-button:selected .summary-source {
|
||||
background-image: url("panel-button-highlight-narrow.svg");
|
||||
background-size: contain;
|
||||
border-image: url("source-button-border.svg") 10 10 0 1;
|
||||
}
|
||||
|
||||
@ -1442,6 +1462,7 @@ StTooltip StLabel {
|
||||
|
||||
.summary-source-button:expanded:selected {
|
||||
background-image: url("panel-button-highlight-wide.svg");
|
||||
background-size: contain;
|
||||
border-image: url("source-button-border.svg") 10 10 0 1;
|
||||
}
|
||||
|
||||
@ -1555,6 +1576,7 @@ StTooltip StLabel {
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
background-image: url("corner-ripple-ltr.png");
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.ripple-box:rtl {
|
||||
@ -1688,6 +1710,10 @@ StTooltip StLabel {
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.flashspot {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
/* End Session Dialog */
|
||||
.end-session-dialog {
|
||||
spacing: 42px;
|
||||
@ -1800,6 +1826,10 @@ StTooltip StLabel {
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
.mount-question-dialog-subject {
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.show-processes-dialog-subject:rtl,
|
||||
.mount-question-dialog-subject:rtl {
|
||||
padding-left: 0px;
|
||||
|
@ -18,11 +18,10 @@ DOC_MODULE=shell
|
||||
# The top-level SGML file. You can change this if you want to.
|
||||
DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml
|
||||
|
||||
# Directories containing the source code, relative to $(srcdir).
|
||||
# Directories containing the source code
|
||||
# gtk-doc will search all .c and .h files beneath these paths
|
||||
# for inline comments documenting functions and macros.
|
||||
# e.g. DOC_SOURCE_DIR=../../../gtk ../../../gdk
|
||||
DOC_SOURCE_DIR=../../../src
|
||||
DOC_SOURCE_DIR=$(top_srcdir)/src
|
||||
|
||||
# Extra options to pass to gtkdoc-scangobj. Not normally needed.
|
||||
SCANGOBJ_OPTIONS=
|
||||
@ -58,7 +57,16 @@ EXTRA_HFILES=
|
||||
|
||||
# Header files or dirs to ignore when scanning. Use base file/dir names
|
||||
# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h private_code
|
||||
IGNORE_HFILES=calendar-server gvc hotplug-sniffer st tray
|
||||
IGNORE_HFILES= \
|
||||
calendar-server \
|
||||
gvc \
|
||||
hotplug-sniffer \
|
||||
st \
|
||||
tray \
|
||||
gactionmuxer.h \
|
||||
gactionobservable.h \
|
||||
gactionobserver.h \
|
||||
shell-recorder-src.h
|
||||
|
||||
# Images to copy into HTML directory.
|
||||
# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
|
||||
@ -79,7 +87,7 @@ expand_content_files=
|
||||
# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
|
||||
# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
|
||||
GTKDOC_CFLAGS=$(GNOME_SHELL_CFLAGS)
|
||||
GTKDOC_LIBS=$(GNOME_SHELL_LIBS) $(top_builddir)/src/libgnome-shell.la
|
||||
GTKDOC_LIBS=$(GNOME_SHELL_LIBS) $(BLUETOOTH_LIBS) $(top_builddir)/src/libgnome-shell.la
|
||||
|
||||
# This includes the standard gtk-doc make rules, copied by gtkdocize.
|
||||
include $(top_srcdir)/gtk-doc.make
|
||||
|
@ -18,11 +18,10 @@ DOC_MODULE=st
|
||||
# The top-level SGML file. You can change this if you want to.
|
||||
DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml
|
||||
|
||||
# Directories containing the source code, relative to $(srcdir).
|
||||
# Directories containing the source code
|
||||
# gtk-doc will search all .c and .h files beneath these paths
|
||||
# for inline comments documenting functions and macros.
|
||||
# e.g. DOC_SOURCE_DIR=../../../gtk ../../../gdk
|
||||
DOC_SOURCE_DIR=../../../src/st
|
||||
DOC_SOURCE_DIR=$(top_srcdir)/src/st
|
||||
|
||||
# Extra options to pass to gtkdoc-scangobj. Not normally needed.
|
||||
SCANGOBJ_OPTIONS=
|
||||
|
@ -7,8 +7,10 @@ nobase_dist_js_DATA = \
|
||||
gdm/fingerprint.js \
|
||||
gdm/loginDialog.js \
|
||||
gdm/powerMenu.js \
|
||||
extensionPrefs/main.js \
|
||||
misc/config.js \
|
||||
misc/docInfo.js \
|
||||
misc/extensionUtils.js \
|
||||
misc/fileUtils.js \
|
||||
misc/format.js \
|
||||
misc/gnomeSession.js \
|
||||
@ -35,6 +37,7 @@ nobase_dist_js_DATA = \
|
||||
ui/endSessionDialog.js \
|
||||
ui/environment.js \
|
||||
ui/extensionSystem.js \
|
||||
ui/flashspot.js \
|
||||
ui/iconGrid.js \
|
||||
ui/keyboard.js \
|
||||
ui/layout.js \
|
||||
@ -72,6 +75,7 @@ nobase_dist_js_DATA = \
|
||||
ui/tweener.js \
|
||||
ui/userMenu.js \
|
||||
ui/viewSelector.js \
|
||||
ui/wanda.js \
|
||||
ui/windowAttentionHandler.js \
|
||||
ui/windowManager.js \
|
||||
ui/workspace.js \
|
||||
|
279
js/extensionPrefs/main.js
Normal file
279
js/extensionPrefs/main.js
Normal file
@ -0,0 +1,279 @@
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Gettext = imports.gettext;
|
||||
const GLib = imports.gi.GLib;
|
||||
const GObject = imports.gi.GObject;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Pango = imports.gi.Pango;
|
||||
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
const Format = imports.misc.format;
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
|
||||
|
||||
const GnomeShellIface = <interface name="org.gnome.Shell">
|
||||
<signal name="ExtensionStatusChanged">
|
||||
<arg type="s" name="uuid"/>
|
||||
<arg type="i" name="state"/>
|
||||
<arg type="s" name="error"/>
|
||||
</signal>
|
||||
</interface>;
|
||||
|
||||
const GnomeShellProxy = Gio.DBusProxy.makeProxyWrapper(GnomeShellIface);
|
||||
|
||||
function stripPrefix(string, prefix) {
|
||||
if (string.slice(0, prefix.length) == prefix)
|
||||
return string.slice(prefix.length);
|
||||
return string;
|
||||
}
|
||||
|
||||
const Application = new Lang.Class({
|
||||
Name: 'Application',
|
||||
_init: function() {
|
||||
GLib.set_prgname('gnome-shell-extension-prefs');
|
||||
this.application = new Gtk.Application({
|
||||
application_id: 'org.gnome.shell.ExtensionPrefs',
|
||||
flags: Gio.ApplicationFlags.HANDLES_COMMAND_LINE
|
||||
});
|
||||
|
||||
this.application.connect('activate', Lang.bind(this, this._onActivate));
|
||||
this.application.connect('command-line', Lang.bind(this, this._onCommandLine));
|
||||
this.application.connect('startup', Lang.bind(this, this._onStartup));
|
||||
|
||||
this._extensionPrefsModules = {};
|
||||
|
||||
this._extensionIters = {};
|
||||
},
|
||||
|
||||
_buildModel: function() {
|
||||
this._model = new Gtk.ListStore();
|
||||
this._model.set_column_types([GObject.TYPE_STRING, GObject.TYPE_STRING]);
|
||||
},
|
||||
|
||||
_extensionAvailable: function(uuid) {
|
||||
let extension = ExtensionUtils.extensions[uuid];
|
||||
|
||||
if (!extension)
|
||||
return false;
|
||||
|
||||
if (ExtensionUtils.isOutOfDate(extension))
|
||||
return false;
|
||||
|
||||
if (!extension.dir.get_child('prefs.js').query_exists(null))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_setExtensionInsensitive: function(layout, cell, model, iter, data) {
|
||||
let uuid = model.get_value(iter, 0);
|
||||
if (!this._extensionAvailable(uuid))
|
||||
cell.set_sensitive(false);
|
||||
},
|
||||
|
||||
_getExtensionPrefsModule: function(extension) {
|
||||
let uuid = extension.metadata.uuid;
|
||||
|
||||
if (this._extensionPrefsModules.hasOwnProperty(uuid))
|
||||
return this._extensionPrefsModules[uuid];
|
||||
|
||||
ExtensionUtils.installImporter(extension);
|
||||
|
||||
let prefsModule = extension.imports.prefs;
|
||||
prefsModule.init(extension.metadata);
|
||||
|
||||
this._extensionPrefsModules[uuid] = prefsModule;
|
||||
return prefsModule;
|
||||
},
|
||||
|
||||
_selectExtension: function(uuid) {
|
||||
if (!this._extensionAvailable(uuid))
|
||||
return;
|
||||
|
||||
let extension = ExtensionUtils.extensions[uuid];
|
||||
let widget;
|
||||
|
||||
try {
|
||||
let prefsModule = this._getExtensionPrefsModule(extension);
|
||||
widget = prefsModule.buildPrefsWidget();
|
||||
} catch (e) {
|
||||
widget = this._buildErrorUI(extension, e);
|
||||
}
|
||||
|
||||
// Destroy the current prefs widget, if it exists
|
||||
if (this._extensionPrefsBin.get_child())
|
||||
this._extensionPrefsBin.get_child().destroy();
|
||||
|
||||
this._extensionPrefsBin.add(widget);
|
||||
this._extensionSelector.set_active_iter(this._extensionIters[uuid]);
|
||||
},
|
||||
|
||||
_extensionSelected: function() {
|
||||
let [success, iter] = this._extensionSelector.get_active_iter();
|
||||
if (!success)
|
||||
return;
|
||||
|
||||
let uuid = this._model.get_value(iter, 0);
|
||||
this._selectExtension(uuid);
|
||||
},
|
||||
|
||||
_buildErrorUI: function(extension, exc) {
|
||||
let box = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
|
||||
let label = new Gtk.Label({
|
||||
label: _("There was an error loading the preferences dialog for %s:").format(extension.metadata.name)
|
||||
});
|
||||
box.add(label);
|
||||
|
||||
let errortext = '';
|
||||
errortext += exc;
|
||||
errortext += '\n\n';
|
||||
errortext += 'Stack trace:\n';
|
||||
|
||||
// Indent stack trace.
|
||||
errortext += exc.stack.split('\n').map(function(line) {
|
||||
return ' ' + line;
|
||||
}).join('\n');
|
||||
|
||||
let scroll = new Gtk.ScrolledWindow({ vexpand: true });
|
||||
let buffer = new Gtk.TextBuffer({ text: errortext });
|
||||
let textview = new Gtk.TextView({ buffer: buffer });
|
||||
textview.override_font(Pango.font_description_from_string('monospace'));
|
||||
scroll.add(textview);
|
||||
box.add(scroll);
|
||||
|
||||
box.show_all();
|
||||
return box;
|
||||
},
|
||||
|
||||
_buildUI: function(app) {
|
||||
this._window = new Gtk.ApplicationWindow({ application: app,
|
||||
window_position: Gtk.WindowPosition.CENTER,
|
||||
title: _("GNOME Shell Extension Preferences") });
|
||||
|
||||
this._window.set_size_request(600, 400);
|
||||
|
||||
let vbox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
|
||||
this._window.add(vbox);
|
||||
|
||||
let toolbar = new Gtk.Toolbar();
|
||||
toolbar.get_style_context().add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR);
|
||||
vbox.add(toolbar);
|
||||
let toolitem;
|
||||
|
||||
let label = new Gtk.Label({ label: _("<b>Extension</b>"),
|
||||
use_markup: true });
|
||||
toolitem = new Gtk.ToolItem({ child: label });
|
||||
toolbar.add(toolitem);
|
||||
|
||||
this._extensionSelector = new Gtk.ComboBox({ model: this._model,
|
||||
margin_left: 8,
|
||||
hexpand: true });
|
||||
this._extensionSelector.get_style_context().add_class(Gtk.STYLE_CLASS_RAISED);
|
||||
|
||||
let renderer = new Gtk.CellRendererText();
|
||||
this._extensionSelector.pack_start(renderer, true);
|
||||
this._extensionSelector.add_attribute(renderer, 'text', 1);
|
||||
this._extensionSelector.set_cell_data_func(renderer, Lang.bind(this, this._setExtensionInsensitive), null);
|
||||
this._extensionSelector.connect('changed', Lang.bind(this, this._extensionSelected));
|
||||
|
||||
toolitem = new Gtk.ToolItem({ child: this._extensionSelector });
|
||||
toolitem.set_expand(true);
|
||||
toolbar.add(toolitem);
|
||||
|
||||
this._extensionPrefsBin = new Gtk.Frame();
|
||||
vbox.add(this._extensionPrefsBin);
|
||||
|
||||
let label = new Gtk.Label({
|
||||
label: _("Select an extension to configure using the combobox above."),
|
||||
vexpand: true
|
||||
});
|
||||
|
||||
this._extensionPrefsBin.add(label);
|
||||
|
||||
this._shellProxy = new GnomeShellProxy(Gio.DBus.session, 'org.gnome.Shell', '/org/gnome/Shell');
|
||||
this._shellProxy.connectSignal('ExtensionStatusChanged', Lang.bind(this, function(proxy, senderName, [uuid, state, error]) {
|
||||
if (ExtensionUtils.extensions[uuid] !== undefined)
|
||||
this._scanExtensions();
|
||||
}));
|
||||
|
||||
this._window.show_all();
|
||||
},
|
||||
|
||||
_scanExtensions: function() {
|
||||
ExtensionUtils.scanExtensions(Lang.bind(this, function(uuid, dir, type) {
|
||||
if (ExtensionUtils.extensions[uuid] !== undefined)
|
||||
return;
|
||||
|
||||
let extension;
|
||||
try {
|
||||
extension = ExtensionUtils.createExtensionObject(uuid, dir, type);
|
||||
} catch(e) {
|
||||
global.logError('' + e);
|
||||
return;
|
||||
}
|
||||
|
||||
let iter = this._model.append();
|
||||
this._model.set(iter, [0, 1], [uuid, extension.metadata.name]);
|
||||
this._extensionIters[uuid] = iter;
|
||||
}));
|
||||
},
|
||||
|
||||
_onActivate: function() {
|
||||
this._window.present();
|
||||
},
|
||||
|
||||
_onStartup: function(app) {
|
||||
this._buildModel();
|
||||
this._buildUI(app);
|
||||
this._scanExtensions();
|
||||
},
|
||||
|
||||
_onCommandLine: function(app, commandLine) {
|
||||
app.activate();
|
||||
let args = commandLine.get_arguments();
|
||||
if (args.length) {
|
||||
let uuid = args[0];
|
||||
|
||||
// Strip off "extension:///" prefix which fakes a URI, if it exists
|
||||
uuid = stripPrefix(uuid, "extension:///");
|
||||
|
||||
if (!this._extensionAvailable(uuid))
|
||||
return 1;
|
||||
|
||||
this._selectExtension(uuid);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
function initEnvironment() {
|
||||
// Monkey-patch in a "global" object that fakes some Shell utilities
|
||||
// that ExtensionUtils depends on.
|
||||
window.global = {
|
||||
log: function() {
|
||||
print([].join.call(arguments, ', '));
|
||||
},
|
||||
|
||||
logError: function(s) {
|
||||
global.log('ERROR: ' + s);
|
||||
},
|
||||
|
||||
userdatadir: GLib.build_filenamev([GLib.get_user_data_dir(), 'gnome-shell'])
|
||||
};
|
||||
|
||||
String.prototype.format = Format.format;
|
||||
}
|
||||
|
||||
function main(argv) {
|
||||
initEnvironment();
|
||||
ExtensionUtils.init();
|
||||
|
||||
Gettext.bindtextdomain(Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
|
||||
Gettext.textdomain(Config.GETTEXT_PACKAGE);
|
||||
|
||||
let app = new Application();
|
||||
app.application.run(argv);
|
||||
}
|
@ -205,7 +205,8 @@ const UserListItem = new Lang.Class({
|
||||
// We use background-image instead of, say, St.TextureCache
|
||||
// so the theme writers can add a rounded frame around the image
|
||||
// and so theme writers can pick the icon size.
|
||||
this._iconBin.set_style('background-image: url("' + iconFile + '");');
|
||||
this._iconBin.set_style('background-image: url("' + iconFile + '");' +
|
||||
'background-size: contain;');
|
||||
} else {
|
||||
this._iconBin.hide();
|
||||
}
|
||||
|
@ -10,3 +10,7 @@ const GJS_VERSION = '@GJS_VERSION@';
|
||||
const HAVE_BLUETOOTH = @HAVE_BLUETOOTH@;
|
||||
/* The system TLS CA list */
|
||||
const SHELL_SYSTEM_CA_FILE = '@SHELL_SYSTEM_CA_FILE@';
|
||||
/* gettext package */
|
||||
const GETTEXT_PACKAGE = '@GETTEXT_PACKAGE@';
|
||||
/* locale dir */
|
||||
const LOCALEDIR = '@datadir@/locale';
|
||||
|
194
js/misc/extensionUtils.js
Normal file
194
js/misc/extensionUtils.js
Normal file
@ -0,0 +1,194 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
// Common utils for the extension system and the extension
|
||||
// preferences tool
|
||||
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const ShellJS = imports.gi.ShellJS;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
|
||||
const ExtensionType = {
|
||||
SYSTEM: 1,
|
||||
PER_USER: 2
|
||||
};
|
||||
|
||||
// GFile for user extensions
|
||||
var userExtensionsDir = null;
|
||||
|
||||
// Maps uuid -> metadata object
|
||||
const extensions = {};
|
||||
|
||||
function getCurrentExtension() {
|
||||
let stack = (new Error()).stack;
|
||||
|
||||
// Assuming we're importing this directly from an extension (and we shouldn't
|
||||
// ever not be), its UUID should be directly in the path here.
|
||||
let extensionStackLine = stack.split('\n')[1];
|
||||
if (!extensionStackLine)
|
||||
throw new Error('Could not find current extension');
|
||||
|
||||
// The stack line is like:
|
||||
// init([object Object])@/home/user/data/gnome-shell/extensions/u@u.id/prefs.js:8
|
||||
//
|
||||
// In the case that we're importing from
|
||||
// module scope, the first field is blank:
|
||||
// @/home/user/data/gnome-shell/extensions/u@u.id/prefs.js:8
|
||||
let match = new RegExp('@(.+):\\d+').exec(extensionStackLine);
|
||||
if (!match)
|
||||
throw new Error('Could not find current extension');
|
||||
|
||||
let path = match[1];
|
||||
let uuid = GLib.path_get_basename(GLib.path_get_dirname(path));
|
||||
|
||||
let extension = extensions[uuid];
|
||||
if (extension === undefined)
|
||||
throw new Error('Could not find current extension');
|
||||
|
||||
return extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* versionCheck:
|
||||
* @required: an array of versions we're compatible with
|
||||
* @current: the version we have
|
||||
*
|
||||
* Check if a component is compatible for an extension.
|
||||
* @required is an array, and at least one version must match.
|
||||
* @current must be in the format <major>.<minor>.<point>.<micro>
|
||||
* <micro> is always ignored
|
||||
* <point> is ignored if <minor> is even (so you can target the
|
||||
* whole stable release)
|
||||
* <minor> and <major> must match
|
||||
* Each target version must be at least <major> and <minor>
|
||||
*/
|
||||
function versionCheck(required, current) {
|
||||
let currentArray = current.split('.');
|
||||
let major = currentArray[0];
|
||||
let minor = currentArray[1];
|
||||
let point = currentArray[2];
|
||||
for (let i = 0; i < required.length; i++) {
|
||||
let requiredArray = required[i].split('.');
|
||||
if (requiredArray[0] == major &&
|
||||
requiredArray[1] == minor &&
|
||||
(requiredArray[2] == point ||
|
||||
(requiredArray[2] == undefined && parseInt(minor) % 2 == 0)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isOutOfDate(extension) {
|
||||
if (!versionCheck(extension.metadata['shell-version'], Config.PACKAGE_VERSION))
|
||||
return true;
|
||||
|
||||
if (extension.metadata['js-version'] && !versionCheck(extension.metadata['js-version'], Config.GJS_VERSION))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function createExtensionObject(uuid, dir, type) {
|
||||
let info;
|
||||
|
||||
let metadataFile = dir.get_child('metadata.json');
|
||||
if (!metadataFile.query_exists(null)) {
|
||||
throw new Error('Missing metadata.json');
|
||||
}
|
||||
|
||||
let metadataContents, success, tag;
|
||||
try {
|
||||
[success, metadataContents, tag] = metadataFile.load_contents(null);
|
||||
} catch (e) {
|
||||
throw new Error('Failed to load metadata.json: ' + e);
|
||||
}
|
||||
let meta;
|
||||
try {
|
||||
meta = JSON.parse(metadataContents);
|
||||
} catch (e) {
|
||||
throw new Error('Failed to parse metadata.json: ' + e);
|
||||
}
|
||||
|
||||
let requiredProperties = ['uuid', 'name', 'description', 'shell-version'];
|
||||
for (let i = 0; i < requiredProperties.length; i++) {
|
||||
let prop = requiredProperties[i];
|
||||
if (!meta[prop]) {
|
||||
throw new Error('missing "' + prop + '" property in metadata.json');
|
||||
}
|
||||
}
|
||||
|
||||
// Encourage people to add this
|
||||
if (!meta.url) {
|
||||
global.log('Warning: Missing "url" property in metadata.json');
|
||||
}
|
||||
|
||||
if (uuid != meta.uuid) {
|
||||
throw new Error('uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + uuid + '"');
|
||||
}
|
||||
|
||||
let extension = {};
|
||||
|
||||
extension.metadata = meta;
|
||||
extension.uuid = meta.uuid;
|
||||
extension.type = type;
|
||||
extension.dir = dir;
|
||||
extension.path = dir.get_path();
|
||||
extension.error = '';
|
||||
extension.hasPrefs = dir.get_child('prefs.js').query_exists(null);
|
||||
|
||||
extensions[uuid] = extension;
|
||||
|
||||
return extension;
|
||||
}
|
||||
|
||||
var _extension = null;
|
||||
|
||||
function installImporter(extension) {
|
||||
_extension = extension;
|
||||
ShellJS.add_extension_importer('imports.misc.extensionUtils._extension', 'imports', extension.path);
|
||||
_extension = null;
|
||||
}
|
||||
|
||||
function init() {
|
||||
let userExtensionsPath = GLib.build_filenamev([global.userdatadir, 'extensions']);
|
||||
userExtensionsDir = Gio.file_new_for_path(userExtensionsPath);
|
||||
try {
|
||||
if (!userExtensionsDir.query_exists(null))
|
||||
userExtensionsDir.make_directory_with_parents(null);
|
||||
} catch (e) {
|
||||
global.logError('' + e);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
@ -38,7 +38,7 @@ function recursivelyDeleteDir(dir) {
|
||||
let child = dir.get_child(info.get_name());
|
||||
if (type == Gio.FileType.REGULAR)
|
||||
deleteGFile(child);
|
||||
else if (type == Gio.TypeType.DIRECTORY)
|
||||
else if (type == Gio.FileType.DIRECTORY)
|
||||
recursivelyDeleteDir(child);
|
||||
}
|
||||
|
||||
|
@ -165,7 +165,7 @@ const ModemCdma = new Lang.Class({
|
||||
|
||||
this.signal_quality = 0;
|
||||
this.operator_name = null;
|
||||
this._proxy.connect('SignalQuality', Lang.bind(this, function(proxy, sender, params) {
|
||||
this._proxy.connectSignal('SignalQuality', Lang.bind(this, function(proxy, sender, params) {
|
||||
this.signal_quality = params[0];
|
||||
this.emit('notify::signal-quality');
|
||||
|
||||
|
@ -232,3 +232,53 @@ function fixupPCIDescription(desc) {
|
||||
|
||||
return out.join(' ');
|
||||
}
|
||||
|
||||
// lowerBound:
|
||||
// @array: an array or array-like object, already sorted
|
||||
// according to @cmp
|
||||
// @val: the value to add
|
||||
// @cmp: a comparator (or undefined to compare as numbers)
|
||||
//
|
||||
// Returns the position of the first element that is not
|
||||
// lower than @val, according to @cmp.
|
||||
// That is, returns the first position at which it
|
||||
// is possible to insert @val without violating the
|
||||
// order.
|
||||
// This is quite like an ordinary binary search, except
|
||||
// that it doesn't stop at first element comparing equal.
|
||||
|
||||
function lowerBound(array, val, cmp) {
|
||||
let min, max, mid, v;
|
||||
cmp = cmp || function(a, b) { return a - b; };
|
||||
|
||||
if (array.length == 0)
|
||||
return 0;
|
||||
|
||||
min = 0; max = array.length;
|
||||
while (min < (max - 1)) {
|
||||
mid = Math.floor((min + max) / 2);
|
||||
v = cmp(array[mid], val);
|
||||
|
||||
if (v < 0)
|
||||
min = mid + 1;
|
||||
else
|
||||
max = mid;
|
||||
}
|
||||
|
||||
return (min == max || cmp(array[min], val) < 0) ? max : min;
|
||||
}
|
||||
|
||||
// insertSorted:
|
||||
// @array: an array sorted according to @cmp
|
||||
// @val: a value to insert
|
||||
// @cmp: the sorting function
|
||||
//
|
||||
// Inserts @val into @array, preserving the
|
||||
// sorting invariants.
|
||||
// Returns the position at which it was inserted
|
||||
function insertSorted(array, val, cmp) {
|
||||
let pos = lowerBound(array, val, cmp);
|
||||
array.splice(pos, 0, val);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
@ -470,6 +470,7 @@ const AppWellIcon = new Lang.Class({
|
||||
Lang.bind(this,
|
||||
this._onStateChanged));
|
||||
this._onStateChanged();
|
||||
this.isMenuUp = false;
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
@ -551,8 +552,8 @@ const AppWellIcon = new Lang.Class({
|
||||
this._menuManager.addMenu(this._menu);
|
||||
}
|
||||
|
||||
this.isMenuUp = true;
|
||||
this.actor.set_hover(true);
|
||||
this.actor.show_tooltip();
|
||||
this._menu.popup();
|
||||
|
||||
return false;
|
||||
@ -568,6 +569,7 @@ const AppWellIcon = new Lang.Class({
|
||||
|
||||
_onMenuPoppedDown: function() {
|
||||
this.actor.sync_hover();
|
||||
this.isMenuUp = false;
|
||||
},
|
||||
|
||||
_onActivate: function (event) {
|
||||
|
@ -43,7 +43,7 @@ function ConsoleKitManager() {
|
||||
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
|
||||
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
|
||||
|
||||
self.connect('notify::g-name-owner', function() {
|
||||
self._updateSessionActive = function() {
|
||||
if (self.g_name_owner) {
|
||||
self.GetCurrentSessionRemote(function([session]) {
|
||||
self._ckSession = new ConsoleKitSessionProxy(Gio.DBus.system, 'org.freedesktop.ConsoleKit', session);
|
||||
@ -58,8 +58,11 @@ function ConsoleKitManager() {
|
||||
} else {
|
||||
self.sessionActive = true;
|
||||
}
|
||||
});
|
||||
};
|
||||
self.connect('notify::g-name-owner',
|
||||
Lang.bind(self, self._updateSessionActive));
|
||||
|
||||
self._updateSessionActive();
|
||||
self.init(null);
|
||||
return self;
|
||||
}
|
||||
|
@ -557,7 +557,7 @@ const AutorunTransientNotification = new Lang.Class({
|
||||
|
||||
let label = new St.Bin({ y_align: St.Align.MIDDLE,
|
||||
child: new St.Label
|
||||
({ text: _("Open with %s").format(app.get_display_name()) })
|
||||
({ text: _("Open with %s").format(app.get_name()) })
|
||||
});
|
||||
box.add(label);
|
||||
|
||||
|
@ -93,23 +93,30 @@ const Contact = new Lang.Class({
|
||||
text = _("Busy");
|
||||
iconName = 'user-busy';
|
||||
break;
|
||||
default:
|
||||
case Folks.PresenceType.OFFLINE:
|
||||
text = _("Offline");
|
||||
iconName = 'user-offline';
|
||||
break;
|
||||
default:
|
||||
text = '';
|
||||
iconName = null;
|
||||
}
|
||||
|
||||
let icon = new St.Icon({ icon_name: iconName,
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: 16,
|
||||
style_class: 'contact-details-status-icon' });
|
||||
let label = new St.Label({ text: text });
|
||||
|
||||
let box = new St.BoxLayout({ vertical: false,
|
||||
style_class: 'contact-details-status' });
|
||||
box.add(icon, { x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
|
||||
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,
|
||||
|
103
js/ui/dash.js
103
js/ui/dash.js
@ -6,6 +6,7 @@ const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
const Mainloop = imports.mainloop;
|
||||
|
||||
const AppDisplay = imports.ui.appDisplay;
|
||||
const AppFavorites = imports.ui.appFavorites;
|
||||
@ -16,6 +17,9 @@ const Tweener = imports.ui.tweener;
|
||||
const Workspace = imports.ui.workspace;
|
||||
|
||||
const DASH_ANIMATION_TIME = 0.2;
|
||||
const DASH_ITEM_LABEL_SHOW_TIME = 0.15;
|
||||
const DASH_ITEM_LABEL_HIDE_TIME = 0.1;
|
||||
const DASH_ITEM_HOVER_TIMEOUT = 300;
|
||||
|
||||
// A container like StBin, but taking the child's scale into account
|
||||
// when requesting a size
|
||||
@ -32,6 +36,8 @@ const DashItemContainer = new Lang.Class({
|
||||
Lang.bind(this, this._allocate));
|
||||
this.actor._delegate = this;
|
||||
|
||||
this._label = null;
|
||||
|
||||
this.child = null;
|
||||
this._childScale = 1;
|
||||
this._childOpacity = 255;
|
||||
@ -84,6 +90,60 @@ const DashItemContainer = new Lang.Class({
|
||||
alloc.natural_size = natWidth * this.child.scale_y;
|
||||
},
|
||||
|
||||
showLabel: function() {
|
||||
if (this._label == null)
|
||||
return;
|
||||
|
||||
this._label.opacity = 0;
|
||||
this._label.show();
|
||||
|
||||
let [stageX, stageY] = this.actor.get_transformed_position();
|
||||
|
||||
let itemHeight = this.actor.allocation.y2 - this.actor.allocation.y1;
|
||||
|
||||
let labelHeight = this._label.get_height();
|
||||
let yOffset = Math.floor((itemHeight - labelHeight) / 2)
|
||||
|
||||
let y = stageY + yOffset;
|
||||
|
||||
let node = this._label.get_theme_node();
|
||||
let xOffset = node.get_length('-x-offset');
|
||||
|
||||
let x;
|
||||
if (St.Widget.get_default_direction () == St.TextDirection.RTL)
|
||||
x = stageX - this._label.get_width() - xOffset;
|
||||
else
|
||||
x = stageX + this.actor.get_width() + xOffset;
|
||||
|
||||
this._label.set_position(x, y);
|
||||
Tweener.addTween(this._label,
|
||||
{ opacity: 255,
|
||||
time: DASH_ITEM_LABEL_SHOW_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
});
|
||||
},
|
||||
|
||||
setLabelText: function(text) {
|
||||
if (this._label == null)
|
||||
this._label = new St.Label({ style_class: 'dash-label'});
|
||||
|
||||
this._label.set_text(text);
|
||||
Main.layoutManager.addChrome(this._label);
|
||||
this._label.hide();
|
||||
},
|
||||
|
||||
hideLabel: function () {
|
||||
this._label.opacity = 255;
|
||||
Tweener.addTween(this._label,
|
||||
{ opacity: 0,
|
||||
time: DASH_ITEM_LABEL_HIDE_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this._label.hide();
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
setChild: function(actor) {
|
||||
if (this.child == actor)
|
||||
return;
|
||||
@ -238,6 +298,9 @@ const Dash = new Lang.Class({
|
||||
this._dragPlaceholderPos = -1;
|
||||
this._animatingPlaceholdersCount = 0;
|
||||
this._favRemoveTarget = null;
|
||||
this._showLabelTimeoutId = 0;
|
||||
this._resetHoverTimeoutId = 0;
|
||||
this._labelShowing = false;
|
||||
|
||||
this._box = new St.BoxLayout({ name: 'dash',
|
||||
vertical: true,
|
||||
@ -371,16 +434,50 @@ const Dash = new Lang.Class({
|
||||
Lang.bind(this, function() {
|
||||
display.actor.opacity = 255;
|
||||
}));
|
||||
display.actor.set_tooltip_text(app.get_name());
|
||||
|
||||
let item = new DashItemContainer();
|
||||
item.setChild(display.actor);
|
||||
|
||||
display.icon.setIconSize(this.iconSize);
|
||||
item.setLabelText(app.get_name());
|
||||
|
||||
display.icon.setIconSize(this.iconSize);
|
||||
display.actor.connect('notify::hover',
|
||||
Lang.bind(this, function() {
|
||||
this._onHover(item, display)
|
||||
}));
|
||||
return item;
|
||||
},
|
||||
|
||||
_onHover: function (item, display) {
|
||||
if (display.actor.get_hover() && !display.isMenuUp) {
|
||||
if (this._showLabelTimeoutId == 0) {
|
||||
let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT;
|
||||
this._showLabelTimeoutId = Mainloop.timeout_add(timeout,
|
||||
Lang.bind(this, function() {
|
||||
this._labelShowing = true;
|
||||
item.showLabel();
|
||||
return false;
|
||||
}));
|
||||
if (this._resetHoverTimeoutId > 0) {
|
||||
Mainloop.source_remove(this._resetHoverTimeoutId);
|
||||
this._resetHoverTimeoutId = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (this._showLabelTimeoutId > 0)
|
||||
Mainloop.source_remove(this._showLabelTimeoutId);
|
||||
this._showLabelTimeoutId = 0;
|
||||
item.hideLabel();
|
||||
if (this._labelShowing) {
|
||||
this._resetHoverTimeoutId = Mainloop.timeout_add(DASH_ITEM_HOVER_TIMEOUT,
|
||||
Lang.bind(this, function() {
|
||||
this._labelShowing = false;
|
||||
return false;
|
||||
}));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_adjustIconSize: function() {
|
||||
// For the icon size, we only consider children which are "proper"
|
||||
// icons (i.e. ignoring drag placeholders) and which are not
|
||||
@ -404,7 +501,7 @@ const Dash = new Lang.Class({
|
||||
return;
|
||||
|
||||
|
||||
let themeNode = this.actor.get_theme_node();
|
||||
let themeNode = this._box.get_theme_node();
|
||||
let maxAllocation = new Clutter.ActorBox({ x1: 0, y1: 0,
|
||||
x2: 42 /* whatever */,
|
||||
y2: this._maxHeight });
|
||||
|
@ -69,6 +69,7 @@ const DateMenuButton = new Lang.Class({
|
||||
|
||||
// Date
|
||||
this._date = new St.Label();
|
||||
this.actor.label_actor = this._date;
|
||||
this._date.style_class = 'datemenu-date-label';
|
||||
vbox.add(this._date);
|
||||
|
||||
|
@ -333,7 +333,8 @@ const EndSessionDialog = new Lang.Class({
|
||||
this._iconBin.child = null;
|
||||
if (iconFile) {
|
||||
this._iconBin.show();
|
||||
this._iconBin.set_style('background-image: url("' + iconFile + '");');
|
||||
this._iconBin.set_style('background-image: url("' + iconFile + '");' +
|
||||
'background-size: contain;');
|
||||
} else {
|
||||
this._iconBin.hide();
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ const Shell = imports.gi.Shell;
|
||||
const Soup = imports.gi.Soup;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const FileUtils = imports.misc.fileUtils;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
|
||||
@ -29,11 +30,6 @@ const ExtensionState = {
|
||||
UNINSTALLED: 99
|
||||
};
|
||||
|
||||
const ExtensionType = {
|
||||
SYSTEM: 1,
|
||||
PER_USER: 2
|
||||
};
|
||||
|
||||
const REPOSITORY_URL_BASE = 'https://extensions.gnome.org';
|
||||
const REPOSITORY_URL_DOWNLOAD = REPOSITORY_URL_BASE + '/download-extension/%s.shell-extension.zip';
|
||||
const REPOSITORY_URL_INFO = REPOSITORY_URL_BASE + '/extension-info/';
|
||||
@ -57,19 +53,10 @@ function _getCertFile() {
|
||||
|
||||
_httpSession.ssl_ca_file = _getCertFile();
|
||||
|
||||
// Maps uuid -> metadata object
|
||||
const extensionMeta = {};
|
||||
// Maps uuid -> importer object (extension directory tree)
|
||||
const extensions = {};
|
||||
// Maps uuid -> extension state object (returned from init())
|
||||
const extensionStateObjs = {};
|
||||
// Contains the order that extensions were enabled in.
|
||||
const extensionOrder = [];
|
||||
|
||||
// Arrays of uuids
|
||||
var enabledExtensions;
|
||||
// GFile for user extensions
|
||||
var userExtensionsDir = null;
|
||||
// Contains the order that extensions were enabled in.
|
||||
const extensionOrder = [];
|
||||
|
||||
// We don't really have a class to add signals on. So, create
|
||||
// a simple dummy object, add the signal methods, and export those
|
||||
@ -80,41 +67,8 @@ Signals.addSignalMethods(_signals);
|
||||
const connect = Lang.bind(_signals, _signals.connect);
|
||||
const disconnect = Lang.bind(_signals, _signals.disconnect);
|
||||
|
||||
// UUID => Array of error messages
|
||||
var errors = {};
|
||||
|
||||
const ENABLED_EXTENSIONS_KEY = 'enabled-extensions';
|
||||
|
||||
/**
|
||||
* versionCheck:
|
||||
* @required: an array of versions we're compatible with
|
||||
* @current: the version we have
|
||||
*
|
||||
* Check if a component is compatible for an extension.
|
||||
* @required is an array, and at least one version must match.
|
||||
* @current must be in the format <major>.<minor>.<point>.<micro>
|
||||
* <micro> is always ignored
|
||||
* <point> is ignored if <minor> is even (so you can target the
|
||||
* whole stable release)
|
||||
* <minor> and <major> must match
|
||||
* Each target version must be at least <major> and <minor>
|
||||
*/
|
||||
function versionCheck(required, current) {
|
||||
let currentArray = current.split('.');
|
||||
let major = currentArray[0];
|
||||
let minor = currentArray[1];
|
||||
let point = currentArray[2];
|
||||
for (let i = 0; i < required.length; i++) {
|
||||
let requiredArray = required[i].split('.');
|
||||
if (requiredArray[0] == major &&
|
||||
requiredArray[1] == minor &&
|
||||
(requiredArray[2] == point ||
|
||||
(requiredArray[2] == undefined && parseInt(minor) % 2 == 0)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function installExtensionFromUUID(uuid, version_tag) {
|
||||
let params = { uuid: uuid,
|
||||
version_tag: version_tag,
|
||||
@ -132,8 +86,8 @@ function installExtensionFromUUID(uuid, version_tag) {
|
||||
}
|
||||
|
||||
function uninstallExtensionFromUUID(uuid) {
|
||||
let meta = extensionMeta[uuid];
|
||||
if (!meta)
|
||||
let extension = ExtensionUtils.extensions[uuid];
|
||||
if (!extension)
|
||||
return false;
|
||||
|
||||
// Try to disable it -- if it's ERROR'd, we can't guarantee that,
|
||||
@ -142,22 +96,17 @@ function uninstallExtensionFromUUID(uuid) {
|
||||
disableExtension(uuid);
|
||||
|
||||
// Don't try to uninstall system extensions
|
||||
if (meta.type != ExtensionType.PER_USER)
|
||||
if (extension.type != ExtensionUtils.ExtensionType.PER_USER)
|
||||
return false;
|
||||
|
||||
meta.state = ExtensionState.UNINSTALLED;
|
||||
_signals.emit('extension-state-changed', meta);
|
||||
|
||||
delete extensionMeta[uuid];
|
||||
|
||||
// Importers are marked as PERMANENT, so we can't do this.
|
||||
// delete extensions[uuid];
|
||||
extensions[uuid] = undefined;
|
||||
extension.state = ExtensionState.UNINSTALLED;
|
||||
_signals.emit('extension-state-changed', extension);
|
||||
|
||||
delete ExtensionUtils.extensions[uuid];
|
||||
delete extensionStateObjs[uuid];
|
||||
delete errors[uuid];
|
||||
|
||||
FileUtils.recursivelyDeleteDir(Gio.file_new_for_path(meta.path));
|
||||
FileUtils.recursivelyDeleteDir(Gio.file_new_for_path(extension.path));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -178,7 +127,7 @@ function gotExtensionZipFile(session, message, uuid) {
|
||||
}
|
||||
|
||||
let stream = new Gio.UnixOutputStream({ fd: fd });
|
||||
let dir = userExtensionsDir.get_child(uuid);
|
||||
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,
|
||||
@ -202,16 +151,16 @@ function gotExtensionZipFile(session, message, uuid) {
|
||||
global.settings.set_strv(ENABLED_EXTENSIONS_KEY, enabledExtensions);
|
||||
}
|
||||
|
||||
loadExtension(dir, true, ExtensionType.PER_USER);
|
||||
loadExtension(dir, ExtensionUtils.ExtensionType.PER_USER, true);
|
||||
});
|
||||
}
|
||||
|
||||
function disableExtension(uuid) {
|
||||
let meta = extensionMeta[uuid];
|
||||
if (!meta)
|
||||
let extension = ExtensionUtils.extensions[uuid];
|
||||
if (!extension)
|
||||
return;
|
||||
|
||||
if (meta.state != ExtensionState.ENABLED)
|
||||
if (extension.state != ExtensionState.ENABLED)
|
||||
return;
|
||||
|
||||
let extensionState = extensionStateObjs[uuid];
|
||||
@ -255,41 +204,45 @@ function disableExtension(uuid) {
|
||||
|
||||
extensionOrder.splice(orderIdx, 1);
|
||||
|
||||
meta.state = ExtensionState.DISABLED;
|
||||
_signals.emit('extension-state-changed', meta);
|
||||
extension.state = ExtensionState.DISABLED;
|
||||
_signals.emit('extension-state-changed', extension);
|
||||
}
|
||||
|
||||
function enableExtension(uuid) {
|
||||
let meta = extensionMeta[uuid];
|
||||
if (!meta)
|
||||
let extension = ExtensionUtils.extensions[uuid];
|
||||
if (!extension)
|
||||
return;
|
||||
|
||||
if (meta.state == ExtensionState.INITIALIZED) {
|
||||
loadExtension(meta.dir, meta.type, true);
|
||||
if (extension.state == ExtensionState.INITIALIZED) {
|
||||
loadExtension(extension.dir, extension.type, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (meta.state != ExtensionState.DISABLED)
|
||||
if (extension.state != ExtensionState.DISABLED)
|
||||
return;
|
||||
|
||||
let extensionState = extensionStateObjs[uuid];
|
||||
|
||||
extensionOrder.push(uuid);
|
||||
|
||||
try {
|
||||
extensionState.enable();
|
||||
extension.stateObj.enable();
|
||||
} catch(e) {
|
||||
logExtensionError(uuid, e.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
meta.state = ExtensionState.ENABLED;
|
||||
_signals.emit('extension-state-changed', meta);
|
||||
extension.state = ExtensionState.ENABLED;
|
||||
_signals.emit('extension-state-changed', extension);
|
||||
}
|
||||
|
||||
function logExtensionError(uuid, message, state) {
|
||||
if (!errors[uuid]) errors[uuid] = [];
|
||||
errors[uuid].push(message);
|
||||
let extension = ExtensionUtils.extensions[uuid];
|
||||
if (!extension)
|
||||
return;
|
||||
|
||||
if (!extension.errors)
|
||||
extension.errors = [];
|
||||
|
||||
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,
|
||||
@ -298,72 +251,31 @@ function logExtensionError(uuid, message, state) {
|
||||
}
|
||||
|
||||
function loadExtension(dir, type, enabled) {
|
||||
let info;
|
||||
let uuid = dir.get_basename();
|
||||
let extension;
|
||||
|
||||
let metadataFile = dir.get_child('metadata.json');
|
||||
if (!metadataFile.query_exists(null)) {
|
||||
logExtensionError(uuid, 'Missing metadata.json');
|
||||
return;
|
||||
if (ExtensionUtils.extensions[uuid] != undefined) {
|
||||
throw new Error('extension already loaded');
|
||||
}
|
||||
|
||||
let metadataContents;
|
||||
try {
|
||||
metadataContents = Shell.get_file_contents_utf8_sync(metadataFile.get_path());
|
||||
} catch (e) {
|
||||
logExtensionError(uuid, 'Failed to load metadata.json: ' + e);
|
||||
extension = ExtensionUtils.createExtensionObject(uuid, dir, type);
|
||||
} catch(e) {
|
||||
logExtensionError(uuid, e.message);
|
||||
return;
|
||||
}
|
||||
let meta;
|
||||
try {
|
||||
meta = JSON.parse(metadataContents);
|
||||
} catch (e) {
|
||||
logExtensionError(uuid, 'Failed to parse metadata.json: ' + e);
|
||||
return;
|
||||
}
|
||||
|
||||
let requiredProperties = ['uuid', 'name', 'description', 'shell-version'];
|
||||
for (let i = 0; i < requiredProperties.length; i++) {
|
||||
let prop = requiredProperties[i];
|
||||
if (!meta[prop]) {
|
||||
logExtensionError(uuid, 'missing "' + prop + '" property in metadata.json');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (extensions[uuid] != undefined) {
|
||||
logExtensionError(uuid, 'extension already loaded');
|
||||
return;
|
||||
}
|
||||
|
||||
// Encourage people to add this
|
||||
if (!meta['url']) {
|
||||
global.log('Warning: Missing "url" property in metadata.json');
|
||||
}
|
||||
|
||||
if (uuid != meta.uuid) {
|
||||
logExtensionError(uuid, 'uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + uuid + '"');
|
||||
return;
|
||||
}
|
||||
|
||||
extensionMeta[uuid] = meta;
|
||||
meta.type = type;
|
||||
meta.dir = dir;
|
||||
meta.path = dir.get_path();
|
||||
meta.error = '';
|
||||
|
||||
// Default to error, we set success as the last step
|
||||
meta.state = ExtensionState.ERROR;
|
||||
extension.state = ExtensionState.ERROR;
|
||||
|
||||
if (!versionCheck(meta['shell-version'], Config.PACKAGE_VERSION) ||
|
||||
(meta['js-version'] && !versionCheck(meta['js-version'], Config.GJS_VERSION))) {
|
||||
if (ExtensionUtils.isOutOfDate(extension)) {
|
||||
logExtensionError(uuid, 'extension is not compatible with current GNOME Shell and/or GJS version', ExtensionState.OUT_OF_DATE);
|
||||
meta.state = ExtensionState.OUT_OF_DATE;
|
||||
extension.state = ExtensionState.OUT_OF_DATE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!enabled) {
|
||||
meta.state = ExtensionState.INITIALIZED;
|
||||
extension.state = ExtensionState.INITIALIZED;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -388,12 +300,12 @@ function loadExtension(dir, type, enabled) {
|
||||
let extensionModule;
|
||||
let extensionState = null;
|
||||
try {
|
||||
global.add_extension_importer('imports.ui.extensionSystem.extensions', meta.uuid, dir.get_path());
|
||||
extensionModule = extensions[meta.uuid].extension;
|
||||
ExtensionUtils.installImporter(extension);
|
||||
extensionModule = extension.imports.extension;
|
||||
} catch (e) {
|
||||
if (stylesheetPath != null)
|
||||
theme.unload_stylesheet(stylesheetPath);
|
||||
logExtensionError(uuid, e);
|
||||
logExtensionError(uuid, '' + e);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -403,7 +315,7 @@ function loadExtension(dir, type, enabled) {
|
||||
}
|
||||
|
||||
try {
|
||||
extensionState = extensionModule.init(meta);
|
||||
extensionState = extensionModule.init(extension);
|
||||
} catch (e) {
|
||||
if (stylesheetPath != null)
|
||||
theme.unload_stylesheet(stylesheetPath);
|
||||
@ -413,7 +325,7 @@ function loadExtension(dir, type, enabled) {
|
||||
|
||||
if (!extensionState)
|
||||
extensionState = extensionModule;
|
||||
extensionStateObjs[uuid] = extensionState;
|
||||
extension.stateObj = extensionState;
|
||||
|
||||
if (!extensionState.enable) {
|
||||
logExtensionError(uuid, 'missing \'enable\' function');
|
||||
@ -424,13 +336,13 @@ function loadExtension(dir, type, enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
meta.state = ExtensionState.DISABLED;
|
||||
extension.state = ExtensionState.DISABLED;
|
||||
|
||||
enableExtension(uuid);
|
||||
|
||||
_signals.emit('extension-loaded', meta.uuid);
|
||||
_signals.emit('extension-state-changed', meta);
|
||||
global.log('Loaded extension ' + meta.uuid);
|
||||
_signals.emit('extension-loaded', uuid);
|
||||
_signals.emit('extension-state-changed', extension);
|
||||
global.log('Loaded extension ' + uuid);
|
||||
}
|
||||
|
||||
function onEnabledExtensionsChanged() {
|
||||
@ -456,50 +368,17 @@ function onEnabledExtensionsChanged() {
|
||||
}
|
||||
|
||||
function init() {
|
||||
let userExtensionsPath = GLib.build_filenamev([global.userdatadir, 'extensions']);
|
||||
userExtensionsDir = Gio.file_new_for_path(userExtensionsPath);
|
||||
try {
|
||||
if (!userExtensionsDir.query_exists(null))
|
||||
userExtensionsDir.make_directory_with_parents(null);
|
||||
} catch (e) {
|
||||
global.logError('' + e);
|
||||
}
|
||||
ExtensionUtils.init();
|
||||
|
||||
global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged);
|
||||
enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
|
||||
}
|
||||
|
||||
function _loadExtensionsIn(dir, type) {
|
||||
let fileEnum;
|
||||
let file, info;
|
||||
try {
|
||||
fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
|
||||
} catch (e) {
|
||||
global.logError('' + e);
|
||||
return;
|
||||
}
|
||||
|
||||
while ((info = fileEnum.next_file(null)) != null) {
|
||||
let fileType = info.get_file_type();
|
||||
if (fileType != Gio.FileType.DIRECTORY)
|
||||
continue;
|
||||
let name = info.get_name();
|
||||
let child = dir.get_child(name);
|
||||
let enabled = enabledExtensions.indexOf(name) != -1;
|
||||
loadExtension(child, type, enabled);
|
||||
}
|
||||
fileEnum.close(null);
|
||||
}
|
||||
|
||||
function loadExtensions() {
|
||||
let systemDataDirs = GLib.get_system_data_dirs();
|
||||
for (let i = 0; i < systemDataDirs.length; i++) {
|
||||
let dirPath = systemDataDirs[i] + '/gnome-shell/extensions';
|
||||
let dir = Gio.file_new_for_path(dirPath);
|
||||
if (dir.query_exists(null))
|
||||
_loadExtensionsIn(dir, ExtensionType.SYSTEM);
|
||||
}
|
||||
_loadExtensionsIn(userExtensionsDir, ExtensionType.PER_USER);
|
||||
ExtensionUtils.scanExtensions(function(uuid, dir, type) {
|
||||
let enabled = enabledExtensions.indexOf(uuid) != -1;
|
||||
loadExtension(dir, type, enabled);
|
||||
});
|
||||
}
|
||||
|
||||
const InstallExtensionDialog = new Lang.Class({
|
||||
@ -545,13 +424,13 @@ const InstallExtensionDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
_onInstallButtonPressed: function(button, event) {
|
||||
let meta = { uuid: this._uuid,
|
||||
state: ExtensionState.DOWNLOADING,
|
||||
error: '' };
|
||||
let extension = { uuid: this._uuid,
|
||||
state: ExtensionState.DOWNLOADING,
|
||||
error: '' };
|
||||
|
||||
extensionMeta[this._uuid] = meta;
|
||||
ExtensionUtils.extensions[this._uuid] = extension;
|
||||
|
||||
_signals.emit('extension-state-changed', meta);
|
||||
_signals.emit('extension-state-changed', extension);
|
||||
|
||||
let params = { version_tag: this._version_tag,
|
||||
shell_version: Config.PACKAGE_VERSION,
|
||||
|
45
js/ui/flashspot.js
Normal file
45
js/ui/flashspot.js
Normal file
@ -0,0 +1,45 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Lang = imports.lang;
|
||||
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const FLASHSPOT_ANIMATION_TIME = 0.25; // seconds
|
||||
|
||||
const Flashspot = new Lang.Class({
|
||||
Name: 'Flashspot',
|
||||
Extends: Lightbox.Lightbox,
|
||||
|
||||
_init: function(area) {
|
||||
this.parent(Main.uiGroup, { inhibitEvents: true,
|
||||
width: area.width,
|
||||
height: area.height });
|
||||
|
||||
this.actor.style_class = 'flashspot';
|
||||
this.actor.set_position(area.x, area.y);
|
||||
},
|
||||
|
||||
fire: function() {
|
||||
this.actor.opacity = 0;
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 255,
|
||||
time: FLASHSPOT_ANIMATION_TIME,
|
||||
transition: 'linear',
|
||||
onComplete: Lang.bind(this, this._onFireShowComplete)
|
||||
});
|
||||
this.actor.show();
|
||||
},
|
||||
|
||||
_onFireShowComplete: function() {
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 0,
|
||||
time: FLASHSPOT_ANIMATION_TIME,
|
||||
transition: 'linear',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this.destroy();
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
@ -35,7 +35,8 @@ const BaseIcon = new Lang.Class({
|
||||
this.actor.set_child(box);
|
||||
|
||||
this.iconSize = ICON_SIZE;
|
||||
this._iconBin = new St.Bin();
|
||||
this._iconBin = new St.Bin({ x_align: St.Align.MIDDLE,
|
||||
y_align: St.Align.MIDDLE });
|
||||
|
||||
box.add_actor(this._iconBin);
|
||||
|
||||
@ -125,12 +126,12 @@ const BaseIcon = new Lang.Class({
|
||||
this.iconSize = size;
|
||||
this.icon = this.createIcon(this.iconSize);
|
||||
|
||||
this._iconBin.child = this.icon;
|
||||
|
||||
// The icon returned by createIcon() might actually be smaller than
|
||||
// the requested icon size (for instance StTextureCache does this
|
||||
// for fallback icons), so set the size explicitly.
|
||||
this.icon.set_size(this.iconSize, this.iconSize);
|
||||
|
||||
this._iconBin.child = this.icon;
|
||||
this._iconBin.set_size(this.iconSize, this.iconSize);
|
||||
},
|
||||
|
||||
_onStyleChanged: function() {
|
||||
@ -145,6 +146,11 @@ const BaseIcon = new Lang.Class({
|
||||
size = found ? len : ICON_SIZE;
|
||||
}
|
||||
|
||||
// don't create icons unnecessarily
|
||||
if (size == this.iconSize &&
|
||||
this._iconBin.child)
|
||||
return;
|
||||
|
||||
this._createIconTexture(size);
|
||||
}
|
||||
});
|
||||
@ -299,7 +305,7 @@ const IconGrid = new Lang.Class({
|
||||
_onStyleChanged: function() {
|
||||
let themeNode = this.actor.get_theme_node();
|
||||
this._spacing = themeNode.get_length('spacing');
|
||||
this._item_size = themeNode.get_length('-shell-grid-item-size');
|
||||
this._item_size = themeNode.get_length('-shell-grid-item-size') || ICON_SIZE;
|
||||
this._grid.queue_relayout();
|
||||
},
|
||||
|
||||
|
@ -433,9 +433,9 @@ const HotCorner = new Lang.Class({
|
||||
Lang.bind(this, this._onCornerLeft));
|
||||
|
||||
// Cache the three ripples instead of dynamically creating and destroying them.
|
||||
this._ripple1 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0 });
|
||||
this._ripple2 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0 });
|
||||
this._ripple3 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0 });
|
||||
this._ripple1 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0, visible: false });
|
||||
this._ripple2 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0, visible: false });
|
||||
this._ripple3 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0, visible: false });
|
||||
|
||||
Main.uiGroup.add_actor(this._ripple1);
|
||||
Main.uiGroup.add_actor(this._ripple2);
|
||||
|
@ -15,6 +15,7 @@ const Mainloop = imports.mainloop;
|
||||
|
||||
const History = imports.misc.history;
|
||||
const ExtensionSystem = imports.ui.extensionSystem;
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Link = imports.ui.link;
|
||||
const ShellEntry = imports.ui.shellEntry;
|
||||
const Tweener = imports.ui.tweener;
|
||||
@ -728,7 +729,7 @@ const Extensions = new Lang.Class({
|
||||
this._extensionsList.add(this._noExtensions);
|
||||
this.actor.add(this._extensionsList);
|
||||
|
||||
for (let uuid in ExtensionSystem.extensionMeta)
|
||||
for (let uuid in ExtensionUtils.extensions)
|
||||
this._loadExtension(null, uuid);
|
||||
|
||||
ExtensionSystem.connect('extension-loaded',
|
||||
@ -736,10 +737,10 @@ const Extensions = new Lang.Class({
|
||||
},
|
||||
|
||||
_loadExtension: function(o, uuid) {
|
||||
let extension = ExtensionSystem.extensionMeta[uuid];
|
||||
let extension = ExtensionUtils.extensions[uuid];
|
||||
// There can be cases where we create dummy extension metadata
|
||||
// that's not really a proper extension. Don't bother with these.
|
||||
if (!extension.name)
|
||||
if (!extension.metadata.name)
|
||||
return;
|
||||
|
||||
let extensionDisplay = this._createExtensionDisplay(extension);
|
||||
@ -751,25 +752,24 @@ const Extensions = new Lang.Class({
|
||||
},
|
||||
|
||||
_onViewSource: function (actor) {
|
||||
let meta = actor._extensionMeta;
|
||||
let file = Gio.file_new_for_path(meta.path);
|
||||
let uri = file.get_uri();
|
||||
let extension = actor._extension;
|
||||
let uri = extension.dir.get_uri();
|
||||
Gio.app_info_launch_default_for_uri(uri, global.create_app_launch_context());
|
||||
Main.lookingGlass.close();
|
||||
},
|
||||
|
||||
_onWebPage: function (actor) {
|
||||
let meta = actor._extensionMeta;
|
||||
Gio.app_info_launch_default_for_uri(meta.url, global.create_app_launch_context());
|
||||
let extension = actor._extension;
|
||||
Gio.app_info_launch_default_for_uri(extension.metadata.url, global.create_app_launch_context());
|
||||
Main.lookingGlass.close();
|
||||
},
|
||||
|
||||
_onViewErrors: function (actor) {
|
||||
let meta = actor._extensionMeta;
|
||||
let extension = actor._extension;
|
||||
let shouldShow = !actor._isShowing;
|
||||
|
||||
if (shouldShow) {
|
||||
let errors = ExtensionSystem.errors[meta.uuid];
|
||||
let errors = extension.errors;
|
||||
let errorDisplay = new St.BoxLayout({ vertical: true });
|
||||
if (errors && errors.length) {
|
||||
for (let i = 0; i < errors.length; i ++)
|
||||
@ -809,36 +809,36 @@ const Extensions = new Lang.Class({
|
||||
return 'Unknown'; // Not translated, shouldn't appear
|
||||
},
|
||||
|
||||
_createExtensionDisplay: function(meta) {
|
||||
_createExtensionDisplay: function(extension) {
|
||||
let box = new St.BoxLayout({ style_class: 'lg-extension', vertical: true });
|
||||
let name = new St.Label({ style_class: 'lg-extension-name',
|
||||
text: meta.name });
|
||||
text: extension.metadata.name });
|
||||
box.add(name, { expand: true });
|
||||
let description = new St.Label({ style_class: 'lg-extension-description',
|
||||
text: meta.description || 'No description' });
|
||||
text: extension.metadata.description || 'No description' });
|
||||
box.add(description, { expand: true });
|
||||
|
||||
let metaBox = new St.BoxLayout({ style_class: 'lg-extension-meta' });
|
||||
box.add(metaBox);
|
||||
let stateString = this._stateToString(meta.state);
|
||||
let stateString = this._stateToString(extension.state);
|
||||
let state = new St.Label({ style_class: 'lg-extension-state',
|
||||
text: this._stateToString(meta.state) });
|
||||
text: this._stateToString(extension.state) });
|
||||
metaBox.add(state);
|
||||
|
||||
let viewsource = new Link.Link({ label: _("View Source") });
|
||||
viewsource.actor._extensionMeta = meta;
|
||||
viewsource.actor._extension = extension;
|
||||
viewsource.actor.connect('clicked', Lang.bind(this, this._onViewSource));
|
||||
metaBox.add(viewsource.actor);
|
||||
|
||||
if (meta.url) {
|
||||
if (extension.metadata.url) {
|
||||
let webpage = new Link.Link({ label: _("Web Page") });
|
||||
webpage.actor._extensionMeta = meta;
|
||||
webpage.actor._extension = extension;
|
||||
webpage.actor.connect('clicked', Lang.bind(this, this._onWebPage));
|
||||
metaBox.add(webpage.actor);
|
||||
}
|
||||
|
||||
let viewerrors = new Link.Link({ label: _("Show Errors") });
|
||||
viewerrors.actor._extensionMeta = meta;
|
||||
viewerrors.actor._extension = extension;
|
||||
viewerrors.actor._parentBox = box;
|
||||
viewerrors.actor._isShowing = false;
|
||||
viewerrors.actor.connect('clicked', Lang.bind(this, this._onViewErrors));
|
||||
|
@ -44,7 +44,7 @@ const Magnifier = new Lang.Class({
|
||||
this._zoomRegions = [];
|
||||
|
||||
// Create small clutter tree for the magnified mouse.
|
||||
let xfixesCursor = Shell.XFixesCursor.get_default();
|
||||
let xfixesCursor = Shell.XFixesCursor.get_for_stage(global.stage);
|
||||
this._mouseSprite = new Clutter.Texture();
|
||||
xfixesCursor.update_texture_image(this._mouseSprite);
|
||||
this._cursorRoot = new Clutter.Group();
|
||||
@ -556,6 +556,7 @@ const ZoomRegion = new Lang.Class({
|
||||
this._screenPosition = GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN;
|
||||
|
||||
this._magView = null;
|
||||
this._background = null;
|
||||
this._uiGroupClone = null;
|
||||
this._mouseSourceActor = mouseSourceActor;
|
||||
this._mouseActor = null;
|
||||
@ -565,12 +566,15 @@ const ZoomRegion = new Lang.Class({
|
||||
this._viewPortX = 0;
|
||||
this._viewPortY = 0;
|
||||
this._viewPortWidth = global.screen_width;
|
||||
this._viewPortWidth = global.screen_height;
|
||||
this._viewPortHeight = global.screen_height;
|
||||
this._xCenter = this._viewPortWidth / 2;
|
||||
this._yCenter = this._viewPortHeight / 2;
|
||||
this._xMagFactor = 1;
|
||||
this._yMagFactor = 1;
|
||||
this._followingCursor = false;
|
||||
|
||||
Main.layoutManager.connect('monitors-changed',
|
||||
Lang.bind(this, this._monitorsChanged));
|
||||
},
|
||||
|
||||
/**
|
||||
@ -891,15 +895,15 @@ const ZoomRegion = new Lang.Class({
|
||||
|
||||
// Add a background for when the magnified uiGroup is scrolled
|
||||
// out of view (don't want to see desktop showing through).
|
||||
let background = new Clutter.Rectangle({ color: Main.DEFAULT_BACKGROUND_COLOR });
|
||||
mainGroup.add_actor(background);
|
||||
this._background = new Clutter.Rectangle({ color: Main.DEFAULT_BACKGROUND_COLOR });
|
||||
mainGroup.add_actor(this._background);
|
||||
|
||||
// Clone the group that contains all of UI on the screen. This is the
|
||||
// chrome, the windows, etc.
|
||||
this._uiGroupClone = new Clutter.Clone({ source: Main.uiGroup });
|
||||
mainGroup.add_actor(this._uiGroupClone);
|
||||
Main.uiGroup.set_size(global.screen_width, global.screen_height);
|
||||
background.set_size(global.screen_width, global.screen_height);
|
||||
this._background.set_size(global.screen_width, global.screen_height);
|
||||
|
||||
// Add either the given mouseSourceActor to the ZoomRegion, or a clone of
|
||||
// it.
|
||||
@ -923,6 +927,7 @@ const ZoomRegion = new Lang.Class({
|
||||
|
||||
this._magView.destroy();
|
||||
this._magView = null;
|
||||
this._background = null;
|
||||
this._uiGroupClone = null;
|
||||
this._mouseActor = null;
|
||||
this._crossHairsActor = null;
|
||||
@ -1145,6 +1150,22 @@ const ZoomRegion = new Lang.Class({
|
||||
this._crossHairsActor.set_position(xMagMouse - groupWidth / 2,
|
||||
yMagMouse - groupHeight / 2);
|
||||
}
|
||||
},
|
||||
|
||||
_monitorsChanged: function() {
|
||||
if (!this.isActive())
|
||||
return;
|
||||
|
||||
Main.uiGroup.set_size(global.screen_width, global.screen_height);
|
||||
this._background.set_size(global.screen_width, global.screen_height);
|
||||
|
||||
if (this._screenPosition == GDesktopEnums.MagnifierScreenPosition.NONE)
|
||||
this._setViewPort({ x: this._viewPortX,
|
||||
y: this._viewPortY,
|
||||
width: this._viewPortWidth,
|
||||
height: this._viewPortHeight });
|
||||
else
|
||||
this.setScreenPosition(this._screenPosition);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1175,6 +1196,14 @@ const Crosshairs = new Lang.Class({
|
||||
this._clipSize = [0, 0];
|
||||
this._clones = [];
|
||||
this.reCenter();
|
||||
|
||||
Main.layoutManager.connect('monitors-changed',
|
||||
Lang.bind(this, this._monitorsChanged));
|
||||
},
|
||||
|
||||
_monitorsChanged: function() {
|
||||
this._actor.set_size(global.screen_width * 3, global.screen_height * 3);
|
||||
this.reCenter();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -332,6 +332,7 @@ function _windowRemoved(workspace, window) {
|
||||
workspace._lastRemovedWindow = null;
|
||||
_queueCheckWorkspaces();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1430,8 +1430,16 @@ const MessageTray = new Lang.Class({
|
||||
this._notificationRemoved = false;
|
||||
this._reNotifyAfterHideNotification = null;
|
||||
|
||||
this._corner = new Clutter.Rectangle({ width: 1,
|
||||
height: 1,
|
||||
opacity: 0,
|
||||
reactive: true });
|
||||
this._corner.connect('enter-event', Lang.bind(this, this._onCornerEnter));
|
||||
Main.layoutManager.trayBox.add_actor(this._corner);
|
||||
Main.layoutManager.trackChrome(this._corner);
|
||||
|
||||
Main.layoutManager.trayBox.add_actor(this.actor);
|
||||
this.actor.y = -1;
|
||||
this.actor.y = this.actor.height;
|
||||
Main.layoutManager.trackChrome(this.actor);
|
||||
Main.layoutManager.trackChrome(this._notificationBin);
|
||||
|
||||
@ -1470,12 +1478,24 @@ const MessageTray = new Lang.Class({
|
||||
this._chatSummaryItemsCount = 0;
|
||||
},
|
||||
|
||||
_onCornerEnter: function(actor, event) {
|
||||
this._pointerInSummary = true;
|
||||
this._updateState();
|
||||
},
|
||||
|
||||
_setSizePosition: function() {
|
||||
let monitor = Main.layoutManager.bottomMonitor;
|
||||
this._notificationBin.x = 0;
|
||||
this._notificationBin.width = monitor.width;
|
||||
this._summaryBin.x = 0;
|
||||
this._summaryBin.width = monitor.width;
|
||||
|
||||
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
|
||||
this._corner.x = 0;
|
||||
else
|
||||
this._corner.x = Main.layoutManager.trayBox.width - 1;
|
||||
|
||||
this._corner.y = Main.layoutManager.trayBox.height - 1;
|
||||
},
|
||||
|
||||
contains: function(source) {
|
||||
@ -2028,8 +2048,11 @@ const MessageTray = new Lang.Class({
|
||||
if (haveClickedSummaryItem && !summarySourceIsMainNotificationSource && canShowSummaryBoxPointer && !requestedNotificationStackIsEmpty)
|
||||
this._showSummaryBoxPointer();
|
||||
} else if (this._summaryBoxPointerState == State.SHOWN) {
|
||||
if (!haveClickedSummaryItem || !canShowSummaryBoxPointer || wrongSummaryBoxPointer || mustHideSummary)
|
||||
if (!haveClickedSummaryItem || !canShowSummaryBoxPointer || wrongSummaryBoxPointer || mustHideSummary) {
|
||||
this._hideSummaryBoxPointer();
|
||||
if (wrongSummaryBoxPointer)
|
||||
this._showSummaryBoxPointer();
|
||||
}
|
||||
}
|
||||
|
||||
// Tray itself
|
||||
@ -2076,7 +2099,7 @@ const MessageTray = new Lang.Class({
|
||||
|
||||
_hideTray: function() {
|
||||
this._tween(this.actor, '_trayState', State.HIDDEN,
|
||||
{ y: -1,
|
||||
{ y: this.actor.height,
|
||||
time: ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
@ -2370,9 +2393,8 @@ const MessageTray = new Lang.Class({
|
||||
}
|
||||
|
||||
this._summaryBoxPointerState = State.HIDING;
|
||||
// Unset this._clickedSummaryItem if we are no longer showing the summary or if
|
||||
// this._clickedSummaryItem is still the item associated with the currently showing box pointer
|
||||
if (this._summaryState != State.SHOWN || this._summaryBoxPointerItem == this._clickedSummaryItem)
|
||||
// Unset this._clickedSummaryItem if we are no longer showing the summary
|
||||
if (this._summaryState != State.SHOWN)
|
||||
this._unsetClickedSummaryItem();
|
||||
|
||||
this._focusGrabber.ungrabFocus();
|
||||
|
@ -94,7 +94,7 @@ const NotificationDaemon = new Lang.Class({
|
||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(NotificationDaemonIface, this);
|
||||
this._dbusImpl.export(Gio.DBus.session, '/org/freedesktop/Notifications');
|
||||
|
||||
this._sources = {};
|
||||
this._sources = [];
|
||||
this._senderToPid = {};
|
||||
this._notifications = {};
|
||||
this._busProxy = new Bus();
|
||||
@ -152,14 +152,30 @@ const NotificationDaemon = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_lookupSource: function(title, pid, trayIcon) {
|
||||
for (let i = 0; i < this._sources.length; i++) {
|
||||
let source = this._sources[i];
|
||||
if (source.pid == pid &&
|
||||
(source.initialTitle == title || source.trayIcon || trayIcon))
|
||||
return source;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
// Returns the source associated with ndata.notification if it is set.
|
||||
// Otherwise, returns the source associated with the pid if one is
|
||||
// stored in this._sources and the notification is not transient.
|
||||
// Otherwise, creates a new source as long as pid is provided.
|
||||
// Otherwise, returns the source associated with the title and pid if
|
||||
// such source is stored in this._sources and the notification is not
|
||||
// transient. If the existing or requested source is associated with
|
||||
// a tray icon and passed in pid matches a pid of an existing source,
|
||||
// the title match is ignored to enable representing a tray icon and
|
||||
// notifications from the same application with a single source.
|
||||
//
|
||||
// If no existing source is found, a new source is created as long as
|
||||
// pid is provided.
|
||||
//
|
||||
// Either a pid or ndata.notification is needed to retrieve or
|
||||
// create a source.
|
||||
_getSource: function(title, pid, ndata, sender) {
|
||||
_getSource: function(title, pid, ndata, sender, trayIcon) {
|
||||
if (!pid && !(ndata && ndata.notification))
|
||||
return null;
|
||||
|
||||
@ -176,20 +192,24 @@ const NotificationDaemon = new Lang.Class({
|
||||
// with a transient one from the same sender, so we
|
||||
// always create a new source object for new transient notifications
|
||||
// and never add it to this._sources .
|
||||
if (!isForTransientNotification && this._sources[pid]) {
|
||||
let source = this._sources[pid];
|
||||
source.setTitle(title);
|
||||
return source;
|
||||
if (!isForTransientNotification) {
|
||||
let source = this._lookupSource(title, pid, trayIcon);
|
||||
if (source) {
|
||||
source.setTitle(title);
|
||||
return source;
|
||||
}
|
||||
}
|
||||
|
||||
let source = new Source(title, pid, sender);
|
||||
let source = new Source(title, pid, sender, trayIcon);
|
||||
source.setTransient(isForTransientNotification);
|
||||
|
||||
if (!isForTransientNotification) {
|
||||
this._sources[pid] = source;
|
||||
this._sources.push(source);
|
||||
source.connect('destroy', Lang.bind(this,
|
||||
function() {
|
||||
delete this._sources[pid];
|
||||
let index = this._sources.indexOf(source);
|
||||
if (index >= 0)
|
||||
this._sources.splice(index, 1);
|
||||
}));
|
||||
}
|
||||
|
||||
@ -268,7 +288,7 @@ const NotificationDaemon = new Lang.Class({
|
||||
let sender = invocation.get_sender();
|
||||
let pid = this._senderToPid[sender];
|
||||
|
||||
let source = this._getSource(appName, pid, ndata, sender);
|
||||
let source = this._getSource(appName, pid, ndata, sender, null);
|
||||
|
||||
if (source) {
|
||||
this._notifyForSource(source, ndata);
|
||||
@ -294,7 +314,7 @@ const NotificationDaemon = new Lang.Class({
|
||||
}
|
||||
|
||||
let [pid] = result;
|
||||
source = this._getSource(appName, pid, ndata, sender);
|
||||
source = this._getSource(appName, pid, ndata, sender, null);
|
||||
|
||||
// We only store sender-pid entries for persistent sources.
|
||||
// Removing the entries once the source is destroyed
|
||||
@ -443,8 +463,8 @@ const NotificationDaemon = new Lang.Class({
|
||||
if (!tracker.focus_app)
|
||||
return;
|
||||
|
||||
for (let id in this._sources) {
|
||||
let source = this._sources[id];
|
||||
for (let i = 0; i < this._sources.length; i++) {
|
||||
let source = this._sources[i];
|
||||
if (source.app == tracker.focus_app) {
|
||||
source.destroyNonResidentNotifications();
|
||||
return;
|
||||
@ -463,12 +483,11 @@ const NotificationDaemon = new Lang.Class({
|
||||
},
|
||||
|
||||
_onTrayIconAdded: function(o, icon) {
|
||||
let source = this._getSource(icon.title || icon.wm_class || _("Unknown"), icon.pid, null, null);
|
||||
source.setTrayIcon(icon);
|
||||
let source = this._getSource(icon.title || icon.wm_class || _("Unknown"), icon.pid, null, null, icon);
|
||||
},
|
||||
|
||||
_onTrayIconRemoved: function(o, icon) {
|
||||
let source = this._sources[icon.pid];
|
||||
let source = this._lookupSource(null, icon.pid, true);
|
||||
if (source)
|
||||
source.destroy();
|
||||
}
|
||||
@ -478,10 +497,12 @@ const Source = new Lang.Class({
|
||||
Name: 'NotificationDaemonSource',
|
||||
Extends: MessageTray.Source,
|
||||
|
||||
_init: function(title, pid, sender) {
|
||||
_init: function(title, pid, sender, trayIcon) {
|
||||
this.parent(title);
|
||||
|
||||
this._pid = pid;
|
||||
this.initialTitle = title;
|
||||
|
||||
this.pid = pid;
|
||||
if (sender)
|
||||
this._nameWatcherId = Gio.DBus.session.watch_name(sender,
|
||||
Gio.BusNameWatcherFlags.NONE,
|
||||
@ -495,7 +516,12 @@ const Source = new Lang.Class({
|
||||
this.title = this.app.get_name();
|
||||
else
|
||||
this.useNotificationIcon = true;
|
||||
this._trayIcon = null;
|
||||
|
||||
this.trayIcon = trayIcon;
|
||||
if (this.trayIcon) {
|
||||
this._setSummaryIcon(this.trayIcon);
|
||||
this.useNotificationIcon = false;
|
||||
}
|
||||
},
|
||||
|
||||
_onNameVanished: function() {
|
||||
@ -522,7 +548,7 @@ const Source = new Lang.Class({
|
||||
},
|
||||
|
||||
handleSummaryClick: function() {
|
||||
if (!this._trayIcon)
|
||||
if (!this.trayIcon)
|
||||
return false;
|
||||
|
||||
let event = Clutter.get_current_event();
|
||||
@ -543,11 +569,11 @@ const Source = new Lang.Class({
|
||||
let id = global.connect('notify::stage-input-mode', Lang.bind(this,
|
||||
function () {
|
||||
global.disconnect(id);
|
||||
this._trayIcon.click(event);
|
||||
this.trayIcon.click(event);
|
||||
}));
|
||||
Main.overview.hide();
|
||||
} else {
|
||||
this._trayIcon.click(event);
|
||||
this.trayIcon.click(event);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
@ -556,31 +582,25 @@ const Source = new Lang.Class({
|
||||
if (this.app)
|
||||
return;
|
||||
|
||||
this.app = Shell.WindowTracker.get_default().get_app_from_pid(this._pid);
|
||||
this.app = Shell.WindowTracker.get_default().get_app_from_pid(this.pid);
|
||||
if (!this.app)
|
||||
return;
|
||||
|
||||
// Only override the icon if we were previously using
|
||||
// notification-based icons (ie, not a trayicon) or if it was unset before
|
||||
if (!this._trayIcon) {
|
||||
if (!this.trayIcon) {
|
||||
this.useNotificationIcon = false;
|
||||
this._setSummaryIcon(this.app.create_icon_texture (this.ICON_SIZE));
|
||||
}
|
||||
},
|
||||
|
||||
setTrayIcon: function(icon) {
|
||||
this._setSummaryIcon(icon);
|
||||
this.useNotificationIcon = false;
|
||||
this._trayIcon = icon;
|
||||
},
|
||||
|
||||
open: function(notification) {
|
||||
this.destroyNonResidentNotifications();
|
||||
this.openApp();
|
||||
},
|
||||
|
||||
_lastNotificationRemoved: function() {
|
||||
if (!this._trayIcon)
|
||||
if (!this.trayIcon)
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
|
@ -23,6 +23,7 @@ const Params = imports.misc.params;
|
||||
const PlaceDisplay = imports.ui.placeDisplay;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const ViewSelector = imports.ui.viewSelector;
|
||||
const Wanda = imports.ui.wanda;
|
||||
const WorkspacesView = imports.ui.workspacesView;
|
||||
const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
|
||||
|
||||
@ -108,7 +109,6 @@ const Overview = new Lang.Class({
|
||||
if (this.isDummy) {
|
||||
this.animationInProgress = false;
|
||||
this.visible = false;
|
||||
this.workspaces = null;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -180,8 +180,6 @@ const Overview = new Lang.Class({
|
||||
this._lastActiveWorkspaceIndex = -1;
|
||||
this._lastHoveredWindow = null;
|
||||
this._needsFakePointerEvent = false;
|
||||
|
||||
this.workspaces = null;
|
||||
},
|
||||
|
||||
// The members we construct that are implemented in JS might
|
||||
@ -204,6 +202,8 @@ const Overview = new Lang.Class({
|
||||
this._viewSelector.addViewTab('applications', _("Applications"), appView.actor, 'system-run');
|
||||
|
||||
// Default search providers
|
||||
// Wanda comes obviously first
|
||||
this.addSearchProvider(new Wanda.WandaSearchProvider());
|
||||
this.addSearchProvider(new AppDisplay.AppSearchProvider());
|
||||
this.addSearchProvider(new AppDisplay.SettingsSearchProvider());
|
||||
this.addSearchProvider(new PlaceDisplay.PlaceSearchProvider());
|
||||
@ -587,13 +587,10 @@ const Overview = new Lang.Class({
|
||||
|
||||
this._workspacesDisplay.show();
|
||||
|
||||
this.workspaces = this._workspacesDisplay.workspacesView;
|
||||
global.overlay_group.add_actor(this.workspaces.actor);
|
||||
|
||||
if (!this._desktopFade.child)
|
||||
this._desktopFade.child = this._getDesktopClone();
|
||||
|
||||
if (!this.workspaces.getActiveWorkspace().hasMaximizedWindows()) {
|
||||
if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows()) {
|
||||
this._desktopFade.opacity = 255;
|
||||
this._desktopFade.show();
|
||||
Tweener.addTween(this._desktopFade,
|
||||
@ -728,7 +725,7 @@ const Overview = new Lang.Class({
|
||||
this.animationInProgress = true;
|
||||
this._hideInProgress = true;
|
||||
|
||||
if (!this.workspaces.getActiveWorkspace().hasMaximizedWindows()) {
|
||||
if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows()) {
|
||||
this._desktopFade.opacity = 0;
|
||||
this._desktopFade.show();
|
||||
Tweener.addTween(this._desktopFade,
|
||||
@ -737,7 +734,7 @@ const Overview = new Lang.Class({
|
||||
transition: 'easeOutQuad' });
|
||||
}
|
||||
|
||||
this.workspaces.hide();
|
||||
this._workspacesDisplay.zoomFromOverview();
|
||||
|
||||
// Make other elements fade out.
|
||||
Tweener.addTween(this._group,
|
||||
@ -779,9 +776,6 @@ const Overview = new Lang.Class({
|
||||
|
||||
global.window_group.show();
|
||||
|
||||
this.workspaces.destroy();
|
||||
this.workspaces = null;
|
||||
|
||||
this._workspacesDisplay.hide();
|
||||
|
||||
this._desktopFade.hide();
|
||||
|
@ -237,16 +237,19 @@ const AppMenuButton = new Lang.Class({
|
||||
Extends: PanelMenu.Button,
|
||||
|
||||
_init: function(menuManager) {
|
||||
this.parent(0.0, true);
|
||||
this.parent(0.0, null, true);
|
||||
|
||||
this._startingApps = [];
|
||||
|
||||
this._menuManager = menuManager;
|
||||
this._targetApp = null;
|
||||
this._appMenuNotifyId = 0;
|
||||
this._actionGroupNotifyId = 0;
|
||||
|
||||
let bin = new St.Bin({ name: 'appMenu' });
|
||||
this.actor.add_actor(bin);
|
||||
|
||||
this.actor.bind_property("reactive", this.actor, "can-focus", 0);
|
||||
this.actor.reactive = false;
|
||||
this._targetIsCurrent = false;
|
||||
|
||||
@ -495,6 +498,9 @@ const AppMenuButton = new Lang.Class({
|
||||
return;
|
||||
}
|
||||
|
||||
if (!targetApp.is_on_workspace(workspace))
|
||||
return;
|
||||
|
||||
if (!this._targetIsCurrent) {
|
||||
this.actor.reactive = true;
|
||||
this._targetIsCurrent = true;
|
||||
@ -519,10 +525,23 @@ const AppMenuButton = new Lang.Class({
|
||||
this._iconBox.hide();
|
||||
this._label.setText('');
|
||||
|
||||
if (this._appMenuNotifyId)
|
||||
this._targetApp.disconnect(this._appMenuNotifyId);
|
||||
if (this._actionGroupNotifyId)
|
||||
this._targetApp.disconnect(this._actionGroupNotifyId);
|
||||
if (targetApp) {
|
||||
this._appMenuNotifyId = targetApp.connect('notify::menu', Lang.bind(this, this._sync));
|
||||
this._actionGroupNotifyId = targetApp.connect('notify::action-group', Lang.bind(this, this._sync));
|
||||
} else {
|
||||
this._appMenuNotifyId = 0;
|
||||
this._actionGroupNotifyId = 0;
|
||||
}
|
||||
|
||||
this._targetApp = targetApp;
|
||||
let icon = targetApp.get_faded_icon(2 * PANEL_ICON_SIZE);
|
||||
|
||||
this._label.setText(targetApp.get_name());
|
||||
this.setName(targetApp.get_name());
|
||||
|
||||
this._iconBox.set_child(icon);
|
||||
this._iconBox.show();
|
||||
@ -538,7 +557,7 @@ const AppMenuButton = new Lang.Class({
|
||||
_maybeSetMenu: function() {
|
||||
let menu;
|
||||
|
||||
if (this._targetApp.action_group) {
|
||||
if (this._targetApp.action_group && this._targetApp.menu) {
|
||||
if (this.menu instanceof PopupMenu.RemoteMenu &&
|
||||
this.menu.actionGroup == this._targetApp.action_group)
|
||||
return;
|
||||
|
@ -96,7 +96,7 @@ const Button = new Lang.Class({
|
||||
Name: 'PanelMenuButton',
|
||||
Extends: ButtonBox,
|
||||
|
||||
_init: function(menuAlignment, dontCreateMenu) {
|
||||
_init: function(menuAlignment, nameText, dontCreateMenu) {
|
||||
this.parent({ reactive: true,
|
||||
can_focus: true,
|
||||
track_hover: true });
|
||||
@ -108,6 +108,24 @@ const Button = new Lang.Class({
|
||||
this.menu = null;
|
||||
else
|
||||
this.setMenu(new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP, 0));
|
||||
|
||||
this.setName(nameText);
|
||||
},
|
||||
|
||||
setName: function(text) {
|
||||
if (text != null) {
|
||||
// This is the easiest way to provide a accessible name to
|
||||
// this widget. The label could be also used for other
|
||||
// purposes in the future.
|
||||
if (!this.label) {
|
||||
this.label = new St.Label({ text: text });
|
||||
this.actor.label_actor = this.label;
|
||||
} else
|
||||
this.label.text = text;
|
||||
} else {
|
||||
this.label = null;
|
||||
this.actor.label_actor = null;
|
||||
}
|
||||
},
|
||||
|
||||
setMenu: function(menu) {
|
||||
@ -203,8 +221,8 @@ const SystemStatusButton = new Lang.Class({
|
||||
Name: 'SystemStatusButton',
|
||||
Extends: Button,
|
||||
|
||||
_init: function(iconName,tooltipText) {
|
||||
this.parent(0.0);
|
||||
_init: function(iconName, tooltipText, nameText) {
|
||||
this.parent(0.0, nameText);
|
||||
|
||||
this._iconActor = new St.Icon({ icon_name: iconName,
|
||||
icon_type: St.IconType.SYMBOLIC,
|
||||
|
@ -155,9 +155,12 @@ const PlacesManager = new Lang.Class({
|
||||
|
||||
this._connect = new PlaceInfo('special:connect', _("Connect to..."),
|
||||
function (size) {
|
||||
return new St.Icon({ icon_name: 'applications-internet',
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: size });
|
||||
// do NOT use St.Icon here, it crashes the shell
|
||||
// see wanda.js for details
|
||||
return St.TextureCache.get_default().load_icon_name(null,
|
||||
'applications-internet',
|
||||
St.IconType.FULLCOLOR,
|
||||
size);
|
||||
},
|
||||
function (params) {
|
||||
// BUG: nautilus-connect-server doesn't have a desktop file, so we can't
|
||||
|
@ -393,6 +393,7 @@ const AuthenticationAgent = new Lang.Class({
|
||||
Lang.bind(this,
|
||||
function() {
|
||||
this._reallyCompleteRequest(wasDismissed);
|
||||
return false;
|
||||
}));
|
||||
} else {
|
||||
this._reallyCompleteRequest(wasDismissed);
|
||||
|
@ -389,6 +389,7 @@ const PopupMenuItem = new Lang.Class({
|
||||
|
||||
this.label = new St.Label({ text: text });
|
||||
this.addActor(this.label);
|
||||
this.actor.label_actor = this.label
|
||||
}
|
||||
});
|
||||
|
||||
@ -773,6 +774,12 @@ const PopupSwitchMenuItem = new Lang.Class({
|
||||
this.toggle();
|
||||
}
|
||||
|
||||
// we allow pressing space to toggle the switch
|
||||
// without closing the menu
|
||||
if (event.type() == Clutter.EventType.KEY_PRESS &&
|
||||
event.get_key_symbol() == Clutter.KEY_space)
|
||||
return;
|
||||
|
||||
this.parent(event);
|
||||
},
|
||||
|
||||
@ -869,6 +876,10 @@ const PopupMenuBase = new Lang.Class({
|
||||
return menuItem;
|
||||
},
|
||||
|
||||
isEmpty: function() {
|
||||
return this.box.get_children().length == 0;
|
||||
},
|
||||
|
||||
isChildMenu: function(menu) {
|
||||
return this._childMenus.indexOf(menu) != -1;
|
||||
},
|
||||
@ -939,7 +950,12 @@ const PopupMenuBase = new Lang.Class({
|
||||
this.emit('activate', menuItem);
|
||||
this.close(true);
|
||||
}));
|
||||
menuItem.connect('destroy', Lang.bind(this, function(emitter) {
|
||||
// the weird name is to avoid a conflict with some random property
|
||||
// the menuItem may have, called destroyId
|
||||
// (FIXME: in the future it may make sense to have container objects
|
||||
// like PopupMenuManager does)
|
||||
menuItem._popupMenuDestroyId = menuItem.connect('destroy', Lang.bind(this, function(menuItem) {
|
||||
menuItem.disconnect(menuItem._popupMenuDestroyId);
|
||||
menuItem.disconnect(menuItem._activateId);
|
||||
menuItem.disconnect(menuItem._activeChangeId);
|
||||
menuItem.disconnect(menuItem._sensitiveChangeId);
|
||||
@ -1189,6 +1205,9 @@ const PopupMenu = new Lang.Class({
|
||||
if (this.isOpen)
|
||||
return;
|
||||
|
||||
if (this.isEmpty())
|
||||
return;
|
||||
|
||||
this.isOpen = true;
|
||||
|
||||
this._boxPointer.setPosition(this.sourceActor, this._arrowAlignment);
|
||||
@ -1280,6 +1299,9 @@ const PopupSubMenu = new Lang.Class({
|
||||
if (this.isOpen)
|
||||
return;
|
||||
|
||||
if (this.isEmpty())
|
||||
return;
|
||||
|
||||
this.isOpen = true;
|
||||
|
||||
this.actor.show();
|
||||
@ -1393,12 +1415,23 @@ const PopupMenuSection = new Lang.Class({
|
||||
this.actor = this.box;
|
||||
this.actor._delegate = this;
|
||||
this.isOpen = true;
|
||||
|
||||
// an array of externally managed separators
|
||||
this.separators = [];
|
||||
},
|
||||
|
||||
// deliberately ignore any attempt to open() or close(), but emit the
|
||||
// corresponding signal so children can still pick it up
|
||||
open: function(animate) { this.emit('open-state-changed', true); },
|
||||
close: function() { this.emit('open-state-changed', false); },
|
||||
|
||||
destroy: function() {
|
||||
for (let i = 0; i < this.separators.length; i++)
|
||||
this.separators[i].destroy();
|
||||
this.separators = [];
|
||||
|
||||
this.parent();
|
||||
}
|
||||
});
|
||||
|
||||
const PopupSubMenuMenuItem = new Lang.Class({
|
||||
@ -1412,6 +1445,7 @@ const PopupSubMenuMenuItem = new Lang.Class({
|
||||
|
||||
this.label = new St.Label({ text: text });
|
||||
this.addActor(this.label);
|
||||
this.actor.label_actor = this.label;
|
||||
this._triangle = new St.Label({ text: '\u25B8' });
|
||||
this.addActor(this._triangle, { align: St.Align.END });
|
||||
|
||||
@ -1512,6 +1546,9 @@ const PopupComboMenu = new Lang.Class({
|
||||
if (this.isOpen)
|
||||
return;
|
||||
|
||||
if (this.isEmpty())
|
||||
return;
|
||||
|
||||
this.isOpen = true;
|
||||
|
||||
let [sourceX, sourceY] = this.sourceActor.get_transformed_position();
|
||||
@ -1732,17 +1769,25 @@ const RemoteMenu = new Lang.Class({
|
||||
},
|
||||
|
||||
_createMenuItem: function(model, index) {
|
||||
let labelValue = model.get_item_attribute_value(index, Gio.MENU_ATTRIBUTE_LABEL, null);
|
||||
let label = labelValue ? labelValue.deep_unpack() : '';
|
||||
// remove all underscores that are not followed by another underscore
|
||||
label = label.replace(/_([^_])/, '$1');
|
||||
|
||||
let section_link = model.get_item_link(index, Gio.MENU_LINK_SECTION);
|
||||
if (section_link) {
|
||||
let item = new PopupMenuSection();
|
||||
if (label) {
|
||||
let title = new PopupMenuItem(label, { reactive: false,
|
||||
style_class: 'popup-subtitle-menu-item' });
|
||||
item._titleMenuItem = title;
|
||||
title._ignored = true;
|
||||
item.addMenuItem(title);
|
||||
}
|
||||
this._modelChanged(section_link, 0, 0, section_link.get_n_items(), item);
|
||||
return [item, true, ''];
|
||||
}
|
||||
|
||||
// labels are not checked for existance, as they're required for all items
|
||||
let label = model.get_item_attribute_value(index, Gio.MENU_ATTRIBUTE_LABEL, null).deep_unpack();
|
||||
// remove all underscores that are not followed by another underscore
|
||||
label = label.replace(/_([^_])/, '$1');
|
||||
let submenu_link = model.get_item_link(index, Gio.MENU_LINK_SUBMENU);
|
||||
|
||||
if (submenu_link) {
|
||||
@ -1777,15 +1822,7 @@ const RemoteMenu = new Lang.Class({
|
||||
item = new PopupSwitchMenuItem(label, action.state.get_boolean());
|
||||
action.items.push(item);
|
||||
specificSignalId = item.connect('toggled', Lang.bind(this, function(item) {
|
||||
this.actionGroup.change_action_state(action_id, GLib.Variant.new_boolean(item.state));
|
||||
}));
|
||||
break;
|
||||
case 'd':
|
||||
item = new PopupSliderMenuItem(label, action.state.get_double());
|
||||
action.items.push(item);
|
||||
// value-changed is emitted for each motion-event, maybe an idle is more appropriate here?
|
||||
specificSignalId = item.connect('value-changed', Lang.bind(this, function(item) {
|
||||
this.actionGroup.change_action_state(action_id, GLib.Variant.new_double(item.value));
|
||||
this.actionGroup.activate_action(action_id, null);
|
||||
}));
|
||||
break;
|
||||
case 's':
|
||||
@ -1794,7 +1831,7 @@ const RemoteMenu = new Lang.Class({
|
||||
action.items.push(item);
|
||||
item.setShowDot(action.state.deep_unpack() == item._remoteTarget);
|
||||
specificSignalId = item.connect('activate', Lang.bind(this, function(item) {
|
||||
this.actionGroup.change_action_state(action_id, GLib.Variant.new_string(item._remoteTarget));
|
||||
this.actionGroup.activate_action(action_id, GLib.Variant.new_string(item._remoteTarget));
|
||||
}));
|
||||
break;
|
||||
default:
|
||||
@ -1834,8 +1871,13 @@ const RemoteMenu = new Lang.Class({
|
||||
|
||||
let currentItems = target._getMenuItems();
|
||||
|
||||
for (j0 = 0, k0 = 0; j0 < position; j0++, k0++) {
|
||||
if (currentItems[k0] instanceof PopupSeparatorMenuItem)
|
||||
k0 = 0;
|
||||
// skip ignored items at the beginning
|
||||
while (k0 < currentItems.length && currentItems[k0]._ignored)
|
||||
k0++;
|
||||
// find the right menu item matching the model item
|
||||
for (j0 = 0; j0 < position; j0++, k0++) {
|
||||
if (currentItems[k0]._ignored)
|
||||
k0++;
|
||||
}
|
||||
|
||||
@ -1847,7 +1889,7 @@ const RemoteMenu = new Lang.Class({
|
||||
for (j = j0, k = k0; j < j0 + removed; j++, k++) {
|
||||
currentItems[k].destroy();
|
||||
|
||||
if (currentItems[k] instanceof PopupSeparatorMenuItem)
|
||||
if (currentItems[k]._ignored)
|
||||
j--;
|
||||
}
|
||||
}
|
||||
@ -1858,14 +1900,20 @@ const RemoteMenu = new Lang.Class({
|
||||
if (item) {
|
||||
// separators must be added in the parent to make autohiding work
|
||||
if (addSeparator) {
|
||||
target.addMenuItem(new PopupSeparatorMenuItem(), k+1);
|
||||
let separator = new PopupSeparatorMenuItem();
|
||||
item.separators.push(separator);
|
||||
separator._ignored = true;
|
||||
target.addMenuItem(separator, k+1);
|
||||
k++;
|
||||
}
|
||||
|
||||
target.addMenuItem(item, k);
|
||||
|
||||
if (addSeparator) {
|
||||
target.addMenuItem(new PopupSeparatorMenuItem(), k+1);
|
||||
let separator = new PopupSeparatorMenuItem();
|
||||
item.separators.push(separator);
|
||||
separator._ignored = true;
|
||||
target.addMenuItem(separator, k+1);
|
||||
k++;
|
||||
}
|
||||
} else if (changeSignal) {
|
||||
@ -1891,7 +1939,10 @@ const RemoteMenu = new Lang.Class({
|
||||
}
|
||||
|
||||
if (target instanceof PopupMenuSection) {
|
||||
target.actor.visible = target.numMenuItems != 0;
|
||||
if (target._titleMenuItem)
|
||||
target.actor.visible = target.numMenuItems != 1;
|
||||
else
|
||||
target.actor.visible = target.numMenuItems != 0;
|
||||
} else {
|
||||
let sourceItem = target.sourceActor._delegate;
|
||||
if (sourceItem instanceof PopupSubMenuMenuItem)
|
||||
|
@ -3,9 +3,12 @@
|
||||
const Lang = imports.lang;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
const ExtensionSystem = imports.ui.extensionSystem;
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Flashspot = imports.ui.flashspot;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const GnomeShellIface = <interface name="org.gnome.Shell">
|
||||
@ -30,15 +33,18 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
|
||||
<arg type="i" direction="in" name="y"/>
|
||||
<arg type="i" direction="in" name="width"/>
|
||||
<arg type="i" direction="in" name="height"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
</method>
|
||||
<method name="ScreenshotWindow">
|
||||
<arg type="b" direction="in" name="include_frame"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
</method>
|
||||
<method name="Screenshot">
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
</method>
|
||||
@ -56,6 +62,9 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
|
||||
<arg type="s" direction="in" name="uuid"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
</method>
|
||||
<method name="LaunchExtensionPrefs">
|
||||
<arg type="s" direction="in" name="uuid"/>
|
||||
</method>
|
||||
<property name="OverviewActive" type="b" access="readwrite" />
|
||||
<property name="ApiVersion" type="i" access="read" />
|
||||
<property name="ShellVersion" type="s" access="read" />
|
||||
@ -109,6 +118,16 @@ const GnomeShell = new Lang.Class({
|
||||
return [success, returnValue];
|
||||
},
|
||||
|
||||
_onScreenshotComplete: function(obj, result, area, flash, invocation) {
|
||||
if (flash) {
|
||||
let flashspot = new Flashspot.Flashspot(area);
|
||||
flashspot.fire();
|
||||
}
|
||||
|
||||
let retval = GLib.Variant.new('(b)', [result]);
|
||||
invocation.return_value(retval);
|
||||
},
|
||||
|
||||
/**
|
||||
* ScreenshotArea:
|
||||
* @x: The X coordinate of the area
|
||||
@ -122,8 +141,11 @@ const GnomeShell = new Lang.Class({
|
||||
* indicating whether the operation was successful or not.
|
||||
*
|
||||
*/
|
||||
ScreenshotAreaAsync : function (x, y, width, height, filename, callback) {
|
||||
global.screenshot_area (x, y, width, height, filename, function (obj, result) { callback(result); });
|
||||
ScreenshotAreaAsync : function (params, invocation) {
|
||||
let [x, y, width, height, flash, filename, callback] = params;
|
||||
global.screenshot_area (x, y, width, height, filename,
|
||||
Lang.bind(this, this._onScreenshotComplete,
|
||||
flash, invocation));
|
||||
},
|
||||
|
||||
/**
|
||||
@ -136,8 +158,11 @@ const GnomeShell = new Lang.Class({
|
||||
* indicating whether the operation was successful or not.
|
||||
*
|
||||
*/
|
||||
ScreenshotWindow : function (include_frame, filename) {
|
||||
return global.screenshot_window (include_frame, filename);
|
||||
ScreenshotWindowAsync : function (params, invocation) {
|
||||
let [include_frame, flash, filename] = params;
|
||||
global.screenshot_window (include_frame, filename,
|
||||
Lang.bind(this, this._onScreenshotComplete,
|
||||
flash, invocation));
|
||||
},
|
||||
|
||||
/**
|
||||
@ -149,13 +174,16 @@ const GnomeShell = new Lang.Class({
|
||||
* indicating whether the operation was successful or not.
|
||||
*
|
||||
*/
|
||||
ScreenshotAsync : function (filename, callback) {
|
||||
global.screenshot(filename, function (obj, result) { callback(result); });
|
||||
ScreenshotAsync : function (params, invocation) {
|
||||
let [flash, filename] = params;
|
||||
global.screenshot(filename,
|
||||
Lang.bind(this, this._onScreenshotComplete,
|
||||
flash, invocation));
|
||||
},
|
||||
|
||||
ListExtensions: function() {
|
||||
let out = {};
|
||||
for (let uuid in ExtensionSystem.extensionMeta) {
|
||||
for (let uuid in ExtensionUtils.extensions) {
|
||||
let dbusObj = this.GetExtensionInfo(uuid);
|
||||
out[uuid] = dbusObj;
|
||||
}
|
||||
@ -163,10 +191,23 @@ const GnomeShell = new Lang.Class({
|
||||
},
|
||||
|
||||
GetExtensionInfo: function(uuid) {
|
||||
let meta = ExtensionSystem.extensionMeta[uuid] || {};
|
||||
let extension = ExtensionUtils.extensions[uuid];
|
||||
if (!extension)
|
||||
return {};
|
||||
|
||||
let obj = {};
|
||||
Lang.copyProperties(extension.metadata, obj);
|
||||
|
||||
// Only serialize the properties that we actually need.
|
||||
const serializedProperties = ["type", "state", "path", "error", "hasPrefs"];
|
||||
|
||||
serializedProperties.forEach(function(prop) {
|
||||
obj[prop] = extension[prop];
|
||||
});
|
||||
|
||||
let out = {};
|
||||
for (let key in meta) {
|
||||
let val = meta[key];
|
||||
for (let key in obj) {
|
||||
let val = obj[key];
|
||||
let type;
|
||||
switch (typeof val) {
|
||||
case 'string':
|
||||
@ -175,16 +216,27 @@ const GnomeShell = new Lang.Class({
|
||||
case 'number':
|
||||
type = 'd';
|
||||
break;
|
||||
case 'boolean':
|
||||
type = 'b';
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
out[key] = GLib.Variant.new(type, val);
|
||||
}
|
||||
|
||||
return out;
|
||||
},
|
||||
|
||||
GetExtensionErrors: function(uuid) {
|
||||
return ExtensionSystem.errors[uuid] || [];
|
||||
let extension = ExtensionUtils.extensions[uuid];
|
||||
if (!extension)
|
||||
return [];
|
||||
|
||||
if (!extension.errors)
|
||||
return [];
|
||||
|
||||
return extension.errors;
|
||||
},
|
||||
|
||||
EnableExtension: function(uuid) {
|
||||
@ -209,6 +261,13 @@ const GnomeShell = new Lang.Class({
|
||||
return ExtensionSystem.uninstallExtensionFromUUID(uuid);
|
||||
},
|
||||
|
||||
LaunchExtensionPrefs: function(uuid) {
|
||||
let appSys = Shell.AppSystem.get_default();
|
||||
let app = appSys.lookup_app('gnome-shell-extension-prefs.desktop');
|
||||
app.launch(global.display.get_current_time_roundtrip(),
|
||||
['extension:///' + uuid], -1, null);
|
||||
},
|
||||
|
||||
get OverviewActive() {
|
||||
return Main.overview.visible;
|
||||
},
|
||||
|
@ -211,6 +211,8 @@ const ShellMountQuestionDialog = new Lang.Class({
|
||||
{ y_align: St.Align.START });
|
||||
|
||||
this.subjectLabel = new St.Label({ style_class: 'mount-question-dialog-subject' });
|
||||
this.subjectLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
this.subjectLabel.clutter_text.line_wrap = true;
|
||||
|
||||
messageLayout.add(this.subjectLabel,
|
||||
{ y_fill: false,
|
||||
|
@ -44,7 +44,7 @@ const ATIndicator = new Lang.Class({
|
||||
Extends: PanelMenu.SystemStatusButton,
|
||||
|
||||
_init: function() {
|
||||
this.parent('preferences-desktop-accessibility', null);
|
||||
this.parent('preferences-desktop-accessibility', null, _("Accessibility"));
|
||||
|
||||
let highContrast = this._buildHCItem();
|
||||
this.menu.addMenuItem(highContrast);
|
||||
|
@ -28,9 +28,8 @@ const Indicator = new Lang.Class({
|
||||
Extends: PanelMenu.SystemStatusButton,
|
||||
|
||||
_init: function() {
|
||||
this.parent('bluetooth-disabled', null);
|
||||
this.parent('bluetooth-disabled', null, _("Bluetooth"));
|
||||
|
||||
GLib.spawn_command_line_sync ('pkill -f "^bluetooth-applet$"');
|
||||
this._applet = new GnomeBluetoothApplet.Applet();
|
||||
|
||||
this._killswitch = new PopupMenu.PopupSwitchMenuItem(_("Bluetooth"), false);
|
||||
|
@ -113,6 +113,7 @@ const NMNetworkMenuItem = new Lang.Class({
|
||||
}
|
||||
|
||||
this._label = new St.Label({ text: title });
|
||||
this.actor.label_actor = this._label;
|
||||
this.addActor(this._label);
|
||||
this._icons = new St.BoxLayout({ style_class: 'nm-menu-item-icons' });
|
||||
this.addActor(this._icons, { align: St.Align.END });
|
||||
@ -249,9 +250,11 @@ const NMWirelessSectionTitleMenuItem = new Lang.Class({
|
||||
updateForDevice: function(device) {
|
||||
// we show the switch
|
||||
// - if there not just one device
|
||||
// - if the switch is off
|
||||
// - if the switch is off (but it can be turned on)
|
||||
// - if the device is activated or disconnected
|
||||
if (device && this._softwareEnabled && this._hardwareEnabled) {
|
||||
if (!this._hardwareEnabled) {
|
||||
this.setStatus(_("hardware disabled"));
|
||||
} else if (device && this._softwareEnabled) {
|
||||
let text = device.getStatusLabel();
|
||||
this.setStatus(text);
|
||||
} else
|
||||
@ -327,7 +330,7 @@ const NMDevice = new Lang.Class({
|
||||
}
|
||||
this.section = new PopupMenu.PopupMenuSection();
|
||||
|
||||
this._createSection();
|
||||
this._deferredWorkId = Main.initializeDeferredWork(this.section.actor, Lang.bind(this, this._createSection));
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
@ -394,16 +397,29 @@ const NMDevice = new Lang.Class({
|
||||
this._activeConnection = activeConnection;
|
||||
|
||||
this._clearSection();
|
||||
this._createSection();
|
||||
this._queueCreateSection();
|
||||
},
|
||||
|
||||
checkConnection: function(connection) {
|
||||
let exists = this._findConnection(connection._uuid) != -1;
|
||||
let pos = this._findConnection(connection._uuid);
|
||||
let exists = pos != -1;
|
||||
let valid = this.connectionValid(connection);
|
||||
|
||||
if (exists && !valid)
|
||||
this.removeConnection(connection);
|
||||
else if (!exists && valid)
|
||||
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) {
|
||||
@ -418,7 +434,7 @@ const NMDevice = new Lang.Class({
|
||||
this._connections.sort(this._connectionSortFunction);
|
||||
|
||||
this._clearSection();
|
||||
this._createSection();
|
||||
this._queueCreateSection();
|
||||
},
|
||||
|
||||
removeConnection: function(connection) {
|
||||
@ -442,7 +458,7 @@ const NMDevice = new Lang.Class({
|
||||
// (or in the case of NMDeviceWired, we want to hide
|
||||
// the only explicit connection)
|
||||
this._clearSection();
|
||||
this._createSection();
|
||||
this._queueCreateSection();
|
||||
}
|
||||
},
|
||||
|
||||
@ -526,6 +542,11 @@ const NMDevice = new Lang.Class({
|
||||
return -1;
|
||||
},
|
||||
|
||||
_queueCreateSection: function() {
|
||||
this._clearSection();
|
||||
Main.queueDeferredWork(this._deferredWorkId);
|
||||
},
|
||||
|
||||
_clearSection: function() {
|
||||
// Clear everything
|
||||
this.section.removeAll();
|
||||
@ -620,7 +641,7 @@ const NMDevice = new Lang.Class({
|
||||
this._updateStatusItem();
|
||||
|
||||
this._clearSection();
|
||||
this._createSection();
|
||||
this._queueCreateSection();
|
||||
this.emit('state-changed');
|
||||
},
|
||||
|
||||
@ -864,7 +885,12 @@ const NMDeviceBluetooth = new Lang.Class({
|
||||
this._autoConnectionName = this._makeConnectionName(this.device);
|
||||
|
||||
this._clearSection();
|
||||
this._createSection();
|
||||
this._queueCreateSection();
|
||||
this._updateStatusItem();
|
||||
},
|
||||
|
||||
_getDescription: function() {
|
||||
return this.device.name || _("Bluetooth");
|
||||
}
|
||||
});
|
||||
|
||||
@ -1209,7 +1235,6 @@ const NMDeviceWireless = new Lang.Class({
|
||||
accessPoints: [ accessPoint ]
|
||||
};
|
||||
apObj.ssidText = ssidToLabel(apObj.ssid);
|
||||
needsupdate = true;
|
||||
}
|
||||
|
||||
// check if this enables new connections for this group
|
||||
@ -1224,37 +1249,13 @@ const NMDeviceWireless = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
if (needsupdate) {
|
||||
if (apObj.item)
|
||||
apObj.item.destroy();
|
||||
|
||||
if (pos == -1 || needsupdate) {
|
||||
if (pos != -1)
|
||||
this._networks.splice(pos, 1);
|
||||
pos = Util.insertSorted(this._networks, apObj, this._networkSortFunction);
|
||||
|
||||
if (this._networks.length == 0) {
|
||||
// only network in the list
|
||||
this._networks.push(apObj);
|
||||
this._clearSection();
|
||||
this._createSection();
|
||||
return;
|
||||
}
|
||||
|
||||
// skip networks that should appear earlier
|
||||
let menuPos = 0;
|
||||
for (pos = 0;
|
||||
pos < this._networks.length &&
|
||||
this._networkSortFunction(this._networks[pos], apObj) < 0; ++pos) {
|
||||
if (this._networks[pos] != this._activeNetwork)
|
||||
menuPos++;
|
||||
}
|
||||
|
||||
// (re-)add the network
|
||||
this._networks.splice(pos, 0, apObj);
|
||||
|
||||
if (this._shouldShowConnectionList()) {
|
||||
menuPos += (this._activeConnectionItem ? 1 : 0);
|
||||
this._createNetworkItem(apObj, menuPos);
|
||||
}
|
||||
this._clearSection();
|
||||
this._queueCreateSection();
|
||||
}
|
||||
},
|
||||
|
||||
@ -1341,7 +1342,7 @@ const NMDeviceWireless = new Lang.Class({
|
||||
let obj = this._connections[pos];
|
||||
this._connections.splice(pos, 1);
|
||||
|
||||
let anyauto = false, forceupdate = false;
|
||||
let forceupdate = false;
|
||||
for (let i = 0; i < this._networks.length; i++) {
|
||||
let apObj = this._networks[i];
|
||||
let connections = apObj.connections;
|
||||
@ -1349,16 +1350,14 @@ const NMDeviceWireless = new Lang.Class({
|
||||
if (connections[k]._uuid == connection._uuid) {
|
||||
// remove the connection from the access point group
|
||||
connections.splice(k);
|
||||
anyauto = connections.length == 0;
|
||||
forceupdate = forceupdate || connections.length == 0;
|
||||
|
||||
if (anyauto) {
|
||||
// this potentially changes the sorting order
|
||||
forceupdate = true;
|
||||
if (forceupdate)
|
||||
break;
|
||||
}
|
||||
|
||||
if (apObj.item) {
|
||||
if (apObj.item instanceof PopupMenu.PopupSubMenuMenuItem) {
|
||||
let items = apObj.item.menu.getMenuItems();
|
||||
let items = apObj.item.menu._getMenuItems();
|
||||
if (items.length == 2) {
|
||||
// we need to update the connection list to convert this to a normal item
|
||||
forceupdate = true;
|
||||
@ -1380,10 +1379,10 @@ const NMDeviceWireless = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
if (forceupdate || anyauto) {
|
||||
if (forceupdate) {
|
||||
this._networks.sort(this._networkSortFunction);
|
||||
this._clearSection();
|
||||
this._createSection();
|
||||
this._queueCreateSection();
|
||||
}
|
||||
},
|
||||
|
||||
@ -1416,13 +1415,13 @@ const NMDeviceWireless = new Lang.Class({
|
||||
if (forceupdate) {
|
||||
this._networks.sort(this._networkSortFunction);
|
||||
this._clearSection();
|
||||
this._createSection();
|
||||
this._queueCreateSection();
|
||||
}
|
||||
},
|
||||
|
||||
_createActiveConnectionItem: function() {
|
||||
let icon, title;
|
||||
if (this._activeConnection._connection) {
|
||||
if (this._activeConnection && this._activeConnection._connection) {
|
||||
let connection = this._activeConnection._connection;
|
||||
if (this._activeNetwork)
|
||||
this._activeConnectionItem = new NMNetworkMenuItem(this._activeNetwork.accessPoints, undefined,
|
||||
@ -1515,7 +1514,7 @@ const NMDeviceWireless = new Lang.Class({
|
||||
if (!this._shouldShowConnectionList())
|
||||
return;
|
||||
|
||||
if(this._activeConnection) {
|
||||
if (this._activeNetwork) {
|
||||
this._createActiveConnectionItem();
|
||||
this.section.addMenuItem(this._activeConnectionItem);
|
||||
}
|
||||
@ -1524,8 +1523,10 @@ const NMDeviceWireless = new Lang.Class({
|
||||
|
||||
for(let j = 0; j < this._networks.length; j++) {
|
||||
let apObj = this._networks[j];
|
||||
if (apObj == this._activeNetwork)
|
||||
if (apObj == this._activeNetwork) {
|
||||
activeOffset--;
|
||||
continue;
|
||||
}
|
||||
|
||||
this._createNetworkItem(apObj, j + activeOffset);
|
||||
}
|
||||
@ -1537,7 +1538,7 @@ const NMApplet = new Lang.Class({
|
||||
Extends: PanelMenu.SystemStatusButton,
|
||||
|
||||
_init: function() {
|
||||
this.parent('network-error', null);
|
||||
this.parent('network-error', null, _("Network"));
|
||||
|
||||
this._client = NMClient.Client.new();
|
||||
|
||||
@ -1803,6 +1804,7 @@ const NMApplet = new Lang.Class({
|
||||
let activating = null;
|
||||
let default_ip4 = null;
|
||||
let default_ip6 = null;
|
||||
let active_vpn = null;
|
||||
for (let i = 0; i < this._activeConnections.length; i++) {
|
||||
let a = this._activeConnections[i];
|
||||
|
||||
@ -1832,6 +1834,8 @@ const NMApplet = new Lang.Class({
|
||||
default_ip4 = a;
|
||||
if (a.default6)
|
||||
default_ip6 = a;
|
||||
if (a._type == 'vpn')
|
||||
active_vpn = a;
|
||||
|
||||
if (a.state == NetworkManager.ActiveConnectionState.ACTIVATING)
|
||||
activating = a;
|
||||
@ -1862,7 +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;
|
||||
},
|
||||
|
||||
_notifyActivated: function(activeConnection) {
|
||||
|
@ -46,7 +46,6 @@ const PowerManagerInterface = <interface name="org.gnome.SettingsDaemon.Power">
|
||||
<method name="GetPrimaryDevice">
|
||||
<arg type="(susdut)" direction="out" />
|
||||
</method>
|
||||
<signal name="Changed" />
|
||||
<property name="Icon" type="s" access="read" />
|
||||
</interface>;
|
||||
|
||||
@ -57,7 +56,7 @@ const Indicator = new Lang.Class({
|
||||
Extends: PanelMenu.SystemStatusButton,
|
||||
|
||||
_init: function() {
|
||||
this.parent('battery-missing', null);
|
||||
this.parent('battery-missing', null, _("Battery"));
|
||||
|
||||
this._proxy = new PowerManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH);
|
||||
|
||||
@ -76,7 +75,8 @@ const Indicator = new Lang.Class({
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop');
|
||||
|
||||
this._proxy.connectSignal('Changed', Lang.bind(this, this._devicesChanged));
|
||||
this._proxy.connect('g-properties-changed',
|
||||
Lang.bind(this, this._devicesChanged));
|
||||
this._devicesChanged();
|
||||
},
|
||||
|
||||
|
@ -22,7 +22,7 @@ const Indicator = new Lang.Class({
|
||||
Extends: PanelMenu.SystemStatusButton,
|
||||
|
||||
_init: function() {
|
||||
this.parent('audio-volume-muted', null);
|
||||
this.parent('audio-volume-muted', null, _("Volume"));
|
||||
|
||||
this._control = new Gvc.MixerControl({ name: 'GNOME Shell Volume Control' });
|
||||
this._control.connect('state-changed', Lang.bind(this, this._onControlStateChanged));
|
||||
|
@ -33,10 +33,6 @@ const NotificationDirection = {
|
||||
RECEIVED: 'chat-received'
|
||||
};
|
||||
|
||||
let contactFeatures = [Tp.ContactFeature.ALIAS,
|
||||
Tp.ContactFeature.AVATAR_DATA,
|
||||
Tp.ContactFeature.PRESENCE];
|
||||
|
||||
// This is GNOME Shell's implementation of the Telepathy 'Client'
|
||||
// interface. Specifically, the shell is a Telepathy 'Observer', which
|
||||
// lets us see messages even if they belong to another app (eg,
|
||||
@ -83,11 +79,21 @@ const Client = new Lang.Class({
|
||||
// account path -> AccountNotification
|
||||
this._accountNotifications = {};
|
||||
|
||||
// Define features we want
|
||||
this._accountManager = Tp.AccountManager.dup();
|
||||
let factory = this._accountManager.get_factory();
|
||||
factory.add_account_features([Tp.Account.get_feature_quark_connection()]);
|
||||
factory.add_connection_features([Tp.Connection.get_feature_quark_contact_list()]);
|
||||
factory.add_channel_features([Tp.Channel.get_feature_quark_contacts()]);
|
||||
factory.add_contact_features([Tp.ContactFeature.ALIAS,
|
||||
Tp.ContactFeature.AVATAR_DATA,
|
||||
Tp.ContactFeature.PRESENCE,
|
||||
Tp.ContactFeature.SUBSCRIPTION_STATES]);
|
||||
|
||||
// Set up a SimpleObserver, which will call _observeChannels whenever a
|
||||
// channel matching its filters is detected.
|
||||
// The second argument, recover, means _observeChannels will be run
|
||||
// for any existing channel as well.
|
||||
this._accountManager = Tp.AccountManager.dup();
|
||||
this._tpClient = new Shell.TpClient({ 'account-manager': this._accountManager,
|
||||
'name': 'GnomeShell',
|
||||
'uniquify-name': true })
|
||||
@ -114,16 +120,9 @@ const Client = new Lang.Class({
|
||||
throw new Error('Couldn\'t register Telepathy client. Error: \n' + e);
|
||||
}
|
||||
|
||||
|
||||
// Watch subscription requests and connection errors
|
||||
this._subscriptionSource = null;
|
||||
this._accountSource = null;
|
||||
let factory = this._accountManager.get_factory();
|
||||
factory.add_account_features([Tp.Account.get_feature_quark_connection()]);
|
||||
factory.add_connection_features([Tp.Connection.get_feature_quark_contact_list()]);
|
||||
factory.add_contact_features([Tp.ContactFeature.SUBSCRIPTION_STATES,
|
||||
Tp.ContactFeature.ALIAS,
|
||||
Tp.ContactFeature.AVATAR_DATA]);
|
||||
|
||||
this._accountManager.connect('account-validity-changed',
|
||||
Lang.bind(this, this._accountValidityChanged));
|
||||
@ -133,22 +132,6 @@ const Client = new Lang.Class({
|
||||
|
||||
_observeChannels: function(observer, account, conn, channels,
|
||||
dispatchOp, requests, context) {
|
||||
// If the self_contact doesn't have the ALIAS, make sure
|
||||
// to fetch it before trying to grab the channels.
|
||||
let self_contact = conn.get_self_contact();
|
||||
if (self_contact.has_feature(Tp.ContactFeature.ALIAS)) {
|
||||
this._finishObserveChannels(account, conn, channels, context);
|
||||
} else {
|
||||
Shell.get_self_contact_features(conn,
|
||||
contactFeatures,
|
||||
Lang.bind(this, function() {
|
||||
this._finishObserveChannels(account, conn, channels, context);
|
||||
}));
|
||||
context.delay();
|
||||
}
|
||||
},
|
||||
|
||||
_finishObserveChannels: function(account, conn, channels, context) {
|
||||
let len = channels.length;
|
||||
for (let i = 0; i < len; i++) {
|
||||
let channel = channels[i];
|
||||
@ -159,16 +142,7 @@ const Client = new Lang.Class({
|
||||
targetHandleType != Tp.HandleType.CONTACT)
|
||||
continue;
|
||||
|
||||
/* Request a TpContact */
|
||||
Shell.get_tp_contacts(conn, [targetHandle],
|
||||
contactFeatures,
|
||||
Lang.bind(this, function (connection, contacts, failed) {
|
||||
if (contacts.length < 1)
|
||||
return;
|
||||
|
||||
/* We got the TpContact */
|
||||
this._createChatSource(account, conn, channel, contacts[0]);
|
||||
}), null);
|
||||
this._createChatSource(account, conn, channel, channel.get_target_contact());
|
||||
}
|
||||
|
||||
context.accept();
|
||||
@ -197,11 +171,11 @@ const Client = new Lang.Class({
|
||||
|
||||
_handleChannels: function(handler, account, conn, channels,
|
||||
requests, user_action_time, context) {
|
||||
this._handlingChannels(account, conn, channels);
|
||||
this._handlingChannels(account, conn, channels, true);
|
||||
context.accept();
|
||||
},
|
||||
|
||||
_handlingChannels: function(account, conn, channels) {
|
||||
_handlingChannels: function(account, conn, channels, notify) {
|
||||
let len = channels.length;
|
||||
for (let i = 0; i < len; i++) {
|
||||
let channel = channels[i];
|
||||
@ -212,7 +186,18 @@ const Client = new Lang.Class({
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this._tpClient.is_handling_channel(channel)) {
|
||||
// 'notify' will be true when coming from an actual HandleChannels
|
||||
// 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
|
||||
// has no new messages (for example, a new channel which only has
|
||||
// a delivery notification). We rely on _displayPendingMessages()
|
||||
// and _messageReceived() to notify for new messages.
|
||||
|
||||
// But we should still notify from HandleChannels because the
|
||||
// Telepathy spec states that handlers must foreground channels
|
||||
// in HandleChannels calls which are already being handled.
|
||||
|
||||
if (notify && this._tpClient.is_handling_channel(channel)) {
|
||||
// We are already handling the channel, display the source
|
||||
let source = this._chatSources[channel.get_object_path()];
|
||||
if (source)
|
||||
@ -223,41 +208,25 @@ const Client = new Lang.Class({
|
||||
|
||||
_displayRoomInvitation: function(conn, channel, dispatchOp, context) {
|
||||
// We can only approve the rooms if we have been invited to it
|
||||
let selfHandle = channel.group_get_self_handle();
|
||||
if (selfHandle == 0) {
|
||||
let selfContact = channel.group_get_self_contact();
|
||||
if (selfContact == null) {
|
||||
Shell.decline_dispatch_op(context, 'Not invited to the room');
|
||||
return;
|
||||
}
|
||||
|
||||
let [invited, inviter, reason, msg] = channel.group_get_local_pending_info(selfHandle);
|
||||
let [invited, inviter, reason, msg] = channel.group_get_local_pending_contact_info(selfContact);
|
||||
if (!invited) {
|
||||
Shell.decline_dispatch_op(context, 'Not invited to the room');
|
||||
return;
|
||||
}
|
||||
|
||||
// Request a TpContact for the inviter
|
||||
Shell.get_tp_contacts(conn, [inviter],
|
||||
contactFeatures,
|
||||
Lang.bind(this, this._createRoomInviteSource, channel, context, dispatchOp));
|
||||
|
||||
context.delay();
|
||||
},
|
||||
|
||||
_createRoomInviteSource: function(connection, contacts, failed, channel, context, dispatchOp) {
|
||||
if (contacts.length < 1) {
|
||||
Shell.decline_dispatch_op(context, 'Failed to get inviter');
|
||||
return;
|
||||
}
|
||||
|
||||
// We got the TpContact
|
||||
|
||||
// FIXME: We don't have a 'chat room' icon (bgo #653737) use
|
||||
// system-users for now as Empathy does.
|
||||
let source = new ApproverSource(dispatchOp, _("Invitation"),
|
||||
Gio.icon_new_for_string('system-users'));
|
||||
Main.messageTray.add(source);
|
||||
|
||||
let notif = new RoomInviteNotification(source, dispatchOp, channel, contacts[0]);
|
||||
let notif = new RoomInviteNotification(source, dispatchOp, channel, inviter);
|
||||
source.notify(notif);
|
||||
context.accept();
|
||||
},
|
||||
@ -285,7 +254,7 @@ const Client = new Lang.Class({
|
||||
Lang.bind(this, function(dispatchOp, result) {
|
||||
try {
|
||||
dispatchOp.claim_with_finish(result);
|
||||
this._handlingChannels(account, conn, [channel]);
|
||||
this._handlingChannels(account, conn, [channel], false);
|
||||
} catch (err) {
|
||||
throw new Error('Failed to Claim channel: ' + err);
|
||||
}}));
|
||||
@ -297,21 +266,6 @@ const Client = new Lang.Class({
|
||||
},
|
||||
|
||||
_approveCall: function(account, conn, channel, dispatchOp, context) {
|
||||
let [targetHandle, targetHandleType] = channel.get_handle();
|
||||
|
||||
Shell.get_tp_contacts(conn, [targetHandle],
|
||||
contactFeatures,
|
||||
Lang.bind(this, this._createAudioVideoSource, channel, context, dispatchOp));
|
||||
|
||||
context.delay();
|
||||
},
|
||||
|
||||
_createAudioVideoSource: function(connection, contacts, failed, channel, context, dispatchOp) {
|
||||
if (contacts.length < 1) {
|
||||
Shell.decline_dispatch_op(context, 'Failed to get inviter');
|
||||
return;
|
||||
}
|
||||
|
||||
let isVideo = false;
|
||||
|
||||
let props = channel.borrow_immutable_properties();
|
||||
@ -326,27 +280,13 @@ const Client = new Lang.Class({
|
||||
Gio.icon_new_for_string('audio-input-microphone'));
|
||||
Main.messageTray.add(source);
|
||||
|
||||
let notif = new AudioVideoNotification(source, dispatchOp, channel, contacts[0], isVideo);
|
||||
let notif = new AudioVideoNotification(source, dispatchOp, channel,
|
||||
channel.get_target_contact(), isVideo);
|
||||
source.notify(notif);
|
||||
context.accept();
|
||||
},
|
||||
|
||||
_approveFileTransfer: function(account, conn, channel, dispatchOp, context) {
|
||||
let [targetHandle, targetHandleType] = channel.get_handle();
|
||||
|
||||
Shell.get_tp_contacts(conn, [targetHandle],
|
||||
contactFeatures,
|
||||
Lang.bind(this, this._createFileTransferSource, channel, context, dispatchOp));
|
||||
|
||||
context.delay();
|
||||
},
|
||||
|
||||
_createFileTransferSource: function(connection, contacts, failed, channel, context, dispatchOp) {
|
||||
if (contacts.length < 1) {
|
||||
Shell.decline_dispatch_op(context, 'Failed to get file sender');
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the icon of the file being transferred
|
||||
let gicon = Gio.content_type_get_icon(channel.get_mime_type());
|
||||
|
||||
@ -354,7 +294,8 @@ const Client = new Lang.Class({
|
||||
let source = new ApproverSource(dispatchOp, _("File Transfer"), gicon);
|
||||
Main.messageTray.add(source);
|
||||
|
||||
let notif = new FileTransferNotification(source, dispatchOp, channel, contacts[0]);
|
||||
let notif = new FileTransferNotification(source, dispatchOp, channel,
|
||||
channel.get_target_contact());
|
||||
source.notify(notif);
|
||||
context.accept();
|
||||
},
|
||||
@ -502,15 +443,9 @@ const ChatSource = new Lang.Class({
|
||||
this._notification.setUrgency(MessageTray.Urgency.HIGH);
|
||||
this._notifyTimeoutId = 0;
|
||||
|
||||
// We ack messages when the message box is collapsed if user has
|
||||
// interacted with it before and so read the messages:
|
||||
// - user clicked on it the tray
|
||||
// - user expanded the notification by hovering over the toaster notification
|
||||
this._shouldAck = false;
|
||||
|
||||
this.connect('summary-item-clicked', Lang.bind(this, this._summaryItemClicked));
|
||||
this._notification.connect('expanded', Lang.bind(this, this._notificationExpanded));
|
||||
this._notification.connect('collapsed', Lang.bind(this, this._notificationCollapsed));
|
||||
// We ack messages when the user expands the new notification or views the summary
|
||||
// notification, in which case the notification is also expanded.
|
||||
this._notification.connect('expanded', Lang.bind(this, this._ackMessages));
|
||||
|
||||
this._presence = contact.get_presence_type();
|
||||
|
||||
@ -734,12 +669,12 @@ const ChatSource = new Lang.Class({
|
||||
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.EXTENDED_AWAY) {
|
||||
} 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) {
|
||||
} 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) {
|
||||
@ -774,24 +709,6 @@ const ChatSource = new Lang.Class({
|
||||
// 'pending-message-removed' for each one.
|
||||
this._channel.ack_all_pending_messages_async(Lang.bind(this, function(src, result) {
|
||||
this._channel.ack_all_pending_messages_finish(result);}));
|
||||
},
|
||||
|
||||
_summaryItemClicked: function(source, button) {
|
||||
if (button != 1)
|
||||
return;
|
||||
|
||||
this._shouldAck = true;
|
||||
},
|
||||
|
||||
_notificationExpanded: function() {
|
||||
this._shouldAck = true;
|
||||
},
|
||||
|
||||
_notificationCollapsed: function() {
|
||||
if (this._shouldAck)
|
||||
this._ackMessages();
|
||||
|
||||
this._shouldAck = false;
|
||||
}
|
||||
});
|
||||
|
||||
@ -1175,7 +1092,7 @@ const AudioVideoNotification = new Lang.Class({
|
||||
/* translators: argument is a contact name like Alice for example. */
|
||||
title = _("Call from %s").format(contact.get_alias());
|
||||
|
||||
this.parent(this, source, title, null, { customContent: true });
|
||||
this.parent(source, title, null, { customContent: true });
|
||||
this.setResident(true);
|
||||
|
||||
this.addButton('reject', _("Reject"));
|
||||
@ -1206,8 +1123,7 @@ const FileTransferNotification = new Lang.Class({
|
||||
Extends: MessageTray.Notification,
|
||||
|
||||
_init: function(source, dispatchOp, channel, contact) {
|
||||
this.parent(this,
|
||||
source,
|
||||
this.parent(source,
|
||||
/* To translators: The first parameter is
|
||||
* the contact's alias and the second one is the
|
||||
* file name. The string will be something
|
||||
@ -1280,7 +1196,7 @@ const SubscriptionRequestNotification = new Lang.Class({
|
||||
Extends: MessageTray.Notification,
|
||||
|
||||
_init: function(source, contact) {
|
||||
this.parent(this, source,
|
||||
this.parent(source,
|
||||
/* To translators: The parameter is the contact's alias */
|
||||
_("%s would like permission to see when you are online").format(contact.get_alias()),
|
||||
null, { customContent: true });
|
||||
|
@ -239,7 +239,8 @@ const IMStatusChooserItem = new Lang.Class({
|
||||
},
|
||||
|
||||
_setIconFromFile: function(iconFile) {
|
||||
this._iconBin.set_style('background-image: url("' + iconFile + '");');
|
||||
this._iconBin.set_style('background-image: url("' + iconFile + '");' +
|
||||
'background-size: contain;');
|
||||
this._iconBin.child = null;
|
||||
},
|
||||
|
||||
@ -375,7 +376,8 @@ const IMStatusChooserItem = new Lang.Class({
|
||||
if (sessionStatus == GnomeSession.PresenceStatus.IDLE) {
|
||||
// Only change presence if the current one is "more present" than
|
||||
// idle
|
||||
if (this._currentPresence != Tp.ConnectionPresenceType.OFFLINE)
|
||||
if (this._currentPresence != Tp.ConnectionPresenceType.OFFLINE &&
|
||||
this._currentPresence != Tp.ConnectionPresenceType.HIDDEN)
|
||||
return Tp.ConnectionPresenceType.EXTENDED_AWAY;
|
||||
}
|
||||
|
||||
@ -465,6 +467,7 @@ const UserMenuButton = new Lang.Class({
|
||||
}));
|
||||
|
||||
this._name = new St.Label();
|
||||
this.actor.label_actor = this._name;
|
||||
box.add(this._name, { y_align: St.Align.MIDDLE, y_fill: false });
|
||||
this._userLoadedId = this._user.connect('notify::is-loaded', Lang.bind(this, this._updateUserName));
|
||||
this._userChangedId = this._user.connect('changed', Lang.bind(this, this._updateUserName));
|
||||
@ -610,7 +613,7 @@ const UserMenuButton = new Lang.Class({
|
||||
this._statusChooser = item;
|
||||
|
||||
item = new PopupMenu.PopupSwitchMenuItem(_("Notifications"));
|
||||
item.connect('activate', Lang.bind(this, this._updatePresenceStatus));
|
||||
item.connect('toggled', Lang.bind(this, this._updatePresenceStatus));
|
||||
this.menu.addMenuItem(item);
|
||||
this._notificationsSwitch = item;
|
||||
|
||||
|
@ -419,29 +419,6 @@ const ViewSelector = new Lang.Class({
|
||||
// not when setting the initially selected one.
|
||||
if (!tab.visible)
|
||||
tab.show(!firstSwitch);
|
||||
|
||||
// Pull a Meg Ryan:
|
||||
if (!firstSwitch && Main.overview.workspaces) {
|
||||
if (tab != this._tabs[0]) {
|
||||
Tweener.addTween(Main.overview.workspaces.actor,
|
||||
{ opacity: 0,
|
||||
time: 0.1,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this,
|
||||
function() {
|
||||
Main.overview.workspaces.actor.hide();
|
||||
Main.overview.workspaces.actor.opacity = 255;
|
||||
})
|
||||
});
|
||||
} else {
|
||||
Main.overview.workspaces.actor.opacity = 0;
|
||||
Main.overview.workspaces.actor.show();
|
||||
Tweener.addTween(Main.overview.workspaces.actor,
|
||||
{ opacity: 255,
|
||||
time: 0.1,
|
||||
transition: 'easeOutQuad' });
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
switchTab: function(id) {
|
||||
|
210
js/ui/wanda.js
Normal file
210
js/ui/wanda.js
Normal file
@ -0,0 +1,210 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GdkPixbuf = imports.gi.GdkPixbuf;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const IconGrid = imports.ui.iconGrid;
|
||||
const Main = imports.ui.main;
|
||||
const Search = imports.ui.search;
|
||||
|
||||
// we could make these gsettings
|
||||
const FISH_NAME = 'wanda';
|
||||
const FISH_SPEED = 300;
|
||||
const FISH_COMMAND = 'fortune';
|
||||
|
||||
const GNOME_PANEL_PIXMAPDIR = '../gnome-panel/pixmaps';
|
||||
const FISH_GROUP = 'Fish Animation';
|
||||
|
||||
const MAGIC_FISH_KEY = 'free the fish';
|
||||
|
||||
const WandaIcon = new Lang.Class({
|
||||
Name: 'WandaIcon',
|
||||
Extends: IconGrid.BaseIcon,
|
||||
|
||||
_init : function(fish, label, params) {
|
||||
this._fish = fish;
|
||||
let file = GLib.build_filenamev([global.datadir, GNOME_PANEL_PIXMAPDIR, fish + '.fish']);
|
||||
|
||||
if (GLib.file_test(file, GLib.FileTest.EXISTS)) {
|
||||
this._keyfile = new GLib.KeyFile();
|
||||
this._keyfile.load_from_file(file, GLib.KeyFileFlags.NONE);
|
||||
|
||||
this._imageFile = GLib.build_filenamev([global.datadir, GNOME_PANEL_PIXMAPDIR,
|
||||
this._keyfile.get_string(FISH_GROUP, 'image')]);
|
||||
|
||||
let tmpPixbuf = GdkPixbuf.Pixbuf.new_from_file(this._imageFile);
|
||||
|
||||
this._imgHeight = tmpPixbuf.height;
|
||||
this._imgWidth = tmpPixbuf.width / this._keyfile.get_integer(FISH_GROUP, 'frames');
|
||||
} else {
|
||||
this._imageFile = null;
|
||||
}
|
||||
|
||||
this.parent(label, params);
|
||||
},
|
||||
|
||||
createIcon: function(iconSize) {
|
||||
if (this._animations)
|
||||
this._animations.destroy();
|
||||
|
||||
if (!this._imageFile) {
|
||||
return new St.Icon({ icon_name: 'face-smile',
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: iconSize
|
||||
});
|
||||
}
|
||||
|
||||
this._animations = St.TextureCache.get_default().load_sliced_image(this._imageFile, this._imgWidth, this._imgHeight);
|
||||
this._animations.connect('destroy', Lang.bind(this, function() {
|
||||
if (this._timeoutId)
|
||||
GLib.source_remove(this._timeoutId);
|
||||
this._timeoutId = 0;
|
||||
this._animations = null;
|
||||
}));
|
||||
this._animations.connect('notify::mapped', Lang.bind(this, function() {
|
||||
if (this._animations.mapped && !this._timeoutId) {
|
||||
this._timeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, FISH_SPEED, Lang.bind(this, this._update));
|
||||
|
||||
this._i = 0;
|
||||
this._update();
|
||||
} else if (!this._animations.mapped && this._timeoutId) {
|
||||
GLib.source_remove(this._timeoutId);
|
||||
this._timeoutId = 0;
|
||||
}
|
||||
}));
|
||||
|
||||
this._i = 0;
|
||||
|
||||
return this._animations;
|
||||
},
|
||||
|
||||
_update: function() {
|
||||
let n = this._animations.get_n_children();
|
||||
if (n == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
this._animations.get_nth_child(this._i).hide();
|
||||
this._i = (this._i + 1) % n;
|
||||
this._animations.get_nth_child(this._i).show();
|
||||
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
const WandaIconBin = new Lang.Class({
|
||||
Name: 'WandaIconBin',
|
||||
|
||||
_init: function(fish, label, params) {
|
||||
this.actor = new St.Bin({ style_class: 'search-result-content',
|
||||
reactive: true,
|
||||
track_hover: true });
|
||||
this.icon = new WandaIcon(fish, label, params);
|
||||
|
||||
this.actor.child = this.icon.actor;
|
||||
this.actor.label_actor = this.icon.label;
|
||||
},
|
||||
});
|
||||
|
||||
const FortuneDialog = new Lang.Class({
|
||||
Name: 'FortuneDialog',
|
||||
|
||||
_init: function(name, command) {
|
||||
let text;
|
||||
|
||||
try {
|
||||
let [res, stdout, stderr, status] = GLib.spawn_command_line_sync(command);
|
||||
text = String.fromCharCode.apply(null, stdout);
|
||||
} catch(e) {
|
||||
text = _("Sorry, no wisdom for you today:\n%s").format(e.message);
|
||||
}
|
||||
|
||||
this._title = new St.Label({ style_class: 'polkit-dialog-headline',
|
||||
text: _("%s the Oracle says").format(name) });
|
||||
this._label = new St.Label({ style_class: 'polkit-dialog-description',
|
||||
text: text });
|
||||
this._label.clutter_text.line_wrap = true;
|
||||
|
||||
this._box = new St.BoxLayout({ vertical: true,
|
||||
style_class: 'polkit-dialog' // this is just to force a reasonable width
|
||||
});
|
||||
this._box.add(this._title, { align: St.Align.MIDDLE });
|
||||
this._box.add(this._label, { expand: true });
|
||||
|
||||
this._button = new St.Button({ button_mask: St.ButtonMask.ONE,
|
||||
style_class: 'modal-dialog',
|
||||
reactive: true });
|
||||
this._button.connect('clicked', Lang.bind(this, this.destroy));
|
||||
this._button.child = this._box;
|
||||
|
||||
let monitor = Main.layoutManager.primaryMonitor;
|
||||
|
||||
Main.layoutManager.addChrome(this._button);
|
||||
this._button.set_position(Math.floor(monitor.width / 2 - this._button.width / 2),
|
||||
Math.floor(monitor.height / 2 - this._button.height / 2));
|
||||
|
||||
GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 10, Lang.bind(this, this.destroy));
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this._button.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
function capitalize(str) {
|
||||
return str[0].toUpperCase() + str.substring(1, str.length);
|
||||
}
|
||||
|
||||
const WandaSearchProvider = new Lang.Class({
|
||||
Name: 'WandaSearchProvider',
|
||||
Extends: Search.SearchProvider,
|
||||
|
||||
_init: function() {
|
||||
this.parent(_("Your favorite Easter Egg"));
|
||||
},
|
||||
|
||||
getResultMeta: function(fish) {
|
||||
return { 'id': fish,
|
||||
'name': capitalize(fish),
|
||||
'createIcon': function(iconSize) {
|
||||
// for DND only (maybe could be improved)
|
||||
// DON'T use St.Icon here, it crashes the shell
|
||||
// (dnd.js code assumes it can query the actor size
|
||||
// without parenting it, while StWidget accesses
|
||||
// StThemeNode in get_preferred_width/height, which
|
||||
// triggers an assertion failure)
|
||||
return St.TextureCache.get_default().load_icon_name(null,
|
||||
'face-smile',
|
||||
St.IconType.FULLCOLOR,
|
||||
iconSize);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
getInitialResultSet: function(terms) {
|
||||
if (terms.join(' ') == MAGIC_FISH_KEY) {
|
||||
return [ FISH_NAME ];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
|
||||
getSubsearchResultSet: function(previousResults, terms) {
|
||||
return this.getInitialResultSet(terms);
|
||||
},
|
||||
|
||||
activateResult: function(fish, params) {
|
||||
if (this._dialog)
|
||||
this._dialog.destroy();
|
||||
this._dialog = new FortuneDialog(capitalize(fish), FISH_COMMAND);
|
||||
},
|
||||
|
||||
createResultActor: function (resultMeta, terms) {
|
||||
let icon = new WandaIconBin(resultMeta.id, resultMeta.name);
|
||||
return icon.actor;
|
||||
}
|
||||
});
|
@ -23,6 +23,8 @@ const WINDOW_DND_SIZE = 256;
|
||||
|
||||
const SCROLL_SCALE_AMOUNT = 100 / 5;
|
||||
|
||||
const WINDOW_CLONE_MAXIMUM_SCALE = 0.7;
|
||||
|
||||
const LIGHTBOX_FADE_TIME = 0.1;
|
||||
const CLOSE_BUTTON_FADE_TIME = 0.1;
|
||||
|
||||
@ -94,10 +96,11 @@ const ScaledPoint = new Lang.Class({
|
||||
const WindowClone = new Lang.Class({
|
||||
Name: 'WindowClone',
|
||||
|
||||
_init : function(realWindow) {
|
||||
_init : function(realWindow, workspace) {
|
||||
this.realWindow = realWindow;
|
||||
this.metaWindow = realWindow.meta_window;
|
||||
this.metaWindow._delegate = this;
|
||||
this._workspace = workspace;
|
||||
|
||||
let [borderX, borderY] = this._getInvisibleBorderPadding();
|
||||
this._windowClone = new Clutter.Clone({ source: realWindow.get_texture(),
|
||||
@ -367,6 +370,7 @@ const WindowClone = new Lang.Class({
|
||||
if (this._selected)
|
||||
return;
|
||||
let [x, y] = action.get_coords();
|
||||
action.release();
|
||||
this._draggable.startDrag(x, y, global.get_current_time());
|
||||
}));
|
||||
}
|
||||
@ -383,19 +387,12 @@ const WindowClone = new Lang.Class({
|
||||
this.emit('drag-begin');
|
||||
},
|
||||
|
||||
_getWorkspaceActor : function() {
|
||||
let index = this.metaWindow.get_workspace().index();
|
||||
return Main.overview.workspaces.getWorkspaceByIndex(index);
|
||||
},
|
||||
|
||||
handleDragOver : function(source, actor, x, y, time) {
|
||||
let workspace = this._getWorkspaceActor();
|
||||
return workspace.handleDragOver(source, actor, x, y, time);
|
||||
return this._workspace.handleDragOver(source, actor, x, y, time);
|
||||
},
|
||||
|
||||
acceptDrop : function(source, actor, x, y, time) {
|
||||
let workspace = this._getWorkspaceActor();
|
||||
workspace.acceptDrop(source, actor, x, y, time);
|
||||
this._workspace.acceptDrop(source, actor, x, y, time);
|
||||
},
|
||||
|
||||
_onDragCancelled : function (draggable, time) {
|
||||
@ -494,6 +491,9 @@ const WindowOverlay = new Lang.Class({
|
||||
},
|
||||
|
||||
fadeIn: function() {
|
||||
if (!this._hidden)
|
||||
return;
|
||||
|
||||
this.show();
|
||||
this.title.opacity = 0;
|
||||
this._parentActor.raise_top();
|
||||
@ -522,7 +522,7 @@ const WindowOverlay = new Lang.Class({
|
||||
// get_transformed_position() and get_transformed_size(),
|
||||
// as windowClone might be moving.
|
||||
// See Workspace._showWindowOverlay
|
||||
updatePositions: function(cloneX, cloneY, cloneWidth, cloneHeight) {
|
||||
updatePositions: function(cloneX, cloneY, cloneWidth, cloneHeight, animate) {
|
||||
let button = this.closeButton;
|
||||
let title = this.title;
|
||||
|
||||
@ -544,15 +544,34 @@ const WindowOverlay = new Lang.Class({
|
||||
else
|
||||
buttonX = cloneX + (cloneWidth - button._overlap);
|
||||
|
||||
button.set_position(Math.floor(buttonX), Math.floor(buttonY));
|
||||
if (animate)
|
||||
this._animateOverlayActor(button, Math.floor(buttonX), Math.floor(buttonY), button.width);
|
||||
else
|
||||
button.set_position(Math.floor(buttonX), Math.floor(buttonY));
|
||||
|
||||
if (!title.fullWidth)
|
||||
title.fullWidth = title.width;
|
||||
title.width = Math.min(title.fullWidth, cloneWidth);
|
||||
let titleWidth = Math.min(title.fullWidth, cloneWidth);
|
||||
|
||||
let titleX = cloneX + (cloneWidth - title.width) / 2;
|
||||
let titleX = cloneX + (cloneWidth - titleWidth) / 2;
|
||||
let titleY = cloneY + cloneHeight + title._spacing;
|
||||
title.set_position(Math.floor(titleX), Math.floor(titleY));
|
||||
|
||||
if (animate)
|
||||
this._animateOverlayActor(title, Math.floor(titleX), Math.floor(titleY), titleWidth);
|
||||
else {
|
||||
title.width = titleWidth;
|
||||
title.set_position(Math.floor(titleX), Math.floor(titleY));
|
||||
}
|
||||
},
|
||||
|
||||
_animateOverlayActor: function(actor, x, y, width) {
|
||||
Tweener.addTween(actor,
|
||||
{ x: x,
|
||||
y: y,
|
||||
width: width,
|
||||
time: Overview.ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
},
|
||||
|
||||
_closeWindow: function(actor) {
|
||||
@ -948,7 +967,7 @@ const Workspace = new Lang.Class({
|
||||
|
||||
let scale = Math.min((width - buttonOuterWidth) / rect.width,
|
||||
(height - buttonOuterHeight - captionHeight) / rect.height,
|
||||
1.0);
|
||||
WINDOW_CLONE_MAXIMUM_SCALE);
|
||||
|
||||
x = Math.floor(x + (width - scale * rect.width) / 2);
|
||||
|
||||
@ -1017,7 +1036,7 @@ const Workspace = new Lang.Class({
|
||||
|
||||
let [x, y, scale] = this._computeWindowLayout(metaWindow, slot);
|
||||
|
||||
if (overlay)
|
||||
if (overlay && initialPositioning)
|
||||
overlay.hide();
|
||||
if (animate && isOnCurrentWorkspace) {
|
||||
if (!metaWindow.showing_on_its_workspace()) {
|
||||
@ -1040,20 +1059,11 @@ const Workspace = new Lang.Class({
|
||||
});
|
||||
}
|
||||
|
||||
Tweener.addTween(clone.actor,
|
||||
{ x: x,
|
||||
y: y,
|
||||
scale_x: scale,
|
||||
scale_y: scale,
|
||||
time: Overview.ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this._showWindowOverlay(clone, overlay, true);
|
||||
})
|
||||
});
|
||||
this._animateClone(clone, overlay, x, y, scale, initialPositioning);
|
||||
} else {
|
||||
clone.actor.set_position(x, y);
|
||||
clone.actor.set_scale(scale, scale);
|
||||
this._updateWindowOverlayPositions(clone, overlay, x, y, scale, false);
|
||||
this._showWindowOverlay(clone, overlay, isOnCurrentWorkspace);
|
||||
}
|
||||
}
|
||||
@ -1075,23 +1085,35 @@ const Workspace = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_animateClone: function(clone, overlay, x, y, scale, initialPositioning) {
|
||||
Tweener.addTween(clone.actor,
|
||||
{ x: x,
|
||||
y: y,
|
||||
scale_x: scale,
|
||||
scale_y: scale,
|
||||
time: Overview.ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this._showWindowOverlay(clone, overlay, true);
|
||||
})
|
||||
});
|
||||
|
||||
this._updateWindowOverlayPositions(clone, overlay, x, y, scale, true);
|
||||
},
|
||||
|
||||
_updateWindowOverlayPositions: function(clone, overlay, x, y, scale, animate) {
|
||||
let [cloneWidth, cloneHeight] = clone.actor.get_size();
|
||||
cloneWidth = scale * cloneWidth;
|
||||
cloneHeight = scale * cloneHeight;
|
||||
if (overlay)
|
||||
overlay.updatePositions(x, y, cloneWidth, cloneHeight, animate);
|
||||
},
|
||||
|
||||
_showWindowOverlay: function(clone, overlay, fade) {
|
||||
if (clone.inDrag)
|
||||
return;
|
||||
|
||||
// This is a little messy and complicated because when we
|
||||
// start the fade-in we may not have done the final positioning
|
||||
// of the workspaces. (Tweener doesn't necessarily finish
|
||||
// all animations before calling onComplete callbacks.)
|
||||
// So we need to manually compute where the window will
|
||||
// be after the workspace animation finishes.
|
||||
let [cloneX, cloneY] = clone.actor.get_position();
|
||||
let [cloneWidth, cloneHeight] = clone.actor.get_size();
|
||||
cloneWidth = clone.actor.scale_x * cloneWidth;
|
||||
cloneHeight = clone.actor.scale_y * cloneHeight;
|
||||
|
||||
if (overlay) {
|
||||
overlay.updatePositions(cloneX, cloneY, cloneWidth, cloneHeight);
|
||||
if (fade)
|
||||
overlay.fadeIn();
|
||||
else
|
||||
@ -1109,6 +1131,16 @@ const Workspace = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_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() {
|
||||
if (this._windowIsZooming)
|
||||
return true;
|
||||
@ -1126,22 +1158,16 @@ const Workspace = new Lang.Class({
|
||||
return true;
|
||||
}
|
||||
|
||||
let actorUnderPointer = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
|
||||
for (let i = 0; i < this._windows.length; i++) {
|
||||
if (this._windows[i].actor == actorUnderPointer)
|
||||
return true;
|
||||
}
|
||||
|
||||
this.positionWindows(WindowPositionFlags.ANIMATE);
|
||||
return false;
|
||||
},
|
||||
|
||||
showWindowsOverlays: function() {
|
||||
if (this.leavingOverview)
|
||||
return;
|
||||
|
||||
this._windowOverlaysGroup.show();
|
||||
this._showAllOverlays();
|
||||
},
|
||||
|
||||
hideWindowsOverlays: function() {
|
||||
this._windowOverlaysGroup.hide();
|
||||
},
|
||||
|
||||
_doRemoveWindow : function(metaWin) {
|
||||
let win = metaWin.get_compositor_private();
|
||||
|
||||
@ -1295,7 +1321,7 @@ const Workspace = new Lang.Class({
|
||||
|
||||
this.leavingOverview = true;
|
||||
|
||||
this.hideWindowsOverlays();
|
||||
this._hideAllOverlays();
|
||||
|
||||
if (this._repositionWindowsId > 0) {
|
||||
Mainloop.source_remove(this._repositionWindowsId);
|
||||
@ -1386,7 +1412,7 @@ const Workspace = new Lang.Class({
|
||||
|
||||
// Create a clone of a (non-desktop) window and add it to the window list
|
||||
_addWindowClone : function(win) {
|
||||
let clone = new WindowClone(win);
|
||||
let clone = new WindowClone(win, this);
|
||||
let overlay = new WindowOverlay(clone, this._windowOverlaysGroup);
|
||||
|
||||
clone.connect('selected',
|
||||
|
@ -51,6 +51,7 @@ const WindowClone = new Lang.Class({
|
||||
dragActorMaxSize: Workspace.WINDOW_DND_SIZE,
|
||||
dragActorOpacity: Workspace.DRAGGING_WINDOW_OPACITY });
|
||||
this._draggable.connect('drag-begin', Lang.bind(this, this._onDragBegin));
|
||||
this._draggable.connect('drag-cancelled', Lang.bind(this, this._onDragCancelled));
|
||||
this._draggable.connect('drag-end', Lang.bind(this, this._onDragEnd));
|
||||
this.inDrag = false;
|
||||
},
|
||||
@ -108,6 +109,10 @@ const WindowClone = new Lang.Class({
|
||||
this.emit('drag-begin');
|
||||
},
|
||||
|
||||
_onDragCancelled : function (draggable, time) {
|
||||
this.emit('drag-cancelled');
|
||||
},
|
||||
|
||||
_onDragEnd : function (draggable, time, snapback) {
|
||||
this.inDrag = false;
|
||||
|
||||
@ -149,6 +154,8 @@ const WorkspaceThumbnail = new Lang.Class({
|
||||
this.metaWorkspace = metaWorkspace;
|
||||
this.monitorIndex = Main.layoutManager.primaryIndex;
|
||||
|
||||
this._removed = false;
|
||||
|
||||
this.actor = new St.Group({ reactive: true,
|
||||
clip_to_allocation: true,
|
||||
style_class: 'workspace-thumbnail' });
|
||||
@ -174,17 +181,21 @@ const WorkspaceThumbnail = new Lang.Class({
|
||||
let monitor = Main.layoutManager.primaryMonitor;
|
||||
this.setPorthole(monitor.x, monitor.y, monitor.width, monitor.height);
|
||||
|
||||
let windows = global.get_window_actors().filter(this._isMyWindow, this);
|
||||
let windows = global.get_window_actors().filter(this._isWorkspaceWindow, this);
|
||||
|
||||
// Create clones for windows that should be visible in the Overview
|
||||
this._windows = [];
|
||||
this._allWindows = [];
|
||||
this._minimizedChangedIds = [];
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
windows[i].meta_window._minimizedChangedId =
|
||||
let minimizedChangedId =
|
||||
windows[i].meta_window.connect('notify::minimized',
|
||||
Lang.bind(this,
|
||||
this._updateMinimized));
|
||||
this._allWindows.push(windows[i].meta_window);
|
||||
this._minimizedChangedIds.push(minimizedChangedId);
|
||||
|
||||
if (this._isOverviewWindow(windows[i])) {
|
||||
if (this._isMyWindow(windows[i]) && this._isOverviewWindow(windows[i])) {
|
||||
this._addWindowClone(windows[i]);
|
||||
}
|
||||
}
|
||||
@ -269,17 +280,11 @@ const WorkspaceThumbnail = new Lang.Class({
|
||||
let clone = this._windows[index];
|
||||
this._windows.splice(index, 1);
|
||||
|
||||
if (win && this._isOverviewWindow(win)) {
|
||||
if (metaWin._minimizedChangedId) {
|
||||
metaWin.disconnect(metaWin._minimizedChangedId);
|
||||
delete metaWin._minimizedChangedId;
|
||||
}
|
||||
}
|
||||
clone.destroy();
|
||||
},
|
||||
|
||||
_doAddWindow : function(metaWin) {
|
||||
if (this.leavingOverview)
|
||||
if (this._removed)
|
||||
return;
|
||||
|
||||
let win = metaWin.get_compositor_private();
|
||||
@ -289,7 +294,7 @@ const WorkspaceThumbnail = new Lang.Class({
|
||||
// the compositor finds out about them...
|
||||
Mainloop.idle_add(Lang.bind(this,
|
||||
function () {
|
||||
if (this.actor &&
|
||||
if (!this._removed &&
|
||||
metaWin.get_compositor_private() &&
|
||||
metaWin.get_workspace() == this.metaWorkspace)
|
||||
this._doAddWindow(metaWin);
|
||||
@ -298,16 +303,19 @@ const WorkspaceThumbnail = new Lang.Class({
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._allWindows.indexOf(metaWin) == -1) {
|
||||
let minimizedChangedId = metaWin.connect('notify::minimized',
|
||||
Lang.bind(this,
|
||||
this._updateMinimized));
|
||||
this._allWindows.push(metaWin);
|
||||
this._minimizedChangedIds.push(minimizedChangedId);
|
||||
}
|
||||
|
||||
// We might have the window in our list already if it was on all workspaces and
|
||||
// now was moved to this workspace
|
||||
if (this._lookupIndex (metaWin) != -1)
|
||||
return;
|
||||
|
||||
if (!metaWin._minimizedChangedId)
|
||||
metaWin._minimizedChangedId = metaWin.connect('notify::minimized',
|
||||
Lang.bind(this,
|
||||
this._updateMinimized));
|
||||
|
||||
if (!this._isMyWindow(win) || !this._isOverviewWindow(win))
|
||||
return;
|
||||
|
||||
@ -319,6 +327,13 @@ const WorkspaceThumbnail = new Lang.Class({
|
||||
},
|
||||
|
||||
_windowRemoved : function(metaWorkspace, metaWin) {
|
||||
let index = this._allWindows.indexOf(metaWin);
|
||||
if (index != -1) {
|
||||
metaWin.disconnect(this._minimizedChangedIds[index]);
|
||||
this._allWindows.splice(index, 1);
|
||||
this._minimizedChangedIds.splice(index, 1);
|
||||
}
|
||||
|
||||
this._doRemoveWindow(metaWin);
|
||||
},
|
||||
|
||||
@ -345,27 +360,36 @@ const WorkspaceThumbnail = new Lang.Class({
|
||||
this.actor.destroy();
|
||||
},
|
||||
|
||||
_onDestroy: function(actor) {
|
||||
workspaceRemoved : function() {
|
||||
if (this._removed)
|
||||
return;
|
||||
|
||||
this._removed = true;
|
||||
|
||||
this.metaWorkspace.disconnect(this._windowAddedId);
|
||||
this.metaWorkspace.disconnect(this._windowRemovedId);
|
||||
global.screen.disconnect(this._windowEnteredMonitorId);
|
||||
global.screen.disconnect(this._windowLeftMonitorId);
|
||||
|
||||
for (let i = 0; i < this._windows.length; i++) {
|
||||
let metaWin = this._windows[i].metaWindow;
|
||||
if (metaWin._minimizedChangedId) {
|
||||
metaWin.disconnect(metaWin._minimizedChangedId);
|
||||
delete metaWin._minimizedChangedId;
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < this._allWindows.length; i++)
|
||||
this._allWindows[i].disconnect(this._minimizedChangedIds[i]);
|
||||
},
|
||||
|
||||
_onDestroy: function(actor) {
|
||||
this.workspaceRemoved();
|
||||
|
||||
this._windows = [];
|
||||
this.actor = null;
|
||||
},
|
||||
|
||||
// Tests if @win belongs to this workspace
|
||||
_isWorkspaceWindow : function (win) {
|
||||
return Main.isWindowActorDisplayedOnWorkspace(win, this.metaWorkspace.index());
|
||||
},
|
||||
|
||||
// Tests if @win belongs to this workspace and monitor
|
||||
_isMyWindow : function (win) {
|
||||
return Main.isWindowActorDisplayedOnWorkspace(win, this.metaWorkspace.index()) &&
|
||||
return this._isWorkspaceWindow(win) &&
|
||||
(!win.get_meta_window() || win.get_meta_window().get_monitor() == this.monitorIndex);
|
||||
},
|
||||
|
||||
@ -386,6 +410,10 @@ const WorkspaceThumbnail = new Lang.Class({
|
||||
Lang.bind(this, function(clone) {
|
||||
Main.overview.beginWindowDrag();
|
||||
}));
|
||||
clone.connect('drag-cancelled',
|
||||
Lang.bind(this, function(clone) {
|
||||
Main.overview.cancelledWindowDrag();
|
||||
}));
|
||||
clone.connect('drag-end',
|
||||
Lang.bind(this, function(clone) {
|
||||
Main.overview.endWindowDrag();
|
||||
@ -519,6 +547,59 @@ const ThumbnailsBox = new Lang.Class({
|
||||
this._stateCounts[ThumbnailState[key]] = 0;
|
||||
|
||||
this._thumbnails = [];
|
||||
|
||||
Main.overview.connect('item-drag-begin',
|
||||
Lang.bind(this, this._onDragBegin));
|
||||
Main.overview.connect('item-drag-end',
|
||||
Lang.bind(this, this._onDragEnd));
|
||||
Main.overview.connect('item-drag-cancelled',
|
||||
Lang.bind(this, this._onDragCancelled));
|
||||
Main.overview.connect('window-drag-begin',
|
||||
Lang.bind(this, this._onDragBegin));
|
||||
Main.overview.connect('window-drag-end',
|
||||
Lang.bind(this, this._onDragEnd));
|
||||
Main.overview.connect('window-drag-cancelled',
|
||||
Lang.bind(this, this._onDragCancelled));
|
||||
},
|
||||
|
||||
_onDragBegin: function() {
|
||||
this._dragCancelled = false;
|
||||
this._dragMonitor = {
|
||||
dragMotion: Lang.bind(this, this._onDragMotion)
|
||||
};
|
||||
DND.addDragMonitor(this._dragMonitor);
|
||||
},
|
||||
|
||||
_onDragEnd: function() {
|
||||
if (this._dragCancelled)
|
||||
return;
|
||||
|
||||
this._endDrag();
|
||||
},
|
||||
|
||||
_onDragCancelled: function() {
|
||||
this._dragCancelled = true;
|
||||
this._endDrag();
|
||||
},
|
||||
|
||||
_endDrag: function() {
|
||||
this._clearDragPlaceholder();
|
||||
DND.removeDragMonitor(this._dragMonitor);
|
||||
},
|
||||
|
||||
_onDragMotion: function(dragEvent) {
|
||||
if (!this.actor.contains(dragEvent.targetActor))
|
||||
this._onLeave();
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
},
|
||||
|
||||
_onLeave: function() {
|
||||
this._clearDragPlaceholder();
|
||||
},
|
||||
|
||||
_clearDragPlaceholder: function() {
|
||||
this._dropPlaceholderPos = -1;
|
||||
this.actor.queue_relayout();
|
||||
},
|
||||
|
||||
// Draggable target interface
|
||||
@ -530,7 +611,11 @@ const ThumbnailsBox = new Lang.Class({
|
||||
let thumbHeight = this._porthole.height * this._scale;
|
||||
|
||||
let workspace = -1;
|
||||
let firstThumbY = this._thumbnails[0].actor.y;
|
||||
let firstThumbY;
|
||||
if (this._dropPlaceholderPos == 0)
|
||||
firstThumbY = this._dropPlaceholder.y;
|
||||
else
|
||||
firstThumbY = this._thumbnails[0].actor.y;
|
||||
for (let i = 0; i < this._thumbnails.length; i ++) {
|
||||
let targetBase = firstThumbY + (thumbHeight + spacing) * i;
|
||||
|
||||
@ -670,8 +755,10 @@ const ThumbnailsBox = new Lang.Class({
|
||||
if (thumbnail.state > ThumbnailState.NORMAL)
|
||||
continue;
|
||||
|
||||
if (currentPos >= start && currentPos < start + count)
|
||||
if (currentPos >= start && currentPos < start + count) {
|
||||
thumbnail.workspaceRemoved();
|
||||
this._setThumbnailState(thumbnail, ThumbnailState.REMOVING);
|
||||
}
|
||||
|
||||
currentPos++;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
@ -19,6 +20,7 @@ const WORKSPACE_SWITCH_TIME = 0.25;
|
||||
// Note that mutter has a compile-time limit of 36
|
||||
const MAX_WORKSPACES = 16;
|
||||
|
||||
const OVERRIDE_SCHEMA = 'org.gnome.shell.overrides';
|
||||
|
||||
const CONTROLS_POP_IN_TIME = 0.1;
|
||||
|
||||
@ -40,8 +42,6 @@ const WorkspacesView = new Lang.Class({
|
||||
this._spacing = node.get_length('spacing');
|
||||
this._updateWorkspaceActors(false);
|
||||
}));
|
||||
this.actor.connect('notify::mapped',
|
||||
Lang.bind(this, this._onMappedChanged));
|
||||
|
||||
this._width = 0;
|
||||
this._height = 0;
|
||||
@ -59,6 +59,11 @@ const WorkspacesView = new Lang.Class({
|
||||
this._zoomOut = false; // zoom to a larger area
|
||||
this._inDrag = false; // dragging a window
|
||||
|
||||
this._settings = new Gio.Settings({ schema: OVERRIDE_SCHEMA });
|
||||
this._updateExtraWorkspacesId =
|
||||
this._settings.connect('changed::workspaces-only-on-primary',
|
||||
Lang.bind(this, this._updateExtraWorkspaces));
|
||||
|
||||
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||
this._workspaces = workspaces;
|
||||
|
||||
@ -67,17 +72,7 @@ const WorkspacesView = new Lang.Class({
|
||||
this._workspaces[w].actor.reparent(this.actor);
|
||||
this._workspaces[activeWorkspaceIndex].actor.raise_top();
|
||||
|
||||
this._extraWorkspaces = [];
|
||||
let monitors = Main.layoutManager.monitors;
|
||||
let m = 0;
|
||||
for (let i = 0; i < monitors.length; i++) {
|
||||
if (i == Main.layoutManager.primaryIndex)
|
||||
continue;
|
||||
let ws = new Workspace.Workspace(null, i);
|
||||
this._extraWorkspaces[m++] = ws;
|
||||
ws.setGeometry(monitors[i].x, monitors[i].y, monitors[i].width, monitors[i].height);
|
||||
global.overlay_group.add_actor(ws.actor);
|
||||
}
|
||||
this._updateExtraWorkspaces();
|
||||
|
||||
// Position/scale the desktop windows and their children after the
|
||||
// workspaces have been created. This cannot be done first because
|
||||
@ -88,6 +83,8 @@ const WorkspacesView = new Lang.Class({
|
||||
Lang.bind(this, function() {
|
||||
for (let w = 0; w < this._workspaces.length; w++)
|
||||
this._workspaces[w].zoomToOverview();
|
||||
if (!this._extraWorkspaces)
|
||||
return;
|
||||
for (let w = 0; w < this._extraWorkspaces.length; w++)
|
||||
this._extraWorkspaces[w].zoomToOverview();
|
||||
}));
|
||||
@ -98,14 +95,14 @@ const WorkspacesView = new Lang.Class({
|
||||
this._clipWidth, this._clipHeight);
|
||||
}));
|
||||
|
||||
this._scrollAdjustment = new St.Adjustment({ value: activeWorkspaceIndex,
|
||||
lower: 0,
|
||||
page_increment: 1,
|
||||
page_size: 1,
|
||||
step_increment: 0,
|
||||
upper: this._workspaces.length });
|
||||
this._scrollAdjustment.connect('notify::value',
|
||||
Lang.bind(this, this._onScroll));
|
||||
this.scrollAdjustment = new St.Adjustment({ value: activeWorkspaceIndex,
|
||||
lower: 0,
|
||||
page_increment: 1,
|
||||
page_size: 1,
|
||||
step_increment: 0,
|
||||
upper: this._workspaces.length });
|
||||
this.scrollAdjustment.connect('notify::value',
|
||||
Lang.bind(this, this._onScroll));
|
||||
|
||||
this._switchWorkspaceNotifyId =
|
||||
global.window_manager.connect('switch-workspace',
|
||||
@ -119,8 +116,35 @@ const WorkspacesView = new Lang.Class({
|
||||
Lang.bind(this, this._dragBegin));
|
||||
this._windowDragEndId = Main.overview.connect('window-drag-end',
|
||||
Lang.bind(this, this._dragEnd));
|
||||
this._swipeScrollBeginId = 0;
|
||||
this._swipeScrollEndId = 0;
|
||||
},
|
||||
|
||||
_updateExtraWorkspaces: function() {
|
||||
this._destroyExtraWorkspaces();
|
||||
|
||||
if (!this._settings.get_boolean('workspaces-only-on-primary'))
|
||||
return;
|
||||
|
||||
this._extraWorkspaces = [];
|
||||
let monitors = Main.layoutManager.monitors;
|
||||
for (let i = 0; i < monitors.length; i++) {
|
||||
if (i == Main.layoutManager.primaryIndex)
|
||||
continue;
|
||||
|
||||
let ws = new Workspace.Workspace(null, i);
|
||||
ws.setGeometry(monitors[i].x, monitors[i].y,
|
||||
monitors[i].width, monitors[i].height);
|
||||
global.overlay_group.add_actor(ws.actor);
|
||||
this._extraWorkspaces.push(ws);
|
||||
}
|
||||
},
|
||||
|
||||
_destroyExtraWorkspaces: function() {
|
||||
if (!this._extraWorkspaces)
|
||||
return;
|
||||
|
||||
for (let m = 0; m < this._extraWorkspaces.length; m++)
|
||||
this._extraWorkspaces[m].destroy();
|
||||
this._extraWorkspaces = null;
|
||||
},
|
||||
|
||||
setGeometry: function(x, y, width, height, spacing) {
|
||||
@ -157,10 +181,6 @@ const WorkspacesView = new Lang.Class({
|
||||
return this._workspaces[active];
|
||||
},
|
||||
|
||||
getWorkspaceByIndex: function(index) {
|
||||
return this._workspaces[index];
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||
let activeWorkspace = this._workspaces[activeWorkspaceIndex];
|
||||
@ -171,6 +191,8 @@ const WorkspacesView = new Lang.Class({
|
||||
|
||||
for (let w = 0; w < this._workspaces.length; w++)
|
||||
this._workspaces[w].zoomFromOverview();
|
||||
if (!this._extraWorkspaces)
|
||||
return;
|
||||
for (let w = 0; w < this._extraWorkspaces.length; w++)
|
||||
this._extraWorkspaces[w].zoomFromOverview();
|
||||
},
|
||||
@ -182,6 +204,8 @@ const WorkspacesView = new Lang.Class({
|
||||
syncStacking: function(stackIndices) {
|
||||
for (let i = 0; i < this._workspaces.length; i++)
|
||||
this._workspaces[i].syncStacking(stackIndices);
|
||||
if (!this._extraWorkspaces)
|
||||
return;
|
||||
for (let i = 0; i < this._extraWorkspaces.length; i++)
|
||||
this._extraWorkspaces[i].syncStacking(stackIndices);
|
||||
},
|
||||
@ -243,10 +267,8 @@ const WorkspacesView = new Lang.Class({
|
||||
for (let w = 0; w < this._workspaces.length; w++) {
|
||||
let workspace = this._workspaces[w];
|
||||
if (this._animating || this._scrolling) {
|
||||
workspace.hideWindowsOverlays();
|
||||
workspace.actor.show();
|
||||
} else {
|
||||
workspace.showWindowsOverlays();
|
||||
if (this._inDrag)
|
||||
workspace.actor.visible = (Math.abs(w - active) <= 1);
|
||||
else
|
||||
@ -262,7 +284,7 @@ const WorkspacesView = new Lang.Class({
|
||||
this._animatingScroll = true;
|
||||
|
||||
if (showAnimation) {
|
||||
Tweener.addTween(this._scrollAdjustment, {
|
||||
Tweener.addTween(this.scrollAdjustment, {
|
||||
value: index,
|
||||
time: WORKSPACE_SWITCH_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
@ -272,7 +294,7 @@ const WorkspacesView = new Lang.Class({
|
||||
})
|
||||
});
|
||||
} else {
|
||||
this._scrollAdjustment.value = index;
|
||||
this.scrollAdjustment.value = index;
|
||||
this._animatingScroll = false;
|
||||
}
|
||||
},
|
||||
@ -280,7 +302,7 @@ const WorkspacesView = new Lang.Class({
|
||||
updateWorkspaces: function(oldNumWorkspaces, newNumWorkspaces) {
|
||||
let active = global.screen.get_active_workspace_index();
|
||||
|
||||
Tweener.addTween(this._scrollAdjustment,
|
||||
Tweener.addTween(this.scrollAdjustment,
|
||||
{ upper: newNumWorkspaces,
|
||||
time: WORKSPACE_SWITCH_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
@ -307,12 +329,12 @@ const WorkspacesView = new Lang.Class({
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
for (let i = 0; i < this._extraWorkspaces.length; i++)
|
||||
this._extraWorkspaces[i].destroy();
|
||||
this._scrollAdjustment.run_dispose();
|
||||
this._destroyExtraWorkspaces();
|
||||
this.scrollAdjustment.run_dispose();
|
||||
Main.overview.disconnect(this._overviewShowingId);
|
||||
Main.overview.disconnect(this._overviewShownId);
|
||||
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
|
||||
this._settings.disconnect(this._updateExtraWorkspacesId);
|
||||
|
||||
if (this._inDrag)
|
||||
this._dragEnd();
|
||||
@ -335,21 +357,6 @@ const WorkspacesView = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_onMappedChanged: function() {
|
||||
if (this.actor.mapped) {
|
||||
let direction = Overview.SwipeScrollDirection.VERTICAL;
|
||||
Main.overview.setScrollAdjustment(this._scrollAdjustment,
|
||||
direction);
|
||||
this._swipeScrollBeginId = Main.overview.connect('swipe-scroll-begin',
|
||||
Lang.bind(this, this._swipeScrollBegin));
|
||||
this._swipeScrollEndId = Main.overview.connect('swipe-scroll-end',
|
||||
Lang.bind(this, this._swipeScrollEnd));
|
||||
} else {
|
||||
Main.overview.disconnect(this._swipeScrollBeginId);
|
||||
Main.overview.disconnect(this._swipeScrollEndId);
|
||||
}
|
||||
},
|
||||
|
||||
_dragBegin: function() {
|
||||
if (this._scrolling)
|
||||
return;
|
||||
@ -371,6 +378,9 @@ const WorkspacesView = new Lang.Class({
|
||||
this._firstDragMotion = false;
|
||||
for (let i = 0; i < this._workspaces.length; i++)
|
||||
this._workspaces[i].setReservedSlot(dragEvent.dragActor._delegate);
|
||||
if (!this._extraWorkspaces)
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
|
||||
for (let i = 0; i < this._extraWorkspaces.length; i++)
|
||||
this._extraWorkspaces[i].setReservedSlot(dragEvent.dragActor._delegate);
|
||||
}
|
||||
@ -384,15 +394,18 @@ const WorkspacesView = new Lang.Class({
|
||||
|
||||
for (let i = 0; i < this._workspaces.length; i++)
|
||||
this._workspaces[i].setReservedSlot(null);
|
||||
|
||||
if (!this._extraWorkspaces)
|
||||
return;
|
||||
for (let i = 0; i < this._extraWorkspaces.length; i++)
|
||||
this._extraWorkspaces[i].setReservedSlot(null);
|
||||
},
|
||||
|
||||
_swipeScrollBegin: function() {
|
||||
startSwipeScroll: function() {
|
||||
this._scrolling = true;
|
||||
},
|
||||
|
||||
_swipeScrollEnd: function(overview, result) {
|
||||
endSwipeScroll: function(result) {
|
||||
this._scrolling = false;
|
||||
|
||||
if (result == Overview.SwipeScrollResult.CLICK) {
|
||||
@ -441,7 +454,6 @@ const WorkspacesView = new Lang.Class({
|
||||
let dy = newY - currentY;
|
||||
|
||||
for (let i = 0; i < this._workspaces.length; i++) {
|
||||
this._workspaces[i].hideWindowsOverlays();
|
||||
this._workspaces[i].actor.visible = Math.abs(i - adj.value) <= 1;
|
||||
this._workspaces[i].actor.y += dy;
|
||||
}
|
||||
@ -462,6 +474,8 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||
this.actor.connect('notify::mapped', Lang.bind(this, this._setupSwipeScrolling));
|
||||
this.actor.connect('parent-set', Lang.bind(this, this._parentSet));
|
||||
this.actor.set_clip_to_allocation(true);
|
||||
|
||||
let controls = new St.Bin({ style_class: 'workspace-controls',
|
||||
@ -478,12 +492,19 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
controls.connect('scroll-event',
|
||||
Lang.bind(this, this._onScrollEvent));
|
||||
|
||||
this._monitorIndex = Main.layoutManager.primaryIndex;
|
||||
this._primaryIndex = Main.layoutManager.primaryIndex;
|
||||
|
||||
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
|
||||
controls.add_actor(this._thumbnailsBox.actor);
|
||||
|
||||
this.workspacesView = null;
|
||||
this._workspacesViews = null;
|
||||
this._primaryScrollAdjustment = null;
|
||||
|
||||
this._settings = new Gio.Settings({ schema: OVERRIDE_SCHEMA });
|
||||
this._settings.connect('changed::workspaces-only-on-primary',
|
||||
Lang.bind(this,
|
||||
this._workspacesOnlyOnPrimaryChanged));
|
||||
this._workspacesOnlyOnPrimaryChanged();
|
||||
|
||||
this._inDrag = false;
|
||||
this._cancelledDrag = false;
|
||||
@ -494,6 +515,8 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
|
||||
this._updateAlwaysZoom();
|
||||
|
||||
// If we stop hiding the overview on layout changes, we will need to
|
||||
// update the _workspacesViews here
|
||||
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._updateAlwaysZoom));
|
||||
|
||||
Main.xdndHandler.connect('drag-begin', Lang.bind(this, function(){
|
||||
@ -514,6 +537,9 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
this._windowDragBeginId = 0;
|
||||
this._windowDragCancelledId = 0;
|
||||
this._windowDragEndId = 0;
|
||||
this._notifyOpacityId = 0;
|
||||
this._swipeScrollBeginId = 0;
|
||||
this._swipeScrollEndId = 0;
|
||||
},
|
||||
|
||||
show: function() {
|
||||
@ -524,16 +550,7 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
this._controls.show();
|
||||
this._thumbnailsBox.show();
|
||||
|
||||
this._workspaces = [];
|
||||
for (let i = 0; i < global.screen.n_workspaces; i++) {
|
||||
let metaWorkspace = global.screen.get_workspace_by_index(i);
|
||||
this._workspaces[i] = new Workspace.Workspace(metaWorkspace, this._monitorIndex);
|
||||
}
|
||||
|
||||
if (this.workspacesView)
|
||||
this.workspacesView.destroy();
|
||||
this.workspacesView = new WorkspacesView(this._workspaces);
|
||||
this._updateWorkspacesGeometry();
|
||||
this._updateWorkspacesViews();
|
||||
|
||||
this._restackedNotifyId =
|
||||
global.screen.connect('restacked',
|
||||
@ -564,6 +581,12 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
this._onRestacked();
|
||||
},
|
||||
|
||||
zoomFromOverview: function() {
|
||||
for (let i = 0; i < this._workspacesViews.length; i++) {
|
||||
this._workspacesViews[i].hide();
|
||||
}
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
this._controls.hide();
|
||||
this._thumbnailsBox.hide();
|
||||
@ -597,12 +620,120 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
this._windowDragEndId = 0;
|
||||
}
|
||||
|
||||
this.workspacesView.destroy();
|
||||
this.workspacesView = null;
|
||||
for (let w = 0; w < this._workspaces.length; w++) {
|
||||
this._workspaces[w].disconnectAll();
|
||||
this._workspaces[w].destroy();
|
||||
for (let i = 0; i < this._workspacesViews.length; i++)
|
||||
this._workspacesViews[i].destroy();
|
||||
this._workspacesViews = null;
|
||||
|
||||
for (let i = 0; i < this._workspaces.length; i++)
|
||||
for (let w = 0; w < this._workspaces[i].length; w++) {
|
||||
this._workspaces[i][w].disconnectAll();
|
||||
this._workspaces[i][w].destroy();
|
||||
}
|
||||
},
|
||||
|
||||
_setupSwipeScrolling: function() {
|
||||
if (this._swipeScrollBeginId)
|
||||
Main.overview.disconnect(this._swipeScrollBeginId);
|
||||
this._swipeScrollBeginId = 0;
|
||||
|
||||
if (this._swipeScrollEndId)
|
||||
Main.overview.disconnect(this._swipeScrollEndId);
|
||||
this._swipeScrollEndId = 0;
|
||||
|
||||
if (!this.actor.mapped)
|
||||
return;
|
||||
|
||||
let direction = Overview.SwipeScrollDirection.VERTICAL;
|
||||
Main.overview.setScrollAdjustment(this._scrollAdjustment,
|
||||
direction);
|
||||
this._swipeScrollBeginId = Main.overview.connect('swipe-scroll-begin',
|
||||
Lang.bind(this, function() {
|
||||
for (let i = 0; i < this._workspacesViews.length; i++)
|
||||
this._workspacesViews[i].startSwipeScroll();
|
||||
}));
|
||||
this._swipeScrollEndId = Main.overview.connect('swipe-scroll-end',
|
||||
Lang.bind(this, function(overview, result) {
|
||||
for (let i = 0; i < this._workspacesViews.length; i++)
|
||||
this._workspacesViews[i].endSwipeScroll(result);
|
||||
}));
|
||||
},
|
||||
|
||||
_workspacesOnlyOnPrimaryChanged: function() {
|
||||
this._workspacesOnlyOnPrimary = this._settings.get_boolean('workspaces-only-on-primary');
|
||||
|
||||
if (!Main.overview.visible)
|
||||
return;
|
||||
|
||||
this._updateWorkspacesViews();
|
||||
},
|
||||
|
||||
_updateWorkspacesViews: function() {
|
||||
if (this._workspacesViews)
|
||||
for (let i = 0; i < this._workspacesViews.length; i++)
|
||||
this._workspacesViews[i].destroy();
|
||||
|
||||
if (this._workspaces)
|
||||
for (let i = 0; i < this._workspaces.length; i++)
|
||||
for (let w = 0; w < this._workspaces[i].length; w++)
|
||||
this._workspaces[i][w].destroy();
|
||||
|
||||
this._workspacesViews = [];
|
||||
this._workspaces = [];
|
||||
let monitors = Main.layoutManager.monitors;
|
||||
for (let i = 0; i < monitors.length; i++) {
|
||||
if (this._workspacesOnlyOnPrimary && i != this._primaryIndex)
|
||||
continue; // we are only interested in the primary monitor
|
||||
|
||||
let monitorWorkspaces = [];
|
||||
for (let w = 0; w < global.screen.n_workspaces; w++) {
|
||||
let metaWorkspace = global.screen.get_workspace_by_index(w);
|
||||
monitorWorkspaces.push(new Workspace.Workspace(metaWorkspace, i));
|
||||
}
|
||||
|
||||
this._workspaces.push(monitorWorkspaces);
|
||||
|
||||
let view = new WorkspacesView(monitorWorkspaces);
|
||||
if (this._workspacesOnlyOnPrimary || i == this._primaryIndex) {
|
||||
this._scrollAdjustment = view.scrollAdjustment;
|
||||
this._scrollAdjustment.connect('notify::value',
|
||||
Lang.bind(this, this._scrollValueChanged));
|
||||
this._setupSwipeScrolling();
|
||||
}
|
||||
this._workspacesViews.push(view);
|
||||
}
|
||||
|
||||
this._updateWorkspacesGeometry();
|
||||
|
||||
for (let i = 0; i < this._workspacesViews.length; i++)
|
||||
global.overlay_group.add_actor(this._workspacesViews[i].actor);
|
||||
},
|
||||
|
||||
_scrollValueChanged: function() {
|
||||
if (this._workspacesOnlyOnPrimary)
|
||||
return;
|
||||
|
||||
for (let i = 0; i < this._workspacesViews.length; i++) {
|
||||
if (i == this._primaryIndex)
|
||||
continue;
|
||||
|
||||
let adjustment = this._workspacesViews[i].scrollAdjustment;
|
||||
// the adjustments work in terms of workspaces, so the
|
||||
// values map directly
|
||||
adjustment.value = this._scrollAdjustment.value;
|
||||
}
|
||||
},
|
||||
|
||||
_getPrimaryView: function() {
|
||||
if (!this._workspacesViews)
|
||||
return null;
|
||||
if (this._workspacesOnlyOnPrimary)
|
||||
return this._workspacesViews[0];
|
||||
else
|
||||
return this._workspacesViews[this._primaryIndex];
|
||||
},
|
||||
|
||||
activeWorkspaceHasMaximizedWindows: function() {
|
||||
return this._getPrimaryView().getActiveWorkspace().hasMaximizedWindows();
|
||||
},
|
||||
|
||||
// zoomFraction property allows us to tween the controls sliding in and out
|
||||
@ -675,8 +806,37 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
this._updateWorkspacesGeometry();
|
||||
},
|
||||
|
||||
_parentSet: function(actor, oldParent) {
|
||||
if (oldParent && this._notifyOpacityId)
|
||||
oldParent.disconnect(this._notifyOpacityId);
|
||||
this._notifyOpacityId = 0;
|
||||
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this,
|
||||
function() {
|
||||
let newParent = this.actor.get_parent();
|
||||
if (!newParent)
|
||||
return;
|
||||
|
||||
// This is kinda hackish - we want the primary view to
|
||||
// appear as parent of this.actor, though in reality it
|
||||
// is added directly to overlay_group
|
||||
this._notifyOpacityId = newParent.connect('notify::opacity',
|
||||
Lang.bind(this, function() {
|
||||
let opacity = this.actor.get_parent().opacity;
|
||||
let primaryView = this._getPrimaryView();
|
||||
if (!primaryView)
|
||||
return;
|
||||
primaryView.actor.opacity = opacity;
|
||||
if (opacity == 0)
|
||||
primaryView.actor.hide();
|
||||
else
|
||||
primaryView.actor.show();
|
||||
}));
|
||||
}));
|
||||
},
|
||||
|
||||
_updateWorkspacesGeometry: function() {
|
||||
if (!this.workspacesView)
|
||||
if (!this._workspacesViews)
|
||||
return;
|
||||
|
||||
let fullWidth = this.actor.allocation.x2 - this.actor.allocation.x1;
|
||||
@ -697,8 +857,6 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
let clipX = rtl ? x + controlsVisible : x;
|
||||
let clipY = y + (fullHeight - clipHeight) / 2;
|
||||
|
||||
this.workspacesView.setClipRect(clipX, clipY, clipWidth, clipHeight);
|
||||
|
||||
if (this._zoomOut) {
|
||||
width -= controlsNatural;
|
||||
if (rtl)
|
||||
@ -713,7 +871,28 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
let difference = fullHeight - height;
|
||||
y += difference / 2;
|
||||
|
||||
this.workspacesView.setGeometry(x, y, width, height, difference);
|
||||
|
||||
let monitors = Main.layoutManager.monitors;
|
||||
let m = 0;
|
||||
for (let i = 0; i < monitors.length; i++) {
|
||||
if (i == this._primaryIndex) {
|
||||
this._workspacesViews[m].setClipRect(clipX, clipY,
|
||||
clipWidth, clipHeight);
|
||||
this._workspacesViews[m].setGeometry(x, y, width, height,
|
||||
difference);
|
||||
m++;
|
||||
} else if (!this._workspacesOnlyOnPrimary) {
|
||||
this._workspacesViews[m].setClipRect(monitors[i].x,
|
||||
monitors[i].y,
|
||||
monitors[i].width,
|
||||
monitors[i].height);
|
||||
this._workspacesViews[m].setGeometry(monitors[i].x,
|
||||
monitors[i].y,
|
||||
monitors[i].width,
|
||||
monitors[i].height, 0);
|
||||
m++;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_onRestacked: function() {
|
||||
@ -725,12 +904,14 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
stackIndices[stack[i].get_meta_window().get_stable_sequence()] = i;
|
||||
}
|
||||
|
||||
this.workspacesView.syncStacking(stackIndices);
|
||||
for (let i = 0; i < this._workspacesViews.length; i++)
|
||||
this._workspacesViews[i].syncStacking(stackIndices);
|
||||
|
||||
this._thumbnailsBox.syncStacking(stackIndices);
|
||||
},
|
||||
|
||||
_workspacesChanged: function() {
|
||||
let oldNumWorkspaces = this._workspaces.length;
|
||||
let oldNumWorkspaces = this._workspaces[0].length;
|
||||
let newNumWorkspaces = global.screen.n_workspaces;
|
||||
let active = global.screen.get_active_workspace_index();
|
||||
|
||||
@ -740,15 +921,24 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
this._updateAlwaysZoom();
|
||||
this._updateZoom();
|
||||
|
||||
if (this.workspacesView == null)
|
||||
if (this._workspacesViews == null)
|
||||
return;
|
||||
|
||||
let lostWorkspaces = [];
|
||||
if (newNumWorkspaces > oldNumWorkspaces) {
|
||||
// Assume workspaces are only added at the end
|
||||
for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
|
||||
let metaWorkspace = global.screen.get_workspace_by_index(w);
|
||||
this._workspaces[w] = new Workspace.Workspace(metaWorkspace, this._monitorIndex);
|
||||
let monitors = Main.layoutManager.monitors;
|
||||
let m = 0;
|
||||
for (let i = 0; i < monitors.length; i++) {
|
||||
if (this._workspacesOnlyOnPrimaryChanged &&
|
||||
i != this._primaryIndex)
|
||||
continue;
|
||||
|
||||
// Assume workspaces are only added at the end
|
||||
for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
|
||||
let metaWorkspace = global.screen.get_workspace_by_index(w);
|
||||
this._workspaces[m++][w] =
|
||||
new Workspace.Workspace(metaWorkspace, i);
|
||||
}
|
||||
}
|
||||
|
||||
this._thumbnailsBox.addThumbnails(oldNumWorkspaces, newNumWorkspaces - oldNumWorkspaces);
|
||||
@ -759,25 +949,28 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
let removedNum = oldNumWorkspaces - newNumWorkspaces;
|
||||
for (let w = 0; w < oldNumWorkspaces; w++) {
|
||||
let metaWorkspace = global.screen.get_workspace_by_index(w);
|
||||
if (this._workspaces[w].metaWorkspace != metaWorkspace) {
|
||||
if (this._workspaces[0][w].metaWorkspace != metaWorkspace) {
|
||||
removedIndex = w;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lostWorkspaces = this._workspaces.splice(removedIndex,
|
||||
removedNum);
|
||||
for (let i = 0; i < this._workspaces.length; i++) {
|
||||
lostWorkspaces = this._workspaces[i].splice(removedIndex,
|
||||
removedNum);
|
||||
|
||||
for (let l = 0; l < lostWorkspaces.length; l++) {
|
||||
lostWorkspaces[l].disconnectAll();
|
||||
lostWorkspaces[l].destroy();
|
||||
for (let l = 0; l < lostWorkspaces.length; l++) {
|
||||
lostWorkspaces[l].disconnectAll();
|
||||
lostWorkspaces[l].destroy();
|
||||
}
|
||||
}
|
||||
|
||||
this._thumbnailsBox.removeThumbmails(removedIndex, removedNum);
|
||||
}
|
||||
|
||||
this.workspacesView.updateWorkspaces(oldNumWorkspaces,
|
||||
newNumWorkspaces);
|
||||
for (let i = 0; i < this._workspacesViews.length; i++)
|
||||
this._workspacesViews[i].updateWorkspaces(oldNumWorkspaces,
|
||||
newNumWorkspaces);
|
||||
},
|
||||
|
||||
_updateZoom : function() {
|
||||
@ -789,7 +982,7 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
this._zoomOut = shouldZoom;
|
||||
this._updateWorkspacesGeometry();
|
||||
|
||||
if (!this.workspacesView)
|
||||
if (!this._workspacesViews)
|
||||
return;
|
||||
|
||||
Tweener.addTween(this,
|
||||
@ -797,7 +990,8 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
time: WORKSPACE_SWITCH_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
|
||||
this.workspacesView.updateWindowPositions();
|
||||
for (let i = 0; i < this._workspacesViews.length; i++)
|
||||
this._workspacesViews[i].updateWindowPositions();
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -35,6 +35,7 @@ kn
|
||||
ku
|
||||
lt
|
||||
lv
|
||||
mk
|
||||
mr
|
||||
ms
|
||||
nb
|
||||
@ -47,6 +48,7 @@ pt
|
||||
pt_BR
|
||||
ro
|
||||
ru
|
||||
si
|
||||
sk
|
||||
sl
|
||||
sr
|
||||
|
@ -1,5 +1,7 @@
|
||||
data/gnome-shell.desktop.in.in
|
||||
data/gnome-shell-extension-prefs.desktop.in.in
|
||||
data/org.gnome.shell.gschema.xml.in
|
||||
js/extensionPrefs/main.js
|
||||
js/gdm/loginDialog.js
|
||||
js/gdm/powerMenu.js
|
||||
js/misc/util.js
|
||||
@ -36,6 +38,7 @@ js/ui/status/volume.js
|
||||
js/ui/telepathyClient.js
|
||||
js/ui/userMenu.js
|
||||
js/ui/viewSelector.js
|
||||
js/ui/wanda.js
|
||||
js/ui/windowAttentionHandler.js
|
||||
src/gvc/gvc-mixer-control.c
|
||||
src/main.c
|
||||
|
@ -1,2 +1,2 @@
|
||||
data/gnome-shell.desktop.in
|
||||
data/gnome-shell-clock-preferences.desktop.in
|
||||
data/gnome-shell-extension-prefs.desktop.in
|
||||
|
4
po/it.po
4
po/it.po
@ -11,7 +11,7 @@ msgstr ""
|
||||
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
|
||||
"shell&keywords=I18N+L10N&component=general\n"
|
||||
"POT-Creation-Date: 2011-10-04 20:49+0000\n"
|
||||
"PO-Revision-Date: 2011-10-17 10:30+0200\n"
|
||||
"PO-Revision-Date: 2012-01-04 10:19+0100\n"
|
||||
"Last-Translator: Luca Ferretti <lferrett@gnome.org>\n"
|
||||
"Language-Team: Italian <tp@lists.linux.it>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -583,7 +583,7 @@ msgstr[1] "Il sistema verrà spento automaticamente tra %d secondi."
|
||||
|
||||
#: ../js/ui/endSessionDialog.js:87
|
||||
msgid "Powering off the system."
|
||||
msgstr "pegnimento del sistema."
|
||||
msgstr "Spegnimento del sistema."
|
||||
|
||||
#: ../js/ui/endSessionDialog.js:98
|
||||
msgid "Click Restart to quit these applications and restart the system."
|
||||
|
723
po/zh_CN.po
723
po/zh_CN.po
File diff suppressed because it is too large
Load Diff
688
po/zh_HK.po
688
po/zh_HK.po
File diff suppressed because it is too large
Load Diff
691
po/zh_TW.po
691
po/zh_TW.po
File diff suppressed because it is too large
Load Diff
@ -11,39 +11,16 @@ st_cflags = \
|
||||
|
||||
st_built_sources = \
|
||||
st-enum-types.h \
|
||||
st-enum-types.c \
|
||||
st-marshal.h \
|
||||
st-marshal.c
|
||||
st-enum-types.c
|
||||
|
||||
BUILT_SOURCES += $(st_built_sources)
|
||||
|
||||
EXTRA_DIST += \
|
||||
st/test-theme.css \
|
||||
st/st-marshal.list \
|
||||
st/st-enum-types.h.in \
|
||||
st/st-enum-types.c.in
|
||||
|
||||
CLEANFILES += stamp-st-marshal.h stamp-st-enum-types.h
|
||||
|
||||
st-marshal.h: stamp-st-marshal.h
|
||||
@true
|
||||
stamp-st-marshal.h: Makefile st/st-marshal.list
|
||||
$(AM_V_GEN) $(GLIB_GENMARSHAL) \
|
||||
--prefix=_st_marshal \
|
||||
--header \
|
||||
$(srcdir)/st/st-marshal.list > $@.tmp && \
|
||||
(cmp -s $@.tmp st-marshal.h || cp -f $@.tmp st-marshal.h) && \
|
||||
rm -f $@.tmp && \
|
||||
echo timestamp > $(@F)
|
||||
|
||||
st-marshal.c: Makefile st/st-marshal.list
|
||||
$(AM_V_GEN) (echo "#include \"st-marshal.h\"" ; \
|
||||
$(GLIB_GENMARSHAL) \
|
||||
--prefix=_st_marshal \
|
||||
--body \
|
||||
$(srcdir)/st/st-marshal.list ) > $@.tmp && \
|
||||
cp -f $@.tmp st-marshal.c && \
|
||||
rm -f $@.tmp
|
||||
CLEANFILES += stamp-st-enum-types.h
|
||||
|
||||
st-enum-types.h: stamp-st-enum-types.h Makefile
|
||||
@true
|
||||
|
@ -5,14 +5,8 @@ tray_cflags = \
|
||||
$(TRAY_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
tray_built_sources = \
|
||||
na-marshal.h \
|
||||
na-marshal.c
|
||||
|
||||
BUILT_SOURCES += $(tray_built_sources)
|
||||
|
||||
TRAY_STAMP_FILES = stamp-na-marshal.h
|
||||
|
||||
# please, keep this sorted alphabetically
|
||||
tray_source = \
|
||||
tray/na-tray-child.c \
|
||||
@ -21,26 +15,6 @@ tray_source = \
|
||||
tray/na-tray-manager.h \
|
||||
$(NULL)
|
||||
|
||||
na-marshal.h: stamp-na-marshal.h
|
||||
@true
|
||||
stamp-na-marshal.h: Makefile tray/na-marshal.list
|
||||
$(AM_V_GEN) $(GLIB_GENMARSHAL) \
|
||||
--prefix=_na_marshal \
|
||||
--header \
|
||||
$(srcdir)/tray/na-marshal.list > xgen-tmh && \
|
||||
(cmp -s xgen-tmh na-marshal.h || cp -f xgen-tmh na-marshal.h) && \
|
||||
rm -f xgen-tmh && \
|
||||
echo timestamp > $(@F)
|
||||
|
||||
na-marshal.c: Makefile tray/na-marshal.list
|
||||
$(AM_V_GEN) (echo "#include \"na-marshal.h\"" ; \
|
||||
$(GLIB_GENMARSHAL) \
|
||||
--prefix=_na_marshal \
|
||||
--body \
|
||||
$(srcdir)/tray/na-marshal.list ) > xgen-tmc && \
|
||||
cp -f xgen-tmc na-marshal.c && \
|
||||
rm -f xgen-tmc
|
||||
|
||||
noinst_LTLIBRARIES += libtray.la
|
||||
|
||||
libtray_la_LIBADD = $(TRAY_LIBS)
|
||||
@ -52,6 +26,3 @@ libtray_la_CPPFLAGS = $(tray_cflags)
|
||||
libtray_la_LDFLAGS = $(LDADD)
|
||||
|
||||
CLEANFILES += $(TRAY_STAMP_FILES) $(BUILT_SOURCES)
|
||||
|
||||
EXTRA_DIST += \
|
||||
tray/na-marshal.list
|
||||
|
113
src/Makefile.am
113
src/Makefile.am
@ -27,8 +27,8 @@ CLEANFILES += $(service_DATA)
|
||||
|
||||
CLEANFILES += $(gir_DATA) $(typelib_DATA)
|
||||
|
||||
bin_SCRIPTS += gnome-shell-extension-tool
|
||||
EXTRA_DIST += gnome-shell-extension-tool.in
|
||||
bin_SCRIPTS += gnome-shell-extension-tool gnome-shell-extension-prefs
|
||||
EXTRA_DIST += gnome-shell-extension-tool.in gnome-shell-extension-prefs.in
|
||||
bin_PROGRAMS = gnome-shell-real
|
||||
|
||||
if USE_JHBUILD_WRAPPER_SCRIPT
|
||||
@ -48,15 +48,17 @@ uninstall-hook:
|
||||
rm -f $(DESTDIR)$(bindir)/gnome-shell
|
||||
|
||||
generated_script_substitutions = \
|
||||
-e "s|@bindir[@]|$(bindir)|" \
|
||||
-e "s|@datadir[@]|$(datadir)|" \
|
||||
-e "s|@libexecdir[@]|$(libexecdir)|" \
|
||||
-e "s|@libdir[@]|$(libdir)|" \
|
||||
-e "s|@JHBUILD_TYPELIBDIR[@]|$(JHBUILD_TYPELIBDIR)|" \
|
||||
-e "s|@pkgdatadir[@]|$(pkgdatadir)|" \
|
||||
-e "s|@PYTHON[@]|$(PYTHON)|" \
|
||||
-e "s|@VERSION[@]|$(VERSION)|" \
|
||||
-e "s|@sysconfdir[@]|$(sysconfdir)|"
|
||||
-e "s|@bindir[@]|$(bindir)|g" \
|
||||
-e "s|@datadir[@]|$(datadir)|g" \
|
||||
-e "s|@libexecdir[@]|$(libexecdir)|g" \
|
||||
-e "s|@libdir[@]|$(libdir)|g" \
|
||||
-e "s|@pkglibdir[@]|$(pkglibdir)|g" \
|
||||
-e "s|@JHBUILD_TYPELIBDIR[@]|$(JHBUILD_TYPELIBDIR)|g" \
|
||||
-e "s|@pkgdatadir[@]|$(pkgdatadir)|g" \
|
||||
-e "s|@PYTHON[@]|$(PYTHON)|g" \
|
||||
-e "s|@VERSION[@]|$(VERSION)|g" \
|
||||
-e "s|@sysconfdir[@]|$(sysconfdir)|g" \
|
||||
-e "s|@GJS_CONSOLE[@]|$(GJS_CONSOLE)|g"
|
||||
|
||||
gnome-shell-jhbuild: gnome-shell-jhbuild.in gnome-shell-real Makefile
|
||||
$(AM_V_GEN) sed $(generated_script_substitutions) $< > $@.tmp && mv $@.tmp $@ && chmod a+x $@
|
||||
@ -66,6 +68,9 @@ gnome-shell-jhbuild: gnome-shell-jhbuild.in gnome-shell-real Makefile
|
||||
gnome-shell-extension-tool: gnome-shell-extension-tool.in Makefile
|
||||
$(AM_V_GEN) sed $(generated_script_substitutions) $< > $@.tmp && mv $@.tmp $@ && chmod a+x $@
|
||||
|
||||
gnome-shell-extension-prefs: gnome-shell-extension-prefs.in Makefile
|
||||
$(AM_V_GEN) sed $(generated_script_substitutions) $< > $@.tmp && mv $@.tmp $@ && chmod a+x $@
|
||||
|
||||
CLEANFILES += gnome-shell $(bin_SCRIPTS)
|
||||
|
||||
include Makefile-st.am
|
||||
@ -86,16 +91,13 @@ gnome_shell_cflags = \
|
||||
-DJSDIR=\"$(pkgdatadir)/js\"
|
||||
|
||||
privlibdir = $(pkglibdir)
|
||||
privlib_LTLIBRARIES = libgnome-shell.la
|
||||
privlib_LTLIBRARIES = libgnome-shell.la libgnome-shell-js.la
|
||||
|
||||
shell_built_sources = \
|
||||
shell-marshal.h \
|
||||
shell-marshal.c \
|
||||
shell-enum-types.h \
|
||||
shell-enum-types.c
|
||||
|
||||
BUILT_SOURCES += $(shell_built_sources)
|
||||
EXTRA_DIST += shell-marshal.list
|
||||
|
||||
shell_public_headers_h = \
|
||||
shell-app.h \
|
||||
@ -111,6 +113,7 @@ shell_public_headers_h = \
|
||||
shell-mount-operation.h \
|
||||
shell-network-agent.h \
|
||||
shell-perf-log.h \
|
||||
shell-screen-grabber.h \
|
||||
shell-slicer.h \
|
||||
shell-stack.h \
|
||||
shell-tp-client.h \
|
||||
@ -121,9 +124,18 @@ shell_public_headers_h = \
|
||||
shell-wm.h \
|
||||
shell-xfixes-cursor.h
|
||||
|
||||
shell_private_sources = \
|
||||
gactionmuxer.h \
|
||||
gactionmuxer.c \
|
||||
gactionobservable.h \
|
||||
gactionobservable.c \
|
||||
gactionobserver.h \
|
||||
gactionobserver.c
|
||||
|
||||
libgnome_shell_la_SOURCES = \
|
||||
$(shell_built_sources) \
|
||||
$(shell_public_headers_h) \
|
||||
$(shell_private_sources) \
|
||||
shell-app-private.h \
|
||||
shell-app-system-private.h \
|
||||
shell-embedded-window-private.h \
|
||||
@ -149,6 +161,7 @@ libgnome_shell_la_SOURCES = \
|
||||
shell-perf-log.c \
|
||||
shell-polkit-authentication-agent.h \
|
||||
shell-polkit-authentication-agent.c \
|
||||
shell-screen-grabber.c \
|
||||
shell-slicer.c \
|
||||
shell-stack.c \
|
||||
shell-tp-client.c \
|
||||
@ -157,10 +170,12 @@ libgnome_shell_la_SOURCES = \
|
||||
shell-util.c \
|
||||
shell-window-tracker.c \
|
||||
shell-wm.c \
|
||||
shell-xfixes-cursor.c
|
||||
shell-xfixes-cursor.c \
|
||||
$(NULL)
|
||||
|
||||
|
||||
libgnome_shell_la_gir_sources = \
|
||||
$(filter-out %-private.h $(shell_recorder_non_gir_sources), $(shell_public_headers_h) $(libgnome_shell_la_SOURCES))
|
||||
$(filter-out %-private.h $(shell_private_sources), $(shell_public_headers_h) $(libgnome_shell_la_SOURCES))
|
||||
|
||||
gnome_shell_real_SOURCES = \
|
||||
main.c
|
||||
@ -168,6 +183,25 @@ gnome_shell_real_CPPFLAGS = $(gnome_shell_cflags)
|
||||
gnome_shell_real_LDADD = libgnome-shell.la $(libgnome_shell_la_LIBADD)
|
||||
gnome_shell_real_DEPENDENCIES = libgnome-shell.la
|
||||
|
||||
EXTRA_DIST += test-gapplication.js
|
||||
|
||||
########################################
|
||||
|
||||
libgnome_shell_js_la_SOURCES = \
|
||||
shell-js.h \
|
||||
shell-js.c \
|
||||
$(NULL)
|
||||
|
||||
libgnome_shell_js_la_LIBADD = \
|
||||
$(GNOME_SHELL_JS_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
libgnome_shell_js_la_LDFLAGS = \
|
||||
-avoid-version
|
||||
|
||||
libgnome_shell_js_la_CPPFLAGS = \
|
||||
$(GNOME_SHELL_JS_CFLAGS)
|
||||
|
||||
########################################
|
||||
|
||||
shell_recorder_sources = \
|
||||
@ -175,12 +209,16 @@ shell_recorder_sources = \
|
||||
shell-recorder.h
|
||||
|
||||
# Custom element is an internal detail
|
||||
shell_recorder_non_gir_sources = \
|
||||
shell-recorder-src.c \
|
||||
shell-recorder-src.h
|
||||
|
||||
if BUILD_RECORDER
|
||||
libgnome_shell_la_SOURCES += $(shell_recorder_sources) $(shell_recorder_non_gir_sources)
|
||||
libgnome_shell_la_SOURCES += $(shell_recorder_sources)
|
||||
|
||||
shell_recorder_private_sources = \
|
||||
shell-recorder-src.c \
|
||||
shell-recorder-src.h \
|
||||
$(NULL)
|
||||
|
||||
shell_private_sources += $(shell_recorder_private_sources)
|
||||
|
||||
noinst_PROGRAMS += test-recorder
|
||||
|
||||
@ -188,7 +226,9 @@ test_recorder_CPPFLAGS = $(TEST_SHELL_RECORDER_CFLAGS)
|
||||
test_recorder_LDADD = $(TEST_SHELL_RECORDER_LIBS)
|
||||
|
||||
test_recorder_SOURCES = \
|
||||
$(shell_recorder_sources) $(shell_recorder_non_gir_sources) \
|
||||
$(shell_recorder_sources) $(shell_recorder_private_sources) \
|
||||
shell-screen-grabber.c \
|
||||
shell-screen-grabber.h \
|
||||
test-recorder.c
|
||||
endif BUILD_RECORDER
|
||||
|
||||
@ -213,28 +253,6 @@ run_js_test_SOURCES = \
|
||||
|
||||
########################################
|
||||
|
||||
shell-marshal.h: stamp-shell-marshal.h
|
||||
@true
|
||||
stamp-shell-marshal.h: Makefile shell-marshal.list
|
||||
$(AM_V_GEN) $(GLIB_GENMARSHAL) \
|
||||
--prefix=_shell_marshal \
|
||||
--header \
|
||||
$(srcdir)/shell-marshal.list > xgen-smh && \
|
||||
(cmp -s xgen-smh shell-marshal.h || cp -f xgen-smh shell-marshal.h) && \
|
||||
rm -f xgen-smh && \
|
||||
echo timestamp > $(@F)
|
||||
CLEANFILES += stamp-shell-marshal.h
|
||||
|
||||
shell-marshal.c: Makefile shell-marshal.list
|
||||
$(AM_V_GEN) (echo "#include \"shell-marshal.h\"" ; \
|
||||
$(GLIB_GENMARSHAL) \
|
||||
--prefix=_shell_marshal \
|
||||
--body \
|
||||
$(srcdir)/shell-marshal.list ) > xgen-smc && \
|
||||
cp -f xgen-smc shell-marshal.c && \
|
||||
rm -f xgen-smc
|
||||
|
||||
|
||||
shell-enum-types.h: stamp-shell-enum-types.h Makefile
|
||||
@true
|
||||
stamp-shell-enum-types.h: $(srcdir)/shell-enum-types.h.in $(shell_public_headers_h)
|
||||
@ -279,6 +297,13 @@ Shell_0_1_gir_SCANNERFLAGS = --include-uninstalled=$(builddir)/St-1.0.gir \
|
||||
INTROSPECTION_GIRS += Shell-0.1.gir
|
||||
CLEANFILES += Shell-0.1.gir
|
||||
|
||||
ShellJS-0.1.gir: libgnome-shell-js.la
|
||||
ShellJS_0_1_gir_CFLAGS = $(libgnome_shell_la_CPPFLAGS) -I $(srcdir)
|
||||
ShellJS_0_1_gir_LIBS = libgnome-shell-js.la
|
||||
ShellJS_0_1_gir_FILES = $(libgnome_shell_js_la_SOURCES)
|
||||
INTROSPECTION_GIRS += ShellJS-0.1.gir
|
||||
CLEANFILES += ShellJS-0.1.gir
|
||||
|
||||
St-1.0.gir: libst-1.0.la
|
||||
St_1_0_gir_INCLUDES = Clutter-1.0 Gtk-3.0
|
||||
St_1_0_gir_CFLAGS = $(st_cflags) -DST_COMPILATION
|
||||
|
@ -150,7 +150,7 @@ calendar_sources_class_init (CalendarSourcesClass *klass)
|
||||
appointment_sources_changed),
|
||||
NULL,
|
||||
NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
NULL,
|
||||
G_TYPE_NONE,
|
||||
0);
|
||||
|
||||
@ -162,7 +162,7 @@ calendar_sources_class_init (CalendarSourcesClass *klass)
|
||||
task_sources_changed),
|
||||
NULL,
|
||||
NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
NULL,
|
||||
G_TYPE_NONE,
|
||||
0);
|
||||
}
|
||||
|
533
src/gactionmuxer.c
Normal file
533
src/gactionmuxer.c
Normal file
@ -0,0 +1,533 @@
|
||||
/*
|
||||
* Copyright © 2011 Canonical Limited
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the licence, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Author: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gactionmuxer.h"
|
||||
|
||||
#include "gactionobservable.h"
|
||||
#include "gactionobserver.h"
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* SECTION:gactionmuxer
|
||||
* @short_description: Aggregate and monitor several action groups
|
||||
*
|
||||
* #GActionMuxer is a #GActionGroup and #GActionObservable that is
|
||||
* capable of containing other #GActionGroup instances.
|
||||
*
|
||||
* The typical use is aggregating all of the actions applicable to a
|
||||
* particular context into a single action group, with namespacing.
|
||||
*
|
||||
* Consider the case of two action groups -- one containing actions
|
||||
* applicable to an entire application (such as 'quit') and one
|
||||
* containing actions applicable to a particular window in the
|
||||
* application (such as 'fullscreen').
|
||||
*
|
||||
* In this case, each of these action groups could be added to a
|
||||
* #GActionMuxer with the prefixes "app" and "win", respectively. This
|
||||
* would expose the actions as "app.quit" and "win.fullscreen" on the
|
||||
* #GActionGroup interface presented by the #GActionMuxer.
|
||||
*
|
||||
* Activations and state change requests on the #GActionMuxer are wired
|
||||
* through to the underlying action group in the expected way.
|
||||
*
|
||||
* This class is typically only used at the site of "consumption" of
|
||||
* actions (eg: when displaying a menu that contains many actions on
|
||||
* different objects).
|
||||
*/
|
||||
|
||||
static void g_action_muxer_group_iface_init (GActionGroupInterface *iface);
|
||||
static void g_action_muxer_observable_iface_init (GActionObservableInterface *iface);
|
||||
|
||||
typedef GObjectClass GActionMuxerClass;
|
||||
|
||||
struct _GActionMuxer
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GHashTable *actions;
|
||||
GHashTable *groups;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GActionMuxer, g_action_muxer, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, g_action_muxer_group_iface_init)
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_OBSERVABLE, g_action_muxer_observable_iface_init))
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GActionMuxer *muxer;
|
||||
GSList *watchers;
|
||||
gchar *fullname;
|
||||
} Action;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GActionMuxer *muxer;
|
||||
GActionGroup *group;
|
||||
gchar *prefix;
|
||||
gulong handler_ids[4];
|
||||
} Group;
|
||||
|
||||
static gchar **
|
||||
g_action_muxer_list_actions (GActionGroup *action_group)
|
||||
{
|
||||
GActionMuxer *muxer = G_ACTION_MUXER (action_group);
|
||||
|
||||
return (gchar **) muxer->groups;
|
||||
}
|
||||
|
||||
static Group *
|
||||
g_action_muxer_find_group (GActionMuxer *muxer,
|
||||
const gchar **name)
|
||||
{
|
||||
const gchar *dot;
|
||||
gchar *prefix;
|
||||
Group *group;
|
||||
|
||||
dot = strchr (*name, '.');
|
||||
|
||||
if (!dot)
|
||||
return NULL;
|
||||
|
||||
prefix = g_strndup (*name, dot - *name);
|
||||
group = g_hash_table_lookup (muxer->groups, prefix);
|
||||
g_free (prefix);
|
||||
|
||||
*name = dot + 1;
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
static Action *
|
||||
g_action_muxer_lookup_action (GActionMuxer *muxer,
|
||||
const gchar *prefix,
|
||||
const gchar *action_name,
|
||||
gchar **fullname)
|
||||
{
|
||||
Action *action;
|
||||
|
||||
*fullname = g_strconcat (prefix, ".", action_name, NULL);
|
||||
action = g_hash_table_lookup (muxer->actions, *fullname);
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_action_enabled_changed (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
gboolean enabled,
|
||||
gpointer user_data)
|
||||
{
|
||||
Group *group = user_data;
|
||||
gchar *fullname;
|
||||
Action *action;
|
||||
GSList *node;
|
||||
|
||||
action = g_action_muxer_lookup_action (group->muxer, group->prefix, action_name, &fullname);
|
||||
for (node = action ? action->watchers : NULL; node; node = node->next)
|
||||
g_action_observer_action_enabled_changed (node->data, G_ACTION_OBSERVABLE (group->muxer), fullname, enabled);
|
||||
g_action_group_action_enabled_changed (G_ACTION_GROUP (group->muxer), fullname, enabled);
|
||||
g_free (fullname);
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_action_state_changed (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
GVariant *state,
|
||||
gpointer user_data)
|
||||
{
|
||||
Group *group = user_data;
|
||||
gchar *fullname;
|
||||
Action *action;
|
||||
GSList *node;
|
||||
|
||||
action = g_action_muxer_lookup_action (group->muxer, group->prefix, action_name, &fullname);
|
||||
for (node = action ? action->watchers : NULL; node; node = node->next)
|
||||
g_action_observer_action_state_changed (node->data, G_ACTION_OBSERVABLE (group->muxer), fullname, state);
|
||||
g_action_group_action_state_changed (G_ACTION_GROUP (group->muxer), fullname, state);
|
||||
g_free (fullname);
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_action_added (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
gpointer user_data)
|
||||
{
|
||||
const GVariantType *parameter_type;
|
||||
Group *group = user_data;
|
||||
gboolean enabled;
|
||||
GVariant *state;
|
||||
|
||||
if (g_action_group_query_action (group->group, action_name, &enabled, ¶meter_type, NULL, NULL, &state))
|
||||
{
|
||||
gchar *fullname;
|
||||
Action *action;
|
||||
GSList *node;
|
||||
|
||||
action = g_action_muxer_lookup_action (group->muxer, group->prefix, action_name, &fullname);
|
||||
|
||||
for (node = action ? action->watchers : NULL; node; node = node->next)
|
||||
g_action_observer_action_added (node->data,
|
||||
G_ACTION_OBSERVABLE (group->muxer),
|
||||
fullname, parameter_type, enabled, state);
|
||||
|
||||
g_action_group_action_added (G_ACTION_GROUP (group->muxer), fullname);
|
||||
|
||||
if (state)
|
||||
g_variant_unref (state);
|
||||
|
||||
g_free (fullname);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_action_removed (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
gpointer user_data)
|
||||
{
|
||||
Group *group = user_data;
|
||||
gchar *fullname;
|
||||
Action *action;
|
||||
GSList *node;
|
||||
|
||||
action = g_action_muxer_lookup_action (group->muxer, group->prefix, action_name, &fullname);
|
||||
for (node = action ? action->watchers : NULL; node; node = node->next)
|
||||
g_action_observer_action_removed (node->data, G_ACTION_OBSERVABLE (group->muxer), fullname);
|
||||
g_action_group_action_removed (G_ACTION_GROUP (group->muxer), fullname);
|
||||
g_free (fullname);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_action_muxer_query_action (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
gboolean *enabled,
|
||||
const GVariantType **parameter_type,
|
||||
const GVariantType **state_type,
|
||||
GVariant **state_hint,
|
||||
GVariant **state)
|
||||
{
|
||||
GActionMuxer *muxer = G_ACTION_MUXER (action_group);
|
||||
Group *group;
|
||||
|
||||
group = g_action_muxer_find_group (muxer, &action_name);
|
||||
|
||||
if (!group)
|
||||
return FALSE;
|
||||
|
||||
return g_action_group_query_action (group->group, action_name, enabled,
|
||||
parameter_type, state_type, state_hint, state);
|
||||
}
|
||||
|
||||
static GVariant *
|
||||
get_platform_data (void)
|
||||
{
|
||||
gchar time[32];
|
||||
GVariantBuilder *builder;
|
||||
GVariant *result;
|
||||
|
||||
g_snprintf (time, 32, "_TIME%d", clutter_get_current_event_time ());
|
||||
|
||||
builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||||
|
||||
g_variant_builder_add (builder, "{sv}", "desktop-startup-id",
|
||||
g_variant_new_string (time));
|
||||
|
||||
result = g_variant_builder_end (builder);
|
||||
g_variant_builder_unref (builder);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_activate_action (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
GVariant *parameter)
|
||||
{
|
||||
GActionMuxer *muxer = G_ACTION_MUXER (action_group);
|
||||
Group *group;
|
||||
|
||||
group = g_action_muxer_find_group (muxer, &action_name);
|
||||
|
||||
if (group)
|
||||
{
|
||||
if (G_IS_REMOTE_ACTION_GROUP (group->group))
|
||||
g_remote_action_group_activate_action_full (G_REMOTE_ACTION_GROUP (group->group),
|
||||
action_name,
|
||||
parameter,
|
||||
get_platform_data ());
|
||||
else
|
||||
g_action_group_activate_action (group->group, action_name, parameter);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_change_action_state (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
GVariant *state)
|
||||
{
|
||||
GActionMuxer *muxer = G_ACTION_MUXER (action_group);
|
||||
Group *group;
|
||||
|
||||
group = g_action_muxer_find_group (muxer, &action_name);
|
||||
|
||||
if (group)
|
||||
{
|
||||
if (G_IS_REMOTE_ACTION_GROUP (group->group))
|
||||
g_remote_action_group_change_action_state_full (G_REMOTE_ACTION_GROUP (group->group),
|
||||
action_name,
|
||||
state,
|
||||
get_platform_data ());
|
||||
else
|
||||
g_action_group_change_action_state (group->group, action_name, state);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_unregister_internal (Action *action,
|
||||
gpointer observer)
|
||||
{
|
||||
GActionMuxer *muxer = action->muxer;
|
||||
GSList **ptr;
|
||||
|
||||
for (ptr = &action->watchers; *ptr; ptr = &(*ptr)->next)
|
||||
if ((*ptr)->data == observer)
|
||||
{
|
||||
*ptr = g_slist_remove (*ptr, observer);
|
||||
|
||||
if (action->watchers == NULL)
|
||||
{
|
||||
g_hash_table_remove (muxer->actions, action->fullname);
|
||||
g_free (action->fullname);
|
||||
|
||||
g_slice_free (Action, action);
|
||||
|
||||
g_object_unref (muxer);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_weak_notify (gpointer data,
|
||||
GObject *where_the_object_was)
|
||||
{
|
||||
Action *action = data;
|
||||
|
||||
g_action_muxer_unregister_internal (action, where_the_object_was);
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_register_observer (GActionObservable *observable,
|
||||
const gchar *name,
|
||||
GActionObserver *observer)
|
||||
{
|
||||
GActionMuxer *muxer = G_ACTION_MUXER (observable);
|
||||
Action *action;
|
||||
|
||||
action = g_hash_table_lookup (muxer->actions, name);
|
||||
|
||||
if (action == NULL)
|
||||
{
|
||||
action = g_slice_new (Action);
|
||||
action->muxer = g_object_ref (muxer);
|
||||
action->fullname = g_strdup (name);
|
||||
action->watchers = NULL;
|
||||
|
||||
g_hash_table_insert (muxer->actions, action->fullname, action);
|
||||
}
|
||||
|
||||
action->watchers = g_slist_prepend (action->watchers, observer);
|
||||
g_object_weak_ref (G_OBJECT (observer), g_action_muxer_weak_notify, action);
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_unregister_observer (GActionObservable *observable,
|
||||
const gchar *name,
|
||||
GActionObserver *observer)
|
||||
{
|
||||
GActionMuxer *muxer = G_ACTION_MUXER (observable);
|
||||
Action *action;
|
||||
|
||||
action = g_hash_table_lookup (muxer->actions, name);
|
||||
g_object_weak_unref (G_OBJECT (observer), g_action_muxer_weak_notify, action);
|
||||
g_action_muxer_unregister_internal (action, observer);
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_free_group (gpointer data)
|
||||
{
|
||||
Group *group = data;
|
||||
|
||||
g_object_unref (group->group);
|
||||
g_free (group->prefix);
|
||||
|
||||
g_slice_free (Group, group);
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_finalize (GObject *object)
|
||||
{
|
||||
GActionMuxer *muxer = G_ACTION_MUXER (object);
|
||||
|
||||
g_assert_cmpint (g_hash_table_size (muxer->actions), ==, 0);
|
||||
g_hash_table_unref (muxer->actions);
|
||||
g_hash_table_unref (muxer->groups);
|
||||
|
||||
G_OBJECT_CLASS (g_action_muxer_parent_class)
|
||||
->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_init (GActionMuxer *muxer)
|
||||
{
|
||||
muxer->actions = g_hash_table_new (g_str_hash, g_str_equal);
|
||||
muxer->groups = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_action_muxer_free_group);
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_observable_iface_init (GActionObservableInterface *iface)
|
||||
{
|
||||
iface->register_observer = g_action_muxer_register_observer;
|
||||
iface->unregister_observer = g_action_muxer_unregister_observer;
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_group_iface_init (GActionGroupInterface *iface)
|
||||
{
|
||||
iface->list_actions = g_action_muxer_list_actions;
|
||||
iface->query_action = g_action_muxer_query_action;
|
||||
iface->activate_action = g_action_muxer_activate_action;
|
||||
iface->change_action_state = g_action_muxer_change_action_state;
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_class_init (GObjectClass *class)
|
||||
{
|
||||
class->finalize = g_action_muxer_finalize;
|
||||
}
|
||||
|
||||
/*
|
||||
* g_action_muxer_insert:
|
||||
* @muxer: a #GActionMuxer
|
||||
* @prefix: the prefix string for the action group
|
||||
* @action_group: a #GActionGroup
|
||||
*
|
||||
* Adds the actions in @action_group to the list of actions provided by
|
||||
* @muxer. @prefix is prefixed to each action name, such that for each
|
||||
* action <varname>x</varname> in @action_group, there is an equivalent
|
||||
* action @prefix<literal>.</literal><varname>x</varname> in @muxer.
|
||||
*
|
||||
* For example, if @prefix is "<literal>app</literal>" and @action_group
|
||||
* contains an action called "<literal>quit</literal>", then @muxer will
|
||||
* now contain an action called "<literal>app.quit</literal>".
|
||||
*
|
||||
* If any #GActionObservers are registered for actions in the group,
|
||||
* "action_added" notifications will be emitted, as appropriate.
|
||||
*
|
||||
* @prefix must not contain a dot ('.').
|
||||
*/
|
||||
void
|
||||
g_action_muxer_insert (GActionMuxer *muxer,
|
||||
const gchar *prefix,
|
||||
GActionGroup *action_group)
|
||||
{
|
||||
gchar **actions;
|
||||
Group *group;
|
||||
gint i;
|
||||
|
||||
/* TODO: diff instead of ripout and replace */
|
||||
g_action_muxer_remove (muxer, prefix);
|
||||
|
||||
group = g_slice_new (Group);
|
||||
group->muxer = muxer;
|
||||
group->group = g_object_ref (action_group);
|
||||
group->prefix = g_strdup (prefix);
|
||||
|
||||
g_hash_table_insert (muxer->groups, group->prefix, group);
|
||||
|
||||
actions = g_action_group_list_actions (group->group);
|
||||
for (i = 0; actions[i]; i++)
|
||||
g_action_muxer_action_added (group->group, actions[i], group);
|
||||
g_strfreev (actions);
|
||||
|
||||
group->handler_ids[0] = g_signal_connect (group->group, "action-added",
|
||||
G_CALLBACK (g_action_muxer_action_added), group);
|
||||
group->handler_ids[1] = g_signal_connect (group->group, "action-removed",
|
||||
G_CALLBACK (g_action_muxer_action_removed), group);
|
||||
group->handler_ids[2] = g_signal_connect (group->group, "action-enabled-changed",
|
||||
G_CALLBACK (g_action_muxer_action_enabled_changed), group);
|
||||
group->handler_ids[3] = g_signal_connect (group->group, "action-state-changed",
|
||||
G_CALLBACK (g_action_muxer_action_state_changed), group);
|
||||
}
|
||||
|
||||
/*
|
||||
* g_action_muxer_remove:
|
||||
* @muxer: a #GActionMuxer
|
||||
* @prefix: the prefix of the action group to remove
|
||||
*
|
||||
* Removes a #GActionGroup from the #GActionMuxer.
|
||||
*
|
||||
* If any #GActionObservers are registered for actions in the group,
|
||||
* "action_removed" notifications will be emitted, as appropriate.
|
||||
*/
|
||||
void
|
||||
g_action_muxer_remove (GActionMuxer *muxer,
|
||||
const gchar *prefix)
|
||||
{
|
||||
Group *group;
|
||||
|
||||
group = g_hash_table_lookup (muxer->groups, prefix);
|
||||
|
||||
if (group != NULL)
|
||||
{
|
||||
gchar **actions;
|
||||
gint i;
|
||||
|
||||
g_hash_table_steal (muxer->groups, prefix);
|
||||
|
||||
actions = g_action_group_list_actions (group->group);
|
||||
for (i = 0; actions[i]; i++)
|
||||
g_action_muxer_action_removed (group->group, actions[i], group);
|
||||
g_strfreev (actions);
|
||||
|
||||
/* 'for loop' or 'four loop'? */
|
||||
for (i = 0; i < 4; i++)
|
||||
g_signal_handler_disconnect (group->group, group->handler_ids[i]);
|
||||
|
||||
g_action_muxer_free_group (group);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* g_action_muxer_new:
|
||||
*
|
||||
* Creates a new #GActionMuxer.
|
||||
*/
|
||||
GActionMuxer *
|
||||
g_action_muxer_new (void)
|
||||
{
|
||||
return g_object_new (G_TYPE_ACTION_MUXER, NULL);
|
||||
}
|
53
src/gactionmuxer.h
Normal file
53
src/gactionmuxer.h
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright © 2011 Canonical Limited
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the licence, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Author: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#ifndef __G_ACTION_MUXER_H__
|
||||
#define __G_ACTION_MUXER_H__
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_ACTION_MUXER (g_action_muxer_get_type ())
|
||||
#define G_ACTION_MUXER(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
|
||||
G_TYPE_ACTION_MUXER, GActionMuxer))
|
||||
#define G_IS_ACTION_MUXER(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
|
||||
G_TYPE_ACTION_MUXER))
|
||||
|
||||
typedef struct _GActionMuxer GActionMuxer;
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
GType g_action_muxer_get_type (void);
|
||||
G_GNUC_INTERNAL
|
||||
GActionMuxer * g_action_muxer_new (void);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
void g_action_muxer_insert (GActionMuxer *muxer,
|
||||
const gchar *prefix,
|
||||
GActionGroup *group);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
void g_action_muxer_remove (GActionMuxer *muxer,
|
||||
const gchar *prefix);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_ACTION_MUXER_H__ */
|
80
src/gactionobservable.c
Normal file
80
src/gactionobservable.c
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright © 2011 Canonical Limited
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* licence or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*
|
||||
* Authors: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gactionobservable.h"
|
||||
|
||||
G_DEFINE_INTERFACE (GActionObservable, g_action_observable, G_TYPE_OBJECT)
|
||||
|
||||
/*
|
||||
* SECTION:gactionobserable
|
||||
* @short_description: an interface implemented by objects that report
|
||||
* changes to actions
|
||||
*/
|
||||
|
||||
void
|
||||
g_action_observable_default_init (GActionObservableInterface *iface)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* g_action_observable_register_observer:
|
||||
* @observable: a #GActionObservable
|
||||
* @action_name: the name of the action
|
||||
* @observer: the #GActionObserver to which the events will be reported
|
||||
*
|
||||
* Registers @observer as being interested in changes to @action_name on
|
||||
* @observable.
|
||||
*/
|
||||
void
|
||||
g_action_observable_register_observer (GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
GActionObserver *observer)
|
||||
{
|
||||
g_return_if_fail (G_IS_ACTION_OBSERVABLE (observable));
|
||||
|
||||
G_ACTION_OBSERVABLE_GET_IFACE (observable)
|
||||
->register_observer (observable, action_name, observer);
|
||||
}
|
||||
|
||||
/*
|
||||
* g_action_observable_unregister_observer:
|
||||
* @observable: a #GActionObservable
|
||||
* @action_name: the name of the action
|
||||
* @observer: the #GActionObserver to which the events will be reported
|
||||
*
|
||||
* Removes the registration of @observer as being interested in changes
|
||||
* to @action_name on @observable.
|
||||
*
|
||||
* If the observer was registered multiple times, it must be
|
||||
* unregistered an equal number of times.
|
||||
*/
|
||||
void
|
||||
g_action_observable_unregister_observer (GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
GActionObserver *observer)
|
||||
{
|
||||
g_return_if_fail (G_IS_ACTION_OBSERVABLE (observable));
|
||||
|
||||
G_ACTION_OBSERVABLE_GET_IFACE (observable)
|
||||
->unregister_observer (observable, action_name, observer);
|
||||
}
|
64
src/gactionobservable.h
Normal file
64
src/gactionobservable.h
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright © 2011 Canonical Limited
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* licence or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*
|
||||
* Authors: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#ifndef __G_ACTION_OBSERVABLE_H__
|
||||
#define __G_ACTION_OBSERVABLE_H__
|
||||
|
||||
#include "gactionobserver.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_ACTION_OBSERVABLE (g_action_observable_get_type ())
|
||||
#define G_ACTION_OBSERVABLE(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
|
||||
G_TYPE_ACTION_OBSERVABLE, GActionObservable))
|
||||
#define G_IS_ACTION_OBSERVABLE(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
|
||||
G_TYPE_ACTION_OBSERVABLE))
|
||||
#define G_ACTION_OBSERVABLE_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \
|
||||
G_TYPE_ACTION_OBSERVABLE, GActionObservableInterface))
|
||||
|
||||
typedef struct _GActionObservableInterface GActionObservableInterface;
|
||||
|
||||
struct _GActionObservableInterface
|
||||
{
|
||||
GTypeInterface g_iface;
|
||||
|
||||
void (* register_observer) (GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
GActionObserver *observer);
|
||||
void (* unregister_observer) (GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
GActionObserver *observer);
|
||||
};
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
GType g_action_observable_get_type (void);
|
||||
G_GNUC_INTERNAL
|
||||
void g_action_observable_register_observer (GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
GActionObserver *observer);
|
||||
G_GNUC_INTERNAL
|
||||
void g_action_observable_unregister_observer (GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
GActionObserver *observer);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_ACTION_OBSERVABLE_H__ */
|
161
src/gactionobserver.c
Normal file
161
src/gactionobserver.c
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright © 2011 Canonical Limited
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* licence or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*
|
||||
* Authors: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gactionobserver.h"
|
||||
|
||||
G_DEFINE_INTERFACE (GActionObserver, g_action_observer, G_TYPE_OBJECT)
|
||||
|
||||
/**
|
||||
* SECTION:gactionobserver
|
||||
* @short_description: an interface implemented by objects that are
|
||||
* interested in monitoring actions for changes
|
||||
*
|
||||
* GActionObserver is a simple interface allowing objects that wish to
|
||||
* be notified of changes to actions to be notified of those changes.
|
||||
*
|
||||
* It is also possible to monitor changes to action groups using
|
||||
* #GObject signals, but there are a number of reasons that this
|
||||
* approach could become problematic:
|
||||
*
|
||||
* - there are four separate signals that must be manually connected
|
||||
* and disconnected
|
||||
*
|
||||
* - when a large number of different observers wish to monitor a
|
||||
* (usually disjoint) set of actions within the same action group,
|
||||
* there is only one way to avoid having all notifications delivered
|
||||
* to all observers: signal detail. In order to use signal detail,
|
||||
* each action name must be quarked, which is not always practical.
|
||||
*
|
||||
* - even if quarking is acceptable, #GObject signal details are
|
||||
* implemented by scanning a linked list, so there is no real
|
||||
* decrease in complexity
|
||||
*/
|
||||
|
||||
void
|
||||
g_action_observer_default_init (GActionObserverInterface *class)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* g_action_observer_action_added:
|
||||
* @observer: a #GActionObserver
|
||||
* @observable: the source of the event
|
||||
* @action_name: the name of the action
|
||||
* @enabled: %TRUE if the action is now enabled
|
||||
* @parameter_type: the parameter type for action invocations, or %NULL
|
||||
* if no parameter is required
|
||||
* @state: the current state of the action, or %NULL if the action is
|
||||
* stateless
|
||||
*
|
||||
* This function is called when an action that the observer is
|
||||
* registered to receive events for is added.
|
||||
*
|
||||
* This function should only be called by objects with which the
|
||||
* observer has explicitly registered itself to receive events.
|
||||
*/
|
||||
void
|
||||
g_action_observer_action_added (GActionObserver *observer,
|
||||
GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
const GVariantType *parameter_type,
|
||||
gboolean enabled,
|
||||
GVariant *state)
|
||||
{
|
||||
g_return_if_fail (G_IS_ACTION_OBSERVER (observer));
|
||||
|
||||
G_ACTION_OBSERVER_GET_IFACE (observer)
|
||||
->action_added (observer, observable, action_name, parameter_type, enabled, state);
|
||||
}
|
||||
|
||||
/*
|
||||
* g_action_observer_action_enabled_changed:
|
||||
* @observer: a #GActionObserver
|
||||
* @observable: the source of the event
|
||||
* @action_name: the name of the action
|
||||
* @enabled: %TRUE if the action is now enabled
|
||||
*
|
||||
* This function is called when an action that the observer is
|
||||
* registered to receive events for becomes enabled or disabled.
|
||||
*
|
||||
* This function should only be called by objects with which the
|
||||
* observer has explicitly registered itself to receive events.
|
||||
*/
|
||||
void
|
||||
g_action_observer_action_enabled_changed (GActionObserver *observer,
|
||||
GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
gboolean enabled)
|
||||
{
|
||||
g_return_if_fail (G_IS_ACTION_OBSERVER (observer));
|
||||
|
||||
G_ACTION_OBSERVER_GET_IFACE (observer)
|
||||
->action_enabled_changed (observer, observable, action_name, enabled);
|
||||
}
|
||||
|
||||
/*
|
||||
* g_action_observer_action_state_changed:
|
||||
* @observer: a #GActionObserver
|
||||
* @observable: the source of the event
|
||||
* @action_name: the name of the action
|
||||
* @state: the new state of the action
|
||||
*
|
||||
* This function is called when an action that the observer is
|
||||
* registered to receive events for changes its state.
|
||||
*
|
||||
* This function should only be called by objects with which the
|
||||
* observer has explicitly registered itself to receive events.
|
||||
*/
|
||||
void
|
||||
g_action_observer_action_state_changed (GActionObserver *observer,
|
||||
GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
GVariant *state)
|
||||
{
|
||||
g_return_if_fail (G_IS_ACTION_OBSERVER (observer));
|
||||
|
||||
G_ACTION_OBSERVER_GET_IFACE (observer)
|
||||
->action_state_changed (observer, observable, action_name, state);
|
||||
}
|
||||
|
||||
/*
|
||||
* g_action_observer_action_removed:
|
||||
* @observer: a #GActionObserver
|
||||
* @observable: the source of the event
|
||||
* @action_name: the name of the action
|
||||
*
|
||||
* This function is called when an action that the observer is
|
||||
* registered to receive events for is removed.
|
||||
*
|
||||
* This function should only be called by objects with which the
|
||||
* observer has explicitly registered itself to receive events.
|
||||
*/
|
||||
void
|
||||
g_action_observer_action_removed (GActionObserver *observer,
|
||||
GActionObservable *observable,
|
||||
const gchar *action_name)
|
||||
{
|
||||
g_return_if_fail (G_IS_ACTION_OBSERVER (observer));
|
||||
|
||||
G_ACTION_OBSERVER_GET_IFACE (observer)
|
||||
->action_removed (observer, observable, action_name);
|
||||
}
|
90
src/gactionobserver.h
Normal file
90
src/gactionobserver.h
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright © 2011 Canonical Limited
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* licence or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*
|
||||
* Authors: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#ifndef __G_ACTION_OBSERVER_H__
|
||||
#define __G_ACTION_OBSERVER_H__
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_ACTION_OBSERVER (g_action_observer_get_type ())
|
||||
#define G_ACTION_OBSERVER(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
|
||||
G_TYPE_ACTION_OBSERVER, GActionObserver))
|
||||
#define G_IS_ACTION_OBSERVER(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
|
||||
G_TYPE_ACTION_OBSERVER))
|
||||
#define G_ACTION_OBSERVER_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \
|
||||
G_TYPE_ACTION_OBSERVER, GActionObserverInterface))
|
||||
|
||||
typedef struct _GActionObserverInterface GActionObserverInterface;
|
||||
typedef struct _GActionObservable GActionObservable;
|
||||
typedef struct _GActionObserver GActionObserver;
|
||||
|
||||
struct _GActionObserverInterface
|
||||
{
|
||||
GTypeInterface g_iface;
|
||||
|
||||
void (* action_added) (GActionObserver *observer,
|
||||
GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
const GVariantType *parameter_type,
|
||||
gboolean enabled,
|
||||
GVariant *state);
|
||||
void (* action_enabled_changed) (GActionObserver *observer,
|
||||
GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
gboolean enabled);
|
||||
void (* action_state_changed) (GActionObserver *observer,
|
||||
GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
GVariant *state);
|
||||
void (* action_removed) (GActionObserver *observer,
|
||||
GActionObservable *observable,
|
||||
const gchar *action_name);
|
||||
};
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
GType g_action_observer_get_type (void);
|
||||
G_GNUC_INTERNAL
|
||||
void g_action_observer_action_added (GActionObserver *observer,
|
||||
GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
const GVariantType *parameter_type,
|
||||
gboolean enabled,
|
||||
GVariant *state);
|
||||
G_GNUC_INTERNAL
|
||||
void g_action_observer_action_enabled_changed (GActionObserver *observer,
|
||||
GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
gboolean enabled);
|
||||
G_GNUC_INTERNAL
|
||||
void g_action_observer_action_state_changed (GActionObserver *observer,
|
||||
GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
GVariant *state);
|
||||
G_GNUC_INTERNAL
|
||||
void g_action_observer_action_removed (GActionObserver *observer,
|
||||
GActionObservable *observable,
|
||||
const gchar *action_name);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_ACTION_OBSERVER_H__ */
|
15
src/gnome-shell-extension-prefs.in
Normal file
15
src/gnome-shell-extension-prefs.in
Normal file
@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -n "$GI_TYPELIB_PATH" ]; then
|
||||
export GI_TYPELIB_PATH=@pkglibdir@
|
||||
else
|
||||
export GI_TYPELIB_PATH=@pkglibdir@:$GI_TYPELIB_PATH
|
||||
fi
|
||||
|
||||
if [ -n "$LD_LIBRARY_PATH" ] ; then
|
||||
export LD_LIBRARY_PATH=@pkglibdir@
|
||||
else
|
||||
export LD_LIBRARY_PATH=@pkglibdir@:$LD_LIBRARY_PATH
|
||||
fi
|
||||
|
||||
@GJS_CONSOLE@ -I @pkgdatadir@/js -c "const Main = imports.extensionPrefs.main; Main.main(ARGV);" "$@"
|
@ -323,7 +323,7 @@ gnome_shell_plugin_xevent_filter (MetaPlugin *plugin,
|
||||
#endif
|
||||
|
||||
if ((xev->xany.type == EnterNotify || xev->xany.type == LeaveNotify)
|
||||
&& xev->xcrossing.window == clutter_x11_get_stage_window (CLUTTER_STAGE (clutter_stage_get_default ())))
|
||||
&& xev->xcrossing.window == clutter_x11_get_stage_window (CLUTTER_STAGE (meta_plugin_get_stage (plugin))))
|
||||
{
|
||||
/* If the pointer enters a child of the stage window (eg, a
|
||||
* trayicon), we want to consider it to still be in the stage,
|
||||
|
@ -171,8 +171,7 @@ gvc_channel_map_class_init (GvcChannelMapClass *klass)
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GvcChannelMapClass, volume_changed),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__BOOLEAN,
|
||||
NULL, NULL, NULL,
|
||||
G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (GvcChannelMapPrivate));
|
||||
|
@ -2137,56 +2137,49 @@ gvc_mixer_control_class_init (GvcMixerControlClass *klass)
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GvcMixerControlClass, state_changed),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__UINT,
|
||||
NULL, NULL, NULL,
|
||||
G_TYPE_NONE, 1, G_TYPE_UINT);
|
||||
signals [STREAM_ADDED] =
|
||||
g_signal_new ("stream-added",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GvcMixerControlClass, stream_added),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__UINT,
|
||||
NULL, NULL, NULL,
|
||||
G_TYPE_NONE, 1, G_TYPE_UINT);
|
||||
signals [STREAM_REMOVED] =
|
||||
g_signal_new ("stream-removed",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GvcMixerControlClass, stream_removed),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__UINT,
|
||||
NULL, NULL, NULL,
|
||||
G_TYPE_NONE, 1, G_TYPE_UINT);
|
||||
signals [CARD_ADDED] =
|
||||
g_signal_new ("card-added",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GvcMixerControlClass, card_added),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__UINT,
|
||||
NULL, NULL, NULL,
|
||||
G_TYPE_NONE, 1, G_TYPE_UINT);
|
||||
signals [CARD_REMOVED] =
|
||||
g_signal_new ("card-removed",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GvcMixerControlClass, card_removed),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__UINT,
|
||||
NULL, NULL, NULL,
|
||||
G_TYPE_NONE, 1, G_TYPE_UINT);
|
||||
signals [DEFAULT_SINK_CHANGED] =
|
||||
g_signal_new ("default-sink-changed",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GvcMixerControlClass, default_sink_changed),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__UINT,
|
||||
NULL, NULL, NULL,
|
||||
G_TYPE_NONE, 1, G_TYPE_UINT);
|
||||
signals [DEFAULT_SOURCE_CHANGED] =
|
||||
g_signal_new ("default-source-changed",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GvcMixerControlClass, default_source_changed),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__UINT,
|
||||
NULL, NULL, NULL,
|
||||
G_TYPE_NONE, 1, G_TYPE_UINT);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (GvcMixerControlPrivate));
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user