Compare commits
180 Commits
wip/re-sea
...
3.7.4
Author | SHA1 | Date | |
---|---|---|---|
f0ebc84840 | |||
490eb59ea3 | |||
16a9391726 | |||
adf8ba67d2 | |||
c37b222cbf | |||
72f8f2beb1 | |||
16bb9c17f5 | |||
ca44977d92 | |||
bcf294475f | |||
3b31774dd3 | |||
c106347c59 | |||
2679be9d97 | |||
4d59368c7d | |||
3f29680fb6 | |||
8f1df14ae7 | |||
7159e360d9 | |||
65723bcac5 | |||
73ae451cb4 | |||
dc0ea3a248 | |||
9548cd8341 | |||
8410fc38c9 | |||
0c9c2168b3 | |||
2690f54743 | |||
22783813c5 | |||
b5ed46a5af | |||
1a3f17e296 | |||
9281129f07 | |||
0e9ddd3b99 | |||
d87db04e55 | |||
906ec3c8a9 | |||
449575ceae | |||
6255c77eba | |||
c33622f2b3 | |||
e4860acb58 | |||
e96867f757 | |||
57bd43baf3 | |||
3a4e595d32 | |||
dcad22bfa8 | |||
acba0e47d8 | |||
dd19459e18 | |||
8be3c5ed21 | |||
51726d8de7 | |||
8f41c6bad8 | |||
e294abc567 | |||
9f9518c872 | |||
5faf7cb59e | |||
4b095d532c | |||
4288761235 | |||
73388f30fd | |||
2d9ddd4bc8 | |||
55aa0cf303 | |||
52ca15b514 | |||
bd383888de | |||
1bd349485f | |||
155f9dc1b1 | |||
fe7ee1edc3 | |||
c11cbff605 | |||
fbc629266f | |||
75d44dca6b | |||
3e6b794a33 | |||
a757ce48a1 | |||
507f29a7bd | |||
e5f4f77073 | |||
f07fee538d | |||
fa1420b384 | |||
724a2bd72f | |||
d525d02348 | |||
4920cf2b98 | |||
f41d0938a4 | |||
924a405829 | |||
fc9225e24a | |||
ac202cbdd3 | |||
cedd68c942 | |||
4231c879ef | |||
42e58a4b72 | |||
ebd1bc83c9 | |||
0bc9d7455f | |||
5274166f8c | |||
5566aa4588 | |||
ab638a4f54 | |||
86a8452ab7 | |||
8c96c16eac | |||
45c2e6575d | |||
a786a7dc55 | |||
6adf5cbee5 | |||
1964b54627 | |||
c9d0e82c52 | |||
1a4948f0f2 | |||
66da3f5668 | |||
9ebeb64570 | |||
571aaece2e | |||
f60fb954a2 | |||
1d136cacfb | |||
994021d77f | |||
8899325a0e | |||
43876a9357 | |||
d6cace32f5 | |||
2388de455b | |||
b14b3ab276 | |||
32ccb779c6 | |||
52536e94e5 | |||
5d6b3abd26 | |||
79682bbcb0 | |||
770ff19313 | |||
8d4855f100 | |||
7d4e14f384 | |||
f162dd7e87 | |||
06dc12e217 | |||
36edff9e15 | |||
34a0c079de | |||
84b581ae13 | |||
587c93eadf | |||
b5aa549e0c | |||
5fb6738612 | |||
685262fc5e | |||
8802f6cf23 | |||
2698d0db29 | |||
2fa7230133 | |||
658243eae4 | |||
7282bc8859 | |||
07511cb21a | |||
f71f767a3c | |||
176daa1469 | |||
5487f8cf93 | |||
944762ac83 | |||
c66068c435 | |||
d479c936b8 | |||
c5a3f784dd | |||
529d012865 | |||
8a1b83417a | |||
6c64c0873c | |||
d2c6149923 | |||
9f98f6f02d | |||
2f7c0b1985 | |||
f61548234a | |||
6eef830ba4 | |||
88a9b7648c | |||
b7678493f9 | |||
a9ec8a354a | |||
a42d35deab | |||
2008feb7da | |||
c41424b57b | |||
3bd5563a7e | |||
eff8ec00c4 | |||
1256af7b9a | |||
b38ecaf925 | |||
bd5d945fb2 | |||
15063ef3d5 | |||
3b8a125732 | |||
9860b1c677 | |||
a0e340f06e | |||
57d3bec95c | |||
be10f3c2b5 | |||
f7af96dbb2 | |||
468a855f04 | |||
429f9e1d15 | |||
5de91197ae | |||
bc6b4b01ea | |||
e89ce7358a | |||
bdf0a6fd0b | |||
783abd4f5f | |||
28aa9201f0 | |||
378df515d0 | |||
04d68c6e36 | |||
5308d12239 | |||
15cac0157c | |||
fc696bc054 | |||
5a7b7b7389 | |||
9c9cf6f5ab | |||
0155739bef | |||
d0902fa28b | |||
3aa0d455c9 | |||
835d4e4b58 | |||
6075332f2f | |||
c5d8484e19 | |||
575ab0d609 | |||
b48a7d5cfd | |||
9af107feff | |||
70b5db16d3 | |||
5072ea7e47 |
@ -16,6 +16,7 @@ EXTRA_DIST = \
|
||||
# These are files checked into Git that we don't want to distribute
|
||||
DIST_EXCLUDE = \
|
||||
.gitignore \
|
||||
.gitmodules \
|
||||
gnome-shell.doap \
|
||||
HACKING \
|
||||
MAINTAINERS \
|
||||
|
94
NEWS
94
NEWS
@ -1,3 +1,97 @@
|
||||
3.7.4
|
||||
=====
|
||||
* Make menu separators crisp [Giovanni, Allan; #641745]
|
||||
* power: Update for new D-Bus name [Bastien; #690506]
|
||||
* Add smooth scrolling support [Jasper; #687573]
|
||||
* Tweak notification layout [Allan; #688506]
|
||||
* Ping the active window when using the app menu [Giovanni; #684340]
|
||||
* Make password entries insensitive after submission [Jasper; #690594, #690895]
|
||||
* Honor lock-delay GSettings key [Giovanni, Matthias; #690766, #691170]
|
||||
* Use text/calendar preferred app as the calendar app [Giovanni; #690767]
|
||||
* lookingGlass: Move to an inspect() function [Jasper; #690726]
|
||||
* Make OSK animation quicker, snappier [Rui; #688642]
|
||||
* Allow to close chat notifications with Escape [Jasper; #690897]
|
||||
* Honor org.gnome.desktop.screensaver.user-switch-enabled [Giovanni; #691042]
|
||||
* Add a SelectArea() DBus method [Cosimo; #687954]
|
||||
* Support non-absolute paths when saving screenshots [Cosimo; #688004]
|
||||
* OSK: Fix extended keys popups [Rui; #674955]
|
||||
* Don't hide or show the keyboard immediately [Rui; #688646]
|
||||
* Improve padding in power menu [Giovanni; #689297]
|
||||
* Add per-window input source switching [Rui; #691414]
|
||||
* Misc bug fixes and cleanups [Rico, Jasper, Giovanni, Rui, Florian, Dan;
|
||||
#690608, #690589, #690539, #687081, #690667, #690665, #690666, #685856,
|
||||
#690858, #690895, #680414, #690965, #691019, #690590, #681376, #690180,
|
||||
#685513, #689263, #691553, #691720, #691743, #691750]
|
||||
|
||||
Contributors:
|
||||
Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Allan Day, Rui Matos,
|
||||
Florian Müllner, Bastien Nocera, Jasper St. Pierre, Rico Tzschichholz,
|
||||
Dan Winship
|
||||
|
||||
Translations:
|
||||
Matej Urbančič [sl], Kjartan Maraas [nb], Mattias Põldaru [et],
|
||||
Yaron Shahrabani [he], Aurimas Černius [lt], Khaled Hosny [ar],
|
||||
Fran Diéguez [gl], Daniel Mustieles [es], Piotr Drąg [pl], Balázs Úr [hu],
|
||||
Baurzhan Muftakhidinov [kk], Tobias Endrigkeit [de], Dušan Kazik [sk],
|
||||
Aron Xu [zh_CN], Gheyret Kenji [ug]
|
||||
|
||||
3.7.3
|
||||
=====
|
||||
* Add 'No Messages' label when message tray is empty [Victoria; #686738]
|
||||
* Use better icons in Ctrl-Alt-Tab popup [Stéphane; #641303]
|
||||
* Show the OSK on the monitor where the focused window lives [Giovanni; #685856]
|
||||
* Highlight window clone and caption when hovered [Giovanni, Marc; #665310]
|
||||
* Improve login process indication [Stéphane; #687113]
|
||||
* Omit empty categories in apps view [Stéphane; #687970]
|
||||
* Style panel differently according to mode [Florian; #684573]
|
||||
* Make it possible to hide the user name [Matthias; #688577]
|
||||
* Consolidate and improve chat connection notifications [Giovanni; #687213]
|
||||
* Improve notification scrollbar appearance [Carlos; #688393]
|
||||
* Fade scroll view fade near scrolling edges [Jasper; #689249]
|
||||
* Add a read-only org.gnome.Shell.Mode property [Debarshi; #689300]
|
||||
* Don't close message tray after using context menus [Giovanni; #689296]
|
||||
* Port swipe-scrolling to ClutterPanAction [Jasper, Florian; #689062, #689552]
|
||||
* Remember state of 'Remember Password' checkbox [Ron; #688039]
|
||||
* Improve timestamp format in chat notifications [Carlos; #680989]
|
||||
* Improve style of missed-messages counter [Carlos; #686472]
|
||||
* Omit connection failure notifications if cancelled by user [Giovanni; #684823]
|
||||
* Add window-based Alt-Tab popup [Florian; #688913]
|
||||
* Support external session mode definitions [Florian; #689304]
|
||||
* Support session-mode-specific extensions [Florian; #689305]
|
||||
* Support 'parentMode' property in session modes [Florian; #689308]
|
||||
* Support a new org.gnome.ShellSearchProvider2 DBus interface
|
||||
[Cosimo; #689735, #690009]
|
||||
* Add "windows" to Ctrl-Alt-Tab popup [Jasper; #689653]
|
||||
* Port PopupMenu to GrabHelper [Jasper; #689109, #689954]
|
||||
* Show headphone icon when headphones are plugged in [Giovanni; #675902]
|
||||
* Display (non-app) search results as list [Tanner, Cosimo; #681797]
|
||||
* Skip diacritical marks in search terms [Aleksander; #648587]
|
||||
* Expose all engine options in input sources [Giovanni, Rui; #682318]
|
||||
* Add input source switcher popup [Rui; #682315]
|
||||
* Add minimal support for InfiniBand in network menu [Dan; #677150]
|
||||
* Misc bug fixes and cleanups [Sebastian, Aleksander, Giovanni, Tim, Cosimo,
|
||||
Florian, Matthias, Rui, Lionel, Colin, Piotr, Guillaume, Bastien, Tanner,
|
||||
Carlos, Stéphane, Jakub; #688422, #688379, #688750, #688771, #686800,
|
||||
#688133, #688895, #688966, #683986, #688004, #689108, #689029, #683449,
|
||||
#688196, #689304, #689243, #689295, #689325, #689400, #679168, #689568,
|
||||
#689537, #689528, #689749, #689789, #689353, #689820, #689868, #689778,
|
||||
#689959, #688589, #688589, #689955, #687250, #689965, #690046, #690049,
|
||||
#689884, #682286, #690173, #690174, #672941, #689876, #687881, #690171,
|
||||
#690241, #690312, #690175, #687955, #650843, #688234, #690427
|
||||
|
||||
Contributors:
|
||||
Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Stéphane Démurget,
|
||||
Guillaume Desmottes, Tanner Doshier, Piotr Drąg, Sebastian Keller,
|
||||
Lionel Landwerlin, Tim Lunn, Victoria Martínez de la Cruz, Aleksander Morgado,
|
||||
Florian Müllner, Bastien Nocera, Marc Plano-Lesay, Carlos Soriano Sánchez,
|
||||
Jakub Steiner, Jasper St. Pierre, Colin Walters, Dan Winship, Ron Yorston
|
||||
|
||||
Translations:
|
||||
Yuri Myasoedov [ru], Wouter Bolsterlee [nl], Yaron Shahrabani [he],
|
||||
Nilamdyuti Goswami [as], Ani Peter [ml], Kjartan Maraas [nb],
|
||||
Dr.T.Vasudevan [ta], A S Alam [pa], Shankar Prasad [kn], Khaled Hosny [ar],
|
||||
Daniel Mustieles [es], Dušan Kazik [sk]
|
||||
|
||||
3.7.2
|
||||
=====
|
||||
* Enforce RTL in he for messages that might end up as LTR [Florian; #686630]
|
||||
|
11
configure.ac
11
configure.ac
@ -1,5 +1,5 @@
|
||||
AC_PREREQ(2.63)
|
||||
AC_INIT([gnome-shell],[3.7.2],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
AC_INIT([gnome-shell],[3.7.4],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_SRCDIR([src/shell-global.c])
|
||||
@ -65,7 +65,7 @@ AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
|
||||
CLUTTER_MIN_VERSION=1.11.11
|
||||
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
|
||||
GJS_MIN_VERSION=1.33.2
|
||||
MUTTER_MIN_VERSION=3.7.2
|
||||
MUTTER_MIN_VERSION=3.7.4
|
||||
GTK_MIN_VERSION=3.3.9
|
||||
GIO_MIN_VERSION=2.35.0
|
||||
LIBECAL_MIN_VERSION=3.5.3
|
||||
@ -78,7 +78,8 @@ STARTUP_NOTIFICATION_MIN_VERSION=0.11
|
||||
GCR_MIN_VERSION=3.3.90
|
||||
GNOME_DESKTOP_REQUIRED_VERSION=3.7.1
|
||||
GNOME_MENUS_REQUIRED_VERSION=3.5.3
|
||||
NETWORKMANAGER_MIN_VERSION=0.9.7
|
||||
NETWORKMANAGER_MIN_VERSION=0.9.6
|
||||
PULSE_MIN_VERS=2.0
|
||||
|
||||
# Collect more than 20 libraries for a prize!
|
||||
PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
|
||||
@ -125,8 +126,8 @@ LIBS=$saved_LIBS
|
||||
PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
|
||||
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11)
|
||||
PKG_CHECK_MODULES(TRAY, gtk+-3.0)
|
||||
PKG_CHECK_MODULES(GVC, libpulse libpulse-mainloop-glib gobject-2.0)
|
||||
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.7.2)
|
||||
PKG_CHECK_MODULES(GVC, libpulse >= $PULSE_MIN_VERS libpulse-mainloop-glib gobject-2.0)
|
||||
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.7.2.2)
|
||||
|
||||
AC_MSG_CHECKING([for bluetooth support])
|
||||
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
|
||||
|
@ -12,6 +12,7 @@ desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
|
||||
|
||||
introspectiondir = $(datadir)/dbus-1/interfaces
|
||||
introspection_DATA = \
|
||||
org.gnome.Shell.Screenshot.xml \
|
||||
org.gnome.ShellSearchProvider.xml \
|
||||
org.gnome.ShellSearchProvider2.xml
|
||||
|
||||
@ -34,6 +35,7 @@ dist_theme_DATA = \
|
||||
theme/gnome-shell.css \
|
||||
theme/logged-in-indicator.svg \
|
||||
theme/message-tray-background.png \
|
||||
theme/more-results.svg \
|
||||
theme/noise-texture.png \
|
||||
theme/panel-button-border.svg \
|
||||
theme/panel-button-highlight-narrow.svg \
|
||||
|
128
data/org.gnome.Shell.Screenshot.xml
Normal file
128
data/org.gnome.Shell.Screenshot.xml
Normal file
@ -0,0 +1,128 @@
|
||||
<!DOCTYPE node PUBLIC
|
||||
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
|
||||
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
|
||||
<node>
|
||||
|
||||
<!--
|
||||
org.gnome.Shell.Screenshot:
|
||||
@short_description: Screenshot interface
|
||||
|
||||
The interface used to capture pictures of the screen contents.
|
||||
-->
|
||||
<interface name="org.gnome.Shell.Screenshot">
|
||||
|
||||
<!--
|
||||
Screenshot:
|
||||
@filename: The filename for the screenshot
|
||||
@include_cursor: Whether to include the cursor image or not
|
||||
@flash: Whether to flash the screen or not
|
||||
@success: whether the screenshot was captured
|
||||
@filename_used: the file where the screenshot was saved
|
||||
|
||||
Takes a screenshot of the whole screen and saves it
|
||||
in @filename as png image, it returns a boolean
|
||||
indicating whether the operation was successful or not.
|
||||
@filename can either be an absolute path or a basename, in
|
||||
which case the screenshot will be saved in the $XDG_PICTURES_DIR
|
||||
or the home directory if it doesn't exist. The filename used
|
||||
to save the screenshot will be returned in @filename_used.
|
||||
-->
|
||||
<method name="Screenshot">
|
||||
<arg type="b" direction="in" name="include_cursor"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
<arg type="s" direction="out" name="filename_used"/>
|
||||
</method>
|
||||
|
||||
<!--
|
||||
ScreenshotWindow:
|
||||
@include_frame: Whether to include the frame or not
|
||||
@include_cursor: Whether to include the cursor image or not
|
||||
@flash: Whether to flash the window area or not
|
||||
@filename: The filename for the screenshot
|
||||
@success: whether the screenshot was captured
|
||||
@filename_used: the file where the screenshot was saved
|
||||
|
||||
Takes a screenshot of the focused window (optionally omitting the frame)
|
||||
and saves it in @filename as png image, it returns a boolean
|
||||
indicating whether the operation was successful or not.
|
||||
@filename can either be an absolute path or a basename, in
|
||||
which case the screenshot will be saved in the $XDG_PICTURES_DIR
|
||||
or the home directory if it doesn't exist. The filename used
|
||||
to save the screenshot will be returned in @filename_used.
|
||||
-->
|
||||
<method name="ScreenshotWindow">
|
||||
<arg type="b" direction="in" name="include_frame"/>
|
||||
<arg type="b" direction="in" name="include_cursor"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
<arg type="s" direction="out" name="filename_used"/>
|
||||
</method>
|
||||
|
||||
<!--
|
||||
ScreenshotArea:
|
||||
@x: the X coordinate of the area to capture
|
||||
@y: the Y coordinate of the area to capture
|
||||
@width: the width of the area to capture
|
||||
@height: the height of the area to capture
|
||||
@flash: whether to flash the area or not
|
||||
@filename: the filename for the screenshot
|
||||
@success: whether the screenshot was captured
|
||||
@filename_used: the file where the screenshot was saved
|
||||
|
||||
Takes a screenshot of the passed in area and saves it
|
||||
in @filename as png image, it returns a boolean
|
||||
indicating whether the operation was successful or not.
|
||||
@filename can either be an absolute path or a basename, in
|
||||
which case the screenshot will be saved in the $XDG_PICTURES_DIR
|
||||
or the home directory if it doesn't exist. The filename used
|
||||
to save the screenshot will be returned in @filename_used.
|
||||
-->
|
||||
<method name="ScreenshotArea">
|
||||
<arg type="i" direction="in" name="x"/>
|
||||
<arg type="i" direction="in" name="y"/>
|
||||
<arg type="i" direction="in" name="width"/>
|
||||
<arg type="i" direction="in" name="height"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
<arg type="s" direction="out" name="filename_used"/>
|
||||
</method>
|
||||
|
||||
<!--
|
||||
FlashArea:
|
||||
@x: the X coordinate of the area to flash
|
||||
@y: the Y coordinate of the area to flash
|
||||
@width: the width of the area to flash
|
||||
@height: the height of the area to flash
|
||||
|
||||
Renders a flash spot effect in the specified rectangle of the screen.
|
||||
-->
|
||||
<method name="FlashArea">
|
||||
<arg type="i" direction="in" name="x"/>
|
||||
<arg type="i" direction="in" name="y"/>
|
||||
<arg type="i" direction="in" name="width"/>
|
||||
<arg type="i" direction="in" name="height"/>
|
||||
</method>
|
||||
|
||||
<!--
|
||||
SelectArea:
|
||||
@x: the X coordinate of the selected area
|
||||
@y: the Y coordinate of the selected area
|
||||
@width: the width of the selected area
|
||||
@height: the height of the selected area
|
||||
|
||||
Interactively allows the user to select a rectangular area of
|
||||
the screen, and returns its coordinates.
|
||||
-->
|
||||
<method name="SelectArea">
|
||||
<arg type="i" direction="out" name="x"/>
|
||||
<arg type="i" direction="out" name="y"/>
|
||||
<arg type="i" direction="out" name="width"/>
|
||||
<arg type="i" direction="out" name="height"/>
|
||||
</method>
|
||||
|
||||
</interface>
|
||||
</node>
|
@ -75,11 +75,13 @@
|
||||
<!--
|
||||
LaunchSearch:
|
||||
@terms: Array of search terms, which the provider should treat as logical AND.
|
||||
@timestamp: A timestamp of the user interaction that triggered this call
|
||||
|
||||
Asks the search provider to launch a full search in the application for the provided terms.
|
||||
-->
|
||||
<method name="LaunchSearch">
|
||||
<arg type="as" name="terms" direction="in" />
|
||||
<arg type="u" name="timestamp" direction="in" />
|
||||
</method>
|
||||
</interface>
|
||||
</node>
|
||||
|
@ -49,7 +49,7 @@ stage {
|
||||
.switcher-list,
|
||||
.app-well-app > .overview-icon,
|
||||
.show-apps > .overview-icon,
|
||||
.search-result-content > .overview-icon {
|
||||
.grid-search-result .overview-icon {
|
||||
font-size: 9pt;
|
||||
font-weight: bold;
|
||||
}
|
||||
@ -206,11 +206,12 @@ StScrollBar StButton#vhandle:active {
|
||||
}
|
||||
|
||||
.popup-separator-menu-item {
|
||||
-gradient-height: 2px;
|
||||
-gradient-start: rgba(8,8,8,0);
|
||||
-gradient-end: #333333;
|
||||
-margin-horizontal: 1.5em;
|
||||
height: 1em;
|
||||
-gradient-height: 1px;
|
||||
-gradient-start: rgba(255,255,255,0.0);
|
||||
-gradient-end: rgba(255,255,255,0.3);
|
||||
-margin-horizontal: 24px;
|
||||
height: 1px;
|
||||
padding: 8px 0px;
|
||||
}
|
||||
|
||||
.popup-alternating-menu-item:alternate {
|
||||
@ -251,6 +252,10 @@ StScrollBar StButton#vhandle:active {
|
||||
icon-size: 1.09em;
|
||||
}
|
||||
|
||||
.popup-battery-percentage {
|
||||
padding-left: 24px;
|
||||
}
|
||||
|
||||
/* Switches */
|
||||
.toggle-switch {
|
||||
width: 65px;
|
||||
@ -287,13 +292,17 @@ StScrollBar StButton#vhandle:active {
|
||||
.hotplug-notification-item,
|
||||
.hotplug-resident-eject-button,
|
||||
.modal-dialog-button {
|
||||
font-weight: bold;
|
||||
border: 1px solid #8b8b8b;
|
||||
background-gradient-direction: vertical;
|
||||
background-gradient-start: rgba(255, 255, 255, 0.2);
|
||||
background-gradient-end: rgba(255, 255, 255, 0);
|
||||
}
|
||||
|
||||
.dash-search-button,
|
||||
.modal-dialog-button {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.dash-search-button:hover,
|
||||
.notification-button:hover,
|
||||
.notification-icon-button:hover,
|
||||
@ -354,7 +363,7 @@ StScrollBar StButton#vhandle:active {
|
||||
background-gradient-start: rgba(5,5,6,0.1);
|
||||
background-gradient-end: rgba(254,254,254,0.1);
|
||||
background-gradient-direction: vertical;
|
||||
transition-duration: 300;
|
||||
transition-duration: 300ms;
|
||||
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
@ -389,7 +398,7 @@ StScrollBar StButton#vhandle:active {
|
||||
color: rgb(64, 64, 64);
|
||||
caret-color: rgb(64, 64, 64);
|
||||
font-weight: bold;
|
||||
transition-duration: 0;
|
||||
transition-duration: 0ms;
|
||||
}
|
||||
|
||||
.notification StEntry,
|
||||
@ -420,7 +429,7 @@ StScrollBar StButton#vhandle:active {
|
||||
background-color: black;
|
||||
font-weight: bold;
|
||||
height: 1.86em;
|
||||
transition-duration: 250;
|
||||
transition-duration: 250ms;
|
||||
}
|
||||
|
||||
#panel.lock-screen {
|
||||
@ -503,7 +512,7 @@ StScrollBar StButton#vhandle:active {
|
||||
-minimum-hpadding: 6px;
|
||||
font-weight: bold;
|
||||
color: #ccc;
|
||||
transition-duration: 100;
|
||||
transition-duration: 100ms;
|
||||
}
|
||||
|
||||
#panel.unlock-screen .panel-button,
|
||||
@ -558,6 +567,10 @@ StScrollBar StButton#vhandle:active {
|
||||
spacing: 4px;
|
||||
}
|
||||
|
||||
.lock-screen-status-button-box {
|
||||
spacing: 8px;
|
||||
}
|
||||
|
||||
/* User Menu */
|
||||
|
||||
#panelUserMenu {
|
||||
@ -613,7 +626,15 @@ StScrollBar StButton#vhandle:active {
|
||||
/* Overview */
|
||||
|
||||
#overview {
|
||||
spacing: 40px;
|
||||
spacing: 24px;
|
||||
}
|
||||
|
||||
#overview-group {
|
||||
spacing: 32px;
|
||||
}
|
||||
|
||||
.workspaces-display {
|
||||
spacing: 32px; /* needs to be the same value as #overview-group */
|
||||
}
|
||||
|
||||
.window-caption {
|
||||
@ -702,8 +723,8 @@ StScrollBar StButton#vhandle:active {
|
||||
}
|
||||
|
||||
.window-picker {
|
||||
-horizontal-spacing: 40px;
|
||||
-vertical-spacing: 40px;
|
||||
-horizontal-spacing: 32px;
|
||||
-vertical-spacing: 32px;
|
||||
}
|
||||
|
||||
/* Dash */
|
||||
@ -760,7 +781,7 @@ StScrollBar StButton#vhandle:active {
|
||||
|
||||
#searchResultsContent {
|
||||
padding-right: 20px;
|
||||
spacing: 36px;
|
||||
spacing: 16px;
|
||||
}
|
||||
|
||||
#searchResultsContent:rtl {
|
||||
@ -768,11 +789,22 @@ StScrollBar StButton#vhandle:active {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.search-section-header {
|
||||
padding: 4px 12px;
|
||||
spacing: 4px;
|
||||
color: #6f6f6f;
|
||||
font-size: .8em;
|
||||
.search-section {
|
||||
/* This should be equal to #searchResultsContent spacing */
|
||||
spacing: 16px;
|
||||
}
|
||||
|
||||
.search-section-separator {
|
||||
-gradient-height: 1px;
|
||||
-gradient-start: rgba(255,255,255,0);
|
||||
-gradient-end: rgba(255,255,255,0.5);
|
||||
-margin-horizontal: 1.5em;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.search-section-content {
|
||||
/* This is the space between the provider icon and the results container */
|
||||
spacing: 32px;
|
||||
}
|
||||
|
||||
.search-statustext {
|
||||
@ -781,16 +813,8 @@ StScrollBar StButton#vhandle:active {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.search-section-results {
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.search-section-list-results {
|
||||
spacing: 4px;
|
||||
}
|
||||
|
||||
.results-container {
|
||||
spacing: 4px;
|
||||
.list-search-results {
|
||||
spacing: 3px;
|
||||
}
|
||||
|
||||
/* Text labels are an odd number of pixels tall. The uneven top and bottom
|
||||
@ -819,7 +843,7 @@ StScrollBar StButton#vhandle:active {
|
||||
-x-offset: 8px;
|
||||
}
|
||||
|
||||
/* Application Launchers and Grid */
|
||||
/* Application Launchers, Grid and List results */
|
||||
|
||||
.icon-grid {
|
||||
spacing: 36px;
|
||||
@ -871,13 +895,38 @@ StScrollBar StButton#vhandle:active {
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
.list-search-result-content {
|
||||
spacing: 12px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.list-search-result-title {
|
||||
font-weight: bold;
|
||||
font-size: 14pt;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.list-search-result-description {
|
||||
font-weight: bold;
|
||||
font-size: 12pt;
|
||||
color: #eeeeec;
|
||||
}
|
||||
|
||||
.search-provider-icon-more {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background-image: url("more-results.svg");
|
||||
}
|
||||
|
||||
.app-well-app > .overview-icon,
|
||||
.show-apps > .overview-icon,
|
||||
.search-result-content > .overview-icon {
|
||||
.search-provider-icon,
|
||||
.list-search-result,
|
||||
.grid-search-result .overview-icon {
|
||||
border-radius: 4px;
|
||||
padding: 3px;
|
||||
border: 1px rgba(0,0,0,0);
|
||||
transition-duration: 100;
|
||||
transition-duration: 100ms;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@ -889,10 +938,12 @@ StScrollBar StButton#vhandle:active {
|
||||
|
||||
.app-well-app:hover > .overview-icon,
|
||||
.show-apps:hover > .overview-icon,
|
||||
.search-result-content:hover > .overview-icon {
|
||||
.search-provider-icon:hover,
|
||||
.list-search-result:hover,
|
||||
.grid-search-result:hover .overview-icon {
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
text-shadow: black 0px 2px 2px;
|
||||
transition-duration: 100;
|
||||
transition-duration: 100ms;
|
||||
color:white;
|
||||
}
|
||||
|
||||
@ -914,20 +965,24 @@ StScrollBar StButton#vhandle:active {
|
||||
background-gradient-direction: vertical;
|
||||
border-radius: 4px;
|
||||
box-shadow: inset 0px 1px 2px 0px rgba(0, 0, 0, 1);
|
||||
transition-duration: 100;
|
||||
transition-duration: 100ms;
|
||||
}
|
||||
|
||||
.show-apps:checked .show-apps-icon,
|
||||
.show-apps:focus .show-apps-icon {
|
||||
color: white;
|
||||
transition-duration: 100;
|
||||
transition-duration: 100ms;
|
||||
}
|
||||
|
||||
.app-well-app:focus > .overview-icon,
|
||||
.search-result-content:focus > .overview-icon,
|
||||
.grid-search-result:focus .overview-icon,
|
||||
.show-apps:focus > .overview-icon,
|
||||
.search-provider-icon:focus,
|
||||
.list-search-result:focus,
|
||||
.app-well-app:selected > .overview-icon,
|
||||
.search-result-content:selected > .overview-icon {
|
||||
.grid-search-result:selected .overview-icon,
|
||||
.search-provider-icon:selected,
|
||||
.list-search-result:selected {
|
||||
background-color: rgba(255,255,255,0.33);
|
||||
}
|
||||
|
||||
@ -955,7 +1010,7 @@ StScrollBar StButton#vhandle:active {
|
||||
-minimum-hpadding: 6px;
|
||||
font-weight: bold;
|
||||
color: #ccc;
|
||||
transition-duration: 100;
|
||||
transition-duration: 100ms;
|
||||
padding-left: .3em;
|
||||
padding-right: .3em;
|
||||
}
|
||||
@ -1258,7 +1313,8 @@ StScrollBar StButton#vhandle:active {
|
||||
#message-tray {
|
||||
background: #2e3436 url(message-tray-background.png);
|
||||
background-repeat: repeat;
|
||||
transition-duration: 250;
|
||||
transition-duration: 250ms;
|
||||
height: 72px;
|
||||
}
|
||||
|
||||
#message-tray:keyboard {
|
||||
@ -1271,6 +1327,10 @@ StScrollBar StButton#vhandle:active {
|
||||
outline: 1px solid rgba(128, 128, 128, 0.3);
|
||||
}
|
||||
|
||||
.message-tray-summary {
|
||||
height: 72px;
|
||||
}
|
||||
|
||||
.no-messages-label {
|
||||
font-family: cantarell, sans-serif;
|
||||
font-size: 11pt;
|
||||
@ -1279,9 +1339,9 @@ StScrollBar StButton#vhandle:active {
|
||||
|
||||
.notification {
|
||||
border-radius: 10px 10px 0px 0px;
|
||||
background: rgba(0,0,0,0.8);
|
||||
background: rgba(0,0,0,0.9);
|
||||
padding: 8px 8px 4px 8px;
|
||||
spacing-rows: 10px;
|
||||
spacing-rows: 4px;
|
||||
spacing-columns: 10px;
|
||||
}
|
||||
|
||||
@ -1311,7 +1371,7 @@ StScrollBar StButton#vhandle:active {
|
||||
|
||||
.summary-boxpointer {
|
||||
-arrow-border-radius: 15px;
|
||||
-arrow-background-color: rgba(0,0,0,0.8);
|
||||
-arrow-background-color: rgba(0,0,0,0.9);
|
||||
-arrow-base: 36px;
|
||||
-arrow-rise: 18px;
|
||||
color: white;
|
||||
@ -1361,6 +1421,7 @@ StScrollBar StButton#vhandle:active {
|
||||
}
|
||||
|
||||
.notification-actions {
|
||||
padding-top: 18px;
|
||||
spacing: 10px;
|
||||
}
|
||||
|
||||
@ -1499,10 +1560,6 @@ StScrollBar StButton#vhandle:active {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
#summary-mode {
|
||||
height: 72px;
|
||||
}
|
||||
|
||||
.summary-source-button {
|
||||
padding: 6px 3px 6px 3px;
|
||||
}
|
||||
@ -1527,7 +1584,7 @@ StScrollBar StButton#vhandle:active {
|
||||
.summary-source {
|
||||
border-radius: 4px;
|
||||
padding: 0 6px 0 6px;
|
||||
transition-duration: 100;
|
||||
transition-duration: 100ms;
|
||||
}
|
||||
|
||||
.summary-source-counter {
|
||||
@ -2389,3 +2446,8 @@ StScrollBar StButton#vhandle:active {
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
.input-source-switcher-symbol {
|
||||
font-size: 42pt;
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
}
|
||||
|
109
data/theme/more-results.svg
Normal file
109
data/theme/more-results.svg
Normal file
@ -0,0 +1,109 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="16"
|
||||
height="16"
|
||||
id="svg12430"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.3.1 r9886"
|
||||
sodipodi:docname="more-results.svg">
|
||||
<defs
|
||||
id="defs12432" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#7a7a7a"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="1"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="8.3155237"
|
||||
inkscape:cy="0.89548874"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="g14642-3-0"
|
||||
showgrid="false"
|
||||
borderlayer="true"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1376"
|
||||
inkscape:window-x="1200"
|
||||
inkscape:window-y="187"
|
||||
inkscape:window-maximized="1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid13002" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata12435">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-1036.3622)">
|
||||
<g
|
||||
style="display:inline"
|
||||
transform="translate(-141.99984,638.37113)"
|
||||
inkscape:label="zoom-in"
|
||||
id="g14642-3-0">
|
||||
<path
|
||||
sodipodi:type="inkscape:offset"
|
||||
inkscape:radius="0"
|
||||
inkscape:original="M 145.1875 400 C 144.5248 400 144 400.54899 144 401.21875 L 144 410.78125 C 144 411.45101 144.5248 412 145.1875 412 L 154.8125 412 C 155.4752 412 156 411.45101 156 410.78125 L 156 401.21875 C 156 400.54899 155.4752 400 154.8125 400 L 145.1875 400 z M 149 403 L 151 403 L 151 405 L 153 405 L 153 407 L 151 407 L 151 409 L 149 409 L 149 407 L 147 407 L 147 405 L 149 405 L 149 403 z "
|
||||
xlink:href="#rect11749-5-0-1-8"
|
||||
style="color:#bebebe;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;opacity:0.8"
|
||||
id="path13004"
|
||||
inkscape:href="#rect11749-5-0-1-8"
|
||||
d="M 145.1875,400 C 144.5248,400 144,400.54899 144,401.21875 l 0,9.5625 c 0,0.66976 0.5248,1.21875 1.1875,1.21875 l 9.625,0 c 0.6627,0 1.1875,-0.54899 1.1875,-1.21875 l 0,-9.5625 C 156,400.54899 155.4752,400 154.8125,400 L 145.1875,400 z m 3.8125,3 2,0 0,2 2,0 0,2 -2,0 0,2 -2,0 0,-2 -2,0 0,-2 2,0 L 149,403 Z"
|
||||
transform="translate(0,1)" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#path13004"
|
||||
id="use11960"
|
||||
transform="translate(1,-1)"
|
||||
width="16"
|
||||
height="16" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use11960"
|
||||
id="use11962"
|
||||
transform="translate(-2,0)"
|
||||
width="16"
|
||||
height="16" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="color:#bebebe;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
d="M 145.1875,400 C 144.5248,400 144,400.54899 144,401.21875 l 0,9.5625 c 0,0.66976 0.5248,1.21875 1.1875,1.21875 l 9.625,0 c 0.6627,0 1.1875,-0.54899 1.1875,-1.21875 l 0,-9.5625 C 156,400.54899 155.4752,400 154.8125,400 L 145.1875,400 z m 3.8125,3 2,0 0,2 2,0 0,2 -2,0 0,2 -2,0 0,-2 -2,0 0,-2 2,0 L 149,403 Z"
|
||||
id="rect11749-5-0-1-8" />
|
||||
<rect
|
||||
style="fill:none;stroke:none"
|
||||
id="rect3620-5-4"
|
||||
width="15.981825"
|
||||
height="16"
|
||||
x="142"
|
||||
y="398"
|
||||
rx="0"
|
||||
ry="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.1 KiB |
@ -88,11 +88,18 @@ doc-gen-org.gnome.Shell.SearchProvider2.xml: $(top_srcdir)/data/org.gnome.ShellS
|
||||
--generate-docbook doc-gen \
|
||||
$(top_srcdir)/data/org.gnome.ShellSearchProvider2.xml
|
||||
|
||||
doc-gen-org.gnome.Shell.Screenshot.xml: $(top_srcdir)/data/org.gnome.Shell.Screenshot.xml
|
||||
gdbus-codegen \
|
||||
--interface-prefix org.gnome.Shell.Screenshot. \
|
||||
--generate-docbook doc-gen \
|
||||
$(top_srcdir)/data/org.gnome.Shell.Screenshot.xml
|
||||
|
||||
# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
|
||||
# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
|
||||
content_files= \
|
||||
doc-gen-org.gnome.Shell.SearchProvider.xml \
|
||||
doc-gen-org.gnome.Shell.SearchProvider2.xml
|
||||
doc-gen-org.gnome.Shell.SearchProvider2.xml \
|
||||
doc-gen-org.gnome.Shell.Screenshot.xml
|
||||
|
||||
# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
|
||||
# These files must be listed here *and* in content_files
|
||||
|
@ -49,7 +49,6 @@ nobase_dist_js_DATA = \
|
||||
ui/extensionSystem.js \
|
||||
ui/extensionDownloader.js \
|
||||
ui/environment.js \
|
||||
ui/flashspot.js \
|
||||
ui/ibusCandidatePopup.js\
|
||||
ui/grabHelper.js \
|
||||
ui/iconGrid.js \
|
||||
@ -62,6 +61,7 @@ nobase_dist_js_DATA = \
|
||||
ui/main.js \
|
||||
ui/messageTray.js \
|
||||
ui/modalDialog.js \
|
||||
ui/separator.js \
|
||||
ui/sessionMode.js \
|
||||
ui/shellEntry.js \
|
||||
ui/shellMountOperation.js \
|
||||
@ -73,6 +73,7 @@ nobase_dist_js_DATA = \
|
||||
ui/popupMenu.js \
|
||||
ui/remoteSearch.js \
|
||||
ui/runDialog.js \
|
||||
ui/screenshot.js \
|
||||
ui/screenShield.js \
|
||||
ui/scripting.js \
|
||||
ui/search.js \
|
||||
|
@ -532,6 +532,7 @@ const SessionListItem = new Lang.Class({
|
||||
color.alpha / 255);
|
||||
cr.arc(width / 2, height / 2, width / 3, 0, 2 * Math.PI);
|
||||
cr.fill();
|
||||
cr.$dispose();
|
||||
},
|
||||
|
||||
_onClicked: function() {
|
||||
|
@ -19,7 +19,6 @@ const IconGrid = imports.ui.iconGrid;
|
||||
const Main = imports.ui.main;
|
||||
const Overview = imports.ui.overview;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Search = imports.ui.search;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const Workspace = imports.ui.workspace;
|
||||
const Params = imports.misc.params;
|
||||
@ -312,11 +311,10 @@ const AllAppDisplay = new Lang.Class({
|
||||
|
||||
const AppSearchProvider = new Lang.Class({
|
||||
Name: 'AppSearchProvider',
|
||||
Extends: Search.SearchProvider,
|
||||
|
||||
_init: function() {
|
||||
this.parent(_("APPLICATIONS"));
|
||||
this._appSys = Shell.AppSystem.get_default();
|
||||
this.id = 'applications';
|
||||
},
|
||||
|
||||
getResultMetas: function(apps, callback) {
|
||||
@ -369,13 +367,10 @@ const AppSearchProvider = new Lang.Class({
|
||||
|
||||
const SettingsSearchProvider = new Lang.Class({
|
||||
Name: 'SettingsSearchProvider',
|
||||
Extends: Search.SearchProvider,
|
||||
|
||||
_init: function() {
|
||||
this.parent(_("SETTINGS"));
|
||||
|
||||
this._appSys = Shell.AppSystem.get_default();
|
||||
this.appInfo = Gio.DesktopAppInfo.new('gnome-control-center.desktop');
|
||||
this._appSys = Shell.AppSystem.get_default();
|
||||
},
|
||||
|
||||
getResultMetas: function(prefs, callback) {
|
||||
@ -384,9 +379,7 @@ const SettingsSearchProvider = new Lang.Class({
|
||||
let pref = prefs[i];
|
||||
metas.push({ 'id': pref,
|
||||
'name': pref.get_name(),
|
||||
'createIcon': function(size) {
|
||||
return pref.create_icon_texture(size);
|
||||
}
|
||||
'createIcon': function() { return null; }
|
||||
});
|
||||
}
|
||||
callback(metas);
|
||||
@ -404,12 +397,6 @@ const SettingsSearchProvider = new Lang.Class({
|
||||
pref.activate();
|
||||
},
|
||||
|
||||
createResultActor: function (resultMeta, terms) {
|
||||
let app = resultMeta['id'];
|
||||
let icon = new AppWellIcon(app);
|
||||
return icon.actor;
|
||||
},
|
||||
|
||||
launchSearch: function(terms) {
|
||||
// FIXME: this should be a remote search provider
|
||||
this.appInfo.launch([], global.create_app_launch_context());
|
||||
|
@ -220,25 +220,8 @@ const BoxPointer = new Lang.Class({
|
||||
this.bin.allocate(childBox, flags);
|
||||
|
||||
if (this._sourceActor && this._sourceActor.mapped) {
|
||||
this._reposition(this._sourceActor, this._arrowAlignment);
|
||||
|
||||
if (this._shouldFlip()) {
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
this._arrowSide = St.Side.BOTTOM;
|
||||
break;
|
||||
case St.Side.BOTTOM:
|
||||
this._arrowSide = St.Side.TOP;
|
||||
break;
|
||||
case St.Side.LEFT:
|
||||
this._arrowSide = St.Side.RIGHT;
|
||||
break;
|
||||
case St.Side.RIGHT:
|
||||
this._arrowSide = St.Side.LEFT;
|
||||
break;
|
||||
}
|
||||
this._reposition(this._sourceActor, this._arrowAlignment);
|
||||
}
|
||||
this._reposition();
|
||||
this._updateFlip();
|
||||
}
|
||||
},
|
||||
|
||||
@ -405,11 +388,11 @@ const BoxPointer = new Lang.Class({
|
||||
cr.setLineWidth(borderWidth);
|
||||
cr.stroke();
|
||||
}
|
||||
|
||||
cr.$dispose();
|
||||
},
|
||||
|
||||
setPosition: function(sourceActor, alignment) {
|
||||
this._arrowSide = this._userArrowSide;
|
||||
|
||||
// We need to show it now to force an allocation,
|
||||
// so that we can query the correct size.
|
||||
this.actor.show();
|
||||
@ -417,7 +400,8 @@ const BoxPointer = new Lang.Class({
|
||||
this._sourceActor = sourceActor;
|
||||
this._arrowAlignment = alignment;
|
||||
|
||||
this._reposition(sourceActor, alignment);
|
||||
this._reposition();
|
||||
this._updateFlip();
|
||||
},
|
||||
|
||||
setSourceAlignment: function(alignment) {
|
||||
@ -429,7 +413,10 @@ const BoxPointer = new Lang.Class({
|
||||
this.setPosition(this._sourceActor, this._arrowAlignment);
|
||||
},
|
||||
|
||||
_reposition: function(sourceActor, alignment) {
|
||||
_reposition: function() {
|
||||
let sourceActor = this._sourceActor;
|
||||
let alignment = this._arrowAlignment;
|
||||
|
||||
// Position correctly relative to the sourceActor
|
||||
let sourceNode = sourceActor.get_theme_node();
|
||||
let sourceContentBox = sourceNode.get_content_box(sourceActor.get_allocation_box());
|
||||
@ -553,7 +540,7 @@ const BoxPointer = new Lang.Class({
|
||||
_shiftActor : function() {
|
||||
// Since the position of the BoxPointer depends on the allocated size
|
||||
// of the BoxPointer and the position of the source actor, trying
|
||||
// to position the BoxPoiner via the x/y properties will result in
|
||||
// to position the BoxPointer via the x/y properties will result in
|
||||
// allocation loops and warnings. Instead we do the positioning via
|
||||
// the anchor point, which is independent of allocation, and leave
|
||||
// x == y == 0.
|
||||
@ -561,37 +548,47 @@ const BoxPointer = new Lang.Class({
|
||||
-(this._yPosition + this._yOffset));
|
||||
},
|
||||
|
||||
_shouldFlip: function() {
|
||||
_calculateArrowSide: function(arrowSide) {
|
||||
let sourceAllocation = Shell.util_get_transformed_allocation(this._sourceActor);
|
||||
let boxAllocation = Shell.util_get_transformed_allocation(this.actor);
|
||||
let boxWidth = boxAllocation.x2 - boxAllocation.x1;
|
||||
let boxHeight = boxAllocation.y2 - boxAllocation.y1;
|
||||
let [minWidth, minHeight, boxWidth, boxHeight] = this._container.get_preferred_size();
|
||||
let monitor = Main.layoutManager.findMonitorForActor(this.actor);
|
||||
|
||||
switch (this._arrowSide) {
|
||||
switch (arrowSide) {
|
||||
case St.Side.TOP:
|
||||
if (boxAllocation.y2 > monitor.y + monitor.height &&
|
||||
if (sourceAllocation.y2 + boxHeight > monitor.y + monitor.height &&
|
||||
boxHeight < sourceAllocation.y1 - monitor.y)
|
||||
return true;
|
||||
return St.Side.BOTTOM;
|
||||
break;
|
||||
case St.Side.BOTTOM:
|
||||
if (boxAllocation.y1 < monitor.y &&
|
||||
if (sourceAllocation.y1 - boxHeight < monitor.y &&
|
||||
boxHeight < monitor.y + monitor.height - sourceAllocation.y2)
|
||||
return true;
|
||||
return St.Side.TOP;
|
||||
break;
|
||||
case St.Side.LEFT:
|
||||
if (boxAllocation.x2 > monitor.x + monitor.width &&
|
||||
if (sourceAllocation.y2 + boxWidth > monitor.x + monitor.width &&
|
||||
boxWidth < sourceAllocation.x1 - monitor.x)
|
||||
return true;
|
||||
return St.Side.RIGHT;
|
||||
break;
|
||||
case St.Side.RIGHT:
|
||||
if (boxAllocation.x1 < monitor.x &&
|
||||
if (sourceAllocation.y1 - boxWidth < monitor.x &&
|
||||
boxWidth < monitor.x + monitor.width - sourceAllocation.x2)
|
||||
return true;
|
||||
return St.Side.LEFT;
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
return arrowSide;
|
||||
},
|
||||
|
||||
_updateFlip: function() {
|
||||
let arrowSide = this._calculateArrowSide(this._userArrowSide);
|
||||
if (this._arrowSide != arrowSide) {
|
||||
this._arrowSide = arrowSide;
|
||||
this._reposition();
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
|
||||
this._container.queue_relayout();
|
||||
return false;
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
set xOffset(offset) {
|
||||
|
@ -11,7 +11,6 @@ const Mainloop = imports.mainloop;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const MSECS_IN_DAY = 24 * 60 * 60 * 1000;
|
||||
const WEEKDATE_HEADER_WIDTH_DIGITS = 3;
|
||||
const SHOW_WEEKDATE_KEY = 'show-weekdate';
|
||||
|
||||
// in org.gnome.desktop.interface
|
||||
@ -95,15 +94,6 @@ function _getCalendarWeekForDate(date) {
|
||||
return weekNumber;
|
||||
}
|
||||
|
||||
function _getDigitWidth(actor){
|
||||
let context = actor.get_pango_context();
|
||||
let themeNode = actor.get_theme_node();
|
||||
let font = themeNode.get_font();
|
||||
let metrics = context.get_metrics(font, context.get_language());
|
||||
let width = metrics.get_approximate_digit_width();
|
||||
return width;
|
||||
}
|
||||
|
||||
function _getCalendarDayAbbreviation(dayNumber) {
|
||||
let abbreviations = [
|
||||
/* Translators: Calendar grid abbreviation for Sunday.
|
||||
@ -345,8 +335,6 @@ const Calendar = new Lang.Class({
|
||||
|
||||
_init: function() {
|
||||
this._weekStart = Shell.util_get_week_start();
|
||||
this._weekdate = NaN;
|
||||
this._digitWidth = NaN;
|
||||
this._settings = new Gio.Settings({ schema: 'org.gnome.shell.calendar' });
|
||||
|
||||
this._settings.connect('changed::' + SHOW_WEEKDATE_KEY, Lang.bind(this, this._onSettingsChange));
|
||||
@ -419,8 +407,6 @@ const Calendar = new Lang.Class({
|
||||
this.actor.add(this._topBox,
|
||||
{ row: 0, col: 0, col_span: offsetCols + 7 });
|
||||
|
||||
this.actor.connect('style-changed', Lang.bind(this, this._onStyleChange));
|
||||
|
||||
let back = new St.Button({ style_class: 'calendar-change-month-back' });
|
||||
this._topBox.add(back);
|
||||
back.connect('clicked', Lang.bind(this, this._onPrevMonthButtonClicked));
|
||||
@ -457,18 +443,6 @@ const Calendar = new Lang.Class({
|
||||
this._firstDayIndex = this.actor.get_n_children();
|
||||
},
|
||||
|
||||
_onStyleChange: function(actor, event) {
|
||||
// width of a digit in pango units
|
||||
this._digitWidth = _getDigitWidth(this.actor) / Pango.SCALE;
|
||||
this._setWeekdateHeaderWidth();
|
||||
},
|
||||
|
||||
_setWeekdateHeaderWidth: function() {
|
||||
if (this.digitWidth != NaN && this._useWeekdate && this._weekdateHeader) {
|
||||
this._weekdateHeader.set_width (this._digitWidth * WEEKDATE_HEADER_WIDTH_DIGITS);
|
||||
}
|
||||
},
|
||||
|
||||
_onScroll : function(actor, event) {
|
||||
switch (event.get_scroll_direction()) {
|
||||
case Clutter.ScrollDirection.UP:
|
||||
|
@ -60,18 +60,14 @@ const KeyringDialog = new Lang.Class({
|
||||
|
||||
this._controlTable = null;
|
||||
|
||||
let buttons = [{ label: '',
|
||||
action: Lang.bind(this, this._onCancelButton),
|
||||
key: Clutter.Escape
|
||||
},
|
||||
{ label: '',
|
||||
action: Lang.bind(this, this._onContinueButton),
|
||||
default: true
|
||||
}]
|
||||
|
||||
this.setButtons(buttons);
|
||||
this._cancelButton = buttons[0].button;
|
||||
this._continueButton = buttons[1].button;
|
||||
this._cancelButton = this.addButton({ label: '',
|
||||
action: Lang.bind(this, this._onCancelButton),
|
||||
key: Clutter.Escape });
|
||||
this._continueButton = this.addButton({ label: '',
|
||||
action: Lang.bind(this, this._onContinueButton),
|
||||
default: true },
|
||||
{ expand: true, x_fill: false, x_align: St.Align.END });
|
||||
|
||||
this.prompt.bind_property('cancel-label', this._cancelButton, 'label', GObject.BindingFlags.SYNC_CREATE);
|
||||
this.prompt.bind_property('continue-label', this._continueButton, 'label', GObject.BindingFlags.SYNC_CREATE);
|
||||
@ -146,6 +142,14 @@ const KeyringDialog = new Lang.Class({
|
||||
this._messageBox.add(table, { x_fill: true, y_fill: true });
|
||||
},
|
||||
|
||||
_updateSensitivity: function(sensitive) {
|
||||
this._passwordEntry.reactive = sensitive;
|
||||
this._passwordEntry.clutter_text.editable = sensitive;
|
||||
|
||||
this._continueButton.can_focus = sensitive;
|
||||
this._continueButton.reactive = sensitive;
|
||||
},
|
||||
|
||||
_ensureOpen: function() {
|
||||
// NOTE: ModalDialog.open() is safe to call if the dialog is
|
||||
// already open - it just returns true without side-effects
|
||||
@ -167,12 +171,14 @@ const KeyringDialog = new Lang.Class({
|
||||
_onShowPassword: function(prompt) {
|
||||
this._buildControlTable();
|
||||
this._ensureOpen();
|
||||
this._updateSensitivity(true);
|
||||
this._passwordEntry.grab_key_focus();
|
||||
},
|
||||
|
||||
_onShowConfirm: function(prompt) {
|
||||
this._buildControlTable();
|
||||
this._ensureOpen();
|
||||
this._updateSensitivity(true);
|
||||
this._continueButton.grab_key_focus();
|
||||
},
|
||||
|
||||
@ -192,6 +198,7 @@ const KeyringDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
_onContinueButton: function() {
|
||||
this._updateSensitivity(false);
|
||||
this.prompt.complete();
|
||||
},
|
||||
|
||||
|
@ -613,7 +613,8 @@ const NetworkAgent = new Lang.Class({
|
||||
this._vpnRequests = { };
|
||||
|
||||
this._native.auto_register = false;
|
||||
this._native.unregister();
|
||||
if (this._native.registered)
|
||||
this._native.unregister();
|
||||
},
|
||||
|
||||
_newRequest: function(agent, requestId, connection, settingName, hints, flags) {
|
||||
|
@ -159,14 +159,13 @@ const AuthenticationDialog = new Lang.Class({
|
||||
messageBox.add(this._nullMessageLabel);
|
||||
this._nullMessageLabel.show();
|
||||
|
||||
this.setButtons([{ label: _("Cancel"),
|
||||
action: Lang.bind(this, this.cancel),
|
||||
key: Clutter.Escape
|
||||
},
|
||||
{ label: _("Authenticate"),
|
||||
action: Lang.bind(this, this._onAuthenticateButtonPressed),
|
||||
default: true
|
||||
}]);
|
||||
this._cancelButton = this.addButton({ label: _("Cancel"),
|
||||
action: Lang.bind(this, this.cancel),
|
||||
key: Clutter.Escape });
|
||||
this._okButton = this.addButton({ label: _("Authenticate"),
|
||||
action: Lang.bind(this, this._onAuthenticateButtonPressed),
|
||||
default: true },
|
||||
{ expand: true, x_fill: false, x_align: St.Align.END });
|
||||
|
||||
this._doneEmitted = false;
|
||||
|
||||
@ -214,8 +213,17 @@ const AuthenticationDialog = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_updateSensitivity: function(sensitive) {
|
||||
this._passwordEntry.reactive = sensitive;
|
||||
this._passwordEntry.clutter_text.editable = sensitive;
|
||||
|
||||
this._okButton.can_focus = sensitive;
|
||||
this._okButton.reactive = sensitive;
|
||||
},
|
||||
|
||||
_onEntryActivate: function() {
|
||||
let response = this._passwordEntry.get_text();
|
||||
this._updateSensitivity(false);
|
||||
this._session.response(response);
|
||||
// When the user responds, dismiss already shown info and
|
||||
// error texts (if any)
|
||||
@ -269,6 +277,7 @@ const AuthenticationDialog = new Lang.Class({
|
||||
this._passwordBox.show();
|
||||
this._passwordEntry.set_text('');
|
||||
this._passwordEntry.grab_key_focus();
|
||||
this._updateSensitivity(true);
|
||||
this._ensureOpen();
|
||||
},
|
||||
|
||||
|
@ -1346,9 +1346,8 @@ const AccountNotification = new Lang.Class({
|
||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||
switch (action) {
|
||||
case 'view':
|
||||
let cmd = '/usr/bin/empathy-accounts'
|
||||
+ ' --select-account=%s'
|
||||
.format(account.get_path_suffix());
|
||||
let cmd = 'empathy-accounts --select-account=' +
|
||||
account.get_path_suffix();
|
||||
let app_info = Gio.app_info_create_from_commandline(cmd, null, 0);
|
||||
app_info.launch([], global.create_app_launch_context());
|
||||
break;
|
||||
|
@ -102,8 +102,7 @@ const CtrlAltTabManager = new Lang.Class({
|
||||
icon = app.create_icon_texture(POPUP_APPICON_SIZE);
|
||||
else
|
||||
icon = textureCache.bind_pixbuf_property(windows[i], 'icon');
|
||||
items.push({ window: windows[i],
|
||||
name: windows[i].title,
|
||||
items.push({ name: windows[i].title,
|
||||
iconActor: icon,
|
||||
sortGroup: SortGroup.MIDDLE });
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ function _onVertSepRepaint (area)
|
||||
cr.setDash([1, 3], 1); // Hard-code for now
|
||||
cr.setLineWidth(stippleWidth);
|
||||
cr.stroke();
|
||||
cr.$dispose();
|
||||
};
|
||||
|
||||
const DateMenuButton = new Lang.Class({
|
||||
@ -113,11 +114,6 @@ const DateMenuButton = new Lang.Class({
|
||||
this._openCalendarItem.actor.can_focus = false;
|
||||
vbox.add(this._openCalendarItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||
|
||||
this._calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' });
|
||||
this._calendarSettings.connect('changed::exec',
|
||||
Lang.bind(this, this._calendarSettingsChanged));
|
||||
this._calendarSettingsChanged();
|
||||
|
||||
// Whenever the menu is opened, select today
|
||||
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
|
||||
if (isOpen) {
|
||||
@ -151,12 +147,6 @@ const DateMenuButton = new Lang.Class({
|
||||
this._sessionUpdated();
|
||||
},
|
||||
|
||||
_calendarSettingsChanged: function() {
|
||||
let exec = this._calendarSettings.get_string('exec');
|
||||
let fullExec = GLib.find_program_in_path(exec);
|
||||
this._openCalendarItem.actor.visible = fullExec != null;
|
||||
},
|
||||
|
||||
_setEventsVisibility: function(visible) {
|
||||
this._openCalendarItem.actor.visible = visible;
|
||||
this._separator.visible = visible;
|
||||
@ -205,24 +195,8 @@ const DateMenuButton = new Lang.Class({
|
||||
|
||||
_onOpenCalendarActivate: function() {
|
||||
this.menu.close();
|
||||
let tool = this._calendarSettings.get_string('exec');
|
||||
if (tool.length == 0 || tool.substr(0, 9) == 'evolution') {
|
||||
// TODO: pass the selected day
|
||||
let app = Shell.AppSystem.get_default().lookup_app('evolution-calendar.desktop');
|
||||
app.activate();
|
||||
} else {
|
||||
let needTerm = this._calendarSettings.get_boolean('needs-term');
|
||||
if (needTerm) {
|
||||
let terminalSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.terminal' });
|
||||
let term = terminalSettings.get_string('exec');
|
||||
let arg = terminalSettings.get_string('exec-arg');
|
||||
if (arg != '')
|
||||
Util.spawn([term, arg, tool]);
|
||||
else
|
||||
Util.spawn([term, tool]);
|
||||
} else {
|
||||
Util.spawnCommandLine(tool)
|
||||
}
|
||||
}
|
||||
|
||||
let app = Gio.AppInfo.get_default_for_type('text/calendar', false);
|
||||
app.launch([], global.create_app_launch_context());
|
||||
}
|
||||
});
|
||||
|
@ -234,7 +234,7 @@ const _Draggable = new Lang.Class({
|
||||
this._dragY = this._dragStartY = stageY;
|
||||
|
||||
if (this.actor._delegate && this.actor._delegate.getDragActor) {
|
||||
this._dragActor = this.actor._delegate.getDragActor(this._dragStartX, this._dragStartY);
|
||||
this._dragActor = this.actor._delegate.getDragActor();
|
||||
this._dragActor.reparent(Main.uiGroup);
|
||||
this._dragActor.raise_top();
|
||||
Shell.util_set_hidden_from_pick(this._dragActor, true);
|
||||
|
@ -129,6 +129,7 @@ function logExtensionError(uuid, error) {
|
||||
extension.state = ExtensionState.ERROR;
|
||||
if (!extension.errors)
|
||||
extension.errors = [];
|
||||
extension.errors.push(message);
|
||||
|
||||
log('Extension "%s" had error: %s'.format(uuid, message));
|
||||
_signals.emit('extension-state-changed', { uuid: uuid,
|
||||
|
@ -1,45 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Lang = imports.lang;
|
||||
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const FLASHSPOT_ANIMATION_TIME = 0.25; // seconds
|
||||
|
||||
const Flashspot = new Lang.Class({
|
||||
Name: 'Flashspot',
|
||||
Extends: Lightbox.Lightbox,
|
||||
|
||||
_init: function(area) {
|
||||
this.parent(Main.uiGroup, { inhibitEvents: true,
|
||||
width: area.width,
|
||||
height: area.height });
|
||||
|
||||
this.actor.style_class = 'flashspot';
|
||||
this.actor.set_position(area.x, area.y);
|
||||
},
|
||||
|
||||
fire: function() {
|
||||
this.actor.opacity = 0;
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 255,
|
||||
time: FLASHSPOT_ANIMATION_TIME,
|
||||
transition: 'linear',
|
||||
onComplete: Lang.bind(this, this._onFireShowComplete)
|
||||
});
|
||||
this.actor.show();
|
||||
},
|
||||
|
||||
_onFireShowComplete: function() {
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 0,
|
||||
time: FLASHSPOT_ANIMATION_TIME,
|
||||
transition: 'linear',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this.destroy();
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
@ -183,6 +183,9 @@ const GrabHelper = new Lang.Class({
|
||||
else if (hadFocus || params.grabFocus)
|
||||
_navigateActor(newFocus);
|
||||
|
||||
if ((params.grabFocus || params.modal) && !this._capturedEventId)
|
||||
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
|
||||
|
||||
this._grabStack.push(params);
|
||||
return true;
|
||||
},
|
||||
@ -192,8 +195,6 @@ const GrabHelper = new Lang.Class({
|
||||
if (firstGrab) {
|
||||
if (!Main.pushModal(this._owner, this._modalParams))
|
||||
return false;
|
||||
|
||||
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
|
||||
}
|
||||
|
||||
this._modalCount++;
|
||||
@ -205,11 +206,6 @@ const GrabHelper = new Lang.Class({
|
||||
if (this._modalCount > 0)
|
||||
return;
|
||||
|
||||
if (this._capturedEventId > 0) {
|
||||
global.stage.disconnect(this._capturedEventId);
|
||||
this._capturedEventId = 0;
|
||||
}
|
||||
|
||||
Main.popModal(this._owner);
|
||||
global.sync_pointer();
|
||||
},
|
||||
@ -308,6 +304,11 @@ const GrabHelper = new Lang.Class({
|
||||
this._releaseFocusGrab();
|
||||
}
|
||||
|
||||
if (!this.grabbed && this._capturedEventId > 0) {
|
||||
global.stage.disconnect(this._capturedEventId);
|
||||
this._capturedEventId = 0;
|
||||
}
|
||||
|
||||
if (hadFocus) {
|
||||
let poppedGrab = poppedGrabs[0];
|
||||
_navigateActor(poppedGrab.savedFocus);
|
||||
@ -316,6 +317,13 @@ const GrabHelper = new Lang.Class({
|
||||
|
||||
_onCapturedEvent: function(actor, event) {
|
||||
let type = event.type();
|
||||
|
||||
if (type == Clutter.EventType.KEY_PRESS &&
|
||||
event.get_key_symbol() == Clutter.KEY_Escape) {
|
||||
this.ungrab({ isUser: true });
|
||||
return true;
|
||||
}
|
||||
|
||||
let press = type == Clutter.EventType.BUTTON_PRESS;
|
||||
let release = type == Clutter.EventType.BUTTON_RELEASE;
|
||||
let button = press || release;
|
||||
@ -328,12 +336,6 @@ const GrabHelper = new Lang.Class({
|
||||
if (!button && this._modalCount == 0)
|
||||
return false;
|
||||
|
||||
if (type == Clutter.EventType.KEY_PRESS &&
|
||||
event.get_key_symbol() == Clutter.KEY_Escape) {
|
||||
this.ungrab({ isUser: true });
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this._isWithinGrabbedActor(event.get_source()))
|
||||
return false;
|
||||
|
||||
|
@ -23,6 +23,8 @@ const BaseIcon = new Lang.Class({
|
||||
this.actor._delegate = this;
|
||||
this.actor.connect('style-changed',
|
||||
Lang.bind(this, this._onStyleChanged));
|
||||
this.actor.connect('destroy',
|
||||
Lang.bind(this, this._onDestroy));
|
||||
|
||||
this._spacing = 0;
|
||||
|
||||
@ -52,6 +54,9 @@ const BaseIcon = new Lang.Class({
|
||||
this._setSizeManually = params.setSizeManually;
|
||||
|
||||
this.icon = null;
|
||||
|
||||
let cache = St.TextureCache.get_default();
|
||||
this._iconThemeChangedId = cache.connect('icon-theme-changed', Lang.bind(this, this._onIconThemeChanged));
|
||||
},
|
||||
|
||||
_allocate: function(actor, box, flags) {
|
||||
@ -146,7 +151,22 @@ const BaseIcon = new Lang.Class({
|
||||
size = found ? len : ICON_SIZE;
|
||||
}
|
||||
|
||||
if (this.iconSize == size && this._iconBin.child)
|
||||
return;
|
||||
|
||||
this._createIconTexture(size);
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
if (this._iconThemeChangedId > 0) {
|
||||
let cache = St.TextureCache.get_default();
|
||||
cache.disconnect(this._iconThemeChangedId);
|
||||
this._iconThemeChangedId = 0;
|
||||
}
|
||||
},
|
||||
|
||||
_onIconThemeChanged: function() {
|
||||
this._createIconTexture(this.iconSize);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -2,18 +2,21 @@
|
||||
|
||||
const Caribou = imports.gi.Caribou;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const DBus = imports.dbus;
|
||||
const Gdk = imports.gi.Gdk;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const BoxPointer = imports.ui.boxpointer;
|
||||
const Layout = imports.ui.layout;
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
|
||||
const KEYBOARD_REST_TIME = Layout.KEYBOARD_ANIMATION_TIME * 2 * 1000;
|
||||
|
||||
const KEYBOARD_SCHEMA = 'org.gnome.shell.keyboard';
|
||||
const KEYBOARD_TYPE = 'keyboard-type';
|
||||
|
||||
@ -56,14 +59,7 @@ const Key = new Lang.Class({
|
||||
if (this._key.name == 'Control_L' || this._key.name == 'Alt_L')
|
||||
this._key.latch = true;
|
||||
|
||||
this._key.connect('key-pressed', Lang.bind(this, function ()
|
||||
{ this.actor.checked = true }));
|
||||
this._key.connect('key-released', Lang.bind(this, function ()
|
||||
{ this.actor.checked = false; }));
|
||||
|
||||
if (this._extended_keys.length > 0) {
|
||||
this._grabbed = false;
|
||||
this._eventCaptureId = 0;
|
||||
this._key.connect('notify::show-subkeys', Lang.bind(this, this._onShowSubkeysChanged));
|
||||
this._boxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
|
||||
{ x_fill: true,
|
||||
@ -115,52 +111,23 @@ const Key = new Lang.Class({
|
||||
this._boxPointer.bin.add_actor(this._extended_keyboard);
|
||||
},
|
||||
|
||||
_onEventCapture: function (actor, event) {
|
||||
let source = event.get_source();
|
||||
let type = event.type();
|
||||
|
||||
if ((type == Clutter.EventType.BUTTON_PRESS ||
|
||||
type == Clutter.EventType.BUTTON_RELEASE) &&
|
||||
this._extended_keyboard.contains(source)) {
|
||||
source.extended_key.press();
|
||||
source.extended_key.release();
|
||||
return false;
|
||||
}
|
||||
if (type == Clutter.EventType.BUTTON_PRESS) {
|
||||
this._boxPointer.actor.hide();
|
||||
this._ungrab();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_ungrab: function () {
|
||||
global.stage.disconnect(this._eventCaptureId);
|
||||
this._eventCaptureId = 0;
|
||||
this._grabbed = false;
|
||||
Main.popModal(this.actor);
|
||||
get subkeys() {
|
||||
return this._boxPointer;
|
||||
},
|
||||
|
||||
_onShowSubkeysChanged: function () {
|
||||
if (this._key.show_subkeys) {
|
||||
this.actor.fake_release();
|
||||
this._boxPointer.actor.raise_top();
|
||||
this._boxPointer.setPosition(this.actor, 0.5);
|
||||
this._boxPointer.show(BoxPointer.PopupAnimation.FULL);
|
||||
this.emit('show-subkeys');
|
||||
this.actor.fake_release();
|
||||
this.actor.set_hover(false);
|
||||
if (!this._grabbed) {
|
||||
Main.pushModal(this.actor);
|
||||
this._eventCaptureId = global.stage.connect('captured-event', Lang.bind(this, this._onEventCapture));
|
||||
this._grabbed = true;
|
||||
}
|
||||
this._key.release();
|
||||
} else {
|
||||
if (this._grabbed)
|
||||
this._ungrab();
|
||||
this._boxPointer.hide(BoxPointer.PopupAnimation.FULL);
|
||||
this.emit('hide-subkeys');
|
||||
}
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(Key.prototype);
|
||||
|
||||
const Keyboard = new Lang.Class({
|
||||
// HACK: we can't set Name, because it collides with Name dbus property
|
||||
@ -182,6 +149,18 @@ const Keyboard = new Lang.Class({
|
||||
this._a11yApplicationsSettings = new Gio.Settings({ schema: A11Y_APPLICATIONS_SCHEMA });
|
||||
this._a11yApplicationsSettings.connect('changed', Lang.bind(this, this._settingsChanged));
|
||||
this._settingsChanged();
|
||||
|
||||
this._showIdleId = 0;
|
||||
this._subkeysBoxPointer = null;
|
||||
this._capturedEventId = 0;
|
||||
this._capturedPress = false;
|
||||
|
||||
this._keyboardVisible = false;
|
||||
Main.layoutManager.connect('keyboard-visible-changed', Lang.bind(this, function(o, visible) {
|
||||
this._keyboardVisible = visible;
|
||||
}));
|
||||
this._keyboardRequested = false;
|
||||
this._keyboardRestingId = 0;
|
||||
},
|
||||
|
||||
init: function () {
|
||||
@ -273,10 +252,14 @@ const Keyboard = new Lang.Class({
|
||||
return;
|
||||
|
||||
let time = global.get_current_time();
|
||||
if (focus instanceof Clutter.Text)
|
||||
this.Show(time);
|
||||
else
|
||||
if (!(focus instanceof Clutter.Text)) {
|
||||
this.Hide(time);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._showIdleId)
|
||||
this._showIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE,
|
||||
Lang.bind(this, function() { this.Show(time); }));
|
||||
},
|
||||
|
||||
_addKeys: function () {
|
||||
@ -331,6 +314,19 @@ const Keyboard = new Lang.Class({
|
||||
return trayButton;
|
||||
},
|
||||
|
||||
_onCapturedEvent: function(actor, event) {
|
||||
let type = event.type();
|
||||
let press = type == Clutter.EventType.BUTTON_PRESS;
|
||||
let release = type == Clutter.EventType.BUTTON_RELEASE;
|
||||
|
||||
if (press)
|
||||
this._capturedPress = true;
|
||||
else if (release && this._capturedPress)
|
||||
this._hideSubkeys();
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_addRows : function (keys, layout) {
|
||||
let keyboard_row = new St.BoxLayout();
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
@ -353,6 +349,19 @@ const Keyboard = new Lang.Class({
|
||||
// Add new key for hiding message tray
|
||||
right_box.add(this._getTrayIcon());
|
||||
}
|
||||
|
||||
button.connect('show-subkeys', Lang.bind(this, function() {
|
||||
if (this._subkeysBoxPointer)
|
||||
this._subkeysBoxPointer.hide(BoxPointer.PopupAnimation.FULL);
|
||||
this._subkeysBoxPointer = button.subkeys;
|
||||
this._subkeysBoxPointer.show(BoxPointer.PopupAnimation.FULL);
|
||||
if (!this._capturedEventId)
|
||||
this._capturedEventId = this.actor.connect('captured-event',
|
||||
Lang.bind(this, this._onCapturedEvent));
|
||||
}));
|
||||
button.connect('hide-subkeys', Lang.bind(this, function() {
|
||||
this._hideSubkeys();
|
||||
}));
|
||||
}
|
||||
keyboard_row.add(left_box, { expand: true, x_fill: false, x_align: St.Align.START });
|
||||
keyboard_row.add(right_box, { expand: true, x_fill: false, x_align: St.Align.END });
|
||||
@ -462,7 +471,37 @@ const Keyboard = new Lang.Class({
|
||||
actor._extended_keys || actor.extended_key;
|
||||
},
|
||||
|
||||
_clearKeyboardRestTimer: function() {
|
||||
if (!this._keyboardRestingId)
|
||||
return;
|
||||
GLib.source_remove(this._keyboardRestingId);
|
||||
this._keyboardRestingId = 0;
|
||||
},
|
||||
|
||||
show: function (monitor) {
|
||||
this._keyboardRequested = true;
|
||||
|
||||
if (this._keyboardVisible) {
|
||||
if (monitor != Main.layoutManager.keyboardIndex) {
|
||||
Main.layoutManager.keyboardIndex = monitor;
|
||||
this._redraw();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this._clearKeyboardRestTimer();
|
||||
this._keyboardRestingId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
||||
KEYBOARD_REST_TIME,
|
||||
Lang.bind(this, function() {
|
||||
this._clearKeyboardRestTimer();
|
||||
this._show(monitor);
|
||||
}));
|
||||
},
|
||||
|
||||
_show: function(monitor) {
|
||||
if (!this._keyboardRequested)
|
||||
return;
|
||||
|
||||
Main.layoutManager.keyboardIndex = monitor;
|
||||
this._redraw();
|
||||
Main.layoutManager.showKeyboard();
|
||||
@ -470,10 +509,41 @@ const Keyboard = new Lang.Class({
|
||||
},
|
||||
|
||||
hide: function () {
|
||||
this._keyboardRequested = false;
|
||||
|
||||
if (!this._keyboardVisible)
|
||||
return;
|
||||
|
||||
this._clearKeyboardRestTimer();
|
||||
this._keyboardRestingId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
||||
KEYBOARD_REST_TIME,
|
||||
Lang.bind(this, function() {
|
||||
this._clearKeyboardRestTimer();
|
||||
this._hide();
|
||||
}));
|
||||
},
|
||||
|
||||
_hide: function() {
|
||||
if (this._keyboardRequested)
|
||||
return;
|
||||
|
||||
this._hideSubkeys();
|
||||
Main.layoutManager.hideKeyboard();
|
||||
this._createSource();
|
||||
},
|
||||
|
||||
_hideSubkeys: function() {
|
||||
if (this._subkeysBoxPointer) {
|
||||
this._subkeysBoxPointer.hide(BoxPointer.PopupAnimation.FULL);
|
||||
this._subkeysBoxPointer = null;
|
||||
}
|
||||
if (this._capturedEventId) {
|
||||
this.actor.disconnect(this._capturedEventId);
|
||||
this._capturedEventId = 0;
|
||||
}
|
||||
this._capturedPress = false;
|
||||
},
|
||||
|
||||
_moveTemporarily: function () {
|
||||
let currentWindow = global.screen.get_display().focus_window;
|
||||
let rect = currentWindow.get_outer_rect();
|
||||
@ -502,6 +572,13 @@ const Keyboard = new Lang.Class({
|
||||
return one - two;
|
||||
},
|
||||
|
||||
_clearShowIdle: function() {
|
||||
if (!this._showIdleId)
|
||||
return;
|
||||
GLib.source_remove(this._showIdleId);
|
||||
this._showIdleId = 0;
|
||||
},
|
||||
|
||||
// D-Bus methods
|
||||
Show: function(timestamp) {
|
||||
if (!this._enableKeyboard)
|
||||
@ -510,6 +587,8 @@ const Keyboard = new Lang.Class({
|
||||
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
|
||||
return;
|
||||
|
||||
this._clearShowIdle();
|
||||
|
||||
if (timestamp != Clutter.CURRENT_TIME)
|
||||
this._timestamp = timestamp;
|
||||
this.show(Main.layoutManager.focusIndex);
|
||||
@ -522,6 +601,8 @@ const Keyboard = new Lang.Class({
|
||||
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
|
||||
return;
|
||||
|
||||
this._clearShowIdle();
|
||||
|
||||
if (timestamp != Clutter.CURRENT_TIME)
|
||||
this._timestamp = timestamp;
|
||||
this.hide();
|
||||
|
@ -16,7 +16,7 @@ const Tweener = imports.ui.tweener;
|
||||
|
||||
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
|
||||
const STARTUP_ANIMATION_TIME = 0.2;
|
||||
const KEYBOARD_ANIMATION_TIME = 0.5;
|
||||
const KEYBOARD_ANIMATION_TIME = 0.15;
|
||||
const PLYMOUTH_TRANSITION_TIME = 1;
|
||||
|
||||
const MonitorConstraint = new Lang.Class({
|
||||
@ -325,7 +325,7 @@ const LayoutManager = new Lang.Class({
|
||||
} else {
|
||||
let focusWindow = global.display.focus_window;
|
||||
if (focusWindow)
|
||||
i = this._chrome.findIndexForWindow(focusWindow.get_compositor_private());
|
||||
i = this._chrome.findIndexForWindow(focusWindow);
|
||||
}
|
||||
|
||||
return i;
|
||||
@ -437,7 +437,7 @@ const LayoutManager = new Lang.Class({
|
||||
Tweener.addTween(this.keyboardBox,
|
||||
{ anchor_y: 0,
|
||||
time: immediate ? 0 : KEYBOARD_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
transition: 'easeInQuad',
|
||||
onComplete: this._hideKeyboardComplete,
|
||||
onCompleteScope: this
|
||||
});
|
||||
@ -513,7 +513,11 @@ const LayoutManager = new Lang.Class({
|
||||
|
||||
findMonitorForActor: function(actor) {
|
||||
return this.monitors[this._chrome.findIndexForActor(actor)];
|
||||
}
|
||||
},
|
||||
|
||||
findMonitorForWindow: function(window) {
|
||||
return this.monitors[this._chrome.findIndexForWindow(window)];
|
||||
},
|
||||
});
|
||||
Signals.addSignalMethods(LayoutManager.prototype);
|
||||
|
||||
@ -891,7 +895,8 @@ const Chrome = new Lang.Class({
|
||||
},
|
||||
|
||||
findIndexForWindow: function(window) {
|
||||
let i = this._findMonitorForRect(window.x, window.y, window.width, window.height);
|
||||
let rect = window.get_input_rect();
|
||||
let i = this._findMonitorForRect(rect.x, rect.y, rect.width, rect.height);
|
||||
if (i >= 0)
|
||||
return i;
|
||||
return this._primaryIndex; // Not on any monitor, pretend its on the primary
|
||||
@ -909,7 +914,8 @@ const Chrome = new Lang.Class({
|
||||
},
|
||||
|
||||
findMonitorForWindow: function(window) {
|
||||
let i = this._findMonitorForRect(window.x, window.y, window.width, window.height);
|
||||
let rect = window.get_input_rect();
|
||||
let i = this._findMonitorForRect(rect.x, rect.y, rect.width, rect.height);
|
||||
if (i >= 0)
|
||||
return this._monitors[i];
|
||||
else
|
||||
@ -958,14 +964,15 @@ const Chrome = new Lang.Class({
|
||||
|
||||
for (let i = windows.length - 1; i > -1; i--) {
|
||||
let window = windows[i];
|
||||
let layer = window.get_meta_window().get_layer();
|
||||
let metaWindow = window.meta_window;
|
||||
let layer = metaWindow.get_layer();
|
||||
|
||||
// Skip minimized windows
|
||||
if (!window.showing_on_its_workspace())
|
||||
continue;
|
||||
|
||||
if (layer == Meta.StackLayer.FULLSCREEN) {
|
||||
let monitor = this.findMonitorForWindow(window);
|
||||
let monitor = this.findMonitorForWindow(metaWindow);
|
||||
if (monitor)
|
||||
monitor.inFullscreen = true;
|
||||
}
|
||||
@ -982,7 +989,7 @@ const Chrome = new Lang.Class({
|
||||
}
|
||||
|
||||
// Or whether it is monitor sized
|
||||
let monitor = this.findMonitorForWindow(window);
|
||||
let monitor = this.findMonitorForWindow(metaWindow);
|
||||
if (monitor &&
|
||||
window.x <= monitor.x &&
|
||||
window.x + window.width >= monitor.x + monitor.width &&
|
||||
@ -1085,7 +1092,7 @@ const Chrome = new Lang.Class({
|
||||
} else if (y1 <= primary.y && y2 >= primary.y + primary.height) {
|
||||
if (x1 <= 0)
|
||||
side = Meta.Side.LEFT;
|
||||
else if (x2 >= global.screen_width)
|
||||
else if (x2 >= primary.x + primary.width)
|
||||
side = Meta.Side.RIGHT;
|
||||
else
|
||||
continue;
|
||||
|
@ -39,6 +39,7 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
|
||||
* in the shell core code too. */
|
||||
'const stage = global.stage; ' +
|
||||
/* Special lookingGlass functions */
|
||||
'const inspect = Lang.bind(Main.lookingGlass, Main.lookingGlass.inspect); ' +
|
||||
'const it = Main.lookingGlass.getIt(); ' +
|
||||
'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ';
|
||||
|
||||
@ -871,8 +872,7 @@ const LookingGlass = new Lang.Class({
|
||||
inspectIcon.connect('button-press-event', Lang.bind(this, function () {
|
||||
let inspector = new Inspector(this);
|
||||
inspector.connect('target', Lang.bind(this, function(i, target, stageX, stageY) {
|
||||
this._pushResult('<inspect x:' + stageX + ' y:' + stageY + '>',
|
||||
target);
|
||||
this._pushResult('inspect(' + Math.round(stageX) + ', ' + Math.round(stageY) + ')', target);
|
||||
}));
|
||||
inspector.connect('closed', Lang.bind(this, function() {
|
||||
this.actor.show();
|
||||
@ -1057,6 +1057,10 @@ const LookingGlass = new Lang.Class({
|
||||
this._entry.text = '';
|
||||
},
|
||||
|
||||
inspect: function(x, y) {
|
||||
return global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
|
||||
},
|
||||
|
||||
getIt: function () {
|
||||
return this._it;
|
||||
},
|
||||
|
@ -49,7 +49,8 @@ const KeybindingMode = {
|
||||
LOGIN_SCREEN: 1 << 4,
|
||||
MESSAGE_TRAY: 1 << 5,
|
||||
SYSTEM_MODAL: 1 << 6,
|
||||
LOOKING_GLASS: 1 << 7
|
||||
LOOKING_GLASS: 1 << 7,
|
||||
ALL: ~0,
|
||||
};
|
||||
|
||||
let componentManager = null;
|
||||
@ -80,8 +81,6 @@ let _defaultCssStylesheet = null;
|
||||
let _cssStylesheet = null;
|
||||
let _overridesSettings = null;
|
||||
|
||||
let background = null;
|
||||
|
||||
function _sessionUpdated() {
|
||||
wm.setCustomKeybindingHandler('panel-main-menu',
|
||||
KeybindingMode.NORMAL |
|
||||
@ -160,13 +159,13 @@ function start() {
|
||||
xdndHandler = new XdndHandler.XdndHandler();
|
||||
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
|
||||
overview = new Overview.Overview();
|
||||
wm = new WindowManager.WindowManager();
|
||||
magnifier = new Magnifier.Magnifier();
|
||||
if (UnlockDialog.isSupported())
|
||||
screenShield = new ScreenShield.ScreenShield();
|
||||
else
|
||||
screenShield = new ScreenShield.ScreenShieldFallback();
|
||||
panel = new Panel.Panel();
|
||||
wm = new WindowManager.WindowManager();
|
||||
messageTray = new MessageTray.MessageTray();
|
||||
keyboard = new Keyboard.Keyboard();
|
||||
notificationDaemon = new NotificationDaemon.NotificationDaemon();
|
||||
@ -546,19 +545,20 @@ function pushModal(actor, params) {
|
||||
if (index >= 0)
|
||||
popModal(actor);
|
||||
});
|
||||
let curFocus = global.stage.get_key_focus();
|
||||
let curFocusDestroyId;
|
||||
if (curFocus != null) {
|
||||
curFocusDestroyId = curFocus.connect('destroy', function() {
|
||||
|
||||
let prevFocus = global.stage.get_key_focus();
|
||||
let prevFocusDestroyId;
|
||||
if (prevFocus != null) {
|
||||
prevFocusDestroyId = prevFocus.connect('destroy', function() {
|
||||
let index = _findModal(actor);
|
||||
if (index >= 0)
|
||||
modalActorFocusStack[index].actor = null;
|
||||
modalActorFocusStack[index].prevFocus = null;
|
||||
});
|
||||
}
|
||||
modalActorFocusStack.push({ actor: actor,
|
||||
focus: curFocus,
|
||||
destroyId: actorDestroyId,
|
||||
focusDestroyId: curFocusDestroyId,
|
||||
prevFocus: prevFocus,
|
||||
prevFocusDestroyId: prevFocusDestroyId,
|
||||
keybindingMode: keybindingMode });
|
||||
|
||||
keybindingMode = params.keybindingMode;
|
||||
@ -599,18 +599,33 @@ function popModal(actor, timestamp) {
|
||||
record.actor.disconnect(record.destroyId);
|
||||
|
||||
if (focusIndex == modalActorFocusStack.length - 1) {
|
||||
if (record.focus)
|
||||
record.focus.disconnect(record.focusDestroyId);
|
||||
if (record.prevFocus)
|
||||
record.prevFocus.disconnect(record.prevFocusDestroyId);
|
||||
keybindingMode = record.keybindingMode;
|
||||
global.stage.set_key_focus(record.focus);
|
||||
global.stage.set_key_focus(record.prevFocus);
|
||||
} else {
|
||||
// If we have:
|
||||
// global.stage.set_focus(a);
|
||||
// Main.pushModal(b);
|
||||
// Main.pushModal(c);
|
||||
// Main.pushModal(d);
|
||||
//
|
||||
// then we have the stack:
|
||||
// [{ prevFocus: a, actor: b },
|
||||
// { prevFocus: b, actor: c },
|
||||
// { prevFocus: c, actor: d }]
|
||||
//
|
||||
// When actor c is destroyed/popped, if we only simply remove the
|
||||
// record, then the focus stack will be [a, c], rather than the correct
|
||||
// [a, b]. Shift the focus stack up before removing the record to ensure
|
||||
// that we get the correct result.
|
||||
let t = modalActorFocusStack[modalActorFocusStack.length - 1];
|
||||
if (t.focus)
|
||||
t.focus.disconnect(t.focusDestroyId);
|
||||
if (t.prevFocus)
|
||||
t.prevFocus.disconnect(t.prevFocusDestroyId);
|
||||
// Remove from the middle, shift the focus chain up
|
||||
for (let i = modalActorFocusStack.length - 1; i > focusIndex; i--) {
|
||||
modalActorFocusStack[i].focus = modalActorFocusStack[i - 1].focus;
|
||||
modalActorFocusStack[i].focusDestroyId = modalActorFocusStack[i - 1].focusDestroyId;
|
||||
modalActorFocusStack[i].prevFocus = modalActorFocusStack[i - 1].prevFocus;
|
||||
modalActorFocusStack[i].prevFocusDestroyId = modalActorFocusStack[i - 1].prevFocusDestroyId;
|
||||
modalActorFocusStack[i].keybindingMode = modalActorFocusStack[i - 1].keybindingMode;
|
||||
}
|
||||
}
|
||||
|
@ -611,6 +611,8 @@ const Notification = new Lang.Class({
|
||||
setImage: function(image) {
|
||||
if (this._imageBin)
|
||||
this.unsetImage();
|
||||
if (!image)
|
||||
return;
|
||||
this._imageBin = new St.Bin();
|
||||
this._imageBin.child = image;
|
||||
this._imageBin.opacity = 230;
|
||||
@ -1412,7 +1414,7 @@ const MessageTray = new Lang.Class({
|
||||
actor.grab_key_focus();
|
||||
}));
|
||||
global.focus_manager.add_group(this.actor);
|
||||
this._summary = new St.BoxLayout({ name: 'summary-mode',
|
||||
this._summary = new St.BoxLayout({ style_class: 'message-tray-summary',
|
||||
reactive: true,
|
||||
track_hover: true,
|
||||
x_align: Clutter.ActorAlign.END,
|
||||
|
@ -96,8 +96,7 @@ const ModalDialog = new Lang.Class({
|
||||
y_align: St.Align.START });
|
||||
|
||||
this.buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
|
||||
visible: false,
|
||||
vertical: false });
|
||||
vertical: false });
|
||||
this.dialogLayout.add(this.buttonLayout,
|
||||
{ expand: true,
|
||||
x_align: St.Align.MIDDLE,
|
||||
@ -120,7 +119,6 @@ const ModalDialog = new Lang.Class({
|
||||
|
||||
setButtons: function(buttons) {
|
||||
this.clearButtons();
|
||||
this.buttonLayout.visible = (buttons.length > 0);
|
||||
|
||||
for (let i = 0; i < buttons.length; i++) {
|
||||
let buttonInfo = buttons[i];
|
||||
|
@ -127,42 +127,42 @@ const NotificationDaemon = new Lang.Class({
|
||||
this._trayManager.manage_stage(global.stage, Main.messageTray.actor);
|
||||
},
|
||||
|
||||
_iconForNotificationData: function(icon, hints) {
|
||||
// If an icon is not specified, we use 'image-data' or 'image-path' hint for an icon
|
||||
// and don't show a large image. There are currently many applications that use
|
||||
// notify_notification_set_icon_from_pixbuf() from libnotify, which in turn sets
|
||||
// the 'image-data' hint. These applications don't typically pass in 'app_icon'
|
||||
// argument to Notify() and actually expect the pixbuf to be shown as an icon.
|
||||
// So the logic here does the right thing for this case. If both an icon and either
|
||||
// one of 'image-data' or 'image-path' are specified, we show both an icon and
|
||||
// a large image.
|
||||
if (icon) {
|
||||
if (icon.substr(0, 7) == 'file://')
|
||||
return new Gio.FileIcon({ file: Gio.File.new_for_uri(icon) });
|
||||
else if (icon[0] == '/') {
|
||||
return new Gio.FileIcon({ file: Gio.File.new_for_path(icon) });
|
||||
} else
|
||||
return new Gio.ThemedIcon({ name: icon });
|
||||
} else if (hints['image-data']) {
|
||||
_imageForNotificationData: function(hints) {
|
||||
if (hints['image-data']) {
|
||||
let [width, height, rowStride, hasAlpha,
|
||||
bitsPerSample, nChannels, data] = hints['image-data'];
|
||||
return Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha,
|
||||
bitsPerSample, width, height, rowStride);
|
||||
} else if (hints['image-path']) {
|
||||
return new Gio.FileIcon({ file: Gio.File.new_for_path(hints['image-path']) });
|
||||
} else {
|
||||
let stockIcon;
|
||||
switch (hints.urgency) {
|
||||
case Urgency.LOW:
|
||||
case Urgency.NORMAL:
|
||||
stockIcon = 'gtk-dialog-info';
|
||||
break;
|
||||
case Urgency.CRITICAL:
|
||||
stockIcon = 'gtk-dialog-error';
|
||||
break;
|
||||
}
|
||||
return new Gio.ThemedIcon({ name: stockIcon });
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
_fallbackIconForNotificationData: function(hints) {
|
||||
let stockIcon;
|
||||
switch (hints.urgency) {
|
||||
case Urgency.LOW:
|
||||
case Urgency.NORMAL:
|
||||
stockIcon = 'gtk-dialog-info';
|
||||
break;
|
||||
case Urgency.CRITICAL:
|
||||
stockIcon = 'gtk-dialog-error';
|
||||
break;
|
||||
}
|
||||
return new Gio.ThemedIcon({ name: stockIcon });
|
||||
},
|
||||
|
||||
_iconForNotificationData: function(icon) {
|
||||
if (icon) {
|
||||
if (icon.substr(0, 7) == 'file://')
|
||||
return new Gio.FileIcon({ file: Gio.File.new_for_uri(icon) });
|
||||
else if (icon[0] == '/')
|
||||
return new Gio.FileIcon({ file: Gio.File.new_for_path(icon) });
|
||||
else
|
||||
return new Gio.ThemedIcon({ name: icon });
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
_lookupSource: function(title, pid, trayIcon) {
|
||||
@ -355,12 +355,8 @@ const NotificationDaemon = new Lang.Class({
|
||||
[ndata.id, ndata.icon, ndata.summary, ndata.body,
|
||||
ndata.actions, ndata.hints, ndata.notification];
|
||||
|
||||
let gicon = this._iconForNotificationData(icon, hints);
|
||||
|
||||
if (notification == null) {
|
||||
notification = new MessageTray.Notification(source, summary, body,
|
||||
{ gicon: gicon,
|
||||
bannerMarkup: true });
|
||||
notification = new MessageTray.Notification(source);
|
||||
ndata.notification = notification;
|
||||
notification.connect('destroy', Lang.bind(this,
|
||||
function(n, reason) {
|
||||
@ -383,29 +379,33 @@ const NotificationDaemon = new Lang.Class({
|
||||
function(n, actionId) {
|
||||
this._emitActionInvoked(ndata.id, actionId);
|
||||
}));
|
||||
} else {
|
||||
notification.update(summary, body, { gicon: gicon,
|
||||
bannerMarkup: true,
|
||||
clear: true });
|
||||
}
|
||||
|
||||
// We only display a large image if an icon is also specified.
|
||||
if (icon && (hints['image-data'] || hints['image-path'])) {
|
||||
let image = null;
|
||||
if (hints['image-data']) {
|
||||
let [width, height, rowStride, hasAlpha,
|
||||
bitsPerSample, nChannels, data] = hints['image-data'];
|
||||
image = St.TextureCache.get_default().load_from_raw(data, hasAlpha,
|
||||
width, height, rowStride, notification.IMAGE_SIZE);
|
||||
} else if (hints['image-path']) {
|
||||
image = St.TextureCache.get_default().load_uri_async(GLib.filename_to_uri(hints['image-path'], null),
|
||||
notification.IMAGE_SIZE,
|
||||
notification.IMAGE_SIZE);
|
||||
}
|
||||
notification.setImage(image);
|
||||
} else {
|
||||
notification.unsetImage();
|
||||
}
|
||||
let gicon = this._iconForNotificationData(icon, hints);
|
||||
let gimage = this._imageForNotificationData(hints);
|
||||
|
||||
let image = null;
|
||||
|
||||
// If an icon is not specified, we use 'image-data' or 'image-path' hint for an icon
|
||||
// and don't show a large image. There are currently many applications that use
|
||||
// notify_notification_set_icon_from_pixbuf() from libnotify, which in turn sets
|
||||
// the 'image-data' hint. These applications don't typically pass in 'app_icon'
|
||||
// argument to Notify() and actually expect the pixbuf to be shown as an icon.
|
||||
// So the logic here does the right thing for this case. If both an icon and either
|
||||
// one of 'image-data' or 'image-path' are specified, we show both an icon and
|
||||
// a large image.
|
||||
if (gicon && gimage)
|
||||
image = new St.Icon({ gicon: gimage });
|
||||
else if (!gicon && gimage)
|
||||
gicon = gimage;
|
||||
else if (!gicon)
|
||||
gicon = this._fallbackIconForNotificationData(hints);
|
||||
|
||||
notification.setImage(image);
|
||||
|
||||
notification.update(summary, body, { gicon: gicon,
|
||||
bannerMarkup: true,
|
||||
clear: true });
|
||||
|
||||
if (actions.length) {
|
||||
notification.setUseActionIcons(hints['action-icons'] == true);
|
||||
@ -435,7 +435,7 @@ const NotificationDaemon = new Lang.Class({
|
||||
// of the 'transient' hint with hints['transient'] rather than hints.transient
|
||||
notification.setTransient(hints['transient'] == true);
|
||||
|
||||
let sourceGIcon = source.useNotificationIcon ? this._iconForNotificationData(icon, hints) : null;
|
||||
let sourceGIcon = source.useNotificationIcon ? gicon : null;
|
||||
source.processNotification(notification, sourceGIcon);
|
||||
},
|
||||
|
||||
|
@ -23,9 +23,6 @@ const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
|
||||
// Time for initial animation going into Overview mode
|
||||
const ANIMATION_TIME = 0.25;
|
||||
|
||||
// XXX -- grab this automatically. Hard to do.
|
||||
const DASH_MAX_WIDTH = 96;
|
||||
|
||||
const DND_WINDOW_SWITCH_TIMEOUT = 1250;
|
||||
|
||||
const GLSL_DIM_EFFECT_DECLARATIONS = '';
|
||||
@ -33,9 +30,9 @@ const GLSL_DIM_EFFECT_CODE = '\
|
||||
vec2 dist = cogl_tex_coord_in[0].xy - vec2(0.5, 0.5); \
|
||||
float elipse_radius = 0.5; \
|
||||
/* from https://bugzilla.gnome.org/show_bug.cgi?id=669798: \
|
||||
the alpha on the gradient goes from 165 at its darkest to 98 at its most transparent. */ \
|
||||
float y = 165.0 / 255.0; \
|
||||
float x = 98.0 / 255.0; \
|
||||
the alpha on the gradient goes from 250 at its darkest to 180 at its most transparent. */ \
|
||||
float y = 250.0 / 255.0; \
|
||||
float x = 180.0 / 255.0; \
|
||||
/* interpolate darkening value, based on distance from screen center */ \
|
||||
float val = min(length(dist), elipse_radius); \
|
||||
float a = mix(x, y, val / elipse_radius); \
|
||||
@ -136,23 +133,15 @@ const Overview = new Lang.Class({
|
||||
this._desktopFade = new St.Bin();
|
||||
global.overlay_group.add_actor(this._desktopFade);
|
||||
|
||||
this._spacing = 0;
|
||||
|
||||
/* Translators: This is the main view to select
|
||||
activities. See also note for "Activities" string. */
|
||||
this._group = new St.Widget({ name: 'overview',
|
||||
accessible_name: _("Overview"),
|
||||
reactive: true });
|
||||
this._group._delegate = this;
|
||||
this._group.connect('style-changed',
|
||||
Lang.bind(this, function() {
|
||||
let node = this._group.get_theme_node();
|
||||
let spacing = node.get_length('spacing');
|
||||
if (spacing != this._spacing) {
|
||||
this._spacing = spacing;
|
||||
this._relayout();
|
||||
}
|
||||
}));
|
||||
this._overview = new St.BoxLayout({ name: 'overview',
|
||||
accessible_name: _("Overview"),
|
||||
reactive: true,
|
||||
vertical: true });
|
||||
this._overview._delegate = this;
|
||||
|
||||
this._group = new St.BoxLayout({ name: 'overview-group' });
|
||||
|
||||
this._capturedEventId = 0;
|
||||
this._buttonPressId = 0;
|
||||
@ -169,12 +158,11 @@ const Overview = new Lang.Class({
|
||||
// Dash elements, or mouseover handlers in the workspaces.
|
||||
this._coverPane = new Clutter.Rectangle({ opacity: 0,
|
||||
reactive: true });
|
||||
this._group.add_actor(this._coverPane);
|
||||
this._overview.add_actor(this._coverPane);
|
||||
this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; }));
|
||||
|
||||
|
||||
this._group.hide();
|
||||
global.overlay_group.add_actor(this._group);
|
||||
this._overview.hide();
|
||||
global.overlay_group.add_actor(this._overview);
|
||||
|
||||
this._coverPane.hide();
|
||||
|
||||
@ -186,6 +174,8 @@ const Overview = new Lang.Class({
|
||||
Main.xdndHandler.connect('drag-begin', Lang.bind(this, this._onDragBegin));
|
||||
Main.xdndHandler.connect('drag-end', Lang.bind(this, this._onDragEnd));
|
||||
|
||||
global.screen.connect('restacked', Lang.bind(this, this._onRestacked));
|
||||
|
||||
this._windowSwitchTimeoutId = 0;
|
||||
this._windowSwitchTimestamp = 0;
|
||||
this._lastActiveWorkspaceIndex = -1;
|
||||
@ -213,6 +203,13 @@ const Overview = new Lang.Class({
|
||||
|
||||
this._shellInfo = new ShellInfo();
|
||||
|
||||
// Add a clone of the panel to the overview so spacing and such is
|
||||
// automatic
|
||||
this._panelGhost = new St.Bin({ child: new Clutter.Clone({ source: Main.panel.actor }),
|
||||
reactive: false,
|
||||
opacity: 0 });
|
||||
this._overview.add_actor(this._panelGhost);
|
||||
|
||||
this._searchEntry = new St.Entry({ name: 'searchEntry',
|
||||
/* Translators: this is the text displayed
|
||||
in the search entry when no search is
|
||||
@ -221,16 +218,13 @@ const Overview = new Lang.Class({
|
||||
hint_text: _("Type to search..."),
|
||||
track_hover: true,
|
||||
can_focus: true });
|
||||
this._group.add_actor(this._searchEntry);
|
||||
|
||||
this._dash = new Dash.Dash();
|
||||
this._viewSelector = new ViewSelector.ViewSelector(this._searchEntry,
|
||||
this._dash.showAppsButton);
|
||||
this._group.add_actor(this._viewSelector.actor);
|
||||
this._group.add_actor(this._dash.actor);
|
||||
this._searchEntryBin = new St.Bin({ child: this._searchEntry,
|
||||
x_align: St.Align.MIDDLE });
|
||||
this._overview.add_actor(this._searchEntryBin);
|
||||
|
||||
// TODO - recalculate everything when desktop size changes
|
||||
this._dash.actor.add_constraint(this._viewSelector.constrainHeight);
|
||||
this._dash = new Dash.Dash();
|
||||
this._group.add_actor(this._dash.actor);
|
||||
this.dashIconSize = this._dash.iconSize;
|
||||
this._dash.connect('icon-size-changed',
|
||||
Lang.bind(this, function() {
|
||||
@ -241,6 +235,23 @@ const Overview = new Lang.Class({
|
||||
// the left of the overview
|
||||
Main.ctrlAltTabManager.addGroup(this._dash.actor, _("Dash"), 'user-bookmarks-symbolic');
|
||||
|
||||
this._viewSelector = new ViewSelector.ViewSelector(this._searchEntry,
|
||||
this._dash.showAppsButton);
|
||||
this._group.add(this._viewSelector.actor, { x_fill: true,
|
||||
expand: true });
|
||||
|
||||
// Add our same-line elements after the search entry
|
||||
this._overview.add(this._group, { y_fill: true,
|
||||
expand: true });
|
||||
|
||||
// Then account for message tray
|
||||
this._messageTrayGhost = new St.Bin({ style_class: 'message-tray-summary',
|
||||
reactive: false,
|
||||
opacity: 0,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
this._overview.add_actor(this._messageTrayGhost);
|
||||
|
||||
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
|
||||
this._relayout();
|
||||
},
|
||||
@ -337,7 +348,7 @@ const Overview = new Lang.Class({
|
||||
if (this.isDummy)
|
||||
return;
|
||||
|
||||
this._group.add_action(action);
|
||||
this._overview.add_action(action);
|
||||
},
|
||||
|
||||
_getDesktopClone: function() {
|
||||
@ -363,41 +374,27 @@ const Overview = new Lang.Class({
|
||||
this.hide();
|
||||
|
||||
let primary = Main.layoutManager.primaryMonitor;
|
||||
let rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL);
|
||||
|
||||
let contentY = Main.panel.actor.height;
|
||||
let contentHeight = primary.height - contentY - Main.messageTray.actor.height;
|
||||
|
||||
this._group.set_position(primary.x, primary.y);
|
||||
this._group.set_size(primary.width, primary.height);
|
||||
this._overview.set_position(primary.x, primary.y);
|
||||
this._overview.set_size(primary.width, primary.height);
|
||||
|
||||
this._coverPane.set_position(0, contentY);
|
||||
this._coverPane.set_size(primary.width, contentHeight);
|
||||
},
|
||||
|
||||
let searchWidth = this._searchEntry.get_width();
|
||||
let searchHeight = this._searchEntry.get_height();
|
||||
let searchX = (primary.width - searchWidth) / 2;
|
||||
let searchY = contentY + this._spacing;
|
||||
_onRestacked: function() {
|
||||
let stack = global.get_window_actors();
|
||||
let stackIndices = {};
|
||||
|
||||
let dashWidth = DASH_MAX_WIDTH;
|
||||
let dashY = searchY + searchHeight + this._spacing;
|
||||
let dashX;
|
||||
if (rtl) {
|
||||
this._dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
|
||||
dashX = primary.width;
|
||||
} else {
|
||||
dashX = 0;
|
||||
for (let i = 0; i < stack.length; i++) {
|
||||
// Use the stable sequence for an integer to use as a hash key
|
||||
stackIndices[stack[i].get_meta_window().get_stable_sequence()] = i;
|
||||
}
|
||||
|
||||
let viewX = rtl ? 0 : dashWidth + this._spacing;
|
||||
let viewY = searchY + searchHeight + this._spacing;
|
||||
let viewWidth = primary.width - dashWidth - this._spacing;
|
||||
let viewHeight = contentHeight - this._spacing - viewY;
|
||||
|
||||
this._searchEntry.set_position(searchX, searchY);
|
||||
this._dash.actor.set_position(dashX, dashY);
|
||||
this._viewSelector.actor.set_position(viewX, viewY);
|
||||
this._viewSelector.actor.set_size(viewWidth, viewHeight);
|
||||
this.emit('windows-restacked', stackIndices);
|
||||
},
|
||||
|
||||
//// Public methods ////
|
||||
@ -481,12 +478,12 @@ const Overview = new Lang.Class({
|
||||
// Disable unredirection while in the overview
|
||||
Meta.disable_unredirect_for_screen(global.screen);
|
||||
global.window_group.hide();
|
||||
this._group.show();
|
||||
this._overview.show();
|
||||
this._background.show();
|
||||
this._viewSelector.show();
|
||||
|
||||
this._group.opacity = 0;
|
||||
Tweener.addTween(this._group,
|
||||
this._overview.opacity = 0;
|
||||
Tweener.addTween(this._overview,
|
||||
{ opacity: 255,
|
||||
transition: 'easeOutQuad',
|
||||
time: ANIMATION_TIME,
|
||||
@ -578,7 +575,7 @@ const Overview = new Lang.Class({
|
||||
|
||||
if (this._shown) {
|
||||
if (!this._modal) {
|
||||
if (Main.pushModal(this._group,
|
||||
if (Main.pushModal(this._overview,
|
||||
{ keybindingMode: Main.KeybindingMode.OVERVIEW }))
|
||||
this._modal = true;
|
||||
else
|
||||
@ -586,13 +583,13 @@ const Overview = new Lang.Class({
|
||||
}
|
||||
} else if (this._shownTemporarily) {
|
||||
if (this._modal) {
|
||||
Main.popModal(this._group);
|
||||
Main.popModal(this._overview);
|
||||
this._modal = false;
|
||||
}
|
||||
global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
|
||||
} else {
|
||||
if (this._modal) {
|
||||
Main.popModal(this._group);
|
||||
Main.popModal(this._overview);
|
||||
this._modal = false;
|
||||
}
|
||||
else if (global.stage_input_mode == Shell.StageInputMode.FULLSCREEN)
|
||||
@ -610,7 +607,7 @@ const Overview = new Lang.Class({
|
||||
this._viewSelector.zoomFromOverview();
|
||||
|
||||
// Make other elements fade out.
|
||||
Tweener.addTween(this._group,
|
||||
Tweener.addTween(this._overview,
|
||||
{ opacity: 0,
|
||||
transition: 'easeOutQuad',
|
||||
time: ANIMATION_TIME,
|
||||
@ -652,7 +649,7 @@ const Overview = new Lang.Class({
|
||||
this._viewSelector.hide();
|
||||
this._desktopFade.hide();
|
||||
this._background.hide();
|
||||
this._group.hide();
|
||||
this._overview.hide();
|
||||
|
||||
this.visible = false;
|
||||
this.animationInProgress = false;
|
||||
|
@ -598,6 +598,11 @@ const AppMenuButton = new Lang.Class({
|
||||
return;
|
||||
|
||||
menu = new PopupMenu.RemoteMenu(this.actor, this._targetApp.menu, this._targetApp.action_group);
|
||||
menu.connect('activate', Lang.bind(this, function() {
|
||||
let win = this._targetApp.get_windows()[0];
|
||||
win.check_alive(global.get_current_time());
|
||||
}));
|
||||
|
||||
} else {
|
||||
if (this.menu.isDummyQuitMenu)
|
||||
return;
|
||||
@ -746,12 +751,9 @@ const ActivitiesButton = new Lang.Class({
|
||||
const PanelCorner = new Lang.Class({
|
||||
Name: 'PanelCorner',
|
||||
|
||||
_init: function(box, side) {
|
||||
_init: function(side) {
|
||||
this._side = side;
|
||||
|
||||
this._box = box;
|
||||
this._box.connect('style-changed', Lang.bind(this, this._boxStyleChanged));
|
||||
|
||||
this.actor = new St.DrawingArea({ style_class: 'panel-corner' });
|
||||
this.actor.connect('style-changed', Lang.bind(this, this._styleChanged));
|
||||
this.actor.connect('repaint', Lang.bind(this, this._repaint));
|
||||
@ -807,12 +809,12 @@ const PanelCorner = new Lang.Class({
|
||||
return children[index];
|
||||
},
|
||||
|
||||
_boxStyleChanged: function() {
|
||||
setStyleParent: function(box) {
|
||||
let side = this._side;
|
||||
|
||||
let rtlAwareContainer = this._box instanceof St.BoxLayout;
|
||||
let rtlAwareContainer = box instanceof St.BoxLayout;
|
||||
if (rtlAwareContainer &&
|
||||
this._box.get_text_direction() == Clutter.TextDirection.RTL) {
|
||||
box.get_text_direction() == Clutter.TextDirection.RTL) {
|
||||
if (this._side == St.Side.LEFT)
|
||||
side = St.Side.RIGHT;
|
||||
else if (this._side == St.Side.RIGHT)
|
||||
@ -821,9 +823,9 @@ const PanelCorner = new Lang.Class({
|
||||
|
||||
let button;
|
||||
if (side == St.Side.LEFT)
|
||||
button = this._findLeftmostButton(this._box);
|
||||
button = this._findLeftmostButton(box);
|
||||
else if (side == St.Side.RIGHT)
|
||||
button = this._findRightmostButton(this._box);
|
||||
button = this._findRightmostButton(box);
|
||||
|
||||
if (button) {
|
||||
if (this._button && this._buttonStyleChangedSignalId) {
|
||||
@ -850,7 +852,7 @@ const PanelCorner = new Lang.Class({
|
||||
|
||||
// The corner doesn't support theme transitions, so override
|
||||
// the .panel-button default
|
||||
button.style = 'transition-duration: 0';
|
||||
button.style = 'transition-duration: 0ms';
|
||||
}
|
||||
},
|
||||
|
||||
@ -863,8 +865,8 @@ const PanelCorner = new Lang.Class({
|
||||
let backgroundColor = node.get_color('-panel-corner-background-color');
|
||||
let borderColor = node.get_color('-panel-corner-border-color');
|
||||
|
||||
let noOverlap = borderColor.alpha == 0;
|
||||
let offsetY = noOverlap ? borderWidth : 0;
|
||||
let overlap = borderColor.alpha != 0;
|
||||
let offsetY = overlap ? 0 : borderWidth;
|
||||
|
||||
let cr = this.actor.get_context();
|
||||
cr.setOperator(Cairo.Operator.SOURCE);
|
||||
@ -888,17 +890,18 @@ const PanelCorner = new Lang.Class({
|
||||
Clutter.cairo_set_source_color(cr, over);
|
||||
cr.fill();
|
||||
|
||||
if (noOverlap)
|
||||
return;
|
||||
if (overlap) {
|
||||
let offset = borderWidth;
|
||||
Clutter.cairo_set_source_color(cr, backgroundColor);
|
||||
|
||||
let offset = borderWidth;
|
||||
Clutter.cairo_set_source_color(cr, backgroundColor);
|
||||
cr.save();
|
||||
cr.translate(xOffsetDirection * offset, - offset);
|
||||
cr.appendPath(savedPath);
|
||||
cr.fill();
|
||||
cr.restore();
|
||||
}
|
||||
|
||||
cr.save();
|
||||
cr.translate(xOffsetDirection * offset, - offset);
|
||||
cr.appendPath(savedPath);
|
||||
cr.fill();
|
||||
cr.restore();
|
||||
cr.$dispose();
|
||||
},
|
||||
|
||||
_styleChanged: function() {
|
||||
@ -958,17 +961,10 @@ const Panel = new Lang.Class({
|
||||
this._rightBox = new St.BoxLayout({ name: 'panelRight' });
|
||||
this.actor.add_actor(this._rightBox);
|
||||
|
||||
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL)
|
||||
this._leftCorner = new PanelCorner(this._rightBox, St.Side.LEFT);
|
||||
else
|
||||
this._leftCorner = new PanelCorner(this._leftBox, St.Side.LEFT);
|
||||
|
||||
this._leftCorner = new PanelCorner(St.Side.LEFT);
|
||||
this.actor.add_actor(this._leftCorner.actor);
|
||||
|
||||
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL)
|
||||
this._rightCorner = new PanelCorner(this._leftBox, St.Side.RIGHT);
|
||||
else
|
||||
this._rightCorner = new PanelCorner(this._rightBox, St.Side.RIGHT);
|
||||
this._rightCorner = new PanelCorner(St.Side.RIGHT);
|
||||
this.actor.add_actor(this._rightCorner.actor);
|
||||
|
||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||
@ -1141,17 +1137,13 @@ const Panel = new Lang.Class({
|
||||
this._sessionStyle = Main.sessionMode.panelStyle;
|
||||
if (this._sessionStyle)
|
||||
this._addStyleClassName(this._sessionStyle);
|
||||
},
|
||||
|
||||
_initBox: function(elements, box) {
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
let role = elements[i];
|
||||
let constructor = PANEL_ITEM_IMPLEMENTATIONS[role];
|
||||
if (!constructor) {
|
||||
// panel icon is not supported (can happen for
|
||||
// bluetooth or network)
|
||||
continue;
|
||||
}
|
||||
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL) {
|
||||
this._leftCorner.setStyleParent(this._rightBox);
|
||||
this._rightCorner.setStyleParent(this._leftBox);
|
||||
} else {
|
||||
this._leftCorner.setStyleParent(this._leftBox);
|
||||
this._rightCorner.setStyleParent(this._rightBox);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -244,18 +244,21 @@ const SystemStatusButton = new Lang.Class({
|
||||
this.setIcon(iconName);
|
||||
},
|
||||
|
||||
get icons() {
|
||||
return this._box.get_children();
|
||||
},
|
||||
|
||||
addIcon: function(gicon) {
|
||||
let icon = new St.Icon({ gicon: gicon,
|
||||
style_class: 'system-status-icon' });
|
||||
this._box.add_actor(icon);
|
||||
|
||||
this.emit('icons-changed');
|
||||
|
||||
return icon;
|
||||
},
|
||||
|
||||
setIcon: function(iconName) {
|
||||
// Need to first add a NULL GIcon and then set icon_name, to ensure
|
||||
// compatibility with -symbolic fallbacks
|
||||
|
||||
if (!this.mainIcon)
|
||||
this.mainIcon = this.addIcon(null);
|
||||
this.mainIcon.icon_name = iconName;
|
||||
|
@ -1,6 +1,5 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Cairo = imports.cairo;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
@ -15,6 +14,7 @@ const BoxPointer = imports.ui.boxpointer;
|
||||
const GrabHelper = imports.ui.grabHelper;
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const Separator = imports.ui.separator;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const SLIDER_SCROLL_STEP = 0.05; /* Slider scrolling step in % */
|
||||
@ -207,6 +207,7 @@ const PopupBaseMenuItem = new Lang.Class({
|
||||
color.alpha / 255);
|
||||
cr.arc(width / 2, height / 2, width / 3, 0, 2 * Math.PI);
|
||||
cr.fill();
|
||||
cr.$dispose();
|
||||
},
|
||||
|
||||
// This returns column widths in logical order (i.e. from the dot
|
||||
@ -405,29 +406,8 @@ const PopupSeparatorMenuItem = new Lang.Class({
|
||||
this.parent({ reactive: false,
|
||||
can_focus: false});
|
||||
|
||||
this._drawingArea = new St.DrawingArea({ style_class: 'popup-separator-menu-item' });
|
||||
this.addActor(this._drawingArea, { span: -1, expand: true });
|
||||
this._drawingArea.connect('repaint', Lang.bind(this, this._onRepaint));
|
||||
},
|
||||
|
||||
_onRepaint: function(area) {
|
||||
let cr = area.get_context();
|
||||
let themeNode = area.get_theme_node();
|
||||
let [width, height] = area.get_surface_size();
|
||||
let margin = themeNode.get_length('-margin-horizontal');
|
||||
let gradientHeight = themeNode.get_length('-gradient-height');
|
||||
let startColor = themeNode.get_color('-gradient-start');
|
||||
let endColor = themeNode.get_color('-gradient-end');
|
||||
|
||||
let gradientWidth = (width - margin * 2);
|
||||
let gradientOffset = (height - gradientHeight) / 2;
|
||||
let pattern = new Cairo.LinearGradient(margin, gradientOffset, width - margin, gradientOffset + gradientHeight);
|
||||
pattern.addColorStopRGBA(0, startColor.red / 255, startColor.green / 255, startColor.blue / 255, startColor.alpha / 255);
|
||||
pattern.addColorStopRGBA(0.5, endColor.red / 255, endColor.green / 255, endColor.blue / 255, endColor.alpha / 255);
|
||||
pattern.addColorStopRGBA(1, startColor.red / 255, startColor.green / 255, startColor.blue / 255, startColor.alpha / 255);
|
||||
cr.setSource(pattern);
|
||||
cr.rectangle(margin, gradientOffset, gradientWidth, gradientHeight);
|
||||
cr.fill();
|
||||
this._separator = new Separator.HorizontalSeparator({ style_class: 'popup-separator-menu-item' });
|
||||
this.addActor(this._separator.actor, { span: -1, expand: true });
|
||||
}
|
||||
});
|
||||
|
||||
@ -625,6 +605,7 @@ const PopupSliderMenuItem = new Lang.Class({
|
||||
color.alpha / 255);
|
||||
cr.arc(handleX, handleY, handleRadius, 0, 2 * Math.PI);
|
||||
cr.fill();
|
||||
cr.$dispose();
|
||||
},
|
||||
|
||||
_startDragging: function(actor, event) {
|
||||
@ -657,20 +638,34 @@ const PopupSliderMenuItem = new Lang.Class({
|
||||
return true;
|
||||
},
|
||||
|
||||
_onScrollEvent: function (actor, event) {
|
||||
scroll: function(event) {
|
||||
let direction = event.get_scroll_direction();
|
||||
let delta;
|
||||
|
||||
if (event.is_pointer_emulated())
|
||||
return;
|
||||
|
||||
if (direction == Clutter.ScrollDirection.DOWN) {
|
||||
this._value = Math.max(0, this._value - SLIDER_SCROLL_STEP);
|
||||
}
|
||||
else if (direction == Clutter.ScrollDirection.UP) {
|
||||
this._value = Math.min(1, this._value + SLIDER_SCROLL_STEP);
|
||||
delta = -SLIDER_SCROLL_STEP;
|
||||
} else if (direction == Clutter.ScrollDirection.UP) {
|
||||
delta = +SLIDER_SCROLL_STEP;
|
||||
} else if (direction == Clutter.ScrollDirection.SMOOTH) {
|
||||
let [dx, dy] = event.get_scroll_delta();
|
||||
// Even though the slider is horizontal, use dy to match
|
||||
// the UP/DOWN above.
|
||||
delta = -dy / 10;
|
||||
}
|
||||
|
||||
this._value = Math.min(Math.max(0, this._value + delta), 1);
|
||||
|
||||
this._slider.queue_repaint();
|
||||
this.emit('value-changed', this._value);
|
||||
},
|
||||
|
||||
_onScrollEvent: function(actor, event) {
|
||||
this.scroll(event);
|
||||
},
|
||||
|
||||
_motionEvent: function(actor, event) {
|
||||
let absX, absY;
|
||||
[absX, absY] = event.get_coords();
|
||||
|
@ -1,9 +1,11 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const GdkPixbuf = imports.gi.GdkPixbuf;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const FileUtils = imports.misc.fileUtils;
|
||||
const Search = imports.ui.search;
|
||||
@ -50,6 +52,7 @@ const SearchProvider2Iface = <interface name="org.gnome.Shell.SearchProvider2">
|
||||
</method>
|
||||
<method name="LaunchSearch">
|
||||
<arg type="as" direction="in" />
|
||||
<arg type="u" direction="in" />
|
||||
</method>
|
||||
</interface>;
|
||||
|
||||
@ -166,7 +169,6 @@ function remoteProvidersLoaded(loadState) {
|
||||
|
||||
const RemoteSearchProvider = new Lang.Class({
|
||||
Name: 'RemoteSearchProvider',
|
||||
Extends: Search.SearchProvider,
|
||||
|
||||
_init: function(appInfo, dbusName, dbusPath, proxyType) {
|
||||
if (!proxyType)
|
||||
@ -175,7 +177,10 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
this.proxy = new proxyType(Gio.DBus.session,
|
||||
dbusName, dbusPath, Lang.bind(this, this._onProxyConstructed));
|
||||
|
||||
this.parent(appInfo.get_name().toUpperCase(), appInfo, true);
|
||||
this.appInfo = appInfo;
|
||||
this.id = appInfo.get_id();
|
||||
this.isRemoteProvider = true;
|
||||
|
||||
this._cancellable = new Gio.Cancellable();
|
||||
},
|
||||
|
||||
@ -184,19 +189,17 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
},
|
||||
|
||||
createIcon: function(size, meta) {
|
||||
let gicon;
|
||||
if (meta['gicon']) {
|
||||
return new St.Icon({ gicon: Gio.icon_new_for_string(meta['gicon']),
|
||||
icon_size: size });
|
||||
gicon = Gio.icon_new_for_string(meta['gicon']);
|
||||
} else if (meta['icon-data']) {
|
||||
let [width, height, rowStride, hasAlpha,
|
||||
bitsPerSample, nChannels, data] = meta['icon-data'];
|
||||
let textureCache = St.TextureCache.get_default();
|
||||
return textureCache.load_from_raw(data, hasAlpha,
|
||||
width, height, rowStride, size);
|
||||
gicon = Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha,
|
||||
bitsPerSample, width, height, rowStride);
|
||||
}
|
||||
|
||||
// Ugh, but we want to fall back to something ...
|
||||
return new St.Icon({ icon_name: 'text-x-generic',
|
||||
return new St.Icon({ gicon: gicon,
|
||||
icon_size: size });
|
||||
},
|
||||
|
||||
@ -290,6 +293,6 @@ const RemoteSearchProvider2 = new Lang.Class({
|
||||
},
|
||||
|
||||
launchSearch: function(terms) {
|
||||
this.proxy.LaunchSearchRemote(terms);
|
||||
this.proxy.LaunchSearchRemote(terms, global.get_current_time());
|
||||
}
|
||||
});
|
||||
|
@ -25,6 +25,7 @@ const Util = imports.misc.util;
|
||||
|
||||
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
|
||||
const LOCK_ENABLED_KEY = 'lock-enabled';
|
||||
const LOCK_DELAY_KEY = 'lock-delay';
|
||||
|
||||
const CURTAIN_SLIDE_TIME = 0.3;
|
||||
// fraction of screen height the arrow must reach before completing
|
||||
@ -636,7 +637,11 @@ const ScreenShield = new Lang.Class({
|
||||
let lightboxWasShown = this._lightbox.shown;
|
||||
this._lightbox.hide();
|
||||
|
||||
let shouldLock = lightboxWasShown && this._settings.get_boolean(LOCK_ENABLED_KEY);
|
||||
// GLib.get_monotonic_time() returns microseconds, convert to seconds
|
||||
let elapsedTime = (GLib.get_monotonic_time() - this._activationTime) / 1000000;
|
||||
let shouldLock = lightboxWasShown &&
|
||||
this._settings.get_boolean(LOCK_ENABLED_KEY) &&
|
||||
(elapsedTime >= this._settings.get_uint(LOCK_DELAY_KEY));
|
||||
if (shouldLock || this._isLocked) {
|
||||
this.lock(false);
|
||||
} else if (this._isActive) {
|
||||
@ -755,6 +760,10 @@ const ScreenShield = new Lang.Class({
|
||||
},
|
||||
|
||||
_resetLockScreen: function(animateLockScreen, animateLockDialog) {
|
||||
if (this._lockScreenState == MessageTray.State.SHOWING ||
|
||||
this._lockScreenState == MessageTray.State.SHOWN)
|
||||
return;
|
||||
|
||||
this._ensureLockScreen();
|
||||
this._lockDialogGroup.scale_x = 1;
|
||||
this._lockDialogGroup.scale_y = 1;
|
||||
|
281
js/ui/screenshot.js
Normal file
281
js/ui/screenshot.js
Normal file
@ -0,0 +1,281 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gdk = imports.gi.Gdk;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const ScreenshotIface = <interface name="org.gnome.Shell.Screenshot">
|
||||
<method name="ScreenshotArea">
|
||||
<arg type="i" direction="in" name="x"/>
|
||||
<arg type="i" direction="in" name="y"/>
|
||||
<arg type="i" direction="in" name="width"/>
|
||||
<arg type="i" direction="in" name="height"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
<arg type="s" direction="out" name="filename_used"/>
|
||||
</method>
|
||||
<method name="ScreenshotWindow">
|
||||
<arg type="b" direction="in" name="include_frame"/>
|
||||
<arg type="b" direction="in" name="include_cursor"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
<arg type="s" direction="out" name="filename_used"/>
|
||||
</method>
|
||||
<method name="Screenshot">
|
||||
<arg type="b" direction="in" name="include_cursor"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
<arg type="s" direction="out" name="filename_used"/>
|
||||
</method>
|
||||
<method name="SelectArea">
|
||||
<arg type="i" direction="out" name="x"/>
|
||||
<arg type="i" direction="out" name="y"/>
|
||||
<arg type="i" direction="out" name="width"/>
|
||||
<arg type="i" direction="out" name="height"/>
|
||||
</method>
|
||||
<method name="FlashArea">
|
||||
<arg type="i" direction="in" name="x"/>
|
||||
<arg type="i" direction="in" name="y"/>
|
||||
<arg type="i" direction="in" name="width"/>
|
||||
<arg type="i" direction="in" name="height"/>
|
||||
</method>
|
||||
</interface>;
|
||||
|
||||
const ScreenshotService = new Lang.Class({
|
||||
Name: 'ScreenshotService',
|
||||
|
||||
_init: function() {
|
||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenshotIface, this);
|
||||
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/Screenshot');
|
||||
|
||||
Gio.DBus.session.own_name('org.gnome.Shell.Screenshot', Gio.BusNameOwnerFlags.REPLACE, null, null);
|
||||
},
|
||||
|
||||
_onScreenshotComplete: function(obj, result, area, filenameUsed, flash, invocation) {
|
||||
if (flash && result) {
|
||||
let flashspot = new Flashspot(area);
|
||||
flashspot.fire();
|
||||
}
|
||||
|
||||
let retval = GLib.Variant.new('(bs)', [result, filenameUsed]);
|
||||
invocation.return_value(retval);
|
||||
},
|
||||
|
||||
ScreenshotAreaAsync : function (params, invocation) {
|
||||
let [x, y, width, height, flash, filename, callback] = params;
|
||||
let screenshot = new Shell.Screenshot();
|
||||
screenshot.screenshot_area (x, y, width, height, filename,
|
||||
Lang.bind(this, this._onScreenshotComplete,
|
||||
flash, invocation));
|
||||
},
|
||||
|
||||
ScreenshotWindowAsync : function (params, invocation) {
|
||||
let [include_frame, include_cursor, flash, filename] = params;
|
||||
let screenshot = new Shell.Screenshot();
|
||||
screenshot.screenshot_window (include_frame, include_cursor, filename,
|
||||
Lang.bind(this, this._onScreenshotComplete,
|
||||
flash, invocation));
|
||||
},
|
||||
|
||||
ScreenshotAsync : function (params, invocation) {
|
||||
let [include_cursor, flash, filename] = params;
|
||||
let screenshot = new Shell.Screenshot();
|
||||
screenshot.screenshot(include_cursor, filename,
|
||||
Lang.bind(this, this._onScreenshotComplete,
|
||||
flash, invocation));
|
||||
},
|
||||
|
||||
SelectAreaAsync: function (params, invocation) {
|
||||
let selectArea = new SelectArea();
|
||||
selectArea.show();
|
||||
selectArea.connect('finished', Lang.bind(this,
|
||||
function(selectArea, areaRectangle) {
|
||||
if (areaRectangle) {
|
||||
let retval = GLib.Variant.new('(iiii)',
|
||||
[areaRectangle.x, areaRectangle.y,
|
||||
areaRectangle.width, areaRectangle.height]);
|
||||
invocation.return_value(retval);
|
||||
} else {
|
||||
invocation.return_error_literal(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
|
||||
"Operation was cancelled");
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
FlashArea: function(x, y, width, height) {
|
||||
let flashspot = new Flashspot({ x : x, y : y, width: width, height: height});
|
||||
flashspot.fire();
|
||||
}
|
||||
});
|
||||
|
||||
const SelectArea = new Lang.Class({
|
||||
Name: 'SelectArea',
|
||||
|
||||
_init: function() {
|
||||
this._startX = -1;
|
||||
this._startY = -1;
|
||||
this._lastX = 0;
|
||||
this._lastY = 0;
|
||||
|
||||
this._initRubberbandColors();
|
||||
|
||||
this._group = new St.Widget({ visible: false,
|
||||
reactive: true,
|
||||
x: 0,
|
||||
y: 0 });
|
||||
Main.uiGroup.add_actor(this._group);
|
||||
|
||||
this._group.connect('button-press-event',
|
||||
Lang.bind(this, this._onButtonPress));
|
||||
this._group.connect('button-release-event',
|
||||
Lang.bind(this, this._onButtonRelease));
|
||||
this._group.connect('key-press-event',
|
||||
Lang.bind(this, this._onKeyPress));
|
||||
this._group.connect('motion-event',
|
||||
Lang.bind(this, this._onMotionEvent));
|
||||
|
||||
let constraint = new Clutter.BindConstraint({ source: global.stage,
|
||||
coordinate: Clutter.BindCoordinate.ALL });
|
||||
this._group.add_constraint(constraint);
|
||||
|
||||
this._rubberband = new Clutter.Rectangle({ color: this._background,
|
||||
has_border: true,
|
||||
border_width: 1,
|
||||
border_color: this._border });
|
||||
this._group.add_actor(this._rubberband);
|
||||
},
|
||||
|
||||
show: function() {
|
||||
if (!Main.pushModal(this._group) || this._group.visible)
|
||||
return;
|
||||
|
||||
global.set_cursor(Shell.Cursor.CROSSHAIR);
|
||||
this._group.visible = true;
|
||||
},
|
||||
|
||||
_initRubberbandColors: function() {
|
||||
function colorFromRGBA(rgba) {
|
||||
return new Clutter.Color({ red: rgba.red * 255,
|
||||
green: rgba.green * 255,
|
||||
blue: rgba.blue * 255,
|
||||
alpha: rgba.alpha * 255 });
|
||||
}
|
||||
|
||||
let path = new Gtk.WidgetPath();
|
||||
path.append_type(Gtk.IconView);
|
||||
|
||||
let context = new Gtk.StyleContext();
|
||||
context.set_path(path);
|
||||
context.add_class('rubberband');
|
||||
|
||||
this._background = colorFromRGBA(context.get_background_color(Gtk.StateFlags.NORMAL));
|
||||
this._border = colorFromRGBA(context.get_border_color(Gtk.StateFlags.NORMAL));
|
||||
},
|
||||
|
||||
_getGeometry: function() {
|
||||
return { x: Math.min(this._startX, this._lastX),
|
||||
y: Math.min(this._startY, this._lastY),
|
||||
width: Math.abs(this._startX - this._lastX),
|
||||
height: Math.abs(this._startY - this._lastY) };
|
||||
},
|
||||
|
||||
_onKeyPress: function(actor, event) {
|
||||
if (event.get_key_symbol() == Clutter.Escape)
|
||||
this._destroy(null, false);
|
||||
|
||||
return;
|
||||
},
|
||||
|
||||
_onMotionEvent: function(actor, event) {
|
||||
if (this._startX == -1 || this._startY == -1)
|
||||
return false;
|
||||
|
||||
[this._lastX, this._lastY] = event.get_coords();
|
||||
let geometry = this._getGeometry();
|
||||
|
||||
this._rubberband.set_position(geometry.x, geometry.y);
|
||||
this._rubberband.set_size(geometry.width, geometry.height);
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_onButtonPress: function(actor, event) {
|
||||
[this._startX, this._startY] = event.get_coords();
|
||||
this._rubberband.set_position(this._startX, this._startY);
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_onButtonRelease: function(actor, event) {
|
||||
this._destroy(this._getGeometry(), true);
|
||||
return false;
|
||||
},
|
||||
|
||||
_destroy: function(geometry, fade) {
|
||||
Tweener.addTween(this._group,
|
||||
{ opacity: 0,
|
||||
time: fade ? 0.2 : 0,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this,
|
||||
function() {
|
||||
Main.popModal(this._group);
|
||||
this._group.destroy();
|
||||
global.unset_cursor();
|
||||
|
||||
this.emit('finished', geometry);
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(SelectArea.prototype);
|
||||
|
||||
const FLASHSPOT_ANIMATION_TIME = 0.25; // seconds
|
||||
|
||||
const Flashspot = new Lang.Class({
|
||||
Name: 'Flashspot',
|
||||
Extends: Lightbox.Lightbox,
|
||||
|
||||
_init: function(area) {
|
||||
this.parent(Main.uiGroup, { inhibitEvents: true,
|
||||
width: area.width,
|
||||
height: area.height });
|
||||
|
||||
this.actor.style_class = 'flashspot';
|
||||
this.actor.set_position(area.x, area.y);
|
||||
},
|
||||
|
||||
fire: function() {
|
||||
this.actor.opacity = 0;
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 255,
|
||||
time: FLASHSPOT_ANIMATION_TIME,
|
||||
transition: 'linear',
|
||||
onComplete: Lang.bind(this, this._onFireShowComplete)
|
||||
});
|
||||
this.actor.show();
|
||||
},
|
||||
|
||||
_onFireShowComplete: function() {
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 0,
|
||||
time: FLASHSPOT_ANIMATION_TIME,
|
||||
transition: 'linear',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this.destroy();
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
181
js/ui/search.js
181
js/ui/search.js
@ -1,187 +1,10 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const FileUtils = imports.misc.fileUtils;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const SEARCH_PROVIDERS_SCHEMA = 'org.gnome.desktop.search-providers';
|
||||
|
||||
// Not currently referenced by the search API, but
|
||||
// this enumeration can be useful for provider
|
||||
// implementations.
|
||||
const MatchType = {
|
||||
NONE: 0,
|
||||
SUBSTRING: 1,
|
||||
PREFIX: 2
|
||||
};
|
||||
|
||||
const SearchResultDisplay = new Lang.Class({
|
||||
Name: 'SearchResultDisplay',
|
||||
|
||||
_init: function(provider) {
|
||||
this.provider = provider;
|
||||
this.actor = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* renderResults:
|
||||
* @results: List of identifier strings
|
||||
* @terms: List of search term strings
|
||||
*
|
||||
* Display the given search matches which resulted
|
||||
* from the given terms. It's expected that not
|
||||
* all results will fit in the space for the container
|
||||
* actor; in this case, show as many as makes sense
|
||||
* for your result type.
|
||||
*
|
||||
* The terms are useful for search match highlighting.
|
||||
*/
|
||||
renderResults: function(results, terms) {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
/**
|
||||
* clear:
|
||||
* Remove all results from this display.
|
||||
*/
|
||||
clear: function() {
|
||||
this.actor.destroy_all_children();
|
||||
},
|
||||
|
||||
/**
|
||||
* getVisibleResultCount:
|
||||
*
|
||||
* Returns: The number of actors visible.
|
||||
*/
|
||||
getVisibleResultCount: function() {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* SearchProvider:
|
||||
*
|
||||
* Subclass this object to add a new result type
|
||||
* to the search system, then call registerProvider()
|
||||
* in SearchSystem with an instance.
|
||||
* Search is asynchronous and uses the
|
||||
* getInitialResultSet()/getSubsearchResultSet() methods.
|
||||
*/
|
||||
const SearchProvider = new Lang.Class({
|
||||
Name: 'SearchProvider',
|
||||
|
||||
_init: function(title, appInfo, isRemoteProvider) {
|
||||
this.title = title;
|
||||
this.appInfo = appInfo;
|
||||
this.searchSystem = null;
|
||||
this.isRemoteProvider = !!isRemoteProvider;
|
||||
this.canLaunchSearch = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* getInitialResultSet:
|
||||
* @terms: Array of search terms, treated as logical AND
|
||||
*
|
||||
* Called when the user first begins a search (most likely
|
||||
* therefore a single term of length one or two), or when
|
||||
* a new term is added.
|
||||
*
|
||||
* Should "return" an array of result identifier strings representing
|
||||
* items which match the given search terms. This
|
||||
* is expected to be a substring match on the metadata for a given
|
||||
* item. Ordering of returned results is up to the discretion of the provider,
|
||||
* but you should follow these heruistics:
|
||||
*
|
||||
* * Put items where the term matches multiple criteria (e.g. name and
|
||||
* description) before single matches
|
||||
* * Put items which match on a prefix before non-prefix substring matches
|
||||
*
|
||||
* We say "return" above, but in order to make the query asynchronous, use
|
||||
* this.searchSystem.pushResults();. The return value should be ignored.
|
||||
*
|
||||
* This function should be fast; do not perform unindexed full-text searches
|
||||
* or network queries.
|
||||
*/
|
||||
getInitialResultSet: function(terms) {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
/**
|
||||
* getSubsearchResultSet:
|
||||
* @previousResults: Array of item identifiers
|
||||
* @newTerms: Updated search terms
|
||||
*
|
||||
* Called when a search is performed which is a "subsearch" of
|
||||
* the previous search; i.e. when every search term has exactly
|
||||
* one corresponding term in the previous search which is a prefix
|
||||
* of the new term.
|
||||
*
|
||||
* This allows search providers to only search through the previous
|
||||
* result set, rather than possibly performing a full re-query.
|
||||
*
|
||||
* Similar to getInitialResultSet, the return value for this will
|
||||
* be ignored; use this.searchSystem.pushResults();.
|
||||
*/
|
||||
getSubsearchResultSet: function(previousResults, newTerms) {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
/**
|
||||
* getResultMetas:
|
||||
* @ids: Result identifier strings
|
||||
*
|
||||
* Call callback with array of objects with 'id', 'name', (both strings) and
|
||||
* 'createIcon' (function(size) returning a Clutter.Texture) properties
|
||||
* with the same number of members as @ids
|
||||
*/
|
||||
getResultMetas: function(ids, callback) {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
/**
|
||||
* createResultActor:
|
||||
* @resultMeta: Object with result metadata
|
||||
* @terms: Array of search terms, should be used for highlighting
|
||||
*
|
||||
* Search providers may optionally override this to render a
|
||||
* particular serch result in a custom fashion. The default
|
||||
* implementation will show the icon next to the name.
|
||||
*
|
||||
* The actor should be an instance of St.Widget, with the style class
|
||||
* 'search-result-content'.
|
||||
*/
|
||||
createResultActor: function(resultMeta, terms) {
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* activateResult:
|
||||
* @id: Result identifier string
|
||||
*
|
||||
* Called when the user chooses a given result.
|
||||
*/
|
||||
activateResult: function(id) {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
/**
|
||||
* launchSearch:
|
||||
* @terms: Current search terms
|
||||
*
|
||||
* Called when the user clicks the provider icon.
|
||||
*/
|
||||
launchSearch: function(terms) {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(SearchProvider.prototype);
|
||||
|
||||
const SearchSystem = new Lang.Class({
|
||||
Name: 'SearchSystem',
|
||||
|
||||
@ -273,7 +96,7 @@ const SearchSystem = new Lang.Class({
|
||||
results.push([provider, []]);
|
||||
provider.getSubsearchResultSet(previousResults, terms);
|
||||
} catch (error) {
|
||||
log('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
|
||||
log('A ' + error.name + ' has occured in ' + provider.id + ': ' + error.message);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -283,7 +106,7 @@ const SearchSystem = new Lang.Class({
|
||||
results.push([provider, []]);
|
||||
provider.getInitialResultSet(terms);
|
||||
} catch (error) {
|
||||
log('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
|
||||
log('A ' + error.name + ' has occured in ' + provider.id + ': ' + error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,10 +11,11 @@ const DND = imports.ui.dnd;
|
||||
const IconGrid = imports.ui.iconGrid;
|
||||
const Main = imports.ui.main;
|
||||
const Overview = imports.ui.overview;
|
||||
const Separator = imports.ui.separator;
|
||||
const Search = imports.ui.search;
|
||||
|
||||
const MAX_SEARCH_RESULTS_ROWS = 1;
|
||||
|
||||
const MAX_LIST_SEARCH_RESULTS_ROWS = 3;
|
||||
const MAX_GRID_SEARCH_RESULTS_ROWS = 1;
|
||||
|
||||
const SearchResult = new Lang.Class({
|
||||
Name: 'SearchResult',
|
||||
@ -23,33 +24,102 @@ const SearchResult = new Lang.Class({
|
||||
this.provider = provider;
|
||||
this.metaInfo = metaInfo;
|
||||
this.terms = terms;
|
||||
this.actor = new St.Button({ style_class: 'search-result',
|
||||
reactive: true,
|
||||
|
||||
this.actor = new St.Button({ reactive: true,
|
||||
can_focus: true,
|
||||
track_hover: true,
|
||||
x_align: St.Align.START,
|
||||
y_fill: true });
|
||||
|
||||
this.actor._delegate = this;
|
||||
this._dragActorSource = null;
|
||||
this.actor.connect('clicked', Lang.bind(this, this.activate));
|
||||
},
|
||||
|
||||
activate: function() {
|
||||
this.provider.activateResult(this.metaInfo.id, this.terms);
|
||||
Main.overview.toggle();
|
||||
},
|
||||
|
||||
setSelected: function(selected) {
|
||||
if (selected)
|
||||
this.actor.add_style_pseudo_class('selected');
|
||||
else
|
||||
this.actor.remove_style_pseudo_class('selected');
|
||||
}
|
||||
});
|
||||
|
||||
const ListSearchResult = new Lang.Class({
|
||||
Name: 'ListSearchResult',
|
||||
Extends: SearchResult,
|
||||
|
||||
ICON_SIZE: 64,
|
||||
|
||||
_init: function(provider, metaInfo, terms) {
|
||||
this.parent(provider, metaInfo, terms);
|
||||
|
||||
this.actor.style_class = 'list-search-result';
|
||||
this.actor.x_fill = true;
|
||||
|
||||
let content = new St.BoxLayout({ style_class: 'list-search-result-content',
|
||||
vertical: false });
|
||||
this.actor.set_child(content);
|
||||
|
||||
// An icon for, or thumbnail of, content
|
||||
let icon = this.metaInfo['createIcon'](this.ICON_SIZE);
|
||||
if (icon) {
|
||||
content.add(icon);
|
||||
}
|
||||
|
||||
let details = new St.BoxLayout({ vertical: true });
|
||||
content.add(details, { x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.MIDDLE });
|
||||
|
||||
let title = new St.Label({ style_class: 'list-search-result-title',
|
||||
text: this.metaInfo['name'] })
|
||||
details.add(title, { x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
|
||||
// TODO: should highlight terms in the description here
|
||||
if (this.metaInfo['description']) {
|
||||
let description = new St.Label({ style_class: 'list-search-result-description',
|
||||
text: '"' + this.metaInfo['description'] + '"' });
|
||||
details.add(description, { x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.END });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const GridSearchResult = new Lang.Class({
|
||||
Name: 'GridSearchResult',
|
||||
Extends: SearchResult,
|
||||
|
||||
_init: function(provider, metaInfo, terms) {
|
||||
this.parent(provider, metaInfo, terms);
|
||||
|
||||
this.actor.style_class = 'grid-search-result';
|
||||
|
||||
let content = provider.createResultActor(metaInfo, terms);
|
||||
let dragSource = null;
|
||||
|
||||
if (content == null) {
|
||||
content = new St.Bin({ style_class: 'search-result-content',
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
track_hover: true,
|
||||
accessible_role: Atk.Role.PUSH_BUTTON });
|
||||
content = new St.Bin();
|
||||
let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
|
||||
{ createIcon: this.metaInfo['createIcon'] });
|
||||
content.set_child(icon.actor);
|
||||
this._dragActorSource = icon.icon;
|
||||
content.label_actor = icon.label;
|
||||
dragSource = icon.icon;
|
||||
} else {
|
||||
if (content._delegate && content._delegate.getDragActorSource)
|
||||
this._dragActorSource = content._delegate.getDragActorSource();
|
||||
dragSource = content._delegate.getDragActorSource();
|
||||
}
|
||||
this._content = content;
|
||||
this.actor.set_child(content);
|
||||
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onResultClicked));
|
||||
this.actor.set_child(content);
|
||||
|
||||
let draggable = DND.makeDraggable(this.actor);
|
||||
draggable.connect('drag-begin',
|
||||
@ -64,32 +134,18 @@ const SearchResult = new Lang.Class({
|
||||
Lang.bind(this, function() {
|
||||
Main.overview.endItemDrag(this);
|
||||
}));
|
||||
},
|
||||
|
||||
setSelected: function(selected) {
|
||||
if (selected)
|
||||
this._content.add_style_pseudo_class('selected');
|
||||
else
|
||||
this._content.remove_style_pseudo_class('selected');
|
||||
},
|
||||
|
||||
activate: function() {
|
||||
this.provider.activateResult(this.metaInfo.id, this.terms);
|
||||
Main.overview.toggle();
|
||||
},
|
||||
|
||||
_onResultClicked: function(actor) {
|
||||
this.activate();
|
||||
if (!dragSource)
|
||||
// not exactly right, but alignment problems are hard to notice
|
||||
dragSource = content;
|
||||
this._dragActorSource = dragSource;
|
||||
},
|
||||
|
||||
getDragActorSource: function() {
|
||||
if (this._dragActorSource)
|
||||
return this._dragActorSource;
|
||||
// not exactly right, but alignment problems are hard to notice
|
||||
return this._content;
|
||||
return this._dragActorSource;
|
||||
},
|
||||
|
||||
getDragActor: function(stageX, stageY) {
|
||||
getDragActor: function() {
|
||||
return this.metaInfo['createIcon'](Main.overview.dashIconSize);
|
||||
},
|
||||
|
||||
@ -101,47 +157,48 @@ const SearchResult = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
const ListSearchResults = new Lang.Class({
|
||||
Name: 'ListSearchResults',
|
||||
|
||||
const GridSearchResults = new Lang.Class({
|
||||
Name: 'GridSearchResults',
|
||||
Extends: Search.SearchResultDisplay,
|
||||
_init: function(provider) {
|
||||
this.provider = provider;
|
||||
|
||||
_init: function(provider, grid) {
|
||||
this.parent(provider);
|
||||
|
||||
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);
|
||||
this._width = 0;
|
||||
this.actor.connect('notify::width', Lang.bind(this, function() {
|
||||
this._width = this.actor.width;
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
|
||||
let results = this.getResultsForDisplay();
|
||||
if (results.length == 0)
|
||||
return;
|
||||
|
||||
provider.getResultMetas(results, Lang.bind(this, this.renderResults));
|
||||
this.actor = new St.BoxLayout({ style_class: 'search-section-content' });
|
||||
this.providerIcon = new ProviderIcon(provider);
|
||||
this.providerIcon.connect('clicked', Lang.bind(this,
|
||||
function() {
|
||||
provider.launchSearch(this._terms);
|
||||
Main.overview.toggle();
|
||||
}));
|
||||
}));
|
||||
|
||||
this.actor.add(this.providerIcon, { x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
|
||||
this._content = new St.BoxLayout({ style_class: 'list-search-results',
|
||||
vertical: true });
|
||||
this.actor.add_actor(this._content);
|
||||
|
||||
this._notDisplayedResult = [];
|
||||
this._terms = [];
|
||||
this._pendingClear = false;
|
||||
},
|
||||
|
||||
getResultsForDisplay: function() {
|
||||
let alreadyVisible = this._pendingClear ? 0 : this._grid.visibleItemsCount();
|
||||
let canDisplay = this._grid.childrenInRow(this._width) * this._grid.getRowLimit()
|
||||
- alreadyVisible;
|
||||
let alreadyVisible = this._pendingClear ? 0 : this.getVisibleResultCount();
|
||||
let canDisplay = MAX_LIST_SEARCH_RESULTS_ROWS - alreadyVisible;
|
||||
|
||||
let numResults = Math.min(this._notDisplayedResult.length, canDisplay);
|
||||
|
||||
return this._notDisplayedResult.splice(0, numResults);
|
||||
let newResults = this._notDisplayedResult.splice(0, canDisplay);
|
||||
return newResults;
|
||||
},
|
||||
|
||||
getVisibleResultCount: function() {
|
||||
return this._grid.visibleItemsCount();
|
||||
return this._content.get_n_children();
|
||||
},
|
||||
|
||||
hasMoreResults: function() {
|
||||
return this._notDisplayedResult.length > 0;
|
||||
},
|
||||
|
||||
setResults: function(results, terms) {
|
||||
@ -153,7 +210,68 @@ const GridSearchResults = new Lang.Class({
|
||||
|
||||
renderResults: function(metas) {
|
||||
for (let i = 0; i < metas.length; i++) {
|
||||
let display = new SearchResult(this.provider, metas[i], this._terms);
|
||||
let display = new ListSearchResult(this.provider, metas[i], this._terms);
|
||||
this._content.add_actor(display.actor);
|
||||
}
|
||||
},
|
||||
|
||||
clear: function () {
|
||||
this._content.destroy_all_children();
|
||||
this._pendingClear = false;
|
||||
},
|
||||
|
||||
getFirstResult: function() {
|
||||
if (this.getVisibleResultCount() > 0)
|
||||
return this._content.get_child_at_index(0)._delegate;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
const GridSearchResults = new Lang.Class({
|
||||
Name: 'GridSearchResults',
|
||||
|
||||
_init: function(provider) {
|
||||
this.provider = provider;
|
||||
|
||||
this._grid = new IconGrid.IconGrid({ rowLimit: MAX_GRID_SEARCH_RESULTS_ROWS,
|
||||
xAlign: St.Align.START });
|
||||
this.actor = new St.Bin({ x_align: St.Align.MIDDLE });
|
||||
|
||||
this.actor.set_child(this._grid.actor);
|
||||
|
||||
this._notDisplayedResult = [];
|
||||
this._terms = [];
|
||||
this._pendingClear = false;
|
||||
},
|
||||
|
||||
getResultsForDisplay: function() {
|
||||
let alreadyVisible = this._pendingClear ? 0 : this._grid.visibleItemsCount();
|
||||
let canDisplay = this._grid.childrenInRow(this.actor.width) * this._grid.getRowLimit()
|
||||
- alreadyVisible;
|
||||
|
||||
let newResults = this._notDisplayedResult.splice(0, canDisplay);
|
||||
return newResults;
|
||||
},
|
||||
|
||||
getVisibleResultCount: function() {
|
||||
return this._grid.visibleItemsCount();
|
||||
},
|
||||
|
||||
hasMoreResults: function() {
|
||||
return this._notDisplayedResult.length > 0;
|
||||
},
|
||||
|
||||
setResults: function(results, terms) {
|
||||
// copy the lists
|
||||
this._notDisplayedResult = results.slice(0);
|
||||
this._terms = terms.slice(0);
|
||||
this._pendingClear = true;
|
||||
},
|
||||
|
||||
renderResults: function(metas) {
|
||||
for (let i = 0; i < metas.length; i++) {
|
||||
let display = new GridSearchResult(this.provider, metas[i], this._terms);
|
||||
this._grid.addItem(display.actor);
|
||||
}
|
||||
},
|
||||
@ -224,19 +342,27 @@ const SearchResults = new Lang.Class({
|
||||
createProviderMeta: function(provider) {
|
||||
let providerBox = new St.BoxLayout({ style_class: 'search-section',
|
||||
vertical: true });
|
||||
let title = new St.Label({ style_class: 'search-section-header',
|
||||
text: provider.title });
|
||||
providerBox.add(title, { x_fill: false, x_align: St.Align.START });
|
||||
let providerIcon = null;
|
||||
let resultDisplay = null;
|
||||
|
||||
let resultDisplayBin = new St.Bin({ style_class: 'search-section-results',
|
||||
if (provider.appInfo) {
|
||||
resultDisplay = new ListSearchResults(provider);
|
||||
providerIcon = resultDisplay.providerIcon;
|
||||
} else {
|
||||
resultDisplay = new GridSearchResults(provider);
|
||||
}
|
||||
|
||||
let resultDisplayBin = new St.Bin({ child: resultDisplay.actor,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
providerBox.add(resultDisplayBin, { expand: true });
|
||||
let resultDisplay = new GridSearchResults(provider);
|
||||
resultDisplayBin.set_child(resultDisplay.actor);
|
||||
|
||||
let separator = new Separator.HorizontalSeparator({ style_class: 'search-section-separator' });
|
||||
providerBox.add(separator.actor);
|
||||
|
||||
this._providerMeta.push({ provider: provider,
|
||||
actor: providerBox,
|
||||
icon: providerIcon,
|
||||
resultDisplay: resultDisplay });
|
||||
this._content.add(providerBox);
|
||||
},
|
||||
@ -343,6 +469,11 @@ const SearchResults = new Lang.Class({
|
||||
meta.resultDisplay.setResults(providerResults, terms);
|
||||
let results = meta.resultDisplay.getResultsForDisplay();
|
||||
|
||||
if (meta.icon)
|
||||
meta.icon.moreIcon.visible =
|
||||
meta.resultDisplay.hasMoreResults() &&
|
||||
provider.canLaunchSearch;
|
||||
|
||||
provider.getResultMetas(results, Lang.bind(this, function(metas) {
|
||||
this._clearDisplayForProvider(provider);
|
||||
meta.actor.show();
|
||||
@ -388,10 +519,37 @@ const SearchResults = new Lang.Class({
|
||||
|
||||
let from = this._defaultResult ? this._defaultResult.actor : null;
|
||||
this.actor.navigate_focus(from, direction, false);
|
||||
if (this._defaultResult) {
|
||||
// The default result appears focused, so navigate directly to the
|
||||
// next result.
|
||||
this.actor.navigate_focus(global.stage.key_focus, direction, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const ProviderIcon = new Lang.Class({
|
||||
Name: 'ProviderIcon',
|
||||
Extends: St.Button,
|
||||
|
||||
PROVIDER_ICON_SIZE: 48,
|
||||
|
||||
_init: function(provider) {
|
||||
this.provider = provider;
|
||||
this.parent({ style_class: 'search-provider-icon',
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
track_hover: true });
|
||||
|
||||
this._content = new St.Widget({ layout_manager: new Clutter.BinLayout() });
|
||||
this.set_child(this._content);
|
||||
|
||||
let rtl = (this.get_text_direction() == Clutter.TextDirection.RTL);
|
||||
|
||||
this.moreIcon = new St.Widget({ style_class: 'search-provider-icon-more',
|
||||
visible: false,
|
||||
x_align: rtl ? Clutter.ActorAlign.START : Clutter.ActorAlign.END,
|
||||
y_align: Clutter.ActorAlign.END,
|
||||
x_expand: true,
|
||||
y_expand: true });
|
||||
|
||||
let icon = new St.Icon({ icon_size: this.PROVIDER_ICON_SIZE,
|
||||
gicon: provider.appInfo.get_icon() });
|
||||
this._content.add_actor(icon);
|
||||
this._content.add_actor(this.moreIcon);
|
||||
}
|
||||
});
|
||||
|
35
js/ui/separator.js
Normal file
35
js/ui/separator.js
Normal file
@ -0,0 +1,35 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Cairo = imports.cairo;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const HorizontalSeparator = new Lang.Class({
|
||||
Name: 'HorizontalSeparator',
|
||||
|
||||
_init: function (params) {
|
||||
this.actor = new St.DrawingArea(params);
|
||||
this.actor.connect('repaint', Lang.bind(this, this._onRepaint));
|
||||
},
|
||||
|
||||
_onRepaint: function(area) {
|
||||
let cr = area.get_context();
|
||||
let themeNode = area.get_theme_node();
|
||||
let [width, height] = area.get_surface_size();
|
||||
let margin = themeNode.get_length('-margin-horizontal');
|
||||
let gradientHeight = themeNode.get_length('-gradient-height');
|
||||
let startColor = themeNode.get_color('-gradient-start');
|
||||
let endColor = themeNode.get_color('-gradient-end');
|
||||
|
||||
let gradientWidth = (width - margin * 2);
|
||||
let gradientOffset = (height - gradientHeight) / 2;
|
||||
let pattern = new Cairo.LinearGradient(margin, gradientOffset, width - margin, gradientOffset + gradientHeight);
|
||||
pattern.addColorStopRGBA(0, startColor.red / 255, startColor.green / 255, startColor.blue / 255, startColor.alpha / 255);
|
||||
pattern.addColorStopRGBA(0.5, endColor.red / 255, endColor.green / 255, endColor.blue / 255, endColor.alpha / 255);
|
||||
pattern.addColorStopRGBA(1, startColor.red / 255, startColor.green / 255, startColor.blue / 255, startColor.alpha / 255);
|
||||
cr.setSource(pattern);
|
||||
cr.rectangle(margin, gradientOffset, gradientWidth, gradientHeight);
|
||||
cr.fill();
|
||||
cr.$dispose();
|
||||
}
|
||||
});
|
@ -9,8 +9,8 @@ const Config = imports.misc.config;
|
||||
const ExtensionSystem = imports.ui.extensionSystem;
|
||||
const ExtensionDownloader = imports.ui.extensionDownloader;
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Flashspot = imports.ui.flashspot;
|
||||
const Main = imports.ui.main;
|
||||
const Screenshot = imports.ui.screenshot;
|
||||
|
||||
const GnomeShellIface = <interface name="org.gnome.Shell">
|
||||
<method name="Eval">
|
||||
@ -18,34 +18,6 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
|
||||
<arg type="b" direction="out" name="success" />
|
||||
<arg type="s" direction="out" name="result" />
|
||||
</method>
|
||||
<method name="ScreenshotArea">
|
||||
<arg type="i" direction="in" name="x"/>
|
||||
<arg type="i" direction="in" name="y"/>
|
||||
<arg type="i" direction="in" name="width"/>
|
||||
<arg type="i" direction="in" name="height"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
</method>
|
||||
<method name="ScreenshotWindow">
|
||||
<arg type="b" direction="in" name="include_frame"/>
|
||||
<arg type="b" direction="in" name="include_cursor"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
</method>
|
||||
<method name="Screenshot">
|
||||
<arg type="b" direction="in" name="include_cursor"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
</method>
|
||||
<method name="FlashArea">
|
||||
<arg type="i" direction="in" name="x"/>
|
||||
<arg type="i" direction="in" name="y"/>
|
||||
<arg type="i" direction="in" name="width"/>
|
||||
<arg type="i" direction="in" name="height"/>
|
||||
</method>
|
||||
<property name="Mode" type="s" access="read" />
|
||||
<property name="OverviewActive" type="b" access="readwrite" />
|
||||
<property name="ShellVersion" type="s" access="read" />
|
||||
@ -76,6 +48,7 @@ const GnomeShell = new Lang.Class({
|
||||
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
|
||||
|
||||
this._extensionsSerivce = new GnomeShellExtensions();
|
||||
this._screenshotService = new Screenshot.ScreenshotService();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -111,82 +84,6 @@ const GnomeShell = new Lang.Class({
|
||||
return [success, returnValue];
|
||||
},
|
||||
|
||||
_onScreenshotComplete: function(obj, result, area, flash, invocation) {
|
||||
if (flash && result) {
|
||||
let flashspot = new Flashspot.Flashspot(area);
|
||||
flashspot.fire();
|
||||
}
|
||||
|
||||
let retval = GLib.Variant.new('(b)', [result]);
|
||||
invocation.return_value(retval);
|
||||
},
|
||||
|
||||
/**
|
||||
* ScreenshotArea:
|
||||
* @x: The X coordinate of the area
|
||||
* @y: The Y coordinate of the area
|
||||
* @width: The width of the area
|
||||
* @height: The height of the area
|
||||
* @flash: Whether to flash the area or not
|
||||
* @filename: The filename for the screenshot
|
||||
*
|
||||
* Takes a screenshot of the passed in area and saves it
|
||||
* in @filename as png image, it returns a boolean
|
||||
* indicating whether the operation was successful or not.
|
||||
*
|
||||
*/
|
||||
ScreenshotAreaAsync : function (params, invocation) {
|
||||
let [x, y, width, height, flash, filename, callback] = params;
|
||||
let screenshot = new Shell.Screenshot();
|
||||
screenshot.screenshot_area (x, y, width, height, filename,
|
||||
Lang.bind(this, this._onScreenshotComplete,
|
||||
flash, invocation));
|
||||
},
|
||||
|
||||
/**
|
||||
* ScreenshotWindow:
|
||||
* @include_frame: Whether to include the frame or not
|
||||
* @include_cursor: Whether to include the cursor image or not
|
||||
* @flash: Whether to flash the window area or not
|
||||
* @filename: The filename for the screenshot
|
||||
*
|
||||
* Takes a screenshot of the focused window (optionally omitting the frame)
|
||||
* and saves it in @filename as png image, it returns a boolean
|
||||
* indicating whether the operation was successful or not.
|
||||
*
|
||||
*/
|
||||
ScreenshotWindowAsync : function (params, invocation) {
|
||||
let [include_frame, include_cursor, flash, filename] = params;
|
||||
let screenshot = new Shell.Screenshot();
|
||||
screenshot.screenshot_window (include_frame, include_cursor, filename,
|
||||
Lang.bind(this, this._onScreenshotComplete,
|
||||
flash, invocation));
|
||||
},
|
||||
|
||||
/**
|
||||
* Screenshot:
|
||||
* @filename: The filename for the screenshot
|
||||
* @include_cursor: Whether to include the cursor image or not
|
||||
* @flash: Whether to flash the screen or not
|
||||
*
|
||||
* Takes a screenshot of the whole screen and saves it
|
||||
* in @filename as png image, it returns a boolean
|
||||
* indicating whether the operation was successful or not.
|
||||
*
|
||||
*/
|
||||
ScreenshotAsync : function (params, invocation) {
|
||||
let [include_cursor, flash, filename] = params;
|
||||
let screenshot = new Shell.Screenshot();
|
||||
screenshot.screenshot(include_cursor, filename,
|
||||
Lang.bind(this, this._onScreenshotComplete,
|
||||
flash, invocation));
|
||||
},
|
||||
|
||||
FlashArea: function(x, y, width, height) {
|
||||
let flashspot = new Flashspot.Flashspot({ x : x, y : y, width: width, height: height});
|
||||
flashspot.fire();
|
||||
},
|
||||
|
||||
Mode: global.session_mode,
|
||||
|
||||
get OverviewActive() {
|
||||
|
@ -80,7 +80,7 @@ const Indicator = new Lang.Class({
|
||||
this._applet.connect('notify::show-full-menu', Lang.bind(this, this._updateFullMenu));
|
||||
this._updateFullMenu();
|
||||
|
||||
this.menu.addSettingsAction(_("Bluetooth Settings"), 'bluetooth-properties.desktop');
|
||||
this.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop');
|
||||
|
||||
this._applet.connect('pincode-request', Lang.bind(this, this._pinRequest));
|
||||
this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest));
|
||||
@ -446,10 +446,5 @@ const PinNotification = new Lang.Class({
|
||||
return this._entry.clutter_text.text.length == 6;
|
||||
else
|
||||
return true;
|
||||
},
|
||||
|
||||
grabFocus: function(lockTray) {
|
||||
this.parent(lockTray);
|
||||
global.stage.set_key_focus(this._entry);
|
||||
}
|
||||
});
|
||||
|
@ -1,9 +1,11 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const GnomeDesktop = imports.gi.GnomeDesktop;
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
@ -21,6 +23,7 @@ try {
|
||||
const Main = imports.ui.main;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const SwitcherPopup = imports.ui.switcherPopup;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const DESKTOP_INPUT_SOURCES_SCHEMA = 'org.gnome.desktop.input-sources';
|
||||
@ -47,6 +50,7 @@ const IBusManager = new Lang.Class({
|
||||
this._engines = {};
|
||||
this._ready = false;
|
||||
this._registerPropertiesId = 0;
|
||||
this._currentEngineName = null;
|
||||
|
||||
this._nameWatcherId = Gio.DBus.session.watch_name(IBus.SERVICE_IBUS,
|
||||
Gio.BusNameWatcherFlags.NONE,
|
||||
@ -66,6 +70,10 @@ const IBusManager = new Lang.Class({
|
||||
this._engines = {};
|
||||
this._ready = false;
|
||||
this._registerPropertiesId = 0;
|
||||
this._currentEngineName = null;
|
||||
|
||||
if (this._readyCallback)
|
||||
this._readyCallback(false);
|
||||
},
|
||||
|
||||
_onNameAppeared: function() {
|
||||
@ -89,12 +97,10 @@ const IBusManager = new Lang.Class({
|
||||
let name = enginesList[i].get_name();
|
||||
this._engines[name] = enginesList[i];
|
||||
}
|
||||
this._updateReadiness();
|
||||
} else {
|
||||
this._clear();
|
||||
return;
|
||||
}
|
||||
|
||||
this._updateReadiness();
|
||||
},
|
||||
|
||||
_initPanelService: function(ibus, result) {
|
||||
@ -105,27 +111,31 @@ const IBusManager = new Lang.Class({
|
||||
this._candidatePopup.setPanelService(this._panelService);
|
||||
// Need to set this to get 'global-engine-changed' emitions
|
||||
this._ibus.set_watch_ibus_signal(true);
|
||||
this._ibus.connect('global-engine-changed', Lang.bind(this, this._resetProperties));
|
||||
this._ibus.connect('global-engine-changed', Lang.bind(this, this._engineChanged));
|
||||
this._panelService.connect('update-property', Lang.bind(this, this._updateProperty));
|
||||
this._resetProperties();
|
||||
// If an engine is already active we need to get its properties
|
||||
this._ibus.get_global_engine_async(-1, null, Lang.bind(this, function(i, result) {
|
||||
let engine = this._ibus.get_global_engine_async_finish(result);
|
||||
if (!engine)
|
||||
return;
|
||||
this._engineChanged(this._ibus, engine.get_name());
|
||||
}));
|
||||
this._updateReadiness();
|
||||
} else {
|
||||
this._clear();
|
||||
return;
|
||||
}
|
||||
|
||||
this._updateReadiness();
|
||||
},
|
||||
|
||||
_updateReadiness: function() {
|
||||
this._ready = (Object.keys(this._engines).length > 0 &&
|
||||
this._panelService != null);
|
||||
|
||||
if (this._ready && this._readyCallback)
|
||||
this._readyCallback();
|
||||
if (this._readyCallback)
|
||||
this._readyCallback(this._ready);
|
||||
},
|
||||
|
||||
_resetProperties: function() {
|
||||
this.emit('properties-registered', null);
|
||||
_engineChanged: function(bus, engineName) {
|
||||
this._currentEngineName = engineName;
|
||||
|
||||
if (this._registerPropertiesId != 0)
|
||||
return;
|
||||
@ -138,25 +148,18 @@ const IBusManager = new Lang.Class({
|
||||
this._panelService.disconnect(this._registerPropertiesId);
|
||||
this._registerPropertiesId = 0;
|
||||
|
||||
this.emit('properties-registered', props);
|
||||
this.emit('properties-registered', this._currentEngineName, props);
|
||||
}));
|
||||
},
|
||||
|
||||
_updateProperty: function(panel, prop) {
|
||||
this.emit('property-updated', prop);
|
||||
this.emit('property-updated', this._currentEngineName, prop);
|
||||
},
|
||||
|
||||
activateProperty: function(key, state) {
|
||||
this._panelService.property_activate(key, state);
|
||||
},
|
||||
|
||||
hasProperties: function(id) {
|
||||
if (id == 'anthy')
|
||||
return true;
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
getEngineDesc: function(id) {
|
||||
if (!IBus || !this._ready)
|
||||
return null;
|
||||
@ -181,16 +184,127 @@ const LayoutMenuItem = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
const InputSource = new Lang.Class({
|
||||
Name: 'InputSource',
|
||||
|
||||
_init: function(type, id, displayName, shortName, index) {
|
||||
this.type = type;
|
||||
this.id = id;
|
||||
this.displayName = displayName;
|
||||
this._shortName = shortName;
|
||||
this.index = index;
|
||||
|
||||
this._menuItem = new LayoutMenuItem(this.displayName, this._shortName);
|
||||
this._menuItem.connect('activate', Lang.bind(this, this.activate));
|
||||
this._indicatorLabel = new St.Label({ text: this._shortName });
|
||||
|
||||
this.properties = null;
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this._menuItem.destroy();
|
||||
this._indicatorLabel.destroy();
|
||||
},
|
||||
|
||||
get shortName() {
|
||||
return this._shortName;
|
||||
},
|
||||
|
||||
set shortName(v) {
|
||||
this._shortName = v;
|
||||
this._menuItem.indicator.set_text(v);
|
||||
this._indicatorLabel.set_text(v);
|
||||
},
|
||||
|
||||
get menuItem() {
|
||||
return this._menuItem;
|
||||
},
|
||||
|
||||
get indicatorLabel() {
|
||||
return this._indicatorLabel;
|
||||
},
|
||||
|
||||
activate: function() {
|
||||
this.emit('activate');
|
||||
},
|
||||
});
|
||||
Signals.addSignalMethods(InputSource.prototype);
|
||||
|
||||
const InputSourcePopup = new Lang.Class({
|
||||
Name: 'InputSourcePopup',
|
||||
Extends: SwitcherPopup.SwitcherPopup,
|
||||
|
||||
_init: function(items, action, actionBackward) {
|
||||
this.parent(items);
|
||||
|
||||
this._action = action;
|
||||
this._actionBackward = actionBackward;
|
||||
},
|
||||
|
||||
_createSwitcher: function() {
|
||||
this._switcherList = new InputSourceSwitcher(this._items);
|
||||
return true;
|
||||
},
|
||||
|
||||
_initialSelection: function(backward, binding) {
|
||||
if (binding == 'switch-input-source') {
|
||||
if (backward)
|
||||
this._selectedIndex = this._items.length - 1;
|
||||
} else if (binding == 'switch-input-source-backward') {
|
||||
if (!backward)
|
||||
this._selectedIndex = this._items.length - 1;
|
||||
}
|
||||
this._select(this._selectedIndex);
|
||||
},
|
||||
|
||||
_keyPressHandler: function(keysym, backwards, action) {
|
||||
if (action == this._action)
|
||||
this._select(backwards ? this._previous() : this._next());
|
||||
else if (action == this._actionBackward)
|
||||
this._select(backwards ? this._next() : this._previous());
|
||||
else if (keysym == Clutter.Left)
|
||||
this._select(this._previous());
|
||||
else if (keysym == Clutter.Right)
|
||||
this._select(this._next());
|
||||
},
|
||||
|
||||
_finish : function() {
|
||||
this.parent();
|
||||
|
||||
this._items[this._selectedIndex].activate();
|
||||
},
|
||||
});
|
||||
|
||||
const InputSourceSwitcher = new Lang.Class({
|
||||
Name: 'InputSourceSwitcher',
|
||||
Extends: SwitcherPopup.SwitcherList,
|
||||
|
||||
_init: function(items) {
|
||||
this.parent(true);
|
||||
|
||||
for (let i = 0; i < items.length; i++)
|
||||
this._addIcon(items[i]);
|
||||
},
|
||||
|
||||
_addIcon: function(item) {
|
||||
let box = new St.BoxLayout({ vertical: true });
|
||||
|
||||
let bin = new St.Bin({ style_class: 'input-source-switcher-symbol' });
|
||||
let symbol = new St.Label({ text: item.shortName });
|
||||
bin.set_child(symbol);
|
||||
box.add(bin, { x_fill: false, y_fill: false } );
|
||||
|
||||
let text = new St.Label({ text: item.displayName });
|
||||
box.add(text, { x_fill: false });
|
||||
|
||||
this.addItem(box, text);
|
||||
}
|
||||
});
|
||||
|
||||
const InputSourceIndicator = new Lang.Class({
|
||||
Name: 'InputSourceIndicator',
|
||||
Extends: PanelMenu.Button,
|
||||
|
||||
_propertiesWhitelist: [
|
||||
'InputMode',
|
||||
'TypingMode',
|
||||
'DictMode'
|
||||
],
|
||||
|
||||
_init: function() {
|
||||
this.parent(0.0, _("Keyboard"));
|
||||
|
||||
@ -201,14 +315,36 @@ const InputSourceIndicator = new Lang.Class({
|
||||
this.actor.add_actor(this._container);
|
||||
this.actor.add_style_class_name('panel-status-button');
|
||||
|
||||
this._labelActors = {};
|
||||
this._layoutItems = {};
|
||||
// All valid input sources currently in the gsettings
|
||||
// KEY_INPUT_SOURCES list indexed by their index there
|
||||
this._inputSources = {};
|
||||
// All valid input sources currently in the gsettings
|
||||
// KEY_INPUT_SOURCES list of type INPUT_SOURCE_TYPE_IBUS
|
||||
// indexed by the IBus ID
|
||||
this._ibusSources = {};
|
||||
|
||||
this._currentSource = null;
|
||||
|
||||
// All valid input sources currently in the gsettings
|
||||
// KEY_INPUT_SOURCES list ordered by most recently used
|
||||
this._mruSources = [];
|
||||
this._keybindingAction =
|
||||
Main.wm.addKeybinding('switch-input-source',
|
||||
new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }),
|
||||
Meta.KeyBindingFlags.REVERSES,
|
||||
Main.KeybindingMode.ALL,
|
||||
Lang.bind(this, this._switchInputSource));
|
||||
this._keybindingActionBackward =
|
||||
Main.wm.addKeybinding('switch-input-source-backward',
|
||||
new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }),
|
||||
Meta.KeyBindingFlags.REVERSES |
|
||||
Meta.KeyBindingFlags.REVERSED,
|
||||
Main.KeybindingMode.ALL,
|
||||
Lang.bind(this, this._switchInputSource));
|
||||
this._settings = new Gio.Settings({ schema: DESKTOP_INPUT_SOURCES_SCHEMA });
|
||||
this._settings.connect('changed::' + KEY_CURRENT_INPUT_SOURCE, Lang.bind(this, this._currentInputSourceChanged));
|
||||
this._settings.connect('changed::' + KEY_INPUT_SOURCES, Lang.bind(this, this._inputSourcesChanged));
|
||||
|
||||
this._currentSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
|
||||
this._xkbInfo = new GnomeDesktop.XkbInfo();
|
||||
|
||||
this._propSeparator = new PopupMenu.PopupSeparatorMenuItem();
|
||||
@ -217,9 +353,8 @@ const InputSourceIndicator = new Lang.Class({
|
||||
this.menu.addMenuItem(this._propSection);
|
||||
this._propSection.actor.hide();
|
||||
|
||||
this._properties = null;
|
||||
|
||||
this._ibusManager = new IBusManager(Lang.bind(this, this._inputSourcesChanged));
|
||||
this._ibusReady = false;
|
||||
this._ibusManager = new IBusManager(Lang.bind(this, this._ibusReadyCallback));
|
||||
this._ibusManager.connect('properties-registered', Lang.bind(this, this._ibusPropertiesRegistered));
|
||||
this._ibusManager.connect('property-updated', Lang.bind(this, this._ibusPropertyUpdated));
|
||||
this._inputSourcesChanged();
|
||||
@ -231,6 +366,13 @@ const InputSourceIndicator = new Lang.Class({
|
||||
this._sessionUpdated();
|
||||
|
||||
this.menu.addSettingsAction(_("Region and Language Settings"), 'gnome-region-panel.desktop');
|
||||
|
||||
this._sourcesPerWindow = false;
|
||||
this._focusWindowNotifyId = 0;
|
||||
this._overviewShowingId = 0;
|
||||
this._overviewHiddenId = 0;
|
||||
this._settings.connect('changed::per-window', Lang.bind(this, this._sourcesPerWindowChanged));
|
||||
this._sourcesPerWindowChanged();
|
||||
},
|
||||
|
||||
_sessionUpdated: function() {
|
||||
@ -241,18 +383,29 @@ const InputSourceIndicator = new Lang.Class({
|
||||
this._showLayoutItem.actor.visible = Main.sessionMode.allowSettings;
|
||||
},
|
||||
|
||||
_ibusReadyCallback: function(ready) {
|
||||
if (this._ibusReady == ready)
|
||||
return;
|
||||
|
||||
this._ibusReady = ready;
|
||||
this._mruSources = [];
|
||||
this._inputSourcesChanged();
|
||||
},
|
||||
|
||||
_switchInputSource: function(display, screen, window, binding) {
|
||||
let popup = new InputSourcePopup(this._mruSources, this._keybindingAction, this._keybindingActionBackward);
|
||||
let modifiers = binding.get_modifiers();
|
||||
let backwards = modifiers & Meta.VirtualModifier.SHIFT_MASK;
|
||||
if (!popup.show(backwards, binding.get_name(), binding.get_mask()))
|
||||
popup.destroy();
|
||||
},
|
||||
|
||||
_currentInputSourceChanged: function() {
|
||||
let nVisibleSources = Object.keys(this._layoutItems).length;
|
||||
let newCurrentSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
|
||||
let newLayoutItem = this._layoutItems[newCurrentSourceIndex];
|
||||
let hasProperties;
|
||||
let nVisibleSources = Object.keys(this._inputSources).length;
|
||||
let newSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
|
||||
let newSource = this._inputSources[newSourceIndex];
|
||||
|
||||
if (newLayoutItem)
|
||||
hasProperties = this._ibusManager.hasProperties(newLayoutItem.ibusEngineId);
|
||||
else
|
||||
hasProperties = false;
|
||||
|
||||
if (!newLayoutItem || (nVisibleSources < 2 && !hasProperties)) {
|
||||
if (!newSource || (nVisibleSources < 2 && !newSource.properties)) {
|
||||
// This source index might be invalid if we weren't able
|
||||
// to build a menu item for it, so we hide ourselves since
|
||||
// we can't fix it here. *shrug*
|
||||
@ -266,106 +419,125 @@ const InputSourceIndicator = new Lang.Class({
|
||||
|
||||
this.actor.show();
|
||||
|
||||
if (this._layoutItems[this._currentSourceIndex]) {
|
||||
this._layoutItems[this._currentSourceIndex].setShowDot(false);
|
||||
this._container.set_skip_paint(this._labelActors[this._currentSourceIndex], true);
|
||||
let oldSource;
|
||||
[oldSource, this._currentSource] = [this._currentSource, newSource];
|
||||
|
||||
if (oldSource) {
|
||||
oldSource.menuItem.setShowDot(false);
|
||||
this._container.set_skip_paint(oldSource.indicatorLabel, true);
|
||||
}
|
||||
|
||||
newLayoutItem.setShowDot(true);
|
||||
newSource.menuItem.setShowDot(true);
|
||||
this._container.set_skip_paint(newSource.indicatorLabel, false);
|
||||
|
||||
let newLabelActor = this._labelActors[newCurrentSourceIndex];
|
||||
this._container.set_skip_paint(newLabelActor, false);
|
||||
this._buildPropSection(newSource.properties);
|
||||
|
||||
if (hasProperties)
|
||||
newLabelActor.set_text(newLayoutItem.indicator.get_text());
|
||||
for (let i = 1; i < this._mruSources.length; ++i)
|
||||
if (this._mruSources[i] == newSource) {
|
||||
let currentSource = this._mruSources.splice(i, 1);
|
||||
this._mruSources = currentSource.concat(this._mruSources);
|
||||
break;
|
||||
}
|
||||
|
||||
this._currentSourceIndex = newCurrentSourceIndex;
|
||||
this._changePerWindowSource();
|
||||
},
|
||||
|
||||
_inputSourcesChanged: function() {
|
||||
let sources = this._settings.get_value(KEY_INPUT_SOURCES);
|
||||
let nSources = sources.n_children();
|
||||
|
||||
for (let i in this._layoutItems)
|
||||
this._layoutItems[i].destroy();
|
||||
for (let i in this._inputSources)
|
||||
this._inputSources[i].destroy();
|
||||
|
||||
for (let i in this._labelActors)
|
||||
this._labelActors[i].destroy();
|
||||
this._inputSources = {};
|
||||
this._ibusSources = {};
|
||||
|
||||
this._layoutItems = {};
|
||||
this._labelActors = {};
|
||||
|
||||
let infos = [];
|
||||
let infosByShortName = {};
|
||||
let inputSourcesByShortName = {};
|
||||
|
||||
for (let i = 0; i < nSources; i++) {
|
||||
let info = { exists: false };
|
||||
let displayName;
|
||||
let shortName;
|
||||
let [type, id] = sources.get_child_value(i).deep_unpack();
|
||||
let exists = false;
|
||||
|
||||
if (type == INPUT_SOURCE_TYPE_XKB) {
|
||||
[info.exists, info.displayName, info.shortName, , ] =
|
||||
[exists, displayName, shortName, , ] =
|
||||
this._xkbInfo.get_layout_info(id);
|
||||
} else if (type == INPUT_SOURCE_TYPE_IBUS) {
|
||||
let engineDesc = this._ibusManager.getEngineDesc(id);
|
||||
if (engineDesc) {
|
||||
let language = IBus.get_language_name(engineDesc.get_language());
|
||||
|
||||
info.exists = true;
|
||||
info.displayName = language + ' (' + engineDesc.get_longname() + ')';
|
||||
info.shortName = this._makeEngineShortName(engineDesc);
|
||||
info.ibusEngineId = id;
|
||||
exists = true;
|
||||
displayName = language + ' (' + engineDesc.get_longname() + ')';
|
||||
shortName = this._makeEngineShortName(engineDesc);
|
||||
}
|
||||
}
|
||||
|
||||
if (!info.exists)
|
||||
if (!exists)
|
||||
continue;
|
||||
|
||||
info.sourceIndex = i;
|
||||
let is = new InputSource(type, id, displayName, shortName, i);
|
||||
|
||||
if (!(info.shortName in infosByShortName))
|
||||
infosByShortName[info.shortName] = [];
|
||||
infosByShortName[info.shortName].push(info);
|
||||
infos.push(info);
|
||||
}
|
||||
|
||||
for (let i = 0; i < infos.length; i++) {
|
||||
let info = infos[i];
|
||||
if (infosByShortName[info.shortName].length > 1) {
|
||||
let sub = infosByShortName[info.shortName].indexOf(info) + 1;
|
||||
info.shortName += String.fromCharCode(0x2080 + sub);
|
||||
}
|
||||
|
||||
let item = new LayoutMenuItem(info.displayName, info.shortName);
|
||||
item.ibusEngineId = info.ibusEngineId;
|
||||
this._layoutItems[info.sourceIndex] = item;
|
||||
this.menu.addMenuItem(item, i);
|
||||
item.connect('activate', Lang.bind(this, function() {
|
||||
is.connect('activate', Lang.bind(this, function() {
|
||||
if (this._currentSource.index == is.index)
|
||||
return;
|
||||
this._settings.set_value(KEY_CURRENT_INPUT_SOURCE,
|
||||
GLib.Variant.new_uint32(info.sourceIndex));
|
||||
GLib.Variant.new_uint32(is.index));
|
||||
}));
|
||||
|
||||
let shortLabel = new St.Label({ text: info.shortName });
|
||||
this._labelActors[info.sourceIndex] = shortLabel;
|
||||
this._container.add_actor(shortLabel);
|
||||
this._container.set_skip_paint(shortLabel, true);
|
||||
if (!(is.shortName in inputSourcesByShortName))
|
||||
inputSourcesByShortName[is.shortName] = [];
|
||||
inputSourcesByShortName[is.shortName].push(is);
|
||||
|
||||
this._inputSources[is.index] = is;
|
||||
|
||||
if (is.type == INPUT_SOURCE_TYPE_IBUS)
|
||||
this._ibusSources[is.id] = is;
|
||||
}
|
||||
|
||||
let menuIndex = 0;
|
||||
for (let i in this._inputSources) {
|
||||
let is = this._inputSources[i];
|
||||
if (inputSourcesByShortName[is.shortName].length > 1) {
|
||||
let sub = inputSourcesByShortName[is.shortName].indexOf(is) + 1;
|
||||
is.shortName += String.fromCharCode(0x2080 + sub);
|
||||
}
|
||||
|
||||
this.menu.addMenuItem(is.menuItem, menuIndex++);
|
||||
|
||||
this._container.add_actor(is.indicatorLabel);
|
||||
this._container.set_skip_paint(is.indicatorLabel, true);
|
||||
}
|
||||
|
||||
let sourcesList = [];
|
||||
for (let i in this._inputSources)
|
||||
sourcesList.push(this._inputSources[i]);
|
||||
|
||||
let mruSources = [];
|
||||
for (let i = 0; i < this._mruSources.length; i++) {
|
||||
for (let j = 0; j < sourcesList.length; j++)
|
||||
if (this._mruSources[i].type == sourcesList[j].type &&
|
||||
this._mruSources[i].id == sourcesList[j].id) {
|
||||
mruSources = mruSources.concat(sourcesList.splice(j, 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
this._mruSources = mruSources.concat(sourcesList);
|
||||
|
||||
this._currentInputSourceChanged();
|
||||
},
|
||||
|
||||
_showLayout: function() {
|
||||
Main.overview.hide();
|
||||
|
||||
let sources = this._settings.get_value(KEY_INPUT_SOURCES);
|
||||
let current = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
|
||||
let [type, id] = sources.get_child_value(current).deep_unpack();
|
||||
let source = this._currentSource;
|
||||
let xkbLayout = '';
|
||||
let xkbVariant = '';
|
||||
|
||||
if (type == INPUT_SOURCE_TYPE_XKB) {
|
||||
[, , , xkbLayout, xkbVariant] = this._xkbInfo.get_layout_info(id);
|
||||
} else if (type == INPUT_SOURCE_TYPE_IBUS) {
|
||||
let engineDesc = this._ibusManager.getEngineDesc(id);
|
||||
if (source.type == INPUT_SOURCE_TYPE_XKB) {
|
||||
[, , , xkbLayout, xkbVariant] = this._xkbInfo.get_layout_info(source.id);
|
||||
} else if (source.type == INPUT_SOURCE_TYPE_IBUS) {
|
||||
let engineDesc = this._ibusManager.getEngineDesc(source.id);
|
||||
if (engineDesc) {
|
||||
xkbLayout = engineDesc.get_layout();
|
||||
xkbVariant = '';
|
||||
@ -394,26 +566,25 @@ const InputSourceIndicator = new Lang.Class({
|
||||
return String.fromCharCode(0x2328); // keyboard glyph
|
||||
},
|
||||
|
||||
_propertyWhitelisted: function(prop) {
|
||||
for (let i = 0; i < this._propertiesWhitelist.length; ++i) {
|
||||
let key = prop.get_key();
|
||||
if (key.substr(0, this._propertiesWhitelist[i].length) == this._propertiesWhitelist[i])
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_ibusPropertiesRegistered: function(im, props) {
|
||||
this._properties = props;
|
||||
this._buildPropSection();
|
||||
},
|
||||
|
||||
_ibusPropertyUpdated: function(im, prop) {
|
||||
if (!this._propertyWhitelisted(prop))
|
||||
_ibusPropertiesRegistered: function(im, engineName, props) {
|
||||
let source = this._ibusSources[engineName];
|
||||
if (!source)
|
||||
return;
|
||||
|
||||
if (this._updateSubProperty(this._properties, prop))
|
||||
this._buildPropSection();
|
||||
source.properties = props;
|
||||
|
||||
if (source == this._currentSource)
|
||||
this._currentInputSourceChanged();
|
||||
},
|
||||
|
||||
_ibusPropertyUpdated: function(im, engineName, prop) {
|
||||
let source = this._ibusSources[engineName];
|
||||
if (!source)
|
||||
return;
|
||||
|
||||
if (this._updateSubProperty(source.properties, prop) &&
|
||||
source == this._currentSource)
|
||||
this._currentInputSourceChanged();
|
||||
},
|
||||
|
||||
_updateSubProperty: function(props, prop) {
|
||||
@ -433,25 +604,12 @@ const InputSourceIndicator = new Lang.Class({
|
||||
return false;
|
||||
},
|
||||
|
||||
_updateIndicatorLabel: function(text) {
|
||||
let layoutItem = this._layoutItems[this._currentSourceIndex];
|
||||
let hasProperties;
|
||||
|
||||
if (layoutItem)
|
||||
hasProperties = this._ibusManager.hasProperties(layoutItem.ibusEngineId);
|
||||
else
|
||||
hasProperties = false;
|
||||
|
||||
if (hasProperties)
|
||||
this._labelActors[this._currentSourceIndex].set_text(text);
|
||||
},
|
||||
|
||||
_buildPropSection: function() {
|
||||
_buildPropSection: function(properties) {
|
||||
this._propSeparator.actor.hide();
|
||||
this._propSection.actor.hide();
|
||||
this._propSection.removeAll();
|
||||
|
||||
this._buildPropSubMenu(this._propSection, this._properties);
|
||||
this._buildPropSubMenu(this._propSection, properties);
|
||||
|
||||
if (!this._propSection.isEmpty()) {
|
||||
this._propSection.actor.show();
|
||||
@ -468,8 +626,7 @@ const InputSourceIndicator = new Lang.Class({
|
||||
for (let i = 0; (p = props.get(i)) != null; ++i) {
|
||||
let prop = p;
|
||||
|
||||
if (!this._propertyWhitelisted(prop) ||
|
||||
!prop.get_visible())
|
||||
if (!prop.get_visible())
|
||||
continue;
|
||||
|
||||
if (prop.get_key() == 'InputMode') {
|
||||
@ -480,15 +637,17 @@ const InputSourceIndicator = new Lang.Class({
|
||||
text = prop.get_label().get_text();
|
||||
|
||||
if (text && text.length > 0 && text.length < 3)
|
||||
this._updateIndicatorLabel(text);
|
||||
this._currentSource.indicatorLabel.set_text(text);
|
||||
}
|
||||
|
||||
let item;
|
||||
let type = prop.get_prop_type();
|
||||
if (type == IBus.PropType.MENU) {
|
||||
switch (prop.get_prop_type()) {
|
||||
case IBus.PropType.MENU:
|
||||
item = new PopupMenu.PopupSubMenuMenuItem(prop.get_label().get_text());
|
||||
this._buildPropSubMenu(item.menu, prop.get_sub_props());
|
||||
} else if (type == IBus.PropType.RADIO) {
|
||||
break;
|
||||
|
||||
case IBus.PropType.RADIO:
|
||||
item = new PopupMenu.PopupMenuItem(prop.get_label().get_text());
|
||||
item.prop = prop;
|
||||
radioGroup.push(item);
|
||||
@ -513,7 +672,39 @@ const InputSourceIndicator = new Lang.Class({
|
||||
}
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
break;
|
||||
|
||||
case IBus.PropType.TOGGLE:
|
||||
item = new PopupMenu.PopupSwitchMenuItem(prop.get_label().get_text(), prop.get_state() == IBus.PropState.CHECKED);
|
||||
item.prop = prop;
|
||||
item.connect('toggled', Lang.bind(this, function() {
|
||||
if (item.state) {
|
||||
item.prop.set_state(IBus.PropState.CHECKED);
|
||||
this._ibusManager.activateProperty(item.prop.get_key(),
|
||||
IBus.PropState.CHECKED);
|
||||
} else {
|
||||
item.prop.set_state(IBus.PropState.UNCHECKED);
|
||||
this._ibusManager.activateProperty(item.prop.get_key(),
|
||||
IBus.PropState.UNCHECKED);
|
||||
}
|
||||
}));
|
||||
break;
|
||||
|
||||
case IBus.PropType.NORMAL:
|
||||
item = new PopupMenu.PopupMenuItem(prop.get_label().get_text());
|
||||
item.prop = prop;
|
||||
item.connect('activate', Lang.bind(this, function() {
|
||||
this._ibusManager.activateProperty(item.prop.get_key(),
|
||||
IBus.PropState.CHECKED);
|
||||
}));
|
||||
break;
|
||||
|
||||
case IBus.PropType.SEPARATOR:
|
||||
item = new PopupMenu.PopupSeparatorMenuItem();
|
||||
break;
|
||||
|
||||
default:
|
||||
log ('IBus property %s has invalid type %d'.format(prop.get_key(), type));
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -522,14 +713,91 @@ const InputSourceIndicator = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_getNewInputSource: function(current) {
|
||||
for (let i in this._inputSources) {
|
||||
let is = this._inputSources[i];
|
||||
if (is.type == current.type &&
|
||||
is.id == current.id)
|
||||
return is;
|
||||
}
|
||||
return this._currentSource;
|
||||
},
|
||||
|
||||
_getCurrentWindow: function() {
|
||||
if (Main.overview.visible)
|
||||
return Main.overview;
|
||||
else
|
||||
return global.display.focus_window;
|
||||
},
|
||||
|
||||
_setPerWindowInputSource: function() {
|
||||
let window = this._getCurrentWindow();
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
if (!window._inputSources) {
|
||||
window._inputSources = this._inputSources;
|
||||
window._currentSource = this._currentSource;
|
||||
} else if (window._inputSources == this._inputSources) {
|
||||
window._currentSource.activate();
|
||||
} else {
|
||||
window._inputSources = this._inputSources;
|
||||
window._currentSource = this._getNewInputSource(window._currentSource);
|
||||
window._currentSource.activate();
|
||||
}
|
||||
},
|
||||
|
||||
_sourcesPerWindowChanged: function() {
|
||||
this._sourcesPerWindow = this._settings.get_boolean('per-window');
|
||||
|
||||
if (this._sourcesPerWindow && this._focusWindowNotifyId == 0) {
|
||||
this._focusWindowNotifyId = global.display.connect('notify::focus-window',
|
||||
Lang.bind(this, this._setPerWindowInputSource));
|
||||
this._overviewShowingId = Main.overview.connect('showing',
|
||||
Lang.bind(this, this._setPerWindowInputSource));
|
||||
this._overviewHiddenId = Main.overview.connect('hidden',
|
||||
Lang.bind(this, this._setPerWindowInputSource));
|
||||
} else if (!this._sourcesPerWindow && this._focusWindowNotifyId != 0) {
|
||||
global.display.disconnect(this._focusWindowNotifyId);
|
||||
this._focusWindowNotifyId = 0;
|
||||
Main.overview.disconnect(this._overviewShowingId);
|
||||
this._overviewShowingId = 0;
|
||||
Main.overview.disconnect(this._overviewHiddenId);
|
||||
this._overviewHiddenId = 0;
|
||||
|
||||
let windows = global.get_window_actors().map(function(w) {
|
||||
return w.meta_window;
|
||||
});
|
||||
for (let i = 0; i < windows.length; ++i) {
|
||||
delete windows[i]._inputSources;
|
||||
delete windows[i]._currentSource;
|
||||
}
|
||||
delete Main.overview._inputSources;
|
||||
delete Main.overview._currentSource;
|
||||
}
|
||||
},
|
||||
|
||||
_changePerWindowSource: function() {
|
||||
if (!this._sourcesPerWindow)
|
||||
return;
|
||||
|
||||
let window = this._getCurrentWindow();
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
window._inputSources = this._inputSources;
|
||||
window._currentSource = this._currentSource;
|
||||
},
|
||||
|
||||
_containerGetPreferredWidth: function(container, for_height, alloc) {
|
||||
// Here, and in _containerGetPreferredHeight, we need to query
|
||||
// for the height of all children, but we ignore the results
|
||||
// for those we don't actually display.
|
||||
let max_min_width = 0, max_natural_width = 0;
|
||||
|
||||
for (let i in this._labelActors) {
|
||||
let [min_width, natural_width] = this._labelActors[i].get_preferred_width(for_height);
|
||||
for (let i in this._inputSources) {
|
||||
let is = this._inputSources[i];
|
||||
let [min_width, natural_width] = is.indicatorLabel.get_preferred_width(for_height);
|
||||
max_min_width = Math.max(max_min_width, min_width);
|
||||
max_natural_width = Math.max(max_natural_width, natural_width);
|
||||
}
|
||||
@ -541,8 +809,9 @@ const InputSourceIndicator = new Lang.Class({
|
||||
_containerGetPreferredHeight: function(container, for_width, alloc) {
|
||||
let max_min_height = 0, max_natural_height = 0;
|
||||
|
||||
for (let i in this._labelActors) {
|
||||
let [min_height, natural_height] = this._labelActors[i].get_preferred_height(for_width);
|
||||
for (let i in this._inputSources) {
|
||||
let is = this._inputSources[i];
|
||||
let [min_height, natural_height] = is.indicatorLabel.get_preferred_height(for_width);
|
||||
max_min_height = Math.max(max_min_height, min_height);
|
||||
max_natural_height = Math.max(max_natural_height, natural_height);
|
||||
}
|
||||
@ -558,7 +827,9 @@ const InputSourceIndicator = new Lang.Class({
|
||||
box.y2 -= box.y1;
|
||||
box.y1 = 0;
|
||||
|
||||
for (let i in this._labelActors)
|
||||
this._labelActors[i].allocate_align_fill(box, 0.5, 0, false, false, flags);
|
||||
for (let i in this._inputSources) {
|
||||
let is = this._inputSources[i];
|
||||
is.indicatorLabel.allocate_align_fill(box, 0.5, 0, false, false, flags);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -10,48 +10,53 @@ const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const VolumeMenu = imports.ui.status.volume;
|
||||
|
||||
const FakeStatusIcon = new Lang.Class({
|
||||
Name: 'FakeStatusIcon',
|
||||
|
||||
_init: function(button) {
|
||||
this.actor = new St.BoxLayout({ style_class: 'panel-status-button-box' });
|
||||
this._button = button;
|
||||
this._button.connect('icons-updated', Lang.bind(this, this._reconstructIcons));
|
||||
this._button.actor.bind_property('visible', this.actor, 'visible',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
this._reconstructIcons();
|
||||
},
|
||||
|
||||
_reconstructIcons: function() {
|
||||
this.actor.destroy_all_children();
|
||||
this._button.icons.forEach(Lang.bind(this, function(icon) {
|
||||
let newIcon = new St.Icon({ style_class: 'system-status-icon' });
|
||||
icon.bind_property('gicon', newIcon, 'gicon',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
icon.bind_property('visible', newIcon, 'visible',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
this.actor.add_actor(newIcon);
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
const Indicator = new Lang.Class({
|
||||
Name: 'LockScreenMenuIndicator',
|
||||
Extends: PanelMenu.SystemStatusButton,
|
||||
|
||||
_init: function() {
|
||||
this.parent(null, _("Volume, network, battery"));
|
||||
this._box.style_class = 'lock-screen-status-button-box';
|
||||
|
||||
this._volume = Main.panel.statusArea.volume;
|
||||
if (this._volume) {
|
||||
this._volumeIcon = this.addIcon(null);
|
||||
this._volume.mainIcon.bind_property('gicon', this._volumeIcon, 'gicon',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
this._volume.mainIcon.bind_property('visible', this._volumeIcon, 'visible',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
this._volumeControl = VolumeMenu.getMixerControl();
|
||||
this._volumeMenu = new VolumeMenu.VolumeMenu(this._volumeControl);
|
||||
this.menu.addMenuItem(this._volumeMenu);
|
||||
|
||||
this._volumeControl = VolumeMenu.getMixerControl();
|
||||
this._volumeMenu = new VolumeMenu.VolumeMenu(this._volumeControl);
|
||||
this.menu.addMenuItem(this._volumeMenu);
|
||||
this._volume = new FakeStatusIcon(Main.panel.statusArea.volume);
|
||||
this._box.add_child(this._volume.actor);
|
||||
|
||||
// Network may not exist if the user doesn't have NetworkManager
|
||||
if (Main.panel.statusArea.network) {
|
||||
this._network = new FakeStatusIcon(Main.panel.statusArea.network);
|
||||
this._box.add_child(this._network.actor);
|
||||
}
|
||||
|
||||
this._network = Main.panel.statusArea.network;
|
||||
if (this._network) {
|
||||
this._networkIcon = this.addIcon(null);
|
||||
this._network.mainIcon.bind_property('gicon', this._networkIcon, 'gicon',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
this._network.mainIcon.bind_property('visible', this._networkIcon, 'visible',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
|
||||
this._networkSecondaryIcon = this.addIcon(null);
|
||||
this._network.secondaryIcon.bind_property('gicon', this._networkSecondaryIcon, 'gicon',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
this._network.secondaryIcon.bind_property('visible', this._networkSecondaryIcon, 'visible',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
}
|
||||
|
||||
this._battery = Main.panel.statusArea.battery;
|
||||
if (this._battery) {
|
||||
this._batteryIcon = this.addIcon(null);
|
||||
this._battery.mainIcon.bind_property('gicon', this._batteryIcon, 'gicon',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
this._battery.mainIcon.bind_property('visible', this._batteryIcon, 'visible',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
}
|
||||
this._battery = new FakeStatusIcon(Main.panel.statusArea.battery);
|
||||
this._box.add_child(this._battery.actor);
|
||||
}
|
||||
});
|
||||
|
@ -5,10 +5,17 @@ const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const NetworkManager = imports.gi.NetworkManager;
|
||||
const NMClient = imports.gi.NMClient;
|
||||
const NMGtk = imports.gi.NMGtk;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
// Some of the new code depends on as-yet-unreleased NM
|
||||
var NMGtk;
|
||||
try {
|
||||
NMGtk = imports.gi.NMGtk;
|
||||
} catch(e) {
|
||||
NMGtk = null;
|
||||
}
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
@ -474,7 +481,8 @@ const NMDevice = new Lang.Class({
|
||||
},
|
||||
|
||||
syncDescription: function() {
|
||||
this.statusItem.label.text = this.device._description;
|
||||
if (this.device._description)
|
||||
this.statusItem.label.text = this.device._description;
|
||||
},
|
||||
|
||||
// protected
|
||||
@ -611,12 +619,11 @@ const NMDevice = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
const NMDeviceWired = new Lang.Class({
|
||||
Name: 'NMDeviceWired',
|
||||
const NMDeviceSimple = new Lang.Class({
|
||||
Name: 'NMDeviceSimple',
|
||||
Extends: NMDevice,
|
||||
|
||||
_init: function(client, device, connections) {
|
||||
this._autoConnectionName = _("Auto Ethernet");
|
||||
this.category = NMConnectionCategory.WIRED;
|
||||
|
||||
this.parent(client, device, connections);
|
||||
@ -631,6 +638,19 @@ const NMDeviceWired = new Lang.Class({
|
||||
// we can do it here because addConnection and removeConnection
|
||||
// both call _createSection at some point
|
||||
this.section.actor.visible = this._connections.length > 1;
|
||||
}
|
||||
});
|
||||
|
||||
const NMDeviceWired = new Lang.Class({
|
||||
Name: 'NMDeviceWired',
|
||||
Extends: NMDeviceSimple,
|
||||
|
||||
_init: function(client, device, connections) {
|
||||
device._description = _("Wired");
|
||||
this._autoConnectionName = _("Auto Ethernet");
|
||||
this.category = NMConnectionCategory.WIRED;
|
||||
|
||||
this.parent(client, device, connections);
|
||||
},
|
||||
|
||||
_createAutomaticConnection: function() {
|
||||
@ -654,6 +674,7 @@ const NMDeviceModem = new Lang.Class({
|
||||
_init: function(client, device, connections) {
|
||||
let is_wwan = false;
|
||||
|
||||
device._description = _("Mobile broadband");
|
||||
this._enabled = true;
|
||||
this.mobileDevice = null;
|
||||
this._connectionType = 'ppp';
|
||||
@ -772,6 +793,7 @@ const NMDeviceBluetooth = new Lang.Class({
|
||||
Extends: NMDevice,
|
||||
|
||||
_init: function(client, device, connections) {
|
||||
device._description = _("Bluetooth");
|
||||
this._autoConnectionName = this._makeConnectionName(device);
|
||||
device.connect('notify::name', Lang.bind(this, this._updateAutoConnectionName));
|
||||
|
||||
@ -1640,6 +1662,7 @@ const NMApplet = new Lang.Class({
|
||||
this._dtypes[NetworkManager.DeviceType.WIFI] = NMDeviceWireless;
|
||||
this._dtypes[NetworkManager.DeviceType.MODEM] = NMDeviceModem;
|
||||
this._dtypes[NetworkManager.DeviceType.BT] = NMDeviceBluetooth;
|
||||
this._dtypes[NetworkManager.DeviceType.INFINIBAND] = NMDeviceSimple;
|
||||
// TODO: WiMax support
|
||||
|
||||
// Connection types
|
||||
@ -1651,6 +1674,7 @@ const NMApplet = new Lang.Class({
|
||||
this._ctypes[NetworkManager.SETTING_BLUETOOTH_SETTING_NAME] = NMConnectionCategory.WWAN;
|
||||
this._ctypes[NetworkManager.SETTING_CDMA_SETTING_NAME] = NMConnectionCategory.WWAN;
|
||||
this._ctypes[NetworkManager.SETTING_GSM_SETTING_NAME] = NMConnectionCategory.WWAN;
|
||||
this._ctypes[NetworkManager.SETTING_INFINIBAND_SETTING_NAME] = NMConnectionCategory.WIRED;
|
||||
this._ctypes[NetworkManager.SETTING_VPN_SETTING_NAME] = NMConnectionCategory.VPN;
|
||||
|
||||
this._settings = NMClient.RemoteSettings.new(null);
|
||||
@ -1781,13 +1805,18 @@ const NMApplet = new Lang.Class({
|
||||
},
|
||||
|
||||
_syncDeviceNames: function() {
|
||||
let names = NMGtk.utils_disambiguate_device_names(this._nmDevices);
|
||||
for (let i = 0; i < this._nmDevices.length; i++) {
|
||||
let device = this._nmDevices[i];
|
||||
if (device._description != names[i]) {
|
||||
if (NMGtk) {
|
||||
let names = NMGtk.utils_disambiguate_device_names(this._nmDevices);
|
||||
for (let i = 0; i < this._nmDevices.length; i++) {
|
||||
let device = this._nmDevices[i];
|
||||
device._description = names[i];
|
||||
device._delegate.syncDescription();
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < this._nmDevices.length; i++) {
|
||||
let device = this._nmDevices[i];
|
||||
device._delegate.syncDescription();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -7,7 +7,7 @@ const St = imports.gi.St;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
const BUS_NAME = 'org.gnome.SettingsDaemon';
|
||||
const BUS_NAME = 'org.gnome.SettingsDaemon.Power';
|
||||
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
|
||||
|
||||
const UPDeviceType = {
|
||||
@ -61,7 +61,7 @@ const Indicator = new Lang.Class({
|
||||
this._primaryDeviceId = null;
|
||||
|
||||
this._batteryItem = new PopupMenu.PopupMenuItem('', { reactive: false });
|
||||
this._primaryPercentage = new St.Label();
|
||||
this._primaryPercentage = new St.Label({ style_class: 'popup-battery-percentage' });
|
||||
this._batteryItem.addActor(this._primaryPercentage, { align: St.Align.END });
|
||||
this.menu.addMenuItem(this._batteryItem);
|
||||
|
||||
@ -183,7 +183,8 @@ const DeviceItem = new Lang.Class({
|
||||
this._box.add_actor(this._label);
|
||||
this.addActor(this._box);
|
||||
|
||||
let percentLabel = new St.Label({ text: C_("percent of battery remaining", "%d%%").format(Math.round(percentage)) });
|
||||
let percentLabel = new St.Label({ text: C_("percent of battery remaining", "%d%%").format(Math.round(percentage)),
|
||||
style_class: 'popup-battery-percentage' });
|
||||
this.addActor(percentLabel, { align: St.Align.END });
|
||||
//FIXME: ideally we would like to expose this._label and percentLabel
|
||||
this.actor.label_actor = percentLabel;
|
||||
|
@ -5,6 +5,7 @@ const Lang = imports.lang;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Gvc = imports.gi.Gvc;
|
||||
const St = imports.gi.St;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
@ -26,79 +27,134 @@ function getMixerControl() {
|
||||
return _mixerControl;
|
||||
}
|
||||
|
||||
const VolumeMenu = new Lang.Class({
|
||||
Name: 'VolumeMenu',
|
||||
Extends: PopupMenu.PopupMenuSection,
|
||||
|
||||
_init: function(control) {
|
||||
this.parent();
|
||||
|
||||
this.hasHeadphones = false;
|
||||
const StreamSlider = new Lang.Class({
|
||||
Name: 'StreamSlider',
|
||||
|
||||
_init: function(control, title) {
|
||||
this._control = control;
|
||||
this._control.connect('state-changed', Lang.bind(this, this._onControlStateChanged));
|
||||
this._control.connect('default-sink-changed', Lang.bind(this, this._readOutput));
|
||||
this._control.connect('default-source-changed', Lang.bind(this, this._readInput));
|
||||
this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput));
|
||||
this._control.connect('stream-removed', Lang.bind(this, this._maybeShowInput));
|
||||
this._volumeMax = this._control.get_vol_max_norm();
|
||||
|
||||
this._output = null;
|
||||
this._outputVolumeId = 0;
|
||||
this._outputMutedId = 0;
|
||||
/* Translators: This is the label for audio volume */
|
||||
this._outputTitle = new PopupMenu.PopupMenuItem(_("Volume"), { reactive: false });
|
||||
this._outputSlider = new PopupMenu.PopupSliderMenuItem(0);
|
||||
this._outputSlider.connect('value-changed', Lang.bind(this, this._sliderChanged, '_output'));
|
||||
this._outputSlider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
|
||||
this.addMenuItem(this._outputTitle);
|
||||
this.addMenuItem(this._outputSlider);
|
||||
this.item = new PopupMenu.PopupMenuSection();
|
||||
|
||||
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this._title = new PopupMenu.PopupMenuItem(title, { reactive: false });
|
||||
this._slider = new PopupMenu.PopupSliderMenuItem(0);
|
||||
this._slider.connect('value-changed', Lang.bind(this, this._sliderChanged));
|
||||
this._slider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
|
||||
|
||||
this._input = null;
|
||||
this._inputVolumeId = 0;
|
||||
this._inputMutedId = 0;
|
||||
this._inputTitle = new PopupMenu.PopupMenuItem(_("Microphone"), { reactive: false });
|
||||
this._inputSlider = new PopupMenu.PopupSliderMenuItem(0);
|
||||
this._inputSlider.connect('value-changed', Lang.bind(this, this._sliderChanged, '_input'));
|
||||
this._inputSlider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
|
||||
this.addMenuItem(this._inputTitle);
|
||||
this.addMenuItem(this._inputSlider);
|
||||
this.item.addMenuItem(this._title);
|
||||
this.item.addMenuItem(this._slider);
|
||||
|
||||
this._onControlStateChanged();
|
||||
this._stream = null;
|
||||
this._shouldShow = true;
|
||||
},
|
||||
|
||||
scroll: function(direction) {
|
||||
let currentVolume = this._output.volume;
|
||||
|
||||
if (direction == Clutter.ScrollDirection.DOWN) {
|
||||
let prev_muted = this._output.is_muted;
|
||||
this._output.volume = Math.max(0, currentVolume - this._volumeMax * VOLUME_ADJUSTMENT_STEP);
|
||||
if (this._output.volume < 1) {
|
||||
this._output.volume = 0;
|
||||
if (!prev_muted)
|
||||
this._output.change_is_muted(true);
|
||||
}
|
||||
this._output.push_volume();
|
||||
}
|
||||
else if (direction == Clutter.ScrollDirection.UP) {
|
||||
this._output.volume = Math.min(this._volumeMax, currentVolume + this._volumeMax * VOLUME_ADJUSTMENT_STEP);
|
||||
this._output.change_is_muted(false);
|
||||
this._output.push_volume();
|
||||
}
|
||||
|
||||
this._notifyVolumeChange();
|
||||
get stream() {
|
||||
return this._stream;
|
||||
},
|
||||
|
||||
_onControlStateChanged: function() {
|
||||
if (this._control.get_state() == Gvc.MixerControlState.READY) {
|
||||
this._readOutput();
|
||||
this._readInput();
|
||||
this._maybeShowInput();
|
||||
set stream(stream) {
|
||||
if (this._stream) {
|
||||
this._disconnectStream(this._stream);
|
||||
}
|
||||
|
||||
this._stream = stream;
|
||||
|
||||
if (this._stream) {
|
||||
this._connectStream(this._stream);
|
||||
this._updateVolume();
|
||||
} else {
|
||||
this.emit('icon-changed', null);
|
||||
this.emit('stream-updated');
|
||||
}
|
||||
|
||||
this._updateVisibility();
|
||||
},
|
||||
|
||||
_disconnectStream: function(stream) {
|
||||
stream.disconnect(this._mutedChangedId);
|
||||
this._mutedChangedId = 0;
|
||||
stream.disconnect(this._volumeChangedId);
|
||||
this._volumeChangedId = 0;
|
||||
},
|
||||
|
||||
_connectStream: function(stream) {
|
||||
this._mutedChangedId = stream.connect('notify::is-muted', Lang.bind(this, this._updateVolume));
|
||||
this._volumeChangedId = stream.connect('notify::volume', Lang.bind(this, this._updateVolume));
|
||||
},
|
||||
|
||||
_shouldBeVisible: function() {
|
||||
return this._stream != null;
|
||||
},
|
||||
|
||||
_updateVisibility: function() {
|
||||
let visible = this._shouldBeVisible();
|
||||
this._title.actor.visible = visible;
|
||||
this._slider.actor.visible = visible;
|
||||
},
|
||||
|
||||
scroll: function(event) {
|
||||
this._slider.scroll(event);
|
||||
},
|
||||
|
||||
setValue: function(value) {
|
||||
// piggy-back off of sliderChanged
|
||||
this._slider.setValue(value);
|
||||
},
|
||||
|
||||
_sliderChanged: function(slider, value, property) {
|
||||
if (!this._stream)
|
||||
return;
|
||||
|
||||
let volume = value * this._control.get_vol_max_norm();
|
||||
let prevMuted = this._stream.is_muted;
|
||||
if (volume < 1) {
|
||||
this._stream.volume = 0;
|
||||
if (!prevMuted)
|
||||
this._stream.change_is_muted(true);
|
||||
} else {
|
||||
this._stream.volume = volume;
|
||||
if (prevMuted)
|
||||
this._stream.change_is_muted(false);
|
||||
}
|
||||
this._stream.push_volume();
|
||||
},
|
||||
|
||||
_notifyVolumeChange: function() {
|
||||
global.cancel_theme_sound(VOLUME_NOTIFY_ID);
|
||||
global.play_theme_sound(VOLUME_NOTIFY_ID, 'audio-volume-change');
|
||||
},
|
||||
|
||||
_updateVolume: function() {
|
||||
let muted = this._stream.is_muted;
|
||||
this._slider.setValue(muted ? 0 : (this._stream.volume / this._control.get_vol_max_norm()));
|
||||
this.emit('stream-updated');
|
||||
},
|
||||
|
||||
getIcon: function() {
|
||||
if (!this._stream)
|
||||
return null;
|
||||
|
||||
let volume = this._stream.volume;
|
||||
if (this._stream.is_muted || volume <= 0) {
|
||||
return 'audio-volume-muted-symbolic';
|
||||
} else {
|
||||
let n = Math.floor(3 * volume / this._control.get_vol_max_norm()) + 1;
|
||||
if (n < 2)
|
||||
return 'audio-volume-low-symbolic';
|
||||
if (n >= 3)
|
||||
return 'audio-volume-high-symbolic';
|
||||
return 'audio-volume-medium-symbolic';
|
||||
}
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(StreamSlider.prototype);
|
||||
|
||||
const OutputStreamSlider = new Lang.Class({
|
||||
Name: 'OutputStreamSlider',
|
||||
Extends: StreamSlider,
|
||||
|
||||
_connectStream: function(stream) {
|
||||
this.parent(stream);
|
||||
this._portChangedId = stream.connect('notify::port', Lang.bind(this, this._portChanged));
|
||||
this._portChanged();
|
||||
},
|
||||
|
||||
_findHeadphones: function(sink) {
|
||||
@ -117,60 +173,41 @@ const VolumeMenu = new Lang.Class({
|
||||
return false;
|
||||
},
|
||||
|
||||
_disconnectStream: function(stream) {
|
||||
this.parent(stream);
|
||||
stream.disconnect(this._portChangedId);
|
||||
this._portChangedId = 0;
|
||||
},
|
||||
|
||||
_portChanged: function() {
|
||||
this.hasHeadphones = this._findHeadphones(this._output);
|
||||
this.emit('headphones-changed');
|
||||
let hasHeadphones = this._findHeadphones(this._stream);
|
||||
if (hasHeadphones != this._hasHeadphones) {
|
||||
this._hasHeadphones = hasHeadphones;
|
||||
this.emit('headphones-changed', this._hasHeadphones);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const InputStreamSlider = new Lang.Class({
|
||||
Name: 'InputStreamSlider',
|
||||
Extends: StreamSlider,
|
||||
|
||||
_init: function(control, title) {
|
||||
this.parent(control, title);
|
||||
this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput));
|
||||
this._control.connect('stream-removed', Lang.bind(this, this._maybeShowInput));
|
||||
},
|
||||
|
||||
_readOutput: function() {
|
||||
if (this._outputVolumeId) {
|
||||
this._output.disconnect(this._outputVolumeId);
|
||||
this._output.disconnect(this._outputMutedId);
|
||||
this._output.disconnect(this._outputPortId);
|
||||
this._outputVolumeId = 0;
|
||||
this._outputMutedId = 0;
|
||||
this._outputPortId = 0;
|
||||
}
|
||||
this._output = this._control.get_default_sink();
|
||||
if (this._output) {
|
||||
this._outputMutedId = this._output.connect('notify::is-muted', Lang.bind(this, this._mutedChanged, '_output'));
|
||||
this._outputVolumeId = this._output.connect('notify::volume', Lang.bind(this, this._volumeChanged, '_output'));
|
||||
this._outputPortId = this._output.connect('notify::port', Lang.bind(this, this._portChanged));
|
||||
|
||||
this._mutedChanged(null, null, '_output');
|
||||
this._volumeChanged(null, null, '_output');
|
||||
this._portChanged();
|
||||
} else {
|
||||
this.hasHeadphones = false;
|
||||
this._outputSlider.setValue(0);
|
||||
this.emit('icon-changed', 'audio-volume-muted-symbolic');
|
||||
}
|
||||
},
|
||||
|
||||
_readInput: function() {
|
||||
if (this._inputVolumeId) {
|
||||
this._input.disconnect(this._inputVolumeId);
|
||||
this._input.disconnect(this._inputMutedId);
|
||||
this._inputVolumeId = 0;
|
||||
this._inputMutedId = 0;
|
||||
}
|
||||
this._input = this._control.get_default_source();
|
||||
if (this._input) {
|
||||
this._inputMutedId = this._input.connect('notify::is-muted', Lang.bind(this, this._mutedChanged, '_input'));
|
||||
this._inputVolumeId = this._input.connect('notify::volume', Lang.bind(this, this._volumeChanged, '_input'));
|
||||
this._mutedChanged (null, null, '_input');
|
||||
this._volumeChanged (null, null, '_input');
|
||||
} else {
|
||||
this._inputTitle.actor.hide();
|
||||
this._inputSlider.actor.hide();
|
||||
}
|
||||
_connectStream: function(stream) {
|
||||
this.parent(stream);
|
||||
this._maybeShowInput();
|
||||
},
|
||||
|
||||
_maybeShowInput: function() {
|
||||
// only show input widgets if any application is recording audio
|
||||
let showInput = false;
|
||||
let recordingApps = this._control.get_source_outputs();
|
||||
if (this._input && recordingApps) {
|
||||
if (this._stream && recordingApps) {
|
||||
for (let i = 0; i < recordingApps.length; i++) {
|
||||
let outputStream = recordingApps[i];
|
||||
let id = outputStream.get_application_id();
|
||||
@ -183,63 +220,70 @@ const VolumeMenu = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
this._inputTitle.actor.visible = showInput;
|
||||
this._inputSlider.actor.visible = showInput;
|
||||
this._showInput = showInput;
|
||||
this._updateVisibility();
|
||||
},
|
||||
|
||||
_volumeToIcon: function(volume) {
|
||||
if (volume <= 0) {
|
||||
return 'audio-volume-muted-symbolic';
|
||||
_shouldBeVisible: function() {
|
||||
return this.parent() && this._showInput;
|
||||
}
|
||||
});
|
||||
|
||||
const VolumeMenu = new Lang.Class({
|
||||
Name: 'VolumeMenu',
|
||||
Extends: PopupMenu.PopupMenuSection,
|
||||
|
||||
_init: function(control) {
|
||||
this.parent();
|
||||
|
||||
this.hasHeadphones = false;
|
||||
|
||||
this._control = control;
|
||||
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));
|
||||
|
||||
/* Translators: This is the label for audio volume */
|
||||
this._output = new OutputStreamSlider(this._control, _("Volume"));
|
||||
this._output.connect('stream-updated', Lang.bind(this, function() {
|
||||
this.emit('icon-changed');
|
||||
}));
|
||||
this._output.connect('headphones-changed', Lang.bind(this, function(stream, value) {
|
||||
this.emit('headphones-changed', value);
|
||||
}));
|
||||
this.addMenuItem(this._output.item);
|
||||
|
||||
this._input = new InputStreamSlider(this._control, _("Microphone"));
|
||||
this.addMenuItem(this._input.item);
|
||||
|
||||
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
|
||||
this._onControlStateChanged();
|
||||
},
|
||||
|
||||
scroll: function(event) {
|
||||
this._output.scroll(event);
|
||||
},
|
||||
|
||||
_onControlStateChanged: function() {
|
||||
if (this._control.get_state() == Gvc.MixerControlState.READY) {
|
||||
this._readInput();
|
||||
this._readOutput();
|
||||
} else {
|
||||
let n = Math.floor(3 * volume / this._volumeMax) + 1;
|
||||
if (n < 2)
|
||||
return 'audio-volume-low-symbolic';
|
||||
if (n >= 3)
|
||||
return 'audio-volume-high-symbolic';
|
||||
return 'audio-volume-medium-symbolic';
|
||||
this.emit('icon-changed');
|
||||
}
|
||||
},
|
||||
|
||||
_sliderChanged: function(slider, value, property) {
|
||||
if (this[property] == null) {
|
||||
log ('Volume slider changed for %s, but %s does not exist'.format(property, property));
|
||||
return;
|
||||
}
|
||||
let volume = value * this._volumeMax;
|
||||
let prev_muted = this[property].is_muted;
|
||||
if (volume < 1) {
|
||||
this[property].volume = 0;
|
||||
if (!prev_muted)
|
||||
this[property].change_is_muted(true);
|
||||
} else {
|
||||
this[property].volume = volume;
|
||||
if (prev_muted)
|
||||
this[property].change_is_muted(false);
|
||||
}
|
||||
this[property].push_volume();
|
||||
_readOutput: function() {
|
||||
this._output.stream = this._control.get_default_sink();
|
||||
},
|
||||
|
||||
_notifyVolumeChange: function() {
|
||||
global.cancel_theme_sound(VOLUME_NOTIFY_ID);
|
||||
global.play_theme_sound(VOLUME_NOTIFY_ID, 'audio-volume-change');
|
||||
_readInput: function() {
|
||||
this._input.stream = this._control.get_default_source();
|
||||
},
|
||||
|
||||
_mutedChanged: function(object, param_spec, property) {
|
||||
let muted = this[property].is_muted;
|
||||
let slider = this[property+'Slider'];
|
||||
slider.setValue(muted ? 0 : (this[property].volume / this._volumeMax));
|
||||
if (property == '_output') {
|
||||
if (muted)
|
||||
this.emit('icon-changed', 'audio-volume-muted-symbolic');
|
||||
else
|
||||
this.emit('icon-changed', this._volumeToIcon(this._output.volume));
|
||||
}
|
||||
},
|
||||
|
||||
_volumeChanged: function(object, param_spec, property) {
|
||||
this[property+'Slider'].setValue(this[property].volume / this._volumeMax);
|
||||
if (property == '_output' && !this._output.is_muted)
|
||||
this.emit('icon-changed', this._volumeToIcon(this._output.volume));
|
||||
getIcon: function() {
|
||||
return this._output.getIcon();
|
||||
}
|
||||
});
|
||||
|
||||
@ -252,13 +296,13 @@ const Indicator = new Lang.Class({
|
||||
|
||||
this._control = getMixerControl();
|
||||
this._volumeMenu = new VolumeMenu(this._control);
|
||||
this._volumeMenu.connect('icon-changed', Lang.bind(this, function(menu, icon) {
|
||||
this._hasPulseAudio = (icon != null);
|
||||
this._volumeMenu.connect('icon-changed', Lang.bind(this, function(menu) {
|
||||
let icon = this._volumeMenu.getIcon();
|
||||
this.actor.visible = (icon != null);
|
||||
this.setIcon(icon);
|
||||
this._syncVisibility();
|
||||
}));
|
||||
this._volumeMenu.connect('headphones-changed', Lang.bind(this, function() {
|
||||
this._syncVisibility();
|
||||
this._volumeMenu.connect('headphones-changed', Lang.bind(this, function(menu, value) {
|
||||
this._headphoneIcon.visible = value;
|
||||
}));
|
||||
|
||||
this._headphoneIcon = this.addIcon(new Gio.ThemedIcon({ name: 'headphones-symbolic' }));
|
||||
@ -272,13 +316,7 @@ const Indicator = new Lang.Class({
|
||||
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||
},
|
||||
|
||||
_syncVisibility: function() {
|
||||
this.actor.visible = this._hasPulseAudio;
|
||||
this.mainIcon.visible = this._hasPulseAudio;
|
||||
this._headphoneIcon.visible = this._hasPulseAudio && this._volumeMenu.hasHeadphones;
|
||||
},
|
||||
|
||||
_onScrollEvent: function(actor, event) {
|
||||
this._volumeMenu.scroll(event.get_scroll_direction());
|
||||
this._volumeMenu.scroll(event);
|
||||
}
|
||||
});
|
||||
|
@ -637,5 +637,6 @@ function drawArrow(area, side) {
|
||||
|
||||
Clutter.cairo_set_source_color(cr, bodyColor);
|
||||
cr.fill();
|
||||
cr.$dispose();
|
||||
}
|
||||
|
||||
|
@ -198,18 +198,23 @@ const UnlockDialog = new Lang.Class({
|
||||
x_align: St.Align.END,
|
||||
y_align: St.Align.MIDDLE });
|
||||
|
||||
let otherUserLabel = new St.Label({ text: _("Log in as another user"),
|
||||
style_class: 'login-dialog-not-listed-label' });
|
||||
this._otherUserButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
|
||||
can_focus: true,
|
||||
child: otherUserLabel,
|
||||
reactive: true,
|
||||
x_align: St.Align.START,
|
||||
x_fill: true });
|
||||
this._otherUserButton.connect('clicked', Lang.bind(this, this._otherUserClicked));
|
||||
this.dialogLayout.add(this._otherUserButton,
|
||||
{ x_align: St.Align.START,
|
||||
x_fill: false });
|
||||
let screenSaverSettings = new Gio.Settings({ schema: 'org.gnome.desktop.screensaver' });
|
||||
if (screenSaverSettings.get_boolean('user-switch-enabled')) {
|
||||
let otherUserLabel = new St.Label({ text: _("Log in as another user"),
|
||||
style_class: 'login-dialog-not-listed-label' });
|
||||
this._otherUserButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
|
||||
can_focus: true,
|
||||
child: otherUserLabel,
|
||||
reactive: true,
|
||||
x_align: St.Align.START,
|
||||
x_fill: true });
|
||||
this._otherUserButton.connect('clicked', Lang.bind(this, this._otherUserClicked));
|
||||
this.dialogLayout.add(this._otherUserButton,
|
||||
{ x_align: St.Align.START,
|
||||
x_fill: false });
|
||||
} else {
|
||||
this._otherUserButton = null;
|
||||
}
|
||||
|
||||
this._updateSensitivity(true);
|
||||
|
||||
@ -231,8 +236,10 @@ const UnlockDialog = new Lang.Class({
|
||||
this._promptEntry.reactive = sensitive;
|
||||
this._promptEntry.clutter_text.editable = sensitive;
|
||||
this._updateOkButtonSensitivity(sensitive && this._promptEntry.text.length > 0);
|
||||
this._otherUserButton.reactive = sensitive;
|
||||
this._otherUserButton.can_focus = sensitive;
|
||||
if (this._otherUserButton) {
|
||||
this._otherUserButton.reactive = sensitive;
|
||||
this._otherUserButton.can_focus = sensitive;
|
||||
}
|
||||
},
|
||||
|
||||
_updateOkButtonSensitivity: function(sensitive) {
|
||||
|
@ -593,6 +593,7 @@ const UserMenuButton = new Lang.Class({
|
||||
|
||||
this.setSensitive(!Main.sessionMode.isLocked);
|
||||
this._updatePresenceIcon();
|
||||
this._updateUserName();
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
|
@ -39,17 +39,11 @@ const ViewSelector = new Lang.Class({
|
||||
Name: 'ViewSelector',
|
||||
|
||||
_init : function(searchEntry, showAppsButton) {
|
||||
this.actor = new St.BoxLayout({ name: 'viewSelector',
|
||||
vertical: true });
|
||||
this.actor = new Shell.Stack({ name: 'viewSelector' });
|
||||
|
||||
this._showAppsButton = showAppsButton;
|
||||
this._showAppsButton.connect('notify::checked', Lang.bind(this, this._onShowAppsButtonToggled));
|
||||
|
||||
this._pageArea = new Shell.Stack();
|
||||
this.actor.add(this._pageArea, { x_fill: true,
|
||||
y_fill: true,
|
||||
expand: true });
|
||||
|
||||
this._activePage = null;
|
||||
|
||||
this.active = false;
|
||||
@ -138,17 +132,6 @@ const ViewSelector = new Lang.Class({
|
||||
}
|
||||
}));
|
||||
|
||||
// Public constraints which may be used to tie actors' height or
|
||||
// vertical position to the current tab's content; as the content's
|
||||
// height and position depend on the view selector's style properties
|
||||
// (e.g. font size, padding, spacing, ...) it would be extremely hard
|
||||
// and ugly to get these from the outside. While it would be possible
|
||||
// to use position and height properties directly, outside code would
|
||||
// need to ensure that the content is properly allocated before
|
||||
// accessing the properties.
|
||||
this.constrainHeight = new Clutter.BindConstraint({ source: this._pageArea,
|
||||
coordinate: Clutter.BindCoordinate.HEIGHT });
|
||||
|
||||
Main.wm.addKeybinding('toggle-application-view',
|
||||
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
|
||||
Meta.KeyBindingFlags.NONE,
|
||||
@ -202,7 +185,7 @@ const ViewSelector = new Lang.Class({
|
||||
this._a11yFocusPage(page);
|
||||
})
|
||||
});;
|
||||
this._pageArea.add_actor(page);
|
||||
this.actor.add_actor(page);
|
||||
return page;
|
||||
},
|
||||
|
||||
|
@ -10,7 +10,6 @@ const IconGrid = imports.ui.iconGrid;
|
||||
const Layout = imports.ui.layout;
|
||||
const Main = imports.ui.main;
|
||||
const Panel = imports.ui.panel;
|
||||
const Search = imports.ui.search;
|
||||
|
||||
// we could make these gsettings
|
||||
const FISH_NAME = 'wanda';
|
||||
@ -71,8 +70,7 @@ const WandaIconBin = new Lang.Class({
|
||||
Name: 'WandaIconBin',
|
||||
|
||||
_init: function(fish, label, params) {
|
||||
this.actor = new St.Bin({ style_class: 'search-result-content',
|
||||
reactive: true,
|
||||
this.actor = new St.Bin({ reactive: true,
|
||||
track_hover: true });
|
||||
this.icon = new WandaIcon(fish, label, params);
|
||||
|
||||
@ -133,10 +131,9 @@ function capitalize(str) {
|
||||
|
||||
const WandaSearchProvider = new Lang.Class({
|
||||
Name: 'WandaSearchProvider',
|
||||
Extends: Search.SearchProvider,
|
||||
|
||||
_init: function() {
|
||||
this.parent(_("Your favorite Easter Egg"));
|
||||
this.id = 'wanda';
|
||||
},
|
||||
|
||||
getResultMetas: function(fish, callback) {
|
||||
|
@ -191,8 +191,10 @@ const WindowManager = new Lang.Class({
|
||||
},
|
||||
|
||||
addKeybinding: function(name, settings, flags, modes, handler) {
|
||||
if (global.display.add_keybinding(name, settings, flags, handler))
|
||||
let action = global.display.add_keybinding(name, settings, flags, handler);
|
||||
if (action != Meta.KeyBindingAction.NONE)
|
||||
this.allowKeybinding(name, modes);
|
||||
return action;
|
||||
},
|
||||
|
||||
removeKeybinding: function(name) {
|
||||
@ -219,7 +221,9 @@ const WindowManager = new Lang.Class({
|
||||
_shouldAnimateActor: function(actor) {
|
||||
if (!this._shouldAnimate())
|
||||
return false;
|
||||
return actor.meta_window.get_window_type() == Meta.WindowType.NORMAL;
|
||||
let windowType = actor.meta_window.get_window_type();
|
||||
return windowType == Meta.WindowType.NORMAL ||
|
||||
windowType == Meta.WindowType.MODAL_DIALOG;
|
||||
},
|
||||
|
||||
_removeEffect : function(list, actor) {
|
||||
@ -245,16 +249,17 @@ const WindowManager = new Lang.Class({
|
||||
*/
|
||||
this._minimizing.push(actor);
|
||||
|
||||
let primary = Main.layoutManager.primaryMonitor;
|
||||
let xDest = primary.x;
|
||||
let monitor = Main.layoutManager.findMonitorForWindow(actor.meta_window);
|
||||
let xDest = monitor.x;
|
||||
let yDest = monitor.y;
|
||||
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
|
||||
xDest += primary.width;
|
||||
xDest += monitor.width;
|
||||
|
||||
Tweener.addTween(actor,
|
||||
{ scale_x: 0.0,
|
||||
scale_y: 0.0,
|
||||
x: xDest,
|
||||
y: 0,
|
||||
y: yDest,
|
||||
time: WINDOW_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: this._minimizeWindowDone,
|
||||
@ -369,57 +374,56 @@ const WindowManager = new Lang.Class({
|
||||
|
||||
actor._windowType = type;
|
||||
}));
|
||||
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);
|
||||
actor.scale_gravity = Clutter.Gravity.CENTER;
|
||||
actor.show();
|
||||
this._mapping.push(actor);
|
||||
|
||||
Tweener.addTween(actor,
|
||||
{ scale_y: 1,
|
||||
time: WINDOW_ANIMATION_TIME,
|
||||
transition: "easeOutQuad",
|
||||
onComplete: this._mapWindowDone,
|
||||
onCompleteScope: this,
|
||||
onCompleteParams: [shellwm, actor],
|
||||
onOverwrite: this._mapWindowOverwrite,
|
||||
onOverwriteScope: this,
|
||||
onOverwriteParams: [shellwm, actor]
|
||||
});
|
||||
return;
|
||||
}
|
||||
shellwm.completed_map(actor);
|
||||
return;
|
||||
}
|
||||
if (!this._shouldAnimateActor(actor)) {
|
||||
shellwm.completed_map(actor);
|
||||
return;
|
||||
}
|
||||
|
||||
actor.opacity = 0;
|
||||
actor.show();
|
||||
if (actor.meta_window.is_attached_dialog()) {
|
||||
/* Scale the window from the center of the parent */
|
||||
this._checkDimming(actor.get_meta_window().get_transient_for());
|
||||
actor.set_scale(1.0, 0.0);
|
||||
actor.scale_gravity = Clutter.Gravity.CENTER;
|
||||
actor.show();
|
||||
this._mapping.push(actor);
|
||||
|
||||
/* Fade window in */
|
||||
this._mapping.push(actor);
|
||||
Tweener.addTween(actor,
|
||||
{ opacity: 255,
|
||||
time: WINDOW_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: this._mapWindowDone,
|
||||
onCompleteScope: this,
|
||||
onCompleteParams: [shellwm, actor],
|
||||
onOverwrite: this._mapWindowOverwrite,
|
||||
onOverwriteScope: this,
|
||||
onOverwriteParams: [shellwm, actor]
|
||||
});
|
||||
Tweener.addTween(actor,
|
||||
{ scale_y: 1,
|
||||
time: WINDOW_ANIMATION_TIME,
|
||||
transition: "easeOutQuad",
|
||||
onComplete: this._mapWindowDone,
|
||||
onCompleteScope: this,
|
||||
onCompleteParams: [shellwm, actor],
|
||||
onOverwrite: this._mapWindowOverwrite,
|
||||
onOverwriteScope: this,
|
||||
onOverwriteParams: [shellwm, actor]
|
||||
});
|
||||
} else {
|
||||
/* Fade window in */
|
||||
actor.opacity = 0;
|
||||
actor.show();
|
||||
this._mapping.push(actor);
|
||||
|
||||
Tweener.addTween(actor,
|
||||
{ opacity: 255,
|
||||
time: WINDOW_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: this._mapWindowDone,
|
||||
onCompleteScope: this,
|
||||
onCompleteParams: [shellwm, actor],
|
||||
onOverwrite: this._mapWindowOverwrite,
|
||||
onOverwriteScope: this,
|
||||
onOverwriteParams: [shellwm, actor]
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_mapWindowDone : function(shellwm, actor) {
|
||||
if (this._removeEffect(this._mapping, actor)) {
|
||||
Tweener.removeTweens(actor);
|
||||
actor.opacity = 255;
|
||||
actor.scale_y = 1;
|
||||
shellwm.completed_map(actor);
|
||||
}
|
||||
},
|
||||
@ -441,18 +445,21 @@ const WindowManager = new Lang.Class({
|
||||
return win != window;
|
||||
});
|
||||
}
|
||||
|
||||
if (!this._shouldAnimateActor(actor)) {
|
||||
shellwm.completed_destroy(actor);
|
||||
return;
|
||||
}
|
||||
|
||||
this._destroying.push(actor);
|
||||
|
||||
if (window.is_attached_dialog()) {
|
||||
let parent = window.get_transient_for();
|
||||
this._checkDimming(parent, window);
|
||||
if (!this._shouldAnimate()) {
|
||||
shellwm.completed_destroy(actor);
|
||||
return;
|
||||
}
|
||||
|
||||
actor.set_scale(1.0, 1.0);
|
||||
actor.scale_gravity = Clutter.Gravity.CENTER;
|
||||
actor.show();
|
||||
this._destroying.push(actor);
|
||||
|
||||
actor._parentDestroyId = parent.connect('unmanaged', Lang.bind(this, function () {
|
||||
Tweener.removeTweens(actor);
|
||||
|
@ -1,7 +1,6 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
@ -30,9 +29,6 @@ const CLOSE_BUTTON_FADE_TIME = 0.1;
|
||||
|
||||
const DRAGGING_WINDOW_OPACITY = 100;
|
||||
|
||||
const BUTTON_LAYOUT_SCHEMA = 'org.gnome.shell.overrides';
|
||||
const BUTTON_LAYOUT_KEY = 'button-layout';
|
||||
|
||||
// When calculating a layout, we calculate the scale of windows and the percent
|
||||
// of the available area the new layout uses. If the values for the new layout,
|
||||
// when weighted with the values as below, are worse than the previous layout's,
|
||||
@ -124,6 +120,8 @@ const WindowClone = new Lang.Class({
|
||||
|
||||
this.actor._delegate = this;
|
||||
|
||||
this._slot = [0, 0, 0, 0];
|
||||
this._dragSlot = [0, 0, 0, 0];
|
||||
this._stackAbove = null;
|
||||
|
||||
this._sizeChangedId = this.realWindow.connect('size-changed',
|
||||
@ -159,22 +157,15 @@ const WindowClone = new Lang.Class({
|
||||
this._selected = false;
|
||||
},
|
||||
|
||||
set slot(slot) {
|
||||
this._slot = slot;
|
||||
},
|
||||
|
||||
get slot() {
|
||||
let x, y, w, h;
|
||||
|
||||
if (this.inDrag) {
|
||||
x = this.dragOrigX;
|
||||
y = this.dragOrigY;
|
||||
w = this.actor.width * this.dragOrigScale;
|
||||
h = this.actor.height * this.dragOrigScale;
|
||||
} else {
|
||||
x = this.actor.x;
|
||||
y = this.actor.y;
|
||||
w = this.actor.width * this.actor.scale_x;
|
||||
h = this.actor.height * this.actor.scale_y;
|
||||
}
|
||||
|
||||
return [x, y, w, h];
|
||||
if (this.inDrag)
|
||||
return this._dragSlot;
|
||||
else
|
||||
return this._slot;
|
||||
},
|
||||
|
||||
setStackAbove: function (actor) {
|
||||
@ -265,23 +256,37 @@ const WindowClone = new Lang.Class({
|
||||
|
||||
_onScroll : function (actor, event) {
|
||||
let direction = event.get_scroll_direction();
|
||||
if (direction == Clutter.ScrollDirection.UP) {
|
||||
let delta;
|
||||
|
||||
if (event.is_pointer_emulated())
|
||||
return;
|
||||
|
||||
if (direction == Clutter.ScrollDirection.DOWN) {
|
||||
delta = -SCROLL_SCALE_AMOUNT;
|
||||
} else if (direction == Clutter.ScrollDirection.UP) {
|
||||
delta = +SCROLL_SCALE_AMOUNT;
|
||||
} else if (direction == Clutter.ScrollDirection.SMOOTH) {
|
||||
let [dx, dy] = event.get_scroll_delta();
|
||||
delta = -dy * 10;
|
||||
}
|
||||
|
||||
if (delta > 0) {
|
||||
if (this._zoomStep == undefined)
|
||||
this._zoomStart();
|
||||
if (this._zoomStep < 100) {
|
||||
this._zoomStep += SCROLL_SCALE_AMOUNT;
|
||||
this._zoomStep += delta;
|
||||
this._zoomStep = Math.min(100, this._zoomStep);
|
||||
this._zoomUpdate();
|
||||
}
|
||||
} else if (direction == Clutter.ScrollDirection.DOWN) {
|
||||
} else if (delta < 0) {
|
||||
if (this._zoomStep > 0) {
|
||||
this._zoomStep -= SCROLL_SCALE_AMOUNT;
|
||||
this._zoomStep += delta;
|
||||
this._zoomStep = Math.max(0, this._zoomStep);
|
||||
this._zoomUpdate();
|
||||
}
|
||||
if (this._zoomStep <= 0.0)
|
||||
this._zoomEnd();
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
_zoomUpdate : function () {
|
||||
@ -392,6 +397,7 @@ const WindowClone = new Lang.Class({
|
||||
if (this._zooming)
|
||||
this._zoomEnd();
|
||||
|
||||
this._dragSlot = this._slot;
|
||||
[this.dragOrigX, this.dragOrigY] = this.actor.get_position();
|
||||
this.dragOrigScale = this.actor.scale_x;
|
||||
this.inDrag = true;
|
||||
@ -456,11 +462,7 @@ const WindowOverlay = new Lang.Class({
|
||||
this._updateCaptionId = metaWindow.connect('notify::title',
|
||||
Lang.bind(this, function(w) {
|
||||
this.title.text = w.title;
|
||||
// we need this for the next call to get_preferred_width
|
||||
// to return useful results
|
||||
this.title.set_size(-1, -1);
|
||||
|
||||
this._repositionSelf();
|
||||
this.relayout(false);
|
||||
}));
|
||||
|
||||
let button = new St.Button({ style_class: 'window-close' });
|
||||
@ -535,35 +537,14 @@ const WindowOverlay = new Lang.Class({
|
||||
return [this.borderSize, this.borderSize];
|
||||
},
|
||||
|
||||
_repositionSelf: function() {
|
||||
relayout: function(animate) {
|
||||
let [cloneX, cloneY, cloneWidth, cloneHeight] = this._windowClone.slot;
|
||||
this.updatePositions(cloneX, cloneY, cloneWidth, cloneHeight, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* @cloneX: x position of windowClone
|
||||
* @cloneY: y position of windowClone
|
||||
* @cloneWidth: width of windowClone
|
||||
* @cloneHeight height of windowClone
|
||||
*/
|
||||
// These parameters are not the values retrieved with
|
||||
// get_transformed_position() and get_transformed_size(),
|
||||
// as windowClone might be moving.
|
||||
// See Workspace._showWindowOverlay
|
||||
updatePositions: function(cloneX, cloneY, cloneWidth, cloneHeight, animate) {
|
||||
let button = this.closeButton;
|
||||
let title = this.title;
|
||||
|
||||
let settings = new Gio.Settings({ schema: BUTTON_LAYOUT_SCHEMA });
|
||||
let layout = settings.get_string(BUTTON_LAYOUT_KEY);
|
||||
let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL;
|
||||
|
||||
let split = layout.split(":");
|
||||
let side;
|
||||
if (split[0].indexOf("close") > -1)
|
||||
side = rtl ? St.Side.RIGHT : St.Side.LEFT;
|
||||
else
|
||||
side = rtl ? St.Side.LEFT : St.Side.RIGHT;
|
||||
let layout = Meta.prefs_get_button_layout();
|
||||
let side = layout.left_buttons.indexOf(Meta.ButtonFunction.CLOSE) > -1 ? St.Side.LEFT : St.Side.RIGHT;
|
||||
|
||||
let buttonX;
|
||||
let buttonY = cloneY - (button.height - button._overlap);
|
||||
@ -577,8 +558,16 @@ const WindowOverlay = new Lang.Class({
|
||||
else
|
||||
button.set_position(Math.floor(buttonX), Math.floor(buttonY));
|
||||
|
||||
// Clutter.Actor.get_preferred_width() will return the fixed width if one
|
||||
// is set, so we need to reset the width by calling set_width(-1), to forward
|
||||
// the call down to StLabel.
|
||||
// We also need to save and restore the current width, otherwise the animation
|
||||
// starts from the wrong point.
|
||||
let prevTitleWidth = title.width;
|
||||
title.set_width(-1);
|
||||
let [titleMinWidth, titleNatWidth] = title.get_preferred_width(-1);
|
||||
let titleWidth = Math.max(titleMinWidth, Math.min(titleNatWidth, cloneWidth));
|
||||
title.width = prevTitleWidth;
|
||||
|
||||
let titleX = cloneX + (cloneWidth - titleWidth) / 2;
|
||||
let titleY = cloneY + cloneHeight + title._spacing;
|
||||
@ -1184,8 +1173,8 @@ const Workspace = new Lang.Class({
|
||||
let slot = slots[i];
|
||||
let clone = clones[i];
|
||||
let metaWindow = clone.metaWindow;
|
||||
let mainIndex = this._lookupIndex(metaWindow);
|
||||
let overlay = this._windowOverlays[mainIndex];
|
||||
let overlay = clone.overlay;
|
||||
clone.slotId = i;
|
||||
|
||||
// Positioning a window currently being dragged must be avoided;
|
||||
// we'll just leave a blank spot in the layout for it.
|
||||
@ -1193,6 +1182,7 @@ const Workspace = new Lang.Class({
|
||||
continue;
|
||||
|
||||
let [x, y, scale] = slot;
|
||||
clone.slot = [x, y, clone.actor.width * scale, clone.actor.height * scale];
|
||||
|
||||
if (overlay && initialPositioning)
|
||||
overlay.hide();
|
||||
@ -1224,7 +1214,7 @@ const Workspace = new Lang.Class({
|
||||
Tweener.removeTweens(clone.actor);
|
||||
clone.actor.set_position(x, y);
|
||||
clone.actor.set_scale(scale, scale);
|
||||
this._updateWindowOverlayPositions(clone, overlay, x, y, scale, false);
|
||||
clone.overlay.relayout(false);
|
||||
this._showWindowOverlay(clone, overlay, isOnCurrentWorkspace);
|
||||
}
|
||||
}
|
||||
@ -1259,15 +1249,7 @@ const Workspace = new Lang.Class({
|
||||
})
|
||||
});
|
||||
|
||||
this._updateWindowOverlayPositions(clone, overlay, x, y, scale, true);
|
||||
},
|
||||
|
||||
_updateWindowOverlayPositions: function(clone, overlay, x, y, scale, animate) {
|
||||
if (!overlay)
|
||||
return;
|
||||
|
||||
let [cloneWidth, cloneHeight] = clone.actor.get_size();
|
||||
overlay.updatePositions(x, y, cloneWidth * scale, cloneHeight * scale, animate);
|
||||
clone.overlay.relayout(true);
|
||||
},
|
||||
|
||||
_showWindowOverlay: function(clone, overlay, fade) {
|
||||
@ -1402,9 +1384,10 @@ const Workspace = new Lang.Class({
|
||||
let scale = win._overviewHint.scale;
|
||||
delete win._overviewHint;
|
||||
|
||||
clone.slot = [x, y, clone.actor.width * scale, clone.actor.height * scale];
|
||||
clone.actor.set_position (x, y);
|
||||
clone.actor.set_scale (scale, scale);
|
||||
this._updateWindowOverlayPositions(clone, overlay, x, y, scale, false);
|
||||
clone.overlay.relayout(false);
|
||||
} else {
|
||||
// Position new windows at the top corner of the workspace rather
|
||||
// than where they were placed for real to avoid the window
|
||||
@ -1569,6 +1552,7 @@ const Workspace = new Lang.Class({
|
||||
_addWindowClone : function(win) {
|
||||
let clone = new WindowClone(win, this);
|
||||
let overlay = new WindowOverlay(clone, this._windowOverlaysGroup);
|
||||
clone.overlay = overlay;
|
||||
|
||||
clone.connect('selected',
|
||||
Lang.bind(this, this._onCloneSelected));
|
||||
|
@ -1,6 +1,7 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
@ -27,6 +28,8 @@ const WORKSPACE_CUT_SIZE = 10;
|
||||
|
||||
const WORKSPACE_KEEP_ALIVE_TIME = 100;
|
||||
|
||||
const OVERRIDE_SCHEMA = 'org.gnome.shell.overrides';
|
||||
|
||||
const WindowClone = new Lang.Class({
|
||||
Name: 'WindowClone',
|
||||
|
||||
@ -541,6 +544,12 @@ const ThumbnailsBox = new Lang.Class({
|
||||
|
||||
this.actor.connect('button-press-event', function() { return true; });
|
||||
this.actor.connect('button-release-event', Lang.bind(this, this._onButtonRelease));
|
||||
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||
|
||||
Main.overview.connect('showing',
|
||||
Lang.bind(this, this._createThumbnails));
|
||||
Main.overview.connect('hidden',
|
||||
Lang.bind(this, this._destroyThumbnails));
|
||||
|
||||
Main.overview.connect('item-drag-begin',
|
||||
Lang.bind(this, this._onDragBegin));
|
||||
@ -554,6 +563,16 @@ const ThumbnailsBox = new Lang.Class({
|
||||
Lang.bind(this, this._onDragEnd));
|
||||
Main.overview.connect('window-drag-cancelled',
|
||||
Lang.bind(this, this._onDragCancelled));
|
||||
|
||||
this._settings = new Gio.Settings({ schema: OVERRIDE_SCHEMA });
|
||||
this._settings.connect('changed::dynamic-workspaces',
|
||||
Lang.bind(this, this._updateSwitcherVisibility));
|
||||
},
|
||||
|
||||
_updateSwitcherVisibility: function() {
|
||||
this.actor.visible =
|
||||
this._settings.get_boolean('dynamic-workspaces') ||
|
||||
global.screen.n_workspaces > 1;
|
||||
},
|
||||
|
||||
_onButtonRelease: function(actor, event) {
|
||||
@ -719,10 +738,16 @@ const ThumbnailsBox = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
show: function() {
|
||||
_createThumbnails: function() {
|
||||
this._switchWorkspaceNotifyId =
|
||||
global.window_manager.connect('switch-workspace',
|
||||
Lang.bind(this, this._activeWorkspaceChanged));
|
||||
this._nWorkspacesNotifyId =
|
||||
global.screen.connect('notify::n-workspaces',
|
||||
Lang.bind(this, this._workspacesChanged));
|
||||
this._syncStackingId =
|
||||
Main.overview.connect('windows-restacked',
|
||||
Lang.bind(this, this._syncStacking));
|
||||
|
||||
this._targetScale = 0;
|
||||
this._scale = 0;
|
||||
@ -744,19 +769,54 @@ const ThumbnailsBox = new Lang.Class({
|
||||
};
|
||||
|
||||
this.addThumbnails(0, global.screen.n_workspaces);
|
||||
|
||||
this._updateSwitcherVisibility();
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
_destroyThumbnails: function() {
|
||||
if (this._switchWorkspaceNotifyId > 0) {
|
||||
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
|
||||
this._switchWorkspaceNotifyId = 0;
|
||||
}
|
||||
if (this._nWorkspacesNotifyId > 0) {
|
||||
global.screen.disconnect(this._nWorkspacesNotifyId);
|
||||
this._nWorkspacesNotifyId = 0;
|
||||
}
|
||||
|
||||
if (this._syncStackingId > 0) {
|
||||
Main.overview.disconnect(this._syncStackingId);
|
||||
this._syncStackingId = 0;
|
||||
}
|
||||
|
||||
for (let w = 0; w < this._thumbnails.length; w++)
|
||||
this._thumbnails[w].destroy();
|
||||
this._thumbnails = [];
|
||||
},
|
||||
|
||||
_workspacesChanged: function() {
|
||||
let oldNumWorkspaces = this._thumbnails.length;
|
||||
let newNumWorkspaces = global.screen.n_workspaces;
|
||||
let active = global.screen.get_active_workspace_index();
|
||||
|
||||
if (newNumWorkspaces > oldNumWorkspaces) {
|
||||
this.addThumbnails(oldNumWorkspaces, newNumWorkspaces - oldNumWorkspaces);
|
||||
} else {
|
||||
let removedIndex;
|
||||
let removedNum = oldNumWorkspaces - newNumWorkspaces;
|
||||
for (let w = 0; w < oldNumWorkspaces; w++) {
|
||||
let metaWorkspace = global.screen.get_workspace_by_index(w);
|
||||
if (this._thumbnails[w].metaWorkspace != metaWorkspace) {
|
||||
removedIndex = w;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.removeThumbnails(removedIndex, removedNum);
|
||||
}
|
||||
|
||||
this._updateSwitcherVisibility();
|
||||
},
|
||||
|
||||
addThumbnails: function(start, count) {
|
||||
for (let k = start; k < start + count; k++) {
|
||||
let metaWorkspace = global.screen.get_workspace_by_index(k);
|
||||
@ -802,7 +862,7 @@ const ThumbnailsBox = new Lang.Class({
|
||||
this._queueUpdateStates();
|
||||
},
|
||||
|
||||
syncStacking: function(stackIndices) {
|
||||
_syncStacking: function(overview, stackIndices) {
|
||||
for (let i = 0; i < this._thumbnails.length; i++)
|
||||
this._thumbnails[i].syncStacking(stackIndices);
|
||||
},
|
||||
@ -1157,5 +1217,16 @@ const ThumbnailsBox = new Lang.Class({
|
||||
},
|
||||
onCompleteScope: this
|
||||
});
|
||||
},
|
||||
|
||||
_onScrollEvent: function (actor, event) {
|
||||
switch (event.get_scroll_direction()) {
|
||||
case Clutter.ScrollDirection.UP:
|
||||
Main.wm.actionMoveWorkspace(Meta.MotionDirection.UP);
|
||||
break;
|
||||
case Clutter.ScrollDirection.DOWN:
|
||||
Main.wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -69,7 +69,7 @@ const WorkspacesView = new Lang.Class({
|
||||
|
||||
// Add workspace actors
|
||||
for (let w = 0; w < global.screen.n_workspaces; w++)
|
||||
this._workspaces[w].actor.reparent(this.actor);
|
||||
this.actor.add_actor(this._workspaces[w].actor);
|
||||
this._workspaces[activeWorkspaceIndex].actor.raise_top();
|
||||
|
||||
this._extraWorkspaces = [];
|
||||
@ -129,11 +129,10 @@ const WorkspacesView = new Lang.Class({
|
||||
continue;
|
||||
|
||||
let ws = new Workspace.Workspace(null, i);
|
||||
let overviewSpacing = Main.overview._spacing;
|
||||
ws.setGeometry(monitors[i].x + overviewSpacing/2,
|
||||
monitors[i].y + overviewSpacing/2,
|
||||
monitors[i].width - overviewSpacing,
|
||||
monitors[i].height - overviewSpacing);
|
||||
ws.setGeometry(monitors[i].x,
|
||||
monitors[i].y,
|
||||
monitors[i].width,
|
||||
monitors[i].height);
|
||||
global.overlay_group.add_actor(ws.actor);
|
||||
this._extraWorkspaces.push(ws);
|
||||
}
|
||||
@ -438,13 +437,24 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
Name: 'WorkspacesDisplay',
|
||||
|
||||
_init: function() {
|
||||
this.actor = new Shell.GenericContainer();
|
||||
this.actor = new Shell.GenericContainer({ style_class: 'workspaces-display' });
|
||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||
this.actor.connect('parent-set', Lang.bind(this, this._parentSet));
|
||||
this.actor.set_clip_to_allocation(true);
|
||||
|
||||
this._spacing = 0;
|
||||
this.actor.connect('style-changed', Lang.bind(this,
|
||||
function() {
|
||||
let node = this.actor.get_theme_node();
|
||||
let spacing = node.get_length('spacing');
|
||||
if (spacing != this._spacing) {
|
||||
this._spacing = spacing;
|
||||
this._updateWorkspacesGeometry();
|
||||
}
|
||||
}));
|
||||
|
||||
let clickAction = new Clutter.ClickAction()
|
||||
clickAction.connect('clicked', Lang.bind(this, function(action) {
|
||||
// Only switch to the workspace when there's no application
|
||||
@ -483,15 +493,14 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
controls.track_hover = true;
|
||||
controls.connect('notify::hover',
|
||||
Lang.bind(this, this._onControlsHoverChanged));
|
||||
controls.connect('scroll-event',
|
||||
Lang.bind(this, this._onScrollEvent));
|
||||
|
||||
this._primaryIndex = Main.layoutManager.primaryIndex;
|
||||
|
||||
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
|
||||
controls.add_actor(this._thumbnailsBox.actor);
|
||||
|
||||
this._workspacesViews = null;
|
||||
this._workspacesViews = [];
|
||||
this._workspaces = [];
|
||||
this._primaryScrollAdjustment = null;
|
||||
|
||||
this._settings = new Gio.Settings({ schema: OVERRIDE_SCHEMA });
|
||||
@ -537,10 +546,6 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
this._notifyOpacityId = 0;
|
||||
this._swipeScrollBeginId = 0;
|
||||
this._swipeScrollEndId = 0;
|
||||
|
||||
this._settings = new Gio.Settings({ schema: OVERRIDE_SCHEMA });
|
||||
this._settings.connect('changed::dynamic-workspaces',
|
||||
Lang.bind(this, this._updateSwitcherVisibility));
|
||||
},
|
||||
|
||||
_onPan: function(action) {
|
||||
@ -550,12 +555,6 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
return false;
|
||||
},
|
||||
|
||||
_updateSwitcherVisibility: function() {
|
||||
this._thumbnailsBox.actor.visible =
|
||||
this._settings.get_boolean('dynamic-workspaces') ||
|
||||
global.screen.n_workspaces > 1;
|
||||
},
|
||||
|
||||
show: function() {
|
||||
if(!this._alwaysZoomOut) {
|
||||
let [mouseX, mouseY] = global.get_pointer();
|
||||
@ -575,13 +574,11 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
this._updateZoom();
|
||||
|
||||
this._controls.show();
|
||||
this._thumbnailsBox.show();
|
||||
this._updateSwitcherVisibility();
|
||||
|
||||
this._updateWorkspacesViews();
|
||||
|
||||
this._restackedNotifyId =
|
||||
global.screen.connect('restacked',
|
||||
Main.overview.connect('windows-restacked',
|
||||
Lang.bind(this, this._onRestacked));
|
||||
|
||||
if (this._itemDragBeginId == 0)
|
||||
@ -602,8 +599,6 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
if (this._windowDragEndId == 0)
|
||||
this._windowDragEndId = Main.overview.connect('window-drag-end',
|
||||
Lang.bind(this, this._dragEnd));
|
||||
|
||||
this._onRestacked();
|
||||
},
|
||||
|
||||
zoomFromOverview: function() {
|
||||
@ -614,13 +609,12 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
|
||||
hide: function() {
|
||||
this._controls.hide();
|
||||
this._thumbnailsBox.hide();
|
||||
|
||||
if (!this._alwaysZoomOut)
|
||||
this.zoomFraction = 0;
|
||||
|
||||
if (this._restackedNotifyId > 0){
|
||||
global.screen.disconnect(this._restackedNotifyId);
|
||||
Main.overview.disconnect(this._restackedNotifyId);
|
||||
this._restackedNotifyId = 0;
|
||||
}
|
||||
if (this._itemDragBeginId > 0) {
|
||||
@ -650,7 +644,7 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
|
||||
for (let i = 0; i < this._workspacesViews.length; i++)
|
||||
this._workspacesViews[i].destroy();
|
||||
this._workspacesViews = null;
|
||||
this._workspacesViews = [];
|
||||
|
||||
for (let i = 0; i < this._workspaces.length; i++)
|
||||
for (let w = 0; w < this._workspaces[i].length; w++) {
|
||||
@ -669,14 +663,12 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
},
|
||||
|
||||
_updateWorkspacesViews: function() {
|
||||
if (this._workspacesViews)
|
||||
for (let i = 0; i < this._workspacesViews.length; i++)
|
||||
this._workspacesViews[i].destroy();
|
||||
for (let i = 0; i < this._workspacesViews.length; i++)
|
||||
this._workspacesViews[i].destroy();
|
||||
|
||||
if (this._workspaces)
|
||||
for (let i = 0; i < this._workspaces.length; i++)
|
||||
for (let w = 0; w < this._workspaces[i].length; w++)
|
||||
this._workspaces[i][w].destroy();
|
||||
for (let i = 0; i < this._workspaces.length; i++)
|
||||
for (let w = 0; w < this._workspaces[i].length; w++)
|
||||
this._workspaces[i][w].destroy();
|
||||
|
||||
this._workspacesViews = [];
|
||||
this._workspaces = [];
|
||||
@ -724,7 +716,7 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
},
|
||||
|
||||
_getPrimaryView: function() {
|
||||
if (!this._workspacesViews)
|
||||
if (!this._workspacesViews.length)
|
||||
return null;
|
||||
if (this._workspacesOnlyOnPrimary)
|
||||
return this._workspacesViews[0];
|
||||
@ -833,7 +825,7 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
},
|
||||
|
||||
_updateWorkspacesGeometry: function() {
|
||||
if (!this._workspacesViews)
|
||||
if (!this._workspacesViews.length)
|
||||
return;
|
||||
|
||||
let fullWidth = this.actor.allocation.x2 - this.actor.allocation.x1;
|
||||
@ -854,9 +846,8 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
let clipX = rtl ? x + controlsVisible : x;
|
||||
let clipY = y + (fullHeight - clipHeight) / 2;
|
||||
|
||||
let overviewSpacing = Main.overview._spacing;
|
||||
let widthAdjust = this._zoomOut ? controlsNatural : controlsVisible;
|
||||
widthAdjust += overviewSpacing;
|
||||
widthAdjust += this._spacing;
|
||||
width -= widthAdjust;
|
||||
if (rtl)
|
||||
x += widthAdjust;
|
||||
@ -874,35 +865,25 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
monitors[i].y,
|
||||
monitors[i].width,
|
||||
monitors[i].height);
|
||||
this._workspacesViews[m].setGeometry(monitors[i].x + overviewSpacing/2,
|
||||
monitors[i].y + overviewSpacing/2,
|
||||
monitors[i].width - overviewSpacing,
|
||||
monitors[i].height - overviewSpacing);
|
||||
this._workspacesViews[m].setGeometry(monitors[i].x,
|
||||
monitors[i].y,
|
||||
monitors[i].width,
|
||||
monitors[i].height);
|
||||
m++;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_onRestacked: function() {
|
||||
let stack = global.get_window_actors();
|
||||
let stackIndices = {};
|
||||
|
||||
for (let i = 0; i < stack.length; i++) {
|
||||
// Use the stable sequence for an integer to use as a hash key
|
||||
stackIndices[stack[i].get_meta_window().get_stable_sequence()] = i;
|
||||
}
|
||||
|
||||
_onRestacked: function(overview, stackIndices) {
|
||||
for (let i = 0; i < this._workspacesViews.length; i++)
|
||||
this._workspacesViews[i].syncStacking(stackIndices);
|
||||
|
||||
this._thumbnailsBox.syncStacking(stackIndices);
|
||||
},
|
||||
|
||||
_workspacesChanged: function() {
|
||||
this._updateAlwaysZoom();
|
||||
this._updateZoom();
|
||||
|
||||
if (this._workspacesViews == null)
|
||||
if (!this._workspacesViews.length)
|
||||
return;
|
||||
|
||||
let oldNumWorkspaces = this._workspaces[0].length;
|
||||
@ -926,8 +907,6 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
}
|
||||
m++;
|
||||
}
|
||||
|
||||
this._thumbnailsBox.addThumbnails(oldNumWorkspaces, newNumWorkspaces - oldNumWorkspaces);
|
||||
} else {
|
||||
// Assume workspaces are only removed sequentially
|
||||
// (e.g. 2,3,4 - not 2,4,7)
|
||||
@ -950,14 +929,11 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
lostWorkspaces[l].destroy();
|
||||
}
|
||||
}
|
||||
|
||||
this._thumbnailsBox.removeThumbnails(removedIndex, removedNum);
|
||||
}
|
||||
|
||||
for (let i = 0; i < this._workspacesViews.length; i++)
|
||||
this._workspacesViews[i].updateWorkspaces(oldNumWorkspaces,
|
||||
newNumWorkspaces);
|
||||
this._updateSwitcherVisibility();
|
||||
},
|
||||
|
||||
_updateZoom : function() {
|
||||
@ -969,7 +945,7 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
this._zoomOut = shouldZoom;
|
||||
this._updateWorkspacesGeometry();
|
||||
|
||||
if (!this._workspacesViews)
|
||||
if (!this._workspacesViews.length)
|
||||
return;
|
||||
|
||||
Tweener.addTween(this,
|
||||
@ -1020,17 +996,6 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
// might as well avoid it.
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
|
||||
Lang.bind(this, this._updateZoom));
|
||||
},
|
||||
|
||||
_onScrollEvent: function (actor, event) {
|
||||
switch ( event.get_scroll_direction() ) {
|
||||
case Clutter.ScrollDirection.UP:
|
||||
Main.wm.actionMoveWorkspace(Meta.MotionDirection.UP);
|
||||
break;
|
||||
case Clutter.ScrollDirection.DOWN:
|
||||
Main.wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(WorkspacesDisplay.prototype);
|
||||
|
237
po/ar.po
237
po/ar.po
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: HEAD\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2012-12-09 04:45+0200\n"
|
||||
"POT-Creation-Date: 2012-12-23 18:34+0200\n"
|
||||
"PO-Revision-Date: 2012-12-09 04:52+0200\n"
|
||||
"Last-Translator: Khaled Hosny <khaledhosny@eglug.org>\n"
|
||||
"Language-Team: Arabic <doc@arabeyes.org>\n"
|
||||
@ -365,8 +365,8 @@ msgstr "نافذة الولوج"
|
||||
msgid "Power"
|
||||
msgstr "الطاقة"
|
||||
|
||||
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:674 ../js/ui/userMenu.js:678
|
||||
#: ../js/ui/userMenu.js:789
|
||||
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:675 ../js/ui/userMenu.js:679
|
||||
#: ../js/ui/userMenu.js:790
|
||||
msgid "Suspend"
|
||||
msgstr "علّق"
|
||||
|
||||
@ -374,8 +374,8 @@ msgstr "علّق"
|
||||
msgid "Restart"
|
||||
msgstr "أعِد التشغيل"
|
||||
|
||||
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:676
|
||||
#: ../js/ui/userMenu.js:678 ../js/ui/userMenu.js:788
|
||||
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:677
|
||||
#: ../js/ui/userMenu.js:679 ../js/ui/userMenu.js:789
|
||||
msgid "Power Off"
|
||||
msgstr "أطفئ الحاسوب"
|
||||
|
||||
@ -410,27 +410,19 @@ msgid "Execution of '%s' failed:"
|
||||
msgstr "فشل تنفيذ '%s':"
|
||||
|
||||
#. Translators: Filter to display all applications
|
||||
#: ../js/ui/appDisplay.js:259
|
||||
#: ../js/ui/appDisplay.js:258
|
||||
msgid "All"
|
||||
msgstr "الكل"
|
||||
|
||||
#: ../js/ui/appDisplay.js:318
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "التطبيقات"
|
||||
|
||||
#: ../js/ui/appDisplay.js:375
|
||||
msgid "SETTINGS"
|
||||
msgstr "الإعدادات"
|
||||
|
||||
#: ../js/ui/appDisplay.js:679
|
||||
#: ../js/ui/appDisplay.js:666
|
||||
msgid "New Window"
|
||||
msgstr "نافذة جديدة"
|
||||
|
||||
#: ../js/ui/appDisplay.js:682 ../js/ui/dash.js:289
|
||||
#: ../js/ui/appDisplay.js:669 ../js/ui/dash.js:289
|
||||
msgid "Remove from Favorites"
|
||||
msgstr "أزِل من المفضّلة"
|
||||
|
||||
#: ../js/ui/appDisplay.js:683
|
||||
#: ../js/ui/appDisplay.js:670
|
||||
msgid "Add to Favorites"
|
||||
msgstr "أضِف إلى المفضّلة"
|
||||
|
||||
@ -447,19 +439,19 @@ msgstr "أُزيل %s من مفضّلتك."
|
||||
#. Translators: Shown in calendar event list for all day events
|
||||
#. * Keep it short, best if you can use less then 10 characters
|
||||
#.
|
||||
#: ../js/ui/calendar.js:62
|
||||
#: ../js/ui/calendar.js:61
|
||||
msgctxt "event list time"
|
||||
msgid "All Day"
|
||||
msgstr "طوال اليوم"
|
||||
|
||||
#. Translators: Shown in calendar event list, if 24h format
|
||||
#: ../js/ui/calendar.js:67
|
||||
#: ../js/ui/calendar.js:66
|
||||
msgctxt "event list time"
|
||||
msgid "%H:%M"
|
||||
msgstr "%H:%M"
|
||||
|
||||
#. Transators: Shown in calendar event list, if 12h format
|
||||
#: ../js/ui/calendar.js:74
|
||||
#: ../js/ui/calendar.js:73
|
||||
msgctxt "event list time"
|
||||
msgid "%l:%M %p"
|
||||
msgstr "%l:%M %p"
|
||||
@ -469,43 +461,43 @@ msgstr "%l:%M %p"
|
||||
#. * NOTE: These grid abbreviations are always shown together
|
||||
#. * and in order, e.g. "S M T W T F S".
|
||||
#.
|
||||
#: ../js/ui/calendar.js:114
|
||||
#: ../js/ui/calendar.js:104
|
||||
msgctxt "grid sunday"
|
||||
msgid "S"
|
||||
msgstr "ح"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Monday
|
||||
#: ../js/ui/calendar.js:116
|
||||
#: ../js/ui/calendar.js:106
|
||||
msgctxt "grid monday"
|
||||
msgid "M"
|
||||
msgstr "ن"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Tuesday
|
||||
#: ../js/ui/calendar.js:118
|
||||
#: ../js/ui/calendar.js:108
|
||||
msgctxt "grid tuesday"
|
||||
msgid "T"
|
||||
msgstr "ث"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Wednesday
|
||||
#: ../js/ui/calendar.js:120
|
||||
#: ../js/ui/calendar.js:110
|
||||
msgctxt "grid wednesday"
|
||||
msgid "W"
|
||||
msgstr "ر"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Thursday
|
||||
#: ../js/ui/calendar.js:122
|
||||
#: ../js/ui/calendar.js:112
|
||||
msgctxt "grid thursday"
|
||||
msgid "T"
|
||||
msgstr "خ"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Friday
|
||||
#: ../js/ui/calendar.js:124
|
||||
#: ../js/ui/calendar.js:114
|
||||
msgctxt "grid friday"
|
||||
msgid "F"
|
||||
msgstr "ج"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Saturday
|
||||
#: ../js/ui/calendar.js:126
|
||||
#: ../js/ui/calendar.js:116
|
||||
msgctxt "grid saturday"
|
||||
msgid "S"
|
||||
msgstr "س"
|
||||
@ -516,77 +508,77 @@ msgstr "س"
|
||||
#. * so they need to be unique (e.g. Tuesday and Thursday cannot
|
||||
#. * both be 'T').
|
||||
#.
|
||||
#: ../js/ui/calendar.js:139
|
||||
#: ../js/ui/calendar.js:129
|
||||
msgctxt "list sunday"
|
||||
msgid "Su"
|
||||
msgstr "الأحد"
|
||||
|
||||
#. Translators: Event list abbreviation for Monday
|
||||
#: ../js/ui/calendar.js:141
|
||||
#: ../js/ui/calendar.js:131
|
||||
msgctxt "list monday"
|
||||
msgid "M"
|
||||
msgstr "الاثنين"
|
||||
|
||||
#. Translators: Event list abbreviation for Tuesday
|
||||
#: ../js/ui/calendar.js:143
|
||||
#: ../js/ui/calendar.js:133
|
||||
msgctxt "list tuesday"
|
||||
msgid "T"
|
||||
msgstr "الثلاثاء"
|
||||
|
||||
#. Translators: Event list abbreviation for Wednesday
|
||||
#: ../js/ui/calendar.js:145
|
||||
#: ../js/ui/calendar.js:135
|
||||
msgctxt "list wednesday"
|
||||
msgid "W"
|
||||
msgstr "الأربعاء"
|
||||
|
||||
#. Translators: Event list abbreviation for Thursday
|
||||
#: ../js/ui/calendar.js:147
|
||||
#: ../js/ui/calendar.js:137
|
||||
msgctxt "list thursday"
|
||||
msgid "Th"
|
||||
msgstr "الخميس"
|
||||
|
||||
#. Translators: Event list abbreviation for Friday
|
||||
#: ../js/ui/calendar.js:149
|
||||
#: ../js/ui/calendar.js:139
|
||||
msgctxt "list friday"
|
||||
msgid "F"
|
||||
msgstr "الجمعة"
|
||||
|
||||
#. Translators: Event list abbreviation for Saturday
|
||||
#: ../js/ui/calendar.js:151
|
||||
#: ../js/ui/calendar.js:141
|
||||
msgctxt "list saturday"
|
||||
msgid "S"
|
||||
msgstr "السبت"
|
||||
|
||||
#. Translators: Text to show if there are no events
|
||||
#: ../js/ui/calendar.js:700
|
||||
#: ../js/ui/calendar.js:674
|
||||
msgid "Nothing Scheduled"
|
||||
msgstr "الجدول خال"
|
||||
|
||||
#. Translators: Shown on calendar heading when selected day occurs on current year
|
||||
#: ../js/ui/calendar.js:716
|
||||
#: ../js/ui/calendar.js:690
|
||||
msgctxt "calendar heading"
|
||||
msgid "%A, %B %d"
|
||||
msgstr "%A %d %B"
|
||||
|
||||
#. Translators: Shown on calendar heading when selected day occurs on different year
|
||||
#: ../js/ui/calendar.js:719
|
||||
#: ../js/ui/calendar.js:693
|
||||
msgctxt "calendar heading"
|
||||
msgid "%A, %B %d, %Y"
|
||||
msgstr "%A %d %B %Y"
|
||||
|
||||
#: ../js/ui/calendar.js:729
|
||||
#: ../js/ui/calendar.js:703
|
||||
msgid "Today"
|
||||
msgstr "اليوم"
|
||||
|
||||
#: ../js/ui/calendar.js:733
|
||||
#: ../js/ui/calendar.js:707
|
||||
msgid "Tomorrow"
|
||||
msgstr "غدا"
|
||||
|
||||
#: ../js/ui/calendar.js:744
|
||||
#: ../js/ui/calendar.js:718
|
||||
msgid "This week"
|
||||
msgstr "هذا الأسبوع"
|
||||
|
||||
#: ../js/ui/calendar.js:752
|
||||
#: ../js/ui/calendar.js:726
|
||||
msgid "Next week"
|
||||
msgstr "الأسبوع القادم"
|
||||
|
||||
@ -948,7 +940,7 @@ msgstr "أظهر الحساب"
|
||||
msgid "Unknown reason"
|
||||
msgstr "السبب غير معروف"
|
||||
|
||||
#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:87
|
||||
#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:81
|
||||
msgid "Windows"
|
||||
msgstr "النوافذ"
|
||||
|
||||
@ -1088,7 +1080,7 @@ msgstr "هل تريد تنزيل وتثبيت '%s' من extensions.gnome.org؟"
|
||||
msgid "tray"
|
||||
msgstr "لوحة النظام"
|
||||
|
||||
#: ../js/ui/keyboard.js:555 ../js/ui/status/keyboard.js:195
|
||||
#: ../js/ui/keyboard.js:555 ../js/ui/status/keyboard.js:309
|
||||
#: ../js/ui/status/power.js:205
|
||||
msgid "Keyboard"
|
||||
msgstr "لوحة المفاتيح"
|
||||
@ -1115,7 +1107,9 @@ msgstr "اظهر الأخطاء"
|
||||
msgid "Enabled"
|
||||
msgstr "مفعّل"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:767
|
||||
#. translators:
|
||||
#. * The device has been disabled
|
||||
#: ../js/ui/lookingGlass.js:767 ../src/gvc/gvc-mixer-control.c:1830
|
||||
msgid "Disabled"
|
||||
msgstr "معطّل"
|
||||
|
||||
@ -1147,15 +1141,15 @@ msgstr "افتح"
|
||||
msgid "Remove"
|
||||
msgstr "أزِل"
|
||||
|
||||
#: ../js/ui/messageTray.js:1551
|
||||
#: ../js/ui/messageTray.js:1552
|
||||
msgid "No Messages"
|
||||
msgstr "لا رسائل"
|
||||
|
||||
#: ../js/ui/messageTray.js:1568
|
||||
#: ../js/ui/messageTray.js:1570
|
||||
msgid "Message Tray"
|
||||
msgstr "لوحة الرسائل"
|
||||
|
||||
#: ../js/ui/messageTray.js:2635
|
||||
#: ../js/ui/messageTray.js:2639
|
||||
msgid "System Information"
|
||||
msgstr "معلومات النظام"
|
||||
|
||||
@ -1164,11 +1158,11 @@ msgctxt "program"
|
||||
msgid "Unknown"
|
||||
msgstr "غير معروف"
|
||||
|
||||
#: ../js/ui/overview.js:95
|
||||
#: ../js/ui/overview.js:92
|
||||
msgid "Undo"
|
||||
msgstr "تراجع"
|
||||
|
||||
#: ../js/ui/overview.js:144
|
||||
#: ../js/ui/overview.js:139
|
||||
msgid "Overview"
|
||||
msgstr "نظرة عامة"
|
||||
|
||||
@ -1176,13 +1170,13 @@ msgstr "نظرة عامة"
|
||||
#. in the search entry when no search is
|
||||
#. active; it should not exceed ~30
|
||||
#. characters.
|
||||
#: ../js/ui/overview.js:221
|
||||
#: ../js/ui/overview.js:218
|
||||
msgid "Type to search..."
|
||||
msgstr "اكتب نصا للبحث عنه..."
|
||||
|
||||
#. Translators: this is the name of the dock/favorites area on
|
||||
#. the left of the overview
|
||||
#: ../js/ui/overview.js:242
|
||||
#: ../js/ui/overview.js:236
|
||||
msgid "Dash"
|
||||
msgstr "الشريط"
|
||||
|
||||
@ -1205,7 +1199,7 @@ msgstr "الشريط العلوي"
|
||||
#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
|
||||
#. switches containing "◯" and "|"). Other values will
|
||||
#. simply result in invisible toggle switches.
|
||||
#: ../js/ui/popupMenu.js:732
|
||||
#: ../js/ui/popupMenu.js:725
|
||||
msgid "toggle-switch-us"
|
||||
msgstr "toggle-switch-intl"
|
||||
|
||||
@ -1245,15 +1239,15 @@ msgstr[3] "%d تنبيهات جديدة"
|
||||
msgstr[4] "%d تنبيها جديدا"
|
||||
msgstr[5] "%d تنبيه جديد"
|
||||
|
||||
#: ../js/ui/screenShield.js:402 ../js/ui/userMenu.js:780
|
||||
#: ../js/ui/screenShield.js:402 ../js/ui/userMenu.js:781
|
||||
msgid "Lock"
|
||||
msgstr "أوصِد"
|
||||
|
||||
#: ../js/ui/searchDisplay.js:277
|
||||
#: ../js/ui/searchDisplay.js:403
|
||||
msgid "Searching..."
|
||||
msgstr "يبحث..."
|
||||
|
||||
#: ../js/ui/searchDisplay.js:325
|
||||
#: ../js/ui/searchDisplay.js:451
|
||||
msgid "No results."
|
||||
msgstr "لا نتائج."
|
||||
|
||||
@ -1369,7 +1363,7 @@ msgid "disconnecting..."
|
||||
msgstr "يقطع الاتّصال..."
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:220 ../js/ui/status/network.js:442
|
||||
#: ../js/ui/status/network.js:1453
|
||||
#: ../js/ui/status/network.js:1464
|
||||
msgid "connecting..."
|
||||
msgstr "يتّصل..."
|
||||
|
||||
@ -1385,7 +1379,7 @@ msgstr "إعدادات لوحة المفاتيح"
|
||||
msgid "Mouse Settings"
|
||||
msgstr "إعدادات الفأرة"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:253 ../js/ui/status/volume.js:270
|
||||
#: ../js/ui/status/bluetooth.js:253 ../js/ui/status/volume.js:314
|
||||
msgid "Sound Settings"
|
||||
msgstr "إعدادات الصوت"
|
||||
|
||||
@ -1449,15 +1443,15 @@ msgstr "من فضلك أدخل الرقم المذكور على الجهاز."
|
||||
msgid "OK"
|
||||
msgstr "حسنا"
|
||||
|
||||
#: ../js/ui/status/keyboard.js:228
|
||||
#: ../js/ui/status/keyboard.js:363
|
||||
msgid "Show Keyboard Layout"
|
||||
msgstr "أظهر تخطيط لوحة المفاتيح"
|
||||
|
||||
#: ../js/ui/status/keyboard.js:233
|
||||
#: ../js/ui/status/keyboard.js:368
|
||||
msgid "Region and Language Settings"
|
||||
msgstr "إعدادات الإقليم واللغة"
|
||||
|
||||
#: ../js/ui/status/lockScreenMenu.js:18
|
||||
#: ../js/ui/status/lockScreenMenu.js:43
|
||||
msgid "Volume, network, battery"
|
||||
msgstr "الصوت، الشبكة، البطارية"
|
||||
|
||||
@ -1477,7 +1471,7 @@ msgid "unmanaged"
|
||||
msgstr "غير مُدار"
|
||||
|
||||
#. Translators: this is for network connections that require some kind of key or password
|
||||
#: ../js/ui/status/network.js:445 ../js/ui/status/network.js:1456
|
||||
#: ../js/ui/status/network.js:445 ../js/ui/status/network.js:1467
|
||||
msgid "authentication required"
|
||||
msgstr "الاستيثاق مطلوب"
|
||||
|
||||
@ -1498,72 +1492,72 @@ msgstr "الكبل مفصول"
|
||||
msgid "unavailable"
|
||||
msgstr "غير متاح"
|
||||
|
||||
#: ../js/ui/status/network.js:469 ../js/ui/status/network.js:1458
|
||||
#: ../js/ui/status/network.js:469 ../js/ui/status/network.js:1469
|
||||
msgid "connection failed"
|
||||
msgstr "فشل الاتصال"
|
||||
|
||||
#: ../js/ui/status/network.js:525 ../js/ui/status/network.js:1392
|
||||
#: ../js/ui/status/network.js:1534
|
||||
#: ../js/ui/status/network.js:525 ../js/ui/status/network.js:1403
|
||||
#: ../js/ui/status/network.js:1545
|
||||
msgid "More..."
|
||||
msgstr "المزيد..."
|
||||
|
||||
#. TRANSLATORS: this is the indication that a connection for another logged in user is active,
|
||||
#. and we cannot access its settings (including the name)
|
||||
#: ../js/ui/status/network.js:561 ../js/ui/status/network.js:1322
|
||||
#: ../js/ui/status/network.js:561 ../js/ui/status/network.js:1333
|
||||
msgid "Connected (private)"
|
||||
msgstr "متّصل (شخصي)"
|
||||
|
||||
#: ../js/ui/status/network.js:619
|
||||
#: ../js/ui/status/network.js:641
|
||||
msgid "Auto Ethernet"
|
||||
msgstr "إيثرنت تلقائي"
|
||||
|
||||
#: ../js/ui/status/network.js:677
|
||||
#: ../js/ui/status/network.js:688
|
||||
msgid "Auto broadband"
|
||||
msgstr "شبكة هاتف محمول تلقائية"
|
||||
|
||||
#: ../js/ui/status/network.js:680
|
||||
#: ../js/ui/status/network.js:691
|
||||
msgid "Auto dial-up"
|
||||
msgstr "اتصال هاتفي تلقائي"
|
||||
|
||||
#. TRANSLATORS: this the automatic wireless connection name (including the network name)
|
||||
#: ../js/ui/status/network.js:809 ../js/ui/status/network.js:1339
|
||||
#: ../js/ui/status/network.js:820 ../js/ui/status/network.js:1350
|
||||
#, c-format
|
||||
msgid "Auto %s"
|
||||
msgstr "%s تلقائي"
|
||||
|
||||
#: ../js/ui/status/network.js:811
|
||||
#: ../js/ui/status/network.js:822
|
||||
msgid "Auto bluetooth"
|
||||
msgstr "بلوتوث تلقائي"
|
||||
|
||||
#: ../js/ui/status/network.js:1341
|
||||
#: ../js/ui/status/network.js:1352
|
||||
msgid "Auto wireless"
|
||||
msgstr "لاسلكي تلقائي"
|
||||
|
||||
#: ../js/ui/status/network.js:1584
|
||||
#: ../js/ui/status/network.js:1595
|
||||
msgid "Enable networking"
|
||||
msgstr "فعّل الشبكات"
|
||||
|
||||
#: ../js/ui/status/network.js:1616
|
||||
#: ../js/ui/status/network.js:1627
|
||||
msgid "Wi-Fi"
|
||||
msgstr "واي فاي"
|
||||
|
||||
#: ../js/ui/status/network.js:1635
|
||||
#: ../js/ui/status/network.js:1646
|
||||
msgid "Network Settings"
|
||||
msgstr "إعدادات الشّبكة"
|
||||
|
||||
#: ../js/ui/status/network.js:1679
|
||||
#: ../js/ui/status/network.js:1692
|
||||
msgid "Network Manager"
|
||||
msgstr "مدير الشبكة"
|
||||
|
||||
#: ../js/ui/status/network.js:1761
|
||||
#: ../js/ui/status/network.js:1774
|
||||
msgid "Connection failed"
|
||||
msgstr "فشل الاتصال"
|
||||
|
||||
#: ../js/ui/status/network.js:1762
|
||||
#: ../js/ui/status/network.js:1775
|
||||
msgid "Activation of network connection failed"
|
||||
msgstr "فشل تفعيل اتصال الشبكة"
|
||||
|
||||
#: ../js/ui/status/network.js:2079
|
||||
#: ../js/ui/status/network.js:2092
|
||||
msgid "Networking is disabled"
|
||||
msgstr "عُطّلت الشبكات"
|
||||
|
||||
@ -1681,11 +1675,11 @@ msgid "Unknown"
|
||||
msgstr "غير معروف"
|
||||
|
||||
#. Translators: This is the label for audio volume
|
||||
#: ../js/ui/status/volume.js:50 ../js/ui/status/volume.js:251
|
||||
#: ../js/ui/status/volume.js:247 ../js/ui/status/volume.js:295
|
||||
msgid "Volume"
|
||||
msgstr "شدة الصوت"
|
||||
|
||||
#: ../js/ui/status/volume.js:62
|
||||
#: ../js/ui/status/volume.js:256
|
||||
msgid "Microphone"
|
||||
msgstr "ميكروفون"
|
||||
|
||||
@ -1721,31 +1715,31 @@ msgstr "ساكن"
|
||||
msgid "Offline"
|
||||
msgstr "غير متصل"
|
||||
|
||||
#: ../js/ui/userMenu.js:754
|
||||
#: ../js/ui/userMenu.js:755
|
||||
msgid "Notifications"
|
||||
msgstr "التنبيهات"
|
||||
|
||||
#: ../js/ui/userMenu.js:762
|
||||
#: ../js/ui/userMenu.js:763
|
||||
msgid "Settings"
|
||||
msgstr "الإعدادات"
|
||||
|
||||
#: ../js/ui/userMenu.js:770
|
||||
#: ../js/ui/userMenu.js:771
|
||||
msgid "Switch User"
|
||||
msgstr "بدّل المستخدم"
|
||||
|
||||
#: ../js/ui/userMenu.js:775
|
||||
#: ../js/ui/userMenu.js:776
|
||||
msgid "Log Out"
|
||||
msgstr "اخرج"
|
||||
|
||||
#: ../js/ui/userMenu.js:795
|
||||
#: ../js/ui/userMenu.js:796
|
||||
msgid "Install Updates & Restart"
|
||||
msgstr "ثبّت التحديثات وأعد التشغيل"
|
||||
|
||||
#: ../js/ui/userMenu.js:813
|
||||
#: ../js/ui/userMenu.js:814
|
||||
msgid "Your chat status will be set to busy"
|
||||
msgstr "ستُجعل حالة اتصالك ”مشغول“"
|
||||
|
||||
#: ../js/ui/userMenu.js:814
|
||||
#: ../js/ui/userMenu.js:815
|
||||
msgid ""
|
||||
"Notifications are now disabled, including chat messages. Your online status "
|
||||
"has been adjusted to let others know that you might not see their messages."
|
||||
@ -1753,15 +1747,15 @@ msgstr ""
|
||||
"التنبيهات معطلة الآن، بما فيها رسائل المحادثة. حالة اتصالك تغيرت حتى يعلم "
|
||||
"الآخرون أنك قد لا ترى رسائلهم."
|
||||
|
||||
#: ../js/ui/viewSelector.js:91
|
||||
#: ../js/ui/viewSelector.js:85
|
||||
msgid "Applications"
|
||||
msgstr "التطبيقات"
|
||||
|
||||
#: ../js/ui/viewSelector.js:95
|
||||
#: ../js/ui/viewSelector.js:89
|
||||
msgid "Search"
|
||||
msgstr "ابحث"
|
||||
|
||||
#: ../js/ui/wanda.js:94
|
||||
#: ../js/ui/wanda.js:92
|
||||
#, c-format
|
||||
msgid ""
|
||||
"Sorry, no wisdom for you today:\n"
|
||||
@ -1770,15 +1764,11 @@ msgstr ""
|
||||
"عذرًا, لا حكمة لك اليوم:\n"
|
||||
"%s"
|
||||
|
||||
#: ../js/ui/wanda.js:98
|
||||
#: ../js/ui/wanda.js:96
|
||||
#, c-format
|
||||
msgid "%s the Oracle says"
|
||||
msgstr "يقول الحكيم %s"
|
||||
|
||||
#: ../js/ui/wanda.js:139
|
||||
msgid "Your favorite Easter Egg"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/windowAttentionHandler.js:19
|
||||
#, c-format
|
||||
msgid "'%s' is ready"
|
||||
@ -1788,6 +1778,36 @@ msgstr "'%s' جاهز"
|
||||
msgid "Evolution Calendar"
|
||||
msgstr "تقويم إيفُليوشِن"
|
||||
|
||||
#. translators:
|
||||
#. * The number of sound outputs on a particular device
|
||||
#: ../src/gvc/gvc-mixer-control.c:1837
|
||||
#, c-format
|
||||
msgid "%u Output"
|
||||
msgid_plural "%u Outputs"
|
||||
msgstr[0] "لا مخرَج"
|
||||
msgstr[1] "مخرَج واحد"
|
||||
msgstr[2] "مخرَجين"
|
||||
msgstr[3] "%u مخارج"
|
||||
msgstr[4] "%u مخرجا"
|
||||
msgstr[5] "%u مخرج"
|
||||
|
||||
#. translators:
|
||||
#. * The number of sound inputs on a particular device
|
||||
#: ../src/gvc/gvc-mixer-control.c:1847
|
||||
#, c-format
|
||||
msgid "%u Input"
|
||||
msgid_plural "%u Inputs"
|
||||
msgstr[0] "لا مدخل"
|
||||
msgstr[1] "مدخل واحد"
|
||||
msgstr[2] "مدخلين"
|
||||
msgstr[3] "%u مداخل"
|
||||
msgstr[4] "%u مدخلا"
|
||||
msgstr[5] "%u مدخل"
|
||||
|
||||
#: ../src/gvc/gvc-mixer-control.c:2371
|
||||
msgid "System Sounds"
|
||||
msgstr "أصوات النظام"
|
||||
|
||||
#: ../src/main.c:332
|
||||
msgid "Print version"
|
||||
msgstr "اطبع الإصدارة"
|
||||
@ -1829,6 +1849,12 @@ msgstr "المبدئي"
|
||||
msgid "Authentication dialog was dismissed by the user"
|
||||
msgstr "أغلق المستخدم مربع الاستيثاق الحِواري"
|
||||
|
||||
#~ msgid "APPLICATIONS"
|
||||
#~ msgstr "التطبيقات"
|
||||
|
||||
#~ msgid "SETTINGS"
|
||||
#~ msgstr "الإعدادات"
|
||||
|
||||
#~ msgid "Subscription request"
|
||||
#~ msgstr "طلب اشتراك"
|
||||
|
||||
@ -1877,27 +1903,6 @@ msgstr "أغلق المستخدم مربع الاستيثاق الحِواري"
|
||||
#~ msgid "System Settings"
|
||||
#~ msgstr "إعدادات النظام"
|
||||
|
||||
#~ msgid "%u Output"
|
||||
#~ msgid_plural "%u Outputs"
|
||||
#~ msgstr[0] "لا مخرَج"
|
||||
#~ msgstr[1] "مخرَج واحد"
|
||||
#~ msgstr[2] "مخرَجين"
|
||||
#~ msgstr[3] "%u مخارج"
|
||||
#~ msgstr[4] "%u مخرجا"
|
||||
#~ msgstr[5] "%u مخرج"
|
||||
|
||||
#~ msgid "%u Input"
|
||||
#~ msgid_plural "%u Inputs"
|
||||
#~ msgstr[0] "لا مدخل"
|
||||
#~ msgstr[1] "مدخل واحد"
|
||||
#~ msgstr[2] "مدخلين"
|
||||
#~ msgstr[3] "%u مداخل"
|
||||
#~ msgstr[4] "%u مدخلا"
|
||||
#~ msgstr[5] "%u مدخل"
|
||||
|
||||
#~ msgid "System Sounds"
|
||||
#~ msgstr "أصوات النظام"
|
||||
|
||||
#~ msgid "Failed to unmount '%s'"
|
||||
#~ msgstr "فشل فصْل '%s'"
|
||||
|
||||
|
173
po/et.po
173
po/et.po
@ -13,8 +13,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: 2012-11-16 18:27+0000\n"
|
||||
"PO-Revision-Date: 2012-11-16 20:40+0300\n"
|
||||
"POT-Creation-Date: 2012-12-09 13:03+0000\n"
|
||||
"PO-Revision-Date: 2012-12-19 16:47+0300\n"
|
||||
"Last-Translator: Mattias Põldaru <mahfiaz@gmail.com>\n"
|
||||
"Language-Team: Estonian <>\n"
|
||||
"Language: et\n"
|
||||
@ -135,6 +135,28 @@ msgstr ""
|
||||
"See võti keelab automaatse 'Logi välja' menüükirje peitmise, kui arvutis on "
|
||||
"üks kasutaja ning avatud üks seanss."
|
||||
|
||||
msgid "Show full name in the user menu"
|
||||
msgstr "Kasutajamenüüs näidatakse kasutaja tervet nime"
|
||||
|
||||
msgid "Whether the users full name is shown in the user menu or not."
|
||||
msgstr "Kas kasutajamenüüs näidatakse kasutaja kogu nime või mitte."
|
||||
|
||||
msgid ""
|
||||
"Whether to remember password for mounting encrypted or remote filesystems"
|
||||
msgstr ""
|
||||
"Kas pidada meeles krüpteeritud või kaugfailisüsteemide haakimise paroole."
|
||||
|
||||
msgid ""
|
||||
"The shell will request a password when an encrypted device or a remote "
|
||||
"filesystem is mounted. If the password can be saved for future use a "
|
||||
"'Remember Password' checkbox will be present. This key sets the default "
|
||||
"state of the checkbox."
|
||||
msgstr ""
|
||||
"Shell küsib parooli, kui haagitakse krüpteeritud seade või kaugfailisüsteem. "
|
||||
"Kui parooli on võimalik salvestada edaspidiseks kasutuseks, näidatakse "
|
||||
"'Salvesta parool' märkeruutu. See võti määrab selle märkeruudu vaikimisi "
|
||||
"oleku."
|
||||
|
||||
msgid "Show the week date in the calendar"
|
||||
msgstr "Kalendris näidatakse kuupäeva nädalavormingus"
|
||||
|
||||
@ -227,6 +249,18 @@ msgstr ""
|
||||
"konteinervormingusse salvestades tuleks ka sellele vormingule vastav laiend "
|
||||
"määrata."
|
||||
|
||||
msgid "The application icon mode."
|
||||
msgstr "Rakenduste ikooni režiim."
|
||||
|
||||
msgid ""
|
||||
"Configures how the windows are shown in the switcher. Valid possibilities "
|
||||
"are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-"
|
||||
"only' (shows only the application icon) or 'both'."
|
||||
msgstr ""
|
||||
"Seadistab, kuidas aknaid aknavahetajas kuvatakse. Sobivad väärtused on "
|
||||
"'thumbnail-only' (näidatakse ainult pisipilti aknast), 'app-icon-"
|
||||
"only' (näidatakse ainult akna ikooni) või 'both' (mõlemad)."
|
||||
|
||||
msgid "Attach modal dialog to the parent window"
|
||||
msgstr "Modaaldialoog kuulub vanemakna juurde"
|
||||
|
||||
@ -588,11 +622,8 @@ msgstr "Helista"
|
||||
msgid "File Transfer"
|
||||
msgstr "Failiülekanne"
|
||||
|
||||
msgid "Subscription request"
|
||||
msgstr "Tellimuse päring"
|
||||
|
||||
msgid "Connection error"
|
||||
msgstr "Ühenduse viga"
|
||||
msgid "Chat"
|
||||
msgstr "Vestlus"
|
||||
|
||||
msgid "Unmute"
|
||||
msgstr "Heli peale"
|
||||
@ -600,27 +631,25 @@ msgstr "Heli peale"
|
||||
msgid "Mute"
|
||||
msgstr "Heli maha"
|
||||
|
||||
#. Translators: this is a time format string followed by the word "Yesterday". i.e. "14:30 on Yesterday"
|
||||
#. Translators: this is the word "Yesterday" followed by a time string. i.e. "Yesterday, 14:30"
|
||||
#, no-c-format
|
||||
msgid "<b>%H:%M</b> on Yesterday"
|
||||
msgstr "Eile <b>%H:%M</b>"
|
||||
msgid "<b>Yesterday</b>, <b>%H:%M</b>"
|
||||
msgstr "<b>Eile</b> <b>%H:%M</b>"
|
||||
|
||||
#. Translators: this is a time format string followed by a week day name. i.e. "14:30 on Monday
|
||||
#. Translators: this is the week day name followed by a time string. i.e. "Monday, 14:30
|
||||
#, no-c-format
|
||||
msgid "<b>%H:%M</b> on <b>%A</b>"
|
||||
msgstr "<b>%H:%M</b>, <b>%A</b>"
|
||||
msgid "<b>%A</b>, <b>%H:%M</b>"
|
||||
msgstr "<b>%A</b>, <b>%H:%M</b>"
|
||||
|
||||
#. Translators: this is a time format in the style of "14:30 on Wednesday, May 25",
|
||||
#. shown when you get a chat message in the same year
|
||||
#. Translators: this is the month name and day number followed by a time string. i.e. "May 25, 14:30"
|
||||
#, no-c-format
|
||||
msgid "<b>%H:%M</b> on <b>%A</b>, <b>%B</b> <b>%d</b>"
|
||||
msgstr "<b>%H:%M</b>, <b>%A</b>, <b>%d. %B</b>"
|
||||
msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
|
||||
msgstr "<b>%d. %B</b>, <b>%H:%M</b>"
|
||||
|
||||
#. Translators: this is a time format in the style of "14:30 on Wednesday, May 25, 2012",
|
||||
#. shown when you get a chat message in a different year
|
||||
#. Translators: this is the month name, day number, year number followed by a time string. i.e. "May 25 2012, 14:30"
|
||||
#, no-c-format
|
||||
msgid "<b>%H:%M</b> on <b>%A</b>, <b>%B</b> <b>%d</b>, %Y"
|
||||
msgstr "<b>%H:%M</b>, <b>%A</b>, <b>%d. %B</b> %Y"
|
||||
msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
|
||||
msgstr "<b>%d. %B %Y</b>, <b>%H:%M</b>"
|
||||
|
||||
#. Translators: this is the other person changing their old IM name to their new
|
||||
#. IM name.
|
||||
@ -757,18 +786,18 @@ msgstr "Sisemine viga"
|
||||
#. translators: argument is the account name, like
|
||||
#. * name@jabber.org for example.
|
||||
#, c-format
|
||||
msgid "Connection to %s failed"
|
||||
msgstr "Ühendus %s nurjus"
|
||||
msgid "Unable to connect to %s"
|
||||
msgstr "Pole võimalik ühenduda võrguga %s"
|
||||
|
||||
msgid "Reconnect"
|
||||
msgstr "Ühendu uuesti"
|
||||
|
||||
msgid "Edit account"
|
||||
msgstr "Konto muutmine"
|
||||
msgid "View account"
|
||||
msgstr "Konto kuvamine"
|
||||
|
||||
msgid "Unknown reason"
|
||||
msgstr "Põhjus teadmata"
|
||||
|
||||
msgid "Windows"
|
||||
msgstr "Aknad"
|
||||
|
||||
msgid "Show Applications"
|
||||
msgstr "Rakenduste kuvamine"
|
||||
|
||||
@ -891,8 +920,6 @@ msgstr "Näita vigu"
|
||||
msgid "Enabled"
|
||||
msgstr "Lubatud"
|
||||
|
||||
#. translators:
|
||||
#. * The device has been disabled
|
||||
msgid "Disabled"
|
||||
msgstr "Keelatud"
|
||||
|
||||
@ -917,6 +944,9 @@ msgstr "Ava"
|
||||
msgid "Remove"
|
||||
msgstr "Eemalda"
|
||||
|
||||
msgid "No Messages"
|
||||
msgstr "Teateid pole"
|
||||
|
||||
msgid "Message Tray"
|
||||
msgstr "Teateala"
|
||||
|
||||
@ -987,6 +1017,9 @@ msgid_plural "%d new notifications"
|
||||
msgstr[0] "%d uus märguanne"
|
||||
msgstr[1] "%d uut märguannet"
|
||||
|
||||
msgid "Lock"
|
||||
msgstr "Lukusta"
|
||||
|
||||
msgid "Searching..."
|
||||
msgstr "Otsimine..."
|
||||
|
||||
@ -1107,6 +1140,7 @@ msgstr "Luba ainult seekord"
|
||||
msgid "Reject"
|
||||
msgstr "Lükka tagasi"
|
||||
|
||||
#. Translators: argument is the device short name
|
||||
#, c-format
|
||||
msgid "Pairing confirmation for %s"
|
||||
msgstr "Paardumise kinnitus seadmele %s"
|
||||
@ -1119,6 +1153,7 @@ msgstr "Seade '%s' tahab selle arvutiga paarduda"
|
||||
msgid "Please confirm whether the PIN '%06d' matches the one on the device."
|
||||
msgstr "Palun kontrolli, kas PIN-kood '%06d' kattub seadme parooliga."
|
||||
|
||||
#. Translators: this is the verb, not the noun
|
||||
msgid "Matches"
|
||||
msgstr "Kattub"
|
||||
|
||||
@ -1208,14 +1243,8 @@ msgstr "Automaatne juhtmeta ühendus"
|
||||
msgid "Enable networking"
|
||||
msgstr "Luba võrguühendused"
|
||||
|
||||
msgid "Wired"
|
||||
msgstr "Juhtmega"
|
||||
|
||||
msgid "Wireless"
|
||||
msgstr "Juhtmeta"
|
||||
|
||||
msgid "Mobile broadband"
|
||||
msgstr "Mobiiliühendus"
|
||||
msgid "Wi-Fi"
|
||||
msgstr "Wi-Fi"
|
||||
|
||||
msgid "Network Settings"
|
||||
msgstr "Võrgusätted"
|
||||
@ -1319,6 +1348,9 @@ msgstr "Mikrofon"
|
||||
msgid "Log in as another user"
|
||||
msgstr "Logi sisse teise kasutajana"
|
||||
|
||||
msgid "Unlock Window"
|
||||
msgstr "Võta aken lukust lahti"
|
||||
|
||||
msgid "Available"
|
||||
msgstr "Saadaval"
|
||||
|
||||
@ -1349,9 +1381,6 @@ msgstr "Vaheta kasutajat"
|
||||
msgid "Log Out"
|
||||
msgstr "Logi välja"
|
||||
|
||||
msgid "Lock"
|
||||
msgstr "Lukusta"
|
||||
|
||||
msgid "Install Updates & Restart"
|
||||
msgstr "Paigalda uuendused ja taaskäivita"
|
||||
|
||||
@ -1365,9 +1394,6 @@ msgstr ""
|
||||
"Märguanded on nüüd keelatud, sealhulgas vestlusteated. Sinu netiolekut "
|
||||
"muudeti, et teised teaksid, et sa ei pruugi nende teateid näha."
|
||||
|
||||
msgid "Windows"
|
||||
msgstr "Aknad"
|
||||
|
||||
msgid "Applications"
|
||||
msgstr "Rakendused"
|
||||
|
||||
@ -1396,25 +1422,6 @@ msgstr "'%s' on valmis"
|
||||
msgid "Evolution Calendar"
|
||||
msgstr "Evolutioni kalender"
|
||||
|
||||
#. translators:
|
||||
#. * The number of sound outputs on a particular device
|
||||
#, c-format
|
||||
msgid "%u Output"
|
||||
msgid_plural "%u Outputs"
|
||||
msgstr[0] "%u väljund"
|
||||
msgstr[1] "%u väljundit"
|
||||
|
||||
#. translators:
|
||||
#. * The number of sound inputs on a particular device
|
||||
#, c-format
|
||||
msgid "%u Input"
|
||||
msgid_plural "%u Inputs"
|
||||
msgstr[0] "%u sisend"
|
||||
msgstr[1] "%u sisendit"
|
||||
|
||||
msgid "System Sounds"
|
||||
msgstr "Süsteemi helid"
|
||||
|
||||
msgid "Print version"
|
||||
msgstr "Printimise versioon"
|
||||
|
||||
@ -1446,6 +1453,46 @@ msgstr "Vaikimisi"
|
||||
msgid "Authentication dialog was dismissed by the user"
|
||||
msgstr "Kasutaja katkestas autentimisdialoogi"
|
||||
|
||||
#~ msgid "Subscription request"
|
||||
#~ msgstr "Tellimuse päring"
|
||||
|
||||
#~ msgid "Connection error"
|
||||
#~ msgstr "Ühenduse viga"
|
||||
|
||||
#~ msgid "<b>%H:%M</b> on Yesterday"
|
||||
#~ msgstr "Eile <b>%H:%M</b>"
|
||||
|
||||
#~ msgid "<b>%H:%M</b> on <b>%A</b>, <b>%B</b> <b>%d</b>, %Y"
|
||||
#~ msgstr "<b>%H:%M</b>, <b>%A</b>, <b>%d. %B</b> %Y"
|
||||
|
||||
#~ msgid "Connection to %s failed"
|
||||
#~ msgstr "Ühendus %s nurjus"
|
||||
|
||||
#~ msgid "Reconnect"
|
||||
#~ msgstr "Ühendu uuesti"
|
||||
|
||||
#~ msgid "Wired"
|
||||
#~ msgstr "Juhtmega"
|
||||
|
||||
#~ msgid "Wireless"
|
||||
#~ msgstr "Juhtmeta"
|
||||
|
||||
#~ msgid "Mobile broadband"
|
||||
#~ msgstr "Mobiiliühendus"
|
||||
|
||||
#~ msgid "%u Output"
|
||||
#~ msgid_plural "%u Outputs"
|
||||
#~ msgstr[0] "%u väljund"
|
||||
#~ msgstr[1] "%u väljundit"
|
||||
|
||||
#~ msgid "%u Input"
|
||||
#~ msgid_plural "%u Inputs"
|
||||
#~ msgstr[0] "%u sisend"
|
||||
#~ msgstr[1] "%u sisendit"
|
||||
|
||||
#~ msgid "System Sounds"
|
||||
#~ msgstr "Süsteemi helid"
|
||||
|
||||
#~ msgid "disabled OpenSearch providers"
|
||||
#~ msgstr "keelatud OpenSearch pakkujad"
|
||||
|
||||
|
308
po/nb.po
308
po/nb.po
@ -2,14 +2,14 @@
|
||||
# Copyright (C) 2009 THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the gnome-shell package.
|
||||
#
|
||||
# Kjartan Maraas <kmaraas@gnome.org>, 2009-2012.
|
||||
# Kjartan Maraas <kmaraas@gnome.org>, 2009-2013.
|
||||
# Torstein Adolf Winterseth <kvikende@fsfe.org>, 2010.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: gnome-shell 3.7.x\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2012-12-09 14:01+0100\n"
|
||||
"PO-Revision-Date: 2012-12-09 14:02+0100\n"
|
||||
"POT-Creation-Date: 2013-01-14 11:20+0100\n"
|
||||
"PO-Revision-Date: 2013-01-14 11:21+0100\n"
|
||||
"Last-Translator: Kjartan Maraas <kmaraas@gnome.org>\n"
|
||||
"Language-Team: Norwegian bokmål <i18n-nb@lister.ping.uio.no>\n"
|
||||
"Language: \n"
|
||||
@ -311,11 +311,11 @@ msgstr ""
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:47
|
||||
msgid "Workspaces are managed dynamically"
|
||||
msgstr ""
|
||||
msgstr "Arbeidsområder håndteres dynamisk"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:48
|
||||
msgid "Workspaces only on primary monitor"
|
||||
msgstr ""
|
||||
msgstr "Arbeidsområder vises kun på hovedskjerm"
|
||||
|
||||
#: ../js/extensionPrefs/main.js:124
|
||||
#, c-format
|
||||
@ -330,11 +330,11 @@ msgstr "Utvidelse"
|
||||
msgid "Select an extension to configure using the combobox above."
|
||||
msgstr "Velg en utvidelse som skal konfigureres med komboboksen over."
|
||||
|
||||
#: ../js/gdm/loginDialog.js:565
|
||||
#: ../js/gdm/loginDialog.js:566
|
||||
msgid "Session..."
|
||||
msgstr "Økt …"
|
||||
|
||||
#: ../js/gdm/loginDialog.js:722
|
||||
#: ../js/gdm/loginDialog.js:723
|
||||
msgctxt "title"
|
||||
msgid "Sign In"
|
||||
msgstr "Logg inn"
|
||||
@ -342,35 +342,35 @@ msgstr "Logg inn"
|
||||
#. translators: this message is shown below the user list on the
|
||||
#. login screen. It can be activated to reveal an entry for
|
||||
#. manually entering the username.
|
||||
#: ../js/gdm/loginDialog.js:786
|
||||
#: ../js/gdm/loginDialog.js:787
|
||||
msgid "Not listed?"
|
||||
msgstr "Ikke listet?"
|
||||
|
||||
#: ../js/gdm/loginDialog.js:962 ../js/ui/components/networkAgent.js:137
|
||||
#: ../js/gdm/loginDialog.js:963 ../js/ui/components/networkAgent.js:137
|
||||
#: ../js/ui/components/polkitAgent.js:162 ../js/ui/endSessionDialog.js:373
|
||||
#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
|
||||
#: ../js/ui/status/bluetooth.js:413 ../js/ui/unlockDialog.js:178
|
||||
msgid "Cancel"
|
||||
msgstr "Avbryt"
|
||||
|
||||
#: ../js/gdm/loginDialog.js:978
|
||||
#: ../js/gdm/loginDialog.js:979
|
||||
msgctxt "button"
|
||||
msgid "Sign In"
|
||||
msgstr "Logg inn"
|
||||
|
||||
#: ../js/gdm/loginDialog.js:978
|
||||
#: ../js/gdm/loginDialog.js:979
|
||||
msgid "Next"
|
||||
msgstr "Neste"
|
||||
|
||||
#. TTLS and PEAP are actually much more complicated, but this complication
|
||||
#. is not visible here since we only care about phase2 authentication
|
||||
#. (and don't even care of which one)
|
||||
#: ../js/gdm/loginDialog.js:1086 ../js/ui/components/networkAgent.js:260
|
||||
#: ../js/gdm/loginDialog.js:1087 ../js/ui/components/networkAgent.js:260
|
||||
#: ../js/ui/components/networkAgent.js:278
|
||||
msgid "Username: "
|
||||
msgstr "Brukernavn: "
|
||||
|
||||
#: ../js/gdm/loginDialog.js:1382
|
||||
#: ../js/gdm/loginDialog.js:1383
|
||||
msgid "Login Window"
|
||||
msgstr "Innloggingsvindu"
|
||||
|
||||
@ -379,8 +379,8 @@ msgstr "Innloggingsvindu"
|
||||
msgid "Power"
|
||||
msgstr "Strøm"
|
||||
|
||||
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:674 ../js/ui/userMenu.js:678
|
||||
#: ../js/ui/userMenu.js:789
|
||||
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:675 ../js/ui/userMenu.js:679
|
||||
#: ../js/ui/userMenu.js:790
|
||||
msgid "Suspend"
|
||||
msgstr "Hvilemodus"
|
||||
|
||||
@ -388,8 +388,8 @@ msgstr "Hvilemodus"
|
||||
msgid "Restart"
|
||||
msgstr "Start på nytt"
|
||||
|
||||
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:676
|
||||
#: ../js/ui/userMenu.js:678 ../js/ui/userMenu.js:788
|
||||
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:677
|
||||
#: ../js/ui/userMenu.js:679 ../js/ui/userMenu.js:789
|
||||
msgid "Power Off"
|
||||
msgstr "Slå av"
|
||||
|
||||
@ -424,27 +424,19 @@ msgid "Execution of '%s' failed:"
|
||||
msgstr "Kjøring av «%s» feilet:"
|
||||
|
||||
#. Translators: Filter to display all applications
|
||||
#: ../js/ui/appDisplay.js:259
|
||||
#: ../js/ui/appDisplay.js:258
|
||||
msgid "All"
|
||||
msgstr "Alle"
|
||||
|
||||
#: ../js/ui/appDisplay.js:318
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "PROGRAMMER"
|
||||
|
||||
#: ../js/ui/appDisplay.js:375
|
||||
msgid "SETTINGS"
|
||||
msgstr "INNSTILLINGER"
|
||||
|
||||
#: ../js/ui/appDisplay.js:679
|
||||
#: ../js/ui/appDisplay.js:666
|
||||
msgid "New Window"
|
||||
msgstr "Nytt vindu"
|
||||
|
||||
#: ../js/ui/appDisplay.js:682 ../js/ui/dash.js:289
|
||||
#: ../js/ui/appDisplay.js:669 ../js/ui/dash.js:289
|
||||
msgid "Remove from Favorites"
|
||||
msgstr "Fjern fra favoritter"
|
||||
|
||||
#: ../js/ui/appDisplay.js:683
|
||||
#: ../js/ui/appDisplay.js:670
|
||||
msgid "Add to Favorites"
|
||||
msgstr "Legg til i favoritter"
|
||||
|
||||
@ -461,19 +453,19 @@ msgstr "%s ble fjernet fra dine favoritter."
|
||||
#. Translators: Shown in calendar event list for all day events
|
||||
#. * Keep it short, best if you can use less then 10 characters
|
||||
#.
|
||||
#: ../js/ui/calendar.js:62
|
||||
#: ../js/ui/calendar.js:61
|
||||
msgctxt "event list time"
|
||||
msgid "All Day"
|
||||
msgstr "Hele dagen"
|
||||
|
||||
#. Translators: Shown in calendar event list, if 24h format
|
||||
#: ../js/ui/calendar.js:67
|
||||
#: ../js/ui/calendar.js:66
|
||||
msgctxt "event list time"
|
||||
msgid "%H:%M"
|
||||
msgstr "%H.%M"
|
||||
|
||||
#. Transators: Shown in calendar event list, if 12h format
|
||||
#: ../js/ui/calendar.js:74
|
||||
#: ../js/ui/calendar.js:73
|
||||
msgctxt "event list time"
|
||||
msgid "%l:%M %p"
|
||||
msgstr "%l.%M %p"
|
||||
@ -483,43 +475,43 @@ msgstr "%l.%M %p"
|
||||
#. * NOTE: These grid abbreviations are always shown together
|
||||
#. * and in order, e.g. "S M T W T F S".
|
||||
#.
|
||||
#: ../js/ui/calendar.js:114
|
||||
#: ../js/ui/calendar.js:104
|
||||
msgctxt "grid sunday"
|
||||
msgid "S"
|
||||
msgstr "S"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Monday
|
||||
#: ../js/ui/calendar.js:116
|
||||
#: ../js/ui/calendar.js:106
|
||||
msgctxt "grid monday"
|
||||
msgid "M"
|
||||
msgstr "M"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Tuesday
|
||||
#: ../js/ui/calendar.js:118
|
||||
#: ../js/ui/calendar.js:108
|
||||
msgctxt "grid tuesday"
|
||||
msgid "T"
|
||||
msgstr "T"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Wednesday
|
||||
#: ../js/ui/calendar.js:120
|
||||
#: ../js/ui/calendar.js:110
|
||||
msgctxt "grid wednesday"
|
||||
msgid "W"
|
||||
msgstr "O"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Thursday
|
||||
#: ../js/ui/calendar.js:122
|
||||
#: ../js/ui/calendar.js:112
|
||||
msgctxt "grid thursday"
|
||||
msgid "T"
|
||||
msgstr "T"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Friday
|
||||
#: ../js/ui/calendar.js:124
|
||||
#: ../js/ui/calendar.js:114
|
||||
msgctxt "grid friday"
|
||||
msgid "F"
|
||||
msgstr "F"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Saturday
|
||||
#: ../js/ui/calendar.js:126
|
||||
#: ../js/ui/calendar.js:116
|
||||
msgctxt "grid saturday"
|
||||
msgid "S"
|
||||
msgstr "L"
|
||||
@ -530,77 +522,77 @@ msgstr "L"
|
||||
#. * so they need to be unique (e.g. Tuesday and Thursday cannot
|
||||
#. * both be 'T').
|
||||
#.
|
||||
#: ../js/ui/calendar.js:139
|
||||
#: ../js/ui/calendar.js:129
|
||||
msgctxt "list sunday"
|
||||
msgid "Su"
|
||||
msgstr "Sø"
|
||||
|
||||
#. Translators: Event list abbreviation for Monday
|
||||
#: ../js/ui/calendar.js:141
|
||||
#: ../js/ui/calendar.js:131
|
||||
msgctxt "list monday"
|
||||
msgid "M"
|
||||
msgstr "Ma"
|
||||
|
||||
#. Translators: Event list abbreviation for Tuesday
|
||||
#: ../js/ui/calendar.js:143
|
||||
#: ../js/ui/calendar.js:133
|
||||
msgctxt "list tuesday"
|
||||
msgid "T"
|
||||
msgstr "Ti"
|
||||
|
||||
#. Translators: Event list abbreviation for Wednesday
|
||||
#: ../js/ui/calendar.js:145
|
||||
#: ../js/ui/calendar.js:135
|
||||
msgctxt "list wednesday"
|
||||
msgid "W"
|
||||
msgstr "On"
|
||||
|
||||
#. Translators: Event list abbreviation for Thursday
|
||||
#: ../js/ui/calendar.js:147
|
||||
#: ../js/ui/calendar.js:137
|
||||
msgctxt "list thursday"
|
||||
msgid "Th"
|
||||
msgstr "To"
|
||||
|
||||
#. Translators: Event list abbreviation for Friday
|
||||
#: ../js/ui/calendar.js:149
|
||||
#: ../js/ui/calendar.js:139
|
||||
msgctxt "list friday"
|
||||
msgid "F"
|
||||
msgstr "Fr"
|
||||
|
||||
#. Translators: Event list abbreviation for Saturday
|
||||
#: ../js/ui/calendar.js:151
|
||||
#: ../js/ui/calendar.js:141
|
||||
msgctxt "list saturday"
|
||||
msgid "S"
|
||||
msgstr "Lø"
|
||||
|
||||
#. Translators: Text to show if there are no events
|
||||
#: ../js/ui/calendar.js:700
|
||||
#: ../js/ui/calendar.js:674
|
||||
msgid "Nothing Scheduled"
|
||||
msgstr "Ingenting planlagt"
|
||||
|
||||
#. Translators: Shown on calendar heading when selected day occurs on current year
|
||||
#: ../js/ui/calendar.js:716
|
||||
#: ../js/ui/calendar.js:690
|
||||
msgctxt "calendar heading"
|
||||
msgid "%A, %B %d"
|
||||
msgstr "%A %B %d"
|
||||
|
||||
#. Translators: Shown on calendar heading when selected day occurs on different year
|
||||
#: ../js/ui/calendar.js:719
|
||||
#: ../js/ui/calendar.js:693
|
||||
msgctxt "calendar heading"
|
||||
msgid "%A, %B %d, %Y"
|
||||
msgstr "%A %B %d, %Y"
|
||||
|
||||
#: ../js/ui/calendar.js:729
|
||||
#: ../js/ui/calendar.js:703
|
||||
msgid "Today"
|
||||
msgstr "I dag"
|
||||
|
||||
#: ../js/ui/calendar.js:733
|
||||
#: ../js/ui/calendar.js:707
|
||||
msgid "Tomorrow"
|
||||
msgstr "I morgen"
|
||||
|
||||
#: ../js/ui/calendar.js:744
|
||||
#: ../js/ui/calendar.js:718
|
||||
msgid "This week"
|
||||
msgstr "Denne uken"
|
||||
|
||||
#: ../js/ui/calendar.js:752
|
||||
#: ../js/ui/calendar.js:726
|
||||
msgid "Next week"
|
||||
msgstr "Neste uke"
|
||||
|
||||
@ -617,11 +609,11 @@ msgstr "Åpne med %s"
|
||||
msgid "Eject"
|
||||
msgstr "Løs ut"
|
||||
|
||||
#: ../js/ui/components/keyring.js:86 ../js/ui/components/polkitAgent.js:260
|
||||
#: ../js/ui/components/keyring.js:82 ../js/ui/components/polkitAgent.js:268
|
||||
msgid "Password:"
|
||||
msgstr "Passord:"
|
||||
|
||||
#: ../js/ui/components/keyring.js:105
|
||||
#: ../js/ui/components/keyring.js:101
|
||||
msgid "Type again:"
|
||||
msgstr "Skriv på nytt:"
|
||||
|
||||
@ -709,7 +701,7 @@ msgstr "Autentisering kreves"
|
||||
msgid "Administrator"
|
||||
msgstr "Administrator"
|
||||
|
||||
#: ../js/ui/components/polkitAgent.js:166
|
||||
#: ../js/ui/components/polkitAgent.js:165
|
||||
msgid "Authenticate"
|
||||
msgstr "Autentiser"
|
||||
|
||||
@ -717,7 +709,7 @@ msgstr "Autentiser"
|
||||
#. * requested authentication was not gained; this can happen
|
||||
#. * because of an authentication error (like invalid password),
|
||||
#. * for instance.
|
||||
#: ../js/ui/components/polkitAgent.js:248 ../js/ui/shellMountOperation.js:383
|
||||
#: ../js/ui/components/polkitAgent.js:256 ../js/ui/shellMountOperation.js:383
|
||||
msgid "Sorry, that didn't work. Please try again."
|
||||
msgstr "Beklager, det virket ikke. Vennligst prøv igjen."
|
||||
|
||||
@ -962,11 +954,11 @@ msgstr "Kan ikke koble til %s"
|
||||
msgid "View account"
|
||||
msgstr "Vis konto"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1384
|
||||
#: ../js/ui/components/telepathyClient.js:1383
|
||||
msgid "Unknown reason"
|
||||
msgstr "Ukjent årsak"
|
||||
|
||||
#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:87
|
||||
#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:81
|
||||
msgid "Windows"
|
||||
msgstr "Vinduer"
|
||||
|
||||
@ -974,18 +966,18 @@ msgstr "Vinduer"
|
||||
msgid "Show Applications"
|
||||
msgstr "Vis programmer"
|
||||
|
||||
#: ../js/ui/dateMenu.js:86
|
||||
#: ../js/ui/dateMenu.js:87
|
||||
msgid "Date and Time Settings"
|
||||
msgstr "Innstillinger for dato og klokkeslett"
|
||||
|
||||
#: ../js/ui/dateMenu.js:111
|
||||
#: ../js/ui/dateMenu.js:112
|
||||
msgid "Open Calendar"
|
||||
msgstr "Åpne kalender"
|
||||
|
||||
#. Translators: This is the date format to use when the calendar popup is
|
||||
#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
|
||||
#.
|
||||
#: ../js/ui/dateMenu.js:201
|
||||
#: ../js/ui/dateMenu.js:191
|
||||
msgid "%A %B %e, %Y"
|
||||
msgstr "%a %e %B, %Y"
|
||||
|
||||
@ -1090,78 +1082,80 @@ msgstr "Installer"
|
||||
msgid "Download and install '%s' from extensions.gnome.org?"
|
||||
msgstr "Last ned og installer «%s» fra extensions.gnome.org?"
|
||||
|
||||
#: ../js/ui/keyboard.js:308
|
||||
#: ../js/ui/keyboard.js:291
|
||||
msgid "tray"
|
||||
msgstr "varslingsområde"
|
||||
|
||||
#: ../js/ui/keyboard.js:555 ../js/ui/status/keyboard.js:195
|
||||
#: ../js/ui/status/power.js:205
|
||||
#: ../js/ui/keyboard.js:636 ../js/ui/status/keyboard.js:309
|
||||
#: ../js/ui/status/power.js:206
|
||||
msgid "Keyboard"
|
||||
msgstr "Tastatur"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:691
|
||||
#: ../js/ui/lookingGlass.js:692
|
||||
msgid "No extensions installed"
|
||||
msgstr "Ingen utvidelser installert"
|
||||
|
||||
#. Translators: argument is an extension UUID.
|
||||
#: ../js/ui/lookingGlass.js:745
|
||||
#: ../js/ui/lookingGlass.js:746
|
||||
#, c-format
|
||||
msgid "%s has not emitted any errors."
|
||||
msgstr "%s har ikke avgitt noen feil."
|
||||
|
||||
#: ../js/ui/lookingGlass.js:751
|
||||
#: ../js/ui/lookingGlass.js:752
|
||||
msgid "Hide Errors"
|
||||
msgstr "Skjul feil"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:755 ../js/ui/lookingGlass.js:815
|
||||
#: ../js/ui/lookingGlass.js:756 ../js/ui/lookingGlass.js:816
|
||||
msgid "Show Errors"
|
||||
msgstr "Vis feil"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:764
|
||||
#: ../js/ui/lookingGlass.js:765
|
||||
msgid "Enabled"
|
||||
msgstr "Aktivert"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:767
|
||||
#. translators:
|
||||
#. * The device has been disabled
|
||||
#: ../js/ui/lookingGlass.js:768 ../src/gvc/gvc-mixer-control.c:1830
|
||||
msgid "Disabled"
|
||||
msgstr "Deaktivert"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:769
|
||||
#: ../js/ui/lookingGlass.js:770
|
||||
msgid "Error"
|
||||
msgstr "Feil"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:771
|
||||
#: ../js/ui/lookingGlass.js:772
|
||||
msgid "Out of date"
|
||||
msgstr "Utdatert"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:773
|
||||
#: ../js/ui/lookingGlass.js:774
|
||||
msgid "Downloading"
|
||||
msgstr "Laster ned"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:797
|
||||
#: ../js/ui/lookingGlass.js:798
|
||||
msgid "View Source"
|
||||
msgstr "Vis kildekode"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:806
|
||||
#: ../js/ui/lookingGlass.js:807
|
||||
msgid "Web Page"
|
||||
msgstr "Nettside"
|
||||
|
||||
#: ../js/ui/messageTray.js:1088
|
||||
#: ../js/ui/messageTray.js:1090
|
||||
msgid "Open"
|
||||
msgstr "Åpne"
|
||||
|
||||
#: ../js/ui/messageTray.js:1095
|
||||
#: ../js/ui/messageTray.js:1097
|
||||
msgid "Remove"
|
||||
msgstr "Fjern"
|
||||
|
||||
#: ../js/ui/messageTray.js:1551
|
||||
#: ../js/ui/messageTray.js:1554
|
||||
msgid "No Messages"
|
||||
msgstr "Ingen meldinger"
|
||||
|
||||
#: ../js/ui/messageTray.js:1568
|
||||
#: ../js/ui/messageTray.js:1572
|
||||
msgid "Message Tray"
|
||||
msgstr "Meldingstrau"
|
||||
|
||||
#: ../js/ui/messageTray.js:2635
|
||||
#: ../js/ui/messageTray.js:2641
|
||||
msgid "System Information"
|
||||
msgstr "Systeminformasjon"
|
||||
|
||||
@ -1170,11 +1164,11 @@ msgctxt "program"
|
||||
msgid "Unknown"
|
||||
msgstr "Ukjent"
|
||||
|
||||
#: ../js/ui/overview.js:95
|
||||
#: ../js/ui/overview.js:92
|
||||
msgid "Undo"
|
||||
msgstr "Angre"
|
||||
|
||||
#: ../js/ui/overview.js:144
|
||||
#: ../js/ui/overview.js:139
|
||||
msgid "Overview"
|
||||
msgstr "Oversikt"
|
||||
|
||||
@ -1182,27 +1176,27 @@ msgstr "Oversikt"
|
||||
#. in the search entry when no search is
|
||||
#. active; it should not exceed ~30
|
||||
#. characters.
|
||||
#: ../js/ui/overview.js:221
|
||||
#: ../js/ui/overview.js:218
|
||||
msgid "Type to search..."
|
||||
msgstr "Skriv for å søke …"
|
||||
|
||||
#. Translators: this is the name of the dock/favorites area on
|
||||
#. the left of the overview
|
||||
#: ../js/ui/overview.js:242
|
||||
#: ../js/ui/overview.js:236
|
||||
msgid "Dash"
|
||||
msgstr "Favoritter"
|
||||
|
||||
#: ../js/ui/panel.js:608
|
||||
#: ../js/ui/panel.js:613
|
||||
msgid "Quit"
|
||||
msgstr "Avslutt"
|
||||
|
||||
#. Translators: If there is no suitable word for "Activities"
|
||||
#. in your language, you can use the word for "Overview".
|
||||
#: ../js/ui/panel.js:637
|
||||
#: ../js/ui/panel.js:642
|
||||
msgid "Activities"
|
||||
msgstr "Aktiviteter"
|
||||
|
||||
#: ../js/ui/panel.js:980
|
||||
#: ../js/ui/panel.js:976
|
||||
msgid "Top Bar"
|
||||
msgstr "Topp-panel"
|
||||
|
||||
@ -1211,7 +1205,7 @@ msgstr "Topp-panel"
|
||||
#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
|
||||
#. switches containing "◯" and "|"). Other values will
|
||||
#. simply result in invisible toggle switches.
|
||||
#: ../js/ui/popupMenu.js:732
|
||||
#: ../js/ui/popupMenu.js:727
|
||||
msgid "toggle-switch-us"
|
||||
msgstr "toggle-switch-intl"
|
||||
|
||||
@ -1225,33 +1219,33 @@ msgstr "Lukk"
|
||||
|
||||
#. Translators: This is a time format for a date in
|
||||
#. long format
|
||||
#: ../js/ui/screenShield.js:112
|
||||
#: ../js/ui/screenShield.js:113
|
||||
msgid "%A, %B %d"
|
||||
msgstr "%A, %B %d"
|
||||
|
||||
#: ../js/ui/screenShield.js:176
|
||||
#: ../js/ui/screenShield.js:177
|
||||
#, c-format
|
||||
msgid "%d new message"
|
||||
msgid_plural "%d new messages"
|
||||
msgstr[0] "%d ny melding"
|
||||
msgstr[1] "%d nye meldinger"
|
||||
|
||||
#: ../js/ui/screenShield.js:178
|
||||
#: ../js/ui/screenShield.js:179
|
||||
#, c-format
|
||||
msgid "%d new notification"
|
||||
msgid_plural "%d new notifications"
|
||||
msgstr[0] "%d ny varsling"
|
||||
msgstr[1] "%d nye varslinger"
|
||||
|
||||
#: ../js/ui/screenShield.js:402 ../js/ui/userMenu.js:780
|
||||
#: ../js/ui/screenShield.js:403 ../js/ui/userMenu.js:781
|
||||
msgid "Lock"
|
||||
msgstr "Lås"
|
||||
|
||||
#: ../js/ui/searchDisplay.js:277
|
||||
#: ../js/ui/searchDisplay.js:403
|
||||
msgid "Searching..."
|
||||
msgstr "Søker …"
|
||||
|
||||
#: ../js/ui/searchDisplay.js:325
|
||||
#: ../js/ui/searchDisplay.js:451
|
||||
msgid "No results."
|
||||
msgstr "Ingen resultater."
|
||||
|
||||
@ -1367,7 +1361,7 @@ msgid "disconnecting..."
|
||||
msgstr "kobler fra …"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:220 ../js/ui/status/network.js:442
|
||||
#: ../js/ui/status/network.js:1453
|
||||
#: ../js/ui/status/network.js:1464
|
||||
msgid "connecting..."
|
||||
msgstr "kobler til …"
|
||||
|
||||
@ -1383,7 +1377,7 @@ msgstr "Innstillinger for tastatur"
|
||||
msgid "Mouse Settings"
|
||||
msgstr "Innstillinger for mus"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:253 ../js/ui/status/volume.js:270
|
||||
#: ../js/ui/status/bluetooth.js:253 ../js/ui/status/volume.js:314
|
||||
msgid "Sound Settings"
|
||||
msgstr "Innstillinger for lyd"
|
||||
|
||||
@ -1447,15 +1441,15 @@ msgstr "Vennligst oppgi PIN som oppgitt på enheten."
|
||||
msgid "OK"
|
||||
msgstr "OK"
|
||||
|
||||
#: ../js/ui/status/keyboard.js:228
|
||||
#: ../js/ui/status/keyboard.js:363
|
||||
msgid "Show Keyboard Layout"
|
||||
msgstr "Vis tastaturutforming"
|
||||
|
||||
#: ../js/ui/status/keyboard.js:233
|
||||
#: ../js/ui/status/keyboard.js:368
|
||||
msgid "Region and Language Settings"
|
||||
msgstr "Innstillinger for region og språk"
|
||||
|
||||
#: ../js/ui/status/lockScreenMenu.js:18
|
||||
#: ../js/ui/status/lockScreenMenu.js:43
|
||||
msgid "Volume, network, battery"
|
||||
msgstr "Volum, nettverk, batteri"
|
||||
|
||||
@ -1475,7 +1469,7 @@ 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:445 ../js/ui/status/network.js:1456
|
||||
#: ../js/ui/status/network.js:445 ../js/ui/status/network.js:1467
|
||||
msgid "authentication required"
|
||||
msgstr "autentisering kreves"
|
||||
|
||||
@ -1496,72 +1490,72 @@ msgstr "kabel koblet fra"
|
||||
msgid "unavailable"
|
||||
msgstr "ikke tilgjengelig"
|
||||
|
||||
#: ../js/ui/status/network.js:469 ../js/ui/status/network.js:1458
|
||||
#: ../js/ui/status/network.js:469 ../js/ui/status/network.js:1469
|
||||
msgid "connection failed"
|
||||
msgstr "tilkobling feilet"
|
||||
|
||||
#: ../js/ui/status/network.js:525 ../js/ui/status/network.js:1392
|
||||
#: ../js/ui/status/network.js:1534
|
||||
#: ../js/ui/status/network.js:525 ../js/ui/status/network.js:1403
|
||||
#: ../js/ui/status/network.js:1545
|
||||
msgid "More..."
|
||||
msgstr "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:561 ../js/ui/status/network.js:1322
|
||||
#: ../js/ui/status/network.js:561 ../js/ui/status/network.js:1333
|
||||
msgid "Connected (private)"
|
||||
msgstr "Tilkoblet (privat)"
|
||||
|
||||
#: ../js/ui/status/network.js:619
|
||||
#: ../js/ui/status/network.js:641
|
||||
msgid "Auto Ethernet"
|
||||
msgstr "Automatisk Ethernet"
|
||||
|
||||
#: ../js/ui/status/network.js:677
|
||||
#: ../js/ui/status/network.js:688
|
||||
msgid "Auto broadband"
|
||||
msgstr "Automatisk bredbånd"
|
||||
|
||||
#: ../js/ui/status/network.js:680
|
||||
#: ../js/ui/status/network.js:691
|
||||
msgid "Auto dial-up"
|
||||
msgstr "Automatisk oppringt"
|
||||
|
||||
#. TRANSLATORS: this the automatic wireless connection name (including the network name)
|
||||
#: ../js/ui/status/network.js:809 ../js/ui/status/network.js:1339
|
||||
#: ../js/ui/status/network.js:820 ../js/ui/status/network.js:1350
|
||||
#, c-format
|
||||
msgid "Auto %s"
|
||||
msgstr "Automatisk %s"
|
||||
|
||||
#: ../js/ui/status/network.js:811
|
||||
#: ../js/ui/status/network.js:822
|
||||
msgid "Auto bluetooth"
|
||||
msgstr "Automatisk Bluetooth"
|
||||
|
||||
#: ../js/ui/status/network.js:1341
|
||||
#: ../js/ui/status/network.js:1352
|
||||
msgid "Auto wireless"
|
||||
msgstr "Automatisk trådløst"
|
||||
|
||||
#: ../js/ui/status/network.js:1584
|
||||
#: ../js/ui/status/network.js:1595
|
||||
msgid "Enable networking"
|
||||
msgstr "Slå på nettverk"
|
||||
|
||||
#: ../js/ui/status/network.js:1616
|
||||
#: ../js/ui/status/network.js:1627
|
||||
msgid "Wi-Fi"
|
||||
msgstr "Wi-Fi"
|
||||
|
||||
#: ../js/ui/status/network.js:1635
|
||||
#: ../js/ui/status/network.js:1646
|
||||
msgid "Network Settings"
|
||||
msgstr "Innstillinger for nettverk"
|
||||
|
||||
#: ../js/ui/status/network.js:1679
|
||||
#: ../js/ui/status/network.js:1692
|
||||
msgid "Network Manager"
|
||||
msgstr "Nettverkshåndtering"
|
||||
|
||||
#: ../js/ui/status/network.js:1761
|
||||
#: ../js/ui/status/network.js:1774
|
||||
msgid "Connection failed"
|
||||
msgstr "Tilkobling feilet"
|
||||
|
||||
#: ../js/ui/status/network.js:1762
|
||||
#: ../js/ui/status/network.js:1775
|
||||
msgid "Activation of network connection failed"
|
||||
msgstr "Aktivering av nettverkstilkobling feilet"
|
||||
|
||||
#: ../js/ui/status/network.js:2079
|
||||
#: ../js/ui/status/network.js:2092
|
||||
msgid "Networking is disabled"
|
||||
msgstr "Nettverk er slått av"
|
||||
|
||||
@ -1617,65 +1611,65 @@ msgctxt "percent of battery remaining"
|
||||
msgid "%d%%"
|
||||
msgstr "%d%%"
|
||||
|
||||
#: ../js/ui/status/power.js:195
|
||||
#: ../js/ui/status/power.js:196
|
||||
msgid "AC adapter"
|
||||
msgstr "Strømadapter"
|
||||
|
||||
#: ../js/ui/status/power.js:197
|
||||
#: ../js/ui/status/power.js:198
|
||||
msgid "Laptop battery"
|
||||
msgstr "Batteri på bærbar"
|
||||
|
||||
#: ../js/ui/status/power.js:199
|
||||
#: ../js/ui/status/power.js:200
|
||||
msgid "UPS"
|
||||
msgstr "UPS"
|
||||
|
||||
#: ../js/ui/status/power.js:201
|
||||
#: ../js/ui/status/power.js:202
|
||||
msgid "Monitor"
|
||||
msgstr "Skjerm"
|
||||
|
||||
#: ../js/ui/status/power.js:203
|
||||
#: ../js/ui/status/power.js:204
|
||||
msgid "Mouse"
|
||||
msgstr "Mus"
|
||||
|
||||
#: ../js/ui/status/power.js:207
|
||||
#: ../js/ui/status/power.js:208
|
||||
msgid "PDA"
|
||||
msgstr "PDA"
|
||||
|
||||
#: ../js/ui/status/power.js:209
|
||||
#: ../js/ui/status/power.js:210
|
||||
msgid "Cell phone"
|
||||
msgstr "Mobiltelefon"
|
||||
|
||||
#: ../js/ui/status/power.js:211
|
||||
#: ../js/ui/status/power.js:212
|
||||
msgid "Media player"
|
||||
msgstr "Medieavspiller"
|
||||
|
||||
#: ../js/ui/status/power.js:213
|
||||
#: ../js/ui/status/power.js:214
|
||||
msgid "Tablet"
|
||||
msgstr "Nettbrett"
|
||||
|
||||
#: ../js/ui/status/power.js:215
|
||||
#: ../js/ui/status/power.js:216
|
||||
msgid "Computer"
|
||||
msgstr "Datamaskin"
|
||||
|
||||
#: ../js/ui/status/power.js:217
|
||||
#: ../js/ui/status/power.js:218
|
||||
msgctxt "device"
|
||||
msgid "Unknown"
|
||||
msgstr "Ukjent"
|
||||
|
||||
#. Translators: This is the label for audio volume
|
||||
#: ../js/ui/status/volume.js:50 ../js/ui/status/volume.js:251
|
||||
#: ../js/ui/status/volume.js:247 ../js/ui/status/volume.js:295
|
||||
msgid "Volume"
|
||||
msgstr "Volum"
|
||||
|
||||
#: ../js/ui/status/volume.js:62
|
||||
#: ../js/ui/status/volume.js:256
|
||||
msgid "Microphone"
|
||||
msgstr "Mikrofon"
|
||||
|
||||
#: ../js/ui/unlockDialog.js:201
|
||||
#: ../js/ui/unlockDialog.js:203
|
||||
msgid "Log in as another user"
|
||||
msgstr "Logg inn som en annen bruker"
|
||||
|
||||
#: ../js/ui/unlockDialog.js:224
|
||||
#: ../js/ui/unlockDialog.js:229
|
||||
msgid "Unlock Window"
|
||||
msgstr "Lås opp vindu"
|
||||
|
||||
@ -1703,31 +1697,31 @@ msgstr "Ledig"
|
||||
msgid "Offline"
|
||||
msgstr "Frakoblet"
|
||||
|
||||
#: ../js/ui/userMenu.js:754
|
||||
#: ../js/ui/userMenu.js:755
|
||||
msgid "Notifications"
|
||||
msgstr "Varslinger"
|
||||
|
||||
#: ../js/ui/userMenu.js:762
|
||||
#: ../js/ui/userMenu.js:763
|
||||
msgid "Settings"
|
||||
msgstr "Innstillinger"
|
||||
|
||||
#: ../js/ui/userMenu.js:770
|
||||
#: ../js/ui/userMenu.js:771
|
||||
msgid "Switch User"
|
||||
msgstr "Bytt bruker"
|
||||
|
||||
#: ../js/ui/userMenu.js:775
|
||||
#: ../js/ui/userMenu.js:776
|
||||
msgid "Log Out"
|
||||
msgstr "Logg ut"
|
||||
|
||||
#: ../js/ui/userMenu.js:795
|
||||
#: ../js/ui/userMenu.js:796
|
||||
msgid "Install Updates & Restart"
|
||||
msgstr "Installer oppdateringer og start på nytt"
|
||||
|
||||
#: ../js/ui/userMenu.js:813
|
||||
#: ../js/ui/userMenu.js:814
|
||||
msgid "Your chat status will be set to busy"
|
||||
msgstr "Din pratestatus vil bli satt til opptatt"
|
||||
|
||||
#: ../js/ui/userMenu.js:814
|
||||
#: ../js/ui/userMenu.js:815
|
||||
msgid ""
|
||||
"Notifications are now disabled, including chat messages. Your online status "
|
||||
"has been adjusted to let others know that you might not see their messages."
|
||||
@ -1736,15 +1730,15 @@ msgstr ""
|
||||
"tilkoblingsstatus er justert for å la andre vite at du kanskje ikke ser "
|
||||
"deres meldinger."
|
||||
|
||||
#: ../js/ui/viewSelector.js:91
|
||||
#: ../js/ui/viewSelector.js:85
|
||||
msgid "Applications"
|
||||
msgstr "Programmer"
|
||||
|
||||
#: ../js/ui/viewSelector.js:95
|
||||
#: ../js/ui/viewSelector.js:89
|
||||
msgid "Search"
|
||||
msgstr "Søk"
|
||||
|
||||
#: ../js/ui/wanda.js:94
|
||||
#: ../js/ui/wanda.js:92
|
||||
#, c-format
|
||||
msgid ""
|
||||
"Sorry, no wisdom for you today:\n"
|
||||
@ -1753,15 +1747,11 @@ msgstr ""
|
||||
"Beklager, ingen visdom til deg i dag:\n"
|
||||
"%s"
|
||||
|
||||
#: ../js/ui/wanda.js:98
|
||||
#: ../js/ui/wanda.js:96
|
||||
#, c-format
|
||||
msgid "%s the Oracle says"
|
||||
msgstr "Orakelet sier %s"
|
||||
|
||||
#: ../js/ui/wanda.js:139
|
||||
msgid "Your favorite Easter Egg"
|
||||
msgstr "Favorittpåskeegget ditt"
|
||||
|
||||
#: ../js/ui/windowAttentionHandler.js:19
|
||||
#, c-format
|
||||
msgid "'%s' is ready"
|
||||
@ -1771,6 +1761,28 @@ msgstr "«%s» er klar"
|
||||
msgid "Evolution Calendar"
|
||||
msgstr "Evolution kalender"
|
||||
|
||||
#. translators:
|
||||
#. * The number of sound outputs on a particular device
|
||||
#: ../src/gvc/gvc-mixer-control.c:1837
|
||||
#, c-format
|
||||
msgid "%u Output"
|
||||
msgid_plural "%u Outputs"
|
||||
msgstr[0] "%u utgang"
|
||||
msgstr[1] "%u utganger"
|
||||
|
||||
#. translators:
|
||||
#. * The number of sound inputs on a particular device
|
||||
#: ../src/gvc/gvc-mixer-control.c:1847
|
||||
#, c-format
|
||||
msgid "%u Input"
|
||||
msgid_plural "%u Inputs"
|
||||
msgstr[0] "%u inngang"
|
||||
msgstr[1] "%u innganger"
|
||||
|
||||
#: ../src/gvc/gvc-mixer-control.c:2371
|
||||
msgid "System Sounds"
|
||||
msgstr "Systemlyder"
|
||||
|
||||
#: ../src/main.c:332
|
||||
msgid "Print version"
|
||||
msgstr "Skriv ut versjon"
|
||||
|
1059
po/pt_BR.po
1059
po/pt_BR.po
File diff suppressed because it is too large
Load Diff
828
po/zh_CN.po
828
po/zh_CN.po
File diff suppressed because it is too large
Load Diff
@ -75,9 +75,8 @@ def start_shell():
|
||||
if 'GI_TYPELIB_PATH' in os.environ:
|
||||
typelib_dir += ':%s' % (os.environ['GI_TYPELIB_PATH'],)
|
||||
|
||||
env.update({'GNOME_SHELL_JS' : os.path.join(top_dir, "js"),
|
||||
'GNOME_SHELL_BINDIR' : self_dir,
|
||||
'GI_TYPELIB_PATH' : typelib_dir,
|
||||
env.update({'GI_TYPELIB_PATH' : typelib_dir,
|
||||
'GNOME_SHELL_JS' : os.path.join(top_dir, "js"),
|
||||
'GNOME_SHELL_DATADIR' : os.path.join(top_dir, "data"),
|
||||
'GSETTINGS_SCHEMA_DIR' : os.path.join(top_dir, "data") })
|
||||
|
||||
|
@ -284,12 +284,44 @@ gnome_shell_plugin_kill_switch_workspace (MetaPlugin *plugin)
|
||||
_shell_wm_kill_switch_workspace (get_shell_wm());
|
||||
}
|
||||
|
||||
static gboolean
|
||||
ignore_crossing_event (MetaPlugin *plugin,
|
||||
XIEnterEvent *enter_event)
|
||||
{
|
||||
MetaScreen *screen = meta_plugin_get_screen (plugin);
|
||||
ClutterStage *stage = CLUTTER_STAGE (meta_get_stage_for_screen (screen));
|
||||
|
||||
if (enter_event->event == clutter_x11_get_stage_window (stage))
|
||||
{
|
||||
/* If the pointer enters a child of the stage window (eg, a
|
||||
* trayicon), we want to consider it to still be in the stage,
|
||||
* so don't let Clutter see the event.
|
||||
*/
|
||||
if (enter_event->detail == XINotifyInferior)
|
||||
return TRUE;
|
||||
|
||||
/* If the pointer is grabbed by a window it is not currently in,
|
||||
* filter that out as well. In particular, if a trayicon grabs
|
||||
* the pointer after a click on its label, we don't want to hide
|
||||
* the message tray. Filtering out this event will leave Clutter
|
||||
* out of sync, but that happens fairly often with grabs, and we
|
||||
* can work around it. (Eg, shell_global_sync_pointer().)
|
||||
*/
|
||||
if (enter_event->mode == XINotifyGrab &&
|
||||
(enter_event->detail == XINotifyNonlinear ||
|
||||
enter_event->detail == XINotifyNonlinearVirtual))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gnome_shell_plugin_xevent_filter (MetaPlugin *plugin,
|
||||
XEvent *xev)
|
||||
{
|
||||
MetaScreen *screen = meta_plugin_get_screen (plugin);
|
||||
ClutterStage *stage = CLUTTER_STAGE (meta_get_stage_for_screen (screen));
|
||||
MetaDisplay *display = meta_screen_get_display (screen);
|
||||
|
||||
GnomeShellPlugin *shell_plugin = GNOME_SHELL_PLUGIN (plugin);
|
||||
#ifdef GLX_INTEL_swap_event
|
||||
@ -309,26 +341,15 @@ gnome_shell_plugin_xevent_filter (MetaPlugin *plugin,
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((xev->xany.type == EnterNotify || xev->xany.type == LeaveNotify)
|
||||
&& xev->xcrossing.window == clutter_x11_get_stage_window (stage))
|
||||
/* Make sure that Clutter doesn't see certain focus change events,
|
||||
* so that when we're moving into something like a tray icon, we
|
||||
* don't unfocus the container. */
|
||||
if (xev->type == GenericEvent &&
|
||||
xev->xcookie.extension == meta_display_get_xinput_opcode (display))
|
||||
{
|
||||
/* If the pointer enters a child of the stage window (eg, a
|
||||
* trayicon), we want to consider it to still be in the stage,
|
||||
* so don't let Clutter see the event.
|
||||
*/
|
||||
if (xev->xcrossing.detail == NotifyInferior)
|
||||
return TRUE;
|
||||
|
||||
/* If the pointer is grabbed by a window it is not currently in,
|
||||
* filter that out as well. In particular, if a trayicon grabs
|
||||
* the pointer after a click on its label, we don't want to hide
|
||||
* the message tray. Filtering out this event will leave Clutter
|
||||
* out of sync, but that happens fairly often with grabs, and we
|
||||
* can work around it. (Eg, shell_global_sync_pointer().)
|
||||
*/
|
||||
if (xev->xcrossing.mode == NotifyGrab &&
|
||||
(xev->xcrossing.detail == NotifyNonlinear ||
|
||||
xev->xcrossing.detail == NotifyNonlinearVirtual))
|
||||
XIEvent *input_event = (XIEvent *) xev->xcookie.data;
|
||||
if ((input_event->evtype == XI_Enter || input_event->evtype == XI_Leave) &&
|
||||
ignore_crossing_event (plugin, (XIEnterEvent *) input_event))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
2
src/gvc
2
src/gvc
Submodule src/gvc updated: 40cdff2479...03894efbcd
@ -738,7 +738,8 @@ normalize_terms (GSList *terms)
|
||||
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));
|
||||
normalized_terms = g_slist_prepend (normalized_terms,
|
||||
shell_util_normalize_casefold_and_unaccent (term));
|
||||
}
|
||||
return normalized_terms;
|
||||
}
|
||||
|
@ -1319,16 +1319,16 @@ shell_app_init_search_data (ShellApp *app)
|
||||
|
||||
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);
|
||||
app->casefolded_name = shell_util_normalize_casefold_and_unaccent (name);
|
||||
|
||||
generic_name = g_desktop_app_info_get_generic_name (appinfo);
|
||||
if (generic_name)
|
||||
app->casefolded_generic_name = shell_util_normalize_and_casefold (generic_name);
|
||||
app->casefolded_generic_name = shell_util_normalize_casefold_and_unaccent (generic_name);
|
||||
else
|
||||
app->casefolded_generic_name = NULL;
|
||||
|
||||
exec = g_app_info_get_executable (G_APP_INFO (appinfo));
|
||||
normalized_exec = shell_util_normalize_and_casefold (exec);
|
||||
normalized_exec = shell_util_normalize_casefold_and_unaccent (exec);
|
||||
app->casefolded_exec = trim_exec_line (normalized_exec);
|
||||
g_free (normalized_exec);
|
||||
|
||||
@ -1343,7 +1343,7 @@ shell_app_init_search_data (ShellApp *app)
|
||||
i = 0;
|
||||
while (keywords[i])
|
||||
{
|
||||
app->casefolded_keywords[i] = shell_util_normalize_and_casefold (keywords[i]);
|
||||
app->casefolded_keywords[i] = shell_util_normalize_casefold_and_unaccent (keywords[i]);
|
||||
++i;
|
||||
}
|
||||
app->casefolded_keywords[i] = NULL;
|
||||
|
@ -616,6 +616,9 @@ shell_global_set_cursor (ShellGlobal *global,
|
||||
case SHELL_CURSOR_POINTING_HAND:
|
||||
name = "hand";
|
||||
break;
|
||||
case SHELL_CURSOR_CROSSHAIR:
|
||||
name = "crosshair";
|
||||
break;
|
||||
default:
|
||||
g_return_if_reached ();
|
||||
}
|
||||
@ -638,6 +641,9 @@ shell_global_set_cursor (ShellGlobal *global,
|
||||
case SHELL_CURSOR_POINTING_HAND:
|
||||
cursor_type = GDK_HAND2;
|
||||
break;
|
||||
case SHELL_CURSOR_CROSSHAIR:
|
||||
cursor_type = GDK_CROSSHAIR;
|
||||
break;
|
||||
case SHELL_CURSOR_DND_UNSUPPORTED_TARGET:
|
||||
cursor_type = GDK_X_CURSOR;
|
||||
break;
|
||||
@ -1404,7 +1410,6 @@ guint32
|
||||
shell_global_get_current_time (ShellGlobal *global)
|
||||
{
|
||||
guint32 time;
|
||||
const ClutterEvent *clutter_event;
|
||||
|
||||
/* In case we have a xdnd timestamp use it */
|
||||
if (global->xdnd_timestamp != 0)
|
||||
@ -1415,7 +1420,7 @@ shell_global_get_current_time (ShellGlobal *global)
|
||||
from some Clutter event callbacks.
|
||||
|
||||
clutter_get_current_event_time() will return the correct time
|
||||
from a Clutter event callback, but may return an out-of-date
|
||||
from a Clutter event callback, but may return CLUTTER_CURRENT_TIME
|
||||
timestamp if called at other times.
|
||||
|
||||
So we try meta_display_get_current_time() first, since we
|
||||
@ -1425,17 +1430,9 @@ shell_global_get_current_time (ShellGlobal *global)
|
||||
|
||||
time = meta_display_get_current_time (global->meta_display);
|
||||
if (time != CLUTTER_CURRENT_TIME)
|
||||
return time;
|
||||
/*
|
||||
* We don't use clutter_get_current_event_time as it can give us a
|
||||
* too old timestamp if there is no current event.
|
||||
*/
|
||||
clutter_event = clutter_get_current_event ();
|
||||
return time;
|
||||
|
||||
if (clutter_event != NULL)
|
||||
return clutter_event_get_time (clutter_event);
|
||||
else
|
||||
return CLUTTER_CURRENT_TIME;
|
||||
return clutter_get_current_event_time ();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -63,7 +63,8 @@ typedef enum {
|
||||
SHELL_CURSOR_DND_UNSUPPORTED_TARGET,
|
||||
SHELL_CURSOR_DND_MOVE,
|
||||
SHELL_CURSOR_DND_COPY,
|
||||
SHELL_CURSOR_POINTING_HAND
|
||||
SHELL_CURSOR_POINTING_HAND,
|
||||
SHELL_CURSOR_CROSSHAIR
|
||||
} ShellCursor;
|
||||
|
||||
void shell_global_set_cursor (ShellGlobal *global,
|
||||
|
@ -32,6 +32,7 @@ typedef struct _screenshot_data {
|
||||
ShellScreenshot *screenshot;
|
||||
|
||||
char *filename;
|
||||
char *filename_used;
|
||||
|
||||
cairo_surface_t *image;
|
||||
cairo_rectangle_int_t screenshot_area;
|
||||
@ -64,25 +65,140 @@ on_screenshot_written (GObject *source,
|
||||
if (screenshot_data->callback)
|
||||
screenshot_data->callback (screenshot_data->screenshot,
|
||||
g_simple_async_result_get_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (result)),
|
||||
&screenshot_data->screenshot_area);
|
||||
&screenshot_data->screenshot_area,
|
||||
screenshot_data->filename_used);
|
||||
|
||||
cairo_surface_destroy (screenshot_data->image);
|
||||
g_object_unref (screenshot_data->screenshot);
|
||||
g_free (screenshot_data->filename);
|
||||
g_free (screenshot_data->filename_used);
|
||||
g_free (screenshot_data);
|
||||
}
|
||||
|
||||
/* called in an I/O thread */
|
||||
static GOutputStream *
|
||||
get_stream_for_unique_path (const gchar *path,
|
||||
const gchar *filename,
|
||||
gchar **filename_used)
|
||||
{
|
||||
GOutputStream *stream;
|
||||
GFile *file;
|
||||
gchar *real_path, *real_filename, *name, *ptr;
|
||||
gint idx;
|
||||
|
||||
ptr = g_strrstr (filename, ".png");
|
||||
|
||||
if (ptr != NULL)
|
||||
real_filename = g_strndup (filename, ptr - filename);
|
||||
else
|
||||
real_filename = g_strdup (filename);
|
||||
|
||||
idx = 0;
|
||||
real_path = NULL;
|
||||
|
||||
do
|
||||
{
|
||||
if (idx == 0)
|
||||
name = g_strdup_printf ("%s.png", real_filename);
|
||||
else
|
||||
name = g_strdup_printf ("%s - %d.png", real_filename, idx);
|
||||
|
||||
real_path = g_build_filename (path, name, NULL);
|
||||
g_free (name);
|
||||
|
||||
file = g_file_new_for_path (real_path);
|
||||
stream = G_OUTPUT_STREAM (g_file_create (file, G_FILE_CREATE_NONE, NULL, NULL));
|
||||
g_object_unref (file);
|
||||
|
||||
if (stream != NULL)
|
||||
*filename_used = real_path;
|
||||
else
|
||||
g_free (real_path);
|
||||
|
||||
idx++;
|
||||
}
|
||||
while (stream == NULL);
|
||||
|
||||
g_free (real_filename);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
/* called in an I/O thread */
|
||||
static GOutputStream *
|
||||
get_stream_for_filename (const gchar *filename,
|
||||
gchar **filename_used)
|
||||
{
|
||||
const gchar *path;
|
||||
|
||||
path = g_get_user_special_dir (G_USER_DIRECTORY_PICTURES);
|
||||
if (!g_file_test (path, G_FILE_TEST_EXISTS))
|
||||
{
|
||||
path = g_get_home_dir ();
|
||||
if (!g_file_test (path, G_FILE_TEST_EXISTS))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return get_stream_for_unique_path (path, filename, filename_used);
|
||||
}
|
||||
|
||||
static GOutputStream *
|
||||
prepare_write_stream (const gchar *filename,
|
||||
gchar **filename_used)
|
||||
{
|
||||
GOutputStream *stream;
|
||||
GFile *file;
|
||||
|
||||
if (g_path_is_absolute (filename))
|
||||
{
|
||||
file = g_file_new_for_path (filename);
|
||||
*filename_used = g_strdup (filename);
|
||||
stream = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL));
|
||||
g_object_unref (file);
|
||||
}
|
||||
else
|
||||
{
|
||||
stream = get_stream_for_filename (filename, filename_used);
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
static cairo_status_t
|
||||
do_write_to_stream (void *closure,
|
||||
const guchar *data,
|
||||
guint length)
|
||||
{
|
||||
GOutputStream *stream = closure;
|
||||
gboolean res;
|
||||
|
||||
res = g_output_stream_write_all (stream, data, length, NULL, NULL, NULL);
|
||||
|
||||
return res ? CAIRO_STATUS_SUCCESS : CAIRO_STATUS_WRITE_ERROR;
|
||||
}
|
||||
|
||||
static void
|
||||
write_screenshot_thread (GSimpleAsyncResult *result,
|
||||
GObject *object,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
cairo_status_t status;
|
||||
GOutputStream *stream;
|
||||
_screenshot_data *screenshot_data = g_async_result_get_user_data (G_ASYNC_RESULT (result));
|
||||
|
||||
g_assert (screenshot_data != NULL);
|
||||
|
||||
status = cairo_surface_write_to_png (screenshot_data->image, screenshot_data->filename);
|
||||
stream = prepare_write_stream (screenshot_data->filename,
|
||||
&screenshot_data->filename_used);
|
||||
|
||||
if (stream == NULL)
|
||||
status = CAIRO_STATUS_FILE_NOT_FOUND;
|
||||
else
|
||||
status = cairo_surface_write_to_png_stream (screenshot_data->image, do_write_to_stream, stream);
|
||||
|
||||
g_simple_async_result_set_op_res_gboolean (result, status == CAIRO_STATUS_SUCCESS);
|
||||
|
||||
g_clear_object (&stream);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -26,8 +26,9 @@ GType shell_screenshot_get_type (void) G_GNUC_CONST;
|
||||
ShellScreenshot *shell_screenshot_new (void);
|
||||
|
||||
typedef void (*ShellScreenshotCallback) (ShellScreenshot *screenshot,
|
||||
gboolean success,
|
||||
cairo_rectangle_int_t *screenshot_area);
|
||||
gboolean success,
|
||||
cairo_rectangle_int_t *screenshot_area,
|
||||
const gchar *filename_used);
|
||||
|
||||
void shell_screenshot_screenshot_area (ShellScreenshot *screenshot,
|
||||
int x,
|
||||
|
@ -122,12 +122,90 @@ shell_util_normalize_and_casefold (const char *str)
|
||||
if (str == NULL)
|
||||
return NULL;
|
||||
|
||||
/* NOTE: 'ALL' is equivalent to 'NFKD'. If this is ever updated, please
|
||||
* update the unaccenting mechanism as well. */
|
||||
normalized = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
|
||||
result = g_utf8_casefold (normalized, -1);
|
||||
g_free (normalized);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Combining diacritical mark?
|
||||
* Basic range: [0x0300,0x036F]
|
||||
* Supplement: [0x1DC0,0x1DFF]
|
||||
* For Symbols: [0x20D0,0x20FF]
|
||||
* Half marks: [0xFE20,0xFE2F]
|
||||
*/
|
||||
#define IS_CDM_UCS4(c) (((c) >= 0x0300 && (c) <= 0x036F) || \
|
||||
((c) >= 0x1DC0 && (c) <= 0x1DFF) || \
|
||||
((c) >= 0x20D0 && (c) <= 0x20FF) || \
|
||||
((c) >= 0xFE20 && (c) <= 0xFE2F))
|
||||
|
||||
/* Copied from tracker/src/libtracker-fts/tracker-parser-glib.c under the GPL
|
||||
* Originally written by Aleksander Morgado <aleksander@gnu.org>
|
||||
*/
|
||||
char *
|
||||
shell_util_normalize_casefold_and_unaccent (const char *str)
|
||||
{
|
||||
char *tmp;
|
||||
gsize i = 0, j = 0, ilen;
|
||||
|
||||
if (str == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Get the NFKD-normalized and casefolded string */
|
||||
tmp = shell_util_normalize_and_casefold (str);
|
||||
ilen = strlen (tmp);
|
||||
|
||||
while (i < ilen)
|
||||
{
|
||||
gunichar unichar;
|
||||
gchar *next_utf8;
|
||||
gint utf8_len;
|
||||
|
||||
/* Get next character of the word as UCS4 */
|
||||
unichar = g_utf8_get_char_validated (&tmp[i], -1);
|
||||
|
||||
/* Invalid UTF-8 character or end of original string. */
|
||||
if (unichar == (gunichar) -1 ||
|
||||
unichar == (gunichar) -2)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* Find next UTF-8 character */
|
||||
next_utf8 = g_utf8_next_char (&tmp[i]);
|
||||
utf8_len = next_utf8 - &tmp[i];
|
||||
|
||||
if (IS_CDM_UCS4 ((guint32) unichar))
|
||||
{
|
||||
/* If the given unichar is a combining diacritical mark,
|
||||
* just update the original index, not the output one */
|
||||
i += utf8_len;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If already found a previous combining
|
||||
* diacritical mark, indexes are different so
|
||||
* need to copy characters. As output and input
|
||||
* buffers may overlap, need to use memmove
|
||||
* instead of memcpy */
|
||||
if (i != j)
|
||||
{
|
||||
memmove (&tmp[j], &tmp[i], utf8_len);
|
||||
}
|
||||
|
||||
/* Update both indexes */
|
||||
i += utf8_len;
|
||||
j += utf8_len;
|
||||
}
|
||||
|
||||
/* Force proper string end */
|
||||
tmp[j] = '\0';
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_util_format_date:
|
||||
* @format: a strftime-style string format, as parsed by
|
||||
|
@ -20,6 +20,8 @@ int shell_util_get_week_start (void);
|
||||
|
||||
char *shell_util_normalize_and_casefold (const char *str);
|
||||
|
||||
char *shell_util_normalize_casefold_and_unaccent (const char *str);
|
||||
|
||||
char *shell_util_format_date (const char *format,
|
||||
gint64 time_ms);
|
||||
|
||||
|
@ -32,6 +32,7 @@
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <clutter/clutter.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "st-adjustment.h"
|
||||
#include "st-private.h"
|
||||
@ -566,3 +567,31 @@ st_adjustment_get_values (StAdjustment *adjustment,
|
||||
*page_size = priv->page_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_adjustment_adjust_for_scroll_event:
|
||||
* @adjustment: An #StAdjustment
|
||||
* @delta: A delta, retrieved directly from clutter_event_get_scroll_delta()
|
||||
* or similar.
|
||||
*
|
||||
* Adjusts the adjustment using delta values from a scroll event.
|
||||
* You should use this instead of using st_adjustment_set_value()
|
||||
* as this method will tweak the values directly using the same
|
||||
* math as GTK+, to ensure that scrolling is consistent across
|
||||
* the environment.
|
||||
*/
|
||||
void
|
||||
st_adjustment_adjust_for_scroll_event (StAdjustment *adjustment,
|
||||
gdouble delta)
|
||||
{
|
||||
StAdjustmentPrivate *priv;
|
||||
gdouble new_value, scroll_unit;
|
||||
|
||||
g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
|
||||
|
||||
priv = adjustment->priv;
|
||||
|
||||
scroll_unit = pow (priv->page_size, 2.0 / 3.0);
|
||||
|
||||
new_value = priv->value + delta * scroll_unit;
|
||||
st_adjustment_set_value (adjustment, new_value);
|
||||
}
|
||||
|
@ -99,6 +99,10 @@ void st_adjustment_get_values (StAdjustment *adjustment,
|
||||
gdouble *step_increment,
|
||||
gdouble *page_increment,
|
||||
gdouble *page_size);
|
||||
|
||||
void st_adjustment_adjust_for_scroll_event (StAdjustment *adjustment,
|
||||
gdouble delta);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __ST_ADJUSTMENT_H__ */
|
||||
|
@ -152,7 +152,7 @@ st_drawing_area_paint (ClutterActor *self)
|
||||
|
||||
cogl_set_source (priv->material);
|
||||
cogl_rectangle_with_texture_coords (content_box.x1, content_box.y1,
|
||||
width, height,
|
||||
content_box.x2, content_box.y2,
|
||||
0.0f, 0.0f, 1.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
@ -433,42 +433,57 @@ st_scroll_bar_constructor (GType type,
|
||||
return obj;
|
||||
}
|
||||
|
||||
static void
|
||||
adjust_with_direction (StAdjustment *adj,
|
||||
ClutterScrollDirection direction)
|
||||
{
|
||||
gdouble delta;
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
case CLUTTER_SCROLL_UP:
|
||||
case CLUTTER_SCROLL_LEFT:
|
||||
delta = -1.0;
|
||||
break;
|
||||
case CLUTTER_SCROLL_RIGHT:
|
||||
case CLUTTER_SCROLL_DOWN:
|
||||
delta = 1.0;
|
||||
break;
|
||||
case CLUTTER_SCROLL_SMOOTH:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
|
||||
st_adjustment_adjust_for_scroll_event (adj, delta);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
st_scroll_bar_scroll_event (ClutterActor *actor,
|
||||
ClutterScrollEvent *event)
|
||||
{
|
||||
StScrollBarPrivate *priv = ST_SCROLL_BAR (actor)->priv;
|
||||
gdouble step, value, delta_x, delta_y;
|
||||
|
||||
if (priv->adjustment)
|
||||
{
|
||||
g_object_get (priv->adjustment,
|
||||
"step-increment", &step,
|
||||
"value", &value,
|
||||
NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
if (clutter_event_is_pointer_emulated ((ClutterEvent *) event))
|
||||
return TRUE;
|
||||
|
||||
switch (event->direction)
|
||||
{
|
||||
case CLUTTER_SCROLL_SMOOTH:
|
||||
clutter_event_get_scroll_delta ((ClutterEvent *)event,
|
||||
&delta_x, &delta_y);
|
||||
if (fabs (delta_x) > fabs (delta_y))
|
||||
st_adjustment_set_value (priv->adjustment, value + delta_x);
|
||||
else
|
||||
st_adjustment_set_value (priv->adjustment, value + delta_y);
|
||||
{
|
||||
gdouble delta_x, delta_y;
|
||||
clutter_event_get_scroll_delta ((ClutterEvent *)event, &delta_x, &delta_y);
|
||||
|
||||
if (priv->vertical)
|
||||
st_adjustment_adjust_for_scroll_event (priv->adjustment, delta_y);
|
||||
else
|
||||
st_adjustment_adjust_for_scroll_event (priv->adjustment, delta_x);
|
||||
}
|
||||
break;
|
||||
case CLUTTER_SCROLL_UP:
|
||||
case CLUTTER_SCROLL_LEFT:
|
||||
st_adjustment_set_value (priv->adjustment, value - step);
|
||||
break;
|
||||
case CLUTTER_SCROLL_DOWN:
|
||||
case CLUTTER_SCROLL_LEFT:
|
||||
case CLUTTER_SCROLL_RIGHT:
|
||||
st_adjustment_set_value (priv->adjustment, value + step);
|
||||
adjust_with_direction (priv->adjustment, event->direction);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -665,6 +665,31 @@ st_scroll_view_allocate (ClutterActor *actor,
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
adjust_with_direction (StAdjustment *adj,
|
||||
ClutterScrollDirection direction)
|
||||
{
|
||||
gdouble delta;
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
case CLUTTER_SCROLL_UP:
|
||||
case CLUTTER_SCROLL_LEFT:
|
||||
delta = -1.0;
|
||||
break;
|
||||
case CLUTTER_SCROLL_RIGHT:
|
||||
case CLUTTER_SCROLL_DOWN:
|
||||
delta = 1.0;
|
||||
break;
|
||||
case CLUTTER_SCROLL_SMOOTH:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
|
||||
st_adjustment_adjust_for_scroll_event (adj, delta);
|
||||
}
|
||||
|
||||
static void
|
||||
st_scroll_view_style_changed (StWidget *widget)
|
||||
{
|
||||
@ -687,57 +712,31 @@ st_scroll_view_scroll_event (ClutterActor *self,
|
||||
ClutterScrollEvent *event)
|
||||
{
|
||||
StScrollViewPrivate *priv = ST_SCROLL_VIEW (self)->priv;
|
||||
gdouble value, step, hvalue, vvalue, delta_x, delta_y;
|
||||
|
||||
/* don't handle scroll events if requested not to */
|
||||
if (!priv->mouse_scroll)
|
||||
return FALSE;
|
||||
|
||||
switch (event->direction)
|
||||
{
|
||||
case CLUTTER_SCROLL_SMOOTH:
|
||||
clutter_event_get_scroll_delta ((ClutterEvent *)event,
|
||||
&delta_x, &delta_y);
|
||||
g_object_get (priv->hadjustment,
|
||||
"value", &hvalue,
|
||||
NULL);
|
||||
g_object_get (priv->vadjustment,
|
||||
"value", &vvalue,
|
||||
NULL);
|
||||
break;
|
||||
case CLUTTER_SCROLL_UP:
|
||||
case CLUTTER_SCROLL_DOWN:
|
||||
g_object_get (priv->vadjustment,
|
||||
"step-increment", &step,
|
||||
"value", &value,
|
||||
NULL);
|
||||
break;
|
||||
case CLUTTER_SCROLL_LEFT:
|
||||
case CLUTTER_SCROLL_RIGHT:
|
||||
g_object_get (priv->hadjustment,
|
||||
"step-increment", &step,
|
||||
"value", &value,
|
||||
NULL);
|
||||
break;
|
||||
}
|
||||
if (clutter_event_is_pointer_emulated ((ClutterEvent *) event))
|
||||
return TRUE;
|
||||
|
||||
switch (event->direction)
|
||||
{
|
||||
case CLUTTER_SCROLL_SMOOTH:
|
||||
st_adjustment_set_value (priv->hadjustment, hvalue + delta_x);
|
||||
st_adjustment_set_value (priv->vadjustment, vvalue + delta_y);
|
||||
{
|
||||
gdouble delta_x, delta_y;
|
||||
clutter_event_get_scroll_delta ((ClutterEvent *)event, &delta_x, &delta_y);
|
||||
st_adjustment_adjust_for_scroll_event (priv->hadjustment, delta_x);
|
||||
st_adjustment_adjust_for_scroll_event (priv->vadjustment, delta_y);
|
||||
}
|
||||
break;
|
||||
case CLUTTER_SCROLL_UP:
|
||||
st_adjustment_set_value (priv->vadjustment, value - step);
|
||||
break;
|
||||
case CLUTTER_SCROLL_DOWN:
|
||||
st_adjustment_set_value (priv->vadjustment, value + step);
|
||||
adjust_with_direction (priv->vadjustment, event->direction);
|
||||
break;
|
||||
case CLUTTER_SCROLL_LEFT:
|
||||
st_adjustment_set_value (priv->hadjustment, value - step);
|
||||
break;
|
||||
case CLUTTER_SCROLL_RIGHT:
|
||||
st_adjustment_set_value (priv->hadjustment, value + step);
|
||||
adjust_with_direction (priv->hadjustment, event->direction);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -30,8 +30,6 @@
|
||||
#define CACHE_PREFIX_ICON "icon:"
|
||||
#define CACHE_PREFIX_URI "uri:"
|
||||
#define CACHE_PREFIX_URI_FOR_CAIRO "uri-for-cairo:"
|
||||
#define CACHE_PREFIX_RAW_CHECKSUM "raw-checksum:"
|
||||
#define CACHE_PREFIX_COMPRESSED_CHECKSUM "compressed-checksum:"
|
||||
|
||||
struct _StTextureCachePrivate
|
||||
{
|
||||
@ -1414,63 +1412,6 @@ st_texture_cache_load_file_to_cairo_surface (StTextureCache *cache,
|
||||
return surface;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_texture_cache_load_from_raw:
|
||||
* @cache: a #StTextureCache
|
||||
* @data: (array length=len): raw pixel data
|
||||
* @len: the length of @data
|
||||
* @has_alpha: whether @data includes an alpha channel
|
||||
* @width: width in pixels of @data
|
||||
* @height: width in pixels of @data
|
||||
* @rowstride: rowstride of @data
|
||||
* @size: size of icon to return
|
||||
*
|
||||
* Creates (or retrieves from cache) an icon based on raw pixel data.
|
||||
*
|
||||
* Return value: (transfer none): a new #ClutterActor displaying a
|
||||
* pixbuf created from @data and the other parameters.
|
||||
**/
|
||||
ClutterActor *
|
||||
st_texture_cache_load_from_raw (StTextureCache *cache,
|
||||
const guchar *data,
|
||||
gsize len,
|
||||
gboolean has_alpha,
|
||||
int width,
|
||||
int height,
|
||||
int rowstride,
|
||||
int size,
|
||||
GError **error)
|
||||
{
|
||||
ClutterTexture *texture;
|
||||
CoglHandle texdata;
|
||||
char *key;
|
||||
char *checksum;
|
||||
|
||||
texture = create_default_texture ();
|
||||
clutter_actor_set_size (CLUTTER_ACTOR (texture), size, size);
|
||||
|
||||
/* In theory, two images of with different width and height could have the same
|
||||
* pixel data and thus hash the same. (Say, a 16x16 and a 8x32 blank image.)
|
||||
* We ignore this for now. If anybody hits this problem they should use
|
||||
* GChecksum directly to compute a checksum including the width and height.
|
||||
*/
|
||||
checksum = g_compute_checksum_for_data (G_CHECKSUM_SHA1, data, len);
|
||||
key = g_strdup_printf (CACHE_PREFIX_RAW_CHECKSUM "checksum=%s", checksum);
|
||||
g_free (checksum);
|
||||
|
||||
texdata = g_hash_table_lookup (cache->priv->keyed_cache, key);
|
||||
if (texdata == NULL)
|
||||
{
|
||||
texdata = data_to_cogl_handle (data, has_alpha, width, height, rowstride, TRUE);
|
||||
g_hash_table_insert (cache->priv->keyed_cache, g_strdup (key), texdata);
|
||||
}
|
||||
|
||||
g_free (key);
|
||||
|
||||
set_texture_cogl_texture (texture, texdata);
|
||||
return CLUTTER_ACTOR (texture);
|
||||
}
|
||||
|
||||
static StTextureCache *instance = NULL;
|
||||
|
||||
/**
|
||||
|
@ -96,16 +96,6 @@ CoglHandle st_texture_cache_load_file_to_cogl_texture (StTextureCache *cache,
|
||||
cairo_surface_t *st_texture_cache_load_file_to_cairo_surface (StTextureCache *cache,
|
||||
const gchar *file_path);
|
||||
|
||||
ClutterActor *st_texture_cache_load_from_raw (StTextureCache *cache,
|
||||
const guchar *data,
|
||||
gsize len,
|
||||
gboolean has_alpha,
|
||||
int width,
|
||||
int height,
|
||||
int rowstride,
|
||||
int size,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* StTextureCacheLoader: (skip)
|
||||
* @cache: a #StTextureCache
|
||||
|
@ -770,6 +770,72 @@ st_theme_node_lookup_double (StThemeNode *node,
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_theme_node_lookup_time:
|
||||
* @node: a #StThemeNode
|
||||
* @property_name: The name of the time property
|
||||
* @inherit: if %TRUE, if a value is not found for the property on the
|
||||
* node, then it will be looked up on the parent node, and then on the
|
||||
* parent's parent, and so forth. Note that if the property has a
|
||||
* value of 'inherit' it will be inherited even if %FALSE is passed
|
||||
* in for @inherit; this only affects the default behavior for inheritance.
|
||||
* @value: (out): location to store the value that was determined.
|
||||
* If the property is not found, the value in this location
|
||||
* will not be changed.
|
||||
*
|
||||
* Generically looks up a property containing a single time value,
|
||||
* which is converted to milliseconds.
|
||||
*
|
||||
* Return value: %TRUE if the property was found in the properties for this
|
||||
* theme node (or in the properties of parent nodes when inheriting.)
|
||||
*/
|
||||
gboolean
|
||||
st_theme_node_lookup_time (StThemeNode *node,
|
||||
const char *property_name,
|
||||
gboolean inherit,
|
||||
double *value)
|
||||
{
|
||||
gboolean result = FALSE;
|
||||
int i;
|
||||
|
||||
ensure_properties (node);
|
||||
|
||||
for (i = node->n_properties - 1; i >= 0; i--)
|
||||
{
|
||||
CRDeclaration *decl = node->properties[i];
|
||||
|
||||
if (strcmp (decl->property->stryng->str, property_name) == 0)
|
||||
{
|
||||
CRTerm *term = decl->value;
|
||||
|
||||
if (term->type != TERM_NUMBER)
|
||||
continue;
|
||||
|
||||
switch (term->content.num->type)
|
||||
{
|
||||
case NUM_TIME_S:
|
||||
*value = 1000 * term->content.num->val;
|
||||
result = TRUE;
|
||||
break;
|
||||
case NUM_TIME_MS:
|
||||
*value = term->content.num->val;
|
||||
result = TRUE;
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
|
||||
if (result)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!result && inherit && node->parent_node)
|
||||
result = st_theme_node_lookup_time (node->parent_node, property_name, inherit, value);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_theme_node_get_double:
|
||||
* @node: a #StThemeNode
|
||||
@ -1738,15 +1804,19 @@ _st_theme_node_ensure_background (StThemeNode *node)
|
||||
else if (term->type == TERM_URI)
|
||||
{
|
||||
CRStyleSheet *base_stylesheet;
|
||||
GFile *file;
|
||||
|
||||
if (decl->parent_statement != NULL)
|
||||
base_stylesheet = decl->parent_statement->parent_sheet;
|
||||
else
|
||||
base_stylesheet = NULL;
|
||||
|
||||
node->background_image = _st_theme_resolve_url (node->theme,
|
||||
base_stylesheet,
|
||||
term->content.str->stryng->str);
|
||||
file = _st_theme_resolve_url (node->theme,
|
||||
base_stylesheet,
|
||||
term->content.str->stryng->str);
|
||||
|
||||
node->background_image = g_file_get_path (file);
|
||||
g_object_unref (file);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1843,6 +1913,7 @@ _st_theme_node_ensure_background (StThemeNode *node)
|
||||
if (decl->value->type == TERM_URI)
|
||||
{
|
||||
CRStyleSheet *base_stylesheet;
|
||||
GFile *file;
|
||||
|
||||
if (decl->parent_statement != NULL)
|
||||
base_stylesheet = decl->parent_statement->parent_sheet;
|
||||
@ -1850,9 +1921,12 @@ _st_theme_node_ensure_background (StThemeNode *node)
|
||||
base_stylesheet = NULL;
|
||||
|
||||
g_free (node->background_image);
|
||||
node->background_image = _st_theme_resolve_url (node->theme,
|
||||
base_stylesheet,
|
||||
decl->value->content.str->stryng->str);
|
||||
file = _st_theme_resolve_url (node->theme,
|
||||
base_stylesheet,
|
||||
decl->value->content.str->stryng->str);
|
||||
|
||||
node->background_image = g_file_get_path (file);
|
||||
g_object_unref (file);
|
||||
}
|
||||
else if (term_is_inherit (decl->value))
|
||||
{
|
||||
@ -2055,7 +2129,7 @@ st_theme_node_get_transition_duration (StThemeNode *node)
|
||||
if (node->transition_duration > -1)
|
||||
return st_slow_down_factor * node->transition_duration;
|
||||
|
||||
st_theme_node_lookup_double (node, "transition-duration", FALSE, &value);
|
||||
st_theme_node_lookup_time (node, "transition-duration", FALSE, &value);
|
||||
|
||||
node->transition_duration = (int)value;
|
||||
|
||||
@ -2668,6 +2742,7 @@ st_theme_node_get_border_image (StThemeNode *node)
|
||||
int border_bottom;
|
||||
int border_left;
|
||||
|
||||
GFile *file;
|
||||
char *filename;
|
||||
|
||||
/* Support border-image: none; to suppress a previously specified border image */
|
||||
@ -2746,7 +2821,10 @@ st_theme_node_get_border_image (StThemeNode *node)
|
||||
else
|
||||
base_stylesheet = NULL;
|
||||
|
||||
filename = _st_theme_resolve_url (node->theme, base_stylesheet, url);
|
||||
file = _st_theme_resolve_url (node->theme, base_stylesheet, url);
|
||||
filename = g_file_get_path (file);
|
||||
g_object_unref (file);
|
||||
|
||||
if (filename == NULL)
|
||||
goto next_property;
|
||||
|
||||
|
@ -134,6 +134,10 @@ gboolean st_theme_node_lookup_length (StThemeNode *node,
|
||||
const char *property_name,
|
||||
gboolean inherit,
|
||||
gdouble *length);
|
||||
gboolean st_theme_node_lookup_time (StThemeNode *node,
|
||||
const char *property_name,
|
||||
gboolean inherit,
|
||||
gdouble *value);
|
||||
gboolean st_theme_node_lookup_shadow (StThemeNode *node,
|
||||
const char *property_name,
|
||||
gboolean inherit,
|
||||
|
@ -29,10 +29,10 @@ G_BEGIN_DECLS
|
||||
GPtrArray *_st_theme_get_matched_properties (StTheme *theme,
|
||||
StThemeNode *node);
|
||||
|
||||
/* Resolve an URL from the stylesheet to a filename */
|
||||
char *_st_theme_resolve_url (StTheme *theme,
|
||||
CRStyleSheet *base_stylesheet,
|
||||
const char *url);
|
||||
/* Resolve an URL from the stylesheet to a file */
|
||||
GFile *_st_theme_resolve_url (StTheme *theme,
|
||||
CRStyleSheet *base_stylesheet,
|
||||
const char *url);
|
||||
|
||||
CRDeclaration *_st_theme_parse_declaration_list (const char *str);
|
||||
|
||||
|
@ -849,9 +849,16 @@ add_matched_properties (StTheme *a_this,
|
||||
char *filename = NULL;
|
||||
|
||||
if (import_rule->url->stryng && import_rule->url->stryng->str)
|
||||
filename = _st_theme_resolve_url (a_this,
|
||||
a_nodesheet,
|
||||
import_rule->url->stryng->str);
|
||||
{
|
||||
GFile *file;
|
||||
|
||||
file = _st_theme_resolve_url (a_this,
|
||||
a_nodesheet,
|
||||
import_rule->url->stryng->str);
|
||||
filename = g_file_get_path (file);
|
||||
|
||||
g_object_unref (file);
|
||||
}
|
||||
|
||||
if (filename)
|
||||
import_rule->sheet = parse_stylesheet (filename, NULL);
|
||||
@ -999,84 +1006,41 @@ _st_theme_get_matched_properties (StTheme *theme,
|
||||
* local filename, if possible. The resolution here is distinctly lame and
|
||||
* will fail on many examples.
|
||||
*/
|
||||
char *
|
||||
GFile *
|
||||
_st_theme_resolve_url (StTheme *theme,
|
||||
CRStyleSheet *base_stylesheet,
|
||||
const char *url)
|
||||
{
|
||||
const char *base_filename = NULL;
|
||||
char *dirname;
|
||||
char *filename;
|
||||
char *canonicalized_path;
|
||||
char *scheme;
|
||||
GFile *stylesheet, *resource;
|
||||
|
||||
/* Handle absolute file:/ URLs */
|
||||
if (g_str_has_prefix (url, "file:") ||
|
||||
g_str_has_prefix (url, "File:") ||
|
||||
g_str_has_prefix (url, "FILE:"))
|
||||
if ((scheme = g_uri_parse_scheme (url)))
|
||||
{
|
||||
GError *error = NULL;
|
||||
char *filename;
|
||||
|
||||
filename = g_filename_from_uri (url, NULL, &error);
|
||||
if (filename == NULL)
|
||||
{
|
||||
g_warning ("%s", error->message);
|
||||
g_error_free (error);
|
||||
}
|
||||
|
||||
return filename;
|
||||
g_free (scheme);
|
||||
resource = g_file_new_for_uri (url);
|
||||
}
|
||||
|
||||
/* Guard against http:/ URLs */
|
||||
|
||||
if (g_str_has_prefix (url, "http:") ||
|
||||
g_str_has_prefix (url, "Http:") ||
|
||||
g_str_has_prefix (url, "HTTP:"))
|
||||
else if (base_stylesheet != NULL)
|
||||
{
|
||||
g_warning ("Http URL '%s' in theme stylesheet is not supported", url);
|
||||
return NULL;
|
||||
}
|
||||
const char *base_filename = NULL;
|
||||
char *dirname;
|
||||
|
||||
/* Assume anything else is a relative URL, and "resolve" it
|
||||
*/
|
||||
if (url[0] == '/')
|
||||
{
|
||||
canonicalized_path = realpath (url, NULL);
|
||||
if (g_mem_is_system_malloc ())
|
||||
{
|
||||
filename = canonicalized_path;
|
||||
}
|
||||
else
|
||||
{
|
||||
filename = g_strdup (canonicalized_path);
|
||||
free (canonicalized_path);
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
base_filename = g_hash_table_lookup (theme->filenames_by_stylesheet, base_stylesheet);
|
||||
|
||||
base_filename = g_hash_table_lookup (theme->filenames_by_stylesheet, base_stylesheet);
|
||||
/* This is an internal function, if we get here with
|
||||
a bad @base_stylesheet we have a problem. */
|
||||
g_assert (base_filename);
|
||||
|
||||
if (base_filename == NULL)
|
||||
{
|
||||
g_warning ("Can't get base to resolve url '%s'", url);
|
||||
return NULL;
|
||||
}
|
||||
dirname = g_path_get_dirname (base_filename);
|
||||
stylesheet = g_file_new_for_path (dirname);
|
||||
resource = g_file_resolve_relative_path (stylesheet, url);
|
||||
|
||||
dirname = g_path_get_dirname (base_filename);
|
||||
filename = g_build_filename (dirname, url, NULL);
|
||||
canonicalized_path = realpath (filename, NULL);
|
||||
g_free (dirname);
|
||||
g_free (filename);
|
||||
|
||||
if (g_mem_is_system_malloc ())
|
||||
{
|
||||
filename = canonicalized_path;
|
||||
g_object_unref (stylesheet);
|
||||
g_free (dirname);
|
||||
}
|
||||
else
|
||||
{
|
||||
filename = g_strdup (canonicalized_path);
|
||||
free (canonicalized_path);
|
||||
resource = g_file_new_for_path (url);
|
||||
}
|
||||
|
||||
return filename;
|
||||
return resource;
|
||||
}
|
||||
|
@ -30,13 +30,15 @@ builddir=`cd $builddir && pwd`
|
||||
srcdir=$builddir/@srcdir@
|
||||
srcdir=`cd $srcdir && pwd`
|
||||
|
||||
GI_TYPELIB_PATH="$GI_TYPELIB_PATH${GI_TYPELIB_PATH:+:}@MUTTER_TYPELIB_DIR@:$builddir/../src"
|
||||
GI_TYPELIB_PATH="$GI_TYPELIB_PATH${GI_TYPELIB_PATH:+:}@MUTTER_TYPELIB_DIR@:$builddir/../src:$builddir/../src/gvc"
|
||||
GJS_PATH="$srcdir:$srcdir/../js"
|
||||
GJS_DEBUG_OUTPUT=stderr
|
||||
$verbose || GJS_DEBUG_TOPICS="JS ERROR;JS LOG"
|
||||
GNOME_SHELL_TESTSDIR="$srcdir/"
|
||||
GNOME_SHELL_JS="$srcdir/../js"
|
||||
GNOME_SHELL_DATADIR="$srcdir/../data"
|
||||
|
||||
export GI_TYPELIB_PATH GJS_PATH GJS_DEBUG_OUTPUT GJS_DEBUG_TOPICS GNOME_SHELL_JS GNOME_SHELL_TESTSDIR LD_PRELOAD
|
||||
export GI_TYPELIB_PATH GJS_PATH GJS_DEBUG_OUTPUT GJS_DEBUG_TOPICS GNOME_SHELL_TESTSDIR GNOME_SHELL_JS GNOME_SHELL_DATADIR LD_PRELOAD
|
||||
|
||||
for test in $tests ; do
|
||||
$debug $builddir/../src/run-js-test $test || exit $?
|
||||
|
@ -98,7 +98,7 @@ stage {
|
||||
background-color: #333;
|
||||
border: 2px solid black;
|
||||
border-radius: 8px;
|
||||
transition-duration: 1000; /* One second */
|
||||
transition-duration: 1s;
|
||||
}
|
||||
|
||||
#transition-container:hover .transition-label {
|
||||
|
Reference in New Issue
Block a user