Compare commits
245 Commits
Author | SHA1 | Date | |
---|---|---|---|
b1051365fa | |||
a935cd0c5d | |||
62ce90795f | |||
6bcad45392 | |||
8747c0c58d | |||
b91d9c2867 | |||
140935bca3 | |||
67c7abaac3 | |||
11bd7dc7de | |||
406f65cb23 | |||
2344706462 | |||
fcc32fca7e | |||
7f587fd4da | |||
f47c0601ce | |||
3d638c692e | |||
e6ef3ea24f | |||
8dd880d0c8 | |||
393a3a079f | |||
ceb035e9b3 | |||
e38570437e | |||
f0203d1f19 | |||
e0c5a61be5 | |||
526a16298c | |||
443f02cd92 | |||
b682c8e052 | |||
d07c8dcd9c | |||
9bcce4f271 | |||
eab497a814 | |||
a8c8df6ee8 | |||
b3549f421d | |||
ae0accb5a4 | |||
d367566db9 | |||
35812208e7 | |||
afaff724f6 | |||
ec8dfb0cad | |||
7798da8dff | |||
89a49ce72e | |||
655dce6a4b | |||
2cf403a3e9 | |||
4351ad2414 | |||
fd2944fa8d | |||
f4626cf8ad | |||
65bf0d20e7 | |||
2f496de98a | |||
644b8304ab | |||
5c4570fcaa | |||
ac0bd3b116 | |||
7ad881d0ec | |||
9a25224890 | |||
3f6f597093 | |||
9ab22fe551 | |||
9794e71a86 | |||
8cb3884fae | |||
098bd4509b | |||
4dc5bac72f | |||
b150869b51 | |||
daf8be9f78 | |||
d14cf1e80b | |||
605d34008e | |||
d3135414cc | |||
b3f29b46cf | |||
4545719e18 | |||
cc60afa31a | |||
85743ede7e | |||
8e231cb2ec | |||
72405cd43f | |||
a9ad9d5e6d | |||
2d79c7333f | |||
7a79cfd76b | |||
6600d6b6d9 | |||
8ca25aed8b | |||
1db353a0fc | |||
2d384eb546 | |||
5d1de33026 | |||
f2ba49fa35 | |||
772ae1cae4 | |||
1d267f2642 | |||
cba299160a | |||
ea33193e73 | |||
ef69c228fd | |||
7ba0f07732 | |||
07696086a2 | |||
426581eb1e | |||
fe88811a40 | |||
30aaa6e26c | |||
4bd071bf3c | |||
309ac65447 | |||
fe246470ce | |||
660cfe707c | |||
a0d7d7bc4b | |||
758764ea75 | |||
6935c62a61 | |||
a34d4d9124 | |||
c49bb5aa03 | |||
427750d6af | |||
b9ad5f8727 | |||
c776826425 | |||
a8ede18db5 | |||
9aa84ec54a | |||
91a6520c3b | |||
45fde4888e | |||
484ef5f2f6 | |||
cdd354739a | |||
420f544bf3 | |||
15b33189ae | |||
685a9f36f6 | |||
3b57812e29 | |||
d583c48992 | |||
0e636ea67e | |||
c9ce1bd30f | |||
f2cbc3192c | |||
56d76791f1 | |||
df2cab877f | |||
27ad8305e5 | |||
c97b4dd48e | |||
59ecd610b1 | |||
42c1285ead | |||
622c1c9236 | |||
2d88508f9d | |||
86efba4ecc | |||
90fae00aa6 | |||
d497ed4040 | |||
c4a21ae5ad | |||
5e5798bee9 | |||
d793077b91 | |||
3e826f9249 | |||
2adfb60dc4 | |||
f7212cf80c | |||
69556643bb | |||
f0ebc84840 | |||
490eb59ea3 | |||
16a9391726 | |||
adf8ba67d2 | |||
c37b222cbf | |||
72f8f2beb1 | |||
16bb9c17f5 | |||
ca44977d92 | |||
bcf294475f | |||
3b31774dd3 | |||
c106347c59 | |||
2679be9d97 | |||
4d59368c7d | |||
3f29680fb6 | |||
8f1df14ae7 | |||
7159e360d9 | |||
65723bcac5 | |||
73ae451cb4 | |||
dc0ea3a248 | |||
9548cd8341 | |||
8410fc38c9 | |||
0c9c2168b3 | |||
2690f54743 | |||
22783813c5 | |||
b5ed46a5af | |||
1a3f17e296 | |||
9281129f07 | |||
0e9ddd3b99 | |||
d87db04e55 | |||
906ec3c8a9 | |||
449575ceae | |||
6255c77eba | |||
c33622f2b3 | |||
e4860acb58 | |||
e96867f757 | |||
57bd43baf3 | |||
3a4e595d32 | |||
dcad22bfa8 | |||
acba0e47d8 | |||
dd19459e18 | |||
8be3c5ed21 | |||
51726d8de7 | |||
8f41c6bad8 | |||
e294abc567 | |||
9f9518c872 | |||
5faf7cb59e | |||
4b095d532c | |||
4288761235 | |||
73388f30fd | |||
2d9ddd4bc8 | |||
55aa0cf303 | |||
52ca15b514 | |||
bd383888de | |||
1bd349485f | |||
155f9dc1b1 | |||
fe7ee1edc3 | |||
c11cbff605 | |||
fbc629266f | |||
75d44dca6b | |||
3e6b794a33 | |||
a757ce48a1 | |||
507f29a7bd | |||
e5f4f77073 | |||
f07fee538d | |||
fa1420b384 | |||
724a2bd72f | |||
d525d02348 | |||
4920cf2b98 | |||
f41d0938a4 | |||
924a405829 | |||
fc9225e24a | |||
ac202cbdd3 | |||
cedd68c942 | |||
4231c879ef | |||
42e58a4b72 | |||
ebd1bc83c9 | |||
0bc9d7455f | |||
5274166f8c | |||
5566aa4588 | |||
ab638a4f54 | |||
86a8452ab7 | |||
8c96c16eac | |||
45c2e6575d | |||
a786a7dc55 | |||
6adf5cbee5 | |||
1964b54627 | |||
c9d0e82c52 | |||
1a4948f0f2 | |||
66da3f5668 | |||
9ebeb64570 | |||
571aaece2e | |||
f60fb954a2 | |||
1d136cacfb | |||
994021d77f | |||
8899325a0e | |||
43876a9357 | |||
d6cace32f5 | |||
2388de455b | |||
b14b3ab276 | |||
32ccb779c6 | |||
52536e94e5 | |||
5d6b3abd26 | |||
79682bbcb0 | |||
770ff19313 | |||
8d4855f100 | |||
7d4e14f384 | |||
f162dd7e87 | |||
06dc12e217 | |||
36edff9e15 | |||
34a0c079de | |||
84b581ae13 | |||
587c93eadf | |||
b5aa549e0c | |||
5fb6738612 | |||
685262fc5e | |||
8802f6cf23 |
97
NEWS
97
NEWS
@ -1,3 +1,100 @@
|
||||
3.7.5
|
||||
=====
|
||||
* MessageTray: pass keyboard events to tray icons [Giovanni; #687425]
|
||||
* network: add support for virtual devices (vlan, bond, bridge) [Dan; #677144]
|
||||
* gdm: Allow right-clicking buttons for left-handed users [Jasper; #688748]
|
||||
* Make list search results span all available horizontal space [Tanner; #691967]
|
||||
* Make Show-Applications button depress when held down [Hashem; #692319]
|
||||
* Set a max width on search results [Cosimo; #692453]
|
||||
* Reserve scrollbar allocation for automatic policy [Cosimo; #686881]
|
||||
* Improve scaling algorithm for window thumbnails [Jasper; #686944]
|
||||
* Fix launching settings panels after g-c-c changes [Jasper; #692483]
|
||||
* Stop launching applications from empty searches [Hashem; #692391]
|
||||
* Implement per-source notification filtering [Giovanni; #685926]
|
||||
* ScreenShield: Omit ActiveChanged() signal at end of fade [Giovanni; #691964]
|
||||
* ScreenShield: Lower the shield on idle before locking [Giovanni; #692560]
|
||||
* Make previews of minimized windows translucent in overview [Florian; #692999]
|
||||
* windowManager: Respect icon geometry when minimizing [Florian; #692997]
|
||||
* ScreenShield: Only show lock icon when actually locked [Giovanni; #693007]
|
||||
* general: Use & instead of 'and' for Settings panels [Jeremy; #689590]
|
||||
* network: Add support for new ModemManager1 interface [Aleksander; #687359]
|
||||
* network: Handle LTE-only modems as GSM ones [Aleksander; #688144]
|
||||
* mobile-providers: Port to libnm-gtk [Aleksander; #688943]
|
||||
* general: Consistently use Title Case in top bar [Jeremy; #689589]
|
||||
* panel: Add :overview pseudo class while in overview [Florian; #693218]
|
||||
* sessionMode: Add support for mode-specific styling [Florian; #693219]
|
||||
* loginManager: Make suspend a NOP in the ConsoleKit patch [Florian; #693162]
|
||||
* screenShield: Inhibit suspend until the screen is locked [Florian; #686482]
|
||||
* Misc bug fixes and cleanups [Jasper, Giovanni, Rui, Cosimo, Florian, Stefano,
|
||||
Adel, Yanko; #691745, #691731, #690171, #689091, #691976, #691963, #684279,
|
||||
#692052, #692091, #642831, #692454, #692715, #692678, #692723, #692677,
|
||||
#683986, #692693, #692749, #692948, #692995, #692996, #692994, #677215,
|
||||
#692586, #693067, #693031, #693049, #643111, #693161, #693220]
|
||||
|
||||
Contributors:
|
||||
Jeremy Bicha, Giovanni Campagna, Cosimo Cecchi, Tanner Doshier,
|
||||
Stefano Facchini, Adel Gadllah, Yanko Kaneti, Rui Matos, Aleksander Morgado,
|
||||
Florian Müllner, Hashem Nasarat, Jasper St. Pierre, Dan Winship
|
||||
|
||||
Translations:
|
||||
Duarte Loreto [pt], Daniel Mustieles [es], Kjartan Maraas [nb],
|
||||
Nilamdyuti Goswami [as], Мирослав Николић [sr,sr@latin],
|
||||
Tobias Endrigkeit [de], Fabio Tomat [fur], Matej Urbančič [sl], A S Alam [pa],
|
||||
Inaki Larranaga Murgoitio [eu], Piotr Drąg [pl], Wouter Bolsterlee [nl],
|
||||
Gheyret Kenji [ug], Yaron Shahrabani [he], Chao-Hsiung Liao [zh_HK,zh_TW],
|
||||
Milo Casagrande [it], Benjamin Steinwender [de]
|
||||
|
||||
3.7.4.1
|
||||
=======
|
||||
* userMenu: Use show-full-name-in-top-bar setting [Bastien; #689561]
|
||||
* dateMenu: Add "Open Clocks" entry [Mathieu; #644390]
|
||||
* screenshot: Immediately show the flash spot [Jasper; #691875]
|
||||
* Misc. bug fixes [Rico, Jeremy]
|
||||
|
||||
Contributors:
|
||||
Jeremy Bicha, Mathieu Bridon, Bastien Nocera, Jasper St. Pierre,
|
||||
Rico Tzschichholz
|
||||
|
||||
Translations:
|
||||
Ihar Hrachyshka [be]
|
||||
|
||||
3.7.4
|
||||
=====
|
||||
* Make menu separators crisp [Giovanni, Allan; #641745]
|
||||
* power: Update for new D-Bus name [Bastien; #690506]
|
||||
* Add smooth scrolling support [Jasper; #687573]
|
||||
* Tweak notification layout [Allan; #688506]
|
||||
* Ping the active window when using the app menu [Giovanni; #684340]
|
||||
* Make password entries insensitive after submission [Jasper; #690594, #690895]
|
||||
* Honor lock-delay GSettings key [Giovanni, Matthias; #690766, #691170]
|
||||
* Use text/calendar preferred app as the calendar app [Giovanni; #690767]
|
||||
* lookingGlass: Move to an inspect() function [Jasper; #690726]
|
||||
* Make OSK animation quicker, snappier [Rui; #688642]
|
||||
* Allow to close chat notifications with Escape [Jasper; #690897]
|
||||
* Honor org.gnome.desktop.screensaver.user-switch-enabled [Giovanni; #691042]
|
||||
* Add a SelectArea() DBus method [Cosimo; #687954]
|
||||
* Support non-absolute paths when saving screenshots [Cosimo; #688004]
|
||||
* OSK: Fix extended keys popups [Rui; #674955]
|
||||
* Don't hide or show the keyboard immediately [Rui; #688646]
|
||||
* Improve padding in power menu [Giovanni; #689297]
|
||||
* Add per-window input source switching [Rui; #691414]
|
||||
* Misc bug fixes and cleanups [Rico, Jasper, Giovanni, Rui, Florian, Dan;
|
||||
#690608, #690589, #690539, #687081, #690667, #690665, #690666, #685856,
|
||||
#690858, #690895, #680414, #690965, #691019, #690590, #681376, #690180,
|
||||
#685513, #689263, #691553, #691720, #691743, #691750]
|
||||
|
||||
Contributors:
|
||||
Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Allan Day, Rui Matos,
|
||||
Florian Müllner, Bastien Nocera, Jasper St. Pierre, Rico Tzschichholz,
|
||||
Dan Winship
|
||||
|
||||
Translations:
|
||||
Matej Urbančič [sl], Kjartan Maraas [nb], Mattias Põldaru [et],
|
||||
Yaron Shahrabani [he], Aurimas Černius [lt], Khaled Hosny [ar],
|
||||
Fran Diéguez [gl], Daniel Mustieles [es], Piotr Drąg [pl], Balázs Úr [hu],
|
||||
Baurzhan Muftakhidinov [kk], Tobias Endrigkeit [de], Dušan Kazik [sk],
|
||||
Aron Xu [zh_CN], Gheyret Kenji [ug]
|
||||
|
||||
3.7.3
|
||||
=====
|
||||
* Add 'No Messages' label when message tray is empty [Victoria; #686738]
|
||||
|
110
configure.ac
110
configure.ac
@ -1,5 +1,5 @@
|
||||
AC_PREREQ(2.63)
|
||||
AC_INIT([gnome-shell],[3.7.3],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
AC_INIT([gnome-shell],[3.7.5],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_SRCDIR([src/shell-global.c])
|
||||
@ -16,8 +16,6 @@ m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_CC
|
||||
# Needed for per-target cflags, like in gnomeshell-taskpanel
|
||||
AM_PROG_CC_C_O
|
||||
|
||||
# Initialize libtool
|
||||
LT_PREREQ([2.2.6])
|
||||
@ -62,15 +60,14 @@ fi
|
||||
|
||||
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
|
||||
|
||||
CLUTTER_MIN_VERSION=1.11.11
|
||||
CLUTTER_MIN_VERSION=1.13.4
|
||||
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
|
||||
GJS_MIN_VERSION=1.33.2
|
||||
MUTTER_MIN_VERSION=3.7.3
|
||||
GJS_MIN_VERSION=1.35.4
|
||||
MUTTER_MIN_VERSION=3.7.5
|
||||
GTK_MIN_VERSION=3.3.9
|
||||
GIO_MIN_VERSION=2.35.0
|
||||
LIBECAL_MIN_VERSION=3.5.3
|
||||
LIBEDATASERVER_MIN_VERSION=3.5.3
|
||||
LIBEDATASERVERUI_MIN_VERSION=3.5.3
|
||||
TELEPATHY_GLIB_MIN_VERSION=0.17.5
|
||||
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
|
||||
POLKIT_MIN_VERSION=0.100
|
||||
@ -78,7 +75,7 @@ STARTUP_NOTIFICATION_MIN_VERSION=0.11
|
||||
GCR_MIN_VERSION=3.3.90
|
||||
GNOME_DESKTOP_REQUIRED_VERSION=3.7.1
|
||||
GNOME_MENUS_REQUIRED_VERSION=3.5.3
|
||||
NETWORKMANAGER_MIN_VERSION=0.9.7
|
||||
NETWORKMANAGER_MIN_VERSION=0.9.6
|
||||
PULSE_MIN_VERS=2.0
|
||||
|
||||
# Collect more than 20 libraries for a prize!
|
||||
@ -96,7 +93,7 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
|
||||
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
|
||||
libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION
|
||||
gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
|
||||
libcanberra
|
||||
libcanberra libcanberra-gtk3
|
||||
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
|
||||
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
|
||||
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
|
||||
@ -104,30 +101,14 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
|
||||
libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
|
||||
gnome-keyring-1 gcr-3 >= $GCR_MIN_VERSION)
|
||||
|
||||
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
|
||||
|
||||
PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
|
||||
|
||||
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
|
||||
|
||||
GNOME_KEYBINDINGS_KEYSDIR=`$PKG_CONFIG --variable keysdir gnome-keybindings`
|
||||
AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
|
||||
|
||||
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
|
||||
|
||||
saved_CFLAGS=$CFLAGS
|
||||
saved_LIBS=$LIBS
|
||||
CFLAGS=$GNOME_SHELL_CFLAGS
|
||||
LIBS=$GNOME_SHELL_LIBS
|
||||
AC_CHECK_FUNCS(JS_NewGlobalObject XFixesCreatePointerBarrier)
|
||||
CFLAGS=$saved_CFLAGS
|
||||
LIBS=$saved_LIBS
|
||||
|
||||
PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
|
||||
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11)
|
||||
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
|
||||
PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
|
||||
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
|
||||
PKG_CHECK_MODULES(TRAY, gtk+-3.0)
|
||||
PKG_CHECK_MODULES(GVC, libpulse >= $PULSE_MIN_VERS libpulse-mainloop-glib gobject-2.0)
|
||||
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.7.2.2)
|
||||
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.7.4)
|
||||
|
||||
AC_MSG_CHECKING([for bluetooth support])
|
||||
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
|
||||
@ -147,32 +128,18 @@ PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedatas
|
||||
AC_SUBST(CALENDAR_SERVER_CFLAGS)
|
||||
AC_SUBST(CALENDAR_SERVER_LIBS)
|
||||
|
||||
AC_ARG_WITH(systemd,
|
||||
AS_HELP_STRING([--with-systemd],
|
||||
[Add systemd support]),
|
||||
[with_systemd=$withval], [with_systemd=auto])
|
||||
GNOME_KEYBINDINGS_KEYSDIR=`$PKG_CONFIG --variable keysdir gnome-keybindings`
|
||||
AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
|
||||
|
||||
PKG_CHECK_MODULES(SYSTEMD,
|
||||
[libsystemd-login libsystemd-daemon],
|
||||
[have_systemd=yes], [have_systemd=no])
|
||||
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
|
||||
|
||||
if test "x$with_systemd" = "xauto" ; then
|
||||
if test x$have_systemd = xno ; then
|
||||
use_systemd=no
|
||||
else
|
||||
use_systemd=yes
|
||||
fi
|
||||
else
|
||||
use_systemd=$with_systemd
|
||||
fi
|
||||
|
||||
if test "x$use_systemd" = "xyes"; then
|
||||
if test "x$have_systemd" = "xno"; then
|
||||
AC_MSG_ERROR([Systemd support explicitly required, but systemd not found])
|
||||
fi
|
||||
|
||||
AC_DEFINE(WITH_SYSTEMD, 1, [systemd support])
|
||||
fi
|
||||
saved_CFLAGS=$CFLAGS
|
||||
saved_LIBS=$LIBS
|
||||
CFLAGS=$GNOME_SHELL_CFLAGS
|
||||
LIBS=$GNOME_SHELL_LIBS
|
||||
AC_CHECK_FUNCS(XFixesCreatePointerBarrier)
|
||||
CFLAGS=$saved_CFLAGS
|
||||
LIBS=$saved_LIBS
|
||||
|
||||
MUTTER_GIR_DIR=`$PKG_CONFIG --variable=girdir libmutter`
|
||||
MUTTER_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter`
|
||||
@ -199,16 +166,6 @@ fi
|
||||
|
||||
# Sets GLIB_GENMARSHAL and GLIB_MKENUMS
|
||||
AM_PATH_GLIB_2_0()
|
||||
G_IR_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0`
|
||||
AC_SUBST(G_IR_SCANNER)
|
||||
G_IR_COMPILER=`$PKG_CONFIG --variable=g_ir_compiler gobject-introspection-1.0`
|
||||
AC_SUBST(G_IR_COMPILER)
|
||||
G_IR_GENERATE=`$PKG_CONFIG --variable=g_ir_generate gobject-introspection-1.0`
|
||||
AC_SUBST(G_IR_GENERATE)
|
||||
GIRDIR=`$PKG_CONFIG --variable=girdir gobject-introspection-1.0`
|
||||
AC_SUBST(GIRDIR)
|
||||
TYPELIBDIR="$($PKG_CONFIG --variable=typelibdir gobject-introspection-1.0)"
|
||||
AC_SUBST(TYPELIBDIR)
|
||||
|
||||
GTK_DOC_CHECK([1.15], [--flavour no-tmpl])
|
||||
|
||||
@ -224,32 +181,7 @@ if test "$enable_man" != no; then
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no)
|
||||
|
||||
# Stay command-line compatible with the gnome-common configure option. Here
|
||||
# minimum/yes/maximum are the same, however.
|
||||
AC_ARG_ENABLE(compile_warnings,
|
||||
AS_HELP_STRING([--enable-compile-warnings=@<:@no/minimum/yes/maximum/error@:>@],[Turn on compiler warnings]),,
|
||||
enable_compile_warnings=error)
|
||||
|
||||
changequote(,)dnl
|
||||
if test "$enable_compile_warnings" != no ; then
|
||||
if test "x$GCC" = "xyes"; then
|
||||
case " $CFLAGS " in
|
||||
*[\ \ ]-Wall[\ \ ]*) ;;
|
||||
*) CFLAGS="$CFLAGS -Wall" ;;
|
||||
esac
|
||||
case " $CFLAGS " in
|
||||
*[\ \ ]-Wmissing-prototypes[\ \ ]*) ;;
|
||||
*) CFLAGS="$CFLAGS -Wmissing-prototypes" ;;
|
||||
esac
|
||||
if test "$enable_compile_warnings" = error ; then
|
||||
case " $CFLAGS " in
|
||||
*[\ \ ]-Werror[\ \ ]*) ;;
|
||||
*) CFLAGS="$CFLAGS -Werror -Wno-error=deprecated-declarations" ;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
changequote([,])dnl
|
||||
GNOME_COMPILE_WARNINGS([error])
|
||||
|
||||
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)
|
||||
|
@ -12,6 +12,7 @@ desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
|
||||
|
||||
introspectiondir = $(datadir)/dbus-1/interfaces
|
||||
introspection_DATA = \
|
||||
org.gnome.Shell.Screenshot.xml \
|
||||
org.gnome.ShellSearchProvider.xml \
|
||||
org.gnome.ShellSearchProvider2.xml
|
||||
|
||||
|
128
data/org.gnome.Shell.Screenshot.xml
Normal file
128
data/org.gnome.Shell.Screenshot.xml
Normal file
@ -0,0 +1,128 @@
|
||||
<!DOCTYPE node PUBLIC
|
||||
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
|
||||
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
|
||||
<node>
|
||||
|
||||
<!--
|
||||
org.gnome.Shell.Screenshot:
|
||||
@short_description: Screenshot interface
|
||||
|
||||
The interface used to capture pictures of the screen contents.
|
||||
-->
|
||||
<interface name="org.gnome.Shell.Screenshot">
|
||||
|
||||
<!--
|
||||
Screenshot:
|
||||
@filename: The filename for the screenshot
|
||||
@include_cursor: Whether to include the cursor image or not
|
||||
@flash: Whether to flash the screen or not
|
||||
@success: whether the screenshot was captured
|
||||
@filename_used: the file where the screenshot was saved
|
||||
|
||||
Takes a screenshot of the whole screen and saves it
|
||||
in @filename as png image, it returns a boolean
|
||||
indicating whether the operation was successful or not.
|
||||
@filename can either be an absolute path or a basename, in
|
||||
which case the screenshot will be saved in the $XDG_PICTURES_DIR
|
||||
or the home directory if it doesn't exist. The filename used
|
||||
to save the screenshot will be returned in @filename_used.
|
||||
-->
|
||||
<method name="Screenshot">
|
||||
<arg type="b" direction="in" name="include_cursor"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
<arg type="s" direction="out" name="filename_used"/>
|
||||
</method>
|
||||
|
||||
<!--
|
||||
ScreenshotWindow:
|
||||
@include_frame: Whether to include the frame or not
|
||||
@include_cursor: Whether to include the cursor image or not
|
||||
@flash: Whether to flash the window area or not
|
||||
@filename: The filename for the screenshot
|
||||
@success: whether the screenshot was captured
|
||||
@filename_used: the file where the screenshot was saved
|
||||
|
||||
Takes a screenshot of the focused window (optionally omitting the frame)
|
||||
and saves it in @filename as png image, it returns a boolean
|
||||
indicating whether the operation was successful or not.
|
||||
@filename can either be an absolute path or a basename, in
|
||||
which case the screenshot will be saved in the $XDG_PICTURES_DIR
|
||||
or the home directory if it doesn't exist. The filename used
|
||||
to save the screenshot will be returned in @filename_used.
|
||||
-->
|
||||
<method name="ScreenshotWindow">
|
||||
<arg type="b" direction="in" name="include_frame"/>
|
||||
<arg type="b" direction="in" name="include_cursor"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
<arg type="s" direction="out" name="filename_used"/>
|
||||
</method>
|
||||
|
||||
<!--
|
||||
ScreenshotArea:
|
||||
@x: the X coordinate of the area to capture
|
||||
@y: the Y coordinate of the area to capture
|
||||
@width: the width of the area to capture
|
||||
@height: the height of the area to capture
|
||||
@flash: whether to flash the area or not
|
||||
@filename: the filename for the screenshot
|
||||
@success: whether the screenshot was captured
|
||||
@filename_used: the file where the screenshot was saved
|
||||
|
||||
Takes a screenshot of the passed in area and saves it
|
||||
in @filename as png image, it returns a boolean
|
||||
indicating whether the operation was successful or not.
|
||||
@filename can either be an absolute path or a basename, in
|
||||
which case the screenshot will be saved in the $XDG_PICTURES_DIR
|
||||
or the home directory if it doesn't exist. The filename used
|
||||
to save the screenshot will be returned in @filename_used.
|
||||
-->
|
||||
<method name="ScreenshotArea">
|
||||
<arg type="i" direction="in" name="x"/>
|
||||
<arg type="i" direction="in" name="y"/>
|
||||
<arg type="i" direction="in" name="width"/>
|
||||
<arg type="i" direction="in" name="height"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
<arg type="s" direction="out" name="filename_used"/>
|
||||
</method>
|
||||
|
||||
<!--
|
||||
FlashArea:
|
||||
@x: the X coordinate of the area to flash
|
||||
@y: the Y coordinate of the area to flash
|
||||
@width: the width of the area to flash
|
||||
@height: the height of the area to flash
|
||||
|
||||
Renders a flash spot effect in the specified rectangle of the screen.
|
||||
-->
|
||||
<method name="FlashArea">
|
||||
<arg type="i" direction="in" name="x"/>
|
||||
<arg type="i" direction="in" name="y"/>
|
||||
<arg type="i" direction="in" name="width"/>
|
||||
<arg type="i" direction="in" name="height"/>
|
||||
</method>
|
||||
|
||||
<!--
|
||||
SelectArea:
|
||||
@x: the X coordinate of the selected area
|
||||
@y: the Y coordinate of the selected area
|
||||
@width: the width of the selected area
|
||||
@height: the height of the selected area
|
||||
|
||||
Interactively allows the user to select a rectangular area of
|
||||
the screen, and returns its coordinates.
|
||||
-->
|
||||
<method name="SelectArea">
|
||||
<arg type="i" direction="out" name="x"/>
|
||||
<arg type="i" direction="out" name="y"/>
|
||||
<arg type="i" direction="out" name="width"/>
|
||||
<arg type="i" direction="out" name="height"/>
|
||||
</method>
|
||||
|
||||
</interface>
|
||||
</node>
|
@ -65,11 +65,6 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
||||
menuitem in single-user, single-session situations.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="show-full-name" type="b">
|
||||
<default>true</default>
|
||||
<_summary>Show full name in the user menu</_summary>
|
||||
<_description>Whether the users full name is shown in the user menu or not.</_description>
|
||||
</key>
|
||||
<key name="remember-mount-password" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Whether to remember password for mounting encrypted or remote filesystems</_summary>
|
||||
|
@ -206,11 +206,12 @@ StScrollBar StButton#vhandle:active {
|
||||
}
|
||||
|
||||
.popup-separator-menu-item {
|
||||
-gradient-height: 2px;
|
||||
-gradient-start: rgba(8,8,8,0);
|
||||
-gradient-end: #333333;
|
||||
-margin-horizontal: 1.5em;
|
||||
height: 1em;
|
||||
-gradient-height: 1px;
|
||||
-gradient-start: rgba(255,255,255,0.0);
|
||||
-gradient-end: rgba(255,255,255,0.3);
|
||||
-margin-horizontal: 24px;
|
||||
height: 1px;
|
||||
padding: 8px 0px;
|
||||
}
|
||||
|
||||
.popup-alternating-menu-item:alternate {
|
||||
@ -251,6 +252,10 @@ StScrollBar StButton#vhandle:active {
|
||||
icon-size: 1.09em;
|
||||
}
|
||||
|
||||
.popup-battery-percentage {
|
||||
padding-left: 24px;
|
||||
}
|
||||
|
||||
/* Switches */
|
||||
.toggle-switch {
|
||||
width: 65px;
|
||||
@ -287,13 +292,17 @@ StScrollBar StButton#vhandle:active {
|
||||
.hotplug-notification-item,
|
||||
.hotplug-resident-eject-button,
|
||||
.modal-dialog-button {
|
||||
font-weight: bold;
|
||||
border: 1px solid #8b8b8b;
|
||||
background-gradient-direction: vertical;
|
||||
background-gradient-start: rgba(255, 255, 255, 0.2);
|
||||
background-gradient-end: rgba(255, 255, 255, 0);
|
||||
}
|
||||
|
||||
.dash-search-button,
|
||||
.modal-dialog-button {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.dash-search-button:hover,
|
||||
.notification-button:hover,
|
||||
.notification-icon-button:hover,
|
||||
@ -354,7 +363,7 @@ StScrollBar StButton#vhandle:active {
|
||||
background-gradient-start: rgba(5,5,6,0.1);
|
||||
background-gradient-end: rgba(254,254,254,0.1);
|
||||
background-gradient-direction: vertical;
|
||||
transition-duration: 300;
|
||||
transition-duration: 300ms;
|
||||
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
@ -389,7 +398,7 @@ StScrollBar StButton#vhandle:active {
|
||||
color: rgb(64, 64, 64);
|
||||
caret-color: rgb(64, 64, 64);
|
||||
font-weight: bold;
|
||||
transition-duration: 0;
|
||||
transition-duration: 0ms;
|
||||
}
|
||||
|
||||
.notification StEntry,
|
||||
@ -420,7 +429,7 @@ StScrollBar StButton#vhandle:active {
|
||||
background-color: black;
|
||||
font-weight: bold;
|
||||
height: 1.86em;
|
||||
transition-duration: 250;
|
||||
transition-duration: 250ms;
|
||||
}
|
||||
|
||||
#panel.lock-screen {
|
||||
@ -503,7 +512,7 @@ StScrollBar StButton#vhandle:active {
|
||||
-minimum-hpadding: 6px;
|
||||
font-weight: bold;
|
||||
color: #ccc;
|
||||
transition-duration: 100;
|
||||
transition-duration: 100ms;
|
||||
}
|
||||
|
||||
#panel.unlock-screen .panel-button,
|
||||
@ -718,6 +727,10 @@ StScrollBar StButton#vhandle:active {
|
||||
-vertical-spacing: 32px;
|
||||
}
|
||||
|
||||
.window-picker.external-monitor {
|
||||
padding: 32px;
|
||||
}
|
||||
|
||||
/* Dash */
|
||||
|
||||
#dash {
|
||||
@ -742,10 +755,6 @@ StScrollBar StButton#vhandle:active {
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
#viewSelector {
|
||||
spacing: 1em;
|
||||
}
|
||||
|
||||
/* Search Box */
|
||||
|
||||
#searchEntry {
|
||||
@ -770,6 +779,10 @@ StScrollBar StButton#vhandle:active {
|
||||
spacing: 18px;
|
||||
}
|
||||
|
||||
#searchResultsBin {
|
||||
max-width: 1000px;
|
||||
}
|
||||
|
||||
#searchResultsContent {
|
||||
padding-right: 20px;
|
||||
spacing: 16px;
|
||||
@ -917,7 +930,7 @@ StScrollBar StButton#vhandle:active {
|
||||
border-radius: 4px;
|
||||
padding: 3px;
|
||||
border: 1px rgba(0,0,0,0);
|
||||
transition-duration: 100;
|
||||
transition-duration: 100ms;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@ -934,7 +947,7 @@ StScrollBar StButton#vhandle:active {
|
||||
.grid-search-result:hover .overview-icon {
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
text-shadow: black 0px 2px 2px;
|
||||
transition-duration: 100;
|
||||
transition-duration: 100ms;
|
||||
color:white;
|
||||
}
|
||||
|
||||
@ -950,19 +963,20 @@ StScrollBar StButton#vhandle:active {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.show-apps:checked > .overview-icon {
|
||||
.show-apps:checked > .overview-icon,
|
||||
.show-apps:active > .overview-icon {
|
||||
background-gradient-start: rgba(255, 255, 255, .05);
|
||||
background-gradient-end: rgba(255, 255, 255, .15);
|
||||
background-gradient-direction: vertical;
|
||||
border-radius: 4px;
|
||||
box-shadow: inset 0px 1px 2px 0px rgba(0, 0, 0, 1);
|
||||
transition-duration: 100;
|
||||
transition-duration: 100ms;
|
||||
}
|
||||
|
||||
.show-apps:checked .show-apps-icon,
|
||||
.show-apps:focus .show-apps-icon {
|
||||
color: white;
|
||||
transition-duration: 100;
|
||||
transition-duration: 100ms;
|
||||
}
|
||||
|
||||
.app-well-app:focus > .overview-icon,
|
||||
@ -1001,7 +1015,7 @@ StScrollBar StButton#vhandle:active {
|
||||
-minimum-hpadding: 6px;
|
||||
font-weight: bold;
|
||||
color: #ccc;
|
||||
transition-duration: 100;
|
||||
transition-duration: 100ms;
|
||||
padding-left: .3em;
|
||||
padding-right: .3em;
|
||||
}
|
||||
@ -1304,7 +1318,7 @@ StScrollBar StButton#vhandle:active {
|
||||
#message-tray {
|
||||
background: #2e3436 url(message-tray-background.png);
|
||||
background-repeat: repeat;
|
||||
transition-duration: 250;
|
||||
height: 72px;
|
||||
}
|
||||
|
||||
#message-tray:keyboard {
|
||||
@ -1331,7 +1345,7 @@ StScrollBar StButton#vhandle:active {
|
||||
border-radius: 10px 10px 0px 0px;
|
||||
background: rgba(0,0,0,0.9);
|
||||
padding: 8px 8px 4px 8px;
|
||||
spacing-rows: 10px;
|
||||
spacing-rows: 4px;
|
||||
spacing-columns: 10px;
|
||||
}
|
||||
|
||||
@ -1411,6 +1425,7 @@ StScrollBar StButton#vhandle:active {
|
||||
}
|
||||
|
||||
.notification-actions {
|
||||
padding-top: 18px;
|
||||
spacing: 10px;
|
||||
}
|
||||
|
||||
@ -1573,7 +1588,7 @@ StScrollBar StButton#vhandle:active {
|
||||
.summary-source {
|
||||
border-radius: 4px;
|
||||
padding: 0 6px 0 6px;
|
||||
transition-duration: 100;
|
||||
transition-duration: 100ms;
|
||||
}
|
||||
|
||||
.summary-source-counter {
|
||||
@ -2402,6 +2417,7 @@ StScrollBar StButton#vhandle:active {
|
||||
|
||||
.screen-shield-notifications-box {
|
||||
spacing: 18px;
|
||||
max-width: 34em;
|
||||
}
|
||||
|
||||
.screen-shield-notification-source {
|
||||
|
@ -88,11 +88,18 @@ doc-gen-org.gnome.Shell.SearchProvider2.xml: $(top_srcdir)/data/org.gnome.ShellS
|
||||
--generate-docbook doc-gen \
|
||||
$(top_srcdir)/data/org.gnome.ShellSearchProvider2.xml
|
||||
|
||||
doc-gen-org.gnome.Shell.Screenshot.xml: $(top_srcdir)/data/org.gnome.Shell.Screenshot.xml
|
||||
gdbus-codegen \
|
||||
--interface-prefix org.gnome.Shell.Screenshot. \
|
||||
--generate-docbook doc-gen \
|
||||
$(top_srcdir)/data/org.gnome.Shell.Screenshot.xml
|
||||
|
||||
# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
|
||||
# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
|
||||
content_files= \
|
||||
doc-gen-org.gnome.Shell.SearchProvider.xml \
|
||||
doc-gen-org.gnome.Shell.SearchProvider2.xml
|
||||
doc-gen-org.gnome.Shell.SearchProvider2.xml \
|
||||
doc-gen-org.gnome.Shell.Screenshot.xml
|
||||
|
||||
# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
|
||||
# These files must be listed here *and* in content_files
|
||||
|
@ -50,7 +50,6 @@
|
||||
<xi:include href="xml/shell-xfixes-cursor.xml"/>
|
||||
<xi:include href="xml/shell-util.xml"/>
|
||||
<xi:include href="xml/shell-mount-operation.xml"/>
|
||||
<xi:include href="xml/shell-mobile-providers.xml"/>
|
||||
<xi:include href="xml/shell-network-agent.xml"/>
|
||||
<xi:include href="xml/shell-polkit-authentication-agent.xml"/>
|
||||
<xi:include href="xml/shell-tp-client.xml"/>
|
||||
|
@ -66,4 +66,11 @@ its dependencies to build from tarballs.</description>
|
||||
<gnome:userid>fmuellner</gnome:userid>
|
||||
</foaf:Person>
|
||||
</maintainer>
|
||||
<maintainer>
|
||||
<foaf:Person>
|
||||
<foaf:name>Ray Strode</foaf:name>
|
||||
<foaf:mbox rdf:resource="mailto:halfline@gmail.com" />
|
||||
<gnome:userid>halfline</gnome:userid>
|
||||
</foaf:Person>
|
||||
</maintainer>
|
||||
</Project>
|
||||
|
@ -28,6 +28,7 @@ nobase_dist_js_DATA = \
|
||||
misc/extensionUtils.js \
|
||||
misc/fileUtils.js \
|
||||
misc/gnomeSession.js \
|
||||
misc/hash.js \
|
||||
misc/history.js \
|
||||
misc/jsParse.js \
|
||||
misc/loginManager.js \
|
||||
@ -49,7 +50,6 @@ nobase_dist_js_DATA = \
|
||||
ui/extensionSystem.js \
|
||||
ui/extensionDownloader.js \
|
||||
ui/environment.js \
|
||||
ui/flashspot.js \
|
||||
ui/ibusCandidatePopup.js\
|
||||
ui/grabHelper.js \
|
||||
ui/iconGrid.js \
|
||||
@ -74,6 +74,7 @@ nobase_dist_js_DATA = \
|
||||
ui/popupMenu.js \
|
||||
ui/remoteSearch.js \
|
||||
ui/runDialog.js \
|
||||
ui/screenshot.js \
|
||||
ui/screenShield.js \
|
||||
ui/scripting.js \
|
||||
ui/search.js \
|
||||
|
@ -128,6 +128,7 @@ const UserListItem = new Lang.Class({
|
||||
|
||||
let layout = new St.BoxLayout({ vertical: false });
|
||||
this.actor = new St.Button({ style_class: 'login-dialog-user-list-item',
|
||||
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
|
||||
can_focus: true,
|
||||
child: layout,
|
||||
reactive: true,
|
||||
@ -492,6 +493,7 @@ const SessionListItem = new Lang.Class({
|
||||
this.id = id;
|
||||
|
||||
this.actor = new St.Button({ style_class: 'login-dialog-session-list-item',
|
||||
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
|
||||
can_focus: true,
|
||||
reactive: true,
|
||||
x_fill: true,
|
||||
@ -532,6 +534,7 @@ const SessionListItem = new Lang.Class({
|
||||
color.alpha / 255);
|
||||
cr.arc(width / 2, height / 2, width / 3, 0, 2 * Math.PI);
|
||||
cr.fill();
|
||||
cr.$dispose();
|
||||
},
|
||||
|
||||
_onClicked: function() {
|
||||
@ -551,6 +554,7 @@ const SessionList = new Lang.Class({
|
||||
this.actor.child = this._box;
|
||||
|
||||
this._button = new St.Button({ style_class: 'login-dialog-session-list-button',
|
||||
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
|
||||
can_focus: true,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
@ -673,7 +677,7 @@ const LoginDialog = new Lang.Class({
|
||||
this.parent({ shellReactive: true,
|
||||
styleClass: 'login-dialog',
|
||||
parentActor: parentActor,
|
||||
keybindingMode: Main.KeybindingMode.LOGIN_SCREEN,
|
||||
keybindingMode: Shell.KeyBindingMode.LOGIN_SCREEN,
|
||||
shouldFadeIn: false });
|
||||
this.connect('destroy',
|
||||
Lang.bind(this, this._onDestroy));
|
||||
@ -786,6 +790,7 @@ const LoginDialog = new Lang.Class({
|
||||
let notListedLabel = new St.Label({ text: _("Not listed?"),
|
||||
style_class: 'login-dialog-not-listed-label' });
|
||||
this._notListedButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
|
||||
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
|
||||
can_focus: true,
|
||||
child: notListedLabel,
|
||||
reactive: true,
|
||||
|
@ -58,6 +58,7 @@ const SessionManagerIface = <interface name="org.gnome.SessionManager">
|
||||
<arg type="u" direction="in" />
|
||||
<arg type="b" direction="out" />
|
||||
</method>
|
||||
<property name="SessionIsActive" type="b" access="read"/>
|
||||
<signal name="InhibitorAdded">
|
||||
<arg type="o" direction="out"/>
|
||||
</signal>
|
||||
|
141
js/misc/hash.js
Normal file
141
js/misc/hash.js
Normal file
@ -0,0 +1,141 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Lang = imports.lang;
|
||||
const System = imports.system;
|
||||
|
||||
const Params = imports.misc.params;
|
||||
|
||||
// This is an implementation of EcmaScript SameValue algorithm,
|
||||
// which returns true if two values are not observably distinguishable.
|
||||
// It was taken from http://wiki.ecmascript.org/doku.php?id=harmony:egal
|
||||
//
|
||||
// In the future, we may want to use the 'is' operator instead.
|
||||
function _sameValue(x, y) {
|
||||
if (x === y) {
|
||||
// 0 === -0, but they are not identical
|
||||
return x !== 0 || 1 / x === 1 / y;
|
||||
}
|
||||
|
||||
// NaN !== NaN, but they are identical.
|
||||
// NaNs are the only non-reflexive value, i.e., if x !== x,
|
||||
// then x is a NaN.
|
||||
// isNaN is broken: it converts its argument to number, so
|
||||
// isNaN("foo") => true
|
||||
return x !== x && y !== y;
|
||||
}
|
||||
|
||||
const _hashers = {
|
||||
object: function(o) { return o ? System.addressOf(o) : 'null'; },
|
||||
function: function(f) { return System.addressOf(f); },
|
||||
string: function(s) { return s; },
|
||||
number: function(n) { return String(n); },
|
||||
undefined: function() { return 'undefined'; },
|
||||
};
|
||||
|
||||
/* Map is meant to be similar in usage to ES6 Map, which is
|
||||
described at http://wiki.ecmascript.org/doku.php?id=harmony:simple_maps_and_sets,
|
||||
without requiring more than ES5 + Gjs extensions.
|
||||
|
||||
Known differences from other implementations:
|
||||
Polyfills around the web usually implement HashMaps for
|
||||
primitive values and reversed WeakMaps for object keys,
|
||||
but we want real maps with real O(1) semantics in all cases,
|
||||
and the easiest way is to have different hashers for different
|
||||
types.
|
||||
|
||||
Known differences from the ES6 specification:
|
||||
- Map is a Lang.Class, not a ES6 class, so inheritance,
|
||||
prototype, sealing, etc. work differently.
|
||||
- items(), keys() and values() don't return iterators,
|
||||
they return actual arrays, so they incur a full copy everytime
|
||||
they're called, and they don't see changes if you mutate
|
||||
the table while iterating
|
||||
(admittedly, the ES6 spec is a bit unclear on this, and
|
||||
the reference code would just blow up)
|
||||
*/
|
||||
const Map = new Lang.Class({
|
||||
Name: 'Map',
|
||||
|
||||
_init: function(iterable) {
|
||||
this._pool = { };
|
||||
|
||||
if (iterable) {
|
||||
for (let i = 0; i < iterable.length; i++) {
|
||||
let [key, value] = iterable[i];
|
||||
this.set(key, value);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_hashKey: function(key) {
|
||||
let type = typeof(key);
|
||||
return type + ':' + _hashers[type](key);
|
||||
},
|
||||
|
||||
_internalGet: function(key) {
|
||||
let hash = this._hashKey(key);
|
||||
let node = this._pool[hash];
|
||||
|
||||
if (node && _sameValue(node.key, key))
|
||||
return [true, node.value];
|
||||
else
|
||||
return [false, null];
|
||||
},
|
||||
|
||||
get: function(key) {
|
||||
return this._internalGet(key)[1];
|
||||
},
|
||||
|
||||
has: function(key) {
|
||||
return this._internalGet(key)[0];
|
||||
},
|
||||
|
||||
set: function(key, value) {
|
||||
let hash = this._hashKey(key);
|
||||
let node = this._pool[hash];
|
||||
|
||||
if (node) {
|
||||
node.key = key;
|
||||
node.value = value;
|
||||
} else {
|
||||
this._pool[hash] = { key: key, value: value };
|
||||
}
|
||||
},
|
||||
|
||||
delete: function(key) {
|
||||
let hash = this._hashKey(key);
|
||||
let node = this._pool[hash];
|
||||
|
||||
if (node && _sameValue(node.key, key)) {
|
||||
delete this._pool[hash];
|
||||
return [node.key, node.value];
|
||||
} else {
|
||||
return [null, null];
|
||||
}
|
||||
},
|
||||
|
||||
keys: function() {
|
||||
let pool = this._pool;
|
||||
return Object.getOwnPropertyNames(pool).map(function(hash) {
|
||||
return pool[hash].key;
|
||||
});
|
||||
},
|
||||
|
||||
values: function() {
|
||||
let pool = this._pool;
|
||||
return Object.getOwnPropertyNames(pool).map(function(hash) {
|
||||
return pool[hash].value;
|
||||
});
|
||||
},
|
||||
|
||||
items: function() {
|
||||
let pool = this._pool;
|
||||
return Object.getOwnPropertyNames(pool).map(function(hash) {
|
||||
return [pool[hash].key, pool[hash].value];
|
||||
});
|
||||
},
|
||||
|
||||
size: function() {
|
||||
return Object.getOwnPropertyNames(this._pool).length;
|
||||
},
|
||||
});
|
@ -5,7 +5,7 @@ const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Shell = imports.gi.Shell;
|
||||
const UPowerGlib = imports.gi.UPowerGlib;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager'>
|
||||
<method name='PowerOff'>
|
||||
@ -26,6 +26,16 @@ const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager
|
||||
<method name='CanSuspend'>
|
||||
<arg type='s' direction='out'/>
|
||||
</method>
|
||||
<method name='Inhibit'>
|
||||
<arg type='s' direction='in'/>
|
||||
<arg type='s' direction='in'/>
|
||||
<arg type='s' direction='in'/>
|
||||
<arg type='s' direction='in'/>
|
||||
<arg type='h' direction='out'/>
|
||||
</method>
|
||||
<signal name='PrepareForSleep'>
|
||||
<arg type='b' direction='out'/>
|
||||
</signal>
|
||||
</interface>;
|
||||
|
||||
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
|
||||
@ -51,12 +61,6 @@ const ConsoleKitManagerIface = <interface name='org.freedesktop.ConsoleKit.Manag
|
||||
</interface>;
|
||||
|
||||
const ConsoleKitSessionIface = <interface name='org.freedesktop.ConsoleKit.Session'>
|
||||
<method name='IsActive'>
|
||||
<arg type='b' direction='out' />
|
||||
</method>
|
||||
<signal name='ActiveChanged'>
|
||||
<arg type='b' direction='out' />
|
||||
</signal>
|
||||
<signal name='Lock' />
|
||||
<signal name='Unlock' />
|
||||
</interface>;
|
||||
@ -93,6 +97,8 @@ const LoginManagerSystemd = new Lang.Class({
|
||||
this._proxy = new SystemdLoginManager(Gio.DBus.system,
|
||||
'org.freedesktop.login1',
|
||||
'/org/freedesktop/login1');
|
||||
this._proxy.connectSignal('PrepareForSleep',
|
||||
Lang.bind(this, this._prepareForSleep));
|
||||
},
|
||||
|
||||
// Having this function is a bit of a hack since the Systemd and ConsoleKit
|
||||
@ -109,10 +115,6 @@ const LoginManagerSystemd = new Lang.Class({
|
||||
return this._currentSession;
|
||||
},
|
||||
|
||||
get sessionActive() {
|
||||
return Shell.session_is_active_for_systemd();
|
||||
},
|
||||
|
||||
canPowerOff: function(asyncCallback) {
|
||||
this._proxy.CanPowerOffRemote(function(result, error) {
|
||||
if (error)
|
||||
@ -150,8 +152,33 @@ const LoginManagerSystemd = new Lang.Class({
|
||||
|
||||
suspend: function() {
|
||||
this._proxy.SuspendRemote(true);
|
||||
},
|
||||
|
||||
inhibit: function(reason, callback) {
|
||||
let inVariant = GLib.Variant.new('(ssss)',
|
||||
['sleep',
|
||||
'GNOME Shell',
|
||||
reason,
|
||||
'delay']);
|
||||
this._proxy.call_with_unix_fd_list('Inhibit', inVariant, 0, -1, null, null,
|
||||
Lang.bind(this, function(proxy, result) {
|
||||
let fd = -1;
|
||||
try {
|
||||
let [outVariant, fdList] = proxy.call_with_unix_fd_list_finish(result);
|
||||
fd = fdList.steal_fds(outVariant.deep_unpack())[0];
|
||||
callback(new Gio.UnixInputStream({ fd: fd }));
|
||||
} catch(e) {
|
||||
logError(e, "Error getting systemd inhibitor");
|
||||
callback(null);
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
_prepareForSleep: function(proxy, sender, [aboutToSuspend]) {
|
||||
this.emit('prepare-for-sleep', aboutToSuspend);
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(LoginManagerSystemd.prototype);
|
||||
|
||||
const LoginManagerConsoleKit = new Lang.Class({
|
||||
Name: 'LoginManagerConsoleKit',
|
||||
@ -160,7 +187,6 @@ const LoginManagerConsoleKit = new Lang.Class({
|
||||
this._proxy = new ConsoleKitManager(Gio.DBus.system,
|
||||
'org.freedesktop.ConsoleKit',
|
||||
'/org/freedesktop/ConsoleKit/Manager');
|
||||
this._upClient = new UPowerGlib.Client();
|
||||
},
|
||||
|
||||
// Having this function is a bit of a hack since the Systemd and ConsoleKit
|
||||
@ -177,19 +203,6 @@ const LoginManagerConsoleKit = new Lang.Class({
|
||||
return this._currentSession;
|
||||
},
|
||||
|
||||
get sessionActive() {
|
||||
if (this._sessionActive !== undefined)
|
||||
return this._sessionActive;
|
||||
|
||||
let session = this.getCurrentSessionProxy();
|
||||
session.connectSignal('ActiveChanged', Lang.bind(this, function(object, senderName, [isActive]) {
|
||||
this._sessionActive = isActive;
|
||||
}));
|
||||
[this._sessionActive] = session.IsActiveSync();
|
||||
|
||||
return this._sessionActive;
|
||||
},
|
||||
|
||||
canPowerOff: function(asyncCallback) {
|
||||
this._proxy.CanStopRemote(function(result, error) {
|
||||
if (error)
|
||||
@ -209,10 +222,7 @@ const LoginManagerConsoleKit = new Lang.Class({
|
||||
},
|
||||
|
||||
canSuspend: function(asyncCallback) {
|
||||
Mainloop.idle_add(Lang.bind(this, function() {
|
||||
asyncCallback(this._upClient.get_can_suspend());
|
||||
return false;
|
||||
}));
|
||||
},
|
||||
|
||||
powerOff: function() {
|
||||
@ -224,6 +234,12 @@ const LoginManagerConsoleKit = new Lang.Class({
|
||||
},
|
||||
|
||||
suspend: function() {
|
||||
this._upClient.suspend_sync(null);
|
||||
this.emit('prepare-for-sleep', true);
|
||||
this.emit('prepare-for-sleep', false);
|
||||
},
|
||||
|
||||
inhibit: function(reason, callback) {
|
||||
callback(null);
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(LoginManagerConsoleKit.prototype);
|
||||
|
@ -2,9 +2,93 @@
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const NMGtk = imports.gi.NMGtk;
|
||||
const Signals = imports.signals;
|
||||
|
||||
// _getMobileProvidersDatabase:
|
||||
//
|
||||
// Gets the database of mobile providers, with references between MCCMNC/SID and
|
||||
// operator name
|
||||
//
|
||||
let _mpd;
|
||||
function _getMobileProvidersDatabase() {
|
||||
if (_mpd == null) {
|
||||
try {
|
||||
_mpd = new NMGtk.MobileProvidersDatabase();
|
||||
_mpd.init(null);
|
||||
} catch (e) {
|
||||
log(e.message);
|
||||
_mpd = null;
|
||||
}
|
||||
}
|
||||
|
||||
return _mpd;
|
||||
}
|
||||
|
||||
// _findProviderForMccMnc:
|
||||
// @operator_name: operator name
|
||||
// @operator_code: operator code
|
||||
//
|
||||
// Given an operator name string (which may not be a real operator name) and an
|
||||
// operator code string, tries to find a proper operator name to display.
|
||||
//
|
||||
function _findProviderForMccMnc(operator_name, operator_code) {
|
||||
if (operator_name) {
|
||||
if (operator_name.length != 0 &&
|
||||
(operator_name.length > 6 || operator_name.length < 5)) {
|
||||
// this looks like a valid name, i.e. not an MCCMNC (that some
|
||||
// devices return when not yet connected
|
||||
return operator_name;
|
||||
}
|
||||
|
||||
if (isNaN(parseInt(operator_name))) {
|
||||
// name is definitely not a MCCMNC, so it may be a name
|
||||
// after all; return that
|
||||
return operator_name;
|
||||
}
|
||||
}
|
||||
|
||||
let needle;
|
||||
if ((!operator_name || operator_name.length == 0) && operator_code)
|
||||
needle = operator_code;
|
||||
else if (operator_name && (operator_name.length == 6 || operator_name.length == 5))
|
||||
needle = operator_name;
|
||||
else // nothing to search
|
||||
return null;
|
||||
|
||||
let mpd = _getMobileProvidersDatabase();
|
||||
if (mpd) {
|
||||
let provider = mpd.lookup_3gpp_mcc_mnc(needle);
|
||||
if (provider)
|
||||
return provider.get_name();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// _findProviderForSid:
|
||||
// @sid: System Identifier of the serving CDMA network
|
||||
//
|
||||
// Tries to find the operator name corresponding to the given SID
|
||||
//
|
||||
function _findProviderForSid(sid) {
|
||||
if (sid == 0)
|
||||
return null;
|
||||
|
||||
let mpd = _getMobileProvidersDatabase();
|
||||
if (mpd) {
|
||||
let provider = mpd.lookup_cdma_sid(sid);
|
||||
if (provider)
|
||||
return provider.get_name();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Support for the old ModemManager interface (MM < 0.7)
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// The following are not the complete interfaces, just the methods we need
|
||||
// (or may need in the future)
|
||||
|
||||
@ -42,76 +126,6 @@ const ModemCdmaInterface = <interface name="org.freedesktop.ModemManager.Modem.C
|
||||
|
||||
const ModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(ModemCdmaInterface);
|
||||
|
||||
let _providersTable;
|
||||
function _getProvidersTable() {
|
||||
if (_providersTable)
|
||||
return _providersTable;
|
||||
return _providersTable = Shell.mobile_providers_parse(null,null);
|
||||
}
|
||||
|
||||
function findProviderForMCCMNC(table, needle) {
|
||||
let needlemcc = needle.substring(0, 3);
|
||||
let needlemnc = needle.substring(3, needle.length);
|
||||
|
||||
let name2, name3;
|
||||
for (let iter in table) {
|
||||
let country = table[iter];
|
||||
let providers = country.get_providers();
|
||||
|
||||
// Search through each country's providers
|
||||
for (let i = 0; i < providers.length; i++) {
|
||||
let provider = providers[i];
|
||||
|
||||
// Search through MCC/MNC list
|
||||
let list = provider.get_gsm_mcc_mnc();
|
||||
for (let j = 0; j < list.length; j++) {
|
||||
let mccmnc = list[j];
|
||||
|
||||
// Match both 2-digit and 3-digit MNC; prefer a
|
||||
// 3-digit match if found, otherwise a 2-digit one.
|
||||
if (mccmnc.mcc != needlemcc)
|
||||
continue; // MCC was wrong
|
||||
|
||||
if (!name3 && needle.length == 6 && needlemnc == mccmnc.mnc)
|
||||
name3 = provider.name;
|
||||
|
||||
if (!name2 && needlemnc.substring(0, 2) == mccmnc.mnc.substring(0, 2))
|
||||
name2 = provider.name;
|
||||
|
||||
if (name2 && name3)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return name3 || name2 || null;
|
||||
}
|
||||
|
||||
function findProviderForSid(table, sid) {
|
||||
if (sid == 0)
|
||||
return null;
|
||||
|
||||
// Search through each country
|
||||
for (let iter in table) {
|
||||
let country = table[iter];
|
||||
let providers = country.get_providers();
|
||||
|
||||
// Search through each country's providers
|
||||
for (let i = 0; i < providers.length; i++) {
|
||||
let provider = providers[i];
|
||||
let cdma_sid = provider.get_cdma_sid();
|
||||
|
||||
// Search through CDMA SID list
|
||||
for (let j = 0; j < cdma_sid.length; j++) {
|
||||
if (cdma_sid[j] == sid)
|
||||
return provider.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const ModemGsm = new Lang.Class({
|
||||
Name: 'ModemGsm',
|
||||
|
||||
@ -127,7 +141,7 @@ const ModemGsm = new Lang.Class({
|
||||
this.emit('notify::signal-quality');
|
||||
}));
|
||||
this._proxy.connectSignal('RegistrationInfo', Lang.bind(this, function(proxy, sender, [status, code, name]) {
|
||||
this.operator_name = this._findOperatorName(name, code);
|
||||
this.operator_name = _findProviderForMccMnc(name, code);
|
||||
this.emit('notify::operator-name');
|
||||
}));
|
||||
this._proxy.GetRegistrationInfoRemote(Lang.bind(this, function([result], err) {
|
||||
@ -137,7 +151,7 @@ const ModemGsm = new Lang.Class({
|
||||
}
|
||||
|
||||
let [status, code, name] = result;
|
||||
this.operator_name = this._findOperatorName(name, code);
|
||||
this.operator_name = _findProviderForMccMnc(name, code);
|
||||
this.emit('notify::operator-name');
|
||||
}));
|
||||
this._proxy.GetSignalQualityRemote(Lang.bind(this, function(result, err) {
|
||||
@ -150,32 +164,6 @@ const ModemGsm = new Lang.Class({
|
||||
}
|
||||
this.emit('notify::signal-quality');
|
||||
}));
|
||||
},
|
||||
|
||||
_findOperatorName: function(name, opCode) {
|
||||
if (name) {
|
||||
if (name && name.length != 0 && (name.length > 6 || name.length < 5)) {
|
||||
// this looks like a valid name, i.e. not an MCCMNC (that some
|
||||
// devices return when not yet connected
|
||||
return name;
|
||||
}
|
||||
if (isNaN(parseInt(name))) {
|
||||
// name is definitely not a MCCMNC, so it may be a name
|
||||
// after all; return that
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
let needle;
|
||||
if ((name == null || name.length == 0) && opCode)
|
||||
needle = opCode;
|
||||
else if (name.length == 6 || name.length == 5)
|
||||
needle = name;
|
||||
else // nothing to search
|
||||
return null;
|
||||
|
||||
let table = _getProvidersTable();
|
||||
return findProviderForMCCMNC(table, needle);
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(ModemGsm.prototype);
|
||||
@ -215,15 +203,99 @@ const ModemCdma = new Lang.Class({
|
||||
// it will return an error if the device is not connected
|
||||
this.operator_name = null;
|
||||
} else {
|
||||
let [bandClass, band, id] = result;
|
||||
if (name.length > 0) {
|
||||
let table = _getProvidersTable();
|
||||
this.operator_name = findProviderForSid(table, id);
|
||||
} else
|
||||
this.operator_name = null;
|
||||
let [bandClass, band, sid] = result;
|
||||
|
||||
this.operator_name = _findProviderForSid(sid)
|
||||
}
|
||||
this.emit('notify::operator-name');
|
||||
}));
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(ModemCdma.prototype);
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Support for the new ModemManager1 interface (MM >= 0.7)
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const BroadbandModemInterface = <interface name="org.freedesktop.ModemManager1.Modem">
|
||||
<property name="SignalQuality" type="(ub)" access="read" />
|
||||
</interface>;
|
||||
const BroadbandModemProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemInterface);
|
||||
|
||||
const BroadbandModem3gppInterface = <interface name="org.freedesktop.ModemManager1.Modem.Modem3gpp">
|
||||
<property name="OperatorCode" type="s" access="read" />
|
||||
<property name="OperatorName" type="s" access="read" />
|
||||
</interface>;
|
||||
const BroadbandModem3gppProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModem3gppInterface);
|
||||
|
||||
const BroadbandModemCdmaInterface = <interface name="org.freedesktop.ModemManager1.Modem.ModemCdma">
|
||||
<property name="Sid" type="u" access="read" />
|
||||
</interface>;
|
||||
const BroadbandModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemCdmaInterface);
|
||||
|
||||
const BroadbandModem = new Lang.Class({
|
||||
Name: 'BroadbandModem',
|
||||
|
||||
_init: function(path, capabilities) {
|
||||
this._proxy = new BroadbandModemProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
|
||||
this._proxy_3gpp = new BroadbandModem3gppProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
|
||||
this._proxy_cdma = new BroadbandModemCdmaProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
|
||||
this._capabilities = capabilities;
|
||||
|
||||
this._proxy.connect('g-properties-changed', Lang.bind(this, function(proxy, properties) {
|
||||
if ('SignalQuality' in properties.deep_unpack())
|
||||
this._reloadSignalQuality();
|
||||
}));
|
||||
this._reloadSignalQuality();
|
||||
|
||||
this._proxy_3gpp.connect('g-properties-changed', Lang.bind(this, function(proxy, properties) {
|
||||
let unpacked = properties.deep_unpack();
|
||||
if ('OperatorName' in unpacked || 'OperatorCode' in unpacked)
|
||||
this._reload3gppOperatorName();
|
||||
}));
|
||||
this._reload3gppOperatorName();
|
||||
|
||||
this._proxy_cdma.connect('g-properties-changed', Lang.bind(this, function(proxy, properties) {
|
||||
let unpacked = properties.deep_unpack();
|
||||
if ('Nid' in unpacked || 'Sid' in unpacked)
|
||||
this._reloadCdmaOperatorName();
|
||||
}));
|
||||
this._reloadCdmaOperatorName();
|
||||
},
|
||||
|
||||
_reloadSignalQuality: function() {
|
||||
let [quality, recent] = this._proxy.SignalQuality;
|
||||
this.signal_quality = quality;
|
||||
this.emit('notify::signal-quality');
|
||||
},
|
||||
|
||||
_reloadOperatorName: function() {
|
||||
let new_name = "";
|
||||
if (this.operator_name_3gpp && this.operator_name_3gpp.length > 0)
|
||||
new_name += this.operator_name_3gpp;
|
||||
|
||||
if (this.operator_name_cdma && this.operator_name_cdma.length > 0) {
|
||||
if (new_name != "")
|
||||
new_name += ", ";
|
||||
new_name += this.operator_name_cdma;
|
||||
}
|
||||
|
||||
this.operator_name = new_name;
|
||||
this.emit('notify::operator-name');
|
||||
},
|
||||
|
||||
_reload3gppOperatorName: function() {
|
||||
let name = this._proxy_3gpp.OperatorName;
|
||||
let code = this._proxy_3gpp.OperatorCode;
|
||||
this.operator_name_3gpp = _findProviderForMccMnc(name, code);
|
||||
this._reloadOperatorName();
|
||||
},
|
||||
|
||||
_reloadCdmaOperatorName: function() {
|
||||
let sid = this._proxy_cdma.Sid;
|
||||
this.operator_name_cdma = _findProviderForSid(sid);
|
||||
this._reloadOperatorName();
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(BroadbandModem.prototype);
|
||||
|
@ -153,6 +153,7 @@ const ViewByCategories = new Lang.Class({
|
||||
y_fill: false,
|
||||
style_class: 'vfade' });
|
||||
this._categoryScroll.add_actor(this._categoryBox);
|
||||
this._categoryScroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||
this.actor.add(this._view.actor, { expand: true, x_fill: true, y_fill: true });
|
||||
this.actor.add(this._categoryScroll, { expand: false, y_fill: false, y_align: St.Align.START });
|
||||
|
||||
|
@ -220,25 +220,8 @@ const BoxPointer = new Lang.Class({
|
||||
this.bin.allocate(childBox, flags);
|
||||
|
||||
if (this._sourceActor && this._sourceActor.mapped) {
|
||||
this._reposition(this._sourceActor, this._arrowAlignment);
|
||||
|
||||
if (this._shouldFlip()) {
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
this._arrowSide = St.Side.BOTTOM;
|
||||
break;
|
||||
case St.Side.BOTTOM:
|
||||
this._arrowSide = St.Side.TOP;
|
||||
break;
|
||||
case St.Side.LEFT:
|
||||
this._arrowSide = St.Side.RIGHT;
|
||||
break;
|
||||
case St.Side.RIGHT:
|
||||
this._arrowSide = St.Side.LEFT;
|
||||
break;
|
||||
}
|
||||
this._reposition(this._sourceActor, this._arrowAlignment);
|
||||
}
|
||||
this._reposition();
|
||||
this._updateFlip();
|
||||
}
|
||||
},
|
||||
|
||||
@ -405,11 +388,11 @@ const BoxPointer = new Lang.Class({
|
||||
cr.setLineWidth(borderWidth);
|
||||
cr.stroke();
|
||||
}
|
||||
|
||||
cr.$dispose();
|
||||
},
|
||||
|
||||
setPosition: function(sourceActor, alignment) {
|
||||
this._arrowSide = this._userArrowSide;
|
||||
|
||||
// We need to show it now to force an allocation,
|
||||
// so that we can query the correct size.
|
||||
this.actor.show();
|
||||
@ -417,7 +400,8 @@ const BoxPointer = new Lang.Class({
|
||||
this._sourceActor = sourceActor;
|
||||
this._arrowAlignment = alignment;
|
||||
|
||||
this._reposition(sourceActor, alignment);
|
||||
this._reposition();
|
||||
this._updateFlip();
|
||||
},
|
||||
|
||||
setSourceAlignment: function(alignment) {
|
||||
@ -429,7 +413,10 @@ const BoxPointer = new Lang.Class({
|
||||
this.setPosition(this._sourceActor, this._arrowAlignment);
|
||||
},
|
||||
|
||||
_reposition: function(sourceActor, alignment) {
|
||||
_reposition: function() {
|
||||
let sourceActor = this._sourceActor;
|
||||
let alignment = this._arrowAlignment;
|
||||
|
||||
// Position correctly relative to the sourceActor
|
||||
let sourceNode = sourceActor.get_theme_node();
|
||||
let sourceContentBox = sourceNode.get_content_box(sourceActor.get_allocation_box());
|
||||
@ -553,7 +540,7 @@ const BoxPointer = new Lang.Class({
|
||||
_shiftActor : function() {
|
||||
// Since the position of the BoxPointer depends on the allocated size
|
||||
// of the BoxPointer and the position of the source actor, trying
|
||||
// to position the BoxPoiner via the x/y properties will result in
|
||||
// to position the BoxPointer via the x/y properties will result in
|
||||
// allocation loops and warnings. Instead we do the positioning via
|
||||
// the anchor point, which is independent of allocation, and leave
|
||||
// x == y == 0.
|
||||
@ -561,37 +548,47 @@ const BoxPointer = new Lang.Class({
|
||||
-(this._yPosition + this._yOffset));
|
||||
},
|
||||
|
||||
_shouldFlip: function() {
|
||||
_calculateArrowSide: function(arrowSide) {
|
||||
let sourceAllocation = Shell.util_get_transformed_allocation(this._sourceActor);
|
||||
let boxAllocation = Shell.util_get_transformed_allocation(this.actor);
|
||||
let boxWidth = boxAllocation.x2 - boxAllocation.x1;
|
||||
let boxHeight = boxAllocation.y2 - boxAllocation.y1;
|
||||
let [minWidth, minHeight, boxWidth, boxHeight] = this._container.get_preferred_size();
|
||||
let monitor = Main.layoutManager.findMonitorForActor(this.actor);
|
||||
|
||||
switch (this._arrowSide) {
|
||||
switch (arrowSide) {
|
||||
case St.Side.TOP:
|
||||
if (boxAllocation.y2 > monitor.y + monitor.height &&
|
||||
if (sourceAllocation.y2 + boxHeight > monitor.y + monitor.height &&
|
||||
boxHeight < sourceAllocation.y1 - monitor.y)
|
||||
return true;
|
||||
return St.Side.BOTTOM;
|
||||
break;
|
||||
case St.Side.BOTTOM:
|
||||
if (boxAllocation.y1 < monitor.y &&
|
||||
if (sourceAllocation.y1 - boxHeight < monitor.y &&
|
||||
boxHeight < monitor.y + monitor.height - sourceAllocation.y2)
|
||||
return true;
|
||||
return St.Side.TOP;
|
||||
break;
|
||||
case St.Side.LEFT:
|
||||
if (boxAllocation.x2 > monitor.x + monitor.width &&
|
||||
if (sourceAllocation.y2 + boxWidth > monitor.x + monitor.width &&
|
||||
boxWidth < sourceAllocation.x1 - monitor.x)
|
||||
return true;
|
||||
return St.Side.RIGHT;
|
||||
break;
|
||||
case St.Side.RIGHT:
|
||||
if (boxAllocation.x1 < monitor.x &&
|
||||
if (sourceAllocation.y1 - boxWidth < monitor.x &&
|
||||
boxWidth < monitor.x + monitor.width - sourceAllocation.x2)
|
||||
return true;
|
||||
return St.Side.LEFT;
|
||||
break;
|
||||
}
|
||||
|
||||
return arrowSide;
|
||||
},
|
||||
|
||||
_updateFlip: function() {
|
||||
let arrowSide = this._calculateArrowSide(this._userArrowSide);
|
||||
if (this._arrowSide != arrowSide) {
|
||||
this._arrowSide = arrowSide;
|
||||
this._reposition();
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
|
||||
this._container.queue_relayout();
|
||||
return false;
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
set xOffset(offset) {
|
||||
|
@ -8,7 +8,6 @@ const Params = imports.misc.params;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const GnomeSession = imports.misc.gnomeSession;
|
||||
const LoginManager = imports.misc.loginManager;
|
||||
const Main = imports.ui.main;
|
||||
const ShellMountOperation = imports.ui.shellMountOperation;
|
||||
|
||||
@ -33,7 +32,6 @@ const AutomountManager = new Lang.Class({
|
||||
Lang.bind(this, this._InhibitorsChanged));
|
||||
this._inhibited = false;
|
||||
|
||||
this._loginManager = LoginManager.getLoginManager();
|
||||
this._volumeMonitor = Gio.VolumeMonitor.get();
|
||||
},
|
||||
|
||||
@ -85,25 +83,29 @@ const AutomountManager = new Lang.Class({
|
||||
_onDriveConnected: function() {
|
||||
// if we're not in the current ConsoleKit session,
|
||||
// or screensaver is active, don't play sounds
|
||||
if (!this._loginManager.sessionActive)
|
||||
if (!this._session.SessionIsActive)
|
||||
return;
|
||||
|
||||
global.play_theme_sound(0, 'device-added-media');
|
||||
global.play_theme_sound(0, 'device-added-media',
|
||||
_("External drive connected"),
|
||||
null);
|
||||
},
|
||||
|
||||
_onDriveDisconnected: function() {
|
||||
// if we're not in the current ConsoleKit session,
|
||||
// or screensaver is active, don't play sounds
|
||||
if (!this._loginManager.sessionActive)
|
||||
if (!this._session.SessionIsActive)
|
||||
return;
|
||||
|
||||
global.play_theme_sound(0, 'device-removed-media');
|
||||
global.play_theme_sound(0, 'device-removed-media',
|
||||
_("External drive disconnected"),
|
||||
null);
|
||||
},
|
||||
|
||||
_onDriveEjectButton: function(monitor, drive) {
|
||||
// TODO: this code path is not tested, as the GVfs volume monitor
|
||||
// doesn't emit this signal just yet.
|
||||
if (!this._loginManager.sessionActive)
|
||||
if (!this._session.SessionIsActive)
|
||||
return;
|
||||
|
||||
// we force stop/eject in this case, so we don't have to pass a
|
||||
@ -143,7 +145,7 @@ const AutomountManager = new Lang.Class({
|
||||
if (params.checkSession) {
|
||||
// if we're not in the current ConsoleKit session,
|
||||
// don't attempt automount
|
||||
if (!this._loginManager.sessionActive)
|
||||
if (!this._session.SessionIsActive)
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,6 @@ const Lang = imports.lang;
|
||||
const Gio = imports.gi.Gio;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const LoginManager = imports.misc.loginManager;
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const ShellMountOperation = imports.ui.shellMountOperation;
|
||||
@ -162,8 +161,6 @@ const AutorunManager = new Lang.Class({
|
||||
Name: 'AutorunManager',
|
||||
|
||||
_init: function() {
|
||||
this._loginManager = LoginManager.getLoginManager();
|
||||
|
||||
this._volumeMonitor = Gio.VolumeMonitor.get();
|
||||
|
||||
this._transDispatcher = new AutorunTransientDispatcher(this);
|
||||
@ -215,7 +212,7 @@ const AutorunManager = new Lang.Class({
|
||||
_onMountAdded: function(monitor, mount) {
|
||||
// don't do anything if our session is not the currently
|
||||
// active one
|
||||
if (!this._loginManager.sessionActive)
|
||||
if (!this._session.SessionIsActive)
|
||||
return;
|
||||
|
||||
this._processMount(mount, true);
|
||||
@ -293,7 +290,6 @@ const AutorunResidentSource = new Lang.Class({
|
||||
|
||||
_init: function(manager) {
|
||||
this.parent(_("Removable Devices"), 'media-removable');
|
||||
this.showInLockScreen = false;
|
||||
|
||||
this._mounts = [];
|
||||
|
||||
@ -301,6 +297,10 @@ const AutorunResidentSource = new Lang.Class({
|
||||
this._notification = new AutorunResidentNotification(this._manager, this);
|
||||
},
|
||||
|
||||
_createPolicy: function() {
|
||||
return new MessageTray.NotificationPolicy({ showInLockScreen: false });
|
||||
},
|
||||
|
||||
buildRightClickMenu: function() {
|
||||
return null;
|
||||
},
|
||||
|
@ -60,18 +60,14 @@ const KeyringDialog = new Lang.Class({
|
||||
|
||||
this._controlTable = null;
|
||||
|
||||
let buttons = [{ label: '',
|
||||
action: Lang.bind(this, this._onCancelButton),
|
||||
key: Clutter.Escape
|
||||
},
|
||||
{ label: '',
|
||||
action: Lang.bind(this, this._onContinueButton),
|
||||
default: true
|
||||
}]
|
||||
|
||||
this.setButtons(buttons);
|
||||
this._cancelButton = buttons[0].button;
|
||||
this._continueButton = buttons[1].button;
|
||||
this._cancelButton = this.addButton({ label: '',
|
||||
action: Lang.bind(this, this._onCancelButton),
|
||||
key: Clutter.Escape });
|
||||
this._continueButton = this.addButton({ label: '',
|
||||
action: Lang.bind(this, this._onContinueButton),
|
||||
default: true },
|
||||
{ expand: true, x_fill: false, x_align: St.Align.END });
|
||||
|
||||
this.prompt.bind_property('cancel-label', this._cancelButton, 'label', GObject.BindingFlags.SYNC_CREATE);
|
||||
this.prompt.bind_property('continue-label', this._continueButton, 'label', GObject.BindingFlags.SYNC_CREATE);
|
||||
@ -146,6 +142,14 @@ const KeyringDialog = new Lang.Class({
|
||||
this._messageBox.add(table, { x_fill: true, y_fill: true });
|
||||
},
|
||||
|
||||
_updateSensitivity: function(sensitive) {
|
||||
this._passwordEntry.reactive = sensitive;
|
||||
this._passwordEntry.clutter_text.editable = sensitive;
|
||||
|
||||
this._continueButton.can_focus = sensitive;
|
||||
this._continueButton.reactive = sensitive;
|
||||
},
|
||||
|
||||
_ensureOpen: function() {
|
||||
// NOTE: ModalDialog.open() is safe to call if the dialog is
|
||||
// already open - it just returns true without side-effects
|
||||
@ -167,12 +171,14 @@ const KeyringDialog = new Lang.Class({
|
||||
_onShowPassword: function(prompt) {
|
||||
this._buildControlTable();
|
||||
this._ensureOpen();
|
||||
this._updateSensitivity(true);
|
||||
this._passwordEntry.grab_key_focus();
|
||||
},
|
||||
|
||||
_onShowConfirm: function(prompt) {
|
||||
this._buildControlTable();
|
||||
this._ensureOpen();
|
||||
this._updateSensitivity(true);
|
||||
this._continueButton.grab_key_focus();
|
||||
},
|
||||
|
||||
@ -192,6 +198,7 @@ const KeyringDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
_onContinueButton: function() {
|
||||
this._updateSensitivity(false);
|
||||
this.prompt.complete();
|
||||
},
|
||||
|
||||
|
@ -159,14 +159,13 @@ const AuthenticationDialog = new Lang.Class({
|
||||
messageBox.add(this._nullMessageLabel);
|
||||
this._nullMessageLabel.show();
|
||||
|
||||
this.setButtons([{ label: _("Cancel"),
|
||||
this._cancelButton = this.addButton({ label: _("Cancel"),
|
||||
action: Lang.bind(this, this.cancel),
|
||||
key: Clutter.Escape
|
||||
},
|
||||
{ label: _("Authenticate"),
|
||||
key: Clutter.Escape });
|
||||
this._okButton = this.addButton({ label: _("Authenticate"),
|
||||
action: Lang.bind(this, this._onAuthenticateButtonPressed),
|
||||
default: true
|
||||
}]);
|
||||
default: true },
|
||||
{ expand: true, x_fill: false, x_align: St.Align.END });
|
||||
|
||||
this._doneEmitted = false;
|
||||
|
||||
@ -214,8 +213,17 @@ const AuthenticationDialog = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_updateSensitivity: function(sensitive) {
|
||||
this._passwordEntry.reactive = sensitive;
|
||||
this._passwordEntry.clutter_text.editable = sensitive;
|
||||
|
||||
this._okButton.can_focus = sensitive;
|
||||
this._okButton.reactive = sensitive;
|
||||
},
|
||||
|
||||
_onEntryActivate: function() {
|
||||
let response = this._passwordEntry.get_text();
|
||||
this._updateSensitivity(false);
|
||||
this._session.response(response);
|
||||
// When the user responds, dismiss already shown info and
|
||||
// error texts (if any)
|
||||
@ -269,6 +277,7 @@ const AuthenticationDialog = new Lang.Class({
|
||||
this._passwordBox.show();
|
||||
this._passwordEntry.set_text('');
|
||||
this._passwordEntry.grab_key_focus();
|
||||
this._updateSensitivity(true);
|
||||
this._ensureOpen();
|
||||
},
|
||||
|
||||
|
@ -20,8 +20,8 @@ const Recorder = new Lang.Class({
|
||||
Main.wm.addKeybinding('toggle-recording',
|
||||
this._bindingSettings,
|
||||
Meta.KeyBindingFlags.NONE,
|
||||
Main.KeybindingMode.NORMAL |
|
||||
Main.KeybindingMode.OVERVIEW,
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
Lang.bind(this, this._toggleRecorder));
|
||||
},
|
||||
|
||||
|
@ -13,6 +13,7 @@ const Tp = imports.gi.TelepathyGLib;
|
||||
const History = imports.misc.history;
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const NotificationDaemon = imports.ui.notificationDaemon;
|
||||
const Params = imports.misc.params;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
@ -415,6 +416,8 @@ const TelepathyClient = new Lang.Class({
|
||||
_ensureAppSource: function() {
|
||||
if (this._appSource == null) {
|
||||
this._appSource = new MessageTray.Source(_("Chat"), 'empathy');
|
||||
this._appSource.policy = new NotificationDaemon.NotificationApplicationPolicy('empathy');
|
||||
|
||||
Main.messageTray.add(this._appSource);
|
||||
this._appSource.connect('destroy', Lang.bind(this, function () {
|
||||
this._appSource = null;
|
||||
@ -484,6 +487,10 @@ const ChatSource = new Lang.Class({
|
||||
return rightClickMenu;
|
||||
},
|
||||
|
||||
_createPolicy: function() {
|
||||
return new NotificationDaemon.NotificationApplicationPolicy('empathy');
|
||||
},
|
||||
|
||||
_updateAlias: function() {
|
||||
let oldAlias = this.title;
|
||||
let newAlias = this._contact.get_alias();
|
||||
@ -1048,6 +1055,10 @@ const ApproverSource = new Lang.Class({
|
||||
}));
|
||||
},
|
||||
|
||||
_createPolicy: function() {
|
||||
return new NotificationDaemon.NotificationApplicationPolicy('empathy');
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
if (this._invalidId != 0) {
|
||||
this._dispatchOp.disconnect(this._invalidId);
|
||||
@ -1346,9 +1357,8 @@ const AccountNotification = new Lang.Class({
|
||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||
switch (action) {
|
||||
case 'view':
|
||||
let cmd = '/usr/bin/empathy-accounts'
|
||||
+ ' --select-account=%s'
|
||||
.format(account.get_path_suffix());
|
||||
let cmd = 'empathy-accounts --select-account=' +
|
||||
account.get_path_suffix();
|
||||
let app_info = Gio.app_info_create_from_commandline(cmd, null, 0);
|
||||
app_info.launch([], global.create_app_launch_context());
|
||||
break;
|
||||
|
@ -32,6 +32,7 @@ function _onVertSepRepaint (area)
|
||||
cr.setDash([1, 3], 1); // Hard-code for now
|
||||
cr.setLineWidth(stippleWidth);
|
||||
cr.stroke();
|
||||
cr.$dispose();
|
||||
};
|
||||
|
||||
const DateMenuButton = new Lang.Class({
|
||||
@ -83,12 +84,26 @@ const DateMenuButton = new Lang.Class({
|
||||
}));
|
||||
vbox.add(this._calendar.actor);
|
||||
|
||||
item = this.menu.addSettingsAction(_("Date and Time Settings"), 'gnome-datetime-panel.desktop');
|
||||
if (item) {
|
||||
let separator = new PopupMenu.PopupSeparatorMenuItem();
|
||||
separator.setColumnWidths(1);
|
||||
vbox.add(separator.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||
|
||||
this._openCalendarItem = new PopupMenu.PopupMenuItem(_("Open Calendar"));
|
||||
this._openCalendarItem.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
|
||||
this._openCalendarItem.actor.can_focus = false;
|
||||
vbox.add(this._openCalendarItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||
|
||||
this._openClocksItem = new PopupMenu.PopupMenuItem(_("Open Clocks"));
|
||||
this._openClocksItem.connect('activate', Lang.bind(this, this._onOpenClocksActivate));
|
||||
this._openClocksItem.actor.can_focus = false;
|
||||
vbox.add(this._openClocksItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||
|
||||
Shell.AppSystem.get_default().connect('installed-changed',
|
||||
Lang.bind(this, this._appInstalledChanged));
|
||||
this._appInstalledChanged();
|
||||
|
||||
item = this.menu.addSettingsAction(_("Date & Time Settings"), 'gnome-datetime-panel.desktop');
|
||||
if (item) {
|
||||
item.actor.show_on_set_parent = false;
|
||||
item.actor.can_focus = false;
|
||||
item.actor.reparent(vbox);
|
||||
@ -108,16 +123,6 @@ const DateMenuButton = new Lang.Class({
|
||||
// Event list
|
||||
vbox.add(this._eventList.actor, { expand: true });
|
||||
|
||||
this._openCalendarItem = new PopupMenu.PopupMenuItem(_("Open Calendar"));
|
||||
this._openCalendarItem.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
|
||||
this._openCalendarItem.actor.can_focus = false;
|
||||
vbox.add(this._openCalendarItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||
|
||||
this._calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' });
|
||||
this._calendarSettings.connect('changed::exec',
|
||||
Lang.bind(this, this._calendarSettingsChanged));
|
||||
this._calendarSettingsChanged();
|
||||
|
||||
// Whenever the menu is opened, select today
|
||||
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
|
||||
if (isOpen) {
|
||||
@ -151,10 +156,9 @@ const DateMenuButton = new Lang.Class({
|
||||
this._sessionUpdated();
|
||||
},
|
||||
|
||||
_calendarSettingsChanged: function() {
|
||||
let exec = this._calendarSettings.get_string('exec');
|
||||
let fullExec = GLib.find_program_in_path(exec);
|
||||
this._openCalendarItem.actor.visible = fullExec != null;
|
||||
_appInstalledChanged: function() {
|
||||
let app = Shell.AppSystem.get_default().lookup_app('gnome-clocks.desktop');
|
||||
this._openClocksItem.actor.visible = app !== null;
|
||||
},
|
||||
|
||||
_setEventsVisibility: function(visible) {
|
||||
@ -205,24 +209,14 @@ const DateMenuButton = new Lang.Class({
|
||||
|
||||
_onOpenCalendarActivate: function() {
|
||||
this.menu.close();
|
||||
let tool = this._calendarSettings.get_string('exec');
|
||||
if (tool.length == 0 || tool.substr(0, 9) == 'evolution') {
|
||||
// TODO: pass the selected day
|
||||
let app = Shell.AppSystem.get_default().lookup_app('evolution-calendar.desktop');
|
||||
|
||||
let app = Gio.AppInfo.get_default_for_type('text/calendar', false);
|
||||
app.launch([], global.create_app_launch_context());
|
||||
},
|
||||
|
||||
_onOpenClocksActivate: function() {
|
||||
this.menu.close();
|
||||
let app = Shell.AppSystem.get_default().lookup_app('gnome-clocks.desktop');
|
||||
app.activate();
|
||||
} else {
|
||||
let needTerm = this._calendarSettings.get_boolean('needs-term');
|
||||
if (needTerm) {
|
||||
let terminalSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.terminal' });
|
||||
let term = terminalSettings.get_string('exec');
|
||||
let arg = terminalSettings.get_string('exec-arg');
|
||||
if (arg != '')
|
||||
Util.spawn([term, arg, tool]);
|
||||
else
|
||||
Util.spawn([term, tool]);
|
||||
} else {
|
||||
Util.spawnCommandLine(tool)
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -106,11 +106,15 @@ function enableExtension(uuid) {
|
||||
|
||||
extensionOrder.push(uuid);
|
||||
|
||||
let stylesheetFile = extension.dir.get_child('stylesheet.css');
|
||||
let stylesheetNames = [global.session_mode + '.css', 'stylesheet.css'];
|
||||
for (let i = 0; i < stylesheetNames.length; i++) {
|
||||
let stylesheetFile = extension.dir.get_child(stylesheetNames[i]);
|
||||
if (stylesheetFile.query_exists(null)) {
|
||||
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
|
||||
theme.load_stylesheet(stylesheetFile.get_path());
|
||||
extension.stylesheet = stylesheetFile;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
extension.stateObj.enable();
|
||||
|
@ -1,45 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Lang = imports.lang;
|
||||
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const FLASHSPOT_ANIMATION_TIME = 0.25; // seconds
|
||||
|
||||
const Flashspot = new Lang.Class({
|
||||
Name: 'Flashspot',
|
||||
Extends: Lightbox.Lightbox,
|
||||
|
||||
_init: function(area) {
|
||||
this.parent(Main.uiGroup, { inhibitEvents: true,
|
||||
width: area.width,
|
||||
height: area.height });
|
||||
|
||||
this.actor.style_class = 'flashspot';
|
||||
this.actor.set_position(area.x, area.y);
|
||||
},
|
||||
|
||||
fire: function() {
|
||||
this.actor.opacity = 0;
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 255,
|
||||
time: FLASHSPOT_ANIMATION_TIME,
|
||||
transition: 'linear',
|
||||
onComplete: Lang.bind(this, this._onFireShowComplete)
|
||||
});
|
||||
this.actor.show();
|
||||
},
|
||||
|
||||
_onFireShowComplete: function() {
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 0,
|
||||
time: FLASHSPOT_ANIMATION_TIME,
|
||||
transition: 'linear',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this.destroy();
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
@ -183,6 +183,9 @@ const GrabHelper = new Lang.Class({
|
||||
else if (hadFocus || params.grabFocus)
|
||||
_navigateActor(newFocus);
|
||||
|
||||
if ((params.grabFocus || params.modal) && !this._capturedEventId)
|
||||
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
|
||||
|
||||
this._grabStack.push(params);
|
||||
return true;
|
||||
},
|
||||
@ -192,8 +195,6 @@ const GrabHelper = new Lang.Class({
|
||||
if (firstGrab) {
|
||||
if (!Main.pushModal(this._owner, this._modalParams))
|
||||
return false;
|
||||
|
||||
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
|
||||
}
|
||||
|
||||
this._modalCount++;
|
||||
@ -205,11 +206,6 @@ const GrabHelper = new Lang.Class({
|
||||
if (this._modalCount > 0)
|
||||
return;
|
||||
|
||||
if (this._capturedEventId > 0) {
|
||||
global.stage.disconnect(this._capturedEventId);
|
||||
this._capturedEventId = 0;
|
||||
}
|
||||
|
||||
Main.popModal(this._owner);
|
||||
global.sync_pointer();
|
||||
},
|
||||
@ -308,6 +304,11 @@ const GrabHelper = new Lang.Class({
|
||||
this._releaseFocusGrab();
|
||||
}
|
||||
|
||||
if (!this.grabbed && this._capturedEventId > 0) {
|
||||
global.stage.disconnect(this._capturedEventId);
|
||||
this._capturedEventId = 0;
|
||||
}
|
||||
|
||||
if (hadFocus) {
|
||||
let poppedGrab = poppedGrabs[0];
|
||||
_navigateActor(poppedGrab.savedFocus);
|
||||
@ -316,6 +317,13 @@ const GrabHelper = new Lang.Class({
|
||||
|
||||
_onCapturedEvent: function(actor, event) {
|
||||
let type = event.type();
|
||||
|
||||
if (type == Clutter.EventType.KEY_PRESS &&
|
||||
event.get_key_symbol() == Clutter.KEY_Escape) {
|
||||
this.ungrab({ isUser: true });
|
||||
return true;
|
||||
}
|
||||
|
||||
let press = type == Clutter.EventType.BUTTON_PRESS;
|
||||
let release = type == Clutter.EventType.BUTTON_RELEASE;
|
||||
let button = press || release;
|
||||
@ -328,12 +336,6 @@ const GrabHelper = new Lang.Class({
|
||||
if (!button && this._modalCount == 0)
|
||||
return false;
|
||||
|
||||
if (type == Clutter.EventType.KEY_PRESS &&
|
||||
event.get_key_symbol() == Clutter.KEY_Escape) {
|
||||
this.ungrab({ isUser: true });
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this._isWithinGrabbedActor(event.get_source()))
|
||||
return false;
|
||||
|
||||
|
@ -2,18 +2,21 @@
|
||||
|
||||
const Caribou = imports.gi.Caribou;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const DBus = imports.dbus;
|
||||
const Gdk = imports.gi.Gdk;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const BoxPointer = imports.ui.boxpointer;
|
||||
const Layout = imports.ui.layout;
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
|
||||
const KEYBOARD_REST_TIME = Layout.KEYBOARD_ANIMATION_TIME * 2 * 1000;
|
||||
|
||||
const KEYBOARD_SCHEMA = 'org.gnome.shell.keyboard';
|
||||
const KEYBOARD_TYPE = 'keyboard-type';
|
||||
|
||||
@ -56,14 +59,7 @@ const Key = new Lang.Class({
|
||||
if (this._key.name == 'Control_L' || this._key.name == 'Alt_L')
|
||||
this._key.latch = true;
|
||||
|
||||
this._key.connect('key-pressed', Lang.bind(this, function ()
|
||||
{ this.actor.checked = true }));
|
||||
this._key.connect('key-released', Lang.bind(this, function ()
|
||||
{ this.actor.checked = false; }));
|
||||
|
||||
if (this._extended_keys.length > 0) {
|
||||
this._grabbed = false;
|
||||
this._eventCaptureId = 0;
|
||||
this._key.connect('notify::show-subkeys', Lang.bind(this, this._onShowSubkeysChanged));
|
||||
this._boxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
|
||||
{ x_fill: true,
|
||||
@ -115,52 +111,23 @@ const Key = new Lang.Class({
|
||||
this._boxPointer.bin.add_actor(this._extended_keyboard);
|
||||
},
|
||||
|
||||
_onEventCapture: function (actor, event) {
|
||||
let source = event.get_source();
|
||||
let type = event.type();
|
||||
|
||||
if ((type == Clutter.EventType.BUTTON_PRESS ||
|
||||
type == Clutter.EventType.BUTTON_RELEASE) &&
|
||||
this._extended_keyboard.contains(source)) {
|
||||
source.extended_key.press();
|
||||
source.extended_key.release();
|
||||
return false;
|
||||
}
|
||||
if (type == Clutter.EventType.BUTTON_PRESS) {
|
||||
this._boxPointer.actor.hide();
|
||||
this._ungrab();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_ungrab: function () {
|
||||
global.stage.disconnect(this._eventCaptureId);
|
||||
this._eventCaptureId = 0;
|
||||
this._grabbed = false;
|
||||
Main.popModal(this.actor);
|
||||
get subkeys() {
|
||||
return this._boxPointer;
|
||||
},
|
||||
|
||||
_onShowSubkeysChanged: function () {
|
||||
if (this._key.show_subkeys) {
|
||||
this.actor.fake_release();
|
||||
this._boxPointer.actor.raise_top();
|
||||
this._boxPointer.setPosition(this.actor, 0.5);
|
||||
this._boxPointer.show(BoxPointer.PopupAnimation.FULL);
|
||||
this.emit('show-subkeys');
|
||||
this.actor.fake_release();
|
||||
this.actor.set_hover(false);
|
||||
if (!this._grabbed) {
|
||||
Main.pushModal(this.actor);
|
||||
this._eventCaptureId = global.stage.connect('captured-event', Lang.bind(this, this._onEventCapture));
|
||||
this._grabbed = true;
|
||||
}
|
||||
this._key.release();
|
||||
} else {
|
||||
if (this._grabbed)
|
||||
this._ungrab();
|
||||
this._boxPointer.hide(BoxPointer.PopupAnimation.FULL);
|
||||
this.emit('hide-subkeys');
|
||||
}
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(Key.prototype);
|
||||
|
||||
const Keyboard = new Lang.Class({
|
||||
// HACK: we can't set Name, because it collides with Name dbus property
|
||||
@ -175,16 +142,26 @@ const Keyboard = new Lang.Class({
|
||||
this._focusInExtendedKeys = false;
|
||||
|
||||
this._timestamp = global.display.get_current_time_roundtrip();
|
||||
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
|
||||
|
||||
this._keyboardSettings = new Gio.Settings({ schema: KEYBOARD_SCHEMA });
|
||||
this._keyboardSettings.connect('changed', Lang.bind(this, this._settingsChanged));
|
||||
this._a11yApplicationsSettings = new Gio.Settings({ schema: A11Y_APPLICATIONS_SCHEMA });
|
||||
this._a11yApplicationsSettings.connect('changed', Lang.bind(this, this._settingsChanged));
|
||||
this._settingsChanged();
|
||||
},
|
||||
|
||||
init: function () {
|
||||
this._showIdleId = 0;
|
||||
this._subkeysBoxPointer = null;
|
||||
this._capturedEventId = 0;
|
||||
this._capturedPress = false;
|
||||
|
||||
this._keyboardVisible = false;
|
||||
Main.layoutManager.connect('keyboard-visible-changed', Lang.bind(this, function(o, visible) {
|
||||
this._keyboardVisible = visible;
|
||||
}));
|
||||
this._keyboardRequested = false;
|
||||
this._keyboardRestingId = 0;
|
||||
|
||||
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
|
||||
this._redraw();
|
||||
},
|
||||
|
||||
@ -273,10 +250,14 @@ const Keyboard = new Lang.Class({
|
||||
return;
|
||||
|
||||
let time = global.get_current_time();
|
||||
if (focus instanceof Clutter.Text)
|
||||
this.Show(time);
|
||||
else
|
||||
if (!(focus instanceof Clutter.Text)) {
|
||||
this.Hide(time);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._showIdleId)
|
||||
this._showIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE,
|
||||
Lang.bind(this, function() { this.Show(time); }));
|
||||
},
|
||||
|
||||
_addKeys: function () {
|
||||
@ -331,6 +312,19 @@ const Keyboard = new Lang.Class({
|
||||
return trayButton;
|
||||
},
|
||||
|
||||
_onCapturedEvent: function(actor, event) {
|
||||
let type = event.type();
|
||||
let press = type == Clutter.EventType.BUTTON_PRESS;
|
||||
let release = type == Clutter.EventType.BUTTON_RELEASE;
|
||||
|
||||
if (press)
|
||||
this._capturedPress = true;
|
||||
else if (release && this._capturedPress)
|
||||
this._hideSubkeys();
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_addRows : function (keys, layout) {
|
||||
let keyboard_row = new St.BoxLayout();
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
@ -353,6 +347,19 @@ const Keyboard = new Lang.Class({
|
||||
// Add new key for hiding message tray
|
||||
right_box.add(this._getTrayIcon());
|
||||
}
|
||||
|
||||
button.connect('show-subkeys', Lang.bind(this, function() {
|
||||
if (this._subkeysBoxPointer)
|
||||
this._subkeysBoxPointer.hide(BoxPointer.PopupAnimation.FULL);
|
||||
this._subkeysBoxPointer = button.subkeys;
|
||||
this._subkeysBoxPointer.show(BoxPointer.PopupAnimation.FULL);
|
||||
if (!this._capturedEventId)
|
||||
this._capturedEventId = this.actor.connect('captured-event',
|
||||
Lang.bind(this, this._onCapturedEvent));
|
||||
}));
|
||||
button.connect('hide-subkeys', Lang.bind(this, function() {
|
||||
this._hideSubkeys();
|
||||
}));
|
||||
}
|
||||
keyboard_row.add(left_box, { expand: true, x_fill: false, x_align: St.Align.START });
|
||||
keyboard_row.add(right_box, { expand: true, x_fill: false, x_align: St.Align.END });
|
||||
@ -462,7 +469,37 @@ const Keyboard = new Lang.Class({
|
||||
actor._extended_keys || actor.extended_key;
|
||||
},
|
||||
|
||||
_clearKeyboardRestTimer: function() {
|
||||
if (!this._keyboardRestingId)
|
||||
return;
|
||||
GLib.source_remove(this._keyboardRestingId);
|
||||
this._keyboardRestingId = 0;
|
||||
},
|
||||
|
||||
show: function (monitor) {
|
||||
this._keyboardRequested = true;
|
||||
|
||||
if (this._keyboardVisible) {
|
||||
if (monitor != Main.layoutManager.keyboardIndex) {
|
||||
Main.layoutManager.keyboardIndex = monitor;
|
||||
this._redraw();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this._clearKeyboardRestTimer();
|
||||
this._keyboardRestingId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
||||
KEYBOARD_REST_TIME,
|
||||
Lang.bind(this, function() {
|
||||
this._clearKeyboardRestTimer();
|
||||
this._show(monitor);
|
||||
}));
|
||||
},
|
||||
|
||||
_show: function(monitor) {
|
||||
if (!this._keyboardRequested)
|
||||
return;
|
||||
|
||||
Main.layoutManager.keyboardIndex = monitor;
|
||||
this._redraw();
|
||||
Main.layoutManager.showKeyboard();
|
||||
@ -470,10 +507,41 @@ const Keyboard = new Lang.Class({
|
||||
},
|
||||
|
||||
hide: function () {
|
||||
this._keyboardRequested = false;
|
||||
|
||||
if (!this._keyboardVisible)
|
||||
return;
|
||||
|
||||
this._clearKeyboardRestTimer();
|
||||
this._keyboardRestingId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
||||
KEYBOARD_REST_TIME,
|
||||
Lang.bind(this, function() {
|
||||
this._clearKeyboardRestTimer();
|
||||
this._hide();
|
||||
}));
|
||||
},
|
||||
|
||||
_hide: function() {
|
||||
if (this._keyboardRequested)
|
||||
return;
|
||||
|
||||
this._hideSubkeys();
|
||||
Main.layoutManager.hideKeyboard();
|
||||
this._createSource();
|
||||
},
|
||||
|
||||
_hideSubkeys: function() {
|
||||
if (this._subkeysBoxPointer) {
|
||||
this._subkeysBoxPointer.hide(BoxPointer.PopupAnimation.FULL);
|
||||
this._subkeysBoxPointer = null;
|
||||
}
|
||||
if (this._capturedEventId) {
|
||||
this.actor.disconnect(this._capturedEventId);
|
||||
this._capturedEventId = 0;
|
||||
}
|
||||
this._capturedPress = false;
|
||||
},
|
||||
|
||||
_moveTemporarily: function () {
|
||||
let currentWindow = global.screen.get_display().focus_window;
|
||||
let rect = currentWindow.get_outer_rect();
|
||||
@ -502,6 +570,13 @@ const Keyboard = new Lang.Class({
|
||||
return one - two;
|
||||
},
|
||||
|
||||
_clearShowIdle: function() {
|
||||
if (!this._showIdleId)
|
||||
return;
|
||||
GLib.source_remove(this._showIdleId);
|
||||
this._showIdleId = 0;
|
||||
},
|
||||
|
||||
// D-Bus methods
|
||||
Show: function(timestamp) {
|
||||
if (!this._enableKeyboard)
|
||||
@ -510,6 +585,8 @@ const Keyboard = new Lang.Class({
|
||||
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
|
||||
return;
|
||||
|
||||
this._clearShowIdle();
|
||||
|
||||
if (timestamp != Clutter.CURRENT_TIME)
|
||||
this._timestamp = timestamp;
|
||||
this.show(Main.layoutManager.focusIndex);
|
||||
@ -522,6 +599,8 @@ const Keyboard = new Lang.Class({
|
||||
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
|
||||
return;
|
||||
|
||||
this._clearShowIdle();
|
||||
|
||||
if (timestamp != Clutter.CURRENT_TIME)
|
||||
this._timestamp = timestamp;
|
||||
this.hide();
|
||||
@ -556,11 +635,7 @@ const KeyboardSource = new Lang.Class({
|
||||
this.keepTrayOnSummaryClick = true;
|
||||
},
|
||||
|
||||
handleSummaryClick: function() {
|
||||
let event = Clutter.get_current_event();
|
||||
if (event.type() != Clutter.EventType.BUTTON_RELEASE)
|
||||
return false;
|
||||
|
||||
handleSummaryClick: function(button) {
|
||||
this.open();
|
||||
return true;
|
||||
},
|
||||
|
883
js/ui/layout.js
883
js/ui/layout.js
File diff suppressed because it is too large
Load Diff
@ -3,6 +3,7 @@
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Params = imports.misc.params;
|
||||
@ -101,6 +102,7 @@ const Lightbox = new Lang.Class({
|
||||
},
|
||||
|
||||
show: function() {
|
||||
Tweener.removeTweens(this.actor);
|
||||
if (this._fadeInTime) {
|
||||
this.shown = false;
|
||||
this.actor.opacity = 0;
|
||||
@ -110,17 +112,20 @@ const Lightbox = new Lang.Class({
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this.shown = true;
|
||||
this.emit('shown');
|
||||
})
|
||||
});
|
||||
} else {
|
||||
this.actor.opacity = 255 * this._fadeFactor;
|
||||
this.shown = true;
|
||||
this.emit('shown');
|
||||
}
|
||||
this.actor.show();
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
this.shown = false;
|
||||
Tweener.removeTweens(this.actor);
|
||||
if (this._fadeOutTime) {
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 0,
|
||||
@ -197,3 +202,4 @@ const Lightbox = new Lang.Class({
|
||||
this.highlight(null);
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(Lightbox.prototype);
|
||||
|
@ -39,6 +39,7 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
|
||||
* in the shell core code too. */
|
||||
'const stage = global.stage; ' +
|
||||
/* Special lookingGlass functions */
|
||||
'const inspect = Lang.bind(Main.lookingGlass, Main.lookingGlass.inspect); ' +
|
||||
'const it = Main.lookingGlass.getIt(); ' +
|
||||
'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ';
|
||||
|
||||
@ -871,8 +872,7 @@ const LookingGlass = new Lang.Class({
|
||||
inspectIcon.connect('button-press-event', Lang.bind(this, function () {
|
||||
let inspector = new Inspector(this);
|
||||
inspector.connect('target', Lang.bind(this, function(i, target, stageX, stageY) {
|
||||
this._pushResult('<inspect x:' + stageX + ' y:' + stageY + '>',
|
||||
target);
|
||||
this._pushResult('inspect(' + Math.round(stageX) + ', ' + Math.round(stageY) + ')', target);
|
||||
}));
|
||||
inspector.connect('closed', Lang.bind(this, function() {
|
||||
this.actor.show();
|
||||
@ -1057,6 +1057,10 @@ const LookingGlass = new Lang.Class({
|
||||
this._entry.text = '';
|
||||
},
|
||||
|
||||
inspect: function(x, y) {
|
||||
return global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
|
||||
},
|
||||
|
||||
getIt: function () {
|
||||
return this._it;
|
||||
},
|
||||
@ -1129,7 +1133,7 @@ const LookingGlass = new Lang.Class({
|
||||
if (this._open)
|
||||
return;
|
||||
|
||||
if (!Main.pushModal(this._entry, { keybindingMode: Main.KeybindingMode.LOOKING_GLASS }))
|
||||
if (!Main.pushModal(this._entry, { keybindingMode: Shell.KeyBindingMode.LOOKING_GLASS }))
|
||||
return;
|
||||
|
||||
this._notebook.selectIndex(0);
|
||||
|
@ -40,19 +40,6 @@ const Util = imports.misc.util;
|
||||
const OVERRIDES_SCHEMA = 'org.gnome.shell.overrides';
|
||||
const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
|
||||
|
||||
const KeybindingMode = {
|
||||
NONE: 0, // block all keybindings
|
||||
NORMAL: 1 << 0, // window mode
|
||||
OVERVIEW: 1 << 1,
|
||||
LOCK_SCREEN: 1 << 2,
|
||||
UNLOCK_SCREEN: 1 << 3,
|
||||
LOGIN_SCREEN: 1 << 4,
|
||||
MESSAGE_TRAY: 1 << 5,
|
||||
SYSTEM_MODAL: 1 << 6,
|
||||
LOOKING_GLASS: 1 << 7,
|
||||
ALL: ~0,
|
||||
};
|
||||
|
||||
let componentManager = null;
|
||||
let panel = null;
|
||||
let overview = null;
|
||||
@ -69,7 +56,7 @@ let shellDBusService = null;
|
||||
let shellMountOpDBusService = null;
|
||||
let screenSaverDBus = null;
|
||||
let modalCount = 0;
|
||||
let keybindingMode = KeybindingMode.NORMAL;
|
||||
let keybindingMode = Shell.KeyBindingMode.NORMAL;
|
||||
let modalActorFocusStack = [];
|
||||
let uiGroup = null;
|
||||
let magnifier = null;
|
||||
@ -81,19 +68,17 @@ let _defaultCssStylesheet = null;
|
||||
let _cssStylesheet = null;
|
||||
let _overridesSettings = null;
|
||||
|
||||
let background = null;
|
||||
|
||||
function _sessionUpdated() {
|
||||
wm.setCustomKeybindingHandler('panel-main-menu',
|
||||
KeybindingMode.NORMAL |
|
||||
KeybindingMode.OVERVIEW,
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
sessionMode.hasOverview ? Lang.bind(overview, overview.toggle) : null);
|
||||
wm.allowKeybinding('overlay-key', KeybindingMode.NORMAL |
|
||||
KeybindingMode.OVERVIEW);
|
||||
wm.allowKeybinding('overlay-key', Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW);
|
||||
|
||||
wm.setCustomKeybindingHandler('panel-run-dialog',
|
||||
KeybindingMode.NORMAL |
|
||||
KeybindingMode.OVERVIEW,
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
sessionMode.hasRunDialog ? openRunDialog : null);
|
||||
if (sessionMode.isGreeter)
|
||||
screenShield.showDialog();
|
||||
@ -110,6 +95,8 @@ function start() {
|
||||
Gio.DesktopAppInfo.set_desktop_env('GNOME');
|
||||
|
||||
sessionMode = new SessionMode.SessionMode();
|
||||
sessionMode.connect('updated', _loadDefaultStylesheet);
|
||||
|
||||
shellDBusService = new ShellDBus.GnomeShell();
|
||||
shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
|
||||
|
||||
@ -132,8 +119,7 @@ function start() {
|
||||
global.stage.color = DEFAULT_BACKGROUND_COLOR;
|
||||
global.stage.no_clear_hint = true;
|
||||
|
||||
_defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
|
||||
loadTheme();
|
||||
_loadDefaultStylesheet();
|
||||
|
||||
// Set up stage hierarchy to group all UI actors under one container.
|
||||
uiGroup = new Shell.GenericContainer({ name: 'uiGroup' });
|
||||
@ -175,7 +161,6 @@ function start() {
|
||||
componentManager = new Components.ComponentManager();
|
||||
|
||||
layoutManager.init();
|
||||
keyboard.init();
|
||||
overview.init();
|
||||
|
||||
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
|
||||
@ -395,6 +380,18 @@ function _nWorkspacesChanged() {
|
||||
return false;
|
||||
}
|
||||
|
||||
function _loadDefaultStylesheet() {
|
||||
if (!sessionMode.isPrimary)
|
||||
return;
|
||||
|
||||
let stylesheet = global.datadir + '/theme/' + sessionMode.stylesheetName;
|
||||
if (_defaultCssStylesheet == stylesheet)
|
||||
return;
|
||||
|
||||
_defaultCssStylesheet = stylesheet;
|
||||
loadTheme();
|
||||
}
|
||||
|
||||
/**
|
||||
* getThemeStylesheet:
|
||||
*
|
||||
@ -520,7 +517,7 @@ function isInModalStack(actor) {
|
||||
* - options: Meta.ModalOptions flags to indicate that the pointer is
|
||||
* already grabbed
|
||||
*
|
||||
* - keybindingMode: used to set the current Main.KeybindingMode to filter
|
||||
* - keybindingMode: used to set the current Shell.KeyBindingMode to filter
|
||||
* global keybindings; the default of NONE will filter
|
||||
* out all keybindings
|
||||
*
|
||||
@ -529,7 +526,7 @@ function isInModalStack(actor) {
|
||||
function pushModal(actor, params) {
|
||||
params = Params.parse(params, { timestamp: global.get_current_time(),
|
||||
options: 0,
|
||||
keybindingMode: KeybindingMode.NONE });
|
||||
keybindingMode: Shell.KeyBindingMode.NONE });
|
||||
|
||||
if (modalCount == 0) {
|
||||
if (!global.begin_modal(params.timestamp, params.options)) {
|
||||
@ -547,19 +544,20 @@ function pushModal(actor, params) {
|
||||
if (index >= 0)
|
||||
popModal(actor);
|
||||
});
|
||||
let curFocus = global.stage.get_key_focus();
|
||||
let curFocusDestroyId;
|
||||
if (curFocus != null) {
|
||||
curFocusDestroyId = curFocus.connect('destroy', function() {
|
||||
|
||||
let prevFocus = global.stage.get_key_focus();
|
||||
let prevFocusDestroyId;
|
||||
if (prevFocus != null) {
|
||||
prevFocusDestroyId = prevFocus.connect('destroy', function() {
|
||||
let index = _findModal(actor);
|
||||
if (index >= 0)
|
||||
modalActorFocusStack[index].actor = null;
|
||||
modalActorFocusStack[index].prevFocus = null;
|
||||
});
|
||||
}
|
||||
modalActorFocusStack.push({ actor: actor,
|
||||
focus: curFocus,
|
||||
destroyId: actorDestroyId,
|
||||
focusDestroyId: curFocusDestroyId,
|
||||
prevFocus: prevFocus,
|
||||
prevFocusDestroyId: prevFocusDestroyId,
|
||||
keybindingMode: keybindingMode });
|
||||
|
||||
keybindingMode = params.keybindingMode;
|
||||
@ -589,7 +587,7 @@ function popModal(actor, timestamp) {
|
||||
global.stage.set_key_focus(null);
|
||||
global.end_modal(timestamp);
|
||||
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
||||
keybindingMode = KeybindingMode.NORMAL;
|
||||
keybindingMode = Shell.KeyBindingMode.NORMAL;
|
||||
|
||||
throw new Error('incorrect pop');
|
||||
}
|
||||
@ -600,18 +598,33 @@ function popModal(actor, timestamp) {
|
||||
record.actor.disconnect(record.destroyId);
|
||||
|
||||
if (focusIndex == modalActorFocusStack.length - 1) {
|
||||
if (record.focus)
|
||||
record.focus.disconnect(record.focusDestroyId);
|
||||
if (record.prevFocus)
|
||||
record.prevFocus.disconnect(record.prevFocusDestroyId);
|
||||
keybindingMode = record.keybindingMode;
|
||||
global.stage.set_key_focus(record.focus);
|
||||
global.stage.set_key_focus(record.prevFocus);
|
||||
} else {
|
||||
// If we have:
|
||||
// global.stage.set_focus(a);
|
||||
// Main.pushModal(b);
|
||||
// Main.pushModal(c);
|
||||
// Main.pushModal(d);
|
||||
//
|
||||
// then we have the stack:
|
||||
// [{ prevFocus: a, actor: b },
|
||||
// { prevFocus: b, actor: c },
|
||||
// { prevFocus: c, actor: d }]
|
||||
//
|
||||
// When actor c is destroyed/popped, if we only simply remove the
|
||||
// record, then the focus stack will be [a, c], rather than the correct
|
||||
// [a, b]. Shift the focus stack up before removing the record to ensure
|
||||
// that we get the correct result.
|
||||
let t = modalActorFocusStack[modalActorFocusStack.length - 1];
|
||||
if (t.focus)
|
||||
t.focus.disconnect(t.focusDestroyId);
|
||||
if (t.prevFocus)
|
||||
t.prevFocus.disconnect(t.prevFocusDestroyId);
|
||||
// Remove from the middle, shift the focus chain up
|
||||
for (let i = modalActorFocusStack.length - 1; i > focusIndex; i--) {
|
||||
modalActorFocusStack[i].focus = modalActorFocusStack[i - 1].focus;
|
||||
modalActorFocusStack[i].focusDestroyId = modalActorFocusStack[i - 1].focusDestroyId;
|
||||
modalActorFocusStack[i].prevFocus = modalActorFocusStack[i - 1].prevFocus;
|
||||
modalActorFocusStack[i].prevFocusDestroyId = modalActorFocusStack[i - 1].prevFocusDestroyId;
|
||||
modalActorFocusStack[i].keybindingMode = modalActorFocusStack[i - 1].keybindingMode;
|
||||
}
|
||||
}
|
||||
@ -623,7 +636,7 @@ function popModal(actor, timestamp) {
|
||||
global.end_modal(timestamp);
|
||||
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
||||
Meta.enable_unredirect_for_screen(global.screen);
|
||||
keybindingMode = KeybindingMode.NORMAL;
|
||||
keybindingMode = Shell.KeyBindingMode.NORMAL;
|
||||
}
|
||||
|
||||
function createLookingGlass() {
|
||||
|
@ -18,6 +18,7 @@ const BoxPointer = imports.ui.boxpointer;
|
||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||
const GnomeSession = imports.misc.gnomeSession;
|
||||
const GrabHelper = imports.ui.grabHelper;
|
||||
const Hash = imports.misc.hash;
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const PointerWatcher = imports.ui.pointerWatcher;
|
||||
@ -243,6 +244,36 @@ function makeCloseButton() {
|
||||
return closeButton;
|
||||
}
|
||||
|
||||
function strHasSuffix(string, suffix) {
|
||||
return string.substr(-suffix.length) == suffix;
|
||||
}
|
||||
|
||||
// NotificationPolicy:
|
||||
// An object that holds all bits of configurable policy related to a notification
|
||||
// source, such as whether to play sound or honour the critical bit.
|
||||
//
|
||||
// A notification without a policy object will inherit the default one.
|
||||
const NotificationPolicy = new Lang.Class({
|
||||
Name: 'NotificationPolicy',
|
||||
|
||||
_init: function(params) {
|
||||
params = Params.parse(params, { enable: true,
|
||||
enableSound: true,
|
||||
showBanners: true,
|
||||
forceExpanded: false,
|
||||
showInLockScreen: true,
|
||||
detailsInLockScreen: false
|
||||
});
|
||||
Lang.copyProperties(params, this);
|
||||
},
|
||||
|
||||
// Do nothing for the default policy. These methods are only useful for the
|
||||
// GSettings policy.
|
||||
store: function() { },
|
||||
destroy: function() { }
|
||||
});
|
||||
Signals.addSignalMethods(NotificationPolicy.prototype);
|
||||
|
||||
// Notification:
|
||||
// @source: the notification's Source
|
||||
// @title: the title
|
||||
@ -314,6 +345,7 @@ const Notification = new Lang.Class({
|
||||
this.resident = false;
|
||||
// 'transient' is a reserved keyword in JS, so we have to use an alternate variable name
|
||||
this.isTransient = false;
|
||||
this.isMusic = false;
|
||||
this.forFeedback = false;
|
||||
this.expanded = false;
|
||||
this.focused = false;
|
||||
@ -321,8 +353,8 @@ const Notification = new Lang.Class({
|
||||
this._destroyed = false;
|
||||
this._useActionIcons = false;
|
||||
this._customContent = false;
|
||||
this._bannerBodyText = null;
|
||||
this._bannerBodyMarkup = false;
|
||||
this.bannerBodyText = null;
|
||||
this.bannerBodyMarkup = false;
|
||||
this._titleFitsInBannerMode = true;
|
||||
this._titleDirection = Clutter.TextDirection.DEFAULT;
|
||||
this._spacing = 0;
|
||||
@ -429,7 +461,7 @@ const Notification = new Lang.Class({
|
||||
this._actionArea = null;
|
||||
this._buttonBox = null;
|
||||
}
|
||||
if (this._imageBin && params.clear)
|
||||
if (params.clear)
|
||||
this.unsetImage();
|
||||
|
||||
if (!this._scrollArea && !this._actionArea && !this._imageBin)
|
||||
@ -474,12 +506,12 @@ const Notification = new Lang.Class({
|
||||
// is done correctly automatically.
|
||||
this._table.set_text_direction(this._titleDirection);
|
||||
|
||||
// Unless the notification has custom content, we save this._bannerBodyText
|
||||
// Unless the notification has custom content, we save this.bannerBodyText
|
||||
// to add it to the content of the notification if the notification is
|
||||
// expandable due to other elements in its content area or due to the banner
|
||||
// not fitting fully in the single-line mode.
|
||||
this._bannerBodyText = this._customContent ? null : banner;
|
||||
this._bannerBodyMarkup = params.bannerMarkup;
|
||||
this.bannerBodyText = this._customContent ? null : banner;
|
||||
this.bannerBodyMarkup = params.bannerMarkup;
|
||||
|
||||
banner = banner ? banner.replace(/\n/g, ' ') : '';
|
||||
|
||||
@ -487,7 +519,7 @@ const Notification = new Lang.Class({
|
||||
this._bannerLabel.queue_relayout();
|
||||
|
||||
// Add the bannerBody now if we know for sure we'll need it
|
||||
if (this._bannerBodyText && this._bannerBodyText.indexOf('\n') > -1)
|
||||
if (this.bannerBodyText && this.bannerBodyText.indexOf('\n') > -1)
|
||||
this._addBannerBody();
|
||||
|
||||
if (params.body)
|
||||
@ -511,7 +543,8 @@ const Notification = new Lang.Class({
|
||||
this._table.add_style_class_name('multi-line-notification');
|
||||
this._scrollArea = new St.ScrollView({ style_class: 'notification-scrollview',
|
||||
vscrollbar_policy: this._scrollPolicy,
|
||||
hscrollbar_policy: Gtk.PolicyType.NEVER });
|
||||
hscrollbar_policy: Gtk.PolicyType.NEVER,
|
||||
visible: this.expanded });
|
||||
this._table.add(this._scrollArea, { row: 1,
|
||||
col: 2 });
|
||||
this._updateLastColumnSettings();
|
||||
@ -552,10 +585,10 @@ const Notification = new Lang.Class({
|
||||
},
|
||||
|
||||
_addBannerBody: function() {
|
||||
if (this._bannerBodyText) {
|
||||
let text = this._bannerBodyText;
|
||||
this._bannerBodyText = null;
|
||||
this.addBody(text, this._bannerBodyMarkup);
|
||||
if (this.bannerBodyText) {
|
||||
let text = this.bannerBodyText;
|
||||
this.bannerBodyText = null;
|
||||
this.addBody(text, this.bannerBodyMarkup);
|
||||
}
|
||||
},
|
||||
|
||||
@ -587,6 +620,7 @@ const Notification = new Lang.Class({
|
||||
this._addBannerBody();
|
||||
}
|
||||
this._actionArea = actor;
|
||||
this._actionArea.visible = this.expanded;
|
||||
|
||||
if (!props)
|
||||
props = {};
|
||||
@ -609,11 +643,15 @@ const Notification = new Lang.Class({
|
||||
},
|
||||
|
||||
setImage: function(image) {
|
||||
if (this._imageBin)
|
||||
this.unsetImage();
|
||||
this._imageBin = new St.Bin();
|
||||
this._imageBin.child = image;
|
||||
this._imageBin.opacity = 230;
|
||||
|
||||
if (!image)
|
||||
return;
|
||||
|
||||
this._imageBin = new St.Bin({ opacity: 230,
|
||||
child: image,
|
||||
visible: this.expanded });
|
||||
|
||||
this._table.add_style_class_name('multi-line-notification');
|
||||
this._table.add_style_class_name('notification-with-image');
|
||||
this._addBannerBody();
|
||||
@ -663,9 +701,10 @@ const Notification = new Lang.Class({
|
||||
let button = new St.Button({ can_focus: true });
|
||||
button._actionId = id;
|
||||
|
||||
if (this._useActionIcons && Gtk.IconTheme.get_default().has_icon(id)) {
|
||||
let iconName = strHasSuffix(id, '-symbolic') ? id : id + '-symbolic';
|
||||
if (this._useActionIcons && Gtk.IconTheme.get_default().has_icon(iconName)) {
|
||||
button.add_style_class_name('notification-icon-button');
|
||||
button.child = new St.Icon({ icon_name: id });
|
||||
button.child = new St.Icon({ icon_name: iconName });
|
||||
} else {
|
||||
button.add_style_class_name('notification-button');
|
||||
button.label = label;
|
||||
@ -841,7 +880,7 @@ const Notification = new Lang.Class({
|
||||
},
|
||||
|
||||
_canExpandContent: function() {
|
||||
return this._bannerBodyText ||
|
||||
return this.bannerBodyText ||
|
||||
(!this._titleFitsInBannerMode && !this._table.has_style_class_name('multi-line-notification'));
|
||||
},
|
||||
|
||||
@ -854,6 +893,14 @@ const Notification = new Lang.Class({
|
||||
this.expanded = true;
|
||||
this.actor.remove_style_class_name('notification-unexpanded');
|
||||
|
||||
// Show additional content that we keep hidden in banner mode
|
||||
if (this._imageBin)
|
||||
this._imageBin.show();
|
||||
if (this._actionArea)
|
||||
this._actionArea.show();
|
||||
if (this._scrollArea)
|
||||
this._scrollArea.show();
|
||||
|
||||
// The banner is never shown when the title did not fit, so this
|
||||
// can be an if-else statement.
|
||||
if (!this._titleFitsInBannerMode) {
|
||||
@ -885,12 +932,23 @@ const Notification = new Lang.Class({
|
||||
if (this._destroyed)
|
||||
return;
|
||||
this.expanded = false;
|
||||
|
||||
// Hide additional content that we keep hidden in banner mode
|
||||
if (this._imageBin)
|
||||
this._imageBin.hide();
|
||||
if (this._actionArea)
|
||||
this._actionArea.hide();
|
||||
if (this._scrollArea)
|
||||
this._scrollArea.hide();
|
||||
|
||||
// Make sure we don't line wrap the title, and ellipsize it instead.
|
||||
this._titleLabel.clutter_text.line_wrap = false;
|
||||
this._titleLabel.clutter_text.ellipsize = Pango.EllipsizeMode.END;
|
||||
|
||||
// Restore banner opacity in case the notification is shown in the
|
||||
// banner mode again on update.
|
||||
this._bannerLabel.opacity = 255;
|
||||
|
||||
// Restore height requisition
|
||||
this.actor.add_style_class_name('notification-unexpanded');
|
||||
this.emit('collapsed');
|
||||
@ -1058,10 +1116,11 @@ const Source = new Lang.Class({
|
||||
this.isTransient = false;
|
||||
this.isChat = false;
|
||||
this.isMuted = false;
|
||||
this.showInLockScreen = true;
|
||||
this.keepTrayOnSummaryClick = false;
|
||||
|
||||
this.notifications = [];
|
||||
|
||||
this.policy = this._createPolicy();
|
||||
},
|
||||
|
||||
get count() {
|
||||
@ -1080,6 +1139,10 @@ const Source = new Lang.Class({
|
||||
this.emit('count-updated');
|
||||
},
|
||||
|
||||
_createPolicy: function() {
|
||||
return new NotificationPolicy();
|
||||
},
|
||||
|
||||
buildRightClickMenu: function() {
|
||||
let item;
|
||||
let rightClickMenu = new St.BoxLayout({ name: 'summary-right-click-menu',
|
||||
@ -1169,11 +1232,13 @@ const Source = new Lang.Class({
|
||||
notify: function(notification) {
|
||||
notification.acknowledged = false;
|
||||
this.pushNotification(notification);
|
||||
if (!this.isMuted)
|
||||
|
||||
if (!this.isMuted && this.policy.showBanners)
|
||||
this.emit('notify', notification);
|
||||
},
|
||||
|
||||
destroy: function(reason) {
|
||||
this.policy.destroy();
|
||||
this.emit('destroy', reason);
|
||||
},
|
||||
|
||||
@ -1215,7 +1280,16 @@ const Source = new Lang.Class({
|
||||
|
||||
hasResidentNotification: function() {
|
||||
return this.notifications.some(function(n) { return n.resident; });
|
||||
},
|
||||
|
||||
getMusicNotification: function() {
|
||||
for (let i = 0; i < this.notifications.length; i++) {
|
||||
if (this.notifications[i].isMusic)
|
||||
return this.notifications[i];
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
});
|
||||
Signals.addSignalMethods(Source.prototype);
|
||||
|
||||
@ -1269,6 +1343,14 @@ const SummaryItem = new Lang.Class({
|
||||
global.focus_manager.add_group(this.rightClickMenu);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
// remove the actor from the summary item so it doesn't get destroyed
|
||||
// with us
|
||||
this._sourceBox.remove_actor(this._sourceIcon);
|
||||
|
||||
this.actor.destroy();
|
||||
},
|
||||
|
||||
_onKeyPress: function(actor, event) {
|
||||
if (event.get_key_symbol() == Clutter.KEY_Up) {
|
||||
actor.emit('clicked', 1);
|
||||
@ -1453,7 +1535,7 @@ const MessageTray = new Lang.Class({
|
||||
this.idleMonitor = new GnomeDesktop.IdleMonitor();
|
||||
|
||||
this._grabHelper = new GrabHelper.GrabHelper(this.actor,
|
||||
{ keybindingMode: Main.KeybindingMode.MESSAGE_TRAY });
|
||||
{ keybindingMode: Shell.KeyBindingMode.MESSAGE_TRAY });
|
||||
this._grabHelper.addActor(this._summaryBoxPointer.actor);
|
||||
this._grabHelper.addActor(this.actor);
|
||||
if (Main.panel.statusArea.activities)
|
||||
@ -1513,7 +1595,6 @@ const MessageTray = new Lang.Class({
|
||||
function() {
|
||||
this._overviewVisible = false;
|
||||
this._escapeTray();
|
||||
this.actor.remove_style_pseudo_class('overview');
|
||||
this._updateState();
|
||||
}));
|
||||
|
||||
@ -1522,19 +1603,19 @@ const MessageTray = new Lang.Class({
|
||||
Main.wm.addKeybinding('toggle-message-tray',
|
||||
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
|
||||
Meta.KeyBindingFlags.NONE,
|
||||
Main.KeybindingMode.NORMAL |
|
||||
Main.KeybindingMode.MESSAGE_TRAY |
|
||||
Main.KeybindingMode.OVERVIEW,
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.MESSAGE_TRAY |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
Lang.bind(this, this.toggleAndNavigate));
|
||||
Main.wm.addKeybinding('focus-active-notification',
|
||||
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
|
||||
Meta.KeyBindingFlags.NONE,
|
||||
Main.KeybindingMode.NORMAL |
|
||||
Main.KeybindingMode.MESSAGE_TRAY |
|
||||
Main.KeybindingMode.OVERVIEW,
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.MESSAGE_TRAY |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
Lang.bind(this, this._expandActiveNotification));
|
||||
|
||||
this._summaryItems = [];
|
||||
this._sources = new Hash.Map();
|
||||
this._chatSummaryItemsCount = 0;
|
||||
|
||||
let pointerWatcher = PointerWatcher.getPointerWatcher();
|
||||
@ -1544,11 +1625,7 @@ const MessageTray = new Lang.Class({
|
||||
this._trayDwellUserTime = 0;
|
||||
|
||||
this._sessionUpdated();
|
||||
this._updateNoMessagesLabel();
|
||||
},
|
||||
|
||||
_updateNoMessagesLabel: function() {
|
||||
if (this._summaryItems.length == 0 && !this._noMessages) {
|
||||
this._noMessages = new St.Label({ text: _("No Messages"),
|
||||
style_class: 'no-messages-label',
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
@ -1556,10 +1633,11 @@ const MessageTray = new Lang.Class({
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
y_expand: true });
|
||||
this.actor.add_actor(this._noMessages);
|
||||
} else if (this._summaryItems.length > 0 && this._noMessages) {
|
||||
this._noMessages.destroy();
|
||||
this._noMessages = null;
|
||||
}
|
||||
this._updateNoMessagesLabel();
|
||||
},
|
||||
|
||||
_updateNoMessagesLabel: function() {
|
||||
this._noMessages.visible = this._sources.size() == 0;
|
||||
},
|
||||
|
||||
_sessionUpdated: function() {
|
||||
@ -1628,10 +1706,7 @@ const MessageTray = new Lang.Class({
|
||||
},
|
||||
|
||||
_onNotificationKeyRelease: function(actor, event) {
|
||||
let ignoredModifiers = global.display.get_ignored_modifier_mask();
|
||||
let modifierState = event.get_state() & ~ignoredModifiers;
|
||||
|
||||
if (event.get_key_symbol() == Clutter.KEY_Escape && modifierState == 0) {
|
||||
if (event.get_key_symbol() == Clutter.KEY_Escape && event.get_state() == 0) {
|
||||
this._closeNotification();
|
||||
return true;
|
||||
}
|
||||
@ -1649,15 +1724,7 @@ const MessageTray = new Lang.Class({
|
||||
},
|
||||
|
||||
contains: function(source) {
|
||||
return this._getIndexOfSummaryItemForSource(source) >= 0;
|
||||
},
|
||||
|
||||
_getIndexOfSummaryItemForSource: function(source) {
|
||||
for (let i = 0; i < this._summaryItems.length; i++) {
|
||||
if (this._summaryItems[i].source == source)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
return this._sources.has(source);
|
||||
},
|
||||
|
||||
add: function(source) {
|
||||
@ -1666,7 +1733,23 @@ const MessageTray = new Lang.Class({
|
||||
return;
|
||||
}
|
||||
|
||||
let summaryItem = new SummaryItem(source);
|
||||
// Register that we got a notification for this source
|
||||
source.policy.store();
|
||||
|
||||
source.policy.connect('enable-changed', Lang.bind(this, this._onSourceEnableChanged, source));
|
||||
source.policy.connect('policy-changed', Lang.bind(this, this._updateState));
|
||||
this._onSourceEnableChanged(source.policy, source);
|
||||
},
|
||||
|
||||
_addSource: function(source) {
|
||||
let obj = {
|
||||
source: source,
|
||||
summaryItem: new SummaryItem(source),
|
||||
notifyId: 0,
|
||||
destroyId: 0,
|
||||
mutedChangedId: 0
|
||||
};
|
||||
let summaryItem = obj.summaryItem;
|
||||
|
||||
if (source.isChat) {
|
||||
this._summary.insert_child_at_index(summaryItem.actor, 0);
|
||||
@ -1675,11 +1758,11 @@ const MessageTray = new Lang.Class({
|
||||
this._summary.insert_child_at_index(summaryItem.actor, this._chatSummaryItemsCount);
|
||||
}
|
||||
|
||||
this._summaryItems.push(summaryItem);
|
||||
this._sources.set(source, obj);
|
||||
|
||||
source.connect('notify', Lang.bind(this, this._onNotify));
|
||||
|
||||
source.connect('muted-changed', Lang.bind(this,
|
||||
obj.notifyId = source.connect('notify', Lang.bind(this, this._onNotify));
|
||||
obj.destroyId = source.connect('destroy', Lang.bind(this, this._onSourceDestroy));
|
||||
obj.mutedChangedId = source.connect('muted-changed', Lang.bind(this,
|
||||
function () {
|
||||
if (source.isMuted)
|
||||
this._notificationQueue = this._notificationQueue.filter(function(notification) {
|
||||
@ -1698,35 +1781,28 @@ const MessageTray = new Lang.Class({
|
||||
this._onSummaryItemClicked(summaryItem, 3);
|
||||
}));
|
||||
|
||||
source.connect('destroy', Lang.bind(this, this._onSourceDestroy));
|
||||
|
||||
// We need to display the newly-added summary item, but if the
|
||||
// caller is about to post a notification, we want to show that
|
||||
// *first* and not show the summary item until after it hides.
|
||||
// So postpone calling _updateState() a tiny bit.
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() { this._updateState(); return false; }));
|
||||
|
||||
this.emit('summary-item-added', summaryItem);
|
||||
this.emit('source-added', source);
|
||||
|
||||
this._updateNoMessagesLabel();
|
||||
},
|
||||
|
||||
getSummaryItems: function() {
|
||||
return this._summaryItems;
|
||||
},
|
||||
|
||||
_onSourceDestroy: function(source) {
|
||||
let index = this._getIndexOfSummaryItemForSource(source);
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
let summaryItemToRemove = this._summaryItems[index];
|
||||
|
||||
this._summaryItems.splice(index, 1);
|
||||
_removeSource: function(source) {
|
||||
let [, obj] = this._sources.delete(source);
|
||||
let summaryItem = obj.summaryItem;
|
||||
|
||||
if (source.isChat)
|
||||
this._chatSummaryItemsCount--;
|
||||
|
||||
source.disconnect(obj.notifyId);
|
||||
source.disconnect(obj.destroyId);
|
||||
source.disconnect(obj.mutedChangedId);
|
||||
|
||||
let needUpdate = false;
|
||||
|
||||
if (this._notification && this._notification.source == source) {
|
||||
@ -1734,12 +1810,14 @@ const MessageTray = new Lang.Class({
|
||||
this._notificationRemoved = true;
|
||||
needUpdate = true;
|
||||
}
|
||||
if (this._clickedSummaryItem == summaryItemToRemove) {
|
||||
if (this._clickedSummaryItem == summaryItem) {
|
||||
this._setClickedSummaryItem(null);
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
summaryItemToRemove.actor.destroy();
|
||||
summaryItem.destroy();
|
||||
|
||||
this.emit('source-removed', source);
|
||||
|
||||
this._updateNoMessagesLabel();
|
||||
|
||||
@ -1747,6 +1825,26 @@ const MessageTray = new Lang.Class({
|
||||
this._updateState();
|
||||
},
|
||||
|
||||
getSources: function() {
|
||||
return this._sources.keys();
|
||||
},
|
||||
|
||||
_onSourceEnableChanged: function(policy, source) {
|
||||
let wasEnabled = this.contains(source);
|
||||
let shouldBeEnabled = policy.enable;
|
||||
|
||||
if (wasEnabled != shouldBeEnabled) {
|
||||
if (shouldBeEnabled)
|
||||
this._addSource(source);
|
||||
else
|
||||
this._removeSource(source);
|
||||
}
|
||||
},
|
||||
|
||||
_onSourceDestroy: function(source) {
|
||||
this._removeSource(source);
|
||||
},
|
||||
|
||||
_onNotificationDestroy: function(notification) {
|
||||
if (this._notification == notification && (this._notificationState == State.SHOWN || this._notificationState == State.SHOWING)) {
|
||||
this._updateNotificationTimeout(0);
|
||||
@ -1832,7 +1930,7 @@ const MessageTray = new Lang.Class({
|
||||
},
|
||||
|
||||
_onSummaryItemClicked: function(summaryItem, button) {
|
||||
if (summaryItem.source.handleSummaryClick()) {
|
||||
if (summaryItem.source.handleSummaryClick(button)) {
|
||||
if (summaryItem.source.keepTrayOnSummaryClick)
|
||||
this._setClickedSummaryItem(null);
|
||||
else
|
||||
@ -2191,7 +2289,9 @@ const MessageTray = new Lang.Class({
|
||||
this._tween(this.actor, '_trayState', State.HIDDEN,
|
||||
{ y: 0,
|
||||
time: ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: this._onTrayHidden,
|
||||
onCompleteScope: this
|
||||
});
|
||||
|
||||
// Note that we might have entered here without a grab,
|
||||
@ -2201,6 +2301,11 @@ const MessageTray = new Lang.Class({
|
||||
this._lightbox.hide();
|
||||
},
|
||||
|
||||
_onTrayHidden: function() {
|
||||
if (!this._overviewVisible)
|
||||
this.actor.remove_style_pseudo_class('overview');
|
||||
},
|
||||
|
||||
_hideDesktopClone: function(now) {
|
||||
this._tween(this._desktopClone, '_desktopCloneState', State.HIDDEN,
|
||||
{ y: 0,
|
||||
@ -2237,7 +2342,7 @@ const MessageTray = new Lang.Class({
|
||||
|
||||
this._notificationClickedId = this._notification.connect('done-displaying',
|
||||
Lang.bind(this, this._escapeTray));
|
||||
this._notification.connect('unfocused', Lang.bind(this, function() {
|
||||
this._notificationUnfocusedId = this._notification.connect('unfocused', Lang.bind(this, function() {
|
||||
this._updateState();
|
||||
}));
|
||||
this._notificationBin.child = this._notification.actor;
|
||||
@ -2266,8 +2371,10 @@ const MessageTray = new Lang.Class({
|
||||
_updateShowingNotification: function() {
|
||||
this._notification.acknowledged = true;
|
||||
|
||||
// We auto-expand notifications with CRITICAL urgency.
|
||||
if (this._notification.urgency == Urgency.CRITICAL)
|
||||
// We auto-expand notifications with CRITICAL urgency, or for which the relevant setting
|
||||
// is on in the control center.
|
||||
if (this._notification.urgency == Urgency.CRITICAL ||
|
||||
this._notification.source.policy.forceExpanded)
|
||||
this._expandNotification(true);
|
||||
|
||||
// We tween all notifications to full opacity. This ensures that both new notifications and
|
||||
@ -2351,11 +2458,18 @@ const MessageTray = new Lang.Class({
|
||||
this.idleMonitor.disconnect(this._idleMonitorBecameActiveId);
|
||||
this._idleMonitorBecameActiveId = 0;
|
||||
}
|
||||
|
||||
if (this._notificationExpandedId) {
|
||||
this._notification.disconnect(this._notificationExpandedId);
|
||||
this._notificationExpandedId = 0;
|
||||
}
|
||||
if (this._notificationClickedId) {
|
||||
this._notification.disconnect(this._notificationClickedId);
|
||||
this._notificationClickedId = 0;
|
||||
}
|
||||
if (this._notificationUnfocusedId) {
|
||||
this._notification.disconnect(this._notificationUnfocusedId);
|
||||
this._notificationUnfocusedId = 0;
|
||||
}
|
||||
|
||||
if (this._notificationRemoved) {
|
||||
Tweener.removeTweens(this._notificationWidget);
|
||||
@ -2377,19 +2491,19 @@ const MessageTray = new Lang.Class({
|
||||
},
|
||||
|
||||
_hideNotificationCompleted: function() {
|
||||
this._notificationRemoved = false;
|
||||
this._notificationWidget.hide();
|
||||
this._closeButton.hide();
|
||||
this._pointerInTray = false;
|
||||
this.actor.hover = false; // Clutter doesn't emit notify::hover when actors move
|
||||
this._notificationBin.child = null;
|
||||
this._notification.collapseCompleted();
|
||||
this._notification.disconnect(this._notificationClickedId);
|
||||
this._notificationClickedId = 0;
|
||||
|
||||
let notification = this._notification;
|
||||
this._notification = null;
|
||||
if (notification.isTransient)
|
||||
notification.destroy(NotificationDestroyedReason.EXPIRED);
|
||||
|
||||
this._notificationRemoved = false;
|
||||
this._closeButton.hide();
|
||||
this._pointerInTray = false;
|
||||
this.actor.hover = false; // Clutter doesn't emit notify::hover when actors move
|
||||
this._notificationBin.child = null;
|
||||
this._notificationWidget.hide();
|
||||
},
|
||||
|
||||
_expandActiveNotification: function() {
|
||||
@ -2577,6 +2691,20 @@ const MessageTray = new Lang.Class({
|
||||
|
||||
_onSummaryBoxPointerUngrabbed: function() {
|
||||
this._summaryBoxPointerState = State.HIDING;
|
||||
|
||||
if (this._summaryBoxPointerContentUpdatedId) {
|
||||
this._summaryBoxPointerItem.disconnect(this._summaryBoxPointerContentUpdatedId);
|
||||
this._summaryBoxPointerContentUpdatedId = 0;
|
||||
}
|
||||
if (this._summaryBoxPointerCloseClickedId != 0) {
|
||||
this._summaryBoxPointerItem.closeButton.disconnect(this._summaryBoxPointerCloseClickedId);
|
||||
this._summaryBoxPointerCloseClickedId = 0;
|
||||
}
|
||||
if (this._sourceDoneDisplayingId) {
|
||||
this._summaryBoxPointerItem.source.disconnect(this._sourceDoneDisplayingId);
|
||||
this._sourceDoneDisplayingId = 0;
|
||||
}
|
||||
|
||||
this._unlock();
|
||||
|
||||
if (this._summaryBoxPointerItem.source.notifications.length == 0) {
|
||||
@ -2599,14 +2727,6 @@ const MessageTray = new Lang.Class({
|
||||
|
||||
this._summaryBoxPointerState = State.HIDDEN;
|
||||
this._summaryBoxPointer.bin.child = null;
|
||||
this._summaryBoxPointerItem.disconnect(this._summaryBoxPointerContentUpdatedId);
|
||||
this._summaryBoxPointerContentUpdatedId = 0;
|
||||
if (this._summaryBoxPointerCloseClickedId != 0) {
|
||||
this._summaryBoxPointerItem.closeButton.disconnect(this._summaryBoxPointerCloseClickedId);
|
||||
this._summaryBoxPointerCloseClickedId = 0;
|
||||
}
|
||||
this._summaryBoxPointerItem.source.disconnect(this._sourceDoneDisplayingId);
|
||||
this._summaryBoxPointerDoneDisplayingId = 0;
|
||||
|
||||
let sourceNotificationStackDoneShowing = null;
|
||||
if (doneShowingNotificationStack) {
|
||||
|
@ -37,7 +37,7 @@ const ModalDialog = new Lang.Class({
|
||||
params = Params.parse(params, { shellReactive: false,
|
||||
styleClass: null,
|
||||
parentActor: Main.uiGroup,
|
||||
keybindingMode: Main.KeybindingMode.SYSTEM_MODAL,
|
||||
keybindingMode: Shell.KeyBindingMode.SYSTEM_MODAL,
|
||||
shouldFadeIn: true });
|
||||
|
||||
this.state = State.CLOSED;
|
||||
@ -96,7 +96,6 @@ const ModalDialog = new Lang.Class({
|
||||
y_align: St.Align.START });
|
||||
|
||||
this.buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
|
||||
visible: false,
|
||||
vertical: false });
|
||||
this.dialogLayout.add(this.buttonLayout,
|
||||
{ expand: true,
|
||||
@ -120,7 +119,6 @@ const ModalDialog = new Lang.Class({
|
||||
|
||||
setButtons: function(buttons) {
|
||||
this.clearButtons();
|
||||
this.buttonLayout.visible = (buttons.length > 0);
|
||||
|
||||
for (let i = 0; i < buttons.length; i++) {
|
||||
let buttonInfo = buttons[i];
|
||||
@ -159,6 +157,7 @@ const ModalDialog = new Lang.Class({
|
||||
keys = [];
|
||||
|
||||
let button = new St.Button({ style_class: 'modal-dialog-button',
|
||||
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
label: label });
|
||||
|
@ -103,6 +103,126 @@ const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
|
||||
'ibus-ui-gtk': 'keyboard'
|
||||
};
|
||||
|
||||
const NotificationGenericPolicy = new Lang.Class({
|
||||
Name: 'NotificationGenericPolicy',
|
||||
Extends: MessageTray.NotificationPolicy,
|
||||
|
||||
_init: function() {
|
||||
// Don't chain to parent, it would try setting
|
||||
// our properties to the defaults
|
||||
|
||||
this.id = 'generic';
|
||||
|
||||
this._masterSettings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications' });
|
||||
this._masterSettings.connect('changed', Lang.bind(this, this._changed));
|
||||
},
|
||||
|
||||
store: function() { },
|
||||
|
||||
destroy: function() {
|
||||
this._masterSettings.run_dispose();
|
||||
},
|
||||
|
||||
_changed: function(settings, key) {
|
||||
this.emit('policy-changed', key);
|
||||
},
|
||||
|
||||
get enable() {
|
||||
return true;
|
||||
},
|
||||
|
||||
get enableSound() {
|
||||
return true;
|
||||
},
|
||||
|
||||
get showBanners() {
|
||||
return this._masterSettings.get_boolean('show-banners');
|
||||
},
|
||||
|
||||
get forceExpanded() {
|
||||
return false;
|
||||
},
|
||||
|
||||
get showInLockScreen() {
|
||||
return this._masterSettings.get_boolean('show-in-lock-screen');
|
||||
},
|
||||
|
||||
get detailsInLockScreen() {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
const NotificationApplicationPolicy = new Lang.Class({
|
||||
Name: 'NotificationApplicationPolicy',
|
||||
Extends: MessageTray.NotificationPolicy,
|
||||
|
||||
_init: function(id) {
|
||||
// Don't chain to parent, it would try setting
|
||||
// our properties to the defaults
|
||||
|
||||
this.id = id;
|
||||
this._canonicalId = this._canonicalizeId(id)
|
||||
|
||||
this._masterSettings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications' });
|
||||
this._settings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications.application',
|
||||
path: '/org/gnome/desktop/notifications/application/' + this._canonicalId + '/' });
|
||||
|
||||
this._masterSettings.connect('changed', Lang.bind(this, this._changed));
|
||||
this._settings.connect('changed', Lang.bind(this, this._changed));
|
||||
},
|
||||
|
||||
store: function() {
|
||||
this._settings.set_string('application-id', this.id + '.desktop');
|
||||
|
||||
let apps = this._masterSettings.get_strv('application-children');
|
||||
if (apps.indexOf(this._canonicalId) < 0) {
|
||||
apps.push(this._canonicalId);
|
||||
this._masterSettings.set_strv('application-children', apps);
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this._masterSettings.run_dispose();
|
||||
this._settings.run_dispose();
|
||||
},
|
||||
|
||||
_changed: function(settings, key) {
|
||||
this.emit('policy-changed', key);
|
||||
},
|
||||
|
||||
_canonicalizeId: function(id) {
|
||||
// Keys are restricted to lowercase alphanumeric characters and dash,
|
||||
// and two dashes cannot be in succession
|
||||
return id.toLowerCase().replace(/[^a-z0-9\-]/g, '-').replace(/--+/g, '-');
|
||||
},
|
||||
|
||||
get enable() {
|
||||
return this._settings.get_boolean('enable');
|
||||
},
|
||||
|
||||
get enableSound() {
|
||||
return this._settings.get_boolean('enable-sound-alerts');
|
||||
},
|
||||
|
||||
get showBanners() {
|
||||
return this._masterSettings.get_boolean('show-banners') &&
|
||||
this._settings.get_boolean('show-banners');
|
||||
},
|
||||
|
||||
get forceExpanded() {
|
||||
return this._settings.get_boolean('force-expanded');
|
||||
},
|
||||
|
||||
get showInLockScreen() {
|
||||
return this._masterSettings.get_boolean('show-in-lock-screen') &&
|
||||
this._settings.get_boolean('show-in-lock-screen');
|
||||
},
|
||||
|
||||
get detailsInLockScreen() {
|
||||
return this._settings.get_boolean('details-in-lock-screen');
|
||||
}
|
||||
});
|
||||
|
||||
const NotificationDaemon = new Lang.Class({
|
||||
Name: 'NotificationDaemon',
|
||||
|
||||
@ -127,30 +247,19 @@ const NotificationDaemon = new Lang.Class({
|
||||
this._trayManager.manage_stage(global.stage, Main.messageTray.actor);
|
||||
},
|
||||
|
||||
_iconForNotificationData: function(icon, hints) {
|
||||
// If an icon is not specified, we use 'image-data' or 'image-path' hint for an icon
|
||||
// and don't show a large image. There are currently many applications that use
|
||||
// notify_notification_set_icon_from_pixbuf() from libnotify, which in turn sets
|
||||
// the 'image-data' hint. These applications don't typically pass in 'app_icon'
|
||||
// argument to Notify() and actually expect the pixbuf to be shown as an icon.
|
||||
// So the logic here does the right thing for this case. If both an icon and either
|
||||
// one of 'image-data' or 'image-path' are specified, we show both an icon and
|
||||
// a large image.
|
||||
if (icon) {
|
||||
if (icon.substr(0, 7) == 'file://')
|
||||
return new Gio.FileIcon({ file: Gio.File.new_for_uri(icon) });
|
||||
else if (icon[0] == '/') {
|
||||
return new Gio.FileIcon({ file: Gio.File.new_for_path(icon) });
|
||||
} else
|
||||
return new Gio.ThemedIcon({ name: icon });
|
||||
} else if (hints['image-data']) {
|
||||
_imageForNotificationData: function(hints) {
|
||||
if (hints['image-data']) {
|
||||
let [width, height, rowStride, hasAlpha,
|
||||
bitsPerSample, nChannels, data] = hints['image-data'];
|
||||
return Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha,
|
||||
bitsPerSample, width, height, rowStride);
|
||||
} else if (hints['image-path']) {
|
||||
return new Gio.FileIcon({ file: Gio.File.new_for_path(hints['image-path']) });
|
||||
} else {
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
_fallbackIconForNotificationData: function(hints) {
|
||||
let stockIcon;
|
||||
switch (hints.urgency) {
|
||||
case Urgency.LOW:
|
||||
@ -162,7 +271,18 @@ const NotificationDaemon = new Lang.Class({
|
||||
break;
|
||||
}
|
||||
return new Gio.ThemedIcon({ name: stockIcon });
|
||||
},
|
||||
|
||||
_iconForNotificationData: function(icon) {
|
||||
if (icon) {
|
||||
if (icon.substr(0, 7) == 'file://')
|
||||
return new Gio.FileIcon({ file: Gio.File.new_for_uri(icon) });
|
||||
else if (icon[0] == '/')
|
||||
return new Gio.FileIcon({ file: Gio.File.new_for_path(icon) });
|
||||
else
|
||||
return new Gio.ThemedIcon({ name: icon });
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
_lookupSource: function(title, pid, trayIcon) {
|
||||
@ -213,7 +333,7 @@ const NotificationDaemon = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
let source = new Source(title, pid, sender, trayIcon);
|
||||
let source = new Source(title, pid, sender, trayIcon, ndata ? ndata.hints['desktop-entry'] : null);
|
||||
source.setTransient(isForTransientNotification);
|
||||
|
||||
if (!isForTransientNotification) {
|
||||
@ -355,12 +475,8 @@ const NotificationDaemon = new Lang.Class({
|
||||
[ndata.id, ndata.icon, ndata.summary, ndata.body,
|
||||
ndata.actions, ndata.hints, ndata.notification];
|
||||
|
||||
let gicon = this._iconForNotificationData(icon, hints);
|
||||
|
||||
if (notification == null) {
|
||||
notification = new MessageTray.Notification(source, summary, body,
|
||||
{ gicon: gicon,
|
||||
bannerMarkup: true });
|
||||
notification = new MessageTray.Notification(source);
|
||||
ndata.notification = notification;
|
||||
notification.connect('destroy', Lang.bind(this,
|
||||
function(n, reason) {
|
||||
@ -383,29 +499,36 @@ const NotificationDaemon = new Lang.Class({
|
||||
function(n, actionId) {
|
||||
this._emitActionInvoked(ndata.id, actionId);
|
||||
}));
|
||||
} else {
|
||||
}
|
||||
|
||||
// Mark music notifications so they can be shown in the screen shield
|
||||
notification.isMusic = (ndata.hints['category'] == 'x-gnome.music');
|
||||
|
||||
let gicon = this._iconForNotificationData(icon, hints);
|
||||
let gimage = this._imageForNotificationData(hints);
|
||||
|
||||
let image = null;
|
||||
|
||||
// If an icon is not specified, we use 'image-data' or 'image-path' hint for an icon
|
||||
// and don't show a large image. There are currently many applications that use
|
||||
// notify_notification_set_icon_from_pixbuf() from libnotify, which in turn sets
|
||||
// the 'image-data' hint. These applications don't typically pass in 'app_icon'
|
||||
// argument to Notify() and actually expect the pixbuf to be shown as an icon.
|
||||
// So the logic here does the right thing for this case. If both an icon and either
|
||||
// one of 'image-data' or 'image-path' are specified, we show both an icon and
|
||||
// a large image.
|
||||
if (gicon && gimage)
|
||||
image = new St.Icon({ gicon: gimage,
|
||||
icon_size: notification.IMAGE_SIZE });
|
||||
else if (!gicon && gimage)
|
||||
gicon = gimage;
|
||||
else if (!gicon)
|
||||
gicon = this._fallbackIconForNotificationData(hints);
|
||||
|
||||
notification.update(summary, body, { gicon: gicon,
|
||||
bannerMarkup: true,
|
||||
clear: true });
|
||||
}
|
||||
|
||||
// We only display a large image if an icon is also specified.
|
||||
if (icon && (hints['image-data'] || hints['image-path'])) {
|
||||
let image = null;
|
||||
if (hints['image-data']) {
|
||||
let [width, height, rowStride, hasAlpha,
|
||||
bitsPerSample, nChannels, data] = hints['image-data'];
|
||||
image = St.TextureCache.get_default().load_from_raw(data, hasAlpha,
|
||||
width, height, rowStride, notification.IMAGE_SIZE);
|
||||
} else if (hints['image-path']) {
|
||||
image = St.TextureCache.get_default().load_uri_async(GLib.filename_to_uri(hints['image-path'], null),
|
||||
notification.IMAGE_SIZE,
|
||||
notification.IMAGE_SIZE);
|
||||
}
|
||||
notification.setImage(image);
|
||||
} else {
|
||||
notification.unsetImage();
|
||||
}
|
||||
|
||||
if (actions.length) {
|
||||
notification.setUseActionIcons(hints['action-icons'] == true);
|
||||
@ -435,7 +558,7 @@ const NotificationDaemon = new Lang.Class({
|
||||
// of the 'transient' hint with hints['transient'] rather than hints.transient
|
||||
notification.setTransient(hints['transient'] == true);
|
||||
|
||||
let sourceGIcon = source.useNotificationIcon ? this._iconForNotificationData(icon, hints) : null;
|
||||
let sourceGIcon = source.useNotificationIcon ? gicon : null;
|
||||
source.processNotification(notification, sourceGIcon);
|
||||
},
|
||||
|
||||
@ -515,12 +638,22 @@ const Source = new Lang.Class({
|
||||
Name: 'NotificationDaemonSource',
|
||||
Extends: MessageTray.Source,
|
||||
|
||||
_init: function(title, pid, sender, trayIcon) {
|
||||
_init: function(title, pid, sender, trayIcon, appId) {
|
||||
// Need to set the app before chaining up, so
|
||||
// methods called from the parent constructor can find it
|
||||
this.trayIcon = trayIcon;
|
||||
this.pid = pid;
|
||||
this.app = this._getApp(appId);
|
||||
|
||||
this.parent(title);
|
||||
|
||||
this.initialTitle = title;
|
||||
|
||||
this.pid = pid;
|
||||
if (this.app)
|
||||
this.title = this.app.get_name();
|
||||
else
|
||||
this.useNotificationIcon = true;
|
||||
|
||||
if (sender)
|
||||
this._nameWatcherId = Gio.DBus.session.watch_name(sender,
|
||||
Gio.BusNameWatcherFlags.NONE,
|
||||
@ -529,19 +662,22 @@ const Source = new Lang.Class({
|
||||
else
|
||||
this._nameWatcherId = 0;
|
||||
|
||||
this._setApp();
|
||||
if (this.app)
|
||||
this.title = this.app.get_name();
|
||||
else
|
||||
this.useNotificationIcon = true;
|
||||
|
||||
this.trayIcon = trayIcon;
|
||||
if (this.trayIcon) {
|
||||
// Try again finding the app, using the WM_CLASS from the tray icon
|
||||
this._setSummaryIcon(this.trayIcon);
|
||||
this.useNotificationIcon = false;
|
||||
}
|
||||
},
|
||||
|
||||
_createPolicy: function() {
|
||||
if (this.app) {
|
||||
let id = this.app.get_id().replace(/\.desktop$/,'');
|
||||
return new NotificationApplicationPolicy(id);
|
||||
} else {
|
||||
return new NotificationGenericPolicy();
|
||||
}
|
||||
},
|
||||
|
||||
_onNameVanished: function() {
|
||||
// Destroy the notification source when its sender is removed from DBus.
|
||||
// Only do so if this.app is set to avoid removing "notify-send" sources, senders
|
||||
@ -565,19 +701,17 @@ const Source = new Lang.Class({
|
||||
this.notify(notification);
|
||||
},
|
||||
|
||||
handleSummaryClick: function() {
|
||||
handleSummaryClick: function(button) {
|
||||
if (!this.trayIcon)
|
||||
return false;
|
||||
|
||||
let event = Clutter.get_current_event();
|
||||
if (event.type() != Clutter.EventType.BUTTON_RELEASE)
|
||||
return false;
|
||||
|
||||
// Left clicks are passed through only where there aren't unacknowledged
|
||||
// notifications, so it possible to open them in summary mode; right
|
||||
// clicks are always forwarded, as the right click menu is not useful for
|
||||
// tray icons
|
||||
if (event.get_button() == 1 &&
|
||||
if (button == 1 &&
|
||||
this.notifications.length > 0)
|
||||
return false;
|
||||
|
||||
@ -590,7 +724,7 @@ const Source = new Lang.Class({
|
||||
return true;
|
||||
},
|
||||
|
||||
_getApp: function() {
|
||||
_getApp: function(appId) {
|
||||
let app;
|
||||
|
||||
app = Shell.WindowTracker.get_default().get_app_from_pid(this.pid);
|
||||
@ -598,7 +732,13 @@ const Source = new Lang.Class({
|
||||
return app;
|
||||
|
||||
if (this.trayIcon) {
|
||||
app = Shell.AppSystem.get_default().lookup_wmclass(this.trayIcon.wmclass);
|
||||
app = Shell.AppSystem.get_default().lookup_wmclass(this.trayIcon.wm_class);
|
||||
if (app != null)
|
||||
return app;
|
||||
}
|
||||
|
||||
if (appId) {
|
||||
app = Shell.AppSystem.get_default().lookup_app(appId + '.desktop');
|
||||
if (app != null)
|
||||
return app;
|
||||
}
|
||||
@ -606,11 +746,11 @@ const Source = new Lang.Class({
|
||||
return null;
|
||||
},
|
||||
|
||||
_setApp: function() {
|
||||
_setApp: function(appId) {
|
||||
if (this.app)
|
||||
return;
|
||||
|
||||
this.app = this._getApp();
|
||||
this.app = this._getApp(appId);
|
||||
if (!this.app)
|
||||
return;
|
||||
|
||||
|
@ -576,7 +576,7 @@ const Overview = new Lang.Class({
|
||||
if (this._shown) {
|
||||
if (!this._modal) {
|
||||
if (Main.pushModal(this._overview,
|
||||
{ keybindingMode: Main.KeybindingMode.OVERVIEW }))
|
||||
{ keybindingMode: Shell.KeyBindingMode.OVERVIEW }))
|
||||
this._modal = true;
|
||||
else
|
||||
this.hide();
|
||||
|
@ -315,7 +315,7 @@ const AppMenuButton = new Lang.Class({
|
||||
},
|
||||
|
||||
show: function() {
|
||||
if (this._visible || Main.screenShield.locked)
|
||||
if (this._visible)
|
||||
return;
|
||||
|
||||
this._visible = true;
|
||||
@ -598,6 +598,11 @@ const AppMenuButton = new Lang.Class({
|
||||
return;
|
||||
|
||||
menu = new PopupMenu.RemoteMenu(this.actor, this._targetApp.menu, this._targetApp.action_group);
|
||||
menu.connect('activate', Lang.bind(this, function() {
|
||||
let win = this._targetApp.get_windows()[0];
|
||||
win.check_alive(global.get_current_time());
|
||||
}));
|
||||
|
||||
} else {
|
||||
if (this.menu.isDummyQuitMenu)
|
||||
return;
|
||||
@ -746,12 +751,9 @@ const ActivitiesButton = new Lang.Class({
|
||||
const PanelCorner = new Lang.Class({
|
||||
Name: 'PanelCorner',
|
||||
|
||||
_init: function(box, side) {
|
||||
_init: function(side) {
|
||||
this._side = side;
|
||||
|
||||
this._box = box;
|
||||
this._box.connect('style-changed', Lang.bind(this, this._boxStyleChanged));
|
||||
|
||||
this.actor = new St.DrawingArea({ style_class: 'panel-corner' });
|
||||
this.actor.connect('style-changed', Lang.bind(this, this._styleChanged));
|
||||
this.actor.connect('repaint', Lang.bind(this, this._repaint));
|
||||
@ -807,12 +809,12 @@ const PanelCorner = new Lang.Class({
|
||||
return children[index];
|
||||
},
|
||||
|
||||
_boxStyleChanged: function() {
|
||||
setStyleParent: function(box) {
|
||||
let side = this._side;
|
||||
|
||||
let rtlAwareContainer = this._box instanceof St.BoxLayout;
|
||||
let rtlAwareContainer = box instanceof St.BoxLayout;
|
||||
if (rtlAwareContainer &&
|
||||
this._box.get_text_direction() == Clutter.TextDirection.RTL) {
|
||||
box.get_text_direction() == Clutter.TextDirection.RTL) {
|
||||
if (this._side == St.Side.LEFT)
|
||||
side = St.Side.RIGHT;
|
||||
else if (this._side == St.Side.RIGHT)
|
||||
@ -821,9 +823,9 @@ const PanelCorner = new Lang.Class({
|
||||
|
||||
let button;
|
||||
if (side == St.Side.LEFT)
|
||||
button = this._findLeftmostButton(this._box);
|
||||
button = this._findLeftmostButton(box);
|
||||
else if (side == St.Side.RIGHT)
|
||||
button = this._findRightmostButton(this._box);
|
||||
button = this._findRightmostButton(box);
|
||||
|
||||
if (button) {
|
||||
if (this._button && this._buttonStyleChangedSignalId) {
|
||||
@ -850,7 +852,7 @@ const PanelCorner = new Lang.Class({
|
||||
|
||||
// The corner doesn't support theme transitions, so override
|
||||
// the .panel-button default
|
||||
button.style = 'transition-duration: 0';
|
||||
button.style = 'transition-duration: 0ms';
|
||||
}
|
||||
},
|
||||
|
||||
@ -863,8 +865,8 @@ const PanelCorner = new Lang.Class({
|
||||
let backgroundColor = node.get_color('-panel-corner-background-color');
|
||||
let borderColor = node.get_color('-panel-corner-border-color');
|
||||
|
||||
let noOverlap = borderColor.alpha == 0;
|
||||
let offsetY = noOverlap ? borderWidth : 0;
|
||||
let overlap = borderColor.alpha != 0;
|
||||
let offsetY = overlap ? 0 : borderWidth;
|
||||
|
||||
let cr = this.actor.get_context();
|
||||
cr.setOperator(Cairo.Operator.SOURCE);
|
||||
@ -888,9 +890,7 @@ const PanelCorner = new Lang.Class({
|
||||
Clutter.cairo_set_source_color(cr, over);
|
||||
cr.fill();
|
||||
|
||||
if (noOverlap)
|
||||
return;
|
||||
|
||||
if (overlap) {
|
||||
let offset = borderWidth;
|
||||
Clutter.cairo_set_source_color(cr, backgroundColor);
|
||||
|
||||
@ -899,6 +899,9 @@ const PanelCorner = new Lang.Class({
|
||||
cr.appendPath(savedPath);
|
||||
cr.fill();
|
||||
cr.restore();
|
||||
}
|
||||
|
||||
cr.$dispose();
|
||||
},
|
||||
|
||||
_styleChanged: function() {
|
||||
@ -958,17 +961,10 @@ const Panel = new Lang.Class({
|
||||
this._rightBox = new St.BoxLayout({ name: 'panelRight' });
|
||||
this.actor.add_actor(this._rightBox);
|
||||
|
||||
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL)
|
||||
this._leftCorner = new PanelCorner(this._rightBox, St.Side.LEFT);
|
||||
else
|
||||
this._leftCorner = new PanelCorner(this._leftBox, St.Side.LEFT);
|
||||
|
||||
this._leftCorner = new PanelCorner(St.Side.LEFT);
|
||||
this.actor.add_actor(this._leftCorner.actor);
|
||||
|
||||
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL)
|
||||
this._rightCorner = new PanelCorner(this._leftBox, St.Side.RIGHT);
|
||||
else
|
||||
this._rightCorner = new PanelCorner(this._rightBox, St.Side.RIGHT);
|
||||
this._rightCorner = new PanelCorner(St.Side.RIGHT);
|
||||
this.actor.add_actor(this._rightCorner.actor);
|
||||
|
||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||
@ -976,6 +972,13 @@ const Panel = new Lang.Class({
|
||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
||||
|
||||
Main.overview.connect('showing', Lang.bind(this, function () {
|
||||
this.actor.add_style_pseudo_class('overview');
|
||||
}));
|
||||
Main.overview.connect('hiding', Lang.bind(this, function () {
|
||||
this.actor.remove_style_pseudo_class('overview');
|
||||
}));
|
||||
|
||||
Main.layoutManager.panelBox.add(this.actor);
|
||||
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'emblem-system-symbolic',
|
||||
{ sortGroup: CtrlAltTab.SortGroup.TOP });
|
||||
@ -1141,17 +1144,13 @@ const Panel = new Lang.Class({
|
||||
this._sessionStyle = Main.sessionMode.panelStyle;
|
||||
if (this._sessionStyle)
|
||||
this._addStyleClassName(this._sessionStyle);
|
||||
},
|
||||
|
||||
_initBox: function(elements, box) {
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
let role = elements[i];
|
||||
let constructor = PANEL_ITEM_IMPLEMENTATIONS[role];
|
||||
if (!constructor) {
|
||||
// panel icon is not supported (can happen for
|
||||
// bluetooth or network)
|
||||
continue;
|
||||
}
|
||||
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL) {
|
||||
this._leftCorner.setStyleParent(this._rightBox);
|
||||
this._rightCorner.setStyleParent(this._leftBox);
|
||||
} else {
|
||||
this._leftCorner.setStyleParent(this._leftBox);
|
||||
this._rightCorner.setStyleParent(this._rightBox);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -244,18 +244,21 @@ const SystemStatusButton = new Lang.Class({
|
||||
this.setIcon(iconName);
|
||||
},
|
||||
|
||||
get icons() {
|
||||
return this._box.get_children();
|
||||
},
|
||||
|
||||
addIcon: function(gicon) {
|
||||
let icon = new St.Icon({ gicon: gicon,
|
||||
style_class: 'system-status-icon' });
|
||||
this._box.add_actor(icon);
|
||||
|
||||
this.emit('icons-changed');
|
||||
|
||||
return icon;
|
||||
},
|
||||
|
||||
setIcon: function(iconName) {
|
||||
// Need to first add a NULL GIcon and then set icon_name, to ensure
|
||||
// compatibility with -symbolic fallbacks
|
||||
|
||||
if (!this.mainIcon)
|
||||
this.mainIcon = this.addIcon(null);
|
||||
this.mainIcon.icon_name = iconName;
|
||||
|
@ -41,10 +41,10 @@ const PointerWatcher = new Lang.Class({
|
||||
Name: 'PointerWatcher',
|
||||
|
||||
_init: function() {
|
||||
let idleMonitor = new GnomeDesktop.IdleMonitor();
|
||||
idleMonitor.connect('became-active', Lang.bind(this, this._onIdleMonitorBecameActive));
|
||||
idleMonitor.add_watch(IDLE_TIME, Lang.bind(this, this._onIdleMonitorBecameIdle));
|
||||
this._idle = idleMonitor.get_idletime() > IDLE_TIME;
|
||||
this._idleMonitor = new GnomeDesktop.IdleMonitor();
|
||||
this._idleMonitor.connect('became-active', Lang.bind(this, this._onIdleMonitorBecameActive));
|
||||
this._idleMonitor.add_watch(IDLE_TIME, Lang.bind(this, this._onIdleMonitorBecameIdle));
|
||||
this._idle = this._idleMonitor.get_idletime() > IDLE_TIME;
|
||||
this._watches = [];
|
||||
this.pointerX = null;
|
||||
this.pointerY = null;
|
||||
|
@ -207,6 +207,7 @@ const PopupBaseMenuItem = new Lang.Class({
|
||||
color.alpha / 255);
|
||||
cr.arc(width / 2, height / 2, width / 3, 0, 2 * Math.PI);
|
||||
cr.fill();
|
||||
cr.$dispose();
|
||||
},
|
||||
|
||||
// This returns column widths in logical order (i.e. from the dot
|
||||
@ -604,6 +605,7 @@ const PopupSliderMenuItem = new Lang.Class({
|
||||
color.alpha / 255);
|
||||
cr.arc(handleX, handleY, handleRadius, 0, 2 * Math.PI);
|
||||
cr.fill();
|
||||
cr.$dispose();
|
||||
},
|
||||
|
||||
_startDragging: function(actor, event) {
|
||||
@ -636,20 +638,34 @@ const PopupSliderMenuItem = new Lang.Class({
|
||||
return true;
|
||||
},
|
||||
|
||||
_onScrollEvent: function (actor, event) {
|
||||
scroll: function(event) {
|
||||
let direction = event.get_scroll_direction();
|
||||
let delta;
|
||||
|
||||
if (event.is_pointer_emulated())
|
||||
return;
|
||||
|
||||
if (direction == Clutter.ScrollDirection.DOWN) {
|
||||
this._value = Math.max(0, this._value - SLIDER_SCROLL_STEP);
|
||||
}
|
||||
else if (direction == Clutter.ScrollDirection.UP) {
|
||||
this._value = Math.min(1, this._value + SLIDER_SCROLL_STEP);
|
||||
delta = -SLIDER_SCROLL_STEP;
|
||||
} else if (direction == Clutter.ScrollDirection.UP) {
|
||||
delta = +SLIDER_SCROLL_STEP;
|
||||
} else if (direction == Clutter.ScrollDirection.SMOOTH) {
|
||||
let [dx, dy] = event.get_scroll_delta();
|
||||
// Even though the slider is horizontal, use dy to match
|
||||
// the UP/DOWN above.
|
||||
delta = -dy / 10;
|
||||
}
|
||||
|
||||
this._value = Math.min(Math.max(0, this._value + delta), 1);
|
||||
|
||||
this._slider.queue_repaint();
|
||||
this.emit('value-changed', this._value);
|
||||
},
|
||||
|
||||
_onScrollEvent: function(actor, event) {
|
||||
this.scroll(event);
|
||||
},
|
||||
|
||||
_motionEvent: function(actor, event) {
|
||||
let absX, absY;
|
||||
[absX, absY] = event.get_coords();
|
||||
@ -873,7 +889,7 @@ const PopupMenuBase = new Lang.Class({
|
||||
|
||||
addSettingsAction: function(title, desktopFile) {
|
||||
let menuItem = this.addAction(title, function() {
|
||||
let app = Shell.AppSystem.get_default().lookup_setting(desktopFile);
|
||||
let app = Shell.AppSystem.get_default().lookup_app(desktopFile);
|
||||
|
||||
if (!app) {
|
||||
log('Settings panel for desktop file ' + desktopFile + ' could not be loaded!');
|
||||
|
@ -1,9 +1,11 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const GdkPixbuf = imports.gi.GdkPixbuf;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const FileUtils = imports.misc.fileUtils;
|
||||
const Search = imports.ui.search;
|
||||
@ -135,8 +137,12 @@ function remoteProvidersLoaded(loadState) {
|
||||
idxB = sortOrder.indexOf(appIdB);
|
||||
|
||||
// if no provider is found in the order, use alphabetical order
|
||||
if ((idxA == -1) && (idxB == -1))
|
||||
return GLib.utf8_collate(providerA.title, providerB.title);
|
||||
if ((idxA == -1) && (idxB == -1)) {
|
||||
let nameA = providerA.appInfo.get_name();
|
||||
let nameB = providerB.appInfo.get_name();
|
||||
|
||||
return GLib.utf8_collate(nameA, nameB);
|
||||
}
|
||||
|
||||
if (numSorted > 1) {
|
||||
// if providerA is the last, it goes after everything
|
||||
@ -187,18 +193,18 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
},
|
||||
|
||||
createIcon: function(size, meta) {
|
||||
let gicon;
|
||||
if (meta['gicon']) {
|
||||
return new St.Icon({ gicon: Gio.icon_new_for_string(meta['gicon']),
|
||||
icon_size: size });
|
||||
gicon = Gio.icon_new_for_string(meta['gicon']);
|
||||
} else if (meta['icon-data']) {
|
||||
let [width, height, rowStride, hasAlpha,
|
||||
bitsPerSample, nChannels, data] = meta['icon-data'];
|
||||
let textureCache = St.TextureCache.get_default();
|
||||
return textureCache.load_from_raw(data, hasAlpha,
|
||||
width, height, rowStride, size);
|
||||
gicon = Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha,
|
||||
bitsPerSample, width, height, rowStride);
|
||||
}
|
||||
|
||||
return null;
|
||||
return new St.Icon({ gicon: gicon,
|
||||
icon_size: size });
|
||||
},
|
||||
|
||||
_getResultsFinished: function(results, error) {
|
||||
@ -215,7 +221,7 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
Lang.bind(this, this._getResultsFinished),
|
||||
this._cancellable);
|
||||
} catch(e) {
|
||||
log('Error calling GetInitialResultSet for provider %s: %s'.format( this.title, e.toString()));
|
||||
log('Error calling GetInitialResultSet for provider %s: %s'.format(this.id, e.toString()));
|
||||
this.searchSystem.pushResults(this, []);
|
||||
}
|
||||
},
|
||||
@ -228,7 +234,7 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
Lang.bind(this, this._getResultsFinished),
|
||||
this._cancellable);
|
||||
} catch(e) {
|
||||
log('Error calling GetSubsearchResultSet for provider %s: %s'.format(this.title, e.toString()));
|
||||
log('Error calling GetSubsearchResultSet for provider %s: %s'.format(this.id, e.toString()));
|
||||
this.searchSystem.pushResults(this, []);
|
||||
}
|
||||
},
|
||||
@ -259,7 +265,7 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
Lang.bind(this, this._getResultMetasFinished, callback),
|
||||
this._cancellable);
|
||||
} catch(e) {
|
||||
log('Error calling GetResultMetas for provider %s: %s'.format(this.title, e.toString()));
|
||||
log('Error calling GetResultMetas for provider %s: %s'.format(this.id, e.toString()));
|
||||
callback([]);
|
||||
}
|
||||
},
|
||||
|
@ -8,11 +8,13 @@ const GnomeDesktop = imports.gi.GnomeDesktop;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
const TweenerEquations = imports.tweener.equations;
|
||||
|
||||
const GnomeSession = imports.misc.gnomeSession;
|
||||
const Hash = imports.misc.hash;
|
||||
const Layout = imports.ui.layout;
|
||||
const LoginManager = imports.misc.loginManager;
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
@ -25,6 +27,7 @@ const Util = imports.misc.util;
|
||||
|
||||
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
|
||||
const LOCK_ENABLED_KEY = 'lock-enabled';
|
||||
const LOCK_DELAY_KEY = 'lock-delay';
|
||||
|
||||
const CURTAIN_SLIDE_TIME = 0.3;
|
||||
// fraction of screen height the arrow must reach before completing
|
||||
@ -126,49 +129,48 @@ const NotificationsBox = new Lang.Class({
|
||||
name: 'screenShieldNotifications',
|
||||
style_class: 'screen-shield-notifications-box' });
|
||||
|
||||
this._residentNotificationBox = new St.BoxLayout({ vertical: true,
|
||||
style_class: 'screen-shield-notifications-box' });
|
||||
let scrollView = new St.ScrollView({ x_fill: false, x_align: St.Align.START });
|
||||
this._persistentNotificationBox = new St.BoxLayout({ vertical: true,
|
||||
style_class: 'screen-shield-notifications-box' });
|
||||
scrollView.add_actor(this._persistentNotificationBox);
|
||||
this._musicBin = new St.Bin({ style_class: 'screen-shield-notifications-box',
|
||||
visible: false });
|
||||
|
||||
this.actor.add(this._residentNotificationBox, { x_fill: true });
|
||||
let scrollView = new St.ScrollView({ x_fill: false, x_align: St.Align.START });
|
||||
this._notificationBox = new St.BoxLayout({ vertical: true,
|
||||
style_class: 'screen-shield-notifications-box' });
|
||||
scrollView.add_actor(this._notificationBox);
|
||||
|
||||
this.actor.add(this._musicBin);
|
||||
this.actor.add(scrollView, { x_fill: true, x_align: St.Align.START });
|
||||
|
||||
this._items = [];
|
||||
Main.messageTray.getSummaryItems().forEach(Lang.bind(this, function(item) {
|
||||
this._summaryItemAdded(Main.messageTray, item, true);
|
||||
this._sources = new Hash.Map();
|
||||
Main.messageTray.getSources().forEach(Lang.bind(this, function(source) {
|
||||
this._sourceAdded(Main.messageTray, source, true);
|
||||
}));
|
||||
this._updateVisibility();
|
||||
|
||||
this._summaryAddedId = Main.messageTray.connect('summary-item-added', Lang.bind(this, this._summaryItemAdded));
|
||||
this._sourceAddedId = Main.messageTray.connect('source-added', Lang.bind(this, this._sourceAdded));
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
if (this._summaryAddedId) {
|
||||
Main.messageTray.disconnect(this._summaryAddedId);
|
||||
this._summaryAddedId = 0;
|
||||
if (this._sourceAddedId) {
|
||||
Main.messageTray.disconnect(this._sourceAddedId);
|
||||
this._sourceAddedId = 0;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this._items.length; i++)
|
||||
this._removeItem(this._items[i]);
|
||||
this._items = [];
|
||||
let items = this._sources.items();
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
let [source, obj] = items[i];
|
||||
this._removeSource(source, obj);
|
||||
}
|
||||
|
||||
this.actor.destroy();
|
||||
},
|
||||
|
||||
_updateVisibility: function() {
|
||||
this._residentNotificationBox.visible = this._residentNotificationBox.get_n_children() > 0;
|
||||
this._persistentNotificationBox.visible = this._persistentNotificationBox.get_children().some(function(a) {
|
||||
this._musicBin.visible = this._musicBin.child != null && this._musicBin.child.visible;
|
||||
this._notificationBox.visible = this._notificationBox.get_children().some(function(a) {
|
||||
return a.visible;
|
||||
});
|
||||
|
||||
this.actor.visible = this._residentNotificationBox.visible || this._persistentNotificationBox.visible;
|
||||
},
|
||||
|
||||
_sourceIsResident: function(source) {
|
||||
return source.hasResidentNotification() && !source.isChat;
|
||||
this.actor.visible = this._musicBin.visible || this._notificationBox.visible;
|
||||
},
|
||||
|
||||
_makeNotificationCountText: function(count, isChat) {
|
||||
@ -178,18 +180,16 @@ const NotificationsBox = new Lang.Class({
|
||||
return ngettext("%d new notification", "%d new notifications", count).format(count);
|
||||
},
|
||||
|
||||
_makeNotificationSource: function(source) {
|
||||
let box = new St.BoxLayout({ style_class: 'screen-shield-notification-source' });
|
||||
|
||||
_makeNotificationSource: function(source, box) {
|
||||
let sourceActor = new MessageTray.SourceActor(source, SUMMARY_ICON_SIZE);
|
||||
box.add(sourceActor.actor, { y_fill: true });
|
||||
|
||||
let textBox = new St.BoxLayout({ vertical: true });
|
||||
box.add(textBox, { y_fill: false, y_align: St.Align.START });
|
||||
|
||||
let label = new St.Label({ text: source.title,
|
||||
let title = new St.Label({ text: source.title,
|
||||
style_class: 'screen-shield-notification-label' });
|
||||
textBox.add(label);
|
||||
textBox.add(title);
|
||||
|
||||
let count = source.unseenCount;
|
||||
let countLabel = new St.Label({ text: this._makeNotificationCountText(count, source.isChat),
|
||||
@ -197,118 +197,179 @@ const NotificationsBox = new Lang.Class({
|
||||
textBox.add(countLabel);
|
||||
|
||||
box.visible = count != 0;
|
||||
return [box, countLabel];
|
||||
return [title, countLabel];
|
||||
},
|
||||
|
||||
_summaryItemAdded: function(tray, item, dontUpdateVisibility) {
|
||||
// Ignore transient sources, or sources explicitly marked not to show
|
||||
// in the lock screen
|
||||
if (item.source.isTransient || !item.source.showInLockScreen)
|
||||
_makeNotificationDetailedSource: function(source, box) {
|
||||
let sourceActor = new MessageTray.SourceActor(source, SUMMARY_ICON_SIZE);
|
||||
box.add(sourceActor.actor, { y_fill: true });
|
||||
|
||||
let textBox = new St.BoxLayout({ vertical: true });
|
||||
box.add(textBox, { y_fill: false, y_align: St.Align.START });
|
||||
|
||||
let title = new St.Label({ text: source.title,
|
||||
style_class: 'screen-shield-notification-label' });
|
||||
textBox.add(title);
|
||||
|
||||
let visible = false;
|
||||
for (let i = 0; i < source.notifications.length; i++) {
|
||||
let n = source.notifications[i];
|
||||
|
||||
if (n.acknowledged || n.isMusic)
|
||||
continue;
|
||||
|
||||
let body = '';
|
||||
if (n.bannerBodyText) {
|
||||
body = n.bannerBodyMarkup ? n.bannerBodyText :
|
||||
GLib.markup_escape_text(n.bannerBodyMarkup, -1);
|
||||
}
|
||||
|
||||
let label = new St.Label({ style_class: 'screen-shield-notification-count-text' });
|
||||
label.clutter_text.set_markup('<b>' + n.title + '</b> ' + body);
|
||||
textBox.add(label);
|
||||
|
||||
visible = true;
|
||||
}
|
||||
|
||||
box.visible = visible;
|
||||
return [title, null];
|
||||
},
|
||||
|
||||
_showSource: function(source, obj, box) {
|
||||
let musicNotification = source.getMusicNotification();
|
||||
|
||||
if (musicNotification != null &&
|
||||
this._musicBin.child == null) {
|
||||
if (musicNotification.actor.get_parent() != null)
|
||||
musicNotification.actor.get_parent().remove_actor(musicNotification.actor);
|
||||
this._musicBin.child = musicNotification.actor;
|
||||
this._musicBin.child.visible = obj.visible;
|
||||
|
||||
musicNotification.expand(false /* animate */);
|
||||
|
||||
obj.musicNotification = musicNotification;
|
||||
}
|
||||
|
||||
if (obj.detailed) {
|
||||
[obj.titleLabel, obj.countLabel] = this._makeNotificationDetailedSource(source, box);
|
||||
} else {
|
||||
[obj.titleLabel, obj.countLabel] = this._makeNotificationSource(source, box);
|
||||
}
|
||||
|
||||
box.visible = obj.visible &&
|
||||
(source.unseenCount > (musicNotification ? 1 : 0));
|
||||
},
|
||||
|
||||
_sourceAdded: function(tray, source, dontUpdateVisibility) {
|
||||
// Ignore transient sources
|
||||
if (source.isTransient)
|
||||
return;
|
||||
|
||||
let obj = {
|
||||
item: item,
|
||||
source: item.source,
|
||||
resident: this._sourceIsResident(item.source),
|
||||
contentUpdatedId: 0,
|
||||
visible: source.policy.showInLockScreen,
|
||||
detailed: source.policy.detailsInLockScreen,
|
||||
sourceDestroyId: 0,
|
||||
sourceCountChangedId: 0,
|
||||
sourceTitleChangedId: 0,
|
||||
sourceUpdatedId: 0,
|
||||
musicNotification: null,
|
||||
sourceBox: null,
|
||||
titleLabel: null,
|
||||
countLabel: null,
|
||||
};
|
||||
|
||||
if (obj.resident) {
|
||||
this._residentNotificationBox.add(item.notificationStackWidget);
|
||||
item.closeButton.hide();
|
||||
item.prepareNotificationStackForShowing();
|
||||
} else {
|
||||
[obj.sourceBox, obj.countLabel] = this._makeNotificationSource(item.source);
|
||||
this._persistentNotificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.START });
|
||||
}
|
||||
obj.sourceBox = new St.BoxLayout({ style_class: 'screen-shield-notification-source' });
|
||||
this._showSource(source, obj, obj.sourceBox);
|
||||
this._notificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.START });
|
||||
|
||||
obj.contentUpdatedId = item.connect('content-updated', Lang.bind(this, this._onItemContentUpdated));
|
||||
obj.sourceCountChangedId = item.source.connect('count-updated', Lang.bind(this, this._onSourceChanged));
|
||||
obj.sourceTitleChangedId = item.source.connect('title-changed', Lang.bind(this, this._onSourceChanged));
|
||||
obj.sourceDestroyId = item.source.connect('destroy', Lang.bind(this, this._onSourceDestroy));
|
||||
this._items.push(obj);
|
||||
obj.sourceCountChangedId = source.connect('count-updated', Lang.bind(this, function(source) {
|
||||
this._countChanged(source, obj);
|
||||
}));
|
||||
obj.sourceTitleChangedId = source.connect('title-changed', Lang.bind(this, function(source) {
|
||||
this._titleChanged(source, obj);
|
||||
}));
|
||||
obj.policyChangedId = source.policy.connect('policy-changed', Lang.bind(this, function(policy, key) {
|
||||
if (key == 'show-in-lock-screen')
|
||||
this._visibleChanged(source, obj);
|
||||
else
|
||||
this._detailedChanged(source, obj);
|
||||
}));
|
||||
obj.sourceDestroyId = source.connect('destroy', Lang.bind(this, function(source) {
|
||||
this._onSourceDestroy(source, obj);
|
||||
}));
|
||||
|
||||
this._sources.set(source, obj);
|
||||
|
||||
if (!dontUpdateVisibility)
|
||||
this._updateVisibility();
|
||||
},
|
||||
|
||||
_findSource: function(source) {
|
||||
for (let i = 0; i < this._items.length; i++) {
|
||||
if (this._items[i].source == source)
|
||||
return i;
|
||||
_titleChanged: function(source, obj) {
|
||||
obj.titleLabel.text = source.title;
|
||||
},
|
||||
|
||||
_countChanged: function(source, obj) {
|
||||
if (obj.detailed) {
|
||||
// A new notification was pushed, or a previous notification was destroyed.
|
||||
// Give up, and build the list again.
|
||||
|
||||
obj.sourceBox.destroy_all_children();
|
||||
obj.titleLabel = obj.countLabel = null;
|
||||
this._showSource(source, obj, obj.sourceBox);
|
||||
} else {
|
||||
let count = source.unseenCount;
|
||||
obj.countLabel.text = this._makeNotificationCountText(count, source.isChat);
|
||||
}
|
||||
|
||||
return -1;
|
||||
obj.sourceBox.visible = obj.visible &&
|
||||
(source.unseenCount > (obj.musicNotification ? 1 : 0));
|
||||
this._updateVisibility();
|
||||
},
|
||||
|
||||
_onItemContentUpdated: function(item) {
|
||||
let obj = this._items[this._findSource(item.source)];
|
||||
this._updateItem(obj);
|
||||
},
|
||||
|
||||
_onSourceChanged: function(source) {
|
||||
let obj = this._items[this._findSource(source)];
|
||||
this._updateItem(obj);
|
||||
},
|
||||
|
||||
_updateItem: function(obj) {
|
||||
let itemShouldBeResident = this._sourceIsResident(obj.source);
|
||||
|
||||
if (itemShouldBeResident && obj.resident) {
|
||||
// Nothing to do here, the actor is already updated
|
||||
_visibleChanged: function(source, obj) {
|
||||
if (obj.visible == source.policy.showInLockScreen)
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj.resident && !itemShouldBeResident) {
|
||||
// make into a regular item
|
||||
obj.item.doneShowingNotificationStack();
|
||||
this._residentNotificationBox.remove_actor(obj.item.notificationStackWidget);
|
||||
|
||||
[obj.sourceBox, obj.countLabel] = this._makeNotificationSource(obj.source);
|
||||
this._persistentNotificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.START });
|
||||
} else if (itemShouldBeResident && !obj.resident) {
|
||||
// make into a resident item
|
||||
obj.sourceBox.destroy();
|
||||
obj.sourceBox = obj.countLabel = null;
|
||||
obj.resident = true;
|
||||
|
||||
this._residentNotificationBox.add(obj.item.notificationStackWidget);
|
||||
obj.item.closeButton.hide();
|
||||
obj.item.prepareNotificationStackForShowing();
|
||||
} else {
|
||||
// just update the counter
|
||||
let count = obj.source.unseenCount;
|
||||
obj.countLabel.text = this._makeNotificationCountText(count, obj.source.isChat);
|
||||
obj.sourceBox.visible = count != 0;
|
||||
}
|
||||
obj.visible = source.policy.showInLockScreen;
|
||||
if (obj.musicNotification)
|
||||
obj.musicNotification.actor.visible = obj.visible;
|
||||
obj.sourceBox.visible = obj.visible &&
|
||||
source.unseenCount > (obj.musicNotification ? 1 : 0);
|
||||
|
||||
this._updateVisibility();
|
||||
},
|
||||
|
||||
_onSourceDestroy: function(source) {
|
||||
let idx = this._findSource(source);
|
||||
_detailedChanged: function(source, obj) {
|
||||
if (obj.detailed == source.policy.detailsInLockScreen)
|
||||
return;
|
||||
|
||||
this._removeItem(this._items[idx]);
|
||||
this._items.splice(idx, 1);
|
||||
obj.detailed = source.policy.detailsInLockScreen;
|
||||
|
||||
obj.sourceBox.destroy_all_children();
|
||||
obj.titleLabel = obj.countLabel = null;
|
||||
this._showSource(source, obj, obj.sourceBox);
|
||||
},
|
||||
|
||||
_onSourceDestroy: function(source, obj) {
|
||||
this._removeSource(source, obj);
|
||||
this._updateVisibility();
|
||||
},
|
||||
|
||||
_removeItem: function(obj) {
|
||||
if (obj.resident) {
|
||||
obj.item.doneShowingNotificationStack();
|
||||
this._residentNotificationBox.remove_actor(obj.item.notificationStackWidget);
|
||||
} else {
|
||||
_removeSource: function(source, obj) {
|
||||
obj.sourceBox.destroy();
|
||||
obj.sourceBox = obj.titleLabel = obj.countLabel = null;
|
||||
|
||||
if (obj.musicNotification) {
|
||||
this._musicBin.child = null;
|
||||
obj.musicNotification = null;
|
||||
}
|
||||
|
||||
obj.item.disconnect(obj.contentUpdatedId);
|
||||
obj.source.disconnect(obj.sourceDestroyId);
|
||||
obj.source.disconnect(obj.sourceCountChangedId);
|
||||
obj.source.disconnect(obj.sourceTitleChangedId);
|
||||
source.disconnect(obj.sourceDestroyId);
|
||||
source.disconnect(obj.sourceCountChangedId);
|
||||
source.disconnect(obj.sourceTitleChangedId);
|
||||
source.policy.disconnect(obj.policyChangedId);
|
||||
|
||||
this._sources.delete(source);
|
||||
},
|
||||
});
|
||||
|
||||
@ -394,6 +455,7 @@ const ScreenShield = new Lang.Class({
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
name: 'lockScreenGroup',
|
||||
visible: false,
|
||||
});
|
||||
this._lockScreenGroup.connect('key-release-event',
|
||||
Lang.bind(this, this._onLockScreenKeyRelease));
|
||||
@ -464,10 +526,15 @@ const ScreenShield = new Lang.Class({
|
||||
|
||||
this._screenSaverDBus = new ShellDBus.ScreenSaverDBus(this);
|
||||
|
||||
this._inhibitor = null;
|
||||
this._loginManager = LoginManager.getLoginManager();
|
||||
this._loginManager.connect('prepare-for-sleep',
|
||||
Lang.bind(this, this._prepareForSleep));
|
||||
this._inhibitSuspend();
|
||||
|
||||
this._loginSession = this._loginManager.getCurrentSessionProxy();
|
||||
this._loginSession.connectSignal('Lock', Lang.bind(this, function() { this.lock(false); }));
|
||||
this._loginSession.connectSignal('Unlock', Lang.bind(this, function() { this.unlock(); }));
|
||||
this._loginSession.connectSignal('Unlock', Lang.bind(this, function() { this.deactivate(false); }));
|
||||
|
||||
this._settings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
|
||||
|
||||
@ -475,17 +542,30 @@ const ScreenShield = new Lang.Class({
|
||||
this._hasLockScreen = false;
|
||||
this._isGreeter = false;
|
||||
this._isActive = false;
|
||||
this._isLocked = false;
|
||||
this._inUnlockAnimation = false;
|
||||
this._activationTime = 0;
|
||||
this._becameActiveId = 0;
|
||||
this._lockTimeoutId = 0;
|
||||
|
||||
this._lightbox = new Lightbox.Lightbox(Main.uiGroup,
|
||||
{ inhibitEvents: true,
|
||||
fadeInTime: STANDARD_FADE_TIME,
|
||||
fadeFactor: 1 });
|
||||
this._lightbox.connect('shown', Lang.bind(this, this._onLightboxShown));
|
||||
|
||||
this.idleMonitor = new GnomeDesktop.IdleMonitor();
|
||||
},
|
||||
|
||||
_liftShield: function(onPrimary, velocity) {
|
||||
if (this._isLocked) {
|
||||
this._ensureUnlockDialog(onPrimary, true /* allowCancel */);
|
||||
this._hideLockScreen(true /* animate */, velocity);
|
||||
} else {
|
||||
this.deactivate(true /* animate */);
|
||||
}
|
||||
},
|
||||
|
||||
_onLockScreenKeyRelease: function(actor, event) {
|
||||
let symbol = event.get_key_symbol();
|
||||
|
||||
@ -503,8 +583,7 @@ const ScreenShield = new Lang.Class({
|
||||
if (symbol == Clutter.KEY_Escape ||
|
||||
symbol == Clutter.KEY_Return ||
|
||||
symbol == Clutter.KEY_KP_Enter) {
|
||||
this._ensureUnlockDialog(true, true);
|
||||
this._hideLockScreen(true, 0);
|
||||
this._liftShield(false, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -526,13 +605,37 @@ const ScreenShield = new Lang.Class({
|
||||
|
||||
// 7 standard scrolls to lift up
|
||||
if (this._lockScreenScrollCounter > 35) {
|
||||
this._ensureUnlockDialog(false, true);
|
||||
this._hideLockScreen(true, 0);
|
||||
this._liftShield(true, 0);
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_inhibitSuspend: function() {
|
||||
this._loginManager.inhibit(_("GNOME needs to lock the screen"),
|
||||
Lang.bind(this, function(inhibitor) {
|
||||
this._inhibitor = inhibitor;
|
||||
}));
|
||||
},
|
||||
|
||||
_uninhibitSuspend: function() {
|
||||
if (this._inhibitor)
|
||||
this._inhibitor.close(null);
|
||||
this._inhibitor = null;
|
||||
},
|
||||
|
||||
_prepareForSleep: function(loginManager, aboutToSuspend) {
|
||||
if (aboutToSuspend) {
|
||||
if (!this._settings.get_boolean(LOCK_ENABLED_KEY)) {
|
||||
this._uninhibitSuspend();
|
||||
return;
|
||||
}
|
||||
this.lock(true);
|
||||
} else {
|
||||
this._inhibitSuspend();
|
||||
}
|
||||
},
|
||||
|
||||
_animateArrows: function() {
|
||||
let arrows = this._arrowContainer.get_children();
|
||||
let unitaryDelay = ARROW_ANIMATION_TIME / (arrows.length + 1);
|
||||
@ -558,6 +661,8 @@ const ScreenShield = new Lang.Class({
|
||||
_onDragBegin: function() {
|
||||
Tweener.removeTweens(this._lockScreenGroup);
|
||||
this._lockScreenState = MessageTray.State.HIDING;
|
||||
|
||||
if (this._isLocked)
|
||||
this._ensureUnlockDialog(false, false);
|
||||
|
||||
return true;
|
||||
@ -579,8 +684,7 @@ const ScreenShield = new Lang.Class({
|
||||
if (this._lockScreenGroup.y < -(ARROW_DRAG_THRESHOLD * global.stage.height)) {
|
||||
// Complete motion automatically
|
||||
let [velocity, velocityX, velocityY] = this._dragAction.get_velocity(0);
|
||||
this._hideLockScreen(true, -velocityY);
|
||||
this._ensureUnlockDialog(false, true);
|
||||
this._liftShield(true, -velocityY);
|
||||
} else {
|
||||
// restore the lock screen to its original place
|
||||
// try to use the same speed as the normal animation
|
||||
@ -620,33 +724,79 @@ const ScreenShield = new Lang.Class({
|
||||
}
|
||||
|
||||
if (!this._isModal) {
|
||||
Main.pushModal(this.actor, { keybindingMode: Main.KeybindingMode.LOCK_SCREEN });
|
||||
Main.pushModal(this.actor, { keybindingMode: Shell.KeyBindingMode.LOCK_SCREEN });
|
||||
this._isModal = true;
|
||||
}
|
||||
|
||||
if (!this._isActive) {
|
||||
if (this._lightbox.actor.visible ||
|
||||
this._isActive) {
|
||||
// We're either shown and active, or in the process of
|
||||
// showing.
|
||||
// The latter is a very unlikely condition (it requires
|
||||
// idle-delay < 20), but in any case we have nothing
|
||||
// to do at this point: either isActive is true, or
|
||||
// it will soon be.
|
||||
// isActive can also be true if the lightbox is hidden,
|
||||
// in case the shield is down and the user hasn't unlocked yet
|
||||
return;
|
||||
}
|
||||
|
||||
this._lightbox.show();
|
||||
|
||||
if (this._activationTime == 0)
|
||||
this._activationTime = GLib.get_monotonic_time();
|
||||
|
||||
this._becameActiveId = this.idleMonitor.connect('became-active', Lang.bind(this, function() {
|
||||
if (this._becameActiveId == 0)
|
||||
this._becameActiveId = this.idleMonitor.connect('became-active',
|
||||
Lang.bind(this, this._onUserBecameActive));
|
||||
|
||||
let shouldLock = this._settings.get_boolean(LOCK_ENABLED_KEY) && !this._isLocked;
|
||||
|
||||
if (shouldLock) {
|
||||
let lockTimeout = Math.max(STANDARD_FADE_TIME, this._settings.get_uint(LOCK_DELAY_KEY));
|
||||
this._lockTimeoutId = Mainloop.timeout_add(lockTimeout * 1000,
|
||||
Lang.bind(this, function() {
|
||||
this._lockTimeoutId = 0;
|
||||
this.lock(true);
|
||||
return false;
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
_onUserBecameActive: function() {
|
||||
// This function gets called here when the user becomes active
|
||||
// after gnome-session changed the status to IDLE
|
||||
// There are four possibilities here:
|
||||
// - we're called when already locked; isActive and isLocked are true,
|
||||
// we just go back to the lock screen curtain
|
||||
// - we're called before the lightbox is fully shown; at this point
|
||||
// isActive is false, so we just hide the ligthbox, reset the activationTime
|
||||
// and go back to the unlocked desktop
|
||||
// - we're called after showing the lightbox, but before the lock
|
||||
// delay; this is mostly like the case above, but isActive is true now
|
||||
// so we need to notify gnome-settings-daemon to go back to the normal
|
||||
// policies for blanking
|
||||
// (they're handled by the same code, and we emit one extra ActiveChanged
|
||||
// signal in the case above)
|
||||
// - we're called after showing the lightbox and after lock-delay; the
|
||||
// session is effectivelly locked now, it's time to build and show
|
||||
// the lock screen
|
||||
|
||||
this.idleMonitor.disconnect(this._becameActiveId);
|
||||
this._becameActiveId = 0;
|
||||
|
||||
let lightboxWasShown = this._lightbox.shown;
|
||||
this._lightbox.hide();
|
||||
|
||||
let shouldLock = lightboxWasShown && this._settings.get_boolean(LOCK_ENABLED_KEY);
|
||||
if (shouldLock || this._isLocked) {
|
||||
this.lock(false);
|
||||
} else if (this._isActive) {
|
||||
this.unlock();
|
||||
// Shortcircuit in case the mouse was moved before the fade completed
|
||||
if (!lightboxWasShown) {
|
||||
this.deactivate(false);
|
||||
return;
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
this._isActive = true;
|
||||
this.emit('lock-status-changed');
|
||||
}
|
||||
_onLightboxShown: function() {
|
||||
this.activate(false);
|
||||
},
|
||||
|
||||
showDialog: function() {
|
||||
@ -663,6 +813,7 @@ const ScreenShield = new Lang.Class({
|
||||
|
||||
this.actor.show();
|
||||
this._isGreeter = Main.sessionMode.isGreeter;
|
||||
this._isLocked = true;
|
||||
this._ensureUnlockDialog(true, true);
|
||||
this._hideLockScreen(false, 0);
|
||||
},
|
||||
@ -683,6 +834,9 @@ const ScreenShield = new Lang.Class({
|
||||
},
|
||||
|
||||
_hideLockScreen: function(animate, velocity) {
|
||||
if (this._lockScreenState == MessageTray.State.HIDDEN)
|
||||
return;
|
||||
|
||||
this._lockScreenState = MessageTray.State.HIDING;
|
||||
|
||||
if (animate) {
|
||||
@ -724,7 +878,7 @@ const ScreenShield = new Lang.Class({
|
||||
let constructor = Main.sessionMode.unlockDialog;
|
||||
if (!constructor) {
|
||||
// This session mode has no locking capabilities
|
||||
this.unlock();
|
||||
this.deactivate(true);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -734,8 +888,10 @@ const ScreenShield = new Lang.Class({
|
||||
let time = global.get_current_time();
|
||||
this._dialog.connect('loaded', Lang.bind(this, function() {
|
||||
if (!this._dialog.open(time, onPrimary)) {
|
||||
// This is kind of an impossible error: we're already modal
|
||||
// by the time we reach this...
|
||||
log('Could not open login dialog: failed to acquire grab');
|
||||
this.unlock();
|
||||
this.deactivate(true);
|
||||
}
|
||||
}));
|
||||
|
||||
@ -751,10 +907,17 @@ const ScreenShield = new Lang.Class({
|
||||
},
|
||||
|
||||
_onUnlockSucceded: function() {
|
||||
this._tweenUnlocked();
|
||||
this.deactivate(true);
|
||||
},
|
||||
|
||||
_resetLockScreen: function(animateLockScreen, animateLockDialog) {
|
||||
// Don't reset the lock screen unless it is completely hidden
|
||||
// This prevents the shield going down if the lock-delay timeout
|
||||
// fires while the user is dragging (which has the potential
|
||||
// to confuse our state)
|
||||
if (this._lockScreenState != MessageTray.State.HIDDEN)
|
||||
return;
|
||||
|
||||
this._ensureLockScreen();
|
||||
this._lockDialogGroup.scale_x = 1;
|
||||
this._lockDialogGroup.scale_y = 1;
|
||||
@ -821,6 +984,14 @@ const ScreenShield = new Lang.Class({
|
||||
this._lockScreenGroup.fixed_position_set = false;
|
||||
this._lockScreenScrollCounter = 0;
|
||||
|
||||
let prevIsActive = this._isActive;
|
||||
this._isActive = true;
|
||||
|
||||
if (prevIsActive != this._isActive)
|
||||
this.emit('active-changed');
|
||||
|
||||
this._uninhibitSuspend();
|
||||
|
||||
this.emit('lock-screen-shown');
|
||||
},
|
||||
|
||||
@ -842,12 +1013,10 @@ const ScreenShield = new Lang.Class({
|
||||
|
||||
this._lockScreenContents.add_actor(this._lockScreenContentsBox);
|
||||
|
||||
if (this._settings.get_boolean('show-notifications')) {
|
||||
this._notificationsBox = new NotificationsBox();
|
||||
this._lockScreenContentsBox.add(this._notificationsBox.actor, { x_fill: true,
|
||||
y_fill: true,
|
||||
expand: true });
|
||||
}
|
||||
|
||||
this._hasLockScreen = true;
|
||||
},
|
||||
@ -872,6 +1041,10 @@ const ScreenShield = new Lang.Class({
|
||||
},
|
||||
|
||||
get locked() {
|
||||
return this._isLocked;
|
||||
},
|
||||
|
||||
get active() {
|
||||
return this._isActive;
|
||||
},
|
||||
|
||||
@ -879,27 +1052,20 @@ const ScreenShield = new Lang.Class({
|
||||
return this._activationTime;
|
||||
},
|
||||
|
||||
_tweenUnlocked: function() {
|
||||
this._inUnlockAnimation = true;
|
||||
this.unlock();
|
||||
deactivate: function(animate) {
|
||||
this._hideLockScreen(animate, 0);
|
||||
|
||||
Tweener.addTween(this._lockDialogGroup, {
|
||||
scale_x: 0,
|
||||
scale_y: 0,
|
||||
time: Overview.ANIMATION_TIME,
|
||||
time: animate ? Overview.ANIMATION_TIME : 0,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: function() {
|
||||
if (this._dialog) {
|
||||
this._dialog.destroy();
|
||||
this._dialog = null;
|
||||
}
|
||||
this.actor.hide();
|
||||
this._inUnlockAnimation = false;
|
||||
},
|
||||
onComplete: Lang.bind(this, this._completeDeactivate),
|
||||
onCompleteScope: this
|
||||
});
|
||||
},
|
||||
|
||||
unlock: function() {
|
||||
_completeDeactivate: function() {
|
||||
if (this._hasLockScreen)
|
||||
this._clearLockScreen();
|
||||
|
||||
@ -915,7 +1081,6 @@ const ScreenShield = new Lang.Class({
|
||||
this._isModal = false;
|
||||
}
|
||||
|
||||
if (!this._inUnlockAnimation)
|
||||
this.actor.hide();
|
||||
|
||||
if (Main.sessionMode.currentMode == 'lock-screen')
|
||||
@ -923,18 +1088,24 @@ const ScreenShield = new Lang.Class({
|
||||
if (Main.sessionMode.currentMode == 'unlock-dialog')
|
||||
Main.sessionMode.popMode('unlock-dialog');
|
||||
|
||||
if (this._becameActiveId != 0) {
|
||||
this.idleMonitor.disconnect(this._becameActiveId);
|
||||
this._becameActiveId = 0;
|
||||
}
|
||||
|
||||
if (this._lockTimeoutId != 0) {
|
||||
Mainloop.source_remove(this._lockTimeoutId);
|
||||
this._lockTimeoutId = 0;
|
||||
}
|
||||
|
||||
this._activationTime = 0;
|
||||
this._isActive = false;
|
||||
this._isLocked = false;
|
||||
this.emit('lock-status-changed');
|
||||
this.emit('active-changed');
|
||||
this.emit('locked-changed');
|
||||
},
|
||||
|
||||
lock: function(animate) {
|
||||
if (!this._isModal) {
|
||||
Main.pushModal(this.actor, { keybindingMode: Main.KeybindingMode.LOCK_SCREEN });
|
||||
this._isModal = true;
|
||||
}
|
||||
|
||||
activate: function(animate) {
|
||||
if (this._activationTime == 0)
|
||||
this._activationTime = GLib.get_monotonic_time();
|
||||
|
||||
@ -949,9 +1120,28 @@ const ScreenShield = new Lang.Class({
|
||||
|
||||
this._resetLockScreen(animate, animate);
|
||||
|
||||
this._isActive = true;
|
||||
// We used to set isActive and emit active-changed here,
|
||||
// but now we do that from lockScreenShown, which means
|
||||
// there is a 0.3 seconds window during which the lock
|
||||
// screen is effectively visible and the screen is locked, but
|
||||
// the DBus interface reports the screensaver is off.
|
||||
// This is because when we emit ActiveChanged(true),
|
||||
// gnome-settings-daemon blanks the screen, and we don't want
|
||||
// blank during the animation.
|
||||
// This is not a problem for the idle fade case, because we
|
||||
// activate without animation in that case.
|
||||
},
|
||||
|
||||
lock: function(animate) {
|
||||
if (!this._isModal) {
|
||||
Main.pushModal(this.actor, { keybindingMode: Shell.KeyBindingMode.LOCK_SCREEN });
|
||||
this._isModal = true;
|
||||
}
|
||||
|
||||
this._isLocked = true;
|
||||
this.emit('lock-status-changed');
|
||||
this.activate(animate);
|
||||
|
||||
this.emit('locked-changed');
|
||||
},
|
||||
});
|
||||
Signals.addSignalMethods(ScreenShield.prototype);
|
||||
@ -985,13 +1175,13 @@ const ScreenShieldFallback = new Lang.Class({
|
||||
else
|
||||
this._locked = false;
|
||||
|
||||
this.emit('lock-status-changed', this._locked);
|
||||
this.emit('active-changed', this._locked);
|
||||
},
|
||||
|
||||
_onSignal: function(proxy, senderName, signalName, params) {
|
||||
if (signalName == 'ActiveChanged') {
|
||||
[this._locked] = params.deep_unpack();
|
||||
this.emit('lock-status-changed', this._locked);
|
||||
this.emit('active-changed', this._locked);
|
||||
}
|
||||
},
|
||||
|
||||
|
272
js/ui/screenshot.js
Normal file
272
js/ui/screenshot.js
Normal file
@ -0,0 +1,272 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gdk = imports.gi.Gdk;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const ScreenshotIface = <interface name="org.gnome.Shell.Screenshot">
|
||||
<method name="ScreenshotArea">
|
||||
<arg type="i" direction="in" name="x"/>
|
||||
<arg type="i" direction="in" name="y"/>
|
||||
<arg type="i" direction="in" name="width"/>
|
||||
<arg type="i" direction="in" name="height"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
<arg type="s" direction="out" name="filename_used"/>
|
||||
</method>
|
||||
<method name="ScreenshotWindow">
|
||||
<arg type="b" direction="in" name="include_frame"/>
|
||||
<arg type="b" direction="in" name="include_cursor"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
<arg type="s" direction="out" name="filename_used"/>
|
||||
</method>
|
||||
<method name="Screenshot">
|
||||
<arg type="b" direction="in" name="include_cursor"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
<arg type="s" direction="out" name="filename_used"/>
|
||||
</method>
|
||||
<method name="SelectArea">
|
||||
<arg type="i" direction="out" name="x"/>
|
||||
<arg type="i" direction="out" name="y"/>
|
||||
<arg type="i" direction="out" name="width"/>
|
||||
<arg type="i" direction="out" name="height"/>
|
||||
</method>
|
||||
<method name="FlashArea">
|
||||
<arg type="i" direction="in" name="x"/>
|
||||
<arg type="i" direction="in" name="y"/>
|
||||
<arg type="i" direction="in" name="width"/>
|
||||
<arg type="i" direction="in" name="height"/>
|
||||
</method>
|
||||
</interface>;
|
||||
|
||||
const ScreenshotService = new Lang.Class({
|
||||
Name: 'ScreenshotService',
|
||||
|
||||
_init: function() {
|
||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenshotIface, this);
|
||||
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/Screenshot');
|
||||
|
||||
Gio.DBus.session.own_name('org.gnome.Shell.Screenshot', Gio.BusNameOwnerFlags.REPLACE, null, null);
|
||||
},
|
||||
|
||||
_onScreenshotComplete: function(obj, result, area, filenameUsed, flash, invocation) {
|
||||
if (flash && result) {
|
||||
let flashspot = new Flashspot(area);
|
||||
flashspot.fire();
|
||||
}
|
||||
|
||||
let retval = GLib.Variant.new('(bs)', [result, filenameUsed]);
|
||||
invocation.return_value(retval);
|
||||
},
|
||||
|
||||
ScreenshotAreaAsync : function (params, invocation) {
|
||||
let [x, y, width, height, flash, filename, callback] = params;
|
||||
let screenshot = new Shell.Screenshot();
|
||||
screenshot.screenshot_area (x, y, width, height, filename,
|
||||
Lang.bind(this, this._onScreenshotComplete,
|
||||
flash, invocation));
|
||||
},
|
||||
|
||||
ScreenshotWindowAsync : function (params, invocation) {
|
||||
let [include_frame, include_cursor, flash, filename] = params;
|
||||
let screenshot = new Shell.Screenshot();
|
||||
screenshot.screenshot_window (include_frame, include_cursor, filename,
|
||||
Lang.bind(this, this._onScreenshotComplete,
|
||||
flash, invocation));
|
||||
},
|
||||
|
||||
ScreenshotAsync : function (params, invocation) {
|
||||
let [include_cursor, flash, filename] = params;
|
||||
let screenshot = new Shell.Screenshot();
|
||||
screenshot.screenshot(include_cursor, filename,
|
||||
Lang.bind(this, this._onScreenshotComplete,
|
||||
flash, invocation));
|
||||
},
|
||||
|
||||
SelectAreaAsync: function (params, invocation) {
|
||||
let selectArea = new SelectArea();
|
||||
selectArea.show();
|
||||
selectArea.connect('finished', Lang.bind(this,
|
||||
function(selectArea, areaRectangle) {
|
||||
if (areaRectangle) {
|
||||
let retval = GLib.Variant.new('(iiii)',
|
||||
[areaRectangle.x, areaRectangle.y,
|
||||
areaRectangle.width, areaRectangle.height]);
|
||||
invocation.return_value(retval);
|
||||
} else {
|
||||
invocation.return_error_literal(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
|
||||
"Operation was cancelled");
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
FlashArea: function(x, y, width, height) {
|
||||
let flashspot = new Flashspot({ x : x, y : y, width: width, height: height});
|
||||
flashspot.fire();
|
||||
}
|
||||
});
|
||||
|
||||
const SelectArea = new Lang.Class({
|
||||
Name: 'SelectArea',
|
||||
|
||||
_init: function() {
|
||||
this._startX = -1;
|
||||
this._startY = -1;
|
||||
this._lastX = 0;
|
||||
this._lastY = 0;
|
||||
|
||||
this._initRubberbandColors();
|
||||
|
||||
this._group = new St.Widget({ visible: false,
|
||||
reactive: true,
|
||||
x: 0,
|
||||
y: 0 });
|
||||
Main.uiGroup.add_actor(this._group);
|
||||
|
||||
this._group.connect('button-press-event',
|
||||
Lang.bind(this, this._onButtonPress));
|
||||
this._group.connect('button-release-event',
|
||||
Lang.bind(this, this._onButtonRelease));
|
||||
this._group.connect('key-press-event',
|
||||
Lang.bind(this, this._onKeyPress));
|
||||
this._group.connect('motion-event',
|
||||
Lang.bind(this, this._onMotionEvent));
|
||||
|
||||
let constraint = new Clutter.BindConstraint({ source: global.stage,
|
||||
coordinate: Clutter.BindCoordinate.ALL });
|
||||
this._group.add_constraint(constraint);
|
||||
|
||||
this._rubberband = new Clutter.Rectangle({ color: this._background,
|
||||
has_border: true,
|
||||
border_width: 1,
|
||||
border_color: this._border });
|
||||
this._group.add_actor(this._rubberband);
|
||||
},
|
||||
|
||||
show: function() {
|
||||
if (!Main.pushModal(this._group) || this._group.visible)
|
||||
return;
|
||||
|
||||
global.set_cursor(Shell.Cursor.CROSSHAIR);
|
||||
this._group.visible = true;
|
||||
},
|
||||
|
||||
_initRubberbandColors: function() {
|
||||
function colorFromRGBA(rgba) {
|
||||
return new Clutter.Color({ red: rgba.red * 255,
|
||||
green: rgba.green * 255,
|
||||
blue: rgba.blue * 255,
|
||||
alpha: rgba.alpha * 255 });
|
||||
}
|
||||
|
||||
let path = new Gtk.WidgetPath();
|
||||
path.append_type(Gtk.IconView);
|
||||
|
||||
let context = new Gtk.StyleContext();
|
||||
context.set_path(path);
|
||||
context.add_class('rubberband');
|
||||
|
||||
this._background = colorFromRGBA(context.get_background_color(Gtk.StateFlags.NORMAL));
|
||||
this._border = colorFromRGBA(context.get_border_color(Gtk.StateFlags.NORMAL));
|
||||
},
|
||||
|
||||
_getGeometry: function() {
|
||||
return { x: Math.min(this._startX, this._lastX),
|
||||
y: Math.min(this._startY, this._lastY),
|
||||
width: Math.abs(this._startX - this._lastX),
|
||||
height: Math.abs(this._startY - this._lastY) };
|
||||
},
|
||||
|
||||
_onKeyPress: function(actor, event) {
|
||||
if (event.get_key_symbol() == Clutter.Escape)
|
||||
this._destroy(null, false);
|
||||
|
||||
return;
|
||||
},
|
||||
|
||||
_onMotionEvent: function(actor, event) {
|
||||
if (this._startX == -1 || this._startY == -1)
|
||||
return false;
|
||||
|
||||
[this._lastX, this._lastY] = event.get_coords();
|
||||
let geometry = this._getGeometry();
|
||||
|
||||
this._rubberband.set_position(geometry.x, geometry.y);
|
||||
this._rubberband.set_size(geometry.width, geometry.height);
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_onButtonPress: function(actor, event) {
|
||||
[this._startX, this._startY] = event.get_coords();
|
||||
this._rubberband.set_position(this._startX, this._startY);
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_onButtonRelease: function(actor, event) {
|
||||
this._destroy(this._getGeometry(), true);
|
||||
return false;
|
||||
},
|
||||
|
||||
_destroy: function(geometry, fade) {
|
||||
Tweener.addTween(this._group,
|
||||
{ opacity: 0,
|
||||
time: fade ? 0.2 : 0,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this,
|
||||
function() {
|
||||
Main.popModal(this._group);
|
||||
this._group.destroy();
|
||||
global.unset_cursor();
|
||||
|
||||
this.emit('finished', geometry);
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(SelectArea.prototype);
|
||||
|
||||
const FLASHSPOT_ANIMATION_OUT_TIME = 0.5; // seconds
|
||||
|
||||
const Flashspot = new Lang.Class({
|
||||
Name: 'Flashspot',
|
||||
Extends: Lightbox.Lightbox,
|
||||
|
||||
_init: function(area) {
|
||||
this.parent(Main.uiGroup, { inhibitEvents: true,
|
||||
width: area.width,
|
||||
height: area.height });
|
||||
|
||||
this.actor.style_class = 'flashspot';
|
||||
this.actor.set_position(area.x, area.y);
|
||||
},
|
||||
|
||||
fire: function() {
|
||||
this.actor.show();
|
||||
this.actor.opacity = 255;
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 0,
|
||||
time: FLASHSPOT_ANIMATION_OUT_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this.destroy();
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
@ -17,6 +17,26 @@ const Search = imports.ui.search;
|
||||
const MAX_LIST_SEARCH_RESULTS_ROWS = 3;
|
||||
const MAX_GRID_SEARCH_RESULTS_ROWS = 1;
|
||||
|
||||
const MaxWidthBin = new Lang.Class({
|
||||
Name: 'MaxWidthBin',
|
||||
Extends: St.Bin,
|
||||
|
||||
vfunc_allocate: function(box, flags) {
|
||||
let themeNode = this.get_theme_node();
|
||||
let maxWidth = themeNode.get_max_width();
|
||||
let availWidth = box.x2 - box.x1;
|
||||
let adjustedBox = box;
|
||||
|
||||
if (availWidth > maxWidth) {
|
||||
let excessWidth = availWidth - maxWidth;
|
||||
adjustedBox.x1 += Math.floor(excessWidth / 2);
|
||||
adjustedBox.x2 -= Math.floor(excessWidth / 2);
|
||||
}
|
||||
|
||||
this.parent(adjustedBox, flags);
|
||||
}
|
||||
});
|
||||
|
||||
const SearchResult = new Lang.Class({
|
||||
Name: 'SearchResult',
|
||||
|
||||
@ -178,7 +198,7 @@ const ListSearchResults = new Lang.Class({
|
||||
|
||||
this._content = new St.BoxLayout({ style_class: 'list-search-results',
|
||||
vertical: true });
|
||||
this.actor.add_actor(this._content);
|
||||
this.actor.add(this._content, { expand: true });
|
||||
|
||||
this._notDisplayedResult = [];
|
||||
this._terms = [];
|
||||
@ -301,12 +321,19 @@ const SearchResults = new Lang.Class({
|
||||
|
||||
this._content = new St.BoxLayout({ name: 'searchResultsContent',
|
||||
vertical: true });
|
||||
this._contentBin = new MaxWidthBin({ name: 'searchResultsBin',
|
||||
x_fill: true,
|
||||
y_fill: true,
|
||||
child: this._content });
|
||||
|
||||
let scrollChild = new St.BoxLayout();
|
||||
scrollChild.add(this._contentBin, { expand: true });
|
||||
|
||||
this._scrollView = new St.ScrollView({ x_fill: true,
|
||||
y_fill: false,
|
||||
style_class: 'vfade' });
|
||||
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||
this._scrollView.add_actor(this._content);
|
||||
this._scrollView.add_actor(scrollChild);
|
||||
let action = new Clutter.PanAction({ interpolate: true });
|
||||
action.connect('pan', Lang.bind(this, this._onPan));
|
||||
this._scrollView.add_action(action);
|
||||
|
@ -30,5 +30,6 @@ const HorizontalSeparator = new Lang.Class({
|
||||
cr.setSource(pattern);
|
||||
cr.rectangle(margin, gradientOffset, gradientWidth, gradientHeight);
|
||||
cr.fill();
|
||||
cr.$dispose();
|
||||
}
|
||||
});
|
||||
|
@ -15,6 +15,7 @@ const DEFAULT_MODE = 'restrictive';
|
||||
const _modes = {
|
||||
'restrictive': {
|
||||
parentMode: null,
|
||||
stylesheetName: 'gnome-shell.css',
|
||||
hasOverview: false,
|
||||
showCalendarEvents: false,
|
||||
allowSettings: false,
|
||||
|
@ -9,8 +9,8 @@ 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;
|
||||
const Screenshot = imports.ui.screenshot;
|
||||
|
||||
const GnomeShellIface = <interface name="org.gnome.Shell">
|
||||
<method name="Eval">
|
||||
@ -18,34 +18,6 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
|
||||
<arg type="b" direction="out" name="success" />
|
||||
<arg type="s" direction="out" name="result" />
|
||||
</method>
|
||||
<method name="ScreenshotArea">
|
||||
<arg type="i" direction="in" name="x"/>
|
||||
<arg type="i" direction="in" name="y"/>
|
||||
<arg type="i" direction="in" name="width"/>
|
||||
<arg type="i" direction="in" name="height"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
</method>
|
||||
<method name="ScreenshotWindow">
|
||||
<arg type="b" direction="in" name="include_frame"/>
|
||||
<arg type="b" direction="in" name="include_cursor"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
</method>
|
||||
<method name="Screenshot">
|
||||
<arg type="b" direction="in" name="include_cursor"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
</method>
|
||||
<method name="FlashArea">
|
||||
<arg type="i" direction="in" name="x"/>
|
||||
<arg type="i" direction="in" name="y"/>
|
||||
<arg type="i" direction="in" name="width"/>
|
||||
<arg type="i" direction="in" name="height"/>
|
||||
</method>
|
||||
<property name="Mode" type="s" access="read" />
|
||||
<property name="OverviewActive" type="b" access="readwrite" />
|
||||
<property name="ShellVersion" type="s" access="read" />
|
||||
@ -76,6 +48,7 @@ const GnomeShell = new Lang.Class({
|
||||
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
|
||||
|
||||
this._extensionsSerivce = new GnomeShellExtensions();
|
||||
this._screenshotService = new Screenshot.ScreenshotService();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -111,82 +84,6 @@ const GnomeShell = new Lang.Class({
|
||||
return [success, returnValue];
|
||||
},
|
||||
|
||||
_onScreenshotComplete: function(obj, result, area, flash, invocation) {
|
||||
if (flash && result) {
|
||||
let flashspot = new Flashspot.Flashspot(area);
|
||||
flashspot.fire();
|
||||
}
|
||||
|
||||
let retval = GLib.Variant.new('(b)', [result]);
|
||||
invocation.return_value(retval);
|
||||
},
|
||||
|
||||
/**
|
||||
* ScreenshotArea:
|
||||
* @x: The X coordinate of the area
|
||||
* @y: The Y coordinate of the area
|
||||
* @width: The width of the area
|
||||
* @height: The height of the area
|
||||
* @flash: Whether to flash the area or not
|
||||
* @filename: The filename for the screenshot
|
||||
*
|
||||
* Takes a screenshot of the passed in area and saves it
|
||||
* in @filename as png image, it returns a boolean
|
||||
* indicating whether the operation was successful or not.
|
||||
*
|
||||
*/
|
||||
ScreenshotAreaAsync : function (params, invocation) {
|
||||
let [x, y, width, height, flash, filename, callback] = params;
|
||||
let screenshot = new Shell.Screenshot();
|
||||
screenshot.screenshot_area (x, y, width, height, filename,
|
||||
Lang.bind(this, this._onScreenshotComplete,
|
||||
flash, invocation));
|
||||
},
|
||||
|
||||
/**
|
||||
* ScreenshotWindow:
|
||||
* @include_frame: Whether to include the frame or not
|
||||
* @include_cursor: Whether to include the cursor image or not
|
||||
* @flash: Whether to flash the window area or not
|
||||
* @filename: The filename for the screenshot
|
||||
*
|
||||
* Takes a screenshot of the focused window (optionally omitting the frame)
|
||||
* and saves it in @filename as png image, it returns a boolean
|
||||
* indicating whether the operation was successful or not.
|
||||
*
|
||||
*/
|
||||
ScreenshotWindowAsync : function (params, invocation) {
|
||||
let [include_frame, include_cursor, flash, filename] = params;
|
||||
let screenshot = new Shell.Screenshot();
|
||||
screenshot.screenshot_window (include_frame, include_cursor, filename,
|
||||
Lang.bind(this, this._onScreenshotComplete,
|
||||
flash, invocation));
|
||||
},
|
||||
|
||||
/**
|
||||
* Screenshot:
|
||||
* @filename: The filename for the screenshot
|
||||
* @include_cursor: Whether to include the cursor image or not
|
||||
* @flash: Whether to flash the screen or not
|
||||
*
|
||||
* Takes a screenshot of the whole screen and saves it
|
||||
* in @filename as png image, it returns a boolean
|
||||
* indicating whether the operation was successful or not.
|
||||
*
|
||||
*/
|
||||
ScreenshotAsync : function (params, invocation) {
|
||||
let [include_cursor, flash, filename] = params;
|
||||
let screenshot = new Shell.Screenshot();
|
||||
screenshot.screenshot(include_cursor, filename,
|
||||
Lang.bind(this, this._onScreenshotComplete,
|
||||
flash, invocation));
|
||||
},
|
||||
|
||||
FlashArea: function(x, y, width, height) {
|
||||
let flashspot = new Flashspot.Flashspot({ x : x, y : y, width: width, height: height});
|
||||
flashspot.fire();
|
||||
},
|
||||
|
||||
Mode: global.session_mode,
|
||||
|
||||
get OverviewActive() {
|
||||
@ -350,8 +247,8 @@ const ScreenSaverDBus = new Lang.Class({
|
||||
this.parent();
|
||||
|
||||
this._screenShield = screenShield;
|
||||
screenShield.connect('lock-status-changed', Lang.bind(this, function(shield) {
|
||||
this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [shield.locked]));
|
||||
screenShield.connect('active-changed', Lang.bind(this, function(shield) {
|
||||
this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [shield.active]));
|
||||
}));
|
||||
|
||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenSaverIface, this);
|
||||
@ -372,13 +269,13 @@ const ScreenSaverDBus = new Lang.Class({
|
||||
|
||||
SetActive: function(active) {
|
||||
if (active)
|
||||
this._screenShield.lock(true);
|
||||
this._screenShield.activate(true);
|
||||
else
|
||||
this._screenShield.unlock();
|
||||
this._screenShield.unlock(false);
|
||||
},
|
||||
|
||||
GetActive: function() {
|
||||
return this._screenShield.locked;
|
||||
return this._screenShield.active;
|
||||
},
|
||||
|
||||
GetActiveTime: function() {
|
||||
|
@ -9,6 +9,7 @@ const St = imports.gi.St;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const NotificationDaemon = imports.ui.notificationDaemon;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
@ -56,7 +57,7 @@ const Indicator = new Lang.Class({
|
||||
|
||||
this._fullMenuItems = [new PopupMenu.PopupSeparatorMenuItem(),
|
||||
new PopupMenu.PopupMenuItem(_("Send Files to Device...")),
|
||||
new PopupMenu.PopupMenuItem(_("Set up a New Device...")),
|
||||
new PopupMenu.PopupMenuItem(_("Set Up a New Device...")),
|
||||
new PopupMenu.PopupSeparatorMenuItem()];
|
||||
this._hasDevices = false;
|
||||
|
||||
@ -286,6 +287,7 @@ const Indicator = new Lang.Class({
|
||||
_ensureSource: function() {
|
||||
if (!this._source) {
|
||||
this._source = new MessageTray.Source(_("Bluetooth"), 'bluetooth-active');
|
||||
this._source.policy = new NotificationDaemon.NotificationApplicationPolicy('gnome-bluetooth-panel');
|
||||
Main.messageTray.add(this._source);
|
||||
}
|
||||
},
|
||||
@ -446,10 +448,5 @@ const PinNotification = new Lang.Class({
|
||||
return this._entry.clutter_text.text.length == 6;
|
||||
else
|
||||
return true;
|
||||
},
|
||||
|
||||
grabFocus: function(lockTray) {
|
||||
this.parent(lockTray);
|
||||
global.stage.set_key_focus(this._entry);
|
||||
}
|
||||
});
|
||||
|
@ -115,9 +115,14 @@ const IBusManager = new Lang.Class({
|
||||
this._panelService.connect('update-property', Lang.bind(this, this._updateProperty));
|
||||
// If an engine is already active we need to get its properties
|
||||
this._ibus.get_global_engine_async(-1, null, Lang.bind(this, function(i, result) {
|
||||
let engine = this._ibus.get_global_engine_async_finish(result);
|
||||
let engine;
|
||||
try {
|
||||
engine = this._ibus.get_global_engine_async_finish(result);
|
||||
if (!engine)
|
||||
return;
|
||||
} catch(e) {
|
||||
return;
|
||||
}
|
||||
this._engineChanged(this._ibus, engine.get_name());
|
||||
}));
|
||||
this._updateReadiness();
|
||||
@ -332,14 +337,14 @@ const InputSourceIndicator = new Lang.Class({
|
||||
Main.wm.addKeybinding('switch-input-source',
|
||||
new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }),
|
||||
Meta.KeyBindingFlags.REVERSES,
|
||||
Main.KeybindingMode.ALL,
|
||||
Shell.KeyBindingMode.ALL,
|
||||
Lang.bind(this, this._switchInputSource));
|
||||
this._keybindingActionBackward =
|
||||
Main.wm.addKeybinding('switch-input-source-backward',
|
||||
new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }),
|
||||
Meta.KeyBindingFlags.REVERSES |
|
||||
Meta.KeyBindingFlags.REVERSED,
|
||||
Main.KeybindingMode.ALL,
|
||||
Shell.KeyBindingMode.ALL,
|
||||
Lang.bind(this, this._switchInputSource));
|
||||
this._settings = new Gio.Settings({ schema: DESKTOP_INPUT_SOURCES_SCHEMA });
|
||||
this._settings.connect('changed::' + KEY_CURRENT_INPUT_SOURCE, Lang.bind(this, this._currentInputSourceChanged));
|
||||
@ -365,7 +370,14 @@ const InputSourceIndicator = new Lang.Class({
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
this._sessionUpdated();
|
||||
|
||||
this.menu.addSettingsAction(_("Region and Language Settings"), 'gnome-region-panel.desktop');
|
||||
this.menu.addSettingsAction(_("Region & Language Settings"), 'gnome-region-panel.desktop');
|
||||
|
||||
this._sourcesPerWindow = false;
|
||||
this._focusWindowNotifyId = 0;
|
||||
this._overviewShowingId = 0;
|
||||
this._overviewHiddenId = 0;
|
||||
this._settings.connect('changed::per-window', Lang.bind(this, this._sourcesPerWindowChanged));
|
||||
this._sourcesPerWindowChanged();
|
||||
},
|
||||
|
||||
_sessionUpdated: function() {
|
||||
@ -431,6 +443,8 @@ const InputSourceIndicator = new Lang.Class({
|
||||
this._mruSources = currentSource.concat(this._mruSources);
|
||||
break;
|
||||
}
|
||||
|
||||
this._changePerWindowSource();
|
||||
},
|
||||
|
||||
_inputSourcesChanged: function() {
|
||||
@ -470,6 +484,8 @@ const InputSourceIndicator = new Lang.Class({
|
||||
let is = new InputSource(type, id, displayName, shortName, i);
|
||||
|
||||
is.connect('activate', Lang.bind(this, function() {
|
||||
if (this._currentSource.index == is.index)
|
||||
return;
|
||||
this._settings.set_value(KEY_CURRENT_INPUT_SOURCE,
|
||||
GLib.Variant.new_uint32(is.index));
|
||||
}));
|
||||
@ -702,6 +718,82 @@ const InputSourceIndicator = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_getNewInputSource: function(current) {
|
||||
for (let i in this._inputSources) {
|
||||
let is = this._inputSources[i];
|
||||
if (is.type == current.type &&
|
||||
is.id == current.id)
|
||||
return is;
|
||||
}
|
||||
return this._currentSource;
|
||||
},
|
||||
|
||||
_getCurrentWindow: function() {
|
||||
if (Main.overview.visible)
|
||||
return Main.overview;
|
||||
else
|
||||
return global.display.focus_window;
|
||||
},
|
||||
|
||||
_setPerWindowInputSource: function() {
|
||||
let window = this._getCurrentWindow();
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
if (!window._inputSources) {
|
||||
window._inputSources = this._inputSources;
|
||||
window._currentSource = this._currentSource;
|
||||
} else if (window._inputSources == this._inputSources) {
|
||||
window._currentSource.activate();
|
||||
} else {
|
||||
window._inputSources = this._inputSources;
|
||||
window._currentSource = this._getNewInputSource(window._currentSource);
|
||||
window._currentSource.activate();
|
||||
}
|
||||
},
|
||||
|
||||
_sourcesPerWindowChanged: function() {
|
||||
this._sourcesPerWindow = this._settings.get_boolean('per-window');
|
||||
|
||||
if (this._sourcesPerWindow && this._focusWindowNotifyId == 0) {
|
||||
this._focusWindowNotifyId = global.display.connect('notify::focus-window',
|
||||
Lang.bind(this, this._setPerWindowInputSource));
|
||||
this._overviewShowingId = Main.overview.connect('showing',
|
||||
Lang.bind(this, this._setPerWindowInputSource));
|
||||
this._overviewHiddenId = Main.overview.connect('hidden',
|
||||
Lang.bind(this, this._setPerWindowInputSource));
|
||||
} else if (!this._sourcesPerWindow && this._focusWindowNotifyId != 0) {
|
||||
global.display.disconnect(this._focusWindowNotifyId);
|
||||
this._focusWindowNotifyId = 0;
|
||||
Main.overview.disconnect(this._overviewShowingId);
|
||||
this._overviewShowingId = 0;
|
||||
Main.overview.disconnect(this._overviewHiddenId);
|
||||
this._overviewHiddenId = 0;
|
||||
|
||||
let windows = global.get_window_actors().map(function(w) {
|
||||
return w.meta_window;
|
||||
});
|
||||
for (let i = 0; i < windows.length; ++i) {
|
||||
delete windows[i]._inputSources;
|
||||
delete windows[i]._currentSource;
|
||||
}
|
||||
delete Main.overview._inputSources;
|
||||
delete Main.overview._currentSource;
|
||||
}
|
||||
},
|
||||
|
||||
_changePerWindowSource: function() {
|
||||
if (!this._sourcesPerWindow)
|
||||
return;
|
||||
|
||||
let window = this._getCurrentWindow();
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
window._inputSources = this._inputSources;
|
||||
window._currentSource = this._currentSource;
|
||||
},
|
||||
|
||||
_containerGetPreferredWidth: function(container, for_height, alloc) {
|
||||
// Here, and in _containerGetPreferredHeight, we need to query
|
||||
// for the height of all children, but we ignore the results
|
||||
|
@ -10,6 +10,31 @@ const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const VolumeMenu = imports.ui.status.volume;
|
||||
|
||||
const FakeStatusIcon = new Lang.Class({
|
||||
Name: 'FakeStatusIcon',
|
||||
|
||||
_init: function(button) {
|
||||
this.actor = new St.BoxLayout({ style_class: 'panel-status-button-box' });
|
||||
this._button = button;
|
||||
this._button.connect('icons-updated', Lang.bind(this, this._reconstructIcons));
|
||||
this._button.actor.bind_property('visible', this.actor, 'visible',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
this._reconstructIcons();
|
||||
},
|
||||
|
||||
_reconstructIcons: function() {
|
||||
this.actor.destroy_all_children();
|
||||
this._button.icons.forEach(Lang.bind(this, function(icon) {
|
||||
let newIcon = new St.Icon({ style_class: 'system-status-icon' });
|
||||
icon.bind_property('gicon', newIcon, 'gicon',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
icon.bind_property('visible', newIcon, 'visible',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
this.actor.add_actor(newIcon);
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
const Indicator = new Lang.Class({
|
||||
Name: 'LockScreenMenuIndicator',
|
||||
Extends: PanelMenu.SystemStatusButton,
|
||||
@ -18,41 +43,20 @@ const Indicator = new Lang.Class({
|
||||
this.parent(null, _("Volume, network, battery"));
|
||||
this._box.style_class = 'lock-screen-status-button-box';
|
||||
|
||||
this._volume = Main.panel.statusArea.volume;
|
||||
if (this._volume) {
|
||||
this._volumeIcon = this.addIcon(null);
|
||||
this._volume.mainIcon.bind_property('gicon', this._volumeIcon, 'gicon',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
this._volume.mainIcon.bind_property('visible', this._volumeIcon, 'visible',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
|
||||
this._volumeControl = VolumeMenu.getMixerControl();
|
||||
this._volumeMenu = new VolumeMenu.VolumeMenu(this._volumeControl);
|
||||
this.menu.addMenuItem(this._volumeMenu);
|
||||
|
||||
this._volume = new FakeStatusIcon(Main.panel.statusArea.volume);
|
||||
this._box.add_child(this._volume.actor);
|
||||
|
||||
// Network may not exist if the user doesn't have NetworkManager
|
||||
if (Main.panel.statusArea.network) {
|
||||
this._network = new FakeStatusIcon(Main.panel.statusArea.network);
|
||||
this._box.add_child(this._network.actor);
|
||||
}
|
||||
|
||||
this._network = Main.panel.statusArea.network;
|
||||
if (this._network) {
|
||||
this._networkIcon = this.addIcon(null);
|
||||
this._network.mainIcon.bind_property('gicon', this._networkIcon, 'gicon',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
this._network.mainIcon.bind_property('visible', this._networkIcon, 'visible',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
|
||||
this._networkSecondaryIcon = this.addIcon(null);
|
||||
this._network.secondaryIcon.bind_property('gicon', this._networkSecondaryIcon, 'gicon',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
this._network.secondaryIcon.bind_property('visible', this._networkSecondaryIcon, 'visible',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
}
|
||||
|
||||
this._battery = Main.panel.statusArea.battery;
|
||||
if (this._battery) {
|
||||
this._batteryIcon = this.addIcon(null);
|
||||
this._battery.mainIcon.bind_property('gicon', this._batteryIcon, 'gicon',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
this._battery.mainIcon.bind_property('visible', this._batteryIcon, 'visible',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
}
|
||||
this._battery = new FakeStatusIcon(Main.panel.statusArea.battery);
|
||||
this._box.add_child(this._battery.actor);
|
||||
}
|
||||
});
|
||||
|
@ -5,20 +5,29 @@ const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const NetworkManager = imports.gi.NetworkManager;
|
||||
const NMClient = imports.gi.NMClient;
|
||||
const NMGtk = imports.gi.NMGtk;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
// Some of the new code depends on as-yet-unreleased NM
|
||||
var NMGtk;
|
||||
try {
|
||||
NMGtk = imports.gi.NMGtk;
|
||||
} catch(e) {
|
||||
NMGtk = null;
|
||||
}
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const NotificationDaemon = imports.ui.notificationDaemon;
|
||||
const ModemManager = imports.misc.modemManager;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const NMConnectionCategory = {
|
||||
INVALID: 'invalid',
|
||||
WIRED: 'wired',
|
||||
VIRTUAL: 'virtual',
|
||||
WIRELESS: 'wireless',
|
||||
WWAN: 'wwan',
|
||||
VPN: 'vpn'
|
||||
@ -300,13 +309,10 @@ const NMDevice = new Lang.Class({
|
||||
Extends: NMConnectionBased,
|
||||
|
||||
_init: function(client, device, connections) {
|
||||
this.device = device;
|
||||
this.device._delegate = this;
|
||||
this._stateChangedId = this.device.connect('state-changed', Lang.bind(this, this._deviceStateChanged));
|
||||
|
||||
// protected
|
||||
this._client = client;
|
||||
this._setDevice(device);
|
||||
this.parent(connections);
|
||||
|
||||
this._activeConnection = null;
|
||||
this._activeConnectionItem = null;
|
||||
this._autoConnectionItem = null;
|
||||
@ -331,7 +337,26 @@ const NMDevice = new Lang.Class({
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
if (this.device)
|
||||
this._setDevice(null);
|
||||
|
||||
if (this._deferredWorkId) {
|
||||
// Just clear out, the actual removal is handled when the
|
||||
// actor is destroyed
|
||||
this._deferredWorkId = 0;
|
||||
}
|
||||
|
||||
this._clearSection();
|
||||
if (this.statusItem)
|
||||
this.statusItem.destroy();
|
||||
this.section.destroy();
|
||||
},
|
||||
|
||||
_setDevice: function(device) {
|
||||
if (device) {
|
||||
this.device = device;
|
||||
this.device._delegate = this;
|
||||
this._stateChangedId = this.device.connect('state-changed', Lang.bind(this, this._deviceStateChanged));
|
||||
} else if (this.device) {
|
||||
this.device._delegate = null;
|
||||
|
||||
if (this._stateChangedId) {
|
||||
@ -341,7 +366,6 @@ const NMDevice = new Lang.Class({
|
||||
this._stateChangedId = 0;
|
||||
}
|
||||
if (this._carrierChangedId) {
|
||||
// see above for why this is needed
|
||||
GObject.Object.prototype.disconnect.call(this.device, this._carrierChangedId);
|
||||
this._carrierChangedId = 0;
|
||||
}
|
||||
@ -350,10 +374,8 @@ const NMDevice = new Lang.Class({
|
||||
this._firmwareChangedId = 0;
|
||||
}
|
||||
|
||||
this._clearSection();
|
||||
if (this.statusItem)
|
||||
this.statusItem.destroy();
|
||||
this.section.destroy();
|
||||
this.device = null;
|
||||
}
|
||||
},
|
||||
|
||||
deactivate: function() {
|
||||
@ -370,7 +392,7 @@ const NMDevice = new Lang.Class({
|
||||
// Otherwise, if no connection is currently configured,
|
||||
// try automatic configuration (or summon the config dialog)
|
||||
if (this._connections.length == 1) {
|
||||
this._client.activate_connection(this._connections[0].connection, this.device, null, null);
|
||||
this._client.activate_connection(this._connections[0].connection, this.device || null, null, null);
|
||||
return true;
|
||||
} else if (this._connections.length == 0) {
|
||||
return this._activateAutomaticConnection();
|
||||
@ -390,7 +412,7 @@ const NMDevice = new Lang.Class({
|
||||
},
|
||||
|
||||
get connected() {
|
||||
return this.device.state == NetworkManager.DeviceState.ACTIVATED;
|
||||
return this.device && this.device.state == NetworkManager.DeviceState.ACTIVATED;
|
||||
},
|
||||
|
||||
clearActiveConnection: function(activeConnection) {
|
||||
@ -410,7 +432,6 @@ const NMDevice = new Lang.Class({
|
||||
|
||||
this._activeConnection = activeConnection;
|
||||
|
||||
this._clearSection();
|
||||
this._queueCreateSection();
|
||||
},
|
||||
|
||||
@ -424,6 +445,9 @@ const NMDevice = new Lang.Class({
|
||||
},
|
||||
|
||||
getStatusLabel: function() {
|
||||
if (!this.device)
|
||||
return null;
|
||||
|
||||
switch(this.device.state) {
|
||||
case NetworkManager.DeviceState.DISCONNECTED:
|
||||
case NetworkManager.DeviceState.ACTIVATED:
|
||||
@ -474,6 +498,7 @@ const NMDevice = new Lang.Class({
|
||||
},
|
||||
|
||||
syncDescription: function() {
|
||||
if (this.device && this.device._description)
|
||||
this.statusItem.label.text = this.device._description;
|
||||
},
|
||||
|
||||
@ -483,8 +508,10 @@ const NMDevice = new Lang.Class({
|
||||
},
|
||||
|
||||
_queueCreateSection: function() {
|
||||
if (this._deferredWorkId) {
|
||||
this._clearSection();
|
||||
Main.queueDeferredWork(this._deferredWorkId);
|
||||
}
|
||||
},
|
||||
|
||||
_clearSection: function() {
|
||||
@ -584,7 +611,6 @@ const NMDevice = new Lang.Class({
|
||||
|
||||
this._updateStatusItem();
|
||||
|
||||
this._clearSection();
|
||||
this._queueCreateSection();
|
||||
this.emit('state-changed');
|
||||
},
|
||||
@ -638,6 +664,7 @@ const NMDeviceWired = new Lang.Class({
|
||||
Extends: NMDeviceSimple,
|
||||
|
||||
_init: function(client, device, connections) {
|
||||
device._description = _("Wired");
|
||||
this._autoConnectionName = _("Auto Ethernet");
|
||||
this.category = NMConnectionCategory.WIRED;
|
||||
|
||||
@ -665,12 +692,24 @@ const NMDeviceModem = new Lang.Class({
|
||||
_init: function(client, device, connections) {
|
||||
let is_wwan = false;
|
||||
|
||||
device._description = _("Mobile broadband");
|
||||
this._enabled = true;
|
||||
this.mobileDevice = null;
|
||||
this._connectionType = 'ppp';
|
||||
|
||||
this._capabilities = device.current_capabilities;
|
||||
// Support new ModemManager1 devices
|
||||
if (device.udi.indexOf('/org/freedesktop/ModemManager1/Modem') == 0) {
|
||||
is_wwan = true;
|
||||
this.mobileDevice = new ModemManager.BroadbandModem(device.udi, device.current_capabilities);
|
||||
if (this._capabilities & NetworkManager.DeviceModemCapabilities.GSM_UMTS) {
|
||||
this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME;
|
||||
} else if (this._capabilities & NetworkManager.DeviceModemCapabilities.LTE) {
|
||||
this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME;
|
||||
} else if (this._capabilities & NetworkManager.DeviceModemCapabilities.CDMA_EVDO) {
|
||||
this._connectionType = NetworkManager.SETTING_CDMA_SETTING_NAME;
|
||||
}
|
||||
} else if (this._capabilities & NetworkManager.DeviceModemCapabilities.GSM_UMTS) {
|
||||
is_wwan = true;
|
||||
this.mobileDevice = new ModemManager.ModemGsm(device.udi);
|
||||
this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME;
|
||||
@ -680,7 +719,8 @@ const NMDeviceModem = new Lang.Class({
|
||||
this._connectionType = NetworkManager.SETTING_CDMA_SETTING_NAME;
|
||||
} else if (this._capabilities & NetworkManager.DeviceModemCapabilities.LTE) {
|
||||
is_wwan = true;
|
||||
// FIXME: support signal quality
|
||||
this.mobileDevice = new ModemManager.ModemGsm(device.udi);
|
||||
this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME;
|
||||
}
|
||||
|
||||
if (is_wwan) {
|
||||
@ -783,6 +823,7 @@ const NMDeviceBluetooth = new Lang.Class({
|
||||
Extends: NMDevice,
|
||||
|
||||
_init: function(client, device, connections) {
|
||||
device._description = _("Bluetooth");
|
||||
this._autoConnectionName = this._makeConnectionName(device);
|
||||
device.connect('notify::name', Lang.bind(this, this._updateAutoConnectionName));
|
||||
|
||||
@ -825,7 +866,6 @@ const NMDeviceBluetooth = new Lang.Class({
|
||||
_updateAutoConnectionName: function() {
|
||||
this._autoConnectionName = this._makeConnectionName(this.device);
|
||||
|
||||
this._clearSection();
|
||||
this._queueCreateSection();
|
||||
this._updateStatusItem();
|
||||
}
|
||||
@ -1087,10 +1127,8 @@ const NMDeviceWireless = new Lang.Class({
|
||||
this._networks.splice(res.network, 1);
|
||||
let newPos = Util.insertSorted(this._networks, network, Lang.bind(this, this._networkSortFunction));
|
||||
|
||||
if (newPos != res.network) {
|
||||
this._clearSection();
|
||||
if (newPos != res.network)
|
||||
this._queueCreateSection();
|
||||
}
|
||||
},
|
||||
|
||||
_accessPointAdded: function(device, accessPoint) {
|
||||
@ -1143,10 +1181,8 @@ const NMDeviceWireless = new Lang.Class({
|
||||
let newPos = Util.insertSorted(this._networks, apObj, this._networkSortFunction);
|
||||
|
||||
// Queue an update of the UI if we changed the order
|
||||
if (newPos != pos) {
|
||||
this._clearSection();
|
||||
if (newPos != pos)
|
||||
this._queueCreateSection();
|
||||
}
|
||||
},
|
||||
|
||||
_accessPointRemoved: function(device, accessPoint) {
|
||||
@ -1206,13 +1242,11 @@ const NMDeviceWireless = new Lang.Class({
|
||||
if (res.network < this._networks.length-1)
|
||||
okNext = this._networkSortFunction(this._networks[res.network + 1], apObj) <= 0;
|
||||
|
||||
if (!okPrev || !okNext) {
|
||||
this._clearSection();
|
||||
if (!okPrev || !okNext)
|
||||
this._queueCreateSection();
|
||||
} else if (apObj.item) {
|
||||
else if (apObj.item)
|
||||
apObj.item.updateBestAP(apObj.accessPoints[0]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_createAPItem: function(connection, accessPointObj, useConnectionName) {
|
||||
@ -1287,7 +1321,6 @@ const NMDeviceWireless = new Lang.Class({
|
||||
|
||||
if (forceupdate) {
|
||||
this._networks.sort(this._networkSortFunction);
|
||||
this._clearSection();
|
||||
this._queueCreateSection();
|
||||
}
|
||||
},
|
||||
@ -1320,7 +1353,6 @@ const NMDeviceWireless = new Lang.Class({
|
||||
|
||||
if (forceupdate) {
|
||||
this._networks.sort(this._networkSortFunction);
|
||||
this._clearSection();
|
||||
this._queueCreateSection();
|
||||
}
|
||||
},
|
||||
@ -1431,6 +1463,56 @@ const NMDeviceWireless = new Lang.Class({
|
||||
},
|
||||
});
|
||||
|
||||
const NMDeviceVirtual = new Lang.Class({
|
||||
Name: 'NMDeviceVirtual',
|
||||
Extends: NMDeviceSimple,
|
||||
|
||||
_init: function(client, iface, connections) {
|
||||
this.iface = iface;
|
||||
this.parent(client, null, connections);
|
||||
this.category = NMConnectionCategory.VIRTUAL;
|
||||
},
|
||||
|
||||
_shouldShowConnectionList: function() {
|
||||
return this.hasConnections();
|
||||
},
|
||||
|
||||
connectionValid: function(connection) {
|
||||
return connection.get_virtual_iface_name() == this.iface;
|
||||
},
|
||||
|
||||
addConnection: function(connection) {
|
||||
if (!this.device && !this.hasConnections())
|
||||
this.statusItem.label.text = NMGtk.utils_get_connection_device_name(connection);
|
||||
|
||||
this.parent(connection);
|
||||
},
|
||||
|
||||
adoptDevice: function(device) {
|
||||
if (device.get_iface() == this.iface) {
|
||||
this._setDevice(device);
|
||||
if (device._description)
|
||||
this.syncDescription();
|
||||
this._updateStatusItem();
|
||||
this.emit('state-changed');
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
},
|
||||
|
||||
removeDevice: function(device) {
|
||||
if (device == this.device) {
|
||||
this._setDevice(null);
|
||||
this._updateStatusItem();
|
||||
this.emit('state-changed');
|
||||
}
|
||||
},
|
||||
|
||||
hasConnections: function() {
|
||||
return this._connections.length != 0;
|
||||
}
|
||||
});
|
||||
|
||||
const NMVPNSection = new Lang.Class({
|
||||
Name: 'NMVPNSection',
|
||||
Extends: NMConnectionBased,
|
||||
@ -1611,6 +1693,7 @@ const NMApplet = new Lang.Class({
|
||||
|
||||
this._nmDevices = [];
|
||||
this._devices = { };
|
||||
this._virtualDevices = [ ];
|
||||
|
||||
this._devices.wired = {
|
||||
section: new PopupMenu.PopupMenuSection(),
|
||||
@ -1621,6 +1704,15 @@ const NMApplet = new Lang.Class({
|
||||
this.menu.addMenuItem(this._devices.wired.section);
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
|
||||
this._devices.virtual = {
|
||||
section: new PopupMenu.PopupMenuSection(),
|
||||
devices: [ ],
|
||||
};
|
||||
|
||||
this._devices.virtual.section.actor.hide();
|
||||
this.menu.addMenuItem(this._devices.virtual.section);
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
|
||||
this._devices.wireless = {
|
||||
section: new PopupMenu.PopupMenuSection(),
|
||||
devices: [ ],
|
||||
@ -1654,6 +1746,14 @@ const NMApplet = new Lang.Class({
|
||||
this._dtypes[NetworkManager.DeviceType.INFINIBAND] = NMDeviceSimple;
|
||||
// TODO: WiMax support
|
||||
|
||||
// Virtual device types
|
||||
this._vtypes = { };
|
||||
if (NMGtk) {
|
||||
this._vtypes[NetworkManager.SETTING_VLAN_SETTING_NAME] = NMDeviceVirtual;
|
||||
this._vtypes[NetworkManager.SETTING_BOND_SETTING_NAME] = NMDeviceVirtual;
|
||||
this._vtypes[NetworkManager.SETTING_BRIDGE_SETTING_NAME] = NMDeviceVirtual;
|
||||
}
|
||||
|
||||
// Connection types
|
||||
this._ctypes = { };
|
||||
this._ctypes[NetworkManager.SETTING_WIRELESS_SETTING_NAME] = NMConnectionCategory.WIRELESS;
|
||||
@ -1664,6 +1764,11 @@ const NMApplet = new Lang.Class({
|
||||
this._ctypes[NetworkManager.SETTING_CDMA_SETTING_NAME] = NMConnectionCategory.WWAN;
|
||||
this._ctypes[NetworkManager.SETTING_GSM_SETTING_NAME] = NMConnectionCategory.WWAN;
|
||||
this._ctypes[NetworkManager.SETTING_INFINIBAND_SETTING_NAME] = NMConnectionCategory.WIRED;
|
||||
if (NMGtk) {
|
||||
this._ctypes[NetworkManager.SETTING_VLAN_SETTING_NAME] = NMConnectionCategory.VIRTUAL;
|
||||
this._ctypes[NetworkManager.SETTING_BOND_SETTING_NAME] = NMConnectionCategory.VIRTUAL;
|
||||
this._ctypes[NetworkManager.SETTING_BRIDGE_SETTING_NAME] = NMConnectionCategory.VIRTUAL;
|
||||
}
|
||||
this._ctypes[NetworkManager.SETTING_VPN_SETTING_NAME] = NMConnectionCategory.VPN;
|
||||
|
||||
this._settings = NMClient.RemoteSettings.new(null);
|
||||
@ -1691,6 +1796,7 @@ const NMApplet = new Lang.Class({
|
||||
if (!this._source) {
|
||||
this._source = new MessageTray.Source(_("Network Manager"),
|
||||
'network-transmit-receive');
|
||||
this._source.policy = new NotificationDaemon.NotificationApplicationPolicy('gnome-network-panel');
|
||||
|
||||
this._source.connect('destroy', Lang.bind(this, function() {
|
||||
this._source = null;
|
||||
@ -1715,7 +1821,14 @@ const NMApplet = new Lang.Class({
|
||||
let devices = this._devices[category].devices;
|
||||
let item = this._devices[category].item;
|
||||
let section = this._devices[category].section;
|
||||
if (devices.length == 0)
|
||||
|
||||
let visible;
|
||||
if (category == NMConnectionCategory.VIRTUAL)
|
||||
visible = !section.isEmpty();
|
||||
else
|
||||
visible = devices.length > 0;
|
||||
|
||||
if (!visible)
|
||||
section.actor.hide();
|
||||
else {
|
||||
section.actor.show();
|
||||
@ -1776,9 +1889,50 @@ const NMApplet = new Lang.Class({
|
||||
MessageTray.Urgency.HIGH);
|
||||
},
|
||||
|
||||
_makeWrapperDevice: function(wrapperClass, device) {
|
||||
let wrapper = new wrapperClass(this._client, device, this._connections);
|
||||
_syncDeviceNames: function() {
|
||||
if (NMGtk) {
|
||||
let names = NMGtk.utils_disambiguate_device_names(this._nmDevices);
|
||||
for (let i = 0; i < this._nmDevices.length; i++) {
|
||||
let device = this._nmDevices[i];
|
||||
device._description = names[i];
|
||||
if (device._delegate)
|
||||
device._delegate.syncDescription();
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < this._nmDevices.length; i++) {
|
||||
let device = this._nmDevices[i];
|
||||
device._delegate.syncDescription();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_deviceAdded: function(client, device, skipSyncDeviceNames) {
|
||||
if (device._delegate) {
|
||||
// already seen, not adding again
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this._virtualDevices.length; i++) {
|
||||
if (this._virtualDevices[i].adoptDevice(device)) {
|
||||
this._nmDevices.push(device);
|
||||
if (!skipSyncDeviceNames)
|
||||
this._syncDeviceNames();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let wrapperClass = this._dtypes[device.get_device_type()];
|
||||
if (wrapperClass) {
|
||||
let wrapper = new wrapperClass(this._client, device, this._connections);
|
||||
this._addDeviceWrapper(wrapper);
|
||||
|
||||
this._nmDevices.push(device);
|
||||
if (!skipSyncDeviceNames)
|
||||
this._syncDeviceNames();
|
||||
}
|
||||
},
|
||||
|
||||
_addDeviceWrapper: function(wrapper) {
|
||||
wrapper._activationFailedId = wrapper.connect('activation-failed',
|
||||
Lang.bind(this, this._onActivationFailed));
|
||||
wrapper._deviceStateChangedId = wrapper.connect('state-changed', Lang.bind(this, function(dev) {
|
||||
@ -1790,60 +1944,42 @@ const NMApplet = new Lang.Class({
|
||||
wrapper.disconnect(wrapper._destroyId);
|
||||
});
|
||||
|
||||
return wrapper;
|
||||
},
|
||||
|
||||
_syncDeviceNames: function() {
|
||||
let names = NMGtk.utils_disambiguate_device_names(this._nmDevices);
|
||||
for (let i = 0; i < this._nmDevices.length; i++) {
|
||||
let device = this._nmDevices[i];
|
||||
if (device._description != names[i]) {
|
||||
device._description = names[i];
|
||||
device._delegate.syncDescription();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_deviceAdded: function(client, device, skipSyncDeviceNames) {
|
||||
if (device._delegate) {
|
||||
// already seen, not adding again
|
||||
return;
|
||||
}
|
||||
let wrapperClass = this._dtypes[device.get_device_type()];
|
||||
if (wrapperClass) {
|
||||
let wrapper = this._makeWrapperDevice(wrapperClass, device);
|
||||
let section = this._devices[wrapper.category].section;
|
||||
let devices = this._devices[wrapper.category].devices;
|
||||
|
||||
section.addMenuItem(wrapper.statusItem);
|
||||
section.addMenuItem(wrapper.section);
|
||||
|
||||
let devices = this._devices[wrapper.category].devices;
|
||||
devices.push(wrapper);
|
||||
|
||||
this._nmDevices.push(device);
|
||||
if (!skipSyncDeviceNames)
|
||||
this._syncDeviceNames();
|
||||
|
||||
this._syncSectionTitle(wrapper.category);
|
||||
}
|
||||
},
|
||||
|
||||
_deviceRemoved: function(client, device) {
|
||||
if (!device._delegate) {
|
||||
let pos = this._nmDevices.indexOf(device);
|
||||
if (pos != -1) {
|
||||
this._nmDevices.splice(pos, 1);
|
||||
this._syncDeviceNames();
|
||||
}
|
||||
|
||||
let wrapper = device._delegate;
|
||||
if (!wrapper) {
|
||||
log('Removing a network device that was not added');
|
||||
return;
|
||||
}
|
||||
|
||||
let wrapper = device._delegate;
|
||||
if (wrapper instanceof NMDeviceVirtual)
|
||||
wrapper.removeDevice(device);
|
||||
else
|
||||
this._removeDeviceWrapper(wrapper);
|
||||
},
|
||||
|
||||
_removeDeviceWrapper: function(wrapper) {
|
||||
wrapper.destroy();
|
||||
|
||||
let devices = this._devices[wrapper.category].devices;
|
||||
let pos = devices.indexOf(wrapper);
|
||||
devices.splice(pos, 1);
|
||||
|
||||
pos = this._nmDevices.indexOf(device);
|
||||
this._nmDevices.splice(pos, 1);
|
||||
this._syncDeviceNames();
|
||||
|
||||
this._syncSectionTitle(wrapper.category)
|
||||
},
|
||||
|
||||
@ -2038,6 +2174,13 @@ const NMApplet = new Lang.Class({
|
||||
devices[i].removeConnection(connection);
|
||||
}
|
||||
|
||||
if (section == NMConnectionCategory.VIRTUAL) {
|
||||
let iface = connection.get_virtual_iface_name();
|
||||
let wrapper = this._findVirtualDevice(iface);
|
||||
if (wrapper && !wrapper.hasConnections())
|
||||
this._removeDeviceWrapper(wrapper);
|
||||
}
|
||||
|
||||
connection.disconnect(connection._removedId);
|
||||
connection.disconnect(connection._updatedId);
|
||||
connection._removedId = connection._updatedId = 0;
|
||||
@ -2051,6 +2194,27 @@ const NMApplet = new Lang.Class({
|
||||
|
||||
let section = connection._section;
|
||||
|
||||
if (section == NMConnectionCategory.VIRTUAL) {
|
||||
let wrapperClass = this._vtypes[connection._type];
|
||||
if (!wrapperClass)
|
||||
return;
|
||||
|
||||
let iface = connection.get_virtual_iface_name();
|
||||
let wrapper = this._findVirtualDevice(iface);
|
||||
if (!wrapper) {
|
||||
wrapper = new wrapperClass(this._client, iface, this._connections);
|
||||
this._addDeviceWrapper(wrapper);
|
||||
this._virtualDevices.push(wrapper);
|
||||
|
||||
// We might already have a device for this connection
|
||||
for (let i = 0; i < this._nmDevices.length; i++) {
|
||||
let device = this._nmDevices[i];
|
||||
if (wrapper.adoptDevice(device))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (section == NMConnectionCategory.INVALID)
|
||||
return;
|
||||
if (section == NMConnectionCategory.VPN) {
|
||||
@ -2063,6 +2227,15 @@ const NMApplet = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_findVirtualDevice: function(iface) {
|
||||
for (let i = 0; i < this._virtualDevices.length; i++) {
|
||||
if (this._virtualDevices[i].iface == iface)
|
||||
return this._virtualDevices[i];
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
_hideDevices: function() {
|
||||
this._devicesHidden = true;
|
||||
|
||||
@ -2078,6 +2251,7 @@ const NMApplet = new Lang.Class({
|
||||
this._statusSection.actor.hide();
|
||||
|
||||
this._syncSectionTitle(NMConnectionCategory.WIRED);
|
||||
this._syncSectionTitle(NMConnectionCategory.VIRTUAL);
|
||||
this._syncSectionTitle(NMConnectionCategory.WIRELESS);
|
||||
this._syncSectionTitle(NMConnectionCategory.WWAN);
|
||||
},
|
||||
@ -2115,6 +2289,7 @@ const NMApplet = new Lang.Class({
|
||||
this.setIcon('network-wireless-acquiring-symbolic');
|
||||
break;
|
||||
case NMConnectionCategory.WIRED:
|
||||
case NMConnectionCategory.VIRTUAL:
|
||||
this.setIcon('network-wired-acquiring-symbolic');
|
||||
break;
|
||||
default:
|
||||
@ -2154,6 +2329,7 @@ const NMApplet = new Lang.Class({
|
||||
break;
|
||||
}
|
||||
case NMConnectionCategory.WIRED:
|
||||
case NMConnectionCategory.VIRTUAL:
|
||||
this.setIcon('network-wired-symbolic');
|
||||
break;
|
||||
case NMConnectionCategory.WWAN:
|
||||
|
@ -7,7 +7,7 @@ const St = imports.gi.St;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
const BUS_NAME = 'org.gnome.SettingsDaemon';
|
||||
const BUS_NAME = 'org.gnome.SettingsDaemon.Power';
|
||||
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
|
||||
|
||||
const UPDeviceType = {
|
||||
@ -54,14 +54,23 @@ const Indicator = new Lang.Class({
|
||||
_init: function() {
|
||||
this.parent('battery-missing-symbolic', _("Battery"));
|
||||
|
||||
this._proxy = new PowerManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH);
|
||||
this._proxy = new PowerManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
|
||||
Lang.bind(this, function(proxy, error) {
|
||||
if (error) {
|
||||
log(error.message);
|
||||
return;
|
||||
}
|
||||
this._proxy.connect('g-properties-changed',
|
||||
Lang.bind(this, this._devicesChanged));
|
||||
this._devicesChanged();
|
||||
}));
|
||||
|
||||
this._deviceItems = [ ];
|
||||
this._hasPrimary = false;
|
||||
this._primaryDeviceId = null;
|
||||
|
||||
this._batteryItem = new PopupMenu.PopupMenuItem('', { reactive: false });
|
||||
this._primaryPercentage = new St.Label();
|
||||
this._primaryPercentage = new St.Label({ style_class: 'popup-battery-percentage' });
|
||||
this._batteryItem.addActor(this._primaryPercentage, { align: St.Align.END });
|
||||
this.menu.addMenuItem(this._batteryItem);
|
||||
|
||||
@ -70,10 +79,6 @@ const Indicator = new Lang.Class({
|
||||
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop');
|
||||
|
||||
this._proxy.connect('g-properties-changed',
|
||||
Lang.bind(this, this._devicesChanged));
|
||||
this._devicesChanged();
|
||||
},
|
||||
|
||||
_readPrimaryDevice: function() {
|
||||
@ -183,7 +188,8 @@ const DeviceItem = new Lang.Class({
|
||||
this._box.add_actor(this._label);
|
||||
this.addActor(this._box);
|
||||
|
||||
let percentLabel = new St.Label({ text: C_("percent of battery remaining", "%d%%").format(Math.round(percentage)) });
|
||||
let percentLabel = new St.Label({ text: C_("percent of battery remaining", "%d%%").format(Math.round(percentage)),
|
||||
style_class: 'popup-battery-percentage' });
|
||||
this.addActor(percentLabel, { align: St.Align.END });
|
||||
//FIXME: ideally we would like to expose this._label and percentLabel
|
||||
this.actor.label_actor = percentLabel;
|
||||
@ -192,9 +198,9 @@ const DeviceItem = new Lang.Class({
|
||||
_deviceTypeToString: function(type) {
|
||||
switch (type) {
|
||||
case UPDeviceType.AC_POWER:
|
||||
return _("AC adapter");
|
||||
return _("AC Adapter");
|
||||
case UPDeviceType.BATTERY:
|
||||
return _("Laptop battery");
|
||||
return _("Laptop Battery");
|
||||
case UPDeviceType.UPS:
|
||||
return _("UPS");
|
||||
case UPDeviceType.MONITOR:
|
||||
@ -206,9 +212,9 @@ const DeviceItem = new Lang.Class({
|
||||
case UPDeviceType.PDA:
|
||||
return _("PDA");
|
||||
case UPDeviceType.PHONE:
|
||||
return _("Cell phone");
|
||||
return _("Cell Phone");
|
||||
case UPDeviceType.MEDIA_PLAYER:
|
||||
return _("Media player");
|
||||
return _("Media Player");
|
||||
case UPDeviceType.TABLET:
|
||||
return _("Tablet");
|
||||
case UPDeviceType.COMPUTER:
|
||||
|
@ -5,6 +5,7 @@ const Lang = imports.lang;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Gvc = imports.gi.Gvc;
|
||||
const St = imports.gi.St;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
@ -26,6 +27,210 @@ function getMixerControl() {
|
||||
return _mixerControl;
|
||||
}
|
||||
|
||||
const StreamSlider = new Lang.Class({
|
||||
Name: 'StreamSlider',
|
||||
|
||||
_init: function(control, title) {
|
||||
this._control = control;
|
||||
|
||||
this.item = new PopupMenu.PopupMenuSection();
|
||||
|
||||
this._title = new PopupMenu.PopupMenuItem(title, { reactive: false });
|
||||
this._slider = new PopupMenu.PopupSliderMenuItem(0);
|
||||
this._slider.connect('value-changed', Lang.bind(this, this._sliderChanged));
|
||||
this._slider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
|
||||
|
||||
this.item.addMenuItem(this._title);
|
||||
this.item.addMenuItem(this._slider);
|
||||
|
||||
this._stream = null;
|
||||
this._shouldShow = true;
|
||||
},
|
||||
|
||||
get stream() {
|
||||
return this._stream;
|
||||
},
|
||||
|
||||
set stream(stream) {
|
||||
if (this._stream) {
|
||||
this._disconnectStream(this._stream);
|
||||
}
|
||||
|
||||
this._stream = stream;
|
||||
|
||||
if (this._stream) {
|
||||
this._connectStream(this._stream);
|
||||
this._updateVolume();
|
||||
} else {
|
||||
this.emit('stream-updated');
|
||||
}
|
||||
|
||||
this._updateVisibility();
|
||||
},
|
||||
|
||||
_disconnectStream: function(stream) {
|
||||
stream.disconnect(this._mutedChangedId);
|
||||
this._mutedChangedId = 0;
|
||||
stream.disconnect(this._volumeChangedId);
|
||||
this._volumeChangedId = 0;
|
||||
},
|
||||
|
||||
_connectStream: function(stream) {
|
||||
this._mutedChangedId = stream.connect('notify::is-muted', Lang.bind(this, this._updateVolume));
|
||||
this._volumeChangedId = stream.connect('notify::volume', Lang.bind(this, this._updateVolume));
|
||||
},
|
||||
|
||||
_shouldBeVisible: function() {
|
||||
return this._stream != null;
|
||||
},
|
||||
|
||||
_updateVisibility: function() {
|
||||
let visible = this._shouldBeVisible();
|
||||
this._title.actor.visible = visible;
|
||||
this._slider.actor.visible = visible;
|
||||
},
|
||||
|
||||
scroll: function(event) {
|
||||
this._slider.scroll(event);
|
||||
},
|
||||
|
||||
setValue: function(value) {
|
||||
// piggy-back off of sliderChanged
|
||||
this._slider.setValue(value);
|
||||
},
|
||||
|
||||
_sliderChanged: function(slider, value, property) {
|
||||
if (!this._stream)
|
||||
return;
|
||||
|
||||
let volume = value * this._control.get_vol_max_norm();
|
||||
let prevMuted = this._stream.is_muted;
|
||||
if (volume < 1) {
|
||||
this._stream.volume = 0;
|
||||
if (!prevMuted)
|
||||
this._stream.change_is_muted(true);
|
||||
} else {
|
||||
this._stream.volume = volume;
|
||||
if (prevMuted)
|
||||
this._stream.change_is_muted(false);
|
||||
}
|
||||
this._stream.push_volume();
|
||||
},
|
||||
|
||||
_notifyVolumeChange: function() {
|
||||
global.cancel_theme_sound(VOLUME_NOTIFY_ID);
|
||||
global.play_theme_sound(VOLUME_NOTIFY_ID,
|
||||
'audio-volume-change',
|
||||
_("Volume changed"),
|
||||
Clutter.get_current_event ());
|
||||
},
|
||||
|
||||
_updateVolume: function() {
|
||||
let muted = this._stream.is_muted;
|
||||
this._slider.setValue(muted ? 0 : (this._stream.volume / this._control.get_vol_max_norm()));
|
||||
this.emit('stream-updated');
|
||||
},
|
||||
|
||||
getIcon: function() {
|
||||
if (!this._stream)
|
||||
return null;
|
||||
|
||||
let volume = this._stream.volume;
|
||||
if (this._stream.is_muted || volume <= 0) {
|
||||
return 'audio-volume-muted-symbolic';
|
||||
} else {
|
||||
let n = Math.floor(3 * volume / this._control.get_vol_max_norm()) + 1;
|
||||
if (n < 2)
|
||||
return 'audio-volume-low-symbolic';
|
||||
if (n >= 3)
|
||||
return 'audio-volume-high-symbolic';
|
||||
return 'audio-volume-medium-symbolic';
|
||||
}
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(StreamSlider.prototype);
|
||||
|
||||
const OutputStreamSlider = new Lang.Class({
|
||||
Name: 'OutputStreamSlider',
|
||||
Extends: StreamSlider,
|
||||
|
||||
_connectStream: function(stream) {
|
||||
this.parent(stream);
|
||||
this._portChangedId = stream.connect('notify::port', Lang.bind(this, this._portChanged));
|
||||
this._portChanged();
|
||||
},
|
||||
|
||||
_findHeadphones: function(sink) {
|
||||
// This only works for external headphones (e.g. bluetooth)
|
||||
if (sink.get_form_factor() == 'headset' ||
|
||||
sink.get_form_factor() == 'headphone')
|
||||
return true;
|
||||
|
||||
// a bit hackish, but ALSA/PulseAudio have a number
|
||||
// of different identifiers for headphones, and I could
|
||||
// not find the complete list
|
||||
if (sink.get_ports().length > 0)
|
||||
return sink.get_port().port.indexOf('headphone') >= 0;
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_disconnectStream: function(stream) {
|
||||
this.parent(stream);
|
||||
stream.disconnect(this._portChangedId);
|
||||
this._portChangedId = 0;
|
||||
},
|
||||
|
||||
_portChanged: function() {
|
||||
let hasHeadphones = this._findHeadphones(this._stream);
|
||||
if (hasHeadphones != this._hasHeadphones) {
|
||||
this._hasHeadphones = hasHeadphones;
|
||||
this.emit('headphones-changed', this._hasHeadphones);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const InputStreamSlider = new Lang.Class({
|
||||
Name: 'InputStreamSlider',
|
||||
Extends: StreamSlider,
|
||||
|
||||
_init: function(control, title) {
|
||||
this.parent(control, title);
|
||||
this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput));
|
||||
this._control.connect('stream-removed', Lang.bind(this, this._maybeShowInput));
|
||||
},
|
||||
|
||||
_connectStream: function(stream) {
|
||||
this.parent(stream);
|
||||
this._maybeShowInput();
|
||||
},
|
||||
|
||||
_maybeShowInput: function() {
|
||||
// only show input widgets if any application is recording audio
|
||||
let showInput = false;
|
||||
let recordingApps = this._control.get_source_outputs();
|
||||
if (this._stream && recordingApps) {
|
||||
for (let i = 0; i < recordingApps.length; i++) {
|
||||
let outputStream = recordingApps[i];
|
||||
let id = outputStream.get_application_id();
|
||||
// but skip gnome-volume-control and pavucontrol
|
||||
// (that appear as recording because they show the input level)
|
||||
if (!id || (id != 'org.gnome.VolumeControl' && id != 'org.PulseAudio.pavucontrol')) {
|
||||
showInput = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._showInput = showInput;
|
||||
this._updateVisibility();
|
||||
},
|
||||
|
||||
_shouldBeVisible: function() {
|
||||
return this.parent() && this._showInput;
|
||||
}
|
||||
});
|
||||
|
||||
const VolumeMenu = new Lang.Class({
|
||||
Name: 'VolumeMenu',
|
||||
Extends: PopupMenu.PopupMenuSection,
|
||||
@ -39,207 +244,48 @@ const VolumeMenu = new Lang.Class({
|
||||
this._control.connect('state-changed', Lang.bind(this, this._onControlStateChanged));
|
||||
this._control.connect('default-sink-changed', Lang.bind(this, this._readOutput));
|
||||
this._control.connect('default-source-changed', Lang.bind(this, this._readInput));
|
||||
this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput));
|
||||
this._control.connect('stream-removed', Lang.bind(this, this._maybeShowInput));
|
||||
this._volumeMax = this._control.get_vol_max_norm();
|
||||
|
||||
this._output = null;
|
||||
this._outputVolumeId = 0;
|
||||
this._outputMutedId = 0;
|
||||
/* Translators: This is the label for audio volume */
|
||||
this._outputTitle = new PopupMenu.PopupMenuItem(_("Volume"), { reactive: false });
|
||||
this._outputSlider = new PopupMenu.PopupSliderMenuItem(0);
|
||||
this._outputSlider.connect('value-changed', Lang.bind(this, this._sliderChanged, '_output'));
|
||||
this._outputSlider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
|
||||
this.addMenuItem(this._outputTitle);
|
||||
this.addMenuItem(this._outputSlider);
|
||||
this._output = new OutputStreamSlider(this._control, _("Volume"));
|
||||
this._output.connect('stream-updated', Lang.bind(this, function() {
|
||||
this.emit('icon-changed');
|
||||
}));
|
||||
this._output.connect('headphones-changed', Lang.bind(this, function(stream, value) {
|
||||
this.emit('headphones-changed', value);
|
||||
}));
|
||||
this.addMenuItem(this._output.item);
|
||||
|
||||
this._input = new InputStreamSlider(this._control, _("Microphone"));
|
||||
this.addMenuItem(this._input.item);
|
||||
|
||||
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
|
||||
this._input = null;
|
||||
this._inputVolumeId = 0;
|
||||
this._inputMutedId = 0;
|
||||
this._inputTitle = new PopupMenu.PopupMenuItem(_("Microphone"), { reactive: false });
|
||||
this._inputSlider = new PopupMenu.PopupSliderMenuItem(0);
|
||||
this._inputSlider.connect('value-changed', Lang.bind(this, this._sliderChanged, '_input'));
|
||||
this._inputSlider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
|
||||
this.addMenuItem(this._inputTitle);
|
||||
this.addMenuItem(this._inputSlider);
|
||||
|
||||
this._onControlStateChanged();
|
||||
},
|
||||
|
||||
scroll: function(direction) {
|
||||
let currentVolume = this._output.volume;
|
||||
|
||||
if (direction == Clutter.ScrollDirection.DOWN) {
|
||||
let prev_muted = this._output.is_muted;
|
||||
this._output.volume = Math.max(0, currentVolume - this._volumeMax * VOLUME_ADJUSTMENT_STEP);
|
||||
if (this._output.volume < 1) {
|
||||
this._output.volume = 0;
|
||||
if (!prev_muted)
|
||||
this._output.change_is_muted(true);
|
||||
}
|
||||
this._output.push_volume();
|
||||
}
|
||||
else if (direction == Clutter.ScrollDirection.UP) {
|
||||
this._output.volume = Math.min(this._volumeMax, currentVolume + this._volumeMax * VOLUME_ADJUSTMENT_STEP);
|
||||
this._output.change_is_muted(false);
|
||||
this._output.push_volume();
|
||||
}
|
||||
|
||||
this._notifyVolumeChange();
|
||||
scroll: function(event) {
|
||||
this._output.scroll(event);
|
||||
},
|
||||
|
||||
_onControlStateChanged: function() {
|
||||
if (this._control.get_state() == Gvc.MixerControlState.READY) {
|
||||
this._readOutput();
|
||||
this._readInput();
|
||||
this._maybeShowInput();
|
||||
this._readOutput();
|
||||
} else {
|
||||
this.emit('icon-changed', null);
|
||||
this.emit('icon-changed');
|
||||
}
|
||||
},
|
||||
|
||||
_findHeadphones: function(sink) {
|
||||
// This only works for external headphones (e.g. bluetooth)
|
||||
if (sink.get_form_factor() == 'headset' ||
|
||||
sink.get_form_factor() == 'headphone')
|
||||
return true;
|
||||
|
||||
// a bit hackish, but ALSA/PulseAudio have a number
|
||||
// of different identifiers for headphones, and I could
|
||||
// not find the complete list
|
||||
let port = sink.get_port();
|
||||
if (port)
|
||||
return port.port.indexOf('headphone') >= 0;
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_portChanged: function() {
|
||||
this.hasHeadphones = this._findHeadphones(this._output);
|
||||
this.emit('headphones-changed');
|
||||
},
|
||||
|
||||
_readOutput: function() {
|
||||
if (this._outputVolumeId) {
|
||||
this._output.disconnect(this._outputVolumeId);
|
||||
this._output.disconnect(this._outputMutedId);
|
||||
this._output.disconnect(this._outputPortId);
|
||||
this._outputVolumeId = 0;
|
||||
this._outputMutedId = 0;
|
||||
this._outputPortId = 0;
|
||||
}
|
||||
this._output = this._control.get_default_sink();
|
||||
if (this._output) {
|
||||
this._outputMutedId = this._output.connect('notify::is-muted', Lang.bind(this, this._mutedChanged, '_output'));
|
||||
this._outputVolumeId = this._output.connect('notify::volume', Lang.bind(this, this._volumeChanged, '_output'));
|
||||
this._outputPortId = this._output.connect('notify::port', Lang.bind(this, this._portChanged));
|
||||
|
||||
this._mutedChanged(null, null, '_output');
|
||||
this._volumeChanged(null, null, '_output');
|
||||
this._portChanged();
|
||||
} else {
|
||||
this.hasHeadphones = false;
|
||||
this._outputSlider.setValue(0);
|
||||
this.emit('icon-changed', 'audio-volume-muted-symbolic');
|
||||
}
|
||||
this._output.stream = this._control.get_default_sink();
|
||||
},
|
||||
|
||||
_readInput: function() {
|
||||
if (this._inputVolumeId) {
|
||||
this._input.disconnect(this._inputVolumeId);
|
||||
this._input.disconnect(this._inputMutedId);
|
||||
this._inputVolumeId = 0;
|
||||
this._inputMutedId = 0;
|
||||
}
|
||||
this._input = this._control.get_default_source();
|
||||
if (this._input) {
|
||||
this._inputMutedId = this._input.connect('notify::is-muted', Lang.bind(this, this._mutedChanged, '_input'));
|
||||
this._inputVolumeId = this._input.connect('notify::volume', Lang.bind(this, this._volumeChanged, '_input'));
|
||||
this._mutedChanged (null, null, '_input');
|
||||
this._volumeChanged (null, null, '_input');
|
||||
} else {
|
||||
this._inputTitle.actor.hide();
|
||||
this._inputSlider.actor.hide();
|
||||
}
|
||||
this._input.stream = this._control.get_default_source();
|
||||
},
|
||||
|
||||
_maybeShowInput: function() {
|
||||
// only show input widgets if any application is recording audio
|
||||
let showInput = false;
|
||||
let recordingApps = this._control.get_source_outputs();
|
||||
if (this._input && recordingApps) {
|
||||
for (let i = 0; i < recordingApps.length; i++) {
|
||||
let outputStream = recordingApps[i];
|
||||
let id = outputStream.get_application_id();
|
||||
// but skip gnome-volume-control and pavucontrol
|
||||
// (that appear as recording because they show the input level)
|
||||
if (!id || (id != 'org.gnome.VolumeControl' && id != 'org.PulseAudio.pavucontrol')) {
|
||||
showInput = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._inputTitle.actor.visible = showInput;
|
||||
this._inputSlider.actor.visible = showInput;
|
||||
},
|
||||
|
||||
_volumeToIcon: function(volume) {
|
||||
if (volume <= 0) {
|
||||
return 'audio-volume-muted-symbolic';
|
||||
} else {
|
||||
let n = Math.floor(3 * volume / this._volumeMax) + 1;
|
||||
if (n < 2)
|
||||
return 'audio-volume-low-symbolic';
|
||||
if (n >= 3)
|
||||
return 'audio-volume-high-symbolic';
|
||||
return 'audio-volume-medium-symbolic';
|
||||
}
|
||||
},
|
||||
|
||||
_sliderChanged: function(slider, value, property) {
|
||||
if (this[property] == null) {
|
||||
log ('Volume slider changed for %s, but %s does not exist'.format(property, property));
|
||||
return;
|
||||
}
|
||||
let volume = value * this._volumeMax;
|
||||
let prev_muted = this[property].is_muted;
|
||||
if (volume < 1) {
|
||||
this[property].volume = 0;
|
||||
if (!prev_muted)
|
||||
this[property].change_is_muted(true);
|
||||
} else {
|
||||
this[property].volume = volume;
|
||||
if (prev_muted)
|
||||
this[property].change_is_muted(false);
|
||||
}
|
||||
this[property].push_volume();
|
||||
},
|
||||
|
||||
_notifyVolumeChange: function() {
|
||||
global.cancel_theme_sound(VOLUME_NOTIFY_ID);
|
||||
global.play_theme_sound(VOLUME_NOTIFY_ID, 'audio-volume-change');
|
||||
},
|
||||
|
||||
_mutedChanged: function(object, param_spec, property) {
|
||||
let muted = this[property].is_muted;
|
||||
let slider = this[property+'Slider'];
|
||||
slider.setValue(muted ? 0 : (this[property].volume / this._volumeMax));
|
||||
if (property == '_output') {
|
||||
if (muted)
|
||||
this.emit('icon-changed', 'audio-volume-muted-symbolic');
|
||||
else
|
||||
this.emit('icon-changed', this._volumeToIcon(this._output.volume));
|
||||
}
|
||||
},
|
||||
|
||||
_volumeChanged: function(object, param_spec, property) {
|
||||
this[property+'Slider'].setValue(this[property].volume / this._volumeMax);
|
||||
if (property == '_output' && !this._output.is_muted)
|
||||
this.emit('icon-changed', this._volumeToIcon(this._output.volume));
|
||||
getIcon: function() {
|
||||
return this._output.getIcon();
|
||||
}
|
||||
});
|
||||
|
||||
@ -252,13 +298,13 @@ const Indicator = new Lang.Class({
|
||||
|
||||
this._control = getMixerControl();
|
||||
this._volumeMenu = new VolumeMenu(this._control);
|
||||
this._volumeMenu.connect('icon-changed', Lang.bind(this, function(menu, icon) {
|
||||
this._hasPulseAudio = (icon != null);
|
||||
this._volumeMenu.connect('icon-changed', Lang.bind(this, function(menu) {
|
||||
let icon = this._volumeMenu.getIcon();
|
||||
this.actor.visible = (icon != null);
|
||||
this.setIcon(icon);
|
||||
this._syncVisibility();
|
||||
}));
|
||||
this._volumeMenu.connect('headphones-changed', Lang.bind(this, function() {
|
||||
this._syncVisibility();
|
||||
this._volumeMenu.connect('headphones-changed', Lang.bind(this, function(menu, value) {
|
||||
this._headphoneIcon.visible = value;
|
||||
}));
|
||||
|
||||
this._headphoneIcon = this.addIcon(new Gio.ThemedIcon({ name: 'headphones-symbolic' }));
|
||||
@ -272,13 +318,7 @@ const Indicator = new Lang.Class({
|
||||
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||
},
|
||||
|
||||
_syncVisibility: function() {
|
||||
this.actor.visible = this._hasPulseAudio;
|
||||
this.mainIcon.visible = this._hasPulseAudio;
|
||||
this._headphoneIcon.visible = this._hasPulseAudio && this._volumeMenu.hasHeadphones;
|
||||
},
|
||||
|
||||
_onScrollEvent: function(actor, event) {
|
||||
this._volumeMenu.scroll(event.get_scroll_direction());
|
||||
this._volumeMenu.scroll(event);
|
||||
}
|
||||
});
|
||||
|
@ -46,7 +46,8 @@ const SwitcherPopup = new Lang.Class({
|
||||
this._selectedIndex = 0;
|
||||
|
||||
this.actor = new Shell.GenericContainer({ style_class: 'switcher-popup',
|
||||
reactive: true });
|
||||
reactive: true,
|
||||
visible: false });
|
||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||
@ -637,5 +638,6 @@ function drawArrow(area, side) {
|
||||
|
||||
Clutter.cairo_set_source_color(cr, bodyColor);
|
||||
cr.fill();
|
||||
cr.$dispose();
|
||||
}
|
||||
|
||||
|
@ -115,7 +115,7 @@ const UnlockDialog = new Lang.Class({
|
||||
_init: function(parentActor) {
|
||||
this.parent({ shellReactive: true,
|
||||
styleClass: 'login-dialog',
|
||||
keybindingMode: Main.KeybindingMode.UNLOCK_SCREEN,
|
||||
keybindingMode: Shell.KeyBindingMode.UNLOCK_SCREEN,
|
||||
parentActor: parentActor
|
||||
});
|
||||
|
||||
@ -198,6 +198,8 @@ const UnlockDialog = new Lang.Class({
|
||||
x_align: St.Align.END,
|
||||
y_align: St.Align.MIDDLE });
|
||||
|
||||
let screenSaverSettings = new Gio.Settings({ schema: 'org.gnome.desktop.screensaver' });
|
||||
if (screenSaverSettings.get_boolean('user-switch-enabled')) {
|
||||
let otherUserLabel = new St.Label({ text: _("Log in as another user"),
|
||||
style_class: 'login-dialog-not-listed-label' });
|
||||
this._otherUserButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
|
||||
@ -210,6 +212,9 @@ const UnlockDialog = new Lang.Class({
|
||||
this.dialogLayout.add(this._otherUserButton,
|
||||
{ x_align: St.Align.START,
|
||||
x_fill: false });
|
||||
} else {
|
||||
this._otherUserButton = null;
|
||||
}
|
||||
|
||||
this._updateSensitivity(true);
|
||||
|
||||
@ -231,8 +236,10 @@ const UnlockDialog = new Lang.Class({
|
||||
this._promptEntry.reactive = sensitive;
|
||||
this._promptEntry.clutter_text.editable = sensitive;
|
||||
this._updateOkButtonSensitivity(sensitive && this._promptEntry.text.length > 0);
|
||||
if (this._otherUserButton) {
|
||||
this._otherUserButton.reactive = sensitive;
|
||||
this._otherUserButton.can_focus = sensitive;
|
||||
}
|
||||
},
|
||||
|
||||
_updateOkButtonSensitivity: function(sensitive) {
|
||||
|
@ -22,12 +22,12 @@ const Util = imports.misc.util;
|
||||
|
||||
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
|
||||
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
|
||||
const PRIVACY_SCHEMA = 'org.gnome.desktop.privacy'
|
||||
const DISABLE_USER_SWITCH_KEY = 'disable-user-switching';
|
||||
const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen';
|
||||
const DISABLE_LOG_OUT_KEY = 'disable-log-out';
|
||||
const LOCK_ENABLED_KEY = 'lock-enabled';
|
||||
const ALWAYS_SHOW_LOG_OUT_KEY = 'always-show-log-out';
|
||||
const SHOW_FULL_NAME_KEY = 'show-full-name';
|
||||
const SHOW_FULL_NAME_IN_TOP_BAR_KEY = 'show-full-name-in-top-bar';
|
||||
|
||||
const DIALOG_ICON_SIZE = 64;
|
||||
|
||||
@ -477,6 +477,7 @@ const UserMenuButton = new Lang.Class({
|
||||
|
||||
this._screenSaverSettings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
|
||||
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
|
||||
this._privacySettings = new Gio.Settings({ schema: PRIVACY_SCHEMA });
|
||||
|
||||
this._userManager = AccountsService.UserManager.get_default();
|
||||
|
||||
@ -553,9 +554,11 @@ const UserMenuButton = new Lang.Class({
|
||||
Lang.bind(this, this._updateLogout));
|
||||
this._lockdownSettings.connect('changed::' + DISABLE_LOCK_SCREEN_KEY,
|
||||
Lang.bind(this, this._updateLockScreen));
|
||||
this._screenSaverSettings.connect('changed::' + SHOW_FULL_NAME_KEY,
|
||||
global.settings.connect('changed::' + ALWAYS_SHOW_LOG_OUT_KEY,
|
||||
Lang.bind(this, this._updateLogout));
|
||||
this._screenSaverSettings.connect('changed::' + SHOW_FULL_NAME_IN_TOP_BAR_KEY,
|
||||
Lang.bind(this, this._updateUserName));
|
||||
global.settings.connect('changed::' + SHOW_FULL_NAME_KEY,
|
||||
this._privacySettings.connect('changed::' + SHOW_FULL_NAME_IN_TOP_BAR_KEY,
|
||||
Lang.bind(this, this._updateUserName));
|
||||
this._updateSwitchUser();
|
||||
this._updateLogout();
|
||||
@ -581,6 +584,7 @@ const UserMenuButton = new Lang.Class({
|
||||
Lang.bind(this, this._updateHaveShutdown));
|
||||
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
Main.screenShield.connect('locked-changed', Lang.bind(this, this._updatePresenceIcon));
|
||||
this._sessionUpdated();
|
||||
},
|
||||
|
||||
@ -602,10 +606,10 @@ const UserMenuButton = new Lang.Class({
|
||||
},
|
||||
|
||||
_updateUserName: function() {
|
||||
let settings = global.settings;
|
||||
let settings = this._privacySettings;
|
||||
if (Main.sessionMode.isLocked)
|
||||
settings = this._screenSaverSettings;
|
||||
if (this._user.is_loaded && settings.get_boolean(SHOW_FULL_NAME_KEY))
|
||||
if (this._user.is_loaded && settings.get_boolean(SHOW_FULL_NAME_IN_TOP_BAR_KEY))
|
||||
this._name.set_text(this._user.get_real_name());
|
||||
else
|
||||
this._name.set_text("");
|
||||
@ -700,6 +704,11 @@ const UserMenuButton = new Lang.Class({
|
||||
this._iconBox.child = this._idleIcon;
|
||||
else
|
||||
this._iconBox.child = this._offlineIcon;
|
||||
|
||||
if (Main.sessionMode.isLocked)
|
||||
this._iconBox.visible = Main.screenShield.locked;
|
||||
else
|
||||
this._iconBox.visible = true;
|
||||
},
|
||||
|
||||
_setupAccounts: function() {
|
||||
@ -820,7 +829,7 @@ const UserMenuButton = new Lang.Class({
|
||||
|
||||
_onMyAccountActivate: function() {
|
||||
Main.overview.hide();
|
||||
let app = Shell.AppSystem.get_default().lookup_setting('gnome-user-accounts-panel.desktop');
|
||||
let app = Shell.AppSystem.get_default().lookup_app('gnome-user-accounts-panel.desktop');
|
||||
app.activate();
|
||||
},
|
||||
|
||||
@ -862,18 +871,8 @@ const UserMenuButton = new Lang.Class({
|
||||
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
|
||||
this._session.ShutdownRemote();
|
||||
} else {
|
||||
if (this._screenSaverSettings.get_boolean(LOCK_ENABLED_KEY)) {
|
||||
let tmpId = Main.screenShield.connect('lock-screen-shown', Lang.bind(this, function() {
|
||||
Main.screenShield.disconnect(tmpId);
|
||||
|
||||
this._loginManager.suspend();
|
||||
}));
|
||||
|
||||
this.menu.close(BoxPointer.PopupAnimation.NONE);
|
||||
Main.screenShield.lock(true);
|
||||
} else {
|
||||
this._loginManager.suspend();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -12,6 +12,7 @@ const St = imports.gi.St;
|
||||
|
||||
const AppDisplay = imports.ui.appDisplay;
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const RemoteSearch = imports.ui.remoteSearch;
|
||||
const Search = imports.ui.search;
|
||||
const SearchDisplay = imports.ui.searchDisplay;
|
||||
@ -46,8 +47,7 @@ const ViewSelector = new Lang.Class({
|
||||
|
||||
this._activePage = null;
|
||||
|
||||
this.active = false;
|
||||
this._searchPending = false;
|
||||
this._searchActive = false;
|
||||
this._searchTimeoutId = 0;
|
||||
|
||||
this._searchSystem = new Search.SearchSystem();
|
||||
@ -77,16 +77,17 @@ const ViewSelector = new Lang.Class({
|
||||
this._capturedEventId = 0;
|
||||
|
||||
this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay();
|
||||
this._workspacesPage = this._addPage(this._workspacesDisplay.actor, null,
|
||||
this._workspacesPage = this._addPage(this._workspacesDisplay.actor,
|
||||
_("Windows"), 'emblem-documents-symbolic');
|
||||
|
||||
this._appDisplay = new AppDisplay.AllAppDisplay();
|
||||
this._appsPage = this._addPage(this._appDisplay.actor, null,
|
||||
this._appsPage = this._addPage(this._appDisplay.actor,
|
||||
_("Applications"), 'view-grid-symbolic');
|
||||
|
||||
this._searchResults = new SearchDisplay.SearchResults(this._searchSystem);
|
||||
this._searchPage = this._addPage(this._searchResults.actor, this._entry,
|
||||
_("Search"), 'edit-find-symbolic');
|
||||
this._searchPage = this._addPage(this._searchResults.actor,
|
||||
_("Search"), 'edit-find-symbolic',
|
||||
{ a11yFocus: this._entry });
|
||||
|
||||
this._searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
|
||||
this._searchSettings.connect('changed::disabled', Lang.bind(this, this._reloadRemoteProviders));
|
||||
@ -135,8 +136,8 @@ const ViewSelector = new Lang.Class({
|
||||
Main.wm.addKeybinding('toggle-application-view',
|
||||
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
|
||||
Meta.KeyBindingFlags.NONE,
|
||||
Main.KeybindingMode.NORMAL |
|
||||
Main.KeybindingMode.OVERVIEW,
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
Lang.bind(this, this._toggleAppsPage));
|
||||
},
|
||||
|
||||
@ -169,14 +170,16 @@ const ViewSelector = new Lang.Class({
|
||||
this._workspacesDisplay.hide();
|
||||
},
|
||||
|
||||
_addPage: function(actor, a11yFocus, name, a11yIcon) {
|
||||
_addPage: function(actor, name, a11yIcon, params) {
|
||||
params = Params.parse(params, { a11yFocus: null });
|
||||
|
||||
let page = new St.Bin({ child: actor,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
if (a11yFocus)
|
||||
Main.ctrlAltTabManager.addGroup(a11yFocus, name, a11yIcon);
|
||||
if (params.a11yFocus)
|
||||
Main.ctrlAltTabManager.addGroup(params.a11yFocus, name, a11yIcon);
|
||||
else
|
||||
Main.ctrlAltTabManager.addGroup(actor, name, a11yIcon,
|
||||
{ proxy: this.actor,
|
||||
@ -220,7 +223,7 @@ const ViewSelector = new Lang.Class({
|
||||
},
|
||||
|
||||
_onShowAppsButtonToggled: function() {
|
||||
if (this.active)
|
||||
if (this._searchActive)
|
||||
this.reset();
|
||||
else
|
||||
this._showPage(this._showAppsButton.checked ? this._appsPage
|
||||
@ -241,7 +244,7 @@ const ViewSelector = new Lang.Class({
|
||||
let symbol = event.get_key_symbol();
|
||||
|
||||
if (symbol == Clutter.Escape) {
|
||||
if (this.active)
|
||||
if (this._searchActive)
|
||||
this.reset();
|
||||
else if (this._showAppsButton.checked)
|
||||
this._resetShowAppsButton();
|
||||
@ -249,9 +252,9 @@ const ViewSelector = new Lang.Class({
|
||||
Main.overview.hide();
|
||||
return true;
|
||||
} else if (Clutter.keysym_to_unicode(symbol) ||
|
||||
(symbol == Clutter.BackSpace && this.active)) {
|
||||
(symbol == Clutter.BackSpace && this._searchActive)) {
|
||||
this.startSearch(event);
|
||||
} else if (!this.active) {
|
||||
} else if (!this._searchActive) {
|
||||
if (symbol == Clutter.Tab || symbol == Clutter.Down) {
|
||||
this._activePage.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||
return true;
|
||||
@ -325,39 +328,37 @@ const ViewSelector = new Lang.Class({
|
||||
},
|
||||
|
||||
_onTextChanged: function (se, prop) {
|
||||
let searchPreviouslyActive = this.active;
|
||||
this.active = this._entry.get_text() != '';
|
||||
this._searchPending = this.active && !searchPreviouslyActive;
|
||||
if (this._searchPending) {
|
||||
let searchPreviouslyActive = this._searchActive;
|
||||
this._searchActive = this._entry.get_text() != '';
|
||||
|
||||
let startSearch = this._searchActive && !searchPreviouslyActive;
|
||||
if (startSearch)
|
||||
this._searchResults.startingSearch();
|
||||
}
|
||||
if (this.active) {
|
||||
|
||||
if (this._searchActive) {
|
||||
this._entry.set_secondary_icon(this._activeIcon);
|
||||
|
||||
if (this._iconClickedId == 0) {
|
||||
if (this._iconClickedId == 0)
|
||||
this._iconClickedId = this._entry.connect('secondary-icon-clicked',
|
||||
Lang.bind(this, function() {
|
||||
this.reset();
|
||||
}));
|
||||
}
|
||||
Lang.bind(this, this.reset));
|
||||
|
||||
if (this._searchTimeoutId == 0)
|
||||
this._searchTimeoutId = Mainloop.timeout_add(150,
|
||||
Lang.bind(this, this._doSearch));
|
||||
} else {
|
||||
if (this._iconClickedId > 0)
|
||||
if (this._iconClickedId > 0) {
|
||||
this._entry.disconnect(this._iconClickedId);
|
||||
this._iconClickedId = 0;
|
||||
|
||||
this._entry.set_secondary_icon(this._inactiveIcon);
|
||||
this._searchCancelled();
|
||||
}
|
||||
if (!this.active) {
|
||||
|
||||
if (this._searchTimeoutId > 0) {
|
||||
Mainloop.source_remove(this._searchTimeoutId);
|
||||
this._searchTimeoutId = 0;
|
||||
}
|
||||
return;
|
||||
|
||||
this._entry.set_secondary_icon(this._inactiveIcon);
|
||||
this._searchCancelled();
|
||||
}
|
||||
if (this._searchTimeoutId > 0)
|
||||
return;
|
||||
this._searchTimeoutId = Mainloop.timeout_add(150, Lang.bind(this, this._doSearch));
|
||||
},
|
||||
|
||||
_onKeyPress: function(entry, event) {
|
||||
@ -367,16 +368,7 @@ const ViewSelector = new Lang.Class({
|
||||
this.reset();
|
||||
return true;
|
||||
}
|
||||
} else if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) {
|
||||
// We can't connect to 'activate' here because search providers
|
||||
// might want to do something with the modifiers in activateDefault.
|
||||
if (this._searchTimeoutId > 0) {
|
||||
Mainloop.source_remove(this._searchTimeoutId);
|
||||
this._doSearch();
|
||||
}
|
||||
this._searchResults.activateDefault();
|
||||
return true;
|
||||
} else if (this.active) {
|
||||
} else if (this._searchActive) {
|
||||
let arrowNext, nextDirection;
|
||||
if (entry.get_text_direction() == Clutter.TextDirection.RTL) {
|
||||
arrowNext = Clutter.Left;
|
||||
@ -400,6 +392,15 @@ const ViewSelector = new Lang.Class({
|
||||
} else if (symbol == arrowNext && this._text.position == -1) {
|
||||
this._searchResults.navigateFocus(nextDirection);
|
||||
return true;
|
||||
} else if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) {
|
||||
// We can't connect to 'activate' here because search providers
|
||||
// might want to do something with the modifiers in activateDefault.
|
||||
if (this._searchTimeoutId > 0) {
|
||||
Mainloop.source_remove(this._searchTimeoutId);
|
||||
this._doSearch();
|
||||
}
|
||||
this._searchResults.activateDefault();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -105,74 +105,74 @@ const WindowManager = new Lang.Class({
|
||||
|
||||
this._workspaceSwitcherPopup = null;
|
||||
this.setCustomKeybindingHandler('switch-to-workspace-left',
|
||||
Main.KeybindingMode.NORMAL |
|
||||
Main.KeybindingMode.OVERVIEW,
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||
this.setCustomKeybindingHandler('switch-to-workspace-right',
|
||||
Main.KeybindingMode.NORMAL |
|
||||
Main.KeybindingMode.OVERVIEW,
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||
this.setCustomKeybindingHandler('switch-to-workspace-up',
|
||||
Main.KeybindingMode.NORMAL |
|
||||
Main.KeybindingMode.OVERVIEW,
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||
this.setCustomKeybindingHandler('switch-to-workspace-down',
|
||||
Main.KeybindingMode.NORMAL |
|
||||
Main.KeybindingMode.OVERVIEW,
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||
this.setCustomKeybindingHandler('move-to-workspace-left',
|
||||
Main.KeybindingMode.NORMAL |
|
||||
Main.KeybindingMode.OVERVIEW,
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||
this.setCustomKeybindingHandler('move-to-workspace-right',
|
||||
Main.KeybindingMode.NORMAL |
|
||||
Main.KeybindingMode.OVERVIEW,
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||
this.setCustomKeybindingHandler('move-to-workspace-up',
|
||||
Main.KeybindingMode.NORMAL |
|
||||
Main.KeybindingMode.OVERVIEW,
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||
this.setCustomKeybindingHandler('move-to-workspace-down',
|
||||
Main.KeybindingMode.NORMAL |
|
||||
Main.KeybindingMode.OVERVIEW,
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||
this.setCustomKeybindingHandler('switch-applications',
|
||||
Main.KeybindingMode.NORMAL,
|
||||
Shell.KeyBindingMode.NORMAL,
|
||||
Lang.bind(this, this._startAppSwitcher));
|
||||
this.setCustomKeybindingHandler('switch-group',
|
||||
Main.KeybindingMode.NORMAL,
|
||||
Shell.KeyBindingMode.NORMAL,
|
||||
Lang.bind(this, this._startAppSwitcher));
|
||||
this.setCustomKeybindingHandler('switch-applications-backward',
|
||||
Main.KeybindingMode.NORMAL,
|
||||
Shell.KeyBindingMode.NORMAL,
|
||||
Lang.bind(this, this._startAppSwitcher));
|
||||
this.setCustomKeybindingHandler('switch-group-backward',
|
||||
Main.KeybindingMode.NORMAL,
|
||||
Shell.KeyBindingMode.NORMAL,
|
||||
Lang.bind(this, this._startAppSwitcher));
|
||||
this.setCustomKeybindingHandler('switch-windows',
|
||||
Main.KeybindingMode.NORMAL,
|
||||
Shell.KeyBindingMode.NORMAL,
|
||||
Lang.bind(this, this._startWindowSwitcher));
|
||||
this.setCustomKeybindingHandler('switch-windows-backward',
|
||||
Main.KeybindingMode.NORMAL,
|
||||
Shell.KeyBindingMode.NORMAL,
|
||||
Lang.bind(this, this._startWindowSwitcher));
|
||||
this.setCustomKeybindingHandler('switch-panels',
|
||||
Main.KeybindingMode.NORMAL |
|
||||
Main.KeybindingMode.OVERVIEW |
|
||||
Main.KeybindingMode.LOCK_SCREEN |
|
||||
Main.KeybindingMode.UNLOCK_SCREEN |
|
||||
Main.KeybindingMode.LOGIN_SCREEN,
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW |
|
||||
Shell.KeyBindingMode.LOCK_SCREEN |
|
||||
Shell.KeyBindingMode.UNLOCK_SCREEN |
|
||||
Shell.KeyBindingMode.LOGIN_SCREEN,
|
||||
Lang.bind(this, this._startA11ySwitcher));
|
||||
this.setCustomKeybindingHandler('switch-panels-backward',
|
||||
Main.KeybindingMode.NORMAL |
|
||||
Main.KeybindingMode.OVERVIEW |
|
||||
Main.KeybindingMode.LOCK_SCREEN |
|
||||
Main.KeybindingMode.UNLOCK_SCREEN |
|
||||
Main.KeybindingMode.LOGIN_SCREEN,
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW |
|
||||
Shell.KeyBindingMode.LOCK_SCREEN |
|
||||
Shell.KeyBindingMode.UNLOCK_SCREEN |
|
||||
Shell.KeyBindingMode.LOGIN_SCREEN,
|
||||
Lang.bind(this, this._startA11ySwitcher));
|
||||
|
||||
this.addKeybinding('open-application-menu',
|
||||
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
|
||||
Meta.KeyBindingFlags.NONE,
|
||||
Main.KeybindingMode.NORMAL,
|
||||
Shell.KeyBindingMode.NORMAL,
|
||||
Lang.bind(this, this._openAppMenu));
|
||||
|
||||
Main.overview.connect('showing', Lang.bind(this, function() {
|
||||
@ -199,7 +199,7 @@ const WindowManager = new Lang.Class({
|
||||
|
||||
removeKeybinding: function(name) {
|
||||
if (global.display.remove_keybinding(name))
|
||||
this.allowKeybinding(name, Main.KeybindingMode.NONE);
|
||||
this.allowKeybinding(name, Shell.KeyBindingMode.NONE);
|
||||
},
|
||||
|
||||
allowKeybinding: function(name, modes) {
|
||||
@ -242,22 +242,30 @@ const WindowManager = new Lang.Class({
|
||||
}
|
||||
|
||||
actor.set_scale(1.0, 1.0);
|
||||
actor.move_anchor_point_from_gravity(Clutter.Gravity.CENTER);
|
||||
|
||||
/* scale window down to 0x0.
|
||||
* maybe TODO: get icon geometry passed through and move the window towards it?
|
||||
*/
|
||||
this._minimizing.push(actor);
|
||||
|
||||
let xDest, yDest, xScale, yScale;
|
||||
let [success, geom] = actor.meta_window.get_icon_geometry();
|
||||
if (success) {
|
||||
xDest = geom.x;
|
||||
yDest = geom.y;
|
||||
xScale = geom.width / actor.width;
|
||||
yScale = geom.height / actor.height;
|
||||
} else {
|
||||
/* scale window down to 0x0. */
|
||||
let monitor = Main.layoutManager.findMonitorForWindow(actor.meta_window);
|
||||
let xDest = monitor.x;
|
||||
let yDest = monitor.y;
|
||||
xDest = monitor.x;
|
||||
yDest = monitor.y;
|
||||
xScale = 0.0;
|
||||
yScale = 0.0;
|
||||
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
|
||||
xDest += monitor.width;
|
||||
}
|
||||
|
||||
Tweener.addTween(actor,
|
||||
{ scale_x: 0.0,
|
||||
scale_y: 0.0,
|
||||
{ scale_x: xScale,
|
||||
scale_y: yScale,
|
||||
x: xDest,
|
||||
y: yDest,
|
||||
time: WINDOW_ANIMATION_TIME,
|
||||
@ -494,14 +502,14 @@ const WindowManager = new Lang.Class({
|
||||
},
|
||||
|
||||
_filterKeybinding: function(shellwm, binding) {
|
||||
if (Main.keybindingMode == Main.KeybindingMode.NONE)
|
||||
if (Main.keybindingMode == Shell.KeyBindingMode.NONE)
|
||||
return true;
|
||||
|
||||
// There's little sense in implementing a keybinding in mutter and
|
||||
// not having it work in NORMAL mode; handle this case generically
|
||||
// so we don't have to explicitly allow all builtin keybindings in
|
||||
// NORMAL mode.
|
||||
if (Main.keybindingMode == Main.KeybindingMode.NORMAL &&
|
||||
if (Main.keybindingMode == Shell.KeyBindingMode.NORMAL &&
|
||||
binding.is_builtin())
|
||||
return false;
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
@ -30,9 +29,6 @@ const CLOSE_BUTTON_FADE_TIME = 0.1;
|
||||
|
||||
const DRAGGING_WINDOW_OPACITY = 100;
|
||||
|
||||
const BUTTON_LAYOUT_SCHEMA = 'org.gnome.shell.overrides';
|
||||
const BUTTON_LAYOUT_KEY = 'button-layout';
|
||||
|
||||
// When calculating a layout, we calculate the scale of windows and the percent
|
||||
// of the available area the new layout uses. If the values for the new layout,
|
||||
// when weighted with the values as below, are worse than the previous layout's,
|
||||
@ -260,23 +256,37 @@ const WindowClone = new Lang.Class({
|
||||
|
||||
_onScroll : function (actor, event) {
|
||||
let direction = event.get_scroll_direction();
|
||||
if (direction == Clutter.ScrollDirection.UP) {
|
||||
let delta;
|
||||
|
||||
if (event.is_pointer_emulated())
|
||||
return;
|
||||
|
||||
if (direction == Clutter.ScrollDirection.DOWN) {
|
||||
delta = -SCROLL_SCALE_AMOUNT;
|
||||
} else if (direction == Clutter.ScrollDirection.UP) {
|
||||
delta = +SCROLL_SCALE_AMOUNT;
|
||||
} else if (direction == Clutter.ScrollDirection.SMOOTH) {
|
||||
let [dx, dy] = event.get_scroll_delta();
|
||||
delta = -dy * 10;
|
||||
}
|
||||
|
||||
if (delta > 0) {
|
||||
if (this._zoomStep == undefined)
|
||||
this._zoomStart();
|
||||
if (this._zoomStep < 100) {
|
||||
this._zoomStep += SCROLL_SCALE_AMOUNT;
|
||||
this._zoomStep += delta;
|
||||
this._zoomStep = Math.min(100, this._zoomStep);
|
||||
this._zoomUpdate();
|
||||
}
|
||||
} else if (direction == Clutter.ScrollDirection.DOWN) {
|
||||
} else if (delta < 0) {
|
||||
if (this._zoomStep > 0) {
|
||||
this._zoomStep -= SCROLL_SCALE_AMOUNT;
|
||||
this._zoomStep += delta;
|
||||
this._zoomStep = Math.max(0, this._zoomStep);
|
||||
this._zoomUpdate();
|
||||
}
|
||||
if (this._zoomStep <= 0.0)
|
||||
this._zoomEnd();
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
_zoomUpdate : function () {
|
||||
@ -441,8 +451,6 @@ const WindowOverlay = new Lang.Class({
|
||||
this._parentActor = parentActor;
|
||||
this._hidden = false;
|
||||
|
||||
this._settings = new Gio.Settings({ schema: BUTTON_LAYOUT_SCHEMA });
|
||||
|
||||
this.borderSize = 0;
|
||||
this.border = new St.Bin({ style_class: 'window-clone-border' });
|
||||
|
||||
@ -535,15 +543,8 @@ const WindowOverlay = new Lang.Class({
|
||||
let button = this.closeButton;
|
||||
let title = this.title;
|
||||
|
||||
let layout = this._settings.get_string(BUTTON_LAYOUT_KEY);
|
||||
let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL;
|
||||
|
||||
let split = layout.split(":");
|
||||
let side;
|
||||
if (split[0].indexOf("close") > -1)
|
||||
side = rtl ? St.Side.RIGHT : St.Side.LEFT;
|
||||
else
|
||||
side = rtl ? St.Side.LEFT : St.Side.RIGHT;
|
||||
let layout = Meta.prefs_get_button_layout();
|
||||
let side = layout.left_buttons.indexOf(Meta.ButtonFunction.CLOSE) > -1 ? St.Side.LEFT : St.Side.RIGHT;
|
||||
|
||||
let buttonX;
|
||||
let buttonY = cloneY - (button.height - button._overlap);
|
||||
@ -745,11 +746,10 @@ const LayoutStrategy = new Lang.Class({
|
||||
Name: 'LayoutStrategy',
|
||||
Abstract: true,
|
||||
|
||||
_init: function(monitor, rowSpacing, columnSpacing, bottomPadding) {
|
||||
_init: function(monitor, rowSpacing, columnSpacing) {
|
||||
this._monitor = monitor;
|
||||
this._rowSpacing = rowSpacing;
|
||||
this._columnSpacing = columnSpacing;
|
||||
this._bottomPadding = bottomPadding;
|
||||
},
|
||||
|
||||
_newRow: function() {
|
||||
@ -774,24 +774,23 @@ const LayoutStrategy = new Lang.Class({
|
||||
windows: [] };
|
||||
},
|
||||
|
||||
// Compute the size and fancy scale for @window using the
|
||||
// Computes and returns a fancy scale for @window using the
|
||||
// base scale, @scale.
|
||||
//
|
||||
// Returns a list structure: [ scaledWidth, scaledHeight, fancyScale ]
|
||||
// where scaledWidth and scaledHeight are the window's
|
||||
// width and height, scaled by fancyScale for convenience.
|
||||
_computeWindowSizeAndScale: function(window, scale) {
|
||||
let width = window.actor.width;
|
||||
let height = window.actor.height;
|
||||
let ratio;
|
||||
_computeWindowScale: function(window, scale) {
|
||||
// Since we align windows next to each other, the height of the
|
||||
// thumbnails is much more important to preserve than the width of
|
||||
// them, so two windows with equal height, but maybe differering
|
||||
// widths line up.
|
||||
let ratio = window.actor.height / this._monitor.height;
|
||||
|
||||
if (width > height)
|
||||
ratio = width / this._monitor.width;
|
||||
else
|
||||
ratio = height / this._monitor.height;
|
||||
// The purpose of this manipulation here is to prevent windows
|
||||
// from getting too small. For something like a calculator window,
|
||||
// we need to bump up the size just a bit to make sure it looks
|
||||
// good. We'll use a multiplier of 1.5 for this.
|
||||
|
||||
let fancyScale = (2 / (1 + ratio)) * scale;
|
||||
return [width * fancyScale, height * fancyScale, fancyScale];
|
||||
// Map from [0, 1] to [1.5, 1]
|
||||
let fancyScale = _interpolate(1.5, 1, ratio) * scale;
|
||||
return fancyScale;
|
||||
},
|
||||
|
||||
// Compute the size of each row, by assigning to the properties
|
||||
@ -829,7 +828,7 @@ const LayoutStrategy = new Lang.Class({
|
||||
let area = layout.area;
|
||||
|
||||
let hspacing = (layout.maxColumns - 1) * this._columnSpacing;
|
||||
let vspacing = (layout.numRows - 1) * this._rowSpacing + this._bottomPadding;
|
||||
let vspacing = (layout.numRows - 1) * this._rowSpacing;
|
||||
|
||||
let spacedWidth = area.width - hspacing;
|
||||
let spacedHeight = area.height - vspacing;
|
||||
@ -863,7 +862,7 @@ const LayoutStrategy = new Lang.Class({
|
||||
y += row.height + this._rowSpacing;
|
||||
}
|
||||
|
||||
let height = y - this._rowSpacing + this._bottomPadding;
|
||||
let height = y - this._rowSpacing;
|
||||
let baseY = (area.height - height) / 2;
|
||||
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
@ -873,7 +872,10 @@ const LayoutStrategy = new Lang.Class({
|
||||
for (let j = 0; j < row.windows.length; j++) {
|
||||
let window = row.windows[j];
|
||||
|
||||
let [width, height, s] = this._computeWindowSizeAndScale(window, scale);
|
||||
let s = this._computeWindowScale(window, scale);
|
||||
s = Math.min(s, WINDOW_CLONE_MAXIMUM_SCALE);
|
||||
let width = window.actor.width * s;
|
||||
let height = window.actor.height * s;
|
||||
let y = row.y + row.height - height;
|
||||
|
||||
let x = baseX;
|
||||
@ -934,7 +936,9 @@ const UnalignedLayoutStrategy = new Lang.Class({
|
||||
|
||||
for (; windowIdx < windows.length; windowIdx++) {
|
||||
let window = windows[windowIdx];
|
||||
let [width, height] = this._computeWindowSizeAndScale(window, 1);
|
||||
let s = this._computeWindowScale(window, 1);
|
||||
let width = window.actor.width * s;
|
||||
let height = window.actor.height * s;
|
||||
row.fullHeight = Math.max(row.fullHeight, height);
|
||||
|
||||
// either new width is < idealWidth or new width is nearer from idealWidth then oldWidth
|
||||
@ -1000,9 +1004,9 @@ const GridLayoutStrategy = new Lang.Class({
|
||||
let window = windows[windowIdx];
|
||||
row.windows.push(window);
|
||||
|
||||
let [width, height] = this._computeWindowSizeAndScale(window, 1);
|
||||
maxWindowWidth = Math.max(maxWindowWidth, width);
|
||||
maxWindowHeight = Math.max(maxWindowHeight, height);
|
||||
let s = this._computeWindowScale(window, 1);
|
||||
maxWindowWidth = Math.max(maxWindowWidth, window.actor.width * s);
|
||||
maxWindowHeight = Math.max(maxWindowHeight, window.actor.height * s);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1037,6 +1041,8 @@ const Workspace = new Lang.Class({
|
||||
this._windowOverlaysGroup.set_size(0, 0);
|
||||
|
||||
this.actor = new St.Widget({ style_class: 'window-picker' });
|
||||
if (monitorIndex != Main.layoutManager.primaryIndex)
|
||||
this.actor.add_style_class_name('external-monitor');
|
||||
this.actor.set_size(0, 0);
|
||||
|
||||
this._dropRect = new Clutter.Rectangle({ opacity: 0 });
|
||||
@ -1201,7 +1207,7 @@ const Workspace = new Lang.Class({
|
||||
|
||||
// Make the window slightly transparent to indicate it's hidden
|
||||
Tweener.addTween(clone.actor,
|
||||
{ opacity: 255,
|
||||
{ opacity: 128,
|
||||
time: Overview.ANIMATION_TIME,
|
||||
transition: 'easeInQuad'
|
||||
});
|
||||
@ -1623,7 +1629,7 @@ const Workspace = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_computeLayout: function(windows, area, rowSpacing, columnSpacing, bottomPadding) {
|
||||
_computeLayout: function(windows, area, rowSpacing, columnSpacing) {
|
||||
// We look for the largest scale that allows us to fit the
|
||||
// largest row/tallest column on the workspace.
|
||||
|
||||
@ -1639,7 +1645,7 @@ const Workspace = new Lang.Class({
|
||||
break;
|
||||
|
||||
let strategyClass = numRows > 2 ? GridLayoutStrategy : UnalignedLayoutStrategy;
|
||||
let strategy = new strategyClass(this._monitor, rowSpacing, columnSpacing, bottomPadding);
|
||||
let strategy = new strategyClass(this._monitor, rowSpacing, columnSpacing);
|
||||
|
||||
let layout = { area: area, strategy: strategy, numRows: numRows, numColumns: numColumns };
|
||||
strategy.computeLayout(windows, layout);
|
||||
@ -1671,6 +1677,12 @@ const Workspace = new Lang.Class({
|
||||
// Window grid spacing
|
||||
let columnSpacing = node.get_length('-horizontal-spacing');
|
||||
let rowSpacing = node.get_length('-vertical-spacing');
|
||||
let padding = {
|
||||
left: node.get_padding(St.Side.LEFT),
|
||||
top: node.get_padding(St.Side.TOP),
|
||||
bottom: node.get_padding(St.Side.BOTTOM),
|
||||
right: node.get_padding(St.Side.RIGHT),
|
||||
};
|
||||
|
||||
if (!totalWindows)
|
||||
return [];
|
||||
@ -1685,19 +1697,25 @@ const Workspace = new Lang.Class({
|
||||
[leftBorder, rightBorder] = overlay.chromeWidths();
|
||||
} else {
|
||||
[closeButtonHeight, captionHeight] = [0, 0];
|
||||
[leftBorder, rightBorder] = [0, 0];
|
||||
}
|
||||
|
||||
rowSpacing += captionHeight;
|
||||
columnSpacing += rightBorder;
|
||||
columnSpacing += (rightBorder + leftBorder) / 2;
|
||||
padding.top += closeButtonHeight;
|
||||
padding.bottom += captionHeight;
|
||||
padding.left += leftBorder;
|
||||
padding.right += rightBorder;
|
||||
|
||||
let area = { x: this._x, y: this._y, width: this._width, height: this._height };
|
||||
area.y += closeButtonHeight;
|
||||
area.height -= closeButtonHeight;
|
||||
area.x += leftBorder;
|
||||
area.width -= leftBorder;
|
||||
let area = {
|
||||
x: this._x + padding.left,
|
||||
y: this._y + padding.top,
|
||||
width: this._width - padding.left - padding.right,
|
||||
height: this._height - padding.top - padding.bottom,
|
||||
};
|
||||
|
||||
if (!this._currentLayout)
|
||||
this._currentLayout = this._computeLayout(windows, area, rowSpacing, columnSpacing, captionHeight);
|
||||
this._currentLayout = this._computeLayout(windows, area, rowSpacing, columnSpacing);
|
||||
|
||||
let layout = this._currentLayout;
|
||||
let strategy = layout.strategy;
|
||||
|
@ -21,6 +21,7 @@ eu
|
||||
fa
|
||||
fi
|
||||
fr
|
||||
fur
|
||||
ga
|
||||
gl
|
||||
gu
|
||||
|
@ -11,6 +11,7 @@ js/misc/util.js
|
||||
js/ui/appDisplay.js
|
||||
js/ui/appFavorites.js
|
||||
js/ui/calendar.js
|
||||
js/ui/components/automountManager.js
|
||||
js/ui/components/autorunManager.js
|
||||
js/ui/components/keyring.js
|
||||
js/ui/components/networkAgent.js
|
||||
@ -55,6 +56,5 @@ src/shell-app.c
|
||||
src/shell-app-system.c
|
||||
src/shell-global.c
|
||||
src/shell-keyring-prompt.c
|
||||
src/shell-mobile-providers.c
|
||||
src/shell-polkit-authentication-agent.c
|
||||
src/shell-util.c
|
||||
|
237
po/ar.po
237
po/ar.po
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: HEAD\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2012-12-09 04:45+0200\n"
|
||||
"POT-Creation-Date: 2012-12-23 18:34+0200\n"
|
||||
"PO-Revision-Date: 2012-12-09 04:52+0200\n"
|
||||
"Last-Translator: Khaled Hosny <khaledhosny@eglug.org>\n"
|
||||
"Language-Team: Arabic <doc@arabeyes.org>\n"
|
||||
@ -365,8 +365,8 @@ msgstr "نافذة الولوج"
|
||||
msgid "Power"
|
||||
msgstr "الطاقة"
|
||||
|
||||
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:674 ../js/ui/userMenu.js:678
|
||||
#: ../js/ui/userMenu.js:789
|
||||
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:675 ../js/ui/userMenu.js:679
|
||||
#: ../js/ui/userMenu.js:790
|
||||
msgid "Suspend"
|
||||
msgstr "علّق"
|
||||
|
||||
@ -374,8 +374,8 @@ msgstr "علّق"
|
||||
msgid "Restart"
|
||||
msgstr "أعِد التشغيل"
|
||||
|
||||
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:676
|
||||
#: ../js/ui/userMenu.js:678 ../js/ui/userMenu.js:788
|
||||
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:677
|
||||
#: ../js/ui/userMenu.js:679 ../js/ui/userMenu.js:789
|
||||
msgid "Power Off"
|
||||
msgstr "أطفئ الحاسوب"
|
||||
|
||||
@ -410,27 +410,19 @@ msgid "Execution of '%s' failed:"
|
||||
msgstr "فشل تنفيذ '%s':"
|
||||
|
||||
#. Translators: Filter to display all applications
|
||||
#: ../js/ui/appDisplay.js:259
|
||||
#: ../js/ui/appDisplay.js:258
|
||||
msgid "All"
|
||||
msgstr "الكل"
|
||||
|
||||
#: ../js/ui/appDisplay.js:318
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "التطبيقات"
|
||||
|
||||
#: ../js/ui/appDisplay.js:375
|
||||
msgid "SETTINGS"
|
||||
msgstr "الإعدادات"
|
||||
|
||||
#: ../js/ui/appDisplay.js:679
|
||||
#: ../js/ui/appDisplay.js:666
|
||||
msgid "New Window"
|
||||
msgstr "نافذة جديدة"
|
||||
|
||||
#: ../js/ui/appDisplay.js:682 ../js/ui/dash.js:289
|
||||
#: ../js/ui/appDisplay.js:669 ../js/ui/dash.js:289
|
||||
msgid "Remove from Favorites"
|
||||
msgstr "أزِل من المفضّلة"
|
||||
|
||||
#: ../js/ui/appDisplay.js:683
|
||||
#: ../js/ui/appDisplay.js:670
|
||||
msgid "Add to Favorites"
|
||||
msgstr "أضِف إلى المفضّلة"
|
||||
|
||||
@ -447,19 +439,19 @@ msgstr "أُزيل %s من مفضّلتك."
|
||||
#. Translators: Shown in calendar event list for all day events
|
||||
#. * Keep it short, best if you can use less then 10 characters
|
||||
#.
|
||||
#: ../js/ui/calendar.js:62
|
||||
#: ../js/ui/calendar.js:61
|
||||
msgctxt "event list time"
|
||||
msgid "All Day"
|
||||
msgstr "طوال اليوم"
|
||||
|
||||
#. Translators: Shown in calendar event list, if 24h format
|
||||
#: ../js/ui/calendar.js:67
|
||||
#: ../js/ui/calendar.js:66
|
||||
msgctxt "event list time"
|
||||
msgid "%H:%M"
|
||||
msgstr "%H:%M"
|
||||
|
||||
#. Transators: Shown in calendar event list, if 12h format
|
||||
#: ../js/ui/calendar.js:74
|
||||
#: ../js/ui/calendar.js:73
|
||||
msgctxt "event list time"
|
||||
msgid "%l:%M %p"
|
||||
msgstr "%l:%M %p"
|
||||
@ -469,43 +461,43 @@ msgstr "%l:%M %p"
|
||||
#. * NOTE: These grid abbreviations are always shown together
|
||||
#. * and in order, e.g. "S M T W T F S".
|
||||
#.
|
||||
#: ../js/ui/calendar.js:114
|
||||
#: ../js/ui/calendar.js:104
|
||||
msgctxt "grid sunday"
|
||||
msgid "S"
|
||||
msgstr "ح"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Monday
|
||||
#: ../js/ui/calendar.js:116
|
||||
#: ../js/ui/calendar.js:106
|
||||
msgctxt "grid monday"
|
||||
msgid "M"
|
||||
msgstr "ن"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Tuesday
|
||||
#: ../js/ui/calendar.js:118
|
||||
#: ../js/ui/calendar.js:108
|
||||
msgctxt "grid tuesday"
|
||||
msgid "T"
|
||||
msgstr "ث"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Wednesday
|
||||
#: ../js/ui/calendar.js:120
|
||||
#: ../js/ui/calendar.js:110
|
||||
msgctxt "grid wednesday"
|
||||
msgid "W"
|
||||
msgstr "ر"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Thursday
|
||||
#: ../js/ui/calendar.js:122
|
||||
#: ../js/ui/calendar.js:112
|
||||
msgctxt "grid thursday"
|
||||
msgid "T"
|
||||
msgstr "خ"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Friday
|
||||
#: ../js/ui/calendar.js:124
|
||||
#: ../js/ui/calendar.js:114
|
||||
msgctxt "grid friday"
|
||||
msgid "F"
|
||||
msgstr "ج"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Saturday
|
||||
#: ../js/ui/calendar.js:126
|
||||
#: ../js/ui/calendar.js:116
|
||||
msgctxt "grid saturday"
|
||||
msgid "S"
|
||||
msgstr "س"
|
||||
@ -516,77 +508,77 @@ msgstr "س"
|
||||
#. * so they need to be unique (e.g. Tuesday and Thursday cannot
|
||||
#. * both be 'T').
|
||||
#.
|
||||
#: ../js/ui/calendar.js:139
|
||||
#: ../js/ui/calendar.js:129
|
||||
msgctxt "list sunday"
|
||||
msgid "Su"
|
||||
msgstr "الأحد"
|
||||
|
||||
#. Translators: Event list abbreviation for Monday
|
||||
#: ../js/ui/calendar.js:141
|
||||
#: ../js/ui/calendar.js:131
|
||||
msgctxt "list monday"
|
||||
msgid "M"
|
||||
msgstr "الاثنين"
|
||||
|
||||
#. Translators: Event list abbreviation for Tuesday
|
||||
#: ../js/ui/calendar.js:143
|
||||
#: ../js/ui/calendar.js:133
|
||||
msgctxt "list tuesday"
|
||||
msgid "T"
|
||||
msgstr "الثلاثاء"
|
||||
|
||||
#. Translators: Event list abbreviation for Wednesday
|
||||
#: ../js/ui/calendar.js:145
|
||||
#: ../js/ui/calendar.js:135
|
||||
msgctxt "list wednesday"
|
||||
msgid "W"
|
||||
msgstr "الأربعاء"
|
||||
|
||||
#. Translators: Event list abbreviation for Thursday
|
||||
#: ../js/ui/calendar.js:147
|
||||
#: ../js/ui/calendar.js:137
|
||||
msgctxt "list thursday"
|
||||
msgid "Th"
|
||||
msgstr "الخميس"
|
||||
|
||||
#. Translators: Event list abbreviation for Friday
|
||||
#: ../js/ui/calendar.js:149
|
||||
#: ../js/ui/calendar.js:139
|
||||
msgctxt "list friday"
|
||||
msgid "F"
|
||||
msgstr "الجمعة"
|
||||
|
||||
#. Translators: Event list abbreviation for Saturday
|
||||
#: ../js/ui/calendar.js:151
|
||||
#: ../js/ui/calendar.js:141
|
||||
msgctxt "list saturday"
|
||||
msgid "S"
|
||||
msgstr "السبت"
|
||||
|
||||
#. Translators: Text to show if there are no events
|
||||
#: ../js/ui/calendar.js:700
|
||||
#: ../js/ui/calendar.js:674
|
||||
msgid "Nothing Scheduled"
|
||||
msgstr "الجدول خال"
|
||||
|
||||
#. Translators: Shown on calendar heading when selected day occurs on current year
|
||||
#: ../js/ui/calendar.js:716
|
||||
#: ../js/ui/calendar.js:690
|
||||
msgctxt "calendar heading"
|
||||
msgid "%A, %B %d"
|
||||
msgstr "%A %d %B"
|
||||
|
||||
#. Translators: Shown on calendar heading when selected day occurs on different year
|
||||
#: ../js/ui/calendar.js:719
|
||||
#: ../js/ui/calendar.js:693
|
||||
msgctxt "calendar heading"
|
||||
msgid "%A, %B %d, %Y"
|
||||
msgstr "%A %d %B %Y"
|
||||
|
||||
#: ../js/ui/calendar.js:729
|
||||
#: ../js/ui/calendar.js:703
|
||||
msgid "Today"
|
||||
msgstr "اليوم"
|
||||
|
||||
#: ../js/ui/calendar.js:733
|
||||
#: ../js/ui/calendar.js:707
|
||||
msgid "Tomorrow"
|
||||
msgstr "غدا"
|
||||
|
||||
#: ../js/ui/calendar.js:744
|
||||
#: ../js/ui/calendar.js:718
|
||||
msgid "This week"
|
||||
msgstr "هذا الأسبوع"
|
||||
|
||||
#: ../js/ui/calendar.js:752
|
||||
#: ../js/ui/calendar.js:726
|
||||
msgid "Next week"
|
||||
msgstr "الأسبوع القادم"
|
||||
|
||||
@ -948,7 +940,7 @@ msgstr "أظهر الحساب"
|
||||
msgid "Unknown reason"
|
||||
msgstr "السبب غير معروف"
|
||||
|
||||
#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:87
|
||||
#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:81
|
||||
msgid "Windows"
|
||||
msgstr "النوافذ"
|
||||
|
||||
@ -1088,7 +1080,7 @@ msgstr "هل تريد تنزيل وتثبيت '%s' من extensions.gnome.org؟"
|
||||
msgid "tray"
|
||||
msgstr "لوحة النظام"
|
||||
|
||||
#: ../js/ui/keyboard.js:555 ../js/ui/status/keyboard.js:195
|
||||
#: ../js/ui/keyboard.js:555 ../js/ui/status/keyboard.js:309
|
||||
#: ../js/ui/status/power.js:205
|
||||
msgid "Keyboard"
|
||||
msgstr "لوحة المفاتيح"
|
||||
@ -1115,7 +1107,9 @@ msgstr "اظهر الأخطاء"
|
||||
msgid "Enabled"
|
||||
msgstr "مفعّل"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:767
|
||||
#. translators:
|
||||
#. * The device has been disabled
|
||||
#: ../js/ui/lookingGlass.js:767 ../src/gvc/gvc-mixer-control.c:1830
|
||||
msgid "Disabled"
|
||||
msgstr "معطّل"
|
||||
|
||||
@ -1147,15 +1141,15 @@ msgstr "افتح"
|
||||
msgid "Remove"
|
||||
msgstr "أزِل"
|
||||
|
||||
#: ../js/ui/messageTray.js:1551
|
||||
#: ../js/ui/messageTray.js:1552
|
||||
msgid "No Messages"
|
||||
msgstr "لا رسائل"
|
||||
|
||||
#: ../js/ui/messageTray.js:1568
|
||||
#: ../js/ui/messageTray.js:1570
|
||||
msgid "Message Tray"
|
||||
msgstr "لوحة الرسائل"
|
||||
|
||||
#: ../js/ui/messageTray.js:2635
|
||||
#: ../js/ui/messageTray.js:2639
|
||||
msgid "System Information"
|
||||
msgstr "معلومات النظام"
|
||||
|
||||
@ -1164,11 +1158,11 @@ msgctxt "program"
|
||||
msgid "Unknown"
|
||||
msgstr "غير معروف"
|
||||
|
||||
#: ../js/ui/overview.js:95
|
||||
#: ../js/ui/overview.js:92
|
||||
msgid "Undo"
|
||||
msgstr "تراجع"
|
||||
|
||||
#: ../js/ui/overview.js:144
|
||||
#: ../js/ui/overview.js:139
|
||||
msgid "Overview"
|
||||
msgstr "نظرة عامة"
|
||||
|
||||
@ -1176,13 +1170,13 @@ msgstr "نظرة عامة"
|
||||
#. in the search entry when no search is
|
||||
#. active; it should not exceed ~30
|
||||
#. characters.
|
||||
#: ../js/ui/overview.js:221
|
||||
#: ../js/ui/overview.js:218
|
||||
msgid "Type to search..."
|
||||
msgstr "اكتب نصا للبحث عنه..."
|
||||
|
||||
#. Translators: this is the name of the dock/favorites area on
|
||||
#. the left of the overview
|
||||
#: ../js/ui/overview.js:242
|
||||
#: ../js/ui/overview.js:236
|
||||
msgid "Dash"
|
||||
msgstr "الشريط"
|
||||
|
||||
@ -1205,7 +1199,7 @@ msgstr "الشريط العلوي"
|
||||
#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
|
||||
#. switches containing "◯" and "|"). Other values will
|
||||
#. simply result in invisible toggle switches.
|
||||
#: ../js/ui/popupMenu.js:732
|
||||
#: ../js/ui/popupMenu.js:725
|
||||
msgid "toggle-switch-us"
|
||||
msgstr "toggle-switch-intl"
|
||||
|
||||
@ -1245,15 +1239,15 @@ msgstr[3] "%d تنبيهات جديدة"
|
||||
msgstr[4] "%d تنبيها جديدا"
|
||||
msgstr[5] "%d تنبيه جديد"
|
||||
|
||||
#: ../js/ui/screenShield.js:402 ../js/ui/userMenu.js:780
|
||||
#: ../js/ui/screenShield.js:402 ../js/ui/userMenu.js:781
|
||||
msgid "Lock"
|
||||
msgstr "أوصِد"
|
||||
|
||||
#: ../js/ui/searchDisplay.js:277
|
||||
#: ../js/ui/searchDisplay.js:403
|
||||
msgid "Searching..."
|
||||
msgstr "يبحث..."
|
||||
|
||||
#: ../js/ui/searchDisplay.js:325
|
||||
#: ../js/ui/searchDisplay.js:451
|
||||
msgid "No results."
|
||||
msgstr "لا نتائج."
|
||||
|
||||
@ -1369,7 +1363,7 @@ msgid "disconnecting..."
|
||||
msgstr "يقطع الاتّصال..."
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:220 ../js/ui/status/network.js:442
|
||||
#: ../js/ui/status/network.js:1453
|
||||
#: ../js/ui/status/network.js:1464
|
||||
msgid "connecting..."
|
||||
msgstr "يتّصل..."
|
||||
|
||||
@ -1385,7 +1379,7 @@ msgstr "إعدادات لوحة المفاتيح"
|
||||
msgid "Mouse Settings"
|
||||
msgstr "إعدادات الفأرة"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:253 ../js/ui/status/volume.js:270
|
||||
#: ../js/ui/status/bluetooth.js:253 ../js/ui/status/volume.js:314
|
||||
msgid "Sound Settings"
|
||||
msgstr "إعدادات الصوت"
|
||||
|
||||
@ -1449,15 +1443,15 @@ msgstr "من فضلك أدخل الرقم المذكور على الجهاز."
|
||||
msgid "OK"
|
||||
msgstr "حسنا"
|
||||
|
||||
#: ../js/ui/status/keyboard.js:228
|
||||
#: ../js/ui/status/keyboard.js:363
|
||||
msgid "Show Keyboard Layout"
|
||||
msgstr "أظهر تخطيط لوحة المفاتيح"
|
||||
|
||||
#: ../js/ui/status/keyboard.js:233
|
||||
#: ../js/ui/status/keyboard.js:368
|
||||
msgid "Region and Language Settings"
|
||||
msgstr "إعدادات الإقليم واللغة"
|
||||
|
||||
#: ../js/ui/status/lockScreenMenu.js:18
|
||||
#: ../js/ui/status/lockScreenMenu.js:43
|
||||
msgid "Volume, network, battery"
|
||||
msgstr "الصوت، الشبكة، البطارية"
|
||||
|
||||
@ -1477,7 +1471,7 @@ msgid "unmanaged"
|
||||
msgstr "غير مُدار"
|
||||
|
||||
#. Translators: this is for network connections that require some kind of key or password
|
||||
#: ../js/ui/status/network.js:445 ../js/ui/status/network.js:1456
|
||||
#: ../js/ui/status/network.js:445 ../js/ui/status/network.js:1467
|
||||
msgid "authentication required"
|
||||
msgstr "الاستيثاق مطلوب"
|
||||
|
||||
@ -1498,72 +1492,72 @@ msgstr "الكبل مفصول"
|
||||
msgid "unavailable"
|
||||
msgstr "غير متاح"
|
||||
|
||||
#: ../js/ui/status/network.js:469 ../js/ui/status/network.js:1458
|
||||
#: ../js/ui/status/network.js:469 ../js/ui/status/network.js:1469
|
||||
msgid "connection failed"
|
||||
msgstr "فشل الاتصال"
|
||||
|
||||
#: ../js/ui/status/network.js:525 ../js/ui/status/network.js:1392
|
||||
#: ../js/ui/status/network.js:1534
|
||||
#: ../js/ui/status/network.js:525 ../js/ui/status/network.js:1403
|
||||
#: ../js/ui/status/network.js:1545
|
||||
msgid "More..."
|
||||
msgstr "المزيد..."
|
||||
|
||||
#. TRANSLATORS: this is the indication that a connection for another logged in user is active,
|
||||
#. and we cannot access its settings (including the name)
|
||||
#: ../js/ui/status/network.js:561 ../js/ui/status/network.js:1322
|
||||
#: ../js/ui/status/network.js:561 ../js/ui/status/network.js:1333
|
||||
msgid "Connected (private)"
|
||||
msgstr "متّصل (شخصي)"
|
||||
|
||||
#: ../js/ui/status/network.js:619
|
||||
#: ../js/ui/status/network.js:641
|
||||
msgid "Auto Ethernet"
|
||||
msgstr "إيثرنت تلقائي"
|
||||
|
||||
#: ../js/ui/status/network.js:677
|
||||
#: ../js/ui/status/network.js:688
|
||||
msgid "Auto broadband"
|
||||
msgstr "شبكة هاتف محمول تلقائية"
|
||||
|
||||
#: ../js/ui/status/network.js:680
|
||||
#: ../js/ui/status/network.js:691
|
||||
msgid "Auto dial-up"
|
||||
msgstr "اتصال هاتفي تلقائي"
|
||||
|
||||
#. TRANSLATORS: this the automatic wireless connection name (including the network name)
|
||||
#: ../js/ui/status/network.js:809 ../js/ui/status/network.js:1339
|
||||
#: ../js/ui/status/network.js:820 ../js/ui/status/network.js:1350
|
||||
#, c-format
|
||||
msgid "Auto %s"
|
||||
msgstr "%s تلقائي"
|
||||
|
||||
#: ../js/ui/status/network.js:811
|
||||
#: ../js/ui/status/network.js:822
|
||||
msgid "Auto bluetooth"
|
||||
msgstr "بلوتوث تلقائي"
|
||||
|
||||
#: ../js/ui/status/network.js:1341
|
||||
#: ../js/ui/status/network.js:1352
|
||||
msgid "Auto wireless"
|
||||
msgstr "لاسلكي تلقائي"
|
||||
|
||||
#: ../js/ui/status/network.js:1584
|
||||
#: ../js/ui/status/network.js:1595
|
||||
msgid "Enable networking"
|
||||
msgstr "فعّل الشبكات"
|
||||
|
||||
#: ../js/ui/status/network.js:1616
|
||||
#: ../js/ui/status/network.js:1627
|
||||
msgid "Wi-Fi"
|
||||
msgstr "واي فاي"
|
||||
|
||||
#: ../js/ui/status/network.js:1635
|
||||
#: ../js/ui/status/network.js:1646
|
||||
msgid "Network Settings"
|
||||
msgstr "إعدادات الشّبكة"
|
||||
|
||||
#: ../js/ui/status/network.js:1679
|
||||
#: ../js/ui/status/network.js:1692
|
||||
msgid "Network Manager"
|
||||
msgstr "مدير الشبكة"
|
||||
|
||||
#: ../js/ui/status/network.js:1761
|
||||
#: ../js/ui/status/network.js:1774
|
||||
msgid "Connection failed"
|
||||
msgstr "فشل الاتصال"
|
||||
|
||||
#: ../js/ui/status/network.js:1762
|
||||
#: ../js/ui/status/network.js:1775
|
||||
msgid "Activation of network connection failed"
|
||||
msgstr "فشل تفعيل اتصال الشبكة"
|
||||
|
||||
#: ../js/ui/status/network.js:2079
|
||||
#: ../js/ui/status/network.js:2092
|
||||
msgid "Networking is disabled"
|
||||
msgstr "عُطّلت الشبكات"
|
||||
|
||||
@ -1681,11 +1675,11 @@ msgid "Unknown"
|
||||
msgstr "غير معروف"
|
||||
|
||||
#. Translators: This is the label for audio volume
|
||||
#: ../js/ui/status/volume.js:50 ../js/ui/status/volume.js:251
|
||||
#: ../js/ui/status/volume.js:247 ../js/ui/status/volume.js:295
|
||||
msgid "Volume"
|
||||
msgstr "شدة الصوت"
|
||||
|
||||
#: ../js/ui/status/volume.js:62
|
||||
#: ../js/ui/status/volume.js:256
|
||||
msgid "Microphone"
|
||||
msgstr "ميكروفون"
|
||||
|
||||
@ -1721,31 +1715,31 @@ msgstr "ساكن"
|
||||
msgid "Offline"
|
||||
msgstr "غير متصل"
|
||||
|
||||
#: ../js/ui/userMenu.js:754
|
||||
#: ../js/ui/userMenu.js:755
|
||||
msgid "Notifications"
|
||||
msgstr "التنبيهات"
|
||||
|
||||
#: ../js/ui/userMenu.js:762
|
||||
#: ../js/ui/userMenu.js:763
|
||||
msgid "Settings"
|
||||
msgstr "الإعدادات"
|
||||
|
||||
#: ../js/ui/userMenu.js:770
|
||||
#: ../js/ui/userMenu.js:771
|
||||
msgid "Switch User"
|
||||
msgstr "بدّل المستخدم"
|
||||
|
||||
#: ../js/ui/userMenu.js:775
|
||||
#: ../js/ui/userMenu.js:776
|
||||
msgid "Log Out"
|
||||
msgstr "اخرج"
|
||||
|
||||
#: ../js/ui/userMenu.js:795
|
||||
#: ../js/ui/userMenu.js:796
|
||||
msgid "Install Updates & Restart"
|
||||
msgstr "ثبّت التحديثات وأعد التشغيل"
|
||||
|
||||
#: ../js/ui/userMenu.js:813
|
||||
#: ../js/ui/userMenu.js:814
|
||||
msgid "Your chat status will be set to busy"
|
||||
msgstr "ستُجعل حالة اتصالك ”مشغول“"
|
||||
|
||||
#: ../js/ui/userMenu.js:814
|
||||
#: ../js/ui/userMenu.js:815
|
||||
msgid ""
|
||||
"Notifications are now disabled, including chat messages. Your online status "
|
||||
"has been adjusted to let others know that you might not see their messages."
|
||||
@ -1753,15 +1747,15 @@ msgstr ""
|
||||
"التنبيهات معطلة الآن، بما فيها رسائل المحادثة. حالة اتصالك تغيرت حتى يعلم "
|
||||
"الآخرون أنك قد لا ترى رسائلهم."
|
||||
|
||||
#: ../js/ui/viewSelector.js:91
|
||||
#: ../js/ui/viewSelector.js:85
|
||||
msgid "Applications"
|
||||
msgstr "التطبيقات"
|
||||
|
||||
#: ../js/ui/viewSelector.js:95
|
||||
#: ../js/ui/viewSelector.js:89
|
||||
msgid "Search"
|
||||
msgstr "ابحث"
|
||||
|
||||
#: ../js/ui/wanda.js:94
|
||||
#: ../js/ui/wanda.js:92
|
||||
#, c-format
|
||||
msgid ""
|
||||
"Sorry, no wisdom for you today:\n"
|
||||
@ -1770,15 +1764,11 @@ msgstr ""
|
||||
"عذرًا, لا حكمة لك اليوم:\n"
|
||||
"%s"
|
||||
|
||||
#: ../js/ui/wanda.js:98
|
||||
#: ../js/ui/wanda.js:96
|
||||
#, c-format
|
||||
msgid "%s the Oracle says"
|
||||
msgstr "يقول الحكيم %s"
|
||||
|
||||
#: ../js/ui/wanda.js:139
|
||||
msgid "Your favorite Easter Egg"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/windowAttentionHandler.js:19
|
||||
#, c-format
|
||||
msgid "'%s' is ready"
|
||||
@ -1788,6 +1778,36 @@ msgstr "'%s' جاهز"
|
||||
msgid "Evolution Calendar"
|
||||
msgstr "تقويم إيفُليوشِن"
|
||||
|
||||
#. translators:
|
||||
#. * The number of sound outputs on a particular device
|
||||
#: ../src/gvc/gvc-mixer-control.c:1837
|
||||
#, c-format
|
||||
msgid "%u Output"
|
||||
msgid_plural "%u Outputs"
|
||||
msgstr[0] "لا مخرَج"
|
||||
msgstr[1] "مخرَج واحد"
|
||||
msgstr[2] "مخرَجين"
|
||||
msgstr[3] "%u مخارج"
|
||||
msgstr[4] "%u مخرجا"
|
||||
msgstr[5] "%u مخرج"
|
||||
|
||||
#. translators:
|
||||
#. * The number of sound inputs on a particular device
|
||||
#: ../src/gvc/gvc-mixer-control.c:1847
|
||||
#, c-format
|
||||
msgid "%u Input"
|
||||
msgid_plural "%u Inputs"
|
||||
msgstr[0] "لا مدخل"
|
||||
msgstr[1] "مدخل واحد"
|
||||
msgstr[2] "مدخلين"
|
||||
msgstr[3] "%u مداخل"
|
||||
msgstr[4] "%u مدخلا"
|
||||
msgstr[5] "%u مدخل"
|
||||
|
||||
#: ../src/gvc/gvc-mixer-control.c:2371
|
||||
msgid "System Sounds"
|
||||
msgstr "أصوات النظام"
|
||||
|
||||
#: ../src/main.c:332
|
||||
msgid "Print version"
|
||||
msgstr "اطبع الإصدارة"
|
||||
@ -1829,6 +1849,12 @@ msgstr "المبدئي"
|
||||
msgid "Authentication dialog was dismissed by the user"
|
||||
msgstr "أغلق المستخدم مربع الاستيثاق الحِواري"
|
||||
|
||||
#~ msgid "APPLICATIONS"
|
||||
#~ msgstr "التطبيقات"
|
||||
|
||||
#~ msgid "SETTINGS"
|
||||
#~ msgstr "الإعدادات"
|
||||
|
||||
#~ msgid "Subscription request"
|
||||
#~ msgstr "طلب اشتراك"
|
||||
|
||||
@ -1877,27 +1903,6 @@ msgstr "أغلق المستخدم مربع الاستيثاق الحِواري"
|
||||
#~ msgid "System Settings"
|
||||
#~ msgstr "إعدادات النظام"
|
||||
|
||||
#~ msgid "%u Output"
|
||||
#~ msgid_plural "%u Outputs"
|
||||
#~ msgstr[0] "لا مخرَج"
|
||||
#~ msgstr[1] "مخرَج واحد"
|
||||
#~ msgstr[2] "مخرَجين"
|
||||
#~ msgstr[3] "%u مخارج"
|
||||
#~ msgstr[4] "%u مخرجا"
|
||||
#~ msgstr[5] "%u مخرج"
|
||||
|
||||
#~ msgid "%u Input"
|
||||
#~ msgid_plural "%u Inputs"
|
||||
#~ msgstr[0] "لا مدخل"
|
||||
#~ msgstr[1] "مدخل واحد"
|
||||
#~ msgstr[2] "مدخلين"
|
||||
#~ msgstr[3] "%u مداخل"
|
||||
#~ msgstr[4] "%u مدخلا"
|
||||
#~ msgstr[5] "%u مدخل"
|
||||
|
||||
#~ msgid "System Sounds"
|
||||
#~ msgstr "أصوات النظام"
|
||||
|
||||
#~ msgid "Failed to unmount '%s'"
|
||||
#~ msgstr "فشل فصْل '%s'"
|
||||
|
||||
|
12
po/et.po
12
po/et.po
@ -14,7 +14,7 @@ msgstr ""
|
||||
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
|
||||
"shell&keywords=I18N+L10N&component=general\n"
|
||||
"POT-Creation-Date: 2012-12-09 13:03+0000\n"
|
||||
"PO-Revision-Date: 2012-12-18 17:13+0300\n"
|
||||
"PO-Revision-Date: 2012-12-19 16:47+0300\n"
|
||||
"Last-Translator: Mattias Põldaru <mahfiaz@gmail.com>\n"
|
||||
"Language-Team: Estonian <>\n"
|
||||
"Language: et\n"
|
||||
@ -153,8 +153,8 @@ msgid ""
|
||||
"state of the checkbox."
|
||||
msgstr ""
|
||||
"Shell küsib parooli, kui haagitakse krüpteeritud seade või kaugfailisüsteem. "
|
||||
"Kui parooli on võimalik salvestada järgmise kasutuse jaoks, näidatakse "
|
||||
"\"Jäta parool meelde\" märkeruutu. See võti määrab märkeruudu vaikimisi "
|
||||
"Kui parooli on võimalik salvestada edaspidiseks kasutuseks, näidatakse "
|
||||
"'Salvesta parool' märkeruutu. See võti määrab selle märkeruudu vaikimisi "
|
||||
"oleku."
|
||||
|
||||
msgid "Show the week date in the calendar"
|
||||
@ -257,9 +257,9 @@ msgid ""
|
||||
"are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-"
|
||||
"only' (shows only the application icon) or 'both'."
|
||||
msgstr ""
|
||||
"Seadistab, kuidas aknaid akendevahetajas kuvatakse. Sobivad väärtused on "
|
||||
"'thumbnail-only' (näidatakse ainult pisipilti), 'app-icon-only' (näidatakse "
|
||||
"ainult rakenduse ikooni) või 'both' (näidatakse mõlemaid)."
|
||||
"Seadistab, kuidas aknaid aknavahetajas kuvatakse. Sobivad väärtused on "
|
||||
"'thumbnail-only' (näidatakse ainult pisipilti aknast), 'app-icon-"
|
||||
"only' (näidatakse ainult akna ikooni) või 'both' (mõlemad)."
|
||||
|
||||
msgid "Attach modal dialog to the parent window"
|
||||
msgstr "Modaaldialoog kuulub vanemakna juurde"
|
||||
|
1059
po/pt_BR.po
1059
po/pt_BR.po
File diff suppressed because it is too large
Load Diff
344
po/sk.po
344
po/sk.po
@ -10,8 +10,8 @@ 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-12-09 02:53+0000\n"
|
||||
"PO-Revision-Date: 2012-11-19 22:23+0100\n"
|
||||
"POT-Creation-Date: 2013-01-08 18:17+0000\n"
|
||||
"PO-Revision-Date: 2013-01-01 17:50+0100\n"
|
||||
"Last-Translator: Dušan Kazik <prescott66@gmail.com>\n"
|
||||
"Language-Team: Slovak <gnome-sk-list@gnome.org>\n"
|
||||
"Language: sk\n"
|
||||
@ -40,7 +40,7 @@ msgstr "Zobrazí lištu správ"
|
||||
|
||||
#: ../data/50-gnome-shell-system.xml.in.h:3
|
||||
msgid "Focus the active notification"
|
||||
msgstr "Zamerať na aktívne oznámenia"
|
||||
msgstr "Zamerať aktívne oznámenie"
|
||||
|
||||
# tooltip
|
||||
#: ../data/50-gnome-shell-system.xml.in.h:4
|
||||
@ -166,20 +166,23 @@ msgstr ""
|
||||
"s jedným používateľom alebo jednou reláciou."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:15
|
||||
#, fuzzy
|
||||
#| msgid "Always show the 'Log out' menuitem in the user menu."
|
||||
msgid "Show full name in the user menu"
|
||||
msgstr "Vždy zobraziť položku „Odhlásiť sa“ v ponuke používateľa"
|
||||
msgstr "Zobraziť celé meno v ponuke používateľa"
|
||||
|
||||
# description
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:16
|
||||
msgid "Whether the users full name is shown in the user menu or not."
|
||||
msgstr ""
|
||||
msgstr "Určuje, či bude alebo nebude zobrazené celé meno v ponuke používateľa."
|
||||
|
||||
# summary
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:17
|
||||
msgid ""
|
||||
"Whether to remember password for mounting encrypted or remote filesystems"
|
||||
msgstr ""
|
||||
"Určiť, či bude zapamätané heslo pre pripojenie zašifrovaných alebo "
|
||||
"prenosných súborových systémov"
|
||||
|
||||
# description
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:18
|
||||
msgid ""
|
||||
"The shell will request a password when an encrypted device or a remote "
|
||||
@ -187,6 +190,10 @@ msgid ""
|
||||
"'Remember Password' checkbox will be present. This key sets the default "
|
||||
"state of the checkbox."
|
||||
msgstr ""
|
||||
"Shell bude po pripojení zašifrovaného alebo prenosného súborového systému "
|
||||
"požadovať heslo. Ak bude možné toto heslo uložiť pre neskoršie použitie, "
|
||||
"zobrazí sa zaškrtávacie pole „Zapamätať heslo“. Tento kľúč nastaví "
|
||||
"predvolený stav zaškrtávacieho poľa."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:19
|
||||
msgid "Show the week date in the calendar"
|
||||
@ -309,11 +316,11 @@ msgstr ""
|
||||
"základe aktuálneho dátumu a použije túto príponu. Pri nahrávaní do iného "
|
||||
"formátu kontajneru by mala byť zmenená."
|
||||
|
||||
# PM: tento preklad podľa mňa nezodpovedá nasledujúcemu dlhému popisu nastavenia. Ide skor o režim tvorby ikon (z) aplikácií
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:40
|
||||
#, fuzzy
|
||||
#| msgid "Open the application menu"
|
||||
msgid "The application icon mode."
|
||||
msgstr "Otvorí ponuku aplikácií"
|
||||
msgstr "Režim aplikácií s ikonami."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:41
|
||||
msgid ""
|
||||
@ -321,6 +328,9 @@ msgid ""
|
||||
"are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-"
|
||||
"only' (shows only the application icon) or 'both'."
|
||||
msgstr ""
|
||||
"Konfiguruje, ako sa majú zobraziť okná v prepínači. Platné možnosti sú "
|
||||
"„thumbnail-only“ (zobrazí miniatúru okna), „app-icon-only“ (zobrazí iba "
|
||||
"ikonu aplikácie) alebo „both“ (zobrazí oboje)."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:42
|
||||
msgid "Attach modal dialog to the parent window"
|
||||
@ -368,11 +378,11 @@ msgstr "Rozšírenie"
|
||||
msgid "Select an extension to configure using the combobox above."
|
||||
msgstr "Použitím ponuky vyberte rozšírenie na nastavenie"
|
||||
|
||||
#: ../js/gdm/loginDialog.js:565
|
||||
#: ../js/gdm/loginDialog.js:566
|
||||
msgid "Session..."
|
||||
msgstr "Relácia…"
|
||||
|
||||
#: ../js/gdm/loginDialog.js:722
|
||||
#: ../js/gdm/loginDialog.js:723
|
||||
msgctxt "title"
|
||||
msgid "Sign In"
|
||||
msgstr "Prihlásenie"
|
||||
@ -381,35 +391,35 @@ msgstr "Prihlásenie"
|
||||
#. 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:786
|
||||
#: ../js/gdm/loginDialog.js:787
|
||||
msgid "Not listed?"
|
||||
msgstr "Nie ste v zozname?"
|
||||
|
||||
#: ../js/gdm/loginDialog.js:962 ../js/ui/components/networkAgent.js:137
|
||||
#: ../js/gdm/loginDialog.js:963 ../js/ui/components/networkAgent.js:137
|
||||
#: ../js/ui/components/polkitAgent.js:162 ../js/ui/endSessionDialog.js:373
|
||||
#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
|
||||
#: ../js/ui/status/bluetooth.js:413 ../js/ui/unlockDialog.js:178
|
||||
msgid "Cancel"
|
||||
msgstr "Zrušiť"
|
||||
|
||||
#: ../js/gdm/loginDialog.js:978
|
||||
#: ../js/gdm/loginDialog.js:979
|
||||
msgctxt "button"
|
||||
msgid "Sign In"
|
||||
msgstr "Prihlásiť sa"
|
||||
|
||||
#: ../js/gdm/loginDialog.js:978
|
||||
#: ../js/gdm/loginDialog.js:979
|
||||
msgid "Next"
|
||||
msgstr "Ďalej"
|
||||
|
||||
#. TTLS and PEAP are actually much more complicated, but this complication
|
||||
#. is not visible here since we only care about phase2 authentication
|
||||
#. (and don't even care of which one)
|
||||
#: ../js/gdm/loginDialog.js:1086 ../js/ui/components/networkAgent.js:260
|
||||
#: ../js/gdm/loginDialog.js:1087 ../js/ui/components/networkAgent.js:260
|
||||
#: ../js/ui/components/networkAgent.js:278
|
||||
msgid "Username: "
|
||||
msgstr "Používateľské meno: "
|
||||
|
||||
#: ../js/gdm/loginDialog.js:1382
|
||||
#: ../js/gdm/loginDialog.js:1383
|
||||
msgid "Login Window"
|
||||
msgstr "Prihlasovacie okno"
|
||||
|
||||
@ -418,8 +428,8 @@ msgstr "Prihlasovacie okno"
|
||||
msgid "Power"
|
||||
msgstr "Napájanie"
|
||||
|
||||
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:674 ../js/ui/userMenu.js:678
|
||||
#: ../js/ui/userMenu.js:789
|
||||
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:675 ../js/ui/userMenu.js:679
|
||||
#: ../js/ui/userMenu.js:790
|
||||
msgid "Suspend"
|
||||
msgstr "Uspať"
|
||||
|
||||
@ -427,8 +437,8 @@ msgstr "Uspať"
|
||||
msgid "Restart"
|
||||
msgstr "Reštartovať"
|
||||
|
||||
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:676
|
||||
#: ../js/ui/userMenu.js:678 ../js/ui/userMenu.js:788
|
||||
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:677
|
||||
#: ../js/ui/userMenu.js:679 ../js/ui/userMenu.js:789
|
||||
msgid "Power Off"
|
||||
msgstr "Vypnúť"
|
||||
|
||||
@ -463,27 +473,19 @@ msgid "Execution of '%s' failed:"
|
||||
msgstr "Spustenie „%s“ zlyhalo:"
|
||||
|
||||
#. Translators: Filter to display all applications
|
||||
#: ../js/ui/appDisplay.js:259
|
||||
#: ../js/ui/appDisplay.js:258
|
||||
msgid "All"
|
||||
msgstr "Všetky"
|
||||
|
||||
#: ../js/ui/appDisplay.js:318
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "APLIKÁCIE"
|
||||
|
||||
#: ../js/ui/appDisplay.js:375
|
||||
msgid "SETTINGS"
|
||||
msgstr "NASTAVENIA"
|
||||
|
||||
#: ../js/ui/appDisplay.js:679
|
||||
#: ../js/ui/appDisplay.js:666
|
||||
msgid "New Window"
|
||||
msgstr "Nové okno"
|
||||
|
||||
#: ../js/ui/appDisplay.js:682 ../js/ui/dash.js:289
|
||||
#: ../js/ui/appDisplay.js:669 ../js/ui/dash.js:289
|
||||
msgid "Remove from Favorites"
|
||||
msgstr "Odstrániť z obľúbených"
|
||||
|
||||
#: ../js/ui/appDisplay.js:683
|
||||
#: ../js/ui/appDisplay.js:670
|
||||
msgid "Add to Favorites"
|
||||
msgstr "Pridať do obľúbených"
|
||||
|
||||
@ -500,19 +502,19 @@ msgstr "Program %s bol odstránený z obľúbených."
|
||||
#. Translators: Shown in calendar event list for all day events
|
||||
#. * Keep it short, best if you can use less then 10 characters
|
||||
#.
|
||||
#: ../js/ui/calendar.js:62
|
||||
#: ../js/ui/calendar.js:61
|
||||
msgctxt "event list time"
|
||||
msgid "All Day"
|
||||
msgstr "Celý deň"
|
||||
|
||||
#. Translators: Shown in calendar event list, if 24h format
|
||||
#: ../js/ui/calendar.js:67
|
||||
#: ../js/ui/calendar.js:66
|
||||
msgctxt "event list time"
|
||||
msgid "%H:%M"
|
||||
msgstr "%H:%M"
|
||||
|
||||
#. Transators: Shown in calendar event list, if 12h format
|
||||
#: ../js/ui/calendar.js:74
|
||||
#: ../js/ui/calendar.js:73
|
||||
msgctxt "event list time"
|
||||
msgid "%l:%M %p"
|
||||
msgstr "%l:%M %p"
|
||||
@ -522,43 +524,43 @@ msgstr "%l:%M %p"
|
||||
#. * NOTE: These grid abbreviations are always shown together
|
||||
#. * and in order, e.g. "S M T W T F S".
|
||||
#.
|
||||
#: ../js/ui/calendar.js:114
|
||||
#: ../js/ui/calendar.js:104
|
||||
msgctxt "grid sunday"
|
||||
msgid "S"
|
||||
msgstr "Ne"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Monday
|
||||
#: ../js/ui/calendar.js:116
|
||||
#: ../js/ui/calendar.js:106
|
||||
msgctxt "grid monday"
|
||||
msgid "M"
|
||||
msgstr "Po"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Tuesday
|
||||
#: ../js/ui/calendar.js:118
|
||||
#: ../js/ui/calendar.js:108
|
||||
msgctxt "grid tuesday"
|
||||
msgid "T"
|
||||
msgstr "Ut"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Wednesday
|
||||
#: ../js/ui/calendar.js:120
|
||||
#: ../js/ui/calendar.js:110
|
||||
msgctxt "grid wednesday"
|
||||
msgid "W"
|
||||
msgstr "St"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Thursday
|
||||
#: ../js/ui/calendar.js:122
|
||||
#: ../js/ui/calendar.js:112
|
||||
msgctxt "grid thursday"
|
||||
msgid "T"
|
||||
msgstr "Št"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Friday
|
||||
#: ../js/ui/calendar.js:124
|
||||
#: ../js/ui/calendar.js:114
|
||||
msgctxt "grid friday"
|
||||
msgid "F"
|
||||
msgstr "Pi"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Saturday
|
||||
#: ../js/ui/calendar.js:126
|
||||
#: ../js/ui/calendar.js:116
|
||||
msgctxt "grid saturday"
|
||||
msgid "S"
|
||||
msgstr "So"
|
||||
@ -569,77 +571,77 @@ msgstr "So"
|
||||
#. * so they need to be unique (e.g. Tuesday and Thursday cannot
|
||||
#. * both be 'T').
|
||||
#.
|
||||
#: ../js/ui/calendar.js:139
|
||||
#: ../js/ui/calendar.js:129
|
||||
msgctxt "list sunday"
|
||||
msgid "Su"
|
||||
msgstr "Ne"
|
||||
|
||||
#. Translators: Event list abbreviation for Monday
|
||||
#: ../js/ui/calendar.js:141
|
||||
#: ../js/ui/calendar.js:131
|
||||
msgctxt "list monday"
|
||||
msgid "M"
|
||||
msgstr "Po"
|
||||
|
||||
#. Translators: Event list abbreviation for Tuesday
|
||||
#: ../js/ui/calendar.js:143
|
||||
#: ../js/ui/calendar.js:133
|
||||
msgctxt "list tuesday"
|
||||
msgid "T"
|
||||
msgstr "Ut"
|
||||
|
||||
#. Translators: Event list abbreviation for Wednesday
|
||||
#: ../js/ui/calendar.js:145
|
||||
#: ../js/ui/calendar.js:135
|
||||
msgctxt "list wednesday"
|
||||
msgid "W"
|
||||
msgstr "St"
|
||||
|
||||
#. Translators: Event list abbreviation for Thursday
|
||||
#: ../js/ui/calendar.js:147
|
||||
#: ../js/ui/calendar.js:137
|
||||
msgctxt "list thursday"
|
||||
msgid "Th"
|
||||
msgstr "Št"
|
||||
|
||||
#. Translators: Event list abbreviation for Friday
|
||||
#: ../js/ui/calendar.js:149
|
||||
#: ../js/ui/calendar.js:139
|
||||
msgctxt "list friday"
|
||||
msgid "F"
|
||||
msgstr "Pi"
|
||||
|
||||
#. Translators: Event list abbreviation for Saturday
|
||||
#: ../js/ui/calendar.js:151
|
||||
#: ../js/ui/calendar.js:141
|
||||
msgctxt "list saturday"
|
||||
msgid "S"
|
||||
msgstr "So"
|
||||
|
||||
#. Translators: Text to show if there are no events
|
||||
#: ../js/ui/calendar.js:700
|
||||
#: ../js/ui/calendar.js:674
|
||||
msgid "Nothing Scheduled"
|
||||
msgstr "Žiadne naplánované udalosti"
|
||||
|
||||
#. Translators: Shown on calendar heading when selected day occurs on current year
|
||||
#: ../js/ui/calendar.js:716
|
||||
#: ../js/ui/calendar.js:690
|
||||
msgctxt "calendar heading"
|
||||
msgid "%A, %B %d"
|
||||
msgstr "%A, %e. %B"
|
||||
|
||||
#. Translators: Shown on calendar heading when selected day occurs on different year
|
||||
#: ../js/ui/calendar.js:719
|
||||
#: ../js/ui/calendar.js:693
|
||||
msgctxt "calendar heading"
|
||||
msgid "%A, %B %d, %Y"
|
||||
msgstr "%A, %e. %B %Y"
|
||||
|
||||
#: ../js/ui/calendar.js:729
|
||||
#: ../js/ui/calendar.js:703
|
||||
msgid "Today"
|
||||
msgstr "Dnes"
|
||||
|
||||
#: ../js/ui/calendar.js:733
|
||||
#: ../js/ui/calendar.js:707
|
||||
msgid "Tomorrow"
|
||||
msgstr "Zajtra"
|
||||
|
||||
#: ../js/ui/calendar.js:744
|
||||
#: ../js/ui/calendar.js:718
|
||||
msgid "This week"
|
||||
msgstr "Tento týždeň"
|
||||
|
||||
#: ../js/ui/calendar.js:752
|
||||
#: ../js/ui/calendar.js:726
|
||||
msgid "Next week"
|
||||
msgstr "Ďalší týždeň"
|
||||
|
||||
@ -657,11 +659,11 @@ msgstr "Otvoriť pomocou programu %s"
|
||||
msgid "Eject"
|
||||
msgstr "Vysunúť"
|
||||
|
||||
#: ../js/ui/components/keyring.js:86 ../js/ui/components/polkitAgent.js:260
|
||||
#: ../js/ui/components/keyring.js:82 ../js/ui/components/polkitAgent.js:268
|
||||
msgid "Password:"
|
||||
msgstr "Heslo:"
|
||||
|
||||
#: ../js/ui/components/keyring.js:105
|
||||
#: ../js/ui/components/keyring.js:101
|
||||
msgid "Type again:"
|
||||
msgstr "Zadajte znovu:"
|
||||
|
||||
@ -750,7 +752,7 @@ msgstr "Požaduje sa overenie totožnosti"
|
||||
msgid "Administrator"
|
||||
msgstr "Správca"
|
||||
|
||||
#: ../js/ui/components/polkitAgent.js:166
|
||||
#: ../js/ui/components/polkitAgent.js:165
|
||||
msgid "Authenticate"
|
||||
msgstr "Overiť totožnosť"
|
||||
|
||||
@ -758,7 +760,7 @@ msgstr "Overiť totožnosť"
|
||||
#. * requested authentication was not gained; this can happen
|
||||
#. * because of an authentication error (like invalid password),
|
||||
#. * for instance.
|
||||
#: ../js/ui/components/polkitAgent.js:248 ../js/ui/shellMountOperation.js:383
|
||||
#: ../js/ui/components/polkitAgent.js:256 ../js/ui/shellMountOperation.js:383
|
||||
msgid "Sorry, that didn't work. Please try again."
|
||||
msgstr "Prepáčte, ale nezabralo to. Skúste to, prosím, znova."
|
||||
|
||||
@ -787,7 +789,7 @@ msgstr "Prenos súborov"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:417
|
||||
msgid "Chat"
|
||||
msgstr ""
|
||||
msgstr "Rozhovor"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:477
|
||||
msgid "Unmute"
|
||||
@ -801,28 +803,25 @@ msgstr "Stlmiť"
|
||||
#: ../js/ui/components/telepathyClient.js:931
|
||||
#, no-c-format
|
||||
msgid "<b>Yesterday</b>, <b>%H:%M</b>"
|
||||
msgstr ""
|
||||
msgstr "<b>Včera</b> o <b>%H:%M</b>"
|
||||
|
||||
#. Translators: this is the week day name followed by a time string. i.e. "Monday, 14:30
|
||||
#: ../js/ui/components/telepathyClient.js:937
|
||||
#, fuzzy, no-c-format
|
||||
#| msgid "<b>%H:%M</b> on <b>%A</b>"
|
||||
#, no-c-format
|
||||
msgid "<b>%A</b>, <b>%H:%M</b>"
|
||||
msgstr "v <b>%A</b> o <b>%H:%M</b>"
|
||||
|
||||
#. Translators: this is the month name and day number followed by a time string. i.e. "May 25, 14:30"
|
||||
#: ../js/ui/components/telepathyClient.js:942
|
||||
#, fuzzy, no-c-format
|
||||
#| msgid "<b>%H:%M</b> on <b>%A</b>, <b>%B</b> <b>%d</b>"
|
||||
#, no-c-format
|
||||
msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
|
||||
msgstr "v <b>%A</b> o <b>%H:%M</b>, <b>%e.</b> <b>%B</b>"
|
||||
msgstr "<b>%e.</b> <b>%B</b> o <b>%H:%M</b>"
|
||||
|
||||
#. Translators: this is the month name, day number, year number followed by a time string. i.e. "May 25 2012, 14:30"
|
||||
#: ../js/ui/components/telepathyClient.js:946
|
||||
#, fuzzy, no-c-format
|
||||
#| msgid "<b>%H:%M</b> on <b>%A</b>, <b>%B</b> <b>%d</b>"
|
||||
#, no-c-format
|
||||
msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
|
||||
msgstr "v <b>%A</b> o <b>%H:%M</b>, <b>%e.</b> <b>%B</b>"
|
||||
msgstr "<b>%e.</b> <b>%B</b> <b>%Y</b> o <b>%H:%M</b>"
|
||||
|
||||
#. Translators: this is the other person changing their old IM name to their new
|
||||
#. IM name.
|
||||
@ -1001,19 +1000,17 @@ msgstr "Vnútorná chyba"
|
||||
#: ../js/ui/components/telepathyClient.js:1339
|
||||
#, c-format
|
||||
msgid "Unable to connect to %s"
|
||||
msgstr ""
|
||||
msgstr "Nepodarilo sa pripojiť účet %s"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1344
|
||||
#, fuzzy
|
||||
#| msgid "Edit account"
|
||||
msgid "View account"
|
||||
msgstr "Upraviť účet"
|
||||
msgstr "Zobraziť účet"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1384
|
||||
msgid "Unknown reason"
|
||||
msgstr "Neznámy dôvod"
|
||||
|
||||
#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:87
|
||||
#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:81
|
||||
msgid "Windows"
|
||||
msgstr "Okná"
|
||||
|
||||
@ -1022,18 +1019,18 @@ msgstr "Okná"
|
||||
msgid "Show Applications"
|
||||
msgstr "Zobrazí aplikácie"
|
||||
|
||||
#: ../js/ui/dateMenu.js:86
|
||||
#: ../js/ui/dateMenu.js:87
|
||||
msgid "Date and Time Settings"
|
||||
msgstr "Nastavenia dátumu a času"
|
||||
|
||||
#: ../js/ui/dateMenu.js:111
|
||||
#: ../js/ui/dateMenu.js:112
|
||||
msgid "Open Calendar"
|
||||
msgstr "Otvoriť kalendár"
|
||||
|
||||
#. 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").
|
||||
#.
|
||||
#: ../js/ui/dateMenu.js:201
|
||||
#: ../js/ui/dateMenu.js:191
|
||||
msgid "%A %B %e, %Y"
|
||||
msgstr "%A, %e. %B %Y"
|
||||
|
||||
@ -1142,85 +1139,81 @@ msgstr "Inštalovať"
|
||||
msgid "Download and install '%s' from extensions.gnome.org?"
|
||||
msgstr "Stiahnuť a nainštalovať „%s“ z extensions.gnome.org?"
|
||||
|
||||
#: ../js/ui/keyboard.js:308
|
||||
#: ../js/ui/keyboard.js:291
|
||||
msgid "tray"
|
||||
msgstr "lišta"
|
||||
|
||||
#: ../js/ui/keyboard.js:555 ../js/ui/status/keyboard.js:195
|
||||
#: ../js/ui/keyboard.js:636 ../js/ui/status/keyboard.js:309
|
||||
#: ../js/ui/status/power.js:205
|
||||
msgid "Keyboard"
|
||||
msgstr "Klávesnica"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:691
|
||||
#: ../js/ui/lookingGlass.js:692
|
||||
msgid "No extensions installed"
|
||||
msgstr "Žiadne nainštalované rozšírenia"
|
||||
|
||||
#. Translators: argument is an extension UUID.
|
||||
#: ../js/ui/lookingGlass.js:745
|
||||
#: ../js/ui/lookingGlass.js:746
|
||||
#, c-format
|
||||
msgid "%s has not emitted any errors."
|
||||
msgstr "%s nevyslal žiadnu chybu."
|
||||
|
||||
#: ../js/ui/lookingGlass.js:751
|
||||
#: ../js/ui/lookingGlass.js:752
|
||||
msgid "Hide Errors"
|
||||
msgstr "Skryť chyby"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:755 ../js/ui/lookingGlass.js:815
|
||||
#: ../js/ui/lookingGlass.js:756 ../js/ui/lookingGlass.js:816
|
||||
msgid "Show Errors"
|
||||
msgstr "Zobraziť chyby"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:764
|
||||
#: ../js/ui/lookingGlass.js:765
|
||||
msgid "Enabled"
|
||||
msgstr "Povolené"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:767
|
||||
#: ../js/ui/lookingGlass.js:768
|
||||
msgid "Disabled"
|
||||
msgstr "Zakázané"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:769
|
||||
#: ../js/ui/lookingGlass.js:770
|
||||
msgid "Error"
|
||||
msgstr "Chyba"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:771
|
||||
#: ../js/ui/lookingGlass.js:772
|
||||
msgid "Out of date"
|
||||
msgstr "Neaktuálne"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:773
|
||||
#: ../js/ui/lookingGlass.js:774
|
||||
msgid "Downloading"
|
||||
msgstr "Sťahuje sa"
|
||||
|
||||
# PK: ide tu o zdrojovy kod?
|
||||
#: ../js/ui/lookingGlass.js:797
|
||||
#: ../js/ui/lookingGlass.js:798
|
||||
msgid "View Source"
|
||||
msgstr "Zobraziť zdroj"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:806
|
||||
#: ../js/ui/lookingGlass.js:807
|
||||
msgid "Web Page"
|
||||
msgstr "Webová stránka"
|
||||
|
||||
#: ../js/ui/messageTray.js:1088
|
||||
#: ../js/ui/messageTray.js:1090
|
||||
msgid "Open"
|
||||
msgstr "Otvoriť"
|
||||
|
||||
#: ../js/ui/messageTray.js:1095
|
||||
#: ../js/ui/messageTray.js:1097
|
||||
msgid "Remove"
|
||||
msgstr "Odstrániť"
|
||||
|
||||
# DK: zvazoval som pouzit "Panel správ"
|
||||
# neviem co bude vhodnejsie ako preklad "tray"
|
||||
#: ../js/ui/messageTray.js:1551
|
||||
#, fuzzy
|
||||
#| msgid "Message Tray"
|
||||
#: ../js/ui/messageTray.js:1554
|
||||
msgid "No Messages"
|
||||
msgstr "Lišta správ"
|
||||
msgstr "Žiadne správy"
|
||||
|
||||
# DK: zvazoval som pouzit "Panel správ"
|
||||
# neviem co bude vhodnejsie ako preklad "tray"
|
||||
#: ../js/ui/messageTray.js:1568
|
||||
#: ../js/ui/messageTray.js:1572
|
||||
msgid "Message Tray"
|
||||
msgstr "Lišta správ"
|
||||
|
||||
#: ../js/ui/messageTray.js:2635
|
||||
#: ../js/ui/messageTray.js:2641
|
||||
msgid "System Information"
|
||||
msgstr "Informácie o systéme"
|
||||
|
||||
@ -1229,11 +1222,11 @@ msgctxt "program"
|
||||
msgid "Unknown"
|
||||
msgstr "Neznámy"
|
||||
|
||||
#: ../js/ui/overview.js:95
|
||||
#: ../js/ui/overview.js:92
|
||||
msgid "Undo"
|
||||
msgstr "Vrátiť"
|
||||
|
||||
#: ../js/ui/overview.js:144
|
||||
#: ../js/ui/overview.js:139
|
||||
msgid "Overview"
|
||||
msgstr "Prehľad"
|
||||
|
||||
@ -1241,27 +1234,27 @@ msgstr "Prehľad"
|
||||
#. in the search entry when no search is
|
||||
#. active; it should not exceed ~30
|
||||
#. characters.
|
||||
#: ../js/ui/overview.js:221
|
||||
#: ../js/ui/overview.js:218
|
||||
msgid "Type to search..."
|
||||
msgstr "Vyhľadávanie…"
|
||||
|
||||
#. Translators: this is the name of the dock/favorites area on
|
||||
#. the left of the overview
|
||||
#: ../js/ui/overview.js:242
|
||||
#: ../js/ui/overview.js:236
|
||||
msgid "Dash"
|
||||
msgstr "Dok"
|
||||
|
||||
#: ../js/ui/panel.js:608
|
||||
#: ../js/ui/panel.js:613
|
||||
msgid "Quit"
|
||||
msgstr "Ukončiť"
|
||||
|
||||
#. Translators: If there is no suitable word for "Activities"
|
||||
#. in your language, you can use the word for "Overview".
|
||||
#: ../js/ui/panel.js:637
|
||||
#: ../js/ui/panel.js:642
|
||||
msgid "Activities"
|
||||
msgstr "Aktivity"
|
||||
|
||||
#: ../js/ui/panel.js:980
|
||||
#: ../js/ui/panel.js:976
|
||||
msgid "Top Bar"
|
||||
msgstr "Horná lišta"
|
||||
|
||||
@ -1270,7 +1263,7 @@ msgstr "Horná lišta"
|
||||
#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
|
||||
#. switches containing "◯" and "|"). Other values will
|
||||
#. simply result in invisible toggle switches.
|
||||
#: ../js/ui/popupMenu.js:732
|
||||
#: ../js/ui/popupMenu.js:727
|
||||
msgid "toggle-switch-us"
|
||||
msgstr "toggle-switch-intl"
|
||||
|
||||
@ -1287,11 +1280,11 @@ msgstr "Zavrieť"
|
||||
# v ostatnych retazcoch je pouzite %e, tak to bude asi OK
|
||||
#. Translators: This is a time format for a date in
|
||||
#. long format
|
||||
#: ../js/ui/screenShield.js:112
|
||||
#: ../js/ui/screenShield.js:113
|
||||
msgid "%A, %B %d"
|
||||
msgstr "%A, %e. %B"
|
||||
|
||||
#: ../js/ui/screenShield.js:176
|
||||
#: ../js/ui/screenShield.js:177
|
||||
#, c-format
|
||||
msgid "%d new message"
|
||||
msgid_plural "%d new messages"
|
||||
@ -1299,7 +1292,7 @@ msgstr[0] "%d nových správ"
|
||||
msgstr[1] "%d nová správa"
|
||||
msgstr[2] "%d nové správy"
|
||||
|
||||
#: ../js/ui/screenShield.js:178
|
||||
#: ../js/ui/screenShield.js:179
|
||||
#, c-format
|
||||
msgid "%d new notification"
|
||||
msgid_plural "%d new notifications"
|
||||
@ -1307,15 +1300,15 @@ msgstr[0] "%d nových oznámení"
|
||||
msgstr[1] "%d nové oznámenie"
|
||||
msgstr[2] "%d nové oznámenia"
|
||||
|
||||
#: ../js/ui/screenShield.js:402 ../js/ui/userMenu.js:780
|
||||
#: ../js/ui/screenShield.js:403 ../js/ui/userMenu.js:781
|
||||
msgid "Lock"
|
||||
msgstr "Uzamknúť"
|
||||
|
||||
#: ../js/ui/searchDisplay.js:277
|
||||
#: ../js/ui/searchDisplay.js:403
|
||||
msgid "Searching..."
|
||||
msgstr "Hľadá sa…"
|
||||
|
||||
#: ../js/ui/searchDisplay.js:325
|
||||
#: ../js/ui/searchDisplay.js:451
|
||||
msgid "No results."
|
||||
msgstr "Žiadne výsledky."
|
||||
|
||||
@ -1434,7 +1427,7 @@ msgid "disconnecting..."
|
||||
msgstr "odpája sa…"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:220 ../js/ui/status/network.js:442
|
||||
#: ../js/ui/status/network.js:1453
|
||||
#: ../js/ui/status/network.js:1464
|
||||
msgid "connecting..."
|
||||
msgstr "pripája sa…"
|
||||
|
||||
@ -1450,7 +1443,7 @@ msgstr "Nastavenia klávesnice"
|
||||
msgid "Mouse Settings"
|
||||
msgstr "Nastavenia myši"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:253 ../js/ui/status/volume.js:270
|
||||
#: ../js/ui/status/bluetooth.js:253 ../js/ui/status/volume.js:314
|
||||
msgid "Sound Settings"
|
||||
msgstr "Nastavenia zvuku"
|
||||
|
||||
@ -1514,15 +1507,15 @@ msgstr "Zadajte PIN, ktoré je uvedené na zariadení."
|
||||
msgid "OK"
|
||||
msgstr "Ok"
|
||||
|
||||
#: ../js/ui/status/keyboard.js:228
|
||||
#: ../js/ui/status/keyboard.js:363
|
||||
msgid "Show Keyboard Layout"
|
||||
msgstr "Zobraziť rozloženie klávesnice"
|
||||
|
||||
#: ../js/ui/status/keyboard.js:233
|
||||
#: ../js/ui/status/keyboard.js:368
|
||||
msgid "Region and Language Settings"
|
||||
msgstr "Miestne a jazykové nastavenia"
|
||||
|
||||
#: ../js/ui/status/lockScreenMenu.js:18
|
||||
#: ../js/ui/status/lockScreenMenu.js:43
|
||||
msgid "Volume, network, battery"
|
||||
msgstr "Hlasitosť, sieť, batéria"
|
||||
|
||||
@ -1543,7 +1536,7 @@ msgid "unmanaged"
|
||||
msgstr "nespravované"
|
||||
|
||||
#. Translators: this is for network connections that require some kind of key or password
|
||||
#: ../js/ui/status/network.js:445 ../js/ui/status/network.js:1456
|
||||
#: ../js/ui/status/network.js:445 ../js/ui/status/network.js:1467
|
||||
msgid "authentication required"
|
||||
msgstr "požaduje sa overenie totožnosti"
|
||||
|
||||
@ -1564,72 +1557,72 @@ msgstr "kábel odpojený"
|
||||
msgid "unavailable"
|
||||
msgstr "nedostupné"
|
||||
|
||||
#: ../js/ui/status/network.js:469 ../js/ui/status/network.js:1458
|
||||
#: ../js/ui/status/network.js:469 ../js/ui/status/network.js:1469
|
||||
msgid "connection failed"
|
||||
msgstr "pripojenie zlyhalo"
|
||||
|
||||
#: ../js/ui/status/network.js:525 ../js/ui/status/network.js:1392
|
||||
#: ../js/ui/status/network.js:1534
|
||||
#: ../js/ui/status/network.js:525 ../js/ui/status/network.js:1403
|
||||
#: ../js/ui/status/network.js:1545
|
||||
msgid "More..."
|
||||
msgstr "Viac…"
|
||||
|
||||
#. TRANSLATORS: this is the indication that a connection for another logged in user is active,
|
||||
#. and we cannot access its settings (including the name)
|
||||
#: ../js/ui/status/network.js:561 ../js/ui/status/network.js:1322
|
||||
#: ../js/ui/status/network.js:561 ../js/ui/status/network.js:1333
|
||||
msgid "Connected (private)"
|
||||
msgstr "Pripojené (súkromne)"
|
||||
|
||||
#: ../js/ui/status/network.js:619
|
||||
#: ../js/ui/status/network.js:641
|
||||
msgid "Auto Ethernet"
|
||||
msgstr "Automatický ethernet"
|
||||
|
||||
#: ../js/ui/status/network.js:677
|
||||
#: ../js/ui/status/network.js:688
|
||||
msgid "Auto broadband"
|
||||
msgstr "Automatické širokopásmové pripojenie"
|
||||
|
||||
#: ../js/ui/status/network.js:680
|
||||
#: ../js/ui/status/network.js:691
|
||||
msgid "Auto dial-up"
|
||||
msgstr "Automatické vytáčané pripojenie"
|
||||
|
||||
#. TRANSLATORS: this the automatic wireless connection name (including the network name)
|
||||
#: ../js/ui/status/network.js:809 ../js/ui/status/network.js:1339
|
||||
#: ../js/ui/status/network.js:820 ../js/ui/status/network.js:1350
|
||||
#, c-format
|
||||
msgid "Auto %s"
|
||||
msgstr "Automatické pripojenie %s"
|
||||
|
||||
#: ../js/ui/status/network.js:811
|
||||
#: ../js/ui/status/network.js:822
|
||||
msgid "Auto bluetooth"
|
||||
msgstr "Automatický bluetooth"
|
||||
|
||||
#: ../js/ui/status/network.js:1341
|
||||
#: ../js/ui/status/network.js:1352
|
||||
msgid "Auto wireless"
|
||||
msgstr "Automatická bezdrôtová sieť"
|
||||
|
||||
#: ../js/ui/status/network.js:1584
|
||||
#: ../js/ui/status/network.js:1595
|
||||
msgid "Enable networking"
|
||||
msgstr "Povoliť sieť"
|
||||
|
||||
#: ../js/ui/status/network.js:1616
|
||||
#: ../js/ui/status/network.js:1627
|
||||
msgid "Wi-Fi"
|
||||
msgstr "Wi-Fi"
|
||||
|
||||
#: ../js/ui/status/network.js:1635
|
||||
#: ../js/ui/status/network.js:1646
|
||||
msgid "Network Settings"
|
||||
msgstr "Nastavenia siete"
|
||||
|
||||
#: ../js/ui/status/network.js:1679
|
||||
#: ../js/ui/status/network.js:1692
|
||||
msgid "Network Manager"
|
||||
msgstr "Správca siete"
|
||||
|
||||
#: ../js/ui/status/network.js:1761
|
||||
#: ../js/ui/status/network.js:1774
|
||||
msgid "Connection failed"
|
||||
msgstr "Pripojenie zlyhalo"
|
||||
|
||||
#: ../js/ui/status/network.js:1762
|
||||
#: ../js/ui/status/network.js:1775
|
||||
msgid "Activation of network connection failed"
|
||||
msgstr "Aktivácia pripojenia k sieti zlyhala"
|
||||
|
||||
#: ../js/ui/status/network.js:2079
|
||||
#: ../js/ui/status/network.js:2092
|
||||
msgid "Networking is disabled"
|
||||
msgstr "Sieť je zakázaná"
|
||||
|
||||
@ -1735,19 +1728,19 @@ msgid "Unknown"
|
||||
msgstr "Neznáme"
|
||||
|
||||
#. Translators: This is the label for audio volume
|
||||
#: ../js/ui/status/volume.js:50 ../js/ui/status/volume.js:251
|
||||
#: ../js/ui/status/volume.js:247 ../js/ui/status/volume.js:295
|
||||
msgid "Volume"
|
||||
msgstr "Hlasitosť"
|
||||
|
||||
#: ../js/ui/status/volume.js:62
|
||||
#: ../js/ui/status/volume.js:256
|
||||
msgid "Microphone"
|
||||
msgstr "Mikrofón"
|
||||
|
||||
#: ../js/ui/unlockDialog.js:201
|
||||
#: ../js/ui/unlockDialog.js:203
|
||||
msgid "Log in as another user"
|
||||
msgstr "Prihlásiť ako iný používateľ"
|
||||
|
||||
#: ../js/ui/unlockDialog.js:224
|
||||
#: ../js/ui/unlockDialog.js:229
|
||||
msgid "Unlock Window"
|
||||
msgstr "Odomykacie okno"
|
||||
|
||||
@ -1775,31 +1768,31 @@ msgstr "Nečinný"
|
||||
msgid "Offline"
|
||||
msgstr "Odhlásený"
|
||||
|
||||
#: ../js/ui/userMenu.js:754
|
||||
#: ../js/ui/userMenu.js:755
|
||||
msgid "Notifications"
|
||||
msgstr "Upozornenia"
|
||||
|
||||
#: ../js/ui/userMenu.js:762
|
||||
#: ../js/ui/userMenu.js:763
|
||||
msgid "Settings"
|
||||
msgstr "Nastavenia"
|
||||
|
||||
#: ../js/ui/userMenu.js:770
|
||||
#: ../js/ui/userMenu.js:771
|
||||
msgid "Switch User"
|
||||
msgstr "Prepnúť používateľa"
|
||||
|
||||
#: ../js/ui/userMenu.js:775
|
||||
#: ../js/ui/userMenu.js:776
|
||||
msgid "Log Out"
|
||||
msgstr "Odhlásiť sa"
|
||||
|
||||
#: ../js/ui/userMenu.js:795
|
||||
#: ../js/ui/userMenu.js:796
|
||||
msgid "Install Updates & Restart"
|
||||
msgstr "Nainštalovať aktualizácie a reštartovať"
|
||||
|
||||
#: ../js/ui/userMenu.js:813
|
||||
#: ../js/ui/userMenu.js:814
|
||||
msgid "Your chat status will be set to busy"
|
||||
msgstr "Váš stav bude nastavený na zaneprázdnený"
|
||||
|
||||
#: ../js/ui/userMenu.js:814
|
||||
#: ../js/ui/userMenu.js:815
|
||||
msgid ""
|
||||
"Notifications are now disabled, including chat messages. Your online status "
|
||||
"has been adjusted to let others know that you might not see their messages."
|
||||
@ -1807,15 +1800,15 @@ msgstr ""
|
||||
"Oznámenia, vrátane správ rozhovoru, sú teraz zakázané. Váš stav online bol "
|
||||
"nastavený tak, aby ostatní vedeli, že nemusíte vidieť ich správy."
|
||||
|
||||
#: ../js/ui/viewSelector.js:91
|
||||
#: ../js/ui/viewSelector.js:85
|
||||
msgid "Applications"
|
||||
msgstr "Aplikácie"
|
||||
|
||||
#: ../js/ui/viewSelector.js:95
|
||||
#: ../js/ui/viewSelector.js:89
|
||||
msgid "Search"
|
||||
msgstr "Hľadať"
|
||||
|
||||
#: ../js/ui/wanda.js:94
|
||||
#: ../js/ui/wanda.js:92
|
||||
#, c-format
|
||||
msgid ""
|
||||
"Sorry, no wisdom for you today:\n"
|
||||
@ -1824,15 +1817,11 @@ msgstr ""
|
||||
"Prepáčte, dnes nie je pre vás pripravená žiadna múdrosť:\n"
|
||||
"%s"
|
||||
|
||||
#: ../js/ui/wanda.js:98
|
||||
#: ../js/ui/wanda.js:96
|
||||
#, c-format
|
||||
msgid "%s the Oracle says"
|
||||
msgstr "Veštec hovorí %s"
|
||||
|
||||
#: ../js/ui/wanda.js:139
|
||||
msgid "Your favorite Easter Egg"
|
||||
msgstr "Vaše obľúbené veľkonočné vajce"
|
||||
|
||||
# %s je totiž titulok okna
|
||||
# informacna bublina
|
||||
#: ../js/ui/windowAttentionHandler.js:19
|
||||
@ -1887,6 +1876,30 @@ msgstr "Predvolený"
|
||||
msgid "Authentication dialog was dismissed by the user"
|
||||
msgstr "Dialógové okno overenia totožnosti bolo zatvorené používateľom"
|
||||
|
||||
#~ msgid "APPLICATIONS"
|
||||
#~ msgstr "APLIKÁCIE"
|
||||
|
||||
#~ msgid "SETTINGS"
|
||||
#~ msgstr "NASTAVENIA"
|
||||
|
||||
#~ msgid "Your favorite Easter Egg"
|
||||
#~ msgstr "Vaše obľúbené veľkonočné vajce"
|
||||
|
||||
#~ msgid "%u Output"
|
||||
#~ msgid_plural "%u Outputs"
|
||||
#~ msgstr[0] "%u výstupov"
|
||||
#~ msgstr[1] "%u výstup"
|
||||
#~ msgstr[2] "%u výstupy"
|
||||
|
||||
#~ msgid "%u Input"
|
||||
#~ msgid_plural "%u Inputs"
|
||||
#~ msgstr[0] "%u vstupov"
|
||||
#~ msgstr[1] "%u vstup"
|
||||
#~ msgstr[2] "%u vstupy"
|
||||
|
||||
#~ msgid "System Sounds"
|
||||
#~ msgstr "Zvuky systému"
|
||||
|
||||
#~ msgid "Subscription request"
|
||||
#~ msgstr "Žiadosť o potvrdenie prístupu"
|
||||
|
||||
@ -1904,18 +1917,3 @@ msgstr "Dialógové okno overenia totožnosti bolo zatvorené používateľom"
|
||||
|
||||
#~ msgid "Reconnect"
|
||||
#~ msgstr "Znovu sa pripojiť"
|
||||
|
||||
#~ msgid "%u Output"
|
||||
#~ msgid_plural "%u Outputs"
|
||||
#~ msgstr[0] "%u výstupov"
|
||||
#~ msgstr[1] "%u výstup"
|
||||
#~ msgstr[2] "%u výstupy"
|
||||
|
||||
#~ msgid "%u Input"
|
||||
#~ msgid_plural "%u Inputs"
|
||||
#~ msgstr[0] "%u vstupov"
|
||||
#~ msgstr[1] "%u vstup"
|
||||
#~ msgstr[2] "%u vstupy"
|
||||
|
||||
#~ msgid "System Sounds"
|
||||
#~ msgstr "Zvuky systému"
|
||||
|
846
po/sr@latin.po
846
po/sr@latin.po
File diff suppressed because it is too large
Load Diff
828
po/zh_CN.po
828
po/zh_CN.po
File diff suppressed because it is too large
Load Diff
891
po/zh_HK.po
891
po/zh_HK.po
File diff suppressed because it is too large
Load Diff
898
po/zh_TW.po
898
po/zh_TW.po
File diff suppressed because it is too large
Load Diff
@ -86,7 +86,6 @@ include Makefile-hotplug-sniffer.am
|
||||
|
||||
gnome_shell_cflags = \
|
||||
$(GNOME_SHELL_CFLAGS) \
|
||||
$(SYSTEMD_CFLAGS) \
|
||||
-I$(srcdir)/tray \
|
||||
-DVERSION=\"$(VERSION)\" \
|
||||
-DLOCALEDIR=\"$(datadir)/locale\" \
|
||||
@ -115,7 +114,7 @@ shell_public_headers_h = \
|
||||
shell-gtk-embed.h \
|
||||
shell-global.h \
|
||||
shell-invert-lightness-effect.h \
|
||||
shell-mobile-providers.h \
|
||||
shell-keybinding-modes.h \
|
||||
shell-mount-operation.h \
|
||||
shell-network-agent.h \
|
||||
shell-perf-log.h \
|
||||
@ -160,7 +159,6 @@ libgnome_shell_la_SOURCES = \
|
||||
shell-invert-lightness-effect.c \
|
||||
shell-keyring-prompt.h \
|
||||
shell-keyring-prompt.c \
|
||||
shell-mobile-providers.c \
|
||||
shell-mount-operation.c \
|
||||
shell-network-agent.c \
|
||||
shell-perf-log.c \
|
||||
@ -281,7 +279,6 @@ libgnome_shell_la_LDFLAGS = -avoid-version
|
||||
libgnome_shell_la_LIBADD = \
|
||||
-lm \
|
||||
$(GNOME_SHELL_LIBS) \
|
||||
$(SYSTEMD_LIBS) \
|
||||
$(BLUETOOTH_LIBS) \
|
||||
libst-1.0.la \
|
||||
libtray.la \
|
||||
|
@ -1,12 +1,12 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ -n "$GI_TYPELIB_PATH" ]; then
|
||||
if [ -z "$GI_TYPELIB_PATH" ]; then
|
||||
export GI_TYPELIB_PATH=@pkglibdir@
|
||||
else
|
||||
export GI_TYPELIB_PATH=@pkglibdir@:$GI_TYPELIB_PATH
|
||||
fi
|
||||
|
||||
if [ -n "$LD_LIBRARY_PATH" ] ; then
|
||||
if [ -z "$LD_LIBRARY_PATH" ] ; then
|
||||
export LD_LIBRARY_PATH=@pkglibdir@
|
||||
else
|
||||
export LD_LIBRARY_PATH=@pkglibdir@:$LD_LIBRARY_PATH
|
||||
|
@ -75,9 +75,8 @@ def start_shell():
|
||||
if 'GI_TYPELIB_PATH' in os.environ:
|
||||
typelib_dir += ':%s' % (os.environ['GI_TYPELIB_PATH'],)
|
||||
|
||||
env.update({'GNOME_SHELL_JS' : os.path.join(top_dir, "js"),
|
||||
'GNOME_SHELL_BINDIR' : self_dir,
|
||||
'GI_TYPELIB_PATH' : typelib_dir,
|
||||
env.update({'GI_TYPELIB_PATH' : typelib_dir,
|
||||
'GNOME_SHELL_JS' : os.path.join(top_dir, "js"),
|
||||
'GNOME_SHELL_DATADIR' : os.path.join(top_dir, "data"),
|
||||
'GSETTINGS_SCHEMA_DIR' : os.path.join(top_dir, "data") })
|
||||
|
||||
|
@ -284,12 +284,44 @@ gnome_shell_plugin_kill_switch_workspace (MetaPlugin *plugin)
|
||||
_shell_wm_kill_switch_workspace (get_shell_wm());
|
||||
}
|
||||
|
||||
static gboolean
|
||||
ignore_crossing_event (MetaPlugin *plugin,
|
||||
XIEnterEvent *enter_event)
|
||||
{
|
||||
MetaScreen *screen = meta_plugin_get_screen (plugin);
|
||||
ClutterStage *stage = CLUTTER_STAGE (meta_get_stage_for_screen (screen));
|
||||
|
||||
if (enter_event->event == clutter_x11_get_stage_window (stage))
|
||||
{
|
||||
/* If the pointer enters a child of the stage window (eg, a
|
||||
* trayicon), we want to consider it to still be in the stage,
|
||||
* so don't let Clutter see the event.
|
||||
*/
|
||||
if (enter_event->detail == XINotifyInferior)
|
||||
return TRUE;
|
||||
|
||||
/* If the pointer is grabbed by a window it is not currently in,
|
||||
* filter that out as well. In particular, if a trayicon grabs
|
||||
* the pointer after a click on its label, we don't want to hide
|
||||
* the message tray. Filtering out this event will leave Clutter
|
||||
* out of sync, but that happens fairly often with grabs, and we
|
||||
* can work around it. (Eg, shell_global_sync_pointer().)
|
||||
*/
|
||||
if (enter_event->mode == XINotifyGrab &&
|
||||
(enter_event->detail == XINotifyNonlinear ||
|
||||
enter_event->detail == XINotifyNonlinearVirtual))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gnome_shell_plugin_xevent_filter (MetaPlugin *plugin,
|
||||
XEvent *xev)
|
||||
{
|
||||
MetaScreen *screen = meta_plugin_get_screen (plugin);
|
||||
ClutterStage *stage = CLUTTER_STAGE (meta_get_stage_for_screen (screen));
|
||||
MetaDisplay *display = meta_screen_get_display (screen);
|
||||
|
||||
GnomeShellPlugin *shell_plugin = GNOME_SHELL_PLUGIN (plugin);
|
||||
#ifdef GLX_INTEL_swap_event
|
||||
@ -309,26 +341,15 @@ gnome_shell_plugin_xevent_filter (MetaPlugin *plugin,
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((xev->xany.type == EnterNotify || xev->xany.type == LeaveNotify)
|
||||
&& xev->xcrossing.window == clutter_x11_get_stage_window (stage))
|
||||
/* Make sure that Clutter doesn't see certain focus change events,
|
||||
* so that when we're moving into something like a tray icon, we
|
||||
* don't unfocus the container. */
|
||||
if (xev->type == GenericEvent &&
|
||||
xev->xcookie.extension == meta_display_get_xinput_opcode (display))
|
||||
{
|
||||
/* If the pointer enters a child of the stage window (eg, a
|
||||
* trayicon), we want to consider it to still be in the stage,
|
||||
* so don't let Clutter see the event.
|
||||
*/
|
||||
if (xev->xcrossing.detail == NotifyInferior)
|
||||
return TRUE;
|
||||
|
||||
/* If the pointer is grabbed by a window it is not currently in,
|
||||
* filter that out as well. In particular, if a trayicon grabs
|
||||
* the pointer after a click on its label, we don't want to hide
|
||||
* the message tray. Filtering out this event will leave Clutter
|
||||
* out of sync, but that happens fairly often with grabs, and we
|
||||
* can work around it. (Eg, shell_global_sync_pointer().)
|
||||
*/
|
||||
if (xev->xcrossing.mode == NotifyGrab &&
|
||||
(xev->xcrossing.detail == NotifyNonlinear ||
|
||||
xev->xcrossing.detail == NotifyNonlinearVirtual))
|
||||
XIEvent *input_event = (XIEvent *) xev->xcookie.data;
|
||||
if ((input_event->evtype == XI_Enter || input_event->evtype == XI_Leave) &&
|
||||
ignore_crossing_event (plugin, (XIEnterEvent *) input_event))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
16
src/main.c
16
src/main.c
@ -8,6 +8,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <cogl-pango/cogl-pango.h>
|
||||
#include <clutter/clutter.h>
|
||||
#include <clutter/x11/clutter-x11.h>
|
||||
#include <gdk/gdk.h>
|
||||
@ -194,6 +195,20 @@ shell_introspection_init (void)
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
shell_fonts_init (void)
|
||||
{
|
||||
CoglPangoFontMap *fontmap;
|
||||
|
||||
/* Disable text mipmapping; it causes problems on pre-GEM Intel
|
||||
* drivers and we should just be rendering text at the right
|
||||
* size rather than scaling it. If we do effects where we dynamically
|
||||
* zoom labels, then we might want to reconsider.
|
||||
*/
|
||||
fontmap = COGL_PANGO_FONT_MAP (clutter_get_font_map ());
|
||||
cogl_pango_font_map_set_use_mipmapping (fontmap, FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
malloc_statistics_callback (ShellPerfLog *perf_log,
|
||||
gpointer data)
|
||||
@ -396,6 +411,7 @@ main (int argc, char **argv)
|
||||
shell_perf_log_init ();
|
||||
shell_prefs_init ();
|
||||
shell_introspection_init ();
|
||||
shell_fonts_init ();
|
||||
|
||||
/* Turn on telepathy-glib debugging but filter it out in
|
||||
* default_log_handler. This handler also exposes all the logs over D-Bus
|
||||
|
@ -457,38 +457,6 @@ shell_app_system_get_tree (ShellAppSystem *self)
|
||||
return self->priv->apps_tree;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_app_system_get_settings_tree:
|
||||
*
|
||||
* Return Value: (transfer none): The #GMenuTree for apps
|
||||
*/
|
||||
GMenuTree *
|
||||
shell_app_system_get_settings_tree (ShellAppSystem *self)
|
||||
{
|
||||
return self->priv->settings_tree;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_app_system_lookup_setting:
|
||||
* @system:
|
||||
* @id: desktop file id
|
||||
*
|
||||
* Returns: (transfer none): Application in gnomecc.menu, or %NULL if none
|
||||
*/
|
||||
ShellApp *
|
||||
shell_app_system_lookup_setting (ShellAppSystem *self,
|
||||
const char *id)
|
||||
{
|
||||
ShellApp *app;
|
||||
|
||||
/* Actually defer to the main app set if there's overlap */
|
||||
app = shell_app_system_lookup_app (self, id);
|
||||
if (app != NULL)
|
||||
return app;
|
||||
|
||||
return g_hash_table_lookup (self->priv->setting_id_to_app, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_app_system_get_default:
|
||||
*
|
||||
|
@ -60,13 +60,7 @@ GSList *shell_app_system_subsearch (ShellAppSystem *sys
|
||||
GSList *previous_results,
|
||||
GSList *terms);
|
||||
|
||||
GMenuTree *shell_app_system_get_settings_tree (ShellAppSystem *system);
|
||||
|
||||
GSList *shell_app_system_search_settings (ShellAppSystem *system,
|
||||
GSList *terms);
|
||||
|
||||
ShellApp *shell_app_system_lookup_setting (ShellAppSystem *system,
|
||||
const char *id);
|
||||
|
||||
|
||||
#endif /* __SHELL_APP_SYSTEM_H__ */
|
||||
|
@ -13,10 +13,11 @@
|
||||
#ifdef HAVE_SYS_RESOURCE_H
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
#include <locale.h>
|
||||
|
||||
#include <X11/extensions/Xfixes.h>
|
||||
#include <cogl-pango/cogl-pango.h>
|
||||
#include <canberra.h>
|
||||
#include <canberra-gtk.h>
|
||||
#include <clutter/glx/clutter-glx.h>
|
||||
#include <clutter/x11/clutter-x11.h>
|
||||
#include <gdk/gdkx.h>
|
||||
@ -266,8 +267,13 @@ shell_global_init (ShellGlobal *global)
|
||||
|
||||
global->input_mode = SHELL_STAGE_INPUT_MODE_NORMAL;
|
||||
|
||||
ca_context_create (&global->sound_context);
|
||||
ca_context_change_props (global->sound_context, CA_PROP_APPLICATION_NAME, PACKAGE_NAME, CA_PROP_APPLICATION_ID, "org.gnome.Shell", NULL);
|
||||
global->sound_context = ca_gtk_context_get ();
|
||||
ca_context_change_props (global->sound_context,
|
||||
CA_PROP_APPLICATION_NAME, "GNOME Shell",
|
||||
CA_PROP_APPLICATION_ID, "org.gnome.Shell",
|
||||
CA_PROP_APPLICATION_ICON_NAME, "start-here",
|
||||
CA_PROP_APPLICATION_LANGUAGE, setlocale (LC_MESSAGES, NULL),
|
||||
NULL);
|
||||
ca_context_open (global->sound_context);
|
||||
|
||||
if (!shell_js)
|
||||
@ -616,6 +622,9 @@ shell_global_set_cursor (ShellGlobal *global,
|
||||
case SHELL_CURSOR_POINTING_HAND:
|
||||
name = "hand";
|
||||
break;
|
||||
case SHELL_CURSOR_CROSSHAIR:
|
||||
name = "crosshair";
|
||||
break;
|
||||
default:
|
||||
g_return_if_reached ();
|
||||
}
|
||||
@ -638,6 +647,9 @@ shell_global_set_cursor (ShellGlobal *global,
|
||||
case SHELL_CURSOR_POINTING_HAND:
|
||||
cursor_type = GDK_HAND2;
|
||||
break;
|
||||
case SHELL_CURSOR_CROSSHAIR:
|
||||
cursor_type = GDK_CROSSHAIR;
|
||||
break;
|
||||
case SHELL_CURSOR_DND_UNSUPPORTED_TARGET:
|
||||
cursor_type = GDK_X_CURSOR;
|
||||
break;
|
||||
@ -805,20 +817,6 @@ global_stage_after_paint (ClutterStage *stage,
|
||||
"clutter.stagePaintDone");
|
||||
}
|
||||
|
||||
static void
|
||||
shell_fonts_init (ClutterStage *stage)
|
||||
{
|
||||
CoglPangoFontMap *fontmap;
|
||||
|
||||
/* Disable text mipmapping; it causes problems on pre-GEM Intel
|
||||
* drivers and we should just be rendering text at the right
|
||||
* size rather than scaling it. If we do effects where we dynamically
|
||||
* zoom labels, then we might want to reconsider.
|
||||
*/
|
||||
fontmap = COGL_PANGO_FONT_MAP (clutter_get_font_map ());
|
||||
cogl_pango_font_map_set_use_mipmapping (fontmap, FALSE);
|
||||
}
|
||||
|
||||
/* This is an IBus workaround. The flow of events with IBus is that every time
|
||||
* it gets gets a key event, it:
|
||||
*
|
||||
@ -957,8 +955,6 @@ _shell_global_set_plugin (ShellGlobal *global,
|
||||
g_signal_connect (global->meta_display, "notify::focus-window",
|
||||
G_CALLBACK (focus_window_changed), global);
|
||||
|
||||
shell_fonts_init (global->stage);
|
||||
|
||||
gdk_event_handler_set (gnome_shell_gdk_event_handler, global->stage, NULL);
|
||||
|
||||
global->focus_manager = st_focus_manager_get_for_stage (global->stage);
|
||||
@ -1404,7 +1400,6 @@ guint32
|
||||
shell_global_get_current_time (ShellGlobal *global)
|
||||
{
|
||||
guint32 time;
|
||||
const ClutterEvent *clutter_event;
|
||||
|
||||
/* In case we have a xdnd timestamp use it */
|
||||
if (global->xdnd_timestamp != 0)
|
||||
@ -1415,7 +1410,7 @@ shell_global_get_current_time (ShellGlobal *global)
|
||||
from some Clutter event callbacks.
|
||||
|
||||
clutter_get_current_event_time() will return the correct time
|
||||
from a Clutter event callback, but may return an out-of-date
|
||||
from a Clutter event callback, but may return CLUTTER_CURRENT_TIME
|
||||
timestamp if called at other times.
|
||||
|
||||
So we try meta_display_get_current_time() first, since we
|
||||
@ -1426,16 +1421,8 @@ shell_global_get_current_time (ShellGlobal *global)
|
||||
time = meta_display_get_current_time (global->meta_display);
|
||||
if (time != CLUTTER_CURRENT_TIME)
|
||||
return time;
|
||||
/*
|
||||
* We don't use clutter_get_current_event_time as it can give us a
|
||||
* too old timestamp if there is no current event.
|
||||
*/
|
||||
clutter_event = clutter_get_current_event ();
|
||||
|
||||
if (clutter_event != NULL)
|
||||
return clutter_event_get_time (clutter_event);
|
||||
else
|
||||
return CLUTTER_CURRENT_TIME;
|
||||
return clutter_get_current_event_time ();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1597,11 +1584,46 @@ shell_global_run_at_leisure (ShellGlobal *global,
|
||||
schedule_leisure_functions (global);
|
||||
}
|
||||
|
||||
static void
|
||||
build_ca_proplist_for_event (ca_proplist *props,
|
||||
const char *event_id,
|
||||
const char *event_description,
|
||||
ClutterEvent *for_event)
|
||||
{
|
||||
ca_proplist_sets (props, CA_PROP_EVENT_ID, event_id);
|
||||
ca_proplist_sets (props, CA_PROP_EVENT_DESCRIPTION, event_description);
|
||||
ca_proplist_sets (props, CA_PROP_CANBERRA_CACHE_CONTROL, "volatile");
|
||||
|
||||
if (for_event)
|
||||
{
|
||||
if (clutter_event_type (for_event) != CLUTTER_KEY_PRESS &&
|
||||
clutter_event_type (for_event) != CLUTTER_KEY_RELEASE)
|
||||
{
|
||||
ClutterPoint point;
|
||||
|
||||
clutter_event_get_position (for_event, &point);
|
||||
|
||||
ca_proplist_setf (props, CA_PROP_EVENT_MOUSE_X, "%d", (int)point.x);
|
||||
ca_proplist_setf (props, CA_PROP_EVENT_MOUSE_Y, "%d", (int)point.y);
|
||||
}
|
||||
|
||||
if (clutter_event_type (for_event) == CLUTTER_BUTTON_PRESS ||
|
||||
clutter_event_type (for_event) == CLUTTER_BUTTON_RELEASE)
|
||||
{
|
||||
gint button;
|
||||
|
||||
button = clutter_event_get_button (for_event);
|
||||
ca_proplist_setf (props, CA_PROP_EVENT_MOUSE_BUTTON, "%d", button);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_global_play_theme_sound:
|
||||
* @global: the #ShellGlobal
|
||||
* @id: an id, used to cancel later (0 if not needed)
|
||||
* @name: the sound name
|
||||
* @for_event: (allow-none): a #ClutterEvent in response to which the sound is played
|
||||
*
|
||||
* Plays a simple sound picked according to Freedesktop sound theme.
|
||||
* Really just a workaround for libcanberra not being introspected.
|
||||
@ -1609,9 +1631,52 @@ shell_global_run_at_leisure (ShellGlobal *global,
|
||||
void
|
||||
shell_global_play_theme_sound (ShellGlobal *global,
|
||||
guint id,
|
||||
const char *name)
|
||||
const char *name,
|
||||
const char *description,
|
||||
ClutterEvent *for_event)
|
||||
{
|
||||
ca_context_play (global->sound_context, id, CA_PROP_EVENT_ID, name, NULL);
|
||||
ca_proplist *props;
|
||||
|
||||
ca_proplist_create (&props);
|
||||
build_ca_proplist_for_event (props, name, description, for_event);
|
||||
|
||||
ca_context_play_full (global->sound_context, id, props, NULL, NULL);
|
||||
|
||||
ca_proplist_destroy (props);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_global_play_theme_sound_full:
|
||||
* @global: the #ShellGlobal
|
||||
* @id: an id, used to cancel later (0 if not needed)
|
||||
* @name: the sound name
|
||||
* @description: the localized description of the event that triggered this alert
|
||||
* @for_event: (allow-none): a #ClutterEvent in response to which the sound is played
|
||||
* @application_id: application on behalf of which the sound is played
|
||||
* @application_name:
|
||||
*
|
||||
* Plays a simple sound picked according to Freedesktop sound theme.
|
||||
* Really just a workaround for libcanberra not being introspected.
|
||||
*/
|
||||
void
|
||||
shell_global_play_theme_sound_full (ShellGlobal *global,
|
||||
guint id,
|
||||
const char *name,
|
||||
const char *description,
|
||||
ClutterEvent *for_event,
|
||||
const char *application_id,
|
||||
const char *application_name)
|
||||
{
|
||||
ca_proplist *props;
|
||||
|
||||
ca_proplist_create (&props);
|
||||
build_ca_proplist_for_event (props, name, description, for_event);
|
||||
ca_proplist_sets (props, CA_PROP_APPLICATION_ID, application_id);
|
||||
ca_proplist_sets (props, CA_PROP_APPLICATION_NAME, application_name);
|
||||
|
||||
ca_context_play_full (global->sound_context, id, props, NULL, NULL);
|
||||
|
||||
ca_proplist_destroy (props);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -63,7 +63,8 @@ typedef enum {
|
||||
SHELL_CURSOR_DND_UNSUPPORTED_TARGET,
|
||||
SHELL_CURSOR_DND_MOVE,
|
||||
SHELL_CURSOR_DND_COPY,
|
||||
SHELL_CURSOR_POINTING_HAND
|
||||
SHELL_CURSOR_POINTING_HAND,
|
||||
SHELL_CURSOR_CROSSHAIR
|
||||
} ShellCursor;
|
||||
|
||||
void shell_global_set_cursor (ShellGlobal *global,
|
||||
@ -122,7 +123,16 @@ GAppLaunchContext *
|
||||
|
||||
void shell_global_play_theme_sound (ShellGlobal *global,
|
||||
guint id,
|
||||
const char *name);
|
||||
const char *name,
|
||||
const char *description,
|
||||
ClutterEvent *for_event);
|
||||
void shell_global_play_theme_sound_full (ShellGlobal *global,
|
||||
guint id,
|
||||
const char *name,
|
||||
const char *description,
|
||||
ClutterEvent *for_event,
|
||||
const char *application_id,
|
||||
const char *application_name);
|
||||
void shell_global_cancel_theme_sound (ShellGlobal *global,
|
||||
guint id);
|
||||
|
||||
|
35
src/shell-keybinding-modes.h
Normal file
35
src/shell-keybinding-modes.h
Normal file
@ -0,0 +1,35 @@
|
||||
/**
|
||||
* ShellKeyBindingMode:
|
||||
* @SHELL_KEYBINDING_MODE_NONE: block keybinding
|
||||
* @SHELL_KEYBINDING_MODE_NORMAL: allow keybinding when in window mode,
|
||||
* e.g. when the focus is in an application window
|
||||
* @SHELL_KEYBINDING_MODE_OVERVIEW: allow keybinding while the overview
|
||||
* is active
|
||||
* @SHELL_KEYBINDING_MODE_LOCK_SCREEN: allow keybinding when the screen
|
||||
* is locked, e.g. when the screen shield is shown
|
||||
* @SHELL_KEYBINDING_MODE_UNLOCK_SCREEN: allow keybinding in the unlock
|
||||
* dialog
|
||||
* @SHELL_KEYBINDING_MODE_LOGIN_SCREEN: allow keybinding in the login screen
|
||||
* @SHELL_KEYBINDING_MODE_MESSAGE_TRAY: allow keybinding while the message
|
||||
* tray is popped up
|
||||
* @SHELL_KEYBINDING_MODE_SYSTEM_MODAL: allow keybinding when a system modal
|
||||
* dialog (e.g. authentification or session dialogs) is open
|
||||
* @SHELL_KEYBINDING_MODE_LOOKING_GLASS: allow keybinding in looking glass
|
||||
* @SHELL_KEYBINDING_MODE_ALL: always allow keybinding
|
||||
*
|
||||
* Controls in which GNOME Shell states a keybinding should be handled.
|
||||
*/
|
||||
typedef enum {
|
||||
SHELL_KEYBINDING_MODE_NONE = 0,
|
||||
SHELL_KEYBINDING_MODE_NORMAL = 1 << 0,
|
||||
SHELL_KEYBINDING_MODE_OVERVIEW = 1 << 1,
|
||||
SHELL_KEYBINDING_MODE_LOCK_SCREEN = 1 << 2,
|
||||
SHELL_KEYBINDING_MODE_UNLOCK_SCREEN = 1 << 3,
|
||||
SHELL_KEYBINDING_MODE_LOGIN_SCREEN = 1 << 4,
|
||||
SHELL_KEYBINDING_MODE_MESSAGE_TRAY = 1 << 5,
|
||||
SHELL_KEYBINDING_MODE_SYSTEM_MODAL = 1 << 6,
|
||||
SHELL_KEYBINDING_MODE_LOOKING_GLASS = 1 << 7,
|
||||
|
||||
SHELL_KEYBINDING_MODE_ALL = ~SHELL_KEYBINDING_MODE_NONE
|
||||
} ShellKeyBindingMode;
|
||||
|
@ -1,914 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright (C) 2009 Novell, Inc.
|
||||
* Author: Tambet Ingo (tambet@gmail.com).
|
||||
*
|
||||
* Copyright (C) 2009 - 2010 Red Hat, Inc.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
#include "shell-mobile-providers.h"
|
||||
|
||||
#ifndef MOBILE_BROADBAND_PROVIDER_INFO
|
||||
#define MOBILE_BROADBAND_PROVIDER_INFO DATADIR "/mobile-broadband-provider-info/serviceproviders.xml"
|
||||
#endif
|
||||
|
||||
#define ISO_3166_COUNTRY_CODES DATADIR "/zoneinfo/iso3166.tab"
|
||||
|
||||
static ShellCountryMobileProvider *country_mobile_provider_new (const char *country_code,
|
||||
const gchar *country_name);
|
||||
|
||||
static GHashTable *
|
||||
read_country_codes (const gchar *country_codes)
|
||||
{
|
||||
GHashTable *table;
|
||||
GIOChannel *channel;
|
||||
GString *buffer;
|
||||
GError *error = NULL;
|
||||
GIOStatus status;
|
||||
|
||||
channel = g_io_channel_new_file (country_codes, "r", &error);
|
||||
if (!channel) {
|
||||
if (error) {
|
||||
g_warning ("Could not read %s: %s", country_codes, error->message);
|
||||
g_error_free (error);
|
||||
} else
|
||||
g_warning ("Could not read %s: Unknown error", country_codes);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
table = g_hash_table_new_full (g_str_hash,
|
||||
g_str_equal,
|
||||
g_free,
|
||||
(GDestroyNotify)shell_country_mobile_provider_unref);
|
||||
buffer = g_string_sized_new (32);
|
||||
|
||||
status = G_IO_STATUS_NORMAL;
|
||||
while (status == G_IO_STATUS_NORMAL) {
|
||||
status = g_io_channel_read_line_string (channel, buffer, NULL, &error);
|
||||
|
||||
switch (status) {
|
||||
case G_IO_STATUS_NORMAL:
|
||||
if (buffer->str[0] != '#') {
|
||||
ShellCountryMobileProvider *country_provider;
|
||||
char **pieces;
|
||||
|
||||
pieces = g_strsplit (buffer->str, "\t", 2);
|
||||
|
||||
/* Hack for rh#556292; iso3166.tab is just wrong */
|
||||
pieces[1] = pieces[1] ? g_strchomp (pieces[1]) : NULL;
|
||||
if (pieces[1] && !strcmp (pieces[1], "Britain (UK)")) {
|
||||
g_free (pieces[1]);
|
||||
pieces[1] = g_strdup (_("United Kingdom"));
|
||||
}
|
||||
|
||||
country_provider = country_mobile_provider_new (pieces[0], pieces[1]);
|
||||
g_hash_table_insert (table, pieces[0], country_provider);
|
||||
g_free (pieces);
|
||||
}
|
||||
|
||||
g_string_truncate (buffer, 0);
|
||||
break;
|
||||
case G_IO_STATUS_EOF:
|
||||
break;
|
||||
case G_IO_STATUS_ERROR:
|
||||
g_warning ("Error while reading: %s", error->message);
|
||||
g_error_free (error);
|
||||
break;
|
||||
case G_IO_STATUS_AGAIN:
|
||||
/* FIXME: Try again a few times, but really, it never happes, right? */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_string_free (buffer, TRUE);
|
||||
g_io_channel_unref (channel);
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
/* XML Parser */
|
||||
|
||||
typedef enum {
|
||||
PARSER_TOPLEVEL = 0,
|
||||
PARSER_COUNTRY,
|
||||
PARSER_PROVIDER,
|
||||
PARSER_METHOD_GSM,
|
||||
PARSER_METHOD_GSM_APN,
|
||||
PARSER_METHOD_CDMA,
|
||||
PARSER_ERROR
|
||||
} MobileContextState;
|
||||
|
||||
typedef struct {
|
||||
GHashTable *table;
|
||||
|
||||
char *current_country;
|
||||
GSList *current_providers;
|
||||
ShellMobileProvider *current_provider;
|
||||
ShellMobileAccessMethod *current_method;
|
||||
|
||||
char *text_buffer;
|
||||
MobileContextState state;
|
||||
} MobileParser;
|
||||
|
||||
static ShellGsmMccMnc *
|
||||
mcc_mnc_new (const char *mcc, const char *mnc)
|
||||
{
|
||||
ShellGsmMccMnc *m;
|
||||
|
||||
m = g_slice_new (ShellGsmMccMnc);
|
||||
m->mcc = g_strstrip (g_strdup (mcc));
|
||||
m->mnc = g_strstrip (g_strdup (mnc));
|
||||
return m;
|
||||
}
|
||||
|
||||
/* added in porting */
|
||||
static ShellGsmMccMnc *
|
||||
mcc_mnc_copy (const ShellGsmMccMnc *other) {
|
||||
ShellGsmMccMnc *ret;
|
||||
|
||||
ret = g_slice_new (ShellGsmMccMnc);
|
||||
ret->mcc = g_strdup (other->mcc);
|
||||
ret->mnc = g_strdup (other->mnc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
mcc_mnc_free (ShellGsmMccMnc *m)
|
||||
{
|
||||
g_return_if_fail (m != NULL);
|
||||
g_free (m->mcc);
|
||||
g_free (m->mnc);
|
||||
g_slice_free (ShellGsmMccMnc, m);
|
||||
}
|
||||
|
||||
/* added in porting */
|
||||
G_DEFINE_BOXED_TYPE (ShellGsmMccMnc, shell_gsm_mcc_mnc, mcc_mnc_copy, mcc_mnc_free)
|
||||
|
||||
static ShellMobileAccessMethod *
|
||||
access_method_new (void)
|
||||
{
|
||||
ShellMobileAccessMethod *method;
|
||||
|
||||
method = g_slice_new0 (ShellMobileAccessMethod);
|
||||
method->refs = 1;
|
||||
method->lcl_names = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
(GDestroyNotify) g_free,
|
||||
(GDestroyNotify) g_free);
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
ShellMobileAccessMethod *
|
||||
shell_mobile_access_method_ref (ShellMobileAccessMethod *method)
|
||||
{
|
||||
g_return_val_if_fail (method != NULL, NULL);
|
||||
g_return_val_if_fail (method->refs > 0, NULL);
|
||||
|
||||
method->refs++;
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
void
|
||||
shell_mobile_access_method_unref (ShellMobileAccessMethod *method)
|
||||
{
|
||||
g_return_if_fail (method != NULL);
|
||||
g_return_if_fail (method->refs > 0);
|
||||
|
||||
if (--method->refs == 0) {
|
||||
g_free (method->name);
|
||||
g_hash_table_destroy (method->lcl_names);
|
||||
g_free (method->username);
|
||||
g_free (method->password);
|
||||
g_free (method->gateway);
|
||||
g_free (method->gsm_apn);
|
||||
g_slist_foreach (method->dns, (GFunc) g_free, NULL);
|
||||
g_slist_free (method->dns);
|
||||
|
||||
g_slice_free (ShellMobileAccessMethod, method);
|
||||
}
|
||||
}
|
||||
|
||||
GType
|
||||
shell_mobile_access_method_get_type (void)
|
||||
{
|
||||
static GType type = 0;
|
||||
|
||||
if (G_UNLIKELY (type == 0)) {
|
||||
type = g_boxed_type_register_static ("ShellMobileAccessMethod",
|
||||
(GBoxedCopyFunc) shell_mobile_access_method_ref,
|
||||
(GBoxedFreeFunc) shell_mobile_access_method_unref);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
static ShellMobileProvider *
|
||||
provider_new (void)
|
||||
{
|
||||
ShellMobileProvider *provider;
|
||||
|
||||
provider = g_slice_new0 (ShellMobileProvider);
|
||||
provider->refs = 1;
|
||||
provider->lcl_names = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
(GDestroyNotify) g_free,
|
||||
(GDestroyNotify) g_free);
|
||||
|
||||
return provider;
|
||||
}
|
||||
|
||||
ShellMobileProvider *
|
||||
shell_mobile_provider_ref (ShellMobileProvider *provider)
|
||||
{
|
||||
provider->refs++;
|
||||
|
||||
return provider;
|
||||
}
|
||||
|
||||
void
|
||||
shell_mobile_provider_unref (ShellMobileProvider *provider)
|
||||
{
|
||||
if (--provider->refs == 0) {
|
||||
g_free (provider->name);
|
||||
g_hash_table_destroy (provider->lcl_names);
|
||||
|
||||
g_slist_foreach (provider->methods, (GFunc) shell_mobile_access_method_unref, NULL);
|
||||
g_slist_free (provider->methods);
|
||||
|
||||
g_slist_foreach (provider->gsm_mcc_mnc, (GFunc) mcc_mnc_free, NULL);
|
||||
g_slist_free (provider->gsm_mcc_mnc);
|
||||
|
||||
g_slist_free (provider->cdma_sid);
|
||||
|
||||
g_slice_free (ShellMobileProvider, provider);
|
||||
}
|
||||
}
|
||||
|
||||
GType
|
||||
shell_mobile_provider_get_type (void)
|
||||
{
|
||||
static GType type = 0;
|
||||
|
||||
if (G_UNLIKELY (type == 0)) {
|
||||
type = g_boxed_type_register_static ("ShellMobileProvider",
|
||||
(GBoxedCopyFunc) shell_mobile_provider_ref,
|
||||
(GBoxedFreeFunc) shell_mobile_provider_unref);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
static ShellCountryMobileProvider *
|
||||
country_mobile_provider_new (const char *country_code,
|
||||
const gchar *country_name)
|
||||
{
|
||||
ShellCountryMobileProvider *country_provider;
|
||||
|
||||
country_provider = g_slice_new0 (ShellCountryMobileProvider);
|
||||
country_provider->refs = 1;
|
||||
country_provider->country_code = g_strdup (country_code);
|
||||
country_provider->country_name = g_strdup (country_name);
|
||||
return country_provider;
|
||||
}
|
||||
|
||||
ShellCountryMobileProvider *
|
||||
shell_country_mobile_provider_ref (ShellCountryMobileProvider *country_provider)
|
||||
{
|
||||
country_provider->refs++;
|
||||
|
||||
return country_provider;
|
||||
}
|
||||
|
||||
void
|
||||
shell_country_mobile_provider_unref (ShellCountryMobileProvider *country_provider)
|
||||
{
|
||||
if (--country_provider->refs == 0) {
|
||||
g_free (country_provider->country_code);
|
||||
g_free (country_provider->country_name);
|
||||
g_slist_free_full (country_provider->providers,
|
||||
(GDestroyNotify) shell_mobile_provider_unref);
|
||||
g_slice_free (ShellCountryMobileProvider, country_provider);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_country_mobile_provider_get_country_code:
|
||||
*
|
||||
* Returns: (transfer none): the code of the country.
|
||||
*/
|
||||
const gchar *
|
||||
shell_country_mobile_provider_get_country_code (ShellCountryMobileProvider *country_provider)
|
||||
{
|
||||
return country_provider->country_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_country_mobile_provider_get_country_name:
|
||||
*
|
||||
* Returns: (transfer none): the name of the country.
|
||||
*/
|
||||
const gchar *
|
||||
shell_country_mobile_provider_get_country_name (ShellCountryMobileProvider *country_provider)
|
||||
{
|
||||
return country_provider->country_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_country_mobile_provider_get_providers:
|
||||
*
|
||||
* Returns: (element-type Shell.MobileProvider) (transfer none): the
|
||||
* list of #ShellMobileProvider this country exposes.
|
||||
*/
|
||||
GSList *
|
||||
shell_country_mobile_provider_get_providers (ShellCountryMobileProvider *country_provider)
|
||||
{
|
||||
return country_provider->providers;
|
||||
}
|
||||
|
||||
GType
|
||||
shell_country_mobile_provider_get_type (void)
|
||||
{
|
||||
static GType type = 0;
|
||||
|
||||
if (G_UNLIKELY (type == 0)) {
|
||||
type = g_boxed_type_register_static ("ShellCountryMobileProvider",
|
||||
(GBoxedCopyFunc) shell_country_mobile_provider_ref,
|
||||
(GBoxedFreeFunc) shell_country_mobile_provider_unref);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
static void
|
||||
provider_list_free (gpointer data)
|
||||
{
|
||||
GSList *list = (GSList *) data;
|
||||
|
||||
while (list) {
|
||||
shell_mobile_provider_unref ((ShellMobileProvider *) list->data);
|
||||
list = g_slist_delete_link (list, list);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
parser_toplevel_start (MobileParser *parser,
|
||||
const char *name,
|
||||
const char **attribute_names,
|
||||
const char **attribute_values)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!strcmp (name, "serviceproviders")) {
|
||||
for (i = 0; attribute_names && attribute_names[i]; i++) {
|
||||
if (!strcmp (attribute_names[i], "format")) {
|
||||
if (strcmp (attribute_values[i], "2.0")) {
|
||||
g_warning ("%s: mobile broadband provider database format '%s'"
|
||||
" not supported.", __func__, attribute_values[i]);
|
||||
parser->state = PARSER_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!strcmp (name, "country")) {
|
||||
for (i = 0; attribute_names && attribute_names[i]; i++) {
|
||||
if (!strcmp (attribute_names[i], "code")) {
|
||||
char *country_code;
|
||||
ShellCountryMobileProvider *country_provider;
|
||||
|
||||
country_code = g_ascii_strup (attribute_values[i], -1);
|
||||
country_provider = g_hash_table_lookup (parser->table, country_code);
|
||||
/* Ensure we have a country provider for this country code */
|
||||
if (!country_provider) {
|
||||
g_warning ("%s: adding providers for unknown country '%s'", __func__, country_code);
|
||||
country_provider = country_mobile_provider_new (country_code, NULL);
|
||||
g_hash_table_insert (parser->table, country_code, country_provider);
|
||||
}
|
||||
parser->current_country = country_code;
|
||||
|
||||
parser->state = PARSER_COUNTRY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
parser_country_start (MobileParser *parser,
|
||||
const char *name,
|
||||
const char **attribute_names,
|
||||
const char **attribute_values)
|
||||
{
|
||||
if (!strcmp (name, "provider")) {
|
||||
parser->state = PARSER_PROVIDER;
|
||||
parser->current_provider = provider_new ();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
parser_provider_start (MobileParser *parser,
|
||||
const char *name,
|
||||
const char **attribute_names,
|
||||
const char **attribute_values)
|
||||
{
|
||||
if (!strcmp (name, "gsm"))
|
||||
parser->state = PARSER_METHOD_GSM;
|
||||
else if (!strcmp (name, "cdma")) {
|
||||
parser->state = PARSER_METHOD_CDMA;
|
||||
parser->current_method = access_method_new ();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
parser_gsm_start (MobileParser *parser,
|
||||
const char *name,
|
||||
const char **attribute_names,
|
||||
const char **attribute_values)
|
||||
{
|
||||
if (!strcmp (name, "network-id")) {
|
||||
const char *mcc = NULL, *mnc = NULL;
|
||||
int i;
|
||||
|
||||
for (i = 0; attribute_names && attribute_names[i]; i++) {
|
||||
if (!strcmp (attribute_names[i], "mcc"))
|
||||
mcc = attribute_values[i];
|
||||
else if (!strcmp (attribute_names[i], "mnc"))
|
||||
mnc = attribute_values[i];
|
||||
|
||||
if (mcc && strlen (mcc) && mnc && strlen (mnc)) {
|
||||
parser->current_provider->gsm_mcc_mnc = g_slist_prepend (parser->current_provider->gsm_mcc_mnc,
|
||||
mcc_mnc_new (mcc, mnc));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (!strcmp (name, "apn")) {
|
||||
int i;
|
||||
|
||||
for (i = 0; attribute_names && attribute_names[i]; i++) {
|
||||
if (!strcmp (attribute_names[i], "value")) {
|
||||
|
||||
parser->state = PARSER_METHOD_GSM_APN;
|
||||
parser->current_method = access_method_new ();
|
||||
parser->current_method->gsm_apn = g_strstrip (g_strdup (attribute_values[i]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
parser_cdma_start (MobileParser *parser,
|
||||
const char *name,
|
||||
const char **attribute_names,
|
||||
const char **attribute_values)
|
||||
{
|
||||
if (!strcmp (name, "sid")) {
|
||||
int i;
|
||||
|
||||
for (i = 0; attribute_names && attribute_names[i]; i++) {
|
||||
if (!strcmp (attribute_names[i], "value")) {
|
||||
unsigned long tmp;
|
||||
|
||||
errno = 0;
|
||||
tmp = strtoul (attribute_values[i], NULL, 10);
|
||||
if (errno == 0 && tmp > 0)
|
||||
parser->current_provider->cdma_sid = g_slist_prepend (parser->current_provider->cdma_sid,
|
||||
GUINT_TO_POINTER ((guint32) tmp));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mobile_parser_start_element (GMarkupParseContext *context,
|
||||
const gchar *element_name,
|
||||
const gchar **attribute_names,
|
||||
const gchar **attribute_values,
|
||||
gpointer data,
|
||||
GError **error)
|
||||
{
|
||||
MobileParser *parser = (MobileParser *) data;
|
||||
|
||||
if (parser->text_buffer) {
|
||||
g_free (parser->text_buffer);
|
||||
parser->text_buffer = NULL;
|
||||
}
|
||||
|
||||
switch (parser->state) {
|
||||
case PARSER_TOPLEVEL:
|
||||
parser_toplevel_start (parser, element_name, attribute_names, attribute_values);
|
||||
break;
|
||||
case PARSER_COUNTRY:
|
||||
parser_country_start (parser, element_name, attribute_names, attribute_values);
|
||||
break;
|
||||
case PARSER_PROVIDER:
|
||||
parser_provider_start (parser, element_name, attribute_names, attribute_values);
|
||||
break;
|
||||
case PARSER_METHOD_GSM:
|
||||
parser_gsm_start (parser, element_name, attribute_names, attribute_values);
|
||||
break;
|
||||
case PARSER_METHOD_CDMA:
|
||||
parser_cdma_start (parser, element_name, attribute_names, attribute_values);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
parser_country_end (MobileParser *parser,
|
||||
const char *name)
|
||||
{
|
||||
if (!strcmp (name, "country")) {
|
||||
ShellCountryMobileProvider *country_provider;
|
||||
|
||||
country_provider = g_hash_table_lookup (parser->table, parser->current_country);
|
||||
if (country_provider)
|
||||
/* Store providers for this country */
|
||||
country_provider->providers = parser->current_providers;
|
||||
|
||||
parser->current_country = NULL;
|
||||
parser->current_providers = NULL;
|
||||
parser->text_buffer = NULL;
|
||||
parser->state = PARSER_TOPLEVEL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
parser_provider_end (MobileParser *parser,
|
||||
const char *name)
|
||||
{
|
||||
if (!strcmp (name, "name")) {
|
||||
if (!parser->current_provider->name) {
|
||||
/* Use the first one. */
|
||||
parser->current_provider->name = parser->text_buffer;
|
||||
parser->text_buffer = NULL;
|
||||
}
|
||||
} else if (!strcmp (name, "provider")) {
|
||||
parser->current_provider->methods = g_slist_reverse (parser->current_provider->methods);
|
||||
|
||||
parser->current_provider->gsm_mcc_mnc = g_slist_reverse (parser->current_provider->gsm_mcc_mnc);
|
||||
parser->current_provider->cdma_sid = g_slist_reverse (parser->current_provider->cdma_sid);
|
||||
|
||||
parser->current_providers = g_slist_prepend (parser->current_providers, parser->current_provider);
|
||||
parser->current_provider = NULL;
|
||||
parser->text_buffer = NULL;
|
||||
parser->state = PARSER_COUNTRY;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
parser_gsm_end (MobileParser *parser,
|
||||
const char *name)
|
||||
{
|
||||
if (!strcmp (name, "gsm")) {
|
||||
parser->text_buffer = NULL;
|
||||
parser->state = PARSER_PROVIDER;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
parser_gsm_apn_end (MobileParser *parser,
|
||||
const char *name)
|
||||
{
|
||||
if (!strcmp (name, "name")) {
|
||||
if (!parser->current_method->name) {
|
||||
/* Use the first one. */
|
||||
parser->current_method->name = parser->text_buffer;
|
||||
parser->text_buffer = NULL;
|
||||
}
|
||||
} else if (!strcmp (name, "username")) {
|
||||
parser->current_method->username = parser->text_buffer;
|
||||
parser->text_buffer = NULL;
|
||||
} else if (!strcmp (name, "password")) {
|
||||
parser->current_method->password = parser->text_buffer;
|
||||
parser->text_buffer = NULL;
|
||||
} else if (!strcmp (name, "dns")) {
|
||||
parser->current_method->dns = g_slist_prepend (parser->current_method->dns, parser->text_buffer);
|
||||
parser->text_buffer = NULL;
|
||||
} else if (!strcmp (name, "gateway")) {
|
||||
parser->current_method->gateway = parser->text_buffer;
|
||||
parser->text_buffer = NULL;
|
||||
} else if (!strcmp (name, "apn")) {
|
||||
parser->current_method->type = SHELL_MOBILE_ACCESS_METHOD_TYPE_GSM;
|
||||
parser->current_method->dns = g_slist_reverse (parser->current_method->dns);
|
||||
|
||||
if (!parser->current_method->name)
|
||||
parser->current_method->name = g_strdup (_("Default"));
|
||||
|
||||
parser->current_provider->methods = g_slist_prepend (parser->current_provider->methods,
|
||||
parser->current_method);
|
||||
parser->current_method = NULL;
|
||||
parser->text_buffer = NULL;
|
||||
parser->state = PARSER_METHOD_GSM;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
parser_cdma_end (MobileParser *parser,
|
||||
const char *name)
|
||||
{
|
||||
if (!strcmp (name, "username")) {
|
||||
parser->current_method->username = parser->text_buffer;
|
||||
parser->text_buffer = NULL;
|
||||
} else if (!strcmp (name, "password")) {
|
||||
parser->current_method->password = parser->text_buffer;
|
||||
parser->text_buffer = NULL;
|
||||
} else if (!strcmp (name, "dns")) {
|
||||
parser->current_method->dns = g_slist_prepend (parser->current_method->dns, parser->text_buffer);
|
||||
parser->text_buffer = NULL;
|
||||
} else if (!strcmp (name, "gateway")) {
|
||||
parser->current_method->gateway = parser->text_buffer;
|
||||
parser->text_buffer = NULL;
|
||||
} else if (!strcmp (name, "cdma")) {
|
||||
parser->current_method->type = SHELL_MOBILE_ACCESS_METHOD_TYPE_CDMA;
|
||||
parser->current_method->dns = g_slist_reverse (parser->current_method->dns);
|
||||
|
||||
if (!parser->current_method->name)
|
||||
parser->current_method->name = g_strdup (parser->current_provider->name);
|
||||
|
||||
parser->current_provider->methods = g_slist_prepend (parser->current_provider->methods,
|
||||
parser->current_method);
|
||||
parser->current_method = NULL;
|
||||
parser->text_buffer = NULL;
|
||||
parser->state = PARSER_PROVIDER;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mobile_parser_end_element (GMarkupParseContext *context,
|
||||
const gchar *element_name,
|
||||
gpointer data,
|
||||
GError **error)
|
||||
{
|
||||
MobileParser *parser = (MobileParser *) data;
|
||||
|
||||
switch (parser->state) {
|
||||
case PARSER_COUNTRY:
|
||||
parser_country_end (parser, element_name);
|
||||
break;
|
||||
case PARSER_PROVIDER:
|
||||
parser_provider_end (parser, element_name);
|
||||
break;
|
||||
case PARSER_METHOD_GSM:
|
||||
parser_gsm_end (parser, element_name);
|
||||
break;
|
||||
case PARSER_METHOD_GSM_APN:
|
||||
parser_gsm_apn_end (parser, element_name);
|
||||
break;
|
||||
case PARSER_METHOD_CDMA:
|
||||
parser_cdma_end (parser, element_name);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mobile_parser_characters (GMarkupParseContext *context,
|
||||
const gchar *text,
|
||||
gsize text_len,
|
||||
gpointer data,
|
||||
GError **error)
|
||||
{
|
||||
MobileParser *parser = (MobileParser *) data;
|
||||
|
||||
g_free (parser->text_buffer);
|
||||
parser->text_buffer = g_strdup (text);
|
||||
}
|
||||
|
||||
static const GMarkupParser mobile_parser = {
|
||||
mobile_parser_start_element,
|
||||
mobile_parser_end_element,
|
||||
mobile_parser_characters,
|
||||
NULL, /* passthrough */
|
||||
NULL /* error */
|
||||
};
|
||||
|
||||
/**
|
||||
* shell_mobile_providers_parse:
|
||||
* @country_codes: (allow-none) File with the list of country codes.
|
||||
* @service_providers: (allow-none) File with the list of service providers.
|
||||
*
|
||||
* Returns: (element-type utf8 Shell.CountryMobileProvider) (transfer full): a
|
||||
* hash table where keys are country names #gchar and values are #ShellCountryMobileProvider.
|
||||
* Everything is destroyed with g_hash_table_destroy().
|
||||
*/
|
||||
GHashTable *
|
||||
shell_mobile_providers_parse (const gchar *country_codes,
|
||||
const gchar *service_providers)
|
||||
{
|
||||
GMarkupParseContext *ctx;
|
||||
GIOChannel *channel;
|
||||
MobileParser parser;
|
||||
GError *error = NULL;
|
||||
char buffer[4096];
|
||||
GIOStatus status;
|
||||
gsize len = 0;
|
||||
|
||||
/* Use default paths if none given */
|
||||
if (!country_codes)
|
||||
country_codes = ISO_3166_COUNTRY_CODES;
|
||||
if (!service_providers)
|
||||
service_providers = MOBILE_BROADBAND_PROVIDER_INFO;
|
||||
|
||||
memset (&parser, 0, sizeof (MobileParser));
|
||||
|
||||
parser.table = read_country_codes (country_codes);
|
||||
if (!parser.table)
|
||||
goto out;
|
||||
|
||||
channel = g_io_channel_new_file (service_providers, "r", &error);
|
||||
if (!channel) {
|
||||
if (error) {
|
||||
g_warning ("Could not read %s: %s", service_providers, error->message);
|
||||
g_error_free (error);
|
||||
} else
|
||||
g_warning ("Could not read %s: Unknown error", service_providers);
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
parser.state = PARSER_TOPLEVEL;
|
||||
|
||||
ctx = g_markup_parse_context_new (&mobile_parser, 0, &parser, NULL);
|
||||
|
||||
status = G_IO_STATUS_NORMAL;
|
||||
while (status == G_IO_STATUS_NORMAL) {
|
||||
status = g_io_channel_read_chars (channel, buffer, sizeof (buffer), &len, &error);
|
||||
|
||||
switch (status) {
|
||||
case G_IO_STATUS_NORMAL:
|
||||
if (!g_markup_parse_context_parse (ctx, buffer, len, &error)) {
|
||||
status = G_IO_STATUS_ERROR;
|
||||
g_warning ("Error while parsing XML: %s", error->message);
|
||||
g_error_free (error);;
|
||||
}
|
||||
break;
|
||||
case G_IO_STATUS_EOF:
|
||||
break;
|
||||
case G_IO_STATUS_ERROR:
|
||||
g_warning ("Error while reading: %s", error->message);
|
||||
g_error_free (error);
|
||||
break;
|
||||
case G_IO_STATUS_AGAIN:
|
||||
/* FIXME: Try again a few times, but really, it never happes, right? */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_io_channel_unref (channel);
|
||||
g_markup_parse_context_free (ctx);
|
||||
|
||||
if (parser.current_provider) {
|
||||
g_warning ("pending current provider");
|
||||
shell_mobile_provider_unref (parser.current_provider);
|
||||
}
|
||||
|
||||
if (parser.current_providers) {
|
||||
g_warning ("pending current providers");
|
||||
provider_list_free (parser.current_providers);
|
||||
}
|
||||
|
||||
g_free (parser.current_country);
|
||||
g_free (parser.text_buffer);
|
||||
|
||||
out:
|
||||
|
||||
return parser.table;
|
||||
}
|
||||
|
||||
static void
|
||||
dump_generic (ShellMobileAccessMethod *method)
|
||||
{
|
||||
GSList *iter;
|
||||
GString *dns;
|
||||
|
||||
g_print (" username: %s\n", method->username ? method->username : "");
|
||||
g_print (" password: %s\n", method->password ? method->password : "");
|
||||
|
||||
dns = g_string_new (NULL);
|
||||
for (iter = method->dns; iter; iter = g_slist_next (iter))
|
||||
g_string_append_printf (dns, "%s%s", dns->len ? ", " : "", (char *) iter->data);
|
||||
g_print (" dns : %s\n", dns->str);
|
||||
g_string_free (dns, TRUE);
|
||||
|
||||
g_print (" gateway : %s\n", method->gateway ? method->gateway : "");
|
||||
}
|
||||
|
||||
static void
|
||||
dump_cdma (ShellMobileAccessMethod *method)
|
||||
{
|
||||
g_print (" CDMA: %s\n", method->name);
|
||||
|
||||
dump_generic (method);
|
||||
}
|
||||
|
||||
static void
|
||||
dump_gsm (ShellMobileAccessMethod *method)
|
||||
{
|
||||
g_print (" APN: %s (%s)\n", method->name, method->gsm_apn);
|
||||
|
||||
dump_generic (method);
|
||||
}
|
||||
|
||||
static void
|
||||
dump_country (gpointer key, gpointer value, gpointer user_data)
|
||||
{
|
||||
GSList *miter, *citer;
|
||||
ShellCountryMobileProvider *country_provider = value;
|
||||
|
||||
g_print ("Country: %s (%s)\n",
|
||||
country_provider->country_code,
|
||||
country_provider->country_name);
|
||||
|
||||
for (citer = country_provider->providers; citer; citer = g_slist_next (citer)) {
|
||||
ShellMobileProvider *provider = citer->data;
|
||||
|
||||
g_print (" Provider: %s (%s)\n", provider->name, (const char *) key);
|
||||
for (miter = provider->methods; miter; miter = g_slist_next (miter)) {
|
||||
ShellMobileAccessMethod *method = miter->data;
|
||||
GSList *liter;
|
||||
|
||||
|
||||
for (liter = provider->gsm_mcc_mnc; liter; liter = g_slist_next (liter)) {
|
||||
ShellGsmMccMnc *m = liter->data;
|
||||
g_print (" MCC/MNC: %s-%s\n", m->mcc, m->mnc);
|
||||
}
|
||||
|
||||
for (liter = provider->cdma_sid; liter; liter = g_slist_next (liter))
|
||||
g_print (" SID: %d\n", GPOINTER_TO_UINT (liter->data));
|
||||
|
||||
switch (method->type) {
|
||||
case SHELL_MOBILE_ACCESS_METHOD_TYPE_CDMA:
|
||||
dump_cdma (method);
|
||||
break;
|
||||
case SHELL_MOBILE_ACCESS_METHOD_TYPE_GSM:
|
||||
dump_gsm (method);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
g_print ("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
shell_mobile_providers_dump (GHashTable *country_providers)
|
||||
{
|
||||
g_return_if_fail (country_providers != NULL);
|
||||
g_hash_table_foreach (country_providers, dump_country, NULL);
|
||||
}
|
||||
|
||||
/* All the following don't exist in nm-applet, because C doesn't need
|
||||
those. They're only needed for the introspection annotations
|
||||
*/
|
||||
|
||||
/**
|
||||
* shell_mobile_provider_get_gsm_mcc_mnc:
|
||||
* @provider: a #ShellMobileProvider
|
||||
*
|
||||
* Returns: (element-type Shell.GsmMccMnc) (transfer none): the
|
||||
* list of #ShellGsmMccMnc this provider exposes
|
||||
*/
|
||||
GSList *
|
||||
shell_mobile_provider_get_gsm_mcc_mnc (ShellMobileProvider *provider)
|
||||
{
|
||||
return provider->gsm_mcc_mnc;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_mobile_provider_get_cdma_sid:
|
||||
* @provider: a #ShellMobileProvider
|
||||
*
|
||||
* Returns: (element-type guint32) (transfer none): the
|
||||
* list of CDMA sids this provider exposes
|
||||
*/
|
||||
GSList *
|
||||
shell_mobile_provider_get_cdma_sid (ShellMobileProvider *provider)
|
||||
{
|
||||
return provider->cdma_sid;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user