Compare commits

..

6 Commits

Author SHA1 Message Date
Giovanni Campagna
352ad8f558 Modal stack: fix handling of destroyed actors
Destroyed modal actors should be completely removed from the modal
stack automatically, including leaving modality if needed.
This allows for destroying modal dialogs without calling close().
2012-05-21 18:47:47 +02:00
Giovanni Campagna
d5dc886748 ScreenShield: improve locking/modal policy
Ensure that the lightbox is above everything (including the screenlock
itself) when fading in - this allows for fading while showing the
unlock dialog. Also, don't pushModal again when already locked, or
we won't get out of it.
2012-05-21 18:47:47 +02:00
Giovanni Campagna
17044adf96 Add UnlockDialog for unlocking the screen shield
When the screenshield is deactivated, instead of going back to the
session immediately, prompt the user for authentication.
This essentially reinstates what used to be provided by gnome-screensaver.
2012-05-21 18:47:44 +02:00
Marina Zhurakhinskaya
9dfd805be2 lightbox: make sure this.shown set to false when start showing 2012-05-21 18:47:44 +02:00
Marina Zhurakhinskaya
bbbb6b685f screenShield: add initial functionality
We are replacing the gnome-screensaver module with with a screen shield
that is part of gnome-shell.

This patch fades out the screen on idle and shows a shield with a background
image when there is activity again. The shield can be removed with a key or
button press.
2012-05-21 18:47:40 +02:00
Giovanni Campagna
71c0d5f82d Login dialog: update for GDM port to GDBus
libgdmgreeter changed interface slightly due to the port to GDBus.
Update for that.

https://bugzilla.gnome.org/show_bug.cgi?id=676401
2012-05-20 17:16:17 +02:00
113 changed files with 6953 additions and 6841 deletions

114
NEWS
View File

@@ -1,117 +1,3 @@
3.5.3
=====
* calendar: Adapt to Evolution-Data-Server API changes [Matthew; #677402]
* messageTray: Don't show non urgent notifications while in fullscreen
[Adel; #677590]
* modalDialog: show dialogs on monitor with the mouse pointer [Tim; #642591]
* extensionSystem: Prepare for extension updating system [Jasper; #677586]
* appDisplay: Don't show apps in NoDisplay categories in the All view
[Jasper; #658176]
* st: Trigger theme updates on resolution changes [Florian; #677975]
* Always enable a11y [Bastien; #678095]
* telepathyClient: ignore invalidated channels [Guillaume; #677457]
* shell-app: Update app menu if necessary [Florian; #676238]
* Enable the Screen Reader menu item [Matthias; #663256]
* Disable unredirection when a modal operation is active [Giovanni]
* Make folks optional [Colin]
* Improve mount-operation support [Cosimo]
- Fix exception when showing password entry [#678428]
- Close the password entry on operation abort [#673787]
- autorun: Don't allow autorun for things we mount on startup [#660595]
- Turn passphrase prompt into a dialog [#674962]
- Implement org.Gtk.MountOperationHandler [#678516]
* Network menu improvements
- Sort Wifi networks by strength [Giovanni; #658946]
- Prefer wifi/3g over VPN in the panel [Cosimo; #672591]
* clock: Switch to using GnomeWallClock [Colin; #657074]
* remoteSearch: Allow to reference .desktop file for Title/Icon
[Florian; #678816]
* Fix memory leaks [Jasper, Pavel; #678079, #678406, #678737]
* Misc fixes [Florian, Giovanni, Guillaume, Jasper, Kjartan, Piotr, Rui;
#658955, #677497, #678396, #678502]
* Misc cleanups [Bastien, Florian, Jasper; #677426, #677515, #678096, #678416]
Contributors:
Matthew Barnes, Giovanni Campagna, Cosimo Cecchi, Matthias Clasen,
Guillaume Desmottes, Piotr Drąg, Adel Gadllah, Tim L, Kjartan Maraas,
Rui Matos, Florian Müllner, Bastien Nocera, Jasper St. Pierre, Colin Walters
Translations:
Matej Urbančič [sl], Yuri Kozlov [ru], Tom Tryfonidis [el],
Kjartan Maraas [nb], Žygimantas Beručka [lt], Luca Ferretti [it],
Khaled Hosny [ar], Daniel Mustieles [es], Fran Diéguez [gl], A S Alam [pa]
3.5.2
=====
* main: Move 'toggle-recording' binding into the shell [Florian; #674377]
* popupMenu: make sure to break the grab when the slider is not visible
[Stefano; #672713]
* st-theme-node-drawing: Don't use GL types [Neil; #672711]
* Mirror Evolution calendar settings into our own schema [Owen; #674424]
* shell-network-agent: don't crash if a request isn't found [Dan; #674961]
* notificationDaemon: Match app based on WM_CLASS [Jasper; #673761]
* NetworkMenu: use network-offline while loading [Giovanni; #674426]
* lookingGlass: Remove the Errors tab [Jasper; #675104]
* searchDisplay: Reset keyboard focus after displaying async results
[Rui; #675078]
* gdm: don't fail if fprintd is unavailable [Ray; #675006]
* messageTray: Fix scrolling up [Jasper; #661615]
* main: Close the recorder instead of pausing it [Rui; #675128]
* Accessibility [Alejandro]
- Use the proper label_actor for date menu on top panel [#675307]
- Set the proper role/label_actor for SearchResult.content [#672242]
- do not expose a label text if 'hidden' style class is used [#675341]
* Magnifier: Add brightness and contrast functionality [Joseph; #639851]
* theme: use a smaller border-radius for top bar [Jakub; #672430]
* placeDisplay: use new bookmark file location [Matthias; #675443]
* port all synchronous search providers to the async API [Jasper, Rui; #675328]
* NetworkAgent: disallow multiple requests for the same connection/setting
[Giovanni; #674961]
* userMenu: Update to latest mockups [Florian; #675802]
* util: Don't double-fork when spawning from Alt-F2 [Colin; #675789]
* messageTray: Make Source usable without subclassing [Jasper; #661236]
* panel: Check for appMenu button's reactivity before opening [Florian; #676316]
* Fix formatting of bluetooth passkey [Florian; #651251]
* notificationDaemon: Filter out file-transfer notifications [Jasper; #676175]
* Don't use global.log() [Jasper; #675790]
* Fix broken extension loading in some distributions [Owen, Alexandre; #670477]
* shell-app: Raise windows in reverse order to preserve the stacking
[Rui; #676371]
* Generalize gdm-mode [Florian; #676156]
* Switch string formatting to the one inside gjs [Jasper; #675479]
* extensionUtils: Support subdirectories in getCurrentExtension
[Jasper; #677001]
* panel: Refuse to add (legacy) status icons not part of the session mode
[Florian; #677058]
* Add an initial-setup mode [Matthias; #676697]
* status/keyboard: Port to the new input sources settings [Rui; #641531]
* NetworkMenu: show notifications for failed VPN connections [Giovanni; #676330]
* userMenu: Indicate progress on status changes [Florian; #659067]
* recorder: Honor "disable-save-to-disk" lockdown key [Rūdolfs; #673630]
* searchDisplay: Use the rowLimit we pass to the IconGrid [Christian; #675527]
* endSessionDialog: Factor out _updateDescription from _updateContent
[Alejandro; #674210]
* Fix empathy's appMenu freezing the shell [Alban; #676447]
* Code cleanups [Florian, Giovanni, Jasper; #672807, #672413, #676837, #676850,
#672272]
* Misc bug fixes [Alban, Florian, Giovanni, Guillaume, Jasper, Piotr, Rico,
Ron, Rui, Stefano; #659968, #672192, #673177, #673198, #674323, #675301,
#675370, #676347, #676806, #677097]
Contributors:
Alban Browaeys, Giovanni Campagna, Matthias Clasen, Guillaume Desmottes,
Piotr Drąg, Stefano Facchini, Rui Matos, Rūdolfs Mazurs, Florian Müllner,
Alejandro Piñeiro, Neil Roberts, Alexandre Rostovtsev, Joseph Scheuhammer,
Jakub Steiner, Jasper St. Pierre, Ray Strode, Owen Taylor, Rico Tzschichholz,
Colin Walters, Dan Winship, Ron Yorston
Translations:
OKANO Takayoshi [ja], Daniel Mustieles [es], Changwoo Ryu [ko],
Yaron Shahrabani [he], Fran Diéguez [gl], Jonh Wendell [pt_BR],
Kjartan Maraas [nb], Luca Ferretti [it], Tom Tryfonidis [el],
Sandeep Sheshrao Shedmake [mr], Takanori MATSUURA [ja], Dirgita [id],
Mantas Kriaučiūnas [lt], Matej Urbančič [sl], Jiro Matsuzawa [ja]
3.4.1
=====
* Fix crash that occurred when an icon theme change caused unexpected

View File

@@ -41,7 +41,7 @@
"It can be used only by extensions.gnome.org"
#define PLUGIN_MIME_STRING "application/x-gnome-shell-integration::Gnome Shell Integration Dummy Content-Type";
#define PLUGIN_API_VERSION 4
#define PLUGIN_API_VERSION 3
typedef struct {
GDBusProxy *proxy;
@@ -163,7 +163,6 @@ NP_Initialize(NPNetscapeFuncs *pfuncs, NPPluginFuncs *plugin)
plugin->newp = NPP_New;
plugin->destroy = NPP_Destroy;
plugin->getvalue = NPP_GetValue;
plugin->setwindow = NPP_SetWindow;
return NPERR_NO_ERROR;
}
@@ -268,7 +267,6 @@ typedef struct {
NPObject parent;
NPP instance;
GDBusProxy *proxy;
GSettings *settings;
NPObject *listener;
NPObject *restart_listener;
gint signal_id;
@@ -325,9 +323,6 @@ on_shell_appeared (GDBusConnection *connection,
}
}
#define SHELL_SCHEMA "org.gnome.shell"
#define ENABLED_EXTENSIONS_KEY "enabled-extensions"
static NPObject *
plugin_object_allocate (NPP instance,
NPClass *klass)
@@ -337,7 +332,6 @@ plugin_object_allocate (NPP instance,
obj->instance = instance;
obj->proxy = g_object_ref (data->proxy);
obj->settings = g_settings_new (SHELL_SCHEMA);
obj->signal_id = g_signal_connect (obj->proxy, "g-signal",
G_CALLBACK (on_shell_signal), obj);
@@ -498,61 +492,7 @@ plugin_enable_extension (PluginObject *obj,
NPString uuid,
gboolean enabled)
{
gboolean ret;
gchar *uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
gsize length;
gchar **uuids;
const gchar **new_uuids;
if (!uuid_is_valid (uuid_str))
{
g_free (uuid_str);
return FALSE;
}
uuids = g_settings_get_strv (obj->settings, ENABLED_EXTENSIONS_KEY);
length = g_strv_length (uuids);
if (enabled)
{
new_uuids = g_new (const gchar *, length + 2); /* New key, NULL */
memcpy (new_uuids, uuids, length * sizeof (*new_uuids));
new_uuids[length] = uuid_str;
new_uuids[length + 1] = NULL;
}
else
{
gsize i = 0, j = 0;
new_uuids = g_new (const gchar *, length);
for (i = 0; i < length; i ++)
{
if (g_str_equal (uuids[i], uuid_str))
continue;
new_uuids[j] = uuids[i];
j++;
}
new_uuids[j] = NULL;
}
ret = g_settings_set_strv (obj->settings,
ENABLED_EXTENSIONS_KEY,
new_uuids);
g_strfreev (uuids);
g_free (new_uuids);
g_free (uuid_str);
return ret;
}
static gboolean
plugin_install_extension (PluginObject *obj,
NPString uuid)
{
gchar *uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
if (!uuid_is_valid (uuid_str))
{
g_free (uuid_str);
@@ -560,7 +500,7 @@ plugin_install_extension (PluginObject *obj,
}
g_dbus_proxy_call (obj->proxy,
"InstallRemoteExtension",
(enabled ? "EnableExtension" : "DisableExtension"),
g_variant_new ("(s)", uuid_str),
G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout */
@@ -573,6 +513,40 @@ plugin_install_extension (PluginObject *obj,
return TRUE;
}
static gboolean
plugin_install_extension (PluginObject *obj,
NPString uuid,
NPString version_tag)
{
gchar *uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
gchar *version_tag_str;
if (!uuid_is_valid (uuid_str))
{
g_free (uuid_str);
return FALSE;
}
version_tag_str = g_strndup (version_tag.UTF8Characters,
version_tag.UTF8Length);
g_dbus_proxy_call (obj->proxy,
"InstallRemoteExtension",
g_variant_new ("(ss)",
uuid_str,
version_tag_str),
G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout */
NULL, /* cancellable */
NULL, /* callback */
NULL /* user_data */);
g_free (uuid_str);
g_free (version_tag_str);
return TRUE;
}
static gboolean
plugin_uninstall_extension (PluginObject *obj,
NPString uuid,
@@ -797,9 +771,11 @@ plugin_object_invoke (NPObject *npobj,
else if (name == install_extension_id)
{
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
if (!NPVARIANT_IS_STRING(args[1])) return FALSE;
return plugin_install_extension (obj,
NPVARIANT_TO_STRING(args[0]));
NPVARIANT_TO_STRING(args[0]),
NPVARIANT_TO_STRING(args[1]));
}
else if (name == uninstall_extension_id)
{
@@ -970,12 +946,3 @@ NPP_GetValue(NPP instance,
return NPERR_NO_ERROR;
}
/* Opera tries to call NPP_SetWindow without checking the
* NULL pointer beforehand. */
NPError
NPP_SetWindow(NPP instance,
NPWindow *window)
{
return NPERR_NO_ERROR;
}

View File

@@ -1,5 +1,5 @@
AC_PREREQ(2.63)
AC_INIT([gnome-shell],[3.5.3],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_INIT([gnome-shell],[3.4.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_SRCDIR([src/shell-global.c])
@@ -62,47 +62,28 @@ AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
CLUTTER_MIN_VERSION=1.9.16
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
GJS_MIN_VERSION=1.33.2
MUTTER_MIN_VERSION=3.5.3
GJS_MIN_VERSION=1.29.18
MUTTER_MIN_VERSION=3.4.1
FOLKS_MIN_VERSION=0.5.2
GTK_MIN_VERSION=3.3.9
GIO_MIN_VERSION=2.31.6
LIBECAL_MIN_VERSION=3.5.3
LIBEDATASERVER_MIN_VERSION=3.5.3
LIBEDATASERVERUI_MIN_VERSION=3.5.3
LIBECAL_MIN_VERSION=2.32.0
LIBEDATASERVER_MIN_VERSION=1.2.0
LIBEDATASERVERUI_MIN_VERSION=2.91.6
TELEPATHY_GLIB_MIN_VERSION=0.17.5
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
POLKIT_MIN_VERSION=0.100
STARTUP_NOTIFICATION_MIN_VERSION=0.11
GCR_MIN_VERSION=3.3.90
GNOME_DESKTOP_REQUIRED_VERSION=3.5.1
GNOME_MENUS_REQUIRED_VERSION=3.5.3
AC_ARG_WITH(folks,
AS_HELP_STRING([--with-folks],
[Enable folks support]),
[with_folks=$withval], [with_folks=yes])
if test x${with_folks} = xyes; then
FOLKS_REQUIREMENT="folks >= $FOLKS_MIN_VERSION"
AC_DEFINE([HAVE_FOLKS], [1], [folks support])
AC_SUBST([HAVE_FOLKS],[1])
else
FOLKS_REQUIREMENT=
AC_SUBST([HAVE_FOLKS],[0])
fi
AM_CONDITIONAL(BUILD_WITH_FOLKS, test x${with_folks} = xyes)
# Collect more than 20 libraries for a prize!
PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
libxml-2.0
gtk+-3.0 >= $GTK_MIN_VERSION
atk-bridge-2.0
$FOLKS_REQUIREMENT
folks >= $FOLKS_MIN_VERSION
libmutter >= $MUTTER_MIN_VERSION
gjs-internals-1.0 >= $GJS_MIN_VERSION
libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION
$recorder_modules
libgnome-menu-3.0 $recorder_modules
gdk-x11-3.0 libsoup-2.4
gl
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
@@ -114,8 +95,7 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
libnm-glib libnm-util gnome-keyring-1
gcr-3 >= $GCR_MIN_VERSION
gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED_VERSION)
gcr-3 >= $GCR_MIN_VERSION)
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
@@ -123,6 +103,10 @@ PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
GJS_VERSION=`$PKG_CONFIG --modversion gjs-internals-1.0`
AC_DEFINE_UNQUOTED([GJS_VERSION], ["$GJS_VERSION"], [The version of GJS we're linking to])
AC_SUBST([GJS_VERSION], ["$GJS_VERSION"])
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
saved_CFLAGS=$CFLAGS
@@ -137,7 +121,7 @@ PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.2 x11)
PKG_CHECK_MODULES(TRAY, gtk+-3.0)
PKG_CHECK_MODULES(GVC, libpulse libpulse-mainloop-glib gobject-2.0)
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.5.1)
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 0.1.7)
AC_MSG_CHECKING([for bluetooth support])
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
@@ -253,6 +237,31 @@ AC_ARG_ENABLE(jhbuild-wrapper-script,
AS_HELP_STRING([--enable-jhbuild-wrapper-script],[Make "gnome-shell" script work for jhbuild]),,enable_jhbuild_wrapper_script=no)
AM_CONDITIONAL(USE_JHBUILD_WRAPPER_SCRIPT, test "x$enable_jhbuild_wrapper_script" = xyes)
AC_MSG_CHECKING([location of system Certificate Authority list])
AC_ARG_WITH(ca-certificates,
[AC_HELP_STRING([--with-ca-certificates=@<:@path@:>@],
[path to system Certificate Authority list])])
if test "$with_ca_certificates" = "no"; then
AC_MSG_RESULT([disabled])
else
if test -z "$with_ca_certificates"; then
for f in /etc/pki/tls/certs/ca-bundle.crt \
/etc/ssl/certs/ca-certificates.crt; do
if test -f "$f"; then
with_ca_certificates="$f"
fi
done
if test -z "$with_ca_certificates"; then
AC_MSG_ERROR([could not find. Use --with-ca-certificates=path to set, or --without-ca-certificates to disable])
fi
fi
AC_MSG_RESULT($with_ca_certificates)
AC_DEFINE_UNQUOTED(SHELL_SYSTEM_CA_FILE, ["$with_ca_certificates"], [The system TLS CA list])
fi
AC_SUBST(SHELL_SYSTEM_CA_FILE,["$with_ca_certificates"])
BROWSER_PLUGIN_DIR="${BROWSER_PLUGIN_DIR:-"\${libdir}/mozilla/plugins"}"
AC_ARG_VAR([BROWSER_PLUGIN_DIR],[Where to install the plugin to])

View File

@@ -53,14 +53,9 @@ dist_theme_DATA = \
theme/ws-switch-arrow-up.svg \
theme/ws-switch-arrow-down.svg
gsettings_SCHEMAS = org.gnome.shell.gschema.xml
gsettings_SCHEMAS = org.gnome.shell.gschema.xml org.gnome.shell.evolution.calendar.gschema.xml
@INTLTOOL_XML_NOMERGE_RULE@
%.gschema.xml.in: %.gschema.xml.in.in Makefile
$(AM_V_GEN) sed -e 's|@GETTEXT_PACKAGE[@]|$(GETTEXT_PACKAGE)|g' \
$< > $@ || rm $@
@GSETTINGS_RULES@
# We need to compile schemas at make time
@@ -85,13 +80,13 @@ EXTRA_DIST = \
$(menu_DATA) \
$(shaders_DATA) \
$(convert_DATA) \
org.gnome.shell.gschema.xml.in.in
org.gnome.shell.evolution.calendar.gschema.xml.in \
org.gnome.shell.gschema.xml.in
CLEANFILES = \
gnome-shell.desktop.in \
gnome-shell-extension-prefs.in \
$(desktop_DATA) \
$(gsettings_SCHEMAS) \
gschemas.compiled \
org.gnome.shell.gschema.valid \
org.gnome.shell.gschema.xml.in
gschemas.compiled

View File

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

View File

@@ -61,6 +61,7 @@ value here is from the TpConnectionPresenceType enumeration.</_summary>
<_summary>Internally used to store the last session presence status for the user. The
value here is from the GsmPresenceStatus enumeration.</_summary>
</key>
<child name="clock" schema="org.gnome.shell.clock"/>
<child name="calendar" schema="org.gnome.shell.calendar"/>
<child name="recorder" schema="org.gnome.shell.recorder"/>
<child name="keybindings" schema="org.gnome.shell.keybindings"/>
@@ -107,6 +108,24 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
</key>
</schema>
<schema id="org.gnome.shell.clock" path="/org/gnome/shell/clock/"
gettext-domain="@GETTEXT_PACKAGE@">
<key name="show-seconds" type="b">
<default>false</default>
<_summary>Show time with seconds</_summary>
<_description>
If true, display seconds in time.
</_description>
</key>
<key name="show-date" type="b">
<default>false</default>
<_summary>Show date in clock</_summary>
<_description>
If true, display date in the clock, in addition to time.
</_description>
</key>
</schema>
<schema id="org.gnome.shell.recorder" path="/org/gnome/shell/recorder/"
gettext-domain="@GETTEXT_PACKAGE@">
<key name="framerate" type="i">

View File

@@ -436,10 +436,6 @@ StScrollBar StButton#vhandle:hover
-boxpointer-gap: 4px
}
#networkMenu {
spacing: 4px;
}
/* User Menu */
#panelUserMenu {
@@ -1719,7 +1715,7 @@ StScrollBar StButton#vhandle:hover
}
.lightbox {
background-color: rgba(0, 0, 0, 0.4);
background-color: black;
}
.flashspot {
@@ -1952,12 +1948,10 @@ StScrollBar StButton#vhandle:hover
padding-bottom: 8px;
}
.hidden {
color: rgba(0,0,0,0);
}
/* intentionally left transparent to avoid dialog changing size */
.prompt-dialog-null-label {
font-size: 10pt;
color: rgba(0,0,0,0);
padding-bottom: 8px;
}

View File

@@ -6,8 +6,9 @@ misc/config.js: misc/config.js.in Makefile
[ -d $(@D) ] || $(mkdir_p) $(@D) ; \
sed -e "s|[@]PACKAGE_NAME@|$(PACKAGE_NAME)|g" \
-e "s|[@]PACKAGE_VERSION@|$(PACKAGE_VERSION)|g" \
-e "s|[@]HAVE_FOLKS@|$(HAVE_FOLKS)|g" \
-e "s|[@]GJS_VERSION@|$(GJS_VERSION)|g" \
-e "s|[@]HAVE_BLUETOOTH@|$(HAVE_BLUETOOTH)|g" \
-e "s|[@]SHELL_SYSTEM_CA_FILE@|$(SHELL_SYSTEM_CA_FILE)|g" \
-e "s|[@]GETTEXT_PACKAGE@|$(GETTEXT_PACKAGE)|g" \
-e "s|[@]datadir@|$(datadir)|g" \
-e "s|[@]libexecdir@|$(libexecdir)|g" \
@@ -27,6 +28,7 @@ nobase_dist_js_DATA = \
misc/config.js \
misc/extensionUtils.js \
misc/fileUtils.js \
misc/format.js \
misc/gnomeSession.js \
misc/history.js \
misc/jsParse.js \
@@ -51,7 +53,6 @@ nobase_dist_js_DATA = \
ui/endSessionDialog.js \
ui/environment.js \
ui/extensionSystem.js \
ui/extensionDownloader.js \
ui/flashspot.js \
ui/iconGrid.js \
ui/keyboard.js \
@@ -66,7 +67,6 @@ nobase_dist_js_DATA = \
ui/messageTray.js \
ui/modalDialog.js \
ui/networkAgent.js \
ui/sessionMode.js \
ui/shellEntry.js \
ui/shellMountOperation.js \
ui/notificationDaemon.js \
@@ -78,6 +78,7 @@ nobase_dist_js_DATA = \
ui/popupMenu.js \
ui/remoteSearch.js \
ui/runDialog.js \
ui/screenShield.js \
ui/scripting.js \
ui/search.js \
ui/searchDisplay.js \
@@ -91,6 +92,7 @@ nobase_dist_js_DATA = \
ui/status/bluetooth.js \
ui/telepathyClient.js \
ui/tweener.js \
ui/unlockDialog.js \
ui/userMenu.js \
ui/viewSelector.js \
ui/wanda.js \

View File

@@ -6,11 +6,11 @@ const GObject = imports.gi.GObject;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Pango = imports.gi.Pango;
const Format = imports.format;
const _ = Gettext.gettext;
const Config = imports.misc.config;
const Format = imports.misc.format;
const ExtensionUtils = imports.misc.extensionUtils;
@@ -202,17 +202,23 @@ const Application = new Lang.Class({
},
_scanExtensions: function() {
let finder = new ExtensionUtils.ExtensionFinder();
finder.connect('extension-found', Lang.bind(this, this._extensionFound));
finder.scanExtensions();
},
ExtensionUtils.scanExtensions(Lang.bind(this, function(uuid, dir, type) {
if (ExtensionUtils.extensions[uuid] !== undefined)
return;
_extensionFound: function(signals, extension) {
let iter = this._model.append();
this._model.set(iter, [0, 1], [extension.uuid, extension.metadata.name]);
this._extensionIters[uuid] = iter;
},
let extension;
try {
extension = ExtensionUtils.createExtensionObject(uuid, dir, type);
} catch(e) {
global.logError('' + e);
return;
}
let iter = this._model.append();
this._model.set(iter, [0, 1], [uuid, extension.metadata.name]);
this._extensionIters[uuid] = iter;
}));
},
_onActivate: function() {
this._window.present();
@@ -251,7 +257,7 @@ function initEnvironment() {
},
logError: function(s) {
log('ERROR: ' + s);
global.log('ERROR: ' + s);
},
userdatadir: GLib.build_filenamev([GLib.get_user_data_dir(), 'gnome-shell'])
@@ -262,6 +268,7 @@ function initEnvironment() {
function main(argv) {
initEnvironment();
ExtensionUtils.init();
Gettext.bindtextdomain(Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
Gettext.textdomain(Config.GETTEXT_PACKAGE);

View File

@@ -39,8 +39,8 @@ const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog;
const Tweener = imports.ui.tweener;
const _PASSWORD_SERVICE_NAME = 'gdm-password';
const _FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
const PASSWORD_SERVICE_NAME = 'gdm-password';
const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
const _FADE_ANIMATION_TIME = 0.16;
const _RESIZE_ANIMATION_TIME = 0.25;
const _SCROLL_ANIMATION_TIME = 2.0;
@@ -747,15 +747,13 @@ const LoginDialog = new Lang.Class({
Lang.bind(this, this._onOpened));
this._userManager = AccountsService.UserManager.get_default()
this._greeterClient = new GdmGreeter.Client();
this._greeterClient = GdmGreeter.Server.new_for_greeter_sync(null);
this._greeterClient.open_connection();
this._greeterClient.call_start_conversation(_PASSWORD_SERVICE_NAME);
this._greeterClient.call_start_conversation_sync(PASSWORD_SERVICE_NAME, null);
this._greeterClient.connect('reset',
Lang.bind(this, this._onReset));
this._greeterClient.connect('default-session-changed',
this._greeterClient.connect('default-session-name-changed',
Lang.bind(this, this._onDefaultSessionChanged));
this._greeterClient.connect('info',
Lang.bind(this, this._onInfo));
@@ -769,8 +767,6 @@ const LoginDialog = new Lang.Class({
Lang.bind(this, this._onSessionOpened));
this._greeterClient.connect('timed-login-requested',
Lang.bind(this, this._onTimedLoginRequested));
this._greeterClient.connect('authentication-failed',
Lang.bind(this, this._onAuthenticationFailed));
this._greeterClient.connect('conversation-stopped',
Lang.bind(this, this._onConversationStopped));
@@ -899,7 +895,7 @@ const LoginDialog = new Lang.Class({
this._haveFingerprintReader = true;
if (this._haveFingerprintReader)
this._greeterClient.call_start_conversation(_FINGERPRINT_SERVICE_NAME);
this._greeterClient.call_start_conversation_sync(FINGERPRINT_SERVICE_NAME, null);
}));
},
@@ -918,7 +914,7 @@ const LoginDialog = new Lang.Class({
},
_onReset: function(client, serviceName) {
this._greeterClient.call_start_conversation(_PASSWORD_SERVICE_NAME);
this._greeterClient.call_start_conversation_sync(PASSWORD_SERVICE_NAME, null);
this._startFingerprintConversationIfNeeded();
let tasks = [this._hidePrompt,
@@ -954,7 +950,7 @@ const LoginDialog = new Lang.Class({
// We don't display fingerprint messages, because they
// have words like UPEK in them. Instead we use the messages
// as a cue to display our own message.
if (serviceName == _FINGERPRINT_SERVICE_NAME &&
if (serviceName == FINGERPRINT_SERVICE_NAME &&
this._haveFingerprintReader &&
(!this._promptFingerprintMessage.visible ||
this._promptFingerprintMessage.opacity != 255)) {
@@ -963,7 +959,7 @@ const LoginDialog = new Lang.Class({
return;
}
if (serviceName != _PASSWORD_SERVICE_NAME)
if (serviceName != PASSWORD_SERVICE_NAME)
return;
Main.notifyError(info);
},
@@ -971,13 +967,13 @@ const LoginDialog = new Lang.Class({
_onProblem: function(client, serviceName, problem) {
// we don't want to show auth failed messages to
// users who haven't enrolled their fingerprint.
if (serviceName != _PASSWORD_SERVICE_NAME)
if (serviceName != PASSWORD_SERVICE_NAME)
return;
Main.notifyError(problem);
},
_onCancel: function(client) {
this._greeterClient.call_cancel();
this._greeterClient.call_cancel_sync(null);
},
_fadeInPrompt: function() {
@@ -1084,7 +1080,7 @@ const LoginDialog = new Lang.Class({
let _text = this._promptEntry.get_text();
this._promptEntry.reactive = false;
this._promptEntry.add_style_pseudo_class('insensitive');
this._greeterClient.call_answer_query(serviceName, _text);
this._greeterClient.call_answer_query_sync(serviceName, _text, null);
}];
let batch = new Batch.ConsecutiveBatch(this, tasks);
@@ -1092,7 +1088,7 @@ const LoginDialog = new Lang.Class({
},
_onInfoQuery: function(client, serviceName, question) {
// We only expect questions to come from the main auth service
if (serviceName != _PASSWORD_SERVICE_NAME)
if (serviceName != PASSWORD_SERVICE_NAME)
return;
this._promptEntry.set_text('');
@@ -1102,7 +1098,7 @@ const LoginDialog = new Lang.Class({
_onSecretInfoQuery: function(client, serviceName, secretQuestion) {
// We only expect secret requests to come from the main auth service
if (serviceName != _PASSWORD_SERVICE_NAME)
if (serviceName != PASSWORD_SERVICE_NAME)
return;
this._promptEntry.set_text('');
@@ -1111,7 +1107,7 @@ const LoginDialog = new Lang.Class({
},
_onSessionOpened: function(client, serviceName) {
this._greeterClient.call_start_session_when_ready(serviceName, true);
this._greeterClient.call_start_session_when_ready_sync(serviceName, true, null);
},
_waitForItemForUser: function(userName) {
@@ -1193,7 +1189,7 @@ const LoginDialog = new Lang.Class({
function() {
this._timedLoginBatch = null;
this._greeterClient.call_begin_auto_login(userName);
this._greeterClient.call_begin_auto_login_sync(userName, null);
}];
this._timedLoginBatch = new Batch.ConsecutiveBatch(this, tasks);
@@ -1236,17 +1232,13 @@ const LoginDialog = new Lang.Class({
}));
},
_onAuthenticationFailed: function(client) {
this._greeterClient.call_cancel();
},
_onConversationStopped: function(client, serviceName) {
// if the password service fails, then cancel everything.
// But if, e.g., fingerprint fails, still give
// password authentication a chance to succeed
if (serviceName == _PASSWORD_SERVICE_NAME) {
this._greeterClient.call_cancel();
} else if (serviceName == _FINGERPRINT_SERVICE_NAME) {
if (serviceName == PASSWORD_SERVICE_NAME) {
this._greeterClient.call_cancel_sync(null);
} else if (serviceName == FINGERPRINT_SERVICE_NAME) {
_fadeOutActor(this._promptFingerprintMessage);
}
},
@@ -1269,7 +1261,7 @@ const LoginDialog = new Lang.Class({
this._fadeOutLogo]),
function() {
this._greeterClient.call_begin_verification(_PASSWORD_SERVICE_NAME);
this._greeterClient.call_begin_verification_sync(PASSWORD_SERVICE_NAME, null);
}];
let batch = new Batch.ConsecutiveBatch(this, tasks);
@@ -1328,11 +1320,11 @@ const LoginDialog = new Lang.Class({
function() {
let userName = activatedItem.user.get_user_name();
this._greeterClient.call_begin_verification_for_user(_PASSWORD_SERVICE_NAME,
userName);
this._greeterClient.call_begin_verification_for_user_sync(PASSWORD_SERVICE_NAME,
userName, null);
if (this._haveFingerprintReader)
this._greeterClient.call_begin_verification_for_user(_FINGERPRINT_SERVICE_NAME, userName);
this._greeterClient.call_begin_verification_for_user_sync(_FINGERPRINT_SERVICE_NAME, userName, null);
}];
this._user = activatedItem.user;

View File

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

View File

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

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

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

View File

@@ -89,22 +89,18 @@ function trySpawn(argv)
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
null, null);
} catch (err) {
/* Rewrite the error in case of ENOENT */
if (err.matches(GLib.SpawnError, GLib.SpawnError.NOENT)) {
throw new GLib.SpawnError({ code: GLib.SpawnError.NOENT,
message: _("Command not found") });
} else if (err instanceof GLib.Error) {
if (err.code == GLib.SpawnError.G_SPAWN_ERROR_NOENT) {
err.message = _("Command not found");
} else {
// The exception from gjs contains an error string like:
// Error invoking GLib.spawn_command_line_async: Failed to
// execute child process "foo" (No such file or directory)
// We are only interested in the part in the parentheses. (And
// we can't pattern match the text, since it gets localized.)
let message = err.message.replace(/.*\((.+)\)/, '$1');
throw new (err.constructor)({ code: err.code,
message: message });
} else {
throw err;
err.message = err.message.replace(/.*\((.+)\)/, '$1');
}
throw err;
}
// Dummy child watch; we don't want to double-fork internally
// because then we lose the parent-child relationship, which

View File

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

View File

@@ -123,8 +123,7 @@ const AutomountManager = new Lang.Class({
let volumes = this._volumeMonitor.get_volumes();
volumes.forEach(Lang.bind(this, function(volume) {
this._checkAndMountVolume(volume, { checkSession: false,
useMountOp: false,
allowAutorun: false });
useMountOp: false });
}));
return false;
@@ -202,8 +201,7 @@ const AutomountManager = new Lang.Class({
_checkAndMountVolume: function(volume, params) {
params = Params.parse(params, { checkSession: true,
useMountOp: true,
allowAutorun: true });
useMountOp: true });
if (params.checkSession) {
// if we're not in the current ConsoleKit session,
@@ -238,20 +236,15 @@ const AutomountManager = new Lang.Class({
if (params.useMountOp) {
let operation = new ShellMountOperation.ShellMountOperation(volume);
this._mountVolume(volume, operation, params.allowAutorun);
this._mountVolume(volume, operation.mountOp);
} else {
this._mountVolume(volume, null, params.allowAutorun);
this._mountVolume(volume, null);
}
},
_mountVolume: function(volume, operation, allowAutorun) {
if (allowAutorun)
this._allowAutorun(volume);
let mountOp = operation ? operation.mountOp : null;
volume._operation = operation;
volume.mount(0, mountOp, null,
_mountVolume: function(volume, operation) {
this._allowAutorun(volume);
volume.mount(0, operation, null,
Lang.bind(this, this._onVolumeMounted));
},
@@ -260,19 +253,15 @@ const AutomountManager = new Lang.Class({
try {
volume.mount_finish(res);
this._closeOperation(volume);
} catch (e) {
// FIXME: we will always get G_IO_ERROR_FAILED from the gvfs udisks
// backend in this case, see
// https://bugs.freedesktop.org/show_bug.cgi?id=51271
if (e.message.indexOf('No key available with this passphrase') != -1) {
this._reaskPassword(volume);
} else {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
log('Unable to mount volume ' + volume.get_name() + ': ' + e.toString());
let string = e.toString();
this._closeOperation(volume);
}
// FIXME: needs proper error code handling instead of this
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
if (string.indexOf('No key available with this passphrase') != -1)
this._reaskPassword(volume);
else
log('Unable to mount volume ' + volume.get_name() + ': ' + string);
}
},
@@ -284,16 +273,8 @@ const AutomountManager = new Lang.Class({
},
_reaskPassword: function(volume) {
let existingDialog = volume._operation ? volume._operation.borrowDialog() : null;
let operation =
new ShellMountOperation.ShellMountOperation(volume,
{ existingDialog: existingDialog });
this._mountVolume(volume, operation);
},
_closeOperation: function(volume) {
if (volume._operation)
volume._operation.close();
let operation = new ShellMountOperation.ShellMountOperation(volume, { reaskPassword: true });
this._mountVolume(volume, operation.mountOp);
},
_allowAutorun: function(volume) {

View File

@@ -23,14 +23,12 @@ const AutorunSetting = {
};
// misc utils
function shouldAutorunMount(mount, forTransient) {
function ignoreAutorunForMount(mount) {
let root = mount.get_root();
let volume = mount.get_volume();
if (!volume || (!volume.allowAutorun && forTransient))
return false;
if (!root.is_native() || isMountRootHidden(root))
if ((root.is_native() && !isMountRootHidden(root)) ||
(volume && volume.allowAutorun && volume.should_automount()))
return false;
return true;
@@ -226,9 +224,11 @@ const AutorunManager = new Lang.Class({
try {
mount.unmount_with_operation_finish(res);
} catch (e) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
log('Unable to eject the mount ' + mount.get_name()
+ ': ' + e.toString());
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
// but we can't access the error code from JS.
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
log('Unable to eject the mount ' + mount.get_name()
+ ': ' + e.toString());
}
},
@@ -236,9 +236,11 @@ const AutorunManager = new Lang.Class({
try {
source.eject_with_operation_finish(res);
} catch (e) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
log('Unable to eject the drive ' + source.get_name()
+ ': ' + e.toString());
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
// but we can't access the error code from JS.
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
log('Unable to eject the drive ' + source.get_name()
+ ': ' + e.toString());
}
},
@@ -246,9 +248,11 @@ const AutorunManager = new Lang.Class({
try {
drive.stop_finish(res);
} catch (e) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
log('Unable to stop the drive ' + drive.get_name()
+ ': ' + e.toString());
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
// but we can't access the error code from JS.
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
log('Unable to stop the drive ' + drive.get_name()
+ ': ' + e.toString());
}
},
});
@@ -266,7 +270,7 @@ const AutorunResidentSource = new Lang.Class({
},
addMount: function(mount, apps) {
if (!shouldAutorunMount(mount, false))
if (ignoreAutorunForMount(mount))
return;
let filtered = this._mounts.filter(function (element) {
@@ -444,7 +448,7 @@ const AutorunTransientDispatcher = new Lang.Class({
return;
// if the mount doesn't want to be autorun, return
if (!shouldAutorunMount(mount, true))
if (ignoreAutorunForMount(mount))
return;
let setting = this._getAutorunSettingForType(contentTypes[0]);

View File

@@ -448,7 +448,7 @@ const Calendar = new Lang.Class({
}
// All the children after this are days, and get removed when we update the calendar
this._firstDayIndex = this.actor.get_n_children();
this._firstDayIndex = this.actor.get_children().length;
},
_onStyleChange: function(actor, event) {

View File

@@ -63,7 +63,7 @@ const Contact = new Lang.Class({
this.individual.full_name ||
this.individual.nickname ||
email ||
C_("contact", "Unknown");
_("Unknown");
let aliasLabel = new St.Label({ text: aliasText,
style_class: 'contact-details-alias' });
details.add(aliasLabel, { x_fill: true,

View File

@@ -2,7 +2,6 @@
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const GnomeDesktop = imports.gi.GnomeDesktop;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Cairo = imports.cairo;
@@ -17,6 +16,14 @@ const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Calendar = imports.ui.calendar;
const UPowerGlib = imports.gi.UPowerGlib;
// in org.gnome.desktop.interface
const CLOCK_FORMAT_KEY = 'clock-format';
// in org.gnome.shell.clock
const CLOCK_SHOW_DATE_KEY = 'show-date';
const CLOCK_SHOW_SECONDS_KEY = 'show-seconds';
function _onVertSepRepaint (area)
{
@@ -38,7 +45,9 @@ const DateMenuButton = new Lang.Class({
Name: 'DateMenuButton',
Extends: PanelMenu.Button,
_init: function() {
_init: function(params) {
params = Params.parse(params, { showEvents: true });
let item;
let hbox;
let vbox;
@@ -53,8 +62,8 @@ const DateMenuButton = new Lang.Class({
// role ATK_ROLE_MENU like other elements of the panel.
this.actor.accessible_role = Atk.Role.LABEL;
this._clockDisplay = new St.Label();
this.actor.add_actor(this._clockDisplay);
this._clock = new St.Label();
this.actor.add_actor(this._clock);
hbox = new St.BoxLayout({name: 'calendarArea' });
this.menu.addActor(hbox);
@@ -66,11 +75,11 @@ const DateMenuButton = new Lang.Class({
// Date
this._date = new St.Label();
this.actor.label_actor = this._clockDisplay;
this.actor.label_actor = this._clock;
this._date.style_class = 'datemenu-date-label';
vbox.add(this._date);
if (Main.sessionMode.showCalendarEvents) {
if (params.showEvents) {
this._eventSource = new Calendar.DBusEventSource();
this._eventList = new Calendar.EventsList(this._eventSource);
} else {
@@ -101,7 +110,7 @@ const DateMenuButton = new Lang.Class({
item.actor.reparent(vbox);
}
if (Main.sessionMode.showCalendarEvents) {
if (params.showEvents) {
// Add vertical separator
item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
@@ -148,13 +157,68 @@ const DateMenuButton = new Lang.Class({
// Done with hbox for calendar and event list
this._clock = new GnomeDesktop.WallClock();
this._clock.connect('notify::clock', Lang.bind(this, this._updateClockAndDate));
// Track changes to clock settings
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
this._clockSettings = new Gio.Settings({ schema: 'org.gnome.shell.clock' });
this._desktopSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
this._clockSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
// https://bugzilla.gnome.org/show_bug.cgi?id=655129
this._upClient = new UPowerGlib.Client();
this._upClient.connect('notify-resume', Lang.bind(this, this._updateClockAndDate));
// Start the clock
this._updateClockAndDate();
},
_updateClockAndDate: function() {
this._clockDisplay.set_text(this._clock.clock);
let format = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);
let showDate = this._clockSettings.get_boolean(CLOCK_SHOW_DATE_KEY);
let showSeconds = this._clockSettings.get_boolean(CLOCK_SHOW_SECONDS_KEY);
let clockFormat;
let dateFormat;
switch (format) {
case '24h':
if (showDate)
/* Translators: This is the time format with date used
in 24-hour mode. */
clockFormat = showSeconds ? _("%a %b %e, %R:%S")
: _("%a %b %e, %R");
else
/* Translators: This is the time format without date used
in 24-hour mode. */
clockFormat = showSeconds ? _("%a %R:%S")
: _("%a %R");
break;
case '12h':
default:
if (showDate)
/* Translators: This is a time format with date used
for AM/PM. */
clockFormat = showSeconds ? _("%a %b %e, %l:%M:%S %p")
: _("%a %b %e, %l:%M %p");
else
/* Translators: This is a time format without date used
for AM/PM. */
clockFormat = showSeconds ? _("%a %l:%M:%S %p")
: _("%a %l:%M %p");
break;
}
let displayDate = new Date();
this._clock.set_text(displayDate.toLocaleFormat(clockFormat));
/* Translators: This is the date format to use when the calendar popup is
* shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
*/
dateFormat = _("%A %B %e, %Y");
this._date.set_text(displayDate.toLocaleFormat(dateFormat));
Mainloop.timeout_add_seconds(1, Lang.bind(this, this._updateClockAndDate));
return false;
},
_onOpenCalendarActivate: function() {

View File

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

View File

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

View File

@@ -1,170 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Soup = imports.gi.Soup;
const St = imports.gi.St;
const Config = imports.misc.config;
const ExtensionUtils = imports.misc.extensionUtils;
const ExtensionSystem = imports.ui.extensionSystem;
const FileUtils = imports.misc.fileUtils;
const ModalDialog = imports.ui.modalDialog;
const _signals = ExtensionSystem._signals;
const REPOSITORY_URL_BASE = 'https://extensions.gnome.org';
const REPOSITORY_URL_DOWNLOAD = REPOSITORY_URL_BASE + '/download-extension/%s.shell-extension.zip';
const REPOSITORY_URL_INFO = REPOSITORY_URL_BASE + '/extension-info/';
let _httpSession;
function installExtensionFromUUID(uuid) {
let params = { uuid: uuid,
shell_version: Config.PACKAGE_VERSION };
let message = Soup.form_request_new_from_hash('GET', REPOSITORY_URL_INFO, params);
_httpSession.queue_message(message,
function(session, message) {
let info = JSON.parse(message.response_body.data);
let dialog = new InstallExtensionDialog(uuid, info);
dialog.open(global.get_current_time());
});
}
function uninstallExtensionFromUUID(uuid) {
let extension = ExtensionUtils.extensions[uuid];
if (!extension)
return false;
// Don't try to uninstall system extensions
if (extension.type != ExtensionUtils.ExtensionType.PER_USER)
return false;
if (!ExtensionSystem.unloadExtension(uuid))
return false;
FileUtils.recursivelyDeleteDir(extension.dir);
return true;
}
function gotExtensionZipFile(session, message, uuid) {
if (message.status_code != Soup.KnownStatusCode.OK) {
logExtensionError(uuid, 'downloading extension: ' + message.status_code);
return;
}
let dir = Gio.File.new_for_path(GLib.build_filenamev([global.userdatadir, 'extensions', uuid]));
try {
if (!dir.query_exists(null))
dir.make_directory_with_parents(null);
} catch (e) {
logExtensionError('Could not create extension directory');
}
let [file, stream] = Gio.File.new_tmp('XXXXXX.shell-extension.zip');
let contents = message.response_body.flatten().as_bytes();
stream.output_stream.write_bytes(contents, null);
stream.close(null);
let [success, pid] = GLib.spawn_async(null,
['unzip', '-uod', dir.get_path(), '--', file.get_path()],
null,
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
null);
if (!success) {
logExtensionError(uuid, 'extract: could not extract');
return;
}
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function(pid, status) {
GLib.spawn_close_pid(pid);
// Add extension to 'enabled-extensions' for the user, always...
let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY);
if (enabledExtensions.indexOf(uuid) == -1) {
enabledExtensions.push(uuid);
global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
}
ExtensionSystem.loadExtension(dir, ExtensionUtils.ExtensionType.PER_USER, true);
});
}
const InstallExtensionDialog = new Lang.Class({
Name: 'InstallExtensionDialog',
Extends: ModalDialog.ModalDialog,
_init: function(uuid, info) {
this.parent({ styleClass: 'extension-dialog' });
this._uuid = uuid;
this._info = info;
this.setButtons([{ label: _("Cancel"),
action: Lang.bind(this, this._onCancelButtonPressed),
key: Clutter.Escape
},
{ label: _("Install"),
action: Lang.bind(this, this._onInstallButtonPressed)
}]);
let message = _("Download and install '%s' from extensions.gnome.org?").format(info.name);
let box = new St.BoxLayout();
this.contentLayout.add(box);
let gicon = new Gio.FileIcon({ file: Gio.File.new_for_uri(REPOSITORY_URL_BASE + info.icon) })
let icon = new St.Icon({ gicon: gicon });
box.add(icon);
let label = new St.Label({ text: message });
box.add(label);
},
_onCancelButtonPressed: function(button, event) {
this.close(global.get_current_time());
// Even though the extension is already "uninstalled", send through
// a state-changed signal for any users who want to know if the install
// went through correctly -- using proper async DBus would block more
// traditional clients like the plugin
let meta = { uuid: this._uuid,
state: ExtensionSystem.ExtensionState.UNINSTALLED,
error: '' };
_signals.emit('extension-state-changed', meta);
},
_onInstallButtonPressed: function(button, event) {
let state = { uuid: this._uuid,
state: ExtensionSystem.ExtensionState.DOWNLOADING,
error: '' };
_signals.emit('extension-state-changed', state);
let params = { shell_version: Config.PACKAGE_VERSION };
let url = REPOSITORY_URL_DOWNLOAD.format(this._uuid);
let message = Soup.form_request_new_from_hash('GET', url, params);
_httpSession.queue_message(message,
Lang.bind(this, function(session, message) {
gotExtensionZipFile(session, message, this._uuid);
}));
this.close(global.get_current_time());
}
});
function init() {
_httpSession = new Soup.SessionAsync({ ssl_use_system_ca_file: true });
// See: https://bugzilla.gnome.org/show_bug.cgi?id=655189 for context.
// _httpSession.add_feature(new Soup.ProxyResolverDefault());
Soup.Session.prototype.add_feature.call(_httpSession, new Soup.ProxyResolverDefault());
}

View File

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

View File

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

View File

@@ -53,10 +53,6 @@ const LayoutManager = new Lang.Class({
global.screen.connect('monitors-changed',
Lang.bind(this, this._monitorsChanged));
this._monitorsChanged();
this._chrome.connect('primary-fullscreen-changed', Lang.bind(this, function(chrome, state) {
this.emit('primary-fullscreen-changed', state);
}));
},
// This is called by Main after everything else is constructed;
@@ -228,9 +224,26 @@ const LayoutManager = new Lang.Class({
return false;
},
get currentMonitor() {
let index = global.screen.get_current_monitor();
return Main.layoutManager.monitors[index];
get focusIndex() {
let focusWindow = global.display.focus_window;
if (focusWindow) {
let wrect = focusWindow.get_outer_rect();
for (let i = 0; i < this.monitors.length; i++) {
let monitor = this.monitors[i];
if (monitor.x <= wrect.x && monitor.y <= wrect.y &&
monitor.x + monitor.width > wrect.x &&
monitor.y + monitor.height > wrect.y)
return i;
}
}
return this.primaryIndex;
},
get focusMonitor() {
return this.monitors[this.focusIndex];
},
_startupAnimation: function() {
@@ -839,8 +852,6 @@ const Chrome = new Lang.Class({
for (let i = 0; i < this._monitors.length; i++)
wasInFullscreen[i] = this._monitors[i].inFullscreen;
let primaryWasInFullscreen = this._primaryMonitor.inFullscreen;
this._updateFullscreen();
let changed = false;
@@ -850,15 +861,10 @@ const Chrome = new Lang.Class({
break;
}
}
if (changed) {
this._updateVisibility();
this._queueUpdateRegions();
}
if (primaryWasInFullscreen != this._primaryMonitor.inFullscreen) {
this.emit('primary-fullscreen-changed', this._primaryMonitor.inFullscreen);
}
},
updateRegions: function() {
@@ -973,5 +979,3 @@ const Chrome = new Lang.Class({
return false;
}
});
Signals.addSignalMethods(Chrome.prototype);

View File

@@ -8,6 +8,8 @@ const St = imports.gi.St;
const Params = imports.misc.params;
const Tweener = imports.ui.tweener;
const DEFAULT_FADE_FACTOR = 0.4;
/**
* Lightbox:
* @container: parent Clutter.Container
@@ -15,7 +17,8 @@ const Tweener = imports.ui.tweener;
* - inhibitEvents: whether to inhibit events for @container
* - width: shade actor width
* - height: shade actor height
* - fadeTime: seconds used to fade in/out
* - fadeInTime: seconds used to fade in
* - fadeOutTime: seconds used to fade out
*
* Lightbox creates a dark translucent "shade" actor to hide the
* contents of @container, and allows you to specify particular actors
@@ -38,12 +41,16 @@ const Lightbox = new Lang.Class({
params = Params.parse(params, { inhibitEvents: false,
width: null,
height: null,
fadeTime: null
fadeInTime: null,
fadeOutTime: null,
fadeFactor: DEFAULT_FADE_FACTOR
});
this._container = container;
this._children = container.get_children();
this._fadeTime = params.fadeTime;
this._fadeInTime = params.fadeInTime;
this._fadeOutTime = params.fadeOutTime;
this._fadeFactor = params.fadeFactor;
this.actor = new St.Bin({ x: 0,
y: 0,
style_class: 'lightbox',
@@ -52,6 +59,7 @@ const Lightbox = new Lang.Class({
container.add_actor(this.actor);
this.actor.raise_top();
this.actor.hide();
this.shown = false;
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
@@ -93,24 +101,30 @@ const Lightbox = new Lang.Class({
},
show: function() {
if (this._fadeTime) {
if (this._fadeInTime) {
this.shown = false;
this.actor.opacity = 0;
Tweener.addTween(this.actor,
{ opacity: 255,
time: this._fadeTime,
transition: 'easeOutQuad'
{ opacity: 255 * this._fadeFactor,
time: this._fadeInTime,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this.shown = true;
})
});
} else {
this.actor.opacity = 255;
this.actor.opacity = 255 * this._fadeFactor;
this.shown = true;
}
this.actor.show();
},
hide: function() {
if (this._fadeTime) {
this.shown = false;
if (this._fadeOutTime) {
Tweener.addTween(this.actor,
{ opacity: 0,
time: this._fadeTime,
time: this._fadeOutTime,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this.actor.hide();

View File

@@ -38,7 +38,7 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
'const stage = global.stage; ' +
'const color = function(pixel) { let c= new Clutter.Color(); c.from_pixel(pixel); return c; }; ' +
/* Special lookingGlass functions */
'const it = Main.lookingGlass.getIt(); ' +
'const it = Main.lookingGlass.getIt(); ' +
'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ';
const HISTORY_KEY = 'looking-glass-history';
@@ -320,7 +320,7 @@ const WindowList = new Lang.Class({
},
_updateWindowList: function() {
this.actor.destroy_all_children();
this.actor.get_children().forEach(function (actor) { actor.destroy(); });
let windows = global.get_window_actors();
let tracker = Shell.WindowTracker.get_default();
for (let i = 0; i < windows.length; i++) {
@@ -378,7 +378,7 @@ const ObjInspector = new Lang.Class({
this._previousObj = null;
this._obj = obj;
this._container.destroy_all_children();
this._container.get_children().forEach(function (child) { child.destroy(); });
let hbox = new St.BoxLayout({ style_class: 'lg-obj-inspector-title' });
this._container.add_actor(hbox);

View File

@@ -18,7 +18,6 @@ const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent;
const KeyringPrompt = imports.ui.keyringPrompt;
const Environment = imports.ui.environment;
const ExtensionSystem = imports.ui.extensionSystem;
const ExtensionDownloader = imports.ui.extensionDownloader;
const Keyboard = imports.ui.keyboard;
const MessageTray = imports.ui.messageTray;
const Overview = imports.ui.overview;
@@ -30,10 +29,9 @@ const LookingGlass = imports.ui.lookingGlass;
const NetworkAgent = imports.ui.networkAgent;
const NotificationDaemon = imports.ui.notificationDaemon;
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
const ScreenShield = imports.ui.screenShield;
const Scripting = imports.ui.scripting;
const SessionMode = imports.ui.sessionMode;
const ShellDBus = imports.ui.shellDBus;
const ShellMountOperation = imports.ui.shellMountOperation;
const TelepathyClient = imports.ui.telepathyClient;
const WindowManager = imports.ui.windowManager;
const Magnifier = imports.ui.magnifier;
@@ -54,14 +52,13 @@ let runDialog = null;
let lookingGlass = null;
let wm = null;
let messageTray = null;
let screenShield = null;
let notificationDaemon = null;
let windowAttentionHandler = null;
let telepathyClient = null;
let ctrlAltTabManager = null;
let recorder = null;
let sessionMode = null;
let shellDBusService = null;
let shellMountOpDBusService = null;
let modalCount = 0;
let modalActorFocusStack = [];
let uiGroup = null;
@@ -74,11 +71,12 @@ let networkAgent = null;
let _startDate;
let _defaultCssStylesheet = null;
let _cssStylesheet = null;
let _gdmCssStylesheet = null;
let _overridesSettings = null;
let background = null;
function createUserSession() {
function _createUserSession() {
// Load the calendar server. Note that we are careful about
// not loading any events until the user presses the clock
global.launch_calendar_server();
@@ -87,11 +85,9 @@ function createUserSession() {
automountManager = new AutomountManager.AutomountManager();
autorunManager = new AutorunManager.AutorunManager();
networkAgent = new NetworkAgent.NetworkAgent();
_initRecorder();
}
function createGDMSession() {
function _createGDMSession() {
// We do this this here instead of at the top to prevent GDM
// related code from getting loaded in normal user sessions
const LoginDialog = imports.gdm.loginDialog;
@@ -102,13 +98,8 @@ function createGDMSession() {
});
}
function createInitialSetupSession() {
networkAgent = new NetworkAgent.NetworkAgent();
}
function _initRecorder() {
let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
let desktopLockdownSettings = new Gio.Settings({ schema: 'org.gnome.desktop.lockdown' });
let bindingSettings = new Gio.Settings({ schema: 'org.gnome.shell.keybindings' });
global.display.add_keybinding('toggle-recording',
@@ -121,7 +112,7 @@ function _initRecorder() {
if (recorder.is_recording()) {
recorder.close();
Meta.enable_unredirect_for_screen(global.screen);
} else if (!desktopLockdownSettings.get_boolean('disable-save-to-disk')) {
} else {
// read the parameters from GSettings always in case they have changed
recorder.set_framerate(recorderSettings.get_int('framerate'));
/* Translators: this is a filename used for screencast recording */
@@ -140,6 +131,26 @@ function _initRecorder() {
});
}
function _initUserSession() {
_initRecorder();
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, false, -1, 1);
ExtensionSystem.init();
ExtensionSystem.loadExtensions();
Meta.keybindings_set_custom_handler('panel-run-dialog', function() {
getRunDialog().open();
});
Meta.keybindings_set_custom_handler('panel-main-menu', function () {
overview.toggle();
});
global.display.connect('overlay-key', Lang.bind(overview, overview.toggle));
}
function start() {
// These are here so we don't break compatibility.
global.logError = window.log;
@@ -150,9 +161,7 @@ function start() {
Gio.DesktopAppInfo.set_desktop_env('GNOME');
sessionMode = new SessionMode.SessionMode();
shellDBusService = new ShellDBus.GnomeShell();
shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
// also initialize ShellAppSystem first. ShellAppSystem
@@ -174,6 +183,7 @@ function start() {
global.stage.no_clear_hint = true;
_defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
_gdmCssStylesheet = global.datadir + '/theme/gdm.css';
loadTheme();
// Set up stage hierarchy to group all UI actors under one container.
@@ -201,17 +211,22 @@ function start() {
layoutManager = new Layout.LayoutManager();
xdndHandler = new XdndHandler.XdndHandler();
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
overview = new Overview.Overview();
// This overview object is just a stub for non-user sessions
overview = new Overview.Overview({ isDummy: global.session_type != Shell.SessionType.USER });
magnifier = new Magnifier.Magnifier();
statusIconDispatcher = new StatusIconDispatcher.StatusIconDispatcher();
panel = new Panel.Panel();
wm = new WindowManager.WindowManager();
messageTray = new MessageTray.MessageTray();
screenShield = new ScreenShield.ScreenShield();
keyboard = new Keyboard.Keyboard();
notificationDaemon = new NotificationDaemon.NotificationDaemon();
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
sessionMode.createSession();
if (global.session_type == Shell.SessionType.USER)
_createUserSession();
else if (global.session_type == Shell.SessionType.GDM)
_createGDMSession();
panel.startStatusArea();
@@ -219,30 +234,8 @@ function start() {
keyboard.init();
overview.init();
if (sessionMode.hasWorkspaces)
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
false, -1, 1);
if (sessionMode.allowExtensions) {
ExtensionDownloader.init();
ExtensionSystem.loadExtensions();
}
if (sessionMode.hasRunDialog) {
Meta.keybindings_set_custom_handler('panel-run-dialog', function() {
getRunDialog().open();
});
}
if (sessionMode.hasOverview) {
Meta.keybindings_set_custom_handler('panel-main-menu', function () {
overview.toggle();
});
global.display.connect('overlay-key',
Lang.bind(overview, overview.toggle));
}
if (global.session_type == Shell.SessionType.USER)
_initUserSession();
statusIconDispatcher.start(messageTray.actor);
// Provide the bus object for gnome-session to
@@ -501,8 +494,8 @@ function loadTheme() {
let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
if (sessionMode.extraStylesheet)
theme.load_stylesheet(sessionMode.extraStylesheet);
// FIXME: merge back into main stylesheet
theme.load_stylesheet(_gdmCssStylesheet);
if (previousTheme) {
let customStylesheets = previousTheme.get_custom_stylesheets();
@@ -566,11 +559,6 @@ function _globalKeyPressHandler(actor, event) {
if (event.type() != Clutter.EventType.KEY_PRESS)
return false;
if (!sessionMode.allowKeybindingsWhenModal) {
if (modalCount > (overview.visible ? 1 : 0))
return false;
}
let symbol = event.get_key_symbol();
let keyCode = event.get_key_code();
let ignoredModifiers = global.display.get_ignored_modifier_mask();
@@ -579,6 +567,11 @@ function _globalKeyPressHandler(actor, event) {
// This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType
let action = global.display.get_keybinding_action(keyCode, modifierState);
// Other bindings are only available to the user session when the overview is up and
// no modal dialog is present.
if (global.session_type == Shell.SessionType.USER && (!overview.visible || modalCount > 1))
return false;
// This isn't a Meta.KeyBindingAction yet
if (symbol == Clutter.Super_L || symbol == Clutter.Super_R) {
overview.hide();
@@ -591,39 +584,28 @@ function _globalKeyPressHandler(actor, event) {
return true;
}
// None of the other bindings are relevant outside of the user's session
if (global.session_type != Shell.SessionType.USER)
return false;
switch (action) {
// left/right would effectively act as synonyms for up/down if we enabled them;
// but that could be considered confusing; we also disable them in the main view.
//
// case Meta.KeyBindingAction.WORKSPACE_LEFT:
// if (!sessionMode.hasWorkspaces)
// return false;
//
// wm.actionMoveWorkspaceLeft();
// return true;
// case Meta.KeyBindingAction.WORKSPACE_RIGHT:
// if (!sessionMode.hasWorkspaces)
// return false;
//
// wm.actionMoveWorkspaceRight();
// return true;
case Meta.KeyBindingAction.WORKSPACE_UP:
if (!sessionMode.hasWorkspaces)
return false;
wm.actionMoveWorkspaceUp();
return true;
case Meta.KeyBindingAction.WORKSPACE_DOWN:
if (!sessionMode.hasWorkspaces)
return false;
wm.actionMoveWorkspaceDown();
return true;
case Meta.KeyBindingAction.PANEL_RUN_DIALOG:
case Meta.KeyBindingAction.COMMAND_2:
if (!sessionMode.hasRunDialog)
return false;
getRunDialog().open();
return true;
case Meta.KeyBindingAction.PANEL_MAIN_MENU:
@@ -642,6 +624,10 @@ function _findModal(actor) {
return -1;
}
function isInModalStack(actor) {
return _findModal(actor) != -1;
}
/**
* pushModal:
* @actor: #ClutterActor which will be given keyboard focus
@@ -674,7 +660,6 @@ function pushModal(actor, timestamp, options) {
log('pushModal: invocation of begin_modal failed');
return false;
}
Meta.disable_unredirect_for_screen(global.screen);
}
global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
@@ -683,7 +668,7 @@ function pushModal(actor, timestamp, options) {
let actorDestroyId = actor.connect('destroy', function() {
let index = _findModal(actor);
if (index >= 0)
modalActorFocusStack.splice(index, 1);
popModal(actor);
});
let curFocus = global.stage.get_key_focus();
let curFocusDestroyId;
@@ -755,7 +740,6 @@ function popModal(actor, timestamp) {
global.end_modal(timestamp);
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
Meta.enable_unredirect_for_screen(global.screen);
}
function createLookingGlass() {
@@ -898,8 +882,7 @@ function initializeDeferredWork(actor, callback, props) {
function queueDeferredWork(workId) {
let data = _deferredWorkData[workId];
if (!data) {
let message = 'Invalid work id %d'.format(workId);
logError(new Error(message), message);
global.logError('invalid work id ', workId);
return;
}
if (_deferredWorkQueue.indexOf(workId) < 0)

View File

@@ -756,7 +756,7 @@ const Notification = new Lang.Class({
button.label = label;
}
if (this._buttonBox.get_n_children() > 0)
if (this._buttonBox.get_children().length > 0)
this._buttonFocusManager.remove_group(this._buttonBox);
this._buttonBox.add(button);
@@ -1284,7 +1284,7 @@ const SummaryItem = new Lang.Class({
},
prepareNotificationStackForShowing: function() {
if (this.notificationStack.get_n_children() > 0)
if (this.notificationStack.get_children().length > 0)
return;
for (let i = 0; i < this.source.notifications.length; i++) {
@@ -1293,6 +1293,7 @@ const SummaryItem = new Lang.Class({
},
doneShowingNotificationStack: function() {
let notificationActors = this.notificationStack.get_children();
for (let i = 0; i < this._stackedNotifications.length; i++) {
let stackedNotification = this._stackedNotifications[i];
let notification = stackedNotification.notification;
@@ -1322,7 +1323,7 @@ const SummaryItem = new Lang.Class({
this._stackedNotifications.push(stackedNotification);
if (!this.source.isChat)
notification.enableScrolling(false);
if (this.notificationStack.get_n_children() > 0)
if (this.notificationStack.get_children().length > 0)
notification.setIconVisible(false);
this.notificationStack.add(notification.actor);
notification.expand(false);
@@ -1468,7 +1469,6 @@ const MessageTray = new Lang.Class({
this._overviewVisible = Main.overview.visible;
this._notificationRemoved = false;
this._reNotifyAfterHideNotification = null;
this._inFullscreen = false;
this._corner = new Clutter.Rectangle({ width: 1,
height: 1,
@@ -1484,7 +1484,6 @@ const MessageTray = new Lang.Class({
Main.layoutManager.trackChrome(this._notificationBin);
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._setSizePosition));
Main.layoutManager.connect('primary-fullscreen-changed', Lang.bind(this, this._onFullscreenChanged));
this._setSizePosition();
@@ -1958,11 +1957,6 @@ const MessageTray = new Lang.Class({
this._updateState();
},
_onFullscreenChanged: function(obj, state) {
this._inFullscreen = state;
this._updateState();
},
_onStatusChanged: function(status) {
if (status == GnomeSession.PresenceStatus.BUSY) {
// remove notification and allow the summary to be closed now
@@ -2020,7 +2014,7 @@ const MessageTray = new Lang.Class({
_updateState: function() {
// Notifications
let notificationUrgent = this._notificationQueue.length > 0 && this._notificationQueue[0].urgency == Urgency.CRITICAL;
let notificationsPending = this._notificationQueue.length > 0 && ((!this._busy && !this._inFullscreen) || notificationUrgent);
let notificationsPending = this._notificationQueue.length > 0 && (!this._busy || notificationUrgent);
let notificationPinned = this._pointerInTray && !this._pointerInSummary && !this._notificationRemoved;
let notificationExpanded = this._notificationBin.y < 0;
let notificationExpired = (this._notificationTimeoutId == 0 && !(this._notification && this._notification.urgency == Urgency.CRITICAL) && !this._pointerInTray && !this._locked && !(this._pointerInKeyboard && notificationExpanded)) || this._notificationRemoved;
@@ -2056,7 +2050,7 @@ const MessageTray = new Lang.Class({
if (this._summaryState == State.HIDDEN && !mustHideSummary) {
if (summarySummoned) {
this._showSummary(0);
} else if (notificationsDone && !this._busy && !this._inFullscreen) {
} else if (notificationsDone && !this._busy) {
if (this._backFromAway && this._unseenNotifications.length > 0)
this._showSummary(LONGER_SUMMARY_TIMEOUT);
else if (this._newSummaryItems.length > 0)
@@ -2435,7 +2429,7 @@ const MessageTray = new Lang.Class({
},
_onSummaryBoxPointerContentUpdated: function() {
if (this._summaryBoxPointerItem.notificationStack.get_n_children() == 0)
if (this._summaryBoxPointerItem.notificationStack.get_children().length == 0)
this._hideSummaryBoxPointer();
this._adjustSummaryBoxPointerPosition();
@@ -2469,7 +2463,7 @@ const MessageTray = new Lang.Class({
// We should be sure to hide the box pointer if all notifications in it are destroyed while
// it is hiding, so that we don't show an an animation of an empty blob being hidden.
if (this._summaryBoxPointerState == State.HIDING &&
this._summaryBoxPointerItem.notificationStack.get_n_children() == 0) {
this._summaryBoxPointerItem.notificationStack.get_children().length == 0) {
this._summaryBoxPointer.actor.hide();
return;
}

View File

@@ -180,7 +180,7 @@ const ModalDialog = new Lang.Class({
},
_fadeOpen: function() {
let monitor = Main.layoutManager.currentMonitor;
let monitor = Main.layoutManager.focusMonitor;
this._backgroundBin.set_position(monitor.x, monitor.y);
this._backgroundBin.set_size(monitor.width, monitor.height);

View File

@@ -221,19 +221,12 @@ const NotificationDaemon = new Lang.Class({
let [appName, replacesId, icon, summary, body, actions, hints, timeout] = params;
let id;
for (let hint in hints) {
// unpack the variants
hints[hint] = hints[hint].deep_unpack();
}
hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
// Filter out chat, presence, calls and invitation notifications from
// Empathy, since we handle that information from telepathyClient.js
if (appName == 'Empathy' && (hints['category'] == 'im.received' ||
hints['category'] == 'x-empathy.im.room-invitation' ||
hints['category'] == 'x-empathy.call.incoming' ||
hints['category'] == 'x-empathy.transfer.incoming' ||
hints['category'] == 'x-empathy.call.incoming"' ||
hints['category'] == 'x-empathy.im.subscription-request' ||
hints['category'] == 'presence.online' ||
hints['category'] == 'presence.offline')) {
@@ -256,6 +249,13 @@ const NotificationDaemon = new Lang.Class({
}
}
for (let hint in hints) {
// unpack the variants
hints[hint] = hints[hint].deep_unpack();
}
hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
// Be compatible with the various hints for image data and image path
// 'image-data' and 'image-path' are the latest name of these hints, introduced in 1.2
@@ -483,7 +483,7 @@ const NotificationDaemon = new Lang.Class({
},
_onTrayIconAdded: function(o, icon) {
let source = this._getSource(icon.title || icon.wm_class || C_("program", "Unknown"), icon.pid, null, null, icon);
let source = this._getSource(icon.title || icon.wm_class || _("Unknown"), icon.pid, null, null, icon);
},
_onTrayIconRemoved: function(o, icon) {

View File

@@ -1,7 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Config = imports.misc.config;
const Gtk = imports.gi.Gtk;
const Meta = imports.gi.Meta;
const Mainloop = imports.mainloop;
@@ -12,9 +11,10 @@ const Shell = imports.gi.Shell;
const Gdk = imports.gi.Gdk;
const AppDisplay = imports.ui.appDisplay;
const ContactDisplay = Config.HAVE_FOLKS ? imports.ui.contactDisplay : null;
const ContactDisplay = imports.ui.contactDisplay;
const Dash = imports.ui.dash;
const DND = imports.ui.dnd;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const Panel = imports.ui.panel;
@@ -99,8 +99,10 @@ const ShellInfo = new Lang.Class({
const Overview = new Lang.Class({
Name: 'Overview',
_init : function() {
this.isDummy = !Main.sessionMode.hasOverview;
_init : function(params) {
params = Params.parse(params, { isDummy: false });
this.isDummy = params.isDummy;
// We only have an overview in user sessions, so
// create a dummy overview in other cases
@@ -208,8 +210,7 @@ const Overview = new Lang.Class({
this.addSearchProvider(new AppDisplay.AppSearchProvider());
this.addSearchProvider(new AppDisplay.SettingsSearchProvider());
this.addSearchProvider(new PlaceDisplay.PlaceSearchProvider());
if (ContactDisplay != null)
this.addSearchProvider(new ContactDisplay.ContactSearchProvider());
this.addSearchProvider(new ContactDisplay.ContactSearchProvider());
// Load remote search providers provided by applications
RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider));

View File

@@ -14,6 +14,7 @@ const St = imports.gi.St;
const Signals = imports.signals;
const Atk = imports.gi.Atk;
const Config = imports.misc.config;
const CtrlAltTab = imports.ui.ctrlAltTab;
const DND = imports.ui.dnd;
const Layout = imports.ui.layout;
@@ -31,6 +32,33 @@ const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
const SPINNER_ANIMATION_TIME = 0.2;
const STANDARD_STATUS_AREA_ORDER = ['a11y', 'keyboard', 'volume', 'bluetooth', 'network', 'battery', 'userMenu'];
const STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION = {
'a11y': imports.ui.status.accessibility.ATIndicator,
'volume': imports.ui.status.volume.Indicator,
'battery': imports.ui.status.power.Indicator,
'keyboard': imports.ui.status.keyboard.XKBIndicator,
'userMenu': imports.ui.userMenu.UserMenuButton
};
if (Config.HAVE_BLUETOOTH)
STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION['bluetooth'] = imports.ui.status.bluetooth.Indicator;
try {
STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION['network'] = imports.ui.status.network.NMApplet;
} catch(e) {
log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
}
const GDM_STATUS_AREA_ORDER = ['a11y', 'display', 'keyboard', 'volume', 'battery', 'powerMenu'];
const GDM_STATUS_AREA_SHELL_IMPLEMENTATION = {
'a11y': imports.ui.status.accessibility.ATIndicator,
'volume': imports.ui.status.volume.Indicator,
'battery': imports.ui.status.power.Indicator,
'keyboard': imports.ui.status.keyboard.XKBIndicator,
'powerMenu': imports.gdm.powerMenu.PowerMenuButton
};
// To make sure the panel corners blend nicely with the panel,
// we draw background and borders the same way, e.g. drawing
// them as filled shapes from the outside inwards instead of
@@ -934,7 +962,7 @@ const Panel = new Lang.Class({
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
/* Button on the left side of the panel. */
if (Main.sessionMode.hasOverview) {
if (global.session_type == Shell.SessionType.USER) {
this._activitiesButton = new ActivitiesButton();
this._activities = this._activitiesButton.actor;
this._leftBox.add(this._activities);
@@ -942,19 +970,28 @@ const Panel = new Lang.Class({
// The activities button has a pretend menu, so as to integrate
// more cleanly with the rest of the panel
this._menus.addMenu(this._activitiesButton.menu);
}
if (Main.sessionMode.hasAppMenu) {
this._appMenu = new AppMenuButton(this._menus);
this._leftBox.add(this._appMenu.actor);
}
/* center */
this._dateMenu = new DateMenu.DateMenuButton();
if (global.session_type == Shell.SessionType.USER)
this._dateMenu = new DateMenu.DateMenuButton({ showEvents: true });
else
this._dateMenu = new DateMenu.DateMenuButton({ showEvents: false });
this._centerBox.add(this._dateMenu.actor, { y_fill: true });
this._menus.addMenu(this._dateMenu.menu);
/* right */
if (global.session_type == Shell.SessionType.GDM) {
this._status_area_order = GDM_STATUS_AREA_ORDER;
this._status_area_shell_implementation = GDM_STATUS_AREA_SHELL_IMPLEMENTATION;
} else {
this._status_area_order = STANDARD_STATUS_AREA_ORDER;
this._status_area_shell_implementation = STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION;
}
Main.statusIconDispatcher.connect('status-icon-added', Lang.bind(this, this._onTrayIconAdded));
Main.statusIconDispatcher.connect('status-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
@@ -1086,9 +1123,9 @@ const Panel = new Lang.Class({
},
startStatusArea: function() {
for (let i = 0; i < Main.sessionMode.statusArea.order.length; i++) {
let role = Main.sessionMode.statusArea.order[i];
let constructor = Main.sessionMode.statusArea.implementation[role];
for (let i = 0; i < this._status_area_order.length; i++) {
let role = this._status_area_order[i];
let constructor = this._status_area_shell_implementation[role];
if (!constructor) {
// This icon is not implemented (this is a bug)
continue;
@@ -1138,21 +1175,18 @@ const Panel = new Lang.Class({
},
_onTrayIconAdded: function(o, icon, role) {
if (Main.sessionMode.statusArea.implementation[role]) {
if (this._status_area_shell_implementation[role]) {
// This icon is legacy, and replaced by a Shell version
// Hide it
return;
}
if (Main.sessionMode.statusArea.order.indexOf(role) == -1)
return;
icon.height = PANEL_ICON_SIZE;
let buttonBox = new PanelMenu.ButtonBox();
let box = buttonBox.actor;
box.add_actor(icon);
this._insertStatusItem(box, Main.sessionMode.statusArea.order.indexOf(role));
this._insertStatusItem(box, this._status_area_order.indexOf(role));
},
_onTrayIconRemoved: function(o, icon) {

View File

@@ -167,7 +167,6 @@ const AuthenticationDialog = new Lang.Class({
*/
this._nullMessageLabel = new St.Label({ style_class: 'prompt-dialog-null-label',
text: 'abc'});
this._nullMessageLabel.add_style_class_name('hidden');
this._nullMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._nullMessageLabel.clutter_text.line_wrap = true;
messageBox.add(this._nullMessageLabel);

View File

@@ -888,7 +888,8 @@ const PopupMenuBase = new Lang.Class({
},
addSettingsAction: function(title, desktopFile) {
if (!Main.sessionMode.allowSettings)
// Don't allow user settings to get edited unless we're in a user session
if (global.session_type != Shell.SessionType.USER)
return null;
let menuItem = this.addAction(title, function() {
@@ -906,7 +907,7 @@ const PopupMenuBase = new Lang.Class({
},
isEmpty: function() {
return this.box.get_n_children() == 0;
return this.box.get_children().length == 0;
},
isChildMenu: function(menu) {
@@ -1920,7 +1921,7 @@ const RemoteMenu = new Lang.Class({
while (k0 < currentItems.length && currentItems[k0]._ignored)
k0++;
// find the right menu item matching the model item
for (j0 = 0; k0 < currentItems.length && j0 < position; j0++, k0++) {
for (j0 = 0; j0 < position; j0++, k0++) {
if (currentItems[k0]._ignored)
k0++;
}
@@ -1930,7 +1931,7 @@ const RemoteMenu = new Lang.Class({
for (k = k0; k < currentItems.length; k++)
currentItems[k].destroy();
} else {
for (j = j0, k = k0; k < currentItems.length && j < j0 + removed; j++, k++) {
for (j = j0, k = k0; j < j0 + removed; j++, k++) {
currentItems[k].destroy();
if (currentItems[k]._ignored)
@@ -1961,9 +1962,8 @@ const RemoteMenu = new Lang.Class({
k++;
}
} else if (changeSignal) {
let signalId = this.actionGroup.connect(changeSignal, Lang.bind(this, function(actionGroup, actionName) {
actionGroup.disconnect(signalId);
if (this._actions[actionName]) return;
let signalId = this.actionGroup.connect(changeSignal, Lang.bind(this, function() {
this.actionGroup.disconnect(signalId);
// force a full update
this._modelChanged(model, 0, -1, model.get_n_items(), target);

View File

@@ -62,25 +62,10 @@ function loadRemoteSearchProvidersFromDir(dir, addProviderCallback) {
let remoteProvider, title;
try {
let group = KEY_FILE_GROUP;
let icon = keyfile.get_string(group, 'Icon');
let busName = keyfile.get_string(group, 'BusName');
let objectPath = keyfile.get_string(group, 'ObjectPath');
let appInfo = null;
try {
let desktopId = keyfile.get_string(group, 'DesktopId');
appInfo = Gio.DesktopAppInfo.new(desktopId);
} catch (e) {
}
let icon;
if (appInfo) {
icon = appInfo.get_icon();
title = appInfo.get_name();
} else {
let iconName = keyfile.get_string(group, 'Icon');
icon = new Gio.ThemedIcon({ name: iconName });
title = keyfile.get_locale_string(group, 'Title', null);
}
title = keyfile.get_locale_string(group, 'Title', null);
remoteProvider = new RemoteSearchProvider(title,
icon,

142
js/ui/screenShield.js Normal file
View File

@@ -0,0 +1,142 @@
// -*- 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 Meta = imports.gi.Meta;
const St = imports.gi.St;
const GnomeSession = imports.misc.gnomeSession;
const Lightbox = imports.ui.lightbox;
const UnlockDialog = imports.ui.unlockDialog;
const Main = imports.ui.main;
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
const LOCK_ENABLED_KEY = 'lock-enabled';
/**
* To test screen shield, make sure to kill gnome-screensaver.
*
* If you are setting org.gnome.desktop.session.idle-delay directly in dconf,
* rather than through System Settings, you also need to set
* org.gnome.settings-daemon.plugins.power.sleep-display-ac and
* org.gnome.settings-daemon.plugins.power.sleep-display-battery to the same value.
* This will ensure that the screen blanks at the right time when it fades out.
* https://bugzilla.gnome.org/show_bug.cgi?id=668703 explains the dependance.
*/
const ScreenShield = new Lang.Class({
Name: 'ScreenShield',
_init: function() {
this._presence = new GnomeSession.Presence(Lang.bind(this, function(proxy, error) {
this._onStatusChanged(proxy.status);
}));
this._presence.connectSignal('StatusChanged', Lang.bind(this, function(proxy, senderName, [status]) {
this._onStatusChanged(status);
}));
this._settings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
this._isModal = false;
this._isLocked = false;
this._group = new St.Widget({ x: 0,
y: 0 });
Main.uiGroup.add_actor(this._group);
let constraint = new Clutter.BindConstraint({ source: global.stage,
coordinate: Clutter.BindCoordinate.POSITION | Clutter.BindCoordinate.SIZE });
this._group.add_constraint(constraint);
this._lightbox = new Lightbox.Lightbox(this._group,
{ inhibitEvents: true, fadeInTime: 10, fadeFactor: 1 });
this._background = Meta.BackgroundActor.new_for_screen(global.screen);
this._background.hide();
Main.uiGroup.add_actor(this._background);
},
_onStatusChanged: function(status) {
log ("in _onStatusChanged");
if (status == GnomeSession.PresenceStatus.IDLE) {
log("session gone idle");
if (this._dialog) {
log('canceling existing dialog');
this._dialog.cancel();
this._dialog = null;
}
this._group.reactive = true;
if (!this._isModal) {
Main.pushModal(this._group);
this._isModal = true;
}
this._group.raise_top();
this._lightbox.show();
} else {
log('status is now ' + status);
let lightboxWasShown = this._lightbox.shown;
log("this._lightbox.shown " + this._lightbox.shown);
this._lightbox.hide();
let shouldLock = lightboxWasShown && this._settings.get_boolean(LOCK_ENABLED_KEY);
if (shouldLock || this._isLocked) {
this._isLocked = true;
this._background.show();
this._background.raise_top();
this._showUnlockDialog();
} else if (this._isModal) {
this._popModal();
}
}
},
_popModal: function() {
this._group.reactive = false;
Main.popModal(this._group);
this._background.hide();
},
_showUnlockDialog: function() {
if (this._dialog) {
log ('_showUnlockDialog called again when the dialog was already there');
return;
}
try {
this._dialog = new UnlockDialog.UnlockDialog();
this._dialog.connect('failed', Lang.bind(this, this._onUnlockFailed));
this._dialog.connect('unlocked', Lang.bind(this, this._onUnlockSucceded));
if (!this._dialog.open(global.get_current_time()))
throw new Error('open failed!')
this._dialog._group.raise_top();
} catch(e) {
// FIXME: this is for debugging purposes
logError(e, 'error while creating unlock dialog');
this._popModal();
}
},
_onUnlockFailed: function() {
// FIXME: for now, on failure we just destroy the dialog and create a new
// one (this is what gnome-screensaver does)
// in the future, we may want to go back to the lock screen instead
this._dialog.destroy();
this._dialog = null;
this._showUnlockDialog();
},
_onUnlockSucceded: function() {
this._dialog.destroy();
this._dialog = null;
this._popModal();
},
});

View File

@@ -51,7 +51,7 @@ const SearchResultDisplay = new Lang.Class({
* Remove all results from this display.
*/
clear: function() {
this.actor.destroy_all_children();
this.actor.get_children().forEach(function (actor) { actor.destroy(); });
},
/**
@@ -354,7 +354,7 @@ const SearchSystem = new Lang.Class({
results.push([provider, []]);
provider.getSubsearchResultSet(previousResults, terms);
} catch (error) {
log('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
}
}
} else {
@@ -364,7 +364,7 @@ const SearchSystem = new Lang.Class({
results.push([provider, []]);
provider.getInitialResultSet(terms);
} catch (error) {
log('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
}
}
}

View File

@@ -5,7 +5,6 @@ const Lang = imports.lang;
const Gtk = imports.gi.Gtk;
const Meta = imports.gi.Meta;
const St = imports.gi.St;
const Atk = imports.gi.Atk;
const DND = imports.ui.dnd;
const IconGrid = imports.ui.iconGrid;
@@ -34,13 +33,12 @@ const SearchResult = new Lang.Class({
content = new St.Bin({ style_class: 'search-result-content',
reactive: true,
can_focus: true,
track_hover: true,
accessible_role: Atk.Role.PUSH_BUTTON });
track_hover: true });
let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
{ createIcon: this.metaInfo['createIcon'] });
content.set_child(icon.actor);
this._dragActorSource = icon.icon;
content.label_actor = icon.label;
this.actor.label_actor = icon.label;
} else {
if (content._delegate && content._delegate.getDragActorSource)
this._dragActorSource = content._delegate.getDragActorSource();
@@ -131,7 +129,7 @@ const GridSearchResults = new Lang.Class({
getResultsForDisplay: function() {
let alreadyVisible = this._pendingClear ? 0 : this._grid.visibleItemsCount();
let canDisplay = this._grid.childrenInRow(this._width) * this._grid.getRowLimit()
let canDisplay = this._grid.childrenInRow(this._width) * MAX_SEARCH_RESULTS_ROWS
- alreadyVisible;
let numResults = Math.min(this._notDisplayedResult.length, canDisplay);

View File

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

View File

@@ -7,7 +7,6 @@ const Shell = imports.gi.Shell;
const Config = imports.misc.config;
const ExtensionSystem = imports.ui.extensionSystem;
const ExtensionDownloader = imports.ui.extensionDownloader;
const ExtensionUtils = imports.misc.extensionUtils;
const Flashspot = imports.ui.flashspot;
const Main = imports.ui.main;
@@ -57,8 +56,15 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
<arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/>
</method>
<method name="EnableExtension">
<arg type="s" direction="in" name="uuid"/>
</method>
<method name="DisableExtension">
<arg type="s" direction="in" name="uuid"/>
</method>
<method name="InstallRemoteExtension">
<arg type="s" direction="in" name="uuid"/>
<arg type="s" direction="in" name="version"/>
</method>
<method name="UninstallExtension">
<arg type="s" direction="in" name="uuid"/>
@@ -67,9 +73,6 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
<method name="LaunchExtensionPrefs">
<arg type="s" direction="in" name="uuid"/>
</method>
<method name="ReloadExtension">
<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" />
@@ -257,12 +260,26 @@ const GnomeShell = new Lang.Class({
return extension.errors;
},
InstallRemoteExtension: function(uuid) {
ExtensionDownloader.installExtensionFromUUID(uuid);
EnableExtension: function(uuid) {
let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY);
if (enabledExtensions.indexOf(uuid) == -1)
enabledExtensions.push(uuid);
global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
},
DisableExtension: function(uuid) {
let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY);
while (enabledExtensions.indexOf(uuid) != -1)
enabledExtensions.splice(enabledExtensions.indexOf(uuid), 1);
global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
},
InstallRemoteExtension: function(uuid, version_tag) {
ExtensionSystem.installExtensionFromUUID(uuid, version_tag);
},
UninstallExtension: function(uuid) {
return ExtensionDownloader.uninstallExtensionFromUUID(uuid);
return ExtensionSystem.uninstallExtensionFromUUID(uuid);
},
LaunchExtensionPrefs: function(uuid) {
@@ -272,11 +289,6 @@ const GnomeShell = new Lang.Class({
['extension:///' + uuid], -1, null);
},
ReloadExtension: function(uuid) {
ExtensionSystem.unloadExtension(uuid);
ExtensionSystem.loadExtension(uuid);
},
get OverviewActive() {
return Main.overview.visible;
},
@@ -288,6 +300,8 @@ const GnomeShell = new Lang.Class({
Main.overview.hide();
},
ApiVersion: ExtensionSystem.API_VERSION,
ShellVersion: Config.PACKAGE_VERSION,
_extensionStateChanged: function(_, newState) {

View File

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

View File

@@ -56,9 +56,9 @@ const ATIndicator = new Lang.Class({
let textZoom = this._buildFontItem();
this.menu.addMenuItem(textZoom);
let screenReader = this._buildItem(_("Screen Reader"), APPLICATIONS_SCHEMA,
'screen-reader-enabled');
this.menu.addMenuItem(screenReader);
// let screenReader = this._buildItem(_("Screen Reader"), APPLICATIONS_SCHEMA,
// 'screen-reader-enabled');
// this.menu.addMenuItem(screenReader);
let screenKeyboard = this._buildItem(_("Screen Keyboard"), APPLICATIONS_SCHEMA,
'screen-keyboard-enabled');

View File

@@ -2,9 +2,9 @@
const Clutter = imports.gi.Clutter;
const GdkPixbuf = imports.gi.GdkPixbuf;
const Gkbd = imports.gi.Gkbd;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const GnomeDesktop = imports.gi.GnomeDesktop;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
@@ -14,28 +14,30 @@ const PopupMenu = imports.ui.popupMenu;
const PanelMenu = imports.ui.panelMenu;
const Util = imports.misc.util;
const DESKTOP_INPUT_SOURCES_SCHEMA = 'org.gnome.desktop.input-sources';
const KEY_CURRENT_INPUT_SOURCE = 'current';
const KEY_INPUT_SOURCES = 'sources';
const INPUT_SOURCE_TYPE_XKB = 'xkb';
const LayoutMenuItem = new Lang.Class({
Name: 'LayoutMenuItem',
Extends: PopupMenu.PopupBaseMenuItem,
_init: function(displayName, shortName) {
_init: function(config, id, indicator, long_name) {
this.parent();
this.label = new St.Label({ text: displayName });
this.indicator = new St.Label({ text: shortName });
this._config = config;
this._id = id;
this.label = new St.Label({ text: long_name });
this.indicator = indicator;
this.addActor(this.label);
this.addActor(this.indicator);
},
activate: function(event) {
this.parent(event);
this._config.lock_group(this._id);
}
});
const InputSourceIndicator = new Lang.Class({
Name: 'InputSourceIndicator',
const XKBIndicator = new Lang.Class({
Name: 'XKBIndicator',
Extends: PanelMenu.Button,
_init: function() {
@@ -48,144 +50,122 @@ const InputSourceIndicator = new Lang.Class({
this.actor.add_actor(this._container);
this.actor.add_style_class_name('panel-status-button');
this._labelActors = {};
this._layoutItems = {};
this._iconActor = new St.Icon({ icon_name: 'keyboard', icon_type: St.IconType.SYMBOLIC, style_class: 'system-status-icon' });
this._container.add_actor(this._iconActor);
this._labelActors = [ ];
this._layoutItems = [ ];
this._settings = new Gio.Settings({ schema: DESKTOP_INPUT_SOURCES_SCHEMA });
this._settings.connect('changed::' + KEY_CURRENT_INPUT_SOURCE, Lang.bind(this, this._currentInputSourceChanged));
this._settings.connect('changed::' + KEY_INPUT_SOURCES, Lang.bind(this, this._inputSourcesChanged));
this._showFlags = false;
this._config = Gkbd.Configuration.get();
this._config.connect('changed', Lang.bind(this, this._syncConfig));
this._config.connect('group-changed', Lang.bind(this, this._syncGroup));
this._config.start_listen();
this._currentSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
this._xkbInfo = new GnomeDesktop.XkbInfo();
this._syncConfig();
this._inputSourcesChanged();
// re-using "allowSettings" for the keyboard layout is a bit shady,
// but at least for now it is used as "allow popping up windows
// from shell menus"; we can always add a separate sessionMode
// option if need arises.
if (Main.sessionMode.allowSettings) {
if (global.session_type == Shell.SessionType.USER) {
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, this._showLayout));
this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, function() {
Main.overview.hide();
Util.spawn(['gkbd-keyboard-display', '-g', String(this._config.get_current_group() + 1)]);
}));
}
this.menu.addSettingsAction(_("Region and Language Settings"), 'gnome-region-panel.desktop');
},
_currentInputSourceChanged: function() {
let nVisibleSources = Object.keys(this._layoutItems).length;
if (nVisibleSources < 2)
return;
_adjustGroupNames: function(names) {
// Disambiguate duplicate names with a subscript
// This is O(N^2) to avoid sorting names
// but N <= 4 so who cares?
let nSources = this._settings.get_value(KEY_INPUT_SOURCES).n_children();
let newCurrentSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
if (newCurrentSourceIndex >= nSources)
return;
if (!this._layoutItems[newCurrentSourceIndex]) {
// This source index is invalid as we weren't able to
// build a menu item for it, so we hide ourselves since we
// can't fix it here. *shrug*
this.menu.close();
this.actor.hide();
return;
} else {
this.actor.show();
for (let i = 0; i < names.length; i++) {
let name = names[i];
let cnt = 0;
for (let j = i + 1; j < names.length; j++) {
if (names[j] == name) {
cnt++;
// U+2081 SUBSCRIPT ONE
names[j] = name + String.fromCharCode(0x2081 + cnt);
}
}
if (cnt != 0)
names[i] = name + '\u2081';
}
if (this._layoutItems[this._currentSourceIndex]) {
this._layoutItems[this._currentSourceIndex].setShowDot(false);
this._container.set_skip_paint(this._labelActors[this._currentSourceIndex], true);
}
this._layoutItems[newCurrentSourceIndex].setShowDot(true);
this._container.set_skip_paint(this._labelActors[newCurrentSourceIndex], false);
this._currentSourceIndex = newCurrentSourceIndex;
return names;
},
_inputSourcesChanged: function() {
let sources = this._settings.get_value(KEY_INPUT_SOURCES);
let nSources = sources.n_children();
for (let i in this._layoutItems)
this._layoutItems[i].destroy();
for (let i in this._labelActors)
this._labelActors[i].destroy();
this._layoutItems = {};
this._labelActors = {};
let infos = [];
let infosByShortName = {};
for (let i = 0; i < nSources; i++) {
let [type, id] = sources.get_child_value(i).deep_unpack();
if (type != INPUT_SOURCE_TYPE_XKB)
continue;
let info = {};
[info.exists, info.displayName, info.shortName, , ] =
this._xkbInfo.get_layout_info(id);
if (!info.exists)
continue;
info.sourceIndex = i;
if (!(info.shortName in infosByShortName))
infosByShortName[info.shortName] = [];
infosByShortName[info.shortName].push(info);
infos.push(info);
_syncConfig: function() {
this._showFlags = this._config.if_flags_shown();
if (this._showFlags) {
this._container.set_skip_paint(this._iconActor, false);
} else {
this._container.set_skip_paint(this._iconActor, true);
}
if (infos.length > 1) {
let groups = this._config.get_group_names();
if (groups.length > 1) {
this.actor.show();
} else {
this.menu.close();
this.actor.hide();
}
for (let i = 0; i < infos.length; i++) {
let info = infos[i];
if (infosByShortName[info.shortName].length > 1) {
let sub = infosByShortName[info.shortName].indexOf(info) + 1;
info.shortName += String.fromCharCode(0x2080 + sub);
}
for (let i = 0; i < this._layoutItems.length; i++)
this._layoutItems[i].destroy();
let item = new LayoutMenuItem(info.displayName, info.shortName);
this._layoutItems[info.sourceIndex] = item;
for (let i = 0; i < this._labelActors.length; i++)
this._labelActors[i].destroy();
let short_names = this._adjustGroupNames(this._config.get_short_group_names());
this._selectedLayout = null;
this._layoutItems = [ ];
this._selectedLabel = null;
this._labelActors = [ ];
for (let i = 0; i < groups.length; i++) {
let icon_name = this._config.get_group_name(i);
let actor;
if (this._showFlags)
actor = new St.Icon({ icon_name: icon_name, icon_type: St.IconType.SYMBOLIC, style_class: 'popup-menu-icon' });
else
actor = new St.Label({ text: short_names[i] });
let item = new LayoutMenuItem(this._config, i, actor, groups[i]);
item._short_group_name = short_names[i];
item._icon_name = icon_name;
this._layoutItems.push(item);
this.menu.addMenuItem(item, i);
item.connect('activate', Lang.bind(this, function() {
this._settings.set_value(KEY_CURRENT_INPUT_SOURCE,
GLib.Variant.new_uint32(info.sourceIndex));
}));
let shortLabel = new St.Label({ text: info.shortName });
this._labelActors[info.sourceIndex] = shortLabel;
let shortLabel = new St.Label({ text: short_names[i] });
this._labelActors.push(shortLabel);
this._container.add_actor(shortLabel);
this._container.set_skip_paint(shortLabel, true);
}
this._currentInputSourceChanged();
this._syncGroup();
},
_showLayout: function() {
Main.overview.hide();
_syncGroup: function() {
let selected = this._config.get_current_group();
let sources = this._settings.get_value(KEY_INPUT_SOURCES);
let current = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
let id = sources.get_child_value(current).deep_unpack()[1];
let [, , , xkbLayout, xkbVariant] = this._xkbInfo.get_layout_info(id);
if (this._selectedLayout) {
this._selectedLayout.setShowDot(false);
this._selectedLayout = null;
}
if (!xkbLayout || xkbLayout.length == 0)
return;
if (this._selectedLabel) {
this._container.set_skip_paint(this._selectedLabel, true);
this._selectedLabel = null;
}
let description = xkbLayout;
if (xkbVariant.length > 0)
description = description + '\t' + xkbVariant;
let item = this._layoutItems[selected];
item.setShowDot(true);
Util.spawn(['gkbd-keyboard-display', '-l', description]);
this._iconActor.icon_name = item._icon_name;
this._selectedLabel = this._labelActors[selected];
this._container.set_skip_paint(this._selectedLabel, this._showFlags);
this._selectedLayout = item;
},
_containerGetPreferredWidth: function(container, for_height, alloc) {
@@ -193,11 +173,15 @@ const InputSourceIndicator = new Lang.Class({
// for the height of all children, but we ignore the results
// for those we don't actually display.
let max_min_width = 0, max_natural_width = 0;
if (this._showFlags)
[max_min_width, max_natural_width] = this._iconActor.get_preferred_width(for_height);
for (let i in this._labelActors) {
for (let i = 0; i < this._labelActors.length; i++) {
let [min_width, natural_width] = this._labelActors[i].get_preferred_width(for_height);
max_min_width = Math.max(max_min_width, min_width);
max_natural_width = Math.max(max_natural_width, natural_width);
if (!this._showFlags) {
max_min_width = Math.max(max_min_width, min_width);
max_natural_width = Math.max(max_natural_width, natural_width);
}
}
alloc.min_size = max_min_width;
@@ -206,11 +190,15 @@ const InputSourceIndicator = new Lang.Class({
_containerGetPreferredHeight: function(container, for_width, alloc) {
let max_min_height = 0, max_natural_height = 0;
for (let i in this._labelActors) {
if (this._showFlags)
[max_min_height, max_natural_height] = this._iconActor.get_preferred_height(for_width);
for (let i = 0; i < this._labelActors.length; i++) {
let [min_height, natural_height] = this._labelActors[i].get_preferred_height(for_width);
max_min_height = Math.max(max_min_height, min_height);
max_natural_height = Math.max(max_natural_height, natural_height);
if (!this._showFlags) {
max_min_height = Math.max(max_min_height, min_height);
max_natural_height = Math.max(max_natural_height, natural_height);
}
}
alloc.min_size = max_min_height;
@@ -224,7 +212,8 @@ const InputSourceIndicator = new Lang.Class({
box.y2 -= box.y1;
box.y1 = 0;
for (let i in this._labelActors)
this._iconActor.allocate_align_fill(box, 0.5, 0, false, false, flags);
for (let i = 0; i < this._labelActors.length; i++)
this._labelActors[i].allocate_align_fill(box, 0.5, 0, false, false, flags);
}
});

View File

@@ -101,10 +101,11 @@ const NMNetworkMenuItem = new Lang.Class({
Name: 'NMNetworkMenuItem',
Extends: PopupMenu.PopupBaseMenuItem,
_init: function(bestAP, title, params) {
_init: function(accessPoints, title, params) {
this.parent(params);
this.bestAP = bestAP;
accessPoints = sortAccessPoints(accessPoints);
this.bestAP = accessPoints[0];
if (!title) {
let ssid = this.bestAP.get_ssid();
@@ -126,10 +127,24 @@ const NMNetworkMenuItem = new Lang.Class({
this.bestAP._secType != NMAccessPointSecurity.NONE)
this._secureIcon.icon_name = 'network-wireless-encrypted';
this._icons.add_actor(this._secureIcon);
this._accessPoints = [ ];
for (let i = 0; i < accessPoints.length; i++) {
let ap = accessPoints[i];
// need a wrapper object here, because the access points can be shared
// between many NMNetworkMenuItems
let apObj = {
ap: ap,
updateId: ap.connect('notify::strength', Lang.bind(this, this._updated))
};
this._accessPoints.push(apObj);
}
},
updateBestAP: function(ap) {
this.bestAP = ap;
_updated: function(ap) {
if (ap.strength > this.bestAP.strength)
this.bestAP = ap;
this._signalIcon.icon_name = this._getIcon();
},
@@ -138,6 +153,36 @@ const NMNetworkMenuItem = new Lang.Class({
return 'network-workgroup';
else
return 'network-wireless-signal-' + signalToIcon(this.bestAP.strength);
},
updateAccessPoints: function(accessPoints) {
for (let i = 0; i < this._accessPoints.length; i++) {
let apObj = this._accessPoints[i];
apObj.ap.disconnect(apObj.updateId);
apObj.updateId = 0;
}
accessPoints = sortAccessPoints(accessPoints);
this.bestAP = accessPoints[0];
this._accessPoints = [ ];
for (let i = 0; i < accessPoints; i++) {
let ap = accessPoints[i];
let apObj = {
ap: ap,
updateId: ap.connect('notify::strength', Lang.bind(this, this._updated))
};
this._accessPoints.push(apObj);
}
},
destroy: function() {
for (let i = 0; i < this._accessPoints.length; i++) {
let apObj = this._accessPoints[i];
apObj.ap.disconnect(apObj.updateId);
apObj.updateId = 0;
}
this.parent();
}
});
@@ -252,17 +297,16 @@ const NMDevice = new Lang.Class({
this._client = client;
this._connections = [ ];
for (let i = 0; i < connections.length; i++) {
if (!connections[i].get_uuid())
if (!connections[i]._uuid)
continue;
if (!this.connectionValid(connections[i]))
continue;
// record the connection
let obj = {
connection: connections[i],
name: connections[i].get_id(),
uuid: connections[i].get_uuid(),
name: connections[i]._name,
uuid: connections[i]._uuid,
timestamp: connections[i]._timestamp,
item: null,
};
this._connections.push(obj);
}
@@ -357,46 +401,48 @@ const NMDevice = new Lang.Class({
},
checkConnection: function(connection) {
let pos = this._findConnection(connection.get_uuid());
let pos = this._findConnection(connection._uuid);
let exists = pos != -1;
let valid = this.connectionValid(connection);
let similar = false;
if (exists) {
let existing = this._connections[pos];
// Check if connection changed name or id
similar = existing.name == connection.get_id() &&
existing.timestamp == connection._timestamp;
}
if (exists && valid && similar) {
// Nothing to do
return;
}
if (exists)
if (exists && !valid)
this.removeConnection(connection);
if (valid)
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) {
// record the connection
let obj = {
connection: connection,
name: connection.get_id(),
uuid: connection.get_uuid(),
name: connection._name,
uuid: connection._uuid,
timestamp: connection._timestamp,
item: null,
};
Util.insertSorted(this._connections, obj, this._connectionSortFunction);
this._connections.push(obj);
this._connections.sort(this._connectionSortFunction);
this._clearSection();
this._queueCreateSection();
},
removeConnection: function(connection) {
let pos = this._findConnection(connection.get_uuid());
if (!connection._uuid) {
log('Cannot remove a connection without an UUID');
return;
}
let pos = this._findConnection(connection._uuid);
if (pos == -1) {
// this connection was never added, nothing to do here
return;
@@ -568,7 +614,7 @@ const NMDevice = new Lang.Class({
let title;
let active = this._activeConnection._connection;
if (active) {
title = active.get_id();
title = active._name;
} else {
/* TRANSLATORS: this is the indication that a connection for another logged in user is active,
and we cannot access its settings (including the name) */
@@ -666,10 +712,10 @@ const NMDeviceWired = new Lang.Class({
_createAutomaticConnection: function() {
let connection = new NetworkManager.Connection();
let uuid = NetworkManager.utils_uuid_generate();
connection._uuid = NetworkManager.utils_uuid_generate();
connection.add_setting(new NetworkManager.SettingWired());
connection.add_setting(new NetworkManager.SettingConnection({
uuid: uuid,
uuid: connection._uuid,
id: this._autoConnectionName,
type: NetworkManager.SETTING_WIRED_SETTING_NAME,
autoconnect: true
@@ -813,10 +859,10 @@ const NMDeviceBluetooth = new Lang.Class({
_createAutomaticConnection: function() {
let connection = new NetworkManager.Connection;
let uuid = NetworkManager.utils_uuid_generate();
connection._uuid = NetworkManager.utils_uuid_generate();
connection.add_setting(new NetworkManager.SettingBluetooth);
connection.add_setting(new NetworkManager.SettingConnection({
uuid: uuid,
uuid: connection._uuid,
id: this._autoConnectionName,
type: NetworkManager.SETTING_BLUETOOTH_SETTING_NAME,
autoconnect: false
@@ -851,12 +897,12 @@ const NMDeviceVPN = new Lang.Class({
Name: 'NMDeviceVPN',
Extends: NMDevice,
_init: function(client, device, connections) {
_init: function(client) {
// Disable autoconnections
this._autoConnectionName = null;
this.category = NMConnectionCategory.VPN;
this.parent(client, null, connections);
this.parent(client, null, [ ]);
},
connectionValid: function(connection) {
@@ -868,24 +914,13 @@ const NMDeviceVPN = new Lang.Class({
},
get connected() {
if (!this._activeConnection)
return false;
return this._activeConnection.vpn_state == NetworkManager.VPNConnectionState.ACTIVATED;
return !!this._activeConnection;
},
setActiveConnection: function(activeConnection) {
if (this._stateChangeId)
this._activeConnection.disconnect(this._stateChangeId);
this._stateChangeId = 0;
this.parent(activeConnection);
if (this._activeConnection)
this._stateChangeId = this._activeConnection.connect('vpn-state-changed',
Lang.bind(this, this._connectionStateChanged));
this.emit('state-changed');
this.emit('active-connection-changed');
},
_shouldShowConnectionList: function() {
@@ -898,39 +933,7 @@ const NMDeviceVPN = new Lang.Class({
},
getStatusLabel: function() {
if (!this._activeConnection) // Same as DISCONNECTED
return null;
switch(this._activeConnection.vpn_state) {
case NetworkManager.VPNConnectionState.DISCONNECTED:
case NetworkManager.VPNConnectionState.ACTIVATED:
return null;
case NetworkManager.VPNConnectionState.PREPARE:
case NetworkManager.VPNConnectionState.CONNECT:
case NetworkManager.VPNConnectionState.IP_CONFIG_GET:
return _("connecting...");
case NetworkManager.VPNConnectionState.NEED_AUTH:
/* Translators: this is for network connections that require some kind of key or password */
return _("authentication required");
case NetworkManager.VPNConnectionState.FAILED:
return _("connection failed");
default:
log('VPN connection state invalid, is %d'.format(this.device.state));
return 'invalid';
}
},
_connectionStateChanged: function(connection, newstate, reason) {
if (newstate == NetworkManager.VPNConnectionState.FAILED) {
// FIXME: if we ever want to show something based on reason,
// we need to convert from NetworkManager.VPNConnectionStateReason
// to NetworkManager.DeviceStateReason
this.emit('activation-failed', reason);
}
// Differently from real NMDevices, there is no need to queue
// an update of the menu section, contents wouldn't change anyway
this.emit('state-changed');
return null;
}
});
@@ -981,7 +984,6 @@ const NMDeviceWireless = new Lang.Class({
obj.ssidText = ssidToLabel(obj.ssid);
this._networks.push(obj);
}
ap._updateId = ap.connect('notify::strength', Lang.bind(this, this._onApStrengthChanged));
// Check if some connection is valid for this AP
for (let j = 0; j < validConnections.length; j++) {
@@ -993,10 +995,6 @@ const NMDeviceWireless = new Lang.Class({
}
}
// Sort APs within each network by strength
for (let i = 0; i < this._networks.length; i++)
sortAccessPoints(this._networks[i].accessPoints);
if (this.device.active_access_point) {
let networkPos = this._findNetwork(this.device.active_access_point);
@@ -1079,7 +1077,7 @@ const NMDeviceWireless = new Lang.Class({
// the user toggles the switch and has more than one wireless device)
if (this._networks.length > 0) {
let connection = this._createAutomaticConnection(this._networks[0]);
let accessPoints = this._networks[0].accessPoints;
let accessPoints = sortAccessPoints(this._networks[0].accessPoints);
this._client.add_and_activate_connection(connection, this.device, accessPoints[0].dbus_path, null);
}
},
@@ -1149,13 +1147,6 @@ const NMDeviceWireless = new Lang.Class({
else if (!oneHasConnection && twoHasConnection)
return 1;
let oneStrength = one.accessPoints[0].strength;
let twoStrength = two.accessPoints[0].strength;
// place stronger connections first
if (oneStrength != twoStrength)
return oneStrength < twoStrength ? 1 : -1;
let oneHasSecurity = one.security != NMAccessPointSecurity.NONE;
let twoHasSecurity = two.security != NMAccessPointSecurity.NONE;
@@ -1205,28 +1196,6 @@ const NMDeviceWireless = new Lang.Class({
return -1;
},
_onApStrengthChanged: function(ap) {
let res = this._findExistingNetwork(ap);
if (res == null) {
// Uhm... stale signal?
return;
}
let network = this._networks[res.network];
network.accessPoints.splice(res.ap, 1);
Util.insertSorted(network.accessPoints, ap, function(one, two) {
return two.strength - one.strength;
});
this._networks.splice(res.network, 1);
let newPos = Util.insertSorted(this._networks, network, Lang.bind(this, this._networkSortFunction));
if (newPos != res.network) {
this._clearSection();
this._queueCreateSection();
}
},
_accessPointAdded: function(device, accessPoint) {
if (accessPoint.get_ssid() == null) {
// This access point is not visible yet
@@ -1246,11 +1215,9 @@ const NMDeviceWireless = new Lang.Class({
return;
}
Util.insertSorted(apObj.accessPoints, accessPoint, function(one, two) {
return two.strength - one.strength;
});
apObj.accessPoints.push(accessPoint);
if (apObj.item)
apObj.item.updateBestAP(apObj.accessPoints[0]);
apObj.item.updateAccessPoints(apObj.accessPoints);
} else {
apObj = { ssid: accessPoint.get_ssid(),
mode: accessPoint.mode,
@@ -1261,7 +1228,6 @@ const NMDeviceWireless = new Lang.Class({
};
apObj.ssidText = ssidToLabel(apObj.ssid);
}
accessPoint._updateId = accessPoint.connect('notify::strength', Lang.bind(this, this._onApStrengthChanged));
// check if this enables new connections for this group
for (let i = 0; i < this._connections.length; i++) {
@@ -1269,26 +1235,23 @@ const NMDeviceWireless = new Lang.Class({
if (accessPoint.connection_valid(connection) &&
apObj.connections.indexOf(connection) == -1) {
apObj.connections.push(connection);
// this potentially changes the order
needsupdate = true;
}
}
if (pos != -1)
this._networks.splice(pos, 1);
let newPos = Util.insertSorted(this._networks, apObj, this._networkSortFunction);
if (pos == -1 || needsupdate) {
if (pos != -1)
this._networks.splice(pos, 1);
pos = Util.insertSorted(this._networks, apObj, this._networkSortFunction);
// Queue an update of the UI if we changed the order
if (newPos != pos) {
this._clearSection();
this._queueCreateSection();
}
},
_accessPointRemoved: function(device, accessPoint) {
if (accessPoint._updateId) {
accessPoint.disconnect(accessPoint._updateId);
accessPoint._updateId = 0;
}
let res = this._findExistingNetwork(accessPoint);
if (res == null) {
@@ -1330,30 +1293,17 @@ const NMDeviceWireless = new Lang.Class({
this._overflowItem = null;
}
}
this._networks.splice(res.network, 1);
} else {
let okPrev = true, okNext = true;
if (res.network > 0)
okPrev = this._networkSortFunction(this._networks[res.network - 1], apObj) >= 0;
if (res.network < this._networks.length-1)
okNext = this._networkSortFunction(this._networks[res.network + 1], apObj) <= 0;
if (!okPrev || !okNext) {
this._clearSection();
this._queueCreateSection();
} else if (apObj.item) {
apObj.item.updateBestAP(apObj.accessPoints[0]);
}
}
} else if (apObj.item)
apObj.item.updateAccessPoints(apObj.accessPoints);
},
_createAPItem: function(connection, accessPointObj, useConnectionName) {
let item = new NMNetworkMenuItem(accessPointObj.accessPoints[0], useConnectionName ? connection.get_id() : undefined);
let item = new NMNetworkMenuItem(accessPointObj.accessPoints, useConnectionName ? connection._name : undefined);
item._connection = connection;
item.connect('activate', Lang.bind(this, function() {
let accessPoints = accessPointObj.accessPoints;
let accessPoints = sortAccessPoints(accessPointObj.accessPoints);
for (let i = 0; i < accessPoints.length; i++) {
if (accessPoints[i].connection_valid(connection)) {
this._client.activate_connection(connection, this.device, accessPoints[i].dbus_path, null);
@@ -1373,7 +1323,9 @@ const NMDeviceWireless = new Lang.Class({
},
removeConnection: function(connection) {
let pos = this._findConnection(connection.get_uuid());
if (!connection._uuid)
return;
let pos = this._findConnection(connection._uuid);
if (pos == -1) {
// removing connection that was never added
return;
@@ -1387,7 +1339,7 @@ const NMDeviceWireless = new Lang.Class({
let apObj = this._networks[i];
let connections = apObj.connections;
for (let k = 0; k < connections.length; k++) {
if (connections[k].get_uuid() == connection.get_uuid()) {
if (connections[k]._uuid == connection._uuid) {
// remove the connection from the access point group
connections.splice(k);
forceupdate = forceupdate || connections.length == 0;
@@ -1403,7 +1355,7 @@ const NMDeviceWireless = new Lang.Class({
forceupdate = true;
} else {
for (let j = 0; j < items.length; j++) {
if (items[j]._connection.get_uuid() == connection.get_uuid()) {
if (items[j]._connection._uuid == connection._uuid) {
items[j].destroy();
break;
}
@@ -1430,8 +1382,8 @@ const NMDeviceWireless = new Lang.Class({
// record the connection
let obj = {
connection: connection,
name: connection.get_id(),
uuid: connection.get_uuid(),
name: connection._name,
uuid: connection._uuid,
};
this._connections.push(obj);
@@ -1460,19 +1412,27 @@ const NMDeviceWireless = new Lang.Class({
},
_createActiveConnectionItem: function() {
let title;
if (this._activeConnection && this._activeConnection._connection)
title = this._activeConnection._connection.get_id();
else
title = _("Connected (private)");
if (this._activeNetwork)
this._activeConnectionItem = new NMNetworkMenuItem(this.device.active_access_point, undefined,
{ reactive: false });
else
this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(title,
'network-wireless-connected',
{ reactive: false });
let icon, title;
if (this._activeConnection && this._activeConnection._connection) {
let connection = this._activeConnection._connection;
if (this._activeNetwork)
this._activeConnectionItem = new NMNetworkMenuItem(this._activeNetwork.accessPoints, undefined,
{ reactive: false });
else
this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(connection._name,
'network-wireless-connected',
{ reactive: false });
} else {
// We cannot read the connection (due to ACL, or API incompatibility), but we still show signal if we have it
let menuItem;
if (this._activeNetwork)
this._activeConnectionItem = new NMNetworkMenuItem(this._activeNetwork.accessPoints, undefined,
{ reactive: false });
else
this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(_("Connected (private)"),
'network-wireless-connected',
{ reactive: false });
}
this._activeConnectionItem.setShowDot(true);
},
@@ -1512,9 +1472,9 @@ const NMDeviceWireless = new Lang.Class({
apObj.item.menu.addMenuItem(this._createAPItem(apObj.connections[i], apObj, true));
}
} else {
apObj.item = new NMNetworkMenuItem(apObj.accessPoints[0]);
apObj.item = new NMNetworkMenuItem(apObj.accessPoints);
apObj.item.connect('activate', Lang.bind(this, function() {
let accessPoints = apObj.accessPoints;
let accessPoints = sortAccessPoints(apObj.accessPoints);
if ( (accessPoints[0]._secType == NMAccessPointSecurity.WPA2_ENT)
|| (accessPoints[0]._secType == NMAccessPointSecurity.WPA_ENT)) {
// 802.1x-enabled APs require further configuration, so they're
@@ -1567,25 +1527,10 @@ const NMDeviceWireless = new Lang.Class({
const NMApplet = new Lang.Class({
Name: 'NMApplet',
Extends: PanelMenu.Button,
Extends: PanelMenu.SystemStatusButton,
_init: function() {
this.parent(0.0, _('Network'));
this._box = new St.BoxLayout({ name: 'networkMenu' });
this.actor.add_actor (this._box);
this.actor.add_style_class_name('panel-status-button');
this._primaryIcon = new St.Icon({ icon_name: 'network-offline',
icon_type: St.IconType.SYMBOLIC,
style_class: 'system-status-icon' });
this._box.add_actor(this._primaryIcon);
this._secondaryIcon = new St.Icon({ icon_name: 'network-vpn',
icon_type: St.IconType.SYMBOLIC,
style_class: 'system-status-icon',
visible: false });
this._box.add_actor(this._secondaryIcon);
this.parent('network-offline', _("Network"));
this._client = NMClient.Client.new();
@@ -1599,16 +1544,6 @@ const NMApplet = new Lang.Class({
this.menu.addMenuItem(this._statusSection);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._activeConnections = [ ];
this._connections = [ ];
this._mainConnection = null;
this._vpnConnection = null;
this._activeAccessPointUpdateId = 0;
this._activeAccessPoint = null;
this._mobileUpdateId = 0;
this._mobileUpdateDevice = null;
this._devices = { };
this._devices.wired = {
@@ -1644,9 +1579,13 @@ const NMApplet = new Lang.Class({
this._devices.vpn = {
section: new PopupMenu.PopupMenuSection(),
device: this._makeWrapperDevice(NMDeviceVPN, null),
device: new NMDeviceVPN(this._client),
item: new NMWiredSectionTitleMenuItem(_("VPN Connections"))
};
this._devices.vpn.device.connect('active-connection-changed', Lang.bind(this, function() {
this._devices.vpn.item.updateForDevice(this._devices.vpn.device);
}));
this._devices.vpn.item.updateForDevice(this._devices.vpn.device);
this._devices.vpn.section.addMenuItem(this._devices.vpn.item);
this._devices.vpn.section.addMenuItem(this._devices.vpn.device.section);
this._devices.vpn.section.actor.hide();
@@ -1654,6 +1593,15 @@ const NMApplet = new Lang.Class({
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addSettingsAction(_("Network Settings"), 'gnome-network-panel.desktop');
this._activeConnections = [ ];
this._connections = [ ];
this._mainConnection = null;
this._activeAccessPointUpdateId = 0;
this._activeAccessPoint = null;
this._mobileUpdateId = 0;
this._mobileUpdateDevice = null;
// Device types
this._dtypes = { };
this._dtypes[NetworkManager.DeviceType.ETHERNET] = NMDeviceWired;
@@ -1694,10 +1642,6 @@ const NMApplet = new Lang.Class({
}));
},
setIcon: function(iconName) {
this._primaryIcon.icon_name = iconName;
},
_ensureSource: function() {
if (!this._source) {
this._source = new MessageTray.Source(_("Network Manager"),
@@ -1724,18 +1668,6 @@ const NMApplet = new Lang.Class({
},
_syncSectionTitle: function(category) {
if (category == NMConnectionCategory.VPN) {
// Special case VPN: it's only one device (and a fake one
// actually), and we don't show it if empty
let device = this._devices.vpn.device;
let section = this._devices.vpn.section;
let item = this._devices.vpn.item;
section.actor.visible = !device.empty;
item.updateForDevice(device);
return;
}
let devices = this._devices[category].devices;
let item = this._devices[category].item;
let section = this._devices[category].section;
@@ -1786,29 +1718,6 @@ const NMApplet = new Lang.Class({
this._source.notify(device._notification);
},
_makeWrapperDevice: function(wrapperClass, device) {
let wrapper = new wrapperClass(this._client, device, this._connections);
wrapper._activationFailedId = wrapper.connect('activation-failed', Lang.bind(this, function(device, reason) {
// XXX: nm-applet has no special text depending on reason
// but I'm not sure of this generic message
this._notifyForDevice(device, 'network-error',
_("Connection failed"),
_("Activation of network connection failed"),
MessageTray.Urgency.HIGH);
}));
wrapper._deviceStateChangedId = wrapper.connect('state-changed', Lang.bind(this, function(dev) {
this._syncSectionTitle(dev.category);
}));
wrapper._destroyId = wrapper.connect('destroy', function(wrapper) {
wrapper.disconnect(wrapper._activationFailedId);
wrapper.disconnect(wrapper._deviceStateChangedId);
wrapper.disconnect(wrapper._destroyId);
});
return wrapper;
},
_deviceAdded: function(client, device) {
if (device._delegate) {
// already seen, not adding again
@@ -1816,8 +1725,24 @@ const NMApplet = new Lang.Class({
}
let wrapperClass = this._dtypes[device.get_device_type()];
if (wrapperClass) {
let wrapper = this._makeWrapperDevice(wrapperClass, device);
let wrapper = new wrapperClass(this._client, device, this._connections);
wrapper._activationFailedId = wrapper.connect('activation-failed', Lang.bind(this, function(device, reason) {
// XXX: nm-applet has no special text depending on reason
// but I'm not sure of this generic message
this._notifyForDevice(device, 'network-error',
_("Connection failed"),
_("Activation of network connection failed"),
MessageTray.Urgency.HIGH);
}));
wrapper._deviceStateChangedId = wrapper.connect('state-changed', Lang.bind(this, function(dev) {
this._syncSectionTitle(dev.category);
}));
wrapper._destroyId = wrapper.connect('destroy', function(wrapper) {
wrapper.disconnect(wrapper._activationFailedId);
wrapper.disconnect(wrapper._deviceStateChangedId);
wrapper.disconnect(wrapper._destroyId);
});
let section = this._devices[wrapper.category].section;
let devices = this._devices[wrapper.category].devices;
@@ -1871,8 +1796,6 @@ const NMApplet = new Lang.Class({
this._activeConnections = newActiveConnections;
this._mainConnection = null;
this._vpnConnection = null;
let activating = null;
let default_ip4 = null;
let default_ip6 = null;
@@ -1906,10 +1829,10 @@ const NMApplet = new Lang.Class({
default_ip4 = a;
if (a.default6)
default_ip6 = a;
if (a._type == 'vpn')
active_vpn = a;
else if (a.state == NetworkManager.ActiveConnectionState.ACTIVATING)
if (a.state == NetworkManager.ActiveConnectionState.ACTIVATING)
activating = a;
if (!a._primaryDevice) {
@@ -1938,8 +1861,7 @@ const NMApplet = new Lang.Class({
}
}
this._mainConnection = activating || default_ip4 || default_ip6 || this._activeConnections[0] || null;
this._vpnConnection = active_vpn;
this._mainConnection = activating || active_vpn || default_ip4 || default_ip6 || this._activeConnections[0] || null;
},
_notifyActivated: function(activeConnection) {
@@ -1956,7 +1878,7 @@ const NMApplet = new Lang.Class({
let connections = this._settings.list_connections();
for (let i = 0; i < connections.length; i++) {
let connection = connections[i];
if (connection._updatedId) {
if (connection._uuid) {
// connection was already seen (for example because NetworkManager was restarted)
continue;
}
@@ -1969,7 +1891,7 @@ const NMApplet = new Lang.Class({
},
_newConnection: function(settings, connection) {
if (connection._updatedId) {
if (connection._uuid) {
// connection was already seen
return;
}
@@ -1992,31 +1914,35 @@ const NMApplet = new Lang.Class({
if (section == NMConnectionCategory.VPN) {
this._devices.vpn.device.removeConnection(connection);
this._syncSectionTitle(section);
if (this._devices.vpn.device.empty)
this._devices.vpn.section.actor.hide();
} else if (section != NMConnectionCategory.INVALID) {
let devices = this._devices[section].devices;
for (let i = 0; i < devices.length; i++)
devices[i].removeConnection(connection);
}
connection._uuid = null;
connection.disconnect(connection._removedId);
connection.disconnect(connection._updatedId);
connection._removedId = connection._updatedId = 0;
},
_updateConnection: function(connection) {
let connectionSettings = connection.get_setting_by_name(NetworkManager.SETTING_CONNECTION_SETTING_NAME);
connection._type = connectionSettings.type;
connection._section = this._ctypes[connection._type] || NMConnectionCategory.INVALID;
connection._name = connectionSettings.id;
connection._uuid = connectionSettings.uuid;
connection._timestamp = connectionSettings.timestamp;
let section = connection._section;
if (section == NMConnectionCategory.INVALID)
if (connection._section == NMConnectionCategory.INVALID)
return;
if (section == NMConnectionCategory.VPN) {
this._devices.vpn.device.checkConnection(connection);
this._syncSectionTitle(section);
this._devices.vpn.section.actor.show();
} else {
let devices = this._devices[section].devices;
for (let i = 0; i < devices.length; i++) {
@@ -2039,10 +1965,12 @@ const NMApplet = new Lang.Class({
this._statusSection.actor.hide();
this._syncSectionTitle(NMConnectionCategory.WIRED);
this._syncSectionTitle(NMConnectionCategory.WIRELESS);
this._syncSectionTitle(NMConnectionCategory.WWAN);
this._syncSectionTitle(NMConnectionCategory.VPN);
this._syncSectionTitle('wired');
this._syncSectionTitle('wireless');
this._syncSectionTitle('wwan');
if (!this._devices.vpn.device.empty)
this._devices.vpn.section.actor.show();
},
_syncNMState: function() {
@@ -2085,6 +2013,9 @@ const NMApplet = new Lang.Class({
case NMConnectionCategory.WIRED:
this.setIcon('network-wired-acquiring');
break;
case NMConnectionCategory.VPN:
this.setIcon('network-vpn-acquiring');
break;
default:
// fallback to a generic connected icon
// (it could be a private connection of some other user)
@@ -2147,6 +2078,9 @@ const NMApplet = new Lang.Class({
this.setIcon('network-cellular-signal-' + signalToIcon(dev.mobileDevice.signal_quality));
hasMobileIcon = true;
break;
case NMConnectionCategory.VPN:
this.setIcon('network-vpn');
break;
default:
// fallback to a generic connected icon
// (it could be a private connection of some other user)
@@ -2155,25 +2089,6 @@ const NMApplet = new Lang.Class({
}
}
// update VPN indicator
if (this._vpnConnection) {
let vpnIconName = 'network-vpn';
if (this._vpnConnection.state == NetworkManager.ActiveConnectionState.ACTIVATING)
vpnIconName = 'network-vpn-acquiring';
// only show a separate icon when we're using a wireless/3g connection
if (mc._section == NMConnectionCategory.WIRELESS ||
mc._section == NMConnectionCategory.WWAN) {
this._secondaryIcon.icon_name = vpnIconName;
this._secondaryIcon.visible = true;
} else {
this.setIcon(vpnIconName);
this._secondaryIcon.visible = false;
}
} else {
this._secondaryIcon.visible = false;
}
// cleanup stale signal connections
if (!hasApIcon && this._activeAccessPointUpdateId) {

View File

@@ -212,7 +212,7 @@ const DeviceItem = new Lang.Class({
case UPDeviceType.COMPUTER:
return _("Computer");
default:
return C_("device", "Unknown");
return _("Unknown");
}
}
});

View File

@@ -132,9 +132,6 @@ const Client = new Lang.Class({
let channel = channels[i];
let [targetHandle, targetHandleType] = channel.get_handle();
if (Shell.is_channel_invalidated(channel))
continue;
/* Only observe contact text channels */
if ((!(channel instanceof Tp.TextChannel)) ||
targetHandleType != Tp.HandleType.CONTACT)
@@ -184,9 +181,6 @@ const Client = new Lang.Class({
continue;
}
if (Shell.is_channel_invalidated(channel))
continue;
// '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
@@ -237,19 +231,12 @@ const Client = new Lang.Class({
let channel = channels[0];
let chanType = channel.get_channel_type();
if (Shell.is_channel_invalidated(channel)) {
Shell.decline_dispatch_op(context, 'Channel is invalidated');
return;
}
if (chanType == Tp.IFACE_CHANNEL_TYPE_TEXT)
this._approveTextChannel(account, conn, channel, dispatchOp, context);
else if (chanType == Tp.IFACE_CHANNEL_TYPE_CALL)
this._approveCall(account, conn, channel, dispatchOp, context);
else if (chanType == Tp.IFACE_CHANNEL_TYPE_FILE_TRANSFER)
this._approveFileTransfer(account, conn, channel, dispatchOp, context);
else
Shell.decline_dispatch_op(context, 'Unsupported channel type');
},
_approveTextChannel: function(account, conn, channel, dispatchOp, context) {
@@ -433,13 +420,14 @@ const ChatSource = new Lang.Class({
Extends: MessageTray.Source,
_init: function(account, conn, channel, contact, client) {
this.isChat = true;
this._account = account;
this._contact = contact;
this._client = client;
this.parent(contact.get_alias());
this.isChat = true;
this._pendingMessages = [];
this._conn = conn;
@@ -522,10 +510,10 @@ const ChatSource = new Lang.Class({
_getLogMessages: function() {
let logManager = Tpl.LogManager.dup_singleton();
let entity = Tpl.Entity.new_from_tp_contact(this._contact, Tpl.EntityType.CONTACT);
logManager.get_filtered_events_async(this._account, entity,
Tpl.EventTypeMask.TEXT, SCROLLBACK_HISTORY_LINES,
null, Lang.bind(this, this._displayPendingMessages));
Shell.get_contact_events(logManager,
this._account, entity,
SCROLLBACK_HISTORY_LINES,
Lang.bind(this, this._displayPendingMessages));
},
_displayPendingMessages: function(logManager, result) {
@@ -815,7 +803,7 @@ const ChatNotification = new Lang.Class({
let groups = this._contentArea.get_children();
for (let i = 0; i < groups.length; i++) {
let group = groups[i];
if (group.get_n_children() == 0)
if (group.get_children().length == 0)
group.destroy();
}
},

363
js/ui/unlockDialog.js Normal file
View File

@@ -0,0 +1,363 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const AccountsService = imports.gi.AccountsService;
const Clutter = imports.gi.Clutter;
const GdmGreeter = imports.gi.GdmGreeter;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Signals = imports.signals;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const ModalDialog = imports.ui.modalDialog;
const Fprint = imports.gdm.fingerprint;
const GdmLoginDialog = imports.gdm.loginDialog;
function _fadeInActor(actor) {
if (actor.opacity == 255 && actor.visible)
return;
actor.show();
let [minHeight, naturalHeight] = actor.get_preferred_height(-1);
actor.opacity = 0;
actor.set_height(0);
Tweener.addTween(actor,
{ opacity: 255,
height: naturalHeight,
time: _FADE_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: function() {
this.set_height(-1);
},
});
}
function _fadeOutActor(actor) {
if (!actor.visible || actor.opacity == 0) {
actor.opacity = 0;
actor.hide();
}
Tweener.addTween(actor,
{ opacity: 0,
height: 0,
time: _FADE_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: function() {
this.hide();
this.set_height(-1);
},
});
}
// A widget showing the user avatar and name
const UserWidget = new Lang.Class({
Name: 'UserWidget',
_init: function(user) {
this._user = user;
this.actor = new St.BoxLayout({ style_class: 'status-chooser',
vertical: false,
reactive: false
});
this._iconBin = new St.Bin({ style_class: 'status-chooser-user-icon' });
this.actor.add(this._iconBin,
{ x_fill: true,
y_fill: true });
this._label = new St.Label({ style_class: 'login-dialog-prompt-label',
// FIXME:
style: 'text-align: right' });
this.actor.add(this._label,
{ expand: true,
x_fill: true,
y_fill: true
});
this._userLoadedId = this._user.connect('notify::is-loaded',
Lang.bind(this,
this._updateUser));
this._userChangedId = this._user.connect('changed',
Lang.bind(this,
this._updateUser));
this.actor.connect('notify::mapped', Lang.bind(this, function() {
if (this.actor.mapped)
this._updateUser();
}));
},
destroy: function() {
// clean up signal handlers
if (this._userLoadedId != 0) {
this._user.disconnect(this._userLoadedId);
this._userLoadedId = 0;
}
if (this._userChangedId != 0) {
this._user.disconnect(this._userChangedId);
this._userChangedId = 0;
}
this.actor.destroy();
},
_updateUser: function() {
let iconFile = null;
if (this._user.is_loaded) {
this._label.text = this._user.get_real_name();
iconFile = this._user.get_icon_file();
if (!GLib.file_test(iconFile, GLib.FileTest.EXISTS))
iconFile = null;
} else {
this._label.text = "";
}
if (iconFile)
this._setIconFromFile(iconFile);
else
this._setIconFromName('avatar-default');
},
// XXX: a GFileIcon instead?
_setIconFromFile: function(iconFile) {
this._iconBin.set_style('background-image: url("' + iconFile + '");' +
'background-size: contain;');
this._iconBin.child = null;
},
_setIconFromName: function(iconName) {
this._iconBin.set_style(null);
if (iconName != null) {
let icon = new St.Icon({ icon_name: iconName,
icon_type: St.IconType.SYMBOLIC,
icon_size: DIALOG_ICON_SIZE });
this._iconBin.child = icon;
this._iconBin.show();
} else {
this._iconBin.child = null;
this._iconBin.hide();
}
}
});
const UnlockDialog = new Lang.Class({
Name: 'UnlockDialog',
Extends: ModalDialog.ModalDialog,
_init: function() {
this.parent({ shellReactive: true,
styleClass: 'login-dialog' });
this._userManager = AccountsService.UserManager.get_default();
this._userName = GLib.get_user_name();
this._user = this._userManager.get_user(this._userName);
this._greeterClient = GdmGreeter.Server.new_for_display_sync(null, null);
this._greeterClient.call_start_conversation_sync(GdmLoginDialog.PASSWORD_SERVICE_NAME, null);
this._greeterClient.connect('reset',
Lang.bind(this, this._onReset));
this._greeterClient.connect('ready',
Lang.bind(this, this._onReady));
this._greeterClient.connect('info',
Lang.bind(this, this._onInfo));
this._greeterClient.connect('problem',
Lang.bind(this, this._onProblem));
this._greeterClient.connect('info-query',
Lang.bind(this, this._onInfoQuery));
this._greeterClient.connect('secret-info-query',
Lang.bind(this, this._onSecretInfoQuery));
this._greeterClient.connect('session-opened',
Lang.bind(this, this._onSessionOpened));
this._greeterClient.connect('conversation-stopped',
Lang.bind(this, this._onConversationStopped));
this._fprintManager = new Fprint.FprintManager();
this._startFingerprintConversationIfNeeded();
this._userWidget = new UserWidget(this._user);
this.contentLayout.add_actor(this._userWidget.actor);
this._promptLayout = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
vertical: false
});
this._promptLabel = new St.Label({ style_class: 'login-dialog-prompt-label' });
this._promptLayout.add(this._promptLabel,
{ expand: false,
x_fill: true,
y_fill: true,
x_align: St.Align.START });
this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
can_focus: true });
this._promptLayout.add(this._promptEntry,
{ expand: true,
x_fill: true,
y_fill: false,
x_align: St.Align.START });
this.contentLayout.add_actor(this._promptLayout);
// Translators: this message is shown below the password entry field
// to indicate the user can swipe their finger instead
this._promptFingerprintMessage = new St.Label({ text: _("(or swipe finger)"),
style_class: 'login-dialog-prompt-fingerprint-message' });
this._promptFingerprintMessage.hide();
this.contentLayout.add_actor(this._promptFingerprintMessage);
this._okButton = { label: _("Unlock"),
action: Lang.bind(this, this._doUnlock),
key: Clutter.KEY_Return,
};
this.setButtons([this._okButton]);
this._updateOkButton(true);
},
_updateOkButton: function(sensitive) {
this._okButton.button.reactive = sensitive;
this._okButton.button.can_focus = sensitive;
if (sensitive)
this._okButton.button.remove_style_pseudo_class('disabled');
else
this._okButton.button.add_style_pseudo_class('disabled');
},
_onReset: function() {
// I'm not sure this is emitted for external greeters...
this._greeterClient.call_start_conversation_sync(GdmLoginDialog.PASSWORD_SERVICE_NAME, null);
this._startFingerprintConversationIfNeeded();
},
_startFingerprintConversationIfNeeded: function() {
this._haveFingerprintReader = false;
// FIXME: the greeter has a GSettings key for disabling fingerprint auth
this._fprintManager.GetDefaultDeviceRemote(Lang.bind(this,
function(device, error) {
if (!error && device)
this._haveFingerprintReader = true;
if (this._haveFingerprintReader)
this._greeterClient.call_start_conversation_sync(GdmLoginDialog.FINGERPRINT_SERVICE_NAME, null);
}));
},
_onReady: function(greeter, serviceName) {
greeter.call_begin_verification_for_user_sync(serviceName, this._userName, null);
},
_onInfo: function(greeter, serviceName, info) {
// We don't display fingerprint messages, because they
// have words like UPEK in them. Instead we use the messages
// as a cue to display our own message.
if (serviceName == GdmLoginDialog.FINGERPRINT_SERVICE_NAME &&
this._haveFingerprintReader &&
(!this._promptFingerprintMessage.visible ||
this._promptFingerprintMessage.opacity != 255)) {
_fadeInActor(this._promptFingerprintMessage);
return;
}
if (serviceName != GdmLoginDialog.PASSWORD_SERVICE_NAME)
return;
Main.notify(info);
},
_onProblem: function(client, serviceName, problem) {
// we don't want to show auth failed messages to
// users who haven't enrolled their fingerprint.
if (serviceName != GdmLoginDialog.PASSWORD_SERVICE_NAME)
return;
Main.notifyError(problem);
},
_onInfoQuery: function(client, serviceName, question) {
// We only expect questions to come from the main auth service
if (serviceName != GdmLoginDialog.PASSWORD_SERVICE_NAME)
return;
this._promptLabel.text = question;
this._promptEntry.text = '';
this._promptEntry.clutter_text.set_password_char('');
this._currentQuery = serviceName;
this._updateOkButton(true);
},
_onSecretInfoQuery: function(client, serviceName, secretQuestion) {
// We only expect secret requests to come from the main auth service
if (serviceName != GdmLoginDialog.PASSWORD_SERVICE_NAME)
return;
this._promptLabel.text = secretQuestion;
this._promptEntry.text = '';
this._promptEntry.clutter_text.set_password_char('\u25cf');
this._currentQuery = serviceName;
this._updateOkButton(true);
},
_doUnlock: function() {
if (!this._currentQuery)
return;
let query = this._currentQuery;
this._currentQuery = null;
this._updateOkButton(false);
this._greeterClient.call_answer_query_sync(query, this._promptEntry.text, null);
},
_onConversationStopped: function(client, serviceName) {
// if the password service fails, then cancel everything.
// But if, e.g., fingerprint fails, still give
// password authentication a chance to succeed
if (serviceName == GdmLoginDialog.PASSWORD_SERVICE_NAME) {
this._greeterClient.call_cancel_sync(null);
this.emit('failed');
} else if (serviceName == GdmLoginDialog.FINGERPRINT_SERVICE_NAME) {
_fadeOutActor(this._promptFingerprintMessage);
}
},
_onSessionOpened: function(client, serviceName) {
// For external greeters, SessionOpened means we succeded
// in the authentication process
// Close the greeter proxy
this._greeterClient.run_dispose();
this._greeterClient = null;
this.emit('unlocked');
},
destroy: function() {
if (this._greeterClient) {
this._greeterClient.run_dispose();
this._greeterClient = null;
}
this.parent();
},
cancel: function() {
this._greeterClient.call_cancel_sync(null);
this.destroy();
},
});

View File

@@ -1,7 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const AccountsService = imports.gi.AccountsService;
const GdmGreeter = imports.gi.GdmGreeter;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
@@ -133,7 +132,7 @@ const IMStatusChooserItem = new Lang.Class({
item = new IMStatusItem(_("Busy"), 'user-busy');
this._combo.addMenuItem(item, IMStatus.BUSY);
item = new IMStatusItem(_("Invisible"), 'user-invisible');
item = new IMStatusItem(_("Hidden"), 'user-invisible');
this._combo.addMenuItem(item, IMStatus.HIDDEN);
item = new IMStatusItem(_("Away"), 'user-away');
@@ -474,22 +473,13 @@ const UserMenuButton = new Lang.Class({
style_class: 'popup-menu-icon' });
this._idleIcon = new St.Icon({ icon_name: 'user-idle',
style_class: 'popup-menu-icon' });
this._pendingIcon = new St.Icon({ icon_name: 'user-status-pending',
style_class: 'popup-menu-icon' });
this._accountMgr.connect('most-available-presence-changed',
Lang.bind(this, this._updatePresenceIcon));
this._accountMgr.connect('account-enabled',
Lang.bind(this, this._onAccountEnabled));
this._accountMgr.connect('account-disabled',
Lang.bind(this, this._onAccountDisabled));
this._accountMgr.connect('account-removed',
Lang.bind(this, this._onAccountDisabled));
this._accountMgr.prepare_async(null, Lang.bind(this,
function(mgr) {
let [presence, s, msg] = mgr.get_most_available_presence();
this._updatePresenceIcon(mgr, presence, s, msg);
this._setupAccounts();
}));
this._name = new St.Label();
@@ -559,20 +549,14 @@ const UserMenuButton = new Lang.Class({
_updateSwitchUser: function() {
let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY);
let multiUser = this._userManager.can_switch() && this._userManager.has_multiple_users;
let multiSession = GdmGreeter.get_session_ids().length > 1;
this._loginScreenItem.label.set_text(multiUser ? _("Switch User")
: _("Switch Session"));
this._loginScreenItem.actor.visible = allowSwitch && (multiUser || multiSession);
this._loginScreenItem.actor.visible = allowSwitch &&
this._userManager.can_switch() &&
this._userManager.has_multiple_users;
},
_updateLogout: function() {
let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY);
let multiUser = this._userManager.has_multiple_users;
let multiSession = GdmGreeter.get_session_ids().length > 1;
this._logoutItem.actor.visible = allowLogout && (multiUser || multiSession);
this._logoutItem.actor.visible = allowLogout && this._userManager.has_multiple_users;
},
_updateLockScreen: function() {
@@ -629,52 +613,11 @@ const UserMenuButton = new Lang.Class({
this._iconBox.child = this._offlineIcon;
},
_setupAccounts: function() {
let accounts = this._accountMgr.get_valid_accounts();
for (let i = 0; i < accounts.length; i++) {
accounts[i]._changingId = accounts[i].connect('notify::connection-status',
Lang.bind(this, this._updateChangingPresence));
}
this._updateChangingPresence();
},
_onAccountEnabled: function(accountMgr, account) {
if (!account._changingId)
account._changingId = account.connect('notify::connection-status',
Lang.bind(this, this._updateChangingPresence));
this._updateChangingPresence();
},
_onAccountDisabled: function(accountMgr, account) {
account.disconnect(account._changingId);
account._changingId = 0;
this._updateChangingPresence();
},
_updateChangingPresence: function() {
let accounts = this._accountMgr.get_valid_accounts();
let changing = false;
for (let i = 0; i < accounts.length; i++) {
if (accounts[i].connection_status == Tp.ConnectionStatus.CONNECTING) {
changing = true;
break;
}
}
if (changing) {
this._iconBox.child = this._pendingIcon;
} else {
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
this._updatePresenceIcon(this._accountMgr, presence, s, msg);
}
},
_createSubMenu: function() {
let item;
item = new IMStatusChooserItem();
if (Main.sessionMode.allowSettings)
item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
this.menu.addMenuItem(item);
this._statusChooser = item;
@@ -686,11 +629,9 @@ const UserMenuButton = new Lang.Class({
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
if (Main.sessionMode.allowSettings) {
item = new PopupMenu.PopupMenuItem(_("System Settings"));
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
this.menu.addMenuItem(item);
}
item = new PopupMenu.PopupMenuItem(_("System Settings"));
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
this.menu.addMenuItem(item);
item = new PopupMenu.PopupAlternatingMenuItem(_("Power Off"),
_("Suspend"));

View File

@@ -301,7 +301,8 @@ const WindowClone = new Lang.Class({
if (!this._zoomLightbox)
this._zoomLightbox = new Lightbox.Lightbox(Main.uiGroup,
{ fadeTime: LIGHTBOX_FADE_TIME });
{ fadeInTime: LIGHTBOX_FADE_TIME,
fadeOutTime: LIGHTBOX_FADE_TIME });
this._zoomLightbox.show();
this._zoomLocalOrig = new ScaledPoint(this.actor.x, this.actor.y, this.actor.scale_x, this.actor.scale_y);
@@ -1121,6 +1122,25 @@ const Workspace = new Lang.Class({
}
},
_showAllOverlays: function() {
let currentWorkspace = global.screen.get_active_workspace();
for (let i = 0; i < this._windows.length; i++) {
let clone = this._windows[i];
let overlay = this._windowOverlays[i];
this._showWindowOverlay(clone, overlay,
this.metaWorkspace == null || this.metaWorkspace == currentWorkspace);
}
},
_hideAllOverlays: function() {
for (let i = 0; i < this._windows.length; i++) {
let clone = this._windows[i];
let overlay = this._windowOverlays[i];
if (overlay)
overlay.hide();
}
},
_delayedWindowRepositioning: function() {
if (this._windowIsZooming)
return true;
@@ -1320,10 +1340,6 @@ const Workspace = new Lang.Class({
// Position and scale the windows.
for (let i = 0; i < this._windows.length; i++) {
let clone = this._windows[i];
let overlay = this._windowOverlays[i];
if (overlay)
overlay.hide();
clone.zoomFromOverview();
@@ -1348,6 +1364,8 @@ const Workspace = new Lang.Class({
});
}
}
this._hideAllOverlays();
},
destroy : function() {

View File

@@ -529,11 +529,9 @@ const WorkspacesDisplay = new Lang.Class({
this._updateAlwaysZoom();
}));
global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._workspacesChanged));
this._switchWorkspaceNotifyId = 0;
this._nWorkspacesChangedId = 0;
this._itemDragBeginId = 0;
this._itemDragCancelledId = 0;
this._itemDragEndId = 0;
@@ -572,6 +570,9 @@ const WorkspacesDisplay = new Lang.Class({
global.screen.connect('restacked',
Lang.bind(this, this._onRestacked));
if (this._nWorkspacesChangedId == 0)
this._nWorkspacesChangedId = global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._workspacesChanged));
if (this._itemDragBeginId == 0)
this._itemDragBeginId = Main.overview.connect('item-drag-begin',
Lang.bind(this, this._dragBegin));
@@ -924,16 +925,19 @@ const WorkspacesDisplay = new Lang.Class({
},
_workspacesChanged: function() {
let oldNumWorkspaces = this._workspaces[0].length;
let newNumWorkspaces = global.screen.n_workspaces;
let active = global.screen.get_active_workspace_index();
if (oldNumWorkspaces == newNumWorkspaces)
return;
this._updateAlwaysZoom();
this._updateZoom();
if (this._workspacesViews == null)
return;
let oldNumWorkspaces = this._workspaces[0].length;
let newNumWorkspaces = global.screen.n_workspaces;
let active = global.screen.get_active_workspace_index();
let lostWorkspaces = [];
if (newNumWorkspaces > oldNumWorkspaces) {
let monitors = Main.layoutManager.monitors;

View File

@@ -1,6 +1,6 @@
data/gnome-shell.desktop.in.in
data/gnome-shell-extension-prefs.desktop.in.in
data/org.gnome.shell.gschema.xml.in.in
data/org.gnome.shell.gschema.xml.in
js/extensionPrefs/main.js
js/gdm/loginDialog.js
js/gdm/powerMenu.js
@@ -13,7 +13,6 @@ js/ui/contactDisplay.js
js/ui/dash.js
js/ui/dateMenu.js
js/ui/endSessionDialog.js
js/ui/extensionDownloader.js
js/ui/extensionSystem.js
js/ui/keyboard.js
js/ui/keyringPrompt.js

540
po/ar.po

File diff suppressed because it is too large Load Diff

957
po/el.po

File diff suppressed because it is too large Load Diff

520
po/es.po

File diff suppressed because it is too large Load Diff

607
po/gl.po

File diff suppressed because it is too large Load Diff

462
po/he.po

File diff suppressed because it is too large Load Diff

472
po/it.po

File diff suppressed because it is too large Load Diff

562
po/ja.po

File diff suppressed because it is too large Load Diff

View File

@@ -13,7 +13,7 @@ msgstr ""
"Project-Id-Version: gnome-shell\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell&keywords=I18N+L10N&component=general\n"
"POT-Creation-Date: 2012-04-19 21:14+0000\n"
"PO-Revision-Date: 2012-05-19 22:57+0900\n"
"PO-Revision-Date: 2012-04-26 01:05+0900\n"
"Last-Translator: Changwoo Ryu <cwryu@debian.org>\n"
"Language-Team: GNOME Korea <gnome-kr@googlegroups.com>\n"
"MIME-Version: 1.0\n"
@@ -51,7 +51,7 @@ msgstr "Alt-F2 대화 상자에서 내부 디버깅 및 감시 기능에 접근
#: ../data/org.gnome.shell.gschema.xml.in.h:3
msgid "Uuids of extensions to enable"
msgstr "사용할 확장의 UUID 목록"
msgstr "사용할 확장 기능의 UUID 목록"
#: ../data/org.gnome.shell.gschema.xml.in.h:4
msgid ""
@@ -71,7 +71,11 @@ msgid ""
"used ones (e.g. in launchers). While this data will be kept private, you may "
"want to disable this for privacy reasons. Please note that doing so won't "
"remove already saved data."
msgstr "셸에서는 최근에 사용한 프로그램을(실행 메뉴 등에서) 표시하는 목적으로, 현재 사용 중인 프로그램을 감시합니다. 이 데이터는 비공개 데이터이지만 사생활 문제가 걱정된다면 이 기능을 끌 수도 있습니다. 단 이 기능을 사용하지 않더라도 이미 저장된 데이터를 제거하지는 않습니다."
msgstr ""
"셸에서는 최근에 사용한 프로그램을 (실행 메뉴 등에서) 표시하는 목적으로, 현재 "
"사용 중인 프로그램을 감시합니다. 이 데이터는 비공개 데이터이지만 사생활 문제"
"가 걱정된다면 이 기능을 끌 수도 있습니다. 단 이 기능을 사용하지 않더라도 이"
"미 저장된 데이터를 제거하지는 않습니다."
#: ../data/org.gnome.shell.gschema.xml.in.h:7
msgid "List of desktop file IDs for favorite applications"
@@ -89,7 +93,7 @@ msgstr "사용하지 않는 OpenSearch 서비스"
#: ../data/org.gnome.shell.gschema.xml.in.h:10
msgid "History for command (Alt-F2) dialog"
msgstr "명령어 대화 상자에(Alt-F2) 기록 기능"
msgstr "명령어 대화 상자에 (Alt-F2) 기록 기능"
#: ../data/org.gnome.shell.gschema.xml.in.h:11
msgid "History for the looking glass dialog"
@@ -162,7 +166,7 @@ msgid "The gstreamer pipeline used to encode the screencast"
msgstr "스크린 방송 인코딩에 사용할 gstreamer 파이프라인"
#: ../data/org.gnome.shell.gschema.xml.in.h:28
#, no-c-format
#, no-c-format, fuzzy
msgid ""
"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
"used for gst-launch. The pipeline should have an unconnected sink pad where "
@@ -174,31 +178,45 @@ msgid ""
"'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."
msgstr "녹화 인코딩에 사용할 GStreamer 파이프라인을 지정합니다. gst-launch 프로그램에 사용하는 문법을 따릅니다. 녹화한 영상이 입력되는 싱크 패드는 이 파이프라인에 연결하지 않은 상태여야 합니다. 보통은 소스 패드도 연결하지 않고, 소스 패드의 출력을 출력 파일에 기록합니다. 하지만 파이프라인에서 이 출력을 처리할 수도 있습니다. shout2send 같은 프로그램을 이용해 아이스캐스트 서버로 출력을 보내거나 하는 용도로 사용할 수 있습니다. 설정을 취소하거나 빈 값으로 설정하면, 기본 파이프라인을 사용합니다. 기본 파이프라인은 'vp8enc quality=8 speed=6 threads=%T ! queue ! webmmux'이고 VP8 코덱을 사용해 WEBM 형식으로 녹화합니다. '%T' 기호는 시스템에서 최적으로 생각되는 스레드 수로 대체됩니다."
msgstr ""
"녹화 인코딩에 사용할 GStreamer 파이프라인을 지정합니다. gst-launch에 사용하"
"는 문법을 따릅니다. 파이프라인은 녹화한 영상이 있는 싱크 패드를 연결하지 않"
"은 상태여야 합니다. 보통 소스 패드와 연결하지 않았을 것입니다. 이 패드의 출력"
"은 출력 파일에 기록할 것입니다. 그러나 파이프라인은 이 출력을 다룰 수 있기도 "
"합니다 - 아마 shout2send와 같은 것을 통해 icecast 서버로 출력을 보내는데 사용"
"할지도 모릅니다. 만약 설정을 취소하거나 빈 값으로 설정한다면, 기본 파이프라인"
"을 사용할 것입니다. 이것은 현재 'vp8enc quality=8 speed=6 threads=%T ! "
"queue ! webmmux' 이며 VP8 코덱을 사용하여 WEBM으로 녹화합니다. %T는 시스템 상"
"의 최적의 스레드 수를 추측하기 위해 대체기호로 사용합니다."
#: ../data/org.gnome.shell.gschema.xml.in.h:29
msgid "File extension used for storing the screencast"
msgstr "스크린 방송을 저장할 때 사용할 파일 확장자"
#: ../data/org.gnome.shell.gschema.xml.in.h:30
#, fuzzy
msgid ""
"The filename for recorded screencasts will be a unique filename based on the "
"current date, and use this extension. It should be changed when recording to "
"a different container format."
msgstr "녹화한 스크린 방송 영상 파일은, 현재 날짜와 여기서 설정하는 확장자를 붙여 파일 이름을 만듭니다. 다른 컨테이너 형식으로 녹화하려면 이 값을 바꿔야 합니다."
msgstr ""
"스크린 방송 녹화 파일 이름은 현재 날짜와 이 확장자를 사용해서 결정됩니다. 녹"
"화할 때 다른 형식으로 바꿀 수도 있습니다."
#: ../js/extensionPrefs/main.js:125
#, c-format
#, c-format, fuzzy
msgid "There was an error loading the preferences dialog for %s:"
msgstr "%s에 대한 기본 설정 대화상자를 불러오는데 오류가 발생했습니다:"
#: ../js/extensionPrefs/main.js:165
#, fuzzy
msgid "<b>Extension</b>"
msgstr "<b>확장</b>"
#: ../js/extensionPrefs/main.js:189
#, fuzzy
msgid "Select an extension to configure using the combobox above."
msgstr "위의 콤보상자를 사용 설정할 확장을 선택하십시오."
msgstr "위의 콤보상자를 사용하여 설정할 확장을 선택하십시오."
#: ../js/gdm/loginDialog.js:627
msgid "Session..."
@@ -656,13 +674,13 @@ msgstr "다시 입력하십시오:"
#: ../js/ui/lookingGlass.js:732
msgid "No extensions installed"
msgstr "확장을 설치하지 않았습니다"
msgstr "확장 기능을 설치하지 않았습니다"
#. Translators: argument is an extension UUID.
#: ../js/ui/lookingGlass.js:786
#, c-format
msgid "%s has not emitted any errors."
msgstr "%s에서 발생한 에러가 없습니다."
msgstr "%s이(가) 발생한 에러가 없습니다."
#: ../js/ui/lookingGlass.js:792
msgid "Hide Errors"
@@ -1149,7 +1167,7 @@ msgstr "더 보기..."
#. and we cannot access its settings (including the name)
#: ../js/ui/status/network.js:621 ../js/ui/status/network.js:1440
msgid "Connected (private)"
msgstr "연결됨(개인)"
msgstr "연결됨 (개인)"
#: ../js/ui/status/network.js:696
msgid "Auto Ethernet"
@@ -1555,7 +1573,7 @@ msgstr "내부 오류"
#. translators: argument is the account name, like
#. * name@jabber.org for example.
#: ../js/ui/telepathyClient.js:1345
#, c-format
#, c-format, fuzzy
msgid "Connection to %s failed"
msgstr "%s에 연결이 실패했습니다"

161
po/lt.po
View File

@@ -3,20 +3,19 @@
# This file is distributed under the same license as the gnome-shell package.
# Žygimantas Beručka <zygis@gnome.org>, 2010, 2011, 2012.
# Algimantas Margevičius <gymka@mail.ru>, 2011.
# Mantas Kriaučiūnas <mantas@akl.lt>, 2012.
msgid ""
msgstr ""
"Project-Id-Version: gnome-shell master\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&keywords=I18N+L10N&component=general\n"
"POT-Creation-Date: 2012-05-19 13:58+0000\n"
"POT-Creation-Date: 2012-03-30 17:59+0000\n"
"PO-Revision-Date: 2012-04-05 15:14+0300\n"
"Last-Translator: Mantas Kriaučiūnas <mantas@akl.lt>\n"
"Last-Translator: Žygimantas Beručka <zygis@gnome.org>\n"
"Language-Team: Lithuanian\n"
"Language: lt\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: lt\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%"
"100<10 || n%100>=20) ? 1 : 2);\n"
"X-Generator: Virtaal 0.7.0\n"
@@ -24,7 +23,7 @@ msgstr ""
#: ../data/gnome-shell.desktop.in.in.h:1
msgid "GNOME Shell"
msgstr "GNOME Shell aplinka"
msgstr "GNOME Shell"
#: ../data/gnome-shell.desktop.in.in.h:2
msgid "Window management and application launching"
@@ -80,11 +79,10 @@ msgid ""
"want to disable this for privacy reasons. Please note that doing so won't "
"remove already saved data."
msgstr ""
"Įprastai GNOME aplinka stebi aktyvias programas siekiant pateikti "
"dažniausiai naudojamas (pvz., leistukuose). Nors šie duomenys "
"konfidencialiai saugomi, jei norite, saugumo sumetimais galite šią funkciją "
"išjungti. Atminkite, kad išjungus šią funkciją anksčiau įrašyti duomenys "
"nebus pašalinti."
"Apvalkalas paprastai stebi aktyvias programas siekiant pateikti dažniausiai "
"naudojamas (pvz., leistukuose). Nors šie duomenys konfidencialiai saugomi, "
"jei norite, saugumo sumetimais galite šią funkciją išjungti. Atminkite, kad "
"tai padarius jau įrašyti duomenys jau nebus įrašyti."
#: ../data/org.gnome.shell.gschema.xml.in.h:7
msgid "List of desktop file IDs for favorite applications"
@@ -224,7 +222,7 @@ msgstr ""
#: ../js/extensionPrefs/main.js:125
#, c-format
msgid "There was an error loading the preferences dialog for %s:"
msgstr "Įvyko klaida įkeliant %s nustatymų dialogą:"
msgstr "Kilo klaida įkeliant %s nustatymų dialogą:"
#: ../js/extensionPrefs/main.js:165
msgid "<b>Extension</b>"
@@ -234,40 +232,40 @@ msgstr "<b>Plėtinys</b>"
msgid "Select an extension to configure using the combobox above."
msgstr "Išskleidžiamajame sąraše pasirinkite konfigūruotiną plėtinį."
#: ../js/gdm/loginDialog.js:627
#: ../js/gdm/loginDialog.js:624
msgid "Session..."
msgstr "Seansas..."
#: ../js/gdm/loginDialog.js:789
#: ../js/gdm/loginDialog.js:786
msgctxt "title"
msgid "Sign In"
msgstr "Prisijungti"
#. Translators: this message is shown below the password entry field
#. to indicate the user can swipe their finger instead
#: ../js/gdm/loginDialog.js:834
#: ../js/gdm/loginDialog.js:831
msgid "(or swipe finger)"
msgstr "(arba perbraukite pirštu)"
#. translators: this message is shown below the user list on the
#. login screen. It can be activated to reveal an entry for
#. manually entering the username.
#: ../js/gdm/loginDialog.js:855
#: ../js/gdm/loginDialog.js:852
msgid "Not listed?"
msgstr "Nėra sąraše?"
#: ../js/gdm/loginDialog.js:1023 ../js/ui/endSessionDialog.js:401
#: ../js/ui/extensionSystem.js:400 ../js/ui/networkAgent.js:153
#: ../js/gdm/loginDialog.js:1020 ../js/ui/endSessionDialog.js:401
#: ../js/ui/extensionSystem.js:399 ../js/ui/networkAgent.js:153
#: ../js/ui/polkitAuthenticationAgent.js:175 ../js/ui/status/bluetooth.js:462
msgid "Cancel"
msgstr "Atsisakyti"
msgstr "Atšaukti"
#: ../js/gdm/loginDialog.js:1028
#: ../js/gdm/loginDialog.js:1025
msgctxt "button"
msgid "Sign In"
msgstr "Prisijungti"
#: ../js/gdm/loginDialog.js:1380
#: ../js/gdm/loginDialog.js:1377
msgid "Login Window"
msgstr "Prisijungimo langas"
@@ -675,11 +673,11 @@ msgstr[2] "Sistema bus paleista iš naujo po %d sekundžių."
msgid "Restarting the system."
msgstr "Sistema paleidžiama iš naujo."
#: ../js/ui/extensionSystem.js:404
#: ../js/ui/extensionSystem.js:403
msgid "Install"
msgstr "Įdiegti"
#: ../js/ui/extensionSystem.js:408
#: ../js/ui/extensionSystem.js:407
#, c-format
msgid "Download and install '%s' from extensions.gnome.org?"
msgstr "Atsiųsti ir įdiegti „%s“ iš extensions.gnome.org?"
@@ -688,8 +686,7 @@ msgstr "Atsiųsti ir įdiegti „%s“ iš extensions.gnome.org?"
msgid "tray"
msgstr "dėklas"
#: ../js/ui/keyboard.js:544 ../js/ui/status/keyboard.js:44
#: ../js/ui/status/power.js:203
#: ../js/ui/keyboard.js:544 ../js/ui/status/power.js:203
msgid "Keyboard"
msgstr "Klaviatūra"
@@ -725,7 +722,7 @@ msgstr "Įjungta"
#. translators:
#. * The device has been disabled
#: ../js/ui/lookingGlass.js:808 ../src/gvc/gvc-mixer-control.c:1082
#: ../js/ui/lookingGlass.js:808 ../src/gvc/gvc-mixer-control.c:1093
msgid "Disabled"
msgstr "Išjungta"
@@ -942,7 +939,7 @@ msgstr "Įveskite komandą:"
msgid "Searching..."
msgstr "Ieškoma..."
#: ../js/ui/searchDisplay.js:415
#: ../js/ui/searchDisplay.js:414
msgid "No matching results."
msgstr "Nerasta atitikmenų."
@@ -1100,7 +1097,7 @@ msgstr "Visada leisti prieigą"
msgid "Grant this time only"
msgstr "Leisti tik šį kartą"
#: ../js/ui/status/bluetooth.js:382 ../js/ui/telepathyClient.js:1093
#: ../js/ui/status/bluetooth.js:382 ../js/ui/telepathyClient.js:1091
msgid "Reject"
msgstr "Atmesti"
@@ -1177,7 +1174,7 @@ msgstr "Trūksta integruotos programinės įrangos (firmware)"
#. Translators: this is for wired network devices that are physically disconnected
#: ../js/ui/status/network.js:517
msgid "cable unplugged"
msgstr "atjungtas laidas"
msgstr "kabelis neįjungtas"
#. Translators: this is for a network device that cannot be activated (for example it
#. is disabled by rfkill, or it has no coverage
@@ -1191,7 +1188,7 @@ msgstr "nepavyko prisijungti"
#: ../js/ui/status/network.js:585 ../js/ui/status/network.js:1505
msgid "More..."
msgstr "Rodyti daugiau tinklų..."
msgstr "Daugiau..."
#. TRANSLATORS: this is the indication that a connection for another logged in user is active,
#. and we cannot access its settings (including the name)
@@ -1388,7 +1385,7 @@ msgstr "Skambutis"
#. We got the TpContact
#: ../js/ui/telepathyClient.js:287
msgid "File Transfer"
msgstr "Failo persiuntimas"
msgstr "Failo perdavimas"
#: ../js/ui/telepathyClient.js:369
msgid "Subscription request"
@@ -1421,35 +1418,35 @@ msgstr "%s yra užsiėmęs (-usi)."
#. Translators: this is a time format string followed by a date.
#. If applicable, replace %X with a strftime format valid for your
#. locale, without seconds.
#: ../js/ui/telepathyClient.js:889
#: ../js/ui/telepathyClient.js:887
#, no-c-format
msgid "Sent at <b>%X</b> on <b>%A</b>"
msgstr "Išsiųsta <b>%X</b> <b>%A</b>"
#. Translators: this is a time format in the style of "Wednesday, May 25",
#. shown when you get a chat message in the same year.
#: ../js/ui/telepathyClient.js:895
#: ../js/ui/telepathyClient.js:893
#, no-c-format
msgid "Sent on <b>%A</b>, <b>%B %d</b>"
msgstr "Išsiųsta <b>%B %d</b>, <b>%A</b>"
#. Translators: this is a time format in the style of "Wednesday, May 25, 2012",
#. shown when you get a chat message in a different year.
#: ../js/ui/telepathyClient.js:900
#: ../js/ui/telepathyClient.js:898
#, no-c-format
msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
msgstr "Išsiųsta %Y <b>%B %d</b>, <b>%A</b>"
#. Translators: this is the other person changing their old IM name to their new
#. IM name.
#: ../js/ui/telepathyClient.js:942
#: ../js/ui/telepathyClient.js:940
#, c-format
msgid "%s is now known as %s"
msgstr "%s nuo šiol vadinasi %s"
#. translators: argument is a room name like
#. * room@jabber.org for example.
#: ../js/ui/telepathyClient.js:1044
#: ../js/ui/telepathyClient.js:1042
#, c-format
msgid "Invitation to %s"
msgstr "Kvietimas į %s"
@@ -1457,35 +1454,35 @@ msgstr "Kvietimas į %s"
#. translators: first argument is the name of a contact and the second
#. * one the name of a room. "Alice is inviting you to join room@jabber.org
#. * for example.
#: ../js/ui/telepathyClient.js:1052
#: ../js/ui/telepathyClient.js:1050
#, c-format
msgid "%s is inviting you to join %s"
msgstr "%s jus kviečia prisijungti prie %s"
#: ../js/ui/telepathyClient.js:1054 ../js/ui/telepathyClient.js:1133
#: ../js/ui/telepathyClient.js:1231
#: ../js/ui/telepathyClient.js:1052 ../js/ui/telepathyClient.js:1131
#: ../js/ui/telepathyClient.js:1229
msgid "Decline"
msgstr "Atmesti"
#: ../js/ui/telepathyClient.js:1055 ../js/ui/telepathyClient.js:1134
#: ../js/ui/telepathyClient.js:1232
#: ../js/ui/telepathyClient.js:1053 ../js/ui/telepathyClient.js:1132
#: ../js/ui/telepathyClient.js:1230
msgid "Accept"
msgstr "Priimti"
#. translators: argument is a contact name like Alice for example.
#: ../js/ui/telepathyClient.js:1085
#: ../js/ui/telepathyClient.js:1083
#, c-format
msgid "Video call from %s"
msgstr "Vaizdo skambutis nuo %s"
#. translators: argument is a contact name like Alice for example.
#: ../js/ui/telepathyClient.js:1088
#: ../js/ui/telepathyClient.js:1086
#, c-format
msgid "Call from %s"
msgstr "Skambutis nuo %s"
#. translators: this is a button label (verb), not a noun
#: ../js/ui/telepathyClient.js:1095
#: ../js/ui/telepathyClient.js:1093
msgid "Answer"
msgstr "Atsiliepti"
@@ -1494,110 +1491,110 @@ msgstr "Atsiliepti"
#. * file name. The string will be something
#. * like: "Alice is sending you test.ogg"
#.
#: ../js/ui/telepathyClient.js:1127
#: ../js/ui/telepathyClient.js:1125
#, c-format
msgid "%s is sending you %s"
msgstr "%s jums siunčia %s"
#. To translators: The parameter is the contact's alias
#: ../js/ui/telepathyClient.js:1196
#: ../js/ui/telepathyClient.js:1194
#, c-format
msgid "%s would like permission to see when you are online"
msgstr "%s pageidauja matyti, kai esate prisijungę prie interneto"
#: ../js/ui/telepathyClient.js:1289
#: ../js/ui/telepathyClient.js:1287
msgid "Network error"
msgstr "Tinklo klaida"
#: ../js/ui/telepathyClient.js:1291
#: ../js/ui/telepathyClient.js:1289
msgid "Authentication failed"
msgstr "Nepavyko patvirtinti tapatybės"
#: ../js/ui/telepathyClient.js:1293
#: ../js/ui/telepathyClient.js:1291
msgid "Encryption error"
msgstr "Šifravimo klaida"
#: ../js/ui/telepathyClient.js:1295
#: ../js/ui/telepathyClient.js:1293
msgid "Certificate not provided"
msgstr "Liudijimas nepateiktas"
#: ../js/ui/telepathyClient.js:1297
#: ../js/ui/telepathyClient.js:1295
msgid "Certificate untrusted"
msgstr "Liudijimas nepatikimas"
#: ../js/ui/telepathyClient.js:1299
#: ../js/ui/telepathyClient.js:1297
msgid "Certificate expired"
msgstr "Liudijimo galiojimas pasibaigęs"
#: ../js/ui/telepathyClient.js:1301
#: ../js/ui/telepathyClient.js:1299
msgid "Certificate not activated"
msgstr "Liudijimas neaktyvuotas"
#: ../js/ui/telepathyClient.js:1303
#: ../js/ui/telepathyClient.js:1301
msgid "Certificate hostname mismatch"
msgstr "Liudijimo serverio vardo nesutapimas"
#: ../js/ui/telepathyClient.js:1305
#: ../js/ui/telepathyClient.js:1303
msgid "Certificate fingerprint mismatch"
msgstr "Liudijimo piršto atspaudo nesutapimas"
#: ../js/ui/telepathyClient.js:1307
#: ../js/ui/telepathyClient.js:1305
msgid "Certificate self-signed"
msgstr "Liudijimas pačių pasirašytas"
#: ../js/ui/telepathyClient.js:1309
#: ../js/ui/telepathyClient.js:1307
msgid "Status is set to offline"
msgstr "Nustatyta atsijungimo būsena"
#: ../js/ui/telepathyClient.js:1311
#: ../js/ui/telepathyClient.js:1309
msgid "Encryption is not available"
msgstr "Šifravimas negalimas"
#: ../js/ui/telepathyClient.js:1313
#: ../js/ui/telepathyClient.js:1311
msgid "Certificate is invalid"
msgstr "Liudijimas netinkamas"
#: ../js/ui/telepathyClient.js:1315
#: ../js/ui/telepathyClient.js:1313
msgid "Connection has been refused"
msgstr "Ryšys atmestas"
#: ../js/ui/telepathyClient.js:1317
#: ../js/ui/telepathyClient.js:1315
msgid "Connection can't be established"
msgstr "Nepavyko užmegzti ryšio"
#: ../js/ui/telepathyClient.js:1319
#: ../js/ui/telepathyClient.js:1317
msgid "Connection has been lost"
msgstr "Ryšys nutrūko"
#: ../js/ui/telepathyClient.js:1321
#: ../js/ui/telepathyClient.js:1319
msgid "This account is already connected to the server"
msgstr "Ši paskyra jau prijungta prie serverio"
#: ../js/ui/telepathyClient.js:1323
#: ../js/ui/telepathyClient.js:1321
msgid ""
"Connection has been replaced by a new connection using the same resource"
msgstr "Ryšys pakeistas nauju ryšiu naudojant tą patį išteklių"
#: ../js/ui/telepathyClient.js:1325
#: ../js/ui/telepathyClient.js:1323
msgid "The account already exists on the server"
msgstr "Tokia paskyra serveryje jau yra"
#: ../js/ui/telepathyClient.js:1327
#: ../js/ui/telepathyClient.js:1325
msgid "Server is currently too busy to handle the connection"
msgstr "Šiuo metu serveris per daug užimtas šiai užklausai apdoroti"
#: ../js/ui/telepathyClient.js:1329
#: ../js/ui/telepathyClient.js:1327
msgid "Certificate has been revoked"
msgstr "Liudijimas atšauktas"
#: ../js/ui/telepathyClient.js:1331
#: ../js/ui/telepathyClient.js:1329
msgid ""
"Certificate uses an insecure cipher algorithm or is cryptographically weak"
msgstr ""
"Liudijimui naudojamas nesaugus šifravimo algoritmas arba jis kriptografiškai "
"silpnas"
#: ../js/ui/telepathyClient.js:1333
#: ../js/ui/telepathyClient.js:1331
msgid ""
"The length of the server certificate, or the depth of the server certificate "
"chain, exceed the limits imposed by the cryptography library"
@@ -1605,26 +1602,26 @@ msgstr ""
"Serverio liudijimo ilgis arba liudijimų eilės dydis viršija kriptografijos "
"bibliotekos apribojimus"
#: ../js/ui/telepathyClient.js:1335
#: ../js/ui/telepathyClient.js:1333
msgid "Internal error"
msgstr "Vidinė klaida"
#. translators: argument is the account name, like
#. * name@jabber.org for example.
#: ../js/ui/telepathyClient.js:1345
#: ../js/ui/telepathyClient.js:1343
#, c-format
msgid "Connection to %s failed"
msgstr "Nepavyko prisijungti prie %s"
#: ../js/ui/telepathyClient.js:1354
#: ../js/ui/telepathyClient.js:1352
msgid "Reconnect"
msgstr "Prisijungti iš naujo"
#: ../js/ui/telepathyClient.js:1355
#: ../js/ui/telepathyClient.js:1353
msgid "Edit account"
msgstr "Taisyti paskyrą"
#: ../js/ui/telepathyClient.js:1401
#: ../js/ui/telepathyClient.js:1399
msgid "Unknown reason"
msgstr "Nežinoma priežastis"
@@ -1650,7 +1647,7 @@ msgstr "Pranešimai"
#: ../js/ui/userMenu.js:639
msgid "Online Accounts"
msgstr "Interneto paskyros"
msgstr "Tinklo paskyros"
#: ../js/ui/userMenu.js:643
msgid "System Settings"
@@ -1705,11 +1702,11 @@ msgstr ""
#: ../js/ui/wanda.js:128
#, c-format
msgid "%s the Oracle says"
msgstr "Orakulė %s sako"
msgstr "Orakulė sako %s"
#: ../js/ui/wanda.js:168
msgid "Your favorite Easter Egg"
msgstr "Jūsų mėgstamiausias velykinis kiaušinis"
msgstr "Jūsų mėgstamiausias vėlykinis kiaušinis"
#: ../js/ui/windowAttentionHandler.js:19
#, c-format
@@ -1718,7 +1715,7 @@ msgstr "„%s“ yra pasirengusi"
#. translators:
#. * The number of sound outputs on a particular device
#: ../src/gvc/gvc-mixer-control.c:1089
#: ../src/gvc/gvc-mixer-control.c:1100
#, c-format
msgid "%u Output"
msgid_plural "%u Outputs"
@@ -1728,7 +1725,7 @@ msgstr[2] "%u išvestys"
#. translators:
#. * The number of sound inputs on a particular device
#: ../src/gvc/gvc-mixer-control.c:1099
#: ../src/gvc/gvc-mixer-control.c:1110
#, c-format
msgid "%u Input"
msgid_plural "%u Inputs"
@@ -1736,15 +1733,15 @@ msgstr[0] "%u įvestis"
msgstr[1] "%u įvestys"
msgstr[2] "%u įvestys"
#: ../src/gvc/gvc-mixer-control.c:1397
#: ../src/gvc/gvc-mixer-control.c:1408
msgid "System Sounds"
msgstr "Sistemos garsai"
#: ../src/main.c:256
#: ../src/main.c:255
msgid "Print version"
msgstr "Išvesti versijos numerį"
#: ../src/main.c:262
#: ../src/main.c:261
msgid "Mode used by GDM for login screen"
msgstr "Veiksena, naudojama GDM prisijungimo ekrane"
@@ -1777,7 +1774,7 @@ msgstr "Naudotojas užvėrė tapatybės patvirtinimo dialogą"
#. * nautilus
#: ../src/shell-util.c:97
msgid "Home"
msgstr "Namų aplankas"
msgstr "Namai"
#. Translators: this is the same string as the one found in
#. * nautilus

496
po/nb.po

File diff suppressed because it is too large Load Diff

707
po/pa.po

File diff suppressed because it is too large Load Diff

697
po/ru.po

File diff suppressed because it is too large Load Diff

650
po/sl.po

File diff suppressed because it is too large Load Diff

View File

@@ -93,8 +93,7 @@ gnome_shell_cflags = \
-DGNOME_SHELL_LIBEXECDIR=\"$(libexecdir)\" \
-DGNOME_SHELL_DATADIR=\"$(pkgdatadir)\" \
-DGNOME_SHELL_PKGLIBDIR=\"$(pkglibdir)\" \
-DJSDIR=\"$(pkgdatadir)/js\" \
-DMUTTER_TYPELIB_DIR=\"$(MUTTER_TYPELIB_DIR)\"
-DJSDIR=\"$(pkgdatadir)/js\"
privlibdir = $(pkglibdir)
privlib_LTLIBRARIES = libgnome-shell.la libgnome-shell-js.la
@@ -109,6 +108,7 @@ shell_public_headers_h = \
shell-app.h \
shell-app-system.h \
shell-app-usage.h \
shell-contact-system.h \
shell-embedded-window.h \
shell-generic-container.h \
shell-gtk-embed.h \
@@ -131,10 +131,6 @@ shell_public_headers_h = \
shell-wm.h \
shell-xfixes-cursor.h
if BUILD_WITH_FOLKS
shell_public_headers_h += shell-contact-system.h
endif
shell_private_sources = \
gactionmuxer.h \
gactionmuxer.c \
@@ -156,8 +152,11 @@ libgnome_shell_la_SOURCES = \
shell-wm-private.h \
gnome-shell-plugin.c \
shell-app.c \
shell-a11y.h \
shell-a11y.c \
shell-app-system.c \
shell-app-usage.c \
shell-contact-system.c \
shell-embedded-window.c \
shell-generic-container.c \
shell-gtk-embed.c \
@@ -187,9 +186,6 @@ libgnome_shell_la_SOURCES = \
shell-xfixes-cursor.c \
$(NULL)
if BUILD_WITH_FOLKS
libgnome_shell_la_SOURCES += shell-contact-system.c
endif
libgnome_shell_la_gir_sources = \
$(filter-out %-private.h $(shell_private_sources), $(shell_public_headers_h) $(libgnome_shell_la_SOURCES))
@@ -197,7 +193,7 @@ libgnome_shell_la_gir_sources = \
gnome_shell_real_SOURCES = \
main.c
gnome_shell_real_CPPFLAGS = $(gnome_shell_cflags)
gnome_shell_real_LDADD = libgnome-shell.la libgnome-shell-js.la $(libgnome_shell_la_LIBADD)
gnome_shell_real_LDADD = libgnome-shell.la $(libgnome_shell_la_LIBADD)
gnome_shell_real_DEPENDENCIES = libgnome-shell.la
EXTRA_DIST += test-gapplication.js
@@ -306,10 +302,7 @@ libgnome_shell_la_LIBADD = \
libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags)
Shell-0.1.gir: libgnome-shell.la St-1.0.gir
Shell_0_1_gir_INCLUDES = Clutter-1.0 ClutterX11-1.0 Meta-3.0 TelepathyGLib-0.12 TelepathyLogger-0.2 Soup-2.4 GMenu-3.0 NetworkManager-1.0 NMClient-1.0
if BUILD_WITH_FOLKS
Shell_0_1_gir_INCLUDES += Folks-0.6
endif
Shell_0_1_gir_INCLUDES = Clutter-1.0 ClutterX11-1.0 Meta-3.0 TelepathyGLib-0.12 TelepathyLogger-0.2 Soup-2.4 GMenu-3.0 NetworkManager-1.0 NMClient-1.0 Folks-0.6
Shell_0_1_gir_CFLAGS = $(libgnome_shell_la_CPPFLAGS) -I $(srcdir)
Shell_0_1_gir_LIBS = libgnome-shell.la
Shell_0_1_gir_FILES = $(libgnome_shell_la_gir_sources)

View File

@@ -29,9 +29,11 @@
#include <libintl.h>
#include <string.h>
#include <gconf/gconf-client.h>
#define HANDLE_LIBICAL_MEMORY
#include <libecal/libecal.h>
#include <libedataserverui/libedataserverui.h>
#include <libecal/e-cal-client.h>
#include <libedataserver/e-source-list.h>
#include <libedataserverui/e-client-utils.h>
#undef CALENDAR_ENABLE_DEBUG
#include "calendar-debug.h"
@@ -46,14 +48,18 @@
#define CALENDAR_SOURCES_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CALENDAR_TYPE_SOURCES, CalendarSourcesPrivate))
typedef struct _ClientData ClientData;
typedef struct _CalendarSourceData CalendarSourceData;
#define CALENDAR_SOURCES_EVO_DIR "/apps/evolution"
#define CALENDAR_SOURCES_APPOINTMENT_SOURCES_KEY CALENDAR_SOURCES_EVO_DIR "/calendar/sources"
#define CALENDAR_SOURCES_TASK_SOURCES_KEY CALENDAR_SOURCES_EVO_DIR "/tasks/sources"
struct _ClientData
{
ECalClient *client;
gulong backend_died_id;
};
/* org.gnome.shell.evolution.calendar has the same data behind it
* as org.gnome.evolution.calendar, but is a small schema we install
* ourselves */
#define CALENDAR_SELECTED_SOURCES_SCHEMA "org.gnome.shell.evolution.calendar"
#define CALENDAR_SOURCES_SELECTED_APPOINTMENT_SOURCES_KEY "selected-calendars"
#define CALENDAR_SOURCES_SELECTED_TASK_SOURCES_KEY "selected-tasks"
typedef struct _CalendarSourceData CalendarSourceData;
struct _CalendarSourceData
{
@@ -61,8 +67,11 @@ struct _CalendarSourceData
CalendarSources *sources;
guint changed_signal;
/* ESource -> EClient */
GHashTable *clients;
GSList *clients;
char **selected_sources;
ESourceList *esource_list;
guint selected_sources_handler_id;
guint timeout_id;
@@ -71,13 +80,11 @@ struct _CalendarSourceData
struct _CalendarSourcesPrivate
{
ESourceRegistry *registry;
gulong source_added_id;
gulong source_changed_id;
gulong source_removed_id;
CalendarSourceData appointment_sources;
CalendarSourceData task_sources;
GConfClient *gconf_client;
GSettings *settings;
};
static void calendar_sources_class_init (CalendarSourcesClass *klass);
@@ -85,12 +92,8 @@ static void calendar_sources_init (CalendarSources *sources);
static void calendar_sources_finalize (GObject *object);
static void backend_died_cb (EClient *client, CalendarSourceData *source_data);
static void calendar_sources_registry_source_changed_cb (ESourceRegistry *registry,
ESource *source,
CalendarSources *sources);
static void calendar_sources_registry_source_removed_cb (ESourceRegistry *registry,
ESource *source,
CalendarSources *sources);
static void calendar_sources_esource_list_changed (ESourceList *source_list,
CalendarSourceData *source_data);
enum
{
@@ -103,14 +106,6 @@ static guint signals [LAST_SIGNAL] = { 0, };
static GObjectClass *parent_class = NULL;
static CalendarSources *calendar_sources_singleton = NULL;
static void
client_data_free (ClientData *data)
{
g_signal_handler_disconnect (data->client, data->backend_died_id);
g_object_unref (data->client);
g_slice_free (ClientData, data);
}
GType
calendar_sources_get_type (void)
{
@@ -178,49 +173,20 @@ calendar_sources_class_init (CalendarSourcesClass *klass)
static void
calendar_sources_init (CalendarSources *sources)
{
GError *error = NULL;
sources->priv = CALENDAR_SOURCES_GET_PRIVATE (sources);
/* XXX Not sure what to do if this fails.
* Should this class implement GInitable or pass the
* registry in as a G_PARAM_CONSTRUCT_ONLY property? */
sources->priv->registry = e_source_registry_new_sync (NULL, &error);
if (error != NULL)
{
g_error ("%s: %s", G_STRFUNC, error->message);
}
sources->priv->source_added_id = g_signal_connect (sources->priv->registry,
"source-added",
G_CALLBACK (calendar_sources_registry_source_changed_cb),
sources);
sources->priv->source_changed_id = g_signal_connect (sources->priv->registry,
"source-changed",
G_CALLBACK (calendar_sources_registry_source_changed_cb),
sources);
sources->priv->source_removed_id = g_signal_connect (sources->priv->registry,
"source-removed",
G_CALLBACK (calendar_sources_registry_source_removed_cb),
sources);
sources->priv->appointment_sources.source_type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS;
sources->priv->appointment_sources.sources = sources;
sources->priv->appointment_sources.changed_signal = signals [APPOINTMENT_SOURCES_CHANGED];
sources->priv->appointment_sources.clients = g_hash_table_new_full ((GHashFunc) e_source_hash,
(GEqualFunc) e_source_equal,
(GDestroyNotify) g_object_unref,
(GDestroyNotify) client_data_free);
sources->priv->appointment_sources.timeout_id = 0;
sources->priv->task_sources.source_type = E_CAL_CLIENT_SOURCE_TYPE_TASKS;
sources->priv->task_sources.sources = sources;
sources->priv->task_sources.changed_signal = signals [TASK_SOURCES_CHANGED];
sources->priv->task_sources.clients = g_hash_table_new_full ((GHashFunc) e_source_hash,
(GEqualFunc) e_source_equal,
(GDestroyNotify) g_object_unref,
(GDestroyNotify) client_data_free);
sources->priv->task_sources.timeout_id = 0;
sources->priv->gconf_client = gconf_client_get_default ();
sources->priv->settings = g_settings_new (CALENDAR_SELECTED_SOURCES_SCHEMA);
}
static void
@@ -229,9 +195,37 @@ calendar_sources_finalize_source_data (CalendarSources *sources,
{
if (source_data->loaded)
{
g_hash_table_destroy (source_data->clients);
GSList *l;
if (source_data->selected_sources_handler_id)
{
g_signal_handler_disconnect (sources->priv->settings,
source_data->selected_sources_handler_id);
source_data->selected_sources_handler_id = 0;
}
for (l = source_data->clients; l; l = l->next)
{
g_signal_handlers_disconnect_by_func (G_OBJECT (l->data),
G_CALLBACK (backend_died_cb),
source_data);
g_object_unref (l->data);
}
g_slist_free (source_data->clients);
source_data->clients = NULL;
if (source_data->esource_list)
{
g_signal_handlers_disconnect_by_func (source_data->esource_list,
G_CALLBACK (calendar_sources_esource_list_changed),
source_data);
g_object_unref (source_data->esource_list);
}
source_data->esource_list = NULL;
g_strfreev (source_data->selected_sources);
source_data->selected_sources = NULL;
if (source_data->timeout_id != 0)
{
g_source_remove (source_data->timeout_id);
@@ -247,21 +241,17 @@ calendar_sources_finalize (GObject *object)
{
CalendarSources *sources = CALENDAR_SOURCES (object);
if (sources->priv->registry)
{
g_signal_handler_disconnect (sources->priv->registry,
sources->priv->source_added_id);
g_signal_handler_disconnect (sources->priv->registry,
sources->priv->source_changed_id);
g_signal_handler_disconnect (sources->priv->registry,
sources->priv->source_removed_id);
g_object_unref (sources->priv->registry);
}
sources->priv->registry = NULL;
calendar_sources_finalize_source_data (sources, &sources->priv->appointment_sources);
calendar_sources_finalize_source_data (sources, &sources->priv->task_sources);
if (sources->priv->gconf_client)
g_object_unref (sources->priv->gconf_client);
sources->priv->gconf_client = NULL;
if (sources->priv->settings)
g_object_unref (sources->priv->settings);
sources->priv->settings = NULL;
if (G_OBJECT_CLASS (parent_class)->finalize)
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@@ -281,72 +271,133 @@ calendar_sources_get (void)
return calendar_sources_singleton;
}
/* The clients are just created here but not loaded */
static void
create_client_for_source (ESource *source,
ECalClientSourceType source_type,
CalendarSourceData *source_data)
static gboolean
is_source_selected (ESource *esource,
char **selected_sources)
{
ClientData *data;
ECalClient *client;
GError *error = NULL;
const char *uid;
char **source;
client = g_hash_table_lookup (source_data->clients, source);
g_return_if_fail (client == NULL);
uid = e_source_peek_uid (esource);
client = e_cal_client_new (source, source_type, &error);
if (!client)
for (source = selected_sources; *source; source++)
{
g_warning ("Could not load source '%s': %s",
e_source_get_uid (source),
error->message);
g_clear_error(&error);
return;
if (!strcmp (*source, uid))
return TRUE;
}
data = g_slice_new0 (ClientData);
data->client = client; /* takes ownership */
data->backend_died_id = g_signal_connect (client,
"backend-died",
G_CALLBACK (backend_died_cb),
source_data);
return FALSE;
}
g_hash_table_insert (source_data->clients, g_object_ref (source), data);
/* The clients are just created here but not loaded */
static ECalClient *
get_ecal_from_source (ESource *esource,
ECalClientSourceType source_type,
GSList *existing_clients)
{
ECalClient *retval;
GError *error = NULL;
if (existing_clients)
{
GSList *l;
for (l = existing_clients; l; l = l->next)
{
EClient *client = E_CLIENT (l->data);
if (e_source_equal (esource, e_client_get_source (client)))
{
dprintf (" load_esource: found existing source ... returning that\n");
return g_object_ref (client);
}
}
}
retval = e_cal_client_new (esource, source_type, &error);
if (!retval)
{
g_warning ("Could not load source '%s' from '%s': %s",
e_source_peek_name (esource),
e_source_peek_relative_uri (esource),
error->message);
g_clear_error(&error);
return NULL;
}
g_signal_connect (retval, "authenticate",
G_CALLBACK (e_client_utils_authenticate_handler), NULL);
return retval;
}
/* - Order doesn't matter
* - Can just compare object pointers since we
* re-use client connections
*/
static gboolean
compare_ecal_lists (GSList *a,
GSList *b)
{
GSList *l;
if (g_slist_length (a) != g_slist_length (b))
return FALSE;
for (l = a; l; l = l->next)
{
if (!g_slist_find (b, l->data))
return FALSE;
}
return TRUE;
}
static inline void
debug_dump_ecal_list (GHashTable *clients)
debug_dump_selected_sources (char **selected_sources)
{
#ifdef CALENDAR_ENABLE_DEBUG
GList *list, *link;
char **source;
dprintf ("Selected sources:\n");
for (source = selected_sources; *source; source++)
{
dprintf (" %s\n", *source);
}
dprintf ("\n");
#endif
}
static inline void
debug_dump_ecal_list (GSList *ecal_list)
{
#ifdef CALENDAR_ENABLE_DEBUG
GSList *l;
dprintf ("Loaded clients:\n");
list = g_hash_table_get_keys (clients);
for (link = list; link != NULL; link = g_list_next (link))
for (l = ecal_list; l; l = l->next)
{
ESource *source = E_SOURCE (link->data);
EClient *client = l->data;
ESource *source = e_client_get_source (client);
dprintf (" %s %s\n",
e_source_get_uid (source),
e_source_get_display_name (source));
dprintf (" %s %s %s\n",
e_source_peek_uid (source),
e_source_peek_name (source),
e_client_get_uri (client));
}
g_list_free (list);
#endif
}
static void
calendar_sources_load_esource_list (ESourceRegistry *registry,
CalendarSourceData *source_data);
calendar_sources_load_esource_list (CalendarSourceData *source_data);
static gboolean
backend_restart (gpointer data)
{
CalendarSourceData *source_data = data;
ESourceRegistry *registry;
registry = source_data->sources->priv->registry;
calendar_sources_load_esource_list (registry, source_data);
g_signal_emit (source_data->sources, source_data->changed_signal, 0);
calendar_sources_load_esource_list (source_data);
source_data->timeout_id = 0;
@@ -356,13 +407,16 @@ backend_restart (gpointer data)
static void
backend_died_cb (EClient *client, CalendarSourceData *source_data)
{
ESource *source;
const char *display_name;
const char *uristr;
source = e_client_get_source (client);
display_name = e_source_get_display_name (source);
g_warning ("The calendar backend for '%s' has crashed.", display_name);
g_hash_table_remove (source_data->clients, source);
source_data->clients = g_slist_remove (source_data->clients, client);
if (g_slist_length (source_data->clients) < 1)
{
g_slist_free (source_data->clients);
source_data->clients = NULL;
}
uristr = e_client_get_uri (client);
g_warning ("The calendar backend for %s has crashed.", uristr);
if (source_data->timeout_id != 0)
{
@@ -375,162 +429,169 @@ backend_died_cb (EClient *client, CalendarSourceData *source_data)
}
static void
calendar_sources_load_esource_list (ESourceRegistry *registry,
CalendarSourceData *source_data)
calendar_sources_load_esource_list (CalendarSourceData *source_data)
{
GList *list, *link;
const gchar *extension_name;
GSList *clients = NULL;
GSList *groups, *l;
gboolean emit_signal = FALSE;
switch (source_data->source_type)
g_return_if_fail (source_data->esource_list != NULL);
debug_dump_selected_sources (source_data->selected_sources);
dprintf ("Source groups:\n");
groups = e_source_list_peek_groups (source_data->esource_list);
for (l = groups; l; l = l->next)
{
case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
extension_name = E_SOURCE_EXTENSION_CALENDAR;
break;
case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
extension_name = E_SOURCE_EXTENSION_TASK_LIST;
break;
default:
g_return_if_reached ();
GSList *esources, *s;
dprintf (" %s\n", e_source_group_peek_uid (l->data));
dprintf (" sources:\n");
esources = e_source_group_peek_sources (l->data);
for (s = esources; s; s = s->next)
{
ESource *esource = E_SOURCE (s->data);
ECalClient *client;
dprintf (" type = '%s' uid = '%s', name = '%s', relative uri = '%s': \n",
source_data->source_type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS ? "appointment" : "task",
e_source_peek_uid (esource),
e_source_peek_name (esource),
e_source_peek_relative_uri (esource));
if (is_source_selected (esource, source_data->selected_sources) &&
(client = get_ecal_from_source (esource, source_data->source_type, source_data->clients)))
{
clients = g_slist_prepend (clients, client);
}
}
}
dprintf ("\n");
if (source_data->loaded &&
!compare_ecal_lists (source_data->clients, clients))
emit_signal = TRUE;
for (l = source_data->clients; l; l = l->next)
{
g_signal_handlers_disconnect_by_func (G_OBJECT (l->data),
G_CALLBACK (backend_died_cb),
source_data);
g_object_unref (l->data);
}
g_slist_free (source_data->clients);
source_data->clients = g_slist_reverse (clients);
/* connect to backend_died after we disconnected the previous signal
* handlers. If we do it before, we'll lose some handlers (for clients that
* were already there before) */
for (l = source_data->clients; l; l = l->next)
{
g_signal_connect (G_OBJECT (l->data), "backend-died",
G_CALLBACK (backend_died_cb), source_data);
}
list = e_source_registry_list_sources (registry, extension_name);
for (link = list; link != NULL; link = g_list_next (link))
if (emit_signal)
{
ESource *source = E_SOURCE (link->data);
ESourceSelectable *extension;
gboolean show_source;
extension = e_source_get_extension (source, extension_name);
show_source = e_source_get_enabled (source) && e_source_selectable_get_selected (extension);
if (show_source)
create_client_for_source (source, source_data->source_type, source_data);
dprintf ("Emitting %s-sources-changed signal\n",
source_data->source_type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS ? "appointment" : "task");
g_signal_emit (source_data->sources, source_data->changed_signal, 0);
}
debug_dump_ecal_list (source_data->clients);
g_list_free_full (list, g_object_unref);
}
static void
calendar_sources_registry_source_changed_cb (ESourceRegistry *registry,
ESource *source,
CalendarSources *sources)
calendar_sources_esource_list_changed (ESourceList *source_list,
CalendarSourceData *source_data)
{
if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR))
{
CalendarSourceData *source_data;
ESourceSelectable *extension;
gboolean have_client;
gboolean show_source;
dprintf ("ESourceList changed, reloading\n");
source_data = &sources->priv->appointment_sources;
extension = e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR);
have_client = (g_hash_table_lookup (source_data->clients, source) != NULL);
show_source = e_source_get_enabled (source) && e_source_selectable_get_selected (extension);
if (!show_source && have_client)
{
g_hash_table_remove (source_data->clients, source);
g_signal_emit (sources, source_data->changed_signal, 0);
}
if (show_source && !have_client)
{
create_client_for_source (source, source_data->source_type, source_data);
g_signal_emit (sources, source_data->changed_signal, 0);
}
}
if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST))
{
CalendarSourceData *source_data;
ESourceSelectable *extension;
gboolean have_client;
gboolean show_source;
source_data = &sources->priv->task_sources;
extension = e_source_get_extension (source, E_SOURCE_EXTENSION_TASK_LIST);
have_client = (g_hash_table_lookup (source_data->clients, source) != NULL);
show_source = e_source_get_enabled (source) && e_source_selectable_get_selected (extension);
if (!show_source && have_client)
{
g_hash_table_remove (source_data->clients, source);
g_signal_emit (sources, source_data->changed_signal, 0);
}
if (show_source && !have_client)
{
create_client_for_source (source, source_data->source_type, source_data);
g_signal_emit (sources, source_data->changed_signal, 0);
}
}
calendar_sources_load_esource_list (source_data);
}
static void
calendar_sources_registry_source_removed_cb (ESourceRegistry *registry,
ESource *source,
CalendarSources *sources)
calendar_sources_selected_sources_notify (GSettings *settings,
const gchar *key,
CalendarSourceData *source_data)
{
if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR))
{
CalendarSourceData *source_data;
dprintf ("Selected sources key (%s) changed, reloading\n", key);
source_data = &sources->priv->appointment_sources;
g_hash_table_remove (source_data->clients, source);
g_signal_emit (sources, source_data->changed_signal, 0);
}
g_strfreev (source_data->selected_sources);
source_data->selected_sources = g_settings_get_strv (settings, key);
if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST))
{
CalendarSourceData *source_data;
source_data = &sources->priv->task_sources;
g_hash_table_remove (source_data->clients, source);
g_signal_emit (sources, source_data->changed_signal, 0);
}
calendar_sources_load_esource_list (source_data);
}
GList *
calendar_sources_get_appointment_clients (CalendarSources *sources)
static void
calendar_sources_load_sources (CalendarSources *sources,
CalendarSourceData *source_data,
const char *sources_key,
const char *selected_sources_key)
{
GList *list, *link;
GConfClient *gconf_client;
GSettings *settings;
char *signal_name;
dprintf ("---------------------------\n");
dprintf ("Loading sources:\n");
dprintf (" sources_key: %s\n", sources_key);
dprintf (" selected_sources_key: %s\n", selected_sources_key);
gconf_client = sources->priv->gconf_client;
settings = sources->priv->settings;
source_data->selected_sources = g_settings_get_strv (settings, selected_sources_key);
signal_name = g_strconcat ("changed::", selected_sources_key, NULL);
source_data->selected_sources_handler_id =
g_signal_connect (settings, signal_name,
G_CALLBACK (calendar_sources_selected_sources_notify), source_data);
g_free (signal_name);
source_data->esource_list = e_source_list_new_for_gconf (gconf_client, sources_key);
g_signal_connect (source_data->esource_list, "changed",
G_CALLBACK (calendar_sources_esource_list_changed),
source_data);
calendar_sources_load_esource_list (source_data);
source_data->loaded = TRUE;
dprintf ("---------------------------\n");
}
GSList *
calendar_sources_get_appointment_sources (CalendarSources *sources)
{
g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL);
if (!sources->priv->appointment_sources.loaded)
{
calendar_sources_load_esource_list (sources->priv->registry,
&sources->priv->appointment_sources);
sources->priv->appointment_sources.loaded = TRUE;
calendar_sources_load_sources (sources,
&sources->priv->appointment_sources,
CALENDAR_SOURCES_APPOINTMENT_SOURCES_KEY,
CALENDAR_SOURCES_SELECTED_APPOINTMENT_SOURCES_KEY);
}
list = g_hash_table_get_values (sources->priv->appointment_sources.clients);
for (link = list; link != NULL; link = g_list_next (link))
link->data = ((ClientData *) link->data)->client;
return list;
return sources->priv->appointment_sources.clients;
}
GList *
calendar_sources_get_task_clients (CalendarSources *sources)
GSList *
calendar_sources_get_task_sources (CalendarSources *sources)
{
GList *list, *link;
g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL);
if (!sources->priv->task_sources.loaded)
{
calendar_sources_load_esource_list (sources->priv->registry,
&sources->priv->task_sources);
sources->priv->task_sources.loaded = TRUE;
calendar_sources_load_sources (sources,
&sources->priv->task_sources,
CALENDAR_SOURCES_TASK_SOURCES_KEY,
CALENDAR_SOURCES_SELECTED_TASK_SOURCES_KEY);
}
list = g_hash_table_get_values (sources->priv->task_sources.clients);
for (link = list; link != NULL; link = g_list_next (link))
link->data = ((ClientData *) link->data)->client;
return list;
return sources->priv->task_sources.clients;
}

View File

@@ -58,8 +58,8 @@ struct _CalendarSourcesClass
GType calendar_sources_get_type (void) G_GNUC_CONST;
CalendarSources *calendar_sources_get (void);
GList *calendar_sources_get_appointment_clients (CalendarSources *sources);
GList *calendar_sources_get_task_clients (CalendarSources *sources);
GSList *calendar_sources_get_appointment_sources (CalendarSources *sources);
GSList *calendar_sources_get_task_sources (CalendarSources *sources);
G_END_DECLS

View File

@@ -37,7 +37,13 @@
#include <gtk/gtk.h>
#define HANDLE_LIBICAL_MEMORY
#include <libecal/libecal.h>
#include <libecal/e-cal-client.h>
#include <libecal/e-cal-time-util.h>
#include <libecal/e-cal-recur.h>
#include <libecal/e-cal-system-timezone.h>
#define CALENDAR_CONFIG_PREFIX "/apps/evolution/calendar"
#define CALENDAR_CONFIG_TIMEZONE CALENDAR_CONFIG_PREFIX "/display/timezone"
#include "calendar-sources.h"
@@ -84,7 +90,7 @@ typedef struct
{
char *uid;
char *rid;
char *backend_name;
char *uri;
char *summary;
char *description;
char *color_string;
@@ -247,60 +253,37 @@ static char *
get_source_color (ECalClient *esource)
{
ESource *source;
ECalClientSourceType source_type;
ESourceSelectable *extension;
const gchar *extension_name;
g_return_val_if_fail (E_IS_CAL_CLIENT (esource), NULL);
source = e_client_get_source (E_CLIENT (esource));
source_type = e_cal_client_get_source_type (esource);
switch (source_type)
{
case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
extension_name = E_SOURCE_EXTENSION_CALENDAR;
break;
case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
extension_name = E_SOURCE_EXTENSION_TASK_LIST;
break;
default:
g_return_val_if_reached (NULL);
}
extension = e_source_get_extension (source, extension_name);
return e_source_selectable_dup_color (extension);
return g_strdup (e_source_peek_color_spec (source));
}
static gchar *
get_source_backend_name (ECalClient *esource)
get_source_uri (ECalClient *esource)
{
ESource *source;
ECalClientSourceType source_type;
ESourceBackend *extension;
const gchar *extension_name;
ESource *source;
gchar *string;
gchar **list;
g_return_val_if_fail (E_IS_CAL_CLIENT (esource), NULL);
g_return_val_if_fail (E_IS_CAL_CLIENT (esource), NULL);
source = e_client_get_source (E_CLIENT (esource));
source_type = e_cal_client_get_source_type (esource);
source = e_client_get_source (E_CLIENT (esource));
string = g_strdup (e_source_get_uri (source));
if (string) {
list = g_strsplit (string, ":", 2);
g_free (string);
switch (source_type)
{
case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
extension_name = E_SOURCE_EXTENSION_CALENDAR;
break;
case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
extension_name = E_SOURCE_EXTENSION_TASK_LIST;
break;
default:
g_return_val_if_reached (NULL);
if (list[0]) {
string = g_strdup (list[0]);
g_strfreev (list);
return string;
}
g_strfreev (list);
}
extension = e_source_get_extension (source, extension_name);
return e_source_backend_dup_backend_name (extension);
return NULL;
}
static inline int
@@ -331,7 +314,7 @@ calendar_appointment_equal (CalendarAppointment *a,
return
null_safe_strcmp (a->uid, b->uid) == 0 &&
null_safe_strcmp (a->backend_name, b->backend_name) == 0 &&
null_safe_strcmp (a->uri, b->uri) == 0 &&
null_safe_strcmp (a->summary, b->summary) == 0 &&
null_safe_strcmp (a->description, b->description) == 0 &&
null_safe_strcmp (a->color_string, b->color_string) == 0 &&
@@ -356,8 +339,8 @@ calendar_appointment_free (CalendarAppointment *appointment)
g_free (appointment->rid);
appointment->rid = NULL;
g_free (appointment->backend_name);
appointment->backend_name = NULL;
g_free (appointment->uri);
appointment->uri = NULL;
g_free (appointment->summary);
appointment->summary = NULL;
@@ -380,7 +363,7 @@ calendar_appointment_init (CalendarAppointment *appointment,
{
appointment->uid = get_ical_uid (ical);
appointment->rid = get_ical_rid (ical);
appointment->backend_name = get_source_backend_name (cal);
appointment->uri = get_source_uri (cal);
appointment->summary = get_ical_summary (ical);
appointment->description = get_ical_description (ical);
appointment->color_string = get_source_color (cal);
@@ -484,6 +467,9 @@ struct _App
CalendarSources *sources;
gulong sources_signal_id;
guint zone_listener;
GConfClient *gconf_client;
/* hash from uid to CalendarAppointment objects */
GHashTable *appointments;
@@ -599,8 +585,8 @@ on_objects_removed (ECalClientView *view,
static void
app_load_events (App *app)
{
GList *clients;
GList *l;
GSList *sources;
GSList *l;
GList *ll;
gchar *since_iso8601;
gchar *until_iso8601;
@@ -630,8 +616,8 @@ app_load_events (App *app)
since_iso8601,
until_iso8601);
clients = calendar_sources_get_appointment_clients (app->sources);
for (l = clients; l != NULL; l = l->next)
sources = calendar_sources_get_appointment_sources (app->sources);
for (l = sources; l != NULL; l = l->next)
{
ECalClient *cal = E_CAL_CLIENT (l->data);
GError *error;
@@ -644,9 +630,8 @@ app_load_events (App *app)
error = NULL;
if (!e_client_open_sync (E_CLIENT (cal), TRUE, NULL, &error))
{
ESource *source = e_client_get_source (E_CLIENT (cal));
g_warning ("Error opening calendar %s: %s\n",
e_source_get_uid (source), error->message);
e_client_get_uri (E_CLIENT (cal)), error->message);
g_error_free (error);
continue;
}
@@ -663,9 +648,8 @@ app_load_events (App *app)
NULL, /* cancellable */
&error))
{
ESource *source = e_client_get_source (E_CLIENT (cal));
g_warning ("Error querying calendar %s: %s\n",
e_source_get_uid (source), error->message);
e_client_get_uri (E_CLIENT (cal)), error->message);
g_error_free (error);
g_free (query);
continue;
@@ -721,7 +705,6 @@ app_load_events (App *app)
g_free (query);
}
g_list_free (clients);
g_free (since_iso8601);
g_free (until_iso8601);
app->cache_invalid = FALSE;

View File

@@ -18,18 +18,15 @@
#include <meta/main.h>
#include <meta/meta-plugin.h>
#include <meta/prefs.h>
#include <atk-bridge.h>
#include <telepathy-glib/debug.h>
#include <telepathy-glib/debug-sender.h>
#include "shell-a11y.h"
#include "shell-global.h"
#include "shell-global-private.h"
#include "shell-js.h"
#include "shell-perf-log.h"
#include "st.h"
#include <jsapi.h>
extern GType gnome_shell_plugin_get_type (void);
#define SHELL_DBUS_SERVICE "org.gnome.Shell"
@@ -38,7 +35,6 @@ extern GType gnome_shell_plugin_get_type (void);
#define OVERRIDES_SCHEMA "org.gnome.shell.overrides"
static gboolean is_gdm_mode = FALSE;
static char *session_mode = NULL;
#define DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER 1
#define DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER 4
@@ -177,18 +173,6 @@ shell_prefs_init (void)
OVERRIDES_SCHEMA);
}
static void
shell_introspection_init (void)
{
g_irepository_prepend_search_path (MUTTER_TYPELIB_DIR);
g_irepository_prepend_search_path (GNOME_SHELL_PKGLIBDIR);
#if HAVE_BLUETOOTH
g_irepository_prepend_search_path (BLUETOOTH_DIR);
#endif
}
static void
malloc_statistics_callback (ShellPerfLog *perf_log,
gpointer data)
@@ -235,20 +219,6 @@ shell_perf_log_init (void)
NULL, NULL);
}
static void
shell_a11y_init (void)
{
if (clutter_get_accessibility_enabled () == FALSE)
{
g_warning ("Accessibility: clutter has no accessibility enabled"
" skipping the atk-bridge load");
}
else
{
atk_bridge_adaptor_init (NULL, NULL);
}
}
static void
default_log_handler (const char *log_domain,
GLogLevelFlags log_level,
@@ -268,48 +238,6 @@ default_log_handler (const char *log_domain,
g_log_default_handler (log_domain, log_level, message, data);
}
static void
shut_up (const char *domain,
GLogLevelFlags level,
const char *message,
gpointer user_data)
{
}
static gboolean
list_modes (const char *option_name,
const char *value,
gpointer data,
GError **error)
{
ShellGlobal *global;
GjsContext *context;
const char *script;
int status;
/* Many of our imports require global to be set, so rather than
* tayloring our imports carefully here to avoid that dependency,
* we just set it.
* ShellGlobal has some GTK+ dependencies, so initialize GTK+; we
* don't really care if it fails though (e.g. when running from a tty),
* so we mute all warnings */
g_log_set_default_handler (shut_up, NULL);
gtk_init_check (NULL, NULL);
_shell_global_init (NULL);
global = shell_global_get ();
context = _shell_global_get_gjs_context (global);
shell_introspection_init ();
script = "imports.ui.environment.init();"
"imports.ui.sessionMode.listModes();";
if (!gjs_context_eval (context, script, -1, "<main>", &status, NULL))
g_message ("Retrieving list of available modes failed.");
exit (status);
}
static gboolean
print_version (const gchar *option_name,
const gchar *value,
@@ -328,23 +256,11 @@ GOptionEntry gnome_shell_options[] = {
NULL
},
{
"gdm-mode", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE,
"gdm-mode", 0, 0, G_OPTION_ARG_NONE,
&is_gdm_mode,
N_("Mode used by GDM for login screen"),
NULL
},
{
"mode", 0, 0, G_OPTION_ARG_STRING,
&session_mode,
N_("Use a specific mode, e.g. \"gdm\" for login screen"),
"MODE"
},
{
"list-modes", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
list_modes,
N_("List possible modes"),
NULL
},
{ NULL }
};
@@ -353,6 +269,7 @@ main (int argc, char **argv)
{
GOptionContext *ctx;
GError *error = NULL;
ShellSessionType session_type;
int ecode;
TpDebugSender *sender;
@@ -372,11 +289,13 @@ main (int argc, char **argv)
g_option_context_free (ctx);
meta_plugin_manager_set_plugin_type (gnome_shell_plugin_get_type ());
meta_plugin_type_register (gnome_shell_plugin_get_type ());
/* Prevent meta_init() from causing gtk to load gail and at-bridge */
g_setenv ("NO_GAIL", "1", TRUE);
g_setenv ("NO_AT_BRIDGE", "1", TRUE);
meta_init ();
g_unsetenv ("NO_GAIL");
g_unsetenv ("NO_AT_BRIDGE");
/* FIXME: Add gjs API to set this stuff and don't depend on the
@@ -389,7 +308,11 @@ main (int argc, char **argv)
shell_a11y_init ();
shell_perf_log_init ();
shell_prefs_init ();
shell_introspection_init ();
g_irepository_prepend_search_path (GNOME_SHELL_PKGLIBDIR);
#if HAVE_BLUETOOTH
g_irepository_prepend_search_path (BLUETOOTH_DIR);
#endif
/* Turn on telepathy-glib debugging but filter it out in
* default_log_handler. This handler also exposes all the logs over D-Bus
@@ -400,10 +323,12 @@ main (int argc, char **argv)
g_log_set_default_handler (default_log_handler, sender);
/* Initialize the global object */
if (session_mode == NULL)
session_mode = is_gdm_mode ? "gdm" : "user";
if (is_gdm_mode)
session_type = SHELL_SESSION_GDM;
else
session_type = SHELL_SESSION_USER;
_shell_global_init ("session-mode", session_mode, NULL);
_shell_global_init ("session-type", session_type, NULL);
ecode = meta_run ();
@@ -417,17 +342,3 @@ main (int argc, char **argv)
return ecode;
}
/* HACK:
Add a dummy function that calls into libgnome-shell-js.so to ensure it's
linked to /usr/bin/gnome-shell even when linking with --as-needed.
This function is never actually called.
https://bugzilla.gnome.org/show_bug.cgi?id=670477
*/
void _shell_link_to_shell_js (void);
void
_shell_link_to_shell_js (void)
{
shell_js_add_extension_importer (NULL, NULL, NULL, NULL);
}

View File

@@ -144,9 +144,6 @@ main(int argc, char **argv)
exit (1);
}
gjs_context_gc (js_context);
gjs_context_gc (js_context);
g_free (script);
exit (code);
}

164
src/shell-a11y.c Normal file
View File

@@ -0,0 +1,164 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2010 Igalia, S.L.
*
* Author: Alejandro Piñeiro Iglesias <apinheiro@igalia.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#include <string.h>
#include <gmodule.h>
#include <clutter/clutter.h>
#include "shell-a11y.h"
#define INIT_METHOD "gnome_accessibility_module_init"
#define DESKTOP_SCHEMA "org.gnome.desktop.interface"
#define ACCESSIBILITY_ENABLED_KEY "toolkit-accessibility"
#define AT_SPI_SCHEMA "org.a11y.atspi"
#define ATK_BRIDGE_LOCATION_KEY "atk-bridge-location"
static gboolean
should_enable_a11y (void)
{
GSettings *desktop_settings = NULL;
gboolean value = FALSE;
desktop_settings = g_settings_new (DESKTOP_SCHEMA);
value = g_settings_get_boolean (desktop_settings, ACCESSIBILITY_ENABLED_KEY);
g_object_unref (desktop_settings);
return value;
}
static char*
get_atk_bridge_path (void)
{
GSettings *atspi_settings = NULL;
GVariant *variant = NULL;
char *value = NULL;
const char * const *schemas = NULL;
gboolean found = FALSE;
int i = 0;
schemas = g_settings_list_schemas ();
for (i = 0; schemas [i]; i++)
{
if (!strcmp (schemas[i], AT_SPI_SCHEMA))
{
found = TRUE;
break;
}
}
if (!found)
{
g_warning ("Accessibility: %s schema not found. Are you sure that at-spi or"
" at-spi2 is installed on your system?", AT_SPI_SCHEMA);
return NULL;
}
atspi_settings = g_settings_new (AT_SPI_SCHEMA);
variant = g_settings_get_value (atspi_settings, ATK_BRIDGE_LOCATION_KEY);
value = g_variant_dup_bytestring (variant, NULL);
g_variant_unref (variant);
g_object_unref (atspi_settings);
return value;
}
static gboolean
a11y_invoke_module (const char *module_path)
{
GModule *handle;
void (*invoke_fn) (void);
if (!module_path)
{
g_warning ("Accessibility: invalid module path (NULL)");
return FALSE;
}
if (!(handle = g_module_open (module_path, 0)))
{
g_warning ("Accessibility: failed to load module '%s': '%s'",
module_path, g_module_error ());
return FALSE;
}
if (!g_module_symbol (handle, INIT_METHOD, (gpointer *)&invoke_fn))
{
g_warning ("Accessibility: error library '%s' does not include "
"method '%s' required for accessibility support",
module_path, INIT_METHOD);
g_module_close (handle);
return FALSE;
}
invoke_fn ();
return TRUE;
}
/*
* It loads the atk-bridge if required. It checks:
* * If the proper gsetting key is set
* * If clutter has already enabled the accessibility
*
* You need to ensure that the atk-bridge was not loaded before this
* call, because in that case the application would be already
* registered on at-spi using the AtkUtil implementation on that
* moment (if any, although without anyone the application would
* crash). Anyway this is the reason of NO_AT_BRIDGE.
*
*/
void
shell_a11y_init (void)
{
char *bridge_path = NULL;
if (!should_enable_a11y ())
return;
if (clutter_get_accessibility_enabled () == FALSE)
{
g_warning ("Accessibility: clutter has no accessibility enabled"
" skipping the atk-bridge load");
return;
}
bridge_path = get_atk_bridge_path ();
if (a11y_invoke_module (bridge_path) == FALSE)
{
g_warning ("Accessibility: error loading the atk-bridge. Although the"
" accessibility on the system is enabled and clutter"
" accessibility is also enabled, accessibility support on"
" GNOME Shell will not work");
}
/* NOTE: We avoid to load gail module, as gail-cally interaction is
* not fully supported right now.
*/
g_free (bridge_path);
}

32
src/shell-a11y.h Normal file
View File

@@ -0,0 +1,32 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2010 Igalia, S.L.
*
* Author: Alejandro Piñeiro Iglesias <apinheiro@igalia.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#ifndef __SHELL_A11Y_H_
#define __SHELL_A11Y_H_
G_BEGIN_DECLS
void shell_a11y_init (void);
G_END_DECLS
#endif /* __SHELL_A11Y_H_ */

View File

@@ -7,13 +7,18 @@
#include <string.h>
#include <gio/gio.h>
#include <gio/gdesktopappinfo.h>
#include <gtk/gtk.h>
#include <clutter/clutter.h>
#include <glib/gi18n.h>
#include <meta/display.h>
#include "shell-app-private.h"
#include "shell-window-tracker-private.h"
#include "shell-app-system-private.h"
#include "shell-global.h"
#include "shell-util.h"
#include "st.h"
/* Vendor prefixes are something that can be preprended to a .desktop
* file name. Undo this.
@@ -41,7 +46,6 @@ struct _ShellAppSystemPrivate {
GMenuTree *apps_tree;
GHashTable *running_apps;
GHashTable *visible_id_to_app;
GHashTable *id_to_app;
GSList *known_vendor_prefixes;
@@ -93,16 +97,14 @@ shell_app_system_init (ShellAppSystem *self)
priv->id_to_app = g_hash_table_new_full (g_str_hash, g_str_equal,
NULL,
(GDestroyNotify)g_object_unref);
/* All the objects in this hash table are owned by id_to_app */
priv->visible_id_to_app = g_hash_table_new (g_str_hash, g_str_equal);
priv->setting_id_to_app = g_hash_table_new_full (g_str_hash, g_str_equal,
NULL,
(GDestroyNotify)g_object_unref);
/* We want to track NoDisplay apps, so we add INCLUDE_NODISPLAY. We'll
* filter NoDisplay apps out when showing them to the user. */
/* For now, we want to pick up Evince, Nautilus, etc. We'll
* handle NODISPLAY semantics at a higher level or investigate them
* case by case.
*/
priv->apps_tree = gmenu_tree_new ("applications.menu", GMENU_TREE_FLAGS_INCLUDE_NODISPLAY);
g_signal_connect (priv->apps_tree, "changed", G_CALLBACK (on_apps_tree_changed_cb), self);
@@ -124,10 +126,10 @@ shell_app_system_finalize (GObject *object)
g_hash_table_destroy (priv->running_apps);
g_hash_table_destroy (priv->id_to_app);
g_hash_table_destroy (priv->visible_id_to_app);
g_hash_table_destroy (priv->setting_id_to_app);
g_slist_free_full (priv->known_vendor_prefixes, g_free);
g_slist_foreach (priv->known_vendor_prefixes, (GFunc)g_free, NULL);
g_slist_free (priv->known_vendor_prefixes);
priv->known_vendor_prefixes = NULL;
G_OBJECT_CLASS (shell_app_system_parent_class)->finalize (object);
@@ -312,8 +314,8 @@ on_apps_tree_changed_cb (GMenuTree *tree,
g_assert (tree == self->priv->apps_tree);
g_hash_table_remove_all (self->priv->visible_id_to_app);
g_slist_free_full (self->priv->known_vendor_prefixes, g_free);
g_slist_foreach (self->priv->known_vendor_prefixes, (GFunc)g_free, NULL);
g_slist_free (self->priv->known_vendor_prefixes);
self->priv->known_vendor_prefixes = NULL;
if (!gmenu_tree_load_sync (self->priv->apps_tree, &error))
@@ -374,8 +376,6 @@ on_apps_tree_changed_cb (GMenuTree *tree,
* string is pointed to.
*/
g_hash_table_replace (self->priv->id_to_app, (char*)id, app);
if (!gmenu_tree_entry_get_is_nodisplay_recurse (entry))
g_hash_table_replace (self->priv->visible_id_to_app, (char*)id, app);
if (old_entry)
gmenu_tree_item_unref (old_entry);
@@ -644,6 +644,30 @@ shell_app_system_lookup_wmclass (ShellAppSystem *system,
return app;
}
/**
* shell_app_system_get_all:
* @system:
*
* Returns: (transfer container) (element-type ShellApp): All installed applications
*/
GSList *
shell_app_system_get_all (ShellAppSystem *self)
{
GSList *result = NULL;
GHashTableIter iter;
gpointer key, value;
g_hash_table_iter_init (&iter, self->priv->id_to_app);
while (g_hash_table_iter_next (&iter, &key, &value))
{
ShellApp *app = value;
if (!g_desktop_app_info_get_nodisplay (shell_app_get_app_info (app)))
result = g_slist_prepend (result, app);
}
return result;
}
void
_shell_app_system_notify_app_state_changed (ShellAppSystem *self,
ShellApp *app)
@@ -766,7 +790,8 @@ search_tree (ShellAppSystem *self,
&prefix_results,
&substring_results);
}
g_slist_free_full (normalized_terms, g_free);
g_slist_foreach (normalized_terms, (GFunc)g_free, NULL);
g_slist_free (normalized_terms);
return sort_and_concat_results (self, prefix_results, substring_results);
@@ -785,7 +810,7 @@ GSList *
shell_app_system_initial_search (ShellAppSystem *self,
GSList *terms)
{
return search_tree (self, terms, self->priv->visible_id_to_app);
return search_tree (self, terms, self->priv->id_to_app);
}
/**
@@ -819,7 +844,8 @@ shell_app_system_subsearch (ShellAppSystem *system,
&prefix_results,
&substring_results);
}
g_slist_free_full (normalized_terms, g_free);
g_slist_foreach (normalized_terms, (GFunc)g_free, NULL);
g_slist_free (normalized_terms);
/* Note that a shorter term might have matched as a prefix, but
when extended only as a substring, so we have to redo the

View File

@@ -52,6 +52,8 @@ ShellApp *shell_app_system_lookup_heuristic_basename (ShellAppSystem *
ShellApp *shell_app_system_lookup_wmclass (ShellAppSystem *system,
const char *wmclass);
GSList *shell_app_system_get_all (ShellAppSystem *system);
GSList *shell_app_system_get_running (ShellAppSystem *self);
GSList *shell_app_system_initial_search (ShellAppSystem *system,

View File

@@ -42,7 +42,6 @@ typedef struct {
/* See GApplication documentation */
GDBusMenuModel *remote_menu;
GActionMuxer *muxer;
char * unique_bus_name;
} ShellAppRunningState;
/**
@@ -96,6 +95,7 @@ enum {
static guint shell_app_signals[LAST_SIGNAL] = { 0 };
static void create_running_state (ShellApp *app);
static void setup_running_state (ShellApp *app, MetaWindow *window);
static void unref_running_state (ShellAppRunningState *state);
G_DEFINE_TYPE (ShellApp, shell_app, G_TYPE_OBJECT)
@@ -371,7 +371,7 @@ shell_app_get_name (ShellApp *app)
name = meta_window_get_wm_class (window);
if (!name)
name = C_("program", "Unknown");
name = _("Unknown");
return name;
}
}
@@ -493,7 +493,7 @@ shell_app_activate_window (ShellApp *app,
return;
else
{
GSList *windows_reversed, *iter;
GSList *iter;
ShellGlobal *global = shell_global_get ();
MetaScreen *screen = shell_global_get_screen (global);
MetaDisplay *display = meta_screen_get_display (screen);
@@ -511,16 +511,13 @@ shell_app_activate_window (ShellApp *app,
/* Now raise all the other windows for the app that are on
* the same workspace, in reverse order to preserve the stacking.
*/
windows_reversed = g_slist_copy (windows);
windows_reversed = g_slist_reverse (windows_reversed);
for (iter = windows_reversed; iter; iter = iter->next)
for (iter = windows; iter; iter = iter->next)
{
MetaWindow *other_window = iter->data;
if (other_window != window)
meta_window_raise (other_window);
}
g_slist_free (windows_reversed);
/* If we have a transient that the user's interacted with more recently than
* the window, pick that.
@@ -975,7 +972,7 @@ _shell_app_add_window (ShellApp *app,
g_signal_connect (window, "unmanaged", G_CALLBACK(shell_app_on_unmanaged), app);
g_signal_connect (window, "notify::user-time", G_CALLBACK(shell_app_on_user_time_changed), app);
shell_app_update_app_menu (app, window);
setup_running_state (app, window);
if (app->state != SHELL_APP_STATE_STARTING)
shell_app_state_transition (app, SHELL_APP_STATE_RUNNING);
@@ -1221,14 +1218,12 @@ create_running_state (ShellApp *app)
app->running_state->muxer = g_action_muxer_new ();
}
void
shell_app_update_app_menu (ShellApp *app,
MetaWindow *window)
static void
setup_running_state (ShellApp *app,
MetaWindow *window)
{
const gchar *unique_bus_name;
/* We assume that 'gtk-application-object-path' and
* 'gtk-app-menu-object-path' are the same for all windows which
/* We assume that 'gtk-unique-bus-name', gtk-application-object-path'
* and 'gtk-app-menu-object-path' are the same for all windows which
* have it set.
*
* It could be possible, however, that the first window we see
@@ -1237,27 +1232,23 @@ shell_app_update_app_menu (ShellApp *app,
* all the rest (until the app is stopped and restarted).
*/
unique_bus_name = meta_window_get_gtk_unique_bus_name (window);
if (app->running_state->remote_menu == NULL ||
g_strcmp0 (app->running_state->unique_bus_name, unique_bus_name) != 0)
if (app->running_state->remote_menu == NULL)
{
const gchar *application_object_path;
const gchar *app_menu_object_path;
const gchar *unique_bus_name;
GDBusConnection *session;
GDBusActionGroup *actions;
application_object_path = meta_window_get_gtk_application_object_path (window);
app_menu_object_path = meta_window_get_gtk_app_menu_object_path (window);
unique_bus_name = meta_window_get_gtk_unique_bus_name (window);
if (application_object_path == NULL || app_menu_object_path == NULL || unique_bus_name == NULL)
return;
session = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
g_assert (session != NULL);
g_clear_pointer (&app->running_state->unique_bus_name, g_free);
app->running_state->unique_bus_name = g_strdup (unique_bus_name);
g_clear_object (&app->running_state->remote_menu);
app->running_state->remote_menu = g_dbus_menu_model_get (session, unique_bus_name, app_menu_object_path);
actions = g_dbus_action_group_get (session, unique_bus_name, application_object_path);
g_action_muxer_insert (app->running_state->muxer, "app", G_ACTION_GROUP (actions));

View File

@@ -82,7 +82,6 @@ int shell_app_compare_by_name (ShellApp *app, ShellApp *other);
int shell_app_compare (ShellApp *app, ShellApp *other);
void shell_app_update_window_actions (ShellApp *app, MetaWindow *window);
void shell_app_update_app_menu (ShellApp *app, MetaWindow *window);
G_END_DECLS

View File

@@ -297,7 +297,7 @@ sort_and_prepare_results (GSList *results)
sorted_results = g_slist_prepend (sorted_results, id);
}
g_slist_free_full (results, (GDestroyNotify) free_result);
g_slist_foreach (results, (GFunc) free_result, NULL);
return sorted_results;
}
@@ -463,9 +463,6 @@ shell_contact_system_initial_search (ShellContactSystem *self,
g_object_unref (individual);
}
g_object_unref (iter);
g_slist_free_full (normalized_terms, (GDestroyNotify) g_free);
return sort_and_prepare_results (results);
}

View File

@@ -16,4 +16,7 @@ GjsContext *_shell_global_get_gjs_context (ShellGlobal *global);
gboolean _shell_global_check_xdnd_event (ShellGlobal *global,
XEvent *xev);
void _shell_global_set_session_type (ShellGlobal *global,
ShellSessionType session_type);
#endif /* __SHELL_GLOBAL_PRIVATE_H__ */

View File

@@ -59,7 +59,7 @@ struct _ShellGlobal {
MetaScreen *meta_screen;
GdkScreen *gdk_screen;
char *session_mode;
ShellSessionType session_type;
/* We use this window to get a notification from GTK+ when
* a widget in our process does a GTK+ grab. See
@@ -97,7 +97,7 @@ struct _ShellGlobal {
enum {
PROP_0,
PROP_SESSION_MODE,
PROP_SESSION_TYPE,
PROP_OVERLAY_GROUP,
PROP_SCREEN,
PROP_GDK_SCREEN,
@@ -143,9 +143,8 @@ shell_global_set_property(GObject *object,
case PROP_STAGE_INPUT_MODE:
shell_global_set_stage_input_mode (global, g_value_get_enum (value));
break;
case PROP_SESSION_MODE:
g_clear_pointer (&global->session_mode, g_free);
global->session_mode = g_ascii_strdown (g_value_get_string (value), -1);
case PROP_SESSION_TYPE:
global->session_type = g_value_get_enum (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -163,8 +162,8 @@ shell_global_get_property(GObject *object,
switch (prop_id)
{
case PROP_SESSION_MODE:
g_value_set_string (value, shell_global_get_session_mode (global));
case PROP_SESSION_TYPE:
g_value_set_enum (value, shell_global_get_session_type (global));
break;
case PROP_OVERLAY_GROUP:
g_value_set_object (value, meta_get_overlay_group_for_screen (global->meta_screen));
@@ -344,12 +343,13 @@ shell_global_class_init (ShellGlobalClass *klass)
G_TYPE_STRING);
g_object_class_install_property (gobject_class,
PROP_SESSION_MODE,
g_param_spec_string ("session-mode",
"Session Mode",
"The session mode to use",
"user",
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
PROP_SESSION_TYPE,
g_param_spec_enum ("session-type",
"Session Type",
"The type of session",
SHELL_TYPE_SESSION_TYPE,
SHELL_SESSION_USER,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (gobject_class,
PROP_OVERLAY_GROUP,
g_param_spec_object ("overlay-group",
@@ -805,9 +805,90 @@ global_stage_after_paint (ClutterStage *stage,
"clutter.stagePaintDone");
}
static void
update_font_options (GtkSettings *settings,
ClutterStage *stage)
{
StThemeContext *context;
ClutterBackend *backend;
gint dpi;
gint hinting;
gchar *hint_style_str;
cairo_hint_style_t hint_style = CAIRO_HINT_STYLE_NONE;
gint antialias;
cairo_antialias_t antialias_mode = CAIRO_ANTIALIAS_NONE;
cairo_font_options_t *options;
g_object_get (settings,
"gtk-xft-dpi", &dpi,
"gtk-xft-antialias", &antialias,
"gtk-xft-hinting", &hinting,
"gtk-xft-hintstyle", &hint_style_str,
NULL);
context = st_theme_context_get_for_stage (stage);
if (dpi != -1)
/* GTK stores resolution as 1024 * dots/inch */
st_theme_context_set_resolution (context, dpi / 1024);
else
st_theme_context_set_default_resolution (context);
/* Clutter (as of 0.9) passes comprehensively wrong font options
* override whatever set_font_flags() did above.
*
* http://bugzilla.openedhand.com/show_bug.cgi?id=1456
*/
backend = clutter_get_default_backend ();
options = cairo_font_options_create ();
cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON);
if (hinting >= 0 && !hinting)
{
hint_style = CAIRO_HINT_STYLE_NONE;
}
else if (hint_style_str)
{
if (strcmp (hint_style_str, "hintnone") == 0)
hint_style = CAIRO_HINT_STYLE_NONE;
else if (strcmp (hint_style_str, "hintslight") == 0)
hint_style = CAIRO_HINT_STYLE_SLIGHT;
else if (strcmp (hint_style_str, "hintmedium") == 0)
hint_style = CAIRO_HINT_STYLE_MEDIUM;
else if (strcmp (hint_style_str, "hintfull") == 0)
hint_style = CAIRO_HINT_STYLE_FULL;
}
g_free (hint_style_str);
cairo_font_options_set_hint_style (options, hint_style);
/* We don't want to turn on subpixel anti-aliasing; since Clutter
* doesn't currently have the code to support ARGB masks,
* generating them then squashing them back to A8 is pointless.
*/
antialias_mode = (antialias < 0 || antialias) ? CAIRO_ANTIALIAS_GRAY
: CAIRO_ANTIALIAS_NONE;
cairo_font_options_set_antialias (options, antialias_mode);
clutter_backend_set_font_options (backend, options);
cairo_font_options_destroy (options);
}
static void
settings_notify_cb (GtkSettings *settings,
GParamSpec *pspec,
gpointer data)
{
update_font_options (settings, CLUTTER_STAGE (data));
}
static void
shell_fonts_init (ClutterStage *stage)
{
GtkSettings *settings;
CoglPangoFontMap *fontmap;
/* Disable text mipmapping; it causes problems on pre-GEM Intel
@@ -817,6 +898,19 @@ shell_fonts_init (ClutterStage *stage)
*/
fontmap = COGL_PANGO_FONT_MAP (clutter_get_font_map ());
cogl_pango_font_map_set_use_mipmapping (fontmap, FALSE);
settings = gtk_settings_get_default ();
g_object_connect (settings,
"signal::notify::gtk-xft-dpi",
G_CALLBACK (settings_notify_cb), stage,
"signal::notify::gtk-xft-antialias",
G_CALLBACK (settings_notify_cb), stage,
"signal::notify::gtk-xft-hinting",
G_CALLBACK (settings_notify_cb), stage,
"signal::notify::gtk-xft-hintstyle",
G_CALLBACK (settings_notify_cb), stage,
NULL);
update_font_options (settings, stage);
}
/* This is an IBus workaround. The flow of events with IBus is that every time
@@ -1776,10 +1870,31 @@ shell_global_launch_calendar_server (ShellGlobal *global)
g_free (calendar_server_exe);
}
const char *
shell_global_get_session_mode (ShellGlobal *global)
/**
* shell_global_get_session_type:
* @global: The #ShellGlobal.
*
* Gets the type of session gnome-shell provides.
*
* The type determines what UI elements are displayed,
* what keybindings work, and generally how the shell
* behaves.
*
* A session type of #SHELL_SESSION_USER means gnome-shell
* will enable the activities overview, status menu, run dialog,
* etc. This is the default.
*
* A session type of #SHELL_SESSION_GDM means gnome-shell
* will enable a login dialog and run in a more confined
* way. This type is suitable for the display manager.
*
* Returns: the type of session gnome-shell is providing.
*/
ShellSessionType
shell_global_get_session_type (ShellGlobal *global)
{
g_return_val_if_fail (SHELL_IS_GLOBAL (global), "user");
g_return_val_if_fail (SHELL_IS_GLOBAL (global),
SHELL_SESSION_USER);
return global->session_mode;
return global->session_type;
}

View File

@@ -141,7 +141,12 @@ void shell_global_reexec_self (ShellGlobal *global);
void shell_global_launch_calendar_server (ShellGlobal *global);
const char * shell_global_get_session_mode (ShellGlobal *global);
typedef enum {
SHELL_SESSION_USER,
SHELL_SESSION_GDM
} ShellSessionType;
ShellSessionType shell_global_get_session_type (ShellGlobal *global);
G_END_DECLS

View File

@@ -79,3 +79,15 @@ shell_js_add_extension_importer (const char *target_object_script,
JS_EndRequest (context);
return ret;
}
/**
* shell_js_format_int_alternative_output:
* @intval:
*
* Returns: (transfer full):
*/
gchar *
shell_js_format_int_alternative_output (gint intval)
{
return g_strdup_printf ("%Id", intval);
}

View File

@@ -11,6 +11,8 @@ gboolean shell_js_add_extension_importer (const char *target_object_script,
const char *directory,
GError **error);
gchar *shell_js_format_int_alternative_output (gint intval);
G_BEGIN_DECLS
#endif /* __SHELL_JS_H__ */

View File

@@ -230,7 +230,6 @@ auth_request_free (AuthRequest *request)
g_free (request->message);
g_free (request->icon_name);
g_object_unref (request->details);
g_free (request->cookie);
g_list_foreach (request->identities, (GFunc) g_object_unref, NULL);
g_list_free (request->identities);
g_object_unref (request->simple);

View File

@@ -330,6 +330,34 @@ shell_tp_client_grab_contact_list_changed (ShellTpClient *self,
/* Telepathy utility functions */
/**
* shell_get_contact_events:
* @log_manager: A #TplLogManager
* @account: A #TpAccount
* @entity: A #TplEntity
* @num_events: The number of events to retrieve
* @callback: (scope async): User callback to run when the contact is ready
*
* Wrap tpl_log_manager_get_filtered_events_async because gjs cannot support
* multiple callbacks in the same function call.
*/
void
shell_get_contact_events (TplLogManager *log_manager,
TpAccount *account,
TplEntity *entity,
guint num_events,
GAsyncReadyCallback callback)
{
tpl_log_manager_get_filtered_events_async (log_manager,
account,
entity,
TPL_EVENT_MASK_TEXT,
num_events,
NULL, NULL,
callback, NULL);
}
/* gjs doesn't allow us to craft a GError so we need a C wrapper */
void
shell_decline_dispatch_op (TpAddDispatchOperationContext *context,
@@ -341,9 +369,3 @@ shell_decline_dispatch_op (TpAddDispatchOperationContext *context,
tp_add_dispatch_operation_context_fail (context, error);
g_error_free (error);
}
/* gjs doesn't cope with tp_proxy_get_invalidated() returning a GError */
gboolean shell_is_channel_invalidated (TpChannel *channel)
{
return tp_proxy_get_invalidated (channel) != NULL;
}

View File

@@ -7,6 +7,7 @@
#include <glib-object.h>
#include <telepathy-glib/telepathy-glib.h>
#include <telepathy-logger/telepathy-logger.h>
G_BEGIN_DECLS
@@ -102,10 +103,14 @@ void shell_tp_client_grab_contact_list_changed (ShellTpClient *self,
/* Telepathy utility functions */
void shell_get_contact_events (TplLogManager *log_manager,
TpAccount *account,
TplEntity *entity,
guint num_events,
GAsyncReadyCallback callback);
void shell_decline_dispatch_op (TpAddDispatchOperationContext *context,
const gchar *message);
gboolean shell_is_channel_invalidated (TpChannel *channel);
G_END_DECLS
#endif /* __SHELL_TP_CLIENT_H__ */

View File

@@ -96,7 +96,6 @@ shell_util_get_file_display_for_common_files (GFile *file)
* nautilus */
return g_strdup (_("Home"));
}
g_object_unref (compare);
compare = g_file_new_for_path ("/");
if (g_file_equal (file, compare))
@@ -593,6 +592,29 @@ shell_util_get_week_start ()
return week_start;
}
/**
* shell_write_soup_message_to_stream:
* @stream: a #GOutputStream
* @message: a #SoupMessage
* @error: location to store GError
*
* Write a string to a GOutputStream as binary data. This is a
* workaround for the lack of proper binary strings in GJS.
*/
void
shell_write_soup_message_to_stream (GOutputStream *stream,
SoupMessage *message,
GError **error)
{
SoupMessageBody *body;
body = message->response_body;
g_output_stream_write_all (stream,
body->data, body->length,
NULL, NULL, error);
}
/**
* shell_write_string_to_stream:
* @stream: a #GOutputStream

View File

@@ -25,6 +25,10 @@ char *shell_util_normalize_and_casefold (const char *str);
char *shell_util_format_date (const char *format,
gint64 time_ms);
void shell_write_soup_message_to_stream (GOutputStream *stream,
SoupMessage *message,
GError **error);
gboolean shell_write_string_to_stream (GOutputStream *stream,
const char *str,
GError **error);

View File

@@ -357,10 +357,7 @@ update_focus_app (ShellWindowTracker *self)
new_focus_app = new_focus_win ? shell_window_tracker_get_window_app (self, new_focus_win) : NULL;
if (new_focus_app)
{
shell_app_update_window_actions (new_focus_app, new_focus_win);
shell_app_update_app_menu (new_focus_app, new_focus_win);
}
shell_app_update_window_actions (new_focus_app, new_focus_win);
set_focus_app (self, new_focus_app);
}

View File

@@ -490,7 +490,7 @@ st_label_accessible_get_name (AtkObject *obj)
actor = CLUTTER_ACTOR (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (obj)));
if (actor == NULL || st_widget_has_style_class_name (ST_WIDGET (actor), "hidden"))
if (actor == NULL) /* State is defunct */
name = NULL;
else
name = st_label_get_text (ST_LABEL (actor));

View File

@@ -933,7 +933,6 @@ load_gicon_with_colors (StTextureCache *cache,
if (ensure_request (cache, key, policy, &request, texture))
{
/* If there's an outstanding request, we've just added ourselves to it */
gtk_icon_info_free (info);
g_free (key);
}
else

View File

@@ -28,6 +28,7 @@
struct _StThemeContext {
GObject parent;
double resolution;
PangoFontDescription *font;
StThemeNode *root_node;
StTheme *theme;
@@ -37,6 +38,7 @@ struct _StThemeContextClass {
GObjectClass parent_class;
};
#define DEFAULT_RESOLUTION 96.
#define DEFAULT_FONT "sans-serif 10"
enum
@@ -52,7 +54,6 @@ G_DEFINE_TYPE (StThemeContext, st_theme_context, G_TYPE_OBJECT)
static void on_icon_theme_changed (StTextureCache *cache,
StThemeContext *context);
static void st_theme_context_changed (StThemeContext *context);
static void
st_theme_context_finalize (GObject *object)
@@ -62,9 +63,6 @@ st_theme_context_finalize (GObject *object)
g_signal_handlers_disconnect_by_func (st_texture_cache_get_default (),
(gpointer) on_icon_theme_changed,
context);
g_signal_handlers_disconnect_by_func (clutter_get_default_backend (),
(gpointer) st_theme_context_changed,
context);
if (context->root_node)
g_object_unref (context->root_node);
@@ -95,16 +93,13 @@ st_theme_context_class_init (StThemeContextClass *klass)
static void
st_theme_context_init (StThemeContext *context)
{
context->resolution = DEFAULT_RESOLUTION;
context->font = pango_font_description_from_string (DEFAULT_FONT);
g_signal_connect (st_texture_cache_get_default (),
"icon-theme-changed",
G_CALLBACK (on_icon_theme_changed),
context);
g_signal_connect_swapped (clutter_get_default_backend (),
"resolution-changed",
G_CALLBACK (st_theme_context_changed),
context);
}
/**
@@ -237,6 +232,66 @@ st_theme_context_get_theme (StThemeContext *context)
return context->theme;
}
/**
* st_theme_context_set_resolution:
* @context: a #StThemeContext
* @resolution: resolution of the context (number of pixels in an "inch")
*
* Sets the resolution of the theme context. This is the scale factor
* used to convert between points and the length units pt, in, and cm.
* This does not necessarily need to correspond to the actual number
* resolution of the device. A value of 72. means that points and
* pixels are identical. The default value is 96.
*/
void
st_theme_context_set_resolution (StThemeContext *context,
double resolution)
{
g_return_if_fail (ST_IS_THEME_CONTEXT (context));
if (resolution == context->resolution)
return;
context->resolution = resolution;
st_theme_context_changed (context);
}
/**
* st_theme_context_set_default_resolution:
* @context: a #StThemeContext
*
* Sets the resolution of the theme context to the default value of 96.
* See st_theme_context_set_resolution().
*/
void
st_theme_context_set_default_resolution (StThemeContext *context)
{
g_return_if_fail (ST_IS_THEME_CONTEXT (context));
if (context->resolution == DEFAULT_RESOLUTION)
return;
context->resolution = DEFAULT_RESOLUTION;
st_theme_context_changed (context);
}
/**
* st_theme_context_get_resolution:
* @context: a #StThemeContext
*
* Gets the current resolution of the theme context.
* See st_theme_context_set_resolution().
*
* Return value: the resolution (in dots-per-"inch")
*/
double
st_theme_context_get_resolution (StThemeContext *context)
{
g_return_val_if_fail (ST_IS_THEME_CONTEXT (context), DEFAULT_RESOLUTION);
return context->resolution;
}
/**
* st_theme_context_set_font:
* @context: a #StThemeContext

View File

@@ -56,6 +56,10 @@ void st_theme_context_set_theme (StThemeContext
StTheme *theme);
StTheme * st_theme_context_get_theme (StThemeContext *context);
void st_theme_context_set_resolution (StThemeContext *context,
gdouble resolution);
void st_theme_context_set_default_resolution (StThemeContext *context);
double st_theme_context_get_resolution (StThemeContext *context);
void st_theme_context_set_font (StThemeContext *context,
const PangoFontDescription *font);
const PangoFontDescription *st_theme_context_get_font (StThemeContext *context);

View File

@@ -67,6 +67,18 @@ st_theme_node_dispose (GObject *gobject)
{
StThemeNode *node = ST_THEME_NODE (gobject);
if (node->context)
{
g_object_unref (node->context);
node->context = NULL;
}
if (node->theme)
{
g_object_unref (node->theme);
node->theme = NULL;
}
if (node->parent_node)
{
g_object_unref (node->parent_node);
@@ -85,8 +97,6 @@ st_theme_node_dispose (GObject *gobject)
node->icon_colors = NULL;
}
g_clear_object (&node->theme);
G_OBJECT_CLASS (st_theme_node_parent_class)->dispose (gobject);
}
@@ -184,7 +194,7 @@ st_theme_node_new (StThemeContext *context,
node = g_object_new (ST_TYPE_THEME_NODE, NULL);
node->context = context;
node->context = g_object_ref (context);
if (parent_node != NULL)
node->parent_node = g_object_ref (parent_node);
else
@@ -194,10 +204,7 @@ st_theme_node_new (StThemeContext *context,
theme = parent_node->theme;
if (theme != NULL)
node->theme = theme;
if (node->theme != NULL)
g_object_ref (node->theme);
node->theme = g_object_ref (theme);
node->element_type = element_type;
node->element_id = g_strdup (element_id);
@@ -813,7 +820,7 @@ get_length_from_term (StThemeNode *node,
break;
case POINTS:
{
double resolution = clutter_backend_get_resolution (clutter_get_default_backend ());
double resolution = st_theme_context_get_resolution (node->context);
*length = num->val * multiplier * (resolution / 72.);
}
break;
@@ -835,7 +842,7 @@ get_length_from_term (StThemeNode *node,
}
else
{
double resolution = clutter_backend_get_resolution (clutter_get_default_backend ());
double resolution = st_theme_context_get_resolution (node->context);
*length = num->val * multiplier * (resolution / 72.) * font_size;
}
}
@@ -2132,7 +2139,7 @@ font_size_from_term (StThemeNode *node,
{
if (term->type == TERM_IDENT)
{
double resolution = clutter_backend_get_resolution (clutter_get_default_backend ());
double resolution = st_theme_context_get_resolution (node->context);
/* We work in integers to avoid double comparisons when converting back
* from a size in pixels to a logical size.
*/
@@ -2339,7 +2346,7 @@ st_theme_node_get_font (StThemeNode *node)
parent_size = pango_font_description_get_size (node->font_desc);
if (!pango_font_description_get_size_is_absolute (node->font_desc))
{
double resolution = clutter_backend_get_resolution (clutter_get_default_backend ());
double resolution = st_theme_context_get_resolution (node->context);
parent_size *= (resolution / 72.);
}

View File

@@ -317,7 +317,6 @@ st_widget_finalize (GObject *gobject)
g_free (priv->pseudo_class);
g_object_unref (priv->local_state_set);
g_free (priv->accessible_name);
g_free (priv->inline_style);
G_OBJECT_CLASS (st_widget_parent_class)->finalize (gobject);
}
@@ -763,17 +762,18 @@ st_widget_get_paint_volume (ClutterActor *self,
static GList *
st_widget_real_get_focus_chain (StWidget *widget)
{
ClutterActorIter iter;
ClutterActor *child;
GList *children;
GList *focus_chain = NULL;
clutter_actor_iter_init (&iter, CLUTTER_ACTOR (widget));
while (clutter_actor_iter_next (&iter, &child))
for (children = clutter_actor_get_children (CLUTTER_ACTOR (widget));
children;
children = children->next)
{
ClutterActor *child = children->data;
if (CLUTTER_ACTOR_IS_VISIBLE (child))
focus_chain = g_list_prepend (focus_chain, child);
}
return g_list_reverse (focus_chain);
}
@@ -1011,7 +1011,7 @@ st_widget_class_init (StWidgetClass *klass)
*/
void
st_widget_set_theme (StWidget *actor,
StTheme *theme)
StTheme *theme)
{
StWidgetPrivate *priv;
@@ -1019,11 +1019,11 @@ st_widget_set_theme (StWidget *actor,
priv = actor->priv;
if (theme != priv->theme)
if (theme !=priv->theme)
{
if (priv->theme)
g_object_unref (priv->theme);
priv->theme = g_object_ref (theme);
priv->theme = g_object_ref (priv->theme);
st_widget_style_changed (actor);
@@ -2503,8 +2503,6 @@ st_widget_accessible_dispose (GObject *gobject)
g_object_unref (self->priv->current_label);
self->priv->current_label = NULL;
}
G_OBJECT_CLASS (st_widget_accessible_parent_class)->dispose (gobject);
}
static void

View File

@@ -425,9 +425,6 @@ main (int argc, char **argv)
{
StTheme *theme;
StThemeContext *context;
PangoFontDescription *font_desc;
gtk_init (&argc, &argv);
if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
return 1;
@@ -438,10 +435,9 @@ main (int argc, char **argv)
stage = clutter_stage_new ();
context = st_theme_context_get_for_stage (CLUTTER_STAGE (stage));
st_theme_context_set_theme (context, theme);
font_desc = pango_font_description_from_string ("sans-serif 12");
st_theme_context_set_font (context, font_desc);
pango_font_description_free (font_desc);
st_theme_context_set_resolution (context, 96.);
st_theme_context_set_font (context,
pango_font_description_from_string ("sans-serif 12"));
root = st_theme_context_get_root_node (context);
group1 = st_theme_node_new (context, root, NULL,
@@ -474,17 +470,5 @@ main (int argc, char **argv)
test_pseudo_class ();
test_inline_style ();
g_object_unref (cairo_texture);
g_object_unref (group1);
g_object_unref (group2);
g_object_unref (group3);
g_object_unref (text1);
g_object_unref (text2);
g_object_unref (text3);
g_object_unref (text4);
g_object_unref (theme);
clutter_actor_destroy (stage);
return fail ? 1 : 0;
}

View File

@@ -6,97 +6,99 @@ const St = imports.gi.St;
const UI = imports.testcommon.ui;
function test() {
let stage = new Clutter.Stage({ user_resizable: true, width: 1024, height: 768 });
UI.init(stage);
UI.init();
let stage = Clutter.Stage.get_default();
stage.user_resizable = true;
stage.width = 1024;
stage.height = 768;
let vbox = new St.BoxLayout({ style: 'background: #ffee88;' });
vbox.add_constraint(new Clutter.BindConstraint({ source: stage,
coordinate: Clutter.BindCoordinate.SIZE }));
stage.add_actor(vbox);
let vbox = new St.BoxLayout({ style: 'background: #ffee88;' });
vbox.add_constraint(new Clutter.BindConstraint({ source: stage,
coordinate: Clutter.BindCoordinate.SIZE }));
stage.add_actor(vbox);
let scroll = new St.ScrollView();
vbox.add(scroll, { expand: true });
let scroll = new St.ScrollView();
vbox.add(scroll, { expand: true });
let vbox = new St.BoxLayout({ vertical: true,
style: 'padding: 10px;'
+ 'spacing: 20px;' });
scroll.add_actor(vbox);
let vbox = new St.BoxLayout({ vertical: true,
style: 'padding: 10px;'
+ 'spacing: 20px;' });
scroll.add_actor(vbox);
let tbox = null;
let tbox = null;
function addTestCase(image, size, backgroundSize, useCairo) {
// Using a border in CSS forces cairo rendering.
// To get a border using cogl, we paint a border using
// paint signal hacks.
function addTestCase(image, size, backgroundSize, useCairo) {
// Using a border in CSS forces cairo rendering.
// To get a border using cogl, we paint a border using
// paint signal hacks.
let obin = new St.Bin();
if (useCairo)
obin.style = 'border: 3px solid green;';
else
obin.connect_after('paint', function(actor) {
Cogl.set_source_color4f(0, 1, 0, 1);
let obin = new St.Bin();
if (useCairo)
obin.style = 'border: 3px solid green;';
else
obin.connect_after('paint', function(actor) {
Cogl.set_source_color4f(0, 1, 0, 1);
let geom = actor.get_allocation_geometry();
let width = 3;
let geom = actor.get_allocation_geometry();
let width = 3;
// clockwise order
Cogl.rectangle(0, 0, geom.width, width);
Cogl.rectangle(geom.width - width, width,
geom.width, geom.height);
Cogl.rectangle(0, geom.height,
geom.width - width, geom.height - width);
Cogl.rectangle(0, geom.height - width,
width, width);
});
tbox.add(obin);
// clockwise order
Cogl.rectangle(0, 0, geom.width, width);
Cogl.rectangle(geom.width - width, width,
geom.width, geom.height);
Cogl.rectangle(0, geom.height,
geom.width - width, geom.height - width);
Cogl.rectangle(0, geom.height - width,
width, width);
});
tbox.add(obin);
let [width, height] = size;
let bin = new St.Bin({ style_class: 'background-image-' + image,
width: width,
height: height,
style: 'border: 1px solid transparent;'
+ 'background-size: ' + backgroundSize + ';',
x_fill: true,
y_fill: true
});
obin.set_child(bin);
let [width, height] = size;
let bin = new St.Bin({ style_class: 'background-image-' + image,
width: width,
height: height,
style: 'border: 1px solid transparent;'
+ 'background-size: ' + backgroundSize + ';',
x_fill: true,
y_fill: true
});
obin.set_child(bin);
bin.set_child(new St.Label({ text: backgroundSize + (useCairo ? ' (cairo)' : ' (cogl)'),
style: 'font-size: 15px;'
+ 'text-align: center;'
}));
}
function addTestLine(image, size, useCairo) {
const backgroundSizes = ["auto", "contain", "cover", "200px 200px", "100px 100px", "100px 200px"];
let [width, height] = size;
vbox.add(new St.Label({ text: image + '.svg / ' + width + '×' + height,
style: 'font-size: 15px;'
+ 'text-align: center;'
}));
tbox = new St.BoxLayout({ style: 'spacing: 20px;' });
vbox.add(tbox);
for each (let s in backgroundSizes)
addTestCase(image, size, s, false);
for each (let s in backgroundSizes)
addTestCase(image, size, s, true);
}
function addTestImage(image) {
const containerSizes = [[100, 100], [200, 200], [250, 250], [100, 250], [250, 100]];
for each (let size in containerSizes)
addTestLine(image, size);
}
addTestImage ('200-200');
addTestImage ('200-100');
addTestImage ('100-200');
UI.main(stage);
bin.set_child(new St.Label({ text: backgroundSize + (useCairo ? ' (cairo)' : ' (cogl)'),
style: 'font-size: 15px;'
+ 'text-align: center;'
}));
}
test();
function addTestLine(image, size, useCairo) {
const backgroundSizes = ["auto", "contain", "cover", "200px 200px", "100px 100px", "100px 200px"];
let [width, height] = size;
vbox.add(new St.Label({ text: image + '.svg / ' + width + '×' + height,
style: 'font-size: 15px;'
+ 'text-align: center;'
}));
tbox = new St.BoxLayout({ style: 'spacing: 20px;' });
vbox.add(tbox);
for each (let s in backgroundSizes)
addTestCase(image, size, s, false);
for each (let s in backgroundSizes)
addTestCase(image, size, s, true);
}
function addTestImage(image) {
const containerSizes = [[100, 100], [200, 200], [250, 250], [100, 250], [250, 100]];
for each (let size in containerSizes)
addTestLine(image, size);
}
addTestImage ('200-200');
addTestImage ('200-100');
addTestImage ('100-200');
stage.show();
Clutter.main();
stage.destroy();

View File

@@ -5,58 +5,59 @@ const St = imports.gi.St;
const UI = imports.testcommon.ui;
function test() {
let stage = new Clutter.Stage({ width: 640, height: 480 });
UI.init(stage);
UI.init();
let stage = Clutter.Stage.get_default();
stage.width = 640;
stage.height = 480;
let vbox = new St.BoxLayout({ width: stage.width,
height: stage.height,
style: 'background: #ffee88;' });
stage.add_actor(vbox);
let vbox = new St.BoxLayout({ width: stage.width,
height: stage.height,
style: 'background: #ffee88;' });
stage.add_actor(vbox);
let scroll = new St.ScrollView();
vbox.add(scroll, { expand: true });
let scroll = new St.ScrollView();
vbox.add(scroll, { expand: true });
let box = new St.BoxLayout({ vertical: true,
style: 'padding: 10px;'
+ 'spacing: 20px;' });
scroll.add_actor(box);
let box = new St.BoxLayout({ vertical: true,
style: 'padding: 10px;'
+ 'spacing: 20px;' });
scroll.add_actor(box);
function addTestCase(radii, useGradient) {
let background;
if (useGradient)
background = 'background-gradient-direction: vertical;'
+ 'background-gradient-start: white;'
+ 'background-gradient-end: gray;';
else
background = 'background: white;';
function addTestCase(radii, useGradient) {
let background;
if (useGradient)
background = 'background-gradient-direction: vertical;'
+ 'background-gradient-start: white;'
+ 'background-gradient-end: gray;';
else
background = 'background: white;';
box.add(new St.Label({ text: "border-radius: " + radii + ";",
style: 'border: 1px solid black; '
+ 'border-radius: ' + radii + ';'
+ 'padding: 5px;' + background }),
{ x_fill: false });
}
// uniform backgrounds
addTestCase(" 0px 5px 10px 15px", false);
addTestCase(" 5px 10px 15px 0px", false);
addTestCase("10px 15px 0px 5px", false);
addTestCase("15px 0px 5px 10px", false);
// gradient backgrounds
addTestCase(" 0px 5px 10px 15px", true);
addTestCase(" 5px 10px 15px 0px", true);
addTestCase("10px 15px 0px 5px", true);
addTestCase("15px 0px 5px 10px", true);
// border-radius reduction
// these should all take the cairo fallback,
// so don't bother testing w/ or w/out gradients.
addTestCase("200px 200px 200px 200px", false);
addTestCase("200px 200px 0px 200px", false);
addTestCase("999px 0px 999px 0px", false);
UI.main(stage);
box.add(new St.Label({ text: "border-radius: " + radii + ";",
style: 'border: 1px solid black; '
+ 'border-radius: ' + radii + ';'
+ 'padding: 5px;' + background }),
{ x_fill: false });
}
test();
// uniform backgrounds
addTestCase(" 0px 5px 10px 15px", false);
addTestCase(" 5px 10px 15px 0px", false);
addTestCase("10px 15px 0px 5px", false);
addTestCase("15px 0px 5px 10px", false);
// gradient backgrounds
addTestCase(" 0px 5px 10px 15px", true);
addTestCase(" 5px 10px 15px 0px", true);
addTestCase("10px 15px 0px 5px", true);
addTestCase("15px 0px 5px 10px", true);
// border-radius reduction
// these should all take the cairo fallback,
// so don't bother testing w/ or w/out gradients.
addTestCase("200px 200px 200px 200px", false);
addTestCase("200px 200px 0px 200px", false);
addTestCase("999px 0px 999px 0px", false);
stage.show();
Clutter.main();
stage.destroy();

View File

@@ -5,55 +5,56 @@ const St = imports.gi.St;
const UI = imports.testcommon.ui;
function test() {
let stage = new Clutter.Stage({ width: 640, height: 480 });
UI.init(stage);
UI.init();
let stage = Clutter.Stage.get_default();
stage.width = 640;
stage.height = 480;
let vbox = new St.BoxLayout({ width: stage.width,
height: stage.height,
style: 'padding: 10px; background: #ffee88;'
});
stage.add_actor(vbox);
let vbox = new St.BoxLayout({ width: stage.width,
height: stage.height,
style: 'padding: 10px; background: #ffee88;'
});
stage.add_actor(vbox);
let scroll = new St.ScrollView();
vbox.add(scroll, { expand: true });
let scroll = new St.ScrollView();
vbox.add(scroll, { expand: true });
let box = new St.BoxLayout({ vertical: true,
style: 'spacing: 20px;' });
scroll.add_actor(box);
let box = new St.BoxLayout({ vertical: true,
style: 'spacing: 20px;' });
scroll.add_actor(box);
function addTestCase(borders, useGradient) {
let background;
if (useGradient)
background = 'background-gradient-direction: vertical;'
+ 'background-gradient-start: white;'
+ 'background-gradient-end: gray;';
else
background = 'background: white;';
function addTestCase(borders, useGradient) {
let background;
if (useGradient)
background = 'background-gradient-direction: vertical;'
+ 'background-gradient-start: white;'
+ 'background-gradient-end: gray;';
else
background = 'background: white;';
let border_style = "border-top: " + borders[St.Side.TOP] + " solid black;\n" +
"border-right: " + borders[St.Side.RIGHT] + " solid black;\n" +
"border-bottom: " + borders[St.Side.BOTTOM] + " solid black;\n" +
"border-left: " + borders[St.Side.LEFT] + " solid black;";
box.add(new St.Label({ text: border_style,
style: border_style
+ 'border-radius: 0px 5px 15px 25px;'
+ 'padding: 5px;' + background }),
{ x_fill: false });
}
// uniform backgrounds
addTestCase([" 0px", " 5px", "10px", "15px"], false);
addTestCase([" 5px", "10px", "15px", " 0px"], false);
addTestCase(["10px", "15px", " 0px", " 5px"], false);
addTestCase(["15px", " 0px", " 5px", "10px"], false);
// gradient backgrounds
addTestCase([" 0px", " 5px", "10px", "15px"], true);
addTestCase([" 5px", "10px", "15px", " 0px"], true);
addTestCase(["10px", "15px", " 0px", " 5px"], true);
addTestCase(["15px", " 0px", " 5px", "10px"], true);
UI.main(stage);
let border_style = "border-top: " + borders[St.Side.TOP] + " solid black;\n" +
"border-right: " + borders[St.Side.RIGHT] + " solid black;\n" +
"border-bottom: " + borders[St.Side.BOTTOM] + " solid black;\n" +
"border-left: " + borders[St.Side.LEFT] + " solid black;";
box.add(new St.Label({ text: border_style,
style: border_style
+ 'border-radius: 0px 5px 15px 25px;'
+ 'padding: 5px;' + background }),
{ x_fill: false });
}
test();
// uniform backgrounds
addTestCase([" 0px", " 5px", "10px", "15px"], false);
addTestCase([" 5px", "10px", "15px", " 0px"], false);
addTestCase(["10px", "15px", " 0px", " 5px"], false);
addTestCase(["15px", " 0px", " 5px", "10px"], false);
// gradient backgrounds
addTestCase([" 0px", " 5px", "10px", "15px"], true);
addTestCase([" 5px", "10px", "15px", " 0px"], true);
addTestCase(["10px", "15px", " 0px", " 5px"], true);
addTestCase(["15px", " 0px", " 5px", "10px"], true);
stage.show();
Clutter.main();
stage.destroy();

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