Compare commits
190 Commits
Author | SHA1 | Date | |
---|---|---|---|
a1aa58bb64 | |||
82ffe233dc | |||
9e16bb85e3 | |||
887b41bce3 | |||
63eef286c9 | |||
14e8cba2b1 | |||
3418e6e85e | |||
8f0c980d3c | |||
c86f3b8d48 | |||
8cf6b4c728 | |||
612b9e9faf | |||
c2c4c26f72 | |||
be4d504e27 | |||
9ed0bbb3a9 | |||
e5bc3a2ba8 | |||
1dee10c575 | |||
352fb7b833 | |||
81cee34c17 | |||
fa786fd3ef | |||
0751a90bd9 | |||
709193d680 | |||
d0d82cdf7e | |||
5d2b7e2c9e | |||
07660f7fcf | |||
5d138e1b79 | |||
0133c6e174 | |||
2054f77e2b | |||
e4911e2f7a | |||
eabc2e6781 | |||
71cf87cbb1 | |||
155997b5fa | |||
82ce8fe3ff | |||
9f1da20161 | |||
d4239d570d | |||
1ecbabc69a | |||
7f767c49d8 | |||
aabe56ba79 | |||
d227ddfc88 | |||
021d3dadbb | |||
4902a600d5 | |||
db39ba3b9f | |||
239a9e4816 | |||
35e99266ba | |||
67ae8ed8e9 | |||
356e4c0967 | |||
80a9d2e7c9 | |||
5088f22388 | |||
4156a4c2d0 | |||
b6c2399a17 | |||
5be9326192 | |||
388cfa3695 | |||
e8914c6699 | |||
13bf64a53d | |||
f96b2ee858 | |||
b12967b930 | |||
d896248ff8 | |||
2ebdc81c8f | |||
2af5e851b3 | |||
bd9455ec8e | |||
fefee3b49e | |||
071c49b7c6 | |||
4e9e6e75d3 | |||
a13af7fbcc | |||
4fa8e2b59d | |||
90783c7cdf | |||
f99b4da4ec | |||
a64e0e1f49 | |||
270e82e3db | |||
8f4a4d93f2 | |||
83265bb12a | |||
5be8d5f9cf | |||
08126e5a38 | |||
b76efe17d6 | |||
ca2678446d | |||
06d906b962 | |||
023f5149e5 | |||
2e45508529 | |||
6241a8269f | |||
465d03ab2c | |||
a56cd3c3d6 | |||
d8a98e5467 | |||
2d813cbdd8 | |||
6d3434f3a5 | |||
fa0268f35a | |||
712ea9b9b6 | |||
b7fd78b254 | |||
a2e6b3167b | |||
72037af241 | |||
62048abbf5 | |||
7b61aca956 | |||
6709e5e458 | |||
11c8405879 | |||
6028628261 | |||
3d8a1537d2 | |||
c5b4decdae | |||
c7237be3e3 | |||
aa39166b74 | |||
e7b9933036 | |||
54d51c713c | |||
72e0c2fe11 | |||
7458d3ef39 | |||
52c5f9b144 | |||
8c7085acd4 | |||
083ca7d39b | |||
68e716ae27 | |||
ef8772916d | |||
b54e374bc5 | |||
b3228258ee | |||
e5036a458e | |||
c714a66ba3 | |||
d80b7be6ca | |||
77de611ec7 | |||
60b54c0052 | |||
6a3130e25f | |||
91cba1f8f4 | |||
22b2661df9 | |||
f8b397a5dc | |||
44b475e746 | |||
d25610903a | |||
2efcbaf206 | |||
7f1d2825fd | |||
b9edb1dc01 | |||
b0cc778c49 | |||
ff840db708 | |||
11f30e2e09 | |||
4886275df4 | |||
c5de239e25 | |||
10dcc100e9 | |||
8ada9b43ae | |||
fd77225c3e | |||
7ed3facf8f | |||
6c97e2a5ab | |||
f19e8b1e78 | |||
08e669adde | |||
bbd2c02b7f | |||
0d9da86b7e | |||
5810fcb14d | |||
2466eb3132 | |||
fc59e222d2 | |||
ff983432d9 | |||
67b4f9b3a9 | |||
f279b6bf7e | |||
daec53f4fe | |||
cb1966612e | |||
cda279f5c2 | |||
31915cdc93 | |||
329f803004 | |||
7780c99de6 | |||
d3ad857ba4 | |||
aa1405e4ea | |||
aa0a7f7816 | |||
3850edced5 | |||
ddd59f2e76 | |||
10a0f2b614 | |||
8bc85d4a79 | |||
3dc07d48c5 | |||
c1acf992fa | |||
99149f9c41 | |||
bcd307066a | |||
446910cb10 | |||
fde200d084 | |||
a376cd1610 | |||
dbeab0ef87 | |||
7765d6a08f | |||
aed50e2a39 | |||
b262a42458 | |||
3fd90dfcb1 | |||
dec55a3291 | |||
f6f3ded842 | |||
004c5cf287 | |||
aba5f2f7b8 | |||
2a96964204 | |||
a9fe2a1493 | |||
8f3bdd4f1a | |||
4322a20cbe | |||
2403fd0680 | |||
e01baf2a25 | |||
d27b37fefe | |||
acedb60abe | |||
77556d181e | |||
60612cace9 | |||
f057502834 | |||
21a1149532 | |||
6724ca4f63 | |||
7a6c25b3fb | |||
0366e320af | |||
f03793b825 | |||
0907f030b4 | |||
7542e68b6f | |||
9003a34285 |
136
NEWS
136
NEWS
@ -1,3 +1,139 @@
|
||||
3.1.90.1
|
||||
========
|
||||
|
||||
* Fix typo that was breaking the "Login Screen" mode [Marc-Antoine]
|
||||
* Fix build with new gobject-introspection [Dan]
|
||||
* Use a better icon for removable devices [Cosimo; #657757]
|
||||
* Add support for asynchronous search provides [Philippe, Jasper, Seif; #655220]
|
||||
* Misc bug fixes [Alex, Guillaume, Jasper; #657657, #657696]
|
||||
* Misc build fixes [Adel; #657697]
|
||||
|
||||
Contributors:
|
||||
Cosimo Cecchi, Guillaume Desmottes, Adel Gadllah, Alexander Larsson, Seif Lotfy,
|
||||
Philippe Normand, Marc-Antoine Perennou, Jasper St. Pierre, Dan Winship
|
||||
|
||||
Translations:
|
||||
Jorge González, Daniel Mustieles [es], Stas Solovey [ru]
|
||||
|
||||
3.1.90
|
||||
======
|
||||
* Add an on-screen keyboard that uses Caribou as a backend
|
||||
[Nohemi, Dan; #612662]
|
||||
* Allow searching for people in the overview using libfolks
|
||||
as the backend [Morten; #643018]
|
||||
* Add a "Login Screen" mode to be used when GDM is running; this
|
||||
mode has a stripped down user interface, and also contains the
|
||||
code to display the user list and authentication. [Ray; #657082]
|
||||
* Rework user menu to separate out "Do Not Disturb" from the IM
|
||||
status and to visually match GNOME Contacts. [Florian; #652837]
|
||||
* Implement displaying images such as cover-art in notifications
|
||||
[Neha, Marina; #621009]
|
||||
* Support default actions for notifications [Florian; #655818]
|
||||
* Networking
|
||||
- Stop using nm-applet for dialogs; do them as proper system modal
|
||||
dialogs in the shell code. [Giovanni; #650244]
|
||||
- Fix handling of hidden access points [Giovanni; #646454]
|
||||
* Telepathy integration
|
||||
- Support subscription requests [Guillaume, Xavier; #653941]
|
||||
- Notify on account connection errors [Alban, Jasper, Xavier; #654159]
|
||||
- Allow approving file transfers [Guillaume; #653940]
|
||||
- Improve styling of messages [Jasper; #640271]
|
||||
* Extension system [Jasper; #654770]
|
||||
- Support live enabling and disabling of extensions
|
||||
- Add the ability to install extensions from HTTP
|
||||
- Enhance D-Bus interface for controlling extensions
|
||||
- Collect errors separately for each extension
|
||||
* Add Main.panel.addToStatusArea for extension convenience
|
||||
[Giovanni, Jasper, Marc-Antoine; #653205]
|
||||
* Port to the new gnome-menus API. Clean up and speed up
|
||||
application information loading [Colin; #648149, #656546]
|
||||
* Use the accountsservice library rather than cut-and-pasted GDM code
|
||||
[Florian; #650893]
|
||||
* Add a D-Bus interface to take a screenshot; this will avoid various race
|
||||
conditions with the current gnome-screenshot approach [Adel; #652952]
|
||||
* Show numeric indicators to distinguish duplicate keyboard names
|
||||
[Giovanni; #650128]
|
||||
* Add GNOME Documents to the favorites list [Adel; #657520]
|
||||
* Update the clock immediately on resume from suspend [Colin; #656403]
|
||||
* Remove animation support from StAdjustment [Ray; #657082]
|
||||
* Support configuration of calendar applications via gsettings
|
||||
[Tassilo; #651190]
|
||||
* Don't fade in alt-Tab - wait a bit and show it instantly [Rui; #652346]
|
||||
* Darken workspace background on all workspaces [Rui; #656433]
|
||||
* Improve detection of the starting day of the week [Florian; #649078]
|
||||
* Add StButtonAccessible [Alejandro]
|
||||
* Visual tweaks to match mockups
|
||||
[Allan, Dan, Jasper, Marina; #640271, #655627, #655428, #656732]
|
||||
* Misc bug fixes [Dan, Florian, Giovanni, Guillaume, Jasper, Jeremy, Rui;
|
||||
#645708, #646761, #653119, #654398, #656125, #654707, #654898, #654638,
|
||||
#656335, #657111]
|
||||
* Code cleanups [Colin, Dan, Guillaume, Ray;
|
||||
#652718, #654639, #648651, #655813, #657082]
|
||||
* String tweaks [Jasper, Jeremy; #652984, #640271]
|
||||
* Build fixes [Jasper, Nohemi; #644275, #655812]
|
||||
|
||||
Contributors:
|
||||
Jeremy Bicha, Giovanni Campagna, Xavier Claessens, Alban Crequy,
|
||||
Guillaume Desmottes, Allan Day, Neha Doijode, Nohemi Fernandez,
|
||||
Tassilo Horn, Rui Matos, Morten Mjelva, Florian Müllner, Alejandro Piñeiro,
|
||||
Jasper St. Pierre, Ray Strode, Colin Walters, Dan Winship,
|
||||
Marina Zhurakhinskaya
|
||||
|
||||
Translations:
|
||||
Ivaylo Valkov [bg], Mario Blättermann [de], Diego Escalante Urrelo,
|
||||
Jorge González, Daniel Mustieles [es], Arash Mousavi [fa], Fran Dieguez [gl],
|
||||
Yaron Shahrabani [he], Andika Triwidada, Wibiharto [id],
|
||||
Aurimas Černius [lt], Umarzuki Bin Mochlis Moktar [ml], Kjartan Maraas [nb],
|
||||
A S Alam [pa], Daniel Nylander [se], Ngô Chin, Nguyễn Thái Ngọc Duy [vi],
|
||||
Aron Xu [zh_CN], Chao-Hsiung Liao [zh_HK, zh_TW]
|
||||
|
||||
3.1.4
|
||||
=====
|
||||
* Take over inserted media handling and autorun from gnome-session [Cosimo]
|
||||
* Message Tray
|
||||
- Display a count of unread notifications on icons
|
||||
[Jasper, Guillaume; #649356, #654139]
|
||||
- Only remove icons when the sender quits from D-Bus, not when it
|
||||
closes its last window [Neha, Marina; #645764]
|
||||
- Solve problems switching chats between shell and Empathy
|
||||
[Guillaume; #654237]
|
||||
- Fix handling of bad GMarkup in messages [Dan; #650298]
|
||||
- Never show notifications when the screensaver is active [Dan; #654550]
|
||||
* Telepathy integrationpp
|
||||
- Implement Telepathy Debug interface to enable empathy-debugger
|
||||
[Guillaume; #652816]
|
||||
- Allow approving room invitations, and audio/video calls
|
||||
[Guillaume; #653740 #653939]
|
||||
- Send typing notifications [Jonny; #650196]
|
||||
* Fix selection highlighting for light-on-dark entries [Jasper; #643768]
|
||||
* Make control-Return in the overview open a new window [Maxim]
|
||||
* Delay showing the alt-Tab switcher to reduce visual noise when
|
||||
flipping betweeen windows [Dan; #652346]
|
||||
* When we have vertically stacked monitors, put the message tray
|
||||
on the bottom one [Dan; #636963]
|
||||
* Fix various problems with keynav and the Activities button
|
||||
[Dan; #641253 #645759]
|
||||
* Ensure screensaver is locked when switching users [Colin; #654565]
|
||||
* Improve extension creation tool [Jasper; #653206]
|
||||
* Fix compatibility with latest GJS [Giovanni; #654349]
|
||||
* Code cleanups [Adel, Dan, Jasper; #645759 #654577 #654791 #654987]
|
||||
* Misc bug fixes [Richard, Dan, Florian, Giovanni, Jasper, Marc-Antoine, Rui;
|
||||
#647175 #649513 #650452 #651082 #653700 #653989 #654105 #654791 #654267
|
||||
#654269 #654527 #655446]
|
||||
* Build fixes [Florian, Siegfried; #654300]
|
||||
|
||||
Contributors:
|
||||
Giovanni Campagna, Cosimo Cecchi, Guillaume Desmottes, Neha Doijode,
|
||||
Maxim Ermilov, Adel Gadllah, Siegfried-Angel Gevatter Pujals, Richard Hughes,
|
||||
Jonny Lamb, Rui Matos, Florian Müllner, Marc-Antoine Perennou, Colin Walters,
|
||||
Dan Winship, Marina Zhurakhinskaya
|
||||
|
||||
Translations:
|
||||
Mario Blättermann, Paul Seyfert [de], Jorge González, Daniel Mustieles [es],
|
||||
Fran Dieguez [gl], Yaron Shahrabani [he], Luca Ferretti [it],
|
||||
Rudolfs Mazurs [lv], Kjartan Maraas [nb], A S Alam [pa], Yuri Kozlov [ru],
|
||||
Michal Štrba, Matej Urbančič [sl]
|
||||
|
||||
3.1.3
|
||||
=====
|
||||
* Fix problem with "user theme extension" breaking the CSS for other
|
||||
|
32
configure.ac
32
configure.ac
@ -1,5 +1,5 @@
|
||||
AC_PREREQ(2.63)
|
||||
AC_INIT([gnome-shell],[3.1.3],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
AC_INIT([gnome-shell],[3.1.90.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_SRCDIR([src/shell-global.c])
|
||||
@ -66,14 +66,15 @@ AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
|
||||
|
||||
CLUTTER_MIN_VERSION=1.7.5
|
||||
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
|
||||
GJS_MIN_VERSION=0.7.11
|
||||
GJS_MIN_VERSION=1.29.15
|
||||
MUTTER_MIN_VERSION=3.0.0
|
||||
FOLKS_MIN_VERSION=0.5.2
|
||||
GTK_MIN_VERSION=3.0.0
|
||||
GIO_MIN_VERSION=2.25.9
|
||||
GIO_MIN_VERSION=2.29.10
|
||||
LIBECAL_MIN_VERSION=2.32.0
|
||||
LIBEDATASERVER_MIN_VERSION=1.2.0
|
||||
LIBEDATASERVERUI_MIN_VERSION=2.91.6
|
||||
TELEPATHY_GLIB_MIN_VERSION=0.15.3
|
||||
TELEPATHY_GLIB_MIN_VERSION=0.15.5
|
||||
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
|
||||
POLKIT_MIN_VERSION=0.100
|
||||
STARTUP_NOTIFICATION_MIN_VERSION=0.11
|
||||
@ -82,9 +83,10 @@ STARTUP_NOTIFICATION_MIN_VERSION=0.11
|
||||
PKG_CHECK_MODULES(GNOME_SHELL, gio-2.0 >= $GIO_MIN_VERSION
|
||||
gio-unix-2.0 dbus-glib-1 libxml-2.0
|
||||
gtk+-3.0 >= $GTK_MIN_VERSION
|
||||
folks >= $FOLKS_MIN_VERSION
|
||||
libmutter >= $MUTTER_MIN_VERSION
|
||||
gjs-internals-1.0 >= $GJS_MIN_VERSION
|
||||
libgnome-menu $recorder_modules gconf-2.0
|
||||
libgnome-menu-3.0 $recorder_modules gconf-2.0
|
||||
gdk-x11-3.0 libsoup-2.4
|
||||
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
|
||||
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
|
||||
@ -93,7 +95,8 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-2.0 >= $GIO_MIN_VERSION
|
||||
libcanberra
|
||||
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
|
||||
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
|
||||
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes)
|
||||
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
|
||||
libnm-glib libnm-util gnome-keyring-1)
|
||||
|
||||
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
|
||||
|
||||
@ -105,12 +108,6 @@ AC_SUBST([GJS_VERSION], ["$GJS_VERSION"])
|
||||
|
||||
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
|
||||
JHBUILD_TYPELIBDIR="$INTROSPECTION_TYPELIBDIR"
|
||||
# NM is the only typelib we use that we don't jhbuild
|
||||
PKG_CHECK_EXISTS([libnm-glib >= 0.8.999],
|
||||
[NM_TYPELIBDIR=`$PKG_CONFIG --variable=libdir libnm-glib`/girepository-1.0
|
||||
if test "$INTROSPECTION_TYPELIBDIR" != "$NM_TYPELIBDIR"; then
|
||||
JHBUILD_TYPELIBDIR="$JHBUILD_TYPELIBDIR:$NM_TYPELIBDIR"
|
||||
fi])
|
||||
AC_SUBST(JHBUILD_TYPELIBDIR)
|
||||
|
||||
saved_CFLAGS=$CFLAGS
|
||||
@ -156,6 +153,17 @@ AC_CHECK_FUNCS(fdwalk)
|
||||
AC_CHECK_FUNCS(mallinfo)
|
||||
AC_CHECK_HEADERS([sys/resource.h])
|
||||
|
||||
# _NL_TIME_FIRST_WEEKDAY is an enum and not a define
|
||||
AC_MSG_CHECKING([for _NL_TIME_FIRST_WEEKDAY])
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <langinfo.h>]],
|
||||
[[nl_langinfo(_NL_TIME_FIRST_WEEKDAY);]])],
|
||||
[langinfo_ok=yes], [langinfo_ok=no])
|
||||
AC_MSG_RESULT($langinfo_ok)
|
||||
if test "$langinfo_ok" = "yes"; then
|
||||
AC_DEFINE([HAVE__NL_TIME_FIRST_WEEKDAY], [1],
|
||||
[Define if _NL_TIME_FIRST_WEEKDAY is available])
|
||||
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`
|
||||
|
@ -29,6 +29,7 @@ dist_theme_DATA = \
|
||||
theme/dash-placeholder.svg \
|
||||
theme/filter-selected-ltr.svg \
|
||||
theme/filter-selected-rtl.svg \
|
||||
theme/gdm.css \
|
||||
theme/gnome-shell.css \
|
||||
theme/panel-border.svg \
|
||||
theme/panel-button-border.svg \
|
||||
|
@ -11,15 +11,6 @@
|
||||
using the Alt-F2 dialog.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="disabled-extensions" type="as">
|
||||
<default>[]</default>
|
||||
<_summary>Uuids of extensions to disable</_summary>
|
||||
<_description>
|
||||
GNOME Shell extensions have a uuid property; this key lists extensions
|
||||
which should not be loaded. This setting overrides enabled-extensions
|
||||
for extensions that appear in both lists.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="enabled-extensions" type="as">
|
||||
<default>[]</default>
|
||||
<_summary>Uuids of extensions to enable</_summary>
|
||||
@ -40,7 +31,7 @@
|
||||
</_description>
|
||||
</key>
|
||||
<key name="favorite-apps" type="as">
|
||||
<default>[ 'mozilla-firefox.desktop', 'evolution.desktop', 'empathy.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'openoffice.org-writer.desktop', 'nautilus.desktop' ]</default>
|
||||
<default>[ 'firefox.desktop', 'evolution.desktop', 'empathy.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'libreoffice-writer.desktop', 'nautilus.desktop', 'gnome-documents.desktop' ]</default>
|
||||
<_summary>List of desktop file IDs for favorite applications</_summary>
|
||||
<_description>
|
||||
The applications corresponding to these identifiers
|
||||
@ -62,6 +53,7 @@
|
||||
<child name="clock" schema="org.gnome.shell.clock"/>
|
||||
<child name="calendar" schema="org.gnome.shell.calendar"/>
|
||||
<child name="recorder" schema="org.gnome.shell.recorder"/>
|
||||
<child name="keyboard" schema="org.gnome.shell.keyboard"/>
|
||||
</schema>
|
||||
|
||||
<schema id="org.gnome.shell.calendar" path="/org/gnome/shell/calendar/"
|
||||
@ -75,6 +67,24 @@
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
<schema id="org.gnome.shell.keyboard" path="/org/gnome/shell/keyboard/"
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="show-keyboard" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Show the onscreen keyboard</_summary>
|
||||
<_description>
|
||||
If true, display onscreen keyboard.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="keyboard-type" type="s">
|
||||
<default>'touch'</default>
|
||||
<_summary>Which keyboard to use</_summary>
|
||||
<_description>
|
||||
The type of keyboard to use.
|
||||
</_description>
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
<schema id="org.gnome.shell.clock" path="/org/gnome/shell/clock/"
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="show-seconds" type="b">
|
||||
|
161
data/theme/gdm.css
Normal file
161
data/theme/gdm.css
Normal file
@ -0,0 +1,161 @@
|
||||
/* Copyright 2011, Red Hat, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU Lesser General Public License,
|
||||
* version 2.1, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
/* Login Dialog */
|
||||
|
||||
.login-dialog-title {
|
||||
font-size: 14pt;
|
||||
font-weight: bold;
|
||||
color: #666666;
|
||||
padding-bottom: 2em;
|
||||
}
|
||||
|
||||
.login-dialog {
|
||||
border-radius: 16px;
|
||||
min-height: 150px;
|
||||
max-height: 700px;
|
||||
min-width: 350px;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-view {
|
||||
-st-vfade-offset: 1em;
|
||||
}
|
||||
|
||||
.login-dialog-user-list {
|
||||
spacing: 12px;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item {
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item:ltr {
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item:rtl {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item .login-dialog-user-list-item-name {
|
||||
font-size: 20pt;
|
||||
padding-left: 1em;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item:hover .login-dialog-user-list-item-name {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item:focus .login-dialog-user-list-item-name {
|
||||
color: white;
|
||||
text-shadow: black 0px 2px 2px;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item-vertical-layout {
|
||||
spacing: 2px;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item .login-dialog-user-list-item-focus-bin {
|
||||
background-color: rgba(0,0,0,0.0);
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item:focus .login-dialog-user-list-item-focus-bin {
|
||||
background-color: #666666;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item-icon {
|
||||
border: 2px solid #8b8b8b;
|
||||
border-radius: 8px;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.login-dialog-not-listed-button {
|
||||
padding-top: 2em;
|
||||
}
|
||||
.login-dialog-not-listed-label {
|
||||
font-size: 14pt;
|
||||
font-weight: bold;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.login-dialog-prompt-layout {
|
||||
padding-bottom: 64px;
|
||||
}
|
||||
.login-dialog-prompt-label {
|
||||
color: white;
|
||||
font-size: 20pt;
|
||||
}
|
||||
|
||||
.login-dialog-prompt-entry {
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
border: 2px solid #5656cc;
|
||||
color: black;
|
||||
background-color: white;
|
||||
caret-color: black;
|
||||
caret-size: 1px;
|
||||
}
|
||||
|
||||
.login-dialog-session-list {
|
||||
color: #ffffff;
|
||||
font-size: 10.5pt;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-button {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-button:focus {
|
||||
background-color: #4c4c4c;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-button:active {
|
||||
background-color: #4c4c4c;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-button:hover {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-scroll-view {
|
||||
background-gradient-start: rgba(80,80,80,0.3);
|
||||
background-gradient-end: rgba(80,80,80,0.7);
|
||||
background-gradient-direction: vertical;
|
||||
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.9);
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(80,80,80,1.0);
|
||||
padding: .5em;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-item:focus {
|
||||
background-color: #666666;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-triangle {
|
||||
padding-right: .5em;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-item-box {
|
||||
spacing: .25em;
|
||||
}
|
||||
|
||||
.login-dialog-session-list-item-dot {
|
||||
width: .75em;
|
||||
height: .75em;
|
||||
}
|
@ -93,12 +93,12 @@ StTooltip StLabel {
|
||||
/* PopupMenu */
|
||||
|
||||
.popup-menu-boxpointer {
|
||||
-arrow-border-radius: 9px;
|
||||
-arrow-border-radius: 8px;
|
||||
-arrow-background-color: rgba(0,0,0,0.9);
|
||||
-arrow-border-width: 2px;
|
||||
-arrow-border-color: #5f5f5f;
|
||||
-arrow-base: 30px;
|
||||
-arrow-rise: 15px;
|
||||
-arrow-border-color: #a5a5a5;
|
||||
-arrow-base: 24px;
|
||||
-arrow-rise: 11px;
|
||||
}
|
||||
|
||||
.popup-menu {
|
||||
@ -139,6 +139,15 @@ StTooltip StLabel {
|
||||
border-width: 0px;
|
||||
}
|
||||
|
||||
.popup-combo-menu {
|
||||
background-color: rgba(0,0,0,0.9);
|
||||
padding: 1em 0em;
|
||||
color: #ffffff;
|
||||
font-size: 10.5pt;
|
||||
border: 1px solid #5f5f5f;
|
||||
border-radius: 9px;
|
||||
}
|
||||
|
||||
/* The remaining popup-menu sizing is all done in ems, so that if you
|
||||
* override .popup-menu.font-size, everything else will scale with it.
|
||||
*/
|
||||
@ -158,6 +167,10 @@ StTooltip StLabel {
|
||||
.popup-image-menu-item {
|
||||
}
|
||||
|
||||
.popup-combobox-item {
|
||||
spacing: 1em;
|
||||
}
|
||||
|
||||
.popup-separator-menu-item {
|
||||
-gradient-height: 2px;
|
||||
-gradient-start: rgba(8,8,8,0);
|
||||
@ -223,6 +236,39 @@ StTooltip StLabel {
|
||||
spacing: .5em;
|
||||
}
|
||||
|
||||
/* Shared button properties */
|
||||
|
||||
.dash-search-button, .notification-button, .notification-icon-button,
|
||||
.hotplug-notification-item, .hotplug-resident-eject-button,
|
||||
.modal-dialog-button {
|
||||
color: white;
|
||||
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:hover, .notification-button:hover,
|
||||
.notification-icon-button:hover, .hotplug-notification-item:hover,
|
||||
.hotplug-resident-eject-button:hover, .modal-dialog-button:hover {
|
||||
background-gradient-start: rgba(255, 255, 255, 0.3);
|
||||
background-gradient-end: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.dash-search-button:selected, .notification-button:focus,
|
||||
.notification-icon-button:focus, .hotplug-notification-item:focus,
|
||||
.modal-dialog-button:focus {
|
||||
border: 2px solid #8b8b8b;
|
||||
}
|
||||
|
||||
.dash-search-button:active, .dash-search-button:pressed,
|
||||
.notification-button:active, .notification-icon-button:active,
|
||||
.hotplug-notification-item:active, .hotplug-resident-eject-button:active,
|
||||
.modal-dialog-button:active, .modal-dialog-button:pressed {
|
||||
background-gradient-start: rgba(255, 255, 255, 0);
|
||||
background-gradient-end: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
/* Panel */
|
||||
|
||||
#panel {
|
||||
@ -321,6 +367,10 @@ StTooltip StLabel {
|
||||
icon-shadow: black 0px 2px 2px;
|
||||
}
|
||||
|
||||
.panel-menu {
|
||||
-boxpointer-gap: 4px
|
||||
}
|
||||
|
||||
/* The rounded panel corners we draw don't
|
||||
* support transitions, so disable transitions
|
||||
* for the buttons at the left/right edges
|
||||
@ -337,6 +387,49 @@ StTooltip StLabel {
|
||||
spacing: 4px;
|
||||
}
|
||||
|
||||
.status-chooser {
|
||||
spacing: .4em;
|
||||
}
|
||||
|
||||
.status-chooser .popup-menu-item,
|
||||
.status-chooser-combo .popup-menu-item {
|
||||
padding: .4em;
|
||||
}
|
||||
|
||||
.status-chooser-user-icon {
|
||||
border: 2px solid #8b8b8b;
|
||||
border-radius: 5px;
|
||||
width: 48pt;
|
||||
height: 48pt;
|
||||
}
|
||||
|
||||
.status-chooser-user-icon:hover {
|
||||
border: 2px solid #bbbbbb;
|
||||
}
|
||||
|
||||
.status-chooser-user-name {
|
||||
font-weight: bold;
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.status-chooser-combo {
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.status-chooser-combo.popup-combo-menu {
|
||||
background-color: rgba(0,0,0,0.7);
|
||||
padding: .4em 0em;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #5f5f5f;
|
||||
color: #ffffff;
|
||||
font-size: 10.5pt;
|
||||
}
|
||||
|
||||
.status-chooser-status-item,
|
||||
.status-chooser-combo > .popup-combobox-item {
|
||||
spacing: .4em;
|
||||
}
|
||||
|
||||
#legacyTray {
|
||||
spacing: 14px;
|
||||
padding-left: 14px;
|
||||
@ -359,7 +452,6 @@ StTooltip StLabel {
|
||||
|
||||
#overview {
|
||||
spacing: 12px;
|
||||
background-color: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.window-caption {
|
||||
@ -552,27 +644,26 @@ StTooltip StLabel {
|
||||
spacing: 12px;
|
||||
}
|
||||
|
||||
/* Text labels are an odd number of pixels tall. The uneven top and bottom
|
||||
* padding compensates for this and ensures that the label is vertically
|
||||
* centered */
|
||||
.dash-search-button {
|
||||
background-gradient-direction: vertical;
|
||||
background-gradient-start: rgba(255, 255, 255, 0.2);
|
||||
background-gradient-end: rgba(255, 255, 255, 0);
|
||||
border: 1px solid #808080;
|
||||
border-radius: 16px;
|
||||
height: 32px;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 5px;
|
||||
width: 300px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.dash-search-button:selected,
|
||||
.dash-search-button:hover {
|
||||
background-gradient-direction: vertical;
|
||||
background-gradient-start: rgba(255, 255, 255, 0.4);
|
||||
background-gradient-end: rgba(255, 255, 255, 0.2);
|
||||
.dash-search-button:selected {
|
||||
padding-top: 3px;
|
||||
padding-bottom: 4px;
|
||||
width: 298px;
|
||||
}
|
||||
|
||||
.dash-search-button-label {
|
||||
color: #cccccc;
|
||||
font-size: 12pt;
|
||||
color: white;
|
||||
font-size: 11pt;
|
||||
}
|
||||
|
||||
/* Apps */
|
||||
@ -582,6 +673,11 @@ StTooltip StLabel {
|
||||
-shell-grid-item-size: 118px;
|
||||
}
|
||||
|
||||
.contact-grid {
|
||||
spacing: 36px;
|
||||
-shell-grid-item-size: 272px; /* 2 * -shell-grid-item-size + spacing */
|
||||
}
|
||||
|
||||
.icon-grid .overview-icon {
|
||||
icon-size: 96px;
|
||||
}
|
||||
@ -648,11 +744,57 @@ StTooltip StLabel {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.contact {
|
||||
width: 272px; /* Same width as two normal results + spacing */
|
||||
height: 118px; /* Aspect ratio = 1.75. Normal US business card ratio */
|
||||
border-radius: 4px;
|
||||
padding: 3px;
|
||||
border: 1px rgba(0,0,0,0);
|
||||
transition-duration: 100;
|
||||
}
|
||||
|
||||
.contact-content {
|
||||
border-radius: 2px;
|
||||
padding: 8px;
|
||||
width: 232px;
|
||||
height: 84px;
|
||||
background-color: white;
|
||||
color: black;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.contact-icon {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.contact-details {
|
||||
padding: 6px 8px 11px 8px;
|
||||
}
|
||||
|
||||
.contact-details-alias {
|
||||
font-size: 16px;
|
||||
padding-bottom: 11px;
|
||||
}
|
||||
|
||||
.contact-details-status {
|
||||
font-size: 11pt;
|
||||
}
|
||||
|
||||
.contact-details-status-icon {
|
||||
padding-right: 2px;
|
||||
}
|
||||
|
||||
.contact:hover {
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
transition-duration: 100;
|
||||
}
|
||||
|
||||
.app-well-app.running > .overview-icon {
|
||||
text-shadow: black 0px 2px 2px;
|
||||
background-image: url("running-indicator.svg");
|
||||
}
|
||||
|
||||
.contact:selected,
|
||||
.app-well-app:selected > .overview-icon,
|
||||
.search-result-content:selected > .overview-icon {
|
||||
background-color: rgba(255,255,255,0.33);
|
||||
@ -666,6 +808,7 @@ StTooltip StLabel {
|
||||
transition-duration: 100;
|
||||
}
|
||||
|
||||
.contact:focus,
|
||||
.app-well-app:focus > .overview-icon,
|
||||
.search-result-content:focus > .overview-icon {
|
||||
border: 1px solid #cccccc;
|
||||
@ -800,9 +943,9 @@ StTooltip StLabel {
|
||||
|
||||
/* Calendar popup */
|
||||
|
||||
#calendarArea {
|
||||
/* this is the width of the entire popup */
|
||||
min-width: 600px;
|
||||
#calendarEventsArea {
|
||||
/* this is the width of the second column of the popup */
|
||||
min-width: 400px;
|
||||
}
|
||||
|
||||
.calendar-vertical-separator {
|
||||
@ -1021,26 +1164,35 @@ StTooltip StLabel {
|
||||
}
|
||||
|
||||
#notification {
|
||||
font-size: 12pt;
|
||||
border-radius: 5px 5px 0px 0px;
|
||||
background: rgba(0,0,0,0.9);
|
||||
font-size: 11pt;
|
||||
border-radius: 10px 10px 0px 0px;
|
||||
background: rgba(0,0,0,0.8);
|
||||
padding: 8px 8px 4px 8px;
|
||||
spacing-rows: 10px;
|
||||
spacing-columns: 10px;
|
||||
width: 34em;
|
||||
}
|
||||
|
||||
.multi-line-notification {
|
||||
#notification.multi-line-notification {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
/* We use row-span = 2 for the image cell, which prevents its height preferences to be
|
||||
taken into account during allocation, so its height ends up being limited by the height
|
||||
of the content in the other rows. To avoid showing a stretched image, we set the minimum
|
||||
height of the table to be ICON_SIZE + IMAGE_SIZE + spacing-rows = 24 + 125 + 10 = 159 */
|
||||
.notification-with-image {
|
||||
min-height: 159px;
|
||||
}
|
||||
|
||||
.summary-boxpointer {
|
||||
-arrow-border-radius: 9px;
|
||||
-arrow-border-radius: 8px;
|
||||
-arrow-background-color: rgba(0,0,0,0.9);
|
||||
-arrow-border-width: 2px;
|
||||
-arrow-border-color: #5f5f5f;
|
||||
-arrow-base: 30px;
|
||||
-arrow-rise: 15px;
|
||||
-arrow-border-color: #a5a5a5;
|
||||
-arrow-base: 24px;
|
||||
-arrow-rise: 11px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.summary-boxpointer #notification {
|
||||
@ -1094,45 +1246,26 @@ StTooltip StLabel {
|
||||
}
|
||||
|
||||
#notification-actions {
|
||||
spacing: 5px;
|
||||
spacing: 10px;
|
||||
}
|
||||
|
||||
.notification-button {
|
||||
background-color: #3c3c3c;
|
||||
padding: 2px 14px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #181818;
|
||||
}
|
||||
|
||||
.notification-button:hover {
|
||||
border: 1px solid #a1a1a1;
|
||||
border-radius: 18px;
|
||||
font-size: 11pt;
|
||||
padding: 4px 42px 5px;
|
||||
}
|
||||
|
||||
.notification-button:focus {
|
||||
background-color: #666666;
|
||||
}
|
||||
|
||||
.notification-button:active {
|
||||
border: 1px solid #a1a1a1;
|
||||
background-color: #2b2b2b;
|
||||
padding: 3px 41px 4px;
|
||||
}
|
||||
|
||||
.notification-icon-button {
|
||||
border: 2px rgba(0,0,0,0.0);
|
||||
border-radius: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.notification-icon-button:hover {
|
||||
border: 2px rgba(161,161,161,0.7);
|
||||
}
|
||||
|
||||
.notification-icon-button:focus {
|
||||
background: rgba(192,192,192,0.7);
|
||||
}
|
||||
|
||||
.notification-icon-button:active {
|
||||
background: rgba(128,128,128,0.7);
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.notification-icon-button > StIcon {
|
||||
@ -1145,23 +1278,13 @@ StTooltip StLabel {
|
||||
}
|
||||
|
||||
.hotplug-notification-item {
|
||||
background-color: #3c3c3c;
|
||||
padding: 0px 10px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #181818;
|
||||
}
|
||||
|
||||
.hotplug-notification-item:hover {
|
||||
border: 1px solid #a1a1a1;
|
||||
padding: 2px 10px;
|
||||
border-radius: 18px;
|
||||
font-size: 10.5pt;
|
||||
}
|
||||
|
||||
.hotplug-notification-item:focus {
|
||||
background-color: #666666;
|
||||
}
|
||||
|
||||
.hotplug-notification-item:active {
|
||||
border: 1px solid #a1a1a1;
|
||||
background-color: #2b2b2b;
|
||||
padding: 1px 71px 1px 11px;
|
||||
}
|
||||
|
||||
.hotplug-notification-item-icon {
|
||||
@ -1203,28 +1326,20 @@ StTooltip StLabel {
|
||||
}
|
||||
|
||||
.hotplug-resident-eject-button {
|
||||
padding: 2px;
|
||||
border: 1px solid #2b2b2b;
|
||||
padding: 7px;
|
||||
border-radius: 5px;
|
||||
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.hotplug-resident-eject-button:hover {
|
||||
color: #fff;
|
||||
background-color: #2b2b2b;
|
||||
border: 1px solid #a1a1a1;
|
||||
}
|
||||
|
||||
.chat-log-message {
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
.chat-received {
|
||||
background-gradient-direction: horizontal;
|
||||
background-gradient-start: rgba(255, 255, 255, 0.2);
|
||||
background-gradient-end: rgba(255, 255, 255, 0);
|
||||
.chat-group-sent, .chat-group-meta {
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.chat-sent {
|
||||
padding-left: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
@ -1235,23 +1350,20 @@ StTooltip StLabel {
|
||||
}
|
||||
|
||||
.chat-sent {
|
||||
background-gradient-direction: horizontal;
|
||||
background-gradient-start: rgba(255, 255, 255, 0);
|
||||
background-gradient-end: rgba(255, 255, 255, 0.2);
|
||||
|
||||
padding-left: 4px;
|
||||
padding-left: 18pt;
|
||||
border-radius: 4px;
|
||||
color: #7E7E7E;
|
||||
}
|
||||
|
||||
.chat-sent:rtl {
|
||||
padding-left: 0px;
|
||||
padding-right: 4px;
|
||||
padding-right: 18pt;
|
||||
}
|
||||
|
||||
.chat-meta-message {
|
||||
padding-left: 4px;
|
||||
border-radius: 4px;
|
||||
font-size: 10.5pt;
|
||||
font-size: 9pt;
|
||||
color: #bbbbbb;
|
||||
}
|
||||
|
||||
@ -1260,24 +1372,35 @@ StTooltip StLabel {
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.subscription-message {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#notification StEntry {
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #565656;
|
||||
color: #a8a8a8;
|
||||
background-color: #404040;
|
||||
caret-color: #ffffff;
|
||||
selected-color: black;
|
||||
border: 1px solid rgba(245,245,245,0.2);
|
||||
background-gradient-direction: vertical;
|
||||
background-gradient-start: rgb(200,200,200);
|
||||
background-gradient-end: white;
|
||||
transition-duration: 300;
|
||||
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.6);
|
||||
|
||||
caret-color: #a8a8a8;
|
||||
caret-size: 1px;
|
||||
}
|
||||
|
||||
#notification StEntry:focus {
|
||||
border: 1px solid #3a3a3a;
|
||||
color: #545454;
|
||||
background-color: #e8e8e8;
|
||||
border: 1px solid #8b8b8b;
|
||||
color: #333333;
|
||||
background-gradient-direction: vertical;
|
||||
background-gradient-start: rgb(200,200,200);
|
||||
background-gradient-end: white;
|
||||
|
||||
caret-color: #545454;
|
||||
selection-background-color: #bcbcbc;
|
||||
selected-color: #323232;
|
||||
box-shadow: 0px 0px 6px 2px rgba(255,255,255,0.9);
|
||||
selection-background-color: #808080;
|
||||
}
|
||||
|
||||
/* The spacing and padding on the summary is tricky; we want to keep
|
||||
@ -1501,7 +1624,7 @@ StTooltip StLabel {
|
||||
border-radius: 24px;
|
||||
background-color: rgba(0.0, 0.0, 0.0, 0.9);
|
||||
border: 2px solid #868686;
|
||||
color: #ffffff;
|
||||
color: #babdb6;
|
||||
|
||||
padding-right: 42px;
|
||||
padding-left: 42px;
|
||||
@ -1514,37 +1637,21 @@ StTooltip StLabel {
|
||||
}
|
||||
|
||||
.modal-dialog-button {
|
||||
border: 1px solid #8b8b8b;
|
||||
border-radius: 18px;
|
||||
font-size: 10.5pt;
|
||||
font-size: 11pt;
|
||||
color: white;
|
||||
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
|
||||
padding-left: 32px;
|
||||
padding-right: 32px;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
|
||||
background-gradient-direction: vertical;
|
||||
background-gradient-start: #29323b;
|
||||
background-gradient-end: #121a24;
|
||||
padding: 4px 32px 5px;
|
||||
}
|
||||
|
||||
.modal-dialog-button:active,
|
||||
.modal-dialog-button:pressed {
|
||||
border-color: #a5a5a5;
|
||||
background-gradient-start: #121a24;
|
||||
background-gradient-end: #29323b;
|
||||
.modal-dialog-button:disabled {
|
||||
color: rgb(60, 60, 60);
|
||||
}
|
||||
|
||||
.modal-dialog-button:focus {
|
||||
border: 2px solid #a5a5a5;
|
||||
|
||||
padding-left: 31px;
|
||||
padding-right: 31px;
|
||||
padding-top: 7px;
|
||||
padding-bottom: 7px;
|
||||
padding: 3px 31px 4px;
|
||||
}
|
||||
|
||||
/* Run Dialog */
|
||||
@ -1806,9 +1913,16 @@ StTooltip StLabel {
|
||||
}
|
||||
|
||||
.polkit-dialog-password-entry {
|
||||
background-color: white;
|
||||
background-gradient-start: rgb(236,236,236);
|
||||
background-gradient-end: white;
|
||||
background-gradient-direction: vertical;
|
||||
color: black;
|
||||
border-radius: 5px;
|
||||
border: 2px solid #555753;
|
||||
}
|
||||
|
||||
.polkit-dialog-password-entry:focus {
|
||||
border: 2px solid #3465a4;
|
||||
}
|
||||
|
||||
.polkit-dialog-error-label {
|
||||
@ -1829,6 +1943,17 @@ StTooltip StLabel {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.network-dialog-show-password-checkbox {
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
font-size: 10pt;
|
||||
color: white;
|
||||
spacing: 10px;
|
||||
}
|
||||
|
||||
.network-dialog-secret-table {
|
||||
spacing-rows: 15px;
|
||||
}
|
||||
|
||||
/* Magnifier */
|
||||
|
||||
@ -1839,3 +1964,58 @@ StTooltip StLabel {
|
||||
.magnifier-zoom-region.full-screen {
|
||||
border-width: 0px;
|
||||
}
|
||||
|
||||
/* On-screen Keyboard */
|
||||
|
||||
#keyboard {
|
||||
background: rgba(0,0,0,0.8);
|
||||
}
|
||||
|
||||
.keyboard-layout {
|
||||
spacing: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.keyboard-row {
|
||||
spacing: 15px;
|
||||
}
|
||||
|
||||
.keyboard-key {
|
||||
min-height: 30px;
|
||||
min-width: 30px;
|
||||
background-gradient-start: rgba(255,245,245,0.4);
|
||||
background-gradient-end: rgba(105,105,105,0.1);
|
||||
background-gradient-direction: vertical;
|
||||
font-size: 14pt;
|
||||
font-weight: bold;
|
||||
border-radius: 10px;
|
||||
border: 2px solid #a0a0a0;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.keyboard-key:grayed {
|
||||
color: #808080;
|
||||
border-color: #808080;
|
||||
}
|
||||
|
||||
.keyboard-key:checked,
|
||||
.keyboard-key:hover {
|
||||
background: #303030;
|
||||
border: 3px solid white;
|
||||
}
|
||||
|
||||
.keyboard-key:active {
|
||||
background: #808080;
|
||||
}
|
||||
|
||||
.keyboard-subkeys {
|
||||
color: white;
|
||||
padding: 5px;
|
||||
-arrow-border-radius: 10px;
|
||||
-arrow-background-color: #090909;
|
||||
-arrow-border-width: 2px;
|
||||
-arrow-border-color: white;
|
||||
-arrow-base: 20px;
|
||||
-arrow-rise: 10px;
|
||||
-boxpointer-gap: 5px;
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
jsdir = $(pkgdatadir)/js
|
||||
|
||||
nobase_dist_js_DATA = \
|
||||
gdm/batch.js \
|
||||
gdm/loginDialog.js \
|
||||
misc/config.js \
|
||||
misc/docInfo.js \
|
||||
misc/fileUtils.js \
|
||||
@ -20,7 +22,7 @@ nobase_dist_js_DATA = \
|
||||
ui/autorunManager.js \
|
||||
ui/boxpointer.js \
|
||||
ui/calendar.js \
|
||||
ui/chrome.js \
|
||||
ui/contactDisplay.js \
|
||||
ui/ctrlAltTab.js \
|
||||
ui/dash.js \
|
||||
ui/dateMenu.js \
|
||||
@ -30,6 +32,7 @@ nobase_dist_js_DATA = \
|
||||
ui/environment.js \
|
||||
ui/extensionSystem.js \
|
||||
ui/iconGrid.js \
|
||||
ui/keyboard.js \
|
||||
ui/layout.js \
|
||||
ui/lightbox.js \
|
||||
ui/link.js \
|
||||
@ -39,6 +42,7 @@ nobase_dist_js_DATA = \
|
||||
ui/main.js \
|
||||
ui/messageTray.js \
|
||||
ui/modalDialog.js \
|
||||
ui/networkAgent.js \
|
||||
ui/shellMountOperation.js \
|
||||
ui/notificationDaemon.js \
|
||||
ui/overview.js \
|
||||
@ -53,7 +57,6 @@ nobase_dist_js_DATA = \
|
||||
ui/searchDisplay.js \
|
||||
ui/shellDBus.js \
|
||||
ui/statusIconDispatcher.js \
|
||||
ui/statusMenu.js \
|
||||
ui/status/accessibility.js \
|
||||
ui/status/keyboard.js \
|
||||
ui/status/network.js \
|
||||
@ -62,6 +65,7 @@ nobase_dist_js_DATA = \
|
||||
ui/status/bluetooth.js \
|
||||
ui/telepathyClient.js \
|
||||
ui/tweener.js \
|
||||
ui/userMenu.js \
|
||||
ui/viewSelector.js \
|
||||
ui/windowAttentionHandler.js \
|
||||
ui/windowManager.js \
|
||||
|
228
js/gdm/batch.js
Normal file
228
js/gdm/batch.js
Normal file
@ -0,0 +1,228 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||
*
|
||||
* Copyright 2011 Red Hat, Inc
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*/
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
|
||||
function Task() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
Task.prototype = {
|
||||
_init: function(scope, handler) {
|
||||
if (scope)
|
||||
this.scope = scope;
|
||||
else
|
||||
this.scope = this;
|
||||
|
||||
this.handler = handler;
|
||||
},
|
||||
|
||||
run: function() {
|
||||
if (this.handler)
|
||||
return this.handler.call(this.scope);
|
||||
|
||||
return null;
|
||||
},
|
||||
};
|
||||
Signals.addSignalMethods(Task.prototype);
|
||||
|
||||
function Hold() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
Hold.prototype = {
|
||||
__proto__: Task.prototype,
|
||||
|
||||
_init: function() {
|
||||
Task.prototype._init.call(this,
|
||||
this,
|
||||
function () {
|
||||
return this;
|
||||
});
|
||||
|
||||
this._acquisitions = 1;
|
||||
},
|
||||
|
||||
acquire: function() {
|
||||
if (this._acquisitions <= 0)
|
||||
throw new Error("Cannot acquire hold after it's been released");
|
||||
this._acquisitions++;
|
||||
},
|
||||
|
||||
acquireUntilAfter: function(hold) {
|
||||
if (!hold.isAcquired())
|
||||
return;
|
||||
|
||||
this.acquire();
|
||||
let signalId = hold.connect('release', Lang.bind(this, function() {
|
||||
hold.disconnect(signalId);
|
||||
this.release();
|
||||
}));
|
||||
},
|
||||
|
||||
release: function() {
|
||||
this._acquisitions--;
|
||||
|
||||
if (this._acquisitions == 0)
|
||||
this.emit('release');
|
||||
},
|
||||
|
||||
isAcquired: function() {
|
||||
return this._acquisitions > 0;
|
||||
}
|
||||
}
|
||||
Signals.addSignalMethods(Hold.prototype);
|
||||
|
||||
function Batch() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
Batch.prototype = {
|
||||
__proto__: Task.prototype,
|
||||
|
||||
_init: function(scope, tasks) {
|
||||
Task.prototype._init.call(this);
|
||||
|
||||
this.tasks = [];
|
||||
|
||||
for (let i = 0; i < tasks.length; i++) {
|
||||
let task;
|
||||
|
||||
if (tasks[i] instanceof Task) {
|
||||
task = tasks[i];
|
||||
} else if (typeof tasks[i] == 'function') {
|
||||
task = new Task(scope, tasks[i]);
|
||||
} else {
|
||||
throw new Error('Batch tasks must be functions or Task, Hold or Batch objects');
|
||||
}
|
||||
|
||||
this.tasks.push(task);
|
||||
}
|
||||
},
|
||||
|
||||
process: function() {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
runTask: function() {
|
||||
if (!(this._currentTaskIndex in this.tasks)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.tasks[this._currentTaskIndex].run();
|
||||
},
|
||||
|
||||
_finish: function() {
|
||||
this.hold.release();
|
||||
},
|
||||
|
||||
nextTask: function() {
|
||||
this._currentTaskIndex++;
|
||||
|
||||
// if the entire batch of tasks is finished, release
|
||||
// the hold and notify anyone waiting on the batch
|
||||
if (this._currentTaskIndex >= this.tasks.length) {
|
||||
this._finish();
|
||||
return;
|
||||
}
|
||||
|
||||
this.process();
|
||||
},
|
||||
|
||||
_start: function() {
|
||||
// acquire a hold to get released when the entire
|
||||
// batch of tasks is finished
|
||||
this.hold = new Hold();
|
||||
this._currentTaskIndex = 0;
|
||||
this.process();
|
||||
},
|
||||
|
||||
run: function() {
|
||||
this._start();
|
||||
|
||||
// hold may be destroyed at this point
|
||||
// if we're already done running
|
||||
return this.hold;
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
this.tasks = this.tasks.splice(0, this._currentTaskIndex + 1);
|
||||
}
|
||||
|
||||
};
|
||||
Signals.addSignalMethods(Batch.prototype);
|
||||
|
||||
function ConcurrentBatch() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
ConcurrentBatch.prototype = {
|
||||
__proto__: Batch.prototype,
|
||||
|
||||
_init: function(scope, tasks) {
|
||||
Batch.prototype._init.call(this, scope, tasks);
|
||||
},
|
||||
|
||||
process: function() {
|
||||
let hold = this.runTask();
|
||||
|
||||
if (hold) {
|
||||
this.hold.acquireUntilAfter(hold);
|
||||
}
|
||||
|
||||
// Regardless of the state of the just run task,
|
||||
// fire off the next one, so all the tasks can run
|
||||
// concurrently.
|
||||
this.nextTask();
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(ConcurrentBatch.prototype);
|
||||
|
||||
function ConsecutiveBatch() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
ConsecutiveBatch.prototype = {
|
||||
__proto__: Batch.prototype,
|
||||
|
||||
_init: function(scope, tasks) {
|
||||
Batch.prototype._init.call(this, scope, tasks);
|
||||
},
|
||||
|
||||
process: function() {
|
||||
let hold = this.runTask();
|
||||
|
||||
if (hold && hold.isAcquired()) {
|
||||
// This task is inhibiting the batch. Wait on it
|
||||
// before processing the next one.
|
||||
let signalId = hold.connect('release',
|
||||
Lang.bind(this, function() {
|
||||
hold.disconnect(signalId);
|
||||
this.nextTask();
|
||||
}));
|
||||
return;
|
||||
} else {
|
||||
// This task finished, process the next one
|
||||
this.nextTask();
|
||||
}
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(ConsecutiveBatch.prototype);
|
1262
js/gdm/loginDialog.js
Normal file
1262
js/gdm/loginDialog.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -113,10 +113,10 @@ function run() {
|
||||
|
||||
for (let i = 0; i < 2; i++) {
|
||||
Scripting.scriptEvent('applicationsShowStart');
|
||||
Main.overview.viewSelector.switchTab('applications');
|
||||
Main.overview._viewSelector.switchTab('applications');
|
||||
yield Scripting.waitLeisure();
|
||||
Scripting.scriptEvent('applicationsShowDone');
|
||||
Main.overview.viewSelector.switchTab('windows');
|
||||
Main.overview._viewSelector.switchTab('windows');
|
||||
yield Scripting.waitLeisure();
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ const Tweener = imports.ui.tweener;
|
||||
|
||||
const POPUP_APPICON_SIZE = 96;
|
||||
const POPUP_SCROLL_TIME = 0.10; // seconds
|
||||
const POPUP_FADE_IN_TIME = 0.4; // seconds
|
||||
const POPUP_DELAY_TIMEOUT = 150; // milliseconds
|
||||
const POPUP_FADE_OUT_TIME = 0.1; // seconds
|
||||
|
||||
const APP_ICON_HOVER_TIMEOUT = 200; // milliseconds
|
||||
@ -53,6 +53,7 @@ AltTabPopup.prototype = {
|
||||
this._currentWindow = -1;
|
||||
this._thumbnailTimeoutId = 0;
|
||||
this._motionTimeoutId = 0;
|
||||
this._initialDelayTimeoutId = 0;
|
||||
|
||||
this.thumbnailsVisible = false;
|
||||
|
||||
@ -146,8 +147,6 @@ AltTabPopup.prototype = {
|
||||
|
||||
// Need to force an allocation so we can figure out whether we
|
||||
// need to scroll when selecting
|
||||
this.actor.opacity = 0;
|
||||
this.actor.show();
|
||||
this.actor.get_allocation_box();
|
||||
|
||||
// Make the initial selection
|
||||
@ -183,16 +182,13 @@ AltTabPopup.prototype = {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Using easeInOutExpo over 400ms gives us 150ms of "nearly
|
||||
// invisible" (less than 10% opacity), followed by a 100ms
|
||||
// tween in (to 90% opacity, with the last 10% coming over the
|
||||
// next 150ms). So if the user releases Alt quickly after we
|
||||
// start tweening, they'll never see the switcher.
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 255,
|
||||
time: POPUP_FADE_IN_TIME,
|
||||
transition: 'easeInOutExpo'
|
||||
});
|
||||
// We delay showing the popup so that fast Alt+Tab users aren't
|
||||
// disturbed by the popup briefly flashing.
|
||||
this._initialDelayTimeoutId = Mainloop.timeout_add(POPUP_DELAY_TIMEOUT,
|
||||
Lang.bind(this, function () {
|
||||
this.actor.show();
|
||||
this._initialDelayTimeoutId = 0;
|
||||
}));
|
||||
|
||||
return true;
|
||||
},
|
||||
@ -223,7 +219,7 @@ AltTabPopup.prototype = {
|
||||
let keysym = event.get_key_symbol();
|
||||
let event_state = Shell.get_event_state(event);
|
||||
let backwards = event_state & Clutter.ModifierType.SHIFT_MASK;
|
||||
let action = global.screen.get_display().get_keybinding_action(event.get_key_code(), event_state);
|
||||
let action = global.display.get_keybinding_action(event.get_key_code(), event_state);
|
||||
|
||||
this._disableHover();
|
||||
|
||||
@ -395,6 +391,8 @@ AltTabPopup.prototype = {
|
||||
Mainloop.source_remove(this._motionTimeoutId);
|
||||
if (this._thumbnailTimeoutId != 0)
|
||||
Mainloop.source_remove(this._thumbnailTimeoutId);
|
||||
if (this._initialDelayTimeoutId != 0)
|
||||
Mainloop.source_remove(this._initialDelayTimeoutId);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -3,6 +3,7 @@
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const GMenu = imports.gi.GMenu;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
@ -35,8 +36,7 @@ AlphabeticalView.prototype = {
|
||||
this._appSystem = Shell.AppSystem.get_default();
|
||||
|
||||
this._pendingAppLaterId = 0;
|
||||
this._apps = [];
|
||||
this._filterApp = null;
|
||||
this._appIcons = {}; // desktop file id
|
||||
|
||||
let box = new St.BoxLayout({ vertical: true });
|
||||
box.add(this._grid.actor, { y_align: St.Align.START, expand: true });
|
||||
@ -63,20 +63,17 @@ AlphabeticalView.prototype = {
|
||||
|
||||
_removeAll: function() {
|
||||
this._grid.removeAll();
|
||||
this._apps = [];
|
||||
this._appIcons = {};
|
||||
},
|
||||
|
||||
_addApp: function(appInfo) {
|
||||
let appIcon = new AppWellIcon(this._appSystem.get_app(appInfo.get_id()));
|
||||
_addApp: function(app) {
|
||||
var id = app.get_id();
|
||||
let appIcon = new AppWellIcon(app);
|
||||
|
||||
this._grid.addItem(appIcon.actor);
|
||||
appIcon.actor.connect('key-focus-in', Lang.bind(this, this._ensureIconVisible));
|
||||
|
||||
appIcon._appInfo = appInfo;
|
||||
if (this._filterApp && !this._filterApp(appInfo))
|
||||
appIcon.actor.hide();
|
||||
|
||||
this._apps.push(appIcon);
|
||||
this._appIcons[id] = appIcon;
|
||||
},
|
||||
|
||||
_ensureIconVisible: function(icon) {
|
||||
@ -105,52 +102,33 @@ AlphabeticalView.prototype = {
|
||||
transition: 'easeOutQuad' });
|
||||
},
|
||||
|
||||
setFilter: function(filter) {
|
||||
this._filterApp = filter;
|
||||
for (let i = 0; i < this._apps.length; i++)
|
||||
this._apps[i].actor.visible = filter(this._apps[i]._appInfo);
|
||||
},
|
||||
|
||||
// Create actors for the applications in an idle to avoid blocking
|
||||
// for too long; see bug 647778
|
||||
_addPendingApps: function() {
|
||||
let i;
|
||||
let startTimeMillis = new Date().getTime();
|
||||
for (i = 0; i < this._pendingAppIds.length; i++) {
|
||||
let id = this._pendingAppIds[i];
|
||||
this._addApp(this._pendingApps[id]);
|
||||
|
||||
let currentTimeMillis = new Date().getTime();
|
||||
if (currentTimeMillis - startTimeMillis > MAX_APPLICATION_WORK_MILLIS)
|
||||
break;
|
||||
}
|
||||
this._pendingAppIds.splice(0, i + 1);
|
||||
if (this._pendingAppIds.length > 0) {
|
||||
return true;
|
||||
setVisibleApps: function(apps) {
|
||||
if (apps == null) { // null implies "all"
|
||||
for (var id in this._appIcons) {
|
||||
var icon = this._appIcons[id];
|
||||
icon.actor.visible = true;
|
||||
}
|
||||
} else {
|
||||
this._pendingAppLaterId = 0;
|
||||
this._pendingAppIds = null;
|
||||
this._pendingApps = null;
|
||||
return false;
|
||||
// Set everything to not-visible, then set to visible what we should see
|
||||
for (var id in this._appIcons) {
|
||||
var icon = this._appIcons[id];
|
||||
icon.actor.visible = false;
|
||||
}
|
||||
for (var i = 0; i < apps.length; i++) {
|
||||
var app = apps[i];
|
||||
var id = app.get_id();
|
||||
var icon = this._appIcons[id];
|
||||
icon.actor.visible = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
refresh: function(apps) {
|
||||
let ids = [];
|
||||
for (let i in apps)
|
||||
ids.push(i);
|
||||
ids.sort(function(a, b) {
|
||||
return apps[a].get_name().localeCompare(apps[b].get_name());
|
||||
});
|
||||
|
||||
setAppList: function(apps) {
|
||||
this._removeAll();
|
||||
|
||||
this._pendingAppIds = ids;
|
||||
this._pendingApps = apps;
|
||||
if (this._pendingAppLaterId)
|
||||
Meta.later_remove(this._pendingAppLaterId);
|
||||
this._pendingAppLaterId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
|
||||
Lang.bind(this, this._addPendingApps));
|
||||
for (var i = 0; i < apps.length; i++) {
|
||||
var app = apps[i];
|
||||
this._addApp(app);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -171,23 +149,24 @@ ViewByCategories.prototype = {
|
||||
// -2 is a flag to indicate that nothing is selected
|
||||
// (used only before the actor is mapped the first time)
|
||||
this._currentCategory = -2;
|
||||
this._filters = new St.BoxLayout({ vertical: true, reactive: true });
|
||||
this._filtersBox = new St.ScrollView({ x_fill: false,
|
||||
y_fill: false,
|
||||
style_class: 'vfade' });
|
||||
this._filtersBox.add_actor(this._filters);
|
||||
this._categories = [];
|
||||
this._apps = null;
|
||||
|
||||
this._categoryBox = new St.BoxLayout({ vertical: true, reactive: true });
|
||||
this._categoryScroll = new St.ScrollView({ x_fill: false,
|
||||
y_fill: false,
|
||||
style_class: 'vfade' });
|
||||
this._categoryScroll.add_actor(this._categoryBox);
|
||||
this.actor.add(this._view.actor, { expand: true, x_fill: true, y_fill: true });
|
||||
this.actor.add(this._filtersBox, { expand: false, y_fill: false, y_align: St.Align.START });
|
||||
this.actor.add(this._categoryScroll, { expand: false, y_fill: false, y_align: St.Align.START });
|
||||
|
||||
// Always select the "All" filter when switching to the app view
|
||||
this.actor.connect('notify::mapped', Lang.bind(this,
|
||||
function() {
|
||||
if (this.actor.mapped && this._allFilter)
|
||||
if (this.actor.mapped && this._allCategoryButton)
|
||||
this._selectCategory(-1);
|
||||
}));
|
||||
|
||||
this._sections = [];
|
||||
|
||||
// We need a dummy actor to catch the keyboard focus if the
|
||||
// user Ctrl-Alt-Tabs here before the deferred work creates
|
||||
// our real contents
|
||||
@ -201,64 +180,95 @@ ViewByCategories.prototype = {
|
||||
|
||||
this._currentCategory = num;
|
||||
|
||||
if (num != -1)
|
||||
this._allFilter.remove_style_pseudo_class('selected');
|
||||
else
|
||||
this._allFilter.add_style_pseudo_class('selected');
|
||||
if (num != -1) {
|
||||
var category = this._categories[num];
|
||||
this._allCategoryButton.remove_style_pseudo_class('selected');
|
||||
this._view.setVisibleApps(category.apps);
|
||||
} else {
|
||||
this._allCategoryButton.add_style_pseudo_class('selected');
|
||||
this._view.setVisibleApps(null);
|
||||
}
|
||||
|
||||
this._view.setFilter(Lang.bind(this, function(app) {
|
||||
if (num == -1)
|
||||
return true;
|
||||
return this._sections[num].name == app.get_section();
|
||||
}));
|
||||
|
||||
for (let i = 0; i < this._sections.length; i++) {
|
||||
for (var i = 0; i < this._categories.length; i++) {
|
||||
if (i == num)
|
||||
this._sections[i].filterActor.add_style_pseudo_class('selected');
|
||||
this._categories[i].button.add_style_pseudo_class('selected');
|
||||
else
|
||||
this._sections[i].filterActor.remove_style_pseudo_class('selected');
|
||||
this._categories[i].button.remove_style_pseudo_class('selected');
|
||||
}
|
||||
},
|
||||
|
||||
_addFilter: function(name, num) {
|
||||
// Recursively load a GMenuTreeDirectory; we could put this in ShellAppSystem too
|
||||
_loadCategory: function(dir, appList) {
|
||||
var iter = dir.iter();
|
||||
var nextType;
|
||||
while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
|
||||
if (nextType == GMenu.TreeItemType.ENTRY) {
|
||||
var entry = iter.get_entry();
|
||||
var app = this._appSystem.lookup_app_by_tree_entry(entry);
|
||||
if (!entry.get_app_info().get_nodisplay())
|
||||
appList.push(app);
|
||||
} else if (nextType == GMenu.TreeItemType.DIRECTORY) {
|
||||
this._loadCategory(iter.get_directory(), appList);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_addCategory: function(name, index, dir, allApps) {
|
||||
let button = new St.Button({ label: GLib.markup_escape_text (name, -1),
|
||||
style_class: 'app-filter',
|
||||
x_align: St.Align.START,
|
||||
can_focus: true });
|
||||
this._filters.add(button, { expand: true, x_fill: true, y_fill: false });
|
||||
button.connect('clicked', Lang.bind(this, function() {
|
||||
this._selectCategory(num);
|
||||
this._selectCategory(index);
|
||||
}));
|
||||
|
||||
if (num != -1)
|
||||
this._sections[num] = { filterActor: button,
|
||||
name: name };
|
||||
else
|
||||
this._allFilter = button;
|
||||
var apps;
|
||||
if (dir == null) {
|
||||
apps = allApps;
|
||||
this._allCategoryButton = button;
|
||||
} else {
|
||||
apps = [];
|
||||
this._loadCategory(dir, apps);
|
||||
this._categories.push({ apps: apps,
|
||||
name: name,
|
||||
button: button });
|
||||
}
|
||||
|
||||
this._categoryBox.add(button, { expand: true, x_fill: true, y_fill: false });
|
||||
},
|
||||
|
||||
_removeAll: function() {
|
||||
this._sections = [];
|
||||
this._filters.destroy_children();
|
||||
this._categories = [];
|
||||
this._categoryBox.destroy_children();
|
||||
},
|
||||
|
||||
refresh: function(apps) {
|
||||
refresh: function() {
|
||||
this._removeAll();
|
||||
|
||||
let sections = this._appSystem.get_sections();
|
||||
this._apps = apps;
|
||||
var allApps = Shell.AppSystem.get_default().get_all();
|
||||
allApps.sort(function(a, b) {
|
||||
return a.compare_by_name(b);
|
||||
});
|
||||
|
||||
/* Translators: Filter to display all applications */
|
||||
this._addFilter(_("All"), -1);
|
||||
this._addCategory(_("All"), -1, null, allApps);
|
||||
|
||||
if (!sections)
|
||||
return;
|
||||
var tree = this._appSystem.get_tree();
|
||||
var root = tree.get_root_directory();
|
||||
|
||||
for (let i = 0; i < sections.length; i++)
|
||||
this._addFilter(sections[i], i);
|
||||
var iter = root.iter();
|
||||
var nextType;
|
||||
var i = 0;
|
||||
while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
|
||||
if (nextType == GMenu.TreeItemType.DIRECTORY) {
|
||||
var dir = iter.get_directory();
|
||||
this._addCategory(dir.get_name(), i, dir);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
this._view.setAppList(allApps);
|
||||
this._selectCategory(-1);
|
||||
this._view.refresh(apps);
|
||||
|
||||
if (this._focusDummy) {
|
||||
let focused = this._focusDummy.has_key_focus();
|
||||
@ -291,60 +301,7 @@ AllAppDisplay.prototype = {
|
||||
},
|
||||
|
||||
_redisplay: function() {
|
||||
let apps = this._appSystem.get_flattened_apps().filter(function(app) {
|
||||
return !app.get_is_nodisplay();
|
||||
});
|
||||
|
||||
this._appView.refresh(apps);
|
||||
}
|
||||
};
|
||||
|
||||
function BaseAppSearchProvider() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
BaseAppSearchProvider.prototype = {
|
||||
__proto__: Search.SearchProvider.prototype,
|
||||
|
||||
_init: function(name) {
|
||||
Search.SearchProvider.prototype._init.call(this, name);
|
||||
this._appSys = Shell.AppSystem.get_default();
|
||||
},
|
||||
|
||||
getResultMeta: function(resultId) {
|
||||
let app = this._appSys.get_app(resultId);
|
||||
if (!app)
|
||||
return null;
|
||||
return { 'id': resultId,
|
||||
'name': app.get_name(),
|
||||
'createIcon': function(size) {
|
||||
return app.create_icon_texture(size);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
activateResult: function(id, params) {
|
||||
params = Params.parse(params, { workspace: null,
|
||||
timestamp: null });
|
||||
|
||||
let workspace = params.workspace ? params.workspace.index() : -1;
|
||||
let event = Clutter.get_current_event();
|
||||
let modifiers = event ? Shell.get_event_state(event) : 0;
|
||||
let openNewWindow = modifiers & Clutter.ModifierType.CONTROL_MASK;
|
||||
|
||||
let app = this._appSys.get_app(id);
|
||||
if (openNewWindow)
|
||||
app.open_new_window(workspace);
|
||||
else
|
||||
app.activate(workspace);
|
||||
},
|
||||
|
||||
dragActivateResult: function(id, params) {
|
||||
params = Params.parse(params, { workspace: null,
|
||||
timestamp: null });
|
||||
|
||||
let app = this._appSys.get_app(id);
|
||||
app.open_new_window(params.workspace ? params.workspace.index() : -1);
|
||||
this._appView.refresh();
|
||||
}
|
||||
};
|
||||
|
||||
@ -353,44 +310,104 @@ function AppSearchProvider() {
|
||||
}
|
||||
|
||||
AppSearchProvider.prototype = {
|
||||
__proto__: BaseAppSearchProvider.prototype,
|
||||
__proto__: Search.SearchProvider.prototype,
|
||||
|
||||
_init: function() {
|
||||
BaseAppSearchProvider.prototype._init.call(this, _("APPLICATIONS"));
|
||||
Search.SearchProvider.prototype._init.call(this, _("APPLICATIONS"));
|
||||
this._appSys = Shell.AppSystem.get_default();
|
||||
},
|
||||
|
||||
getResultMeta: function(app) {
|
||||
return { 'id': app,
|
||||
'name': app.get_name(),
|
||||
'createIcon': function(size) {
|
||||
return app.create_icon_texture(size);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
getInitialResultSet: function(terms) {
|
||||
return this._appSys.initial_search(false, terms);
|
||||
return this._appSys.initial_search(terms);
|
||||
},
|
||||
|
||||
getSubsearchResultSet: function(previousResults, terms) {
|
||||
return this._appSys.subsearch(false, previousResults, terms);
|
||||
return this._appSys.subsearch(previousResults, terms);
|
||||
},
|
||||
|
||||
activateResult: function(app, params) {
|
||||
params = Params.parse(params, { workspace: -1,
|
||||
timestamp: 0 });
|
||||
|
||||
let event = Clutter.get_current_event();
|
||||
let modifiers = event ? Shell.get_event_state(event) : 0;
|
||||
let openNewWindow = modifiers & Clutter.ModifierType.CONTROL_MASK;
|
||||
|
||||
if (openNewWindow)
|
||||
app.open_new_window(params.workspace);
|
||||
else
|
||||
app.activate_full(params.workspace, params.timestamp);
|
||||
},
|
||||
|
||||
dragActivateResult: function(id, params) {
|
||||
params = Params.parse(params, { workspace: -1,
|
||||
timestamp: 0 });
|
||||
|
||||
let app = this._appSys.lookup_app(id);
|
||||
app.open_new_window(workspace);
|
||||
},
|
||||
|
||||
createResultActor: function (resultMeta, terms) {
|
||||
let app = this._appSys.get_app(resultMeta['id']);
|
||||
let app = resultMeta['id'];
|
||||
let icon = new AppWellIcon(app);
|
||||
return icon.actor;
|
||||
}
|
||||
};
|
||||
|
||||
function PrefsSearchProvider() {
|
||||
function SettingsSearchProvider() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
PrefsSearchProvider.prototype = {
|
||||
__proto__: BaseAppSearchProvider.prototype,
|
||||
SettingsSearchProvider.prototype = {
|
||||
__proto__: Search.SearchProvider.prototype,
|
||||
|
||||
_init: function() {
|
||||
BaseAppSearchProvider.prototype._init.call(this, _("SETTINGS"));
|
||||
Search.SearchProvider.prototype._init.call(this, _("SETTINGS"));
|
||||
this._appSys = Shell.AppSystem.get_default();
|
||||
this._gnomecc = this._appSys.lookup_app('gnome-control-center.desktop');
|
||||
},
|
||||
|
||||
getResultMeta: function(pref) {
|
||||
return { 'id': pref,
|
||||
'name': pref.get_name(),
|
||||
'createIcon': function(size) {
|
||||
return pref.create_icon_texture(size);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
getInitialResultSet: function(terms) {
|
||||
return this._appSys.initial_search(true, terms);
|
||||
return this._appSys.search_settings(terms);
|
||||
},
|
||||
|
||||
getSubsearchResultSet: function(previousResults, terms) {
|
||||
return this._appSys.subsearch(true, previousResults, terms);
|
||||
return this._appSys.search_settings(terms);
|
||||
},
|
||||
|
||||
activateResult: function(pref, params) {
|
||||
params = Params.parse(params, { workspace: -1,
|
||||
timestamp: 0 });
|
||||
|
||||
pref.activate_full(params.workspace, params.timestamp);
|
||||
},
|
||||
|
||||
dragActivateResult: function(pref, params) {
|
||||
this.activateResult(pref, params);
|
||||
},
|
||||
|
||||
createResultActor: function (resultMeta, terms) {
|
||||
let app = resultMeta['id'];
|
||||
let icon = new AppWellIcon(app);
|
||||
return icon.actor;
|
||||
}
|
||||
};
|
||||
|
||||
@ -416,12 +433,12 @@ AppIcon.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
function AppWellIcon(app, iconParams) {
|
||||
this._init(app, iconParams);
|
||||
function AppWellIcon(app, iconParams, onActivateOverride) {
|
||||
this._init(app, iconParams, onActivateOverride);
|
||||
}
|
||||
|
||||
AppWellIcon.prototype = {
|
||||
_init : function(app, iconParams) {
|
||||
_init : function(app, iconParams, onActivateOverride) {
|
||||
this.app = app;
|
||||
this.actor = new St.Button({ style_class: 'app-well-app',
|
||||
reactive: true,
|
||||
@ -436,6 +453,8 @@ AppWellIcon.prototype = {
|
||||
|
||||
this.actor.label_actor = this.icon.label;
|
||||
|
||||
// A function callback to override the default "app.activate()"; used by preferences
|
||||
this._onActivateOverride = onActivateOverride;
|
||||
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||
this.actor.connect('popup-menu', Lang.bind(this, this._onKeyboardPopupMenu));
|
||||
@ -569,24 +588,28 @@ AppWellIcon.prototype = {
|
||||
this.emit('launching');
|
||||
let modifiers = Shell.get_event_state(event);
|
||||
|
||||
if (modifiers & Clutter.ModifierType.CONTROL_MASK
|
||||
&& this.app.state == Shell.AppState.RUNNING) {
|
||||
this.app.open_new_window(-1);
|
||||
if (this._onActivateOverride) {
|
||||
this._onActivateOverride(event);
|
||||
} else {
|
||||
this.app.activate(-1);
|
||||
if (modifiers & Clutter.ModifierType.CONTROL_MASK
|
||||
&& this.app.state == Shell.AppState.RUNNING) {
|
||||
this.app.open_new_window(-1);
|
||||
} else {
|
||||
this.app.activate();
|
||||
}
|
||||
}
|
||||
Main.overview.hide();
|
||||
},
|
||||
|
||||
shellWorkspaceLaunch : function(params) {
|
||||
params = Params.parse(params, { workspace: null,
|
||||
timestamp: null });
|
||||
params = Params.parse(params, { workspace: -1,
|
||||
timestamp: 0 });
|
||||
|
||||
this.app.open_new_window(params.workspace ? params.workspace.index() : -1);
|
||||
this.app.open_new_window(params.workspace);
|
||||
},
|
||||
|
||||
getDragActor: function() {
|
||||
return this.app.create_icon_texture(Main.overview.dash.iconSize);
|
||||
return this.app.create_icon_texture(Main.overview.dashIconSize);
|
||||
},
|
||||
|
||||
// Returns the original actor that should align with the actor
|
||||
@ -609,7 +632,7 @@ AppIconMenu.prototype = {
|
||||
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
|
||||
side = St.Side.RIGHT;
|
||||
|
||||
PopupMenu.PopupMenu.prototype._init.call(this, source.actor, 0.5, side, 0);
|
||||
PopupMenu.PopupMenu.prototype._init.call(this, source.actor, 0.5, side);
|
||||
|
||||
// We want to keep the item hovered while the menu is up
|
||||
this.blockSourceEvents = true;
|
||||
@ -650,17 +673,18 @@ AppIconMenu.prototype = {
|
||||
item._window = windows[i];
|
||||
}
|
||||
|
||||
if (windows.length > 0)
|
||||
if (!this._source.app.is_window_backed()) {
|
||||
if (windows.length > 0)
|
||||
this._appendSeparator();
|
||||
|
||||
let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
|
||||
|
||||
this._newWindowMenuItem = this._appendMenuItem(_("New Window"));
|
||||
this._appendSeparator();
|
||||
|
||||
let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
|
||||
|
||||
this._newWindowMenuItem = this._appendMenuItem(_("New Window"));
|
||||
this._appendSeparator();
|
||||
|
||||
this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ? _("Remove from Favorites")
|
||||
: _("Add to Favorites"));
|
||||
|
||||
this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ? _("Remove from Favorites")
|
||||
: _("Add to Favorites"));
|
||||
}
|
||||
},
|
||||
|
||||
_appendSeparator: function () {
|
||||
|
@ -28,7 +28,7 @@ AppFavorites.prototype = {
|
||||
let ids = global.settings.get_strv(this.FAVORITE_APPS_KEY);
|
||||
let appSys = Shell.AppSystem.get_default();
|
||||
let apps = ids.map(function (id) {
|
||||
return appSys.get_app(id);
|
||||
return appSys.lookup_app(id);
|
||||
}).filter(function (app) {
|
||||
return app != null;
|
||||
});
|
||||
@ -65,7 +65,7 @@ AppFavorites.prototype = {
|
||||
if (appId in this._favorites)
|
||||
return false;
|
||||
|
||||
let app = Shell.AppSystem.get_default().get_app(appId);
|
||||
let app = Shell.AppSystem.get_default().lookup_app(appId);
|
||||
|
||||
if (!app)
|
||||
return false;
|
||||
@ -84,9 +84,9 @@ AppFavorites.prototype = {
|
||||
if (!this._addFavorite(appId, pos))
|
||||
return;
|
||||
|
||||
let app = Shell.AppSystem.get_default().get_app(appId);
|
||||
let app = Shell.AppSystem.get_default().lookup_app(appId);
|
||||
|
||||
Main.overview.shellInfo.setMessage(_("%s has been added to your favorites.").format(app.get_name()), Lang.bind(this, function () {
|
||||
Main.overview.setMessage(_("%s has been added to your favorites.").format(app.get_name()), Lang.bind(this, function () {
|
||||
this._removeFavorite(appId);
|
||||
}));
|
||||
},
|
||||
@ -117,8 +117,8 @@ AppFavorites.prototype = {
|
||||
if (!this._removeFavorite(appId))
|
||||
return;
|
||||
|
||||
Main.overview.shellInfo.setMessage(_("%s has been removed from your favorites.").format(app.get_name()),
|
||||
Lang.bind(this, function () {
|
||||
Main.overview.setMessage(_("%s has been removed from your favorites.").format(app.get_name()),
|
||||
Lang.bind(this, function () {
|
||||
this._addFavorite(appId, pos);
|
||||
}));
|
||||
}
|
||||
|
@ -330,7 +330,8 @@ AutorunResidentSource.prototype = {
|
||||
},
|
||||
|
||||
createNotificationIcon: function(iconSize) {
|
||||
return new St.Icon ({ icon_name: 'drive-harddisk',
|
||||
return new St.Icon ({ icon_name: 'media-removable',
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: iconSize ? iconSize : this.ICON_SIZE });
|
||||
}
|
||||
}
|
||||
|
@ -180,7 +180,7 @@ BoxPointer.prototype = {
|
||||
this.bin.allocate(childBox, flags);
|
||||
|
||||
if (this._sourceActor && this._sourceActor.mapped)
|
||||
this._reposition(this._sourceActor, this._gap, this._alignment);
|
||||
this._reposition(this._sourceActor, this._alignment);
|
||||
},
|
||||
|
||||
_drawBorder: function(area) {
|
||||
@ -306,19 +306,18 @@ BoxPointer.prototype = {
|
||||
cr.stroke();
|
||||
},
|
||||
|
||||
setPosition: function(sourceActor, gap, alignment) {
|
||||
setPosition: function(sourceActor, alignment) {
|
||||
// We need to show it now to force an allocation,
|
||||
// so that we can query the correct size.
|
||||
this.actor.show();
|
||||
|
||||
this._sourceActor = sourceActor;
|
||||
this._gap = gap;
|
||||
this._alignment = alignment;
|
||||
|
||||
this._reposition(sourceActor, gap, alignment);
|
||||
this._reposition(sourceActor, alignment);
|
||||
},
|
||||
|
||||
_reposition: function(sourceActor, gap, alignment) {
|
||||
_reposition: function(sourceActor, alignment) {
|
||||
// Position correctly relative to the sourceActor
|
||||
let sourceNode = sourceActor.get_theme_node();
|
||||
let sourceContentBox = sourceNode.get_content_box(sourceActor.get_allocation_box());
|
||||
@ -338,6 +337,9 @@ BoxPointer.prototype = {
|
||||
let margin = (4 * borderRadius + borderWidth + arrowBase);
|
||||
let halfMargin = margin / 2;
|
||||
|
||||
let themeNode = this.actor.get_theme_node();
|
||||
let gap = themeNode.get_length('-boxpointer-gap');
|
||||
|
||||
let resX, resY;
|
||||
|
||||
switch (this._arrowSide) {
|
||||
|
@ -351,17 +351,16 @@ function Calendar(eventSource) {
|
||||
|
||||
Calendar.prototype = {
|
||||
_init: function(eventSource) {
|
||||
this._eventSource = eventSource;
|
||||
if (eventSource) {
|
||||
this._eventSource = eventSource;
|
||||
|
||||
this._eventSource.connect('changed', Lang.bind(this,
|
||||
function() {
|
||||
this._update(false);
|
||||
}));
|
||||
this._eventSource.connect('changed', Lang.bind(this,
|
||||
function() {
|
||||
this._update(false);
|
||||
}));
|
||||
}
|
||||
|
||||
// FIXME: This is actually the fallback method for GTK+ for the week start;
|
||||
// GTK+ by preference uses nl_langinfo (NL_TIME_FIRST_WEEKDAY). We probably
|
||||
// should add a C function so we can do the full handling.
|
||||
this._weekStart = NaN;
|
||||
this._weekStart = Shell.util_get_week_start();
|
||||
this._weekdate = NaN;
|
||||
this._digitWidth = NaN;
|
||||
this._settings = new Gio.Settings({ schema: 'org.gnome.shell.calendar' });
|
||||
@ -369,16 +368,6 @@ Calendar.prototype = {
|
||||
this._settings.connect('changed::' + SHOW_WEEKDATE_KEY, Lang.bind(this, this._onSettingsChange));
|
||||
this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY);
|
||||
|
||||
let weekStartString = Gettext_gtk30.gettext('calendar:week_start:0');
|
||||
if (weekStartString.indexOf('calendar:week_start:') == 0) {
|
||||
this._weekStart = parseInt(weekStartString.substring(20));
|
||||
}
|
||||
|
||||
if (isNaN(this._weekStart) || this._weekStart < 0 || this._weekStart > 6) {
|
||||
log('Translation of "calendar:week_start:0" in GTK+ is not correct');
|
||||
this._weekStart = 0;
|
||||
}
|
||||
|
||||
// Find the ordering for month/year in the calendar heading
|
||||
this._headerFormatWithoutYear = '%B';
|
||||
switch (Gettext_gtk30.gettext('calendar:MY')) {
|
||||
@ -567,13 +556,16 @@ Calendar.prototype = {
|
||||
while (true) {
|
||||
let button = new St.Button({ label: iter.getDate().toString() });
|
||||
|
||||
if (!this._eventSource)
|
||||
button.reactive = false;
|
||||
|
||||
let iterStr = iter.toUTCString();
|
||||
button.connect('clicked', Lang.bind(this, function() {
|
||||
let newlySelectedDate = new Date(iterStr);
|
||||
this.setDate(newlySelectedDate, false);
|
||||
}));
|
||||
|
||||
let hasEvents = this._eventSource.hasEvents(iter);
|
||||
let hasEvents = this._eventSource && this._eventSource.hasEvents(iter);
|
||||
let styleClass = 'calendar-day-base calendar-day';
|
||||
if (_isWorkDay(iter))
|
||||
styleClass += ' calendar-work-day'
|
||||
@ -620,7 +612,8 @@ Calendar.prototype = {
|
||||
}
|
||||
// Signal to the event source that we are interested in events
|
||||
// only from this date range
|
||||
this._eventSource.requestRange(beginDate, iter, forceReload);
|
||||
if (this._eventSource)
|
||||
this._eventSource.requestRange(beginDate, iter, forceReload);
|
||||
}
|
||||
};
|
||||
|
||||
@ -638,17 +631,7 @@ EventsList.prototype = {
|
||||
this._eventSource.connect('changed', Lang.bind(this, this._update));
|
||||
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
|
||||
this._desktopSettings.connect('changed', Lang.bind(this, this._update));
|
||||
let weekStartString = Gettext_gtk30.gettext('calendar:week_start:0');
|
||||
if (weekStartString.indexOf('calendar:week_start:') == 0) {
|
||||
this._weekStart = parseInt(weekStartString.substring(20));
|
||||
}
|
||||
|
||||
if (isNaN(this._weekStart) ||
|
||||
this._weekStart < 0 ||
|
||||
this._weekStart > 6) {
|
||||
log('Translation of "calendar:week_start:0" in GTK+ is not correct');
|
||||
this._weekStart = 0;
|
||||
}
|
||||
this._weekStart = Shell.util_get_week_start();
|
||||
|
||||
this._update();
|
||||
},
|
||||
@ -667,6 +650,9 @@ EventsList.prototype = {
|
||||
},
|
||||
|
||||
_addPeriod: function(header, begin, end, includeDayName, showNothingScheduled) {
|
||||
if (!this._eventSource)
|
||||
return;
|
||||
|
||||
let events = this._eventSource.getEvents(begin, end);
|
||||
|
||||
let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);;
|
||||
|
441
js/ui/chrome.js
441
js/ui/chrome.js
@ -1,441 +0,0 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const ScreenSaver = imports.misc.screenSaver;
|
||||
|
||||
// This manages the shell "chrome"; the UI that's visible in the
|
||||
// normal mode (ie, outside the Overview), that surrounds the main
|
||||
// workspace content.
|
||||
|
||||
const defaultParams = {
|
||||
visibleInFullscreen: false,
|
||||
affectsStruts: true,
|
||||
affectsInputRegion: true
|
||||
};
|
||||
|
||||
function Chrome() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
Chrome.prototype = {
|
||||
_init: function() {
|
||||
// The group itself has zero size so it doesn't interfere with DND
|
||||
this.actor = new Shell.GenericContainer({ width: 0, height: 0 });
|
||||
Main.uiGroup.add_actor(this.actor);
|
||||
this.actor.connect('allocate', Lang.bind(this, this._allocated));
|
||||
|
||||
this._monitors = [];
|
||||
this._inOverview = false;
|
||||
|
||||
this._trackedActors = [];
|
||||
|
||||
Main.layoutManager.connect('monitors-changed',
|
||||
Lang.bind(this, this._relayout));
|
||||
global.screen.connect('restacked',
|
||||
Lang.bind(this, this._windowsRestacked));
|
||||
|
||||
// Need to update struts on new workspaces when they are added
|
||||
global.screen.connect('notify::n-workspaces',
|
||||
Lang.bind(this, this._queueUpdateRegions));
|
||||
|
||||
Main.overview.connect('showing',
|
||||
Lang.bind(this, this._overviewShowing));
|
||||
Main.overview.connect('hidden',
|
||||
Lang.bind(this, this._overviewHidden));
|
||||
|
||||
this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy();
|
||||
this._screenSaverProxy.connect('ActiveChanged', Lang.bind(this, this._onScreenSaverActiveChanged));
|
||||
this._screenSaverProxy.GetActiveRemote(Lang.bind(this,
|
||||
function(result, err) {
|
||||
if (!err)
|
||||
this._onScreenSaverActiveChanged(this._screenSaverProxy, result);
|
||||
}));
|
||||
|
||||
this._relayout();
|
||||
},
|
||||
|
||||
_allocated: function(actor, box, flags) {
|
||||
let children = this.actor.get_children();
|
||||
for (let i = 0; i < children.length; i++)
|
||||
children[i].allocate_preferred_size(flags);
|
||||
},
|
||||
|
||||
// addActor:
|
||||
// @actor: an actor to add to the chrome layer
|
||||
// @params: (optional) additional params
|
||||
//
|
||||
// Adds @actor to the chrome layer and extends the input region
|
||||
// and window manager struts to include it. (Window manager struts
|
||||
// will only be affected if @actor is touching a screen edge.)
|
||||
// Changes in @actor's size and position will automatically result
|
||||
// in appropriate changes to the input region and struts. Changes
|
||||
// in its visibility will affect the input region, but NOT the
|
||||
// struts.
|
||||
//
|
||||
// If %visibleInFullscreen is %true, the actor will be visible
|
||||
// even when a fullscreen window should be covering it.
|
||||
//
|
||||
// If %affectsStruts or %affectsInputRegion is %false, the actor
|
||||
// will not have the indicated effect.
|
||||
addActor: function(actor, params) {
|
||||
this.actor.add_actor(actor);
|
||||
this._trackActor(actor, params);
|
||||
},
|
||||
|
||||
// trackActor:
|
||||
// @actor: a descendant of the chrome to begin tracking
|
||||
// @params: parameters describing how to track @actor
|
||||
//
|
||||
// Tells the chrome to track @actor, which must be a descendant
|
||||
// of an actor added via addActor(). This can be used to extend the
|
||||
// struts or input region to cover specific children.
|
||||
//
|
||||
// @params can have any of the same values as in addActor(), though
|
||||
// some possibilities don't make sense (eg, trying to have a
|
||||
// %visibleInFullscreen child of a non-%visibleInFullscreen parent).
|
||||
// By default, @actor has the same params as its chrome ancestor.
|
||||
trackActor: function(actor, params) {
|
||||
let ancestor = actor.get_parent();
|
||||
let index = this._findActor(ancestor);
|
||||
while (ancestor && index == -1) {
|
||||
ancestor = ancestor.get_parent();
|
||||
index = this._findActor(ancestor);
|
||||
}
|
||||
if (!ancestor)
|
||||
throw new Error('actor is not a descendent of the chrome layer');
|
||||
|
||||
let ancestorData = this._trackedActors[index];
|
||||
if (!params)
|
||||
params = {};
|
||||
// We can't use Params.parse here because we want to drop
|
||||
// the extra values like ancestorData.actor
|
||||
for (let prop in defaultParams) {
|
||||
if (!params[prop])
|
||||
params[prop] = ancestorData[prop];
|
||||
}
|
||||
|
||||
this._trackActor(actor, params);
|
||||
},
|
||||
|
||||
// untrackActor:
|
||||
// @actor: an actor previously tracked via trackActor()
|
||||
//
|
||||
// Undoes the effect of trackActor()
|
||||
untrackActor: function(actor) {
|
||||
this._untrackActor(actor);
|
||||
},
|
||||
|
||||
// removeActor:
|
||||
// @actor: a child of the chrome layer
|
||||
//
|
||||
// Removes @actor from the chrome layer
|
||||
removeActor: function(actor) {
|
||||
this.actor.remove_actor(actor);
|
||||
this._untrackActor(actor);
|
||||
},
|
||||
|
||||
_findActor: function(actor) {
|
||||
for (let i = 0; i < this._trackedActors.length; i++) {
|
||||
let actorData = this._trackedActors[i];
|
||||
if (actorData.actor == actor)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
|
||||
_trackActor: function(actor, params) {
|
||||
if (this._findActor(actor) != -1)
|
||||
throw new Error('trying to re-track existing chrome actor');
|
||||
|
||||
let actorData = Params.parse(params, defaultParams);
|
||||
actorData.actor = actor;
|
||||
actorData.visibleId = actor.connect('notify::visible',
|
||||
Lang.bind(this, this._queueUpdateRegions));
|
||||
actorData.allocationId = actor.connect('notify::allocation',
|
||||
Lang.bind(this, this._queueUpdateRegions));
|
||||
actorData.parentSetId = actor.connect('parent-set',
|
||||
Lang.bind(this, this._actorReparented));
|
||||
// Note that destroying actor will unset its parent, so we don't
|
||||
// need to connect to 'destroy' too.
|
||||
|
||||
this._trackedActors.push(actorData);
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_untrackActor: function(actor) {
|
||||
let i = this._findActor(actor);
|
||||
|
||||
if (i == -1)
|
||||
return;
|
||||
let actorData = this._trackedActors[i];
|
||||
|
||||
this._trackedActors.splice(i, 1);
|
||||
actor.disconnect(actorData.visibleId);
|
||||
actor.disconnect(actorData.allocationId);
|
||||
actor.disconnect(actorData.parentSetId);
|
||||
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_actorReparented: function(actor, oldParent) {
|
||||
if (!this.actor.contains(actor))
|
||||
this._untrackActor(actor);
|
||||
},
|
||||
|
||||
_updateVisibility: function() {
|
||||
for (let i = 0; i < this._trackedActors.length; i++) {
|
||||
let actorData = this._trackedActors[i];
|
||||
if (!this._inOverview && !actorData.visibleInFullscreen &&
|
||||
this._findMonitorForActor(actorData.actor).inFullscreen)
|
||||
this.actor.set_skip_paint(actorData.actor, true);
|
||||
else
|
||||
this.actor.set_skip_paint(actorData.actor, false);
|
||||
}
|
||||
},
|
||||
|
||||
_overviewShowing: function() {
|
||||
this._inOverview = true;
|
||||
this._updateVisibility();
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_overviewHidden: function() {
|
||||
this._inOverview = false;
|
||||
this._updateVisibility();
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_relayout: function() {
|
||||
this._monitors = Main.layoutManager.monitors;
|
||||
this._primaryMonitor = Main.layoutManager.primaryMonitor;
|
||||
|
||||
this._updateFullscreen();
|
||||
this._updateVisibility();
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_onScreenSaverActiveChanged: function(proxy, screenSaverActive) {
|
||||
this.actor.visible = !screenSaverActive;
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_findMonitorForRect: function(x, y, w, h) {
|
||||
// First look at what monitor the center of the rectangle is at
|
||||
let cx = x + w/2;
|
||||
let cy = y + h/2;
|
||||
for (let i = 0; i < this._monitors.length; i++) {
|
||||
let monitor = this._monitors[i];
|
||||
if (cx >= monitor.x && cx < monitor.x + monitor.width &&
|
||||
cy >= monitor.y && cy < monitor.y + monitor.height)
|
||||
return monitor;
|
||||
}
|
||||
// If the center is not on a monitor, return the first overlapping monitor
|
||||
for (let i = 0; i < this._monitors.length; i++) {
|
||||
let monitor = this._monitors[i];
|
||||
if (x + w > monitor.x && x < monitor.x + monitor.width &&
|
||||
y + h > monitor.y && y < monitor.y + monitor.height)
|
||||
return monitor;
|
||||
}
|
||||
// otherwise on no monitor
|
||||
return null;
|
||||
},
|
||||
|
||||
_findMonitorForWindow: function(window) {
|
||||
return this._findMonitorForRect(window.x, window.y, window.width, window.height);
|
||||
},
|
||||
|
||||
// This call guarantees that we return some monitor to simplify usage of it
|
||||
// In practice all tracked actors should be visible on some monitor anyway
|
||||
_findMonitorForActor: function(actor) {
|
||||
let [x, y] = actor.get_transformed_position();
|
||||
let [w, h] = actor.get_transformed_size();
|
||||
let monitor = this._findMonitorForRect(x, y, w, h);
|
||||
if (monitor)
|
||||
return monitor;
|
||||
return this._primaryMonitor; // Not on any monitor, pretend its on the primary
|
||||
},
|
||||
|
||||
_queueUpdateRegions: function() {
|
||||
if (!this._updateRegionIdle)
|
||||
this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this._updateRegions),
|
||||
Meta.PRIORITY_BEFORE_REDRAW);
|
||||
},
|
||||
|
||||
_updateFullscreen: function() {
|
||||
let windows = Main.getWindowActorsForWorkspace(global.screen.get_active_workspace_index());
|
||||
|
||||
// Reset all monitors to not fullscreen
|
||||
for (let i = 0; i < this._monitors.length; i++)
|
||||
this._monitors[i].inFullscreen = false;
|
||||
|
||||
// The chrome layer should be visible unless there is a window
|
||||
// with layer FULLSCREEN, or a window with layer
|
||||
// OVERRIDE_REDIRECT that covers the whole screen.
|
||||
// ('override_redirect' is not actually a layer above all
|
||||
// other windows, but this seems to be how mutter treats it
|
||||
// currently...) If we wanted to be extra clever, we could
|
||||
// figure out when an OVERRIDE_REDIRECT window was trying to
|
||||
// partially overlap us, and then adjust the input region and
|
||||
// our clip region accordingly...
|
||||
|
||||
// @windows is sorted bottom to top.
|
||||
|
||||
for (let i = windows.length - 1; i > -1; i--) {
|
||||
let window = windows[i];
|
||||
let layer = window.get_meta_window().get_layer();
|
||||
|
||||
if (layer == Meta.StackLayer.FULLSCREEN) {
|
||||
let monitor = this._findMonitorForWindow(window);
|
||||
if (monitor)
|
||||
monitor.inFullscreen = true;
|
||||
}
|
||||
if (layer == Meta.StackLayer.OVERRIDE_REDIRECT) {
|
||||
let monitor = this._findMonitorForWindow(window);
|
||||
if (monitor &&
|
||||
window.x <= monitor.x &&
|
||||
window.x + window.width >= monitor.x + monitor.width &&
|
||||
window.y <= monitor.y &&
|
||||
window.y + window.height >= monitor.y + monitor.height)
|
||||
monitor.inFullscreen = true;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_windowsRestacked: function() {
|
||||
let wasInFullscreen = [];
|
||||
for (let i = 0; i < this._monitors.length; i++)
|
||||
wasInFullscreen[i] = this._monitors[i].inFullscreen;
|
||||
|
||||
this._updateFullscreen();
|
||||
|
||||
let changed = false;
|
||||
for (let i = 0; i < wasInFullscreen.length; i++) {
|
||||
if (wasInFullscreen[i] != this._monitors[i].inFullscreen) {
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
this._updateVisibility();
|
||||
this._queueUpdateRegions();
|
||||
}
|
||||
},
|
||||
|
||||
_updateRegions: function() {
|
||||
let rects = [], struts = [], i;
|
||||
|
||||
delete this._updateRegionIdle;
|
||||
|
||||
for (i = 0; i < this._trackedActors.length; i++) {
|
||||
let actorData = this._trackedActors[i];
|
||||
if (!actorData.affectsInputRegion && !actorData.affectsStruts)
|
||||
continue;
|
||||
|
||||
let [x, y] = actorData.actor.get_transformed_position();
|
||||
let [w, h] = actorData.actor.get_transformed_size();
|
||||
x = Math.round(x);
|
||||
y = Math.round(y);
|
||||
w = Math.round(w);
|
||||
h = Math.round(h);
|
||||
let rect = new Meta.Rectangle({ x: x, y: y, width: w, height: h});
|
||||
|
||||
if (actorData.affectsInputRegion &&
|
||||
actorData.actor.get_paint_visibility() &&
|
||||
!this.actor.get_skip_paint(actorData.actor))
|
||||
rects.push(rect);
|
||||
|
||||
if (!actorData.affectsStruts)
|
||||
continue;
|
||||
|
||||
// Limit struts to the size of the screen
|
||||
let x1 = Math.max(x, 0);
|
||||
let x2 = Math.min(x + w, global.screen_width);
|
||||
let y1 = Math.max(y, 0);
|
||||
let y2 = Math.min(y + h, global.screen_height);
|
||||
|
||||
// NetWM struts are not really powerful enought to handle
|
||||
// a multi-monitor scenario, they only describe what happens
|
||||
// around the outer sides of the full display region. However
|
||||
// it can describe a partial region along each side, so
|
||||
// we can support having the struts only affect the
|
||||
// primary monitor. This should be enough as we only have
|
||||
// chrome affecting the struts on the primary monitor so
|
||||
// far.
|
||||
//
|
||||
// Metacity wants to know what side of the screen the
|
||||
// strut is considered to be attached to. If the actor is
|
||||
// only touching one edge, or is touching the entire
|
||||
// border of the primary monitor, then it's obvious which
|
||||
// side to call it. If it's in a corner, we pick a side
|
||||
// arbitrarily. If it doesn't touch any edges, or it spans
|
||||
// the width/height across the middle of the screen, then
|
||||
// we don't create a strut for it at all.
|
||||
let side;
|
||||
let primary = this._primaryMonitor;
|
||||
if (x1 <= primary.x && x2 >= primary.x + primary.width) {
|
||||
if (y1 <= primary.y)
|
||||
side = Meta.Side.TOP;
|
||||
else if (y2 >= primary.y + primary.height)
|
||||
side = Meta.Side.BOTTOM;
|
||||
else
|
||||
continue;
|
||||
} else if (y1 <= primary.y && y2 >= primary.y + primary.height) {
|
||||
if (x1 <= 0)
|
||||
side = Meta.Side.LEFT;
|
||||
else if (x2 >= global.screen_width)
|
||||
side = Meta.Side.RIGHT;
|
||||
else
|
||||
continue;
|
||||
} else if (x1 <= 0)
|
||||
side = Meta.Side.LEFT;
|
||||
else if (y1 <= 0)
|
||||
side = Meta.Side.TOP;
|
||||
else if (x2 >= global.screen_width)
|
||||
side = Meta.Side.RIGHT;
|
||||
else if (y2 >= global.screen_height)
|
||||
side = Meta.Side.BOTTOM;
|
||||
else
|
||||
continue;
|
||||
|
||||
// Ensure that the strut rects goes all the way to the screen edge,
|
||||
// as this really what mutter expects.
|
||||
switch (side) {
|
||||
case Meta.Side.TOP:
|
||||
y1 = 0;
|
||||
break;
|
||||
case Meta.Side.BOTTOM:
|
||||
y2 = global.screen_height;
|
||||
break;
|
||||
case Meta.Side.LEFT:
|
||||
x1 = 0;
|
||||
break;
|
||||
case Meta.Side.RIGHT:
|
||||
x2 = global.screen_width;
|
||||
break;
|
||||
}
|
||||
|
||||
let strutRect = new Meta.Rectangle({ x: x1, y: y1, width: x2 - x1, height: y2 - y1});
|
||||
let strut = new Meta.Strut({ rect: strutRect, side: side });
|
||||
struts.push(strut);
|
||||
}
|
||||
|
||||
global.set_stage_input_region(rects);
|
||||
|
||||
let screen = global.screen;
|
||||
for (let w = 0; w < screen.n_workspaces; w++) {
|
||||
let workspace = screen.get_workspace_by_index(w);
|
||||
workspace.set_builtin_struts(struts);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(Chrome.prototype);
|
179
js/ui/contactDisplay.js
Normal file
179
js/ui/contactDisplay.js
Normal file
@ -0,0 +1,179 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Folks = imports.gi.Folks
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Util = imports.misc.util;
|
||||
const IconGrid = imports.ui.iconGrid;
|
||||
const Search = imports.ui.search;
|
||||
const SearchDisplay = imports.ui.searchDisplay;
|
||||
|
||||
const MAX_SEARCH_RESULTS_ROWS = 1;
|
||||
const ICON_SIZE = 81;
|
||||
|
||||
function launchContact(id) {
|
||||
Util.spawn(['gnome-contacts', '-i', id]);
|
||||
}
|
||||
|
||||
|
||||
/* This class represents a shown contact search result in the overview */
|
||||
function Contact(id) {
|
||||
this._init(id);
|
||||
}
|
||||
|
||||
Contact.prototype = {
|
||||
_init: function(id) {
|
||||
this.individual = Shell.ContactSystem.get_default().get_individual(id);
|
||||
|
||||
this.actor = new St.Bin({ style_class: 'contact',
|
||||
reactive: true,
|
||||
track_hover: true });
|
||||
|
||||
let content = new St.BoxLayout( { style_class: 'contact-content',
|
||||
vertical: false });
|
||||
this.actor.set_child(content);
|
||||
|
||||
let icon = new St.Icon({ icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: ICON_SIZE,
|
||||
style_class: 'contact-icon' });
|
||||
if (this.individual.avatar != null)
|
||||
icon.gicon = this.individual.avatar;
|
||||
else
|
||||
icon.icon_name = 'avatar-default';
|
||||
|
||||
content.add(icon, { x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.MIDDLE });
|
||||
|
||||
let details = new St.BoxLayout({ style_class: 'contact-details',
|
||||
vertical: true });
|
||||
content.add(details, { x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.MIDDLE });
|
||||
|
||||
let aliasText = this.individual.alias || _("Unknown");
|
||||
let aliasLabel = new St.Label({ text: aliasText,
|
||||
style_class: 'contact-details-alias' });
|
||||
details.add(aliasLabel, { x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
|
||||
let presence = this._createPresence(this.individual.presence_type);
|
||||
details.add(presence, { x_fill: false,
|
||||
y_fill: true,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.END });
|
||||
},
|
||||
|
||||
_createPresence: function(presence) {
|
||||
let text;
|
||||
let iconName;
|
||||
|
||||
switch(presence) {
|
||||
case Folks.PresenceType.AVAILABLE:
|
||||
text = _("Available");
|
||||
iconName = 'user-available';
|
||||
break;
|
||||
case Folks.PresenceType.AWAY:
|
||||
case Folks.PresenceType.EXTENDED_AWAY:
|
||||
text = _("Away");
|
||||
iconName = 'user-away';
|
||||
break;
|
||||
case Folks.PresenceType.BUSY:
|
||||
text = _("Busy");
|
||||
iconName = 'user-busy';
|
||||
break;
|
||||
default:
|
||||
text = _("Offline");
|
||||
iconName = 'user-offline';
|
||||
}
|
||||
|
||||
let icon = new St.Icon({ icon_name: iconName,
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: 16,
|
||||
style_class: 'contact-details-status-icon' });
|
||||
let label = new St.Label({ text: text });
|
||||
|
||||
let box = new St.BoxLayout({ vertical: false,
|
||||
style_class: 'contact-details-status' });
|
||||
box.add(icon, { x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
|
||||
box.add(label, { x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.END,
|
||||
y_align: St.Align.START });
|
||||
|
||||
return box;
|
||||
},
|
||||
|
||||
createIcon: function(size) {
|
||||
let tc = St.TextureCache.get_default();
|
||||
let icon = this.individual.avatar;
|
||||
|
||||
if (icon != null) {
|
||||
return tc.load_gicon(null, icon, size);
|
||||
} else {
|
||||
return tc.load_icon_name(null, 'avatar-default', St.IconType.FULLCOLOR, size);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/* Searches for and returns contacts */
|
||||
function ContactSearchProvider() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
ContactSearchProvider.prototype = {
|
||||
__proto__: Search.SearchProvider.prototype,
|
||||
|
||||
_init: function() {
|
||||
Search.SearchProvider.prototype._init.call(this, _("CONTACTS"));
|
||||
this._contactSys = Shell.ContactSystem.get_default();
|
||||
},
|
||||
|
||||
getResultMeta: function(id) {
|
||||
let contact = new Contact(id);
|
||||
return { 'id': id,
|
||||
'name': contact.alias,
|
||||
'createIcon': function(size) {
|
||||
return contact.createIcon(size);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
getInitialResultSet: function(terms) {
|
||||
return this._contactSys.initial_search(terms);
|
||||
},
|
||||
|
||||
getSubsearchResultSet: function(previousResults, terms) {
|
||||
return this._contactSys.subsearch(previousResults, terms);
|
||||
},
|
||||
|
||||
createResultActor: function(resultMeta, terms) {
|
||||
let contact = new Contact(resultMeta.id);
|
||||
return contact.actor;
|
||||
},
|
||||
|
||||
createResultContainerActor: function() {
|
||||
let grid = new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
|
||||
xAlign: St.Align.START });
|
||||
grid.actor.style_class = 'contact-grid';
|
||||
|
||||
let actor = new SearchDisplay.GridSearchResults(this, grid);
|
||||
return actor;
|
||||
},
|
||||
|
||||
activateResult: function(id, params) {
|
||||
launchContact(id);
|
||||
}
|
||||
};
|
@ -207,7 +207,7 @@ RemoveFavoriteIcon.prototype = {
|
||||
let app = null;
|
||||
if (source instanceof AppDisplay.AppWellIcon) {
|
||||
let appSystem = Shell.AppSystem.get_default();
|
||||
app = appSystem.get_app(source.getId());
|
||||
app = appSystem.lookup_app(source.getId());
|
||||
} else if (source.metaWindow) {
|
||||
let tracker = Shell.WindowTracker.get_default();
|
||||
app = tracker.get_window_app(source.metaWindow);
|
||||
@ -330,7 +330,7 @@ Dash.prototype = {
|
||||
_onDragMotion: function(dragEvent) {
|
||||
let app = null;
|
||||
if (dragEvent.source instanceof AppDisplay.AppWellIcon)
|
||||
app = this._appSystem.get_app(dragEvent.source.getId());
|
||||
app = this._appSystem.lookup_app(dragEvent.source.getId());
|
||||
else if (dragEvent.source.metaWindow)
|
||||
app = this._tracker.get_window_app(dragEvent.source.metaWindow);
|
||||
else
|
||||
@ -437,6 +437,7 @@ Dash.prototype = {
|
||||
|
||||
let oldIconSize = this.iconSize;
|
||||
this.iconSize = newIconSize;
|
||||
this.emit('icon-size-changed');
|
||||
|
||||
let scale = oldIconSize / newIconSize;
|
||||
for (let i = 0; i < iconChildren.length; i++) {
|
||||
@ -619,12 +620,12 @@ Dash.prototype = {
|
||||
handleDragOver : function(source, actor, x, y, time) {
|
||||
let app = null;
|
||||
if (source instanceof AppDisplay.AppWellIcon)
|
||||
app = this._appSystem.get_app(source.getId());
|
||||
app = this._appSystem.lookup_app(source.getId());
|
||||
else if (source.metaWindow)
|
||||
app = this._tracker.get_window_app(source.metaWindow);
|
||||
|
||||
// Don't allow favoriting of transient apps
|
||||
if (app == null || app.is_transient())
|
||||
if (app == null || app.is_window_backed())
|
||||
return DND.DragMotionResult.NO_DROP;
|
||||
|
||||
let favorites = AppFavorites.getAppFavorites().getFavorites();
|
||||
@ -704,13 +705,13 @@ Dash.prototype = {
|
||||
acceptDrop : function(source, actor, x, y, time) {
|
||||
let app = null;
|
||||
if (source instanceof AppDisplay.AppWellIcon) {
|
||||
app = this._appSystem.get_app(source.getId());
|
||||
app = this._appSystem.lookup_app(source.getId());
|
||||
} else if (source.metaWindow) {
|
||||
app = this._tracker.get_window_app(source.metaWindow);
|
||||
}
|
||||
|
||||
// Don't allow favoriting of transient apps
|
||||
if (app == null || app.is_transient()) {
|
||||
if (app == null || app.is_window_backed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -9,11 +9,13 @@ const Clutter = imports.gi.Clutter;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Params = imports.misc.params;
|
||||
const Util = imports.misc.util;
|
||||
const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Calendar = imports.ui.calendar;
|
||||
const UPowerGlib = imports.gi.UPowerGlib;
|
||||
|
||||
// in org.gnome.desktop.interface
|
||||
const CLOCK_FORMAT_KEY = 'clock-format';
|
||||
@ -39,19 +41,19 @@ function _onVertSepRepaint (area)
|
||||
};
|
||||
|
||||
function DateMenuButton() {
|
||||
this._init();
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
DateMenuButton.prototype = {
|
||||
__proto__: PanelMenu.Button.prototype,
|
||||
|
||||
_init: function() {
|
||||
_init: function(params) {
|
||||
params = Params.parse(params, { showEvents: true });
|
||||
|
||||
let item;
|
||||
let hbox;
|
||||
let vbox;
|
||||
|
||||
this._eventSource = new Calendar.DBusEventSource();
|
||||
|
||||
let menuAlignment = 0.25;
|
||||
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
|
||||
menuAlignment = 1.0 - menuAlignment;
|
||||
@ -60,7 +62,7 @@ DateMenuButton.prototype = {
|
||||
this._clock = new St.Label();
|
||||
this.actor.set_child(this._clock);
|
||||
|
||||
hbox = new St.BoxLayout({name: 'calendarArea'});
|
||||
hbox = new St.BoxLayout({name: 'calendarArea' });
|
||||
this.menu.addActor(hbox);
|
||||
|
||||
// Fill up the first column
|
||||
@ -73,43 +75,58 @@ DateMenuButton.prototype = {
|
||||
this._date.style_class = 'datemenu-date-label';
|
||||
vbox.add(this._date);
|
||||
|
||||
this._eventList = new Calendar.EventsList(this._eventSource);
|
||||
if (params.showEvents) {
|
||||
this._eventSource = new Calendar.DBusEventSource();
|
||||
this._eventList = new Calendar.EventsList(this._eventSource);
|
||||
} else {
|
||||
this._eventSource = null;
|
||||
this._eventList = null;
|
||||
}
|
||||
|
||||
// Calendar
|
||||
this._calendar = new Calendar.Calendar(this._eventSource);
|
||||
|
||||
this._calendar.connect('selected-date-changed',
|
||||
Lang.bind(this, function(calendar, date) {
|
||||
// we know this._eventList is defined here, because selected-data-changed
|
||||
// only gets emitted when the user clicks a date in the calendar,
|
||||
// and the calender makes those dates unclickable when instantiated with
|
||||
// a null event source
|
||||
this._eventList.setDate(date);
|
||||
}));
|
||||
vbox.add(this._calendar.actor);
|
||||
|
||||
item = new PopupMenu.PopupSeparatorMenuItem();
|
||||
item.setColumnWidths(1);
|
||||
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||
item = new PopupMenu.PopupMenuItem(_("Date and Time Settings"));
|
||||
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
|
||||
item.actor.can_focus = false;
|
||||
vbox.add(item.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});
|
||||
|
||||
// Add vertical separator
|
||||
item.actor.can_focus = false;
|
||||
item.actor.reparent(vbox);
|
||||
}
|
||||
|
||||
item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
|
||||
pseudo_class: 'highlighted' });
|
||||
item.connect('repaint', Lang.bind(this, _onVertSepRepaint));
|
||||
hbox.add(item);
|
||||
if (params.showEvents) {
|
||||
// Add vertical separator
|
||||
|
||||
// Fill up the second column
|
||||
item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
|
||||
pseudo_class: 'highlighted' });
|
||||
item.connect('repaint', Lang.bind(this, _onVertSepRepaint));
|
||||
hbox.add(item);
|
||||
|
||||
vbox = new St.BoxLayout({vertical: true});
|
||||
hbox.add(vbox, { expand: true });
|
||||
// Fill up the second column
|
||||
vbox = new St.BoxLayout({name: 'calendarEventsArea',
|
||||
vertical: true});
|
||||
hbox.add(vbox, { expand: true });
|
||||
|
||||
// Event list
|
||||
vbox.add(this._eventList.actor, { expand: true });
|
||||
// Event list
|
||||
vbox.add(this._eventList.actor, { expand: true });
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Open Calendar"));
|
||||
item.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
|
||||
item.actor.can_focus = false;
|
||||
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||
item = new PopupMenu.PopupMenuItem(_("Open Calendar"));
|
||||
item.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
|
||||
item.actor.can_focus = false;
|
||||
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||
}
|
||||
|
||||
// Whenever the menu is opened, select today
|
||||
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
|
||||
@ -142,6 +159,10 @@ DateMenuButton.prototype = {
|
||||
this._desktopSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
|
||||
this._clockSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
|
||||
|
||||
// https://bugzilla.gnome.org/show_bug.cgi?id=655129
|
||||
this._upClient = new UPowerGlib.Client();
|
||||
this._upClient.connect('notify-resume', Lang.bind(this, this._updateClockAndDate));
|
||||
|
||||
// Start the clock
|
||||
this._updateClockAndDate();
|
||||
},
|
||||
@ -157,12 +178,12 @@ DateMenuButton.prototype = {
|
||||
switch (format) {
|
||||
case '24h':
|
||||
if (showDate)
|
||||
/* Translators: This is the time format with date used
|
||||
/* Translators: This is the time format with date used
|
||||
in 24-hour mode. */
|
||||
clockFormat = showSeconds ? _("%a %b %e, %R:%S")
|
||||
: _("%a %b %e, %R");
|
||||
else
|
||||
/* Translators: This is the time format without date used
|
||||
/* Translators: This is the time format without date used
|
||||
in 24-hour mode. */
|
||||
clockFormat = showSeconds ? _("%a %R:%S")
|
||||
: _("%a %R");
|
||||
@ -170,12 +191,12 @@ DateMenuButton.prototype = {
|
||||
case '12h':
|
||||
default:
|
||||
if (showDate)
|
||||
/* Translators: This is a time format with date used
|
||||
/* Translators: This is a time format with date used
|
||||
for AM/PM. */
|
||||
clockFormat = showSeconds ? _("%a %b %e, %l:%M:%S %p")
|
||||
: _("%a %b %e, %l:%M %p");
|
||||
else
|
||||
/* Translators: This is a time format without date used
|
||||
/* Translators: This is a time format without date used
|
||||
for AM/PM. */
|
||||
clockFormat = showSeconds ? _("%a %l:%M:%S %p")
|
||||
: _("%a %l:%M %p");
|
||||
@ -196,16 +217,26 @@ DateMenuButton.prototype = {
|
||||
return false;
|
||||
},
|
||||
|
||||
_onPreferencesActivate: function() {
|
||||
this.menu.close();
|
||||
Main.overview.hide();
|
||||
let app = Shell.AppSystem.get_default().get_app('gnome-datetime-panel.desktop');
|
||||
app.activate(-1);
|
||||
},
|
||||
|
||||
_onOpenCalendarActivate: function() {
|
||||
this.menu.close();
|
||||
// TODO: pass the selected day
|
||||
Util.spawn(['evolution', '-c', 'calendar']);
|
||||
let calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' });
|
||||
let tool = calendarSettings.get_string('exec');
|
||||
if (tool.length == 0 || tool == 'evolution') {
|
||||
// TODO: pass the selected day
|
||||
Util.spawn(['evolution', '-c', 'calendar']);
|
||||
} else {
|
||||
let needTerm = calendarSettings.get_boolean('needs-term');
|
||||
if (needTerm) {
|
||||
let terminalSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.terminal' });
|
||||
let term = terminalSettings.get_string('exec');
|
||||
let arg = terminalSettings.get_string('exec-arg');
|
||||
if (arg != '')
|
||||
Util.spawn([term, arg, tool]);
|
||||
else
|
||||
Util.spawn([term, tool]);
|
||||
} else {
|
||||
Util.spawnCommandLine(tool)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -30,11 +30,11 @@ DocSearchProvider.prototype = {
|
||||
},
|
||||
|
||||
activateResult: function(id, params) {
|
||||
params = Params.parse(params, { workspace: null,
|
||||
timestamp: null });
|
||||
params = Params.parse(params, { workspace: -1,
|
||||
timestamp: 0 });
|
||||
|
||||
let docInfo = this._docManager.lookupByUri(id);
|
||||
docInfo.launch(params.workspace ? params.workspace.index() : -1);
|
||||
docInfo.launch(params.workspace);
|
||||
},
|
||||
|
||||
getInitialResultSet: function(terms) {
|
||||
|
@ -22,8 +22,8 @@ const DBus = imports.dbus;
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const AccountsService = imports.gi.AccountsService;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gdm = imports.gi.Gdm;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Pango = imports.gi.Pango;
|
||||
@ -113,7 +113,7 @@ function findAppFromInhibitor(inhibitor) {
|
||||
let app = null;
|
||||
for (let i = 0; i < candidateDesktopFiles.length; i++) {
|
||||
try {
|
||||
app = appSystem.get_app(candidateDesktopFiles[i]);
|
||||
app = appSystem.lookup_app(candidateDesktopFiles[i]);
|
||||
|
||||
if (app)
|
||||
break;
|
||||
@ -173,7 +173,7 @@ ListItem.prototype = {
|
||||
|
||||
_onClicked: function() {
|
||||
this.emit('activate');
|
||||
this._app.activate(-1);
|
||||
this._app.activate();
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(ListItem.prototype);
|
||||
@ -237,7 +237,7 @@ EndSessionDialog.prototype = {
|
||||
_init: function() {
|
||||
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'end-session-dialog' });
|
||||
|
||||
this._user = Gdm.UserManager.ref_default().get_user(GLib.get_user_name());
|
||||
this._user = AccountsService.UserManager.get_default().get_user(GLib.get_user_name());
|
||||
|
||||
this._secondsLeft = 0;
|
||||
this._totalSecondsToStayOpen = 0;
|
||||
|
@ -1,9 +1,13 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Soup = imports.gi.Soup;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
|
||||
@ -11,7 +15,12 @@ const ExtensionState = {
|
||||
ENABLED: 1,
|
||||
DISABLED: 2,
|
||||
ERROR: 3,
|
||||
OUT_OF_DATE: 4
|
||||
OUT_OF_DATE: 4,
|
||||
DOWNLOADING: 5,
|
||||
|
||||
// Used as an error state for operations on unknown extensions,
|
||||
// should never be in a real extensionMeta object.
|
||||
UNINSTALLED: 99
|
||||
};
|
||||
|
||||
const ExtensionType = {
|
||||
@ -19,16 +28,40 @@ const ExtensionType = {
|
||||
PER_USER: 2
|
||||
};
|
||||
|
||||
const _httpSession = new Soup.SessionAsync();
|
||||
|
||||
// The unfortunate state of gjs, gobject-introspection and libsoup
|
||||
// means that I have to do a hack to add a feature.
|
||||
// See: https://bugzilla.gnome.org/show_bug.cgi?id=655189 for context.
|
||||
|
||||
if (Soup.Session.prototype.add_feature != null)
|
||||
Soup.Session.prototype.add_feature.call(_httpSession, new Soup.ProxyResolverDefault());
|
||||
|
||||
// Maps uuid -> metadata object
|
||||
const extensionMeta = {};
|
||||
// Maps uuid -> importer object (extension directory tree)
|
||||
const extensions = {};
|
||||
// Maps uuid -> extension state object (returned from init())
|
||||
const extensionStateObjs = {};
|
||||
// Arrays of uuids
|
||||
var disabledExtensions;
|
||||
var enabledExtensions;
|
||||
// GFile for user extensions
|
||||
var userExtensionsDir = null;
|
||||
|
||||
// We don't really have a class to add signals on. So, create
|
||||
// a simple dummy object, add the signal methods, and export those
|
||||
// publically.
|
||||
var _signals = {};
|
||||
Signals.addSignalMethods(_signals);
|
||||
|
||||
const connect = Lang.bind(_signals, _signals.connect);
|
||||
const disconnect = Lang.bind(_signals, _signals.disconnect);
|
||||
|
||||
// UUID => Array of error messages
|
||||
var errors = {};
|
||||
|
||||
const ENABLED_EXTENSIONS_KEY = 'enabled-extensions';
|
||||
|
||||
/**
|
||||
* versionCheck:
|
||||
* @required: an array of versions we're compatible with
|
||||
@ -59,13 +92,150 @@ function versionCheck(required, current) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function installExtensionFromManifestURL(uuid, url) {
|
||||
_httpSession.queue_message(
|
||||
Soup.Message.new('GET', url),
|
||||
function(session, message) {
|
||||
if (message.status_code != Soup.KnownStatusCode.OK) {
|
||||
logExtensionError(uuid, 'downloading manifest: ' + message.status_code.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
let manifest;
|
||||
try {
|
||||
manifest = JSON.parse(message.response_body.data);
|
||||
} catch (e) {
|
||||
logExtensionError(uuid, 'parsing: ' + e.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
if (uuid != manifest['uuid']) {
|
||||
logExtensionError(uuid, 'manifest: manifest uuids do not match');
|
||||
return;
|
||||
}
|
||||
|
||||
let meta = extensionMeta[uuid] = { uuid: uuid,
|
||||
state: ExtensionState.DOWNLOADING,
|
||||
error: '' };
|
||||
|
||||
_signals.emit('extension-state-changed', meta);
|
||||
|
||||
installExtensionFromManifest(manifest, meta);
|
||||
});
|
||||
}
|
||||
|
||||
function installExtensionFromManifest(manifest, meta) {
|
||||
let uuid = manifest['uuid'];
|
||||
let name = manifest['name'];
|
||||
|
||||
if (!versionCheck(manifest['shell-version'], Config.PACKAGE_VERSION)) {
|
||||
meta.state = ExtensionState.OUT_OF_DATE;
|
||||
logExtensionError(uuid, 'version: ' + name + ' is not compatible with current GNOME Shell version', meta.state);
|
||||
return;
|
||||
}
|
||||
|
||||
let url = manifest['__installer'];
|
||||
_httpSession.queue_message(Soup.Message.new('GET', url),
|
||||
function(session, message) {
|
||||
gotExtensionZipFile(session, message, uuid);
|
||||
});
|
||||
}
|
||||
|
||||
function gotExtensionZipFile(session, message, uuid) {
|
||||
if (message.status_code != Soup.KnownStatusCode.OK) {
|
||||
logExtensionError(uuid, 'downloading extension: ' + message.status_code);
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: use a GFile mkstemp-type method once one exists
|
||||
let fd, tmpzip;
|
||||
try {
|
||||
[fd, tmpzip] = GLib.file_open_tmp('XXXXXX.shell-extension.zip');
|
||||
} catch (e) {
|
||||
logExtensionError(uuid, 'tempfile: ' + e.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
let stream = new Gio.UnixOutputStream({ fd: fd });
|
||||
let dir = userExtensionsDir.get_child(uuid);
|
||||
Shell.write_soup_message_to_stream(stream, message);
|
||||
stream.close(null);
|
||||
let [success, pid] = GLib.spawn_async(null,
|
||||
['unzip', '-uod', dir.get_path(), '--', tmpzip],
|
||||
null,
|
||||
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
|
||||
null);
|
||||
|
||||
if (!success) {
|
||||
logExtensionError(uuid, 'extract: could not extract');
|
||||
return;
|
||||
}
|
||||
|
||||
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function(pid, status) {
|
||||
GLib.spawn_close_pid(pid);
|
||||
loadExtension(dir, true, ExtensionType.PER_USER);
|
||||
});
|
||||
}
|
||||
|
||||
function disableExtension(uuid) {
|
||||
let meta = extensionMeta[uuid];
|
||||
if (!meta)
|
||||
return;
|
||||
|
||||
if (meta.state != ExtensionState.ENABLED)
|
||||
return;
|
||||
|
||||
let extensionState = extensionStateObjs[uuid];
|
||||
|
||||
try {
|
||||
extensionState.disable();
|
||||
} catch(e) {
|
||||
logExtensionError(uuid, e.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
meta.state = ExtensionState.DISABLED;
|
||||
_signals.emit('extension-state-changed', meta);
|
||||
}
|
||||
|
||||
function enableExtension(uuid) {
|
||||
let meta = extensionMeta[uuid];
|
||||
if (!meta)
|
||||
return;
|
||||
|
||||
if (meta.state != ExtensionState.DISABLED)
|
||||
return;
|
||||
|
||||
let extensionState = extensionStateObjs[uuid];
|
||||
|
||||
try {
|
||||
extensionState.enable();
|
||||
} catch(e) {
|
||||
logExtensionError(uuid, e.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
meta.state = ExtensionState.ENABLED;
|
||||
_signals.emit('extension-state-changed', meta);
|
||||
}
|
||||
|
||||
function logExtensionError(uuid, message, state) {
|
||||
if (!errors[uuid]) errors[uuid] = [];
|
||||
errors[uuid].push(message);
|
||||
global.logError('Extension "%s" had error: %s'.format(uuid, message));
|
||||
state = state || ExtensionState.ERROR;
|
||||
_signals.emit('extension-state-changed', { uuid: uuid,
|
||||
error: message,
|
||||
state: state });
|
||||
}
|
||||
|
||||
function loadExtension(dir, enabled, type) {
|
||||
let info;
|
||||
let baseErrorString = 'While loading extension from "' + dir.get_parse_name() + '": ';
|
||||
let uuid = dir.get_basename();
|
||||
|
||||
let metadataFile = dir.get_child('metadata.json');
|
||||
if (!metadataFile.query_exists(null)) {
|
||||
global.logError(baseErrorString + 'Missing metadata.json');
|
||||
logExtensionError(uuid, 'Missing metadata.json');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -73,61 +243,65 @@ function loadExtension(dir, enabled, type) {
|
||||
try {
|
||||
metadataContents = Shell.get_file_contents_utf8_sync(metadataFile.get_path());
|
||||
} catch (e) {
|
||||
global.logError(baseErrorString + 'Failed to load metadata.json: ' + e);
|
||||
logExtensionError(uuid, 'Failed to load metadata.json: ' + e);
|
||||
return;
|
||||
}
|
||||
let meta;
|
||||
try {
|
||||
meta = JSON.parse(metadataContents);
|
||||
} catch (e) {
|
||||
global.logError(baseErrorString + 'Failed to parse metadata.json: ' + e);
|
||||
logExtensionError(uuid, 'Failed to parse metadata.json: ' + e);
|
||||
return;
|
||||
}
|
||||
|
||||
let requiredProperties = ['uuid', 'name', 'description', 'shell-version'];
|
||||
for (let i = 0; i < requiredProperties.length; i++) {
|
||||
let prop = requiredProperties[i];
|
||||
if (!meta[prop]) {
|
||||
global.logError(baseErrorString + 'missing "' + prop + '" property in metadata.json');
|
||||
logExtensionError(uuid, 'missing "' + prop + '" property in metadata.json');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (extensions[meta.uuid] != undefined) {
|
||||
global.logError(baseErrorString + "extension already loaded");
|
||||
if (extensions[uuid] != undefined) {
|
||||
logExtensionError(uuid, "extension already loaded");
|
||||
return;
|
||||
}
|
||||
|
||||
// Encourage people to add this
|
||||
if (!meta['url']) {
|
||||
global.log(baseErrorString + 'Warning: Missing "url" property in metadata.json');
|
||||
global.log('Warning: Missing "url" property in metadata.json');
|
||||
}
|
||||
|
||||
let base = dir.get_basename();
|
||||
if (base != meta.uuid) {
|
||||
global.logError(baseErrorString + 'uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + base + '"');
|
||||
if (uuid != meta.uuid) {
|
||||
logExtensionError(uuid, 'uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + uuid + '"');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!versionCheck(meta['shell-version'], Config.PACKAGE_VERSION) ||
|
||||
(meta['js-version'] && !versionCheck(meta['js-version'], Config.GJS_VERSION))) {
|
||||
global.logError(baseErrorString + 'extension is not compatible with current GNOME Shell and/or GJS version');
|
||||
logExtensionError(uuid, 'extension is not compatible with current GNOME Shell and/or GJS version');
|
||||
return;
|
||||
}
|
||||
|
||||
extensionMeta[meta.uuid] = meta;
|
||||
extensionMeta[meta.uuid].type = type;
|
||||
extensionMeta[meta.uuid].path = dir.get_path();
|
||||
if (!enabled) {
|
||||
extensionMeta[meta.uuid].state = ExtensionState.DISABLED;
|
||||
return;
|
||||
}
|
||||
extensionMeta[uuid] = meta;
|
||||
meta.type = type;
|
||||
meta.path = dir.get_path();
|
||||
meta.error = '';
|
||||
|
||||
// Default to error, we set success as the last step
|
||||
extensionMeta[meta.uuid].state = ExtensionState.ERROR;
|
||||
meta.state = ExtensionState.ERROR;
|
||||
|
||||
if (!versionCheck(meta['shell-version'], Config.PACKAGE_VERSION) ||
|
||||
(meta['js-version'] && !versionCheck(meta['js-version'], Config.GJS_VERSION))) {
|
||||
logExtensionError(uuid, 'extension is not compatible with current GNOME Shell and/or GJS version', ExtensionState.OUT_OF_DATE);
|
||||
meta.state = ExtensionState.OUT_OF_DATE;
|
||||
return;
|
||||
}
|
||||
|
||||
let extensionJs = dir.get_child('extension.js');
|
||||
if (!extensionJs.query_exists(null)) {
|
||||
global.logError(baseErrorString + 'Missing extension.js');
|
||||
logExtensionError(uuid, 'Missing extension.js');
|
||||
return;
|
||||
}
|
||||
let stylesheetPath = null;
|
||||
@ -138,48 +312,94 @@ function loadExtension(dir, enabled, type) {
|
||||
try {
|
||||
theme.load_stylesheet(stylesheetFile.get_path());
|
||||
} catch (e) {
|
||||
global.logError(baseErrorString + 'Stylesheet parse error: ' + e);
|
||||
logExtensionError(uuid, 'Stylesheet parse error: ' + e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let extensionModule;
|
||||
let extensionState = null;
|
||||
try {
|
||||
global.add_extension_importer('imports.ui.extensionSystem.extensions', meta.uuid, dir.get_path());
|
||||
extensionModule = extensions[meta.uuid].extension;
|
||||
} catch (e) {
|
||||
if (stylesheetPath != null)
|
||||
theme.unload_stylesheet(stylesheetPath);
|
||||
global.logError(baseErrorString + e);
|
||||
logExtensionError(uuid, e);
|
||||
return;
|
||||
}
|
||||
if (!extensionModule.main) {
|
||||
global.logError(baseErrorString + 'missing \'main\' function');
|
||||
|
||||
if (!extensionModule.init) {
|
||||
logExtensionError(uuid, 'missing \'init\' function');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
extensionModule.main(meta);
|
||||
extensionState = extensionModule.init(meta);
|
||||
} catch (e) {
|
||||
if (stylesheetPath != null)
|
||||
theme.unload_stylesheet(stylesheetPath);
|
||||
global.logError(baseErrorString + 'Failed to evaluate main function:' + e);
|
||||
logExtensionError(uuid, 'Failed to evaluate init function:' + e);
|
||||
return;
|
||||
}
|
||||
extensionMeta[meta.uuid].state = ExtensionState.ENABLED;
|
||||
|
||||
if (!extensionState)
|
||||
extensionState = extensionModule;
|
||||
extensionStateObjs[uuid] = extensionState;
|
||||
|
||||
if (!extensionState.enable) {
|
||||
logExtensionError(uuid, 'missing \'enable\' function');
|
||||
return;
|
||||
}
|
||||
if (!extensionState.disable) {
|
||||
logExtensionError(uuid, 'missing \'disable\' function');
|
||||
return;
|
||||
}
|
||||
|
||||
meta.state = ExtensionState.DISABLED;
|
||||
|
||||
if (enabled)
|
||||
enableExtension(uuid);
|
||||
|
||||
_signals.emit('extension-loaded', meta.uuid);
|
||||
_signals.emit('extension-state-changed', meta);
|
||||
global.log('Loaded extension ' + meta.uuid);
|
||||
}
|
||||
|
||||
function onEnabledExtensionsChanged() {
|
||||
let newEnabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
|
||||
|
||||
// Find and enable all the newly enabled extensions: UUIDs found in the
|
||||
// new setting, but not in the old one.
|
||||
newEnabledExtensions.filter(function(uuid) {
|
||||
return enabledExtensions.indexOf(uuid) == -1;
|
||||
}).forEach(function(uuid) {
|
||||
enableExtension(uuid);
|
||||
});
|
||||
|
||||
// Find and disable all the newly disabled extensions: UUIDs found in the
|
||||
// old setting, but not in the new one.
|
||||
enabledExtensions.filter(function(item) {
|
||||
return newEnabledExtensions.indexOf(item) == -1;
|
||||
}).forEach(function(uuid) {
|
||||
disableExtension(uuid);
|
||||
});
|
||||
|
||||
enabledExtensions = newEnabledExtensions;
|
||||
}
|
||||
|
||||
function init() {
|
||||
let userExtensionsPath = GLib.build_filenamev([global.userdatadir, 'extensions']);
|
||||
userExtensionsDir = Gio.file_new_for_path(userExtensionsPath);
|
||||
try {
|
||||
userExtensionsDir.make_directory_with_parents(null);
|
||||
if (!userExtensionsDir.query_exists(null))
|
||||
userExtensionsDir.make_directory_with_parents(null);
|
||||
} catch (e) {
|
||||
global.logError('' + e);
|
||||
}
|
||||
|
||||
disabledExtensions = global.settings.get_strv('disabled-extensions', -1);
|
||||
enabledExtensions = global.settings.get_strv('enabled-extensions', -1);
|
||||
global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged);
|
||||
enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
|
||||
}
|
||||
|
||||
function _loadExtensionsIn(dir, type) {
|
||||
@ -197,11 +417,8 @@ function _loadExtensionsIn(dir, type) {
|
||||
if (fileType != Gio.FileType.DIRECTORY)
|
||||
continue;
|
||||
let name = info.get_name();
|
||||
// Enable all but disabled extensions if enabledExtensions is not set.
|
||||
// If it is set, enable one those, except they are disabled as well.
|
||||
let enabled = (enabledExtensions.length == 0 || enabledExtensions.indexOf(name) >= 0)
|
||||
&& disabledExtensions.indexOf(name) < 0;
|
||||
let child = dir.get_child(name);
|
||||
let enabled = enabledExtensions.indexOf(name) != -1;
|
||||
loadExtension(child, enabled, type);
|
||||
}
|
||||
fileEnum.close(null);
|
||||
|
532
js/ui/keyboard.js
Normal file
532
js/ui/keyboard.js
Normal file
@ -0,0 +1,532 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
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 St = imports.gi.St;
|
||||
|
||||
const BoxPointer = imports.ui.boxpointer;
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
|
||||
const KEYBOARD_SCHEMA = 'org.gnome.shell.keyboard';
|
||||
const SHOW_KEYBOARD = 'show-keyboard';
|
||||
const KEYBOARD_TYPE = 'keyboard-type';
|
||||
|
||||
// Key constants taken from Antler
|
||||
// FIXME: ought to be moved into libcaribou
|
||||
const PRETTY_KEYS = {
|
||||
'BackSpace': '\u232b',
|
||||
'space': ' ',
|
||||
'Return': '\u23ce',
|
||||
'Caribou_Prefs': '\u2328',
|
||||
'Caribou_ShiftUp': '\u2b06',
|
||||
'Caribou_ShiftDown': '\u2b07',
|
||||
'Caribou_Emoticons': '\u263a',
|
||||
'Caribou_Symbols': '123',
|
||||
'Caribou_Symbols_More': '{#*',
|
||||
'Caribou_Alpha': 'Abc',
|
||||
'Tab': 'Tab',
|
||||
'Escape': 'Esc',
|
||||
'Control_L': 'Ctrl',
|
||||
'Alt_L': 'Alt'
|
||||
};
|
||||
|
||||
const CaribouKeyboardIface = {
|
||||
name: 'org.gnome.Caribou.Keyboard',
|
||||
methods: [ { name: 'Show',
|
||||
inSignature: 'u',
|
||||
outSignature: ''
|
||||
},
|
||||
{ name: 'Hide',
|
||||
inSignature: 'u',
|
||||
outSignature: ''
|
||||
},
|
||||
{ name: 'SetCursorLocation',
|
||||
inSignature: 'iiii',
|
||||
outSignature: ''
|
||||
},
|
||||
{ name: 'SetEntryLocation',
|
||||
inSignature: 'iiii',
|
||||
outSignature: ''
|
||||
} ],
|
||||
properties: [ { name: 'Name',
|
||||
signature: 's',
|
||||
access: 'read' } ]
|
||||
};
|
||||
|
||||
function Key() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
Key.prototype = {
|
||||
_init : function(key) {
|
||||
this._key = key;
|
||||
|
||||
this.actor = this._makeKey();
|
||||
|
||||
this._extended_keys = this._key.get_extended_keys();
|
||||
this._extended_keyboard = null;
|
||||
|
||||
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,
|
||||
y_fill: true,
|
||||
x_align: St.Align.START });
|
||||
// Adds style to existing keyboard style to avoid repetition
|
||||
this._boxPointer.actor.add_style_class_name('keyboard-subkeys');
|
||||
this._getExtendedKeys();
|
||||
this.actor._extended_keys = this._extended_keyboard;
|
||||
this._boxPointer.actor.hide();
|
||||
Main.layoutManager.addChrome(this._boxPointer.actor, { visibleInFullscreen: true });
|
||||
}
|
||||
},
|
||||
|
||||
_makeKey: function () {
|
||||
let label = this._key.name;
|
||||
|
||||
if (label.length > 1) {
|
||||
let pretty = PRETTY_KEYS[label];
|
||||
if (pretty)
|
||||
label = pretty;
|
||||
else
|
||||
label = this._getUnichar(this._key);
|
||||
}
|
||||
|
||||
label = GLib.markup_escape_text(label, -1);
|
||||
let button = new St.Button ({ label: label,
|
||||
style_class: 'keyboard-key' });
|
||||
|
||||
button.key_width = this._key.width;
|
||||
button.connect('button-press-event', Lang.bind(this, function () { this._key.press(); }));
|
||||
button.connect('button-release-event', Lang.bind(this, function () { this._key.release(); }));
|
||||
|
||||
return button;
|
||||
},
|
||||
|
||||
_getUnichar: function(key) {
|
||||
let keyval = key.keyval;
|
||||
let unichar = Gdk.keyval_to_unicode(keyval);
|
||||
if (unichar) {
|
||||
return String.fromCharCode(unichar);
|
||||
} else {
|
||||
return key.name;
|
||||
}
|
||||
},
|
||||
|
||||
_getExtendedKeys: function () {
|
||||
this._extended_keyboard = new St.BoxLayout({ style_class: 'keyboard-layout',
|
||||
vertical: false });
|
||||
for (let i = 0; i < this._extended_keys.length; ++i) {
|
||||
let extended_key = this._extended_keys[i];
|
||||
let label = this._getUnichar(extended_key);
|
||||
let key = new St.Button({ label: label, style_class: 'keyboard-key' });
|
||||
key.extended_key = extended_key;
|
||||
key.connect('button-press-event', Lang.bind(this, function () { extended_key.press(); }));
|
||||
key.connect('button-release-event', Lang.bind(this, function () { extended_key.release(); }));
|
||||
this._extended_keyboard.add(key);
|
||||
}
|
||||
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);
|
||||
},
|
||||
|
||||
_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(true);
|
||||
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(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function Keyboard() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
Keyboard.prototype = {
|
||||
_init: function () {
|
||||
DBus.session.exportObject('/org/gnome/Caribou/Keyboard', this);
|
||||
DBus.session.acquire_name('org.gnome.Caribou.Keyboard', 0, null, null);
|
||||
|
||||
this._timestamp = global.get_current_time();
|
||||
this.actor = new St.BoxLayout({ name: 'keyboard', vertical: true, reactive: true });
|
||||
Main.layoutManager.keyboardBox.add_actor(this.actor);
|
||||
Main.layoutManager.trackChrome(this.actor);
|
||||
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._settingsChanged();
|
||||
},
|
||||
|
||||
init: function () {
|
||||
if (this._enableKeyboard)
|
||||
this._redraw();
|
||||
},
|
||||
|
||||
_settingsChanged: function () {
|
||||
this._enableKeyboard = this._keyboardSettings.get_boolean(SHOW_KEYBOARD);
|
||||
if (!this._enableKeyboard && !this._keyboard)
|
||||
return;
|
||||
if (this._enableKeyboard && this._keyboard &&
|
||||
this._keyboard.keyboard_type == this._keyboardSettings.get_string(KEYBOARD_TYPE))
|
||||
return;
|
||||
|
||||
if (this._keyboard)
|
||||
this._destroyKeyboard();
|
||||
if (this._enableKeyboard)
|
||||
this._setupKeyboard();
|
||||
else
|
||||
Main.layoutManager.hideKeyboard(true);
|
||||
},
|
||||
|
||||
_destroyKeyboard: function() {
|
||||
if (this._keyboardNotifyId)
|
||||
this._keyboard.disconnect(this._keyboardNotifyId);
|
||||
if (this._focusNotifyId)
|
||||
global.stage.disconnect(this._focusNotifyId);
|
||||
this._keyboard = null;
|
||||
this.actor.destroy_children();
|
||||
|
||||
this._destroySource();
|
||||
},
|
||||
|
||||
_setupKeyboard: function() {
|
||||
this._keyboard = new Caribou.KeyboardModel({ keyboard_type: this._keyboardSettings.get_string(KEYBOARD_TYPE) });
|
||||
this._groups = {};
|
||||
this._current_page = null;
|
||||
|
||||
// Initialize keyboard key measurements
|
||||
this._numOfHorizKeys = 0;
|
||||
this._numOfVertKeys = 0;
|
||||
|
||||
this._addKeys();
|
||||
|
||||
this._keyboardNotifyId = this._keyboard.connect('notify::active-group', Lang.bind(this, this._onGroupChanged));
|
||||
this._focusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
|
||||
this._createSource();
|
||||
},
|
||||
|
||||
_onKeyFocusChanged: function () {
|
||||
let focus = global.stage.key_focus;
|
||||
|
||||
if (focus == global.stage || focus == null)
|
||||
return;
|
||||
|
||||
if (focus instanceof Clutter.Text)
|
||||
this.show();
|
||||
else {
|
||||
if (focus._extended_keys == null)
|
||||
this.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_addKeys: function () {
|
||||
let groups = this._keyboard.get_groups();
|
||||
for (let i = 0; i < groups.length; ++i) {
|
||||
let gname = groups[i];
|
||||
let group = this._keyboard.get_group(gname);
|
||||
group.connect('notify::active-level', Lang.bind(this, this._onLevelChanged));
|
||||
let layers = {};
|
||||
let levels = group.get_levels();
|
||||
for (let j = 0; j < levels.length; ++j) {
|
||||
let lname = levels[j];
|
||||
let level = group.get_level(lname);
|
||||
let layout = new St.BoxLayout({ style_class: 'keyboard-layout',
|
||||
vertical: true });
|
||||
this._loadRows(level, layout);
|
||||
layers[lname] = layout;
|
||||
this.actor.add(layout, { x_fill: false });
|
||||
|
||||
layout.hide();
|
||||
}
|
||||
this._groups[gname] = layers;
|
||||
}
|
||||
|
||||
this._setActiveLayer();
|
||||
},
|
||||
|
||||
_getTrayIcon: function () {
|
||||
let trayButton = new St.Button ({ label: "tray", style_class: 'keyboard-key' });
|
||||
trayButton.key_width = 1;
|
||||
trayButton.connect('button-press-event', Lang.bind(this, function () {
|
||||
Main.messageTray.toggle();
|
||||
}));
|
||||
|
||||
Main.overview.connect('showing', Lang.bind(this, function () {
|
||||
trayButton.reactive = false;
|
||||
trayButton.add_style_pseudo_class('grayed');
|
||||
}));
|
||||
Main.overview.connect('hiding', Lang.bind(this, function () {
|
||||
trayButton.reactive = true;
|
||||
trayButton.remove_style_pseudo_class('grayed');
|
||||
}));
|
||||
|
||||
return trayButton;
|
||||
},
|
||||
|
||||
_addRows : function (keys, layout) {
|
||||
let keyboard_row = new St.BoxLayout();
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
let children = keys[i].get_children();
|
||||
let right_box = new St.BoxLayout({ style_class: 'keyboard-row' });
|
||||
let left_box = new St.BoxLayout({ style_class: 'keyboard-row' });
|
||||
for (let j = 0; j < children.length; ++j) {
|
||||
if (this._numOfHorizKeys == 0)
|
||||
this._numOfHorizKeys = children.length;
|
||||
let key = children[j];
|
||||
let button = new Key(key);
|
||||
|
||||
if (key.align == 'right')
|
||||
right_box.add(button.actor);
|
||||
else
|
||||
left_box.add(button.actor);
|
||||
if (key.name == "Caribou_Prefs") {
|
||||
key.connect('key-released', Lang.bind(this, this.hide));
|
||||
|
||||
// Add new key for hiding message tray
|
||||
right_box.add(this._getTrayIcon());
|
||||
}
|
||||
}
|
||||
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 });
|
||||
}
|
||||
layout.add(keyboard_row);
|
||||
},
|
||||
|
||||
_loadRows : function (level, layout) {
|
||||
let rows = level.get_rows();
|
||||
for (let i = 0; i < rows.length; ++i) {
|
||||
let row = rows[i];
|
||||
if (this._numOfVertKeys == 0)
|
||||
this._numOfVertKeys = rows.length;
|
||||
this._addRows(row.get_columns(), layout);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
_redraw: function () {
|
||||
let monitor = Main.layoutManager.bottomMonitor;
|
||||
let maxHeight = monitor.height / 3;
|
||||
this.actor.width = monitor.width;
|
||||
|
||||
let layout = this._current_page;
|
||||
let verticalSpacing = layout.get_theme_node().get_length('spacing');
|
||||
let padding = layout.get_theme_node().get_length('padding');
|
||||
|
||||
let box = layout.get_children()[0].get_children()[0];
|
||||
let horizontalSpacing = box.get_theme_node().get_length('spacing');
|
||||
let allHorizontalSpacing = (this._numOfHorizKeys - 1) * horizontalSpacing;
|
||||
let keyWidth = Math.floor((this.actor.width - allHorizontalSpacing - 2 * padding) / this._numOfHorizKeys);
|
||||
|
||||
let allVerticalSpacing = (this._numOfVertKeys - 1) * verticalSpacing;
|
||||
let keyHeight = Math.floor((maxHeight - allVerticalSpacing - 2 * padding) / this._numOfVertKeys);
|
||||
|
||||
let keySize = Math.min(keyWidth, keyHeight);
|
||||
this.actor.height = keySize * this._numOfVertKeys + allVerticalSpacing + 2 * padding;
|
||||
|
||||
let rows = this._current_page.get_children();
|
||||
for (let i = 0; i < rows.length; ++i) {
|
||||
let keyboard_row = rows[i];
|
||||
let boxes = keyboard_row.get_children();
|
||||
for (let j = 0; j < boxes.length; ++j) {
|
||||
let keys = boxes[j].get_children();
|
||||
for (let k = 0; k < keys.length; ++k) {
|
||||
let child = keys[k];
|
||||
child.width = keySize * child.key_width;
|
||||
child.height = keySize;
|
||||
if (child._extended_keys) {
|
||||
let extended_keys = child._extended_keys.get_children();
|
||||
for (let n = 0; n < extended_keys.length; ++n) {
|
||||
let extended_key = extended_keys[n];
|
||||
extended_key.width = keySize;
|
||||
extended_key.height = keySize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_onLevelChanged: function () {
|
||||
this._setActiveLayer();
|
||||
this._redraw();
|
||||
},
|
||||
|
||||
_onGroupChanged: function () {
|
||||
this._setActiveLayer();
|
||||
this._redraw();
|
||||
},
|
||||
|
||||
_setActiveLayer: function () {
|
||||
let active_group_name = this._keyboard.active_group;
|
||||
let active_group = this._keyboard.get_group(active_group_name);
|
||||
let active_level = active_group.active_level;
|
||||
let layers = this._groups[active_group_name];
|
||||
|
||||
if (this._current_page != null) {
|
||||
this._current_page.hide();
|
||||
}
|
||||
|
||||
this._current_page = layers[active_level];
|
||||
this._current_page.show();
|
||||
},
|
||||
|
||||
_createSource: function () {
|
||||
if (this._source == null) {
|
||||
this._source = new KeyboardSource(this);
|
||||
this._source.setTransient(true);
|
||||
Main.messageTray.add(this._source);
|
||||
}
|
||||
},
|
||||
|
||||
_destroySource: function () {
|
||||
if (this._source) {
|
||||
this._source.destroy();
|
||||
this._source = null;
|
||||
}
|
||||
},
|
||||
|
||||
show: function () {
|
||||
this._redraw();
|
||||
|
||||
Main.layoutManager.showKeyboard();
|
||||
this._destroySource();
|
||||
},
|
||||
|
||||
hide: function () {
|
||||
Main.layoutManager.hideKeyboard();
|
||||
this._createSource();
|
||||
},
|
||||
|
||||
_moveTemporarily: function () {
|
||||
let currentWindow = global.screen.get_display().focus_window;
|
||||
let rect = currentWindow.get_outer_rect();
|
||||
|
||||
let newX = rect.x;
|
||||
let newY = 3 * this.actor.height / 2;
|
||||
currentWindow.move_frame(true, newX, newY);
|
||||
},
|
||||
|
||||
_setLocation: function (x, y) {
|
||||
if (y >= 2 * this.actor.height)
|
||||
this._moveTemporarily();
|
||||
},
|
||||
|
||||
// D-Bus methods
|
||||
Show: function(timestamp) {
|
||||
if (timestamp - this._timestamp < 0)
|
||||
return;
|
||||
|
||||
this._timestamp = timestamp;
|
||||
this.show();
|
||||
},
|
||||
|
||||
Hide: function(timestamp) {
|
||||
if (timestamp - this._timestamp < 0)
|
||||
return;
|
||||
|
||||
this._timestamp = timestamp;
|
||||
this.hide();
|
||||
},
|
||||
|
||||
SetCursorLocation: function(x, y, w, h) {
|
||||
this._setLocation(x, y);
|
||||
},
|
||||
|
||||
SetEntryLocation: function(x, y, w, h) {
|
||||
this._setLocation(x, y);
|
||||
},
|
||||
|
||||
get Name() {
|
||||
return 'gnome-shell';
|
||||
}
|
||||
};
|
||||
DBus.conformExport(Keyboard.prototype, CaribouKeyboardIface);
|
||||
|
||||
function KeyboardSource() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
KeyboardSource.prototype = {
|
||||
__proto__: MessageTray.Source.prototype,
|
||||
|
||||
_init: function(keyboard) {
|
||||
this._keyboard = keyboard;
|
||||
MessageTray.Source.prototype._init.call(this, _("Keyboard"));
|
||||
|
||||
this._setSummaryIcon(this.createNotificationIcon());
|
||||
},
|
||||
|
||||
createNotificationIcon: function() {
|
||||
return new St.Icon({ icon_name: 'input-keyboard',
|
||||
icon_type: St.IconType.SYMBOLIC,
|
||||
icon_size: this.ICON_SIZE });
|
||||
},
|
||||
|
||||
handleSummaryClick: function() {
|
||||
let event = Clutter.get_current_event();
|
||||
if (event.type() != Clutter.EventType.BUTTON_RELEASE)
|
||||
return false;
|
||||
|
||||
this.open();
|
||||
return true;
|
||||
},
|
||||
|
||||
open: function() {
|
||||
this._keyboard.show();
|
||||
}
|
||||
};
|
653
js/ui/layout.js
653
js/ui/layout.js
@ -2,13 +2,20 @@
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
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 Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const ScreenSaver = imports.misc.screenSaver;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
|
||||
const STARTUP_ANIMATION_TIME = 0.2;
|
||||
const KEYBOARD_ANIMATION_TIME = 0.5;
|
||||
|
||||
function LayoutManager() {
|
||||
this._init.apply(this, arguments);
|
||||
@ -21,16 +28,45 @@ LayoutManager.prototype = {
|
||||
this.primaryMonitor = null;
|
||||
this.primaryIndex = -1;
|
||||
this._hotCorners = [];
|
||||
this._leftPanelBarrier = 0;
|
||||
this._rightPanelBarrier = 0;
|
||||
this._trayBarrier = 0;
|
||||
|
||||
this._updateMonitors();
|
||||
this._chrome = new Chrome(this);
|
||||
|
||||
this.panelBox = new St.BoxLayout({ name: 'panelBox',
|
||||
vertical: true });
|
||||
this.addChrome(this.panelBox, { affectsStruts: true });
|
||||
this.panelBox.connect('allocation-changed',
|
||||
Lang.bind(this, this._updatePanelBarriers));
|
||||
|
||||
// bottomBox contains the tray and keyboard (which move
|
||||
// together, since the tray slides up from the top of the
|
||||
// keyboard when the keyboard is visible).
|
||||
this._bottomBox = new St.BoxLayout({ name: 'bottomBox',
|
||||
vertical: true });
|
||||
this.addChrome(this._bottomBox, { visibleInFullscreen: true });
|
||||
|
||||
this.trayBox = new St.BoxLayout({ name: 'trayBox' });
|
||||
this.trayBox.connect('allocation-changed',
|
||||
Lang.bind(this, this._updateTrayBarrier));
|
||||
this._bottomBox.add_actor(this.trayBox);
|
||||
|
||||
this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox' });
|
||||
this._bottomBox.add_actor(this.keyboardBox);
|
||||
|
||||
global.screen.connect('monitors-changed',
|
||||
Lang.bind(this, this._monitorsChanged));
|
||||
this._monitorsChanged();
|
||||
},
|
||||
|
||||
// This is called by Main after everything else is constructed;
|
||||
// _updateHotCorners needs access to Main.panel, which didn't exist
|
||||
// Chrome.init() needs access to Main.overview, which didn't exist
|
||||
// yet when the LayoutManager was constructed.
|
||||
init: function() {
|
||||
global.screen.connect('monitors-changed', Lang.bind(this, this._monitorsChanged));
|
||||
this._updateHotCorners();
|
||||
this._chrome.init();
|
||||
|
||||
this._startupAnimation();
|
||||
},
|
||||
|
||||
_updateMonitors: function() {
|
||||
@ -109,12 +145,69 @@ LayoutManager.prototype = {
|
||||
let corner = new HotCorner();
|
||||
this._hotCorners.push(corner);
|
||||
corner.actor.set_position(cornerX, cornerY);
|
||||
Main.chrome.addActor(corner.actor, { affectsStruts: false });
|
||||
this._chrome.addActor(corner.actor);
|
||||
}
|
||||
},
|
||||
|
||||
_updateBoxes: function() {
|
||||
this.panelBox.set_position(this.primaryMonitor.x, this.primaryMonitor.y);
|
||||
this.panelBox.set_size(this.primaryMonitor.width, -1);
|
||||
|
||||
this._bottomBox.set_position(this.bottomMonitor.x,
|
||||
this.bottomMonitor.y + this.bottomMonitor.height);
|
||||
this._bottomBox.set_size(this.bottomMonitor.width, -1);
|
||||
|
||||
this.trayBox.width = this.bottomMonitor.width;
|
||||
|
||||
// Set trayBox's clip to show things above it, but not below
|
||||
// it (so it's not visible behind the keyboard). The exact
|
||||
// height of the clip doesn't matter, as long as it's taller
|
||||
// than any Notification.actor.
|
||||
this.trayBox.set_clip(0, -this.bottomMonitor.height,
|
||||
this.bottomMonitor.width, this.bottomMonitor.height);
|
||||
},
|
||||
|
||||
_updatePanelBarriers: function() {
|
||||
if (this._leftPanelBarrier)
|
||||
global.destroy_pointer_barrier(this._leftPanelBarrier);
|
||||
if (this._rightPanelBarrier)
|
||||
global.destroy_pointer_barrier(this._rightPanelBarrier);
|
||||
|
||||
if (this.panelBox.height) {
|
||||
let primary = this.primaryMonitor;
|
||||
this._leftPanelBarrier =
|
||||
global.create_pointer_barrier(primary.x, primary.y,
|
||||
primary.x, primary.y + this.panelBox.height,
|
||||
1 /* BarrierPositiveX */);
|
||||
this._rightPanelBarrier =
|
||||
global.create_pointer_barrier(primary.x + primary.width, primary.y,
|
||||
primary.x + primary.width, primary.y + this.panelBox.height,
|
||||
4 /* BarrierNegativeX */);
|
||||
} else {
|
||||
this._leftPanelBarrier = 0;
|
||||
this._rightPanelBarrier = 0;
|
||||
}
|
||||
},
|
||||
|
||||
_updateTrayBarrier: function() {
|
||||
let monitor = this.bottomMonitor;
|
||||
|
||||
if (this._trayBarrier)
|
||||
global.destroy_pointer_barrier(this._trayBarrier);
|
||||
|
||||
if (Main.messageTray) {
|
||||
this._trayBarrier =
|
||||
global.create_pointer_barrier(monitor.x + monitor.width, monitor.y + monitor.height - Main.messageTray.actor.height,
|
||||
monitor.x + monitor.width, monitor.y + monitor.height,
|
||||
4 /* BarrierNegativeX */);
|
||||
} else {
|
||||
this._trayBarrier = 0;
|
||||
}
|
||||
},
|
||||
|
||||
_monitorsChanged: function() {
|
||||
this._updateMonitors();
|
||||
this._updateBoxes();
|
||||
this._updateHotCorners();
|
||||
|
||||
this.emit('monitors-changed');
|
||||
@ -135,9 +228,7 @@ LayoutManager.prototype = {
|
||||
},
|
||||
|
||||
get focusIndex() {
|
||||
let screen = global.screen;
|
||||
let display = screen.get_display();
|
||||
let focusWindow = display.focus_window;
|
||||
let focusWindow = global.display.focus_window;
|
||||
|
||||
if (focusWindow) {
|
||||
let wrect = focusWindow.get_outer_rect();
|
||||
@ -156,6 +247,105 @@ LayoutManager.prototype = {
|
||||
|
||||
get focusMonitor() {
|
||||
return this.monitors[this.focusIndex];
|
||||
},
|
||||
|
||||
_startupAnimation: function() {
|
||||
this.panelBox.anchor_y = this.panelBox.height;
|
||||
Tweener.addTween(this.panelBox,
|
||||
{ anchor_y: 0,
|
||||
time: STARTUP_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
},
|
||||
|
||||
showKeyboard: function () {
|
||||
Main.messageTray.hide();
|
||||
Tweener.addTween(this._bottomBox,
|
||||
{ anchor_y: this._bottomBox.height,
|
||||
time: KEYBOARD_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: this._showKeyboardComplete,
|
||||
onCompleteScope: this
|
||||
});
|
||||
},
|
||||
|
||||
_showKeyboardComplete: function() {
|
||||
// Poke Chrome to update the input shape; it doesn't notice
|
||||
// anchor point changes
|
||||
this._chrome.updateRegions();
|
||||
|
||||
this._bottomBox.connect('notify::height', Lang.bind(this, function () {
|
||||
this._bottomBoxAnchor = this._bottomBox.height;
|
||||
}));
|
||||
},
|
||||
|
||||
hideKeyboard: function (immediate) {
|
||||
Main.messageTray.hide();
|
||||
Tweener.addTween(this._bottomBox,
|
||||
{ anchor_y: 0,
|
||||
time: immediate ? 0 : KEYBOARD_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: this._showKeyboardComplete,
|
||||
onCompleteScope: this
|
||||
});
|
||||
},
|
||||
|
||||
_hideKeyboardComplete: function() {
|
||||
this._chrome.updateRegions();
|
||||
},
|
||||
|
||||
// addChrome:
|
||||
// @actor: an actor to add to the chrome layer
|
||||
// @params: (optional) additional params
|
||||
//
|
||||
// Adds @actor to the chrome layer and (unless %affectsInputRegion
|
||||
// in @params is %false) extends the input region to include it.
|
||||
// Changes in @actor's size, position, and visibility will
|
||||
// automatically result in appropriate changes to the input
|
||||
// region.
|
||||
//
|
||||
// If %affectsStruts in @params is %true (and @actor is along a
|
||||
// screen edge), then @actor's size and position will also affect
|
||||
// the window manager struts. Changes to @actor's visibility will
|
||||
// NOT affect whether or not the strut is present, however.
|
||||
//
|
||||
// If %visibleInFullscreen in @params is %true, the actor will be
|
||||
// visible even when a fullscreen window should be covering it.
|
||||
addChrome: function(actor, params) {
|
||||
this._chrome.addActor(actor, params);
|
||||
},
|
||||
|
||||
// trackChrome:
|
||||
// @actor: a descendant of the chrome to begin tracking
|
||||
// @params: parameters describing how to track @actor
|
||||
//
|
||||
// Tells the chrome to track @actor, which must be a descendant
|
||||
// of an actor added via addChrome(). This can be used to extend the
|
||||
// struts or input region to cover specific children.
|
||||
//
|
||||
// @params can have any of the same values as in addChrome(),
|
||||
// though some possibilities don't make sense (eg, trying to have
|
||||
// a %visibleInFullscreen child of a non-%visibleInFullscreen
|
||||
// parent). By default, @actor has the same params as its chrome
|
||||
// ancestor.
|
||||
trackChrome: function(actor, params) {
|
||||
this._chrome.trackActor(actor, params);
|
||||
},
|
||||
|
||||
// untrackChrome:
|
||||
// @actor: an actor previously tracked via trackChrome()
|
||||
//
|
||||
// Undoes the effect of trackChrome()
|
||||
untrackChrome: function(actor) {
|
||||
this._chrome.untrackActor(actor);
|
||||
},
|
||||
|
||||
// removeChrome:
|
||||
// @actor: a child of the chrome layer
|
||||
//
|
||||
// Removes @actor from the chrome layer
|
||||
removeChrome: function(actor) {
|
||||
this._chrome.removeActor(actor);
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(LayoutManager.prototype);
|
||||
@ -218,13 +408,22 @@ HotCorner.prototype = {
|
||||
Lang.bind(this, this._onCornerClicked));
|
||||
this._corner.connect('leave-event',
|
||||
Lang.bind(this, this._onCornerLeft));
|
||||
|
||||
// Cache the three ripples instead of dynamically creating and destroying them.
|
||||
this._ripple1 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0 });
|
||||
this._ripple2 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0 });
|
||||
this._ripple3 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0 });
|
||||
|
||||
Main.uiGroup.add_actor(this._ripple1);
|
||||
Main.uiGroup.add_actor(this._ripple2);
|
||||
Main.uiGroup.add_actor(this._ripple3);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.actor.destroy();
|
||||
},
|
||||
|
||||
_addRipple : function(delay, time, startScale, startOpacity, finalScale, finalOpacity) {
|
||||
_animRipple : function(ripple, delay, time, startScale, startOpacity, finalScale) {
|
||||
// We draw a ripple by using a source image and animating it scaling
|
||||
// outwards and fading away. We want the ripples to move linearly
|
||||
// or it looks unrealistic, but if the opacity of the ripple goes
|
||||
@ -232,25 +431,27 @@ HotCorner.prototype = {
|
||||
// 'onUpdate' to give a non-linear curve to the fade-away and make
|
||||
// it more visible in the middle section.
|
||||
|
||||
let [x, y] = this._corner.get_transformed_position();
|
||||
let ripple = new St.BoxLayout({ style_class: 'ripple-box',
|
||||
opacity: 255 * Math.sqrt(startOpacity),
|
||||
scale_x: startScale,
|
||||
scale_y: startScale,
|
||||
x: x,
|
||||
y: y });
|
||||
ripple._opacity = startOpacity;
|
||||
ripple._opacity = startOpacity;
|
||||
|
||||
if (ripple.get_direction() == St.TextDirection.RTL)
|
||||
ripple.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
|
||||
Tweener.addTween(ripple, { _opacity: finalOpacity,
|
||||
|
||||
ripple.visible = true;
|
||||
ripple.opacity = 255 * Math.sqrt(startOpacity);
|
||||
ripple.scale_x = ripple.scale_y = startScale;
|
||||
|
||||
let [x, y] = this._corner.get_transformed_position();
|
||||
ripple.x = x;
|
||||
ripple.y = y;
|
||||
|
||||
Tweener.addTween(ripple, { _opacity: 0,
|
||||
scale_x: finalScale,
|
||||
scale_y: finalScale,
|
||||
delay: delay,
|
||||
time: time,
|
||||
transition: 'linear',
|
||||
onUpdate: function() { ripple.opacity = 255 * Math.sqrt(ripple._opacity); },
|
||||
onComplete: function() { ripple.destroy(); } });
|
||||
Main.uiGroup.add_actor(ripple);
|
||||
onComplete: function() { ripple.visible = false; } });
|
||||
},
|
||||
|
||||
rippleAnimation: function() {
|
||||
@ -258,10 +459,10 @@ HotCorner.prototype = {
|
||||
// parameters were found by trial and error, so don't look
|
||||
// for them to make perfect sense mathematically
|
||||
|
||||
// delay time scale opacity => scale opacity
|
||||
this._addRipple(0.0, 0.83, 0.25, 1.0, 1.5, 0.0);
|
||||
this._addRipple(0.05, 1.0, 0.0, 0.7, 1.25, 0.0);
|
||||
this._addRipple(0.35, 1.0, 0.0, 0.3, 1, 0.0);
|
||||
// delay time scale opacity => scale
|
||||
this._animRipple(this._ripple1, 0.0, 0.83, 0.25, 1.0, 1.5);
|
||||
this._animRipple(this._ripple2, 0.05, 1.0, 0.0, 0.7, 1.25);
|
||||
this._animRipple(this._ripple3, 0.35, 1.0, 0.0, 0.3, 1);
|
||||
},
|
||||
|
||||
handleDragOver: function(source, actor, x, y, time) {
|
||||
@ -321,3 +522,407 @@ HotCorner.prototype = {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// This manages the shell "chrome"; the UI that's visible in the
|
||||
// normal mode (ie, outside the Overview), that surrounds the main
|
||||
// workspace content.
|
||||
|
||||
const defaultParams = {
|
||||
visibleInFullscreen: false,
|
||||
affectsStruts: false,
|
||||
affectsInputRegion: true
|
||||
};
|
||||
|
||||
function Chrome() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
Chrome.prototype = {
|
||||
_init: function(layoutManager) {
|
||||
this._layoutManager = layoutManager;
|
||||
|
||||
// The group itself has zero size so it doesn't interfere with DND
|
||||
this.actor = new Shell.GenericContainer({ width: 0, height: 0 });
|
||||
Main.uiGroup.add_actor(this.actor);
|
||||
this.actor.connect('allocate', Lang.bind(this, this._allocated));
|
||||
|
||||
this._monitors = [];
|
||||
this._inOverview = false;
|
||||
|
||||
this._trackedActors = [];
|
||||
|
||||
this._layoutManager.connect('monitors-changed',
|
||||
Lang.bind(this, this._relayout));
|
||||
global.screen.connect('restacked',
|
||||
Lang.bind(this, this._windowsRestacked));
|
||||
|
||||
// Need to update struts on new workspaces when they are added
|
||||
global.screen.connect('notify::n-workspaces',
|
||||
Lang.bind(this, this._queueUpdateRegions));
|
||||
|
||||
this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy();
|
||||
this._screenSaverProxy.connect('ActiveChanged', Lang.bind(this, this._onScreenSaverActiveChanged));
|
||||
this._screenSaverProxy.GetActiveRemote(Lang.bind(this,
|
||||
function(result, err) {
|
||||
if (!err)
|
||||
this._onScreenSaverActiveChanged(this._screenSaverProxy, result);
|
||||
}));
|
||||
|
||||
this._relayout();
|
||||
},
|
||||
|
||||
init: function() {
|
||||
Main.overview.connect('showing',
|
||||
Lang.bind(this, this._overviewShowing));
|
||||
Main.overview.connect('hidden',
|
||||
Lang.bind(this, this._overviewHidden));
|
||||
},
|
||||
|
||||
_allocated: function(actor, box, flags) {
|
||||
let children = this.actor.get_children();
|
||||
for (let i = 0; i < children.length; i++)
|
||||
children[i].allocate_preferred_size(flags);
|
||||
},
|
||||
|
||||
addActor: function(actor, params) {
|
||||
this.actor.add_actor(actor);
|
||||
this._trackActor(actor, params);
|
||||
},
|
||||
|
||||
trackActor: function(actor, params) {
|
||||
let ancestor = actor.get_parent();
|
||||
let index = this._findActor(ancestor);
|
||||
while (ancestor && index == -1) {
|
||||
ancestor = ancestor.get_parent();
|
||||
index = this._findActor(ancestor);
|
||||
}
|
||||
if (!ancestor)
|
||||
throw new Error('actor is not a descendent of the chrome layer');
|
||||
|
||||
let ancestorData = this._trackedActors[index];
|
||||
if (!params)
|
||||
params = {};
|
||||
// We can't use Params.parse here because we want to drop
|
||||
// the extra values like ancestorData.actor
|
||||
for (let prop in defaultParams) {
|
||||
if (!params[prop])
|
||||
params[prop] = ancestorData[prop];
|
||||
}
|
||||
|
||||
this._trackActor(actor, params);
|
||||
},
|
||||
|
||||
untrackActor: function(actor) {
|
||||
this._untrackActor(actor);
|
||||
},
|
||||
|
||||
removeActor: function(actor) {
|
||||
this.actor.remove_actor(actor);
|
||||
this._untrackActor(actor);
|
||||
},
|
||||
|
||||
_findActor: function(actor) {
|
||||
for (let i = 0; i < this._trackedActors.length; i++) {
|
||||
let actorData = this._trackedActors[i];
|
||||
if (actorData.actor == actor)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
|
||||
_trackActor: function(actor, params) {
|
||||
if (this._findActor(actor) != -1)
|
||||
throw new Error('trying to re-track existing chrome actor');
|
||||
|
||||
let actorData = Params.parse(params, defaultParams);
|
||||
actorData.actor = actor;
|
||||
actorData.visibleId = actor.connect('notify::visible',
|
||||
Lang.bind(this, this._queueUpdateRegions));
|
||||
actorData.allocationId = actor.connect('notify::allocation',
|
||||
Lang.bind(this, this._queueUpdateRegions));
|
||||
actorData.parentSetId = actor.connect('parent-set',
|
||||
Lang.bind(this, this._actorReparented));
|
||||
// Note that destroying actor will unset its parent, so we don't
|
||||
// need to connect to 'destroy' too.
|
||||
|
||||
this._trackedActors.push(actorData);
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_untrackActor: function(actor) {
|
||||
let i = this._findActor(actor);
|
||||
|
||||
if (i == -1)
|
||||
return;
|
||||
let actorData = this._trackedActors[i];
|
||||
|
||||
this._trackedActors.splice(i, 1);
|
||||
actor.disconnect(actorData.visibleId);
|
||||
actor.disconnect(actorData.allocationId);
|
||||
actor.disconnect(actorData.parentSetId);
|
||||
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_actorReparented: function(actor, oldParent) {
|
||||
if (!this.actor.contains(actor))
|
||||
this._untrackActor(actor);
|
||||
},
|
||||
|
||||
_updateVisibility: function() {
|
||||
for (let i = 0; i < this._trackedActors.length; i++) {
|
||||
let actorData = this._trackedActors[i];
|
||||
if (!this._inOverview && !actorData.visibleInFullscreen &&
|
||||
this._findMonitorForActor(actorData.actor).inFullscreen)
|
||||
this.actor.set_skip_paint(actorData.actor, true);
|
||||
else
|
||||
this.actor.set_skip_paint(actorData.actor, false);
|
||||
}
|
||||
},
|
||||
|
||||
_overviewShowing: function() {
|
||||
this._inOverview = true;
|
||||
this._updateVisibility();
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_overviewHidden: function() {
|
||||
this._inOverview = false;
|
||||
this._updateVisibility();
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_relayout: function() {
|
||||
this._monitors = this._layoutManager.monitors;
|
||||
this._primaryMonitor = this._layoutManager.primaryMonitor;
|
||||
|
||||
this._updateFullscreen();
|
||||
this._updateVisibility();
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_onScreenSaverActiveChanged: function(proxy, screenSaverActive) {
|
||||
this.actor.visible = !screenSaverActive;
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_findMonitorForRect: function(x, y, w, h) {
|
||||
// First look at what monitor the center of the rectangle is at
|
||||
let cx = x + w/2;
|
||||
let cy = y + h/2;
|
||||
for (let i = 0; i < this._monitors.length; i++) {
|
||||
let monitor = this._monitors[i];
|
||||
if (cx >= monitor.x && cx < monitor.x + monitor.width &&
|
||||
cy >= monitor.y && cy < monitor.y + monitor.height)
|
||||
return monitor;
|
||||
}
|
||||
// If the center is not on a monitor, return the first overlapping monitor
|
||||
for (let i = 0; i < this._monitors.length; i++) {
|
||||
let monitor = this._monitors[i];
|
||||
if (x + w > monitor.x && x < monitor.x + monitor.width &&
|
||||
y + h > monitor.y && y < monitor.y + monitor.height)
|
||||
return monitor;
|
||||
}
|
||||
// otherwise on no monitor
|
||||
return null;
|
||||
},
|
||||
|
||||
_findMonitorForWindow: function(window) {
|
||||
return this._findMonitorForRect(window.x, window.y, window.width, window.height);
|
||||
},
|
||||
|
||||
// This call guarantees that we return some monitor to simplify usage of it
|
||||
// In practice all tracked actors should be visible on some monitor anyway
|
||||
_findMonitorForActor: function(actor) {
|
||||
let [x, y] = actor.get_transformed_position();
|
||||
let [w, h] = actor.get_transformed_size();
|
||||
let monitor = this._findMonitorForRect(x, y, w, h);
|
||||
if (monitor)
|
||||
return monitor;
|
||||
return this._primaryMonitor; // Not on any monitor, pretend its on the primary
|
||||
},
|
||||
|
||||
_queueUpdateRegions: function() {
|
||||
if (!this._updateRegionIdle)
|
||||
this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this.updateRegions),
|
||||
Meta.PRIORITY_BEFORE_REDRAW);
|
||||
},
|
||||
|
||||
_updateFullscreen: function() {
|
||||
let windows = Main.getWindowActorsForWorkspace(global.screen.get_active_workspace_index());
|
||||
|
||||
// Reset all monitors to not fullscreen
|
||||
for (let i = 0; i < this._monitors.length; i++)
|
||||
this._monitors[i].inFullscreen = false;
|
||||
|
||||
// The chrome layer should be visible unless there is a window
|
||||
// with layer FULLSCREEN, or a window with layer
|
||||
// OVERRIDE_REDIRECT that covers the whole screen.
|
||||
// ('override_redirect' is not actually a layer above all
|
||||
// other windows, but this seems to be how mutter treats it
|
||||
// currently...) If we wanted to be extra clever, we could
|
||||
// figure out when an OVERRIDE_REDIRECT window was trying to
|
||||
// partially overlap us, and then adjust the input region and
|
||||
// our clip region accordingly...
|
||||
|
||||
// @windows is sorted bottom to top.
|
||||
|
||||
for (let i = windows.length - 1; i > -1; i--) {
|
||||
let window = windows[i];
|
||||
let layer = window.get_meta_window().get_layer();
|
||||
|
||||
// Skip minimized windows
|
||||
if (!window.showing_on_its_workspace())
|
||||
continue;
|
||||
|
||||
if (layer == Meta.StackLayer.FULLSCREEN) {
|
||||
let monitor = this._findMonitorForWindow(window);
|
||||
if (monitor)
|
||||
monitor.inFullscreen = true;
|
||||
}
|
||||
if (layer == Meta.StackLayer.OVERRIDE_REDIRECT) {
|
||||
let monitor = this._findMonitorForWindow(window);
|
||||
if (monitor &&
|
||||
window.x <= monitor.x &&
|
||||
window.x + window.width >= monitor.x + monitor.width &&
|
||||
window.y <= monitor.y &&
|
||||
window.y + window.height >= monitor.y + monitor.height)
|
||||
monitor.inFullscreen = true;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_windowsRestacked: function() {
|
||||
let wasInFullscreen = [];
|
||||
for (let i = 0; i < this._monitors.length; i++)
|
||||
wasInFullscreen[i] = this._monitors[i].inFullscreen;
|
||||
|
||||
this._updateFullscreen();
|
||||
|
||||
let changed = false;
|
||||
for (let i = 0; i < wasInFullscreen.length; i++) {
|
||||
if (wasInFullscreen[i] != this._monitors[i].inFullscreen) {
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
this._updateVisibility();
|
||||
this._queueUpdateRegions();
|
||||
}
|
||||
},
|
||||
|
||||
updateRegions: function() {
|
||||
let rects = [], struts = [], i;
|
||||
|
||||
if (this._updateRegionIdle) {
|
||||
Mainloop.source_remove(this._updateRegionIdle);
|
||||
delete this._updateRegionIdle;
|
||||
}
|
||||
|
||||
for (i = 0; i < this._trackedActors.length; i++) {
|
||||
let actorData = this._trackedActors[i];
|
||||
if (!actorData.affectsInputRegion && !actorData.affectsStruts)
|
||||
continue;
|
||||
|
||||
let [x, y] = actorData.actor.get_transformed_position();
|
||||
let [w, h] = actorData.actor.get_transformed_size();
|
||||
x = Math.round(x);
|
||||
y = Math.round(y);
|
||||
w = Math.round(w);
|
||||
h = Math.round(h);
|
||||
let rect = new Meta.Rectangle({ x: x, y: y, width: w, height: h});
|
||||
|
||||
if (actorData.affectsInputRegion &&
|
||||
actorData.actor.get_paint_visibility() &&
|
||||
!this.actor.get_skip_paint(actorData.actor))
|
||||
rects.push(rect);
|
||||
|
||||
if (!actorData.affectsStruts)
|
||||
continue;
|
||||
|
||||
// Limit struts to the size of the screen
|
||||
let x1 = Math.max(x, 0);
|
||||
let x2 = Math.min(x + w, global.screen_width);
|
||||
let y1 = Math.max(y, 0);
|
||||
let y2 = Math.min(y + h, global.screen_height);
|
||||
|
||||
// NetWM struts are not really powerful enought to handle
|
||||
// a multi-monitor scenario, they only describe what happens
|
||||
// around the outer sides of the full display region. However
|
||||
// it can describe a partial region along each side, so
|
||||
// we can support having the struts only affect the
|
||||
// primary monitor. This should be enough as we only have
|
||||
// chrome affecting the struts on the primary monitor so
|
||||
// far.
|
||||
//
|
||||
// Metacity wants to know what side of the screen the
|
||||
// strut is considered to be attached to. If the actor is
|
||||
// only touching one edge, or is touching the entire
|
||||
// border of the primary monitor, then it's obvious which
|
||||
// side to call it. If it's in a corner, we pick a side
|
||||
// arbitrarily. If it doesn't touch any edges, or it spans
|
||||
// the width/height across the middle of the screen, then
|
||||
// we don't create a strut for it at all.
|
||||
let side;
|
||||
let primary = this._primaryMonitor;
|
||||
if (x1 <= primary.x && x2 >= primary.x + primary.width) {
|
||||
if (y1 <= primary.y)
|
||||
side = Meta.Side.TOP;
|
||||
else if (y2 >= primary.y + primary.height)
|
||||
side = Meta.Side.BOTTOM;
|
||||
else
|
||||
continue;
|
||||
} else if (y1 <= primary.y && y2 >= primary.y + primary.height) {
|
||||
if (x1 <= 0)
|
||||
side = Meta.Side.LEFT;
|
||||
else if (x2 >= global.screen_width)
|
||||
side = Meta.Side.RIGHT;
|
||||
else
|
||||
continue;
|
||||
} else if (x1 <= 0)
|
||||
side = Meta.Side.LEFT;
|
||||
else if (y1 <= 0)
|
||||
side = Meta.Side.TOP;
|
||||
else if (x2 >= global.screen_width)
|
||||
side = Meta.Side.RIGHT;
|
||||
else if (y2 >= global.screen_height)
|
||||
side = Meta.Side.BOTTOM;
|
||||
else
|
||||
continue;
|
||||
|
||||
// Ensure that the strut rects goes all the way to the screen edge,
|
||||
// as this really what mutter expects.
|
||||
switch (side) {
|
||||
case Meta.Side.TOP:
|
||||
y1 = 0;
|
||||
break;
|
||||
case Meta.Side.BOTTOM:
|
||||
y2 = global.screen_height;
|
||||
break;
|
||||
case Meta.Side.LEFT:
|
||||
x1 = 0;
|
||||
break;
|
||||
case Meta.Side.RIGHT:
|
||||
x2 = global.screen_width;
|
||||
break;
|
||||
}
|
||||
|
||||
let strutRect = new Meta.Rectangle({ x: x1, y: y1, width: x2 - x1, height: y2 - y1});
|
||||
let strut = new Meta.Strut({ rect: strutRect, side: side });
|
||||
struts.push(strut);
|
||||
}
|
||||
|
||||
global.set_stage_input_region(rects);
|
||||
|
||||
let screen = global.screen;
|
||||
for (let w = 0; w < screen.n_workspaces; w++) {
|
||||
let workspace = screen.get_workspace_by_index(w);
|
||||
workspace.set_builtin_struts(struts);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
@ -222,10 +222,9 @@ function WindowList() {
|
||||
WindowList.prototype = {
|
||||
_init : function () {
|
||||
this.actor = new St.BoxLayout({ name: 'Windows', vertical: true, style: 'spacing: 8px' });
|
||||
let display = global.screen.get_display();
|
||||
let tracker = Shell.WindowTracker.get_default();
|
||||
this._updateId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._updateWindowList));
|
||||
display.connect('window-created', Lang.bind(this, this._updateWindowList));
|
||||
global.display.connect('window-created', Lang.bind(this, this._updateWindowList));
|
||||
tracker.connect('tracked-windows-changed', Lang.bind(this, this._updateWindowList));
|
||||
},
|
||||
|
||||
@ -248,7 +247,7 @@ WindowList.prototype = {
|
||||
box.add(propsBox);
|
||||
propsBox.add(new St.Label({ text: 'wmclass: ' + metaWindow.get_wm_class() }));
|
||||
let app = tracker.get_window_app(metaWindow);
|
||||
if (app != null && !app.is_transient()) {
|
||||
if (app != null && !app.is_window_backed()) {
|
||||
let icon = app.create_icon_texture(22);
|
||||
let propBox = new St.BoxLayout({ style: 'spacing: 6px; ' });
|
||||
propsBox.add(propBox);
|
||||
@ -639,23 +638,32 @@ Extensions.prototype = {
|
||||
name: 'lookingGlassExtensions' });
|
||||
this._noExtensions = new St.Label({ style_class: 'lg-extensions-none',
|
||||
text: _("No extensions installed") });
|
||||
this._numExtensions = 0;
|
||||
this._extensionsList = new St.BoxLayout({ vertical: true,
|
||||
style_class: 'lg-extensions-list' });
|
||||
this._extensionsList.add(this._noExtensions);
|
||||
this.actor.add(this._extensionsList);
|
||||
this._loadExtensionList();
|
||||
|
||||
for (let uuid in ExtensionSystem.extensionMeta)
|
||||
this._loadExtension(null, uuid);
|
||||
|
||||
ExtensionSystem.connect('extension-loaded',
|
||||
Lang.bind(this, this._loadExtension));
|
||||
},
|
||||
|
||||
_loadExtensionList: function() {
|
||||
let extensions = ExtensionSystem.extensionMeta;
|
||||
let totalExtensions = 0;
|
||||
for (let uuid in extensions) {
|
||||
let extensionDisplay = this._createExtensionDisplay(extensions[uuid]);
|
||||
this._extensionsList.add(extensionDisplay);
|
||||
totalExtensions++;
|
||||
}
|
||||
if (totalExtensions == 0) {
|
||||
this._extensionsList.add(this._noExtensions);
|
||||
}
|
||||
_loadExtension: function(o, uuid) {
|
||||
let extension = ExtensionSystem.extensionMeta[uuid];
|
||||
// There can be cases where we create dummy extension metadata
|
||||
// that's not really a proper extension. Don't bother with these.
|
||||
if (!extension.name)
|
||||
return;
|
||||
|
||||
let extensionDisplay = this._createExtensionDisplay(extension);
|
||||
if (this._numExtensions == 0)
|
||||
this._extensionsList.remove_actor(this._noExtensions);
|
||||
|
||||
this._numExtensions ++;
|
||||
this._extensionsList.add(extensionDisplay);
|
||||
},
|
||||
|
||||
_onViewSource: function (actor) {
|
||||
@ -682,6 +690,8 @@ Extensions.prototype = {
|
||||
return _("Error");
|
||||
case ExtensionSystem.ExtensionState.OUT_OF_DATE:
|
||||
return _("Out of date");
|
||||
case ExtensionSystem.ExtensionState.DOWNLOADING:
|
||||
return _("Downloading");
|
||||
}
|
||||
return 'Unknown'; // Not translated, shouldn't appear
|
||||
},
|
||||
@ -692,7 +702,7 @@ Extensions.prototype = {
|
||||
text: meta.name });
|
||||
box.add(name, { expand: true });
|
||||
let description = new St.Label({ style_class: 'lg-extension-description',
|
||||
text: meta.description });
|
||||
text: meta.description || 'No description' });
|
||||
box.add(description, { expand: true });
|
||||
|
||||
let metaBox = new St.BoxLayout();
|
||||
@ -751,7 +761,9 @@ LookingGlass.prototype = {
|
||||
Lang.bind(this, this._updateFont));
|
||||
this._updateFont();
|
||||
|
||||
Main.uiGroup.add_actor(this.actor);
|
||||
// we add it to the chrome because we want it to appear to slide
|
||||
// out from underneath the panel
|
||||
Main.layoutManager.addChrome(this.actor);
|
||||
|
||||
this._objInspector = new ObjInspector();
|
||||
Main.uiGroup.add_actor(this._objInspector.actor);
|
||||
@ -962,7 +974,7 @@ LookingGlass.prototype = {
|
||||
|
||||
this._notebook.selectIndex(0);
|
||||
this.actor.show();
|
||||
this.actor.lower(Main.chrome.actor);
|
||||
this.actor.lower_bottom();
|
||||
this._open = true;
|
||||
this._history.lastItem();
|
||||
|
||||
@ -993,6 +1005,7 @@ LookingGlass.prototype = {
|
||||
|
||||
Main.popModal(this._entry);
|
||||
|
||||
this.actor.lower_bottom();
|
||||
Tweener.addTween(this.actor, { time: 0.5 / St.get_slow_down_factor(),
|
||||
transition: 'easeOutQuad',
|
||||
y: this._hiddenY,
|
||||
|
194
js/ui/main.js
194
js/ui/main.js
@ -14,12 +14,12 @@ const St = imports.gi.St;
|
||||
|
||||
const AutomountManager = imports.ui.automountManager;
|
||||
const AutorunManager = imports.ui.autorunManager;
|
||||
const Chrome = imports.ui.chrome;
|
||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||
const EndSessionDialog = imports.ui.endSessionDialog;
|
||||
const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent;
|
||||
const Environment = imports.ui.environment;
|
||||
const ExtensionSystem = imports.ui.extensionSystem;
|
||||
const Keyboard = imports.ui.keyboard;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const Overview = imports.ui.overview;
|
||||
const Panel = imports.ui.panel;
|
||||
@ -27,6 +27,7 @@ const PlaceDisplay = imports.ui.placeDisplay;
|
||||
const RunDialog = imports.ui.runDialog;
|
||||
const Layout = imports.ui.layout;
|
||||
const LookingGlass = imports.ui.lookingGlass;
|
||||
const NetworkAgent = imports.ui.networkAgent;
|
||||
const NotificationDaemon = imports.ui.notificationDaemon;
|
||||
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
|
||||
const Scripting = imports.ui.scripting;
|
||||
@ -43,7 +44,6 @@ DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff);
|
||||
|
||||
let automountManager = null;
|
||||
let autorunManager = null;
|
||||
let chrome = null;
|
||||
let panel = null;
|
||||
let hotCorners = [];
|
||||
let placesManager = null;
|
||||
@ -64,14 +64,94 @@ let uiGroup = null;
|
||||
let magnifier = null;
|
||||
let xdndHandler = null;
|
||||
let statusIconDispatcher = null;
|
||||
let keyboard = null;
|
||||
let layoutManager = null;
|
||||
let networkAgent = null;
|
||||
let _errorLogStack = [];
|
||||
let _startDate;
|
||||
let _defaultCssStylesheet = null;
|
||||
let _cssStylesheet = null;
|
||||
let _gdmCssStylesheet = null;
|
||||
|
||||
let background = null;
|
||||
|
||||
function _createUserSession() {
|
||||
// Load the calendar server. Note that we are careful about
|
||||
// not loading any events until the user presses the clock
|
||||
global.launch_calendar_server();
|
||||
|
||||
placesManager = new PlaceDisplay.PlacesManager();
|
||||
telepathyClient = new TelepathyClient.Client();
|
||||
automountManager = new AutomountManager.AutomountManager();
|
||||
autorunManager = new AutorunManager.AutorunManager();
|
||||
networkAgent = new NetworkAgent.NetworkAgent();
|
||||
}
|
||||
|
||||
function _createGDMSession() {
|
||||
// We do this this here instead of at the top to prevent GDM
|
||||
// related code from getting loaded in normal user sessions
|
||||
const LoginDialog = imports.gdm.loginDialog;
|
||||
|
||||
let loginDialog = new LoginDialog.LoginDialog();
|
||||
loginDialog.connect('loaded', function() {
|
||||
loginDialog.open();
|
||||
});
|
||||
}
|
||||
|
||||
function _initRecorder() {
|
||||
let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
|
||||
|
||||
global.screen.connect('toggle-recording', function() {
|
||||
if (recorder == null) {
|
||||
recorder = new Shell.Recorder({ stage: global.stage });
|
||||
}
|
||||
|
||||
if (recorder.is_recording()) {
|
||||
recorder.pause();
|
||||
Meta.enable_unredirect_for_screen(global.screen);
|
||||
} else {
|
||||
// read the parameters from GSettings always in case they have changed
|
||||
recorder.set_framerate(recorderSettings.get_int('framerate'));
|
||||
recorder.set_filename('shell-%d%u-%c.' + recorderSettings.get_string('file-extension'));
|
||||
let pipeline = recorderSettings.get_string('pipeline');
|
||||
|
||||
if (!pipeline.match(/^\s*$/))
|
||||
recorder.set_pipeline(pipeline);
|
||||
else
|
||||
recorder.set_pipeline(null);
|
||||
|
||||
Meta.disable_unredirect_for_screen(global.screen);
|
||||
recorder.record();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function _initUserSession() {
|
||||
_initRecorder();
|
||||
|
||||
keyboard.init();
|
||||
|
||||
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, false, -1, 1);
|
||||
|
||||
ExtensionSystem.init();
|
||||
ExtensionSystem.loadExtensions();
|
||||
|
||||
let shellwm = global.window_manager;
|
||||
|
||||
shellwm.takeover_keybinding('panel_run_dialog');
|
||||
shellwm.connect('keybinding::panel_run_dialog', function () {
|
||||
getRunDialog().open();
|
||||
});
|
||||
|
||||
shellwm.takeover_keybinding('panel_main_menu');
|
||||
shellwm.connect('keybinding::panel_main_menu', function () {
|
||||
overview.toggle();
|
||||
});
|
||||
|
||||
global.display.connect('overlay-key', Lang.bind(overview, overview.toggle));
|
||||
|
||||
}
|
||||
|
||||
function start() {
|
||||
// Monkey patch utility functions into the global proxy;
|
||||
// This is easier and faster than indirecting down into global
|
||||
@ -91,10 +171,6 @@ function start() {
|
||||
// back into sync ones.
|
||||
DBus.session.flush();
|
||||
|
||||
// Load the calendar server. Note that we are careful about
|
||||
// not loading any events until the user presses the clock
|
||||
global.launch_calendar_server();
|
||||
|
||||
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
|
||||
// also initialize ShellAppSystem first. ShellAppSystem
|
||||
// needs to load all the .desktop files, and ShellWindowTracker
|
||||
@ -113,18 +189,9 @@ function start() {
|
||||
global.stage.no_clear_hint = true;
|
||||
|
||||
_defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
|
||||
_gdmCssStylesheet = global.datadir + '/theme/gdm.css';
|
||||
loadTheme();
|
||||
|
||||
let shellwm = global.window_manager;
|
||||
shellwm.takeover_keybinding('panel_main_menu');
|
||||
shellwm.connect('keybinding::panel_main_menu', function () {
|
||||
overview.toggle();
|
||||
});
|
||||
shellwm.takeover_keybinding('panel_run_dialog');
|
||||
shellwm.connect('keybinding::panel_run_dialog', function () {
|
||||
getRunDialog().open();
|
||||
});
|
||||
|
||||
// Set up stage hierarchy to group all UI actors under one container.
|
||||
uiGroup = new Clutter.Group();
|
||||
St.set_ui_root(global.stage, uiGroup);
|
||||
@ -133,54 +200,33 @@ function start() {
|
||||
global.stage.add_actor(uiGroup);
|
||||
|
||||
layoutManager = new Layout.LayoutManager();
|
||||
placesManager = new PlaceDisplay.PlacesManager();
|
||||
xdndHandler = new XdndHandler.XdndHandler();
|
||||
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
|
||||
overview = new Overview.Overview();
|
||||
chrome = new Chrome.Chrome();
|
||||
// This overview object is just a stub for non-user sessions
|
||||
overview = new Overview.Overview({ isDummy: global.session_type != Shell.SessionType.USER });
|
||||
magnifier = new Magnifier.Magnifier();
|
||||
statusIconDispatcher = new StatusIconDispatcher.StatusIconDispatcher();
|
||||
panel = new Panel.Panel();
|
||||
wm = new WindowManager.WindowManager();
|
||||
messageTray = new MessageTray.MessageTray();
|
||||
keyboard = new Keyboard.Keyboard();
|
||||
notificationDaemon = new NotificationDaemon.NotificationDaemon();
|
||||
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
|
||||
telepathyClient = new TelepathyClient.Client();
|
||||
automountManager = new AutomountManager.AutomountManager();
|
||||
autorunManager = new AutorunManager.AutorunManager();
|
||||
|
||||
layoutManager.init();
|
||||
if (global.session_type == Shell.SessionType.USER)
|
||||
_createUserSession();
|
||||
else if (global.session_type == Shell.SessionType.GDM)
|
||||
_createGDMSession();
|
||||
|
||||
panel.startStatusArea();
|
||||
|
||||
keyboard.init();
|
||||
overview.init();
|
||||
|
||||
if (global.session_type == Shell.SessionType.USER)
|
||||
_initUserSession();
|
||||
statusIconDispatcher.start(messageTray.actor);
|
||||
|
||||
_startDate = new Date();
|
||||
|
||||
let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
|
||||
|
||||
global.screen.connect('toggle-recording', function() {
|
||||
if (recorder == null) {
|
||||
recorder = new Shell.Recorder({ stage: global.stage });
|
||||
}
|
||||
|
||||
if (recorder.is_recording()) {
|
||||
recorder.pause();
|
||||
} else {
|
||||
// read the parameters from GSettings always in case they have changed
|
||||
recorder.set_framerate(recorderSettings.get_int('framerate'));
|
||||
recorder.set_filename('shell-%d%u-%c.' + recorderSettings.get_string('file-extension'));
|
||||
let pipeline = recorderSettings.get_string('pipeline');
|
||||
|
||||
if (!pipeline.match(/^\s*$/))
|
||||
recorder.set_pipeline(pipeline);
|
||||
else
|
||||
recorder.set_pipeline(null);
|
||||
|
||||
recorder.record();
|
||||
}
|
||||
});
|
||||
|
||||
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, false, -1, 1);
|
||||
|
||||
// Provide the bus object for gnome-session to
|
||||
// initiate logouts.
|
||||
EndSessionDialog.init();
|
||||
@ -188,14 +234,7 @@ function start() {
|
||||
// Attempt to become a PolicyKit authentication agent
|
||||
PolkitAuthenticationAgent.init()
|
||||
|
||||
ExtensionSystem.init();
|
||||
ExtensionSystem.loadExtensions();
|
||||
|
||||
panel.startStatusArea();
|
||||
panel.startupAnimation();
|
||||
|
||||
let display = global.screen.get_display();
|
||||
display.connect('overlay-key', Lang.bind(overview, overview.toggle));
|
||||
_startDate = new Date();
|
||||
|
||||
global.stage.connect('captured-event', _globalKeyPressHandler);
|
||||
|
||||
@ -414,6 +453,9 @@ function loadTheme() {
|
||||
|
||||
let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
|
||||
|
||||
if (global.session_type == Shell.SessionType.GDM)
|
||||
theme.load_stylesheet(_gdmCssStylesheet);
|
||||
|
||||
if (previousTheme) {
|
||||
let customStylesheets = previousTheme.get_custom_stylesheets();
|
||||
|
||||
@ -424,6 +466,19 @@ function loadTheme() {
|
||||
themeContext.set_theme (theme);
|
||||
}
|
||||
|
||||
/**
|
||||
* notify:
|
||||
* @msg: A message
|
||||
* @details: Additional information
|
||||
*/
|
||||
function notify(msg, details) {
|
||||
let source = new MessageTray.SystemNotificationSource();
|
||||
messageTray.add(source);
|
||||
let notification = new MessageTray.Notification(source, msg, details);
|
||||
notification.setTransient(true);
|
||||
source.notify(notification);
|
||||
}
|
||||
|
||||
/**
|
||||
* notifyError:
|
||||
* @msg: An error message
|
||||
@ -438,11 +493,7 @@ function notifyError(msg, details) {
|
||||
else
|
||||
log("error: " + msg)
|
||||
|
||||
let source = new MessageTray.SystemNotificationSource();
|
||||
messageTray.add(source);
|
||||
let notification = new MessageTray.Notification(source, msg, details);
|
||||
notification.setTransient(true);
|
||||
source.notify(notification);
|
||||
notify(msg, details);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -524,9 +575,8 @@ function _globalKeyPressHandler(actor, event) {
|
||||
let keyCode = event.get_key_code();
|
||||
let modifierState = Shell.get_event_state(event);
|
||||
|
||||
let display = global.screen.get_display();
|
||||
// This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType
|
||||
let action = display.get_keybinding_action(keyCode, modifierState);
|
||||
let action = global.display.get_keybinding_action(keyCode, modifierState);
|
||||
|
||||
// The screenshot action should always be available (even if a
|
||||
// modal dialog is present)
|
||||
@ -549,6 +599,15 @@ function _globalKeyPressHandler(actor, event) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action == Meta.KeyBindingAction.SWITCH_PANELS) {
|
||||
ctrlAltTabManager.popup(modifierState & Clutter.ModifierType.SHIFT_MASK);
|
||||
return true;
|
||||
}
|
||||
|
||||
// None of the other bindings are relevant outside of the user's session
|
||||
if (global.session_type != Shell.SessionType.USER)
|
||||
return false;
|
||||
|
||||
switch (action) {
|
||||
// left/right would effectively act as synonyms for up/down if we enabled them;
|
||||
// but that could be considered confusing; we also disable them in the main view.
|
||||
@ -572,9 +631,6 @@ function _globalKeyPressHandler(actor, event) {
|
||||
case Meta.KeyBindingAction.PANEL_MAIN_MENU:
|
||||
overview.hide();
|
||||
return true;
|
||||
case Meta.KeyBindingAction.SWITCH_PANELS:
|
||||
ctrlAltTabManager.popup(modifierState & Clutter.ModifierType.SHIFT_MASK);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -68,14 +68,19 @@ function _fixMarkup(text, allowMarkup) {
|
||||
// Support &, ", ', < and >, escape all other
|
||||
// occurrences of '&'.
|
||||
let _text = text.replace(/&(?!amp;|quot;|apos;|lt;|gt;)/g, '&');
|
||||
|
||||
// Support <b>, <i>, and <u>, escape anything else
|
||||
// so it displays as raw markup.
|
||||
return _text.replace(/<(\/?[^biu]>|[^>\/][^>])/g, '<$1');
|
||||
} else {
|
||||
// Escape everything
|
||||
let _text = text.replace(/&/g, '&');
|
||||
return _text.replace(/</g, '<');
|
||||
_text = _text.replace(/<(?!\/?[biu]>)/g, '<');
|
||||
|
||||
try {
|
||||
Pango.parse_markup(_text, -1, '');
|
||||
return _text;
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// !allowMarkup, or invalid markup
|
||||
return GLib.markup_escape_text(text, -1);
|
||||
}
|
||||
|
||||
function URLHighlighter(text, lineWrap, allowMarkup) {
|
||||
@ -229,9 +234,7 @@ FocusGrabber.prototype = {
|
||||
|
||||
this.actor = actor;
|
||||
|
||||
let metaDisplay = global.screen.get_display();
|
||||
|
||||
this._prevFocusedWindow = metaDisplay.focus_window;
|
||||
this._prevFocusedWindow = global.display.focus_window;
|
||||
this._prevKeyFocusActor = global.stage.get_key_focus();
|
||||
|
||||
if (!Main.overview.visible)
|
||||
@ -266,7 +269,7 @@ FocusGrabber.prototype = {
|
||||
let source = event.get_source();
|
||||
switch (event.type()) {
|
||||
case Clutter.EventType.BUTTON_PRESS:
|
||||
if (!this.actor.contains(source))
|
||||
if (!this.actor.contains(source) && !Main.keyboard.actor.contains(source))
|
||||
this.emit('button-pressed', source);
|
||||
break;
|
||||
case Clutter.EventType.KEY_PRESS:
|
||||
@ -285,8 +288,6 @@ FocusGrabber.prototype = {
|
||||
if (!this._hasFocus)
|
||||
return;
|
||||
|
||||
let metaDisplay = global.screen.get_display();
|
||||
|
||||
if (this._focusActorChangedId > 0) {
|
||||
global.stage.disconnect(this._focusActorChangedId);
|
||||
this._focusActorChangedId = 0;
|
||||
@ -305,8 +306,8 @@ FocusGrabber.prototype = {
|
||||
this._hasFocus = false;
|
||||
this.emit('focus-ungrabbed');
|
||||
|
||||
if (this._prevFocusedWindow && !metaDisplay.focus_window) {
|
||||
metaDisplay.set_input_focus_window(this._prevFocusedWindow, false, global.get_current_time());
|
||||
if (this._prevFocusedWindow && !global.display.focus_window) {
|
||||
global.display.set_input_focus_window(this._prevFocusedWindow, false, global.get_current_time());
|
||||
this._prevFocusedWindow = null;
|
||||
}
|
||||
if (this._prevKeyFocusActor) {
|
||||
@ -395,6 +396,8 @@ function Notification(source, title, banner, params) {
|
||||
}
|
||||
|
||||
Notification.prototype = {
|
||||
IMAGE_SIZE: 125,
|
||||
|
||||
_init: function(source, title, banner, params) {
|
||||
this.source = source;
|
||||
this.urgency = Urgency.NORMAL;
|
||||
@ -411,6 +414,7 @@ Notification.prototype = {
|
||||
this._titleDirection = St.TextDirection.NONE;
|
||||
this._spacing = 0;
|
||||
this._scrollPolicy = Gtk.PolicyType.AUTOMATIC;
|
||||
this._imageBin = null;
|
||||
|
||||
source.connect('destroy', Lang.bind(this,
|
||||
function (source, reason) {
|
||||
@ -440,9 +444,19 @@ Notification.prototype = {
|
||||
this._bannerBox.connect('allocate', Lang.bind(this, this._bannerBoxAllocate));
|
||||
this._table.add(this._bannerBox, { row: 0,
|
||||
col: 1,
|
||||
col_span: 2,
|
||||
x_expand: false,
|
||||
y_expand: false,
|
||||
y_fill: false });
|
||||
|
||||
// This is an empty cell that overlaps with this._bannerBox cell to ensure
|
||||
// that this._bannerBox cell expands horizontally, while not forcing the
|
||||
// this._imageBin that is also in col: 2 to expand horizontally.
|
||||
this._table.add(new St.Bin(), { row: 0,
|
||||
col: 2,
|
||||
y_expand: false,
|
||||
y_fill: false });
|
||||
|
||||
this._titleLabel = new St.Label();
|
||||
this._bannerBox.add_actor(this._titleLabel);
|
||||
this._bannerUrlHighlighter = new URLHighlighter();
|
||||
@ -494,7 +508,10 @@ Notification.prototype = {
|
||||
this._actionArea = null;
|
||||
this._buttonBox = null;
|
||||
}
|
||||
if (!this._scrollArea && !this._actionArea)
|
||||
if (this._imageBin && params.clear)
|
||||
this.unsetImage();
|
||||
|
||||
if (!this._scrollArea && !this._actionArea && !this._imageBin)
|
||||
this._table.remove_style_class_name('multi-line-notification');
|
||||
|
||||
this._icon = params.icon || this.source.createNotificationIcon();
|
||||
@ -558,7 +575,9 @@ Notification.prototype = {
|
||||
vscrollbar_policy: this._scrollPolicy,
|
||||
hscrollbar_policy: Gtk.PolicyType.NEVER,
|
||||
style_class: 'vfade' });
|
||||
this._table.add(this._scrollArea, { row: 1, col: 1 });
|
||||
this._table.add(this._scrollArea, { row: 1,
|
||||
col: 2 });
|
||||
this._updateLastColumnSettings();
|
||||
this._contentArea = new St.BoxLayout({ name: 'notification-body',
|
||||
vertical: true });
|
||||
this._scrollArea.add_actor(this._contentArea);
|
||||
@ -635,13 +654,52 @@ Notification.prototype = {
|
||||
if (!props)
|
||||
props = {};
|
||||
props.row = 2;
|
||||
props.col = 1;
|
||||
props.col = 2;
|
||||
|
||||
this._table.add_style_class_name('multi-line-notification');
|
||||
this._table.add(this._actionArea, props);
|
||||
this._updateLastColumnSettings();
|
||||
this._updated();
|
||||
},
|
||||
|
||||
_updateLastColumnSettings: function() {
|
||||
if (this._scrollArea)
|
||||
this._table.child_set(this._scrollArea, { col: this._imageBin ? 2 : 1,
|
||||
col_span: this._imageBin ? 1 : 2 });
|
||||
if (this._actionArea)
|
||||
this._table.child_set(this._actionArea, { col: this._imageBin ? 2 : 1,
|
||||
col_span: this._imageBin ? 1 : 2 });
|
||||
},
|
||||
|
||||
setImage: function(image) {
|
||||
if (this._imageBin)
|
||||
this.unsetImage();
|
||||
this._imageBin = new St.Bin();
|
||||
this._imageBin.child = image;
|
||||
this._imageBin.opacity = 230;
|
||||
this._table.add_style_class_name('multi-line-notification');
|
||||
this._table.add_style_class_name('notification-with-image');
|
||||
this._updateLastColumnSettings();
|
||||
this._table.add(this._imageBin, { row: 1,
|
||||
col: 1,
|
||||
row_span: 2,
|
||||
x_expand: false,
|
||||
y_expand: false,
|
||||
x_fill: false,
|
||||
y_fill: false });
|
||||
},
|
||||
|
||||
unsetImage: function() {
|
||||
if (this._imageBin) {
|
||||
this._table.remove_style_class_name('notification-with-image');
|
||||
this._table.remove_actor(this._imageBin);
|
||||
this._imageBin = null;
|
||||
this._updateLastColumnSettings();
|
||||
if (!this._scrollArea && !this._actionArea)
|
||||
this._table.remove_style_class_name('multi-line-notification');
|
||||
}
|
||||
},
|
||||
|
||||
// addButton:
|
||||
// @id: the action ID
|
||||
// @label: the label for the action's button
|
||||
@ -657,7 +715,9 @@ Notification.prototype = {
|
||||
|
||||
let box = new St.BoxLayout({ name: 'notification-actions' });
|
||||
this.setActionArea(box, { x_expand: false,
|
||||
y_expand: false,
|
||||
x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: St.Align.END });
|
||||
this._buttonBox = box;
|
||||
}
|
||||
@ -1274,12 +1334,11 @@ MessageTray.prototype = {
|
||||
this._summaryMotionId = 0;
|
||||
|
||||
this._summaryBoxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
|
||||
{ reactive: true,
|
||||
track_hover: true });
|
||||
{ reactive: true,
|
||||
track_hover: true });
|
||||
this._summaryBoxPointer.actor.style_class = 'summary-boxpointer';
|
||||
this.actor.add_actor(this._summaryBoxPointer.actor);
|
||||
this._summaryBoxPointer.actor.lower_bottom();
|
||||
this._summaryBoxPointer.actor.hide();
|
||||
Main.layoutManager.addChrome(this._summaryBoxPointer.actor, { visibleInFullscreen: true });
|
||||
|
||||
this._summaryBoxPointerItem = null;
|
||||
this._summaryBoxPointerContentUpdatedId = 0;
|
||||
@ -1314,6 +1373,7 @@ MessageTray.prototype = {
|
||||
|
||||
this._trayState = State.HIDDEN;
|
||||
this._locked = false;
|
||||
this._traySummoned = false;
|
||||
this._useLongerTrayLeftTimeout = false;
|
||||
this._trayLeftTimeoutId = 0;
|
||||
this._pointerInTray = false;
|
||||
@ -1329,10 +1389,10 @@ MessageTray.prototype = {
|
||||
this._notificationRemoved = false;
|
||||
this._reNotifyAfterHideNotification = null;
|
||||
|
||||
Main.chrome.addActor(this.actor, { affectsStruts: false,
|
||||
visibleInFullscreen: true });
|
||||
Main.chrome.trackActor(this._notificationBin);
|
||||
Main.chrome.trackActor(this._summaryBoxPointer.actor);
|
||||
Main.layoutManager.trayBox.add_actor(this.actor);
|
||||
this.actor.y = -1;
|
||||
Main.layoutManager.trackChrome(this.actor);
|
||||
Main.layoutManager.trackChrome(this._notificationBin);
|
||||
|
||||
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._setSizePosition));
|
||||
|
||||
@ -1371,22 +1431,10 @@ MessageTray.prototype = {
|
||||
|
||||
_setSizePosition: function() {
|
||||
let monitor = Main.layoutManager.bottomMonitor;
|
||||
this.actor.x = monitor.x;
|
||||
this.actor.y = monitor.y + monitor.height - 1;
|
||||
this.actor.width = monitor.width;
|
||||
this._notificationBin.x = 0;
|
||||
this._notificationBin.width = monitor.width;
|
||||
this._summaryBin.x = 0;
|
||||
this._summaryBin.width = monitor.width;
|
||||
|
||||
if (this._pointerBarrier)
|
||||
global.destroy_pointer_barrier(this._pointerBarrier);
|
||||
this._pointerBarrier =
|
||||
global.create_pointer_barrier(monitor.x + monitor.width, monitor.y + monitor.height - this.actor.height,
|
||||
monitor.x + monitor.width, monitor.y + monitor.height,
|
||||
4 /* BarrierNegativeX */);
|
||||
|
||||
|
||||
},
|
||||
|
||||
contains: function(source) {
|
||||
@ -1532,7 +1580,19 @@ MessageTray.prototype = {
|
||||
if (!this._locked)
|
||||
return;
|
||||
this._locked = false;
|
||||
this._pointerInTray = this.actor.hover && !this._summaryBoxPointer.bin.hover;
|
||||
this._pointerInTray = this.actor.hover;
|
||||
this._updateState();
|
||||
},
|
||||
|
||||
toggle: function() {
|
||||
this._traySummoned = !this._traySummoned;
|
||||
this._updateState();
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
this._traySummoned = false;
|
||||
this.actor.set_hover(false);
|
||||
this._summary.set_hover(false);
|
||||
this._updateState();
|
||||
},
|
||||
|
||||
@ -1720,13 +1780,6 @@ MessageTray.prototype = {
|
||||
if (this._useLongerTrayLeftTimeout && !this._trayLeftTimeoutId)
|
||||
return;
|
||||
|
||||
// Don't do anything if the mouse is over the summary notification as this should be considered as
|
||||
// leaving the tray. The tray is locked when the summary notification is visible anyway, but we
|
||||
// should treat the mouse being over the summary notification as the tray being left for collapsing
|
||||
// any expanded summary item other than the one related to the notification.
|
||||
if (this._summaryBoxPointer.bin.hover)
|
||||
return;
|
||||
|
||||
this._useLongerTrayLeftTimeout = false;
|
||||
if (this._trayLeftTimeoutId) {
|
||||
Mainloop.source_remove(this._trayLeftTimeoutId);
|
||||
@ -1850,7 +1903,7 @@ MessageTray.prototype = {
|
||||
}
|
||||
|
||||
// Summary
|
||||
let summarySummoned = this._pointerInSummary || this._overviewVisible;
|
||||
let summarySummoned = this._pointerInSummary || this._overviewVisible || this._traySummoned;
|
||||
let summaryPinned = this._summaryTimeoutId != 0 || this._pointerInTray || summarySummoned || this._locked;
|
||||
let summaryHovered = this._pointerInTray || this._pointerInSummary;
|
||||
let summaryVisibleWithNoHover = (this._overviewVisible || this._locked) && !summaryHovered;
|
||||
@ -1944,18 +1997,16 @@ MessageTray.prototype = {
|
||||
},
|
||||
|
||||
_showTray: function() {
|
||||
let monitor = Main.layoutManager.bottomMonitor;
|
||||
this._tween(this.actor, '_trayState', State.SHOWN,
|
||||
{ y: monitor.y + monitor.height - this.actor.height,
|
||||
{ y: -this.actor.height,
|
||||
time: ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
},
|
||||
|
||||
_hideTray: function() {
|
||||
let monitor = Main.layoutManager.bottomMonitor;
|
||||
this._tween(this.actor, '_trayState', State.HIDDEN,
|
||||
{ y: monitor.y + monitor.height - 1,
|
||||
{ y: -1,
|
||||
time: ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
@ -2043,7 +2094,7 @@ MessageTray.prototype = {
|
||||
|
||||
_notificationTimeout: function() {
|
||||
let [x, y, mods] = global.get_pointer();
|
||||
if (y > this._lastSeenMouseY + 10 && y < this.actor.y) {
|
||||
if (y > this._lastSeenMouseY + 10 && !this.actor.hover) {
|
||||
// The mouse is moving towards the notification, so don't
|
||||
// hide it yet. (We just create a new timeout (and destroy
|
||||
// the old one) each time because the bookkeeping is
|
||||
|
@ -18,6 +18,7 @@ const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const OPEN_AND_CLOSE_TIME = 0.1;
|
||||
const FADE_IN_BUTTONS_TIME = 0.33;
|
||||
const FADE_OUT_DIALOG_TIME = 1.0;
|
||||
|
||||
const State = {
|
||||
@ -34,10 +35,12 @@ function ModalDialog() {
|
||||
|
||||
ModalDialog.prototype = {
|
||||
_init: function(params) {
|
||||
params = Params.parse(params, { styleClass: null });
|
||||
params = Params.parse(params, { shellReactive: false,
|
||||
styleClass: null });
|
||||
|
||||
this.state = State.CLOSED;
|
||||
this._hasModal = false;
|
||||
this._shellReactive = params.shellReactive;
|
||||
|
||||
this._group = new St.Group({ visible: false,
|
||||
x: 0,
|
||||
@ -53,26 +56,30 @@ ModalDialog.prototype = {
|
||||
this._actionKeys = {};
|
||||
this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
|
||||
|
||||
this._lightbox = new Lightbox.Lightbox(this._group,
|
||||
{ inhibitEvents: true });
|
||||
|
||||
this._backgroundBin = new St.Bin();
|
||||
|
||||
this._group.add_actor(this._backgroundBin);
|
||||
this._lightbox.highlight(this._backgroundBin);
|
||||
|
||||
this._backgroundStack = new Shell.Stack();
|
||||
this._backgroundBin.child = this._backgroundStack;
|
||||
|
||||
this._eventBlocker = new Clutter.Group({ reactive: true });
|
||||
this._backgroundStack.add_actor(this._eventBlocker);
|
||||
|
||||
this._dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
|
||||
vertical: true });
|
||||
if (params.styleClass != null) {
|
||||
this._dialogLayout.add_style_class_name(params.styleClass);
|
||||
}
|
||||
this._backgroundStack.add_actor(this._dialogLayout);
|
||||
|
||||
if (!this._shellReactive) {
|
||||
this._lightbox = new Lightbox.Lightbox(this._group,
|
||||
{ inhibitEvents: true });
|
||||
this._lightbox.highlight(this._backgroundBin);
|
||||
|
||||
let stack = new Shell.Stack();
|
||||
this._backgroundBin.child = stack;
|
||||
|
||||
this._eventBlocker = new Clutter.Group({ reactive: true });
|
||||
stack.add_actor(this._eventBlocker);
|
||||
stack.add_actor(this._dialogLayout);
|
||||
} else {
|
||||
this._backgroundBin.child = this._dialogLayout;
|
||||
}
|
||||
|
||||
|
||||
this.contentLayout = new St.BoxLayout({ vertical: true });
|
||||
this._dialogLayout.add(this.contentLayout,
|
||||
@ -82,7 +89,6 @@ ModalDialog.prototype = {
|
||||
y_align: St.Align.START });
|
||||
|
||||
this._buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
|
||||
opacity: 220,
|
||||
vertical: false });
|
||||
this._dialogLayout.add(this._buttonLayout,
|
||||
{ expand: true,
|
||||
@ -94,7 +100,13 @@ ModalDialog.prototype = {
|
||||
this._savedKeyFocus = null;
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this._group.destroy();
|
||||
},
|
||||
|
||||
setButtons: function(buttons) {
|
||||
let hadChildren = this._buttonLayout.get_children() > 0;
|
||||
|
||||
this._buttonLayout.destroy_children();
|
||||
this._actionKeys = {};
|
||||
|
||||
@ -105,10 +117,10 @@ ModalDialog.prototype = {
|
||||
let action = buttonInfo['action'];
|
||||
let key = buttonInfo['key'];
|
||||
|
||||
let button = new St.Button({ style_class: 'modal-dialog-button',
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
label: label });
|
||||
buttonInfo.button = new St.Button({ style_class: 'modal-dialog-button',
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
label: label });
|
||||
|
||||
let x_alignment;
|
||||
if (buttons.length == 1)
|
||||
@ -120,20 +132,36 @@ ModalDialog.prototype = {
|
||||
else
|
||||
x_alignment = St.Align.MIDDLE;
|
||||
|
||||
this._initialKeyFocus = button;
|
||||
this._buttonLayout.add(button,
|
||||
this._initialKeyFocus = buttonInfo.button;
|
||||
this._buttonLayout.add(buttonInfo.button,
|
||||
{ expand: true,
|
||||
x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: x_alignment,
|
||||
y_align: St.Align.MIDDLE });
|
||||
|
||||
button.connect('clicked', action);
|
||||
buttonInfo.button.connect('clicked', action);
|
||||
|
||||
if (key)
|
||||
this._actionKeys[key] = action;
|
||||
i++;
|
||||
}
|
||||
|
||||
// Fade in buttons if there weren't any before
|
||||
if (!hadChildren && i > 0) {
|
||||
this._buttonLayout.opacity = 0;
|
||||
Tweener.addTween(this._buttonLayout,
|
||||
{ opacity: 255,
|
||||
time: FADE_IN_BUTTONS_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this.emit('buttons-set');
|
||||
})
|
||||
});
|
||||
} else {
|
||||
this.emit('buttons-set');
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
_onKeyPressEvent: function(object, keyPressEvent) {
|
||||
@ -157,7 +185,8 @@ ModalDialog.prototype = {
|
||||
this.state = State.OPENING;
|
||||
|
||||
this._dialogLayout.opacity = 255;
|
||||
this._lightbox.show();
|
||||
if (this._lightbox)
|
||||
this._lightbox.show();
|
||||
this._group.opacity = 0;
|
||||
this._group.show();
|
||||
Tweener.addTween(this._group,
|
||||
@ -223,7 +252,8 @@ ModalDialog.prototype = {
|
||||
global.gdk_screen.get_display().sync();
|
||||
this._hasModal = false;
|
||||
|
||||
this._eventBlocker.raise_top();
|
||||
if (!this._shellReactive)
|
||||
this._eventBlocker.raise_top();
|
||||
},
|
||||
|
||||
pushModal: function (timestamp) {
|
||||
@ -239,7 +269,8 @@ ModalDialog.prototype = {
|
||||
} else
|
||||
this._initialKeyFocus.grab_key_focus();
|
||||
|
||||
this._eventBlocker.lower_bottom();
|
||||
if (!this._shellReactive)
|
||||
this._eventBlocker.lower_bottom();
|
||||
return true;
|
||||
},
|
||||
|
||||
|
400
js/ui/networkAgent.js
Normal file
400
js/ui/networkAgent.js
Normal file
@ -0,0 +1,400 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||
*
|
||||
* Copyright 2011 Giovanni Campagna <scampa.giovanni@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const NetworkManager = imports.gi.NetworkManager;
|
||||
const NMClient = imports.gi.NMClient;
|
||||
const Pango = imports.gi.Pango;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
function NetworkSecretDialog() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
NetworkSecretDialog.prototype = {
|
||||
__proto__: ModalDialog.ModalDialog.prototype,
|
||||
|
||||
_init: function(agent, requestId, connection, settingName, hints) {
|
||||
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'polkit-dialog' });
|
||||
|
||||
this._agent = agent;
|
||||
this._requestId = requestId;
|
||||
this._connection = connection;
|
||||
this._settingName = settingName;
|
||||
this._hints = hints;
|
||||
|
||||
this._content = this._getContent();
|
||||
|
||||
let mainContentBox = new St.BoxLayout({ style_class: 'polkit-dialog-main-layout',
|
||||
vertical: false });
|
||||
this.contentLayout.add(mainContentBox,
|
||||
{ x_fill: true,
|
||||
y_fill: true });
|
||||
|
||||
let icon = new St.Icon({ icon_name: 'dialog-password-symbolic' });
|
||||
mainContentBox.add(icon,
|
||||
{ x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.END,
|
||||
y_align: St.Align.START });
|
||||
|
||||
let messageBox = new St.BoxLayout({ style_class: 'polkit-dialog-message-layout',
|
||||
vertical: true });
|
||||
mainContentBox.add(messageBox,
|
||||
{ y_align: St.Align.START });
|
||||
|
||||
let subjectLabel = new St.Label({ style_class: 'polkit-dialog-headline',
|
||||
text: this._content.title });
|
||||
messageBox.add(subjectLabel,
|
||||
{ y_fill: false,
|
||||
y_align: St.Align.START });
|
||||
|
||||
if (this._content.message != null) {
|
||||
let descriptionLabel = new St.Label({ style_class: 'polkit-dialog-description',
|
||||
text: this._content.message,
|
||||
// HACK: for reasons unknown to me, the label
|
||||
// is not asked the correct height for width,
|
||||
// and thus is underallocated
|
||||
// place a fixed height to avoid overflowing
|
||||
style: 'height: 3em'
|
||||
});
|
||||
descriptionLabel.clutter_text.line_wrap = true;
|
||||
|
||||
messageBox.add(descriptionLabel,
|
||||
{ y_fill: true,
|
||||
y_align: St.Align.START,
|
||||
expand: true });
|
||||
}
|
||||
|
||||
let secretTable = new St.Table({ style_class: 'network-dialog-secret-table' });
|
||||
let pos = 0;
|
||||
for (let i = 0; i < this._content.secrets.length; i++) {
|
||||
let secret = this._content.secrets[i];
|
||||
let label = new St.Label({ style_class: 'polkit-dialog-password-label',
|
||||
text: secret.label });
|
||||
|
||||
let reactive = secret.key != null;
|
||||
|
||||
secret.entry = new St.Entry({ style_class: 'polkit-dialog-password-entry',
|
||||
text: secret.value, can_focus: reactive,
|
||||
reactive: reactive });
|
||||
|
||||
if (secret.validate)
|
||||
secret.valid = secret.validate(secret);
|
||||
else // no special validation, just ensure it's not empty
|
||||
secret.valid = secret.value.length > 0;
|
||||
|
||||
if (reactive) {
|
||||
secret.entry.clutter_text.connect('text-changed', Lang.bind(this, function() {
|
||||
secret.value = secret.entry.get_text();
|
||||
if (secret.validate)
|
||||
secret.valid = secret.validate(secret);
|
||||
else
|
||||
secret.valid = secret.value.length > 0;
|
||||
this._updateOkButton();
|
||||
}));
|
||||
} else
|
||||
secret.valid = true;
|
||||
|
||||
secretTable.add(label, { row: pos, col: 0, x_align: St.Align.START, y_align: St.Align.START });
|
||||
secretTable.add(secret.entry, { row: pos, col: 1, x_expand: true, x_fill: true, y_align: St.Align.END });
|
||||
pos++;
|
||||
|
||||
if (secret.password) {
|
||||
secret.entry.clutter_text.set_password_char('\u25cf');
|
||||
|
||||
// FIXME: need a real checkbox here
|
||||
let button = new St.Button({ button_mask: St.ButtonMask.ONE,
|
||||
can_focus: true });
|
||||
let checkbox = new St.BoxLayout({ vertical: false,
|
||||
style_class: 'network-dialog-show-password-checkbox'
|
||||
});
|
||||
let _switch = new PopupMenu.Switch(false);
|
||||
checkbox.add(_switch.actor);
|
||||
checkbox.add(new St.Label({ text: _("Show password") }), { expand: true });
|
||||
button.connect('clicked', function() {
|
||||
_switch.toggle();
|
||||
if (_switch.state)
|
||||
secret.entry.clutter_text.set_password_char('');
|
||||
else
|
||||
secret.entry.clutter_text.set_password_char('\u25cf');
|
||||
});
|
||||
button.child = checkbox;
|
||||
secretTable.add(button, { row: pos, col: 1, x_expand: true, x_fill: true, y_fill: true })
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
messageBox.add(secretTable);
|
||||
|
||||
this._okButton = { label: _("Connect"),
|
||||
action: Lang.bind(this, this._onOk),
|
||||
key: Clutter.KEY_Return,
|
||||
};
|
||||
|
||||
this.setButtons([{ label: _("Cancel"),
|
||||
action: Lang.bind(this, this.cancel),
|
||||
key: Clutter.KEY_Escape,
|
||||
},
|
||||
this._okButton]);
|
||||
},
|
||||
|
||||
_updateOkButton: function() {
|
||||
let valid = true;
|
||||
for (let i = 0; i < this._content.secrets.length; i++) {
|
||||
let secret = this._content.secrets[i];
|
||||
valid = valid && secret.valid;
|
||||
}
|
||||
|
||||
this._okButton.button.reactive = valid;
|
||||
this._okButton.button.can_focus = valid;
|
||||
if (valid)
|
||||
this._okButton.button.remove_style_pseudo_class('disabled');
|
||||
else
|
||||
this._okButton.button.add_style_pseudo_class('disabled');
|
||||
},
|
||||
|
||||
_onOk: function() {
|
||||
let valid = true;
|
||||
for (let i = 0; i < this._content.secrets.length; i++) {
|
||||
let secret = this._content.secrets[i];
|
||||
valid = valid && secret.valid;
|
||||
if (secret.key != null)
|
||||
this._agent.set_password(this._requestId, secret.key, secret.value);
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
this._agent.respond(this._requestId, false);
|
||||
this.close(global.get_current_time());
|
||||
}
|
||||
// do nothing if not valid
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
this._agent.respond(this._requestId, true);
|
||||
this.close(global.get_current_time());
|
||||
},
|
||||
|
||||
_validateWpaPsk: function(secret) {
|
||||
let value = secret.value;
|
||||
if (value.length == 64) {
|
||||
// must be composed of hexadecimal digits only
|
||||
for (let i = 0; i < 64; i++) {
|
||||
if (!((value[i] >= 'a' && value[i] <= 'f')
|
||||
|| (value[i] >= 'A' && value[i] <= 'F')
|
||||
|| (value[i] >= '0' && value[i] <= '9')))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return (value.length >= 8 && value.length <= 63);
|
||||
},
|
||||
|
||||
_validateStaticWep: function(secret) {
|
||||
let value = secret.value;
|
||||
if (secret.wep_key_type == NetworkManager.WepKeyType.KEY) {
|
||||
if (value.length == 10 || value.length == 26) {
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
if (!((value[i] >= 'a' && value[i] <= 'f')
|
||||
|| (value[i] >= 'A' && value[i] <= 'F')
|
||||
|| (value[i] >= '0' && value[i] <= '9')))
|
||||
return false;
|
||||
}
|
||||
} else if (value.length == 5 || value.length == 13) {
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
if (!((value[i] >= 'a' && value[i] <= 'z')
|
||||
|| (value[i] >= 'A' && value[i] <= 'Z')))
|
||||
return false;
|
||||
}
|
||||
} else
|
||||
return false;
|
||||
} else if (secret.wep_key_type == NetworkManager.WepKeyType.PASSPHRASE) {
|
||||
if (value.length < 0 || value.length > 64)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
_getWirelessSecrets: function(secrets, wirelessSetting) {
|
||||
let wirelessSecuritySetting = this._connection.get_setting_wireless_security();
|
||||
switch (wirelessSecuritySetting.key_mgmt) {
|
||||
// First the easy ones
|
||||
case 'wpa-none':
|
||||
case 'wpa-psk':
|
||||
secrets.push({ label: _("Password: "), key: 'psk',
|
||||
value: wirelessSecuritySetting.psk || '',
|
||||
validate: this._validateWpaPsk, password: true });
|
||||
break;
|
||||
case 'none': // static WEP
|
||||
secrets.push({ label: _("Key: "), key: 'wep-key' + wirelessSecuritySetting.wep_tx_keyidx,
|
||||
value: wirelessSecuritySetting.get_wep_key(wirelessSecuritySetting.wep_tx_keyidx) || '',
|
||||
wep_key_type: wirelessSecuritySetting.wep_key_type,
|
||||
validate: this._validateStaticWep, password: true });
|
||||
break;
|
||||
case 'ieee8021x':
|
||||
if (wirelessSecuritySetting.auth_alg == 'leap') // Cisco LEAP
|
||||
secrets.push({ label: _("Password: "), key: 'leap-password',
|
||||
value: wirelessSecuritySetting.leap_password || '', password: true });
|
||||
else // Dynamic (IEEE 802.1x) WEP
|
||||
this._get8021xSecrets(secrets);
|
||||
break;
|
||||
case 'wpa-eap':
|
||||
this._get8021xSecrets(secrets);
|
||||
break;
|
||||
default:
|
||||
log('Invalid wireless key management: ' + wirelessSecuritySetting.key_mgmt);
|
||||
}
|
||||
},
|
||||
|
||||
_get8021xSecrets: function(secrets) {
|
||||
let ieee8021xSetting = this._connection.get_setting_802_1x();
|
||||
let phase2method;
|
||||
|
||||
switch (ieee8021xSetting.get_eap_method(0)) {
|
||||
case 'md5':
|
||||
case 'leap':
|
||||
case 'ttls':
|
||||
case 'peap':
|
||||
// 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)
|
||||
secrets.push({ label: _("Username: "), key: null,
|
||||
value: ieee8021xSetting.identity || '', password: false });
|
||||
secrets.push({ label: _("Password: "), key: 'password',
|
||||
value: ieee8021xSetting.password || '', password: true });
|
||||
break;
|
||||
case 'tls':
|
||||
secrets.push({ label: _("Identity: "), key: null,
|
||||
value: ieee8021xSetting.identity || '', password: false });
|
||||
secrets.push({ label: _("Private key password: "), key: 'private-key-password',
|
||||
value: ieee8021xSetting.private_key_password || '', password: true });
|
||||
break;
|
||||
default:
|
||||
log('Invalid EAP/IEEE802.1x method: ' + ieee8021xSetting.get_eap_method(0));
|
||||
}
|
||||
},
|
||||
|
||||
_getPPPoESecrets: function(secrets) {
|
||||
let pppoeSetting = this._connection.get_setting_pppoe();
|
||||
secrets.push({ label: _("Username: "), key: 'username',
|
||||
value: pppoeSetting.username || '', password: false });
|
||||
secrets.push({ label: _("Service: "), key: 'service',
|
||||
value: pppoeSetting.service || '', password: false });
|
||||
secrets.push({ label: _("Password: "), key: 'password',
|
||||
value: pppoeSetting.password || '', password: true });
|
||||
},
|
||||
|
||||
_getMobileSecrets: function(secrets, connectionType) {
|
||||
let setting;
|
||||
if (connectionType == 'bluetooth')
|
||||
setting = this._connection.get_setting_cdma() || this._connection.get_setting_gsm();
|
||||
else
|
||||
setting = this._connection.get_setting_by_name(connectionType);
|
||||
secrets.push({ label: _("Password: "), key: 'password',
|
||||
value: setting.value || '', password: true });
|
||||
},
|
||||
|
||||
_getContent: function() {
|
||||
let connectionSetting = this._connection.get_setting_connection();
|
||||
let connectionType = connectionSetting.get_connection_type();
|
||||
let wirelessSetting;
|
||||
let ssid;
|
||||
|
||||
let content = { };
|
||||
content.secrets = [ ];
|
||||
|
||||
switch (connectionType) {
|
||||
case '802-11-wireless':
|
||||
wirelessSetting = this._connection.get_setting_wireless();
|
||||
ssid = NetworkManager.utils_ssid_to_utf8(wirelessSetting.get_ssid());
|
||||
content.title = _("Authentication required by wireless network");
|
||||
content.message = _("Passwords or encryption keys are required to access the wireless network '%s'.").format(ssid);
|
||||
this._getWirelessSecrets(content.secrets, wirelessSetting);
|
||||
break;
|
||||
case '802-3-ethernet':
|
||||
content.title = _("Wired 802.1X authentication");
|
||||
content.message = null;
|
||||
content.secrets.push({ label: _("Network name: "), key: null,
|
||||
value: connectionSetting.get_id(), password: false });
|
||||
this._get8021xSecrets(content.secrets);
|
||||
break;
|
||||
case 'pppoe':
|
||||
content.title = _("DSL authentication");
|
||||
content.message = null;
|
||||
this._getPPPoESecrets(content.secrets);
|
||||
break;
|
||||
case 'gsm':
|
||||
if (this._hints.indexOf('pin') != -1) {
|
||||
let gsmSetting = this._connection.get_setting_gsm();
|
||||
content.title = _("PIN code required");
|
||||
content.message = _("PIN code is needed for the mobile broadband device");
|
||||
content.secrets.push({ label: _("PIN: "), key: 'pin',
|
||||
value: gsmSetting.pin || '', password: true });
|
||||
}
|
||||
// fall through
|
||||
case 'cdma':
|
||||
case 'bluetooth':
|
||||
content.title = _("Mobile broadband network password");
|
||||
content.message = _("A password is required to connect to '%s'.").format(connectionSetting.get_id());
|
||||
this._getMobileSecrets(content.secrets);
|
||||
break;
|
||||
default:
|
||||
log('Invalid connection type: ' + connectionType);
|
||||
};
|
||||
|
||||
return content;
|
||||
}
|
||||
};
|
||||
|
||||
function NetworkAgent() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
NetworkAgent.prototype = {
|
||||
_init: function() {
|
||||
this._native = new Shell.NetworkAgent({ auto_register: true,
|
||||
identifier: 'org.gnome.Shell.NetworkAgent' });
|
||||
|
||||
this._dialogs = { };
|
||||
this._native.connect('new-request', Lang.bind(this, this._newRequest));
|
||||
this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest));
|
||||
},
|
||||
|
||||
_newRequest: function(agent, requestId, connection, settingName, hints) {
|
||||
let dialog = new NetworkSecretDialog(agent, requestId, connection, settingName, hints);
|
||||
dialog.connect('destroy', Lang.bind(this, function() {
|
||||
delete this._dialogs[requestId];
|
||||
}));
|
||||
this._dialogs[requestId] = dialog;
|
||||
dialog.open(global.get_current_time());
|
||||
},
|
||||
|
||||
_cancelRequest: function(agent, requestId) {
|
||||
this._dialogs[requestId].close(global.get_current_time());
|
||||
this._dialogs[requestId].destroy();
|
||||
}
|
||||
};
|
@ -119,11 +119,6 @@ NotificationDaemon.prototype = {
|
||||
return new St.Icon({ icon_name: icon,
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: size });
|
||||
} else if (hints['image-data']) {
|
||||
let [width, height, rowStride, hasAlpha,
|
||||
bitsPerSample, nChannels, data] = hints['image-data'];
|
||||
return textureCache.load_from_raw(data, hasAlpha,
|
||||
width, height, rowStride, size);
|
||||
} else {
|
||||
let stockIcon;
|
||||
switch (hints.urgency) {
|
||||
@ -195,6 +190,8 @@ NotificationDaemon.prototype = {
|
||||
if (appName == 'Empathy' && (hints['category'] == 'im.received' ||
|
||||
hints['category'] == 'x-empathy.im.room-invitation' ||
|
||||
hints['category'] == 'x-empathy.call.incoming' ||
|
||||
hints['category'] == 'x-empathy.call.incoming"' ||
|
||||
hints['category'] == 'x-empathy.im.subscription-request' ||
|
||||
hints['category'] == 'presence.online' ||
|
||||
hints['category'] == 'presence.offline')) {
|
||||
// Ignore replacesId since we already sent back a
|
||||
@ -218,14 +215,18 @@ NotificationDaemon.prototype = {
|
||||
|
||||
hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
|
||||
|
||||
// Be compatible with the various hints for image data
|
||||
// 'image-data' is the latest name of this hint, introduced in 1.2
|
||||
if (!hints['image-data']) {
|
||||
// Be compatible with the various hints for image data and image path
|
||||
// 'image-data' and 'image-path' are the latest name of these hints, introduced in 1.2
|
||||
|
||||
if (!hints['image-path'] && hints['image_path'])
|
||||
hints['image-path'] = hints['image_path']; // version 1.1 of the spec
|
||||
|
||||
if (!hints['image-data'])
|
||||
if (hints['image_data'])
|
||||
hints['image-data'] = hints['image_data']; // version 1.1 of the spec
|
||||
else if (hints['icon_data'])
|
||||
hints['image-data'] = hints['icon_data']; // previous versions of the spec
|
||||
}
|
||||
else if (hints['icon_data'] && !hints['image-path'])
|
||||
// early versions of the spec; 'icon_data' should only be used if 'image-path' is not available
|
||||
hints['image-data'] = hints['icon_data'];
|
||||
|
||||
let ndata = { appName: appName,
|
||||
icon: icon,
|
||||
@ -304,7 +305,7 @@ NotificationDaemon.prototype = {
|
||||
ndata.notification = notification;
|
||||
notification.connect('destroy', Lang.bind(this,
|
||||
function(n, reason) {
|
||||
delete this._notifications[id];
|
||||
delete this._notifications[ndata.id];
|
||||
let notificationClosedReason;
|
||||
switch (reason) {
|
||||
case MessageTray.NotificationDestroyedReason.EXPIRED:
|
||||
@ -317,11 +318,11 @@ NotificationDaemon.prototype = {
|
||||
notificationClosedReason = NotificationClosedReason.APP_CLOSED;
|
||||
break;
|
||||
}
|
||||
this._emitNotificationClosed(id, notificationClosedReason);
|
||||
this._emitNotificationClosed(ndata.id, notificationClosedReason);
|
||||
}));
|
||||
notification.connect('action-invoked', Lang.bind(this,
|
||||
function(n, actionId) {
|
||||
this._emitActionInvoked(id, actionId);
|
||||
this._emitActionInvoked(ndata.id, actionId);
|
||||
}));
|
||||
} else {
|
||||
notification.update(summary, body, { icon: iconActor,
|
||||
@ -329,10 +330,34 @@ NotificationDaemon.prototype = {
|
||||
clear: true });
|
||||
}
|
||||
|
||||
if (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);
|
||||
for (let i = 0; i < actions.length - 1; i += 2)
|
||||
notification.addButton(actions[i], actions[i + 1]);
|
||||
for (let i = 0; i < actions.length - 1; i += 2) {
|
||||
if (actions[i] == 'default')
|
||||
notification.connect('clicked', Lang.bind(this,
|
||||
function() {
|
||||
this._emitActionInvoked(ndata.id, "default");
|
||||
}));
|
||||
else
|
||||
notification.addButton(actions[i], actions[i + 1]);
|
||||
}
|
||||
}
|
||||
switch (hints.urgency) {
|
||||
case Urgency.LOW:
|
||||
@ -461,9 +486,11 @@ Source.prototype = {
|
||||
|
||||
_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
|
||||
// of which аre removed from DBus immediately.
|
||||
// Sender being removed from DBus would normally result in a tray icon being removed,
|
||||
// so allow the code path that handles the tray icon being removed to handle that case.
|
||||
if (!this.trayIcon)
|
||||
if (!this.trayIcon && this.app)
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
|
@ -11,6 +11,7 @@ const Shell = imports.gi.Shell;
|
||||
const Gdk = imports.gi.Gdk;
|
||||
|
||||
const AppDisplay = imports.ui.appDisplay;
|
||||
const ContactDisplay = imports.ui.contactDisplay;
|
||||
const Dash = imports.ui.dash;
|
||||
const DND = imports.ui.dnd;
|
||||
const DocDisplay = imports.ui.docDisplay;
|
||||
@ -18,6 +19,7 @@ const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const Panel = imports.ui.panel;
|
||||
const Params = imports.misc.params;
|
||||
const PlaceDisplay = imports.ui.placeDisplay;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const ViewSelector = imports.ui.viewSelector;
|
||||
@ -96,14 +98,30 @@ ShellInfo.prototype = {
|
||||
};
|
||||
|
||||
function Overview() {
|
||||
this._init();
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
Overview.prototype = {
|
||||
_init : function() {
|
||||
// The actual global.background_actor is inside global.window_group,
|
||||
// which is hidden when displaying the overview, so we display a clone.
|
||||
this._background = new Clutter.Clone({ source: global.background_actor });
|
||||
_init : function(params) {
|
||||
params = Params.parse(params, { isDummy: false });
|
||||
|
||||
this.isDummy = params.isDummy;
|
||||
|
||||
// We only have an overview in user sessions, so
|
||||
// create a dummy overview in other cases
|
||||
if (this.isDummy) {
|
||||
this.animationInProgress = false;
|
||||
this.visible = false;
|
||||
this.workspaces = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// The main BackgroundActor is inside global.window_group which is
|
||||
// hidden when displaying the overview, so we create a new
|
||||
// one. Instances of this class share a single CoglTexture behind the
|
||||
// scenes which allows us to show the background with different
|
||||
// rendering options without duplicating the texture data.
|
||||
this._background = Meta.BackgroundActor.new_for_screen(global.screen);
|
||||
this._background.hide();
|
||||
global.overlay_group.add_actor(this._background);
|
||||
|
||||
@ -175,37 +193,53 @@ Overview.prototype = {
|
||||
// signal handlers and so forth. So we create them after
|
||||
// construction in this init() method.
|
||||
init: function() {
|
||||
this.shellInfo = new ShellInfo();
|
||||
if (this.isDummy)
|
||||
return;
|
||||
|
||||
this.viewSelector = new ViewSelector.ViewSelector();
|
||||
this._group.add_actor(this.viewSelector.actor);
|
||||
this._shellInfo = new ShellInfo();
|
||||
|
||||
this._viewSelector = new ViewSelector.ViewSelector();
|
||||
this._group.add_actor(this._viewSelector.actor);
|
||||
|
||||
this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay();
|
||||
this.viewSelector.addViewTab('windows', _("Windows"), this._workspacesDisplay.actor, 'text-x-generic');
|
||||
this._viewSelector.addViewTab('windows', _("Windows"), this._workspacesDisplay.actor, 'text-x-generic');
|
||||
|
||||
let appView = new AppDisplay.AllAppDisplay();
|
||||
this.viewSelector.addViewTab('applications', _("Applications"), appView.actor, 'system-run');
|
||||
this._viewSelector.addViewTab('applications', _("Applications"), appView.actor, 'system-run');
|
||||
|
||||
// Default search providers
|
||||
this.viewSelector.addSearchProvider(new AppDisplay.AppSearchProvider());
|
||||
this.viewSelector.addSearchProvider(new AppDisplay.PrefsSearchProvider());
|
||||
this.viewSelector.addSearchProvider(new PlaceDisplay.PlaceSearchProvider());
|
||||
this.viewSelector.addSearchProvider(new DocDisplay.DocSearchProvider());
|
||||
this._viewSelector.addSearchProvider(new AppDisplay.AppSearchProvider());
|
||||
this._viewSelector.addSearchProvider(new AppDisplay.SettingsSearchProvider());
|
||||
this._viewSelector.addSearchProvider(new PlaceDisplay.PlaceSearchProvider());
|
||||
this._viewSelector.addSearchProvider(new DocDisplay.DocSearchProvider());
|
||||
this._viewSelector.addSearchProvider(new ContactDisplay.ContactSearchProvider());
|
||||
|
||||
// TODO - recalculate everything when desktop size changes
|
||||
this.dash = new Dash.Dash();
|
||||
this._group.add_actor(this.dash.actor);
|
||||
this.dash.actor.add_constraint(this.viewSelector.constrainY);
|
||||
this.dash.actor.add_constraint(this.viewSelector.constrainHeight);
|
||||
this._dash = new Dash.Dash();
|
||||
this._group.add_actor(this._dash.actor);
|
||||
this._dash.actor.add_constraint(this._viewSelector.constrainY);
|
||||
this._dash.actor.add_constraint(this._viewSelector.constrainHeight);
|
||||
this.dashIconSize = this._dash.iconSize;
|
||||
this._dash.connect('icon-size-changed',
|
||||
Lang.bind(this, function() {
|
||||
this.dashIconSize = this._dash.iconSize;
|
||||
}));
|
||||
|
||||
// Translators: this is the name of the dock/favorites area on
|
||||
// the left of the overview
|
||||
Main.ctrlAltTabManager.addGroup(this.dash.actor, _("Dash"), 'user-bookmarks');
|
||||
Main.ctrlAltTabManager.addGroup(this._dash.actor, _("Dash"), 'user-bookmarks');
|
||||
|
||||
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
|
||||
this._relayout();
|
||||
},
|
||||
|
||||
setMessage: function(text, undoCallback, undoLabel) {
|
||||
if (this.isDummy)
|
||||
return;
|
||||
|
||||
this._shellInfo.setMessage(text, undoCallback, undoLabel);
|
||||
},
|
||||
|
||||
_onDragBegin: function() {
|
||||
DND.addDragMonitor(this._dragMonitor);
|
||||
// Remember the workspace we started from
|
||||
@ -275,6 +309,9 @@ Overview.prototype = {
|
||||
},
|
||||
|
||||
setScrollAdjustment: function(adjustment, direction) {
|
||||
if (this.isDummy)
|
||||
return;
|
||||
|
||||
this._scrollAdjustment = adjustment;
|
||||
if (this._scrollAdjustment == null)
|
||||
this._scrollDirection = SwipeScrollDirection.NONE;
|
||||
@ -467,15 +504,15 @@ Overview.prototype = {
|
||||
// Set the dash's x position - y is handled by a constraint
|
||||
let dashX;
|
||||
if (rtl) {
|
||||
this.dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
|
||||
this._dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
|
||||
dashX = primary.width;
|
||||
} else {
|
||||
dashX = 0;
|
||||
}
|
||||
this.dash.actor.set_x(dashX);
|
||||
this._dash.actor.set_x(dashX);
|
||||
|
||||
this.viewSelector.actor.set_position(viewX, viewY);
|
||||
this.viewSelector.actor.set_size(viewWidth, viewHeight);
|
||||
this._viewSelector.actor.set_position(viewX, viewY);
|
||||
this._viewSelector.actor.set_size(viewWidth, viewHeight);
|
||||
},
|
||||
|
||||
//// Public methods ////
|
||||
@ -508,6 +545,8 @@ Overview.prototype = {
|
||||
//
|
||||
// Animates the overview visible and grabs mouse and keyboard input
|
||||
show : function() {
|
||||
if (this.isDummy)
|
||||
return;
|
||||
if (this._shown)
|
||||
return;
|
||||
// Do this manually instead of using _syncInputMode, to handle failure
|
||||
@ -535,6 +574,9 @@ Overview.prototype = {
|
||||
//
|
||||
// If we switched to displaying the actors in the Overview rather than
|
||||
// clones of them, this would obviously no longer be necessary.
|
||||
//
|
||||
// Disable unredirection while in the overview
|
||||
Meta.disable_unredirect_for_screen(global.screen);
|
||||
global.window_group.hide();
|
||||
this._group.show();
|
||||
this._background.show();
|
||||
@ -566,6 +608,12 @@ Overview.prototype = {
|
||||
onCompleteScope: this
|
||||
});
|
||||
|
||||
Tweener.addTween(this._background,
|
||||
{ dim_factor: 0.4,
|
||||
time: ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
|
||||
this._coverPane.raise_top();
|
||||
this._coverPane.show();
|
||||
this.emit('showing');
|
||||
@ -578,6 +626,9 @@ Overview.prototype = {
|
||||
// will result in the overview not being hidden until hideTemporarily() is
|
||||
// called.
|
||||
showTemporarily: function() {
|
||||
if (this.isDummy)
|
||||
return;
|
||||
|
||||
if (this._shownTemporarily)
|
||||
return;
|
||||
|
||||
@ -590,6 +641,9 @@ Overview.prototype = {
|
||||
//
|
||||
// Reverses the effect of show()
|
||||
hide: function() {
|
||||
if (this.isDummy)
|
||||
return;
|
||||
|
||||
if (!this._shown)
|
||||
return;
|
||||
|
||||
@ -608,6 +662,9 @@ Overview.prototype = {
|
||||
//
|
||||
// Reverses the effect of showTemporarily()
|
||||
hideTemporarily: function() {
|
||||
if (this.isDummy)
|
||||
return;
|
||||
|
||||
if (!this._shownTemporarily)
|
||||
return;
|
||||
|
||||
@ -619,6 +676,9 @@ Overview.prototype = {
|
||||
},
|
||||
|
||||
toggle: function() {
|
||||
if (this.isDummy)
|
||||
return;
|
||||
|
||||
if (this._shown)
|
||||
this.hide();
|
||||
else
|
||||
@ -684,6 +744,12 @@ Overview.prototype = {
|
||||
onCompleteScope: this
|
||||
});
|
||||
|
||||
Tweener.addTween(this._background,
|
||||
{ dim_factor: 1.0,
|
||||
time: ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
|
||||
this._coverPane.raise_top();
|
||||
this._coverPane.show();
|
||||
this.emit('hiding');
|
||||
@ -704,6 +770,9 @@ Overview.prototype = {
|
||||
},
|
||||
|
||||
_hideDone: function() {
|
||||
// Re-enable unredirection
|
||||
Meta.enable_unredirect_for_screen(global.screen);
|
||||
|
||||
global.window_group.show();
|
||||
|
||||
this.workspaces.destroy();
|
||||
|
425
js/ui/panel.js
425
js/ui/panel.js
@ -16,21 +16,19 @@ const Layout = imports.ui.layout;
|
||||
const Overview = imports.ui.overview;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const StatusMenu = imports.ui.statusMenu;
|
||||
const UserMenu = imports.ui.userMenu;
|
||||
const DateMenu = imports.ui.dateMenu;
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const PANEL_ICON_SIZE = 24;
|
||||
|
||||
const STARTUP_ANIMATION_TIME = 0.2;
|
||||
|
||||
const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
|
||||
|
||||
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
|
||||
const SPINNER_ANIMATION_TIME = 0.2;
|
||||
|
||||
const STANDARD_TRAY_ICON_ORDER = ['a11y', 'display', 'keyboard', 'volume', 'bluetooth', 'network', 'battery'];
|
||||
const STANDARD_TRAY_ICON_ORDER = ['a11y', 'keyboard', 'volume', 'bluetooth', 'network', 'battery'];
|
||||
const STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION = {
|
||||
'a11y': imports.ui.status.accessibility.ATIndicator,
|
||||
'volume': imports.ui.status.volume.Indicator,
|
||||
@ -47,6 +45,14 @@ try {
|
||||
log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
|
||||
}
|
||||
|
||||
const GDM_TRAY_ICON_ORDER = ['a11y', 'display', 'keyboard', 'volume', 'battery'];
|
||||
const GDM_TRAY_ICON_SHELL_IMPLEMENTATION = {
|
||||
'a11y': imports.ui.status.accessibility.ATIndicator,
|
||||
'volume': imports.ui.status.volume.Indicator,
|
||||
'battery': imports.ui.status.power.Indicator,
|
||||
'keyboard': imports.ui.status.keyboard.XKBIndicator
|
||||
};
|
||||
|
||||
// To make sure the panel corners blend nicely with the panel,
|
||||
// we draw background and borders the same way, e.g. drawing
|
||||
// them as filled shapes from the outside inwards instead of
|
||||
@ -237,7 +243,6 @@ AppMenuButton.prototype = {
|
||||
|
||||
_init: function() {
|
||||
PanelMenu.Button.prototype._init.call(this, 0.0);
|
||||
this._metaDisplay = global.screen.get_display();
|
||||
this._startingApps = [];
|
||||
|
||||
this._targetApp = null;
|
||||
@ -698,12 +703,94 @@ function PanelCorner(panel, side) {
|
||||
}
|
||||
|
||||
PanelCorner.prototype = {
|
||||
_init: function(panel, side) {
|
||||
this._panel = panel;
|
||||
_init: function(box, 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));
|
||||
this.actor.connect('style-changed', Lang.bind(this, this.relayout));
|
||||
},
|
||||
|
||||
_findRightmostButton: function(container) {
|
||||
if (!container.get_children)
|
||||
return null;
|
||||
|
||||
let children = container.get_children();
|
||||
|
||||
if (!children || children.length == 0)
|
||||
return null;
|
||||
|
||||
// Start at the back and work backward
|
||||
let index = children.length - 1;
|
||||
while (!children[index].visible && index >= 0)
|
||||
index--;
|
||||
|
||||
if (index < 0)
|
||||
return null;
|
||||
|
||||
if (!(children[index].has_style_class_name('panel-menu')) &&
|
||||
!(children[index].has_style_class_name('panel-button')))
|
||||
return this._findRightmostButton(children[index]);
|
||||
|
||||
return children[index];
|
||||
},
|
||||
|
||||
_findLeftmostButton: function(container) {
|
||||
if (!container.get_children)
|
||||
return null;
|
||||
|
||||
let children = container.get_children();
|
||||
|
||||
if (!children || children.length == 0)
|
||||
return null;
|
||||
|
||||
// Start at the front and work forward
|
||||
let index = 0;
|
||||
while (!children[index].visible && index < children.length)
|
||||
index++;
|
||||
|
||||
if (index == children.length)
|
||||
return null;
|
||||
|
||||
if (!(children[index].has_style_class_name('panel-menu')) &&
|
||||
!(children[index].has_style_class_name('panel-button')))
|
||||
return this._findLeftmostButton(children[index]);
|
||||
|
||||
return children[index];
|
||||
},
|
||||
|
||||
_boxStyleChanged: function() {
|
||||
let button;
|
||||
|
||||
if (this._side == St.Side.LEFT)
|
||||
button = this._findLeftmostButton(this._box);
|
||||
else if (this._side == St.Side.RIGHT)
|
||||
button = this._findRightmostButton(this._box);
|
||||
|
||||
if (button) {
|
||||
if (this._button && this._buttonStyleChangedSignalId)
|
||||
this._button.disconnect(this._buttonStyleChangedSignalId);
|
||||
|
||||
this._button = button;
|
||||
|
||||
button.connect('destroy', Lang.bind(this,
|
||||
function() {
|
||||
if (this._button == button) {
|
||||
this._button = null;
|
||||
this._buttonStyleChangedSignalId = 0;
|
||||
}
|
||||
}));
|
||||
|
||||
// Synchronize the locate button's pseudo classes with this corner
|
||||
this._buttonStyleChangedSignalId = button.connect('style-changed', Lang.bind(this,
|
||||
function(actor) {
|
||||
let pseudoClass = button.get_style_pseudo_class();
|
||||
this.actor.set_style_pseudo_class(pseudoClass);
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
_repaint: function() {
|
||||
@ -766,20 +853,14 @@ PanelCorner.prototype = {
|
||||
cr.restore();
|
||||
},
|
||||
|
||||
relayout: function() {
|
||||
_styleChanged: function() {
|
||||
let node = this.actor.get_theme_node();
|
||||
|
||||
let cornerRadius = node.get_length("-panel-corner-radius");
|
||||
let innerBorderWidth = node.get_length('-panel-corner-inner-border-width');
|
||||
|
||||
this.actor.set_size(cornerRadius,
|
||||
innerBorderWidth + cornerRadius);
|
||||
if (this._side == St.Side.LEFT)
|
||||
this.actor.set_position(this._panel.actor.x,
|
||||
this._panel.actor.y + this._panel.actor.height - innerBorderWidth);
|
||||
else
|
||||
this.actor.set_position(this._panel.actor.x + this._panel.actor.width - cornerRadius,
|
||||
this._panel.actor.y + this._panel.actor.height - innerBorderWidth);
|
||||
this.actor.set_size(cornerRadius, innerBorderWidth + cornerRadius);
|
||||
this.actor.set_anchor_point(0, innerBorderWidth);
|
||||
}
|
||||
};
|
||||
|
||||
@ -790,9 +871,8 @@ function Panel() {
|
||||
|
||||
Panel.prototype = {
|
||||
_init : function() {
|
||||
this.actor = new St.BoxLayout({ style_class: 'menu-bar',
|
||||
name: 'panel',
|
||||
reactive: true });
|
||||
this.actor = new Shell.GenericContainer({ name: 'panel',
|
||||
reactive: true });
|
||||
this.actor._delegate = this;
|
||||
|
||||
this._statusArea = {};
|
||||
@ -804,113 +884,60 @@ Panel.prototype = {
|
||||
this.actor.remove_style_class_name('in-overview');
|
||||
}));
|
||||
|
||||
this._leftPointerBarrier = 0;
|
||||
this._rightPointerBarrier = 0;
|
||||
if (global.session_type == Shell.SessionType.GDM) {
|
||||
this._tray_icon_order = GDM_TRAY_ICON_ORDER;
|
||||
this._tray_icon_shell_implementation = GDM_TRAY_ICON_SHELL_IMPLEMENTATION;
|
||||
} else {
|
||||
this._tray_icon_order = STANDARD_TRAY_ICON_ORDER;
|
||||
this._tray_icon_shell_implementation = STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION;
|
||||
}
|
||||
|
||||
this._menus = new PopupMenu.PopupMenuManager(this);
|
||||
|
||||
this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
|
||||
this.actor.add_actor(this._leftBox);
|
||||
this._centerBox = new St.BoxLayout({ name: 'panelCenter' });
|
||||
this.actor.add_actor(this._centerBox);
|
||||
this._rightBox = new St.BoxLayout({ name: 'panelRight' });
|
||||
this.actor.add_actor(this._rightBox);
|
||||
|
||||
this._leftCorner = new PanelCorner(this, St.Side.LEFT);
|
||||
this._rightCorner = new PanelCorner(this, St.Side.RIGHT);
|
||||
if (this.actor.get_direction() == St.TextDirection.RTL)
|
||||
this._leftCorner = new PanelCorner(this._rightBox, St.Side.LEFT);
|
||||
else
|
||||
this._leftCorner = new PanelCorner(this._leftBox, St.Side.LEFT);
|
||||
|
||||
/* This box container ensures that the centerBox is positioned in the *absolute*
|
||||
* center, but can be pushed aside if necessary. */
|
||||
this._boxContainer = new Shell.GenericContainer();
|
||||
this.actor.add(this._boxContainer, { expand: true });
|
||||
this._boxContainer.add_actor(this._leftBox);
|
||||
this._boxContainer.add_actor(this._centerBox);
|
||||
this._boxContainer.add_actor(this._rightBox);
|
||||
this._boxContainer.connect('get-preferred-width', Lang.bind(this, function(box, forHeight, alloc) {
|
||||
let children = box.get_children();
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let [childMin, childNatural] = children[i].get_preferred_width(forHeight);
|
||||
alloc.min_size += childMin;
|
||||
alloc.natural_size += childNatural;
|
||||
}
|
||||
}));
|
||||
this._boxContainer.connect('get-preferred-height', Lang.bind(this, function(box, forWidth, alloc) {
|
||||
let children = box.get_children();
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let [childMin, childNatural] = children[i].get_preferred_height(forWidth);
|
||||
if (childMin > alloc.min_size)
|
||||
alloc.min_size = childMin;
|
||||
if (childNatural > alloc.natural_size)
|
||||
alloc.natural_size = childNatural;
|
||||
}
|
||||
}));
|
||||
this._boxContainer.connect('allocate', Lang.bind(this, function(container, box, flags) {
|
||||
let allocWidth = box.x2 - box.x1;
|
||||
let allocHeight = box.y2 - box.y1;
|
||||
let [leftMinWidth, leftNaturalWidth] = this._leftBox.get_preferred_width(-1);
|
||||
let [centerMinWidth, centerNaturalWidth] = this._centerBox.get_preferred_width(-1);
|
||||
let [rightMinWidth, rightNaturalWidth] = this._rightBox.get_preferred_width(-1);
|
||||
this.actor.add_actor(this._leftCorner.actor);
|
||||
|
||||
let sideWidth, centerWidth;
|
||||
centerWidth = centerNaturalWidth;
|
||||
sideWidth = (allocWidth - centerWidth) / 2;
|
||||
if (this.actor.get_direction() == St.TextDirection.RTL)
|
||||
this._rightCorner = new PanelCorner(this._leftBox, St.Side.RIGHT);
|
||||
else
|
||||
this._rightCorner = new PanelCorner(this._rightBox, St.Side.RIGHT);
|
||||
this.actor.add_actor(this._rightCorner.actor);
|
||||
|
||||
let childBox = new Clutter.ActorBox();
|
||||
|
||||
childBox.y1 = 0;
|
||||
childBox.y2 = allocHeight;
|
||||
if (this.actor.get_direction() == St.TextDirection.RTL) {
|
||||
childBox.x1 = allocWidth - Math.min(Math.floor(sideWidth),
|
||||
leftNaturalWidth);
|
||||
childBox.x2 = allocWidth;
|
||||
} else {
|
||||
childBox.x1 = 0;
|
||||
childBox.x2 = Math.min(Math.floor(sideWidth),
|
||||
leftNaturalWidth);
|
||||
}
|
||||
this._leftBox.allocate(childBox, flags);
|
||||
|
||||
childBox.x1 = Math.ceil(sideWidth);
|
||||
childBox.y1 = 0;
|
||||
childBox.x2 = childBox.x1 + centerWidth;
|
||||
childBox.y2 = allocHeight;
|
||||
this._centerBox.allocate(childBox, flags);
|
||||
|
||||
childBox.y1 = 0;
|
||||
childBox.y2 = allocHeight;
|
||||
if (this.actor.get_direction() == St.TextDirection.RTL) {
|
||||
childBox.x1 = 0;
|
||||
childBox.x2 = Math.min(Math.floor(sideWidth),
|
||||
rightNaturalWidth);
|
||||
} else {
|
||||
childBox.x1 = allocWidth - Math.min(Math.floor(sideWidth),
|
||||
rightNaturalWidth);
|
||||
childBox.x2 = allocWidth;
|
||||
}
|
||||
this._rightBox.allocate(childBox, flags);
|
||||
}));
|
||||
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));
|
||||
|
||||
/* Button on the left side of the panel. */
|
||||
this._activitiesButton = new ActivitiesButton();
|
||||
this._activities = this._activitiesButton.actor;
|
||||
this._leftBox.add(this._activities);
|
||||
if (global.session_type == Shell.SessionType.USER) {
|
||||
this._activitiesButton = new ActivitiesButton();
|
||||
this._activities = this._activitiesButton.actor;
|
||||
this._leftBox.add(this._activities);
|
||||
|
||||
// The activities button has a pretend menu, so as to integrate
|
||||
// more cleanly with the rest of the panel
|
||||
this._menus.addMenu(this._activitiesButton.menu);
|
||||
// The activities button has a pretend menu, so as to integrate
|
||||
// more cleanly with the rest of the panel
|
||||
this._menus.addMenu(this._activitiesButton.menu);
|
||||
|
||||
// Synchronize the button's pseudo classes with its corner
|
||||
this._activities.connect('style-changed', Lang.bind(this,
|
||||
function(actor) {
|
||||
let rtl = actor.get_direction() == St.TextDirection.RTL;
|
||||
let corner = rtl ? this._rightCorner : this._leftCorner;
|
||||
let pseudoClass = actor.get_style_pseudo_class();
|
||||
corner.actor.set_style_pseudo_class(pseudoClass);
|
||||
}));
|
||||
|
||||
this._appMenu = new AppMenuButton();
|
||||
this._leftBox.add(this._appMenu.actor);
|
||||
|
||||
this._menus.addMenu(this._appMenu.menu);
|
||||
this._appMenu = new AppMenuButton();
|
||||
this._leftBox.add(this._appMenu.actor);
|
||||
this._menus.addMenu(this._appMenu.menu);
|
||||
}
|
||||
|
||||
/* center */
|
||||
this._dateMenu = new DateMenu.DateMenuButton();
|
||||
if (global.session_type == Shell.SessionType.USER)
|
||||
this._dateMenu = new DateMenu.DateMenuButton({ showEvents: true });
|
||||
else
|
||||
this._dateMenu = new DateMenu.DateMenuButton({ showEvents: false });
|
||||
this._centerBox.add(this._dateMenu.actor, { y_fill: true });
|
||||
this._menus.addMenu(this._dateMenu.menu);
|
||||
|
||||
@ -926,113 +953,145 @@ Panel.prototype = {
|
||||
this._rightBox.add(this._trayBox);
|
||||
this._rightBox.add(this._statusBox);
|
||||
|
||||
this._userMenu = new StatusMenu.StatusMenuButton();
|
||||
this._userMenu.actor.name = 'panelStatus';
|
||||
this._rightBox.add(this._userMenu.actor);
|
||||
|
||||
// Synchronize the buttons pseudo classes with its corner
|
||||
this._userMenu.actor.connect('style-changed', Lang.bind(this,
|
||||
function(actor) {
|
||||
let rtl = actor.get_direction() == St.TextDirection.RTL;
|
||||
let corner = rtl ? this._leftCorner : this._rightCorner;
|
||||
let pseudoClass = actor.get_style_pseudo_class();
|
||||
corner.actor.set_style_pseudo_class(pseudoClass);
|
||||
}));
|
||||
if (global.session_type == Shell.SessionType.USER) {
|
||||
this._userMenu = new UserMenu.UserMenuButton();
|
||||
this._userMenu.actor.name = 'panelStatus';
|
||||
this._rightBox.add(this._userMenu.actor);
|
||||
}
|
||||
|
||||
Main.statusIconDispatcher.connect('status-icon-added', Lang.bind(this, this._onTrayIconAdded));
|
||||
Main.statusIconDispatcher.connect('status-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
|
||||
|
||||
Main.chrome.addActor(this.actor);
|
||||
Main.chrome.addActor(this._leftCorner.actor, { affectsStruts: false,
|
||||
affectsInputRegion: false });
|
||||
Main.chrome.addActor(this._rightCorner.actor, { affectsStruts: false,
|
||||
affectsInputRegion: false });
|
||||
|
||||
Main.layoutManager.panelBox.add(this.actor);
|
||||
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'start-here',
|
||||
{ sortGroup: CtrlAltTab.SortGroup.TOP });
|
||||
},
|
||||
|
||||
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
|
||||
this._relayout();
|
||||
_getPreferredWidth: function(actor, forHeight, alloc) {
|
||||
alloc.min_size = -1;
|
||||
alloc.natural_size = Main.layoutManager.primaryMonitor.width;
|
||||
},
|
||||
|
||||
_getPreferredHeight: function(actor, forWidth, alloc) {
|
||||
// We don't need to implement this; it's forced by the CSS
|
||||
alloc.min_size = -1;
|
||||
alloc.natural_size = -1;
|
||||
},
|
||||
|
||||
_allocate: function(actor, box, flags) {
|
||||
let allocWidth = box.x2 - box.x1;
|
||||
let allocHeight = box.y2 - box.y1;
|
||||
|
||||
let [leftMinWidth, leftNaturalWidth] = this._leftBox.get_preferred_width(-1);
|
||||
let [centerMinWidth, centerNaturalWidth] = this._centerBox.get_preferred_width(-1);
|
||||
let [rightMinWidth, rightNaturalWidth] = this._rightBox.get_preferred_width(-1);
|
||||
|
||||
let sideWidth, centerWidth;
|
||||
centerWidth = centerNaturalWidth;
|
||||
sideWidth = (allocWidth - centerWidth) / 2;
|
||||
|
||||
let childBox = new Clutter.ActorBox();
|
||||
|
||||
childBox.y1 = 0;
|
||||
childBox.y2 = allocHeight;
|
||||
if (this.actor.get_direction() == St.TextDirection.RTL) {
|
||||
childBox.x1 = allocWidth - Math.min(Math.floor(sideWidth),
|
||||
leftNaturalWidth);
|
||||
childBox.x2 = allocWidth;
|
||||
} else {
|
||||
childBox.x1 = 0;
|
||||
childBox.x2 = Math.min(Math.floor(sideWidth),
|
||||
leftNaturalWidth);
|
||||
}
|
||||
this._leftBox.allocate(childBox, flags);
|
||||
|
||||
childBox.x1 = Math.ceil(sideWidth);
|
||||
childBox.y1 = 0;
|
||||
childBox.x2 = childBox.x1 + centerWidth;
|
||||
childBox.y2 = allocHeight;
|
||||
this._centerBox.allocate(childBox, flags);
|
||||
|
||||
childBox.y1 = 0;
|
||||
childBox.y2 = allocHeight;
|
||||
if (this.actor.get_direction() == St.TextDirection.RTL) {
|
||||
childBox.x1 = 0;
|
||||
childBox.x2 = Math.min(Math.floor(sideWidth),
|
||||
rightNaturalWidth);
|
||||
} else {
|
||||
childBox.x1 = allocWidth - Math.min(Math.floor(sideWidth),
|
||||
rightNaturalWidth);
|
||||
childBox.x2 = allocWidth;
|
||||
}
|
||||
this._rightBox.allocate(childBox, flags);
|
||||
|
||||
let [cornerMinWidth, cornerWidth] = this._leftCorner.actor.get_preferred_width(-1);
|
||||
let [cornerMinHeight, cornerHeight] = this._leftCorner.actor.get_preferred_width(-1);
|
||||
childBox.x1 = 0;
|
||||
childBox.x2 = cornerWidth;
|
||||
childBox.y1 = allocHeight;
|
||||
childBox.y2 = allocHeight + cornerHeight;
|
||||
this._leftCorner.actor.allocate(childBox, flags);
|
||||
|
||||
let [cornerMinWidth, cornerWidth] = this._rightCorner.actor.get_preferred_width(-1);
|
||||
let [cornerMinHeight, cornerHeight] = this._rightCorner.actor.get_preferred_width(-1);
|
||||
childBox.x1 = allocWidth - cornerWidth;
|
||||
childBox.x2 = allocWidth;
|
||||
childBox.y1 = allocHeight;
|
||||
childBox.y2 = allocHeight + cornerHeight;
|
||||
this._rightCorner.actor.allocate(childBox, flags);
|
||||
},
|
||||
|
||||
startStatusArea: function() {
|
||||
for (let i = 0; i < STANDARD_TRAY_ICON_ORDER.length; i++) {
|
||||
let role = STANDARD_TRAY_ICON_ORDER[i];
|
||||
let constructor = STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION[role];
|
||||
for (let i = 0; i < this._tray_icon_order.length; i++) {
|
||||
let role = this._tray_icon_order[i];
|
||||
let constructor = this._tray_icon_shell_implementation[role];
|
||||
if (!constructor) {
|
||||
// This icon is not implemented (this is a bug)
|
||||
continue;
|
||||
}
|
||||
let indicator = new constructor();
|
||||
this._statusBox.add(indicator.actor);
|
||||
this._menus.addMenu(indicator.menu);
|
||||
|
||||
this._statusArea[role] = indicator;
|
||||
let indicator = new constructor();
|
||||
this.addToStatusArea(role, indicator, i);
|
||||
}
|
||||
|
||||
// PopupMenuManager depends on menus being added in order for
|
||||
// keyboard navigation
|
||||
this._menus.addMenu(this._userMenu.menu);
|
||||
if (this._userMenu)
|
||||
this._menus.addMenu(this._userMenu.menu);
|
||||
},
|
||||
|
||||
startupAnimation: function() {
|
||||
let oldY = this.actor.y;
|
||||
this.actor.y = oldY - this.actor.height;
|
||||
Tweener.addTween(this.actor,
|
||||
{ y: oldY,
|
||||
time: STARTUP_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
addToStatusArea: function(role, indicator, position) {
|
||||
if (this._statusArea[role])
|
||||
throw new Error('Extension point conflict: there is already a status indicator for role ' + role);
|
||||
|
||||
let oldCornerY = this._leftCorner.actor.y;
|
||||
this._leftCorner.actor.y = oldCornerY - this.actor.height;
|
||||
this._rightCorner.actor.y = oldCornerY - this.actor.height;
|
||||
Tweener.addTween(this._leftCorner.actor,
|
||||
{ y: oldCornerY,
|
||||
time: STARTUP_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
Tweener.addTween(this._rightCorner.actor,
|
||||
{ y: oldCornerY,
|
||||
time: STARTUP_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
},
|
||||
if (!(indicator instanceof PanelMenu.Button))
|
||||
throw new TypeError('Status indicator must be an instance of PanelMenu.Button');
|
||||
|
||||
_relayout: function() {
|
||||
let primary = Main.layoutManager.primaryMonitor;
|
||||
if (!position)
|
||||
position = 0;
|
||||
|
||||
this.actor.set_position(primary.x, primary.y);
|
||||
this.actor.set_size(primary.width, -1);
|
||||
this._statusBox.insert_actor(indicator.actor, position);
|
||||
this._menus.addMenu(indicator.menu);
|
||||
|
||||
if (this._leftPointerBarrier)
|
||||
global.destroy_pointer_barrier(this._leftPointerBarrier);
|
||||
if (this._rightPointerBarrier)
|
||||
global.destroy_pointer_barrier(this._rightPointerBarrier);
|
||||
this._statusArea[role] = indicator;
|
||||
let destroyId = indicator.connect('destroy', Lang.bind(this, function(emitter) {
|
||||
this._statusArea[role] = null;
|
||||
emitter.disconnect(destroyId);
|
||||
}));
|
||||
|
||||
this._leftPointerBarrier =
|
||||
global.create_pointer_barrier(primary.x, primary.y,
|
||||
primary.x, primary.y + this.actor.height,
|
||||
1 /* BarrierPositiveX */);
|
||||
this._rightPointerBarrier =
|
||||
global.create_pointer_barrier(primary.x + primary.width, primary.y,
|
||||
primary.x + primary.width, primary.y + this.actor.height,
|
||||
4 /* BarrierNegativeX */);
|
||||
|
||||
this._leftCorner.relayout();
|
||||
this._rightCorner.relayout();
|
||||
return indicator;
|
||||
},
|
||||
|
||||
_onTrayIconAdded: function(o, icon, role) {
|
||||
icon.height = PANEL_ICON_SIZE;
|
||||
|
||||
if (STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION[role]) {
|
||||
if (this._tray_icon_shell_implementation[role]) {
|
||||
// This icon is legacy, and replaced by a Shell version
|
||||
// Hide it
|
||||
return;
|
||||
}
|
||||
// Figure out the index in our well-known order for this icon
|
||||
let position = STANDARD_TRAY_ICON_ORDER.indexOf(role);
|
||||
let position = this._tray_icon_order.indexOf(role);
|
||||
icon._rolePosition = position;
|
||||
let children = this._trayBox.get_children();
|
||||
let i;
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Lang = imports.lang;
|
||||
@ -23,10 +24,11 @@ Button.prototype = {
|
||||
this.actor._delegate = this;
|
||||
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
||||
this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress));
|
||||
this.menu = new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP, 0);
|
||||
this.menu = new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP);
|
||||
this.menu.actor.add_style_class_name('panel-menu');
|
||||
this.menu.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged));
|
||||
this.menu.actor.connect('key-press-event', Lang.bind(this, this._onMenuKeyPress));
|
||||
Main.chrome.addActor(this.menu.actor, { affectsStruts: false });
|
||||
Main.uiGroup.add_actor(this.menu.actor);
|
||||
this.menu.actor.hide();
|
||||
},
|
||||
|
||||
@ -79,8 +81,18 @@ Button.prototype = {
|
||||
this.actor.add_style_pseudo_class('active');
|
||||
else
|
||||
this.actor.remove_style_pseudo_class('active');
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.actor._delegate = null;
|
||||
|
||||
this.menu.destroy();
|
||||
this.actor.destroy();
|
||||
|
||||
this.emit('destroy');
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(Button.prototype);
|
||||
|
||||
/* SystemStatusButton:
|
||||
*
|
||||
|
@ -60,13 +60,13 @@ PlaceInfo.prototype = {
|
||||
// Helper function to translate launch parameters into a GAppLaunchContext
|
||||
function _makeLaunchContext(params)
|
||||
{
|
||||
params = Params.parse(params, { workspace: null,
|
||||
timestamp: null });
|
||||
params = Params.parse(params, { workspace: -1,
|
||||
timestamp: 0 });
|
||||
|
||||
let launchContext = global.create_app_launch_context();
|
||||
if (params.workspace != null)
|
||||
launchContext.set_desktop(params.workspace.index());
|
||||
if (params.timestamp != null)
|
||||
if (params.workspace != -1)
|
||||
launchContext.set_desktop(params.workspace);
|
||||
if (params.timestamp != 0)
|
||||
launchContext.set_timestamp(params.timestamp);
|
||||
|
||||
return launchContext;
|
||||
@ -118,9 +118,9 @@ PlaceDeviceInfo.prototype = {
|
||||
this._mount.unmount_finish(res);
|
||||
} catch (e) {
|
||||
let message = _("Failed to unmount '%s'").format(o.get_name());
|
||||
Main.overview.shellInfo.setMessage(message,
|
||||
Lang.bind(this, this.remove),
|
||||
_("Retry"));
|
||||
Main.overview.setMessage(message,
|
||||
Lang.bind(this, this.remove),
|
||||
_("Retry"));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -23,10 +23,10 @@
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
const Shell = imports.gi.Shell;
|
||||
const AccountsService = imports.gi.AccountsService;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const St = imports.gi.St;
|
||||
const Pango = imports.gi.Pango;
|
||||
const Gdm = imports.gi.Gdm;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Polkit = imports.gi.Polkit;
|
||||
@ -92,7 +92,7 @@ AuthenticationDialog.prototype = {
|
||||
|
||||
let userName = userNames[0];
|
||||
|
||||
this._user = Gdm.UserManager.ref_default().get_user(userName);
|
||||
this._user = AccountsService.UserManager.get_default().get_user(userName);
|
||||
let userRealName = this._user.get_real_name()
|
||||
this._userLoadedId = this._user.connect('notify::is_loaded',
|
||||
Lang.bind(this, this._onUserChanged));
|
||||
|
@ -15,6 +15,17 @@ const Tweener = imports.ui.tweener;
|
||||
|
||||
const SLIDER_SCROLL_STEP = 0.05; /* Slider scrolling step in % */
|
||||
|
||||
function _ensureStyle(actor) {
|
||||
if (actor.get_children) {
|
||||
let children = actor.get_children();
|
||||
for (let i = 0; i < children.length; i++)
|
||||
_ensureStyle(children[i]);
|
||||
}
|
||||
|
||||
if (actor instanceof St.Widget)
|
||||
actor.ensure_style();
|
||||
}
|
||||
|
||||
function PopupBaseMenuItem(params) {
|
||||
this._init(params);
|
||||
}
|
||||
@ -706,7 +717,8 @@ PopupSwitchMenuItem.prototype = {
|
||||
this.addActor(this.label);
|
||||
|
||||
this._statusBin = new St.Bin({ x_align: St.Align.END });
|
||||
this.addActor(this._statusBin, { align: St.Align.END });
|
||||
this.addActor(this._statusBin,
|
||||
{ expand: true, span: -1, align: St.Align.END });
|
||||
|
||||
this._statusLabel = new St.Label({ text: '',
|
||||
style_class: 'popup-inactive-menu-item'
|
||||
@ -800,14 +812,58 @@ PopupMenuBase.prototype = {
|
||||
this.passEvents = false;
|
||||
|
||||
this._activeMenuItem = null;
|
||||
this._childMenus = [];
|
||||
},
|
||||
|
||||
addAction: function(title, callback) {
|
||||
var menuItem = new PopupMenuItem(title);
|
||||
let menuItem = new PopupMenuItem(title);
|
||||
this.addMenuItem(menuItem);
|
||||
menuItem.connect('activate', Lang.bind(this, function (menuItem, event) {
|
||||
callback(event);
|
||||
}));
|
||||
|
||||
return menuItem;
|
||||
},
|
||||
|
||||
addSettingsAction: function(title, desktopFile) {
|
||||
// Don't allow user settings to get edited unless we're in a user session
|
||||
if (global.session_type != Shell.SessionType.USER)
|
||||
return null;
|
||||
|
||||
let menuItem = this.addAction(title, function() {
|
||||
let app = Shell.AppSystem.get_default().lookup_setting(desktopFile);
|
||||
|
||||
if (!app) {
|
||||
log('Settings panel for desktop file ' + desktopFile + ' could not be loaded!');
|
||||
return;
|
||||
}
|
||||
|
||||
Main.overview.hide();
|
||||
app.activate();
|
||||
});
|
||||
return menuItem;
|
||||
},
|
||||
|
||||
isChildMenu: function(menu) {
|
||||
return this._childMenus.indexOf(menu) != -1;
|
||||
},
|
||||
|
||||
addChildMenu: function(menu) {
|
||||
if (this.isChildMenu(menu))
|
||||
return;
|
||||
|
||||
this._childMenus.push(menu);
|
||||
this.emit('child-menu-added', menu);
|
||||
},
|
||||
|
||||
removeChildMenu: function(menu) {
|
||||
let index = this._childMenus.indexOf(menu);
|
||||
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
this._childMenus.splice(index, 1);
|
||||
this.emit('child-menu-removed', menu);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -860,6 +916,39 @@ PopupMenuBase.prototype = {
|
||||
}));
|
||||
},
|
||||
|
||||
_updateSeparatorVisibility: function(menuItem) {
|
||||
let children = this.box.get_children();
|
||||
|
||||
let index = children.indexOf(menuItem.actor);
|
||||
|
||||
if (index < 0)
|
||||
return;
|
||||
|
||||
let childBeforeIndex = index - 1;
|
||||
|
||||
while (childBeforeIndex >= 0 && !children[childBeforeIndex].visible)
|
||||
childBeforeIndex--;
|
||||
|
||||
if (childBeforeIndex < 0
|
||||
|| children[childBeforeIndex]._delegate instanceof PopupSeparatorMenuItem) {
|
||||
menuItem.actor.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
let childAfterIndex = index + 1;
|
||||
|
||||
while (childAfterIndex < children.length && !children[childAfterIndex].visible)
|
||||
childAfterIndex++;
|
||||
|
||||
if (childAfterIndex >= children.length
|
||||
|| children[childAfterIndex]._delegate instanceof PopupSeparatorMenuItem) {
|
||||
menuItem.actor.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
menuItem.actor.show();
|
||||
},
|
||||
|
||||
addMenuItem: function(menuItem, position) {
|
||||
let before_item = null;
|
||||
if (position == undefined) {
|
||||
@ -891,6 +980,14 @@ PopupMenuBase.prototype = {
|
||||
if (!open)
|
||||
menuItem.menu.close(false);
|
||||
});
|
||||
} else if (menuItem instanceof PopupSeparatorMenuItem) {
|
||||
this._connectItemSignals(menuItem);
|
||||
|
||||
// updateSeparatorVisibility needs to get called any time the
|
||||
// separator's adjacent siblings change visibility or position.
|
||||
// open-state-changed isn't exactly that, but doing it in more
|
||||
// precise ways would require a lot more bookkeeping.
|
||||
this.connect('open-state-changed', Lang.bind(this, function() { this._updateSeparatorVisibility(menuItem); }));
|
||||
} else if (menuItem instanceof PopupBaseMenuItem)
|
||||
this._connectItemSignals(menuItem);
|
||||
else
|
||||
@ -954,6 +1051,10 @@ PopupMenuBase.prototype = {
|
||||
return null;
|
||||
},
|
||||
|
||||
get numMenuItems() {
|
||||
return this._getMenuItems().length;
|
||||
},
|
||||
|
||||
removeAll: function() {
|
||||
let children = this._getMenuItems();
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
@ -985,12 +1086,11 @@ function PopupMenu() {
|
||||
PopupMenu.prototype = {
|
||||
__proto__: PopupMenuBase.prototype,
|
||||
|
||||
_init: function(sourceActor, alignment, arrowSide, gap) {
|
||||
_init: function(sourceActor, alignment, arrowSide) {
|
||||
PopupMenuBase.prototype._init.call (this, sourceActor, 'popup-menu-content');
|
||||
|
||||
this._alignment = alignment;
|
||||
this._arrowSide = arrowSide;
|
||||
this._gap = gap;
|
||||
|
||||
this._boxPointer = new BoxPointer.BoxPointer(arrowSide,
|
||||
{ x_fill: true,
|
||||
@ -1048,9 +1148,11 @@ PopupMenu.prototype = {
|
||||
|
||||
this.isOpen = true;
|
||||
|
||||
this._boxPointer.setPosition(this.sourceActor, this._gap, this._alignment);
|
||||
this._boxPointer.setPosition(this.sourceActor, this._alignment);
|
||||
this._boxPointer.show(animate);
|
||||
|
||||
this.actor.raise_top();
|
||||
|
||||
this.emit('open-state-changed', true);
|
||||
},
|
||||
|
||||
@ -1318,6 +1420,193 @@ PopupSubMenuMenuItem.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
function PopupComboMenu() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
PopupComboMenu.prototype = {
|
||||
__proto__: PopupMenuBase.prototype,
|
||||
|
||||
_init: function(sourceActor) {
|
||||
PopupMenuBase.prototype._init.call(this,
|
||||
sourceActor, 'popup-combo-menu');
|
||||
this.actor = this.box;
|
||||
this.actor._delegate = this;
|
||||
this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
|
||||
this.actor.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn));
|
||||
this._activeItemPos = -1;
|
||||
global.focus_manager.add_group(this.actor);
|
||||
},
|
||||
|
||||
_onKeyPressEvent: function(actor, event) {
|
||||
if (event.get_key_symbol() == Clutter.Escape) {
|
||||
this.close(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_onKeyFocusIn: function(actor) {
|
||||
let items = this._getMenuItems();
|
||||
let activeItem = items[this._activeItemPos];
|
||||
activeItem.actor.grab_key_focus();
|
||||
},
|
||||
|
||||
open: function() {
|
||||
if (this.isOpen)
|
||||
return;
|
||||
|
||||
this.isOpen = true;
|
||||
|
||||
let [sourceX, sourceY] = this.sourceActor.get_transformed_position();
|
||||
let items = this._getMenuItems();
|
||||
let activeItem = items[this._activeItemPos];
|
||||
|
||||
this.actor.set_position(sourceX, sourceY - activeItem.actor.y);
|
||||
this.actor.width = Math.max(this.actor.width, this.sourceActor.width);
|
||||
this.actor.raise_top();
|
||||
|
||||
this.actor.opacity = 0;
|
||||
this.actor.show();
|
||||
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 255,
|
||||
transition: 'linear',
|
||||
time: BoxPointer.POPUP_ANIMATION_TIME });
|
||||
|
||||
this.emit('open-state-changed', true);
|
||||
},
|
||||
|
||||
close: function() {
|
||||
if (!this.isOpen)
|
||||
return;
|
||||
|
||||
this.isOpen = false;
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 0,
|
||||
transition: 'linear',
|
||||
time: BoxPointer.POPUP_ANIMATION_TIME,
|
||||
onComplete: Lang.bind(this,
|
||||
function() {
|
||||
this.actor.hide();
|
||||
})
|
||||
});
|
||||
|
||||
this.emit('open-state-changed', false);
|
||||
},
|
||||
|
||||
setActiveItem: function(position) {
|
||||
this._activeItemPos = position;
|
||||
},
|
||||
|
||||
setItemVisible: function(position, visible) {
|
||||
if (!visible && position == this._activeItemPos) {
|
||||
log('Trying to hide the active menu item.');
|
||||
return;
|
||||
}
|
||||
|
||||
this._getMenuItems()[position].actor.visible = visible;
|
||||
}
|
||||
};
|
||||
|
||||
function PopupComboBoxMenuItem() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
PopupComboBoxMenuItem.prototype = {
|
||||
__proto__: PopupBaseMenuItem.prototype,
|
||||
|
||||
_init: function (params) {
|
||||
PopupBaseMenuItem.prototype._init.call(this, params);
|
||||
|
||||
this._itemBox = new Shell.Stack();
|
||||
this.addActor(this._itemBox);
|
||||
|
||||
let expander = new St.Label({ text: '\u2304' });
|
||||
this.addActor(expander, { align: St.Align.END });
|
||||
|
||||
this._menu = new PopupComboMenu(this.actor);
|
||||
Main.uiGroup.add_actor(this._menu.actor);
|
||||
this._menu.actor.hide();
|
||||
|
||||
if (params.style_class)
|
||||
this._menu.actor.add_style_class_name(params.style_class);
|
||||
|
||||
this._activeItemPos = -1;
|
||||
this._items = [];
|
||||
},
|
||||
|
||||
_getTopMenu: function() {
|
||||
let actor = this.actor.get_parent();
|
||||
while (actor) {
|
||||
if (actor._delegate &&
|
||||
(actor._delegate instanceof PopupMenu ||
|
||||
actor._delegate instanceof PopupComboMenu))
|
||||
return actor._delegate;
|
||||
|
||||
actor = actor.get_parent();
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
activate: function(event) {
|
||||
let topMenu = this._getTopMenu();
|
||||
if (!topMenu)
|
||||
return;
|
||||
|
||||
topMenu.addChildMenu(this._menu);
|
||||
this._menu.toggle();
|
||||
},
|
||||
|
||||
addMenuItem: function(menuItem, position) {
|
||||
if (position === undefined)
|
||||
position = this._menu.numMenuItems;
|
||||
|
||||
this._menu.addMenuItem(menuItem, position);
|
||||
_ensureStyle(this._menu.actor);
|
||||
|
||||
let item = new St.BoxLayout({ style_class: 'popup-combobox-item' });
|
||||
|
||||
let children = menuItem.actor.get_children();
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let clone = new Clutter.Clone({ source: children[i] });
|
||||
item.add(clone, { y_fill: false });
|
||||
}
|
||||
|
||||
let oldItem = this._items[position];
|
||||
if (oldItem)
|
||||
this._itemBox.remove_actor(oldItem);
|
||||
|
||||
this._items[position] = item;
|
||||
this._itemBox.add_actor(item);
|
||||
|
||||
menuItem.connect('activate',
|
||||
Lang.bind(this, this._itemActivated, position));
|
||||
},
|
||||
|
||||
setActiveItem: function(position) {
|
||||
let item = this._items[position];
|
||||
if (!item)
|
||||
return;
|
||||
if (this._activeItemPos == position)
|
||||
return;
|
||||
this._menu.setActiveItem(position);
|
||||
this._activeItemPos = position;
|
||||
for (let i = 0; i < this._items.length; i++)
|
||||
this._items[i].visible = (i == this._activeItemPos);
|
||||
},
|
||||
|
||||
setItemVisible: function(position, visible) {
|
||||
this._menu.setItemVisible(position, visible);
|
||||
},
|
||||
|
||||
_itemActivated: function(menuItem, event, position) {
|
||||
this.setActiveItem(position);
|
||||
this.emit('active-item-changed', position);
|
||||
}
|
||||
};
|
||||
|
||||
/* Basic implementation of a menu manager.
|
||||
* Call addMenu to add menus
|
||||
@ -1337,6 +1626,7 @@ PopupMenuManager.prototype = {
|
||||
this._keyFocusNotifyId = 0;
|
||||
this._activeMenu = null;
|
||||
this._menus = [];
|
||||
this._menuStack = [];
|
||||
this._preGrabInputMode = null;
|
||||
this._grabbedFromKeynav = false;
|
||||
},
|
||||
@ -1345,6 +1635,8 @@ PopupMenuManager.prototype = {
|
||||
let menudata = {
|
||||
menu: menu,
|
||||
openStateChangeId: menu.connect('open-state-changed', Lang.bind(this, this._onMenuOpenState)),
|
||||
childMenuAddedId: menu.connect('child-menu-added', Lang.bind(this, this._onChildMenuAdded)),
|
||||
childMenuRemovedId: menu.connect('child-menu-removed', Lang.bind(this, this._onChildMenuRemoved)),
|
||||
destroyId: menu.connect('destroy', Lang.bind(this, this._onMenuDestroy)),
|
||||
enterId: 0,
|
||||
focusInId: 0
|
||||
@ -1372,6 +1664,8 @@ PopupMenuManager.prototype = {
|
||||
|
||||
let menudata = this._menus[position];
|
||||
menu.disconnect(menudata.openStateChangeId);
|
||||
menu.disconnect(menudata.childMenuAddedId);
|
||||
menu.disconnect(menudata.childMenuRemovedId);
|
||||
menu.disconnect(menudata.destroyId);
|
||||
|
||||
if (menudata.enterId)
|
||||
@ -1409,8 +1703,20 @@ PopupMenuManager.prototype = {
|
||||
},
|
||||
|
||||
_onMenuOpenState: function(menu, open) {
|
||||
if (open)
|
||||
if (open) {
|
||||
if (this._activeMenu && this._activeMenu.isChildMenu(menu)) {
|
||||
this._menuStack.push(this._activeMenu);
|
||||
menu.actor.grab_key_focus();
|
||||
}
|
||||
this._activeMenu = menu;
|
||||
} else {
|
||||
if (this._menuStack.length > 0) {
|
||||
this._activeMenu = this._menuStack.pop();
|
||||
if (menu.sourceActor)
|
||||
menu.sourceActor.grab_key_focus();
|
||||
this._didPop = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check what the focus was before calling pushModal/popModal
|
||||
let focus = global.stage.key_focus;
|
||||
@ -1443,6 +1749,14 @@ PopupMenuManager.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_onChildMenuAdded: function(menu, childMenu) {
|
||||
this.addMenu(childMenu);
|
||||
},
|
||||
|
||||
_onChildMenuRemoved: function(menu, childMenu) {
|
||||
this.removeMenu(childMenu);
|
||||
},
|
||||
|
||||
// change the currently-open menu without dropping grab
|
||||
_changeMenu: function(newMenu) {
|
||||
if (this._activeMenu) {
|
||||
@ -1451,6 +1765,8 @@ PopupMenuManager.prototype = {
|
||||
// before closing it to keep that from happening
|
||||
let oldMenu = this._activeMenu;
|
||||
this._activeMenu = null;
|
||||
for (let i = this._menuStack.length - 1; i >= 0; i--)
|
||||
this._menuStack[i].close(false);
|
||||
oldMenu.close(false);
|
||||
newMenu.open(false);
|
||||
} else
|
||||
@ -1461,6 +1777,15 @@ PopupMenuManager.prototype = {
|
||||
if (!this.grabbed || menu == this._activeMenu)
|
||||
return false;
|
||||
|
||||
if (this._activeMenu && this._activeMenu.isChildMenu(menu))
|
||||
return false;
|
||||
|
||||
if (this._menuStack.indexOf(menu) != -1)
|
||||
return false;
|
||||
|
||||
if (this._menuStack.length > 0 && this._menuStack[0].isChildMenu(menu))
|
||||
return false;
|
||||
|
||||
this._changeMenu(menu);
|
||||
return false;
|
||||
},
|
||||
@ -1473,6 +1798,8 @@ PopupMenuManager.prototype = {
|
||||
if (focus) {
|
||||
if (this._activeMenuContains(focus))
|
||||
return;
|
||||
if (this._menuStack.length > 0)
|
||||
return;
|
||||
if (focus._delegate && focus._delegate.menu &&
|
||||
this._findMenu(focus._delegate.menu) != -1)
|
||||
return;
|
||||
@ -1531,6 +1858,11 @@ PopupMenuManager.prototype = {
|
||||
if (this._activeMenu != null && this._activeMenu.passEvents)
|
||||
return false;
|
||||
|
||||
if (this._didPop) {
|
||||
this._didPop = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
let activeMenuContains = this._eventIsOnActiveMenu(event);
|
||||
let eventType = event.type();
|
||||
|
||||
|
@ -112,6 +112,43 @@ function SearchProvider(title) {
|
||||
SearchProvider.prototype = {
|
||||
_init: function(title) {
|
||||
this.title = title;
|
||||
this.searchSystem = null;
|
||||
this.searchAsync = false;
|
||||
},
|
||||
|
||||
_asyncCancelled: function() {
|
||||
},
|
||||
|
||||
startAsync: function() {
|
||||
this.searchAsync = true;
|
||||
},
|
||||
|
||||
tryCancelAsync: function() {
|
||||
if (!this.searchAsync)
|
||||
return;
|
||||
this._asyncCancelled();
|
||||
this.searchAsync = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* addItems:
|
||||
* @items: an array of result identifier strings representing
|
||||
* items which match the last given search terms.
|
||||
*
|
||||
* This should be used for something that requires a bit more
|
||||
* logic; it's designed to be an asyncronous way to add a result
|
||||
* to the current search.
|
||||
*/
|
||||
addItems: function(items) {
|
||||
if (!this.searchSystem)
|
||||
throw new Error('Search provider not registered');
|
||||
|
||||
if (!items.length)
|
||||
return;
|
||||
|
||||
this.tryCancelAsync();
|
||||
|
||||
this.searchSystem.addProviderItems(this, items);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -315,6 +352,7 @@ SearchSystem.prototype = {
|
||||
},
|
||||
|
||||
registerProvider: function (provider) {
|
||||
provider.searchSystem = this;
|
||||
this._providers.push(provider);
|
||||
},
|
||||
|
||||
@ -331,12 +369,23 @@ SearchSystem.prototype = {
|
||||
this._previousResults = [];
|
||||
},
|
||||
|
||||
addProviderItems: function(provider, items) {
|
||||
this.emit('search-updated', provider, items);
|
||||
},
|
||||
|
||||
updateSearch: function(searchString) {
|
||||
searchString = searchString.replace(/^\s+/g, '').replace(/\s+$/g, '');
|
||||
if (searchString == '')
|
||||
return [];
|
||||
return;
|
||||
|
||||
let terms = searchString.split(/\s+/);
|
||||
this.updateSearchResults(terms);
|
||||
},
|
||||
|
||||
updateSearchResults: function(terms) {
|
||||
if (!terms)
|
||||
return;
|
||||
|
||||
let isSubSearch = terms.length == this._previousTerms.length;
|
||||
if (isSubSearch) {
|
||||
for (let i = 0; i < terms.length; i++) {
|
||||
@ -349,12 +398,12 @@ SearchSystem.prototype = {
|
||||
|
||||
let results = [];
|
||||
if (isSubSearch) {
|
||||
for (let i = 0; i < this._previousResults.length; i++) {
|
||||
for (let i = 0; i < this._providers.length; i++) {
|
||||
let [provider, previousResults] = this._previousResults[i];
|
||||
provider.tryCancelAsync();
|
||||
try {
|
||||
let providerResults = provider.getSubsearchResultSet(previousResults, terms);
|
||||
if (providerResults.length > 0)
|
||||
results.push([provider, providerResults]);
|
||||
results.push([provider, providerResults]);
|
||||
} catch (error) {
|
||||
global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
|
||||
}
|
||||
@ -362,10 +411,10 @@ SearchSystem.prototype = {
|
||||
} else {
|
||||
for (let i = 0; i < this._providers.length; i++) {
|
||||
let provider = this._providers[i];
|
||||
provider.tryCancelAsync();
|
||||
try {
|
||||
let providerResults = provider.getInitialResultSet(terms);
|
||||
if (providerResults.length > 0)
|
||||
results.push([provider, providerResults]);
|
||||
results.push([provider, providerResults]);
|
||||
} catch (error) {
|
||||
global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
|
||||
}
|
||||
@ -374,8 +423,7 @@ SearchSystem.prototype = {
|
||||
|
||||
this._previousTerms = terms;
|
||||
this._previousResults = results;
|
||||
|
||||
return results;
|
||||
}
|
||||
this.emit('search-completed', results);
|
||||
},
|
||||
};
|
||||
Signals.addSignalMethods(SearchSystem.prototype);
|
||||
|
@ -88,7 +88,7 @@ SearchResult.prototype = {
|
||||
},
|
||||
|
||||
getDragActor: function(stageX, stageY) {
|
||||
return this.metaInfo['createIcon'](Main.overview.dash.iconSize);
|
||||
return this.metaInfo['createIcon'](Main.overview.dashIconSize);
|
||||
},
|
||||
|
||||
shellWorkspaceLaunch: function(params) {
|
||||
@ -100,17 +100,17 @@ SearchResult.prototype = {
|
||||
};
|
||||
|
||||
|
||||
function GridSearchResults(provider) {
|
||||
this._init(provider);
|
||||
function GridSearchResults(provider, grid) {
|
||||
this._init(provider, grid);
|
||||
}
|
||||
|
||||
GridSearchResults.prototype = {
|
||||
__proto__: Search.SearchResultDisplay.prototype,
|
||||
|
||||
_init: function(provider) {
|
||||
_init: function(provider, grid) {
|
||||
Search.SearchResultDisplay.prototype._init.call(this, provider);
|
||||
this._grid = new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
|
||||
xAlign: St.Align.START });
|
||||
this._grid = grid || new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
|
||||
xAlign: St.Align.START });
|
||||
this.actor = new St.Bin({ x_align: St.Align.START });
|
||||
|
||||
this.actor.set_child(this._grid.actor);
|
||||
@ -189,6 +189,8 @@ function SearchResults(searchSystem, openSearchSystem) {
|
||||
SearchResults.prototype = {
|
||||
_init: function(searchSystem, openSearchSystem) {
|
||||
this._searchSystem = searchSystem;
|
||||
this._searchSystem.connect('search-updated', Lang.bind(this, this._updateCurrentResults));
|
||||
this._searchSystem.connect('search-completed', Lang.bind(this, this._updateResults));
|
||||
this._openSearchSystem = openSearchSystem;
|
||||
|
||||
this.actor = new St.BoxLayout({ name: 'searchResults',
|
||||
@ -223,9 +225,11 @@ SearchResults.prototype = {
|
||||
this._selectedProvider = -1;
|
||||
this._providers = this._searchSystem.getProviders();
|
||||
this._providerMeta = [];
|
||||
for (let i = 0; i < this._providers.length; i++)
|
||||
this._providerMetaResults = {};
|
||||
for (let i = 0; i < this._providers.length; i++) {
|
||||
this.createProviderMeta(this._providers[i]);
|
||||
|
||||
this._providerMetaResults[this.providers[i].title] = [];
|
||||
}
|
||||
this._searchProvidersBox = new St.BoxLayout({ style_class: 'search-providers-box' });
|
||||
this.actor.add(this._searchProvidersBox);
|
||||
|
||||
@ -305,6 +309,12 @@ SearchResults.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_clearDisplayForProvider: function(index) {
|
||||
let meta = this._providerMeta[index];
|
||||
meta.resultDisplay.clear();
|
||||
meta.actor.hide();
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this._searchSystem.reset();
|
||||
this._statusText.hide();
|
||||
@ -319,15 +329,24 @@ SearchResults.prototype = {
|
||||
this._statusText.show();
|
||||
},
|
||||
|
||||
doSearch: function (searchString) {
|
||||
this._searchSystem.updateSearch(searchString);
|
||||
},
|
||||
|
||||
_metaForProvider: function(provider) {
|
||||
return this._providerMeta[this._providers.indexOf(provider)];
|
||||
},
|
||||
|
||||
updateSearch: function (searchString) {
|
||||
let results = this._searchSystem.updateSearch(searchString);
|
||||
|
||||
this._clearDisplay();
|
||||
_updateCurrentResults: function(searchSystem, provider, results) {
|
||||
let terms = searchSystem.getTerms();
|
||||
let meta = this._metaForProvider(provider);
|
||||
meta.resultDisplay.clear();
|
||||
meta.actor.show();
|
||||
meta.resultDisplay.renderResults(results, terms);
|
||||
return true;
|
||||
},
|
||||
|
||||
_updateResults: function(searchSystem, results) {
|
||||
if (results.length == 0) {
|
||||
this._statusText.set_text(_("No matching results."));
|
||||
this._statusText.show();
|
||||
@ -337,7 +356,7 @@ SearchResults.prototype = {
|
||||
this._statusText.hide();
|
||||
}
|
||||
|
||||
let terms = this._searchSystem.getTerms();
|
||||
let terms = searchSystem.getTerms();
|
||||
this._openSearchSystem.setSearchTerms(terms);
|
||||
|
||||
// To avoid CSS transitions causing flickering
|
||||
@ -349,9 +368,15 @@ SearchResults.prototype = {
|
||||
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
let [provider, providerResults] = results[i];
|
||||
let meta = this._metaForProvider(provider);
|
||||
meta.actor.show();
|
||||
meta.resultDisplay.renderResults(providerResults, terms);
|
||||
if (providerResults.length == 0) {
|
||||
this._clearDisplayForProvider(i);
|
||||
} else {
|
||||
this._providerMetaResults[provider.title] = providerResults;
|
||||
this._clearDisplayForProvider(i);
|
||||
let meta = this._metaForProvider(provider);
|
||||
meta.actor.show();
|
||||
meta.resultDisplay.renderResults(providerResults, terms);
|
||||
}
|
||||
}
|
||||
|
||||
if (this._selectedOpenSearchButton == -1)
|
||||
|
@ -1,7 +1,10 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const DBus = imports.dbus;
|
||||
const Lang = imports.lang;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
const ExtensionSystem = imports.ui.extensionSystem;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const GnomeShellIface = {
|
||||
@ -9,12 +12,55 @@ const GnomeShellIface = {
|
||||
methods: [{ name: 'Eval',
|
||||
inSignature: 's',
|
||||
outSignature: 'bs'
|
||||
},
|
||||
{ name: 'ListExtensions',
|
||||
inSignature: '',
|
||||
outSignature: 'a{sa{sv}}'
|
||||
},
|
||||
{ name: 'GetExtensionInfo',
|
||||
inSignature: 's',
|
||||
outSignature: 'a{sv}'
|
||||
},
|
||||
{ name: 'GetExtensionErrors',
|
||||
inSignature: 's',
|
||||
outSignature: 'as'
|
||||
},
|
||||
{ name: 'ScreenshotArea',
|
||||
inSignature: 'iiiis',
|
||||
outSignature: 'b'
|
||||
},
|
||||
{ name: 'ScreenshotWindow',
|
||||
inSignature: 'bs',
|
||||
outSignature: 'b'
|
||||
},
|
||||
{ name: 'Screenshot',
|
||||
inSignature: 's',
|
||||
outSignature: 'b'
|
||||
},
|
||||
{ name: 'EnableExtension',
|
||||
inSignature: 's',
|
||||
outSignature: ''
|
||||
},
|
||||
{ name: 'DisableExtension',
|
||||
inSignature: 's',
|
||||
outSignature: ''
|
||||
},
|
||||
{ name: 'InstallRemoteExtension',
|
||||
inSignature: 's',
|
||||
outSignature: ''
|
||||
}
|
||||
],
|
||||
signals: [],
|
||||
signals: [{ name: 'ExtensionStatusChanged',
|
||||
inSignature: 'sis' }],
|
||||
properties: [{ name: 'OverviewActive',
|
||||
signature: 'b',
|
||||
access: 'readwrite' }]
|
||||
access: 'readwrite' },
|
||||
{ name: 'ApiVersion',
|
||||
signature: 'i',
|
||||
access: 'read' },
|
||||
{ name: 'ShellVersion',
|
||||
signature: 's',
|
||||
access: 'read' }]
|
||||
};
|
||||
|
||||
function GnomeShell() {
|
||||
@ -24,6 +70,8 @@ function GnomeShell() {
|
||||
GnomeShell.prototype = {
|
||||
_init: function() {
|
||||
DBus.session.exportObject('/org/gnome/Shell', this);
|
||||
ExtensionSystem.connect('extension-state-changed',
|
||||
Lang.bind(this, this._extensionStateChanged));
|
||||
},
|
||||
|
||||
/**
|
||||
@ -56,6 +104,80 @@ GnomeShell.prototype = {
|
||||
return [success, returnValue];
|
||||
},
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @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 (x, y, width, height, filename, callback) {
|
||||
global.screenshot_area (x, y, width, height, filename, function (obj, result) { callback(result); });
|
||||
},
|
||||
|
||||
/**
|
||||
* ScreenshotWindow:
|
||||
* @include_frame: Whether to include the frame 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.
|
||||
*
|
||||
*/
|
||||
ScreenshotWindow : function (include_frame, filename) {
|
||||
return global.screenshot_window (include_frame, filename);
|
||||
},
|
||||
|
||||
/**
|
||||
* Screenshot:
|
||||
* @filename: The filename for the screenshot
|
||||
*
|
||||
* 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 (filename, callback) {
|
||||
global.screenshot(filename, function (obj, result) { callback(result); });
|
||||
},
|
||||
|
||||
ListExtensions: function() {
|
||||
return ExtensionSystem.extensionMeta;
|
||||
},
|
||||
|
||||
GetExtensionInfo: function(uuid) {
|
||||
return ExtensionSystem.extensionMeta[uuid] || {};
|
||||
},
|
||||
|
||||
GetExtensionErrors: function(uuid) {
|
||||
return ExtensionSystem.errors[uuid] || [];
|
||||
},
|
||||
|
||||
EnableExtension: function(uuid) {
|
||||
let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY);
|
||||
if (enabledExtensions.indexOf(uuid) == -1)
|
||||
enabledExtensions.push(uuid);
|
||||
global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
|
||||
},
|
||||
|
||||
DisableExtension: function(uuid) {
|
||||
let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY);
|
||||
while (enabledExtensions.indexOf(uuid) != -1)
|
||||
enabledExtensions.splice(enabledExtensions.indexOf(uuid), 1);
|
||||
global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
|
||||
},
|
||||
|
||||
InstallRemoteExtension: function(uuid, url) {
|
||||
ExtensionSystem.installExtensionFromManifestURL(uuid, url);
|
||||
},
|
||||
|
||||
get OverviewActive() {
|
||||
return Main.overview.visible;
|
||||
},
|
||||
@ -65,6 +187,17 @@ GnomeShell.prototype = {
|
||||
Main.overview.show();
|
||||
else
|
||||
Main.overview.hide();
|
||||
},
|
||||
|
||||
ApiVersion: 1,
|
||||
|
||||
ShellVersion: Config.PACKAGE_VERSION,
|
||||
|
||||
_extensionStateChanged: function(_, newState) {
|
||||
DBus.session.emit_signal('/org/gnome/Shell',
|
||||
'org.gnome.Shell',
|
||||
'ExtensionStatusChanged', 'sis',
|
||||
[newState.uuid, newState.state, newState.error]);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -84,7 +84,7 @@ ListItem.prototype = {
|
||||
|
||||
_onClicked: function() {
|
||||
this.emit('activate');
|
||||
this._app.activate(-1);
|
||||
this._app.activate();
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(ListItem.prototype);
|
||||
@ -402,4 +402,4 @@ ShellProcessesDialog.prototype = {
|
||||
_setButtonsForChoices(this, choices);
|
||||
}
|
||||
}
|
||||
Signals.addSignalMethods(ShellProcessesDialog.prototype);
|
||||
Signals.addSignalMethods(ShellProcessesDialog.prototype);
|
||||
|
@ -88,11 +88,7 @@ ATIndicator.prototype = {
|
||||
this.menu.addMenuItem(mouseKeys);
|
||||
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addAction(_("Universal Access Settings"), function() {
|
||||
Main.overview.hide();
|
||||
let app = Shell.AppSystem.get_default().get_app('gnome-universal-access-panel.desktop');
|
||||
app.activate(-1);
|
||||
});
|
||||
this.menu.addSettingsAction(_("Universal Access Settings"), 'gnome-universal-access-panel.desktop');
|
||||
},
|
||||
|
||||
_buildItemExtended: function(string, initial_value, writable, on_set) {
|
||||
|
@ -67,7 +67,6 @@ Indicator.prototype = {
|
||||
new PopupMenu.PopupMenuItem(_("Set up a New Device...")),
|
||||
new PopupMenu.PopupSeparatorMenuItem()];
|
||||
this._hasDevices = false;
|
||||
this._deviceSep = this._fullMenuItems[0]; // hidden if no device exists
|
||||
|
||||
this._fullMenuItems[1].connect('activate', function() {
|
||||
GLib.spawn_command_line_async('bluetooth-sendto');
|
||||
@ -89,11 +88,7 @@ Indicator.prototype = {
|
||||
this._applet.connect('notify::show-full-menu', Lang.bind(this, this._updateFullMenu));
|
||||
this._updateFullMenu();
|
||||
|
||||
this.menu.addAction(_("Bluetooth Settings"), function() {
|
||||
Main.overview.hide()
|
||||
let app = Shell.AppSystem.get_default().get_app('bluetooth-properties.desktop');
|
||||
app.activate(-1);
|
||||
});
|
||||
this.menu.addSettingsAction(_("Bluetooth Settings"), 'bluetooth-properties.desktop');
|
||||
|
||||
this._applet.connect('pincode-request', Lang.bind(this, this._pinRequest));
|
||||
this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest));
|
||||
@ -162,10 +157,6 @@ Indicator.prototype = {
|
||||
this._hasDevices = true;
|
||||
}
|
||||
}
|
||||
if (this._hasDevices)
|
||||
this._deviceSep.actor.show();
|
||||
else
|
||||
this._deviceSep.actor.hide();
|
||||
},
|
||||
|
||||
_updateDeviceItem: function(item, device) {
|
||||
@ -277,21 +268,15 @@ Indicator.prototype = {
|
||||
|
||||
switch (device.type) {
|
||||
case GnomeBluetoothApplet.Type.KEYBOARD:
|
||||
item.menu.addAction(_("Keyboard Settings"), function() {
|
||||
GLib.spawn_command_line_async('gnome-control-center keyboard');
|
||||
});
|
||||
item.menu.addSettingsAction(_("Keyboard Settings"), 'gnome-keyboard-panel.desktop');
|
||||
break;
|
||||
case GnomeBluetoothApplet.Type.MOUSE:
|
||||
item.menu.addAction(_("Mouse Settings"), function() {
|
||||
GLib.spawn_command_line_async('gnome-control-center mouse');
|
||||
});
|
||||
item.menu.addSettingsAction(_("Mouse Settings"), 'gnome-mouse-panel.desktop');
|
||||
break;
|
||||
case GnomeBluetoothApplet.Type.HEADSET:
|
||||
case GnomeBluetoothApplet.Type.HEADPHONES:
|
||||
case GnomeBluetoothApplet.Type.OTHER_AUDIO:
|
||||
item.menu.addAction(_("Sound Settings"), function() {
|
||||
GLib.spawn_command_line_async('gnome-control-center sound');
|
||||
});
|
||||
item.menu.addSettingsAction(_("Sound Settings"), 'gnome-sound-panel.desktop');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -303,8 +288,6 @@ Indicator.prototype = {
|
||||
this._showAll(this._fullMenuItems);
|
||||
if (this._hasDevices)
|
||||
this._showAll(this._deviceItems);
|
||||
else
|
||||
this._deviceSep.actor.hide();
|
||||
} else {
|
||||
this._hideAll(this._fullMenuItems);
|
||||
this._hideAll(this._deviceItems);
|
||||
|
@ -68,15 +68,33 @@ XKBIndicator.prototype = {
|
||||
this._sync_config();
|
||||
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addAction(_("Show Keyboard Layout..."), Lang.bind(this, function() {
|
||||
this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, function() {
|
||||
Main.overview.hide();
|
||||
Util.spawn(['gkbd-keyboard-display', '-g', String(this._config.get_current_group() + 1)]);
|
||||
}));
|
||||
this.menu.addAction(_("Localization Settings"), function() {
|
||||
Main.overview.hide();
|
||||
let app = Shell.AppSystem.get_default().get_app('gnome-region-panel.desktop');
|
||||
app.activate(-1);
|
||||
});
|
||||
this.menu.addSettingsAction(_("Region and Language Settings"), 'gnome-region-panel.desktop');
|
||||
},
|
||||
|
||||
_adjust_group_names: function(names) {
|
||||
// Disambiguate duplicate names with a subscript
|
||||
// This is O(N^2) to avoid sorting names
|
||||
// but N <= 4 so who cares?
|
||||
|
||||
for (let i = 0; i < names.length; i++) {
|
||||
let name = names[i];
|
||||
let cnt = 0;
|
||||
for (let j = i + 1; j < names.length; j++) {
|
||||
if (names[j] == name) {
|
||||
cnt++;
|
||||
// U+2081 SUBSCRIPT ONE
|
||||
names[j] = name + String.fromCharCode(0x2081 + cnt);
|
||||
}
|
||||
}
|
||||
if (cnt != 0)
|
||||
names[i] = name + '\u2081';
|
||||
}
|
||||
|
||||
return names;
|
||||
},
|
||||
|
||||
_sync_config: function() {
|
||||
@ -101,7 +119,7 @@ XKBIndicator.prototype = {
|
||||
for (let i = 0; i < this._labelActors.length; i++)
|
||||
this._labelActors[i].destroy();
|
||||
|
||||
let short_names = this._config.get_short_group_names();
|
||||
let short_names = this._adjust_group_names(this._config.get_short_group_names());
|
||||
|
||||
this._selectedLayout = null;
|
||||
this._layoutItems = [ ];
|
||||
|
@ -45,17 +45,6 @@ const NM80211ApSecurityFlags = NetworkManager['80211ApSecurityFlags'];
|
||||
// (the remaining are placed into More...)
|
||||
const NUM_VISIBLE_NETWORKS = 5;
|
||||
|
||||
const NMAppletHelperInterface = {
|
||||
name: 'org.gnome.network_manager_applet',
|
||||
methods: [
|
||||
{ name: 'ConnectToHiddenNetwork', inSignature: '', outSignature: '' },
|
||||
{ name: 'CreateWifiNetwork', inSignature: '', outSignature: '' },
|
||||
{ name: 'ConnectTo8021xNetwork', inSignature: 'oo', outSignature: '' },
|
||||
{ name: 'ConnectTo3gNetwork', inSignature: 'o', outSignature: '' }
|
||||
],
|
||||
};
|
||||
const NMAppletProxy = DBus.makeProxyClass(NMAppletHelperInterface);
|
||||
|
||||
function macToArray(string) {
|
||||
return string.split(':').map(function(el) {
|
||||
return parseInt(el, 16);
|
||||
@ -102,6 +91,13 @@ function sortAccessPoints(accessPoints) {
|
||||
});
|
||||
}
|
||||
|
||||
function ssidToLabel(ssid) {
|
||||
let label = NetworkManager.utils_ssid_to_utf8(ssid);
|
||||
if (!label)
|
||||
label = _("<unknown>");
|
||||
return label;
|
||||
}
|
||||
|
||||
function NMNetworkMenuItem() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
@ -117,10 +113,7 @@ NMNetworkMenuItem.prototype = {
|
||||
|
||||
if (!title) {
|
||||
let ssid = this.bestAP.get_ssid();
|
||||
if (ssid)
|
||||
title = NetworkManager.utils_ssid_to_utf8(ssid);
|
||||
if (!title)
|
||||
title = _("<unknown>");
|
||||
title = ssidToLabel(ssid);
|
||||
}
|
||||
|
||||
this._label = new St.Label({ text: title });
|
||||
@ -631,15 +624,8 @@ NMDevice.prototype = {
|
||||
this.emit('network-lost');
|
||||
}
|
||||
|
||||
switch(newstate) {
|
||||
case NetworkManager.DeviceState.NEED_AUTH:
|
||||
// FIXME: make this have a real effect
|
||||
// (currently we rely on a running nm-applet)
|
||||
this.emit('need-auth');
|
||||
break;
|
||||
case NetworkManager.DeviceState.FAILED:
|
||||
if (newstate == NetworkManager.DeviceState.FAILED) {
|
||||
this.emit('activation-failed', reason);
|
||||
break;
|
||||
}
|
||||
|
||||
this._updateStatusItem();
|
||||
@ -674,7 +660,7 @@ NMDevice.prototype = {
|
||||
let dev_product = this.device.get_product();
|
||||
let dev_vendor = this.device.get_vendor();
|
||||
if (!dev_product || !dev_vendor)
|
||||
return null;
|
||||
return '';
|
||||
|
||||
let product = Util.fixupPCIDescription(dev_product);
|
||||
let vendor = Util.fixupPCIDescription(dev_vendor);
|
||||
@ -748,10 +734,6 @@ NMDeviceModem.prototype = {
|
||||
this.mobileDevice = null;
|
||||
this._connectionType = 'ppp';
|
||||
|
||||
this._applet_proxy = new NMAppletProxy(DBus.session,
|
||||
'org.gnome.network_manager_applet',
|
||||
'/org/gnome/network_manager_applet');
|
||||
|
||||
this._capabilities = device.current_capabilities;
|
||||
if (this._capabilities & NetworkManager.DeviceModemCapabilities.GSM_UMTS) {
|
||||
is_wwan = true;
|
||||
@ -853,12 +835,10 @@ NMDeviceModem.prototype = {
|
||||
},
|
||||
|
||||
_createAutomaticConnection: function() {
|
||||
// Mobile wizard is handled by nm-applet for now...
|
||||
this._applet_proxy.ConnectTo3gNetworkRemote(this.device.get_path(),
|
||||
Lang.bind(this, function(results, err) {
|
||||
if (err)
|
||||
log(err);
|
||||
}));
|
||||
// Mobile wizard is too complex for the shell UI and
|
||||
// is handled by the network panel
|
||||
Util.spawn(['gnome-control-center', 'network',
|
||||
'connect-3g', this.device.get_path()]);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
@ -970,10 +950,6 @@ NMDeviceWireless.prototype = {
|
||||
this._overflowItem = null;
|
||||
this._networks = [ ];
|
||||
|
||||
this._applet_proxy = new NMAppletProxy(DBus.session,
|
||||
'org.gnome.network_manager_applet',
|
||||
'/org/gnome/network_manager_applet');
|
||||
|
||||
// breaking the layers with this, but cannot call
|
||||
// this.connectionValid until I have a device
|
||||
this.device = device;
|
||||
@ -985,6 +961,16 @@ NMDeviceWireless.prototype = {
|
||||
for (let i = 0; i < accessPoints.length; i++) {
|
||||
// Access points are grouped by network
|
||||
let ap = accessPoints[i];
|
||||
|
||||
if (ap.get_ssid() == null) {
|
||||
// hidden access point cannot be added, we need to know
|
||||
// the SSID and security details to connect
|
||||
// nevertheless, the access point can acquire a SSID when
|
||||
// NetworkManager connects to it (via nmcli or the control-center)
|
||||
ap._notifySsidId = ap.connect('notify::ssid', Lang.bind(this, this._notifySsidCb));
|
||||
continue;
|
||||
}
|
||||
|
||||
let pos = this._findNetwork(ap);
|
||||
let obj;
|
||||
if (pos != -1) {
|
||||
@ -998,7 +984,7 @@ NMDeviceWireless.prototype = {
|
||||
item: null,
|
||||
accessPoints: [ ap ]
|
||||
};
|
||||
obj.ssidText = NetworkManager.utils_ssid_to_utf8(obj.ssid);
|
||||
obj.ssidText = ssidToLabel(obj.ssid);
|
||||
this._networks.push(obj);
|
||||
}
|
||||
|
||||
@ -1011,8 +997,14 @@ NMDeviceWireless.prototype = {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.device.active_access_point) {
|
||||
this._activeNetwork = this._networks[this._findNetwork(this.device.active_access_point)];
|
||||
let networkPos = this._findNetwork(this.device.active_access_point);
|
||||
|
||||
if (networkPos == -1) // the connected access point is invisible
|
||||
this._activeNetwork = null;
|
||||
else
|
||||
this._activeNetwork = this._networks[networkPos];
|
||||
} else {
|
||||
this._activeNetwork = null;
|
||||
}
|
||||
@ -1098,6 +1090,14 @@ NMDeviceWireless.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_notifySsidCb: function(accessPoint) {
|
||||
if (accessPoint.get_ssid() != null) {
|
||||
accessPoint.disconnect(accessPoint._notifySsidId);
|
||||
accessPoint._notifySsidId = 0;
|
||||
this._accessPointAdded(this.device, accessPoint);
|
||||
}
|
||||
},
|
||||
|
||||
_activeApChanged: function() {
|
||||
this._activeNetwork = null;
|
||||
|
||||
@ -1105,7 +1105,9 @@ NMDeviceWireless.prototype = {
|
||||
|
||||
if (activeAp) {
|
||||
let pos = this._findNetwork(activeAp);
|
||||
this._activeNetwork = this._networks[pos];
|
||||
|
||||
if (pos != -1)
|
||||
this._activeNetwork = this._networks[pos];
|
||||
}
|
||||
|
||||
// we don't refresh the view here, setActiveConnection will
|
||||
@ -1180,6 +1182,9 @@ NMDeviceWireless.prototype = {
|
||||
},
|
||||
|
||||
_findNetwork: function(accessPoint) {
|
||||
if (accessPoint.get_ssid() == null)
|
||||
return -1;
|
||||
|
||||
for (let i = 0; i < this._networks.length; i++) {
|
||||
if (this._networkCompare(this._networks[i], accessPoint))
|
||||
return i;
|
||||
@ -1188,6 +1193,13 @@ NMDeviceWireless.prototype = {
|
||||
},
|
||||
|
||||
_accessPointAdded: function(device, accessPoint) {
|
||||
if (accessPoint.get_ssid() == null) {
|
||||
// This access point is not visible yet
|
||||
// Wait for it to get a ssid
|
||||
accessPoint._notifySsidId = accessPoint.connect('notify::ssid', Lang.bind(this, this._notifySsidCb));
|
||||
return;
|
||||
}
|
||||
|
||||
let pos = this._findNetwork(accessPoint);
|
||||
let apObj;
|
||||
let needsupdate = false;
|
||||
@ -1210,7 +1222,7 @@ NMDeviceWireless.prototype = {
|
||||
item: null,
|
||||
accessPoints: [ accessPoint ]
|
||||
};
|
||||
apObj.ssidText = NetworkManager.utils_ssid_to_utf8(apObj.ssid);
|
||||
apObj.ssidText = ssidToLabel(apObj.ssid);
|
||||
needsupdate = true;
|
||||
}
|
||||
|
||||
@ -1423,13 +1435,12 @@ NMDeviceWireless.prototype = {
|
||||
},
|
||||
|
||||
_createActiveConnectionItem: function() {
|
||||
let activeAp = this.device.active_access_point;
|
||||
let icon, title;
|
||||
if (this._activeConnection._connection) {
|
||||
let connection = this._activeConnection._connection;
|
||||
if (activeAp)
|
||||
this._activeConnectionItem = new NMNetworkMenuItem([ activeAp ], undefined,
|
||||
{ reactive: false });
|
||||
if (this._activeNetwork)
|
||||
this._activeConnectionItem = new NMNetworkMenuItem(this._activeNetwork.accessPoints, undefined,
|
||||
{ reactive: false });
|
||||
else
|
||||
this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(connection._name,
|
||||
'network-wireless-connected',
|
||||
@ -1437,9 +1448,9 @@ NMDeviceWireless.prototype = {
|
||||
} else {
|
||||
// We cannot read the connection (due to ACL, or API incompatibility), but we still show signal if we have it
|
||||
let menuItem;
|
||||
if (activeAp)
|
||||
this._activeConnectionItem = new NMNetworkMenuItem([ activeAp ], undefined,
|
||||
{ reactive: false });
|
||||
if (this._activeNetwork)
|
||||
this._activeConnectionItem = new NMNetworkMenuItem(this._activeNetwork.accessPoints, undefined,
|
||||
{ reactive: false });
|
||||
else
|
||||
this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(_("Connected (private)"),
|
||||
'network-wireless-connected',
|
||||
@ -1486,13 +1497,10 @@ NMDeviceWireless.prototype = {
|
||||
let accessPoints = sortAccessPoints(apObj.accessPoints);
|
||||
if ( (accessPoints[0]._secType == NMAccessPointSecurity.WPA2_ENT)
|
||||
|| (accessPoints[0]._secType == NMAccessPointSecurity.WPA_ENT)) {
|
||||
// 802.1x-enabled APs get handled by nm-applet for now...
|
||||
this._applet_proxy.ConnectTo8021xNetworkRemote(this.device.get_path(),
|
||||
accessPoints[0].dbus_path,
|
||||
Lang.bind(this, function(results, err) {
|
||||
if (err)
|
||||
log(err);
|
||||
}));
|
||||
// 802.1x-enabled APs require further configuration, so they're
|
||||
// handled in gnome-control-center
|
||||
Util.spawn(['gnome-control-center', 'network', 'connect-8021x-wifi',
|
||||
this.device.get_path(), accessPoints[0].dbus_path]);
|
||||
} else {
|
||||
let connection = this._createAutomaticConnection(apObj);
|
||||
this._client.add_and_activate_connection(connection, this.device, accessPoints[0].dbus_path, null)
|
||||
@ -1550,9 +1558,9 @@ NMApplet.prototype = {
|
||||
this._statusSection.addAction(_("Enable networking"), Lang.bind(this, function() {
|
||||
this._client.networking_enabled = true;
|
||||
}));
|
||||
this._statusSection.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this._statusSection.actor.hide();
|
||||
this.menu.addMenuItem(this._statusSection);
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
|
||||
this._devices = { };
|
||||
|
||||
@ -1563,9 +1571,9 @@ NMApplet.prototype = {
|
||||
};
|
||||
|
||||
this._devices.wired.section.addMenuItem(this._devices.wired.item);
|
||||
this._devices.wired.section.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this._devices.wired.section.actor.hide();
|
||||
this.menu.addMenuItem(this._devices.wired.section);
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
|
||||
this._devices.wireless = {
|
||||
section: new PopupMenu.PopupMenuSection(),
|
||||
@ -1573,9 +1581,9 @@ NMApplet.prototype = {
|
||||
item: this._makeToggleItem('wireless', _("Wireless"))
|
||||
};
|
||||
this._devices.wireless.section.addMenuItem(this._devices.wireless.item);
|
||||
this._devices.wireless.section.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this._devices.wireless.section.actor.hide();
|
||||
this.menu.addMenuItem(this._devices.wireless.section);
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
|
||||
this._devices.wwan = {
|
||||
section: new PopupMenu.PopupMenuSection(),
|
||||
@ -1583,9 +1591,9 @@ NMApplet.prototype = {
|
||||
item: this._makeToggleItem('wwan', _("Mobile broadband"))
|
||||
};
|
||||
this._devices.wwan.section.addMenuItem(this._devices.wwan.item);
|
||||
this._devices.wwan.section.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this._devices.wwan.section.actor.hide();
|
||||
this.menu.addMenuItem(this._devices.wwan.section);
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
|
||||
this._devices.vpn = {
|
||||
section: new PopupMenu.PopupMenuSection(),
|
||||
@ -1598,15 +1606,10 @@ NMApplet.prototype = {
|
||||
this._devices.vpn.item.updateForDevice(this._devices.vpn.device);
|
||||
this._devices.vpn.section.addMenuItem(this._devices.vpn.item);
|
||||
this._devices.vpn.section.addMenuItem(this._devices.vpn.device.section);
|
||||
this._devices.vpn.section.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this._devices.vpn.section.actor.hide();
|
||||
this.menu.addMenuItem(this._devices.vpn.section);
|
||||
|
||||
this.menu.addAction(_("Network Settings"), function() {
|
||||
Main.overview.hide();
|
||||
let app = Shell.AppSystem.get_default().get_app('gnome-network-panel.desktop');
|
||||
app.activate(-1);
|
||||
});
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addSettingsAction(_("Network Settings"), 'gnome-network-panel.desktop');
|
||||
|
||||
this._activeConnections = [ ];
|
||||
this._connections = [ ];
|
||||
@ -1660,8 +1663,7 @@ NMApplet.prototype = {
|
||||
_ensureSource: function() {
|
||||
if (!this._source) {
|
||||
this._source = new NMMessageTraySource();
|
||||
this._source._destroyId = this._source.connect('destroy', Lang.bind(this, function() {
|
||||
this._source._destroyId = 0;
|
||||
this._source.connect('destroy', Lang.bind(this, function() {
|
||||
this._source = null;
|
||||
}));
|
||||
Main.messageTray.add(this._source);
|
||||
@ -1709,6 +1711,28 @@ NMApplet.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_notifyForDevice: function(device, iconName, title, text, urgency) {
|
||||
if (device._notification)
|
||||
device._notification.destroy();
|
||||
|
||||
/* must call after destroying previous notification,
|
||||
or this._source will be cleared */
|
||||
this._ensureSource();
|
||||
|
||||
let icon = new St.Icon({ icon_name: iconName,
|
||||
icon_type: St.IconType.SYMBOLIC,
|
||||
icon_size: this._source.ICON_SIZE
|
||||
});
|
||||
device._notification = new MessageTray.Notification(this._source, title, text,
|
||||
{ icon: icon });
|
||||
device._notification.setUrgency(urgency);
|
||||
device._notification.setTransient(true);
|
||||
device._notification.connect('destroy', function() {
|
||||
device._notification = null;
|
||||
});
|
||||
this._source.notify(device._notification);
|
||||
},
|
||||
|
||||
_deviceAdded: function(client, device) {
|
||||
if (device._delegate) {
|
||||
// already seen, not adding again
|
||||
@ -1718,42 +1742,29 @@ NMApplet.prototype = {
|
||||
if (wrapperClass) {
|
||||
let wrapper = new wrapperClass(this._client, device, this._connections);
|
||||
|
||||
// FIXME: these notifications are duplicate with those exposed by nm-applet
|
||||
// uncomment this code in 3.2, when we'll conflict with and kill nm-applet
|
||||
/* wrapper._networkLostId = wrapper.connect('network-lost', Lang.bind(this, function(emitter) {
|
||||
this._ensureSource();
|
||||
let icon = new St.Icon({ icon_name: 'network-offline',
|
||||
icon_type: St.IconType.SYMBOLIC,
|
||||
icon_size: this._source.ICON_SIZE
|
||||
});
|
||||
let notification = new MessageTray.Notification(this._source,
|
||||
_("Connectivity lost"),
|
||||
_("You're no longer connected to the network"),
|
||||
{ icon: icon });
|
||||
this._source.notify(notification);
|
||||
wrapper._networkLostId = wrapper.connect('network-lost', Lang.bind(this, function(device) {
|
||||
this._notifyForDevice(device, 'network-offline',
|
||||
_("Connectivity lost"),
|
||||
_("You're no longer connected to the network"),
|
||||
// set critical urgency to popup the notification automatically
|
||||
MessageTray.Urgency.CRITICAL);
|
||||
}));
|
||||
wrapper._activationFailedId = wrapper.connect('activation-failed', Lang.bind(this, function(wrapper, reason) {
|
||||
this._ensureSource();
|
||||
let icon = new St.Icon({ icon_name: 'network-error',
|
||||
icon_type: St.IconType.SYMBOLIC,
|
||||
icon_size: this._source.ICON_SIZE,
|
||||
});
|
||||
let banner;
|
||||
wrapper._activationFailedId = wrapper.connect('activation-failed', Lang.bind(this, function(device, reason) {
|
||||
// XXX: nm-applet has no special text depending on reason
|
||||
// but I'm not sure of this generic message
|
||||
let notification = new MessageTray.Notification(this._source,
|
||||
_("Connection failed"),
|
||||
_("Activation of network connection failed"),
|
||||
{ icon: icon });
|
||||
this._source.notify(notification);
|
||||
})); */
|
||||
this._notifyForDevice(device, 'network-error',
|
||||
_("Connection failed"),
|
||||
_("Activation of network connection failed"),
|
||||
MessageTray.Urgency.HIGH);
|
||||
}));
|
||||
wrapper._deviceStateChangedId = wrapper.connect('state-changed', Lang.bind(this, function(dev) {
|
||||
this._syncSectionTitle(dev.category);
|
||||
}));
|
||||
wrapper._destroyId = wrapper.connect('destroy', function(wrapper) {
|
||||
//wrapper.disconnect(wrapper._networkLostId);
|
||||
//wrapper.disconnect(wrapper._activationFailedId);
|
||||
wrapper.disconnect(wrapper._networkLostId);
|
||||
wrapper.disconnect(wrapper._activationFailedId);
|
||||
wrapper.disconnect(wrapper._deviceStateChangedId);
|
||||
wrapper.disconnect(wrapper._destroyId);
|
||||
});
|
||||
let section = this._devices[wrapper.category].section;
|
||||
let devices = this._devices[wrapper.category].devices;
|
||||
@ -1798,11 +1809,8 @@ NMApplet.prototype = {
|
||||
active._primaryDevice.setActiveConnection(null);
|
||||
active._primaryDevice = null;
|
||||
}
|
||||
if (active._notifyStateId) {
|
||||
active.disconnect(active._notifyStateId);
|
||||
active._notifyStateId = 0;
|
||||
}
|
||||
if (active._inited) {
|
||||
active.disconnect(active._notifyStateId);
|
||||
active.disconnect(active._notifyDefaultId);
|
||||
active.disconnect(active._notifyDefault6Id);
|
||||
active._inited = false;
|
||||
@ -1820,14 +1828,7 @@ NMApplet.prototype = {
|
||||
if (!a._inited) {
|
||||
a._notifyDefaultId = a.connect('notify::default', Lang.bind(this, this._updateIcon));
|
||||
a._notifyDefault6Id = a.connect('notify::default6', Lang.bind(this, this._updateIcon));
|
||||
if (a.state == NetworkManager.ActiveConnectionState.ACTIVATING) // prepare to notify to the user
|
||||
a._notifyStateId = a.connect('notify::state', Lang.bind(this, this._notifyActiveConnection));
|
||||
else {
|
||||
// notify as soon as possible
|
||||
Mainloop.idle_add(Lang.bind(this, function() {
|
||||
this._notifyActiveConnection(a);
|
||||
}));
|
||||
}
|
||||
a._notifyStateId = a.connect('notify::state', Lang.bind(this, this._updateIcon));
|
||||
|
||||
a._inited = true;
|
||||
}
|
||||
@ -1877,63 +1878,6 @@ NMApplet.prototype = {
|
||||
this._mainConnection = activating || default_ip4 || default_ip6 || this._activeConnections[0] || null;
|
||||
},
|
||||
|
||||
_notifyActiveConnection: function(active) {
|
||||
// FIXME: duplicate notifications when nm-applet is running
|
||||
// This code will come back when nm-applet is killed
|
||||
this._syncNMState();
|
||||
return;
|
||||
|
||||
if (active.state == NetworkManager.ActiveConnectionState.ACTIVATED) {
|
||||
|
||||
// notify only connections that are visible
|
||||
if (active._connection) {
|
||||
this._ensureSource();
|
||||
|
||||
let icon;
|
||||
let banner;
|
||||
switch (active._section) {
|
||||
case NMConnectionCategory.WWAN:
|
||||
icon = 'network-cellular-signal-excellent';
|
||||
banner = _("You're now connected to mobile broadband connection '%s'").format(active._connection._name);
|
||||
break;
|
||||
case NMConnectionCategory.WIRELESS:
|
||||
icon = 'network-wireless-signal-excellent';
|
||||
banner = _("You're now connected to wireless network '%s'").format(active._connection._name);
|
||||
break;
|
||||
case NMConnectionCategory.WIRED:
|
||||
icon = 'network-wired';
|
||||
banner = _("You're now connected to wired network '%s'").format(active._connection._name);
|
||||
break;
|
||||
case NMConnectionCategory.VPN:
|
||||
icon = 'network-vpn';
|
||||
banner = _("You're now connected to VPN network '%s'").format(active._connection._name);
|
||||
break;
|
||||
default:
|
||||
// a fallback for a generic 'connected' icon
|
||||
icon = 'network-transmit-receive';
|
||||
banner = _("You're now connected to '%s'").format(active._connection._name);
|
||||
}
|
||||
|
||||
let iconActor = new St.Icon({ icon_name: icon,
|
||||
icon_type: St.IconType.SYMBOLIC,
|
||||
icon_size: this._source.ICON_SIZE
|
||||
});
|
||||
let notification = new MessageTray.Notification(this._source,
|
||||
_("Connection established"),
|
||||
banner,
|
||||
{ icon: iconActor });
|
||||
this._source.notify(notification);
|
||||
}
|
||||
|
||||
if (active._stateChangeId) {
|
||||
active.disconnect(active._stateChangeId);
|
||||
active._stateChangeId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
this._syncNMState();
|
||||
},
|
||||
|
||||
_readConnections: function() {
|
||||
let connections = this._settings.list_connections();
|
||||
for (let i = 0; i < connections.length; i++) {
|
||||
|
@ -75,16 +75,11 @@ Indicator.prototype = {
|
||||
this._batteryItem.addActor(this._primaryPercentage, { align: St.Align.END });
|
||||
this.menu.addMenuItem(this._batteryItem);
|
||||
|
||||
this._deviceSep = new PopupMenu.PopupSeparatorMenuItem();
|
||||
this.menu.addMenuItem(this._deviceSep);
|
||||
this._otherDevicePosition = 2;
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this._otherDevicePosition = 2;
|
||||
|
||||
this.menu.addAction(_("Power Settings"),function() {
|
||||
Main.overview.hide();
|
||||
let app = Shell.AppSystem.get_default().get_app('gnome-power-panel.desktop');
|
||||
app.activate(-1);
|
||||
});
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop');
|
||||
|
||||
this._proxy.connect('Changed', Lang.bind(this, this._devicesChanged));
|
||||
this._devicesChanged();
|
||||
@ -96,7 +91,6 @@ Indicator.prototype = {
|
||||
this._hasPrimary = false;
|
||||
this._primaryDeviceId = null;
|
||||
this._batteryItem.actor.hide();
|
||||
this._deviceSep.actor.hide();
|
||||
return;
|
||||
}
|
||||
let [device_id, device_type, icon, percentage, state, seconds] = device;
|
||||
@ -126,12 +120,9 @@ Indicator.prototype = {
|
||||
}
|
||||
this._primaryPercentage.text = Math.round(percentage) + '%';
|
||||
this._batteryItem.actor.show();
|
||||
if (this._deviceItems.length > 0)
|
||||
this._deviceSep.actor.show();
|
||||
} else {
|
||||
this._hasPrimary = false;
|
||||
this._batteryItem.actor.hide();
|
||||
this._deviceSep.actor.hide();
|
||||
}
|
||||
|
||||
this._primaryDeviceId = device_id;
|
||||
@ -144,7 +135,6 @@ Indicator.prototype = {
|
||||
this._deviceItems = [];
|
||||
|
||||
if (error) {
|
||||
this._deviceSep.actor.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -159,11 +149,6 @@ Indicator.prototype = {
|
||||
this.menu.addMenuItem(item, this._otherDevicePosition + position);
|
||||
position++;
|
||||
}
|
||||
|
||||
if (this._hasPrimary && position > 0)
|
||||
this._deviceSep.actor.show();
|
||||
else
|
||||
this._deviceSep.actor.hide();
|
||||
}));
|
||||
},
|
||||
|
||||
|
@ -29,7 +29,7 @@ Indicator.prototype = {
|
||||
PanelMenu.SystemStatusButton.prototype._init.call(this, 'audio-volume-muted', null);
|
||||
|
||||
this._control = new Gvc.MixerControl({ name: 'GNOME Shell Volume Control' });
|
||||
this._control.connect('ready', Lang.bind(this, this._onControlReady));
|
||||
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));
|
||||
@ -47,8 +47,7 @@ Indicator.prototype = {
|
||||
this.menu.addMenuItem(this._outputTitle);
|
||||
this.menu.addMenuItem(this._outputSlider);
|
||||
|
||||
this._separator = new PopupMenu.PopupSeparatorMenuItem();
|
||||
this.menu.addMenuItem(this._separator);
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
|
||||
this._input = null;
|
||||
this._inputVolumeId = 0;
|
||||
@ -61,11 +60,7 @@ Indicator.prototype = {
|
||||
this.menu.addMenuItem(this._inputSlider);
|
||||
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addAction(_("Sound Settings"), function() {
|
||||
Main.overview.hide();
|
||||
let app = Shell.AppSystem.get_default().get_app('gnome-sound-panel.desktop');
|
||||
app.activate(-1);
|
||||
});
|
||||
this.menu.addSettingsAction(_("Sound Settings"), 'gnome-sound-panel.desktop');
|
||||
|
||||
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||
this._control.open();
|
||||
@ -102,9 +97,14 @@ Indicator.prototype = {
|
||||
this._notifyVolumeChange();
|
||||
},
|
||||
|
||||
_onControlReady: function() {
|
||||
this._readOutput();
|
||||
this._readInput();
|
||||
_onControlStateChanged: function() {
|
||||
if (this._control.get_state() == Gvc.MixerControlState.READY) {
|
||||
this._readOutput();
|
||||
this._readInput();
|
||||
this.actor.show();
|
||||
} else {
|
||||
this.actor.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_readOutput: function() {
|
||||
@ -140,7 +140,6 @@ Indicator.prototype = {
|
||||
this._mutedChanged (null, null, '_input');
|
||||
this._volumeChanged (null, null, '_input');
|
||||
} else {
|
||||
this._separator.actor.hide();
|
||||
this._inputTitle.actor.hide();
|
||||
this._inputSlider.actor.hide();
|
||||
}
|
||||
@ -163,11 +162,9 @@ Indicator.prototype = {
|
||||
}
|
||||
}
|
||||
if (showInput) {
|
||||
this._separator.actor.show();
|
||||
this._inputTitle.actor.show();
|
||||
this._inputSlider.actor.show();
|
||||
} else {
|
||||
this._separator.actor.hide();
|
||||
this._inputTitle.actor.hide();
|
||||
this._inputSlider.actor.hide();
|
||||
}
|
||||
|
@ -1,345 +0,0 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Gdm = imports.gi.Gdm;
|
||||
const DBus = imports.dbus;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
const Tp = imports.gi.TelepathyGLib;
|
||||
const UPowerGlib = imports.gi.UPowerGlib;
|
||||
|
||||
const GnomeSession = imports.misc.gnomeSession;
|
||||
const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const ScreenSaver = imports.misc.screenSaver;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
|
||||
const DISABLE_USER_SWITCH_KEY = 'disable-user-switching';
|
||||
const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen';
|
||||
const DISABLE_LOG_OUT_KEY = 'disable-log-out';
|
||||
|
||||
// Adapted from gdm/gui/user-switch-applet/applet.c
|
||||
//
|
||||
// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
|
||||
// Copyright (C) 2008,2009 Red Hat, Inc.
|
||||
|
||||
function StatusMenuButton() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
StatusMenuButton.prototype = {
|
||||
__proto__: PanelMenu.Button.prototype,
|
||||
|
||||
_init: function() {
|
||||
PanelMenu.Button.prototype._init.call(this, 0.0);
|
||||
let box = new St.BoxLayout({ name: 'panelStatusMenu' });
|
||||
this.actor.set_child(box);
|
||||
|
||||
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
|
||||
|
||||
this._gdm = Gdm.UserManager.ref_default();
|
||||
this._gdm.queue_load();
|
||||
|
||||
this._user = this._gdm.get_user(GLib.get_user_name());
|
||||
this._presence = new GnomeSession.Presence();
|
||||
this._presenceItems = {};
|
||||
this._session = new GnomeSession.SessionManager();
|
||||
this._haveShutdown = true;
|
||||
|
||||
this._account_mgr = Tp.AccountManager.dup()
|
||||
|
||||
this._upClient = new UPowerGlib.Client();
|
||||
this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy();
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
this._iconBox = new St.Bin();
|
||||
box.add(this._iconBox, { y_align: St.Align.MIDDLE, y_fill: false });
|
||||
|
||||
let textureCache = St.TextureCache.get_default();
|
||||
this._availableIcon = new St.Icon({ icon_name: 'user-available', style_class: 'popup-menu-icon' });
|
||||
this._busyIcon = new St.Icon({ icon_name: 'user-busy', style_class: 'popup-menu-icon' });
|
||||
this._invisibleIcon = new St.Icon({ icon_name: 'user-invisible', style_class: 'popup-menu-icon' });
|
||||
this._idleIcon = new St.Icon({ icon_name: 'user-idle', style_class: 'popup-menu-icon' });
|
||||
|
||||
this._presence.connect('StatusChanged', Lang.bind(this, this._updatePresenceIcon));
|
||||
this._presence.getStatus(Lang.bind(this, this._updatePresenceIcon));
|
||||
|
||||
this._name = new St.Label();
|
||||
box.add(this._name, { y_align: St.Align.MIDDLE, y_fill: false });
|
||||
this._userLoadedId = this._user.connect('notify::is-loaded', Lang.bind(this, this._updateUserName));
|
||||
this._userChangedId = this._user.connect('changed', Lang.bind(this, this._updateUserName));
|
||||
|
||||
this._createSubMenu();
|
||||
this._gdm.connect('notify::is-loaded', Lang.bind(this, this._updateSwitchUser));
|
||||
this._gdm.connect('user-added', Lang.bind(this, this._updateSwitchUser));
|
||||
this._gdm.connect('user-removed', Lang.bind(this, this._updateSwitchUser));
|
||||
this._lockdownSettings.connect('changed::' + DISABLE_USER_SWITCH_KEY,
|
||||
Lang.bind(this, this._updateSwitchUser));
|
||||
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
|
||||
Lang.bind(this, this._updateLogout));
|
||||
|
||||
this._lockdownSettings.connect('changed::' + DISABLE_LOCK_SCREEN_KEY,
|
||||
Lang.bind(this, this._updateLockScreen));
|
||||
this._updateSwitchUser();
|
||||
this._updateLogout();
|
||||
this._updateLockScreen();
|
||||
|
||||
// Whether shutdown is available or not depends on both lockdown
|
||||
// settings (disable-log-out) and Polkit policy - the latter doesn't
|
||||
// notify, so we update the menu item each time the menu opens or
|
||||
// the lockdown setting changes, which should be close enough.
|
||||
this.menu.connect('open-state-changed', Lang.bind(this,
|
||||
function(menu, open) {
|
||||
if (open)
|
||||
this._updateHaveShutdown();
|
||||
}));
|
||||
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
|
||||
Lang.bind(this, this._updateHaveShutdown));
|
||||
|
||||
this._upClient.connect('notify::can-suspend', Lang.bind(this, this._updateSuspendOrPowerOff));
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
this._user.disconnect(this._userLoadedId);
|
||||
this._user.disconnect(this._userChangedId);
|
||||
},
|
||||
|
||||
_updateUserName: function() {
|
||||
if (this._user.is_loaded)
|
||||
this._name.set_text(this._user.get_real_name());
|
||||
else
|
||||
this._name.set_text("");
|
||||
},
|
||||
|
||||
_updateSessionSeparator: function() {
|
||||
let sessionItemsVisible = this._loginScreenItem.actor.visible ||
|
||||
this._logoutItem.actor.visible ||
|
||||
this._lockScreenItem.actor.visible;
|
||||
|
||||
let showSessionSeparator = sessionItemsVisible &&
|
||||
this._suspendOrPowerOffItem.actor.visible;
|
||||
|
||||
let showSettingsSeparator = sessionItemsVisible ||
|
||||
this._suspendOrPowerOffItem.actor.visible;
|
||||
|
||||
if (showSessionSeparator)
|
||||
this._sessionSeparator.actor.show();
|
||||
else
|
||||
this._sessionSeparator.actor.hide();
|
||||
|
||||
if (showSettingsSeparator)
|
||||
this._settingsSeparator.actor.show();
|
||||
else
|
||||
this._settingsSeparator.actor.hide();
|
||||
},
|
||||
|
||||
_updateSwitchUser: function() {
|
||||
let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY);
|
||||
if (allowSwitch && this._gdm.can_switch ())
|
||||
this._loginScreenItem.actor.show();
|
||||
else
|
||||
this._loginScreenItem.actor.hide();
|
||||
this._updateSessionSeparator();
|
||||
},
|
||||
|
||||
_updateLogout: function() {
|
||||
let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY);
|
||||
if (allowLogout)
|
||||
this._logoutItem.actor.show();
|
||||
else
|
||||
this._logoutItem.actor.hide();
|
||||
this._updateSessionSeparator();
|
||||
},
|
||||
|
||||
_updateLockScreen: function() {
|
||||
let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
|
||||
if (allowLockScreen)
|
||||
this._lockScreenItem.actor.show();
|
||||
else
|
||||
this._lockScreenItem.actor.hide();
|
||||
this._updateSessionSeparator();
|
||||
},
|
||||
|
||||
_updateHaveShutdown: function() {
|
||||
this._session.CanShutdownRemote(Lang.bind(this,
|
||||
function(result, error) {
|
||||
if (!error) {
|
||||
this._haveShutdown = result;
|
||||
this._updateSuspendOrPowerOff();
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
_updateSuspendOrPowerOff: function() {
|
||||
this._haveSuspend = this._upClient.get_can_suspend();
|
||||
|
||||
if (!this._suspendOrPowerOffItem)
|
||||
return;
|
||||
|
||||
if (!this._haveShutdown && !this._haveSuspend)
|
||||
this._suspendOrPowerOffItem.actor.hide();
|
||||
else
|
||||
this._suspendOrPowerOffItem.actor.show();
|
||||
this._updateSessionSeparator();
|
||||
|
||||
// If we can't suspend show Power Off... instead
|
||||
// and disable the alt key
|
||||
if (!this._haveSuspend) {
|
||||
this._suspendOrPowerOffItem.updateText(_("Power Off..."), null);
|
||||
} else if (!this._haveShutdown) {
|
||||
this._suspendOrPowerOffItem.updateText(_("Suspend"), null);
|
||||
} else {
|
||||
this._suspendOrPowerOffItem.updateText(_("Suspend"), _("Power Off..."));
|
||||
}
|
||||
},
|
||||
|
||||
_updatePresenceIcon: function(presence, status) {
|
||||
if (status == GnomeSession.PresenceStatus.AVAILABLE)
|
||||
this._iconBox.child = this._availableIcon;
|
||||
else if (status == GnomeSession.PresenceStatus.BUSY)
|
||||
this._iconBox.child = this._busyIcon;
|
||||
else if (status == GnomeSession.PresenceStatus.INVISIBLE)
|
||||
this._iconBox.child = this._invisibleIcon;
|
||||
else
|
||||
this._iconBox.child = this._idleIcon;
|
||||
|
||||
for (let itemStatus in this._presenceItems)
|
||||
this._presenceItems[itemStatus].setShowDot(itemStatus == status);
|
||||
},
|
||||
|
||||
_createSubMenu: function() {
|
||||
let item;
|
||||
|
||||
item = new PopupMenu.PopupImageMenuItem(_("Available"), 'user-available');
|
||||
item.connect('activate', Lang.bind(this, this._setPresenceStatus, GnomeSession.PresenceStatus.AVAILABLE));
|
||||
this.menu.addMenuItem(item);
|
||||
this._presenceItems[GnomeSession.PresenceStatus.AVAILABLE] = item;
|
||||
|
||||
item = new PopupMenu.PopupImageMenuItem(_("Busy"), 'user-busy');
|
||||
item.connect('activate', Lang.bind(this, this._setPresenceStatus, GnomeSession.PresenceStatus.BUSY));
|
||||
this.menu.addMenuItem(item);
|
||||
this._presenceItems[GnomeSession.PresenceStatus.BUSY] = item;
|
||||
|
||||
item = new PopupMenu.PopupSeparatorMenuItem();
|
||||
this.menu.addMenuItem(item);
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("My Account"));
|
||||
item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("System Settings"));
|
||||
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
|
||||
item = new PopupMenu.PopupSeparatorMenuItem();
|
||||
this.menu.addMenuItem(item);
|
||||
this._settingsSeparator = item;
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Lock Screen"));
|
||||
item.connect('activate', Lang.bind(this, this._onLockScreenActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
this._lockScreenItem = item;
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Switch User"));
|
||||
item.connect('activate', Lang.bind(this, this._onLoginScreenActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
this._loginScreenItem = item;
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Log Out..."));
|
||||
item.connect('activate', Lang.bind(this, this._onQuitSessionActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
this._logoutItem = item;
|
||||
|
||||
item = new PopupMenu.PopupSeparatorMenuItem();
|
||||
this.menu.addMenuItem(item);
|
||||
this._sessionSeparator = item;
|
||||
|
||||
item = new PopupMenu.PopupAlternatingMenuItem(_("Suspend"),
|
||||
_("Power Off..."));
|
||||
this.menu.addMenuItem(item);
|
||||
this._suspendOrPowerOffItem = item;
|
||||
item.connect('activate', Lang.bind(this, this._onSuspendOrPowerOffActivate));
|
||||
this._updateSuspendOrPowerOff();
|
||||
},
|
||||
|
||||
_setPresenceStatus: function(item, event, status) {
|
||||
this._presence.setStatus(status);
|
||||
|
||||
this._setIMStatus(status);
|
||||
},
|
||||
|
||||
_onMyAccountActivate: function() {
|
||||
Main.overview.hide();
|
||||
let app = Shell.AppSystem.get_default().get_app('gnome-user-accounts-panel.desktop');
|
||||
app.activate(-1);
|
||||
},
|
||||
|
||||
_onPreferencesActivate: function() {
|
||||
Main.overview.hide();
|
||||
let app = Shell.AppSystem.get_default().get_app('gnome-control-center.desktop');
|
||||
app.activate(-1);
|
||||
},
|
||||
|
||||
_onLockScreenActivate: function() {
|
||||
Main.overview.hide();
|
||||
this._screenSaverProxy.LockRemote();
|
||||
},
|
||||
|
||||
_onLoginScreenActivate: function() {
|
||||
Main.overview.hide();
|
||||
// Ensure we only move to GDM after the screensaver has activated; in some
|
||||
// OS configurations, the X server may block event processing on VT switch
|
||||
this._screenSaverProxy.SetActiveRemote(true, Lang.bind(this, function() {
|
||||
this._gdm.goto_login_session();
|
||||
}));
|
||||
},
|
||||
|
||||
_onQuitSessionActivate: function() {
|
||||
Main.overview.hide();
|
||||
this._session.LogoutRemote(0);
|
||||
},
|
||||
|
||||
_onSuspendOrPowerOffActivate: function() {
|
||||
Main.overview.hide();
|
||||
|
||||
if (this._haveSuspend &&
|
||||
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
|
||||
// Ensure we only suspend after the screensaver has activated
|
||||
this._screenSaverProxy.SetActiveRemote(true, Lang.bind(this, function() {
|
||||
this._upClient.suspend_sync(null);
|
||||
}));
|
||||
} else {
|
||||
this._session.ShutdownRemote();
|
||||
}
|
||||
},
|
||||
|
||||
_setIMStatus: function(session_status) {
|
||||
let [presence_type, presence_status, msg] = this._account_mgr.get_most_available_presence();
|
||||
let type, status;
|
||||
|
||||
// We change the IM presence only if there are connected accounts
|
||||
if (presence_type == Tp.ConnectionPresenceType.UNSET ||
|
||||
presence_type == Tp.ConnectionPresenceType.OFFLINE ||
|
||||
presence_type == Tp.ConnectionPresenceType.UNKNOWN ||
|
||||
presence_type == Tp.ConnectionPresenceType.ERROR)
|
||||
return;
|
||||
|
||||
if (session_status == GnomeSession.PresenceStatus.AVAILABLE) {
|
||||
type = Tp.ConnectionPresenceType.AVAILABLE;
|
||||
status = "available";
|
||||
}
|
||||
else if (session_status == GnomeSession.PresenceStatus.BUSY) {
|
||||
type = Tp.ConnectionPresenceType.BUSY;
|
||||
status = "busy";
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
|
||||
this._account_mgr.set_all_requested_presences(type, status, msg);
|
||||
}
|
||||
};
|
@ -1,6 +1,7 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const DBus = imports.dbus;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
@ -11,6 +12,7 @@ const Tpl = imports.gi.TelepathyLogger;
|
||||
const Tp = imports.gi.TelepathyGLib;
|
||||
|
||||
const History = imports.misc.history;
|
||||
const Params = imports.misc.params;
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
|
||||
@ -43,11 +45,16 @@ let contactFeatures = [Tp.ContactFeature.ALIAS,
|
||||
|
||||
function makeMessageFromTpMessage(tpMessage, direction) {
|
||||
let [text, flags] = tpMessage.to_text();
|
||||
|
||||
let timestamp = tpMessage.get_sent_timestamp();
|
||||
if (timestamp == 0)
|
||||
timestamp = tpMessage.get_received_timestamp();
|
||||
|
||||
return {
|
||||
messageType: tpMessage.get_message_type(),
|
||||
text: text,
|
||||
sender: tpMessage.sender.alias,
|
||||
timestamp: tpMessage.get_received_timestamp(),
|
||||
timestamp: timestamp,
|
||||
direction: direction
|
||||
};
|
||||
}
|
||||
@ -80,8 +87,8 @@ Client.prototype = {
|
||||
// channel matching its filters is detected.
|
||||
// The second argument, recover, means _observeChannels will be run
|
||||
// for any existing channel as well.
|
||||
let dbus = Tp.DBusDaemon.dup();
|
||||
this._tpClient = new Shell.TpClient({ 'dbus_daemon': dbus,
|
||||
this._accountManager = Tp.AccountManager.dup();
|
||||
this._tpClient = new Shell.TpClient({ 'account-manager': this._accountManager,
|
||||
'name': 'GnomeShell',
|
||||
'uniquify-name': true })
|
||||
this._tpClient.set_observe_channels_func(
|
||||
@ -91,6 +98,11 @@ Client.prototype = {
|
||||
this._tpClient.set_handle_channels_func(
|
||||
Lang.bind(this, this._handleChannels));
|
||||
|
||||
// Workaround for gjs not supporting GPtrArray in signals.
|
||||
// See BGO bug #653941 for context.
|
||||
this._tpClient.set_contact_list_changed_func(
|
||||
Lang.bind(this, this._contactListChanged));
|
||||
|
||||
// Allow other clients (such as Empathy) to pre-empt our channels if
|
||||
// needed
|
||||
this._tpClient.set_delegated_channels_callback(
|
||||
@ -101,6 +113,22 @@ Client.prototype = {
|
||||
} catch (e) {
|
||||
throw new Error('Couldn\'t register Telepathy client. Error: \n' + e);
|
||||
}
|
||||
|
||||
|
||||
// Watch subscription requests and connection errors
|
||||
this._subscriptionSource = null;
|
||||
this._accountSource = null;
|
||||
let factory = this._accountManager.get_factory();
|
||||
factory.add_account_features([Tp.Account.get_feature_quark_connection()]);
|
||||
factory.add_connection_features([Tp.Connection.get_feature_quark_contact_list()]);
|
||||
factory.add_contact_features([Tp.ContactFeature.SUBSCRIPTION_STATES,
|
||||
Tp.ContactFeature.ALIAS,
|
||||
Tp.ContactFeature.AVATAR_DATA]);
|
||||
|
||||
this._accountManager.connect('account-validity-changed',
|
||||
Lang.bind(this, this._accountValidityChanged));
|
||||
|
||||
this._accountManager.prepare_async(null, Lang.bind(this, this._accountManagerPrepared));
|
||||
},
|
||||
|
||||
_observeChannels: function(observer, account, conn, channels,
|
||||
@ -225,7 +253,8 @@ Client.prototype = {
|
||||
|
||||
// FIXME: We don't have a 'chat room' icon (bgo #653737) use
|
||||
// system-users for now as Empathy does.
|
||||
let source = new ApproverSource(dispatchOp, _("Invitation"), 'system-users');
|
||||
let source = new ApproverSource(dispatchOp, _("Invitation"),
|
||||
Shell.util_icon_from_string('system-users'));
|
||||
Main.messageTray.add(source);
|
||||
|
||||
let notif = new RoomInviteNotification(source, dispatchOp, channel, contacts[0]);
|
||||
@ -243,6 +272,8 @@ Client.prototype = {
|
||||
else if (chanType == Tp.IFACE_CHANNEL_TYPE_STREAMED_MEDIA ||
|
||||
chanType == 'org.freedesktop.Telepathy.Channel.Type.Call.DRAFT')
|
||||
this._approveCall(account, conn, channel, dispatchOp, context);
|
||||
else if (chanType == Tp.IFACE_CHANNEL_TYPE_FILE_TRANSFER)
|
||||
this._approveFileTransfer(account, conn, channel, dispatchOp, context);
|
||||
},
|
||||
|
||||
_approveTextChannel: function(account, conn, channel, dispatchOp, context) {
|
||||
@ -288,7 +319,9 @@ Client.prototype = {
|
||||
isVideo = true;
|
||||
|
||||
// We got the TpContact
|
||||
let source = new ApproverSource(dispatchOp, _("Call"), isVideo ? 'camera-web' : 'audio-input-microphone');
|
||||
let source = new ApproverSource(dispatchOp, _("Call"), isVideo ?
|
||||
Shell.util_icon_from_string('camera-web') :
|
||||
Shell.util_icon_from_string('audio-input-microphone'));
|
||||
Main.messageTray.add(source);
|
||||
|
||||
let notif = new AudioVideoNotification(source, dispatchOp, channel, contacts[0], isVideo);
|
||||
@ -296,9 +329,141 @@ Client.prototype = {
|
||||
context.accept();
|
||||
},
|
||||
|
||||
_approveFileTransfer: function(account, conn, channel, dispatchOp, context) {
|
||||
let [targetHandle, targetHandleType] = channel.get_handle();
|
||||
|
||||
Shell.get_tp_contacts(conn, [targetHandle],
|
||||
contactFeatures,
|
||||
Lang.bind(this, this._createFileTransferSource, channel, context, dispatchOp));
|
||||
},
|
||||
|
||||
_createFileTransferSource: function(connection, contacts, failed, channel, context, dispatchOp) {
|
||||
if (contacts.length < 1) {
|
||||
Shell.decline_dispatch_op(context, 'Failed to get file sender');
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the icon of the file being transferred
|
||||
let gicon = Gio.content_type_get_icon(channel.get_mime_type());
|
||||
|
||||
// We got the TpContact
|
||||
let source = new ApproverSource(dispatchOp, _("File Transfer"), gicon);
|
||||
Main.messageTray.add(source);
|
||||
|
||||
let notif = new FileTransferNotification(source, dispatchOp, channel, contacts[0]);
|
||||
source.notify(notif);
|
||||
context.accept();
|
||||
},
|
||||
|
||||
_delegatedChannelsCb: function(client, channels) {
|
||||
// Nothing to do as we don't make a distinction between observed and
|
||||
// handled channels.
|
||||
},
|
||||
|
||||
_accountManagerPrepared: function(am, result) {
|
||||
am.prepare_finish(result);
|
||||
|
||||
let accounts = am.get_valid_accounts();
|
||||
for (let i = 0; i < accounts.length; i++) {
|
||||
this._accountValidityChanged(am, accounts[i], true);
|
||||
}
|
||||
},
|
||||
|
||||
_accountValidityChanged: function(am, account, valid) {
|
||||
if (!valid)
|
||||
return;
|
||||
|
||||
// It would be better to connect to "status-changed" but we cannot.
|
||||
// See discussion in https://bugzilla.gnome.org/show_bug.cgi?id=654159
|
||||
account.connect("notify::connection-status",
|
||||
Lang.bind(this, this._accountConnectionStatusNotifyCb));
|
||||
|
||||
account.connect('notify::connection',
|
||||
Lang.bind(this, this._connectionChanged));
|
||||
this._connectionChanged(account);
|
||||
},
|
||||
|
||||
_connectionChanged: function(account) {
|
||||
let conn = account.get_connection();
|
||||
if (conn == null)
|
||||
return;
|
||||
|
||||
this._tpClient.grab_contact_list_changed(conn);
|
||||
if (conn.get_contact_list_state() == Tp.ContactListState.SUCCESS) {
|
||||
this._contactListChanged(conn, conn.dup_contact_list(), []);
|
||||
}
|
||||
},
|
||||
|
||||
_contactListChanged: function(conn, added, removed) {
|
||||
for (let i = 0; i < added.length; i++) {
|
||||
let contact = added[i];
|
||||
|
||||
contact.connect('subscription-states-changed',
|
||||
Lang.bind(this, this._subscriptionStateChanged));
|
||||
this._subscriptionStateChanged(contact);
|
||||
}
|
||||
},
|
||||
|
||||
_subscriptionStateChanged: function(contact) {
|
||||
if (contact.get_publish_state() != Tp.SubscriptionState.ASK)
|
||||
return;
|
||||
|
||||
/* Implicitly accept publish requests if contact is already subscribed */
|
||||
if (contact.get_subscribe_state() == Tp.SubscriptionState.YES ||
|
||||
contact.get_subscribe_state() == Tp.SubscriptionState.ASK) {
|
||||
|
||||
contact.authorize_publication_async(function(src, result) {
|
||||
src.authorize_publication_finish(result)});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Display notification to ask user to accept/reject request */
|
||||
let source = this._ensureSubscriptionSource();
|
||||
Main.messageTray.add(source);
|
||||
|
||||
let notif = new SubscriptionRequestNotification(source, contact);
|
||||
source.notify(notif);
|
||||
},
|
||||
|
||||
_ensureSubscriptionSource: function() {
|
||||
if (this._subscriptionSource == null) {
|
||||
this._subscriptionSource = new MultiNotificationSource(
|
||||
_("Subscription request"), 'gtk-dialog-question');
|
||||
this._subscriptionSource.connect('destroy', Lang.bind(this, function () {
|
||||
this._subscriptionSource = null;
|
||||
}));
|
||||
}
|
||||
|
||||
return this._subscriptionSource;
|
||||
},
|
||||
|
||||
_accountConnectionStatusNotifyCb: function(account) {
|
||||
let connectionError = account.connection_error;
|
||||
|
||||
if (account.connection_status != Tp.ConnectionStatus.DISCONNECTED ||
|
||||
connectionError == Tp.error_get_dbus_name(Tp.Error.CANCELLED)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Display notification that account failed to connect */
|
||||
let source = this._ensureAccountSource();
|
||||
Main.messageTray.add(source);
|
||||
|
||||
let notif = new AccountNotification(source, account, connectionError);
|
||||
source.notify(notif);
|
||||
},
|
||||
|
||||
_ensureAccountSource: function() {
|
||||
if (this._accountSource == null) {
|
||||
this._accountSource = new MultiNotificationSource(
|
||||
_("Connection error"), 'gtk-dialog-error');
|
||||
this._accountSource.connect('destroy', Lang.bind(this, function () {
|
||||
this._accountSource = null;
|
||||
}));
|
||||
}
|
||||
|
||||
return this._accountSource;
|
||||
}
|
||||
};
|
||||
|
||||
@ -326,6 +491,7 @@ ChatSource.prototype = {
|
||||
|
||||
this._notification = new ChatNotification(this);
|
||||
this._notification.setUrgency(MessageTray.Urgency.HIGH);
|
||||
this._notifyTimeoutId = 0;
|
||||
|
||||
// We ack messages when the message box is collapsed if user has
|
||||
// interacted with it before and so read the messages:
|
||||
@ -487,7 +653,22 @@ ChatSource.prototype = {
|
||||
|
||||
message = makeMessageFromTpMessage(message, NotificationDirection.RECEIVED);
|
||||
this._notification.appendMessage(message);
|
||||
this.notify();
|
||||
|
||||
// Wait a bit before notifying for the received message, a handler
|
||||
// could ack it in the meantime.
|
||||
if (this._notifyTimeoutId != 0)
|
||||
Mainloop.source_remove(this._notifyTimeoutId);
|
||||
this._notifyTimeoutId = Mainloop.timeout_add(500,
|
||||
Lang.bind(this, this._notifyTimeout));
|
||||
},
|
||||
|
||||
_notifyTimeout: function() {
|
||||
if (this._pendingMessages.length != 0)
|
||||
this.notify();
|
||||
|
||||
this._notifyTimeoutId = 0;
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
// This is called for both messages we send from
|
||||
@ -576,13 +757,10 @@ ChatSource.prototype = {
|
||||
},
|
||||
|
||||
_ackMessages: function() {
|
||||
if (this._pendingMessages.length == 0)
|
||||
return;
|
||||
|
||||
// Don't clear our messages here, tp-glib will send a
|
||||
// 'pending-message-removed' for each one.
|
||||
this._channel.ack_messages_async(this._pendingMessages, Lang.bind(this, function(src, result) {
|
||||
this._channel.ack_messages_finish(result);}));
|
||||
this._channel.ack_all_pending_messages_async(Lang.bind(this, function(src, result) {
|
||||
this._channel.ack_all_pending_messages_finish(result);}));
|
||||
},
|
||||
|
||||
_summaryItemClicked: function(source, button) {
|
||||
@ -623,6 +801,8 @@ ChatNotification.prototype = {
|
||||
|
||||
this._oldMaxScrollAdjustment = 0;
|
||||
this._createScrollArea();
|
||||
this._lastGroup = null;
|
||||
this._lastGroupActor = null;
|
||||
|
||||
this._scrollArea.vscroll.adjustment.connect('changed', Lang.bind(this, function(adjustment) {
|
||||
let currentValue = adjustment.value + adjustment.page_size;
|
||||
@ -650,12 +830,10 @@ ChatNotification.prototype = {
|
||||
* @noTimestamp: Whether to add a timestamp. If %true, no timestamp
|
||||
* will be added, regardless of the difference since the
|
||||
* last timestamp
|
||||
* @styles: A list of CSS class names.
|
||||
*/
|
||||
appendMessage: function(message, noTimestamp, styles) {
|
||||
appendMessage: function(message, noTimestamp) {
|
||||
let messageBody = GLib.markup_escape_text(message.text, -1);
|
||||
styles = styles || [];
|
||||
styles.push(message.direction);
|
||||
let styles = [message.direction];
|
||||
|
||||
if (message.messageType == Tp.ChannelTextMessageType.ACTION) {
|
||||
let senderAlias = GLib.markup_escape_text(message.sender, -1);
|
||||
@ -668,7 +846,14 @@ ChatNotification.prototype = {
|
||||
bannerMarkup: true });
|
||||
}
|
||||
|
||||
this._append(messageBody, styles, message.timestamp, noTimestamp);
|
||||
let group = (message.direction == NotificationDirection.RECEIVED ?
|
||||
'received' : 'sent');
|
||||
|
||||
this._append({ body: messageBody,
|
||||
group: group,
|
||||
styles: styles,
|
||||
timestamp: message.timestamp,
|
||||
noTimestamp: noTimestamp });
|
||||
},
|
||||
|
||||
_filterMessages: function() {
|
||||
@ -694,24 +879,65 @@ ChatNotification.prototype = {
|
||||
for (let i = 0; i < expired.length; i++)
|
||||
expired[i].actor.destroy();
|
||||
}
|
||||
|
||||
let groups = this._contentArea.get_children();
|
||||
for (let i = 0; i < groups.length; i ++) {
|
||||
let group = groups[i];
|
||||
if (group.get_children().length == 0)
|
||||
group.destroy();
|
||||
}
|
||||
},
|
||||
|
||||
_append: function(text, styles, timestamp, noTimestamp) {
|
||||
/**
|
||||
* _append:
|
||||
* @props: An object with the properties:
|
||||
* body: The text of the message.
|
||||
* group: The group of the message, one of:
|
||||
* 'received', 'sent', 'meta'.
|
||||
* styles: Style class names for the message to have.
|
||||
* timestamp: The timestamp of the message.
|
||||
* noTimestamp: suppress timestamp signal?
|
||||
* childProps: props to add the actor with.
|
||||
*/
|
||||
_append: function(props) {
|
||||
let currentTime = (Date.now() / 1000);
|
||||
if (!timestamp)
|
||||
timestamp = currentTime;
|
||||
props = Params.parse(props, { body: null,
|
||||
group: null,
|
||||
styles: [],
|
||||
timestamp: currentTime,
|
||||
noTimestamp: false,
|
||||
childProps: null });
|
||||
|
||||
// Reset the old message timeout
|
||||
if (this._timestampTimeoutId)
|
||||
Mainloop.source_remove(this._timestampTimeoutId);
|
||||
|
||||
let body = this.addBody(text, true);
|
||||
let highlighter = new MessageTray.URLHighlighter(props.body,
|
||||
true, // line wrap?
|
||||
true); // allow markup?
|
||||
|
||||
let body = highlighter.actor;
|
||||
|
||||
let styles = props.styles;
|
||||
for (let i = 0; i < styles.length; i ++)
|
||||
body.add_style_class_name(styles[i]);
|
||||
|
||||
this._history.unshift({ actor: body, time: timestamp, realMessage: true });
|
||||
let group = props.group;
|
||||
if (group != this._lastGroup) {
|
||||
let style = 'chat-group-' + group;
|
||||
this._lastGroup = group;
|
||||
this._lastGroupActor = new St.BoxLayout({ style_class: style,
|
||||
vertical: true });
|
||||
this.addActor(this._lastGroupActor);
|
||||
}
|
||||
|
||||
if (!noTimestamp) {
|
||||
this._lastGroupActor.add(body, props.childProps);
|
||||
|
||||
let timestamp = props.timestamp;
|
||||
this._history.unshift({ actor: body, time: timestamp,
|
||||
realMessage: group != 'meta' });
|
||||
|
||||
if (!props.noTimestamp) {
|
||||
if (timestamp < currentTime - SCROLLBACK_IMMEDIATE_TIME)
|
||||
this.appendTimestamp();
|
||||
else
|
||||
@ -738,18 +964,18 @@ ChatNotification.prototype = {
|
||||
If applicable, replace %X with a strftime format valid for your
|
||||
locale, without seconds. */
|
||||
// xgettext:no-c-format
|
||||
format = _("Sent at %X on %A");
|
||||
|
||||
// FIXME: The next two are stolen from calendar.js with the comment to avoid
|
||||
// a string-freeze break. They should be replaced with better strings
|
||||
// with 'Sent at', appropriate context and appropriate translator comment.
|
||||
format = _("Sent at <b>%X</b> on <b>%A</b>");
|
||||
|
||||
} else if (date.getYear() == now.getYear()) {
|
||||
/* Translators: Shown on calendar heading when selected day occurs on current year */
|
||||
format = C_("calendar heading", "%A, %B %d");
|
||||
/* Translators: this is a time format in the style of "Wednesday, May 25",
|
||||
shown when you get a chat message in the same year. */
|
||||
// xgettext:no-c-format
|
||||
format = _("Sent on <b>%A</b>, <b>%B %d</b>");
|
||||
} else {
|
||||
/* Translators: Shown on calendar heading when selected day occurs on different year */
|
||||
format = C_("calendar heading", "%A, %B %d, %Y");
|
||||
/* Translators: this is a time format in the style of "Wednesday, May 25, 2012",
|
||||
shown when you get a chat message in a different year. */
|
||||
// xgettext:no-c-format
|
||||
format = _("Sent on <b>%A</b>, <b>%B %d</b>, %Y");
|
||||
}
|
||||
|
||||
return date.toLocaleFormat(format);
|
||||
@ -759,11 +985,13 @@ ChatNotification.prototype = {
|
||||
let lastMessageTime = this._history[0].time;
|
||||
let lastMessageDate = new Date(lastMessageTime * 1000);
|
||||
|
||||
let timeLabel = this.addBody(this._formatTimestamp(lastMessageDate), false, { expand: true, x_fill: false, x_align: St.Align.END });
|
||||
timeLabel.add_style_class_name('chat-meta-message');
|
||||
this._history.unshift({ actor: timeLabel, time: lastMessageTime, realMessage: false });
|
||||
|
||||
this._timestampTimeoutId = 0;
|
||||
let timeLabel = this._append({ body: this._formatTimestamp(lastMessageDate),
|
||||
group: 'meta',
|
||||
styles: ['chat-meta-message'],
|
||||
childProps: { expand: true, x_fill: false,
|
||||
x_align: St.Align.END },
|
||||
noTimestamp: true,
|
||||
timestamp: lastMessageTime });
|
||||
|
||||
this._filterMessages();
|
||||
|
||||
@ -775,9 +1003,10 @@ ChatNotification.prototype = {
|
||||
this.update(text, null, { customContent: true, titleMarkup: true });
|
||||
else
|
||||
this.update(this.source.title, null, { customContent: true });
|
||||
let label = this.addBody(text, true);
|
||||
label.add_style_class_name('chat-meta-message');
|
||||
this._history.unshift({ actor: label, time: (Date.now() / 1000), realMessage: false});
|
||||
|
||||
let label = this._append({ body: text,
|
||||
group: 'meta',
|
||||
styles: ['chat-meta-message'] });
|
||||
|
||||
this._filterMessages();
|
||||
},
|
||||
@ -789,9 +1018,11 @@ ChatNotification.prototype = {
|
||||
/* Translators: this is the other person changing their old IM name to their new
|
||||
IM name. */
|
||||
let message = '<i>' + _("%s is now known as %s").format(oldAlias, newAlias) + '</i>';
|
||||
let label = this.addBody(message, true);
|
||||
label.add_style_class_name('chat-meta-message');
|
||||
this._history.unshift({ actor: label, time: (Date.now() / 1000), realMessage: false });
|
||||
|
||||
let label = this._append({ body: text,
|
||||
group: 'meta',
|
||||
styles: ['chat-meta-message'] });
|
||||
|
||||
this.update(newAlias, null, { customContent: true });
|
||||
|
||||
this._filterMessages();
|
||||
@ -844,17 +1075,17 @@ ChatNotification.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
function ApproverSource(dispatchOp, text, icon) {
|
||||
this._init(dispatchOp, text, icon);
|
||||
function ApproverSource(dispatchOp, text, gicon) {
|
||||
this._init(dispatchOp, text, gicon);
|
||||
}
|
||||
|
||||
ApproverSource.prototype = {
|
||||
__proto__: MessageTray.Source.prototype,
|
||||
|
||||
_init: function(dispatchOp, text, icon) {
|
||||
_init: function(dispatchOp, text, gicon) {
|
||||
MessageTray.Source.prototype._init.call(this, text);
|
||||
|
||||
this._icon = icon;
|
||||
this._gicon = gicon;
|
||||
this._setSummaryIcon(this.createNotificationIcon());
|
||||
|
||||
this._dispatchOp = dispatchOp;
|
||||
@ -877,7 +1108,7 @@ ApproverSource.prototype = {
|
||||
},
|
||||
|
||||
createNotificationIcon: function() {
|
||||
return new St.Icon({ icon_name: this._icon,
|
||||
return new St.Icon({ gicon: this._gicon,
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: this.ICON_SIZE });
|
||||
}
|
||||
@ -971,3 +1202,321 @@ AudioVideoNotification.prototype = {
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
// File Transfer
|
||||
function FileTransferNotification(source, dispatchOp, channel, contact) {
|
||||
this._init(source, dispatchOp, channel, contact);
|
||||
}
|
||||
|
||||
FileTransferNotification.prototype = {
|
||||
__proto__: MessageTray.Notification.prototype,
|
||||
|
||||
_init: function(source, dispatchOp, channel, contact) {
|
||||
MessageTray.Notification.prototype._init.call(this,
|
||||
source,
|
||||
/* To translators: The first parameter is
|
||||
* the contact's alias and the second one is the
|
||||
* file name. The string will be something
|
||||
* like: "Alice is sending you test.ogg"
|
||||
*/
|
||||
_("%s is sending you %s").format(contact.get_alias(),
|
||||
channel.get_filename()),
|
||||
null,
|
||||
{ customContent: true });
|
||||
this.setResident(true);
|
||||
|
||||
this.addButton('decline', _("Decline"));
|
||||
this.addButton('accept', _("Accept"));
|
||||
|
||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||
switch (action) {
|
||||
case 'decline':
|
||||
dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE,
|
||||
'', function(src, result) {
|
||||
src.leave_channels_finish(result)});
|
||||
break;
|
||||
case 'accept':
|
||||
dispatchOp.handle_with_time_async('', global.get_current_time(),
|
||||
function(src, result) {
|
||||
src.handle_with_time_finish(result)});
|
||||
break;
|
||||
}
|
||||
this.destroy();
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
// A notification source that can embed multiple notifications
|
||||
function MultiNotificationSource(title, icon) {
|
||||
this._init(title, icon);
|
||||
}
|
||||
|
||||
MultiNotificationSource.prototype = {
|
||||
__proto__: MessageTray.Source.prototype,
|
||||
|
||||
_init: function(title, icon) {
|
||||
MessageTray.Source.prototype._init.call(this, title);
|
||||
|
||||
this._icon = icon;
|
||||
this._setSummaryIcon(this.createNotificationIcon());
|
||||
this._nbNotifications = 0;
|
||||
},
|
||||
|
||||
notify: function(notification) {
|
||||
MessageTray.Source.prototype.notify.call(this, notification);
|
||||
|
||||
this._nbNotifications += 1;
|
||||
|
||||
// Display the source while there is at least one notification
|
||||
notification.connect('destroy', Lang.bind(this, function () {
|
||||
this._nbNotifications -= 1;
|
||||
|
||||
if (this._nbNotifications == 0)
|
||||
this.destroy();
|
||||
}));
|
||||
},
|
||||
|
||||
createNotificationIcon: function() {
|
||||
return new St.Icon({ gicon: Shell.util_icon_from_string(this._icon),
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: this.ICON_SIZE });
|
||||
}
|
||||
};
|
||||
|
||||
// Subscription request
|
||||
function SubscriptionRequestNotification(source, contact) {
|
||||
this._init(source, contact);
|
||||
}
|
||||
|
||||
SubscriptionRequestNotification.prototype = {
|
||||
__proto__: MessageTray.Notification.prototype,
|
||||
|
||||
_init: function(source, contact) {
|
||||
MessageTray.Notification.prototype._init.call(this, source,
|
||||
/* To translators: The parameter is the contact's alias */
|
||||
_("%s would like permission to see when you are online").format(contact.get_alias()),
|
||||
null, { customContent: true });
|
||||
|
||||
this._contact = contact;
|
||||
this._connection = contact.get_connection();
|
||||
|
||||
let layout = new St.BoxLayout({ vertical: false });
|
||||
|
||||
// Display avatar
|
||||
let iconBox = new St.Bin({ style_class: 'avatar-box' });
|
||||
iconBox._size = 48;
|
||||
|
||||
let textureCache = St.TextureCache.get_default();
|
||||
let file = contact.get_avatar_file();
|
||||
|
||||
if (file) {
|
||||
let uri = file.get_uri();
|
||||
iconBox.child = textureCache.load_uri_async(uri, iconBox._size, iconBox._size);
|
||||
}
|
||||
else {
|
||||
iconBox.child = new St.Icon({ icon_name: 'avatar-default',
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: iconBox._size });
|
||||
}
|
||||
|
||||
layout.add(iconBox);
|
||||
|
||||
// subscription request message
|
||||
let label = new St.Label({ style_class: 'subscription-message',
|
||||
text: contact.get_publish_request() });
|
||||
|
||||
layout.add(label);
|
||||
|
||||
this.addActor(layout);
|
||||
|
||||
this.addButton('decline', _("Decline"));
|
||||
this.addButton('accept', _("Accept"));
|
||||
|
||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||
switch (action) {
|
||||
case 'decline':
|
||||
contact.remove_async(function(src, result) {
|
||||
src.remove_finish(result)});
|
||||
break;
|
||||
case 'accept':
|
||||
// Authorize the contact and request to see his status as well
|
||||
contact.authorize_publication_async(function(src, result) {
|
||||
src.authorize_publication_finish(result)});
|
||||
|
||||
contact.request_subscription_async('', function(src, result) {
|
||||
src.request_subscription_finish(result)});
|
||||
break;
|
||||
}
|
||||
|
||||
// rely on _subscriptionStatesChangedCb to destroy the
|
||||
// notification
|
||||
}));
|
||||
|
||||
this._changedId = contact.connect('subscription-states-changed',
|
||||
Lang.bind(this, this._subscriptionStatesChangedCb));
|
||||
this._invalidatedId = this._connection.connect('invalidated',
|
||||
Lang.bind(this, this.destroy));
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
if (this._changedId != 0) {
|
||||
this._contact.disconnect(this._changedId);
|
||||
this._changedId = 0;
|
||||
}
|
||||
|
||||
if (this._invalidatedId != 0) {
|
||||
this._connection.disconnect(this._invalidatedId);
|
||||
this._invalidatedId = 0;
|
||||
}
|
||||
|
||||
MessageTray.Notification.prototype.destroy.call(this);
|
||||
},
|
||||
|
||||
_subscriptionStatesChangedCb: function(contact, subscribe, publish, msg) {
|
||||
// Destroy the notification if the subscription request has been
|
||||
// answered
|
||||
if (publish != Tp.SubscriptionState.ASK)
|
||||
this.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function AccountNotification(source, account, connectionError) {
|
||||
this._init(source, account, connectionError);
|
||||
}
|
||||
|
||||
// Messages from empathy/libempathy/empathy-utils.c
|
||||
// create_errors_to_message_hash()
|
||||
|
||||
/* Translator note: these should be the same messages that are
|
||||
* used in Empathy, so just copy and paste from there. */
|
||||
let _connectionErrorMessages = {};
|
||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.NETWORK_ERROR)]
|
||||
= _("Network error");
|
||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.AUTHENTICATION_FAILED)]
|
||||
= _("Authentication failed");
|
||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.ENCRYPTION_ERROR)]
|
||||
= _("Encryption error");
|
||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_NOT_PROVIDED)]
|
||||
= _("Certificate not provided");
|
||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_UNTRUSTED)]
|
||||
= _("Certificate untrusted");
|
||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_EXPIRED)]
|
||||
= _("Certificate expired");
|
||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_NOT_ACTIVATED)]
|
||||
= _("Certificate not activated");
|
||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_HOSTNAME_MISMATCH)]
|
||||
= _("Certificate hostname mismatch");
|
||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_FINGERPRINT_MISMATCH)]
|
||||
= _("Certificate fingerprint mismatch");
|
||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_SELF_SIGNED)]
|
||||
= _("Certificate self-signed");
|
||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CANCELLED)]
|
||||
= _("Status is set to offline");
|
||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.ENCRYPTION_NOT_AVAILABLE)]
|
||||
= _("Encryption is not available");
|
||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_INVALID)]
|
||||
= _("Certificate is invalid");
|
||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CONNECTION_REFUSED)]
|
||||
= _("Connection has been refused");
|
||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CONNECTION_FAILED)]
|
||||
= _("Connection can't be established");
|
||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CONNECTION_LOST)]
|
||||
= _("Connection has been lost");
|
||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.ALREADY_CONNECTED)]
|
||||
= _("This resource is already connected to the server");
|
||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CONNECTION_REPLACED)]
|
||||
= _("Connection has been replaced by a new connection using the "
|
||||
+ "same resource");
|
||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.REGISTRATION_EXISTS)]
|
||||
= _("The account already exists on the server");
|
||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.SERVICE_BUSY)]
|
||||
= _("Server is currently too busy to handle the connection");
|
||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_REVOKED)]
|
||||
= _("Certificate has been revoked");
|
||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_INSECURE)]
|
||||
= _("Certificate uses an insecure cipher algorithm or is "
|
||||
+ "cryptographically weak");
|
||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_LIMIT_EXCEEDED)]
|
||||
= _("The length of the server certificate, or the depth of the "
|
||||
+ "server certificate chain, exceed the limits imposed by the "
|
||||
+ "cryptography library");
|
||||
|
||||
AccountNotification.prototype = {
|
||||
__proto__: MessageTray.Notification.prototype,
|
||||
|
||||
_init: function(source, account, connectionError) {
|
||||
MessageTray.Notification.prototype._init.call(this, source,
|
||||
/* translators: argument is the account name, like
|
||||
* name@jabber.org for example. */
|
||||
_("Connection to %s failed").format(account.get_display_name()),
|
||||
null, { customContent: true });
|
||||
|
||||
let message;
|
||||
if (connectionError in _connectionErrorMessages) {
|
||||
message = _connectionErrorMessages[connectionError];
|
||||
} else {
|
||||
message = _("Unknown reason");
|
||||
}
|
||||
|
||||
this._account = account;
|
||||
|
||||
this.addBody(message);
|
||||
|
||||
this.addButton('reconnect', _("Reconnect"));
|
||||
this.addButton('edit', _("Edit account"));
|
||||
|
||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||
switch (action) {
|
||||
case 'reconnect':
|
||||
// If it fails again, a new notification should pop up with the
|
||||
// new error.
|
||||
account.reconnect_async(null, null);
|
||||
break;
|
||||
case 'edit':
|
||||
let cmd = '/usr/bin/empathy-accounts'
|
||||
+ ' --select-account=%s'
|
||||
.format(account.get_path_suffix());
|
||||
let app_info = Gio.app_info_create_from_commandline(cmd, null, 0,
|
||||
null);
|
||||
app_info.launch([], null, null);
|
||||
break;
|
||||
}
|
||||
this.destroy();
|
||||
}));
|
||||
|
||||
this._enabledId = account.connect('notify::enabled',
|
||||
Lang.bind(this, function() {
|
||||
if (!account.is_enabled())
|
||||
this.destroy();
|
||||
}));
|
||||
|
||||
this._invalidatedId = account.connect('invalidated',
|
||||
Lang.bind(this, this.destroy));
|
||||
|
||||
this._connectionStatusId = account.connect('notify::connection-status',
|
||||
Lang.bind(this, function() {
|
||||
if (account.connection_status != Tp.ConnectionStatus.DISCONNECTED)
|
||||
this.destroy();
|
||||
}));
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
if (this._enabledId != 0) {
|
||||
this._account.disconnect(this._enabledId);
|
||||
this._enabledId = 0;
|
||||
}
|
||||
|
||||
if (this._invalidatedId != 0) {
|
||||
this._account.disconnect(this._invalidatedId);
|
||||
this._invalidatedId = 0;
|
||||
}
|
||||
|
||||
if (this._connectionStatusId != 0) {
|
||||
this._account.disconnect(this._connectionStatusId);
|
||||
this._connectionStatusId = 0;
|
||||
}
|
||||
|
||||
MessageTray.Notification.prototype.destroy.call(this);
|
||||
}
|
||||
};
|
||||
|
646
js/ui/userMenu.js
Normal file
646
js/ui/userMenu.js
Normal file
@ -0,0 +1,646 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const AccountsService = imports.gi.AccountsService;
|
||||
const DBus = imports.dbus;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
const Tp = imports.gi.TelepathyGLib;
|
||||
const UPowerGlib = imports.gi.UPowerGlib;
|
||||
|
||||
const GnomeSession = imports.misc.gnomeSession;
|
||||
const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const ScreenSaver = imports.misc.screenSaver;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
|
||||
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 WRAP_WIDTH = 150;
|
||||
const DIALOG_ICON_SIZE = 64;
|
||||
|
||||
const IMStatus = {
|
||||
AVAILABLE: 0,
|
||||
BUSY: 1,
|
||||
HIDDEN: 2,
|
||||
AWAY: 3,
|
||||
IDLE: 4,
|
||||
OFFLINE: 5,
|
||||
LAST: 6
|
||||
};
|
||||
|
||||
// Adapted from gdm/gui/user-switch-applet/applet.c
|
||||
//
|
||||
// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
|
||||
// Copyright (C) 2008,2009 Red Hat, Inc.
|
||||
|
||||
|
||||
function IMStatusItem(label, iconName) {
|
||||
this._init(label, iconName);
|
||||
}
|
||||
|
||||
IMStatusItem.prototype = {
|
||||
__proto__: PopupMenu.PopupBaseMenuItem.prototype,
|
||||
|
||||
_init: function(label, iconName) {
|
||||
PopupMenu.PopupBaseMenuItem.prototype._init.call(this);
|
||||
|
||||
this.actor.add_style_class_name('status-chooser-status-item');
|
||||
|
||||
this._icon = new St.Icon({ style_class: 'popup-menu-icon' });
|
||||
this.addActor(this._icon);
|
||||
|
||||
if (iconName)
|
||||
this._icon.icon_name = iconName;
|
||||
|
||||
this.label = new St.Label({ text: label });
|
||||
this.addActor(this.label);
|
||||
}
|
||||
};
|
||||
|
||||
function IMUserNameItem() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
IMUserNameItem.prototype = {
|
||||
__proto__: PopupMenu.PopupBaseMenuItem.prototype,
|
||||
|
||||
_init: function() {
|
||||
PopupMenu.PopupBaseMenuItem.prototype._init.call(this,
|
||||
{ reactive: false,
|
||||
style_class: 'status-chooser-user-name' });
|
||||
|
||||
this._wrapper = new Shell.GenericContainer();
|
||||
this._wrapper.connect('get-preferred-width',
|
||||
Lang.bind(this, this._wrapperGetPreferredWidth));
|
||||
this._wrapper.connect('get-preferred-height',
|
||||
Lang.bind(this, this._wrapperGetPreferredHeight));
|
||||
this._wrapper.connect('allocate',
|
||||
Lang.bind(this, this._wrapperAllocate));
|
||||
this.addActor(this._wrapper, { expand: true, span: -1 });
|
||||
|
||||
this.label = new St.Label();
|
||||
this.label.clutter_text.set_line_wrap(true);
|
||||
this._wrapper.add_actor(this.label);
|
||||
},
|
||||
|
||||
_wrapperGetPreferredWidth: function(actor, forHeight, alloc) {
|
||||
[alloc.min_size, alloc.natural_size] = this.label.get_preferred_width(-1);
|
||||
if (alloc.natural_size > WRAP_WIDTH)
|
||||
alloc.natural_size = WRAP_WIDTH;
|
||||
},
|
||||
|
||||
_wrapperGetPreferredHeight: function(actor, forWidth, alloc) {
|
||||
let minWidth, natWidth;
|
||||
[alloc.min_size, alloc.natural_size] = this.label.get_preferred_height(forWidth);
|
||||
[minWidth, natWidth] = this.label.get_preferred_width(-1);
|
||||
if (natWidth > WRAP_WIDTH) {
|
||||
alloc.min_size *= 2;
|
||||
alloc.natural_size *= 2;
|
||||
}
|
||||
},
|
||||
|
||||
_wrapperAllocate: function(actor, box, flags) {
|
||||
let availWidth = box.x2 - box.x1;
|
||||
let availHeight = box.y2 - box.y1;
|
||||
this.label.allocate(box, flags);
|
||||
}
|
||||
};
|
||||
|
||||
function IMStatusChooserItem() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
IMStatusChooserItem.prototype = {
|
||||
__proto__: PopupMenu.PopupBaseMenuItem.prototype,
|
||||
|
||||
_init: function() {
|
||||
PopupMenu.PopupBaseMenuItem.prototype._init.call (this,
|
||||
{ reactive: false,
|
||||
style_class: 'status-chooser' });
|
||||
|
||||
this._iconBin = new St.Button({ style_class: 'status-chooser-user-icon' });
|
||||
this.addActor(this._iconBin);
|
||||
|
||||
this._iconBin.connect('clicked', Lang.bind(this,
|
||||
function() {
|
||||
this.activate();
|
||||
}));
|
||||
|
||||
this._section = new PopupMenu.PopupMenuSection();
|
||||
this.addActor(this._section.actor);
|
||||
|
||||
this._name = new IMUserNameItem();
|
||||
this._section.addMenuItem(this._name);
|
||||
|
||||
this._combo = new PopupMenu.PopupComboBoxMenuItem({ style_class: 'status-chooser-combo' });
|
||||
this._section.addMenuItem(this._combo);
|
||||
|
||||
let item;
|
||||
|
||||
item = new IMStatusItem(_("Available"), 'user-available');
|
||||
this._combo.addMenuItem(item, IMStatus.AVAILABLE);
|
||||
|
||||
item = new IMStatusItem(_("Busy"), 'user-busy');
|
||||
this._combo.addMenuItem(item, IMStatus.BUSY);
|
||||
|
||||
item = new IMStatusItem(_("Hidden"), 'user-invisible');
|
||||
this._combo.addMenuItem(item, IMStatus.HIDDEN);
|
||||
|
||||
item = new IMStatusItem(_("Away"), 'user-away');
|
||||
this._combo.addMenuItem(item, IMStatus.AWAY);
|
||||
|
||||
item = new IMStatusItem(_("Idle"), 'user-idle');
|
||||
this._combo.addMenuItem(item, IMStatus.IDLE);
|
||||
|
||||
item = new IMStatusItem(_("Unavailable"), 'user-offline');
|
||||
this._combo.addMenuItem(item, IMStatus.OFFLINE);
|
||||
|
||||
this._combo.connect('active-item-changed',
|
||||
Lang.bind(this, this._changeIMStatus));
|
||||
|
||||
this._presence = new GnomeSession.Presence();
|
||||
this._presence.getStatus(Lang.bind(this, this._sessionStatusChanged));
|
||||
this._presence.connect('StatusChanged',
|
||||
Lang.bind(this, this._sessionStatusChanged));
|
||||
|
||||
this._previousPresence = undefined;
|
||||
|
||||
this._accountMgr = Tp.AccountManager.dup()
|
||||
this._accountMgr.connect('most-available-presence-changed',
|
||||
Lang.bind(this, this._IMStatusChanged));
|
||||
this._accountMgr.prepare_async(null, Lang.bind(this,
|
||||
function(mgr) {
|
||||
let [presence, s, msg] = mgr.get_most_available_presence();
|
||||
|
||||
this._previousPresence = presence;
|
||||
this._IMStatusChanged(mgr, presence, s, msg);
|
||||
}));
|
||||
|
||||
this._userManager = AccountsService.UserManager.get_default();
|
||||
|
||||
this._user = this._userManager.get_user(GLib.get_user_name());
|
||||
|
||||
this._userLoadedId = this._user.connect('notify::is-loaded',
|
||||
Lang.bind(this,
|
||||
this._updateUser));
|
||||
this._userChangedId = this._user.connect('changed',
|
||||
Lang.bind(this,
|
||||
this._updateUser));
|
||||
},
|
||||
|
||||
// Override getColumnWidths()/setColumnWidths() to make the item
|
||||
// independent from the overall column layout of the menu
|
||||
getColumnWidths: function() {
|
||||
return [];
|
||||
},
|
||||
|
||||
setColumnWidths: function(widths) {
|
||||
this._columnWidths = PopupMenu.PopupBaseMenuItem.prototype.getColumnWidths.call(this);
|
||||
let sectionWidths = this._section.getColumnWidths();
|
||||
this._section.setColumnWidths(sectionWidths);
|
||||
},
|
||||
|
||||
_updateUser: function() {
|
||||
let iconFile = null;
|
||||
if (this._user.is_loaded) {
|
||||
this._name.label.set_text(this._user.get_real_name());
|
||||
iconFile = this._user.get_icon_file();
|
||||
if (!GLib.file_test(iconFile, GLib.FileTest.EXISTS))
|
||||
iconFile = null;
|
||||
} else {
|
||||
this._name.label.set_text("");
|
||||
}
|
||||
|
||||
if (iconFile)
|
||||
this._setIconFromFile(iconFile);
|
||||
else
|
||||
this._setIconFromName('avatar-default');
|
||||
},
|
||||
|
||||
_setIconFromFile: function(iconFile) {
|
||||
this._iconBin.set_style('background-image: url("' + iconFile + '");');
|
||||
this._iconBin.child = null;
|
||||
},
|
||||
|
||||
_setIconFromName: function(iconName) {
|
||||
this._iconBin.set_style(null);
|
||||
|
||||
if (iconName != null) {
|
||||
let textureCache = St.TextureCache.get_default();
|
||||
let icon = textureCache.load_icon_name(this._iconBin.get_theme_node(),
|
||||
iconName,
|
||||
St.IconType.SYMBOLIC,
|
||||
DIALOG_ICON_SIZE);
|
||||
|
||||
this._iconBin.child = icon;
|
||||
this._iconBin.show();
|
||||
} else {
|
||||
this._iconBin.child = null;
|
||||
this._iconBin.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_statusForPresence: function(presence) {
|
||||
switch(presence) {
|
||||
case Tp.ConnectionPresenceType.AVAILABLE:
|
||||
return 'available';
|
||||
case Tp.ConnectionPresenceType.BUSY:
|
||||
return 'busy';
|
||||
case Tp.ConnectionPresenceType.OFFLINE:
|
||||
return 'offline';
|
||||
case Tp.ConnectionPresenceType.HIDDEN:
|
||||
return 'hidden';
|
||||
case Tp.ConnectionPresenceType.AWAY:
|
||||
return 'away';
|
||||
case Tp.ConnectionPresenceType.EXTENDED_AWAY:
|
||||
return 'xa';
|
||||
default:
|
||||
return 'unknown';
|
||||
}
|
||||
},
|
||||
|
||||
_IMStatusChanged: function(accountMgr, presence, status, message) {
|
||||
if (presence == Tp.ConnectionPresenceType.AVAILABLE)
|
||||
this._presence.setStatus(GnomeSession.PresenceStatus.AVAILABLE);
|
||||
|
||||
if (!this._expectedPresence || presence != this._expectedPresence)
|
||||
this._previousPresence = presence;
|
||||
else
|
||||
this._expectedPresence = undefined;
|
||||
|
||||
let activatedItem;
|
||||
|
||||
if (presence == Tp.ConnectionPresenceType.AVAILABLE)
|
||||
activatedItem = IMStatus.AVAILABLE;
|
||||
else if (presence == Tp.ConnectionPresenceType.BUSY)
|
||||
activatedItem = IMStatus.BUSY;
|
||||
else if (presence == Tp.ConnectionPresenceType.HIDDEN)
|
||||
activatedItem = IMStatus.HIDDEN;
|
||||
else if (presence == Tp.ConnectionPresenceType.AWAY)
|
||||
activatedItem = IMStatus.AWAY;
|
||||
else if (presence == Tp.ConnectionPresenceType.EXTENDED_AWAY)
|
||||
activatedItem = IMStatus.IDLE;
|
||||
else
|
||||
activatedItem = IMStatus.OFFLINE;
|
||||
|
||||
this._combo.setActiveItem(activatedItem);
|
||||
for (let i = 0; i < IMStatus.LAST; i++) {
|
||||
if (i == IMStatus.AVAILABLE || i == IMStatus.OFFLINE)
|
||||
continue; // always visible
|
||||
|
||||
this._combo.setItemVisible(i, i == activatedItem);
|
||||
}
|
||||
},
|
||||
|
||||
_changeIMStatus: function(menuItem, id) {
|
||||
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
|
||||
let newPresence, status;
|
||||
|
||||
if (id == IMStatus.AVAILABLE) {
|
||||
newPresence = Tp.ConnectionPresenceType.AVAILABLE;
|
||||
} else if (id == IMStatus.OFFLINE) {
|
||||
newPresence = Tp.ConnectionPresenceType.OFFLINE;
|
||||
} else
|
||||
return;
|
||||
|
||||
status = this._statusForPresence(newPresence);
|
||||
msg = msg ? msg : "";
|
||||
this._accountMgr.set_all_requested_presences(newPresence, status, msg);
|
||||
},
|
||||
|
||||
_sessionStatusChanged: function(sessionPresence, sessionStatus) {
|
||||
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
|
||||
let newPresence, status;
|
||||
|
||||
if (sessionStatus == GnomeSession.PresenceStatus.AVAILABLE) {
|
||||
newPresence = this._previousPresence;
|
||||
} else if (sessionStatus == GnomeSession.PresenceStatus.BUSY) {
|
||||
// Only change presence if the current one is "more present" than
|
||||
// busy, or if coming back from idle
|
||||
if (presence == Tp.ConnectionPresenceType.AVAILABLE ||
|
||||
presence == Tp.ConnectionPresenceType.EXTENDED_AWAY) {
|
||||
newPresence = Tp.ConnectionPresenceType.BUSY;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else if (sessionStatus == GnomeSession.PresenceStatus.IDLE) {
|
||||
// Only change presence if the current one is "more present" than
|
||||
// idle
|
||||
if (presence != Tp.ConnectionPresenceType.OFFLINE)
|
||||
newPresence = Tp.ConnectionPresenceType.EXTENDED_AWAY;
|
||||
else
|
||||
return;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newPresence == undefined)
|
||||
return;
|
||||
|
||||
status = this._statusForPresence(newPresence);
|
||||
msg = msg ? msg : "";
|
||||
|
||||
this._expectedPresence = newPresence;
|
||||
this._accountMgr.set_all_requested_presences(newPresence, status, msg);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function UserMenuButton() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
UserMenuButton.prototype = {
|
||||
__proto__: PanelMenu.Button.prototype,
|
||||
|
||||
_init: function() {
|
||||
PanelMenu.Button.prototype._init.call(this, 0.0);
|
||||
let box = new St.BoxLayout({ name: 'panelStatusMenu' });
|
||||
this.actor.set_child(box);
|
||||
|
||||
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
|
||||
|
||||
this._userManager = AccountsService.UserManager.get_default();
|
||||
|
||||
this._user = this._userManager.get_user(GLib.get_user_name());
|
||||
this._presence = new GnomeSession.Presence();
|
||||
this._session = new GnomeSession.SessionManager();
|
||||
this._haveShutdown = true;
|
||||
|
||||
this._account_mgr = Tp.AccountManager.dup()
|
||||
|
||||
this._upClient = new UPowerGlib.Client();
|
||||
this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy();
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
this._iconBox = new St.Bin();
|
||||
box.add(this._iconBox, { y_align: St.Align.MIDDLE, y_fill: false });
|
||||
|
||||
let textureCache = St.TextureCache.get_default();
|
||||
this._offlineIcon = new St.Icon({ icon_name: 'user-offline',
|
||||
style_class: 'popup-menu-icon' });
|
||||
this._availableIcon = new St.Icon({ icon_name: 'user-available',
|
||||
style_class: 'popup-menu-icon' });
|
||||
this._busyIcon = new St.Icon({ icon_name: 'user-busy',
|
||||
style_class: 'popup-menu-icon' });
|
||||
this._invisibleIcon = new St.Icon({ icon_name: 'user-invisible',
|
||||
style_class: 'popup-menu-icon' });
|
||||
this._awayIcon = new St.Icon({ icon_name: 'user-away',
|
||||
style_class: 'popup-menu-icon' });
|
||||
this._idleIcon = new St.Icon({ icon_name: 'user-idle',
|
||||
style_class: 'popup-menu-icon' });
|
||||
|
||||
this._presence.connect('StatusChanged',
|
||||
Lang.bind(this, this._updateSwitch));
|
||||
this._presence.getStatus(Lang.bind(this, this._updateSwitch));
|
||||
|
||||
this._account_mgr.connect('most-available-presence-changed',
|
||||
Lang.bind(this, this._updatePresenceIcon));
|
||||
this._account_mgr.prepare_async(null, Lang.bind(this,
|
||||
function(mgr) {
|
||||
let [presence, s, msg] = mgr.get_most_available_presence();
|
||||
this._updatePresenceIcon(mgr, presence, s, msg);
|
||||
}));
|
||||
|
||||
this._name = new St.Label();
|
||||
box.add(this._name, { y_align: St.Align.MIDDLE, y_fill: false });
|
||||
this._userLoadedId = this._user.connect('notify::is-loaded', Lang.bind(this, this._updateUserName));
|
||||
this._userChangedId = this._user.connect('changed', Lang.bind(this, this._updateUserName));
|
||||
|
||||
this._createSubMenu();
|
||||
this._userManager.connect('notify::is-loaded',
|
||||
Lang.bind(this, this._updateSwitchUser));
|
||||
this._userManager.connect('user-added',
|
||||
Lang.bind(this, this._updateSwitchUser));
|
||||
this._userManager.connect('user-removed',
|
||||
Lang.bind(this, this._updateSwitchUser));
|
||||
this._lockdownSettings.connect('changed::' + DISABLE_USER_SWITCH_KEY,
|
||||
Lang.bind(this, this._updateSwitchUser));
|
||||
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
|
||||
Lang.bind(this, this._updateLogout));
|
||||
|
||||
this._lockdownSettings.connect('changed::' + DISABLE_LOCK_SCREEN_KEY,
|
||||
Lang.bind(this, this._updateLockScreen));
|
||||
this._updateSwitchUser();
|
||||
this._updateLogout();
|
||||
this._updateLockScreen();
|
||||
|
||||
// Whether shutdown is available or not depends on both lockdown
|
||||
// settings (disable-log-out) and Polkit policy - the latter doesn't
|
||||
// notify, so we update the menu item each time the menu opens or
|
||||
// the lockdown setting changes, which should be close enough.
|
||||
this.menu.connect('open-state-changed', Lang.bind(this,
|
||||
function(menu, open) {
|
||||
if (open)
|
||||
this._updateHaveShutdown();
|
||||
}));
|
||||
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
|
||||
Lang.bind(this, this._updateHaveShutdown));
|
||||
|
||||
this._upClient.connect('notify::can-suspend', Lang.bind(this, this._updateSuspendOrPowerOff));
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
this._user.disconnect(this._userLoadedId);
|
||||
this._user.disconnect(this._userChangedId);
|
||||
},
|
||||
|
||||
_updateUserName: function() {
|
||||
if (this._user.is_loaded)
|
||||
this._name.set_text(this._user.get_real_name());
|
||||
else
|
||||
this._name.set_text("");
|
||||
},
|
||||
|
||||
_updateSwitchUser: function() {
|
||||
let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY);
|
||||
if (allowSwitch && this._userManager.can_switch ())
|
||||
this._loginScreenItem.actor.show();
|
||||
else
|
||||
this._loginScreenItem.actor.hide();
|
||||
},
|
||||
|
||||
_updateLogout: function() {
|
||||
let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY);
|
||||
if (allowLogout)
|
||||
this._logoutItem.actor.show();
|
||||
else
|
||||
this._logoutItem.actor.hide();
|
||||
},
|
||||
|
||||
_updateLockScreen: function() {
|
||||
let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
|
||||
if (allowLockScreen)
|
||||
this._lockScreenItem.actor.show();
|
||||
else
|
||||
this._lockScreenItem.actor.hide();
|
||||
},
|
||||
|
||||
_updateHaveShutdown: function() {
|
||||
this._session.CanShutdownRemote(Lang.bind(this,
|
||||
function(result, error) {
|
||||
if (!error) {
|
||||
this._haveShutdown = result;
|
||||
this._updateSuspendOrPowerOff();
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
_updateSuspendOrPowerOff: function() {
|
||||
this._haveSuspend = this._upClient.get_can_suspend();
|
||||
|
||||
if (!this._suspendOrPowerOffItem)
|
||||
return;
|
||||
|
||||
if (!this._haveShutdown && !this._haveSuspend)
|
||||
this._suspendOrPowerOffItem.actor.hide();
|
||||
else
|
||||
this._suspendOrPowerOffItem.actor.show();
|
||||
|
||||
// If we can't suspend show Power Off... instead
|
||||
// and disable the alt key
|
||||
if (!this._haveSuspend) {
|
||||
this._suspendOrPowerOffItem.updateText(_("Power Off..."), null);
|
||||
} else if (!this._haveShutdown) {
|
||||
this._suspendOrPowerOffItem.updateText(_("Suspend"), null);
|
||||
} else {
|
||||
this._suspendOrPowerOffItem.updateText(_("Suspend"), _("Power Off..."));
|
||||
}
|
||||
},
|
||||
|
||||
_updateSwitch: function(presence, status) {
|
||||
let active = status == GnomeSession.PresenceStatus.BUSY;
|
||||
this._dontDisturbSwitch.setToggleState(active);
|
||||
},
|
||||
|
||||
_updatePresenceIcon: function(accountMgr, presence, status, message) {
|
||||
if (presence == Tp.ConnectionPresenceType.AVAILABLE)
|
||||
this._iconBox.child = this._availableIcon;
|
||||
else if (presence == Tp.ConnectionPresenceType.BUSY)
|
||||
this._iconBox.child = this._busyIcon;
|
||||
else if (presence == Tp.ConnectionPresenceType.HIDDEN)
|
||||
this._iconBox.child = this._invisibleIcon;
|
||||
else if (presence == Tp.ConnectionPresenceType.AWAY)
|
||||
this._iconBox.child = this._awayIcon;
|
||||
else if (presence == Tp.ConnectionPresenceType.EXTENDED_AWAY)
|
||||
this._iconBox.child = this._idleIcon;
|
||||
else
|
||||
this._iconBox.child = this._offlineIcon;
|
||||
},
|
||||
|
||||
_createSubMenu: function() {
|
||||
let item;
|
||||
|
||||
item = new IMStatusChooserItem();
|
||||
item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
|
||||
item = new PopupMenu.PopupSwitchMenuItem(_("Do Not Disturb"));
|
||||
item.connect('activate', Lang.bind(this, this._updatePresenceStatus));
|
||||
this.menu.addMenuItem(item);
|
||||
this._dontDisturbSwitch = item;
|
||||
|
||||
item = new PopupMenu.PopupSeparatorMenuItem();
|
||||
this.menu.addMenuItem(item);
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Online Accounts"));
|
||||
item.connect('activate', Lang.bind(this, this._onOnlineAccountsActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("System Settings"));
|
||||
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
|
||||
item = new PopupMenu.PopupSeparatorMenuItem();
|
||||
this.menu.addMenuItem(item);
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Lock Screen"));
|
||||
item.connect('activate', Lang.bind(this, this._onLockScreenActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
this._lockScreenItem = item;
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Switch User"));
|
||||
item.connect('activate', Lang.bind(this, this._onLoginScreenActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
this._loginScreenItem = item;
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Log Out..."));
|
||||
item.connect('activate', Lang.bind(this, this._onQuitSessionActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
this._logoutItem = item;
|
||||
|
||||
item = new PopupMenu.PopupSeparatorMenuItem();
|
||||
this.menu.addMenuItem(item);
|
||||
|
||||
item = new PopupMenu.PopupAlternatingMenuItem(_("Suspend"),
|
||||
_("Power Off..."));
|
||||
this.menu.addMenuItem(item);
|
||||
this._suspendOrPowerOffItem = item;
|
||||
item.connect('activate', Lang.bind(this, this._onSuspendOrPowerOffActivate));
|
||||
this._updateSuspendOrPowerOff();
|
||||
},
|
||||
|
||||
_updatePresenceStatus: function(item, event) {
|
||||
let status = item.state ? GnomeSession.PresenceStatus.BUSY
|
||||
: GnomeSession.PresenceStatus.AVAILABLE;
|
||||
this._presence.setStatus(status);
|
||||
},
|
||||
|
||||
_onMyAccountActivate: function() {
|
||||
Main.overview.hide();
|
||||
let app = Shell.AppSystem.get_default().lookup_setting('gnome-user-accounts-panel.desktop');
|
||||
app.activate();
|
||||
},
|
||||
|
||||
_onOnlineAccountsActivate: function() {
|
||||
Main.overview.hide();
|
||||
let app = Shell.AppSystem.get_default().lookup_setting('gnome-online-accounts-panel.desktop');
|
||||
app.activate(-1);
|
||||
},
|
||||
|
||||
_onPreferencesActivate: function() {
|
||||
Main.overview.hide();
|
||||
let app = Shell.AppSystem.get_default().lookup_app('gnome-control-center.desktop');
|
||||
app.activate();
|
||||
},
|
||||
|
||||
_onLockScreenActivate: function() {
|
||||
Main.overview.hide();
|
||||
this._screenSaverProxy.LockRemote();
|
||||
},
|
||||
|
||||
_onLoginScreenActivate: function() {
|
||||
Main.overview.hide();
|
||||
// Ensure we only move to GDM after the screensaver has activated; in some
|
||||
// OS configurations, the X server may block event processing on VT switch
|
||||
this._screenSaverProxy.SetActiveRemote(true, Lang.bind(this, function() {
|
||||
this._userManager.goto_login_session();
|
||||
}));
|
||||
},
|
||||
|
||||
_onQuitSessionActivate: function() {
|
||||
Main.overview.hide();
|
||||
this._session.LogoutRemote(0);
|
||||
},
|
||||
|
||||
_onSuspendOrPowerOffActivate: function() {
|
||||
Main.overview.hide();
|
||||
|
||||
if (this._haveSuspend &&
|
||||
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
|
||||
// Ensure we only suspend after the screensaver has activated
|
||||
this._screenSaverProxy.SetActiveRemote(true, Lang.bind(this, function() {
|
||||
this._upClient.suspend_sync(null);
|
||||
}));
|
||||
} else {
|
||||
this._session.ShutdownRemote();
|
||||
}
|
||||
}
|
||||
};
|
@ -297,7 +297,7 @@ SearchTab.prototype = {
|
||||
_doSearch: function () {
|
||||
this._searchTimeoutId = 0;
|
||||
let text = this._text.get_text().replace(/^\s+/g, '').replace(/\s+$/g, '');
|
||||
this._searchResults.updateSearch(text);
|
||||
this._searchResults.doSearch(text);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -16,8 +16,7 @@ WindowAttentionHandler.prototype = {
|
||||
this._tracker = Shell.WindowTracker.get_default();
|
||||
this._tracker.connect('startup-sequence-changed', Lang.bind(this, this._onStartupSequenceChanged));
|
||||
|
||||
let display = global.screen.get_display();
|
||||
display.connect('window-demands-attention', Lang.bind(this, this._onWindowDemandsAttention));
|
||||
global.display.connect('window-demands-attention', Lang.bind(this, this._onWindowDemandsAttention));
|
||||
},
|
||||
|
||||
_onStartupSequenceChanged : function(tracker) {
|
||||
|
@ -239,7 +239,7 @@ WindowManager.prototype = {
|
||||
_hasAttachedDialogs: function(window, ignoreWindow) {
|
||||
var count = 0;
|
||||
window.foreach_transient(function(win) {
|
||||
if (win != ignoreWindow && win.get_window_type() == Meta.WindowType.MODAL_DIALOG)
|
||||
if (win != ignoreWindow && win.is_attached_dialog())
|
||||
count++;
|
||||
return false;
|
||||
});
|
||||
@ -247,7 +247,7 @@ WindowManager.prototype = {
|
||||
},
|
||||
|
||||
_checkDimming: function(window, ignoreWindow) {
|
||||
let shouldDim = Meta.prefs_get_attach_modal_dialogs() && this._hasAttachedDialogs(window, ignoreWindow);
|
||||
let shouldDim = this._hasAttachedDialogs(window, ignoreWindow);
|
||||
|
||||
if (shouldDim && !window._dimmed) {
|
||||
window._dimmed = true;
|
||||
@ -309,9 +309,7 @@ WindowManager.prototype = {
|
||||
|
||||
actor._windowType = type;
|
||||
}));
|
||||
if (actor.meta_window.get_window_type() == Meta.WindowType.MODAL_DIALOG
|
||||
&& Meta.prefs_get_attach_modal_dialogs()
|
||||
&& actor.get_meta_window().get_transient_for()) {
|
||||
if (actor.meta_window.is_attached_dialog()) {
|
||||
this._checkDimming(actor.get_meta_window().get_transient_for());
|
||||
if (this._shouldAnimate()) {
|
||||
actor.set_scale(1.0, 0.0);
|
||||
@ -373,7 +371,6 @@ WindowManager.prototype = {
|
||||
|
||||
_destroyWindow : function(shellwm, actor) {
|
||||
let window = actor.meta_window;
|
||||
let parent = window.get_transient_for();
|
||||
if (actor._notifyWindowTypeSignalId) {
|
||||
window.disconnect(actor._notifyWindowTypeSignalId);
|
||||
actor._notifyWindowTypeSignalId = 0;
|
||||
@ -383,12 +380,14 @@ WindowManager.prototype = {
|
||||
return win != window;
|
||||
});
|
||||
}
|
||||
while (window.get_window_type() == Meta.WindowType.MODAL_DIALOG
|
||||
&& parent) {
|
||||
if (window.is_attached_dialog()) {
|
||||
let parent = window.get_transient_for();
|
||||
this._checkDimming(parent, window);
|
||||
if (!Meta.prefs_get_attach_modal_dialogs()
|
||||
|| !this._shouldAnimate())
|
||||
break;
|
||||
if (!this._shouldAnimate()) {
|
||||
shellwm.completed_destroy(actor);
|
||||
return;
|
||||
}
|
||||
|
||||
actor.set_scale(1.0, 1.0);
|
||||
actor.show();
|
||||
this._destroying.push(actor);
|
||||
|
@ -94,22 +94,38 @@ function WindowClone(realWindow) {
|
||||
|
||||
WindowClone.prototype = {
|
||||
_init : function(realWindow) {
|
||||
this.actor = new Clutter.Clone({ source: realWindow.get_texture(),
|
||||
reactive: true,
|
||||
x: realWindow.x,
|
||||
y: realWindow.y });
|
||||
this.actor._delegate = this;
|
||||
this.realWindow = realWindow;
|
||||
this.metaWindow = realWindow.meta_window;
|
||||
this.metaWindow._delegate = this;
|
||||
this.origX = realWindow.x;
|
||||
this.origY = realWindow.y;
|
||||
|
||||
let [borderX, borderY] = this._getInvisibleBorderPadding();
|
||||
this._windowClone = new Clutter.Clone({ source: realWindow.get_texture(),
|
||||
x: -borderX,
|
||||
y: -borderY });
|
||||
|
||||
this.origX = realWindow.x + borderX;
|
||||
this.origY = realWindow.y + borderY;
|
||||
|
||||
let outerRect = realWindow.meta_window.get_outer_rect();
|
||||
|
||||
// The MetaShapedTexture that we clone has a size that includes
|
||||
// the invisible border; this is inconvenient; rather than trying
|
||||
// to compensate all over the place we insert a ClutterGroup into
|
||||
// the hierarchy that is sized to only the visible portion.
|
||||
this.actor = new Clutter.Group({ reactive: true,
|
||||
x: this.origX,
|
||||
y: this.origY,
|
||||
width: outerRect.width,
|
||||
height: outerRect.height });
|
||||
|
||||
this.actor.add_actor(this._windowClone);
|
||||
|
||||
this.actor._delegate = this;
|
||||
|
||||
this._stackAbove = null;
|
||||
|
||||
this._sizeChangedId = this.realWindow.connect('size-changed', Lang.bind(this, function() {
|
||||
this.emit('size-changed');
|
||||
}));
|
||||
this._sizeChangedId = this.realWindow.connect('size-changed',
|
||||
Lang.bind(this, this._onRealWindowSizeChanged));
|
||||
this._realWindowDestroyId = this.realWindow.connect('destroy',
|
||||
Lang.bind(this, this._disconnectRealWindowSignals));
|
||||
|
||||
@ -161,7 +177,7 @@ WindowClone.prototype = {
|
||||
// will look funny.
|
||||
|
||||
if (!this._selected &&
|
||||
this.metaWindow != global.screen.get_display().focus_window)
|
||||
this.metaWindow != global.display.focus_window)
|
||||
this._zoomEnd();
|
||||
}
|
||||
},
|
||||
@ -176,6 +192,32 @@ WindowClone.prototype = {
|
||||
this._realWindowDestroyId = 0;
|
||||
},
|
||||
|
||||
_getInvisibleBorderPadding: function() {
|
||||
// We need to adjust the position of the actor because of the
|
||||
// consequences of invisible borders -- in reality, the texture
|
||||
// has an extra set of "padding" around it that we need to trim
|
||||
// down.
|
||||
|
||||
// The outer rect paradoxically is the smaller rectangle,
|
||||
// containing the positions of the visible frame. The input
|
||||
// rect contains everything, including the invisible border
|
||||
// padding.
|
||||
let outerRect = this.metaWindow.get_outer_rect();
|
||||
let inputRect = this.metaWindow.get_input_rect();
|
||||
let [borderX, borderY] = [outerRect.x - inputRect.x,
|
||||
outerRect.y - inputRect.y];
|
||||
|
||||
return [borderX, borderY];
|
||||
},
|
||||
|
||||
_onRealWindowSizeChanged: function() {
|
||||
let [borderX, borderY] = this._getInvisibleBorderPadding();
|
||||
let outerRect = this.metaWindow.get_outer_rect();
|
||||
this.actor.set_size(outerRect.width, outerRect.height);
|
||||
this._windowClone.set_position(-borderX, -borderY);
|
||||
this.emit('size-changed');
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
this._disconnectRealWindowSignals();
|
||||
|
||||
@ -1440,7 +1482,7 @@ Workspace.prototype = {
|
||||
time);
|
||||
return true;
|
||||
} else if (source.shellWorkspaceLaunch) {
|
||||
source.shellWorkspaceLaunch({ workspace: this.metaWorkspace,
|
||||
source.shellWorkspaceLaunch({ workspace: this.metaWorkspace ? this.metaWorkspace.index() : -1,
|
||||
timestamp: time });
|
||||
return true;
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ WorkspaceThumbnail.prototype = {
|
||||
return true;
|
||||
}));
|
||||
|
||||
this._background = new Clutter.Clone({ source: global.background_actor });
|
||||
this._background = Meta.BackgroundActor.new_for_screen(global.screen);
|
||||
this._contents.add_actor(this._background);
|
||||
|
||||
let monitor = Main.layoutManager.primaryMonitor;
|
||||
@ -452,7 +452,7 @@ WorkspaceThumbnail.prototype = {
|
||||
time);
|
||||
return true;
|
||||
} else if (source.shellWorkspaceLaunch) {
|
||||
source.shellWorkspaceLaunch({ workspace: this.metaWorkspace,
|
||||
source.shellWorkspaceLaunch({ workspace: this.metaWorkspace ? this.metaWorkspace.index() : -1,
|
||||
timestamp: time });
|
||||
return true;
|
||||
}
|
||||
|
@ -551,7 +551,6 @@ WorkspacesDisplay.prototype = {
|
||||
Lang.bind(this, this._onScrollEvent));
|
||||
|
||||
this._monitorIndex = Main.layoutManager.primaryIndex;
|
||||
this._monitor = Main.layoutManager.monitors[this._monitorIndex];
|
||||
|
||||
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
|
||||
controls.add_actor(this._thumbnailsBox.actor);
|
||||
|
@ -33,6 +33,7 @@ kn
|
||||
lt
|
||||
lv
|
||||
mr
|
||||
ms
|
||||
nb
|
||||
nl
|
||||
nn
|
||||
|
@ -1,15 +1,21 @@
|
||||
data/gnome-shell.desktop.in.in
|
||||
data/org.gnome.shell.gschema.xml.in
|
||||
js/gdm/loginDialog.js
|
||||
js/misc/util.js
|
||||
js/ui/appDisplay.js
|
||||
js/ui/appFavorites.js
|
||||
js/ui/autorunManager.js
|
||||
js/ui/calendar.js
|
||||
js/ui/contactDisplay.js
|
||||
js/ui/dash.js
|
||||
js/ui/dateMenu.js
|
||||
js/ui/docDisplay.js
|
||||
js/ui/endSessionDialog.js
|
||||
js/ui/keyboard.js
|
||||
js/ui/lookingGlass.js
|
||||
js/ui/messageTray.js
|
||||
js/ui/networkAgent.js
|
||||
js/ui/notificationDaemon.js
|
||||
js/ui/overview.js
|
||||
js/ui/panel.js
|
||||
js/ui/placeDisplay.js
|
||||
@ -17,7 +23,7 @@ js/ui/polkitAuthenticationAgent.js
|
||||
js/ui/popupMenu.js
|
||||
js/ui/runDialog.js
|
||||
js/ui/searchDisplay.js
|
||||
js/ui/statusMenu.js
|
||||
js/ui/shellMountOperation.js
|
||||
js/ui/status/accessibility.js
|
||||
js/ui/status/bluetooth.js
|
||||
js/ui/status/keyboard.js
|
||||
@ -25,11 +31,10 @@ js/ui/status/network.js
|
||||
js/ui/status/power.js
|
||||
js/ui/status/volume.js
|
||||
js/ui/telepathyClient.js
|
||||
js/ui/userMenu.js
|
||||
js/ui/viewSelector.js
|
||||
js/ui/windowAttentionHandler.js
|
||||
js/ui/workspacesView.js
|
||||
src/gvc/gvc-mixer-control.c
|
||||
src/gdmuser/gdm-user.c
|
||||
src/main.c
|
||||
src/shell-app.c
|
||||
src/shell-app-system.c
|
||||
|
415
po/de.po
415
po/de.po
@ -17,8 +17,8 @@ msgstr ""
|
||||
"Project-Id-Version: gnome-shell master\n"
|
||||
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
|
||||
"shell&keywords=I18N+L10N&component=general\n"
|
||||
"POT-Creation-Date: 2011-07-14 18:02+0000\n"
|
||||
"PO-Revision-Date: 2011-07-14 20:25+0100\n"
|
||||
"POT-Creation-Date: 2011-08-24 17:59+0000\n"
|
||||
"PO-Revision-Date: 2011-08-28 14:03+0100\n"
|
||||
"Last-Translator: Mario Blättermann <mariobl@freenet.de>\n"
|
||||
"Language-Team: Deutsch <gnome-de@gnome.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -67,43 +67,32 @@ msgstr ""
|
||||
"Listen erscheinen."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:6
|
||||
msgid ""
|
||||
"GNOME Shell extensions have a uuid property; this key lists extensions which "
|
||||
"should not be loaded. This setting overrides enabled-extensions for "
|
||||
"extensions that appear in both lists."
|
||||
msgstr ""
|
||||
"Die Erweiterungen der GNOME-Shell besitzen eine UUID-Eigenschaft. Dieser "
|
||||
"Schlüssel listet Erweiterungen auf, welche nicht geladen werden sollen. Diese "
|
||||
"Einstellung überschreibt »enabled-extensions« für Erweiterungen, die in "
|
||||
"beiden Listen erscheinen."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:7
|
||||
msgid "History for command (Alt-F2) dialog"
|
||||
msgstr "Verlauf des Befehlsdialogs (Alt+F2)"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:8
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:7
|
||||
msgid "History for the looking glass dialog"
|
||||
msgstr "Chronik des Dialogs »looking glass«"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:9
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:8
|
||||
msgid "If true, display date in the clock, in addition to time."
|
||||
msgstr "Legt fest, ob das Datum zusätzlich zur Uhrzeit angezeigt wird."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:10
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:9
|
||||
msgid "If true, display seconds in time."
|
||||
msgstr "Legt fest, ob in der Uhrzeit Sekunden angezeigt werden."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:11
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:10
|
||||
msgid "If true, display the ISO week date in the calendar."
|
||||
msgstr ""
|
||||
"Wenn dieser Wert gesetzt ist, wird der ISO-Wochentag im Kalender angezeigt."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:12
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:11
|
||||
msgid "List of desktop file IDs for favorite applications"
|
||||
msgstr "Liste der Kennungen der Desktop-Dateien für bevorzugte Anwendungen"
|
||||
|
||||
# Hier blicke ich überhaupt nicht durch.
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:14
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:13
|
||||
#, no-c-format
|
||||
msgid ""
|
||||
"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
|
||||
@ -130,19 +119,19 @@ msgstr ""
|
||||
"mittels des VP8-Codecs aufzeichnet. %T wird als Platzhalter für die vermutete "
|
||||
"optimale Threadanzahl auf dem System verwendet."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:15
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:14
|
||||
msgid "Show date in clock"
|
||||
msgstr "Datum in der Uhr anzeigen"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:16
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:15
|
||||
msgid "Show the week date in the calendar"
|
||||
msgstr "Wochentag im Kalender anzeigen"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:17
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:16
|
||||
msgid "Show time with seconds"
|
||||
msgstr "Zeit sekundengenau anzeigen"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:18
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:17
|
||||
msgid ""
|
||||
"The applications corresponding to these identifiers will be displayed in the "
|
||||
"favorites area."
|
||||
@ -150,7 +139,7 @@ msgstr ""
|
||||
"Programme, welche auf diese Bezeichner zutreffen, werden im Favoriten-Bereich "
|
||||
"angezeigt."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:19
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:18
|
||||
msgid ""
|
||||
"The filename for recorded screencasts will be a unique filename based on the "
|
||||
"current date, and use this extension. It should be changed when recording to "
|
||||
@ -161,7 +150,7 @@ msgstr ""
|
||||
"Dateiname sollte geändert werden, wenn Sie in einem anderen Containerformat "
|
||||
"aufnehmen."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:20
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:19
|
||||
msgid ""
|
||||
"The framerate of the resulting screencast recordered by GNOME Shell's "
|
||||
"screencast recorder in frames-per-second."
|
||||
@ -170,11 +159,11 @@ msgstr ""
|
||||
"der GNOME-Shell aufgezeichnet werden soll, in Einzelbildern pro Sekunde."
|
||||
|
||||
# hmm Enkodieren oder Kodieren?
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:21
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:20
|
||||
msgid "The gstreamer pipeline used to encode the screencast"
|
||||
msgstr "Die GStreamer-Weiterleitung zur Enkodierung des Screencasts"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:22
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:21
|
||||
msgid ""
|
||||
"The shell normally monitors active applications in order to present the most "
|
||||
"used ones (e.g. in launchers). While this data will be kept private, you may "
|
||||
@ -187,19 +176,15 @@ msgstr ""
|
||||
"deaktivieren, um Ihre Privatsphäre zu schützen. Bitte beachten Sie, dass "
|
||||
"bereits gespeicherte Daten hiervon nicht beeinflusst werden."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:23
|
||||
msgid "Uuids of extensions to disable"
|
||||
msgstr "UUIDs der zu deaktivierenden Erweiterungen"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:24
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:22
|
||||
msgid "Uuids of extensions to enable"
|
||||
msgstr "UUIDs der zu aktivierenden Erweiterungen"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:25
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:23
|
||||
msgid "Whether to collect stats about applications usage"
|
||||
msgstr "Legt fest, ob Statistiken über Anwendungsnutzung erfasst werden sollen"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:26
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:24
|
||||
msgid "disabled OpenSearch providers"
|
||||
msgstr "deaktivierte OpenSearch-Provider"
|
||||
|
||||
@ -219,27 +204,27 @@ msgid "Execution of '%s' failed:"
|
||||
msgstr "Ausführung von »%s« ist gescheitert:"
|
||||
|
||||
#. Translators: Filter to display all applications
|
||||
#: ../js/ui/appDisplay.js:252
|
||||
#: ../js/ui/appDisplay.js:253
|
||||
msgid "All"
|
||||
msgstr "Alle"
|
||||
|
||||
#: ../js/ui/appDisplay.js:351
|
||||
#: ../js/ui/appDisplay.js:315
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "ANWENDUNGEN"
|
||||
|
||||
#: ../js/ui/appDisplay.js:377
|
||||
#: ../js/ui/appDisplay.js:373
|
||||
msgid "SETTINGS"
|
||||
msgstr "EINSTELLUNGEN"
|
||||
|
||||
#: ../js/ui/appDisplay.js:650
|
||||
#: ../js/ui/appDisplay.js:681
|
||||
msgid "New Window"
|
||||
msgstr "Neues Fenster"
|
||||
|
||||
#: ../js/ui/appDisplay.js:653
|
||||
#: ../js/ui/appDisplay.js:684
|
||||
msgid "Remove from Favorites"
|
||||
msgstr "Aus Favoriten entfernen"
|
||||
|
||||
#: ../js/ui/appDisplay.js:654
|
||||
#: ../js/ui/appDisplay.js:685
|
||||
msgid "Add to Favorites"
|
||||
msgstr "Zu Favoriten hinzufügen"
|
||||
|
||||
@ -253,6 +238,15 @@ msgstr "%s wurde zu Ihren Favoriten hinzugefügt"
|
||||
msgid "%s has been removed from your favorites."
|
||||
msgstr "%s wurde aus Ihren Favoriten entfernt"
|
||||
|
||||
#: ../js/ui/autorunManager.js:591
|
||||
#, c-format
|
||||
msgid "Open with %s"
|
||||
msgstr "Öffnen mit %s"
|
||||
|
||||
#: ../js/ui/autorunManager.js:617
|
||||
msgid "Eject"
|
||||
msgstr "Auswerfen"
|
||||
|
||||
#. Translators: Shown in calendar event list for all day events
|
||||
#. * Keep it short, best if you can use less then 10 characters
|
||||
#.
|
||||
@ -371,94 +365,94 @@ msgid "S"
|
||||
msgstr "Sa"
|
||||
|
||||
#. Translators: Text to show if there are no events
|
||||
#: ../js/ui/calendar.js:701
|
||||
#: ../js/ui/calendar.js:678
|
||||
msgid "Nothing Scheduled"
|
||||
msgstr "Nichts geplant"
|
||||
|
||||
#. Translators: Shown on calendar heading when selected day occurs on current year
|
||||
#: ../js/ui/calendar.js:717 ../js/ui/telepathyClient.js:708
|
||||
#: ../js/ui/calendar.js:694
|
||||
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:720 ../js/ui/telepathyClient.js:711
|
||||
#: ../js/ui/calendar.js:697
|
||||
msgctxt "calendar heading"
|
||||
msgid "%A, %B %d, %Y"
|
||||
msgstr "%a, %d. %B %Y"
|
||||
|
||||
#: ../js/ui/calendar.js:730
|
||||
#: ../js/ui/calendar.js:707
|
||||
msgid "Today"
|
||||
msgstr "Heute"
|
||||
|
||||
#: ../js/ui/calendar.js:734
|
||||
#: ../js/ui/calendar.js:711
|
||||
msgid "Tomorrow"
|
||||
msgstr "Morgen"
|
||||
|
||||
#: ../js/ui/calendar.js:743
|
||||
#: ../js/ui/calendar.js:720
|
||||
msgid "This week"
|
||||
msgstr "Diese Woche"
|
||||
|
||||
#: ../js/ui/calendar.js:751
|
||||
#: ../js/ui/calendar.js:728
|
||||
msgid "Next week"
|
||||
msgstr "Nächste Woche"
|
||||
|
||||
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1122
|
||||
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1123
|
||||
msgid "Remove"
|
||||
msgstr "Entfernen"
|
||||
|
||||
#: ../js/ui/dateMenu.js:89
|
||||
#: ../js/ui/dateMenu.js:90
|
||||
msgid "Date and Time Settings"
|
||||
msgstr "Einstellungen für Datum und Uhrzeit"
|
||||
|
||||
#: ../js/ui/dateMenu.js:109
|
||||
#: ../js/ui/dateMenu.js:110
|
||||
msgid "Open Calendar"
|
||||
msgstr "Kalender öffnen"
|
||||
|
||||
#. Translators: This is the time format with date used
|
||||
#. in 24-hour mode.
|
||||
#: ../js/ui/dateMenu.js:162
|
||||
#: ../js/ui/dateMenu.js:167
|
||||
msgid "%a %b %e, %R:%S"
|
||||
msgstr "%a, %e. %b, %R:%S"
|
||||
|
||||
#: ../js/ui/dateMenu.js:163
|
||||
#: ../js/ui/dateMenu.js:168
|
||||
msgid "%a %b %e, %R"
|
||||
msgstr "%a, %e. %b, %R"
|
||||
|
||||
#. Translators: This is the time format without date used
|
||||
#. in 24-hour mode.
|
||||
#: ../js/ui/dateMenu.js:167
|
||||
#: ../js/ui/dateMenu.js:172
|
||||
msgid "%a %R:%S"
|
||||
msgstr "%a %R:%S"
|
||||
|
||||
#: ../js/ui/dateMenu.js:168
|
||||
#: ../js/ui/dateMenu.js:173
|
||||
msgid "%a %R"
|
||||
msgstr "%a %R"
|
||||
|
||||
#. Translators: This is a time format with date used
|
||||
#. for AM/PM.
|
||||
#: ../js/ui/dateMenu.js:175
|
||||
#: ../js/ui/dateMenu.js:180
|
||||
msgid "%a %b %e, %l:%M:%S %p"
|
||||
msgstr "%a, %e. %b, %l:%M:%S"
|
||||
|
||||
#: ../js/ui/dateMenu.js:176
|
||||
#: ../js/ui/dateMenu.js:181
|
||||
msgid "%a %b %e, %l:%M %p"
|
||||
msgstr "%a, %e. %b, %l:%M"
|
||||
|
||||
#. Translators: This is a time format without date used
|
||||
#. for AM/PM.
|
||||
#: ../js/ui/dateMenu.js:180
|
||||
#: ../js/ui/dateMenu.js:185
|
||||
msgid "%a %l:%M:%S %p"
|
||||
msgstr "%a %l:%M:%S"
|
||||
|
||||
#: ../js/ui/dateMenu.js:181
|
||||
#: ../js/ui/dateMenu.js:186
|
||||
msgid "%a %l:%M %p"
|
||||
msgstr "%a %l:%M"
|
||||
|
||||
#. 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:192
|
||||
#: ../js/ui/dateMenu.js:197
|
||||
msgid "%A %B %e, %Y"
|
||||
msgstr "%A, %e. %B %Y"
|
||||
|
||||
@ -539,44 +533,53 @@ msgstr "Neustart des Systems."
|
||||
msgid "Cancel"
|
||||
msgstr "Abbrechen"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:641
|
||||
#: ../js/ui/lookingGlass.js:640
|
||||
msgid "No extensions installed"
|
||||
msgstr "Keine Erweiterungen installiert"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:678
|
||||
#: ../js/ui/lookingGlass.js:686
|
||||
msgid "Enabled"
|
||||
msgstr "Aktiviert"
|
||||
|
||||
#. translators:
|
||||
#. * The device has been disabled
|
||||
#: ../js/ui/lookingGlass.js:680 ../src/gvc/gvc-mixer-control.c:1091
|
||||
#: ../js/ui/lookingGlass.js:688 ../src/gvc/gvc-mixer-control.c:1091
|
||||
msgid "Disabled"
|
||||
msgstr "Deaktiviert"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:682
|
||||
#: ../js/ui/lookingGlass.js:690
|
||||
msgid "Error"
|
||||
msgstr "Fehler"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:684
|
||||
#: ../js/ui/lookingGlass.js:692
|
||||
msgid "Out of date"
|
||||
msgstr "Veraltet"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:709
|
||||
#: ../js/ui/lookingGlass.js:694
|
||||
msgid "Downloading"
|
||||
msgstr "Herunterladen"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:719
|
||||
msgid "View Source"
|
||||
msgstr "Quelle zeigen"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:715
|
||||
#: ../js/ui/lookingGlass.js:725
|
||||
msgid "Web Page"
|
||||
msgstr "Webseite"
|
||||
|
||||
#: ../js/ui/messageTray.js:1115
|
||||
#: ../js/ui/messageTray.js:1116
|
||||
msgid "Open"
|
||||
msgstr "Öffnen"
|
||||
|
||||
#: ../js/ui/messageTray.js:2286
|
||||
#: ../js/ui/messageTray.js:2277
|
||||
msgid "System Information"
|
||||
msgstr "Systeminformationen"
|
||||
|
||||
#: ../js/ui/notificationDaemon.js:427 ../js/ui/status/power.js:238
|
||||
#: ../src/shell-app.c:354
|
||||
msgid "Unknown"
|
||||
msgstr "Unbekannt"
|
||||
|
||||
#: ../js/ui/overview.js:89
|
||||
msgid "Undo"
|
||||
msgstr "Rückgängig"
|
||||
@ -597,18 +600,18 @@ msgid "Dash"
|
||||
msgstr "Dash"
|
||||
|
||||
#. TODO - _quit() doesn't really work on apps in state STARTING yet
|
||||
#: ../js/ui/panel.js:533
|
||||
#: ../js/ui/panel.js:531
|
||||
#, c-format
|
||||
msgid "Quit %s"
|
||||
msgstr "%s beenden"
|
||||
|
||||
#. Button on the left side of the panel.
|
||||
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
|
||||
#: ../js/ui/panel.js:914
|
||||
#. Translators: If there is no suitable word for "Activities"
|
||||
#. in your language, you can use the word for "Overview".
|
||||
#: ../js/ui/panel.js:567
|
||||
msgid "Activities"
|
||||
msgstr "Aktivitäten"
|
||||
|
||||
#: ../js/ui/panel.js:1014
|
||||
#: ../js/ui/panel.js:876
|
||||
msgid "Top Bar"
|
||||
msgstr "Obere Leiste"
|
||||
|
||||
@ -674,43 +677,9 @@ msgstr "Suche läuft …"
|
||||
msgid "No matching results."
|
||||
msgstr "Keine passenden Ergebnisse."
|
||||
|
||||
#: ../js/ui/statusMenu.js:192 ../js/ui/statusMenu.js:196
|
||||
#: ../js/ui/statusMenu.js:262
|
||||
msgid "Power Off..."
|
||||
msgstr "Ausschalten …"
|
||||
|
||||
#: ../js/ui/statusMenu.js:194 ../js/ui/statusMenu.js:196
|
||||
#: ../js/ui/statusMenu.js:261
|
||||
msgid "Suspend"
|
||||
msgstr "Bereitschaft"
|
||||
|
||||
#: ../js/ui/statusMenu.js:217
|
||||
msgid "Available"
|
||||
msgstr "Verfügbar"
|
||||
|
||||
#: ../js/ui/statusMenu.js:222
|
||||
msgid "Busy"
|
||||
msgstr "Beschäftigt"
|
||||
|
||||
#: ../js/ui/statusMenu.js:230
|
||||
msgid "My Account"
|
||||
msgstr "Eigenes Konto"
|
||||
|
||||
#: ../js/ui/statusMenu.js:234
|
||||
msgid "System Settings"
|
||||
msgstr "Systemeinstellungen"
|
||||
|
||||
#: ../js/ui/statusMenu.js:242
|
||||
msgid "Lock Screen"
|
||||
msgstr "Bildschirm sperren"
|
||||
|
||||
#: ../js/ui/statusMenu.js:247
|
||||
msgid "Switch User"
|
||||
msgstr "Benutzer wechseln"
|
||||
|
||||
#: ../js/ui/statusMenu.js:252
|
||||
msgid "Log Out..."
|
||||
msgstr "Abmelden …"
|
||||
#: ../js/ui/shellMountOperation.js:285
|
||||
msgid "Wrong password, please try again"
|
||||
msgstr "Falsches Passwort, bitte versuchen Sie es erneut"
|
||||
|
||||
#: ../js/ui/status/accessibility.js:60
|
||||
msgid "Zoom"
|
||||
@ -785,11 +754,11 @@ msgstr "Hardware deaktiviert"
|
||||
msgid "Connection"
|
||||
msgstr "Verbindung"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:226 ../js/ui/status/network.js:493
|
||||
#: ../js/ui/status/bluetooth.js:226 ../js/ui/status/network.js:497
|
||||
msgid "disconnecting..."
|
||||
msgstr "Verbindungsabbau …"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:239 ../js/ui/status/network.js:499
|
||||
#: ../js/ui/status/bluetooth.js:239 ../js/ui/status/network.js:503
|
||||
msgid "connecting..."
|
||||
msgstr "Verbindungsaufbau …"
|
||||
|
||||
@ -841,7 +810,7 @@ msgstr "Immer Zugriff gewähren"
|
||||
msgid "Grant this time only"
|
||||
msgstr "Nur dieses Mal gewähren"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:409
|
||||
#: ../js/ui/status/bluetooth.js:409 ../js/ui/telepathyClient.js:1006
|
||||
msgid "Reject"
|
||||
msgstr "Abweisen"
|
||||
|
||||
@ -883,148 +852,186 @@ msgid "OK"
|
||||
msgstr "OK"
|
||||
|
||||
#: ../js/ui/status/keyboard.js:71
|
||||
msgid "Show Keyboard Layout..."
|
||||
msgstr "Tastaturbelegung zeigen …"
|
||||
msgid "Show Keyboard Layout"
|
||||
msgstr "Tastaturbelegung zeigen"
|
||||
|
||||
#: ../js/ui/status/keyboard.js:75
|
||||
msgid "Localization Settings"
|
||||
msgstr "Lokalisierungseinstellungen"
|
||||
msgid "Region and Language Settings"
|
||||
msgstr "Einstellungen für Region und Sprache"
|
||||
|
||||
#: ../js/ui/status/network.js:123
|
||||
#: ../js/ui/statusMenu.js:192 ../js/ui/statusMenu.js:196
|
||||
#: ../js/ui/statusMenu.js:262
|
||||
msgid "Power Off..."
|
||||
msgstr "Ausschalten …"
|
||||
|
||||
#: ../js/ui/statusMenu.js:194 ../js/ui/statusMenu.js:196
|
||||
#: ../js/ui/statusMenu.js:261
|
||||
msgid "Suspend"
|
||||
msgstr "Bereitschaft"
|
||||
|
||||
#: ../js/ui/statusMenu.js:217
|
||||
msgid "Available"
|
||||
msgstr "Verfügbar"
|
||||
|
||||
#: ../js/ui/statusMenu.js:222
|
||||
msgid "Busy"
|
||||
msgstr "Beschäftigt"
|
||||
|
||||
#: ../js/ui/statusMenu.js:230
|
||||
msgid "My Account"
|
||||
msgstr "Eigenes Konto"
|
||||
|
||||
#: ../js/ui/statusMenu.js:234
|
||||
msgid "System Settings"
|
||||
msgstr "Systemeinstellungen"
|
||||
|
||||
#: ../js/ui/statusMenu.js:242
|
||||
msgid "Lock Screen"
|
||||
msgstr "Bildschirm sperren"
|
||||
|
||||
#: ../js/ui/statusMenu.js:247
|
||||
msgid "Switch User"
|
||||
msgstr "Benutzer wechseln"
|
||||
|
||||
#: ../js/ui/statusMenu.js:252
|
||||
msgid "Log Out..."
|
||||
msgstr "Abmelden …"
|
||||
|
||||
#: ../js/ui/status/network.js:108
|
||||
msgid "<unknown>"
|
||||
msgstr "<Unbekannt>"
|
||||
|
||||
#. Translators: this indicates that wireless or wwan is disabled by hardware killswitch
|
||||
#: ../js/ui/status/network.js:292
|
||||
#: ../js/ui/status/network.js:296
|
||||
msgid "disabled"
|
||||
msgstr "Deaktiviert"
|
||||
|
||||
#. Translators: this is for network devices that are physically present but are not
|
||||
#. under NetworkManager's control (and thus cannot be used in the menu)
|
||||
#: ../js/ui/status/network.js:491
|
||||
#: ../js/ui/status/network.js:495
|
||||
msgid "unmanaged"
|
||||
msgstr "nicht verwaltet"
|
||||
|
||||
#. Translators: this is for network connections that require some kind of key or password
|
||||
#: ../js/ui/status/network.js:502
|
||||
#: ../js/ui/status/network.js:506
|
||||
msgid "authentication required"
|
||||
msgstr "Legitimierung erforderlich"
|
||||
|
||||
#. Translators: this is for devices that require some kind of firmware or kernel
|
||||
#. module, which is missing
|
||||
#: ../js/ui/status/network.js:512
|
||||
#: ../js/ui/status/network.js:516
|
||||
msgid "firmware missing"
|
||||
msgstr "Firmware fehlt"
|
||||
|
||||
#. Translators: this is for wired network devices that are physically disconnected
|
||||
#: ../js/ui/status/network.js:519
|
||||
#: ../js/ui/status/network.js:523
|
||||
msgid "cable unplugged"
|
||||
msgstr "Kabel nicht angeschlossen"
|
||||
|
||||
#. Translators: this is for a network device that cannot be activated (for example it
|
||||
#. is disabled by rfkill, or it has no coverage
|
||||
#: ../js/ui/status/network.js:524
|
||||
#: ../js/ui/status/network.js:528
|
||||
msgid "unavailable"
|
||||
msgstr "nicht verfügbar"
|
||||
|
||||
#: ../js/ui/status/network.js:526
|
||||
#: ../js/ui/status/network.js:530
|
||||
msgid "connection failed"
|
||||
msgstr "Verbindung gescheitert"
|
||||
|
||||
#: ../js/ui/status/network.js:582 ../js/ui/status/network.js:1507
|
||||
#: ../js/ui/status/network.js:586 ../js/ui/status/network.js:1546
|
||||
msgid "More..."
|
||||
msgstr "Mehr ..."
|
||||
|
||||
#. 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:618 ../js/ui/status/network.js:1444
|
||||
#: ../js/ui/status/network.js:622 ../js/ui/status/network.js:1483
|
||||
msgid "Connected (private)"
|
||||
msgstr "Verbunden (privat)"
|
||||
|
||||
#: ../js/ui/status/network.js:703
|
||||
#: ../js/ui/status/network.js:707
|
||||
msgid "Auto Ethernet"
|
||||
msgstr "Ethernet (automatisch)"
|
||||
|
||||
#: ../js/ui/status/network.js:771
|
||||
#: ../js/ui/status/network.js:775
|
||||
msgid "Auto broadband"
|
||||
msgstr "Mobiles Breitband (automatisch)"
|
||||
|
||||
#: ../js/ui/status/network.js:774
|
||||
#: ../js/ui/status/network.js:778
|
||||
msgid "Auto dial-up"
|
||||
msgstr "Einwählverbindung (automatisch)"
|
||||
|
||||
#. TRANSLATORS: this the automatic wireless connection name (including the network name)
|
||||
#: ../js/ui/status/network.js:898 ../js/ui/status/network.js:1456
|
||||
#: ../js/ui/status/network.js:902 ../js/ui/status/network.js:1495
|
||||
#, c-format
|
||||
msgid "Auto %s"
|
||||
msgstr "%s (automatisch)"
|
||||
|
||||
#: ../js/ui/status/network.js:900
|
||||
#: ../js/ui/status/network.js:904
|
||||
msgid "Auto bluetooth"
|
||||
msgstr "Bluetooth (automatisch)"
|
||||
|
||||
#: ../js/ui/status/network.js:1458
|
||||
#: ../js/ui/status/network.js:1497
|
||||
msgid "Auto wireless"
|
||||
msgstr "Drahtlos (automatisch)"
|
||||
|
||||
#: ../js/ui/status/network.js:1550
|
||||
#: ../js/ui/status/network.js:1589
|
||||
msgid "Enable networking"
|
||||
msgstr "Netzwerk aktivieren"
|
||||
|
||||
#: ../js/ui/status/network.js:1562
|
||||
#: ../js/ui/status/network.js:1601
|
||||
msgid "Wired"
|
||||
msgstr "Kabelgebunden"
|
||||
|
||||
#: ../js/ui/status/network.js:1573
|
||||
#: ../js/ui/status/network.js:1612
|
||||
msgid "Wireless"
|
||||
msgstr "Drahtlos"
|
||||
|
||||
#: ../js/ui/status/network.js:1583
|
||||
#: ../js/ui/status/network.js:1622
|
||||
msgid "Mobile broadband"
|
||||
msgstr "Mobiles Breitband"
|
||||
|
||||
#: ../js/ui/status/network.js:1593
|
||||
#: ../js/ui/status/network.js:1632
|
||||
msgid "VPN Connections"
|
||||
msgstr "VPN-Verbindungen"
|
||||
|
||||
#: ../js/ui/status/network.js:1605
|
||||
#: ../js/ui/status/network.js:1644
|
||||
msgid "Network Settings"
|
||||
msgstr "Netzwerkeinstellungen"
|
||||
|
||||
#: ../js/ui/status/network.js:1897
|
||||
#: ../js/ui/status/network.js:1936
|
||||
#, c-format
|
||||
msgid "You're now connected to mobile broadband connection '%s'"
|
||||
msgstr "Sie sind nun mit dem mobilen Breitbandnetzwerk »%s« verbunden."
|
||||
|
||||
#: ../js/ui/status/network.js:1901
|
||||
#: ../js/ui/status/network.js:1940
|
||||
#, c-format
|
||||
msgid "You're now connected to wireless network '%s'"
|
||||
msgstr "Sie sind nun mit dem Funknetzwerk »%s« verbunden."
|
||||
|
||||
#: ../js/ui/status/network.js:1905
|
||||
#: ../js/ui/status/network.js:1944
|
||||
#, c-format
|
||||
msgid "You're now connected to wired network '%s'"
|
||||
msgstr "Sie sind nun über Kabel mit dem Netzwerk »%s« verbunden."
|
||||
|
||||
#: ../js/ui/status/network.js:1909
|
||||
#: ../js/ui/status/network.js:1948
|
||||
#, c-format
|
||||
msgid "You're now connected to VPN network '%s'"
|
||||
msgstr "Sie sind nun mit dem VPN-Netzwerk »%s« verbunden."
|
||||
|
||||
#: ../js/ui/status/network.js:1914
|
||||
#: ../js/ui/status/network.js:1953
|
||||
#, c-format
|
||||
msgid "You're now connected to '%s'"
|
||||
msgstr "Sie sind nun mit »%s« verbunden."
|
||||
|
||||
#: ../js/ui/status/network.js:1922
|
||||
#: ../js/ui/status/network.js:1961
|
||||
msgid "Connection established"
|
||||
msgstr "Verbindung wurde aufgebaut"
|
||||
|
||||
#: ../js/ui/status/network.js:2048
|
||||
#: ../js/ui/status/network.js:2087
|
||||
msgid "Networking is disabled"
|
||||
msgstr "Netzwerk ist deaktiviert"
|
||||
|
||||
#: ../js/ui/status/network.js:2173
|
||||
#: ../js/ui/status/network.js:2212
|
||||
msgid "Network Manager"
|
||||
msgstr "Netzwerk-Verwaltung"
|
||||
|
||||
@ -1114,10 +1121,6 @@ msgstr "Tablet"
|
||||
msgid "Computer"
|
||||
msgstr "Rechner"
|
||||
|
||||
#: ../js/ui/status/power.js:238 ../src/shell-app-system.c:1088
|
||||
msgid "Unknown"
|
||||
msgstr "Unbekannt"
|
||||
|
||||
#: ../js/ui/status/volume.js:43
|
||||
msgid "Volume"
|
||||
msgstr "Lautstärke"
|
||||
@ -1129,26 +1132,36 @@ msgstr "Mikrofon"
|
||||
#. We got the TpContact
|
||||
#. FIXME: We don't have a 'chat room' icon (bgo #653737) use
|
||||
#. system-users for now as Empathy does.
|
||||
#: ../js/ui/telepathyClient.js:222
|
||||
#: ../js/ui/telepathyClient.js:234
|
||||
msgid "Invitation"
|
||||
msgstr "Einladung"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:500
|
||||
#. We got the TpContact
|
||||
#: ../js/ui/telepathyClient.js:300
|
||||
msgid "Call"
|
||||
msgstr "Anruf"
|
||||
|
||||
#. We got the TpContact
|
||||
#: ../js/ui/telepathyClient.js:328
|
||||
msgid "File Transfer"
|
||||
msgstr "Dateiübertragung"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:594
|
||||
#, c-format
|
||||
msgid "%s is online."
|
||||
msgstr "%s ist angemeldet."
|
||||
|
||||
#: ../js/ui/telepathyClient.js:505
|
||||
#: ../js/ui/telepathyClient.js:599
|
||||
#, c-format
|
||||
msgid "%s is offline."
|
||||
msgstr "%s ist abgemeldet."
|
||||
|
||||
#: ../js/ui/telepathyClient.js:508
|
||||
#: ../js/ui/telepathyClient.js:602
|
||||
#, c-format
|
||||
msgid "%s is away."
|
||||
msgstr "»%s« ist abwesend."
|
||||
|
||||
#: ../js/ui/telepathyClient.js:511
|
||||
#: ../js/ui/telepathyClient.js:605
|
||||
#, c-format
|
||||
msgid "%s is busy."
|
||||
msgstr "%s ist beschäftigt."
|
||||
@ -1156,21 +1169,35 @@ msgstr "%s ist beschäftigt."
|
||||
#. Translators: this is a time format string followed by a date.
|
||||
#. If applicable, replace %X with a strftime format valid for your
|
||||
#. locale, without seconds.
|
||||
#: ../js/ui/telepathyClient.js:700
|
||||
#: ../js/ui/telepathyClient.js:791
|
||||
#, no-c-format
|
||||
msgid "Sent at %X on %A"
|
||||
msgstr "Gesendet am %A um %X "
|
||||
msgid "Sent at <b>%X</b> on <b>%A</b>"
|
||||
msgstr "Gesendet am <b>%A</b> um <b>%X</b> "
|
||||
|
||||
#. Translators: this is a time format in the style of "Wednesday, May 25",
|
||||
#. shown when you get a chat message in the same year.
|
||||
#: ../js/ui/telepathyClient.js:797
|
||||
#, no-c-format
|
||||
msgid "Sent on <b>%A</b>, <b>%B %d</b>"
|
||||
msgstr "Gesendet am <b>%A</b>, <b>%d. %B</b>"
|
||||
|
||||
#. Translators: this is a time format in the style of "Wednesday, May 25, 2012",
|
||||
#. shown when you get a chat message in a different year.
|
||||
#: ../js/ui/telepathyClient.js:802
|
||||
#, no-c-format
|
||||
msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
|
||||
msgstr "Gesendet am <b>%A</b>, <b>%d. %B</b> %Y"
|
||||
|
||||
#. Translators: this is the other person changing their old IM name to their new
|
||||
#. IM name.
|
||||
#: ../js/ui/telepathyClient.js:750
|
||||
#: ../js/ui/telepathyClient.js:843
|
||||
#, c-format
|
||||
msgid "%s is now known as %s"
|
||||
msgstr "%s heißt jetzt %s"
|
||||
|
||||
#. translators: argument is a room name like
|
||||
#. * room@jabber.org for example.
|
||||
#: ../js/ui/telepathyClient.js:857
|
||||
#: ../js/ui/telepathyClient.js:950
|
||||
#, c-format
|
||||
msgid "Invitation to %s"
|
||||
msgstr "Einladung zum Betreten von %s"
|
||||
@ -1178,19 +1205,45 @@ msgstr "Einladung zum Betreten von %s"
|
||||
#. translators: first argument is the name of a contact and the second
|
||||
#. * one the name of a room. "Alice is inviting you to join room@jabber.org
|
||||
#. * for example.
|
||||
#: ../js/ui/telepathyClient.js:865
|
||||
#: ../js/ui/telepathyClient.js:958
|
||||
#, c-format
|
||||
msgid "%s is inviting you to join %s"
|
||||
msgstr "%s lädt Sie ein, %s beizutreten"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:867
|
||||
#: ../js/ui/telepathyClient.js:960 ../js/ui/telepathyClient.js:1049
|
||||
msgid "Decline"
|
||||
msgstr "Ablehnen"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:868
|
||||
#: ../js/ui/telepathyClient.js:961 ../js/ui/telepathyClient.js:1050
|
||||
msgid "Accept"
|
||||
msgstr "Annehmen"
|
||||
|
||||
#. translators: argument is a contact name like Alice for example.
|
||||
#: ../js/ui/telepathyClient.js:994
|
||||
#, c-format
|
||||
msgid "Video call from %s"
|
||||
msgstr "Video-Anruf von %s"
|
||||
|
||||
#. translators: argument is a contact name like Alice for example.
|
||||
#: ../js/ui/telepathyClient.js:997
|
||||
#, c-format
|
||||
msgid "Call from %s"
|
||||
msgstr "Anruf von %s"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:1007
|
||||
msgid "Answer"
|
||||
msgstr "Antworten"
|
||||
|
||||
#. To translators: The first parameter is
|
||||
#. * the contact's alias and the second one is the
|
||||
#. * file name. The string will be something
|
||||
#. * like: "Alice is sending you test.ogg"
|
||||
#.
|
||||
#: ../js/ui/telepathyClient.js:1043
|
||||
#, c-format
|
||||
msgid "%s is sending you %s"
|
||||
msgstr "%s sendet Ihnen %s"
|
||||
|
||||
#. Translators: this is the text displayed
|
||||
#. in the search entry when no search is
|
||||
#. active; it should not exceed ~30
|
||||
@ -1199,16 +1252,16 @@ msgstr "Annehmen"
|
||||
msgid "Type to search..."
|
||||
msgstr "Suchbegriff eingeben …"
|
||||
|
||||
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:257
|
||||
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:261
|
||||
msgid "Search"
|
||||
msgstr "Suchen"
|
||||
|
||||
#: ../js/ui/windowAttentionHandler.js:40
|
||||
#: ../js/ui/windowAttentionHandler.js:39
|
||||
#, c-format
|
||||
msgid "%s has finished starting"
|
||||
msgstr "Start von %s ist abgeschlossen"
|
||||
|
||||
#: ../js/ui/windowAttentionHandler.js:42
|
||||
#: ../js/ui/windowAttentionHandler.js:41
|
||||
#, c-format
|
||||
msgid "'%s' is ready"
|
||||
msgstr "»%s« ist bereit"
|
||||
@ -1239,7 +1292,7 @@ msgstr "Systemklänge"
|
||||
msgid "Print version"
|
||||
msgstr "Version ausgeben"
|
||||
|
||||
#: ../src/shell-app.c:464
|
||||
#: ../src/shell-app.c:580
|
||||
#, c-format
|
||||
msgid "Failed to launch '%s'"
|
||||
msgstr "»%s« konnte nicht gestartet werden"
|
||||
@ -1256,13 +1309,13 @@ msgstr "Vorgabe"
|
||||
msgid "Authentication dialog was dismissed by the user"
|
||||
msgstr "Der Dialog zur Legitimierung wurde vom Benutzer geschlossen"
|
||||
|
||||
#: ../src/shell-util.c:96
|
||||
#: ../src/shell-util.c:100
|
||||
msgid "Home Folder"
|
||||
msgstr "Persönlicher Ordner"
|
||||
|
||||
#. Translators: this is the same string as the one found in
|
||||
#. * nautilus
|
||||
#: ../src/shell-util.c:111
|
||||
#: ../src/shell-util.c:115
|
||||
msgid "File System"
|
||||
msgstr "Dateisystem"
|
||||
|
||||
@ -1271,11 +1324,31 @@ msgstr "Dateisystem"
|
||||
#. * example, "Trash: some-directory". It means that the
|
||||
#. * directory called "some-directory" is in the trash.
|
||||
#.
|
||||
#: ../src/shell-util.c:307
|
||||
#: ../src/shell-util.c:311
|
||||
#, c-format
|
||||
msgid "%1$s: %2$s"
|
||||
msgstr "%1$s: %2$s"
|
||||
|
||||
#: ../src/shell-util.c:600
|
||||
msgid "calendar:week_start:0"
|
||||
msgstr "calendar:week_start:0"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "GNOME Shell extensions have a uuid property; this key lists extensions "
|
||||
#~ "which should not be loaded. This setting overrides enabled-extensions for "
|
||||
#~ "extensions that appear in both lists."
|
||||
#~ msgstr ""
|
||||
#~ "Die Erweiterungen der GNOME-Shell besitzen eine UUID-Eigenschaft. Dieser "
|
||||
#~ "Schlüssel listet Erweiterungen auf, welche nicht geladen werden sollen. "
|
||||
#~ "Diese Einstellung überschreibt »enabled-extensions« für Erweiterungen, die "
|
||||
#~ "in beiden Listen erscheinen."
|
||||
|
||||
#~ msgid "Uuids of extensions to disable"
|
||||
#~ msgstr "UUIDs der zu deaktivierenden Erweiterungen"
|
||||
|
||||
#~ msgid "Localization Settings"
|
||||
#~ msgstr "Lokalisierungseinstellungen"
|
||||
|
||||
#~ msgid "Less than a minute ago"
|
||||
#~ msgstr "Vor weniger als einer Minute"
|
||||
|
||||
|
470
po/gl.po
470
po/gl.po
@ -12,8 +12,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: gnome-shell master\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2011-07-04 22:20+0200\n"
|
||||
"PO-Revision-Date: 2011-07-04 22:20+0200\n"
|
||||
"POT-Creation-Date: 2011-08-24 21:18+0200\n"
|
||||
"PO-Revision-Date: 2011-08-24 21:19+0200\n"
|
||||
"Last-Translator: Fran Diéguez <frandieguez@gnome.org>\n"
|
||||
"Language-Team: Galician <gnome-l10n-gl@gnome.org>\n"
|
||||
"Language: gl\n"
|
||||
@ -64,40 +64,30 @@ msgstr ""
|
||||
"axuste para as extensións que aparecen en ámbalas dúas listas."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:6
|
||||
msgid ""
|
||||
"GNOME Shell extensions have a uuid property; this key lists extensions which "
|
||||
"should not be loaded. This setting overrides enabled-extensions for "
|
||||
"extensions that appear in both lists."
|
||||
msgstr ""
|
||||
"As extensións do GNOME Shell teñen unha propiedade uuid. Esta chave lista as "
|
||||
"extensións que deberían cargarse. Esta configuración sobrescribe a «enabled-"
|
||||
"extensions» para as extensións que aparecen en ámbalas dúas listas."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:7
|
||||
msgid "History for command (Alt-F2) dialog"
|
||||
msgstr "Historial do diálogo de orde (Alt-F2)"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:8
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:7
|
||||
msgid "History for the looking glass dialog"
|
||||
msgstr "Historial do diálogo de «looking glass»"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:9
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:8
|
||||
msgid "If true, display date in the clock, in addition to time."
|
||||
msgstr "Se é verdadeiro, móstrase a data no reloxo, ademais da hora."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:10
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:9
|
||||
msgid "If true, display seconds in time."
|
||||
msgstr "Se é verdadeiro, móstranse os segundos na hora."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:11
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:10
|
||||
msgid "If true, display the ISO week date in the calendar."
|
||||
msgstr "Se é verdadeiro, móstrase a data da semana ISO no calendario."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:12
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:11
|
||||
msgid "List of desktop file IDs for favorite applications"
|
||||
msgstr "Mostra os ID de ficheiros desktop para os aplicativos preferidos"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:14
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:13
|
||||
#, no-c-format
|
||||
msgid ""
|
||||
"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
|
||||
@ -123,19 +113,19 @@ msgstr ""
|
||||
"códec VP8. Úsase %T como suposición para o número de fillos óptimos no "
|
||||
"sistema."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:15
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:14
|
||||
msgid "Show date in clock"
|
||||
msgstr "Mostrar a data no reloxo"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:16
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:15
|
||||
msgid "Show the week date in the calendar"
|
||||
msgstr "Mostrar a data da semana no calendario"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:17
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:16
|
||||
msgid "Show time with seconds"
|
||||
msgstr "Mostrar a hora con segundos"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:18
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:17
|
||||
msgid ""
|
||||
"The applications corresponding to these identifiers will be displayed in the "
|
||||
"favorites area."
|
||||
@ -143,7 +133,7 @@ msgstr ""
|
||||
"Os aplicativos que corresponden a estes identificadores mostraranse na área "
|
||||
"de preferidos."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:19
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:18
|
||||
msgid ""
|
||||
"The filename for recorded screencasts will be a unique filename based on the "
|
||||
"current date, and use this extension. It should be changed when recording to "
|
||||
@ -153,7 +143,7 @@ msgstr ""
|
||||
"baseado na data actual e usa esta extensión. Debería cambiar ao grabar nun "
|
||||
"formato de contedor diferente."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:20
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:19
|
||||
msgid ""
|
||||
"The framerate of the resulting screencast recordered by GNOME Shell's "
|
||||
"screencast recorder in frames-per-second."
|
||||
@ -161,11 +151,11 @@ msgstr ""
|
||||
"A taxa de marcos do screencast resultante grabado polo grabador de "
|
||||
"screencasts de GNOME Shell en marcos-por-segundo."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:21
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:20
|
||||
msgid "The gstreamer pipeline used to encode the screencast"
|
||||
msgstr "A tubería de gstreamer usada para codificar o screencast"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:22
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:21
|
||||
msgid ""
|
||||
"The shell normally monitors active applications in order to present the most "
|
||||
"used ones (e.g. in launchers). While this data will be kept private, you may "
|
||||
@ -177,19 +167,15 @@ msgstr ""
|
||||
"privados, vostede pode desactivar isto por motivos de privacidade. Teña en "
|
||||
"conta que facendo isto non eliminará os datos gardados."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:23
|
||||
msgid "Uuids of extensions to disable"
|
||||
msgstr "Uuid das extensións que activar"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:24
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:22
|
||||
msgid "Uuids of extensions to enable"
|
||||
msgstr "Uuid das extensións que activar"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:25
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:23
|
||||
msgid "Whether to collect stats about applications usage"
|
||||
msgstr "Indica se recoller estatísticas sobre o uso dos aplicativos"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:26
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:24
|
||||
msgid "disabled OpenSearch providers"
|
||||
msgstr "fornecedores de OpenSearch desactivados"
|
||||
|
||||
@ -209,27 +195,27 @@ msgid "Execution of '%s' failed:"
|
||||
msgstr "Produciuse un fallo na execución de «%s»:"
|
||||
|
||||
#. Translators: Filter to display all applications
|
||||
#: ../js/ui/appDisplay.js:258
|
||||
#: ../js/ui/appDisplay.js:253
|
||||
msgid "All"
|
||||
msgstr "Todos"
|
||||
|
||||
#: ../js/ui/appDisplay.js:357
|
||||
#: ../js/ui/appDisplay.js:315
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "APLICATIVOS"
|
||||
|
||||
#: ../js/ui/appDisplay.js:383
|
||||
#: ../js/ui/appDisplay.js:373
|
||||
msgid "SETTINGS"
|
||||
msgstr "CONFIGURACIÓN"
|
||||
|
||||
#: ../js/ui/appDisplay.js:656
|
||||
#: ../js/ui/appDisplay.js:681
|
||||
msgid "New Window"
|
||||
msgstr "Xanela nova"
|
||||
|
||||
#: ../js/ui/appDisplay.js:659
|
||||
#: ../js/ui/appDisplay.js:684
|
||||
msgid "Remove from Favorites"
|
||||
msgstr "Eliminar dos favoritos"
|
||||
|
||||
#: ../js/ui/appDisplay.js:660
|
||||
#: ../js/ui/appDisplay.js:685
|
||||
msgid "Add to Favorites"
|
||||
msgstr "Engadir aos favoritos"
|
||||
|
||||
@ -243,6 +229,15 @@ msgstr "%s foi engadido aos seus favoritos."
|
||||
msgid "%s has been removed from your favorites."
|
||||
msgstr "%s foi eliminado dos seus favoritos."
|
||||
|
||||
#: ../js/ui/autorunManager.js:591
|
||||
#, c-format
|
||||
msgid "Open with %s"
|
||||
msgstr "Abrir con %s"
|
||||
|
||||
#: ../js/ui/autorunManager.js:617
|
||||
msgid "Eject"
|
||||
msgstr "Expulsar"
|
||||
|
||||
#. Translators: Shown in calendar event list for all day events
|
||||
#. * Keep it short, best if you can use less then 10 characters
|
||||
#.
|
||||
@ -357,94 +352,94 @@ msgid "S"
|
||||
msgstr "S"
|
||||
|
||||
#. Translators: Text to show if there are no events
|
||||
#: ../js/ui/calendar.js:701
|
||||
#: ../js/ui/calendar.js:678
|
||||
msgid "Nothing Scheduled"
|
||||
msgstr "Nada programado"
|
||||
|
||||
#. Translators: Shown on calendar heading when selected day occurs on current year
|
||||
#: ../js/ui/calendar.js:717 ../js/ui/telepathyClient.js:623
|
||||
#: ../js/ui/calendar.js:694
|
||||
msgctxt "calendar heading"
|
||||
msgid "%A, %B %d"
|
||||
msgstr "%A, %d de %B"
|
||||
|
||||
#. Translators: Shown on calendar heading when selected day occurs on different year
|
||||
#: ../js/ui/calendar.js:720 ../js/ui/telepathyClient.js:626
|
||||
#: ../js/ui/calendar.js:697
|
||||
msgctxt "calendar heading"
|
||||
msgid "%A, %B %d, %Y"
|
||||
msgstr "%A, %d de %B de %Y"
|
||||
|
||||
#: ../js/ui/calendar.js:730
|
||||
#: ../js/ui/calendar.js:707
|
||||
msgid "Today"
|
||||
msgstr "Hoxe"
|
||||
|
||||
#: ../js/ui/calendar.js:734
|
||||
#: ../js/ui/calendar.js:711
|
||||
msgid "Tomorrow"
|
||||
msgstr "Mañá"
|
||||
|
||||
#: ../js/ui/calendar.js:743
|
||||
#: ../js/ui/calendar.js:720
|
||||
msgid "This week"
|
||||
msgstr "Esta semana"
|
||||
|
||||
#: ../js/ui/calendar.js:751
|
||||
#: ../js/ui/calendar.js:728
|
||||
msgid "Next week"
|
||||
msgstr "A vindeira semana"
|
||||
|
||||
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1045
|
||||
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1123
|
||||
msgid "Remove"
|
||||
msgstr "Eliminar"
|
||||
|
||||
#: ../js/ui/dateMenu.js:89
|
||||
#: ../js/ui/dateMenu.js:90
|
||||
msgid "Date and Time Settings"
|
||||
msgstr "Configuracións do data e hora"
|
||||
|
||||
#: ../js/ui/dateMenu.js:109
|
||||
#: ../js/ui/dateMenu.js:110
|
||||
msgid "Open Calendar"
|
||||
msgstr "Abrir o calendario"
|
||||
|
||||
#. Translators: This is the time format with date used
|
||||
#. in 24-hour mode.
|
||||
#: ../js/ui/dateMenu.js:162
|
||||
#: ../js/ui/dateMenu.js:167
|
||||
msgid "%a %b %e, %R:%S"
|
||||
msgstr "%a %e de %b, %R:%S"
|
||||
|
||||
#: ../js/ui/dateMenu.js:163
|
||||
#: ../js/ui/dateMenu.js:168
|
||||
msgid "%a %b %e, %R"
|
||||
msgstr "%a %e de %b, %R"
|
||||
|
||||
#. Translators: This is the time format without date used
|
||||
#. in 24-hour mode.
|
||||
#: ../js/ui/dateMenu.js:167
|
||||
#: ../js/ui/dateMenu.js:172
|
||||
msgid "%a %R:%S"
|
||||
msgstr "%a %R:%S"
|
||||
|
||||
#: ../js/ui/dateMenu.js:168
|
||||
#: ../js/ui/dateMenu.js:173
|
||||
msgid "%a %R"
|
||||
msgstr "%a %R"
|
||||
|
||||
#. Translators: This is a time format with date used
|
||||
#. for AM/PM.
|
||||
#: ../js/ui/dateMenu.js:175
|
||||
#: ../js/ui/dateMenu.js:180
|
||||
msgid "%a %b %e, %l:%M:%S %p"
|
||||
msgstr "%a %e de %b, %l:%M:%S"
|
||||
|
||||
#: ../js/ui/dateMenu.js:176
|
||||
#: ../js/ui/dateMenu.js:181
|
||||
msgid "%a %b %e, %l:%M %p"
|
||||
msgstr "%a %e de %b, %l:%M %p"
|
||||
|
||||
#. Translators: This is a time format without date used
|
||||
#. for AM/PM.
|
||||
#: ../js/ui/dateMenu.js:180
|
||||
#: ../js/ui/dateMenu.js:185
|
||||
msgid "%a %l:%M:%S %p"
|
||||
msgstr "%a %l:%M:%S %p"
|
||||
|
||||
#: ../js/ui/dateMenu.js:181
|
||||
#: ../js/ui/dateMenu.js:186
|
||||
msgid "%a %l:%M %p"
|
||||
msgstr "%a %l:%M %p"
|
||||
|
||||
#. 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:192
|
||||
#: ../js/ui/dateMenu.js:197
|
||||
msgid "%A %B %e, %Y"
|
||||
msgstr "%a, %e de %B, %Y"
|
||||
|
||||
@ -523,44 +518,53 @@ msgstr "Reiniciando o computador."
|
||||
msgid "Cancel"
|
||||
msgstr "Cancelar"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:641
|
||||
#: ../js/ui/lookingGlass.js:640
|
||||
msgid "No extensions installed"
|
||||
msgstr "Non hai ningunha extensión instalada"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:678
|
||||
#: ../js/ui/lookingGlass.js:686
|
||||
msgid "Enabled"
|
||||
msgstr "Activado"
|
||||
|
||||
#. translators:
|
||||
#. * The device has been disabled
|
||||
#: ../js/ui/lookingGlass.js:680 ../src/gvc/gvc-mixer-control.c:1091
|
||||
#: ../js/ui/lookingGlass.js:688 ../src/gvc/gvc-mixer-control.c:1091
|
||||
msgid "Disabled"
|
||||
msgstr "Desactivado"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:682
|
||||
#: ../js/ui/lookingGlass.js:690
|
||||
msgid "Error"
|
||||
msgstr "Erro"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:684
|
||||
#: ../js/ui/lookingGlass.js:692
|
||||
msgid "Out of date"
|
||||
msgstr "Desactualizado"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:709
|
||||
#: ../js/ui/lookingGlass.js:694
|
||||
msgid "Downloading"
|
||||
msgstr "Descargando"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:719
|
||||
msgid "View Source"
|
||||
msgstr "Ver fonte"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:715
|
||||
#: ../js/ui/lookingGlass.js:725
|
||||
msgid "Web Page"
|
||||
msgstr "Páxina web"
|
||||
|
||||
#: ../js/ui/messageTray.js:1038
|
||||
#: ../js/ui/messageTray.js:1116
|
||||
msgid "Open"
|
||||
msgstr "Abrir"
|
||||
|
||||
#: ../js/ui/messageTray.js:2210
|
||||
#: ../js/ui/messageTray.js:2277
|
||||
msgid "System Information"
|
||||
msgstr "Información do sistema"
|
||||
|
||||
#: ../js/ui/notificationDaemon.js:427 ../js/ui/status/power.js:238
|
||||
#: ../src/shell-app.c:354
|
||||
msgid "Unknown"
|
||||
msgstr "Descoñecido"
|
||||
|
||||
#: ../js/ui/overview.js:89
|
||||
msgid "Undo"
|
||||
msgstr "Desfacer"
|
||||
@ -580,18 +584,18 @@ msgid "Dash"
|
||||
msgstr "Panel"
|
||||
|
||||
#. TODO - _quit() doesn't really work on apps in state STARTING yet
|
||||
#: ../js/ui/panel.js:533
|
||||
#: ../js/ui/panel.js:531
|
||||
#, c-format
|
||||
msgid "Quit %s"
|
||||
msgstr "Saír de %s"
|
||||
|
||||
#. Button on the left side of the panel.
|
||||
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
|
||||
#: ../js/ui/panel.js:913
|
||||
#. Translators: If there is no suitable word for "Activities"
|
||||
#. in your language, you can use the word for "Overview".
|
||||
#: ../js/ui/panel.js:567
|
||||
msgid "Activities"
|
||||
msgstr "Actividades"
|
||||
|
||||
#: ../js/ui/panel.js:1013
|
||||
#: ../js/ui/panel.js:876
|
||||
msgid "Top Bar"
|
||||
msgstr "Barra superior"
|
||||
|
||||
@ -657,43 +661,9 @@ msgstr "Buscando..."
|
||||
msgid "No matching results."
|
||||
msgstr "Non hai resultados que coincidan."
|
||||
|
||||
#: ../js/ui/statusMenu.js:202 ../js/ui/statusMenu.js:206
|
||||
#: ../js/ui/statusMenu.js:272
|
||||
msgid "Power Off..."
|
||||
msgstr "Apagar…"
|
||||
|
||||
#: ../js/ui/statusMenu.js:204 ../js/ui/statusMenu.js:206
|
||||
#: ../js/ui/statusMenu.js:271
|
||||
msgid "Suspend"
|
||||
msgstr "Suspender"
|
||||
|
||||
#: ../js/ui/statusMenu.js:227
|
||||
msgid "Available"
|
||||
msgstr "Dispoñíbel"
|
||||
|
||||
#: ../js/ui/statusMenu.js:232
|
||||
msgid "Busy"
|
||||
msgstr "Ocupado"
|
||||
|
||||
#: ../js/ui/statusMenu.js:240
|
||||
msgid "My Account"
|
||||
msgstr "A miña conta"
|
||||
|
||||
#: ../js/ui/statusMenu.js:244
|
||||
msgid "System Settings"
|
||||
msgstr "Configuracións do sistema"
|
||||
|
||||
#: ../js/ui/statusMenu.js:252
|
||||
msgid "Lock Screen"
|
||||
msgstr "Bloquear pantalla"
|
||||
|
||||
#: ../js/ui/statusMenu.js:257
|
||||
msgid "Switch User"
|
||||
msgstr "Cambiar de usuario"
|
||||
|
||||
#: ../js/ui/statusMenu.js:262
|
||||
msgid "Log Out..."
|
||||
msgstr "Saír da sesión…"
|
||||
#: ../js/ui/shellMountOperation.js:285
|
||||
msgid "Wrong password, please try again"
|
||||
msgstr "O contrasinal é incorrecto, ténteo de novo"
|
||||
|
||||
#: ../js/ui/status/accessibility.js:60
|
||||
msgid "Zoom"
|
||||
@ -768,11 +738,11 @@ msgstr "hardware desactivado"
|
||||
msgid "Connection"
|
||||
msgstr "Conexión"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:226 ../js/ui/status/network.js:493
|
||||
#: ../js/ui/status/bluetooth.js:226 ../js/ui/status/network.js:497
|
||||
msgid "disconnecting..."
|
||||
msgstr "conectando…"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:239 ../js/ui/status/network.js:499
|
||||
#: ../js/ui/status/bluetooth.js:239 ../js/ui/status/network.js:503
|
||||
msgid "connecting..."
|
||||
msgstr "conectando…"
|
||||
|
||||
@ -823,7 +793,7 @@ msgstr "Conceder acceso sempre"
|
||||
msgid "Grant this time only"
|
||||
msgstr "Conceder só esta vez"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:409
|
||||
#: ../js/ui/status/bluetooth.js:409 ../js/ui/telepathyClient.js:1006
|
||||
msgid "Reject"
|
||||
msgstr "Rexeitar"
|
||||
|
||||
@ -864,148 +834,186 @@ msgid "OK"
|
||||
msgstr "Aceptar"
|
||||
|
||||
#: ../js/ui/status/keyboard.js:71
|
||||
msgid "Show Keyboard Layout..."
|
||||
msgstr "Mostrar a distribución do teclado…"
|
||||
msgid "Show Keyboard Layout"
|
||||
msgstr "Mostrar a distribución do teclado"
|
||||
|
||||
#: ../js/ui/status/keyboard.js:75
|
||||
msgid "Localization Settings"
|
||||
msgstr "Configuracións do son"
|
||||
msgid "Region and Language Settings"
|
||||
msgstr "Configuración rexional e de idioma"
|
||||
|
||||
#: ../js/ui/status/network.js:123
|
||||
#: ../js/ui/statusMenu.js:192 ../js/ui/statusMenu.js:196
|
||||
#: ../js/ui/statusMenu.js:262
|
||||
msgid "Power Off..."
|
||||
msgstr "Apagar…"
|
||||
|
||||
#: ../js/ui/statusMenu.js:194 ../js/ui/statusMenu.js:196
|
||||
#: ../js/ui/statusMenu.js:261
|
||||
msgid "Suspend"
|
||||
msgstr "Suspender"
|
||||
|
||||
#: ../js/ui/statusMenu.js:217
|
||||
msgid "Available"
|
||||
msgstr "Dispoñíbel"
|
||||
|
||||
#: ../js/ui/statusMenu.js:222
|
||||
msgid "Busy"
|
||||
msgstr "Ocupado"
|
||||
|
||||
#: ../js/ui/statusMenu.js:230
|
||||
msgid "My Account"
|
||||
msgstr "A miña conta"
|
||||
|
||||
#: ../js/ui/statusMenu.js:234
|
||||
msgid "System Settings"
|
||||
msgstr "Configuracións do sistema"
|
||||
|
||||
#: ../js/ui/statusMenu.js:242
|
||||
msgid "Lock Screen"
|
||||
msgstr "Bloquear pantalla"
|
||||
|
||||
#: ../js/ui/statusMenu.js:247
|
||||
msgid "Switch User"
|
||||
msgstr "Cambiar de usuario"
|
||||
|
||||
#: ../js/ui/statusMenu.js:252
|
||||
msgid "Log Out..."
|
||||
msgstr "Saír da sesión…"
|
||||
|
||||
#: ../js/ui/status/network.js:108
|
||||
msgid "<unknown>"
|
||||
msgstr "<descoñecida>"
|
||||
|
||||
#. Translators: this indicates that wireless or wwan is disabled by hardware killswitch
|
||||
#: ../js/ui/status/network.js:292
|
||||
#: ../js/ui/status/network.js:296
|
||||
msgid "disabled"
|
||||
msgstr "desactivada"
|
||||
|
||||
#. Translators: this is for network devices that are physically present but are not
|
||||
#. under NetworkManager's control (and thus cannot be used in the menu)
|
||||
#: ../js/ui/status/network.js:491
|
||||
#: ../js/ui/status/network.js:495
|
||||
msgid "unmanaged"
|
||||
msgstr "non xestionada"
|
||||
|
||||
#. Translators: this is for network connections that require some kind of key or password
|
||||
#: ../js/ui/status/network.js:502
|
||||
#: ../js/ui/status/network.js:506
|
||||
msgid "authentication required"
|
||||
msgstr "autenticación requirida"
|
||||
|
||||
#. Translators: this is for devices that require some kind of firmware or kernel
|
||||
#. module, which is missing
|
||||
#: ../js/ui/status/network.js:512
|
||||
#: ../js/ui/status/network.js:516
|
||||
msgid "firmware missing"
|
||||
msgstr "falta o «firmware»"
|
||||
|
||||
#. Translators: this is for wired network devices that are physically disconnected
|
||||
#: ../js/ui/status/network.js:519
|
||||
#: ../js/ui/status/network.js:523
|
||||
msgid "cable unplugged"
|
||||
msgstr "cable desconectado"
|
||||
|
||||
#. Translators: this is for a network device that cannot be activated (for example it
|
||||
#. is disabled by rfkill, or it has no coverage
|
||||
#: ../js/ui/status/network.js:524
|
||||
#: ../js/ui/status/network.js:528
|
||||
msgid "unavailable"
|
||||
msgstr "non dispoñíbel"
|
||||
|
||||
#: ../js/ui/status/network.js:526
|
||||
#: ../js/ui/status/network.js:530
|
||||
msgid "connection failed"
|
||||
msgstr "conexión fallida"
|
||||
|
||||
#: ../js/ui/status/network.js:582 ../js/ui/status/network.js:1489
|
||||
#: ../js/ui/status/network.js:586 ../js/ui/status/network.js:1546
|
||||
msgid "More..."
|
||||
msgstr "Máis..."
|
||||
|
||||
#. 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:618 ../js/ui/status/network.js:1427
|
||||
#: ../js/ui/status/network.js:622 ../js/ui/status/network.js:1483
|
||||
msgid "Connected (private)"
|
||||
msgstr "Conectado (privado)"
|
||||
|
||||
#: ../js/ui/status/network.js:703
|
||||
#: ../js/ui/status/network.js:707
|
||||
msgid "Auto Ethernet"
|
||||
msgstr "Ethernet automática"
|
||||
|
||||
#: ../js/ui/status/network.js:771
|
||||
#: ../js/ui/status/network.js:775
|
||||
msgid "Auto broadband"
|
||||
msgstr "Banda larga automática"
|
||||
|
||||
#: ../js/ui/status/network.js:774
|
||||
#: ../js/ui/status/network.js:778
|
||||
msgid "Auto dial-up"
|
||||
msgstr "Por liña conmutada automática"
|
||||
|
||||
#. TRANSLATORS: this the automatic wireless connection name (including the network name)
|
||||
#: ../js/ui/status/network.js:898 ../js/ui/status/network.js:1439
|
||||
#: ../js/ui/status/network.js:902 ../js/ui/status/network.js:1495
|
||||
#, c-format
|
||||
msgid "Auto %s"
|
||||
msgstr "%s automática"
|
||||
|
||||
#: ../js/ui/status/network.js:900
|
||||
#: ../js/ui/status/network.js:904
|
||||
msgid "Auto bluetooth"
|
||||
msgstr "Bluetooth automática"
|
||||
|
||||
#: ../js/ui/status/network.js:1441
|
||||
#: ../js/ui/status/network.js:1497
|
||||
msgid "Auto wireless"
|
||||
msgstr "Sen fíos automática"
|
||||
|
||||
#: ../js/ui/status/network.js:1531
|
||||
#: ../js/ui/status/network.js:1589
|
||||
msgid "Enable networking"
|
||||
msgstr "Activar rede"
|
||||
|
||||
#: ../js/ui/status/network.js:1543
|
||||
#: ../js/ui/status/network.js:1601
|
||||
msgid "Wired"
|
||||
msgstr "Con fíos"
|
||||
|
||||
#: ../js/ui/status/network.js:1554
|
||||
#: ../js/ui/status/network.js:1612
|
||||
msgid "Wireless"
|
||||
msgstr "Sen fíos"
|
||||
|
||||
#: ../js/ui/status/network.js:1564
|
||||
#: ../js/ui/status/network.js:1622
|
||||
msgid "Mobile broadband"
|
||||
msgstr "Banda larga móbil"
|
||||
|
||||
#: ../js/ui/status/network.js:1574
|
||||
#: ../js/ui/status/network.js:1632
|
||||
msgid "VPN Connections"
|
||||
msgstr "Conexións VPN"
|
||||
|
||||
#: ../js/ui/status/network.js:1586
|
||||
#: ../js/ui/status/network.js:1644
|
||||
msgid "Network Settings"
|
||||
msgstr "Configuracións da rede"
|
||||
|
||||
#: ../js/ui/status/network.js:1878
|
||||
#: ../js/ui/status/network.js:1936
|
||||
#, c-format
|
||||
msgid "You're now connected to mobile broadband connection '%s'"
|
||||
msgstr "Vostede está conectado agora á conexión de banda larga móbil «%s»"
|
||||
|
||||
#: ../js/ui/status/network.js:1882
|
||||
#: ../js/ui/status/network.js:1940
|
||||
#, c-format
|
||||
msgid "You're now connected to wireless network '%s'"
|
||||
msgstr "Vostede está conectado agora á conexión sen fíos «%s»"
|
||||
|
||||
#: ../js/ui/status/network.js:1886
|
||||
#: ../js/ui/status/network.js:1944
|
||||
#, c-format
|
||||
msgid "You're now connected to wired network '%s'"
|
||||
msgstr "Vostede está conectado agora á conexión con fíos «%s»"
|
||||
|
||||
#: ../js/ui/status/network.js:1890
|
||||
#: ../js/ui/status/network.js:1948
|
||||
#, c-format
|
||||
msgid "You're now connected to VPN network '%s'"
|
||||
msgstr "Vostede está conectado agora á conexión VPN «%s»"
|
||||
|
||||
#: ../js/ui/status/network.js:1895
|
||||
#: ../js/ui/status/network.js:1953
|
||||
#, c-format
|
||||
msgid "You're now connected to '%s'"
|
||||
msgstr "Vostede está conectado agora a «%s»"
|
||||
|
||||
#: ../js/ui/status/network.js:1903
|
||||
#: ../js/ui/status/network.js:1961
|
||||
msgid "Connection established"
|
||||
msgstr "Conexión estabelecida"
|
||||
|
||||
#: ../js/ui/status/network.js:2029
|
||||
#: ../js/ui/status/network.js:2087
|
||||
msgid "Networking is disabled"
|
||||
msgstr "A rede está desactivada"
|
||||
|
||||
#: ../js/ui/status/network.js:2154
|
||||
#: ../js/ui/status/network.js:2212
|
||||
msgid "Network Manager"
|
||||
msgstr "Xestor de rede"
|
||||
|
||||
@ -1015,11 +1023,11 @@ msgstr "Configuracións de enerxía"
|
||||
|
||||
#. 0 is reported when UPower does not have enough data
|
||||
#. to estimate battery life
|
||||
#: ../js/ui/status/power.js:110
|
||||
#: ../js/ui/status/power.js:109
|
||||
msgid "Estimating..."
|
||||
msgstr "Estimando…"
|
||||
|
||||
#: ../js/ui/status/power.js:117
|
||||
#: ../js/ui/status/power.js:116
|
||||
#, c-format
|
||||
msgid "%d hour remaining"
|
||||
msgid_plural "%d hours remaining"
|
||||
@ -1027,78 +1035,74 @@ msgstr[0] "%d hora restante"
|
||||
msgstr[1] "%d horas restante"
|
||||
|
||||
#. TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining"
|
||||
#: ../js/ui/status/power.js:120
|
||||
#: ../js/ui/status/power.js:119
|
||||
#, c-format
|
||||
msgid "%d %s %d %s remaining"
|
||||
msgstr "%d %s %d %s retante"
|
||||
|
||||
#: ../js/ui/status/power.js:122
|
||||
#: ../js/ui/status/power.js:121
|
||||
msgid "hour"
|
||||
msgid_plural "hours"
|
||||
msgstr[0] "hora"
|
||||
msgstr[1] "horas"
|
||||
|
||||
#: ../js/ui/status/power.js:122
|
||||
#: ../js/ui/status/power.js:121
|
||||
msgid "minute"
|
||||
msgid_plural "minutes"
|
||||
msgstr[0] "minuto"
|
||||
msgstr[1] "minutos"
|
||||
|
||||
#: ../js/ui/status/power.js:125
|
||||
#: ../js/ui/status/power.js:124
|
||||
#, c-format
|
||||
msgid "%d minute remaining"
|
||||
msgid_plural "%d minutes remaining"
|
||||
msgstr[0] "%d minuto restante"
|
||||
msgstr[1] "%d minutos restantes"
|
||||
|
||||
#: ../js/ui/status/power.js:227
|
||||
#: ../js/ui/status/power.js:216
|
||||
msgid "AC adapter"
|
||||
msgstr "Adaptador de corrente"
|
||||
|
||||
#: ../js/ui/status/power.js:229
|
||||
#: ../js/ui/status/power.js:218
|
||||
msgid "Laptop battery"
|
||||
msgstr "Batería do portátil"
|
||||
|
||||
#: ../js/ui/status/power.js:231
|
||||
#: ../js/ui/status/power.js:220
|
||||
msgid "UPS"
|
||||
msgstr "UPS"
|
||||
|
||||
#: ../js/ui/status/power.js:233
|
||||
#: ../js/ui/status/power.js:222
|
||||
msgid "Monitor"
|
||||
msgstr "Monitor"
|
||||
|
||||
#: ../js/ui/status/power.js:235
|
||||
#: ../js/ui/status/power.js:224
|
||||
msgid "Mouse"
|
||||
msgstr "Rato"
|
||||
|
||||
#: ../js/ui/status/power.js:237
|
||||
#: ../js/ui/status/power.js:226
|
||||
msgid "Keyboard"
|
||||
msgstr "Teclado"
|
||||
|
||||
#: ../js/ui/status/power.js:239
|
||||
#: ../js/ui/status/power.js:228
|
||||
msgid "PDA"
|
||||
msgstr "PDA"
|
||||
|
||||
#: ../js/ui/status/power.js:241
|
||||
#: ../js/ui/status/power.js:230
|
||||
msgid "Cell phone"
|
||||
msgstr "Teléfono móbil"
|
||||
|
||||
#: ../js/ui/status/power.js:243
|
||||
#: ../js/ui/status/power.js:232
|
||||
msgid "Media player"
|
||||
msgstr "Reprodutor multimedia"
|
||||
|
||||
#: ../js/ui/status/power.js:245
|
||||
#: ../js/ui/status/power.js:234
|
||||
msgid "Tablet"
|
||||
msgstr "Tablet"
|
||||
|
||||
#: ../js/ui/status/power.js:247
|
||||
#: ../js/ui/status/power.js:236
|
||||
msgid "Computer"
|
||||
msgstr "Computador"
|
||||
|
||||
#: ../js/ui/status/power.js:249 ../src/shell-app-system.c:1088
|
||||
msgid "Unknown"
|
||||
msgstr "Descoñecido"
|
||||
|
||||
#: ../js/ui/status/volume.js:43
|
||||
msgid "Volume"
|
||||
msgstr "Volume"
|
||||
@ -1107,22 +1111,39 @@ msgstr "Volume"
|
||||
msgid "Microphone"
|
||||
msgstr "Micrófono"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:419
|
||||
#. We got the TpContact
|
||||
#. FIXME: We don't have a 'chat room' icon (bgo #653737) use
|
||||
#. system-users for now as Empathy does.
|
||||
#: ../js/ui/telepathyClient.js:234
|
||||
msgid "Invitation"
|
||||
msgstr "Convite"
|
||||
|
||||
#. We got the TpContact
|
||||
#: ../js/ui/telepathyClient.js:300
|
||||
msgid "Call"
|
||||
msgstr "Chamar"
|
||||
|
||||
#. We got the TpContact
|
||||
#: ../js/ui/telepathyClient.js:328
|
||||
msgid "File Transfer"
|
||||
msgstr "Transferencia de ficheiro"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:594
|
||||
#, c-format
|
||||
msgid "%s is online."
|
||||
msgstr "%s está conectado/a."
|
||||
|
||||
#: ../js/ui/telepathyClient.js:424
|
||||
#: ../js/ui/telepathyClient.js:599
|
||||
#, c-format
|
||||
msgid "%s is offline."
|
||||
msgstr "%s está desconectado/a."
|
||||
|
||||
#: ../js/ui/telepathyClient.js:427
|
||||
#: ../js/ui/telepathyClient.js:602
|
||||
#, c-format
|
||||
msgid "%s is away."
|
||||
msgstr "%s está ausente."
|
||||
|
||||
#: ../js/ui/telepathyClient.js:430
|
||||
#: ../js/ui/telepathyClient.js:605
|
||||
#, c-format
|
||||
msgid "%s is busy."
|
||||
msgstr "%s está ocupado/a."
|
||||
@ -1130,18 +1151,81 @@ msgstr "%s está ocupado/a."
|
||||
#. Translators: this is a time format string followed by a date.
|
||||
#. If applicable, replace %X with a strftime format valid for your
|
||||
#. locale, without seconds.
|
||||
#: ../js/ui/telepathyClient.js:615
|
||||
#: ../js/ui/telepathyClient.js:791
|
||||
#, no-c-format
|
||||
msgid "Sent at %X on %A"
|
||||
msgstr "Enviado ás %X o %A"
|
||||
msgid "Sent at <b>%X</b> on <b>%A</b>"
|
||||
msgstr "Enviado ás <b>%X</b> o <b>%A</b>"
|
||||
|
||||
#. Translators: this is a time format in the style of "Wednesday, May 25",
|
||||
#. shown when you get a chat message in the same year.
|
||||
#: ../js/ui/telepathyClient.js:797
|
||||
#, no-c-format
|
||||
msgid "Sent on <b>%A</b>, <b>%B %d</b>"
|
||||
msgstr "Enviado ás <b>%X</b> o <b>%B %d</b>"
|
||||
|
||||
#. Translators: this is a time format in the style of "Wednesday, May 25, 2012",
|
||||
#. shown when you get a chat message in a different year.
|
||||
#: ../js/ui/telepathyClient.js:802
|
||||
#, no-c-format
|
||||
msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
|
||||
msgstr "Enviado ás <b>%X</b> o <b>%B %d</b>, %Y"
|
||||
|
||||
#. Translators: this is the other person changing their old IM name to their new
|
||||
#. IM name.
|
||||
#: ../js/ui/telepathyClient.js:665
|
||||
#: ../js/ui/telepathyClient.js:843
|
||||
#, c-format
|
||||
msgid "%s is now known as %s"
|
||||
msgstr "%s é coñecido agora como %s"
|
||||
|
||||
#. translators: argument is a room name like
|
||||
#. * room@jabber.org for example.
|
||||
#: ../js/ui/telepathyClient.js:950
|
||||
#, c-format
|
||||
msgid "Invitation to %s"
|
||||
msgstr "Convidado a %s"
|
||||
|
||||
#. translators: first argument is the name of a contact and the second
|
||||
#. * one the name of a room. "Alice is inviting you to join room@jabber.org
|
||||
#. * for example.
|
||||
#: ../js/ui/telepathyClient.js:958
|
||||
#, c-format
|
||||
msgid "%s is inviting you to join %s"
|
||||
msgstr "%s esta convidando a unirse a %s"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:960 ../js/ui/telepathyClient.js:1049
|
||||
msgid "Decline"
|
||||
msgstr "Rexeitar"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:961 ../js/ui/telepathyClient.js:1050
|
||||
msgid "Accept"
|
||||
msgstr "Aceptar"
|
||||
|
||||
#. translators: argument is a contact name like Alice for example.
|
||||
#: ../js/ui/telepathyClient.js:994
|
||||
#, c-format
|
||||
msgid "Video call from %s"
|
||||
msgstr "Videochamada de %s"
|
||||
|
||||
#. translators: argument is a contact name like Alice for example.
|
||||
#: ../js/ui/telepathyClient.js:997
|
||||
#, c-format
|
||||
msgid "Call from %s"
|
||||
msgstr "Chamada de %s"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:1007
|
||||
msgid "Answer"
|
||||
msgstr "Respostar"
|
||||
|
||||
#. To translators: The first parameter is
|
||||
#. * the contact's alias and the second one is the
|
||||
#. * file name. The string will be something
|
||||
#. * like: "Alice is sending you test.ogg"
|
||||
#.
|
||||
#: ../js/ui/telepathyClient.js:1043
|
||||
#, c-format
|
||||
msgid "%s is sending you %s"
|
||||
msgstr "%s esta enviándolle %s"
|
||||
|
||||
#. Translators: this is the text displayed
|
||||
#. in the search entry when no search is
|
||||
#. active; it should not exceed ~30
|
||||
@ -1150,16 +1234,16 @@ msgstr "%s é coñecido agora como %s"
|
||||
msgid "Type to search..."
|
||||
msgstr "Teclear para buscar…"
|
||||
|
||||
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:257
|
||||
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:261
|
||||
msgid "Search"
|
||||
msgstr "Buscar"
|
||||
|
||||
#: ../js/ui/windowAttentionHandler.js:40
|
||||
#: ../js/ui/windowAttentionHandler.js:39
|
||||
#, c-format
|
||||
msgid "%s has finished starting"
|
||||
msgstr "%s rematou de iniarse"
|
||||
|
||||
#: ../js/ui/windowAttentionHandler.js:42
|
||||
#: ../js/ui/windowAttentionHandler.js:41
|
||||
#, c-format
|
||||
msgid "'%s' is ready"
|
||||
msgstr "«%s» está preparado"
|
||||
@ -1186,11 +1270,11 @@ msgstr[1] "%u entradas"
|
||||
msgid "System Sounds"
|
||||
msgstr "Sons do sistema"
|
||||
|
||||
#: ../src/main.c:445
|
||||
#: ../src/main.c:466
|
||||
msgid "Print version"
|
||||
msgstr "Imprimir versión"
|
||||
|
||||
#: ../src/shell-app.c:464
|
||||
#: ../src/shell-app.c:580
|
||||
#, c-format
|
||||
msgid "Failed to launch '%s'"
|
||||
msgstr "Produciuse un fallo ao iniciar «%s»"
|
||||
@ -1207,13 +1291,13 @@ msgstr "Predeterminado"
|
||||
msgid "Authentication dialog was dismissed by the user"
|
||||
msgstr "O usuario rexeitou o diálogo de autenticación"
|
||||
|
||||
#: ../src/shell-util.c:96
|
||||
#: ../src/shell-util.c:100
|
||||
msgid "Home Folder"
|
||||
msgstr "Cartafol persoal"
|
||||
|
||||
#. Translators: this is the same string as the one found in
|
||||
#. * nautilus
|
||||
#: ../src/shell-util.c:111
|
||||
#: ../src/shell-util.c:115
|
||||
msgid "File System"
|
||||
msgstr "Sistema de ficheiros"
|
||||
|
||||
@ -1222,11 +1306,31 @@ msgstr "Sistema de ficheiros"
|
||||
#. * example, "Trash: some-directory". It means that the
|
||||
#. * directory called "some-directory" is in the trash.
|
||||
#.
|
||||
#: ../src/shell-util.c:307
|
||||
#: ../src/shell-util.c:311
|
||||
#, c-format
|
||||
msgid "%1$s: %2$s"
|
||||
msgstr "%1$s: %2$s"
|
||||
|
||||
#: ../src/shell-util.c:600
|
||||
msgid "calendar:week_start:0"
|
||||
msgstr "calendar:week_start:1"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "GNOME Shell extensions have a uuid property; this key lists extensions "
|
||||
#~ "which should not be loaded. This setting overrides enabled-extensions for "
|
||||
#~ "extensions that appear in both lists."
|
||||
#~ msgstr ""
|
||||
#~ "As extensións do GNOME Shell teñen unha propiedade uuid. Esta chave lista "
|
||||
#~ "as extensións que deberían cargarse. Esta configuración sobrescribe a "
|
||||
#~ "«enabled-extensions» para as extensións que aparecen en ámbalas dúas "
|
||||
#~ "listas."
|
||||
|
||||
#~ msgid "Uuids of extensions to disable"
|
||||
#~ msgstr "Uuid das extensións que activar"
|
||||
|
||||
#~ msgid "Localization Settings"
|
||||
#~ msgstr "Configuracións do son"
|
||||
|
||||
#~ msgid "Less than a minute ago"
|
||||
#~ msgstr "Hai menos dun minuto"
|
||||
|
||||
|
332
po/he.po
332
po/he.po
@ -8,8 +8,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: gnome-shell master\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2011-07-16 17:26+0300\n"
|
||||
"PO-Revision-Date: 2011-07-16 17:27+0200\n"
|
||||
"POT-Creation-Date: 2011-08-18 23:52+0300\n"
|
||||
"PO-Revision-Date: 2011-08-18 23:55+0200\n"
|
||||
"Last-Translator: Yaron Shahrabani <sh.yaron@gmail.com>\n"
|
||||
"Language-Team: Hebrew <sh.yaron@gmail.com>\n"
|
||||
"Language: he\n"
|
||||
@ -204,27 +204,27 @@ msgid "Execution of '%s' failed:"
|
||||
msgstr "ההרצה של '%s' נכשלה:"
|
||||
|
||||
#. Translators: Filter to display all applications
|
||||
#: ../js/ui/appDisplay.js:252
|
||||
#: ../js/ui/appDisplay.js:253
|
||||
msgid "All"
|
||||
msgstr "הכול"
|
||||
|
||||
#: ../js/ui/appDisplay.js:351
|
||||
#: ../js/ui/appDisplay.js:315
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "יישומים"
|
||||
|
||||
#: ../js/ui/appDisplay.js:377
|
||||
#: ../js/ui/appDisplay.js:373
|
||||
msgid "SETTINGS"
|
||||
msgstr "הגדרות"
|
||||
|
||||
#: ../js/ui/appDisplay.js:650
|
||||
#: ../js/ui/appDisplay.js:681
|
||||
msgid "New Window"
|
||||
msgstr "חלון חדש"
|
||||
|
||||
#: ../js/ui/appDisplay.js:653
|
||||
#: ../js/ui/appDisplay.js:684
|
||||
msgid "Remove from Favorites"
|
||||
msgstr "הסרה מהמועדפים"
|
||||
|
||||
#: ../js/ui/appDisplay.js:654
|
||||
#: ../js/ui/appDisplay.js:685
|
||||
msgid "Add to Favorites"
|
||||
msgstr "הוספה למועדפים"
|
||||
|
||||
@ -238,6 +238,15 @@ msgstr "%s נוסף למועדפים שלך."
|
||||
msgid "%s has been removed from your favorites."
|
||||
msgstr "%s הוסר מהמועדפים שלך."
|
||||
|
||||
#: ../js/ui/autorunManager.js:591
|
||||
#, c-format
|
||||
msgid "Open with %s"
|
||||
msgstr "פתיחה באמצעות %s"
|
||||
|
||||
#: ../js/ui/autorunManager.js:617
|
||||
msgid "Eject"
|
||||
msgstr "שליפה"
|
||||
|
||||
#. Translators: Shown in calendar event list for all day events
|
||||
#. * Keep it short, best if you can use less then 10 characters
|
||||
#.
|
||||
@ -352,94 +361,94 @@ msgid "S"
|
||||
msgstr "ש׳"
|
||||
|
||||
#. Translators: Text to show if there are no events
|
||||
#: ../js/ui/calendar.js:701
|
||||
#: ../js/ui/calendar.js:678
|
||||
msgid "Nothing Scheduled"
|
||||
msgstr "היומן ריק"
|
||||
|
||||
#. Translators: Shown on calendar heading when selected day occurs on current year
|
||||
#: ../js/ui/calendar.js:717 ../js/ui/telepathyClient.js:749
|
||||
#: ../js/ui/calendar.js:694
|
||||
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:720 ../js/ui/telepathyClient.js:752
|
||||
#: ../js/ui/calendar.js:697
|
||||
msgctxt "calendar heading"
|
||||
msgid "%A, %B %d, %Y"
|
||||
msgstr "%A, ה־%e ב%B, %Y"
|
||||
|
||||
#: ../js/ui/calendar.js:730
|
||||
#: ../js/ui/calendar.js:707
|
||||
msgid "Today"
|
||||
msgstr "היום"
|
||||
|
||||
#: ../js/ui/calendar.js:734
|
||||
#: ../js/ui/calendar.js:711
|
||||
msgid "Tomorrow"
|
||||
msgstr "מחר"
|
||||
|
||||
#: ../js/ui/calendar.js:743
|
||||
#: ../js/ui/calendar.js:720
|
||||
msgid "This week"
|
||||
msgstr "השבוע"
|
||||
|
||||
#: ../js/ui/calendar.js:751
|
||||
#: ../js/ui/calendar.js:728
|
||||
msgid "Next week"
|
||||
msgstr "בשבוע הבא"
|
||||
|
||||
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1122
|
||||
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1123
|
||||
msgid "Remove"
|
||||
msgstr "הסרה"
|
||||
|
||||
#: ../js/ui/dateMenu.js:89
|
||||
#: ../js/ui/dateMenu.js:90
|
||||
msgid "Date and Time Settings"
|
||||
msgstr "הגדרות תאריך ושעה"
|
||||
|
||||
#: ../js/ui/dateMenu.js:109
|
||||
#: ../js/ui/dateMenu.js:110
|
||||
msgid "Open Calendar"
|
||||
msgstr "פתיחת היומן"
|
||||
|
||||
#. Translators: This is the time format with date used
|
||||
#. in 24-hour mode.
|
||||
#: ../js/ui/dateMenu.js:162
|
||||
#: ../js/ui/dateMenu.js:167
|
||||
msgid "%a %b %e, %R:%S"
|
||||
msgstr "%a %b %e, %R:%S"
|
||||
|
||||
#: ../js/ui/dateMenu.js:163
|
||||
#: ../js/ui/dateMenu.js:168
|
||||
msgid "%a %b %e, %R"
|
||||
msgstr "%a %b %e, %R"
|
||||
|
||||
#. Translators: This is the time format without date used
|
||||
#. in 24-hour mode.
|
||||
#: ../js/ui/dateMenu.js:167
|
||||
#: ../js/ui/dateMenu.js:172
|
||||
msgid "%a %R:%S"
|
||||
msgstr "%a %R:%S"
|
||||
|
||||
#: ../js/ui/dateMenu.js:168
|
||||
#: ../js/ui/dateMenu.js:173
|
||||
msgid "%a %R"
|
||||
msgstr "%a %R"
|
||||
|
||||
#. Translators: This is a time format with date used
|
||||
#. for AM/PM.
|
||||
#: ../js/ui/dateMenu.js:175
|
||||
#: ../js/ui/dateMenu.js:180
|
||||
msgid "%a %b %e, %l:%M:%S %p"
|
||||
msgstr "%a %b %e, %l:%M:%S %p"
|
||||
|
||||
#: ../js/ui/dateMenu.js:176
|
||||
#: ../js/ui/dateMenu.js:181
|
||||
msgid "%a %b %e, %l:%M %p"
|
||||
msgstr "%a %b %e, %l:%M %p"
|
||||
|
||||
#. Translators: This is a time format without date used
|
||||
#. for AM/PM.
|
||||
#: ../js/ui/dateMenu.js:180
|
||||
#: ../js/ui/dateMenu.js:185
|
||||
msgid "%a %l:%M:%S %p"
|
||||
msgstr "%a %l:%M:%S %p"
|
||||
|
||||
#: ../js/ui/dateMenu.js:181
|
||||
#: ../js/ui/dateMenu.js:186
|
||||
msgid "%a %l:%M %p"
|
||||
msgstr "%a %l:%M %p"
|
||||
|
||||
#. 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:192
|
||||
#: ../js/ui/dateMenu.js:197
|
||||
msgid "%A %B %e, %Y"
|
||||
msgstr "%A ה־%e ב%B, %Y"
|
||||
|
||||
@ -514,44 +523,49 @@ msgstr "המערכת מופעלת מחדש"
|
||||
msgid "Cancel"
|
||||
msgstr "ביטול"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:641
|
||||
#: ../js/ui/lookingGlass.js:640
|
||||
msgid "No extensions installed"
|
||||
msgstr "לא מותקנות הרחבות"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:678
|
||||
#: ../js/ui/lookingGlass.js:686
|
||||
msgid "Enabled"
|
||||
msgstr "פעיל"
|
||||
|
||||
#. translators:
|
||||
#. * The device has been disabled
|
||||
#: ../js/ui/lookingGlass.js:680 ../src/gvc/gvc-mixer-control.c:1091
|
||||
#: ../js/ui/lookingGlass.js:688 ../src/gvc/gvc-mixer-control.c:1091
|
||||
msgid "Disabled"
|
||||
msgstr "מנוטרל"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:682
|
||||
#: ../js/ui/lookingGlass.js:690
|
||||
msgid "Error"
|
||||
msgstr "שגיאה"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:684
|
||||
#: ../js/ui/lookingGlass.js:692
|
||||
msgid "Out of date"
|
||||
msgstr "לא בתוקף"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:709
|
||||
#: ../js/ui/lookingGlass.js:717
|
||||
msgid "View Source"
|
||||
msgstr "צפייה במקור"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:715
|
||||
#: ../js/ui/lookingGlass.js:723
|
||||
msgid "Web Page"
|
||||
msgstr "דף אינטרנט"
|
||||
|
||||
#: ../js/ui/messageTray.js:1115
|
||||
#: ../js/ui/messageTray.js:1116
|
||||
msgid "Open"
|
||||
msgstr "פתיחה"
|
||||
|
||||
#: ../js/ui/messageTray.js:2286
|
||||
#: ../js/ui/messageTray.js:2277
|
||||
msgid "System Information"
|
||||
msgstr "פרטי המערכת"
|
||||
|
||||
#: ../js/ui/notificationDaemon.js:427 ../js/ui/status/power.js:238
|
||||
#: ../src/shell-app.c:340
|
||||
msgid "Unknown"
|
||||
msgstr "לא ידוע"
|
||||
|
||||
#: ../js/ui/overview.js:89
|
||||
msgid "Undo"
|
||||
msgstr "ביטול"
|
||||
@ -571,18 +585,18 @@ msgid "Dash"
|
||||
msgstr "חלונית"
|
||||
|
||||
#. TODO - _quit() doesn't really work on apps in state STARTING yet
|
||||
#: ../js/ui/panel.js:532
|
||||
#: ../js/ui/panel.js:531
|
||||
#, c-format
|
||||
msgid "Quit %s"
|
||||
msgstr "יציאה מ־%s"
|
||||
|
||||
#. Translators: If there is no suitable word for "Activities"
|
||||
#. in your language, you can use the word for "Overview".
|
||||
#: ../js/ui/panel.js:568
|
||||
#: ../js/ui/panel.js:567
|
||||
msgid "Activities"
|
||||
msgstr "פעילויות"
|
||||
|
||||
#: ../js/ui/panel.js:951
|
||||
#: ../js/ui/panel.js:876
|
||||
msgid "Top Bar"
|
||||
msgstr "הסרגל העליון"
|
||||
|
||||
@ -648,43 +662,9 @@ msgstr "בחיפוש..."
|
||||
msgid "No matching results."
|
||||
msgstr "אין תוצאות תואמות."
|
||||
|
||||
#: ../js/ui/statusMenu.js:192 ../js/ui/statusMenu.js:196
|
||||
#: ../js/ui/statusMenu.js:262
|
||||
msgid "Power Off..."
|
||||
msgstr "כיבוי..."
|
||||
|
||||
#: ../js/ui/statusMenu.js:194 ../js/ui/statusMenu.js:196
|
||||
#: ../js/ui/statusMenu.js:261
|
||||
msgid "Suspend"
|
||||
msgstr "השהיה"
|
||||
|
||||
#: ../js/ui/statusMenu.js:217
|
||||
msgid "Available"
|
||||
msgstr "פנוי"
|
||||
|
||||
#: ../js/ui/statusMenu.js:222
|
||||
msgid "Busy"
|
||||
msgstr "עסוק"
|
||||
|
||||
#: ../js/ui/statusMenu.js:230
|
||||
msgid "My Account"
|
||||
msgstr "החשבון שלי"
|
||||
|
||||
#: ../js/ui/statusMenu.js:234
|
||||
msgid "System Settings"
|
||||
msgstr "הגדרות המערכת"
|
||||
|
||||
#: ../js/ui/statusMenu.js:242
|
||||
msgid "Lock Screen"
|
||||
msgstr "נעילת המסך"
|
||||
|
||||
#: ../js/ui/statusMenu.js:247
|
||||
msgid "Switch User"
|
||||
msgstr "החלפת משתמש"
|
||||
|
||||
#: ../js/ui/statusMenu.js:252
|
||||
msgid "Log Out..."
|
||||
msgstr "ניתוק..."
|
||||
#: ../js/ui/shellMountOperation.js:285
|
||||
msgid "Wrong password, please try again"
|
||||
msgstr "ססמה שגויה, נא לנסות שוב"
|
||||
|
||||
#: ../js/ui/status/accessibility.js:60
|
||||
msgid "Zoom"
|
||||
@ -759,11 +739,11 @@ msgstr "מנוטרל חומרתית"
|
||||
msgid "Connection"
|
||||
msgstr "חיבור"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:226 ../js/ui/status/network.js:493
|
||||
#: ../js/ui/status/bluetooth.js:226 ../js/ui/status/network.js:497
|
||||
msgid "disconnecting..."
|
||||
msgstr "בהליכי ניתוק..."
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:239 ../js/ui/status/network.js:499
|
||||
#: ../js/ui/status/bluetooth.js:239 ../js/ui/status/network.js:503
|
||||
msgid "connecting..."
|
||||
msgstr "בהתחברות..."
|
||||
|
||||
@ -814,7 +794,7 @@ msgstr "תמיד להעניק גישה"
|
||||
msgid "Grant this time only"
|
||||
msgstr "הענקת גישה הפעם בלבד"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:409 ../js/ui/telepathyClient.js:954
|
||||
#: ../js/ui/status/bluetooth.js:409 ../js/ui/telepathyClient.js:985
|
||||
msgid "Reject"
|
||||
msgstr "סירוב"
|
||||
|
||||
@ -855,148 +835,186 @@ msgid "OK"
|
||||
msgstr "אישור"
|
||||
|
||||
#: ../js/ui/status/keyboard.js:71
|
||||
msgid "Show Keyboard Layout..."
|
||||
msgstr "הצגת פריסת המקלדת..."
|
||||
msgid "Show Keyboard Layout"
|
||||
msgstr "הצגת פריסת המקלדת"
|
||||
|
||||
#: ../js/ui/status/keyboard.js:75
|
||||
msgid "Localization Settings"
|
||||
msgstr "הגדרות אזוריות"
|
||||
msgid "Region and Language Settings"
|
||||
msgstr "הגדרות אזור ושפה"
|
||||
|
||||
#: ../js/ui/status/network.js:123
|
||||
#: ../js/ui/statusMenu.js:192 ../js/ui/statusMenu.js:196
|
||||
#: ../js/ui/statusMenu.js:262
|
||||
msgid "Power Off..."
|
||||
msgstr "כיבוי..."
|
||||
|
||||
#: ../js/ui/statusMenu.js:194 ../js/ui/statusMenu.js:196
|
||||
#: ../js/ui/statusMenu.js:261
|
||||
msgid "Suspend"
|
||||
msgstr "השהיה"
|
||||
|
||||
#: ../js/ui/statusMenu.js:217
|
||||
msgid "Available"
|
||||
msgstr "פנוי"
|
||||
|
||||
#: ../js/ui/statusMenu.js:222
|
||||
msgid "Busy"
|
||||
msgstr "עסוק"
|
||||
|
||||
#: ../js/ui/statusMenu.js:230
|
||||
msgid "My Account"
|
||||
msgstr "החשבון שלי"
|
||||
|
||||
#: ../js/ui/statusMenu.js:234
|
||||
msgid "System Settings"
|
||||
msgstr "הגדרות המערכת"
|
||||
|
||||
#: ../js/ui/statusMenu.js:242
|
||||
msgid "Lock Screen"
|
||||
msgstr "נעילת המסך"
|
||||
|
||||
#: ../js/ui/statusMenu.js:247
|
||||
msgid "Switch User"
|
||||
msgstr "החלפת משתמש"
|
||||
|
||||
#: ../js/ui/statusMenu.js:252
|
||||
msgid "Log Out..."
|
||||
msgstr "ניתוק..."
|
||||
|
||||
#: ../js/ui/status/network.js:108
|
||||
msgid "<unknown>"
|
||||
msgstr "<לא ידוע>"
|
||||
|
||||
#. Translators: this indicates that wireless or wwan is disabled by hardware killswitch
|
||||
#: ../js/ui/status/network.js:292
|
||||
#: ../js/ui/status/network.js:296
|
||||
msgid "disabled"
|
||||
msgstr "מנוטרל"
|
||||
|
||||
#. Translators: this is for network devices that are physically present but are not
|
||||
#. under NetworkManager's control (and thus cannot be used in the menu)
|
||||
#: ../js/ui/status/network.js:491
|
||||
#: ../js/ui/status/network.js:495
|
||||
msgid "unmanaged"
|
||||
msgstr "לא מנוהל"
|
||||
|
||||
#. Translators: this is for network connections that require some kind of key or password
|
||||
#: ../js/ui/status/network.js:502
|
||||
#: ../js/ui/status/network.js:506
|
||||
msgid "authentication required"
|
||||
msgstr "נדרש אימות"
|
||||
|
||||
#. Translators: this is for devices that require some kind of firmware or kernel
|
||||
#. module, which is missing
|
||||
#: ../js/ui/status/network.js:512
|
||||
#: ../js/ui/status/network.js:516
|
||||
msgid "firmware missing"
|
||||
msgstr "הקושחה חסרה"
|
||||
|
||||
#. Translators: this is for wired network devices that are physically disconnected
|
||||
#: ../js/ui/status/network.js:519
|
||||
#: ../js/ui/status/network.js:523
|
||||
msgid "cable unplugged"
|
||||
msgstr "הכבל מנותק"
|
||||
|
||||
#. Translators: this is for a network device that cannot be activated (for example it
|
||||
#. is disabled by rfkill, or it has no coverage
|
||||
#: ../js/ui/status/network.js:524
|
||||
#: ../js/ui/status/network.js:528
|
||||
msgid "unavailable"
|
||||
msgstr "לא זמין"
|
||||
|
||||
#: ../js/ui/status/network.js:526
|
||||
#: ../js/ui/status/network.js:530
|
||||
msgid "connection failed"
|
||||
msgstr "החיבור נכשל"
|
||||
|
||||
#: ../js/ui/status/network.js:582 ../js/ui/status/network.js:1507
|
||||
#: ../js/ui/status/network.js:586 ../js/ui/status/network.js:1511
|
||||
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:618 ../js/ui/status/network.js:1444
|
||||
#: ../js/ui/status/network.js:622 ../js/ui/status/network.js:1448
|
||||
msgid "Connected (private)"
|
||||
msgstr "מחובר (פרטי)"
|
||||
|
||||
#: ../js/ui/status/network.js:703
|
||||
#: ../js/ui/status/network.js:707
|
||||
msgid "Auto Ethernet"
|
||||
msgstr "אתרנט אוטומטי"
|
||||
|
||||
#: ../js/ui/status/network.js:771
|
||||
#: ../js/ui/status/network.js:775
|
||||
msgid "Auto broadband"
|
||||
msgstr "פס רחב אוטומטי"
|
||||
|
||||
#: ../js/ui/status/network.js:774
|
||||
#: ../js/ui/status/network.js:778
|
||||
msgid "Auto dial-up"
|
||||
msgstr "חיוג אוטומטי"
|
||||
|
||||
#. TRANSLATORS: this the automatic wireless connection name (including the network name)
|
||||
#: ../js/ui/status/network.js:898 ../js/ui/status/network.js:1456
|
||||
#: ../js/ui/status/network.js:902 ../js/ui/status/network.js:1460
|
||||
#, c-format
|
||||
msgid "Auto %s"
|
||||
msgstr "%s אוטומטי"
|
||||
|
||||
#: ../js/ui/status/network.js:900
|
||||
#: ../js/ui/status/network.js:904
|
||||
msgid "Auto bluetooth"
|
||||
msgstr "Bluetooth אוטומטי"
|
||||
|
||||
#: ../js/ui/status/network.js:1458
|
||||
#: ../js/ui/status/network.js:1462
|
||||
msgid "Auto wireless"
|
||||
msgstr "אלחוטי אוטומטי"
|
||||
|
||||
#: ../js/ui/status/network.js:1550
|
||||
#: ../js/ui/status/network.js:1554
|
||||
msgid "Enable networking"
|
||||
msgstr "הפעלת תכונת הרשת"
|
||||
|
||||
#: ../js/ui/status/network.js:1562
|
||||
#: ../js/ui/status/network.js:1566
|
||||
msgid "Wired"
|
||||
msgstr "קווי"
|
||||
|
||||
#: ../js/ui/status/network.js:1573
|
||||
#: ../js/ui/status/network.js:1577
|
||||
msgid "Wireless"
|
||||
msgstr "אלחוטי"
|
||||
|
||||
#: ../js/ui/status/network.js:1583
|
||||
#: ../js/ui/status/network.js:1587
|
||||
msgid "Mobile broadband"
|
||||
msgstr "פס־רחב נייד"
|
||||
|
||||
#: ../js/ui/status/network.js:1593
|
||||
#: ../js/ui/status/network.js:1597
|
||||
msgid "VPN Connections"
|
||||
msgstr "חיבורי VPN"
|
||||
|
||||
#: ../js/ui/status/network.js:1605
|
||||
#: ../js/ui/status/network.js:1609
|
||||
msgid "Network Settings"
|
||||
msgstr "הגדרות הרשת"
|
||||
|
||||
#: ../js/ui/status/network.js:1897
|
||||
#: ../js/ui/status/network.js:1901
|
||||
#, c-format
|
||||
msgid "You're now connected to mobile broadband connection '%s'"
|
||||
msgstr "כרגע ישנו חיבור בינך ובין רשת הפס הרחב הניידת '%s'"
|
||||
|
||||
#: ../js/ui/status/network.js:1901
|
||||
#: ../js/ui/status/network.js:1905
|
||||
#, c-format
|
||||
msgid "You're now connected to wireless network '%s'"
|
||||
msgstr "כרגע ישנו חיבור בינך ובין הרשת האלחוטית '%s'"
|
||||
|
||||
#: ../js/ui/status/network.js:1905
|
||||
#: ../js/ui/status/network.js:1909
|
||||
#, c-format
|
||||
msgid "You're now connected to wired network '%s'"
|
||||
msgstr "כרגע ישנו חיבור בינך ובין הרשת הקווית '%s'"
|
||||
|
||||
#: ../js/ui/status/network.js:1909
|
||||
#: ../js/ui/status/network.js:1913
|
||||
#, c-format
|
||||
msgid "You're now connected to VPN network '%s'"
|
||||
msgstr "כרגע ישנו חיבור בינך ובין רשת ה־VPN '%s'"
|
||||
|
||||
#: ../js/ui/status/network.js:1914
|
||||
#: ../js/ui/status/network.js:1918
|
||||
#, c-format
|
||||
msgid "You're now connected to '%s'"
|
||||
msgstr "כעת ישנו חיבור בינך ובין '%s'"
|
||||
|
||||
#: ../js/ui/status/network.js:1922
|
||||
#: ../js/ui/status/network.js:1926
|
||||
msgid "Connection established"
|
||||
msgstr "ההתחברות הצליחה"
|
||||
|
||||
#: ../js/ui/status/network.js:2048
|
||||
#: ../js/ui/status/network.js:2052
|
||||
msgid "Networking is disabled"
|
||||
msgstr "תכונת הרשת מנוטרלת"
|
||||
|
||||
#: ../js/ui/status/network.js:2173
|
||||
#: ../js/ui/status/network.js:2177
|
||||
msgid "Network Manager"
|
||||
msgstr "מנהל הרשתות"
|
||||
|
||||
@ -1090,10 +1108,6 @@ msgstr "טבלת שליטה"
|
||||
msgid "Computer"
|
||||
msgstr "מחשב"
|
||||
|
||||
#: ../js/ui/status/power.js:238 ../src/shell-app-system.c:1088
|
||||
msgid "Unknown"
|
||||
msgstr "לא ידוע"
|
||||
|
||||
#: ../js/ui/status/volume.js:43
|
||||
msgid "Volume"
|
||||
msgstr "עצמה"
|
||||
@ -1105,31 +1119,36 @@ msgstr "מיקרופון"
|
||||
#. We got the TpContact
|
||||
#. FIXME: We don't have a 'chat room' icon (bgo #653737) use
|
||||
#. system-users for now as Empathy does.
|
||||
#: ../js/ui/telepathyClient.js:222
|
||||
#: ../js/ui/telepathyClient.js:229
|
||||
msgid "Invitation"
|
||||
msgstr "הזמנה"
|
||||
|
||||
#. We got the TpContact
|
||||
#: ../js/ui/telepathyClient.js:285
|
||||
#: ../js/ui/telepathyClient.js:295
|
||||
msgid "Call"
|
||||
msgstr "התקשרות"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:541
|
||||
#. We got the TpContact
|
||||
#: ../js/ui/telepathyClient.js:323
|
||||
msgid "File Transfer"
|
||||
msgstr "העברת קבצים"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:573
|
||||
#, c-format
|
||||
msgid "%s is online."
|
||||
msgstr "%s התחבר/ה."
|
||||
|
||||
#: ../js/ui/telepathyClient.js:546
|
||||
#: ../js/ui/telepathyClient.js:578
|
||||
#, c-format
|
||||
msgid "%s is offline."
|
||||
msgstr "%s התנתק/ה."
|
||||
|
||||
#: ../js/ui/telepathyClient.js:549
|
||||
#: ../js/ui/telepathyClient.js:581
|
||||
#, c-format
|
||||
msgid "%s is away."
|
||||
msgstr "'%s' מרוחק/ת."
|
||||
|
||||
#: ../js/ui/telepathyClient.js:552
|
||||
#: ../js/ui/telepathyClient.js:584
|
||||
#, c-format
|
||||
msgid "%s is busy."
|
||||
msgstr "%s עסוק/ה."
|
||||
@ -1137,21 +1156,35 @@ msgstr "%s עסוק/ה."
|
||||
#. Translators: this is a time format string followed by a date.
|
||||
#. If applicable, replace %X with a strftime format valid for your
|
||||
#. locale, without seconds.
|
||||
#: ../js/ui/telepathyClient.js:741
|
||||
#: ../js/ui/telepathyClient.js:770
|
||||
#, no-c-format
|
||||
msgid "Sent at %X on %A"
|
||||
msgstr "נשלח ב־%X בשעה %A"
|
||||
msgid "Sent at <b>%X</b> on <b>%A</b>"
|
||||
msgstr "נשלח ב־<b>%X</b> בשעה <b>%A</b>"
|
||||
|
||||
#. Translators: this is a time format in the style of "Wednesday, May 25",
|
||||
#. shown when you get a chat message in the same year.
|
||||
#: ../js/ui/telepathyClient.js:776
|
||||
#, no-c-format
|
||||
msgid "Sent on <b>%A</b>, <b>%B %d</b>"
|
||||
msgstr "נשלח ב<b>%A</b>, <b>ה־%d ב%B</b>"
|
||||
|
||||
#. Translators: this is a time format in the style of "Wednesday, May 25, 2012",
|
||||
#. shown when you get a chat message in a different year.
|
||||
#: ../js/ui/telepathyClient.js:781
|
||||
#, no-c-format
|
||||
msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
|
||||
msgstr "נשלח ב<b>%A</b>, <b>ה־%d ב%B</b>, %Y"
|
||||
|
||||
#. Translators: this is the other person changing their old IM name to their new
|
||||
#. IM name.
|
||||
#: ../js/ui/telepathyClient.js:791
|
||||
#: ../js/ui/telepathyClient.js:822
|
||||
#, c-format
|
||||
msgid "%s is now known as %s"
|
||||
msgstr "השם של %s הוחלף ל־%s"
|
||||
|
||||
#. translators: argument is a room name like
|
||||
#. * room@jabber.org for example.
|
||||
#: ../js/ui/telepathyClient.js:898
|
||||
#: ../js/ui/telepathyClient.js:929
|
||||
#, c-format
|
||||
msgid "Invitation to %s"
|
||||
msgstr "הזמנה ל־%s"
|
||||
@ -1159,35 +1192,45 @@ msgstr "הזמנה ל־%s"
|
||||
#. translators: first argument is the name of a contact and the second
|
||||
#. * one the name of a room. "Alice is inviting you to join room@jabber.org
|
||||
#. * for example.
|
||||
#: ../js/ui/telepathyClient.js:906
|
||||
#: ../js/ui/telepathyClient.js:937
|
||||
#, c-format
|
||||
msgid "%s is inviting you to join %s"
|
||||
msgstr "הוזמנת על ידי %s להצטרף אל %s"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:908
|
||||
#: ../js/ui/telepathyClient.js:939 ../js/ui/telepathyClient.js:1028
|
||||
msgid "Decline"
|
||||
msgstr "דחייה"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:909
|
||||
#: ../js/ui/telepathyClient.js:940 ../js/ui/telepathyClient.js:1029
|
||||
msgid "Accept"
|
||||
msgstr "אישור"
|
||||
|
||||
#. translators: argument is a contact name like Alice for example.
|
||||
#: ../js/ui/telepathyClient.js:942
|
||||
#: ../js/ui/telepathyClient.js:973
|
||||
#, c-format
|
||||
msgid "Video call from %s"
|
||||
msgstr "שיחת וידאו מאת %s"
|
||||
|
||||
#. translators: argument is a contact name like Alice for example.
|
||||
#: ../js/ui/telepathyClient.js:945
|
||||
#: ../js/ui/telepathyClient.js:976
|
||||
#, c-format
|
||||
msgid "Call from %s"
|
||||
msgstr "שיחה מאת %s"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:955
|
||||
#: ../js/ui/telepathyClient.js:986
|
||||
msgid "Answer"
|
||||
msgstr "מענה"
|
||||
|
||||
#. To translators: The first parameter is
|
||||
#. * the contact's alias and the second one is the
|
||||
#. * file name. The string will be something
|
||||
#. * like: "Alice is sending you test.ogg"
|
||||
#.
|
||||
#: ../js/ui/telepathyClient.js:1022
|
||||
#, c-format
|
||||
msgid "%s is sending you %s"
|
||||
msgstr "%s שולח/ת אליך %s"
|
||||
|
||||
#. Translators: this is the text displayed
|
||||
#. in the search entry when no search is
|
||||
#. active; it should not exceed ~30
|
||||
@ -1196,16 +1239,16 @@ msgstr "מענה"
|
||||
msgid "Type to search..."
|
||||
msgstr "יש להקליד כדי לחפש..."
|
||||
|
||||
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:257
|
||||
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:261
|
||||
msgid "Search"
|
||||
msgstr "חיפוש"
|
||||
|
||||
#: ../js/ui/windowAttentionHandler.js:40
|
||||
#: ../js/ui/windowAttentionHandler.js:39
|
||||
#, c-format
|
||||
msgid "%s has finished starting"
|
||||
msgstr "%s סיים את תהליך ההתחלה"
|
||||
|
||||
#: ../js/ui/windowAttentionHandler.js:42
|
||||
#: ../js/ui/windowAttentionHandler.js:41
|
||||
#, c-format
|
||||
msgid "'%s' is ready"
|
||||
msgstr "'%s' מוכן"
|
||||
@ -1238,7 +1281,7 @@ msgstr "צלילי מערכת"
|
||||
msgid "Print version"
|
||||
msgstr "Print version"
|
||||
|
||||
#: ../src/shell-app.c:464
|
||||
#: ../src/shell-app.c:566
|
||||
#, c-format
|
||||
msgid "Failed to launch '%s'"
|
||||
msgstr "אירע כשל בטעינת '%s'"
|
||||
@ -1255,13 +1298,13 @@ msgstr "בררת מחדל"
|
||||
msgid "Authentication dialog was dismissed by the user"
|
||||
msgstr "המשתמש בחר להתעלם מתיבת דו־שיח האימות"
|
||||
|
||||
#: ../src/shell-util.c:96
|
||||
#: ../src/shell-util.c:100
|
||||
msgid "Home Folder"
|
||||
msgstr "תיקיית הבית"
|
||||
|
||||
#. Translators: this is the same string as the one found in
|
||||
#. * nautilus
|
||||
#: ../src/shell-util.c:111
|
||||
#: ../src/shell-util.c:115
|
||||
msgid "File System"
|
||||
msgstr "מערכת הקבצים"
|
||||
|
||||
@ -1270,11 +1313,18 @@ msgstr "מערכת הקבצים"
|
||||
#. * example, "Trash: some-directory". It means that the
|
||||
#. * directory called "some-directory" is in the trash.
|
||||
#.
|
||||
#: ../src/shell-util.c:307
|
||||
#: ../src/shell-util.c:311
|
||||
#, c-format
|
||||
msgid "%1$s: %2$s"
|
||||
msgstr "%1$s: %2$s"
|
||||
|
||||
#: ../src/shell-util.c:600
|
||||
msgid "calendar:week_start:0"
|
||||
msgstr "calendar:week_start:0"
|
||||
|
||||
#~ msgid "Localization Settings"
|
||||
#~ msgstr "הגדרות אזוריות"
|
||||
|
||||
#~ msgid "Less than a minute ago"
|
||||
#~ msgstr "לפני פחות מדקה"
|
||||
|
||||
|
385
po/nb.po
385
po/nb.po
@ -8,8 +8,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: gnome-shell 3.1.x\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2011-07-18 13:48+0200\n"
|
||||
"PO-Revision-Date: 2011-07-18 13:50+0200\n"
|
||||
"POT-Creation-Date: 2011-08-24 22:16+0200\n"
|
||||
"PO-Revision-Date: 2011-08-24 22:17+0200\n"
|
||||
"Last-Translator: Kjartan Maraas <kmaraas@gnome.org>\n"
|
||||
"Language-Team: Norwegian Bokmål <i18n-nb@lister.ping.uio.no>\n"
|
||||
"Language: \n"
|
||||
@ -57,40 +57,30 @@ msgstr ""
|
||||
"innstillingen for utvidelser som finnes i begge listene."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:6
|
||||
msgid ""
|
||||
"GNOME Shell extensions have a uuid property; this key lists extensions which "
|
||||
"should not be loaded. This setting overrides enabled-extensions for "
|
||||
"extensions that appear in both lists."
|
||||
msgstr ""
|
||||
"GNOME Shell-utvidelser har en uuid-egenskap. Denne nøkkelen lister "
|
||||
"utvidelser som ikke bør lastes. Denne innstillingen overstyrer enabled-"
|
||||
"extensions for utvidelser som finnes i begge listene."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:7
|
||||
msgid "History for command (Alt-F2) dialog"
|
||||
msgstr "Historikk for kommandodialog (Alt-F2)"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:8
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:7
|
||||
msgid "History for the looking glass dialog"
|
||||
msgstr "Historikk for forstørrelsesglass-dialogen"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:9
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:8
|
||||
msgid "If true, display date in the clock, in addition to time."
|
||||
msgstr "Viser dato i tillegg til tid i klokken hvis «true»."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:10
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:9
|
||||
msgid "If true, display seconds in time."
|
||||
msgstr "Viser sekunder i klokken hvis «true»."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:11
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:10
|
||||
msgid "If true, display the ISO week date in the calendar."
|
||||
msgstr "Viser ISO-ukedato i kalenderen hvis «true»."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:12
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:11
|
||||
msgid "List of desktop file IDs for favorite applications"
|
||||
msgstr "Liste av skrivebordfil-ider for favorittprogrammer"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:14
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:13
|
||||
#, fuzzy, no-c-format
|
||||
msgid ""
|
||||
"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
|
||||
@ -103,21 +93,23 @@ msgid ""
|
||||
"'videorate ! vp8enc quality=10 speed=2 threads=%T ! queue ! webmmux' and "
|
||||
"records to WEBM using the VP8 codec. %T is used as a placeholder for a guess "
|
||||
"at the optimal thread count on the system."
|
||||
msgstr "Setter GStreamer-rør som brukes til å kode opptak. Den følger syntaksen som brukes for gst-launch."
|
||||
msgstr ""
|
||||
"Setter GStreamer-rør som brukes til å kode opptak. Den følger syntaksen som "
|
||||
"brukes for gst-launch."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:15
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:14
|
||||
msgid "Show date in clock"
|
||||
msgstr "Vis dato i klokken"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:16
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:15
|
||||
msgid "Show the week date in the calendar"
|
||||
msgstr "Vis dato for uken i kalender"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:17
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:16
|
||||
msgid "Show time with seconds"
|
||||
msgstr "Vis tid med sekunder"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:18
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:17
|
||||
msgid ""
|
||||
"The applications corresponding to these identifiers will be displayed in the "
|
||||
"favorites area."
|
||||
@ -125,7 +117,7 @@ msgstr ""
|
||||
"Programmene som passer til disse identifikatorene vil bli vist i "
|
||||
"favorittområdet."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:19
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:18
|
||||
msgid ""
|
||||
"The filename for recorded screencasts will be a unique filename based on the "
|
||||
"current date, and use this extension. It should be changed when recording to "
|
||||
@ -135,7 +127,7 @@ msgstr ""
|
||||
"og bruke denne filendelsen. Den bør endres når du gjør opptak til et nytt "
|
||||
"oppbevaringsformat."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:20
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:19
|
||||
msgid ""
|
||||
"The framerate of the resulting screencast recordered by GNOME Shell's "
|
||||
"screencast recorder in frames-per-second."
|
||||
@ -143,11 +135,11 @@ msgstr ""
|
||||
"Bildefrekvensen i den ferdige skjermvideoen tatt opp med GNOME Shells "
|
||||
"skjermvideoopptaker i bilder per sekund."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:21
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:20
|
||||
msgid "The gstreamer pipeline used to encode the screencast"
|
||||
msgstr "Gstreamer-kommandokø brukt til å kode skjermvideoen"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:22
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:21
|
||||
msgid ""
|
||||
"The shell normally monitors active applications in order to present the most "
|
||||
"used ones (e.g. in launchers). While this data will be kept private, you may "
|
||||
@ -159,19 +151,15 @@ msgstr ""
|
||||
"holdt privat, men du kan deaktivere denne lagringen av personvernårsaker. "
|
||||
"Hvis du slår det av, vil det ikke fjerne allerede lagret informasjon."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:23
|
||||
msgid "Uuids of extensions to disable"
|
||||
msgstr "Uuider på utvidelser som skal slås av"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:24
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:22
|
||||
msgid "Uuids of extensions to enable"
|
||||
msgstr "Uuider på utvidelser som skal slås på"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:25
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:23
|
||||
msgid "Whether to collect stats about applications usage"
|
||||
msgstr "Om det skal samles statistikk om bruk av programmer"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:26
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:24
|
||||
msgid "disabled OpenSearch providers"
|
||||
msgstr "OpenSearch tilbydere slått av"
|
||||
|
||||
@ -191,27 +179,27 @@ msgid "Execution of '%s' failed:"
|
||||
msgstr "Kjøring av «%s» feilet:"
|
||||
|
||||
#. Translators: Filter to display all applications
|
||||
#: ../js/ui/appDisplay.js:252
|
||||
#: ../js/ui/appDisplay.js:253
|
||||
msgid "All"
|
||||
msgstr "Alle"
|
||||
|
||||
#: ../js/ui/appDisplay.js:359
|
||||
#: ../js/ui/appDisplay.js:315
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "PROGRAMMER"
|
||||
|
||||
#: ../js/ui/appDisplay.js:385
|
||||
#: ../js/ui/appDisplay.js:373
|
||||
msgid "SETTINGS"
|
||||
msgstr "INNSTILLINGER"
|
||||
|
||||
#: ../js/ui/appDisplay.js:658
|
||||
#: ../js/ui/appDisplay.js:681
|
||||
msgid "New Window"
|
||||
msgstr "Nytt vindu"
|
||||
|
||||
#: ../js/ui/appDisplay.js:661
|
||||
#: ../js/ui/appDisplay.js:684
|
||||
msgid "Remove from Favorites"
|
||||
msgstr "Fjern fra favoritter"
|
||||
|
||||
#: ../js/ui/appDisplay.js:662
|
||||
#: ../js/ui/appDisplay.js:685
|
||||
msgid "Add to Favorites"
|
||||
msgstr "Legg til i favoritter"
|
||||
|
||||
@ -225,6 +213,15 @@ msgstr "%s ble lagt til i dine favoritter."
|
||||
msgid "%s has been removed from your favorites."
|
||||
msgstr "%s ble fjernet fra dine favoritter."
|
||||
|
||||
#: ../js/ui/autorunManager.js:591
|
||||
#, c-format
|
||||
msgid "Open with %s"
|
||||
msgstr "Åpne med %s"
|
||||
|
||||
#: ../js/ui/autorunManager.js:617
|
||||
msgid "Eject"
|
||||
msgstr "Løs ut"
|
||||
|
||||
#. Translators: Shown in calendar event list for all day events
|
||||
#. * Keep it short, best if you can use less then 10 characters
|
||||
#.
|
||||
@ -339,94 +336,94 @@ msgid "S"
|
||||
msgstr "Lø"
|
||||
|
||||
#. Translators: Text to show if there are no events
|
||||
#: ../js/ui/calendar.js:701
|
||||
#: ../js/ui/calendar.js:678
|
||||
msgid "Nothing Scheduled"
|
||||
msgstr "Ingenting planlagt"
|
||||
|
||||
#. Translators: Shown on calendar heading when selected day occurs on current year
|
||||
#: ../js/ui/calendar.js:717 ../js/ui/telepathyClient.js:749
|
||||
#: ../js/ui/calendar.js:694
|
||||
msgctxt "calendar heading"
|
||||
msgid "%A, %B %d"
|
||||
msgstr "%A %B %d"
|
||||
|
||||
#. Translators: Shown on calendar heading when selected day occurs on different year
|
||||
#: ../js/ui/calendar.js:720 ../js/ui/telepathyClient.js:752
|
||||
#: ../js/ui/calendar.js:697
|
||||
msgctxt "calendar heading"
|
||||
msgid "%A, %B %d, %Y"
|
||||
msgstr "%A %B %d, %Y"
|
||||
|
||||
#: ../js/ui/calendar.js:730
|
||||
#: ../js/ui/calendar.js:707
|
||||
msgid "Today"
|
||||
msgstr "I dag"
|
||||
|
||||
#: ../js/ui/calendar.js:734
|
||||
#: ../js/ui/calendar.js:711
|
||||
msgid "Tomorrow"
|
||||
msgstr "I morgen"
|
||||
|
||||
#: ../js/ui/calendar.js:743
|
||||
#: ../js/ui/calendar.js:720
|
||||
msgid "This week"
|
||||
msgstr "Denne uken"
|
||||
|
||||
#: ../js/ui/calendar.js:751
|
||||
#: ../js/ui/calendar.js:728
|
||||
msgid "Next week"
|
||||
msgstr "Neste uke"
|
||||
|
||||
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1122
|
||||
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1123
|
||||
msgid "Remove"
|
||||
msgstr "Fjern"
|
||||
|
||||
#: ../js/ui/dateMenu.js:89
|
||||
#: ../js/ui/dateMenu.js:90
|
||||
msgid "Date and Time Settings"
|
||||
msgstr "Innstillinger for dato og klokkeslett"
|
||||
|
||||
#: ../js/ui/dateMenu.js:109
|
||||
#: ../js/ui/dateMenu.js:110
|
||||
msgid "Open Calendar"
|
||||
msgstr "Åpne kalender"
|
||||
|
||||
#. Translators: This is the time format with date used
|
||||
#. in 24-hour mode.
|
||||
#: ../js/ui/dateMenu.js:162
|
||||
#: ../js/ui/dateMenu.js:167
|
||||
msgid "%a %b %e, %R:%S"
|
||||
msgstr "%a %e %b, %R.%S"
|
||||
|
||||
#: ../js/ui/dateMenu.js:163
|
||||
#: ../js/ui/dateMenu.js:168
|
||||
msgid "%a %b %e, %R"
|
||||
msgstr "%a %e %b, %R"
|
||||
|
||||
#. Translators: This is the time format without date used
|
||||
#. in 24-hour mode.
|
||||
#: ../js/ui/dateMenu.js:167
|
||||
#: ../js/ui/dateMenu.js:172
|
||||
msgid "%a %R:%S"
|
||||
msgstr "%a %R.%S"
|
||||
|
||||
#: ../js/ui/dateMenu.js:168
|
||||
#: ../js/ui/dateMenu.js:173
|
||||
msgid "%a %R"
|
||||
msgstr "%a %R"
|
||||
|
||||
#. Translators: This is a time format with date used
|
||||
#. for AM/PM.
|
||||
#: ../js/ui/dateMenu.js:175
|
||||
#: ../js/ui/dateMenu.js:180
|
||||
msgid "%a %b %e, %l:%M:%S %p"
|
||||
msgstr "%a %e %b, %l.%M.%S %p"
|
||||
|
||||
#: ../js/ui/dateMenu.js:176
|
||||
#: ../js/ui/dateMenu.js:181
|
||||
msgid "%a %b %e, %l:%M %p"
|
||||
msgstr "%a %e %b, %l.%M %p"
|
||||
|
||||
#. Translators: This is a time format without date used
|
||||
#. for AM/PM.
|
||||
#: ../js/ui/dateMenu.js:180
|
||||
#: ../js/ui/dateMenu.js:185
|
||||
msgid "%a %l:%M:%S %p"
|
||||
msgstr "%a %l.%M.%S %p"
|
||||
|
||||
#: ../js/ui/dateMenu.js:181
|
||||
#: ../js/ui/dateMenu.js:186
|
||||
msgid "%a %l:%M %p"
|
||||
msgstr "%a %l.%M %p"
|
||||
|
||||
#. 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:192
|
||||
#: ../js/ui/dateMenu.js:197
|
||||
msgid "%A %B %e, %Y"
|
||||
msgstr "%a %e %B, %Y"
|
||||
|
||||
@ -505,44 +502,53 @@ msgstr "Starter systemet på nytt."
|
||||
msgid "Cancel"
|
||||
msgstr "Avbryt"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:641
|
||||
#: ../js/ui/lookingGlass.js:640
|
||||
msgid "No extensions installed"
|
||||
msgstr "Ingen utvidelser installert"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:678
|
||||
#: ../js/ui/lookingGlass.js:686
|
||||
msgid "Enabled"
|
||||
msgstr "Aktivert"
|
||||
|
||||
#. translators:
|
||||
#. * The device has been disabled
|
||||
#: ../js/ui/lookingGlass.js:680 ../src/gvc/gvc-mixer-control.c:1091
|
||||
#: ../js/ui/lookingGlass.js:688 ../src/gvc/gvc-mixer-control.c:1091
|
||||
msgid "Disabled"
|
||||
msgstr "Deaktivert"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:682
|
||||
#: ../js/ui/lookingGlass.js:690
|
||||
msgid "Error"
|
||||
msgstr "Feil"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:684
|
||||
#: ../js/ui/lookingGlass.js:692
|
||||
msgid "Out of date"
|
||||
msgstr "Utdatert"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:709
|
||||
#: ../js/ui/lookingGlass.js:694
|
||||
msgid "Downloading"
|
||||
msgstr "Laster ned"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:719
|
||||
msgid "View Source"
|
||||
msgstr "Vis kildekode"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:715
|
||||
#: ../js/ui/lookingGlass.js:725
|
||||
msgid "Web Page"
|
||||
msgstr "Nettside"
|
||||
|
||||
#: ../js/ui/messageTray.js:1115
|
||||
#: ../js/ui/messageTray.js:1116
|
||||
msgid "Open"
|
||||
msgstr "Åpne"
|
||||
|
||||
#: ../js/ui/messageTray.js:2286
|
||||
#: ../js/ui/messageTray.js:2277
|
||||
msgid "System Information"
|
||||
msgstr "Systeminformasjon"
|
||||
|
||||
#: ../js/ui/notificationDaemon.js:427 ../js/ui/status/power.js:238
|
||||
#: ../src/shell-app.c:354
|
||||
msgid "Unknown"
|
||||
msgstr "Ukjent"
|
||||
|
||||
#: ../js/ui/overview.js:89
|
||||
msgid "Undo"
|
||||
msgstr "Angre"
|
||||
@ -562,18 +568,18 @@ msgid "Dash"
|
||||
msgstr "Favoritter"
|
||||
|
||||
#. TODO - _quit() doesn't really work on apps in state STARTING yet
|
||||
#: ../js/ui/panel.js:532
|
||||
#: ../js/ui/panel.js:531
|
||||
#, c-format
|
||||
msgid "Quit %s"
|
||||
msgstr "Avslutt %s"
|
||||
|
||||
#. Translators: If there is no suitable word for "Activities"
|
||||
#. in your language, you can use the word for "Overview".
|
||||
#: ../js/ui/panel.js:568
|
||||
#: ../js/ui/panel.js:567
|
||||
msgid "Activities"
|
||||
msgstr "Aktiviteter"
|
||||
|
||||
#: ../js/ui/panel.js:951
|
||||
#: ../js/ui/panel.js:876
|
||||
msgid "Top Bar"
|
||||
msgstr "Topp-panel"
|
||||
|
||||
@ -639,43 +645,9 @@ msgstr "Søker …"
|
||||
msgid "No matching results."
|
||||
msgstr "Ingen treff."
|
||||
|
||||
#: ../js/ui/statusMenu.js:192 ../js/ui/statusMenu.js:196
|
||||
#: ../js/ui/statusMenu.js:262
|
||||
msgid "Power Off..."
|
||||
msgstr "Slå av …"
|
||||
|
||||
#: ../js/ui/statusMenu.js:194 ../js/ui/statusMenu.js:196
|
||||
#: ../js/ui/statusMenu.js:261
|
||||
msgid "Suspend"
|
||||
msgstr "Hvilemodus"
|
||||
|
||||
#: ../js/ui/statusMenu.js:217
|
||||
msgid "Available"
|
||||
msgstr "Tilgjengelig"
|
||||
|
||||
#: ../js/ui/statusMenu.js:222
|
||||
msgid "Busy"
|
||||
msgstr "Opptatt"
|
||||
|
||||
#: ../js/ui/statusMenu.js:230
|
||||
msgid "My Account"
|
||||
msgstr "Min konto"
|
||||
|
||||
#: ../js/ui/statusMenu.js:234
|
||||
msgid "System Settings"
|
||||
msgstr "Systeminnstillinger"
|
||||
|
||||
#: ../js/ui/statusMenu.js:242
|
||||
msgid "Lock Screen"
|
||||
msgstr "Lås skjerm"
|
||||
|
||||
#: ../js/ui/statusMenu.js:247
|
||||
msgid "Switch User"
|
||||
msgstr "Bytt bruker"
|
||||
|
||||
#: ../js/ui/statusMenu.js:252
|
||||
msgid "Log Out..."
|
||||
msgstr "Logg ut …"
|
||||
#: ../js/ui/shellMountOperation.js:285
|
||||
msgid "Wrong password, please try again"
|
||||
msgstr "Feil passord. Prøv igjen"
|
||||
|
||||
#: ../js/ui/status/accessibility.js:60
|
||||
msgid "Zoom"
|
||||
@ -750,11 +722,11 @@ msgstr "maskinvare slått av"
|
||||
msgid "Connection"
|
||||
msgstr "Tilkobling"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:226 ../js/ui/status/network.js:493
|
||||
#: ../js/ui/status/bluetooth.js:226 ../js/ui/status/network.js:497
|
||||
msgid "disconnecting..."
|
||||
msgstr "kobler fra …"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:239 ../js/ui/status/network.js:499
|
||||
#: ../js/ui/status/bluetooth.js:239 ../js/ui/status/network.js:503
|
||||
msgid "connecting..."
|
||||
msgstr "kobler til …"
|
||||
|
||||
@ -805,7 +777,7 @@ msgstr "Alltid gi tilgang"
|
||||
msgid "Grant this time only"
|
||||
msgstr "Gi tilgang kun denne ene gangen"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:409 ../js/ui/telepathyClient.js:954
|
||||
#: ../js/ui/status/bluetooth.js:409 ../js/ui/telepathyClient.js:1058
|
||||
msgid "Reject"
|
||||
msgstr "Avvis"
|
||||
|
||||
@ -846,148 +818,186 @@ msgid "OK"
|
||||
msgstr "OK"
|
||||
|
||||
#: ../js/ui/status/keyboard.js:71
|
||||
msgid "Show Keyboard Layout..."
|
||||
msgstr "Vis tastaturutforming …"
|
||||
msgid "Show Keyboard Layout"
|
||||
msgstr "Vis tastaturutforming"
|
||||
|
||||
#: ../js/ui/status/keyboard.js:75
|
||||
msgid "Localization Settings"
|
||||
msgstr "Innstillinger for lokalisering"
|
||||
msgid "Region and Language Settings"
|
||||
msgstr "Innstillinger for region og språk"
|
||||
|
||||
#: ../js/ui/status/network.js:123
|
||||
#: ../js/ui/statusMenu.js:192 ../js/ui/statusMenu.js:196
|
||||
#: ../js/ui/statusMenu.js:262
|
||||
msgid "Power Off..."
|
||||
msgstr "Slå av …"
|
||||
|
||||
#: ../js/ui/statusMenu.js:194 ../js/ui/statusMenu.js:196
|
||||
#: ../js/ui/statusMenu.js:261
|
||||
msgid "Suspend"
|
||||
msgstr "Hvilemodus"
|
||||
|
||||
#: ../js/ui/statusMenu.js:217
|
||||
msgid "Available"
|
||||
msgstr "Tilgjengelig"
|
||||
|
||||
#: ../js/ui/statusMenu.js:222
|
||||
msgid "Busy"
|
||||
msgstr "Opptatt"
|
||||
|
||||
#: ../js/ui/statusMenu.js:230
|
||||
msgid "My Account"
|
||||
msgstr "Min konto"
|
||||
|
||||
#: ../js/ui/statusMenu.js:234
|
||||
msgid "System Settings"
|
||||
msgstr "Systeminnstillinger"
|
||||
|
||||
#: ../js/ui/statusMenu.js:242
|
||||
msgid "Lock Screen"
|
||||
msgstr "Lås skjerm"
|
||||
|
||||
#: ../js/ui/statusMenu.js:247
|
||||
msgid "Switch User"
|
||||
msgstr "Bytt bruker"
|
||||
|
||||
#: ../js/ui/statusMenu.js:252
|
||||
msgid "Log Out..."
|
||||
msgstr "Logg ut …"
|
||||
|
||||
#: ../js/ui/status/network.js:108
|
||||
msgid "<unknown>"
|
||||
msgstr "<ukjent>"
|
||||
|
||||
#. Translators: this indicates that wireless or wwan is disabled by hardware killswitch
|
||||
#: ../js/ui/status/network.js:292
|
||||
#: ../js/ui/status/network.js:296
|
||||
msgid "disabled"
|
||||
msgstr "slått av"
|
||||
|
||||
#. Translators: this is for network devices that are physically present but are not
|
||||
#. under NetworkManager's control (and thus cannot be used in the menu)
|
||||
#: ../js/ui/status/network.js:491
|
||||
#: ../js/ui/status/network.js:495
|
||||
msgid "unmanaged"
|
||||
msgstr "ikke håndtert"
|
||||
|
||||
#. Translators: this is for network connections that require some kind of key or password
|
||||
#: ../js/ui/status/network.js:502
|
||||
#: ../js/ui/status/network.js:506
|
||||
msgid "authentication required"
|
||||
msgstr "autentisering kreves"
|
||||
|
||||
#. Translators: this is for devices that require some kind of firmware or kernel
|
||||
#. module, which is missing
|
||||
#: ../js/ui/status/network.js:512
|
||||
#: ../js/ui/status/network.js:516
|
||||
msgid "firmware missing"
|
||||
msgstr "fastvare mangler"
|
||||
|
||||
#. Translators: this is for wired network devices that are physically disconnected
|
||||
#: ../js/ui/status/network.js:519
|
||||
#: ../js/ui/status/network.js:523
|
||||
msgid "cable unplugged"
|
||||
msgstr "kabel koblet fra"
|
||||
|
||||
#. Translators: this is for a network device that cannot be activated (for example it
|
||||
#. is disabled by rfkill, or it has no coverage
|
||||
#: ../js/ui/status/network.js:524
|
||||
#: ../js/ui/status/network.js:528
|
||||
msgid "unavailable"
|
||||
msgstr "ikke tilgjengelig"
|
||||
|
||||
#: ../js/ui/status/network.js:526
|
||||
#: ../js/ui/status/network.js:530
|
||||
msgid "connection failed"
|
||||
msgstr "tilkobling feilet"
|
||||
|
||||
#: ../js/ui/status/network.js:582 ../js/ui/status/network.js:1507
|
||||
#: ../js/ui/status/network.js:586 ../js/ui/status/network.js:1546
|
||||
msgid "More..."
|
||||
msgstr "Mer …"
|
||||
|
||||
#. 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:618 ../js/ui/status/network.js:1444
|
||||
#: ../js/ui/status/network.js:622 ../js/ui/status/network.js:1483
|
||||
msgid "Connected (private)"
|
||||
msgstr "Tilkoblet (privat)"
|
||||
|
||||
#: ../js/ui/status/network.js:703
|
||||
#: ../js/ui/status/network.js:707
|
||||
msgid "Auto Ethernet"
|
||||
msgstr "Automatisk Ethernet"
|
||||
|
||||
#: ../js/ui/status/network.js:771
|
||||
#: ../js/ui/status/network.js:775
|
||||
msgid "Auto broadband"
|
||||
msgstr "Automatisk bredbånd"
|
||||
|
||||
#: ../js/ui/status/network.js:774
|
||||
#: ../js/ui/status/network.js:778
|
||||
msgid "Auto dial-up"
|
||||
msgstr "Automatisk oppringt"
|
||||
|
||||
#. TRANSLATORS: this the automatic wireless connection name (including the network name)
|
||||
#: ../js/ui/status/network.js:898 ../js/ui/status/network.js:1456
|
||||
#: ../js/ui/status/network.js:902 ../js/ui/status/network.js:1495
|
||||
#, c-format
|
||||
msgid "Auto %s"
|
||||
msgstr "Automatisk %s"
|
||||
|
||||
#: ../js/ui/status/network.js:900
|
||||
#: ../js/ui/status/network.js:904
|
||||
msgid "Auto bluetooth"
|
||||
msgstr "Automatisk Bluetooth"
|
||||
|
||||
#: ../js/ui/status/network.js:1458
|
||||
#: ../js/ui/status/network.js:1497
|
||||
msgid "Auto wireless"
|
||||
msgstr "Automatisk trådløst"
|
||||
|
||||
#: ../js/ui/status/network.js:1550
|
||||
#: ../js/ui/status/network.js:1589
|
||||
msgid "Enable networking"
|
||||
msgstr "Slå på nettverk"
|
||||
|
||||
#: ../js/ui/status/network.js:1562
|
||||
#: ../js/ui/status/network.js:1601
|
||||
msgid "Wired"
|
||||
msgstr "Kablet"
|
||||
|
||||
#: ../js/ui/status/network.js:1573
|
||||
#: ../js/ui/status/network.js:1612
|
||||
msgid "Wireless"
|
||||
msgstr "Trådløst"
|
||||
|
||||
#: ../js/ui/status/network.js:1583
|
||||
#: ../js/ui/status/network.js:1622
|
||||
msgid "Mobile broadband"
|
||||
msgstr "Mobilt bredbånd"
|
||||
|
||||
#: ../js/ui/status/network.js:1593
|
||||
#: ../js/ui/status/network.js:1632
|
||||
msgid "VPN Connections"
|
||||
msgstr "VPN-tilkoblinger"
|
||||
|
||||
#: ../js/ui/status/network.js:1605
|
||||
#: ../js/ui/status/network.js:1644
|
||||
msgid "Network Settings"
|
||||
msgstr "Innstillinger for nettverk"
|
||||
|
||||
#: ../js/ui/status/network.js:1897
|
||||
#: ../js/ui/status/network.js:1936
|
||||
#, c-format
|
||||
msgid "You're now connected to mobile broadband connection '%s'"
|
||||
msgstr "Du er nå koblet til mobil bredbåndstilkobling «%s»"
|
||||
|
||||
#: ../js/ui/status/network.js:1901
|
||||
#: ../js/ui/status/network.js:1940
|
||||
#, c-format
|
||||
msgid "You're now connected to wireless network '%s'"
|
||||
msgstr "Du er nå koblet til trådløst nettverk «%s»"
|
||||
|
||||
#: ../js/ui/status/network.js:1905
|
||||
#: ../js/ui/status/network.js:1944
|
||||
#, c-format
|
||||
msgid "You're now connected to wired network '%s'"
|
||||
msgstr "Du er nå koblet til kablet nettverk «%s»"
|
||||
|
||||
#: ../js/ui/status/network.js:1909
|
||||
#: ../js/ui/status/network.js:1948
|
||||
#, c-format
|
||||
msgid "You're now connected to VPN network '%s'"
|
||||
msgstr "Du er nå koblet til virtuelt privat nettverk «%s»"
|
||||
|
||||
#: ../js/ui/status/network.js:1914
|
||||
#: ../js/ui/status/network.js:1953
|
||||
#, c-format
|
||||
msgid "You're now connected to '%s'"
|
||||
msgstr "Du er nå koblet til «%s»"
|
||||
|
||||
#: ../js/ui/status/network.js:1922
|
||||
#: ../js/ui/status/network.js:1961
|
||||
msgid "Connection established"
|
||||
msgstr "Tilkobling etablert"
|
||||
|
||||
#: ../js/ui/status/network.js:2048
|
||||
#: ../js/ui/status/network.js:2087
|
||||
msgid "Networking is disabled"
|
||||
msgstr "Nettverk er slått av"
|
||||
|
||||
#: ../js/ui/status/network.js:2173
|
||||
#: ../js/ui/status/network.js:2212
|
||||
msgid "Network Manager"
|
||||
msgstr "Nettverkshåndtering"
|
||||
|
||||
@ -1077,10 +1087,6 @@ msgstr "Nettbrett"
|
||||
msgid "Computer"
|
||||
msgstr "Datamaskin"
|
||||
|
||||
#: ../js/ui/status/power.js:238 ../src/shell-app-system.c:1088
|
||||
msgid "Unknown"
|
||||
msgstr "Ukjent"
|
||||
|
||||
#: ../js/ui/status/volume.js:43
|
||||
msgid "Volume"
|
||||
msgstr "Volum"
|
||||
@ -1092,31 +1098,36 @@ msgstr "Mikrofon"
|
||||
#. We got the TpContact
|
||||
#. FIXME: We don't have a 'chat room' icon (bgo #653737) use
|
||||
#. system-users for now as Empathy does.
|
||||
#: ../js/ui/telepathyClient.js:228
|
||||
#: ../js/ui/telepathyClient.js:235
|
||||
msgid "Invitation"
|
||||
msgstr "Invitasjon"
|
||||
|
||||
#. We got the TpContact
|
||||
#: ../js/ui/telepathyClient.js:291
|
||||
#: ../js/ui/telepathyClient.js:301
|
||||
msgid "Call"
|
||||
msgstr "Ring"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:541
|
||||
#. We got the TpContact
|
||||
#: ../js/ui/telepathyClient.js:329
|
||||
msgid "File Transfer"
|
||||
msgstr "Filoverføring"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:595
|
||||
#, c-format
|
||||
msgid "%s is online."
|
||||
msgstr "%s er tilkoblet."
|
||||
|
||||
#: ../js/ui/telepathyClient.js:546
|
||||
#: ../js/ui/telepathyClient.js:600
|
||||
#, c-format
|
||||
msgid "%s is offline."
|
||||
msgstr "%s er frakoblet."
|
||||
|
||||
#: ../js/ui/telepathyClient.js:549
|
||||
#: ../js/ui/telepathyClient.js:603
|
||||
#, c-format
|
||||
msgid "%s is away."
|
||||
msgstr "«%s» er borte."
|
||||
|
||||
#: ../js/ui/telepathyClient.js:552
|
||||
#: ../js/ui/telepathyClient.js:606
|
||||
#, c-format
|
||||
msgid "%s is busy."
|
||||
msgstr "%s er opptatt."
|
||||
@ -1124,21 +1135,35 @@ msgstr "%s er opptatt."
|
||||
#. Translators: this is a time format string followed by a date.
|
||||
#. If applicable, replace %X with a strftime format valid for your
|
||||
#. locale, without seconds.
|
||||
#: ../js/ui/telepathyClient.js:741
|
||||
#: ../js/ui/telepathyClient.js:840
|
||||
#, no-c-format
|
||||
msgid "Sent at %X on %A"
|
||||
msgstr "Sendt %X på %A"
|
||||
msgid "Sent at <b>%X</b> on <b>%A</b>"
|
||||
msgstr "Sendt <b>%X</b> på <b>%A</b>"
|
||||
|
||||
#. Translators: this is a time format in the style of "Wednesday, May 25",
|
||||
#. shown when you get a chat message in the same year.
|
||||
#: ../js/ui/telepathyClient.js:846
|
||||
#, no-c-format
|
||||
msgid "Sent on <b>%A</b>, <b>%B %d</b>"
|
||||
msgstr "Sendt <b>%A</b>, <b>%B %d</b>"
|
||||
|
||||
#. Translators: this is a time format in the style of "Wednesday, May 25, 2012",
|
||||
#. shown when you get a chat message in a different year.
|
||||
#: ../js/ui/telepathyClient.js:851
|
||||
#, no-c-format
|
||||
msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
|
||||
msgstr "Sendt <b>%A</b>, <b>%B %d</b>, %Y"
|
||||
|
||||
#. Translators: this is the other person changing their old IM name to their new
|
||||
#. IM name.
|
||||
#: ../js/ui/telepathyClient.js:791
|
||||
#: ../js/ui/telepathyClient.js:893
|
||||
#, c-format
|
||||
msgid "%s is now known as %s"
|
||||
msgstr "%s er nå kjent som %s"
|
||||
|
||||
#. translators: argument is a room name like
|
||||
#. * room@jabber.org for example.
|
||||
#: ../js/ui/telepathyClient.js:898
|
||||
#: ../js/ui/telepathyClient.js:1002
|
||||
#, c-format
|
||||
msgid "Invitation to %s"
|
||||
msgstr "Invitasjon til %s"
|
||||
@ -1146,35 +1171,45 @@ msgstr "Invitasjon til %s"
|
||||
#. translators: first argument is the name of a contact and the second
|
||||
#. * one the name of a room. "Alice is inviting you to join room@jabber.org
|
||||
#. * for example.
|
||||
#: ../js/ui/telepathyClient.js:906
|
||||
#: ../js/ui/telepathyClient.js:1010
|
||||
#, c-format
|
||||
msgid "%s is inviting you to join %s"
|
||||
msgstr "%s inviterer deg til å bli med i %s"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:908
|
||||
#: ../js/ui/telepathyClient.js:1012 ../js/ui/telepathyClient.js:1101
|
||||
msgid "Decline"
|
||||
msgstr "Avslå"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:909
|
||||
#: ../js/ui/telepathyClient.js:1013 ../js/ui/telepathyClient.js:1102
|
||||
msgid "Accept"
|
||||
msgstr "Godta"
|
||||
|
||||
#. translators: argument is a contact name like Alice for example.
|
||||
#: ../js/ui/telepathyClient.js:942
|
||||
#: ../js/ui/telepathyClient.js:1046
|
||||
#, c-format
|
||||
msgid "Video call from %s"
|
||||
msgstr "Videosamtale fra %s"
|
||||
|
||||
#. translators: argument is a contact name like Alice for example.
|
||||
#: ../js/ui/telepathyClient.js:945
|
||||
#: ../js/ui/telepathyClient.js:1049
|
||||
#, c-format
|
||||
msgid "Call from %s"
|
||||
msgstr "Samtale fra %s"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:955
|
||||
#: ../js/ui/telepathyClient.js:1059
|
||||
msgid "Answer"
|
||||
msgstr "Svar"
|
||||
|
||||
#. To translators: The first parameter is
|
||||
#. * the contact's alias and the second one is the
|
||||
#. * file name. The string will be something
|
||||
#. * like: "Alice is sending you test.ogg"
|
||||
#.
|
||||
#: ../js/ui/telepathyClient.js:1095
|
||||
#, c-format
|
||||
msgid "%s is sending you %s"
|
||||
msgstr "%s sender deg %s"
|
||||
|
||||
#. Translators: this is the text displayed
|
||||
#. in the search entry when no search is
|
||||
#. active; it should not exceed ~30
|
||||
@ -1183,16 +1218,16 @@ msgstr "Svar"
|
||||
msgid "Type to search..."
|
||||
msgstr "Skriv for å søke …"
|
||||
|
||||
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:257
|
||||
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:261
|
||||
msgid "Search"
|
||||
msgstr "Søk"
|
||||
|
||||
#: ../js/ui/windowAttentionHandler.js:40
|
||||
#: ../js/ui/windowAttentionHandler.js:39
|
||||
#, c-format
|
||||
msgid "%s has finished starting"
|
||||
msgstr "%s er ferdig startet"
|
||||
|
||||
#: ../js/ui/windowAttentionHandler.js:42
|
||||
#: ../js/ui/windowAttentionHandler.js:41
|
||||
#, c-format
|
||||
msgid "'%s' is ready"
|
||||
msgstr "«%s» er klar"
|
||||
@ -1223,7 +1258,7 @@ msgstr "Systemlyder"
|
||||
msgid "Print version"
|
||||
msgstr "Skriv ut versjon"
|
||||
|
||||
#: ../src/shell-app.c:464
|
||||
#: ../src/shell-app.c:580
|
||||
#, c-format
|
||||
msgid "Failed to launch '%s'"
|
||||
msgstr "Klarte ikke å starte «%s»"
|
||||
@ -1240,13 +1275,13 @@ msgstr "Forvalg"
|
||||
msgid "Authentication dialog was dismissed by the user"
|
||||
msgstr "Autentiseringsdialogen ble lukket av brukeren"
|
||||
|
||||
#: ../src/shell-util.c:96
|
||||
#: ../src/shell-util.c:100
|
||||
msgid "Home Folder"
|
||||
msgstr "Hjemmemappe"
|
||||
|
||||
#. Translators: this is the same string as the one found in
|
||||
#. * nautilus
|
||||
#: ../src/shell-util.c:111
|
||||
#: ../src/shell-util.c:115
|
||||
msgid "File System"
|
||||
msgstr "Filsystem"
|
||||
|
||||
@ -1255,7 +1290,11 @@ msgstr "Filsystem"
|
||||
#. * example, "Trash: some-directory". It means that the
|
||||
#. * directory called "some-directory" is in the trash.
|
||||
#.
|
||||
#: ../src/shell-util.c:307
|
||||
#: ../src/shell-util.c:311
|
||||
#, c-format
|
||||
msgid "%1$s: %2$s"
|
||||
msgstr "%1$s: %2$s"
|
||||
|
||||
#: ../src/shell-util.c:600
|
||||
msgid "calendar:week_start:0"
|
||||
msgstr "calendar:week_start:1"
|
||||
|
231
po/pa.po
231
po/pa.po
@ -7,8 +7,8 @@ msgstr ""
|
||||
"Project-Id-Version: gnome-shell master\n"
|
||||
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
|
||||
"shell&keywords=I18N+L10N&component=general\n"
|
||||
"POT-Creation-Date: 2011-07-15 16:19+0000\n"
|
||||
"PO-Revision-Date: 2011-07-18 08:29+0530\n"
|
||||
"POT-Creation-Date: 2011-08-09 23:03+0000\n"
|
||||
"PO-Revision-Date: 2011-08-18 06:57+0530\n"
|
||||
"Last-Translator: A S Alam <aalam@users.sf.net>\n"
|
||||
"Language-Team: Punjabi/Panjabi <punjabi-users@lists.sf.net>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -47,9 +47,6 @@ msgid "Framerate used for recording screencasts."
|
||||
msgstr "ਸਕਰੀਨਕਾਸਟ ਰਿਕਾਰਡ ਕਰਨ ਲਈ ਫਰੇਮਰੇਟ ਹੈ।"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:5
|
||||
#| msgid ""
|
||||
#| "GNOME Shell extensions have a uuid property; this key lists extensions "
|
||||
#| "which should not be loaded."
|
||||
msgid ""
|
||||
"GNOME Shell extensions have a uuid property; this key lists extensions which "
|
||||
"should be loaded. disabled-extensions overrides this setting for extensions "
|
||||
@ -58,7 +55,8 @@ msgstr ""
|
||||
"ਗਨੋਮ ਸ਼ੈਲ ਇਕਸਟੈਨਸ਼ਨ ਲਈ ਇੱਕ uuid ਵਿਸ਼ੇਸ਼ਤਾ ਹੈ; ਇਹ ਕੁੰਜੀ ਇਕਸਟੈਨਸ਼ਨ ਦਰਸਾਉਂਦੀ ਹੈ, "
|
||||
"ਜੋ ਲੋਡ ਹਨ। "
|
||||
"disabled-extensions ਇਹ ਸੈਟਿੰਗ ਉਹਨਾਂ ਇਕਸਟੈਨਸ਼ਨਾਂ ਨੂੰ ਅਣਡਿੱਠਾ ਕਰਦੀ ਹੈ, ਜੋ ਦੋਵੇਂ "
|
||||
"ਲਿਸਟਾਂ ਵਿੱਚ ਹੁੰਦੀਆਂ ਹਨ।"
|
||||
"ਲਿਸਟਾਂ ਵਿੱਚ "
|
||||
"ਹੁੰਦੀਆਂ ਹਨ।"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:6
|
||||
msgid ""
|
||||
@ -190,7 +188,6 @@ msgid "Uuids of extensions to disable"
|
||||
msgstr "ਇਕਟੈਨਸ਼ਨ ਦੀ Uuids ਬੰਦ ਹੈ"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:24
|
||||
#| msgid "Uuids of extensions to disable"
|
||||
msgid "Uuids of extensions to enable"
|
||||
msgstr "ਚਾਲੂ ਕਰਨ ਲਈ ਇਕਟੈਨਸ਼ਨ ਦੀ Uuid"
|
||||
|
||||
@ -222,23 +219,23 @@ msgstr "'%s' ਚਲਾਉਣ ਲਈ ਫੇਲ੍ਹ:"
|
||||
msgid "All"
|
||||
msgstr "ਸਭ"
|
||||
|
||||
#: ../js/ui/appDisplay.js:351
|
||||
#: ../js/ui/appDisplay.js:359
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "ਐਪਲੀਕੇਸ਼ਨ"
|
||||
|
||||
#: ../js/ui/appDisplay.js:377
|
||||
#: ../js/ui/appDisplay.js:385
|
||||
msgid "SETTINGS"
|
||||
msgstr "ਸੈਟਿੰਗ"
|
||||
|
||||
#: ../js/ui/appDisplay.js:650
|
||||
#: ../js/ui/appDisplay.js:658
|
||||
msgid "New Window"
|
||||
msgstr "ਨਵੀਂ ਵਿੰਡੋ"
|
||||
|
||||
#: ../js/ui/appDisplay.js:653
|
||||
#: ../js/ui/appDisplay.js:661
|
||||
msgid "Remove from Favorites"
|
||||
msgstr "ਪਸੰਦ ਵਿੱਚੋਂ ਹਟਾਓ"
|
||||
|
||||
#: ../js/ui/appDisplay.js:654
|
||||
#: ../js/ui/appDisplay.js:662
|
||||
msgid "Add to Favorites"
|
||||
msgstr "ਪਸੰਦ 'ਚ ਸ਼ਾਮਲ ਕਰੋ"
|
||||
|
||||
@ -252,6 +249,16 @@ msgstr "%s ਨੂੰ ਤੁਹਾਡੀ ਪਸੰਦ ਵਿੱਚ ਸ਼ਾਮ
|
||||
msgid "%s has been removed from your favorites."
|
||||
msgstr "%s ਨੂੰ ਤੁਹਾਡੀ ਪਸੰਦ ਤੋਂ ਹਟਾਇਆ ਜਾ ਚੁੱਕਿਆ ਹੈ।"
|
||||
|
||||
#: ../js/ui/autorunManager.js:591
|
||||
#, c-format
|
||||
msgid "Open with %s"
|
||||
msgstr "%s ਨਾਲ ਖੋਲ੍ਹੋ"
|
||||
|
||||
#: ../js/ui/autorunManager.js:617
|
||||
#| msgid "Reject"
|
||||
msgid "Eject"
|
||||
msgstr "ਬਾਹਰ ਕੱਢੋ"
|
||||
|
||||
#. Translators: Shown in calendar event list for all day events
|
||||
#. * Keep it short, best if you can use less then 10 characters
|
||||
#.
|
||||
@ -366,39 +373,39 @@ msgid "S"
|
||||
msgstr "ਸ਼"
|
||||
|
||||
#. Translators: Text to show if there are no events
|
||||
#: ../js/ui/calendar.js:701
|
||||
#: ../js/ui/calendar.js:678
|
||||
msgid "Nothing Scheduled"
|
||||
msgstr "ਕੋਈ ਵੀ ਸੈਡਿਊਲ ਨਹੀਂ ਹੈ"
|
||||
|
||||
#. Translators: Shown on calendar heading when selected day occurs on current year
|
||||
#: ../js/ui/calendar.js:717 ../js/ui/telepathyClient.js:749
|
||||
#: ../js/ui/calendar.js:694
|
||||
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:720 ../js/ui/telepathyClient.js:752
|
||||
#: ../js/ui/calendar.js:697
|
||||
msgctxt "calendar heading"
|
||||
msgid "%A, %B %d, %Y"
|
||||
msgstr "%A, %d %B %Y"
|
||||
|
||||
#: ../js/ui/calendar.js:730
|
||||
#: ../js/ui/calendar.js:707
|
||||
msgid "Today"
|
||||
msgstr "ਅੱਜ"
|
||||
|
||||
#: ../js/ui/calendar.js:734
|
||||
#: ../js/ui/calendar.js:711
|
||||
msgid "Tomorrow"
|
||||
msgstr "ਭਲਕ"
|
||||
|
||||
#: ../js/ui/calendar.js:743
|
||||
#: ../js/ui/calendar.js:720
|
||||
msgid "This week"
|
||||
msgstr "ਇਹ ਹਫ਼ਤਾ"
|
||||
|
||||
#: ../js/ui/calendar.js:751
|
||||
#: ../js/ui/calendar.js:728
|
||||
msgid "Next week"
|
||||
msgstr "ਹਫ਼ਤਾ ਅੱਗੇ"
|
||||
|
||||
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1122
|
||||
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1123
|
||||
msgid "Remove"
|
||||
msgstr "ਹਟਾਓ"
|
||||
|
||||
@ -530,44 +537,49 @@ msgstr "ਸਿਸਟਮ ਮੁੜ-ਚਾਲੂ ਕੀਤਾ ਜਾ ਰਿਹਾ
|
||||
msgid "Cancel"
|
||||
msgstr "ਰੱਦ ਕਰੋ"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:641
|
||||
#: ../js/ui/lookingGlass.js:640
|
||||
msgid "No extensions installed"
|
||||
msgstr "ਕੋਈ ਇਕਸਟੈਨਸ਼ਨ ਇੰਸਟਾਲ ਨਹੀਂ ਹੈ"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:678
|
||||
#: ../js/ui/lookingGlass.js:686
|
||||
msgid "Enabled"
|
||||
msgstr "ਚਾਲੂ ਹੈ"
|
||||
|
||||
#. translators:
|
||||
#. * The device has been disabled
|
||||
#: ../js/ui/lookingGlass.js:680 ../src/gvc/gvc-mixer-control.c:1091
|
||||
#: ../js/ui/lookingGlass.js:688 ../src/gvc/gvc-mixer-control.c:1091
|
||||
msgid "Disabled"
|
||||
msgstr "ਬੰਦ ਹੈ"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:682
|
||||
#: ../js/ui/lookingGlass.js:690
|
||||
msgid "Error"
|
||||
msgstr "ਗਲਤੀ"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:684
|
||||
#: ../js/ui/lookingGlass.js:692
|
||||
msgid "Out of date"
|
||||
msgstr "ਪੁਰਾਣਾ"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:709
|
||||
#: ../js/ui/lookingGlass.js:717
|
||||
msgid "View Source"
|
||||
msgstr "ਸਰੋਤ ਵੇਖੋ"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:715
|
||||
#: ../js/ui/lookingGlass.js:723
|
||||
msgid "Web Page"
|
||||
msgstr "ਵੈੱਬ ਪੇਜ਼"
|
||||
|
||||
#: ../js/ui/messageTray.js:1115
|
||||
#: ../js/ui/messageTray.js:1116
|
||||
msgid "Open"
|
||||
msgstr "ਖੋਲ੍ਹੋ"
|
||||
|
||||
#: ../js/ui/messageTray.js:2286
|
||||
#: ../js/ui/messageTray.js:2277
|
||||
msgid "System Information"
|
||||
msgstr "ਸਿਸਟਮ ਜਾਣਕਾਰੀ"
|
||||
|
||||
#: ../js/ui/notificationDaemon.js:426 ../js/ui/status/power.js:238
|
||||
#: ../src/shell-app-system.c:1115
|
||||
msgid "Unknown"
|
||||
msgstr "ਅਣਜਾਣ"
|
||||
|
||||
#: ../js/ui/overview.js:89
|
||||
msgid "Undo"
|
||||
msgstr "ਵਾਪਸ"
|
||||
@ -587,18 +599,18 @@ msgid "Dash"
|
||||
msgstr "ਡੈਸ਼"
|
||||
|
||||
#. TODO - _quit() doesn't really work on apps in state STARTING yet
|
||||
#: ../js/ui/panel.js:532
|
||||
#: ../js/ui/panel.js:531
|
||||
#, c-format
|
||||
msgid "Quit %s"
|
||||
msgstr "%s ਬੰਦ ਕਰੋ"
|
||||
|
||||
#. Translators: If there is no suitable word for "Activities"
|
||||
#. in your language, you can use the word for "Overview".
|
||||
#: ../js/ui/panel.js:568
|
||||
#: ../js/ui/panel.js:567
|
||||
msgid "Activities"
|
||||
msgstr "ਸਰਗਰਮੀਆਂ"
|
||||
|
||||
#: ../js/ui/panel.js:951
|
||||
#: ../js/ui/panel.js:876
|
||||
msgid "Top Bar"
|
||||
msgstr "ਉੱਤਲੀ ਪੱਟੀ"
|
||||
|
||||
@ -664,43 +676,9 @@ msgstr "ਖੋਜ ਜਾਰੀ ਹੈ..."
|
||||
msgid "No matching results."
|
||||
msgstr "ਕੋਈ ਨਤੀਜਾ ਨਹੀਂ ਲੱਭਿਆ।"
|
||||
|
||||
#: ../js/ui/statusMenu.js:192 ../js/ui/statusMenu.js:196
|
||||
#: ../js/ui/statusMenu.js:262
|
||||
msgid "Power Off..."
|
||||
msgstr "...ਬੰਦ ਕਰੋ"
|
||||
|
||||
#: ../js/ui/statusMenu.js:194 ../js/ui/statusMenu.js:196
|
||||
#: ../js/ui/statusMenu.js:261
|
||||
msgid "Suspend"
|
||||
msgstr "ਸਸਪੈਂਡ"
|
||||
|
||||
#: ../js/ui/statusMenu.js:217
|
||||
msgid "Available"
|
||||
msgstr "ਉਪਲੱਬਧ"
|
||||
|
||||
#: ../js/ui/statusMenu.js:222
|
||||
msgid "Busy"
|
||||
msgstr "ਰੁਝਿਆ"
|
||||
|
||||
#: ../js/ui/statusMenu.js:230
|
||||
msgid "My Account"
|
||||
msgstr "ਮੇਰਾ ਅਕਾਊਂਟ"
|
||||
|
||||
#: ../js/ui/statusMenu.js:234
|
||||
msgid "System Settings"
|
||||
msgstr "ਸਿਸਟਮ ਸੈਟਿੰਗ"
|
||||
|
||||
#: ../js/ui/statusMenu.js:242
|
||||
msgid "Lock Screen"
|
||||
msgstr "ਸਕਰੀਨ ਲਾਕ ਕਰੋ"
|
||||
|
||||
#: ../js/ui/statusMenu.js:247
|
||||
msgid "Switch User"
|
||||
msgstr "ਯੂਜ਼ਰ ਬਦਲੋ"
|
||||
|
||||
#: ../js/ui/statusMenu.js:252
|
||||
msgid "Log Out..."
|
||||
msgstr "...ਲਾਗਆਉਟ"
|
||||
#: ../js/ui/shellMountOperation.js:285
|
||||
msgid "Wrong password, please try again"
|
||||
msgstr "ਗਲਤ ਪਾਸਵਰਡ; ਮੁੜ ਕੋਸ਼ਿਸ਼ ਕਰੋ ਜੀ"
|
||||
|
||||
#: ../js/ui/status/accessibility.js:60
|
||||
msgid "Zoom"
|
||||
@ -830,7 +808,7 @@ msgstr "ਹਮੇਸ਼ਾ ਪਹੁੰਚ ਮਨਜ਼ੂਰ"
|
||||
msgid "Grant this time only"
|
||||
msgstr "ਕੇਵਲ ਇਸ ਸਮੇਂ ਹੀ ਮਨਜ਼ੂਰ"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:409 ../js/ui/telepathyClient.js:954
|
||||
#: ../js/ui/status/bluetooth.js:409 ../js/ui/telepathyClient.js:953
|
||||
msgid "Reject"
|
||||
msgstr "ਨਾ-ਮਨਜ਼ੂਰ"
|
||||
|
||||
@ -871,12 +849,52 @@ msgid "OK"
|
||||
msgstr "ਠੀਕ ਹੈ"
|
||||
|
||||
#: ../js/ui/status/keyboard.js:71
|
||||
msgid "Show Keyboard Layout..."
|
||||
msgstr "...ਕੀਬੋਰਡ ਲੇਆਉਟ ਵੇਖੋ"
|
||||
#| msgid "Show Keyboard Layout..."
|
||||
msgid "Show Keyboard Layout"
|
||||
msgstr "ਕੀਬੋਰਡ ਲੇਆਉਟ ਵੇਖਾਓ"
|
||||
|
||||
#: ../js/ui/status/keyboard.js:75
|
||||
msgid "Localization Settings"
|
||||
msgstr "ਲੋਕਲਾਈਜ਼ੇਸ਼ਨ ਸੈਟਿੰਗ"
|
||||
#| msgid "Date and Time Settings"
|
||||
msgid "Region and Language Settings"
|
||||
msgstr "ਖੇਤਰ ਅਤੇ ਭਾਸ਼ਾ ਸੈਟਿੰਗ"
|
||||
|
||||
#: ../js/ui/statusMenu.js:192 ../js/ui/statusMenu.js:196
|
||||
#: ../js/ui/statusMenu.js:262
|
||||
msgid "Power Off..."
|
||||
msgstr "...ਬੰਦ ਕਰੋ"
|
||||
|
||||
#: ../js/ui/statusMenu.js:194 ../js/ui/statusMenu.js:196
|
||||
#: ../js/ui/statusMenu.js:261
|
||||
msgid "Suspend"
|
||||
msgstr "ਸਸਪੈਂਡ"
|
||||
|
||||
#: ../js/ui/statusMenu.js:217
|
||||
msgid "Available"
|
||||
msgstr "ਉਪਲੱਬਧ"
|
||||
|
||||
#: ../js/ui/statusMenu.js:222
|
||||
msgid "Busy"
|
||||
msgstr "ਰੁਝਿਆ"
|
||||
|
||||
#: ../js/ui/statusMenu.js:230
|
||||
msgid "My Account"
|
||||
msgstr "ਮੇਰਾ ਅਕਾਊਂਟ"
|
||||
|
||||
#: ../js/ui/statusMenu.js:234
|
||||
msgid "System Settings"
|
||||
msgstr "ਸਿਸਟਮ ਸੈਟਿੰਗ"
|
||||
|
||||
#: ../js/ui/statusMenu.js:242
|
||||
msgid "Lock Screen"
|
||||
msgstr "ਸਕਰੀਨ ਲਾਕ ਕਰੋ"
|
||||
|
||||
#: ../js/ui/statusMenu.js:247
|
||||
msgid "Switch User"
|
||||
msgstr "ਯੂਜ਼ਰ ਬਦਲੋ"
|
||||
|
||||
#: ../js/ui/statusMenu.js:252
|
||||
msgid "Log Out..."
|
||||
msgstr "...ਲਾਗਆਉਟ"
|
||||
|
||||
#: ../js/ui/status/network.js:123
|
||||
msgid "<unknown>"
|
||||
@ -1102,10 +1120,6 @@ msgstr "ਟੇਬਲੇਟ"
|
||||
msgid "Computer"
|
||||
msgstr "ਕੰਪਿਊਟਰ"
|
||||
|
||||
#: ../js/ui/status/power.js:238 ../src/shell-app-system.c:1088
|
||||
msgid "Unknown"
|
||||
msgstr "ਅਣਜਾਣ"
|
||||
|
||||
#: ../js/ui/status/volume.js:43
|
||||
msgid "Volume"
|
||||
msgstr "ਆਵਾਜ਼"
|
||||
@ -1117,13 +1131,12 @@ msgstr "ਮਾਈਕਰੋਫੋਨ"
|
||||
#. We got the TpContact
|
||||
#. FIXME: We don't have a 'chat room' icon (bgo #653737) use
|
||||
#. system-users for now as Empathy does.
|
||||
#: ../js/ui/telepathyClient.js:222
|
||||
#: ../js/ui/telepathyClient.js:228
|
||||
msgid "Invitation"
|
||||
msgstr "ਸੱਦਾ"
|
||||
|
||||
#. We got the TpContact
|
||||
#: ../js/ui/telepathyClient.js:285
|
||||
#| msgid "Cancel"
|
||||
#: ../js/ui/telepathyClient.js:291
|
||||
msgid "Call"
|
||||
msgstr "ਕਾਲ ਕਰੋ"
|
||||
|
||||
@ -1150,21 +1163,36 @@ msgstr "%s ਰੁੱਝਿਆ/ਰੁੱਝੀ ਹੈ।"
|
||||
#. Translators: this is a time format string followed by a date.
|
||||
#. If applicable, replace %X with a strftime format valid for your
|
||||
#. locale, without seconds.
|
||||
#: ../js/ui/telepathyClient.js:741
|
||||
#: ../js/ui/telepathyClient.js:738
|
||||
#, no-c-format
|
||||
msgid "Sent at %X on %A"
|
||||
msgstr "%2$A ਨੂੰ %1$X ਵਜੇ ਭੇਜਿਆ"
|
||||
#| msgid "Sent at %X on %A"
|
||||
msgid "Sent at <b>%X</b> on <b>%A</b>"
|
||||
msgstr "<b>%2$A</b> ਨੂੰ <b>%1$X</b> ਵਜੇ ਭੇਜਿਆ"
|
||||
|
||||
#. Translators: this is a time format in the style of "Wednesday, May 25",
|
||||
#. shown when you get a chat message in the same year.
|
||||
#: ../js/ui/telepathyClient.js:744
|
||||
#, no-c-format
|
||||
msgid "Sent on <b>%A</b>, <b>%B %d</b>"
|
||||
msgstr " <b>%A</b> ਨੂੰ <b>%B %d</b> ਵਜੇ ਭੇਜਿਆ"
|
||||
|
||||
#. Translators: this is a time format in the style of "Wednesday, May 25, 2012",
|
||||
#. shown when you get a chat message in a different year.
|
||||
#: ../js/ui/telepathyClient.js:749
|
||||
#, no-c-format
|
||||
msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
|
||||
msgstr "<b>%A</b> <b>%B %d</b>, %Y ਨੂੰ ਭੇਜਿਆ"
|
||||
|
||||
#. Translators: this is the other person changing their old IM name to their new
|
||||
#. IM name.
|
||||
#: ../js/ui/telepathyClient.js:791
|
||||
#: ../js/ui/telepathyClient.js:790
|
||||
#, c-format
|
||||
msgid "%s is now known as %s"
|
||||
msgstr "%s ਨੂੰ ਹੁਣ %s ਵਜੋਂ ਜਾਣਿਆ ਜਾਵੇਗਾ"
|
||||
|
||||
#. translators: argument is a room name like
|
||||
#. * room@jabber.org for example.
|
||||
#: ../js/ui/telepathyClient.js:898
|
||||
#: ../js/ui/telepathyClient.js:897
|
||||
#, c-format
|
||||
msgid "Invitation to %s"
|
||||
msgstr "%s ਲਈ ਸੱਦਾ"
|
||||
@ -1172,32 +1200,32 @@ msgstr "%s ਲਈ ਸੱਦਾ"
|
||||
#. translators: first argument is the name of a contact and the second
|
||||
#. * one the name of a room. "Alice is inviting you to join room@jabber.org
|
||||
#. * for example.
|
||||
#: ../js/ui/telepathyClient.js:906
|
||||
#: ../js/ui/telepathyClient.js:905
|
||||
#, c-format
|
||||
msgid "%s is inviting you to join %s"
|
||||
msgstr "%s ਤੁਹਾਨੂੰ %s ਜੁਆਇੰਨ ਕਰਨ ਲਈ ਸੱਦ ਰਿਹਾ ਹੈ"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:908
|
||||
#: ../js/ui/telepathyClient.js:907
|
||||
msgid "Decline"
|
||||
msgstr "ਇਨਕਾਰ"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:909
|
||||
#: ../js/ui/telepathyClient.js:908
|
||||
msgid "Accept"
|
||||
msgstr "ਮਨਜ਼ੂਰ"
|
||||
|
||||
#. translators: argument is a contact name like Alice for example.
|
||||
#: ../js/ui/telepathyClient.js:942
|
||||
#: ../js/ui/telepathyClient.js:941
|
||||
#, c-format
|
||||
msgid "Video call from %s"
|
||||
msgstr "%s ਵਲੋਂ ਵਿਡੀਓ ਕਾਲ"
|
||||
|
||||
#. translators: argument is a contact name like Alice for example.
|
||||
#: ../js/ui/telepathyClient.js:945
|
||||
#: ../js/ui/telepathyClient.js:944
|
||||
#, c-format
|
||||
msgid "Call from %s"
|
||||
msgstr "%s ਵਲੋਂ ਕਾਲ"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:955
|
||||
#: ../js/ui/telepathyClient.js:954
|
||||
msgid "Answer"
|
||||
msgstr "ਜਵਾਬ"
|
||||
|
||||
@ -1209,16 +1237,16 @@ msgstr "ਜਵਾਬ"
|
||||
msgid "Type to search..."
|
||||
msgstr "...ਲੱਭਣ ਲਈ ਲਿਖੋ"
|
||||
|
||||
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:257
|
||||
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:261
|
||||
msgid "Search"
|
||||
msgstr "ਖੋਜ"
|
||||
|
||||
#: ../js/ui/windowAttentionHandler.js:40
|
||||
#: ../js/ui/windowAttentionHandler.js:39
|
||||
#, c-format
|
||||
msgid "%s has finished starting"
|
||||
msgstr "%s ਸ਼ੁਰੂ ਹੋਣਾ ਖਤਮ ਹੋਇਆ"
|
||||
|
||||
#: ../js/ui/windowAttentionHandler.js:42
|
||||
#: ../js/ui/windowAttentionHandler.js:41
|
||||
#, c-format
|
||||
msgid "'%s' is ready"
|
||||
msgstr "'%s' ਤਿਆਰ ਹੈ"
|
||||
@ -1266,13 +1294,13 @@ msgstr "ਡਿਫਾਲਟ"
|
||||
msgid "Authentication dialog was dismissed by the user"
|
||||
msgstr "ਪਰਮਾਣਕਿਤਾ ਡਾਈਲਾਗ ਯੂਜ਼ਰ ਵਲੋਂ ਰੱਦ ਕੀਤਾ"
|
||||
|
||||
#: ../src/shell-util.c:96
|
||||
#: ../src/shell-util.c:100
|
||||
msgid "Home Folder"
|
||||
msgstr "ਘਰ ਫੋਲਡਰ"
|
||||
|
||||
#. Translators: this is the same string as the one found in
|
||||
#. * nautilus
|
||||
#: ../src/shell-util.c:111
|
||||
#: ../src/shell-util.c:115
|
||||
msgid "File System"
|
||||
msgstr "ਫਾਇਲ ਸਿਸਟਮ"
|
||||
|
||||
@ -1281,11 +1309,18 @@ msgstr "ਫਾਇਲ ਸਿਸਟਮ"
|
||||
#. * example, "Trash: some-directory". It means that the
|
||||
#. * directory called "some-directory" is in the trash.
|
||||
#.
|
||||
#: ../src/shell-util.c:307
|
||||
#: ../src/shell-util.c:311
|
||||
#, c-format
|
||||
msgid "%1$s: %2$s"
|
||||
msgstr "%1$s: %2$s"
|
||||
|
||||
#: ../src/shell-util.c:586
|
||||
msgid "calendar:week_start:0"
|
||||
msgstr "calendar:week_start:1"
|
||||
|
||||
#~ msgid "Localization Settings"
|
||||
#~ msgstr "ਲੋਕਲਾਈਜ਼ੇਸ਼ਨ ਸੈਟਿੰਗ"
|
||||
|
||||
#~ msgid "Less than a minute ago"
|
||||
#~ msgstr "ਇੱਕ ਮਿੰਟ ਤੋਂ ਘੱਟ ਚਿਰ ਪਹਿਲਾਂ"
|
||||
|
||||
|
503
po/zh_CN.po
503
po/zh_CN.po
File diff suppressed because it is too large
Load Diff
767
po/zh_HK.po
767
po/zh_HK.po
File diff suppressed because it is too large
Load Diff
791
po/zh_TW.po
791
po/zh_TW.po
File diff suppressed because it is too large
Load Diff
@ -1,35 +0,0 @@
|
||||
gdmuser_cflags = \
|
||||
-I$(top_srcdir)/src \
|
||||
-DPREFIX=\""$(prefix)"\" \
|
||||
-DLIBDIR=\""$(libdir)"\" \
|
||||
-DDATADIR=\""$(datadir)"\" \
|
||||
-DG_DISABLE_DEPRECATED \
|
||||
-DG_LOG_DOMAIN=\"GdmUser\" \
|
||||
-DGDM_CACHE_DIR=\""$(localstatedir)/cache/gdm"\" \
|
||||
$(GDMUSER_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
# please, keep this sorted alphabetically
|
||||
gdmuser_source_h = \
|
||||
gdmuser/gdm-user.h \
|
||||
gdmuser/gdm-user-private.h \
|
||||
gdmuser/gdm-user-manager.h \
|
||||
$(NULL)
|
||||
|
||||
# please, keep this sorted alphabetically
|
||||
gdmuser_source_c = \
|
||||
gdmuser/gdm-user.c \
|
||||
gdmuser/gdm-user-manager.c \
|
||||
$(NULL)
|
||||
|
||||
noinst_LTLIBRARIES += libgdmuser-1.0.la
|
||||
|
||||
libgdmuser_1_0_la_LIBADD = $(GDMUSER_LIBS)
|
||||
libgdmuser_1_0_la_SOURCES = \
|
||||
$(gdmuser_source_c) \
|
||||
$(gdmuser_source_h) \
|
||||
$(NULL)
|
||||
libgdmuser_1_0_la_CPPFLAGS = $(gdmuser_cflags)
|
||||
libgdmuser_1_0_la_LDFLAGS = $(LDADD)
|
||||
|
||||
|
@ -28,6 +28,7 @@ CLEANFILES += $(service_DATA)
|
||||
CLEANFILES += $(gir_DATA) $(typelib_DATA)
|
||||
|
||||
bin_SCRIPTS += gnome-shell-extension-tool
|
||||
EXTRA_DIST += gnome-shell-extension-tool.in
|
||||
bin_PROGRAMS = gnome-shell-real
|
||||
|
||||
if USE_JHBUILD_WRAPPER_SCRIPT
|
||||
@ -36,6 +37,7 @@ bin_SCRIPTS += gnome-shell-jhbuild
|
||||
else
|
||||
gnome_shell = gnome-shell-real
|
||||
endif
|
||||
EXTRA_DIST += gnome-shell-jhbuild.in
|
||||
|
||||
noinst_DATA = gnome-shell
|
||||
gnome-shell: $(gnome_shell) Makefile
|
||||
@ -65,9 +67,7 @@ gnome-shell-extension-tool: gnome-shell-extension-tool.in Makefile
|
||||
$(AM_V_GEN) sed $(generated_script_substitutions) $< > $@.tmp && mv $@.tmp $@ && chmod a+x $@
|
||||
|
||||
CLEANFILES += gnome-shell $(bin_SCRIPTS)
|
||||
EXTRA_DIST += $(bin_SCRIPTS:=.in)
|
||||
|
||||
include Makefile-gdmuser.am
|
||||
include Makefile-st.am
|
||||
include Makefile-tray.am
|
||||
include Makefile-gvc.am
|
||||
@ -102,6 +102,7 @@ shell_public_headers_h = \
|
||||
shell-app-system.h \
|
||||
shell-app-usage.h \
|
||||
shell-arrow.h \
|
||||
shell-contact-system.h \
|
||||
shell-doc-system.h \
|
||||
shell-embedded-window.h \
|
||||
shell-generic-container.h \
|
||||
@ -109,10 +110,11 @@ shell_public_headers_h = \
|
||||
shell-global.h \
|
||||
shell-mobile-providers.h \
|
||||
shell-mount-operation.h \
|
||||
shell-network-agent.h \
|
||||
shell-perf-log.h \
|
||||
shell-slicer.h \
|
||||
shell-stack.h \
|
||||
shell-tp-client.h \
|
||||
shell-tp-client.h \
|
||||
shell-tray-icon.h \
|
||||
shell-tray-manager.h \
|
||||
shell-util.h \
|
||||
@ -136,6 +138,7 @@ libgnome_shell_la_SOURCES = \
|
||||
shell-app-system.c \
|
||||
shell-app-usage.c \
|
||||
shell-arrow.c \
|
||||
shell-contact-system.c \
|
||||
shell-doc-system.c \
|
||||
shell-embedded-window.c \
|
||||
shell-generic-container.c \
|
||||
@ -143,6 +146,7 @@ libgnome_shell_la_SOURCES = \
|
||||
shell-global.c \
|
||||
shell-mobile-providers.c \
|
||||
shell-mount-operation.c \
|
||||
shell-network-agent.c \
|
||||
shell-perf-log.c \
|
||||
shell-polkit-authentication-agent.h \
|
||||
shell-polkit-authentication-agent.c \
|
||||
@ -260,7 +264,6 @@ libgnome_shell_la_LIBADD = \
|
||||
$(GNOME_SHELL_LIBS) \
|
||||
$(BLUETOOTH_LIBS) \
|
||||
libst-1.0.la \
|
||||
libgdmuser-1.0.la \
|
||||
libtray.la \
|
||||
libgvc.la \
|
||||
$(NULL)
|
||||
@ -268,7 +271,7 @@ libgnome_shell_la_LIBADD = \
|
||||
libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags)
|
||||
|
||||
Shell-0.1.gir: libgnome-shell.la St-1.0.gir
|
||||
Shell_0_1_gir_INCLUDES = Clutter-1.0 ClutterX11-1.0 Meta-3.0 TelepathyGLib-0.12 TelepathyLogger-0.2 Soup-2.4
|
||||
Shell_0_1_gir_INCLUDES = Clutter-1.0 ClutterX11-1.0 Meta-3.0 TelepathyGLib-0.12 TelepathyLogger-0.2 Soup-2.4 GMenu-3.0 NetworkManager-1.0 NMClient-1.0 Folks-0.6
|
||||
Shell_0_1_gir_CFLAGS = $(libgnome_shell_la_CPPFLAGS) -I $(srcdir)
|
||||
Shell_0_1_gir_LIBS = libgnome-shell.la
|
||||
Shell_0_1_gir_FILES = $(libgnome_shell_la_gir_sources)
|
||||
@ -285,12 +288,3 @@ St_1_0_gir_FILES = $(filter-out %-private.h $(st_non_gir_sources), $(addprefix $
|
||||
$(addprefix $(srcdir)/,$(st_source_c))
|
||||
INTROSPECTION_GIRS += St-1.0.gir
|
||||
CLEANFILES += St-1.0.gir
|
||||
|
||||
Gdm-1.0.gir: libgdmuser-1.0.la
|
||||
Gdm_1_0_gir_INCLUDES = GObject-2.0 GdkPixbuf-2.0
|
||||
Gdm_1_0_gir_CFLAGS = $(gdmuser_cflags)
|
||||
Gdm_1_0_gir_LIBS = libgdmuser-1.0.la
|
||||
Gdm_1_0_gir_FILES = $(filter-out %-private.h, $(addprefix $(srcdir)/,$(gdmuser_source_h))) \
|
||||
$(addprefix $(srcdir)/,$(gdmuser_source_c))
|
||||
INTROSPECTION_GIRS += Gdm-1.0.gir
|
||||
CLEANFILES += Gdm-1.0.gir
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,91 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
|
||||
*
|
||||
* Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __GDM_USER_MANAGER_H__
|
||||
#define __GDM_USER_MANAGER_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "gdm-user.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GDM_TYPE_USER_MANAGER (gdm_user_manager_get_type ())
|
||||
#define GDM_USER_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_USER_MANAGER, GdmUserManager))
|
||||
#define GDM_USER_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_USER_MANAGER, GdmUserManagerClass))
|
||||
#define GDM_IS_USER_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_USER_MANAGER))
|
||||
#define GDM_IS_USER_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_USER_MANAGER))
|
||||
#define GDM_USER_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_USER_MANAGER, GdmUserManagerClass))
|
||||
|
||||
typedef struct GdmUserManagerPrivate GdmUserManagerPrivate;
|
||||
typedef struct GdmUserManager GdmUserManager;
|
||||
typedef struct GdmUserManagerClass GdmUserManagerClass;
|
||||
typedef enum GdmUserManagerError GdmUserManagerError;
|
||||
|
||||
struct GdmUserManager
|
||||
{
|
||||
GObject parent;
|
||||
GdmUserManagerPrivate *priv;
|
||||
};
|
||||
|
||||
struct GdmUserManagerClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
void (* user_added) (GdmUserManager *user_manager,
|
||||
GdmUser *user);
|
||||
void (* user_removed) (GdmUserManager *user_manager,
|
||||
GdmUser *user);
|
||||
void (* user_is_logged_in_changed) (GdmUserManager *user_manager,
|
||||
GdmUser *user);
|
||||
void (* user_changed) (GdmUserManager *user_manager,
|
||||
GdmUser *user);
|
||||
};
|
||||
|
||||
enum GdmUserManagerError
|
||||
{
|
||||
GDM_USER_MANAGER_ERROR_GENERAL,
|
||||
GDM_USER_MANAGER_ERROR_KEY_NOT_FOUND
|
||||
};
|
||||
|
||||
#define GDM_USER_MANAGER_ERROR gdm_user_manager_error_quark ()
|
||||
|
||||
GQuark gdm_user_manager_error_quark (void);
|
||||
GType gdm_user_manager_get_type (void);
|
||||
|
||||
GdmUserManager * gdm_user_manager_ref_default (void);
|
||||
|
||||
void gdm_user_manager_queue_load (GdmUserManager *manager);
|
||||
GSList * gdm_user_manager_list_users (GdmUserManager *manager);
|
||||
GdmUser * gdm_user_manager_get_user (GdmUserManager *manager,
|
||||
const char *username);
|
||||
GdmUser * gdm_user_manager_get_user_by_uid (GdmUserManager *manager,
|
||||
gulong uid);
|
||||
|
||||
gboolean gdm_user_manager_activate_user_session (GdmUserManager *manager,
|
||||
GdmUser *user);
|
||||
|
||||
gboolean gdm_user_manager_can_switch (GdmUserManager *manager);
|
||||
|
||||
gboolean gdm_user_manager_goto_login_session (GdmUserManager *manager);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GDM_USER_MANAGER_H */
|
@ -1,49 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
|
||||
*
|
||||
* Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Private interfaces to the GdmUser object
|
||||
*/
|
||||
|
||||
#ifndef __GDM_USER_PRIVATE_H_
|
||||
#define __GDM_USER_PRIVATE_H_
|
||||
|
||||
#include <pwd.h>
|
||||
|
||||
#include "gdm-user.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
void _gdm_user_update_from_object_path (GdmUser *user,
|
||||
const char *object_path);
|
||||
|
||||
void _gdm_user_update_from_pwent (GdmUser *user,
|
||||
const struct passwd *pwent);
|
||||
|
||||
void _gdm_user_update_login_frequency (GdmUser *user,
|
||||
guint64 login_frequency);
|
||||
|
||||
void _gdm_user_add_session (GdmUser *user,
|
||||
const char *session_id);
|
||||
void _gdm_user_remove_session (GdmUser *user,
|
||||
const char *session_id);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* !__GDM_USER_PRIVATE__ */
|
File diff suppressed because it is too large
Load Diff
@ -1,64 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
|
||||
*
|
||||
* Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
|
||||
* Copyright (C) 2007-2008 William Jon McCann <mccann@jhu.edu>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Facade object for user data, owned by GdmUserManager
|
||||
*/
|
||||
|
||||
#ifndef __GDM_USER_H__
|
||||
#define __GDM_USER_H__
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GDM_TYPE_USER (gdm_user_get_type ())
|
||||
#define GDM_USER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDM_TYPE_USER, GdmUser))
|
||||
#define GDM_IS_USER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDM_TYPE_USER))
|
||||
|
||||
typedef struct _GdmUser GdmUser;
|
||||
typedef struct _GdmUserClass GdmUserClass;
|
||||
|
||||
GType gdm_user_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GdmUser *gdm_user_new_from_object_path (const char *path);
|
||||
const char *gdm_user_get_object_path (GdmUser *user);
|
||||
|
||||
gulong gdm_user_get_uid (GdmUser *user);
|
||||
const char *gdm_user_get_user_name (GdmUser *user);
|
||||
const char *gdm_user_get_real_name (GdmUser *user);
|
||||
guint gdm_user_get_num_sessions (GdmUser *user);
|
||||
gboolean gdm_user_is_logged_in (GdmUser *user);
|
||||
gulong gdm_user_get_login_frequency (GdmUser *user);
|
||||
const char *gdm_user_get_icon_file (GdmUser *user);
|
||||
const char *gdm_user_get_primary_session_id (GdmUser *user);
|
||||
|
||||
GdkPixbuf *gdm_user_render_icon (GdmUser *user,
|
||||
gint icon_size);
|
||||
|
||||
gint gdm_user_collate (GdmUser *user1,
|
||||
GdmUser *user2);
|
||||
gboolean gdm_user_is_loaded (GdmUser *user);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
@ -33,7 +33,7 @@ const St = imports.gi.St;
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
let text;
|
||||
let text, button;
|
||||
|
||||
function _hideHello() {
|
||||
Main.uiGroup.remove_actor(text);
|
||||
@ -60,22 +60,28 @@ function _showHello() {
|
||||
onComplete: _hideHello });
|
||||
}
|
||||
|
||||
function main() {
|
||||
let button = new St.Bin({ style_class: 'panel-button',
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
x_fill: true,
|
||||
y_fill: false,
|
||||
track_hover: true });
|
||||
function init() {
|
||||
button = new St.Bin({ style_class: 'panel-button',
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
x_fill: true,
|
||||
y_fill: false,
|
||||
track_hover: true });
|
||||
let icon = new St.Icon({ icon_name: 'system-run',
|
||||
icon_type: St.IconType.SYMBOLIC,
|
||||
style_class: 'system-status-icon' });
|
||||
|
||||
button.set_child(icon);
|
||||
button.connect('button-press-event', _showHello);
|
||||
}
|
||||
|
||||
function enable() {
|
||||
Main.panel._rightBox.insert_actor(button, 0);
|
||||
}
|
||||
|
||||
function disable() {
|
||||
Main.panel._rightBox.remove_actor(button);
|
||||
}
|
||||
""",
|
||||
|
||||
"stylesheet.css": """
|
||||
|
@ -263,6 +263,11 @@ gvc_mixer_card_change_profile (GvcMixerCard *card,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gvc_mixer_card_get_profiles:
|
||||
*
|
||||
* Return value: (transfer none) (element-type GvcMixerCardProfile):
|
||||
*/
|
||||
const GList *
|
||||
gvc_mixer_card_get_profiles (GvcMixerCard *card)
|
||||
{
|
||||
@ -281,6 +286,10 @@ sort_profiles (GvcMixerCardProfile *a,
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* gvc_mixer_card_set_profiles:
|
||||
* @profiles: (transfer full) (element-type GvcMixerCardProfile):
|
||||
*/
|
||||
gboolean
|
||||
gvc_mixer_card_set_profiles (GvcMixerCard *card,
|
||||
GList *profiles)
|
||||
|
@ -81,11 +81,12 @@ struct GvcMixerControlPrivate
|
||||
GHashTable *cards;
|
||||
|
||||
GvcMixerStream *new_default_stream; /* new default stream, used in gvc_mixer_control_set_default_sink () */
|
||||
|
||||
GvcMixerControlState state;
|
||||
};
|
||||
|
||||
enum {
|
||||
CONNECTING,
|
||||
READY,
|
||||
STATE_CHANGED,
|
||||
STREAM_ADDED,
|
||||
STREAM_REMOVED,
|
||||
CARD_ADDED,
|
||||
@ -508,16 +509,17 @@ dec_outstanding (GvcMixerControl *control)
|
||||
}
|
||||
|
||||
if (--control->priv->n_outstanding <= 0) {
|
||||
g_signal_emit (G_OBJECT (control), signals[READY], 0);
|
||||
control->priv->state = GVC_STATE_READY;
|
||||
g_signal_emit (G_OBJECT (control), signals[STATE_CHANGED], 0, GVC_STATE_READY);
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
gvc_mixer_control_is_ready (GvcMixerControl *control)
|
||||
GvcMixerControlState
|
||||
gvc_mixer_control_get_state (GvcMixerControl *control)
|
||||
{
|
||||
g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE);
|
||||
|
||||
return (control->priv->n_outstanding == 0);
|
||||
return control->priv->state;
|
||||
}
|
||||
|
||||
|
||||
@ -1943,7 +1945,8 @@ _pa_context_state_cb (pa_context *context,
|
||||
break;
|
||||
|
||||
case PA_CONTEXT_FAILED:
|
||||
g_warning ("Connection failed, reconnecting...");
|
||||
control->priv->state = GVC_STATE_FAILED;
|
||||
g_signal_emit (control, signals[STATE_CHANGED], 0, GVC_STATE_FAILED);
|
||||
if (control->priv->reconnect_id == 0)
|
||||
control->priv->reconnect_id = g_timeout_add_seconds (RECONNECT_DELAY, idle_reconnect, control);
|
||||
break;
|
||||
@ -1968,7 +1971,8 @@ gvc_mixer_control_open (GvcMixerControl *control)
|
||||
_pa_context_state_cb,
|
||||
control);
|
||||
|
||||
g_signal_emit (G_OBJECT (control), signals[CONNECTING], 0);
|
||||
control->priv->state = GVC_STATE_CONNECTING;
|
||||
g_signal_emit (G_OBJECT (control), signals[STATE_CHANGED], 0, GVC_STATE_CONNECTING);
|
||||
res = pa_context_connect (control->priv->pa_context, NULL, (pa_context_flags_t) PA_CONTEXT_NOFAIL, NULL);
|
||||
if (res < 0) {
|
||||
g_warning ("Failed to connect context: %s",
|
||||
@ -1985,6 +1989,9 @@ gvc_mixer_control_close (GvcMixerControl *control)
|
||||
g_return_val_if_fail (control->priv->pa_context != NULL, FALSE);
|
||||
|
||||
pa_context_disconnect (control->priv->pa_context);
|
||||
|
||||
control->priv->state = GVC_STATE_CLOSED;
|
||||
g_signal_emit (G_OBJECT (control), signals[STATE_CHANGED], 0, GVC_STATE_CLOSED);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -2125,22 +2132,14 @@ gvc_mixer_control_class_init (GvcMixerControlClass *klass)
|
||||
NULL,
|
||||
G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
|
||||
|
||||
signals [CONNECTING] =
|
||||
g_signal_new ("connecting",
|
||||
signals [STATE_CHANGED] =
|
||||
g_signal_new ("state-changed",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GvcMixerControlClass, connecting),
|
||||
G_STRUCT_OFFSET (GvcMixerControlClass, state_changed),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0);
|
||||
signals [READY] =
|
||||
g_signal_new ("ready",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GvcMixerControlClass, ready),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0);
|
||||
g_cclosure_marshal_VOID__UINT,
|
||||
G_TYPE_NONE, 1, G_TYPE_UINT);
|
||||
signals [STREAM_ADDED] =
|
||||
g_signal_new ("stream-added",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
@ -2212,6 +2211,8 @@ gvc_mixer_control_init (GvcMixerControl *control)
|
||||
control->priv->cards = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
|
||||
|
||||
control->priv->clients = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_free);
|
||||
|
||||
control->priv->state = GVC_STATE_CLOSED;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -27,6 +27,14 @@
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GVC_STATE_CLOSED,
|
||||
GVC_STATE_READY,
|
||||
GVC_STATE_CONNECTING,
|
||||
GVC_STATE_FAILED
|
||||
} GvcMixerControlState;
|
||||
|
||||
#define GVC_TYPE_MIXER_CONTROL (gvc_mixer_control_get_type ())
|
||||
#define GVC_MIXER_CONTROL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_CONTROL, GvcMixerControl))
|
||||
#define GVC_MIXER_CONTROL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_CONTROL, GvcMixerControlClass))
|
||||
@ -46,8 +54,8 @@ typedef struct
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
void (*connecting) (GvcMixerControl *control);
|
||||
void (*ready) (GvcMixerControl *control);
|
||||
void (*state_changed) (GvcMixerControl *control,
|
||||
GvcMixerControlState new_state);
|
||||
void (*stream_added) (GvcMixerControl *control,
|
||||
guint id);
|
||||
void (*stream_removed) (GvcMixerControl *control,
|
||||
@ -68,7 +76,6 @@ GvcMixerControl * gvc_mixer_control_new (const char *name);
|
||||
|
||||
gboolean gvc_mixer_control_open (GvcMixerControl *control);
|
||||
gboolean gvc_mixer_control_close (GvcMixerControl *control);
|
||||
gboolean gvc_mixer_control_is_ready (GvcMixerControl *control);
|
||||
|
||||
GSList * gvc_mixer_control_get_cards (GvcMixerControl *control);
|
||||
GSList * gvc_mixer_control_get_streams (GvcMixerControl *control);
|
||||
@ -94,6 +101,8 @@ gboolean gvc_mixer_control_set_default_source (GvcMixerControl *con
|
||||
gdouble gvc_mixer_control_get_vol_max_norm (GvcMixerControl *control);
|
||||
gdouble gvc_mixer_control_get_vol_max_amplified (GvcMixerControl *control);
|
||||
|
||||
GvcMixerControlState gvc_mixer_control_get_state (GvcMixerControl *control);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GVC_MIXER_CONTROL_H */
|
||||
|
@ -508,6 +508,11 @@ gvc_mixer_stream_change_port (GvcMixerStream *stream,
|
||||
return GVC_MIXER_STREAM_GET_CLASS (stream)->change_port (stream, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* gvc_mixer_stream_get_ports:
|
||||
*
|
||||
* Return value: (transfer none) (element-type GvcMixerStreamPort):
|
||||
*/
|
||||
const GList *
|
||||
gvc_mixer_stream_get_ports (GvcMixerStream *stream)
|
||||
{
|
||||
@ -526,6 +531,10 @@ sort_ports (GvcMixerStreamPort *a,
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* gvc_mixer_stream_set_ports:
|
||||
* @ports: (transfer full) (element-type GvcMixerStreamPort):
|
||||
*/
|
||||
gboolean
|
||||
gvc_mixer_stream_set_ports (GvcMixerStream *stream,
|
||||
GList *ports)
|
||||
|
19
src/main.c
19
src/main.c
@ -24,6 +24,7 @@
|
||||
|
||||
#include "shell-a11y.h"
|
||||
#include "shell-global.h"
|
||||
#include "shell-global-private.h"
|
||||
#include "shell-perf-log.h"
|
||||
#include "st.h"
|
||||
|
||||
@ -32,6 +33,8 @@ extern GType gnome_shell_plugin_get_type (void);
|
||||
#define SHELL_DBUS_SERVICE "org.gnome.Shell"
|
||||
#define MAGNIFIER_DBUS_SERVICE "org.gnome.Magnifier"
|
||||
|
||||
static gboolean is_gdm_mode = FALSE;
|
||||
|
||||
static void
|
||||
shell_dbus_init (gboolean replace)
|
||||
{
|
||||
@ -466,6 +469,12 @@ GOptionEntry gnome_shell_options[] = {
|
||||
N_("Print version"),
|
||||
NULL
|
||||
},
|
||||
{
|
||||
"gdm-mode", 0, 0, G_OPTION_ARG_NONE,
|
||||
&is_gdm_mode,
|
||||
N_("Mode used by GDM for login screen"),
|
||||
NULL
|
||||
},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
@ -474,6 +483,7 @@ main (int argc, char **argv)
|
||||
{
|
||||
GOptionContext *ctx;
|
||||
GError *error = NULL;
|
||||
ShellSessionType session_type;
|
||||
int ecode;
|
||||
TpDebugSender *sender;
|
||||
|
||||
@ -524,7 +534,7 @@ main (int argc, char **argv)
|
||||
/* Disable debug spew from various libraries */
|
||||
g_log_set_handler ("Gvc", G_LOG_LEVEL_DEBUG,
|
||||
muted_log_handler, NULL);
|
||||
g_log_set_handler ("GdmUser", G_LOG_LEVEL_DEBUG,
|
||||
g_log_set_handler ("AccountsService", G_LOG_LEVEL_DEBUG,
|
||||
muted_log_handler, NULL);
|
||||
g_log_set_handler ("Bluetooth", G_LOG_LEVEL_DEBUG | G_LOG_LEVEL_MESSAGE,
|
||||
muted_log_handler, NULL);
|
||||
@ -540,7 +550,12 @@ main (int argc, char **argv)
|
||||
g_log_set_default_handler (default_log_handler, sender);
|
||||
|
||||
/* Initialize the global object */
|
||||
shell_global_get ();
|
||||
if (is_gdm_mode)
|
||||
session_type = SHELL_SESSION_GDM;
|
||||
else
|
||||
session_type = SHELL_SESSION_USER;
|
||||
|
||||
_shell_global_init ("session-type", session_type, NULL);
|
||||
|
||||
ecode = meta_run ();
|
||||
|
||||
|
@ -32,18 +32,17 @@
|
||||
|
||||
#include <clutter/x11/clutter-x11.h>
|
||||
#include <gdk/gdkx.h>
|
||||
#include <girepository.h>
|
||||
#include <gjs/gjs.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "shell-global.h"
|
||||
#include "shell-global-private.h"
|
||||
|
||||
static char **include_path = NULL;
|
||||
static char *command = NULL;
|
||||
|
||||
static GOptionEntry entries[] = {
|
||||
{ "command", 'c', 0, G_OPTION_ARG_STRING, &command, "Program passed in as a string", "COMMAND" },
|
||||
{ "include-path", 'I', 0, G_OPTION_ARG_STRING_ARRAY, &include_path, "Add the directory DIR to the list of directories to search for js files.", "DIR" },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
@ -131,6 +130,14 @@ main(int argc, char **argv)
|
||||
clutter_stage_set_title (CLUTTER_STAGE (stage), title);
|
||||
g_free (title);
|
||||
|
||||
#if HAVE_BLUETOOTH
|
||||
/* The module imports are all so intertwined that if the test
|
||||
* imports anything in js/ui, it will probably eventually end up
|
||||
* pulling in ui/status/bluetooth.js. So we need this.
|
||||
*/
|
||||
g_irepository_prepend_search_path (BLUETOOTH_DIR);
|
||||
#endif
|
||||
|
||||
/* evaluate the script */
|
||||
error = NULL;
|
||||
if (!gjs_context_eval (js_context, script, len,
|
||||
|
@ -10,11 +10,9 @@
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
ShellAppInfo *_shell_app_get_info (ShellApp *app);
|
||||
|
||||
ShellApp* _shell_app_new_for_window (MetaWindow *window);
|
||||
|
||||
ShellApp* _shell_app_new (ShellAppInfo *appinfo);
|
||||
ShellApp* _shell_app_new (GMenuTreeEntry *entry);
|
||||
|
||||
void _shell_app_handle_startup_sequence (ShellApp *app, SnStartupSequence *sequence);
|
||||
|
||||
@ -22,6 +20,13 @@ void _shell_app_add_window (ShellApp *app, MetaWindow *window);
|
||||
|
||||
void _shell_app_remove_window (ShellApp *app, MetaWindow *window);
|
||||
|
||||
void _shell_app_do_match (ShellApp *app,
|
||||
GSList *terms,
|
||||
GSList **multiple_prefix_results,
|
||||
GSList **prefix_results,
|
||||
GSList **multiple_substring_results,
|
||||
GSList **substring_results);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __SHELL_APP_PRIVATE_H__ */
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -5,6 +5,8 @@
|
||||
#include <gio/gio.h>
|
||||
#include <clutter/clutter.h>
|
||||
#include <meta/window.h>
|
||||
#define GMENU_I_KNOW_THIS_IS_UNSTABLE
|
||||
#include <gmenu-tree.h>
|
||||
|
||||
#include "shell-app.h"
|
||||
|
||||
@ -37,63 +39,33 @@ struct _ShellAppSystemClass
|
||||
GType shell_app_system_get_type (void) G_GNUC_CONST;
|
||||
ShellAppSystem *shell_app_system_get_default (void);
|
||||
|
||||
typedef struct _ShellAppInfo ShellAppInfo;
|
||||
GMenuTree *shell_app_system_get_tree (ShellAppSystem *system);
|
||||
|
||||
#define SHELL_TYPE_APP_INFO (shell_app_info_get_type ())
|
||||
GType shell_app_info_get_type (void);
|
||||
|
||||
ShellAppInfo *shell_app_info_ref (ShellAppInfo *info);
|
||||
void shell_app_info_unref (ShellAppInfo *info);
|
||||
|
||||
const char *shell_app_info_get_id (ShellAppInfo *info);
|
||||
char *shell_app_info_get_name (ShellAppInfo *info);
|
||||
char *shell_app_info_get_description (ShellAppInfo *info);
|
||||
char *shell_app_info_get_executable (ShellAppInfo *info);
|
||||
char *shell_app_info_get_desktop_file_path (ShellAppInfo *info);
|
||||
GIcon *shell_app_info_get_icon (ShellAppInfo *info);
|
||||
ClutterActor *shell_app_info_create_icon_texture (ShellAppInfo *info,
|
||||
float size);
|
||||
char *shell_app_info_get_section (ShellAppInfo *info);
|
||||
gboolean shell_app_info_get_is_nodisplay (ShellAppInfo *info);
|
||||
gboolean shell_app_info_is_transient (ShellAppInfo *info);
|
||||
MetaWindow *shell_app_info_get_source_window (ShellAppInfo *info);
|
||||
|
||||
gboolean shell_app_info_launch (ShellAppInfo *info,
|
||||
GError **error);
|
||||
gboolean shell_app_info_launch_full (ShellAppInfo *info,
|
||||
guint timestamp,
|
||||
GList *uris,
|
||||
int workspace,
|
||||
char **startup_id,
|
||||
GError **error);
|
||||
ShellApp *shell_app_system_lookup_app (ShellAppSystem *system,
|
||||
const char *id);
|
||||
ShellApp *shell_app_system_lookup_app_by_tree_entry (ShellAppSystem *system,
|
||||
GMenuTreeEntry *entry);
|
||||
ShellApp *shell_app_system_lookup_app_for_path (ShellAppSystem *system,
|
||||
const char *desktop_path);
|
||||
ShellApp *shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
|
||||
const char *id);
|
||||
|
||||
|
||||
GList *shell_app_system_get_sections (ShellAppSystem *system);
|
||||
GSList *shell_app_system_get_flattened_apps (ShellAppSystem *system);
|
||||
GSList *shell_app_system_get_all_settings (ShellAppSystem *system);
|
||||
|
||||
ShellApp *shell_app_system_get_app (ShellAppSystem *system,
|
||||
const char *id);
|
||||
ShellApp *shell_app_system_get_app_for_path (ShellAppSystem *system,
|
||||
const char *desktop_path);
|
||||
ShellApp *shell_app_system_get_app_for_window (ShellAppSystem *self,
|
||||
MetaWindow *window);
|
||||
ShellApp *shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
|
||||
const char *id);
|
||||
|
||||
ShellAppInfo *shell_app_system_create_from_window (ShellAppSystem *system,
|
||||
MetaWindow *window);
|
||||
GSList *shell_app_system_get_all (ShellAppSystem *system);
|
||||
|
||||
GSList *shell_app_system_initial_search (ShellAppSystem *system,
|
||||
gboolean prefs,
|
||||
GSList *terms);
|
||||
GSList *shell_app_system_subsearch (ShellAppSystem *system,
|
||||
gboolean prefs,
|
||||
GSList *previous_results,
|
||||
GSList *terms);
|
||||
|
||||
/* internal API */
|
||||
void _shell_app_system_register_app (ShellAppSystem *self,
|
||||
ShellApp *app);
|
||||
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__ */
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include <gdk/gdkx.h>
|
||||
#include <glib.h>
|
||||
#include <gio/gio.h>
|
||||
#include <dbus/dbus-glib.h>
|
||||
#include <meta/display.h>
|
||||
#include <meta/group.h>
|
||||
#include <meta/window.h>
|
||||
@ -82,7 +81,7 @@ struct _ShellAppUsage
|
||||
GObject parent;
|
||||
|
||||
GFile *configfile;
|
||||
DBusGProxy *session_proxy;
|
||||
GDBusProxy *session_proxy;
|
||||
GdkDisplay *display;
|
||||
gulong last_idle;
|
||||
guint idle_focus_change_id;
|
||||
@ -106,7 +105,7 @@ G_DEFINE_TYPE (ShellAppUsage, shell_app_usage, G_TYPE_OBJECT);
|
||||
struct UsageData
|
||||
{
|
||||
/* Whether the application we're tracking is "transient", see
|
||||
* shell_app_info_is_transient.
|
||||
* shell_app_is_window_backed.
|
||||
*/
|
||||
gboolean transient;
|
||||
|
||||
@ -116,7 +115,7 @@ struct UsageData
|
||||
|
||||
static void shell_app_usage_finalize (GObject *object);
|
||||
|
||||
static void on_session_status_changed (DBusGProxy *proxy, guint status, ShellAppUsage *self);
|
||||
static void on_session_status_changed (GDBusProxy *proxy, guint status, ShellAppUsage *self);
|
||||
static void on_focus_app_changed (ShellWindowTracker *tracker, GParamSpec *spec, ShellAppUsage *self);
|
||||
static void ensure_queued_save (ShellAppUsage *self);
|
||||
static UsageData * get_app_usage_for_context_and_id (ShellAppUsage *self,
|
||||
@ -316,7 +315,7 @@ on_app_state_changed (ShellWindowTracker *tracker,
|
||||
UsageData *usage;
|
||||
gboolean running;
|
||||
|
||||
if (shell_app_is_transient (app))
|
||||
if (shell_app_is_window_backed (app))
|
||||
return;
|
||||
|
||||
usage = get_usage_for_app (self, app);
|
||||
@ -343,7 +342,7 @@ on_focus_app_changed (ShellWindowTracker *tracker,
|
||||
}
|
||||
|
||||
static void
|
||||
on_session_status_changed (DBusGProxy *proxy,
|
||||
on_session_status_changed (GDBusProxy *proxy,
|
||||
guint status,
|
||||
ShellAppUsage *self)
|
||||
{
|
||||
@ -374,12 +373,23 @@ on_session_status_changed (DBusGProxy *proxy,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
session_proxy_signal (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, gpointer user_data)
|
||||
{
|
||||
if (g_str_equal (signal_name, "StatusChanged"))
|
||||
{
|
||||
guint status;
|
||||
g_variant_get (parameters, "(u)", &status);
|
||||
on_session_status_changed (proxy, status, SHELL_APP_USAGE (user_data));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
shell_app_usage_init (ShellAppUsage *self)
|
||||
{
|
||||
ShellGlobal *global;
|
||||
char *shell_userdata_dir, *path;
|
||||
DBusGConnection *session_bus;
|
||||
GDBusConnection *session_bus;
|
||||
ShellWindowTracker *tracker;
|
||||
|
||||
global = shell_global_get ();
|
||||
@ -390,14 +400,17 @@ shell_app_usage_init (ShellAppUsage *self)
|
||||
g_signal_connect (tracker, "notify::focus-app", G_CALLBACK (on_focus_app_changed), self);
|
||||
g_signal_connect (tracker, "app-state-changed", G_CALLBACK (on_app_state_changed), self);
|
||||
|
||||
session_bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
|
||||
self->session_proxy = dbus_g_proxy_new_for_name (session_bus, "org.gnome.SessionManager",
|
||||
"/org/gnome/SessionManager/Presence",
|
||||
"org.gnome.SessionManager");
|
||||
dbus_g_proxy_add_signal (self->session_proxy, "StatusChanged",
|
||||
G_TYPE_UINT, G_TYPE_INVALID, G_TYPE_INVALID);
|
||||
dbus_g_proxy_connect_signal (self->session_proxy, "StatusChanged",
|
||||
G_CALLBACK (on_session_status_changed), self, NULL);
|
||||
session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
|
||||
self->session_proxy = g_dbus_proxy_new_sync (session_bus,
|
||||
G_DBUS_PROXY_FLAGS_NONE,
|
||||
NULL, /* interface info */
|
||||
"org.gnome.SessionManager",
|
||||
"/org/gnome/SessionManager/Presence",
|
||||
"org.gnome.SessionManager",
|
||||
NULL, /* cancellable */
|
||||
NULL /* error */);
|
||||
g_signal_connect (self->session_proxy, "g-signal", G_CALLBACK (session_proxy_signal), self);
|
||||
g_object_unref (session_bus);
|
||||
|
||||
self->last_idle = 0;
|
||||
self->currently_idle = FALSE;
|
||||
@ -433,6 +446,8 @@ shell_app_usage_finalize (GObject *object)
|
||||
|
||||
g_object_unref (self->configfile);
|
||||
|
||||
g_object_unref (self->session_proxy);
|
||||
|
||||
G_OBJECT_CLASS (shell_app_usage_parent_class)->finalize(object);
|
||||
}
|
||||
|
||||
@ -494,7 +509,7 @@ shell_app_usage_get_most_used (ShellAppUsage *self,
|
||||
const char *appid = iter->data;
|
||||
ShellApp *app;
|
||||
|
||||
app = shell_app_system_get_app (appsys, appid);
|
||||
app = shell_app_system_lookup_app (appsys, appid);
|
||||
if (!app)
|
||||
continue;
|
||||
|
||||
@ -655,7 +670,7 @@ idle_save_application_usage (gpointer data)
|
||||
{
|
||||
ShellApp *app;
|
||||
|
||||
app = shell_app_system_get_app (shell_app_system_get_default(), id);
|
||||
app = shell_app_system_lookup_app (shell_app_system_get_default(), id);
|
||||
|
||||
if (!app)
|
||||
continue;
|
||||
|
508
src/shell-app.c
508
src/shell-app.c
@ -11,9 +11,18 @@
|
||||
#include "shell-app-private.h"
|
||||
#include "shell-enum-types.h"
|
||||
#include "shell-global.h"
|
||||
#include "shell-util.h"
|
||||
#include "shell-window-tracker-private.h"
|
||||
#include "st.h"
|
||||
|
||||
typedef enum {
|
||||
MATCH_NONE,
|
||||
MATCH_SUBSTRING, /* Not prefix, substring */
|
||||
MATCH_MULTIPLE_SUBSTRING, /* Matches multiple criteria with substrings */
|
||||
MATCH_PREFIX, /* Strict prefix */
|
||||
MATCH_MULTIPLE_PREFIX, /* Matches multiple criteria, at least one prefix */
|
||||
} ShellAppSearchMatch;
|
||||
|
||||
/* This is mainly a memory usage optimization - the user is going to
|
||||
* be running far fewer of the applications at one time than they have
|
||||
* installed. But it also just helps keep the code more logically
|
||||
@ -38,7 +47,7 @@ typedef struct {
|
||||
* SECTION:shell-app
|
||||
* @short_description: Object representing an application
|
||||
*
|
||||
* This object wraps a #ShellAppInfo, providing methods and signals
|
||||
* This object wraps a #GMenuTreeEntry, providing methods and signals
|
||||
* primarily useful for running applications.
|
||||
*/
|
||||
struct _ShellApp
|
||||
@ -49,9 +58,22 @@ struct _ShellApp
|
||||
|
||||
ShellAppState state;
|
||||
|
||||
ShellAppInfo *info;
|
||||
GMenuTreeEntry *entry; /* If NULL, this app is backed by one or more
|
||||
* MetaWindow. For purposes of app title
|
||||
* etc., we use the first window added,
|
||||
* because it's most likely to be what we
|
||||
* want (e.g. it will be of TYPE_NORMAL from
|
||||
* the way shell-window-tracker.c works).
|
||||
*/
|
||||
|
||||
ShellAppRunningState *running_state;
|
||||
|
||||
char *window_id_string;
|
||||
|
||||
char *casefolded_name;
|
||||
char *name_collation_key;
|
||||
char *casefolded_description;
|
||||
char *casefolded_exec;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (ShellApp, shell_app, G_TYPE_OBJECT);
|
||||
@ -93,7 +115,44 @@ shell_app_get_property (GObject *gobject,
|
||||
const char *
|
||||
shell_app_get_id (ShellApp *app)
|
||||
{
|
||||
return shell_app_info_get_id (app->info);
|
||||
if (app->entry)
|
||||
return gmenu_tree_entry_get_desktop_file_id (app->entry);
|
||||
return app->window_id_string;
|
||||
}
|
||||
|
||||
static MetaWindow *
|
||||
window_backed_app_get_window (ShellApp *app)
|
||||
{
|
||||
g_assert (app->entry == NULL);
|
||||
g_assert (app->running_state);
|
||||
g_assert (app->running_state->windows);
|
||||
return app->running_state->windows->data;
|
||||
}
|
||||
|
||||
static ClutterActor *
|
||||
window_backed_app_get_icon (ShellApp *app,
|
||||
int size)
|
||||
{
|
||||
MetaWindow *window;
|
||||
ClutterActor *actor;
|
||||
|
||||
/* During a state transition from running to not-running for
|
||||
* window-backend apps, it's possible we get a request for the icon.
|
||||
* Avoid asserting here and just return an empty image.
|
||||
*/
|
||||
if (app->running_state == NULL)
|
||||
{
|
||||
actor = clutter_texture_new ();
|
||||
g_object_set (actor, "opacity", 0, "width", (float) size, "height", (float) size, NULL);
|
||||
return actor;
|
||||
}
|
||||
|
||||
window = window_backed_app_get_window (app);
|
||||
actor = st_texture_cache_bind_pixbuf_property (st_texture_cache_get_default (),
|
||||
G_OBJECT (window),
|
||||
"icon");
|
||||
g_object_set (actor, "width", (float) size, "height", (float) size, NULL);
|
||||
return actor;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -106,10 +165,30 @@ shell_app_get_id (ShellApp *app)
|
||||
*/
|
||||
ClutterActor *
|
||||
shell_app_create_icon_texture (ShellApp *app,
|
||||
float size)
|
||||
int size)
|
||||
{
|
||||
return shell_app_info_create_icon_texture (app->info, size);
|
||||
GIcon *icon;
|
||||
ClutterActor *ret;
|
||||
|
||||
ret = NULL;
|
||||
|
||||
if (app->entry == NULL)
|
||||
return window_backed_app_get_icon (app, size);
|
||||
|
||||
icon = g_app_info_get_icon (G_APP_INFO (gmenu_tree_entry_get_app_info (app->entry)));
|
||||
if (icon != NULL)
|
||||
ret = st_texture_cache_load_gicon (st_texture_cache_get_default (), NULL, icon, size);
|
||||
|
||||
if (ret == NULL)
|
||||
{
|
||||
icon = g_themed_icon_new ("application-x-executable");
|
||||
ret = st_texture_cache_load_gicon (st_texture_cache_get_default (), NULL, icon, size);
|
||||
g_object_unref (icon);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
ShellApp *app;
|
||||
int size;
|
||||
@ -143,20 +222,19 @@ shell_app_create_faded_icon_cpu (StTextureCache *cache,
|
||||
|
||||
info = NULL;
|
||||
|
||||
icon = shell_app_info_get_icon (app->info);
|
||||
icon = g_app_info_get_icon (G_APP_INFO (gmenu_tree_entry_get_app_info (app->entry)));
|
||||
if (icon != NULL)
|
||||
{
|
||||
info = gtk_icon_theme_lookup_by_gicon (gtk_icon_theme_get_default (),
|
||||
icon, (int) (size + 0.5),
|
||||
icon, size,
|
||||
GTK_ICON_LOOKUP_FORCE_SIZE);
|
||||
g_object_unref (icon);
|
||||
}
|
||||
|
||||
if (info == NULL)
|
||||
{
|
||||
icon = g_themed_icon_new ("application-x-executable");
|
||||
info = gtk_icon_theme_lookup_by_gicon (gtk_icon_theme_get_default (),
|
||||
icon, (int) (size + 0.5),
|
||||
icon, size,
|
||||
GTK_ICON_LOOKUP_FORCE_SIZE);
|
||||
g_object_unref (icon);
|
||||
}
|
||||
@ -222,28 +300,23 @@ shell_app_create_faded_icon_cpu (StTextureCache *cache,
|
||||
* Return value: (transfer none): A floating #ClutterActor, or %NULL if no icon
|
||||
*/
|
||||
ClutterActor *
|
||||
shell_app_get_faded_icon (ShellApp *app, float size)
|
||||
shell_app_get_faded_icon (ShellApp *app, int size)
|
||||
{
|
||||
MetaWindow *window;
|
||||
CoglHandle texture;
|
||||
ClutterActor *result;
|
||||
char *cache_key;
|
||||
CreateFadedIconData data;
|
||||
|
||||
/* Punt for WINDOW types for now...easier to reuse the property tracking bits,
|
||||
* and this helps us visually distinguish app-tracked from not.
|
||||
/* Don't fade for window backed apps for now...easier to reuse the
|
||||
* property tracking bits, and this helps us visually distinguish
|
||||
* app-tracked from not.
|
||||
*/
|
||||
window = shell_app_info_get_source_window (app->info);
|
||||
if (window)
|
||||
{
|
||||
return st_texture_cache_bind_pixbuf_property (st_texture_cache_get_default (),
|
||||
G_OBJECT (window),
|
||||
"icon");
|
||||
}
|
||||
if (!app->entry)
|
||||
return window_backed_app_get_icon (app, size);
|
||||
|
||||
cache_key = g_strdup_printf ("faded-icon:%s,size=%f", shell_app_get_id (app), size);
|
||||
cache_key = g_strdup_printf ("faded-icon:%s,size=%d", shell_app_get_id (app), size);
|
||||
data.app = app;
|
||||
data.size = (int) (0.5 + size);
|
||||
data.size = size;
|
||||
texture = st_texture_cache_load (st_texture_cache_get_default (),
|
||||
cache_key,
|
||||
ST_TEXTURE_CACHE_POLICY_FOREVER,
|
||||
@ -260,28 +333,49 @@ shell_app_get_faded_icon (ShellApp *app, float size)
|
||||
else
|
||||
{
|
||||
result = clutter_texture_new ();
|
||||
g_object_set (result, "opacity", 0, "width", size, "height", size, NULL);
|
||||
g_object_set (result, "opacity", 0, "width", (float) size, "height", (float) size, NULL);
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
char *
|
||||
const char *
|
||||
shell_app_get_name (ShellApp *app)
|
||||
{
|
||||
return shell_app_info_get_name (app->info);
|
||||
if (app->entry)
|
||||
return g_app_info_get_name (G_APP_INFO (gmenu_tree_entry_get_app_info (app->entry)));
|
||||
else
|
||||
{
|
||||
MetaWindow *window = window_backed_app_get_window (app);
|
||||
const char *name;
|
||||
|
||||
name = meta_window_get_wm_class (window);
|
||||
if (!name)
|
||||
name = _("Unknown");
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
char *
|
||||
const char *
|
||||
shell_app_get_description (ShellApp *app)
|
||||
{
|
||||
return shell_app_info_get_description (app->info);
|
||||
if (app->entry)
|
||||
return g_app_info_get_description (G_APP_INFO (gmenu_tree_entry_get_app_info (app->entry)));
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_app_is_window_backed:
|
||||
*
|
||||
* A window backed application is one which represents just an open
|
||||
* window, i.e. there's no .desktop file assocation, so we don't know
|
||||
* how to launch it again.
|
||||
*/
|
||||
gboolean
|
||||
shell_app_is_transient (ShellApp *app)
|
||||
shell_app_is_window_backed (ShellApp *app)
|
||||
{
|
||||
return shell_app_info_is_transient (app->info);
|
||||
return app->entry == NULL;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
@ -435,8 +529,22 @@ shell_app_activate_window (ShellApp *app,
|
||||
/**
|
||||
* shell_app_activate:
|
||||
* @app: a #ShellApp
|
||||
*
|
||||
* Like shell_app_activate_full(), but using the default workspace and
|
||||
* event timestamp.
|
||||
*/
|
||||
void
|
||||
shell_app_activate (ShellApp *app)
|
||||
{
|
||||
return shell_app_activate_full (app, -1, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_app_activate_full:
|
||||
* @app: a #ShellApp
|
||||
* @workspace: launch on this workspace, or -1 for default. Ignored if
|
||||
* activating an existing window
|
||||
* @timestamp: Event timestamp
|
||||
*
|
||||
* Perform an appropriate default action for operating on this application,
|
||||
* dependent on its current state. For example, if the application is not
|
||||
@ -445,24 +553,32 @@ shell_app_activate_window (ShellApp *app,
|
||||
* recently used transient for that window).
|
||||
*/
|
||||
void
|
||||
shell_app_activate (ShellApp *app,
|
||||
int workspace)
|
||||
shell_app_activate_full (ShellApp *app,
|
||||
int workspace,
|
||||
guint32 timestamp)
|
||||
{
|
||||
ShellGlobal *global;
|
||||
|
||||
global = shell_global_get ();
|
||||
|
||||
if (timestamp == 0)
|
||||
timestamp = shell_global_get_current_time (global);
|
||||
|
||||
switch (app->state)
|
||||
{
|
||||
case SHELL_APP_STATE_STOPPED:
|
||||
{
|
||||
GError *error = NULL;
|
||||
if (!shell_app_info_launch_full (app->info,
|
||||
0,
|
||||
NULL,
|
||||
workspace,
|
||||
NULL,
|
||||
&error))
|
||||
if (!shell_app_launch (app,
|
||||
timestamp,
|
||||
NULL,
|
||||
workspace,
|
||||
NULL,
|
||||
&error))
|
||||
{
|
||||
char *msg;
|
||||
msg = g_strdup_printf (_("Failed to launch '%s'"), shell_app_get_name (app));
|
||||
shell_global_notify_error (shell_global_get (),
|
||||
shell_global_notify_error (global,
|
||||
msg,
|
||||
error->message);
|
||||
g_free (msg);
|
||||
@ -473,7 +589,7 @@ shell_app_activate (ShellApp *app,
|
||||
case SHELL_APP_STATE_STARTING:
|
||||
break;
|
||||
case SHELL_APP_STATE_RUNNING:
|
||||
shell_app_activate_window (app, NULL, shell_global_get_current_time (shell_global_get ()));
|
||||
shell_app_activate_window (app, NULL, timestamp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -489,6 +605,8 @@ void
|
||||
shell_app_open_new_window (ShellApp *app,
|
||||
int workspace)
|
||||
{
|
||||
g_return_if_fail (app->entry != NULL);
|
||||
|
||||
/* Here we just always launch the application again, even if we know
|
||||
* it was already running. For most applications this
|
||||
* should have the effect of creating a new window, whether that's
|
||||
@ -497,12 +615,12 @@ shell_app_open_new_window (ShellApp *app,
|
||||
* as say Pidgin. Ideally, we have the application express to us
|
||||
* that it supports an explicit new-window action.
|
||||
*/
|
||||
shell_app_info_launch_full (app->info,
|
||||
0,
|
||||
NULL,
|
||||
workspace,
|
||||
NULL,
|
||||
NULL);
|
||||
shell_app_launch (app,
|
||||
0,
|
||||
NULL,
|
||||
workspace,
|
||||
NULL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -517,17 +635,6 @@ shell_app_get_state (ShellApp *app)
|
||||
return app->state;
|
||||
}
|
||||
|
||||
/**
|
||||
* _shell_app_get_info:
|
||||
*
|
||||
* Returns: (transfer none): Associated app info
|
||||
*/
|
||||
ShellAppInfo *
|
||||
_shell_app_get_info (ShellApp *app)
|
||||
{
|
||||
return app->info;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
ShellApp *app;
|
||||
MetaWorkspace *active_workspace;
|
||||
@ -697,21 +804,22 @@ _shell_app_new_for_window (MetaWindow *window)
|
||||
ShellApp *app;
|
||||
|
||||
app = g_object_new (SHELL_TYPE_APP, NULL);
|
||||
app->info = shell_app_system_create_from_window (shell_app_system_get_default (), window);
|
||||
_shell_app_system_register_app (shell_app_system_get_default (), app);
|
||||
|
||||
app->window_id_string = g_strdup_printf ("window:%d", meta_window_get_stable_sequence (window));
|
||||
|
||||
_shell_app_add_window (app, window);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
ShellApp *
|
||||
_shell_app_new (ShellAppInfo *info)
|
||||
_shell_app_new (GMenuTreeEntry *info)
|
||||
{
|
||||
ShellApp *app;
|
||||
|
||||
app = g_object_new (SHELL_TYPE_APP, NULL);
|
||||
app->info = shell_app_info_ref (info);
|
||||
_shell_app_system_register_app (shell_app_system_get_default (), app);
|
||||
app->entry = gmenu_tree_item_ref (info);
|
||||
app->name_collation_key = g_utf8_collate_key (shell_app_get_name (app), -1);
|
||||
|
||||
return app;
|
||||
}
|
||||
@ -922,6 +1030,112 @@ shell_app_request_quit (ShellApp *app)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
_gather_pid_callback (GDesktopAppInfo *gapp,
|
||||
GPid pid,
|
||||
gpointer data)
|
||||
{
|
||||
ShellApp *app;
|
||||
ShellWindowTracker *tracker;
|
||||
|
||||
g_return_if_fail (data != NULL);
|
||||
|
||||
app = SHELL_APP (data);
|
||||
tracker = shell_window_tracker_get_default ();
|
||||
|
||||
_shell_window_tracker_add_child_process_app (tracker,
|
||||
pid,
|
||||
app);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_app_launch:
|
||||
* @timestamp: Event timestamp, or 0 for current event timestamp
|
||||
* @uris: (element-type utf8): List of uris to pass to application
|
||||
* @workspace: Start on this workspace, or -1 for default
|
||||
* @startup_id: (out): Returned startup notification ID, or %NULL if none
|
||||
* @error: A #GError
|
||||
*/
|
||||
gboolean
|
||||
shell_app_launch (ShellApp *app,
|
||||
guint timestamp,
|
||||
GList *uris,
|
||||
int workspace,
|
||||
char **startup_id,
|
||||
GError **error)
|
||||
{
|
||||
GDesktopAppInfo *gapp;
|
||||
GdkAppLaunchContext *context;
|
||||
gboolean ret;
|
||||
ShellGlobal *global;
|
||||
MetaScreen *screen;
|
||||
|
||||
if (startup_id)
|
||||
*startup_id = NULL;
|
||||
|
||||
if (app->entry == NULL)
|
||||
{
|
||||
MetaWindow *window = window_backed_app_get_window (app);
|
||||
/* We can't pass URIs into a window; shouldn't hit this
|
||||
* code path. If we do, fix the caller to disallow it.
|
||||
*/
|
||||
g_return_val_if_fail (uris == NULL, TRUE);
|
||||
|
||||
meta_window_activate (window, timestamp);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
global = shell_global_get ();
|
||||
screen = shell_global_get_screen (global);
|
||||
|
||||
if (timestamp == 0)
|
||||
timestamp = shell_global_get_current_time (global);
|
||||
|
||||
if (workspace < 0)
|
||||
workspace = meta_screen_get_active_workspace_index (screen);
|
||||
|
||||
context = gdk_app_launch_context_new ();
|
||||
gdk_app_launch_context_set_timestamp (context, timestamp);
|
||||
gdk_app_launch_context_set_desktop (context, workspace);
|
||||
|
||||
gapp = gmenu_tree_entry_get_app_info (app->entry);
|
||||
ret = g_desktop_app_info_launch_uris_as_manager (gapp, uris,
|
||||
G_APP_LAUNCH_CONTEXT (context),
|
||||
G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
|
||||
NULL, NULL,
|
||||
_gather_pid_callback, app,
|
||||
error);
|
||||
g_object_unref (context);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_app_get_app_info:
|
||||
* @app: a #ShellApp
|
||||
*
|
||||
* Returns: (transfer none): The #GDesktopAppInfo for this app, or %NULL if backed by a window
|
||||
*/
|
||||
GDesktopAppInfo *
|
||||
shell_app_get_app_info (ShellApp *app)
|
||||
{
|
||||
if (app->entry)
|
||||
return gmenu_tree_entry_get_app_info (app->entry);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_app_get_tree_entry:
|
||||
* @app: a #ShellApp
|
||||
*
|
||||
* Returns: (transfer none): The #GMenuTreeEntry for this app, or %NULL if backed by a window
|
||||
*/
|
||||
GMenuTreeEntry *
|
||||
shell_app_get_tree_entry (ShellApp *app)
|
||||
{
|
||||
return app->entry;
|
||||
}
|
||||
|
||||
static void
|
||||
create_running_state (ShellApp *app)
|
||||
{
|
||||
@ -951,6 +1165,157 @@ unref_running_state (ShellAppRunningState *state)
|
||||
g_slice_free (ShellAppRunningState, state);
|
||||
}
|
||||
|
||||
static char *
|
||||
trim_exec_line (const char *str)
|
||||
{
|
||||
const char *start, *end, *pos;
|
||||
|
||||
end = strchr (str, ' ');
|
||||
if (end == NULL)
|
||||
end = str + strlen (str);
|
||||
|
||||
start = str;
|
||||
while ((pos = strchr (start, '/')) && pos < end)
|
||||
start = ++pos;
|
||||
|
||||
return g_strndup (start, end - start);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_app_init_search_data (ShellApp *app)
|
||||
{
|
||||
const char *name;
|
||||
const char *exec;
|
||||
const char *comment;
|
||||
char *normalized_exec;
|
||||
GDesktopAppInfo *appinfo;
|
||||
|
||||
appinfo = gmenu_tree_entry_get_app_info (app->entry);
|
||||
name = g_app_info_get_name (G_APP_INFO (appinfo));
|
||||
app->casefolded_name = shell_util_normalize_and_casefold (name);
|
||||
|
||||
comment = g_app_info_get_description (G_APP_INFO (appinfo));
|
||||
app->casefolded_description = shell_util_normalize_and_casefold (comment);
|
||||
|
||||
exec = g_app_info_get_executable (G_APP_INFO (appinfo));
|
||||
normalized_exec = shell_util_normalize_and_casefold (exec);
|
||||
app->casefolded_exec = trim_exec_line (normalized_exec);
|
||||
g_free (normalized_exec);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_app_compare_by_name:
|
||||
* @app:
|
||||
* @other:
|
||||
*
|
||||
* Order two applications by name.
|
||||
*
|
||||
* Returns: -1, 0, or 1; suitable for use as a comparison function for e.g. g_slist_sort()
|
||||
*/
|
||||
int
|
||||
shell_app_compare_by_name (ShellApp *app, ShellApp *other)
|
||||
{
|
||||
return strcmp (app->name_collation_key, other->name_collation_key);
|
||||
}
|
||||
|
||||
static ShellAppSearchMatch
|
||||
_shell_app_match_search_terms (ShellApp *app,
|
||||
GSList *terms)
|
||||
{
|
||||
GSList *iter;
|
||||
ShellAppSearchMatch match;
|
||||
|
||||
if (G_UNLIKELY (!app->casefolded_name))
|
||||
shell_app_init_search_data (app);
|
||||
|
||||
match = MATCH_NONE;
|
||||
for (iter = terms; iter; iter = iter->next)
|
||||
{
|
||||
ShellAppSearchMatch current_match;
|
||||
const char *term = iter->data;
|
||||
const char *p;
|
||||
|
||||
current_match = MATCH_NONE;
|
||||
|
||||
p = strstr (app->casefolded_name, term);
|
||||
if (p == app->casefolded_name)
|
||||
current_match = MATCH_PREFIX;
|
||||
else if (p != NULL)
|
||||
current_match = MATCH_SUBSTRING;
|
||||
|
||||
p = strstr (app->casefolded_exec, term);
|
||||
if (p != NULL)
|
||||
{
|
||||
if (p == app->casefolded_exec)
|
||||
current_match = (current_match == MATCH_NONE) ? MATCH_PREFIX
|
||||
: MATCH_MULTIPLE_PREFIX;
|
||||
else if (current_match < MATCH_PREFIX)
|
||||
current_match = (current_match == MATCH_NONE) ? MATCH_SUBSTRING
|
||||
: MATCH_MULTIPLE_SUBSTRING;
|
||||
}
|
||||
|
||||
if (app->casefolded_description && current_match < MATCH_PREFIX)
|
||||
{
|
||||
/* Only do substring matches, as prefix matches are not meaningful
|
||||
* enough for descriptions
|
||||
*/
|
||||
p = strstr (app->casefolded_description, term);
|
||||
if (p != NULL)
|
||||
current_match = (current_match == MATCH_NONE) ? MATCH_SUBSTRING
|
||||
: MATCH_MULTIPLE_SUBSTRING;
|
||||
}
|
||||
|
||||
if (current_match == MATCH_NONE)
|
||||
return current_match;
|
||||
|
||||
if (current_match > match)
|
||||
match = current_match;
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
void
|
||||
_shell_app_do_match (ShellApp *app,
|
||||
GSList *terms,
|
||||
GSList **multiple_prefix_results,
|
||||
GSList **prefix_results,
|
||||
GSList **multiple_substring_results,
|
||||
GSList **substring_results)
|
||||
{
|
||||
ShellAppSearchMatch match;
|
||||
GAppInfo *appinfo;
|
||||
|
||||
g_assert (app != NULL);
|
||||
|
||||
/* Skip window-backed apps */
|
||||
appinfo = (GAppInfo*)shell_app_get_app_info (app);
|
||||
if (appinfo == NULL)
|
||||
return;
|
||||
/* Skip not-visible apps */
|
||||
if (!g_app_info_should_show (appinfo))
|
||||
return;
|
||||
|
||||
match = _shell_app_match_search_terms (app, terms);
|
||||
switch (match)
|
||||
{
|
||||
case MATCH_NONE:
|
||||
break;
|
||||
case MATCH_MULTIPLE_PREFIX:
|
||||
*multiple_prefix_results = g_slist_prepend (*multiple_prefix_results, app);
|
||||
break;
|
||||
case MATCH_PREFIX:
|
||||
*prefix_results = g_slist_prepend (*prefix_results, app);
|
||||
break;
|
||||
case MATCH_MULTIPLE_SUBSTRING:
|
||||
*multiple_substring_results = g_slist_prepend (*multiple_substring_results, app);
|
||||
break;
|
||||
case MATCH_SUBSTRING:
|
||||
*substring_results = g_slist_prepend (*substring_results, app);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
shell_app_init (ShellApp *self)
|
||||
{
|
||||
@ -962,10 +1327,10 @@ shell_app_dispose (GObject *object)
|
||||
{
|
||||
ShellApp *app = SHELL_APP (object);
|
||||
|
||||
if (app->info)
|
||||
if (app->entry)
|
||||
{
|
||||
shell_app_info_unref (app->info);
|
||||
app->info = NULL;
|
||||
gmenu_tree_item_unref (app->entry);
|
||||
app->entry = NULL;
|
||||
}
|
||||
|
||||
if (app->running_state)
|
||||
@ -977,6 +1342,20 @@ shell_app_dispose (GObject *object)
|
||||
G_OBJECT_CLASS(shell_app_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_app_finalize (GObject *object)
|
||||
{
|
||||
ShellApp *app = SHELL_APP (object);
|
||||
|
||||
g_free (app->window_id_string);
|
||||
|
||||
g_free (app->casefolded_name);
|
||||
g_free (app->name_collation_key);
|
||||
g_free (app->casefolded_description);
|
||||
|
||||
G_OBJECT_CLASS(shell_app_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_app_class_init(ShellAppClass *klass)
|
||||
{
|
||||
@ -984,6 +1363,7 @@ shell_app_class_init(ShellAppClass *klass)
|
||||
|
||||
gobject_class->get_property = shell_app_get_property;
|
||||
gobject_class->dispose = shell_app_dispose;
|
||||
gobject_class->finalize = shell_app_finalize;
|
||||
|
||||
shell_app_signals[WINDOWS_CHANGED] = g_signal_new ("windows-changed",
|
||||
SHELL_TYPE_APP,
|
||||
|
@ -5,6 +5,8 @@
|
||||
#include <clutter/clutter.h>
|
||||
#include <gio/gio.h>
|
||||
#include <meta/window.h>
|
||||
#define GMENU_I_KNOW_THIS_IS_UNSTABLE
|
||||
#include <gmenu-tree.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@ -34,17 +36,22 @@ typedef enum {
|
||||
GType shell_app_get_type (void) G_GNUC_CONST;
|
||||
|
||||
const char *shell_app_get_id (ShellApp *app);
|
||||
GMenuTreeEntry *shell_app_get_tree_entry (ShellApp *app);
|
||||
GDesktopAppInfo *shell_app_get_app_info (ShellApp *app);
|
||||
|
||||
ClutterActor *shell_app_create_icon_texture (ShellApp *app, float size);
|
||||
ClutterActor *shell_app_get_faded_icon (ShellApp *app, float size);
|
||||
char *shell_app_get_name (ShellApp *app);
|
||||
char *shell_app_get_description (ShellApp *app);
|
||||
gboolean shell_app_is_transient (ShellApp *app);
|
||||
ClutterActor *shell_app_create_icon_texture (ShellApp *app, int size);
|
||||
ClutterActor *shell_app_get_faded_icon (ShellApp *app, int size);
|
||||
const char *shell_app_get_name (ShellApp *app);
|
||||
const char *shell_app_get_description (ShellApp *app);
|
||||
gboolean shell_app_is_window_backed (ShellApp *app);
|
||||
|
||||
void shell_app_activate_window (ShellApp *app, MetaWindow *window, guint32 timestamp);
|
||||
|
||||
void shell_app_activate (ShellApp *app,
|
||||
int workspace);
|
||||
void shell_app_activate (ShellApp *app);
|
||||
|
||||
void shell_app_activate_full (ShellApp *app,
|
||||
int workspace,
|
||||
guint32 timestamp);
|
||||
|
||||
void shell_app_open_new_window (ShellApp *app,
|
||||
int workspace);
|
||||
@ -61,6 +68,15 @@ GSList *shell_app_get_pids (ShellApp *app);
|
||||
|
||||
gboolean shell_app_is_on_workspace (ShellApp *app, MetaWorkspace *workspace);
|
||||
|
||||
gboolean shell_app_launch (ShellApp *app,
|
||||
guint timestamp,
|
||||
GList *uris,
|
||||
int workspace,
|
||||
char **startup_id,
|
||||
GError **error);
|
||||
|
||||
int shell_app_compare_by_name (ShellApp *app, ShellApp *other);
|
||||
|
||||
int shell_app_compare (ShellApp *app, ShellApp *other);
|
||||
|
||||
G_END_DECLS
|
||||
|
353
src/shell-contact-system.c
Normal file
353
src/shell-contact-system.c
Normal file
@ -0,0 +1,353 @@
|
||||
/* This implements a complete suite for caching and searching contacts in the
|
||||
* Shell. We retrieve contacts from libfolks asynchronously and we search
|
||||
* these for display to the user. */
|
||||
|
||||
#include "shell-contact-system.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gprintf.h>
|
||||
#include <gee.h>
|
||||
#include <clutter/clutter.h>
|
||||
#include <folks/folks.h>
|
||||
|
||||
#include "shell-global.h"
|
||||
#include "shell-util.h"
|
||||
#include "st.h"
|
||||
|
||||
G_DEFINE_TYPE (ShellContactSystem, shell_contact_system, G_TYPE_OBJECT);
|
||||
|
||||
#define ALIAS_PREFIX_MATCH_WEIGHT 100
|
||||
#define ALIAS_SUBSTRING_MATCH_WEIGHT 90
|
||||
#define IM_PREFIX_MATCH_WEIGHT 10
|
||||
#define IM_SUBSTRING_MATCH_WEIGHT 5
|
||||
|
||||
|
||||
/* Callbacks */
|
||||
|
||||
static void
|
||||
prepare_individual_aggregator_cb (GObject *obj,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
FolksIndividualAggregator *aggregator = FOLKS_INDIVIDUAL_AGGREGATOR (obj);
|
||||
|
||||
folks_individual_aggregator_prepare_finish (aggregator, res, NULL);
|
||||
}
|
||||
|
||||
|
||||
/* Internal stuff */
|
||||
|
||||
typedef struct {
|
||||
gchar *key;
|
||||
guint weight;
|
||||
} ContactSearchResult;
|
||||
|
||||
struct _ShellContactSystemPrivate {
|
||||
FolksIndividualAggregator *aggregator;
|
||||
};
|
||||
|
||||
static void
|
||||
shell_contact_system_constructed (GObject *obj)
|
||||
{
|
||||
ShellContactSystem *self = SHELL_CONTACT_SYSTEM (obj);
|
||||
|
||||
G_OBJECT_CLASS (shell_contact_system_parent_class)->constructed (obj);
|
||||
|
||||
/* We intentionally do not care about the "individuals-changed" signal, as
|
||||
* we don't intend to update searches after they've been performed.
|
||||
* Therefore, we will simply retrieve the "individuals" property which
|
||||
* represents a snapshot of the individuals in the aggregator.
|
||||
*/
|
||||
self->priv->aggregator = folks_individual_aggregator_new ();
|
||||
folks_individual_aggregator_prepare (self->priv->aggregator, prepare_individual_aggregator_cb, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_contact_system_finalize (GObject *obj)
|
||||
{
|
||||
ShellContactSystem *self = SHELL_CONTACT_SYSTEM (obj);
|
||||
|
||||
g_object_unref (self->priv->aggregator);
|
||||
|
||||
G_OBJECT_CLASS (shell_contact_system_parent_class)->finalize (obj);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_contact_system_init (ShellContactSystem *self)
|
||||
{
|
||||
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, SHELL_TYPE_CONTACT_SYSTEM, ShellContactSystemPrivate);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_contact_system_class_init (ShellContactSystemClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->constructed = shell_contact_system_constructed;
|
||||
object_class->finalize = shell_contact_system_finalize;
|
||||
|
||||
g_type_class_add_private (object_class, sizeof (ShellContactSystemPrivate));
|
||||
}
|
||||
|
||||
/**
|
||||
* normalize_terms:
|
||||
* @terms: (element-type utf8): Input search terms
|
||||
*
|
||||
* Returns: (element-type utf8) (transfer full): Unicode-normalized and lowercased terms
|
||||
*/
|
||||
static GSList *
|
||||
normalize_terms (GSList *terms)
|
||||
{
|
||||
GSList *normalized_terms = NULL;
|
||||
GSList *iter;
|
||||
for (iter = terms; iter; iter = iter->next)
|
||||
{
|
||||
const char *term = iter->data;
|
||||
normalized_terms = g_slist_prepend (normalized_terms, shell_util_normalize_and_casefold (term));
|
||||
}
|
||||
return normalized_terms;
|
||||
}
|
||||
|
||||
static guint
|
||||
do_match (ShellContactSystem *self,
|
||||
FolksIndividual *individual,
|
||||
GSList *terms)
|
||||
{
|
||||
GSList *term_iter;
|
||||
guint weight = 0;
|
||||
|
||||
char *alias = shell_util_normalize_and_casefold (folks_alias_details_get_alias (FOLKS_ALIAS_DETAILS (individual)));
|
||||
|
||||
GeeMultiMap *im_addr_map = folks_im_details_get_im_addresses (FOLKS_IM_DETAILS (individual));
|
||||
GeeCollection *im_addrs = gee_multi_map_get_values (im_addr_map);
|
||||
GeeIterator *im_addrs_iter;
|
||||
|
||||
gboolean have_alias_prefix = FALSE;
|
||||
gboolean have_alias_substring = FALSE;
|
||||
|
||||
gboolean have_im_prefix = FALSE;
|
||||
gboolean have_im_substring = FALSE;
|
||||
|
||||
for (term_iter = terms; term_iter; term_iter = term_iter->next)
|
||||
{
|
||||
const char *term = term_iter->data;
|
||||
const char *p;
|
||||
|
||||
/* Match on alias */
|
||||
if (alias != NULL)
|
||||
{
|
||||
p = strstr (alias, term);
|
||||
if (p == alias)
|
||||
have_alias_prefix = TRUE;
|
||||
else if (p != NULL)
|
||||
have_alias_substring = TRUE;
|
||||
}
|
||||
|
||||
/* Match on one or more IM addresses */
|
||||
im_addrs_iter = gee_iterable_iterator (GEE_ITERABLE (im_addrs));
|
||||
|
||||
while (gee_iterator_next (im_addrs_iter))
|
||||
{
|
||||
const gchar *addr = gee_iterator_get (im_addrs_iter);
|
||||
|
||||
p = strstr (addr, term);
|
||||
if (p == addr)
|
||||
have_im_prefix = TRUE;
|
||||
else if (p != NULL)
|
||||
have_im_substring = TRUE;
|
||||
}
|
||||
|
||||
g_object_unref (im_addrs_iter);
|
||||
}
|
||||
|
||||
if (have_alias_prefix)
|
||||
weight += ALIAS_PREFIX_MATCH_WEIGHT;
|
||||
else if (have_alias_substring)
|
||||
weight += ALIAS_SUBSTRING_MATCH_WEIGHT;
|
||||
|
||||
if (have_im_prefix)
|
||||
weight += IM_PREFIX_MATCH_WEIGHT;
|
||||
else if (have_im_substring)
|
||||
weight += IM_SUBSTRING_MATCH_WEIGHT;
|
||||
|
||||
g_free (alias);
|
||||
g_object_unref (im_addrs);
|
||||
|
||||
return weight;
|
||||
}
|
||||
|
||||
static gint
|
||||
compare_results (gconstpointer a,
|
||||
gconstpointer b)
|
||||
{
|
||||
ContactSearchResult *first = (ContactSearchResult *) a;
|
||||
ContactSearchResult *second = (ContactSearchResult *) b;
|
||||
|
||||
if (first->weight > second->weight)
|
||||
return 1;
|
||||
else if (first->weight < second->weight)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
free_result (gpointer data,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_slice_free (ContactSearchResult, data);
|
||||
}
|
||||
|
||||
/* modifies and frees @results */
|
||||
static GSList *
|
||||
sort_and_prepare_results (GSList *results)
|
||||
{
|
||||
GSList *iter;
|
||||
GSList *sorted_results = NULL;
|
||||
|
||||
results = g_slist_sort (results, compare_results);
|
||||
|
||||
for (iter = results; iter; iter = iter->next)
|
||||
{
|
||||
ContactSearchResult *result = iter->data;
|
||||
gchar *id = result->key;
|
||||
sorted_results = g_slist_prepend (sorted_results, id);
|
||||
}
|
||||
|
||||
g_slist_foreach (results, (GFunc) free_result, NULL);
|
||||
|
||||
return sorted_results;
|
||||
}
|
||||
|
||||
|
||||
/* Methods */
|
||||
|
||||
/**
|
||||
* shell_contact_system_get_default:
|
||||
*
|
||||
* Return Value: (transfer none): The global #ShellContactSystem singleton
|
||||
*/
|
||||
ShellContactSystem *
|
||||
shell_contact_system_get_default (void)
|
||||
{
|
||||
static ShellContactSystem *instance = NULL;
|
||||
|
||||
if (instance == NULL)
|
||||
instance = g_object_new (SHELL_TYPE_CONTACT_SYSTEM, NULL);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_contact_system_get_all:
|
||||
* @self: A #ShellContactSystem
|
||||
*
|
||||
* Returns: (transfer none): All individuals
|
||||
*/
|
||||
GeeMap *
|
||||
shell_contact_system_get_all (ShellContactSystem *self)
|
||||
{
|
||||
GeeMap *individuals;
|
||||
|
||||
g_return_val_if_fail (SHELL_IS_CONTACT_SYSTEM (self), NULL);
|
||||
|
||||
individuals = folks_individual_aggregator_get_individuals (self->priv->aggregator);
|
||||
|
||||
return individuals;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_contact_system_get_individual:
|
||||
* @self: A #ShellContactSystem
|
||||
* @id: A #gchar with the ID of the FolksIndividual to be returned.
|
||||
*
|
||||
* Returns: (transfer full): A #FolksIndividual or NULL if @id could not be found.
|
||||
*/
|
||||
FolksIndividual *
|
||||
shell_contact_system_get_individual (ShellContactSystem *self,
|
||||
gchar *id)
|
||||
{
|
||||
GeeMap *individuals;
|
||||
gpointer key, value;
|
||||
|
||||
key = (gpointer) id;
|
||||
|
||||
g_return_val_if_fail (SHELL_IS_CONTACT_SYSTEM (self), NULL);
|
||||
|
||||
individuals = folks_individual_aggregator_get_individuals (self->priv->aggregator);
|
||||
|
||||
value = gee_map_get (individuals, key);
|
||||
|
||||
return FOLKS_INDIVIDUAL (value);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_contact_system_initial_search:
|
||||
* @shell: A #ShellContactSystem
|
||||
* @terms: (element-type utf8): List of terms, logical AND
|
||||
*
|
||||
* Search through contacts for the given search terms.
|
||||
*
|
||||
* Returns: (transfer container) (element-type utf8): List of contact
|
||||
* identifiers
|
||||
*/
|
||||
GSList *
|
||||
shell_contact_system_initial_search (ShellContactSystem *self,
|
||||
GSList *terms)
|
||||
{
|
||||
FolksIndividual *individual;
|
||||
GSList *results = NULL;
|
||||
GeeMap *individuals = NULL;
|
||||
ContactSearchResult *result;
|
||||
GeeMapIterator *iter;
|
||||
gpointer key;
|
||||
guint weight;
|
||||
GSList *normalized_terms = normalize_terms (terms);
|
||||
|
||||
g_return_val_if_fail (SHELL_IS_CONTACT_SYSTEM (self), NULL);
|
||||
|
||||
individuals = folks_individual_aggregator_get_individuals (self->priv->aggregator);
|
||||
|
||||
iter = gee_map_map_iterator (individuals);
|
||||
|
||||
while (gee_map_iterator_next (iter))
|
||||
{
|
||||
individual = gee_map_iterator_get_value (iter);
|
||||
weight = do_match (self, individual, normalized_terms);
|
||||
|
||||
if (weight != 0)
|
||||
{
|
||||
key = gee_map_iterator_get_key (iter);
|
||||
|
||||
result = g_slice_new (ContactSearchResult);
|
||||
result->key = (gchar *) key;
|
||||
result->weight = weight;
|
||||
|
||||
results = g_slist_append (results, result);
|
||||
}
|
||||
|
||||
g_object_unref (individual);
|
||||
}
|
||||
|
||||
return sort_and_prepare_results (results);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_contact_system_subsearch:
|
||||
* @shell: A #ShellContactSystem
|
||||
* @previous_results: (element-type utf8): List of previous results
|
||||
* @terms: (element-type utf8): List of terms, logical AND
|
||||
*
|
||||
* Search through a previous result set; for more information see
|
||||
* js/ui/search.js.
|
||||
*
|
||||
* Returns: (transfer container) (element-type utf8): List of contact
|
||||
* identifiers
|
||||
*/
|
||||
GSList *
|
||||
shell_contact_system_subsearch (ShellContactSystem *self,
|
||||
GSList *previous_results,
|
||||
GSList *terms)
|
||||
{
|
||||
return shell_contact_system_initial_search (self, terms);
|
||||
}
|
50
src/shell-contact-system.h
Normal file
50
src/shell-contact-system.h
Normal file
@ -0,0 +1,50 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
#ifndef __SHELL_CONTACT_SYSTEM_H__
|
||||
#define __SHELL_CONTACT_SYSTEM_H__
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
#include <gio/gio.h>
|
||||
#include <folks/folks.h>
|
||||
|
||||
#define SHELL_TYPE_CONTACT_SYSTEM (shell_contact_system_get_type ())
|
||||
#define SHELL_CONTACT_SYSTEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_CONTACT_SYSTEM, ShellContactSystem))
|
||||
#define SHELL_CONTACT_SYSTEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_CONTACT_SYSTEM, ShellContactSystemClass))
|
||||
#define SHELL_IS_CONTACT_SYSTEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_CONTACT_SYSTEM))
|
||||
#define SHELL_IS_CONTACT_SYSTEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_CONTACT_SYSTEM))
|
||||
#define SHELL_CONTACT_SYSTEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_CONTACT_SYSTEM, ShellContactSystemClass))
|
||||
|
||||
typedef struct _ShellContactSystem ShellContactSystem;
|
||||
typedef struct _ShellContactSystemClass ShellContactSystemClass;
|
||||
typedef struct _ShellContactSystemPrivate ShellContactSystemPrivate;
|
||||
|
||||
struct _ShellContactSystem
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
ShellContactSystemPrivate *priv;
|
||||
};
|
||||
|
||||
struct _ShellContactSystemClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
GType shell_contact_system_get_type (void) G_GNUC_CONST;
|
||||
|
||||
/* Methods */
|
||||
|
||||
ShellContactSystem * shell_contact_system_get_default (void);
|
||||
|
||||
GeeMap *shell_contact_system_get_all (ShellContactSystem *self);
|
||||
|
||||
FolksIndividual *shell_contact_system_get_individual (ShellContactSystem *self,
|
||||
gchar *id);
|
||||
|
||||
GSList * shell_contact_system_initial_search (ShellContactSystem *shell,
|
||||
GSList *terms);
|
||||
|
||||
GSList * shell_contact_system_subsearch (ShellContactSystem *shell,
|
||||
GSList *previous_results,
|
||||
GSList *terms);
|
||||
|
||||
#endif /* __SHELL_CONTACT_SYSTEM_H__ */
|
@ -247,34 +247,6 @@ shell_generic_container_finalize (GObject *object)
|
||||
G_OBJECT_CLASS (shell_generic_container_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
/* Based on implementation from clutter-group.c */
|
||||
static gboolean
|
||||
shell_generic_container_get_paint_volume (ClutterActor *actor,
|
||||
ClutterPaintVolume *volume)
|
||||
{
|
||||
GList *l, *children;
|
||||
|
||||
children = st_container_get_children_list (ST_CONTAINER (actor));
|
||||
|
||||
CLUTTER_ACTOR_CLASS (shell_generic_container_parent_class)->get_paint_volume (actor, volume);
|
||||
|
||||
for (l = children; l != NULL; l = l->next)
|
||||
{
|
||||
ClutterActor *child = l->data;
|
||||
const ClutterPaintVolume *child_volume;
|
||||
|
||||
/* This gets the paint volume of the child transformed into the
|
||||
* group's coordinate space... */
|
||||
child_volume = clutter_actor_get_transformed_paint_volume (child, actor);
|
||||
if (!child_volume)
|
||||
return FALSE;
|
||||
|
||||
clutter_paint_volume_union (volume, child_volume);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
shell_generic_container_class_init (ShellGenericContainerClass *klass)
|
||||
{
|
||||
@ -287,7 +259,6 @@ shell_generic_container_class_init (ShellGenericContainerClass *klass)
|
||||
actor_class->get_preferred_width = shell_generic_container_get_preferred_width;
|
||||
actor_class->get_preferred_height = shell_generic_container_get_preferred_height;
|
||||
actor_class->allocate = shell_generic_container_allocate;
|
||||
actor_class->get_paint_volume = shell_generic_container_get_paint_volume;
|
||||
actor_class->paint = shell_generic_container_paint;
|
||||
actor_class->pick = shell_generic_container_pick;
|
||||
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
#include <gjs/gjs.h>
|
||||
|
||||
void _shell_global_init (const char *first_property_name,
|
||||
...);
|
||||
void _shell_global_set_plugin (ShellGlobal *global,
|
||||
MetaPlugin *plugin);
|
||||
|
||||
@ -13,4 +15,22 @@ GjsContext *_shell_global_get_gjs_context (ShellGlobal *global);
|
||||
|
||||
gboolean _shell_global_check_xdnd_event (ShellGlobal *global,
|
||||
XEvent *xev);
|
||||
|
||||
void _shell_global_set_session_type (ShellGlobal *global,
|
||||
ShellSessionType session_type);
|
||||
|
||||
/* Used for async screenshot grabbing */
|
||||
typedef struct _screenshot_data {
|
||||
ShellGlobal *global;
|
||||
|
||||
char *filename;
|
||||
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
ShellGlobalScreenshotCallback callback;
|
||||
} _screenshot_data;
|
||||
|
||||
#endif /* __SHELL_GLOBAL_PRIVATE_H__ */
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user