Compare commits
66 Commits
wip/re-sea
...
3.7.3.1-br
Author | SHA1 | Date | |
---|---|---|---|
7d598b7725 | |||
9f890982b2 | |||
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 \
|
||||
|
61
NEWS
61
NEWS
@ -1,3 +1,64 @@
|
||||
3.7.3.1
|
||||
=======
|
||||
* Revert 490206b to not depend on NMGTK-0.9.7, which hasn't been released yet
|
||||
|
||||
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]
|
||||
|
15
configure.ac
15
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.3.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_SRCDIR([src/shell-global.c])
|
||||
@ -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.3
|
||||
GTK_MIN_VERSION=3.3.9
|
||||
GIO_MIN_VERSION=2.35.0
|
||||
LIBECAL_MIN_VERSION=3.5.3
|
||||
@ -78,7 +78,7 @@ STARTUP_NOTIFICATION_MIN_VERSION=0.11
|
||||
GCR_MIN_VERSION=3.3.90
|
||||
GNOME_DESKTOP_REQUIRED_VERSION=3.7.1
|
||||
GNOME_MENUS_REQUIRED_VERSION=3.5.3
|
||||
NETWORKMANAGER_MIN_VERSION=0.9.7
|
||||
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
|
||||
@ -99,9 +99,8 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
|
||||
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
|
||||
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
|
||||
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
|
||||
libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION
|
||||
libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
|
||||
gnome-keyring-1 gcr-3 >= $GCR_MIN_VERSION)
|
||||
libnm-glib libnm-util gnome-keyring-1
|
||||
gcr-3 >= $GCR_MIN_VERSION)
|
||||
|
||||
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
|
||||
|
||||
@ -125,8 +124,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],
|
||||
|
@ -34,6 +34,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 \
|
||||
|
@ -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;
|
||||
}
|
||||
@ -558,6 +558,10 @@ StScrollBar StButton#vhandle:active {
|
||||
spacing: 4px;
|
||||
}
|
||||
|
||||
.lock-screen-status-button-box {
|
||||
spacing: 8px;
|
||||
}
|
||||
|
||||
/* User Menu */
|
||||
|
||||
#panelUserMenu {
|
||||
@ -613,7 +617,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 +714,8 @@ StScrollBar StButton#vhandle:active {
|
||||
}
|
||||
|
||||
.window-picker {
|
||||
-horizontal-spacing: 40px;
|
||||
-vertical-spacing: 40px;
|
||||
-horizontal-spacing: 32px;
|
||||
-vertical-spacing: 32px;
|
||||
}
|
||||
|
||||
/* Dash */
|
||||
@ -760,7 +772,7 @@ StScrollBar StButton#vhandle:active {
|
||||
|
||||
#searchResultsContent {
|
||||
padding-right: 20px;
|
||||
spacing: 36px;
|
||||
spacing: 16px;
|
||||
}
|
||||
|
||||
#searchResultsContent:rtl {
|
||||
@ -768,11 +780,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 +804,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 +834,7 @@ StScrollBar StButton#vhandle:active {
|
||||
-x-offset: 8px;
|
||||
}
|
||||
|
||||
/* Application Launchers and Grid */
|
||||
/* Application Launchers, Grid and List results */
|
||||
|
||||
.icon-grid {
|
||||
spacing: 36px;
|
||||
@ -871,9 +886,34 @@ 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);
|
||||
@ -889,7 +929,9 @@ 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;
|
||||
@ -924,10 +966,14 @@ StScrollBar StButton#vhandle:active {
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
|
||||
@ -1271,6 +1317,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,7 +1329,7 @@ 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-columns: 10px;
|
||||
@ -1311,7 +1361,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;
|
||||
@ -1499,10 +1549,6 @@ StScrollBar StButton#vhandle:active {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
#summary-mode {
|
||||
height: 72px;
|
||||
}
|
||||
|
||||
.summary-source-button {
|
||||
padding: 6px 3px 6px 3px;
|
||||
}
|
||||
@ -2389,3 +2435,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 |
@ -62,6 +62,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 \
|
||||
|
@ -159,6 +159,86 @@ function killall(processName) {
|
||||
}
|
||||
}
|
||||
|
||||
// This was ported from network-manager-applet
|
||||
// Copyright 2007 - 2011 Red Hat, Inc.
|
||||
// Author: Dan Williams <dcbw@redhat.com>
|
||||
|
||||
const _IGNORED_WORDS = [
|
||||
'Semiconductor',
|
||||
'Components',
|
||||
'Corporation',
|
||||
'Communications',
|
||||
'Company',
|
||||
'Corp.',
|
||||
'Corp',
|
||||
'Co.',
|
||||
'Inc.',
|
||||
'Inc',
|
||||
'Incorporated',
|
||||
'Ltd.',
|
||||
'Limited.',
|
||||
'Intel',
|
||||
'chipset',
|
||||
'adapter',
|
||||
'[hex]',
|
||||
'NDIS',
|
||||
'Module'
|
||||
];
|
||||
|
||||
const _IGNORED_PHRASES = [
|
||||
'Multiprotocol MAC/baseband processor',
|
||||
'Wireless LAN Controller',
|
||||
'Wireless LAN Adapter',
|
||||
'Wireless Adapter',
|
||||
'Network Connection',
|
||||
'Wireless Cardbus Adapter',
|
||||
'Wireless CardBus Adapter',
|
||||
'54 Mbps Wireless PC Card',
|
||||
'Wireless PC Card',
|
||||
'Wireless PC',
|
||||
'PC Card with XJACK(r) Antenna',
|
||||
'Wireless cardbus',
|
||||
'Wireless LAN PC Card',
|
||||
'Technology Group Ltd.',
|
||||
'Communication S.p.A.',
|
||||
'Business Mobile Networks BV',
|
||||
'Mobile Broadband Minicard Composite Device',
|
||||
'Mobile Communications AB',
|
||||
'(PC-Suite Mode)'
|
||||
];
|
||||
|
||||
function fixupPCIDescription(desc) {
|
||||
desc = desc.replace(/[_,]/, ' ');
|
||||
|
||||
/* Attempt to shorten ID by ignoring certain phrases */
|
||||
for (let i = 0; i < _IGNORED_PHRASES.length; i++) {
|
||||
let item = _IGNORED_PHRASES[i];
|
||||
let pos = desc.indexOf(item);
|
||||
if (pos != -1) {
|
||||
let before = desc.substring(0, pos);
|
||||
let after = desc.substring(pos + item.length, desc.length);
|
||||
desc = before + after;
|
||||
}
|
||||
}
|
||||
|
||||
/* Attmept to shorten ID by ignoring certain individual words */
|
||||
let words = desc.split(' ');
|
||||
let out = [ ];
|
||||
for (let i = 0; i < words.length; i++) {
|
||||
let item = words[i];
|
||||
|
||||
// skip empty items (that come out from consecutive spaces)
|
||||
if (item.length == 0)
|
||||
continue;
|
||||
|
||||
if (_IGNORED_WORDS.indexOf(item) == -1) {
|
||||
out.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return out.join(' ');
|
||||
}
|
||||
|
||||
// lowerBound:
|
||||
// @array: an array or array-like object, already sorted
|
||||
// according to @cmp
|
||||
|
@ -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());
|
||||
|
@ -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:
|
||||
|
@ -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) {
|
||||
|
@ -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 });
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -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;
|
||||
@ -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 &&
|
||||
|
@ -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;
|
||||
@ -160,13 +161,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();
|
||||
|
@ -1412,7 +1412,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,
|
||||
|
@ -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;
|
||||
|
@ -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 % */
|
||||
@ -405,29 +405,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 });
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -50,6 +50,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 +167,6 @@ function remoteProvidersLoaded(loadState) {
|
||||
|
||||
const RemoteSearchProvider = new Lang.Class({
|
||||
Name: 'RemoteSearchProvider',
|
||||
Extends: Search.SearchProvider,
|
||||
|
||||
_init: function(appInfo, dbusName, dbusPath, proxyType) {
|
||||
if (!proxyType)
|
||||
@ -175,7 +175,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();
|
||||
},
|
||||
|
||||
@ -195,9 +198,7 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
width, height, rowStride, size);
|
||||
}
|
||||
|
||||
// Ugh, but we want to fall back to something ...
|
||||
return new St.Icon({ icon_name: 'text-x-generic',
|
||||
icon_size: size });
|
||||
return null;
|
||||
},
|
||||
|
||||
_getResultsFinished: function(results, error) {
|
||||
@ -290,6 +291,6 @@ const RemoteSearchProvider2 = new Lang.Class({
|
||||
},
|
||||
|
||||
launchSearch: function(terms) {
|
||||
this.proxy.LaunchSearchRemote(terms);
|
||||
this.proxy.LaunchSearchRemote(terms, global.get_current_time());
|
||||
}
|
||||
});
|
||||
|
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);
|
||||
}
|
||||
});
|
||||
|
34
js/ui/separator.js
Normal file
34
js/ui/separator.js
Normal file
@ -0,0 +1,34 @@
|
||||
// -*- 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();
|
||||
}
|
||||
});
|
@ -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));
|
||||
|
@ -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();
|
||||
@ -241,18 +376,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 +412,121 @@ 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());
|
||||
|
||||
this._currentSourceIndex = newCurrentSourceIndex;
|
||||
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;
|
||||
}
|
||||
},
|
||||
|
||||
_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() {
|
||||
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 +555,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 +593,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 +615,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 +626,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 +661,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;
|
||||
}
|
||||
|
||||
@ -528,8 +708,9 @@ const InputSourceIndicator = new Lang.Class({
|
||||
// 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 +722,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 +740,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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -16,6 +16,7 @@ const Indicator = new Lang.Class({
|
||||
|
||||
_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) {
|
||||
|
@ -5,7 +5,6 @@ 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;
|
||||
|
||||
@ -140,6 +139,46 @@ const NMNetworkMenuItem = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
const NMWiredSectionTitleMenuItem = new Lang.Class({
|
||||
Name: 'NMWiredSectionTitleMenuItem',
|
||||
Extends: PopupMenu.PopupSwitchMenuItem,
|
||||
|
||||
_init: function(label, params) {
|
||||
params = params || { };
|
||||
params.style_class = 'popup-subtitle-menu-item';
|
||||
this.parent(label, false, params);
|
||||
},
|
||||
|
||||
updateForDevice: function(device) {
|
||||
if (device) {
|
||||
this._device = device;
|
||||
this.setStatus(device.getStatusLabel());
|
||||
this.setToggleState(device.connected);
|
||||
} else
|
||||
this.setStatus('');
|
||||
},
|
||||
|
||||
activate: function(event) {
|
||||
this.parent(event);
|
||||
|
||||
if (!this._device) {
|
||||
log('Section title activated when there is more than one device, should be non reactive');
|
||||
return;
|
||||
}
|
||||
|
||||
let newState = this._switch.state;
|
||||
|
||||
let ok;
|
||||
if (newState)
|
||||
ok = this._device.activate();
|
||||
else
|
||||
ok = this._device.deactivate();
|
||||
|
||||
if (!ok)
|
||||
this._switch.setToggleState(false);
|
||||
}
|
||||
});
|
||||
|
||||
const NMWirelessSectionTitleMenuItem = new Lang.Class({
|
||||
Name: 'NMWirelessSectionTitleMenuItem',
|
||||
Extends: PopupMenu.PopupSwitchMenuItem,
|
||||
@ -312,7 +351,7 @@ const NMDevice = new Lang.Class({
|
||||
this._autoConnectionItem = null;
|
||||
this._overflowItem = null;
|
||||
|
||||
this.statusItem = new PopupMenu.PopupSwitchMenuItem('', this.connected, { style_class: 'popup-subtitle-menu-item' });
|
||||
this.statusItem = new PopupMenu.PopupSwitchMenuItem(this._getDescription(), this.connected, { style_class: 'popup-subtitle-menu-item' });
|
||||
this._statusChanged = this.statusItem.connect('toggled', Lang.bind(this, function(item, state) {
|
||||
let ok;
|
||||
if (state)
|
||||
@ -473,10 +512,6 @@ const NMDevice = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
syncDescription: function() {
|
||||
this.statusItem.label.text = this.device._description;
|
||||
},
|
||||
|
||||
// protected
|
||||
_createAutomaticConnection: function() {
|
||||
throw new TypeError('Invoking pure virtual function NMDevice.createAutomaticConnection');
|
||||
@ -608,15 +643,33 @@ const NMDevice = new Lang.Class({
|
||||
this.statusItem.setStatus(this.getStatusLabel());
|
||||
|
||||
this.emit('state-changed');
|
||||
},
|
||||
|
||||
_getDescription: function() {
|
||||
let dev_product = this.device.get_product();
|
||||
let dev_vendor = this.device.get_vendor();
|
||||
if (!dev_product || !dev_vendor)
|
||||
return '';
|
||||
|
||||
let product = Util.fixupPCIDescription(dev_product);
|
||||
let vendor = Util.fixupPCIDescription(dev_vendor);
|
||||
let out = '';
|
||||
|
||||
// Another quick hack; if all of the fixed up vendor string
|
||||
// is found in product, ignore the vendor.
|
||||
if (product.indexOf(vendor) == -1)
|
||||
out += vendor + ' ';
|
||||
out += product;
|
||||
|
||||
return out;
|
||||
}
|
||||
});
|
||||
|
||||
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 +684,18 @@ 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) {
|
||||
this._autoConnectionName = _("Auto Ethernet");
|
||||
this.category = NMConnectionCategory.WIRED;
|
||||
|
||||
this.parent(client, device, connections);
|
||||
},
|
||||
|
||||
_createAutomaticConnection: function() {
|
||||
@ -817,6 +882,10 @@ const NMDeviceBluetooth = new Lang.Class({
|
||||
this._clearSection();
|
||||
this._queueCreateSection();
|
||||
this._updateStatusItem();
|
||||
},
|
||||
|
||||
_getDescription: function() {
|
||||
return this.device.name || _("Bluetooth");
|
||||
}
|
||||
});
|
||||
|
||||
@ -1598,14 +1667,15 @@ const NMApplet = new Lang.Class({
|
||||
this._mobileUpdateId = 0;
|
||||
this._mobileUpdateDevice = null;
|
||||
|
||||
this._nmDevices = [];
|
||||
this._devices = { };
|
||||
|
||||
this._devices.wired = {
|
||||
section: new PopupMenu.PopupMenuSection(),
|
||||
devices: [ ],
|
||||
item: new NMWiredSectionTitleMenuItem(_("Wired"))
|
||||
};
|
||||
|
||||
this._devices.wired.section.addMenuItem(this._devices.wired.item);
|
||||
this._devices.wired.section.actor.hide();
|
||||
this.menu.addMenuItem(this._devices.wired.section);
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
@ -1613,7 +1683,7 @@ const NMApplet = new Lang.Class({
|
||||
this._devices.wireless = {
|
||||
section: new PopupMenu.PopupMenuSection(),
|
||||
devices: [ ],
|
||||
item: this._makeToggleItem('wireless', _("Wi-Fi"))
|
||||
item: this._makeToggleItem('wireless', _("Wireless"))
|
||||
};
|
||||
this._devices.wireless.section.addMenuItem(this._devices.wireless.item);
|
||||
this._devices.wireless.section.actor.hide();
|
||||
@ -1623,7 +1693,9 @@ const NMApplet = new Lang.Class({
|
||||
this._devices.wwan = {
|
||||
section: new PopupMenu.PopupMenuSection(),
|
||||
devices: [ ],
|
||||
item: this._makeToggleItem('wwan', _("Mobile broadband"))
|
||||
};
|
||||
this._devices.wwan.section.addMenuItem(this._devices.wwan.item);
|
||||
this._devices.wwan.section.actor.hide();
|
||||
this.menu.addMenuItem(this._devices.wwan.section);
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
@ -1640,6 +1712,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 +1724,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);
|
||||
@ -1706,23 +1780,16 @@ const NMApplet = new Lang.Class({
|
||||
section.actor.hide();
|
||||
else {
|
||||
section.actor.show();
|
||||
|
||||
// Sync the relation between the section title
|
||||
// item (the one with the airplane mode switch)
|
||||
// and the individual device switches
|
||||
if (item) {
|
||||
if (devices.length == 1) {
|
||||
let dev = devices[0];
|
||||
dev.statusItem.actor.hide();
|
||||
item.updateForDevice(dev);
|
||||
} else {
|
||||
devices.forEach(function(dev) {
|
||||
dev.statusItem.actor.show();
|
||||
});
|
||||
|
||||
// remove status text from the section title item
|
||||
item.updateForDevice(null);
|
||||
}
|
||||
if (devices.length == 1) {
|
||||
let dev = devices[0];
|
||||
dev.statusItem.actor.hide();
|
||||
item.updateForDevice(dev);
|
||||
} else {
|
||||
devices.forEach(function(dev) {
|
||||
dev.statusItem.actor.show();
|
||||
});
|
||||
// remove status text from the section title item
|
||||
item.updateForDevice(null);
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -1730,9 +1797,8 @@ const NMApplet = new Lang.Class({
|
||||
_readDevices: function() {
|
||||
let devices = this._client.get_devices() || [ ];
|
||||
for (let i = 0; i < devices.length; ++i) {
|
||||
this._deviceAdded(this._client, devices[i], true);
|
||||
this._deviceAdded(this._client, devices[i]);
|
||||
}
|
||||
this._syncDeviceNames();
|
||||
},
|
||||
|
||||
_notifyForDevice: function(device, iconName, title, text, urgency) {
|
||||
@ -1780,18 +1846,7 @@ const NMApplet = new Lang.Class({
|
||||
return wrapper;
|
||||
},
|
||||
|
||||
_syncDeviceNames: function() {
|
||||
let names = NMGtk.utils_disambiguate_device_names(this._nmDevices);
|
||||
for (let i = 0; i < this._nmDevices.length; i++) {
|
||||
let device = this._nmDevices[i];
|
||||
if (device._description != names[i]) {
|
||||
device._description = names[i];
|
||||
device._delegate.syncDescription();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_deviceAdded: function(client, device, skipSyncDeviceNames) {
|
||||
_deviceAdded: function(client, device) {
|
||||
if (device._delegate) {
|
||||
// already seen, not adding again
|
||||
return;
|
||||
@ -1802,14 +1857,10 @@ const NMApplet = new Lang.Class({
|
||||
let section = this._devices[wrapper.category].section;
|
||||
let devices = this._devices[wrapper.category].devices;
|
||||
|
||||
section.addMenuItem(wrapper.statusItem);
|
||||
section.addMenuItem(wrapper.section);
|
||||
section.addMenuItem(wrapper.section, 1);
|
||||
section.addMenuItem(wrapper.statusItem, 1);
|
||||
devices.push(wrapper);
|
||||
|
||||
this._nmDevices.push(device);
|
||||
if (!skipSyncDeviceNames)
|
||||
this._syncDeviceNames();
|
||||
|
||||
this._syncSectionTitle(wrapper.category);
|
||||
}
|
||||
},
|
||||
@ -1827,10 +1878,6 @@ const NMApplet = new Lang.Class({
|
||||
let pos = devices.indexOf(wrapper);
|
||||
devices.splice(pos, 1);
|
||||
|
||||
pos = this._nmDevices.indexOf(device);
|
||||
this._nmDevices.splice(pos, 1);
|
||||
this._syncDeviceNames();
|
||||
|
||||
this._syncSectionTitle(wrapper.category)
|
||||
},
|
||||
|
||||
|
@ -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);
|
||||
|
@ -124,6 +124,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 +161,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) {
|
||||
@ -392,6 +387,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;
|
||||
@ -445,6 +441,8 @@ const WindowOverlay = new Lang.Class({
|
||||
this._parentActor = parentActor;
|
||||
this._hidden = false;
|
||||
|
||||
this._settings = new Gio.Settings({ schema: BUTTON_LAYOUT_SCHEMA });
|
||||
|
||||
this.borderSize = 0;
|
||||
this.border = new St.Bin({ style_class: 'window-clone-border' });
|
||||
|
||||
@ -456,11 +454,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,27 +529,13 @@ 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 layout = this._settings.get_string(BUTTON_LAYOUT_KEY);
|
||||
let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL;
|
||||
|
||||
let split = layout.split(":");
|
||||
@ -577,8 +557,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 +1172,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 +1181,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 +1213,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 +1248,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 +1383,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 +1551,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);
|
||||
|
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-18 17:13+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 järgmise kasutuse jaoks, näidatakse "
|
||||
"\"Jäta parool meelde\" märkeruutu. See võti määrab 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 akendevahetajas kuvatakse. Sobivad väärtused on "
|
||||
"'thumbnail-only' (näidatakse ainult pisipilti), 'app-icon-only' (näidatakse "
|
||||
"ainult rakenduse ikooni) või 'both' (näidatakse mõlemaid)."
|
||||
|
||||
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"
|
||||
|
||||
|
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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -1738,15 +1738,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 +1847,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 +1855,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))
|
||||
{
|
||||
@ -2668,6 +2676,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 +2755,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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user