Compare commits

..

24 Commits

Author SHA1 Message Date
9efa271888 Implement Sent signals for Telepathy.
This will allow us to update our notifications when
someone uses a different Telepathy client to send
messages.

https://bugzilla.gnome.org/show_bug.cgi?id=635991
2010-11-28 10:28:51 -05:00
9ed3ce9006 Add a search provider for the session's current windows.
https://bugzilla.gnome.org/show_bug.cgi?id=635989
2010-11-28 10:26:09 -05:00
b400fc2837 workspace-indicators: Add hover indication
Scale up indicators on hover to hint at their clickability.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 05:11:13 +01:00
a66af6fbac overview: Update animation
Update the animation on entering/leaving the overview to only zoom
the window previews and fade other elements.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 05:11:13 +01:00
1bbc138ed2 dash: Improve DND to dash and allow reordering
Show a positional indicator where a new favorite will be added and
make the favorites re-orderable. Also allow the removal of favorites
using drag-and-drop according to the mockups.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 05:11:13 +01:00
868d4756e4 workspaces: Change handling of window-drag signals
Delegate the emission of the window-drag-begin/window-drag-end
signals to overview functions, as done already for other items.
This will enable objects to react to those signals without having
access to the workspace objects / the workspaces view.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 05:11:13 +01:00
dfa3be59dc search-display: Change the default display to use iconGrid
Current mockups display all search results as icons as used by
application results, so change the default result display to use
iconGrid/BaseIcon. Remove the custom application results display,
as it is no longer needed.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 05:11:13 +01:00
62677336a5 overview: Add ViewSelector to the overview
Add the view selector and adjust the positioning of elements in the
overview. Unlike the old dash, the view selector is made public to
indicate that extensions may add additional views or search providers.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 05:11:06 +01:00
264a82cbfc workspaces: Rework workspace controls for the view selector
As workspaces will appear as a particular view in the view selector,
merge WorkspacesControls and WorkspacesManager to control workspaces
and related controls, so that a single actor can be added to the
selector instead of positioning the elements from the overview.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 05:11:06 +01:00
f8cfd9f903 workspaces-view: Swap workspace ordering for RTL locales
Make the first workspace the right-most one in RTL locales, as one
would expect. Update all dragging/scrolling functions to behave
correctly.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 05:11:06 +01:00
13f8cd3bcf workspaces-view: Remove MosaicView
The new layout does no longer support view switching, so merge
GenericWorkspacesView and SingleView, and remove MosaicView.
Also rename or remove workspace properties and functions which
are now unused.
The grid will have a comeback with the new DND behavior.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 05:11:06 +01:00
e1f336374b app-display: Slight cleanup and style update
Being no longer an independent menu pane, both the toggle() and
close() functions are no longer needed, and the view's structure
can be simplified a bit.

Also update the style to fit into the view selector.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 05:11:06 +01:00
0ba1387d19 Fake workspaces tab
https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 05:11:06 +01:00
8832ce3a2b view-selector: Add keyboard shortcut for view switching
As the view selector is a tabbed interface, use the default keyboard
shortcut of Ctrl-PageUp/PageDown of GtkNotebook for switching between
views.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 05:11:06 +01:00
91d7a022a9 view-selector: Move search logic into SearchTab
The view selector should only deal with view switching, so move the
logic to deal with search (find-as-you-type, cancelling a search,
navigating/activating results) into the SearchTab.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 05:11:03 +01:00
95d15725fc Use the old dash code to implement the view selector
The view selector is a tabbed interface with a search entry. Starting
a search switches focus to the results' tab, ending a search moves the
focus back to the previously selected tab. Activating a normal tab
while a search is active cancels the search.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 05:10:21 +01:00
bb2b7d83af search-display: Move SearchResults to a separate file
With the new layout, search results will be displayed in an independent
view like window previews, applications and possible future additions;
it does not make much sense keeping it with the switching logic, so move
the code to its own file.

Also remove the dash-prefix from the relevant style classes.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 00:13:51 +01:00
0abac6f67d dash: Move padding into the icon for Fittsability
With this change, the icons' reactive area extends to the screen
edge, making them good targets according to Fitts' law.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 00:13:51 +01:00
e00398e2ac dash: Reimplement the dash based on AppWell code
The new dash implementation is a single-column vertical sidebar,
whose items are scaled dynamically to fit the available height.
If the height is still exceeded after scaling down to a minimum
item size, excess items are cut off.
The now unused old dash implementation is renamed to OldDash, as
its code will be used as a base for the new view selector element.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 00:13:51 +01:00
0f19492545 linear-view: Remove NewWorkspaceArea
As the button to add workspaces will move to the same position as
the new workspace drop area in drag mode, the latter is redundant
and can be removed.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 00:13:51 +01:00
9d46a6ceea linear-view: Remove shadows when zoomed out
Overlaying inactive workspaces with a gradient to fade out the actors
does no longer work when re-using the normal desktop background. If
we keep the current DND behavior, we probably want to implement a real
fade effect - for now, just remove the visually disruptive shadows.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 00:13:51 +01:00
6432de95cc overview: Do not zoom the desktop background
While scaling the desktop background with the window previews represents
workspaces quite intuitively, the approach is not without problems.
As window previews in the overview behave quite differently to "real"
windows, the representation of workspaces as miniature versions of
"real" workspaces is flawed. The scaling also makes the transitions
to and from the overview much more visually expensive, without adding
much benefit.
Leaving the background in place provides more visual stability to the
transitions and emphasizes the distinctive behavior of elements in the
overview.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-19 00:13:46 +01:00
738fc375c0 overview: Replace InfoBar with message tray notifications
The layout of recent mockups occupies the space previously reserved
for the info bar with the view selector. As the bar's purpose is
mainly to provide the user with feedback, it makes sense to use the
existing message tray facility instead of moving the bar elsewhere.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-18 20:35:55 +01:00
4e9a530a64 linear-view: Remove the scrollbar
The scrollbar is the main culprit for cluttered controls in the
linear view - all its functionality is already provided by the
workspace indicators, so it is save to remove the scrollbar in
order to clean up the interface.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
2010-11-18 20:35:55 +01:00
308 changed files with 25724 additions and 94543 deletions

23
.gitignore vendored
View File

@ -3,11 +3,11 @@
*.o
.deps
.libs
ABOUT-NLS
ChangeLog
INSTALL
Makefile
Makefile.in
NEWS
aclocal.m4
autom4te.cache
config.h
@ -18,10 +18,13 @@ config
configure
data/gnome-shell.desktop
data/gnome-shell.desktop.in
data/gnome-shell-clock-preferences.desktop
data/gnome-shell-clock-preferences.desktop.in
data/gschemas.compiled
data/org.gnome.shell.gschema.xml
data/org.gnome.shell.gschema.valid
js/misc/config.js
data/org.gnome.accessibility.magnifier.gschema.xml
data/org.gnome.accessibility.magnifier.gschema.valid
intltool-extract.in
intltool-merge.in
intltool-update.in
@ -30,13 +33,8 @@ m4/
omf.make
po/*.gmo
po/gnome-shell.pot
po/*.header
po/*.sed
po/*.sin
po/Makefile.in.in
po/Makevars.template
po/POTFILES
po/Rules-quot
po/stamp-it
scripts/launcher.pyc
src/*.gir
@ -45,23 +43,16 @@ src/*-enum-types.[ch]
src/*-marshal.[ch]
src/Makefile
src/Makefile.in
src/calendar-server/org.gnome.Shell.CalendarServer.service
src/gnomeshell-taskpanel
src/gnome-shell
src/gnome-shell-calendar-server
src/gnome-shell-extension-tool
src/gnome-shell-jhbuild
src/gnome-shell-perf-helper
src/gnome-shell-real
src/gnome-shell-clock-preferences
src/run-js-test
src/test-recorder
src/test-recorder.ogg
src/test-theme
src/st.h
src/stamp-st.h
src/stamp-st.h.tmp
stamp-h1
tests/run-test.sh
xmldocs.make
*~
*.patch
*.sw?

View File

@ -3,5 +3,5 @@ E-mail: otaylor@redhat.com
Userid: otaylor
Colin Walters
E-mail: walters@verbum.org
E-mail: walters@redhat.com
Userid: walters

263
NEWS
View File

@ -1,263 +0,0 @@
3.1.2
=====
* Fix problem with "user theme extension" breaking the CSS for other
extensions [Giovanni; #650971]
* Telepathy IM framework integration
- Switch to using telepathy-glib rather than talking to
Telepathy via D-Bus [Guillaume, Jasper; #645585, #649633, #651138, #651227]
- Acknowledge messages when the user clicks on them [Guillaume, #647893]
- Fix problem with telepathy icon blinking for incoming messages
even though the user has been notified of them [Guillaume; #643594]
* Networking
- keep wirelesss networks in predictable order [Giovanni; #646580, #652313]
- Show unmanaged devices in the menu [Giovanni; #646946]
- Fix overflow when too many VPN connections [Giovanni; #651602]
* Bluetooth
- Show "hardware disabled" when disabled by rfkill [Giovanni; #648048]
- Fix bug updating status of devices [Giovanni; #647565]
* LookingGlass console:
- Add a "Memory" tab [Colin; #650692]
- Make escape work from any page [Dan Winship; #647303]
- Provide a way to refer to panel items as, e.g.,
Main.panel._activities [Dan Winship; #646915]
* User menu
- Fix problem with suspend menu option locking the screen even when the user
disabled that. [Florian; #652327]
- Hide "power off..." option if shutdown is disabled via PolicyKit
[Florian; #652038]
* Track changes to WM_CLASS (fixes problems with LibreOffice tracking)
[Colin; #649315]
* Remove app tracking workarounds for Firefox and LibreOffice [Colin; #651015]
* Use upstream gettext autoconfigury rather than glib version [Javier; #631576]
* Show messages in the message tray when an application is fullscreen
[Dan Winship; #608667]
* Don't autohide the workspace pager if there is more than one workspace
[Florian; #652714, #653078, #653142]
* Don't always slide out the workspace pager at drag begin [Florian; #652730]
* Only offer to remove a favorite app when dragging it's icon [Owen; #642895]
* Allow dropping an icon anywhere on a workspace [Adel; #652079]
* st-scroll-view: Make the fade effect and offset themable [Jasper; #651813]
* Obey the user's preference when running an application in a terminal
from the run dialog [Florian; #648422]
* Consistently exit overview when launching external applications
[Colin; #653095]
* Adapt to changes in GJS for how GObject APIs are bound
[Alex, Colin, Florian, Jasper, Marc-Antoine; #649981, #652597]
* Fix problems with scrolling in overflow for alt-Tab switcher
[Dan Winship, Adel; #647807]
* Mark relationships between labels and actors for accessibility [Alejandro]
* Add org.gnome.shell.enabled-extensions complementing disabled-extensions
GSetting [Tassilo; #651088]
* Visual tweaks [Jakub, Jasper; #646261, #652715]
* Switch to building against clutter-1.7 with independent Cogl [Adel; #653397]
* Code cleanups [Colin, Dan Winship, Florian; #633620, #645031, #648755, #648758,
#648760, #649203, #649517, #650317, #652730]
* Memory leak fixes [Colin, Maxim; #649508, #650934]
* Build Fixes [Colin, Dan Winship, Florian, Ionut, Morten, Owen, Sean; #647395,
#648006, #650869, #653199, #653275
* Miscellaneous bug fixes [Adam, Adel, Dan Williams, Dan Winship, Florian,
Ionut, Jasper, Maxim, Ray; #620105, #639459, #641570, #642793, #643513,
#645848, #646919, #647186, #648305, #648410, #648562, #648894, #649001,
#645990, #647893, #647907, #651012, #651086, #651606, #651569, #651866,
#652388, #653511]
Contributors:
Ionut Biru, Giovanni Campagna, Guillaume Desmottes, Adam Dingle,
Maxim Ermilov, Adel Gadllah, Tassilo Horn, Javier Jardón, Jonny Lamb,
Alexander Larsson, Rui Matos, Morten Mjelva, Florian Müllner,
Marc-Antoine Perennou, Alejandro Piñeiro, Jasper St. Pierre, Jakub Steiner,
Ray Strode, Owen Taylor, Colin Walters, Dan Williams, Sean Wilson, Dan Winship
Translations:
Daniel Martinez Cucalon [ar], Ihar Hrachyshka [be], Carles Ferrando,
Gil Forcada, Sílvia Miranda [ca], Kristjan Schmidt [eo], Jorge González,
Daniel Mustieles [es], Seán de Búrca [ga], Fran Diéguez [gl],
Yaron Shahrabani [he], Kjartan Maraas [nb], Misha Shnurapet,
Yuri Myasoedov [ru], Daniel Nylander [se], Peter Mráz [sk],
Matej Urbančič [sl], Krishnababu Krothapalli [te], Daniel Korostil [uk],
Aron Xu [zh_CN]
3.0.2
=====
* Network Menu [Dan Williams]
- Fix connecting to WPA2 Enterprise access points
Fixes https://bugzilla.gnome.org/show_bug.cgi?id=648171
- Show the mobile broadband wizard when selecting 3G network
Fixes https://bugzilla.gnome.org/show_bug.cgi?id=649318
- Miscellaneous bug fixes
648648, 650124
* Fix duplicate icons in the application browser [Owen]
https://bugzilla.gnome.org/show_bug.cgi?id=648739
* Make clicking anywhere on the volume icon slider work [Giovanni]
https://bugzilla.gnome.org/show_bug.cgi?id=646660
* Fix a case where activating and clicking the hot corner
at the same time could result in immediately leaving the
overview [Rui]
https://bugzilla.gnome.org/show_bug.cgi?id=649427
* Fix a case where applications became misordered in Alt-Tab [Jasper]
https://bugzilla.gnome.org/show_bug.cgi?id=643302
* Fix a bug where messages you send could show up in
notifications as if someone else sent them [Jonny]
https://bugzilla.gnome.org/show_bug.cgi?id=650219
* Memory leak fixes [Colin, Maxim]
642652, 649508, 649497
* Miscellaneous minor bug fixes [Adel, Christopher, Jasper]
649596, 648765, 648983, 649632
Contributors:
Christopher Aillon, Giovanni Campagna, Maxim Ermilov,
Adel Gadllah, Jonny Lamb, Rui Matos, Jasper St. Pierre,
Owen Taylor, Colin Walters, Dan Williams
Translations:
Arash Mousavi [fa], Seán de Búrca [ga], Timo Jyrinki [fi],
Sigurd Gartmann [nb], Daniel Nylander [se], Peter Mráz [sl],
Abduxukur Abdurixit [ug], Nguyễn Thái Ngọc Duy [vi]
3.0.1
=====
* Network menu
- Fix problems updating the menu for mobile broadband devices [Giovanni]
https://bugzilla.gnome.org/show_bug.cgi?id=646395
- Fix missing device descriptions with multiple devices of the
same type [Giovanni]
https://bugzilla.gnome.org/show_bug.cgi?id=646074
- Label ad-hoc neworks with an appropriate icon [Dan]
https://bugzilla.gnome.org/show_bug.cgi?id=646141
- Fix displaying some devices states as "invalid" [Dan]
https://bugzilla.gnome.org/show_bug.cgi?id=646946
- Fix problems with access points that don't report a SSID [Giovanni]
https://bugzilla.gnome.org/show_bug.cgi?id=647040
- Miscellaneous minor bug fixes [Dan, Giovanni, Owen]
645981, 646558, 646443, 646708, 646968
* Application menu and icon
- Fix bug where application menu icon was missing at GNOME Shell
startup. [Florian]
https://bugzilla.gnome.org/show_bug.cgi?id=644122
- Fix missing application menu for dialog windows [Colin]
https://bugzilla.gnome.org/show_bug.cgi?id=647082
- When launching an application through an alternate launcher
(like for a System Settings pane), association the windows with
the application, not the launcher. [Colin]
https://bugzilla.gnome.org/show_bug.cgi?id=646689
* Activities overview
- Load the applications view incrementally to avoid potentially freezing
for multiple seconds [Colin]
https://bugzilla.gnome.org/show_bug.cgi?id=647778
- Fix bug where package installation while the overview
was up could result in a corrupted application display. [Giovanni]
https://bugzilla.gnome.org/show_bug.cgi?id=645801
- Fix dragging from the search results to launch apps and docs [Florian]
https://bugzilla.gnome.org/show_bug.cgi?id=645990
- Fix flickering of selection when searching in the overview [Florian]
https://bugzilla.gnome.org/show_bug.cgi?id=646019
- Fix bug when typing into the search box when text was already
selected [Nohemi]
https://bugzilla.gnome.org/show_bug.cgi?id=636341
* Fix layout of notifications for right-to-left languages [Florian]
https://bugzilla.gnome.org/show_bug.cgi?id=646921
* Remove a confusing special case where Alt-Tab sometimes switched
to a different window of the same application rather than to
a different application. [Rui]
https://bugzilla.gnome.org/show_bug.cgi?id=648132
* Fix a crash that could happen when a window was opened on a
workspace that was immediately removed [Dan]
https://bugzilla.gnome.org/show_bug.cgi?id=648132
* Fix keyboard navigation in logout/reboot dialogs [Dan]
https://bugzilla.gnome.org/show_bug.cgi?id=646740
* Fix missing inspector icon in Looking Glass console [Dan]
* Miscellaneous minor bug fixes [Adel, Colin, Dan, Florian, Nohemi]
645648, 646205, 646257, 646855, 647098, 646730
Contributors:
Giovanni Campagna, Nohemi Fernandez, Adel Gadllah, Rui Matos, Florian Müllner,
Owen Taylor, Colin Walters, Dan Winship
Translations:
Hendrik Richter [de], Jorge González [es], Arash Mousavi [fa],
Fran Diéguez [gl], Jiro Matsuzawa [ja], Piotr Drąg [pl], Daniel Nylander [sv],
Sira Nokyoongtong [th], Muhammet Kara [tr], Nguyễn Thái Ngọc Duy [vi],
Aron Xu [zh_CN], Chao-Hsiung Liao [zh_HK, zh_TW]
3.0.0.2
=======
* Fix missing import that was preventing extensions from loading.
[Maxim Ermilov]
https://bugzilla.gnome.org/show_bug.cgi?id=646333
Translations:
Timo Jyrinki [fi]
3.0.0.1
=======
* Fix problem with stuck event handling if network menu pops down while
user is using the scrollbar. [Owen Taylor]
https://bugzilla.gnome.org/show_bug.cgi?id=646825
Contributors to GNOME Shell 3.0
===============================
Code:
Josh Adams, Kiyoshi Aman, Nuno Araujo, Emmanuele Bassi, Dirk-Jan C. Binnema,
Wouter Bolsterlee, Raphael Bosshard, Milan Bouchet-Valat, Christina Boumpouka,
Mathieu Bridon, Alban Browaeys, Phil Bull, Micro Cai, Giovanni Campagna,
Cosimo Cecchi, Tor-björn Claesson, Matthias Clasen, Jason D. Clinton,
Frederic Crozat, Guillaume Desmottes, Sander Dijkhuis, Neha Doijode,
Maxim Ermilov, Diego Escalante Urrelo, Luca Ferretti, Steve Frécinaux,
Takao Fujiwara, Adel Gadllah, Vadim Girlin, Nick Glynn, Guido Günther,
Leon Handreke, Lex Hider, Richard Hughes, Javier Jardón, Abderrahim Kitouni,
Andre Klapper, Alexander Larsson, Nickolas Lloyd, Ryan Lortie, Kjartan Maraas,
Koop Mast, Rui Matos, Jonathan Matthew, William Jon McCann, Morten Mjelva,
Federico Mena Quintero, Florian Müllner, Jon Nettleton, Hellyna Ng,
Discardi Nicola, Carlos Martín Nieto, Bastien Nocera, Bill Nottingham,
Matt Novenstern, Marc-Antoine Perennou, Neil Perry, Frédéric Péters,
Alejandro Piñeiro, Siegfried-Angel Gevatter Pujals, "res", Neil Roberts,
"Sardem FF7", Florian Scandella, Joseph Scheuhammer, Christian Schramm,
Gustavo Noronha Silva, Jasper St. Pierre, Eric Springer, Jakub Steiner,
Jonathan Strander, Ray Strode, Owen Taylor, Rico Tzschichholz,
Sergey V. Udaltsov, Daiki Ueno, Vincent Untz, Marcelo Jorge Vieira,
Mads Villadsen, Colin Walters, Dan Winship, William Wolf, Thomas Wood,
Pierre Yager, David Zeuthen, Marina Zhurakhinskaya
Design:
Allan Day, William Jon McCann, Jeremy Perry, Jakub Steiner
2008 Boston GNOME design hackfest participants (especially Neil J. Patel
for turning the resulting sketches into our first mockups.)
Everybody on irc.gnome.org:#gnome-design
Translations:
Friedel Wolff (af), Khaled Hosny (ar), Ivaylo Valkov (bg), Jamil Ahmed (bn)
Runa Bhattacharjee (bn_IN), Gil Forcada, Siegfried-Angel Gevatter Pujals,
Jordi Serratosa (ca), Andre Klapper, Petr Kovar (cs), Kenneth Nielsen,
Kris Thomsen (da), Mario Blättermann, Hendrik Brandt, Christian Kirbach,
Hendrik Richter, Wolfgang Stöggl (de), Michael Kotsarinis, Kostas Papadimas,
Jennie Petoumenou, Sterios Prosiniklis, Fotis Tsamis, Simos Xenitellis (el),
Bruce Cowan, Philip Withnall (en_GB), Jorge Gonzalez, Daniel Mustieles (es),
Mattias Põldaru, Ivar Smolin (et), Inaki Larranaga Murgoitio (eu),
Mahyar Moghimi (fa), Timo Jyrinki (fi), Cyril Arnaud, Bruno Brouard,
Pablo Martin-Gomez, Claude Paroz, Frédéric Peters (fr), Seán de Búrca (ga)
Francisco Diéguez, Antón Méixome (gl), Sweta Kothari (gu), Liel Fridman,
Yaron Shahrabani (he), Rajesh Ranjan (hi), Gabor Kelemen (hu), Milo Casagrande,
Luca Ferretti (it), Dirgita, Andika Triwidada (id), Takayuki KUSANO,
Takayoshi OKANO, Kiyotaka NISHIBORI, Futoshi NISHIO (ja), Shankar Prasad (kn),
Young-Ho Cha, Changwoo Ryu (ko), Žygimantas Beručka, Gintautas Miliauskas (lt),
Rudolfs Mazurs (lv), Sandeep Shedmake (mr), Kjartan Maraas (nb),
Wouter Bolsterlee, Sander Dijkhuis, Reinout van Schouwen (nl),
Torstein Winterseth (nn), A S Alam (pa), Tomasz Dominikowski, Piotr Drąg (pl),
Duarte Loreto (pt), Felipe Borges, Rodrigo Padula de Oliveira,
Rodrigo L. M. Flores, Amanda Magalhães, Og B. Maciel, Gabriel F. Vilar,
Jonh Wendell (pt_BR), Lucian Adrian Grijincu, Daniel Șerbănescu (ro),
Sergey V. Kovylov, Andrey Korzinev, Yuri Myasoedov, Marina Zhurakhinskaya (ru),
Daniel Nylander (se), Matej Urbančič, Andrej Žnidaršič (sl),
Miloš Popović (sr, sr@latin), Miroslav Nikolić (sr), Tirumurti Vasudevan (ta),
Sira Nokyoongtong (th), Baris Cicek (tr), Abduxukur Abdurixit,
Gheyret T. Kenji (ug), Maxim V. Dziumanenko, Daniel Korostil (uk),
Nguyễn Thái Ngọc Duy (vi), Jessica Ban, 'jiero', Wei Li, YunQiang Su, Ray Wang,
Aron Xu (zh_CN), Chao-Hsiung Liao (zh_HK, zh_TW)

View File

@ -1,14 +1,11 @@
AC_PREREQ(2.63)
AC_INIT([gnome-shell],[3.1.3],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_INIT([gnome-shell],[2.91.2],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_SRCDIR([src/shell-global.c])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_AUX_DIR([config])
AC_SUBST([PACKAGE_NAME], ["$PACKAGE_NAME"])
AC_SUBST([PACKAGE_VERSION], ["$PACKAGE_VERSION"])
AM_INIT_AUTOMAKE([1.10 dist-bzip2 no-dist-gzip foreign])
AM_MAINTAINER_MODE
@ -23,16 +20,12 @@ AM_PROG_CC_C_O
LT_PREREQ([2.2.6])
LT_INIT([disable-static])
# i18n
IT_PROG_INTLTOOL([0.40])
AM_GNU_GETTEXT([external])
AM_GNU_GETTEXT_VERSION([0.17])
GETTEXT_PACKAGE=gnome-shell
AC_SUBST(GETTEXT_PACKAGE)
AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",
[The prefix for our gettext translation domains.])
IT_PROG_INTLTOOL(0.26)
AM_GLIB_GNU_GETTEXT
PKG_PROG_PKG_CONFIG([0.22])
@ -56,98 +49,65 @@ AC_MSG_CHECKING([for GStreamer (needed for recording functionality)])
if $PKG_CONFIG --exists gstreamer-0.10 '>=' $GSTREAMER_MIN_VERSION ; then
AC_MSG_RESULT(yes)
build_recorder=true
recorder_modules="gstreamer-0.10 gstreamer-base-0.10 x11"
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes)
recorder_modules="gstreamer-0.10 gstreamer-base-0.10 xfixes"
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0)
else
AC_MSG_RESULT(no)
fi
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
CLUTTER_MIN_VERSION=1.5.15
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
GJS_MIN_VERSION=0.7.11
MUTTER_MIN_VERSION=3.0.0
GTK_MIN_VERSION=3.0.0
CLUTTER_MIN_VERSION=1.3.14
GOBJECT_INTROSPECTION_MIN_VERSION=0.6.11
GJS_MIN_VERSION=0.7
MUTTER_MIN_VERSION=2.91.0
GTK_MIN_VERSION=2.91.0
GIO_MIN_VERSION=2.25.9
LIBECAL_MIN_VERSION=2.32.0
LIBEDATASERVER_MIN_VERSION=1.2.0
LIBEDATASERVERUI_MIN_VERSION=2.91.6
TELEPATHY_GLIB_MIN_VERSION=0.15.0
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
POLKIT_MIN_VERSION=0.100
STARTUP_NOTIFICATION_MIN_VERSION=0.11
# Collect more than 20 libraries for a prize!
PKG_CHECK_MODULES(GNOME_SHELL, gio-2.0 >= $GIO_MIN_VERSION
gio-unix-2.0 dbus-glib-1 libxml-2.0
gtk+-3.0 >= $GTK_MIN_VERSION
libmutter >= $MUTTER_MIN_VERSION
gjs-internals-1.0 >= $GJS_MIN_VERSION
libgnome-menu $recorder_modules gconf-2.0
gdk-x11-3.0
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION
gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
libcanberra
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes)
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
GJS_VERSION=`$PKG_CONFIG --modversion gjs-internals-1.0`
AC_DEFINE_UNQUOTED([GJS_VERSION], ["$GJS_VERSION"], [The version of GJS we're linking to])
AC_SUBST([GJS_VERSION], ["$GJS_VERSION"])
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
JHBUILD_TYPELIBDIR="$INTROSPECTION_TYPELIBDIR"
# NM is the only typelib we use that we don't jhbuild
PKG_CHECK_EXISTS([libnm-glib >= 0.8.999],
[NM_TYPELIBDIR=`$PKG_CONFIG --variable=libdir libnm-glib`/girepository-1.0
if test "$INTROSPECTION_TYPELIBDIR" != "$NM_TYPELIBDIR"; then
JHBUILD_TYPELIBDIR="$JHBUILD_TYPELIBDIR:$NM_TYPELIBDIR"
fi])
AC_SUBST(JHBUILD_TYPELIBDIR)
PKG_CHECK_MODULES(MUTTER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION
gio-unix-2.0 dbus-glib-1
gtk+-3.0 >= $GTK_MIN_VERSION
mutter-plugins >= $MUTTER_MIN_VERSION
gjs-internals-1.0 >= $GJS_MIN_VERSION
libgnome-menu $recorder_modules gconf-2.0
gdk-x11-3.0
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
libstartup-notification-1.0
gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
libcanberra)
saved_CFLAGS=$CFLAGS
saved_LIBS=$LIBS
CFLAGS=$GNOME_SHELL_CFLAGS
LIBS=$GNOME_SHELL_LIBS
AC_CHECK_FUNCS(JS_NewGlobalObject XFixesCreatePointerBarrier)
CFLAGS=$MUTTER_PLUGIN_CFLAGS
LIBS=$MUTTER_PLUGIN_LIBS
# sn_startup_sequence_get_application_id, we can replace with a version check later
AC_CHECK_FUNCS(JS_NewGlobalObject sn_startup_sequence_get_application_id)
CFLAGS=$saved_CFLAGS
LIBS=$saved_LIBS
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.2 gnome-desktop-3.0 >= 2.90.0 x11)
PKG_CHECK_MODULES(TIDY, clutter-1.0)
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 gnome-desktop-3.0 >= 2.90.0)
PKG_CHECK_MODULES(GDMUSER, dbus-glib-1 gtk+-3.0)
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 >= 0.1.7)
AC_MSG_CHECKING([for bluetooth support])
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
[BLUETOOTH_DIR=`$PKG_CONFIG --variable=applet_libdir gnome-bluetooth-1.0`
BLUETOOTH_LIBS=`$PKG_CONFIG --variable=applet_libs gnome-bluetooth-1.0`
AC_SUBST([BLUETOOTH_LIBS],["$BLUETOOTH_LIBS"])
AC_DEFINE_UNQUOTED([BLUETOOTH_DIR],["$BLUETOOTH_DIR"],[Path to installed GnomeBluetooth typelib and library])
AC_DEFINE([HAVE_BLUETOOTH],[1],[Define if you have libgnome-bluetooth-applet])
AC_SUBST([HAVE_BLUETOOTH],[1])
AC_MSG_RESULT([yes])],
[AC_DEFINE([HAVE_BLUETOOTH],[0])
AC_SUBST([HAVE_BLUETOOTH],[0])
AC_MSG_RESULT([no])])
PKG_CHECK_MODULES(JS_TEST, clutter-x11-1.0 gjs-1.0 gobject-introspection-1.0 gtk+-3.0)
PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION libedataserverui-3.0 >= $LIBEDATASERVERUI_MIN_VERSION gio-2.0)
AC_SUBST(CALENDAR_SERVER_CFLAGS)
AC_SUBST(CALENDAR_SERVER_LIBS)
MUTTER_GIR_DIR=`$PKG_CONFIG --variable=girdir libmutter`
MUTTER_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter`
AC_SUBST(MUTTER_GIR_DIR)
AC_SUBST(MUTTER_TYPELIB_DIR)
MUTTER_BIN_DIR=`$PKG_CONFIG --variable=exec_prefix mutter-plugins`/bin
# FIXME: metacity-plugins.pc should point directly to its .gir file
MUTTER_LIB_DIR=`$PKG_CONFIG --variable=libdir mutter-plugins`
MUTTER_PLUGIN_DIR=`$PKG_CONFIG --variable=plugindir mutter-plugins`
AC_SUBST(MUTTER_BIN_DIR)
AC_SUBST(MUTTER_LIB_DIR)
AC_SUBST(MUTTER_PLUGIN_DIR)
GJS_JS_DIR=`$PKG_CONFIG --variable=jsdir gjs-1.0`
GJS_JS_NATIVE_DIR=`$PKG_CONFIG --variable=jsnativedir gjs-1.0`
GJS_CONSOLE=`$PKG_CONFIG --variable=gjs_console gjs-1.0`
AC_SUBST(GJS_JS_DIR)
AC_SUBST(GJS_JS_NATIVE_DIR)
AC_SUBST(GJS_CONSOLE)
AC_CHECK_FUNCS(fdwalk)
@ -194,15 +154,21 @@ if test "$enable_compile_warnings" != no ; then
fi
changequote([,])dnl
AC_ARG_ENABLE(jhbuild-wrapper-script,
AS_HELP_STRING([--jhbuild-wrapper-script=yes],[Make "gnome-shell" script work for jhbuild]),,enable_jhbuild_wrapper_script=no)
AM_CONDITIONAL(USE_JHBUILD_WRAPPER_SCRIPT, test "x$enable_jhbuild_wrapper_script" = xyes)
AC_PATH_PROG(mutter, [mutter])
AC_SUBST(mutter)
AC_MSG_CHECKING([if mutter was compiled with GTK+-3.0])
if $PKG_CONFIG --libs libmutter-private | grep gtk-x11-3 >/dev/null; then
AC_MSG_RESULT(yes)
else
AC_MSG_RESULT(no)
AC_MSG_ERROR([GNOME Shell requires Mutter to be compiled against GTK+-3.0])
fi
AC_CONFIG_FILES([
Makefile
data/Makefile
js/Makefile
js/misc/config.js
src/Makefile
tests/Makefile
po/Makefile.in

View File

@ -1,5 +1,5 @@
desktopdir=$(datadir)/applications
desktop_DATA = gnome-shell.desktop
desktop_DATA = gnome-shell.desktop gnome-shell-clock-preferences.desktop
# We substitute in bindir so it works as an autostart
# file when built in a non-system prefix
@ -12,41 +12,49 @@ desktop_DATA = gnome-shell.desktop
%.desktop:%.desktop.in
$(AM_V_GEN) sed s/^_// < $< > $@ || rm $@
searchprovidersdir = $(pkgdatadir)/search_providers
dist_searchproviders_DATA = \
search_providers/google.xml \
search_providers/wikipedia.xml
dist_pkgdata_DATA = clock-preferences.ui
imagesdir = $(pkgdatadir)/images
dist_images_DATA = \
close-black.svg \
magnifier.svg
themedir = $(pkgdatadir)/theme
dist_theme_DATA = \
theme/calendar-arrow-left.svg \
theme/calendar-arrow-right.svg \
theme/calendar-today.svg \
theme/add-workspace.svg \
theme/close-window.svg \
theme/close.svg \
theme/corner-ripple-ltr.png \
theme/corner-ripple-rtl.png \
theme/corner-ripple.png \
theme/dash-placeholder.svg \
theme/filter-selected-ltr.svg \
theme/filter-selected-rtl.svg \
theme/dialog-error.svg \
theme/gnome-shell.css \
theme/panel-border.svg \
theme/panel-button-border.svg \
theme/panel-button-highlight-narrow.svg \
theme/panel-button-highlight-wide.svg \
theme/process-working.svg \
theme/mosaic-view-active.svg \
theme/mosaic-view.svg \
theme/move-window-on-new.svg \
theme/process-working.png \
theme/remove-workspace.svg \
theme/running-indicator.svg \
theme/scroll-button-down-hover.png \
theme/scroll-button-down.png \
theme/scroll-button-up-hover.png \
theme/scroll-button-up.png \
theme/scroll-hhandle.svg \
theme/scroll-vhandle.svg \
theme/source-button-border.svg \
theme/section-more.svg \
theme/section-more-open.svg \
theme/separator-white.png \
theme/single-view-active.svg \
theme/single-view.svg \
theme/toggle-off-us.svg \
theme/toggle-off-intl.svg \
theme/toggle-on-us.svg \
theme/toggle-on-intl.svg \
theme/ws-switch-arrow-up.svg \
theme/ws-switch-arrow-down.svg
theme/ws-switch-arrow-left.svg \
theme/ws-switch-arrow-right.svg
gsettings_SCHEMAS = org.gnome.shell.gschema.xml
gsettings_SCHEMAS = \
org.gnome.accessibility.magnifier.gschema.xml \
org.gnome.shell.gschema.xml
@INTLTOOL_XML_NOMERGE_RULE@
@GSETTINGS_RULES@
@ -63,6 +71,11 @@ all-local: gschemas.compiled
gconfschemadir = @GCONF_SCHEMA_FILE_DIR@
gconfschema_DATA = gnome-shell.schemas
menudir = $(sysconfdir)/xdg/menus
menu_DATA = \
gs-applications.menu
shadersdir = $(pkgdatadir)/shaders
shaders_DATA = \
shaders/dim-window.glsl
@ -74,13 +87,16 @@ install-data-local:
EXTRA_DIST = \
gnome-shell.desktop.in.in \
gnome-shell-clock-preferences.desktop.in.in \
$(menu_DATA) \
$(gconfschema_DATA) \
$(shaders_DATA) \
org.gnome.accessibility.magnifier.gschema.xml.in \
org.gnome.shell.gschema.xml.in
CLEANFILES = \
gnome-shell.desktop.in \
gnome-shell-clock-preferences.desktop.in \
$(desktop_DATA) \
$(gsettings_SCHEMAS) \
gschemas.compiled

188
data/clock-preferences.ui Normal file
View File

@ -0,0 +1,188 @@
<?xml version="1.0"?>
<interface domain="gnome-shell">
<requires lib="gtk+" version="2.16"/>
<!-- interface-naming-policy project-wide -->
<object class="GtkDialog" id="prefs-dialog">
<property name="border_width">5</property>
<property name="title" translatable="yes">Clock Preferences</property>
<property name="window_position">center</property>
<property name="type_hint">normal</property>
<property name="has_separator">False</property>
<child internal-child="vbox">
<object class="GtkVBox" id="dialog-vbox1">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">2</property>
<child>
<object class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">18</property>
<child>
<object class="GtkFrame" id="frame1">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
<property name="top_padding">6</property>
<property name="left_padding">12</property>
<property name="right_padding">6</property>
<child>
<object class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<property name="spacing">12</property>
<child>
<object class="GtkRadioButton" id="12hr_radio">
<property name="label" translatable="yes">_12 hour format</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="24hr_radio">
<property name="label" translatable="yes">_24 hour format</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<property name="group">12hr_radio</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</child>
<child type="label">
<object class="GtkLabel" id="label_format">
<property name="visible">True</property>
<property name="label" translatable="yes">Clock Format</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkFrame" id="frame2">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkAlignment" id="alignment2">
<property name="visible">True</property>
<property name="top_padding">6</property>
<property name="left_padding">12</property>
<child>
<object class="GtkVBox" id="vbox2">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkCheckButton" id="date_check">
<property name="label" translatable="yes">Show the _date</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="seconds_check">
<property name="label" translatable="yes">Show seco_nds</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</child>
<child type="label">
<object class="GtkLabel" id="label_display">
<property name="visible">True</property>
<property name="label" translatable="yes">Panel Display</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="padding">6</property>
<property name="position">1</property>
</packing>
</child>
<child internal-child="action_area">
<object class="GtkHButtonBox" id="dialog-action_area1">
<property name="visible">True</property>
<property name="layout_style">end</property>
<child>
<placeholder/>
</child>
<child>
<object class="GtkButton" id="prefs_close_button">
<property name="label">gtk-close</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="0">prefs_close_button</action-widget>
</action-widgets>
</object>
</interface>

66
data/close-black.svg Normal file
View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
<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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Foreground"
x="0px"
y="0px"
width="16px"
height="16px"
viewBox="0 0 16 16"
enable-background="new 0 0 16 16"
xml:space="preserve"
sodipodi:version="0.32"
inkscape:version="0.46+devel"
sodipodi:docname="close-black.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
id="metadata2399"><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></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs2397"><linearGradient
id="linearGradient3173"><stop
style="stop-color:#c4c4c4;stop-opacity:1;"
offset="0"
id="stop3175" /><stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="1"
id="stop3177" /></linearGradient><inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 8 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="16 : 8 : 1"
inkscape:persp3d-origin="8 : 5.3333333 : 1"
id="perspective2401" /></defs><sodipodi:namedview
inkscape:window-height="811"
inkscape:window-width="1272"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
guidetolerance="10.0"
gridtolerance="10.0"
objecttolerance="10.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base"
showgrid="false"
inkscape:zoom="32.125"
inkscape:cx="8"
inkscape:cy="10.440056"
inkscape:window-x="40"
inkscape:window-y="40"
inkscape:current-layer="Foreground" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M10.5,3.5l2,2L10,8l2.5,2.5l-2,2L8,10l-2.5,2.5l-2-2L6,8L3.5,5.5l2-2L8,6L10.5,3.5 z M0,8c0-4.418,3.582-8,8-8s8,3.582,8,8s-3.582,8-8,8S0,12.418,0,8z"
id="path2394"
style="fill-opacity:1;fill:#545454" />
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,15 @@
[Desktop Entry]
_Name=Clock
_Comment=Customize the panel clock
Exec=@bindir@/gnome-shell-clock-preferences
Icon=gnome-panel-clock
Terminal=false
Type=Application
StartupNotify=true
Categories=GNOME;GTK;Settings;DesktopSettings;
OnlyShowIn=GNOME;
X-GNOME-ShellOnly=true
X-GNOME-Bugzilla-Bugzilla=GNOME
X-GNOME-Bugzilla-Product=gnome-shell
X-GNOME-Bugzilla-Component=general
X-GNOME-Bugzilla-Version=@VERSION@

View File

@ -7,10 +7,9 @@ X-GNOME-Bugzilla-Bugzilla=GNOME
X-GNOME-Bugzilla-Product=gnome-shell
X-GNOME-Bugzilla-Component=general
X-GNOME-Bugzilla-Version=@VERSION@
Categories=GNOME;GTK;Core;
Categories=GNOME;GTK;Utility;Core;
OnlyShowIn=GNOME;
NoDisplay=true
X-GNOME-Autostart-Phase=WindowManager
X-GNOME-Provides=panel;windowmanager;
X-GNOME-Autostart-Notify=true
X-GNOME-AutoRestart=true

View File

@ -22,7 +22,7 @@
<applyto>/desktop/gnome/shell/windows/button_layout</applyto>
<owner>gnome-shell</owner>
<type>string</type>
<default>:close</default>
<default>:minimize,maximize,close</default>
<locale name="C">
<short>Arrangement of buttons on the titlebar</short>
<long>
@ -44,20 +44,19 @@
</schema>
<schema>
<key>/schemas/desktop/gnome/shell/windows/edge_tiling</key>
<applyto>/desktop/gnome/shell/windows/edge_tiling</applyto>
<key>/schemas/desktop/gnome/shell/windows/side_by_side_tiling</key>
<applyto>/desktop/gnome/shell/windows/side_by_side_tiling</applyto>
<owner>gnome-shell</owner>
<type>bool</type>
<default>true</default>
<locale name="C">
<short>enable edge tiling when dropping windows on screen edges</short>
<short>enable side-by-side tiling when dropping windows on screen edges</short>
<long>
If enabled, dropping windows on vertical screen edges maximizes them
If enabled, dropping windows on screen edges maximizes them
vertically and resizes them horizontally to cover half of the
available area. Dropping windows on the top screen edge maximizes them
completely.
available area.
This key overrides /apps/metacity/general/edge_tiling when
This key overrides /apps/metacity/general/side_by_side_tiling when
running GNOME Shell.
</long>
</locale>
@ -81,20 +80,5 @@
</locale>
</schema>
<schema>
<key>/schemas/desktop/gnome/shell/windows/workspaces_only_on_primary</key>
<applyto>/desktop/gnome/shell/windows/workspaces_only_on_primary</applyto>
<owner>gnome-shell</owner>
<type>bool</type>
<default>true</default>
<locale name="C">
<short>Workspaces only on primary monitor</short>
<long>
This key overrides /apps/mutter/general/workspaces_only_on_primary when
running GNOME Shell.
</long>
</locale>
</schema>
</schemalist>
</gconfschemafile>

45
data/gs-applications.menu Normal file
View File

@ -0,0 +1,45 @@
<Menu>
<DefaultLayout>
<Menuname>Apps</Menuname>
<Menuname>Games</Menuname>
<Menuname>Tools</Menuname>
</DefaultLayout>
<Name>Applications</Name>
<AppDir>/usr/local/share/applications</AppDir>
<DefaultAppDirs/>
<Menu>
<Name>Games</Name>
<Include>
<And>
<Category>Game</Category>
</And>
</Include>
</Menu>
<Menu>
<Name>Tools</Name>
<Include>
<Category>Development</Category>
<And>
<Category>System</Category>
<Not>
<Category>Settings</Category>
</Not>
</And>
<Category>Utility</Category>
</Include>
</Menu>
<Menu>
<Name>Apps</Name>
<OnlyUnallocated/>
<Include>
<And>
<Or>
<Category>Documentation</Category>
<Not><Category>Core</Category></Not>
</Or>
<Not><Category>Settings</Category></Not>
<Not><Category>Screensaver</Category></Not>
</And>
</Include>
</Menu>
</Menu>

80
data/magnifier.svg Normal file
View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
<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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.0"
id="Foreground"
x="0px"
y="0px"
width="18"
height="18"
viewBox="0 0 18 18"
enable-background="new 0 0 29 18"
xml:space="preserve"
sodipodi:version="0.32"
inkscape:version="0.46+devel"
sodipodi:docname="magnifier.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
id="metadata16"><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><defs
id="defs14"><inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 9 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="29 : 9 : 1"
inkscape:persp3d-origin="14.5 : 6 : 1"
id="perspective18" /></defs><sodipodi:namedview
inkscape:window-height="728"
inkscape:window-width="1103"
inkscape:pageshadow="2"
inkscape:pageopacity="1"
guidetolerance="10.0"
gridtolerance="10.0"
objecttolerance="10.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#000000"
id="base"
showgrid="true"
inkscape:zoom="27.260185"
inkscape:cx="9.5844061"
inkscape:cy="9.4435574"
inkscape:window-x="142"
inkscape:window-y="26"
inkscape:current-layer="Foreground"
inkscape:snap-global="true"
showguides="false"><inkscape:grid
type="xygrid"
id="grid2391"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" /></sodipodi:namedview>
<g
id="g5"
style="fill:#ffffff;fill-opacity:1"
transform="translate(-4,-0.023114)">
<path
d="m 6.246,13.98 c -0.319,-0.319 -0.319,-0.837 0,-1.157 L 9.963,9.106 c 0.319,-0.319 0.837,-0.319 1.157,0 l 0.786,0.787 c 0.32,0.319 0.32,0.837 0,1.157 l -3.717,3.717 c -0.32,0.319 -0.838,0.319 -1.157,0 l -0.786,-0.787 0,0 z"
id="path7"
style="fill:#ffffff;fill-opacity:1" />
<path
d="M 9.076,11.937"
id="path9"
style="fill:#ffffff;fill-opacity:1" />
</g>
<path
clip-rule="evenodd"
d="m 7.25,7.476886 c 0,-1.243 1.007,-2.25 2.2499998,-2.25 1.2430002,0 2.2500002,1.007 2.2500002,2.25 0,1.243 -1.007,2.25 -2.2500002,2.25 C 8.257,9.726886 7.25,8.719886 7.25,7.476886 z m -2.25,0 c 0,-2.485 2.015,-4.5 4.4999998,-4.5 2.4850002,0 4.5000002,2.015 4.5000002,4.5 0,2.4849998 -2.015,4.5 -4.5000002,4.5 C 7.015,11.976886 5,9.9618858 5,7.476886 z"
id="path11"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd" />
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -0,0 +1,133 @@
<schemalist>
<enum id="MouseTrackingMode">
<value nick="none" value="0"/>
<value nick="centered" value="1"/>
<value nick="proportional" value="2"/>
<value nick="push" value="3"/>
</enum>
<enum id="ScreenPosition">
<value nick="none" value="0"/>
<value nick="full-screen" value="1"/>
<value nick="top-half" value="2"/>
<value nick="bottom-half" value="3"/>
<value nick="left-half" value="4"/>
<value nick="right-half" value="5"/>
</enum>
<schema id="org.gnome.accessibility.magnifier"
path="/desktop/gnome/accessibility/magnifier/"
gettext-domain="@GETTEXT_PACKAGE@">
<key name="show-magnifier" type="b">
<default>false</default>
<_summary>Show or hide the magnifier</_summary>
<_description>
Show or hide the magnifier and all of its zoom regions.
</_description>
</key>
<key name="mouse-tracking" enum="MouseTrackingMode">
<default>'proportional'</default>
<_summary>Mouse Tracking Mode</_summary>
<_description>
Determines the position of the magnified mouse image within the
magnified view and how it reacts to system mouse movement. The values
are
- none: no mouse tracking;
- centered: the mouse image is
displayed at the center of the zoom region (which also represents
the point under the system mouse) and the magnified contents are
scrolled as the system mouse moves;
- proportional: the position of the magnified mouse in the zoom region
is proportionally the same as the position of the system mouse on screen;
- push: when the magnified mouse intersects a boundary of the zoom
region, the contents are scrolled into view.
</_description>
</key>
<key name="screen-position" enum="ScreenPosition">
<default>'full-screen'</default>
<_summary>Screen position</_summary>
<_description>
The magnified view either fills the entire screen, or occupies the
top-half, bottom-half, left-half, or right-half of the screen.
</_description>
</key>
<key name="mag-factor" type="d">
<default>2.0</default>
<_summary>Magnification factor</_summary>
<_description>
The power of the magnification. A value of 1.0 means no magnification.
A value of 2.0 doubles the size.
</_description>
</key>
<key name="lens-mode" type="b">
<default>false</default>
<_summary>Enable lens mode</_summary>
<_description>
Whether the magnified view should be centered over the location of
the system mouse and move with it.
</_description>
</key>
<key name="scroll-at-edges" type="b">
<default>false</default>
<_summary>
Scroll magnified contents beyond the edges of the desktop
</_summary>
<_description>
For centered mouse tracking, when the system pointer is at or near the
edge of the screen, the magnified contents continue to scroll such that
the screen edge moves into the magnified view.
</_description>
</key>
<!-- Cross-hairs -->
<key name="show-cross-hairs" type="b">
<default>false</default>
<_summary>Show or hide crosshairs</_summary>
<_description>
Enables/disables display of crosshairs centered on the magnified
mouse sprite.
</_description>
</key>
<key name="cross-hairs-thickness" type="i">
<default>8</default>
<_summary>Thickness of the crosshairs</_summary>
<_description>
Width of the vertical and horizontal lines that make up the crosshairs.
</_description>
</key>
<key name="cross-hairs-color" type="s">
<default>'#ff0000'</default>
<_summary>Color of the crosshairs</_summary>
<_description>
The color of the the vertical and horizontal lines that make up
the crosshairs.
</_description>
</key>
<key name="cross-hairs-opacity" type="i">
<default>169</default>
<_summary>Opacity of the crosshairs</_summary>
<_description>
Determines the transparency of the crosshairs, from fully opaque
to fully transparent.
</_description>
</key>
<key name="cross-hairs-length" type="i">
<default>4096</default>
<_summary>Length of the crosshairs</_summary>
<_description>
Determines the length of the vertical and horizontal lines that
make up the crosshairs.
</_description>
</key>
<key name="cross-hairs-clip" type="b">
<default>false</default>
<_summary>Clip the crosshairs at the center</_summary>
<_description>
Determines whether the crosshairs intersect the magnified mouse sprite,
or are clipped such that the ends of the horizontal and vertical lines
surround the mouse image.
</_description>
</key>
</schema>
</schemalist>

View File

@ -1,5 +1,5 @@
<schemalist>
<schema id="org.gnome.shell" path="/org/gnome/shell/"
<schema id="org.gnome.shell" path="/apps/gnome-shell/"
gettext-domain="@GETTEXT_PACKAGE@">
<key name="development-tools" type="b">
<default>true</default>
@ -15,18 +15,8 @@
<default>[]</default>
<_summary>Uuids of extensions to disable</_summary>
<_description>
GNOME Shell extensions have a uuid property; this key lists extensions
which should not be loaded. This setting overrides enabled-extensions
for extensions that appear in both lists.
</_description>
</key>
<key name="enabled-extensions" type="as">
<default>[]</default>
<_summary>Uuids of extensions to enable</_summary>
<_description>
GNOME Shell extensions have a uuid property; this key lists extensions
which should be loaded. disabled-extensions overrides this setting for
extensions that appear in both lists.
GNOME Shell extensions have a uuid property;
this key lists extensions which should not be loaded.
</_description>
</key>
<key name="enable-app-monitoring" type="b">
@ -40,31 +30,23 @@
</_description>
</key>
<key name="favorite-apps" type="as">
<default>[ 'mozilla-firefox.desktop', 'evolution.desktop', 'empathy.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'openoffice.org-writer.desktop', 'nautilus.desktop' ]</default>
<default>[ 'mozilla-firefox.desktop', 'evolution.desktop', 'openoffice.org-writer.desktop' ]</default>
<_summary>List of desktop file IDs for favorite applications</_summary>
<_description>
The applications corresponding to these identifiers
will be displayed in the favorites area.
</_description>
</key>
<key name="disabled-open-search-providers" type="as">
<default>[]</default>
<_summary>disabled OpenSearch providers</_summary>
</key>
<key name="command-history" type="as">
<default>[]</default>
<_summary>History for command (Alt-F2) dialog</_summary>
</key>
<key name="looking-glass-history" type="as">
<default>[]</default>
<_summary>History for the looking glass dialog</_summary>
</key>
<child name="clock" schema="org.gnome.shell.clock"/>
<child name="calendar" schema="org.gnome.shell.calendar"/>
<child name="recorder" schema="org.gnome.shell.recorder"/>
</schema>
<schema id="org.gnome.shell.calendar" path="/org/gnome/shell/calendar/"
<schema id="org.gnome.shell.calendar" path="/apps/gnome-shell/calendar/"
gettext-domain="@GETTEXT_PACKAGE@">
<key name="show-weekdate" type="b">
<default>false</default>
@ -75,25 +57,58 @@
</key>
</schema>
<schema id="org.gnome.shell.clock" path="/org/gnome/shell/clock/"
<schema id="org.gnome.shell.clock" path="/apps/gnome-shell/clock/"
gettext-domain="@GETTEXT_PACKAGE@">
<key name="format" type="s">
<default l10n="messages" context="hour_format">
<!-- TRANSLATORS: This is the default hour format, choose ONLY '12-hour' or '24-hour'. -->
"12-hour"
</default>
<_summary>Hour format</_summary>
<_description>
This key specifies the hour format used by the panel clock.
Possible values are "12-hour", "24-hour", "unix" and "custom". If set
to "unix", the clock will display time in seconds since Epoch,
i.e. 1970-01-01. If set to "custom", the clock will display time
according to the format specified in the custom_format key. Note that
if set to either "unix" or "custom", the show_date and show_seconds
keys are ignored.
</_description>
<choices>
<choice value="12-hour"/>
<choice value="24-hour"/>
<choice value="unix"/>
<choice value="custom"/>
</choices>
</key>
<key name="custom-format" type="s">
<default>''</default>
<_summary>Custom format of the clock</_summary>
<_description>
This key specifies the format used by the panel clock when the format
key is set to "custom". You can use conversion specifiers understood
by strftime() to obtain a specific format. See the strftime() manual
for more information.
</_description>
</key>
<key name="show-seconds" type="b">
<default>false</default>
<_summary>Show time with seconds</_summary>
<_description>
If true, display seconds in time.
If true and format is either "12-hour" or "24-hour", display seconds in time.
</_description>
</key>
<key name="show-date" type="b">
<default>false</default>
<_summary>Show date in clock</_summary>
<_description>
If true, display date in the clock, in addition to time.
If true and format is either "12-hour" or "24-hour",
display date in the clock, in addition to time.
</_description>
</key>
</schema>
<schema id="org.gnome.shell.recorder" path="/org/gnome/shell/recorder/"
<schema id="org.gnome.shell.recorder" path="/apps/gnome-shell/recorder/"
gettext-domain="@GETTEXT_PACKAGE@">
<key name="framerate" type="i">
<default>15</default>
@ -115,13 +130,11 @@
take care of its own output - this might be used to send the output
to an icecast server via shout2send or similar. When unset or set
to an empty value, the default pipeline will be used. This is currently
'videorate ! vp8enc quality=10 speed=2 threads=%T ! queue ! webmmux'
and records to WEBM using the VP8 codec. %T is used as a placeholder
for a guess at the optimal thread count on the system.
'videorate ! theoraenc ! oggmux' and records to Ogg Theora.
</_description>
</key>
<key name="file-extension" type="s">
<default>'webm'</default>
<default>'ogv'</default>
<_summary>File extension used for storing the screencast</_summary>
<_description>
The filename for recorded screencasts will be a unique filename

View File

@ -1,7 +0,0 @@
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
<ShortName>Google</ShortName>
<Description>Google Search</Description>
<InputEncoding>UTF-8</InputEncoding>
<Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAAAAEAGABoAwAAFgAAACgAAAAQAAAAIAAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADs9Pt8xetPtu9FsfFNtu%2BTzvb2%2B%2Fne4dFJeBw0egA%2FfAJAfAA8ewBBegAAAAD%2B%2FPtft98Mp%2BwWsfAVsvEbs%2FQeqvF8xO7%2F%2F%2F63yqkxdgM7gwE%2FggM%2BfQA%2BegBDeQDe7PIbotgQufcMufEPtfIPsvAbs%2FQvq%2Bfz%2Bf%2F%2B%2B%2FZKhR05hgBBhQI8hgBAgAI9ewD0%2B%2Fg3pswAtO8Cxf4Kw%2FsJvvYAqupKsNv%2B%2Fv7%2F%2FP5VkSU0iQA7jQA9hgBDgQU%2BfQH%2F%2Ff%2FQ6fM4sM4KsN8AteMCruIqqdbZ7PH8%2Fv%2Fg6Nc%2Fhg05kAA8jAM9iQI%2BhQA%2BgQDQu6b97uv%2F%2F%2F7V8Pqw3eiWz97q8%2Ff%2F%2F%2F%2F7%2FPptpkkqjQE4kwA7kAA5iwI8iAA8hQCOSSKdXjiyflbAkG7u2s%2F%2B%2F%2F39%2F%2F7r8utrqEYtjQE8lgA7kwA7kwA9jwA9igA9hACiWSekVRyeSgiYSBHx6N%2F%2B%2Fv7k7OFRmiYtlAA5lwI7lwI4lAA7kgI9jwE9iwI4iQCoVhWcTxCmb0K%2BooT8%2Fv%2F7%2F%2F%2FJ2r8fdwI1mwA3mQA3mgA8lAE8lAE4jwA9iwE%2BhwGfXifWvqz%2B%2Ff%2F58u%2Fev6Dt4tr%2B%2F%2F2ZuIUsggA7mgM6mAM3lgA5lgA6kQE%2FkwBChwHt4dv%2F%2F%2F728ei1bCi7VAC5XQ7kz7n%2F%2F%2F6bsZkgcB03lQA9lgM7kwA2iQktZToPK4r9%2F%2F%2F9%2F%2F%2FSqYK5UwDKZAS9WALIkFn%2B%2F%2F3%2F%2BP8oKccGGcIRJrERILYFEMwAAuEAAdX%2F%2Ff7%2F%2FP%2B%2BfDvGXQLIZgLEWgLOjlf7%2F%2F%2F%2F%2F%2F9QU90EAPQAAf8DAP0AAfMAAOUDAtr%2F%2F%2F%2F7%2B%2Fu2bCTIYwDPZgDBWQDSr4P%2F%2Fv%2F%2F%2FP5GRuABAPkAA%2FwBAfkDAPAAAesAAN%2F%2F%2B%2Fz%2F%2F%2F64g1C5VwDMYwK8Yg7y5tz8%2Fv%2FV1PYKDOcAAP0DAf4AAf0AAfYEAOwAAuAAAAD%2F%2FPvi28ymXyChTATRrIb8%2F%2F3v8fk6P8MAAdUCAvoAAP0CAP0AAfYAAO4AAACAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAA</Image>
<Url type="text/html" method="GET" template="http://www.google.com/search?q={searchTerms}"/>
</OpenSearchDescription>

View File

@ -1,44 +0,0 @@
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
<ShortName>Wikipedia</ShortName>
<Description>Wikipedia, the free encyclopedia</Description>
<InputEncoding>UTF-8</InputEncoding>
<Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAQAAEABAAoAQAAFgAAACgAAAAQAAAAIAAAAAEABAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAEAgQAhIOEAMjHyABIR0gA6ejpAGlqaQCpqKkAKCgoAPz9%2FAAZGBkAmJiYANjZ2ABXWFcAent6ALm6uQA8OjwAiIiIiIiIiIiIiI4oiL6IiIiIgzuIV4iIiIhndo53KIiIiB%2FWvXoYiIiIfEZfWBSIiIEGi%2FfoqoiIgzuL84i9iIjpGIoMiEHoiMkos3FojmiLlUipYliEWIF%2BiDe0GoRa7D6GPbjcu1yIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</Image>
<Url type="text/html" method="GET" template="http://{language}.wikipedia.org/wiki/Special:Search?search={searchTerms}"/>
<!-- The criterion for being below is being listed with more than 100,000
articles on http://meta.wikimedia.org/wiki/List_of_Wikipedias -->
<Language>ar</Language>
<Language>bg</Language>
<Language>ca</Language>
<Language>cs</Language>
<Language>da</Language>
<Language>de</Language>
<Language>en</Language>
<Language>eo</Language>
<Language>es</Language>
<Language>fa</Language>
<Language>fi</Language>
<Language>fr</Language>
<Language>he</Language>
<Language>hu</Language>
<Language>id</Language>
<Language>it</Language>
<Language>ja</Language>
<Language>ko</Language>
<Language>lt</Language>
<Language>nl</Language>
<Language>no</Language>
<Language>pl</Language>
<Language>pt</Language>
<Language>ro</Language>
<Language>ru</Language>
<Language>sk</Language>
<Language>sl</Language>
<Language>sr</Language>
<Language>sv</Language>
<Language>tr</Language>
<Language>uk</Language>
<Language>vi</Language>
<Language>vo</Language>
<Language>war</Language>
<Language>zh</Language>
</OpenSearchDescription>

View File

@ -0,0 +1,98 @@
<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="23"
height="15"
id="svg6375"
version="1.1"
inkscape:version="0.47pre4 r22446"
sodipodi:docname="New document 13">
<defs
id="defs6377">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 16 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="32 : 16 : 1"
inkscape:persp3d-origin="16 : 10.666667 : 1"
id="perspective6383" />
<inkscape:perspective
id="perspective6366"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="11.197802"
inkscape:cx="16"
inkscape:cy="16"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="1680"
inkscape:window-height="997"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:window-maximized="1" />
<metadata
id="metadata6380">
<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></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer"
transform="translate(0,-17)">
<g
style="display:inline"
id="g6243"
transform="translate(-986.28859,-658.2796)">
<rect
style="fill:#000000;fill-opacity:0.98770495;stroke:#666666;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
id="rect5318"
width="22"
height="14"
x="986.89801"
y="675.86743"
rx="0.49999979"
ry="0.5" />
<g
id="g5320"
transform="translate(402.77304,-12.882544)">
<path
id="path5322"
d="m 595.125,692.53048 0,6.43903"
style="fill:none;stroke:#666666;stroke-width:1.99999952;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<path
id="path5324"
d="m 598.34451,695.75 -6.43902,0"
style="fill:none;stroke:#666666;stroke-width:1.99999952;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -1,187 +0,0 @@
<?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="28"
height="25"
id="svg10621"
version="1.1"
inkscape:version="0.48.1 r9760"
sodipodi:docname="calendar-today.svg">
<defs
id="defs10623">
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient34508-1-3"
id="radialGradient99561-1"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
cx="51"
cy="30"
fx="51"
fy="30"
r="42" />
<linearGradient
inkscape:collect="always"
id="linearGradient34508-1-3">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop34510-1-9" />
<stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop34512-4-5" />
</linearGradient>
<radialGradient
r="42"
fy="30"
fx="51"
cy="30"
cx="51"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
gradientUnits="userSpaceOnUse"
id="radialGradient10592"
xlink:href="#linearGradient34508-1-3"
inkscape:collect="always" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient34508-1-3"
id="radialGradient3770"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
cx="51"
cy="30"
fx="51"
fy="30"
r="42" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient34508-1-3"
id="radialGradient3001"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
cx="51"
cy="30"
fx="51"
fy="30"
r="42" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient34508-1-3"
id="radialGradient3007"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
cx="51"
cy="30"
fx="51"
fy="30"
r="42" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient34508-1-3"
id="radialGradient3067"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
cx="51"
cy="30"
fx="51"
fy="30"
r="42" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient34508-1-3"
id="radialGradient3072"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
cx="51"
cy="30"
fx="51"
fy="30"
r="42" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient34508-1-3"
id="radialGradient2997"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
cx="51"
cy="30"
fx="51"
fy="30"
r="42" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#000000"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="15.839192"
inkscape:cx="8.3750933"
inkscape:cy="8.0837211"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="1440"
inkscape:window-height="843"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:window-maximized="1" />
<metadata
id="metadata10626">
<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(-469.08263,-536.99307)">
<g
id="g3003">
<path
inkscape:export-ydpi="90"
inkscape:export-xdpi="90"
inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/motion/textures/panel.png"
transform="matrix(0.43692393,0,0,1.3783114,460.60467,517.48289)"
sodipodi:end="6.2831853"
sodipodi:start="3.1415927"
d="M 9,29.999999 C 9.0000011,21.163443 27.804042,14 51.000002,14 74.195961,14 93,21.163444 93,30 l -42,0 z"
sodipodi:ry="16"
sodipodi:rx="42"
sodipodi:cy="30"
sodipodi:cx="51"
id="path34506-3"
style="opacity:0.4625;color:#000000;fill:url(#radialGradient2997);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
sodipodi:type="arc" />
<rect
y="558.85046"
x="468.96878"
height="3.1425927"
width="28.149134"
id="rect2996"
style="fill:#ffffff;fill-opacity:0.50196078;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.7 KiB

View File

@ -1,26 +1,24 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
<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"
version="1.0"
id="Foreground"
x="0px"
y="0px"
width="32"
height="32"
viewBox="0 0 23.272727 23.272727"
width="22"
height="22"
viewBox="0 0 16 16"
enable-background="new 0 0 16 16"
xml:space="preserve"
sodipodi:version="0.32"
inkscape:version="0.48+devel r10081 custom"
inkscape:version="0.46"
sodipodi:docname="close-window.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
id="metadata2399"><rdf:RDF><cc:Work
@ -39,49 +37,11 @@
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="16 : 8 : 1"
inkscape:persp3d-origin="8 : 5.3333333 : 1"
id="perspective2401" /><filter
color-interpolation-filters="sRGB"
inkscape:collect="always"
id="filter16494-4"
x="-0.20989846"
width="1.4197969"
y="-0.20903821"
height="1.4180764"><feGaussianBlur
inkscape:collect="always"
stdDeviation="1.3282637"
id="feGaussianBlur16496-8" /></filter><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient16498-6"
id="radialGradient16504-1"
cx="7.6582627"
cy="5.8191104"
fx="7.6582627"
fy="5.8191104"
r="8.6928644"
gradientTransform="matrix(1.0474339,0,0,1.0517402,-0.3632615,-0.42032492)"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
id="linearGradient16498-6"><stop
style="stop-color:#7b7b7b;stop-opacity:1"
offset="0"
id="stop16500-8" /><stop
style="stop-color:#101010;stop-opacity:1"
offset="1"
id="stop16502-0" /></linearGradient><filter
color-interpolation-filters="sRGB"
inkscape:collect="always"
id="filter16524-9"
x="-0.212979"
width="1.425958"
y="-0.21305652"
height="1.426113"><feGaussianBlur
inkscape:collect="always"
stdDeviation="0.71020915"
id="feGaussianBlur16526-0" /></filter></defs><sodipodi:namedview
inkscape:window-height="1114"
inkscape:window-width="1463"
id="perspective2401" /></defs><sodipodi:namedview
inkscape:window-height="999"
inkscape:window-width="1680"
inkscape:pageshadow="2"
inkscape:pageopacity="0"
inkscape:pageopacity="1"
guidetolerance="10.0"
gridtolerance="10.0"
objecttolerance="10.0"
@ -90,63 +50,27 @@
pagecolor="#000000"
id="base"
showgrid="false"
inkscape:zoom="1"
inkscape:cx="10.720189"
inkscape:cy="13.739577"
inkscape:zoom="25.648691"
inkscape:cx="8.8097603"
inkscape:cy="9.0472789"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:current-layer="Foreground"
showguides="true"
inkscape:guide-bbox="true"
borderlayer="true"
inkscape:showpageshadow="false"
inkscape:window-maximized="0"><inkscape:grid
type="xygrid"
id="grid11246"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" /></sodipodi:namedview>
inkscape:guide-bbox="true" />
<g
style="display:inline"
id="g16402-8"
transform="translate(4.7533483,2.8238929)"><g
id="g3175-4"><path
sodipodi:type="inkscape:offset"
inkscape:radius="0"
inkscape:original="M 7.65625 0.125 C 3.2589349 0.125 -0.3125 3.7070002 -0.3125 8.125 C -0.3125 12.543001 3.2589349 16.125 7.65625 16.125 C 12.053566 16.125 15.625 12.543001 15.625 8.125 C 15.625 3.7070002 12.053566 0.125 7.65625 0.125 z "
xlink:href="#path2394-32"
style="opacity:0.52994014;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.18181825;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter16494-4);enable-background:accumulate"
id="path16480-5"
inkscape:href="#path2394-32"
d="m 7.65625,0.125 c -4.3973151,0 -7.96875,3.5820002 -7.96875,8 0,4.418001 3.5714349,8 7.96875,8 4.397316,0 7.96875,-3.581999 7.96875,-8 0,-4.4179998 -3.571434,-8 -7.96875,-8 z"
transform="translate(0,1.028519)" /><path
clip-rule="evenodd"
d="m -0.30428257,8.1237596 c 0,-4.4179998 3.56522987,-7.9999996 7.96254497,-7.9999996 4.3973156,0 7.9625456,3.5819998 7.9625456,7.9999996 0,4.4180014 -3.56523,8.0000004 -7.9625456,8.0000004 -4.3973151,0 -7.96254497,-3.581999 -7.96254497,-8.0000004 z"
id="path2394-32"
style="color:#000000;fill:url(#radialGradient16504-1);fill-opacity:1;fill-rule:nonzero;stroke:#eeeeec;stroke-width:1.4545455;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
sodipodi:nodetypes="csssc"
inkscape:connector-curvature="0" /><g
id="g3172-6" /></g><g
transform="matrix(0.72727273,0,0,0.72727273,2.368236,2.1803254)"
style="fill:#ffffff;fill-opacity:1;display:inline"
id="g27275-6-6"
inkscape:label="window-close"><g
style="fill:#ffffff;fill-opacity:1;display:inline"
id="g27277-1-1"
transform="translate(-41,-760)"><path
sodipodi:type="inkscape:offset"
inkscape:radius="0"
inkscape:original="M 44.21875 764.1875 L 44.21875 765.1875 C 44.19684 765.46825 44.289258 765.74287 44.5 765.9375 L 46.78125 768.21875 L 44.5 770.46875 C 44.31181 770.65692 44.218747 770.92221 44.21875 771.1875 L 44.21875 772.1875 L 45.21875 772.1875 C 45.48404 772.1875 45.749336 772.09444 45.9375 771.90625 L 48.21875 769.625 L 50.5 771.90625 C 50.688164 772.0944 50.953449 772.18749 51.21875 772.1875 L 52.21875 772.1875 L 52.21875 771.1875 C 52.218742 770.9222 52.125688 770.65692 51.9375 770.46875 L 49.6875 768.21875 L 51.96875 765.9375 C 52.18441 765.73815 52.21875 765.47397 52.21875 765.1875 L 52.21875 764.1875 L 51.21875 764.1875 C 50.977922 764.1945 50.796875 764.2695 50.53125 764.5 L 48.21875 766.78125 L 45.9375 764.5 C 45.75987 764.31608 45.504951 764.1987 45.25 764.1875 C 45.23954 764.18704 45.22912 764.18738 45.21875 764.1875 L 44.21875 764.1875 z "
xlink:href="#path27279-0-5"
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;color:#bebebe;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.78124988;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter16524-9);enable-background:new;font-family:Andale Mono;-inkscape-font-specification:Andale Mono"
id="path16506-5"
inkscape:href="#path27279-0-5"
d="m 44.21875,764.1875 0,1 c -0.02191,0.28075 0.07051,0.55537 0.28125,0.75 l 2.28125,2.28125 -2.28125,2.25 c -0.18819,0.18817 -0.281253,0.45346 -0.28125,0.71875 l 0,1 1,0 c 0.26529,0 0.530586,-0.0931 0.71875,-0.28125 L 48.21875,769.625 50.5,771.90625 c 0.188164,0.18815 0.453449,0.28124 0.71875,0.28125 l 1,0 0,-1 c -8e-6,-0.2653 -0.09306,-0.53058 -0.28125,-0.71875 l -2.25,-2.25 2.28125,-2.28125 c 0.21566,-0.19935 0.25,-0.46353 0.25,-0.75 l 0,-1 -1,0 c -0.240828,0.007 -0.421875,0.082 -0.6875,0.3125 l -2.3125,2.28125 L 45.9375,764.5 c -0.17763,-0.18392 -0.432549,-0.3013 -0.6875,-0.3125 -0.01046,-4.6e-4 -0.02088,-1.2e-4 -0.03125,0 l -1,0 z"
transform="translate(0,1.3535534)" /><path
sodipodi:nodetypes="ccsccccccccccccccccccccccc"
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;color:#bebebe;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.78124988;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:new;font-family:Andale Mono;-inkscape-font-specification:Andale Mono"
id="path27279-0-5"
inkscape:connector-curvature="0"
d="m 44.226475,764.17222 1,0 c 0.01037,-1.2e-4 0.02079,-4.6e-4 0.03125,0 0.254951,0.0112 0.50987,0.12858 0.6875,0.3125 l 2.28125,2.28125 2.3125,-2.28125 c 0.265625,-0.2305 0.446672,-0.3055 0.6875,-0.3125 l 1,0 0,1 c 0,0.28647 -0.03434,0.55065 -0.25,0.75 l -2.28125,2.28125 2.25,2.25 c 0.188188,0.18817 0.281242,0.45345 0.28125,0.71875 l 0,1 -1,0 c -0.265301,-1e-5 -0.530586,-0.0931 -0.71875,-0.28125 l -2.28125,-2.28125 -2.28125,2.28125 c -0.188164,0.18819 -0.45346,0.28125 -0.71875,0.28125 l -1,0 0,-1 c -3e-6,-0.26529 0.09306,-0.53058 0.28125,-0.71875 l 2.28125,-2.25 -2.28125,-2.28125 c -0.210742,-0.19463 -0.30316,-0.46925 -0.28125,-0.75 l 0,-1 z" /></g></g></g></svg>
id="g3175"><path
sodipodi:nodetypes="csssc"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.59217799;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path2394"
d="M 0.83987936,8.0425327 C 0.83987936,4.0805265 4.0712155,0.86823453 8.0567103,0.86823453 C 12.042205,0.86823453 15.273542,4.0805265 15.273542,8.0425327 C 15.273542,12.004539 12.042205,15.216831 8.0567103,15.216831 C 4.0712155,15.216831 0.83987936,12.004539 0.83987936,8.0425327 z"
clip-rule="evenodd" /><g
id="g3172"><path
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.67127273;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 5.4242673,5.3313047 L 10.515414,10.421272 L 10.714004,10.646491"
id="path3152" /></g></g><path
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.67127273;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 5.4402527,10.650392 L 10.688082,5.3573033"
id="path3154"
sodipodi:nodetypes="cc" /></svg>

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

222
data/theme/dialog-error.svg Normal file
View File

@ -0,0 +1,222 @@
<?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="24"
height="24"
id="svg4908"
sodipodi:version="0.32"
inkscape:version="0.47 r22583"
sodipodi:docname="dialog-error.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
inkscape:export-filename="/home/andreas/project/gnome-icon-theme/scalable/actions/process-stop.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90"
version="1.0">
<defs
id="defs4910">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 24 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="48 : 24 : 1"
inkscape:persp3d-origin="24 : 16 : 1"
id="perspective25" />
<radialGradient
gradientTransform="matrix(1.349881,0,0,1.349881,-3.498814,-1.810859)"
gradientUnits="userSpaceOnUse"
r="9.7183542"
fy="4.9892726"
fx="9.6893959"
cy="4.9892726"
cx="9.6893959"
id="radialGradient5177"
xlink:href="#linearGradient5171"
inkscape:collect="always" />
<radialGradient
gradientTransform="matrix(2.417917,0,0,2.417917,-14.17917,-4.903184)"
gradientUnits="userSpaceOnUse"
r="9.7785711"
fy="3.458019"
fx="10"
cy="3.458019"
cx="10"
id="radialGradient5157"
xlink:href="#linearGradient5151"
inkscape:collect="always" />
<radialGradient
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.928125,0,0,0.3143011,0.7718789,12.358015)"
r="9.0598059"
fy="18.022524"
fx="10.739184"
cy="18.022524"
cx="10.739184"
id="radialGradient5145"
xlink:href="#linearGradient5139"
inkscape:collect="always" />
<linearGradient
id="linearGradient5139"
inkscape:collect="always">
<stop
id="stop5141"
offset="0"
style="stop-color:black;stop-opacity:1;" />
<stop
id="stop5143"
offset="1"
style="stop-color:black;stop-opacity:0;" />
</linearGradient>
<linearGradient
id="linearGradient5151"
inkscape:collect="always">
<stop
id="stop5153"
offset="0"
style="stop-color:white;stop-opacity:1;" />
<stop
id="stop5155"
offset="1"
style="stop-color:white;stop-opacity:0;" />
</linearGradient>
<linearGradient
id="linearGradient5171">
<stop
id="stop5173"
offset="0"
style="stop-color:#fe3a00;stop-opacity:1" />
<stop
id="stop5175"
offset="1"
style="stop-color:#c00;stop-opacity:1;" />
</linearGradient>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="22.627417"
inkscape:cx="24.442987"
inkscape:cy="10.142308"
inkscape:current-layer="g7001"
showgrid="false"
inkscape:grid-bbox="true"
inkscape:document-units="px"
showguides="true"
inkscape:guide-bbox="true"
inkscape:window-width="1674"
inkscape:window-height="970"
inkscape:window-x="0"
inkscape:window-y="26"
width="48px"
height="48px"
inkscape:window-maximized="0" />
<metadata
id="metadata4913">
<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>Stop Process</dc:title>
<dc:date>December 2006</dc:date>
<dc:creator>
<cc:Agent>
<dc:title>Jakub Steiner</dc:title>
</cc:Agent>
</dc:creator>
<dc:contributor>
<cc:Agent>
<dc:title>Andreas Nilsson</dc:title>
</cc:Agent>
</dc:contributor>
<cc:license
rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
<dc:subject>
<rdf:Bag>
<rdf:li>stop</rdf:li>
<rdf:li>halt</rdf:li>
</rdf:Bag>
</dc:subject>
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
<cc:permits
rdf:resource="http://web.resource.org/cc/Reproduction" />
<cc:permits
rdf:resource="http://web.resource.org/cc/Distribution" />
<cc:requires
rdf:resource="http://web.resource.org/cc/Notice" />
<cc:permits
rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
<cc:requires
rdf:resource="http://web.resource.org/cc/ShareAlike" />
<cc:requires
rdf:resource="http://web.resource.org/cc/SourceCode" />
</cc:License>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer"
transform="translate(0,-24)">
<g
inkscape:label="Layer 1"
id="g7001"
transform="matrix(1.4566048,0,0,1.4455352,0.4112881,1.2324709)">
<path
transform="matrix(0.91468137,0,0,0.70055266,-1.8812476,17.474032)"
d="m 19.79899,18.022524 a 9.0598059,3.0935922 0 1 1 -18.1196115,0 9.0598059,3.0935922 0 1 1 18.1196115,0 z"
sodipodi:ry="3.0935922"
sodipodi:rx="9.0598059"
sodipodi:cy="18.022524"
sodipodi:cx="10.739184"
id="path5137"
style="color:#000000;fill:url(#radialGradient5145);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
sodipodi:type="arc" />
<path
transform="matrix(0.87347736,0,0,0.83068052,-0.79308842,15.602788)"
d="m 19.25,9.625 a 9.25,9.25 0 1 1 -18.5,0 9.25,9.25 0 1 1 18.5,0 z"
sodipodi:ry="9.25"
sodipodi:rx="9.25"
sodipodi:cy="9.625"
sodipodi:cx="10"
id="path4262"
style="color:#000000;fill:url(#radialGradient5177);fill-opacity:1;fill-rule:nonzero;stroke:#a40000;stroke-width:0.47435912;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
sodipodi:type="arc" />
<path
sodipodi:type="arc"
style="opacity:0.35393258;color:#000000;fill:none;stroke:url(#radialGradient5157);stroke-width:0.49999994;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
id="path5149"
sodipodi:cx="10"
sodipodi:cy="9.625"
sodipodi:rx="9.25"
sodipodi:ry="9.25"
d="m 19.25,9.625 a 9.25,9.25 0 1 1 -18.5,0 9.25,9.25 0 1 1 18.5,0 z"
transform="matrix(0.82868359,0,0,0.78808147,-0.34515141,16.012803)" />
<path
sodipodi:nodetypes="cc"
id="path5159"
d="m 4.834121,20.642783 6.215127,5.91061"
style="color:#000000;fill:none;stroke:#ffffff;stroke-width:1.21219134;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" />
<path
style="color:#000000;fill:none;stroke:#ffffff;stroke-width:1.21219146;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
d="M 11.04925,20.622826 4.8159529,26.553393"
id="path5161"
sodipodi:nodetypes="cc" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.0 KiB

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,113 @@
<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="24"
height="16"
id="svg6503"
version="1.1"
inkscape:version="0.47pre4 r22446"
sodipodi:docname="mosaic-view-active.svg">
<defs
id="defs6505">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 16 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="32 : 16 : 1"
inkscape:persp3d-origin="16 : 10.666667 : 1"
id="perspective6511" />
<inkscape:perspective
id="perspective6494"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="11.197802"
inkscape:cx="-15.97056"
inkscape:cy="16"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="1680"
inkscape:window-height="997"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:window-maximized="1" />
<metadata
id="metadata6508">
<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
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer"
transform="translate(0,-16)">
<g
style="display:inline;fill:#cbcbcb;fill-opacity:1"
transform="translate(-449.85476,-685.85869)"
id="g5306">
<rect
style="fill:#cbcbcb;fill-opacity:1;stroke:#000000;stroke-width:0.99999970000000005;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.44262299999999999;stroke-dasharray:none"
id="rect5308"
width="11"
height="7"
x="450.5"
y="710.5"
rx="0.99999958"
ry="1" />
<rect
style="fill:#cbcbcb;fill-opacity:1;stroke:#000000;stroke-width:0.99999970000000005;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.44262299999999999;stroke-dasharray:none;display:inline"
id="rect5310"
width="11"
height="7"
x="462.5"
y="702.5"
rx="0.99999958"
ry="1" />
<rect
style="fill:#cbcbcb;fill-opacity:1;stroke:#000000;stroke-width:0.99999976000000002;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.44262299999999999;stroke-dasharray:none;display:inline"
id="rect5312"
width="11"
height="7"
x="450.5"
y="702.5"
rx="0.99999958"
ry="1" />
<rect
style="fill:#cbcbcb;fill-opacity:1;stroke:#000000;stroke-width:0.99999970000000005;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.44262299999999999;stroke-dasharray:none;display:inline"
id="rect5314"
width="11"
height="7"
x="462.5"
y="710.5"
rx="0.99999958"
ry="1" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

113
data/theme/mosaic-view.svg Normal file
View File

@ -0,0 +1,113 @@
<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="24"
height="16"
id="svg6503"
version="1.1"
inkscape:version="0.47pre4 r22446"
sodipodi:docname="New document 19">
<defs
id="defs6505">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 16 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="32 : 16 : 1"
inkscape:persp3d-origin="16 : 10.666667 : 1"
id="perspective6511" />
<inkscape:perspective
id="perspective6494"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="11.197802"
inkscape:cx="16"
inkscape:cy="16"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="1680"
inkscape:window-height="997"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:window-maximized="1" />
<metadata
id="metadata6508">
<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></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer"
transform="translate(0,-16)">
<g
style="display:inline"
transform="translate(-449.85476,-685.85869)"
id="g5306">
<rect
style="fill:#666666;fill-opacity:1;stroke:#000000;stroke-width:0.9999997;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.442623;stroke-dasharray:none"
id="rect5308"
width="11"
height="7"
x="450.5"
y="710.5"
rx="0.99999958"
ry="1" />
<rect
style="fill:#666666;fill-opacity:1;stroke:#000000;stroke-width:0.9999997;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.442623;stroke-dasharray:none;display:inline"
id="rect5310"
width="11"
height="7"
x="462.5"
y="702.5"
rx="0.99999958"
ry="1" />
<rect
style="fill:#666666;fill-opacity:1;stroke:#000000;stroke-width:0.99999976;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.442623;stroke-dasharray:none;display:inline"
id="rect5312"
width="11"
height="7"
x="450.5"
y="702.5"
rx="0.99999958"
ry="1" />
<rect
style="fill:#666666;fill-opacity:1;stroke:#000000;stroke-width:0.9999997;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.442623;stroke-dasharray:none;display:inline"
id="rect5314"
width="11"
height="7"
x="462.5"
y="710.5"
rx="0.99999958"
ry="1" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -0,0 +1,89 @@
<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="98"
height="98"
id="svg6375"
version="1.1"
inkscape:version="0.47 r22583"
sodipodi:docname="add-workspace.svg">
<defs
id="defs6377">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 16 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="32 : 16 : 1"
inkscape:persp3d-origin="16 : 10.666667 : 1"
id="perspective6383" />
<inkscape:perspective
id="perspective6366"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="3.9590209"
inkscape:cx="56.650687"
inkscape:cy="20.635343"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="1680"
inkscape:window-height="997"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:window-maximized="1" />
<metadata
id="metadata6380">
<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></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer"
transform="translate(0,66)">
<g
id="g2824"
transform="matrix(11.568551,0,0,11.698271,-78.828159,-304.81518)">
<path
style="fill:none;stroke:#666666;stroke-width:1.99999952;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 11.07363,21.36834 0,6.43903"
id="path5322" />
<path
style="fill:none;stroke:#666666;stroke-width:1.99999952;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
d="m 14.29314,24.58786 -6.43902,0"
id="path5324" />
</g>
<path
style="fill:#000000;fill-opacity:0.98823529"
d="m 48.239516,97.908047 c -0.41677,-0.05102 -1.269253,-0.222408 -1.894408,-0.380859 -4.088493,-1.036262 -7.520781,-4.753234 -8.330163,-9.021094 -0.154947,-0.817026 -0.257819,-6.68112 -0.257819,-14.696556 l 0,-13.337088 -13.829177,-0.08909 C 10.802042,60.298796 10.026884,60.268266 8.6851548,59.783022 3.6288503,57.954375 0.62673331,53.828648 0.62673331,48.708554 c 0,-5.625522 4.25936019,-10.425065 9.97721469,-11.242548 0.987903,-0.141242 7.368912,-0.254994 14.460646,-0.257791 l 12.692532,-0.005 0,-13.586668 c 0,-14.6441583 0.03287,-15.0698926 1.364686,-17.6753047 2.185477,-4.2754229 6.938193,-6.75739913 11.687647,-6.10355607 3.382776,0.46569661 6.737962,2.72496967 8.414081,5.66577137 1.480816,2.5981315 1.519067,3.0522448 1.519067,18.0333334 l 0,13.666424 12.692533,0.005 c 7.091733,0.0028 13.472742,0.116549 14.460646,0.257791 6.395303,0.914337 10.804785,6.623716 9.941157,12.871766 -0.698243,5.051565 -4.792685,9.104635 -9.941157,9.840713 -0.987904,0.141242 -7.368913,0.254995 -14.460646,0.257791 l -12.692533,0.005 0,13.801945 c 0,13.031417 -0.02798,13.895893 -0.501177,15.484801 -1.526902,5.127058 -6.919246,8.802262 -12.001914,8.18002 z"
id="path2828"
transform="translate(0,-66)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -1,33 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
width="3"
height="10"
id="svg2"
version="1.1">
<defs
id="defs4" />
<metadata
id="metadata7">
</metadata>
<g
id="layer1">
<rect
style="fill:#000000;fill-opacity:1;stroke-width:0.43599999000000000;stroke-miterlimit:4;stroke-dasharray:none"
id="rect3779"
width="3"
height="10"
x="0"
y="0" />
<rect
style="fill:#536272;fill-opacity:1;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none"
id="rect3796"
width="3"
height="1"
x="0"
y="9" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 787 B

View File

@ -1,74 +0,0 @@
<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="21"
height="10"
id="svg2"
version="1.1"
inkscape:version="0.48.1 r9760"
sodipodi:docname="panel-button-border.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#000000"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="44.8"
inkscape:cx="8.6594891"
inkscape:cy="5.7029946"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
showguides="true"
inkscape:guide-bbox="true"
inkscape:window-width="1440"
inkscape:window-height="843"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:window-maximized="1"
guidetolerance="10000"
objecttolerance="10000">
<inkscape:grid
type="xygrid"
id="grid3792"
empspacing="10"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata7">
<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">
<rect
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none"
id="rect3796"
width="3"
height="2"
x="9"
y="8" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -1,111 +0,0 @@
<?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="30"
height="25"
id="svg10621"
version="1.1"
inkscape:version="0.48.1 r9760"
sodipodi:docname="panel-button-highlight-narrow.svg">
<defs
id="defs10623">
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient34508-1-3"
id="radialGradient99561-1"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
cx="51"
cy="30"
fx="51"
fy="30"
r="42" />
<linearGradient
inkscape:collect="always"
id="linearGradient34508-1-3">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop34510-1-9" />
<stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop34512-4-5" />
</linearGradient>
<radialGradient
r="42"
fy="30"
fx="51"
cy="30"
cx="51"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
gradientUnits="userSpaceOnUse"
id="radialGradient10592"
xlink:href="#linearGradient34508-1-3"
inkscape:collect="always" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#000000"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="1.979899"
inkscape:cx="-171.36384"
inkscape:cy="-53.255157"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="1440"
inkscape:window-height="843"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:window-maximized="1" />
<metadata
id="metadata10626">
<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(-468.08632,-537.03477)">
<path
sodipodi:type="arc"
style="opacity:0.4625;color:#000000;fill:url(#radialGradient10592);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="path34506-3"
sodipodi:cx="51"
sodipodi:cy="30"
sodipodi:rx="42"
sodipodi:ry="16"
d="M 9,29.999999 C 9.0000011,21.163443 27.804042,14 51.000002,14 74.195961,14 93,21.163444 93,30 l -42,0 z"
sodipodi:start="3.1415927"
sodipodi:end="6.2831853"
transform="matrix(0.35714286,0,0,1.5625,464.87203,515.15977)"
inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/motion/textures/panel.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -1,111 +0,0 @@
<?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="84"
height="25"
id="svg10621"
version="1.1"
inkscape:version="0.48.0 r9654"
sodipodi:docname="panel-button-highlight-wide.svg">
<defs
id="defs10623">
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient34508-1-3"
id="radialGradient99561-1"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
cx="51"
cy="30"
fx="51"
fy="30"
r="42" />
<linearGradient
inkscape:collect="always"
id="linearGradient34508-1-3">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop34510-1-9" />
<stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop34512-4-5" />
</linearGradient>
<radialGradient
r="42"
fy="30"
fx="51"
cy="30"
cx="51"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
gradientUnits="userSpaceOnUse"
id="radialGradient10592"
xlink:href="#linearGradient34508-1-3"
inkscape:collect="always" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#000000"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="1.979899"
inkscape:cx="-118.50071"
inkscape:cy="27.304508"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="1440"
inkscape:window-height="843"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:window-maximized="1" />
<metadata
id="metadata10626">
<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(-441.08632,-537.03477)">
<path
sodipodi:type="arc"
style="opacity:0.4625;color:#000000;fill:url(#radialGradient10592);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="path34506-3"
sodipodi:cx="51"
sodipodi:cy="30"
sodipodi:rx="42"
sodipodi:ry="16"
d="M 9,29.999999 C 9.0000011,21.163443 27.804042,14 51.000002,14 74.195961,14 93,21.163444 93,30 l -42,0 z"
sodipodi:start="3.1415927"
sodipodi:end="6.2831853"
transform="matrix(1,0,0,1.5625,432.08632,515.15977)"
inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/motion/textures/panel.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -1,261 +0,0 @@
<?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"
id="svg5369"
version="1.1"
inkscape:version="0.48+devel r10053 custom"
width="96"
height="48"
sodipodi:docname="process-working.svg"
style="display:inline">
<metadata
id="metadata5375">
<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></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs5373" />
<sodipodi:namedview
pagecolor="#808080"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1975"
inkscape:window-height="1098"
id="namedview5371"
showgrid="true"
borderlayer="true"
inkscape:showpageshadow="false"
inkscape:zoom="16"
inkscape:cx="53.997662"
inkscape:cy="22.367695"
inkscape:window-x="1600"
inkscape:window-y="33"
inkscape:window-maximized="0"
inkscape:current-layer="layer2">
<inkscape:grid
type="xygrid"
id="grid11933"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="tiles"
style="display:none">
<rect
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect12451"
width="24"
height="24"
x="0"
y="0" />
<rect
y="24"
x="0"
height="24"
width="24"
id="rect12453"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
y="0"
x="24"
height="24"
width="24"
id="rect12455"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect12457"
width="24"
height="24"
x="24"
y="24" />
<rect
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect12459"
width="24"
height="24"
x="48"
y="0" />
<rect
y="24"
x="48"
height="24"
width="24"
id="rect12461"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
y="0"
x="72"
height="24"
width="24"
id="rect12463"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect12465"
width="24"
height="24"
x="72"
y="24" />
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="spinner">
<g
transform="matrix(0.28240106,0,0,0.28240106,146.92015,-382.52444)"
id="g10450-5"
style="display:inline">
<path
inkscape:connector-curvature="0"
style="opacity:0.6;color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m -477.76072,1373.3569 0,9.4717"
id="path18768"
sodipodi:nodetypes="cc"
inkscape:transform-center-y="-4.6808838" />
<path
inkscape:connector-curvature="0"
inkscape:transform-center-y="-3.3099227"
sodipodi:nodetypes="cc"
id="path18770"
d="m -461.0171,1380.2922 -7.23427,7.3824"
style="opacity:0.7;color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
inkscape:transform-center-x="-3.3098966" />
<path
inkscape:connector-curvature="0"
inkscape:transform-center-x="-4.6808962"
style="opacity:0.8;color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m -454.08163,1397.0359 -9.47165,0"
id="path18772"
sodipodi:nodetypes="cc"
inkscape:transform-center-y="-2.6596956e-05" />
<path
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc"
id="path18774"
d="m -461.01709,1413.7796 -6.93831,-7.0864"
style="opacity:0.9;color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
inkscape:transform-center-x="-3.3098966"
inkscape:transform-center-y="3.3098652" />
<path
inkscape:connector-curvature="0"
inkscape:transform-center-y="4.6808757"
style="color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m -477.76074,1420.715 9e-5,-9.4716"
id="path18776"
sodipodi:nodetypes="cc" />
<path
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc"
id="path18778"
d="m -494.50442,1413.7796 6.79048,-6.9384"
style="opacity:0.3;color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
inkscape:transform-center-y="3.3098769"
inkscape:transform-center-x="3.3098883" />
<path
inkscape:connector-curvature="0"
inkscape:transform-center-x="4.6808941"
style="opacity:0.4;color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m -501.43987,1397.0359 9.47174,0"
id="path18780"
sodipodi:nodetypes="cc"
inkscape:transform-center-y="-2.6596956e-05" />
<path
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc"
id="path18782"
d="m -494.5044,1380.2922 6.64243,6.9384"
style="opacity:0.5;color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
inkscape:transform-center-x="3.3098902"
inkscape:transform-center-y="-3.3099302" />
</g>
<use
style="display:inline"
x="0"
y="0"
xlink:href="#g10450-5"
id="use4981"
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,36,-4.9705636)"
width="400"
height="400" />
<use
style="display:inline"
x="0"
y="0"
xlink:href="#use4981"
id="use4983"
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,43.032478,-21.909695)"
width="400"
height="400" />
<use
style="display:inline"
x="0"
y="0"
xlink:href="#use4983"
id="use4985"
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,50.081986,-38.904617)"
width="400"
height="400" />
<use
style="display:inline"
x="0"
y="0"
xlink:href="#use4985"
id="use4987"
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,-38.919996,-31.872139)"
width="400"
height="400" />
<use
style="display:inline"
x="0"
y="0"
xlink:href="#use4987"
id="use4989"
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,52.986628,2.0890543)"
width="400"
height="400" />
<use
style="display:inline"
x="0"
y="0"
xlink:href="#use4989"
id="use4991"
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,60.013026,-14.912936)"
width="400"
height="400" />
<use
style="display:inline"
x="0"
y="0"
xlink:href="#use4991"
id="use4993"
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,67.022396,-31.859127)"
width="400"
height="400" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 9.8 KiB

View File

@ -0,0 +1,92 @@
<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="23"
height="15"
id="svg5501"
version="1.1"
inkscape:version="0.47pre4 r22446"
sodipodi:docname="add-workspace.svg">
<defs
id="defs5503">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 16 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="32 : 16 : 1"
inkscape:persp3d-origin="16 : 10.666667 : 1"
id="perspective5509" />
<inkscape:perspective
id="perspective5314"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="11.197802"
inkscape:cx="-0.074583208"
inkscape:cy="16"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="1680"
inkscape:window-height="997"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:window-maximized="1"
inkscape:snap-grids="true"
inkscape:snap-bbox="true" />
<metadata
id="metadata5506">
<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></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer"
transform="translate(0,-17)">
<g
style="display:inline"
id="g6239"
transform="translate(-953.97989,-657.32287)">
<rect
style="fill:#000000;fill-opacity:0.98770495;stroke:#666666;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
id="rect5318-6"
width="22"
height="14"
x="954.5"
y="675"
rx="0.49999979"
ry="0.5" />
<path
style="fill:none;stroke:#666666;stroke-width:1.99999952;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
d="m 968.71951,682 -6.43902,0"
id="path5324-5" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -2,62 +2,24 @@
<!-- 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="119.97824"
height="119.97824"
width="74.01342"
height="74.006706"
id="svg7355"
version="1.1"
inkscape:version="0.48.1 r9760"
sodipodi:docname="running-indicator.svg">
<metadata
id="metadata4175">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#2c1cff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1141"
id="namedview4173"
showgrid="false"
inkscape:zoom="8.1348081"
inkscape:cx="81.120662"
inkscape:cy="58.117986"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:window-maximized="1"
inkscape:current-layer="g30864" />
version="1.1">
<defs
id="defs7357">
<radialGradient
xlink:href="#linearGradient36429"
id="radialGradient7461"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.011539,0,0,0.57582113,-0.39262194,71.83807)"
cx="47.428951"
cy="167.16817"
fx="47.428951"
fy="167.16817"
gradientTransform="matrix(1.0525552,0,0,1.0525552,-2.5162753,-9.0000838)"
cx="47.878681"
cy="171.25"
fx="47.878681"
fy="171.25"
r="37" />
<linearGradient
id="linearGradient36429">
@ -97,34 +59,31 @@
fx="49.067139"
cy="242.50381"
cx="49.067139"
gradientTransform="matrix(1.1891549,0,0,0.15252127,-9.281289,132.52772)"
gradientTransform="matrix(1.1891549,0,0,0.55513246,-9.281289,36.12653)"
gradientUnits="userSpaceOnUse"
id="radialGradient7488"
xlink:href="#linearGradient36471" />
</defs>
<g
id="layer1"
transform="matrix(1.6213276,0,0,1.6213276,-431.6347,-272.5745)">
transform="translate(-266.21629,-168.11809)">
<g
style="display:inline"
id="g30864"
transform="translate(255.223,70.118091)">
<rect
ry="3.4593496"
rx="3.4593496"
y="99.596962"
x="12.596948"
height="71.116341"
width="71.116341"
ry="3.5996203"
rx="3.5996203"
y="98"
x="11"
height="74"
width="74"
id="rect14000"
style="opacity:0.37187500000000001;fill:url(#radialGradient7461);fill-opacity:1;stroke:none" />
style="opacity:0.371875;fill:url(#radialGradient7461);fill-opacity:1;stroke:none" />
<path
id="rect34520"
d="m 83.273151,166.72152 c 0,1.96759 -1.584022,3.55163 -3.551629,3.55163 l -63.443032,0 c -1.967608,0 -3.551648,-1.58402 -3.551643,-3.55164 0,-5.85318 0,-5.85318 0,0"
style="opacity:0.35;fill:none;stroke:url(#radialGradient7488);stroke-width:1;stroke-opacity:1"
connector-curvature="0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccscc" />
d="m 84.506708,167.95508 c 6e-6,1.96759 -1.584022,3.55162 -3.551629,3.55163 l -65.910146,0 c -1.967608,-1e-5 -3.551648,-1.58402 -3.551643,-3.55164"
style="opacity:0.2;fill:none;stroke:url(#radialGradient7488);stroke-width:1;stroke-opacity:1" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

View File

@ -9,14 +9,29 @@
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
id="svg2"
width="5.8600588"
height="9"
id="svg3647"
version="1.1"
inkscape:version="0.48+devel r9942 custom"
sodipodi:docname="New document 4">
inkscape:version="0.47 r22583"
sodipodi:docname="section-more.svg">
<defs
id="defs4" />
id="defs3649">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective3655" />
<inkscape:perspective
id="perspective3603"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
@ -24,29 +39,19 @@
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="8.984481"
inkscape:cy="5.6224906"
inkscape:zoom="82.777778"
inkscape:cx="2.9300294"
inkscape:cy="5.466443"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
borderlayer="true"
inkscape:showpageshadow="false"
inkscape:window-width="930"
inkscape:window-height="681"
inkscape:window-x="1892"
inkscape:window-y="272"
inkscape:window-maximized="0">
<inkscape:grid
type="xygrid"
id="grid17403"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
inkscape:window-width="1680"
inkscape:window-height="997"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
id="metadata3652">
<rdf:RDF>
<cc:Work
rdf:about="">
@ -61,22 +66,22 @@
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-1036.3622)">
transform="translate(-262.78425,-490.71933)">
<path
sodipodi:type="star"
style="fill:#5f5f5f;fill-opacity:1;stroke:#5f5f5f;stroke-width:0.43015847;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
id="path18028"
sodipodi:sides="3"
sodipodi:cx="84.5"
sodipodi:cy="337.5"
sodipodi:r1="5"
sodipodi:r2="2.5"
sodipodi:arg1="0.52359878"
sodipodi:arg2="1.5707963"
inkscape:flatsided="true"
inkscape:rounded="0"
transform="matrix(0,-0.98149546,0.71467449,0,25.404986,578.15569)"
d="M 88.830127,340 80.169873,340 84.5,332.5 88.830127,340 z"
inkscape:randomized="0"
d="M 88.830127,340 80.169873,340 84.5,332.5 z"
transform="matrix(0,1.3621708,0.99186247,0,-325.48222,929.32667)" />
inkscape:rounded="0"
inkscape:flatsided="true"
sodipodi:arg2="1.5707963"
sodipodi:arg1="0.52359878"
sodipodi:r2="2.5"
sodipodi:r1="5"
sodipodi:cy="337.5"
sodipodi:cx="84.5"
sodipodi:sides="3"
id="path5497-5"
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.59699643;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
sodipodi:type="star" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -9,14 +9,29 @@
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
id="svg2"
width="5.8600588"
height="9"
id="svg3647"
version="1.1"
inkscape:version="0.48+devel r9942 custom"
sodipodi:docname="arrow-left.svg">
inkscape:version="0.46+devel"
sodipodi:docname="New document 6">
<defs
id="defs4" />
id="defs3649">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective3655" />
<inkscape:perspective
id="perspective3603"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
@ -24,29 +39,19 @@
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="7.7366092"
inkscape:cy="6.4536271"
inkscape:zoom="0.35"
inkscape:cx="112.21575"
inkscape:cy="-32.642856"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
borderlayer="true"
inkscape:showpageshadow="false"
inkscape:window-width="930"
inkscape:window-height="681"
inkscape:window-x="1892"
inkscape:window-y="272"
inkscape:window-maximized="0">
<inkscape:grid
type="xygrid"
id="grid17403"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
inkscape:window-width="609"
inkscape:window-height="501"
inkscape:window-x="164"
inkscape:window-y="26"
inkscape:window-maximized="0" />
<metadata
id="metadata7">
id="metadata3652">
<rdf:RDF>
<cc:Work
rdf:about="">
@ -61,22 +66,22 @@
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-1036.3622)">
transform="translate(-262.78425,-490.71933)">
<path
sodipodi:type="star"
style="fill:#5f5f5f;fill-opacity:1;stroke:#5f5f5f;stroke-width:0.43015847;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
id="path18028"
sodipodi:sides="3"
sodipodi:cx="84.5"
sodipodi:cy="337.5"
sodipodi:r1="5"
sodipodi:r2="2.5"
sodipodi:arg1="0.52359878"
sodipodi:arg2="1.5707963"
inkscape:flatsided="true"
inkscape:rounded="0"
transform="matrix(0,0.98149546,-0.71467449,0,506.02358,412.28296)"
d="M 88.830127,340 80.169873,340 84.5,332.5 88.830127,340 z"
inkscape:randomized="0"
d="M 88.830127,340 80.169873,340 84.5,332.5 z"
transform="matrix(0,1.3621708,-0.99186247,0,342.48324,929.32667)" />
inkscape:rounded="0"
inkscape:flatsided="true"
sodipodi:arg2="1.5707963"
sodipodi:arg1="0.52359878"
sodipodi:r2="2.5"
sodipodi:r1="5"
sodipodi:cy="337.5"
sodipodi:cx="84.5"
sodipodi:sides="3"
id="path5497-5"
style="fill:#5f5f5f;fill-opacity:1;stroke:#5f5f5f;stroke-width:0.59699643;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
sodipodi:type="star" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 531 B

View File

@ -9,23 +9,23 @@
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="10"
height="20"
id="svg10003"
width="24"
height="16"
id="svg6446"
version="1.1"
inkscape:version="0.47 r22583"
sodipodi:docname="filter-selected.svg">
inkscape:version="0.47pre4 r22446"
sodipodi:docname="single-view-active.svg">
<defs
id="defs10005">
id="defs6448">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 32 : 1"
inkscape:vp_x="0 : 16 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="64 : 32 : 1"
inkscape:persp3d-origin="32 : 21.333333 : 1"
id="perspective10011" />
inkscape:vp_z="32 : 16 : 1"
inkscape:persp3d-origin="16 : 10.666667 : 1"
id="perspective6454" />
<inkscape:perspective
id="perspective9998"
id="perspective6441"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
@ -39,20 +39,20 @@
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.5"
inkscape:cx="32"
inkscape:cy="10.181818"
inkscape:zoom="11.197802"
inkscape:cx="0.014720032"
inkscape:cy="16"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="1680"
inkscape:window-height="994"
inkscape:window-height="997"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:window-maximized="1" />
<metadata
id="metadata10008">
id="metadata6451">
<rdf:RDF>
<cc:Work
rdf:about="">
@ -67,15 +67,15 @@
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer"
transform="translate(0,-44)">
<path
inkscape:export-ydpi="90"
inkscape:export-xdpi="90"
inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/app-picker.png"
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="rect34320"
d="m -0.18726572,54.181804 10.55634072,10.55636 10e-6,-21.11269 z"
style="opacity:0.21000001;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.99999988;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
transform="translate(0,-17)">
<rect
ry="0.5"
rx="0.49999979"
y="17.483809"
x="0.53483802"
height="15"
width="23"
id="rect5304"
style="fill:#cccccc;fill-opacity:1;stroke:#cccccc;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -9,23 +9,23 @@
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="10"
height="20"
id="svg10003"
width="24"
height="16"
id="svg6446"
version="1.1"
inkscape:version="0.48.1 r9760"
sodipodi:docname="filter-selected-ltr.svg">
inkscape:version="0.47pre4 r22446"
sodipodi:docname="single-view.svg">
<defs
id="defs10005">
id="defs6448">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 32 : 1"
inkscape:vp_x="0 : 16 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="64 : 32 : 1"
inkscape:persp3d-origin="32 : 21.333333 : 1"
id="perspective10011" />
inkscape:vp_z="32 : 16 : 1"
inkscape:persp3d-origin="16 : 10.666667 : 1"
id="perspective6454" />
<inkscape:perspective
id="perspective9998"
id="perspective6441"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
@ -37,29 +37,29 @@
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.5"
inkscape:cx="32.363636"
inkscape:cy="10.181818"
inkscape:zoom="11.197802"
inkscape:cx="0.014720032"
inkscape:cy="16"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="1440"
inkscape:window-height="839"
inkscape:document-units="px"
inkscape:window-width="1680"
inkscape:window-height="997"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:window-maximized="1" />
<metadata
id="metadata10008">
id="metadata6451">
<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></dc:title>
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
@ -67,15 +67,15 @@
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer"
transform="translate(0,-44)">
<path
inkscape:export-ydpi="90"
inkscape:export-xdpi="90"
inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/app-picker.png"
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="rect34320"
d="m 10.369085,54.181804 -10.55634072,10.55636 -1e-5,-21.11269 z"
style="opacity:0.21000001;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.99999988;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
transform="translate(0,-17)">
<rect
ry="0.5"
rx="0.49999979"
y="17.483809"
x="0.53483802"
height="15"
width="23"
id="rect5304"
style="fill:#626262;fill-opacity:1;stroke:#cccccc;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -1,74 +0,0 @@
<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="21"
height="10"
id="svg2"
version="1.1"
inkscape:version="0.48.0 r9654"
sodipodi:docname="source-button-border.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#000000"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="44.8"
inkscape:cx="8.704132"
inkscape:cy="5.7029946"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
showguides="true"
inkscape:guide-bbox="true"
inkscape:window-width="1600"
inkscape:window-height="1145"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:window-maximized="1"
guidetolerance="10000"
objecttolerance="10000">
<inkscape:grid
type="xygrid"
id="grid3792"
empspacing="10"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata7">
<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></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none"
id="rect3796"
width="19"
height="2"
x="1"
y="8" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -13,8 +13,8 @@
height="22"
id="svg3199"
version="1.1"
inkscape:version="0.48.1 r9760"
sodipodi:docname="toggle-on-intl.svg">
inkscape:version="0.47 r22583"
sodipodi:docname="New document 11">
<defs
id="defs3201">
<inkscape:perspective
@ -39,14 +39,14 @@
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="49.147112"
inkscape:cy="17.532036"
inkscape:zoom="0.35"
inkscape:cx="32.500004"
inkscape:cy="10.999997"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1412"
inkscape:window-height="1067"
inkscape:window-width="609"
inkscape:window-height="501"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:window-maximized="0" />
@ -58,7 +58,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
@ -72,7 +72,7 @@
transform="translate(-453.5,448.36218)"
id="g16453">
<rect
style="color:#000000;fill:#4a90d9;fill-opacity:1;fill-rule:nonzero;stroke:#3465a4;stroke-width:0.99999994000000003;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
style="color:#000000;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:#3465a4;stroke-width:0.99999994;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect16256-9-4"
width="63.000004"
height="19"

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -13,8 +13,8 @@
height="22"
id="svg2857"
version="1.1"
inkscape:version="0.48.1 r9760"
sodipodi:docname="toggle-on-us.svg">
inkscape:version="0.47 r22583"
sodipodi:docname="New document 2">
<defs
id="defs2859">
<inkscape:perspective
@ -40,18 +40,16 @@
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="19.689855"
inkscape:cy="2.0517979"
inkscape:cx="-69.642856"
inkscape:cy="42.428569"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="941"
inkscape:window-height="751"
inkscape:window-x="2577"
inkscape:window-y="206"
inkscape:window-maximized="0"
borderlayer="true"
inkscape:showpageshadow="false" />
inkscape:window-width="609"
inkscape:window-height="501"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:window-maximized="0" />
<metadata
id="metadata2862">
<rdf:RDF>
@ -60,7 +58,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
@ -74,7 +72,7 @@
transform="translate(-351.35714,708.36218)"
id="g16453">
<rect
style="color:#000000;fill:#4a90d9;fill-opacity:1;fill-rule:nonzero;stroke:#3465a4;stroke-width:0.99999994000000003;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
style="color:#000000;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:#3465a4;stroke-width:0.99999994;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect16256-9-4"
width="63.000004"
height="19"

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -0,0 +1,96 @@
<?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="96" height="96" id="svg25070" version="1.1" inkscape:version="0.47 r22583" sodipodi:docname="dark-arrow-larger.svg">
<defs id="defs25072">
<inkscape:perspective sodipodi:type="inkscape:persp3d" inkscape:vp_x="0 : 24 : 1" inkscape:vp_y="0 : 1000 : 0" inkscape:vp_z="48 : 24 : 1" inkscape:persp3d-origin="24 : 16 : 1" id="perspective25078"/>
<inkscape:perspective id="perspective24985" inkscape:persp3d-origin="0.5 : 0.33333333 : 1" inkscape:vp_z="1 : 0.5 : 1" inkscape:vp_y="0 : 1000 : 0" inkscape:vp_x="0 : 0.5 : 1" sodipodi:type="inkscape:persp3d"/>
<linearGradient inkscape:collect="always" xlink:href="#linearGradient4034-0-4" id="linearGradient24957" gradientUnits="userSpaceOnUse" gradientTransform="translate(6)" x1="-86.552246" y1="185.439" x2="-83.37072" y2="197.31261"/>
<linearGradient inkscape:collect="always" id="linearGradient4034-0-4">
<stop style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" offset="0" id="stop4036-5-7"/>
<stop style="stop-color: rgb(186, 189, 182); stop-opacity: 1;" offset="1" id="stop4038-9-6"/>
</linearGradient>
<filter id="filter24765" inkscape:label="Invert" x="0" y="0" width="1" height="1" inkscape:menu="Color" inkscape:menu-tooltip="Invert colors" color-interpolation-filters="sRGB">
<feColorMatrix id="feColorMatrix24767" type="saturate" values="1" result="fbSourceGraphic"/>
<feColorMatrix id="feColorMatrix24769" in="fbSourceGraphic" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>
</filter>
<linearGradient inkscape:collect="always" xlink:href="#linearGradient4632-1-3-9-3-2" id="linearGradient24955" gradientUnits="userSpaceOnUse" gradientTransform="translate(-5)" x1="-74.520325" y1="169.06032" x2="-74.520325" y2="205.94189"/>
<linearGradient id="linearGradient4632-1-3-9-3-2">
<stop style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" offset="0" id="stop4634-1-8-3-9-0"/>
<stop id="stop4636-1-9-9-8-8" offset="0.0274937" style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"/>
<stop id="stop4638-8-3-9-6-6" offset="0.274937" style="stop-color: rgb(242, 242, 242); stop-opacity: 1;"/>
<stop id="stop4640-8-5-7-8-9" offset="0.38707438" style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"/>
<stop id="stop4642-5-41-9-6-9" offset="0.66528589" style="stop-color: rgb(217, 218, 216); stop-opacity: 1;"/>
<stop id="stop4644-5-2-7-9-2" offset="0.76745707" style="stop-color: rgb(223, 224, 221); stop-opacity: 1;"/>
<stop style="stop-color: rgb(240, 240, 240); stop-opacity: 1;" offset="1" id="stop4646-3-2-3-7-3"/>
</linearGradient>
<radialGradient inkscape:collect="always" xlink:href="#linearGradient4869-4-1" id="radialGradient24959" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.0075, 0, 0, 1.0075, -5.4544, -1.25141)" cx="-33.412369" cy="185.74171" fx="-33.412369" fy="185.74171" r="2.3554697"/>
<linearGradient id="linearGradient4869-4-1">
<stop style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" offset="0" id="stop4871-6-2"/>
<stop id="stop4879-7-4" offset="0.31807542" style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"/>
<stop id="stop4877-6-1" offset="0.74691135" style="stop-color: rgb(200, 201, 198); stop-opacity: 1;"/>
<stop style="stop-color: rgb(211, 215, 207); stop-opacity: 1;" offset="1" id="stop4873-1-0"/>
</linearGradient>
<filter id="filter25011" inkscape:label="Invert" x="0" y="0" width="1" height="1" inkscape:menu="Color" inkscape:menu-tooltip="Invert colors" color-interpolation-filters="sRGB">
<feColorMatrix id="feColorMatrix25013" type="saturate" values="1" result="fbSourceGraphic"/>
<feColorMatrix id="feColorMatrix25015" in="fbSourceGraphic" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>
</filter>
<radialGradient inkscape:collect="always" xlink:href="#linearGradient4869-4-0" id="radialGradient24961" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.0075, 0, 0, 1.0075, -5.4544, -1.25141)" cx="-33.412369" cy="185.74171" fx="-33.412369" fy="185.74171" r="2.3554697"/>
<linearGradient id="linearGradient4869-4-0">
<stop style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" offset="0" id="stop4871-6-8"/>
<stop id="stop4879-7-5" offset="0.31807542" style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"/>
<stop id="stop4877-6-5" offset="0.74691135" style="stop-color: rgb(200, 201, 198); stop-opacity: 1;"/>
<stop style="stop-color: rgb(211, 215, 207); stop-opacity: 1;" offset="1" id="stop4873-1-4"/>
</linearGradient>
<filter id="filter25023" inkscape:label="Invert" x="0" y="0" width="1" height="1" inkscape:menu="Color" inkscape:menu-tooltip="Invert colors" color-interpolation-filters="sRGB">
<feColorMatrix id="feColorMatrix25025" type="saturate" values="1" result="fbSourceGraphic"/>
<feColorMatrix id="feColorMatrix25027" in="fbSourceGraphic" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>
</filter>
<linearGradient inkscape:collect="always" xlink:href="#linearGradient4941" id="linearGradient24963" gradientUnits="userSpaceOnUse" x1="-39.858727" y1="184.61784" x2="-38.244785" y2="188.84898"/>
<linearGradient inkscape:collect="always" id="linearGradient4941">
<stop style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" offset="0" id="stop4943"/>
<stop style="stop-color: rgb(255, 255, 255); stop-opacity: 0;" offset="1" id="stop4945"/>
</linearGradient>
<filter id="filter25033" inkscape:label="Invert" x="0" y="0" width="1" height="1" inkscape:menu="Color" inkscape:menu-tooltip="Invert colors" color-interpolation-filters="sRGB">
<feColorMatrix id="feColorMatrix25035" type="saturate" values="1" result="fbSourceGraphic"/>
<feColorMatrix id="feColorMatrix25037" in="fbSourceGraphic" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>
</filter>
<linearGradient inkscape:collect="always" xlink:href="#linearGradient4941-7" id="linearGradient24965" gradientUnits="userSpaceOnUse" x1="-39.858727" y1="184.61784" x2="-38.244785" y2="188.84898"/>
<linearGradient inkscape:collect="always" id="linearGradient4941-7">
<stop style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" offset="0" id="stop4943-2"/>
<stop style="stop-color: rgb(255, 255, 255); stop-opacity: 0;" offset="1" id="stop4945-5"/>
</linearGradient>
<filter id="filter25043" inkscape:label="Invert" x="0" y="0" width="1" height="1" inkscape:menu="Color" inkscape:menu-tooltip="Invert colors" color-interpolation-filters="sRGB">
<feColorMatrix id="feColorMatrix25045" type="saturate" values="1" result="fbSourceGraphic"/>
<feColorMatrix id="feColorMatrix25047" in="fbSourceGraphic" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>
</filter>
<filter id="filter25049" inkscape:label="Invert" x="0" y="0" width="1" height="1" inkscape:menu="Color" inkscape:menu-tooltip="Invert colors" color-interpolation-filters="sRGB">
<feColorMatrix id="feColorMatrix25051" type="saturate" values="1" result="fbSourceGraphic"/>
<feColorMatrix id="feColorMatrix25053" in="fbSourceGraphic" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>
</filter>
<filter id="filter25055" inkscape:label="Invert" x="0" y="0" width="1" height="1" inkscape:menu="Color" inkscape:menu-tooltip="Invert colors" color-interpolation-filters="sRGB">
<feColorMatrix id="feColorMatrix25057" type="saturate" values="1" result="fbSourceGraphic"/>
<feColorMatrix id="feColorMatrix25059" in="fbSourceGraphic" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>
</filter>
</defs>
<sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="2.8284271" inkscape:cx="48.631638" inkscape:cy="57.536221" inkscape:current-layer="layer1" showgrid="true" inkscape:grid-bbox="true" inkscape:document-units="px" inkscape:window-width="1200" inkscape:window-height="851" inkscape:window-x="0" inkscape:window-y="52" inkscape:window-maximized="0"/>
<metadata id="metadata25075">
<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 id="layer1" inkscape:label="Layer 1" inkscape:groupmode="layer" transform="translate(0, 48)">
<g id="g4030-1-8" transform="matrix(2, 0, 0, 2, 193.25, -374.967)" style="stroke: rgb(0, 0, 0); display: inline; stroke-opacity: 1;">
<path sodipodi:nodetypes="ccc" id="path3165-7-3" d="m -72.5,173.5 -14,14 14,14" style="overflow: visible; marker: none; color: rgb(0, 0, 0); fill: none; stroke: rgb(0, 0, 0); stroke-width: 7; stroke-linecap: round; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-dasharray: none; stroke-dashoffset: 0pt; visibility: visible; display: inline;"/>
</g>
<path sodipodi:type="arc" style="overflow: visible; marker: none; color: rgb(0, 0, 0); fill: rgb(0, 0, 0); fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 0.523439; visibility: visible; display: inline;" id="path4050-2-7-9-4" sodipodi:cx="-38.59375" sodipodi:cy="186.40625" sodipodi:rx="2.09375" sodipodi:ry="2.09375" d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z" transform="matrix(3.34328, 0, 0, 3.34328, 185.28, -623.176)"/>
<path sodipodi:type="arc" style="overflow: visible; marker: none; color: rgb(0, 0, 0); fill: rgb(0, 0, 0); fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 0.523439; visibility: visible; display: inline;" id="path4050-2-7-9-4-8" sodipodi:cx="-38.59375" sodipodi:cy="186.40625" sodipodi:rx="2.09375" sodipodi:ry="2.09375" d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z" transform="matrix(3.34328, 0, 0, 3.34328, 207.28, -623.176)"/>
<path sodipodi:type="arc" style="overflow: visible; marker: none; color: rgb(0, 0, 0); fill: none; stroke: rgb(0, 0, 0); stroke-width: 0.697921; stroke-linecap: round; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-dasharray: none; stroke-dashoffset: 0pt; visibility: visible; display: inline;" id="path4050-2-7-9-4-0" sodipodi:cx="-38.59375" sodipodi:cy="186.40625" sodipodi:rx="2.09375" sodipodi:ry="2.09375" d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z" transform="matrix(2.86565, 0, 0, 2.86565, 166.846, -534.143)"/>
<path sodipodi:type="arc" style="overflow: visible; marker: none; color: rgb(0, 0, 0); fill: none; stroke: rgb(0, 0, 0); stroke-width: 0.697921; stroke-linecap: round; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-dasharray: none; stroke-dashoffset: 0pt; visibility: visible; display: inline;" id="path4050-2-7-9-4-0-9" sodipodi:cx="-38.59375" sodipodi:cy="186.40625" sodipodi:rx="2.09375" sodipodi:ry="2.09375" d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z" transform="matrix(2.86565, 0, 0, 2.86565, 188.846, -534.143)"/>
<path style="overflow: visible; marker: none; font-size: medium; font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; text-indent: 0pt; text-align: start; text-decoration: none; line-height: normal; letter-spacing: normal; word-spacing: normal; text-transform: none; direction: ltr; text-anchor: start; opacity: 0.35; color: rgb(0, 0, 0); fill: none; stroke: rgb(0, 0, 0); stroke-width: 1; stroke-miterlimit: 4; stroke-dasharray: none; visibility: visible; display: inline; font-family: Bitstream Vera Sans; stroke-opacity: 1;" d="m 317.06251,365.96875 c -0.76948,0.0224 -1.52555,0.35464 -2.0625,0.90625 l -16.125,16.125 16.125,16.125 c 1.11265,1.11265 3.13735,1.11265 4.25,0 1.11265,-1.11264 1.11265,-3.13735 0,-4.25 l -11.875,-11.875 11.875,-11.875 c 0.86584,-0.83655 1.1475,-2.22114 0.6773,-3.32947 -0.47021,-1.10834 -1.66156,-1.86802 -2.8648,-1.82678 z" id="path3165-7-3-1" sodipodi:nodetypes="ccccscccsc" transform="matrix(2, 0, 0, 2, -586, -765.967)"/>
<path style="overflow: visible; marker: none; font-size: medium; font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; text-indent: 0pt; text-align: start; text-decoration: none; line-height: normal; letter-spacing: normal; word-spacing: normal; text-transform: none; direction: ltr; text-anchor: start; color: rgb(0, 0, 0); fill: none; stroke: rgb(0, 0, 0); stroke-width: 1; stroke-linecap: round; stroke-miterlimit: 4; stroke-dasharray: none; visibility: visible; display: inline; font-family: Bitstream Vera Sans; stroke-opacity: 1;" d="m 320.08435,397.03059 c 0.007,-0.79449 -0.27079,-1.59203 -0.83434,-2.15559 L 307.37501,383 m 12.5523,-15.20447 c -0.47021,-1.10834 -1.66156,-1.86802 -2.8648,-1.82678 -0.76948,0.0224 -1.52555,0.35464 -2.0625,0.90625 L 298.87501,383" id="path3165-7-3-1-9" sodipodi:nodetypes="ccccccc" transform="matrix(2, 0, 0, 2, -586, -765.967)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -2,51 +2,13 @@
<!-- 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"
version="1.1"
width="96"
height="96"
id="svg25070"
inkscape:version="0.48.0 r9654"
sodipodi:docname="ws-switch-arrow-down.svg">
<metadata
id="metadata3353">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="718"
inkscape:window-height="480"
id="namedview3351"
showgrid="false"
inkscape:zoom="2.6979167"
inkscape:cx="48"
inkscape:cy="48"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:window-maximized="0"
inkscape:current-layer="svg25070" />
id="svg25070">
<defs
id="defs25072">
<linearGradient
@ -326,7 +288,7 @@
</filter>
</defs>
<g
transform="matrix(0,1,-1,0,48.0003,4.1307112e-7)"
transform="translate(0,48)"
id="layer1">
<g
transform="matrix(-2,0,0,2,-97.2497,-374.967)"
@ -335,42 +297,35 @@
<path
d="m -72.5,173.5 -14,14 14,14"
id="path3165-7-3"
style="color:#000000;fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
inkscape:connector-curvature="0" />
style="color:#000000;fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" />
</g>
<path
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
transform="matrix(-3.34328,0,0,3.34328,-89.2797,-623.176)"
id="path4050-2-7-9-4"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
inkscape:connector-curvature="0" />
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible" />
<path
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
transform="matrix(-3.34328,0,0,3.34328,-111.2797,-623.176)"
id="path4050-2-7-9-4-8"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
inkscape:connector-curvature="0" />
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible" />
<path
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
transform="matrix(-2.86565,0,0,2.86565,-70.8457,-534.143)"
id="path4050-2-7-9-4-0"
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
inkscape:connector-curvature="0" />
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" />
<path
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
transform="matrix(-2.86565,0,0,2.86565,-92.8457,-534.143)"
id="path4050-2-7-9-4-0-9"
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
inkscape:connector-curvature="0" />
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" />
<path
d="m 47.87528,-34.0295 c 1.53896,0.0448 3.0511,0.70928 4.125,1.8125 l 32.25,32.25 -32.25,32.25 c -2.2253,2.2253 -6.2747,2.2253 -8.5,0 -2.2253,-2.22528 -2.2253,-6.2747 0,-8.5 l 23.75,-23.75 -23.75,-23.75 c -1.73168,-1.6731 -2.295,-4.44228 -1.3546,-6.65894 0.94042,-2.21668 3.32312,-3.73604 5.7296,-3.65356 z"
id="path3165-7-3-1"
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;opacity:0.35;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
inkscape:connector-curvature="0" />
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;opacity:0.35;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans" />
<path
d="m 41.8316,28.09418 c -0.014,-1.58898 0.54158,-3.18406 1.66868,-4.31118 l 23.75,-23.75 m -25.1046,-30.40894 c 0.94042,-2.21668 3.32312,-3.73604 5.7296,-3.65356 1.53896,0.0448 3.0511,0.70928 4.125,1.8125 l 32.25,32.25"
id="path3165-7-3-1-9"
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
inkscape:connector-curvature="0" />
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,447 +0,0 @@
<?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="96"
height="96"
id="svg25070"
version="1.1"
inkscape:version="0.48.0 r9654"
sodipodi:docname="ws-switch-arrow-up.svg">
<defs
id="defs25072">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 24 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="48 : 24 : 1"
inkscape:persp3d-origin="24 : 16 : 1"
id="perspective25078" />
<inkscape:perspective
id="perspective24985"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4034-0-4"
id="linearGradient24957"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(6)"
x1="-86.552246"
y1="185.439"
x2="-83.37072"
y2="197.31261" />
<linearGradient
inkscape:collect="always"
id="linearGradient4034-0-4">
<stop
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"
offset="0"
id="stop4036-5-7" />
<stop
style="stop-color: rgb(186, 189, 182); stop-opacity: 1;"
offset="1"
id="stop4038-9-6" />
</linearGradient>
<filter
id="filter24765"
inkscape:label="Invert"
x="0"
y="0"
width="1"
height="1"
inkscape:menu="Color"
inkscape:menu-tooltip="Invert colors"
color-interpolation-filters="sRGB">
<feColorMatrix
id="feColorMatrix24767"
type="saturate"
values="1"
result="fbSourceGraphic" />
<feColorMatrix
id="feColorMatrix24769"
in="fbSourceGraphic"
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
</filter>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4632-1-3-9-3-2"
id="linearGradient24955"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-5)"
x1="-74.520325"
y1="169.06032"
x2="-74.520325"
y2="205.94189" />
<linearGradient
id="linearGradient4632-1-3-9-3-2">
<stop
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"
offset="0"
id="stop4634-1-8-3-9-0" />
<stop
id="stop4636-1-9-9-8-8"
offset="0.0274937"
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" />
<stop
id="stop4638-8-3-9-6-6"
offset="0.274937"
style="stop-color: rgb(242, 242, 242); stop-opacity: 1;" />
<stop
id="stop4640-8-5-7-8-9"
offset="0.38707438"
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" />
<stop
id="stop4642-5-41-9-6-9"
offset="0.66528589"
style="stop-color: rgb(217, 218, 216); stop-opacity: 1;" />
<stop
id="stop4644-5-2-7-9-2"
offset="0.76745707"
style="stop-color: rgb(223, 224, 221); stop-opacity: 1;" />
<stop
style="stop-color: rgb(240, 240, 240); stop-opacity: 1;"
offset="1"
id="stop4646-3-2-3-7-3" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient4869-4-1"
id="radialGradient24959"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0075, 0, 0, 1.0075, -5.4544, -1.25141)"
cx="-33.412369"
cy="185.74171"
fx="-33.412369"
fy="185.74171"
r="2.3554697" />
<linearGradient
id="linearGradient4869-4-1">
<stop
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
offset="0"
id="stop4871-6-2" />
<stop
id="stop4879-7-4"
offset="0.31807542"
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" />
<stop
id="stop4877-6-1"
offset="0.74691135"
style="stop-color: rgb(200, 201, 198); stop-opacity: 1;" />
<stop
style="stop-color: rgb(211, 215, 207); stop-opacity: 1;"
offset="1"
id="stop4873-1-0" />
</linearGradient>
<filter
id="filter25011"
inkscape:label="Invert"
x="0"
y="0"
width="1"
height="1"
inkscape:menu="Color"
inkscape:menu-tooltip="Invert colors"
color-interpolation-filters="sRGB">
<feColorMatrix
id="feColorMatrix25013"
type="saturate"
values="1"
result="fbSourceGraphic" />
<feColorMatrix
id="feColorMatrix25015"
in="fbSourceGraphic"
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
</filter>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient4869-4-0"
id="radialGradient24961"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0075, 0, 0, 1.0075, -5.4544, -1.25141)"
cx="-33.412369"
cy="185.74171"
fx="-33.412369"
fy="185.74171"
r="2.3554697" />
<linearGradient
id="linearGradient4869-4-0">
<stop
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
offset="0"
id="stop4871-6-8" />
<stop
id="stop4879-7-5"
offset="0.31807542"
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" />
<stop
id="stop4877-6-5"
offset="0.74691135"
style="stop-color: rgb(200, 201, 198); stop-opacity: 1;" />
<stop
style="stop-color: rgb(211, 215, 207); stop-opacity: 1;"
offset="1"
id="stop4873-1-4" />
</linearGradient>
<filter
id="filter25023"
inkscape:label="Invert"
x="0"
y="0"
width="1"
height="1"
inkscape:menu="Color"
inkscape:menu-tooltip="Invert colors"
color-interpolation-filters="sRGB">
<feColorMatrix
id="feColorMatrix25025"
type="saturate"
values="1"
result="fbSourceGraphic" />
<feColorMatrix
id="feColorMatrix25027"
in="fbSourceGraphic"
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
</filter>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4941"
id="linearGradient24963"
gradientUnits="userSpaceOnUse"
x1="-39.858727"
y1="184.61784"
x2="-38.244785"
y2="188.84898" />
<linearGradient
inkscape:collect="always"
id="linearGradient4941">
<stop
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
offset="0"
id="stop4943" />
<stop
style="stop-color: rgb(255, 255, 255); stop-opacity: 0;"
offset="1"
id="stop4945" />
</linearGradient>
<filter
id="filter25033"
inkscape:label="Invert"
x="0"
y="0"
width="1"
height="1"
inkscape:menu="Color"
inkscape:menu-tooltip="Invert colors"
color-interpolation-filters="sRGB">
<feColorMatrix
id="feColorMatrix25035"
type="saturate"
values="1"
result="fbSourceGraphic" />
<feColorMatrix
id="feColorMatrix25037"
in="fbSourceGraphic"
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
</filter>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4941-7"
id="linearGradient24965"
gradientUnits="userSpaceOnUse"
x1="-39.858727"
y1="184.61784"
x2="-38.244785"
y2="188.84898" />
<linearGradient
inkscape:collect="always"
id="linearGradient4941-7">
<stop
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
offset="0"
id="stop4943-2" />
<stop
style="stop-color: rgb(255, 255, 255); stop-opacity: 0;"
offset="1"
id="stop4945-5" />
</linearGradient>
<filter
id="filter25043"
inkscape:label="Invert"
x="0"
y="0"
width="1"
height="1"
inkscape:menu="Color"
inkscape:menu-tooltip="Invert colors"
color-interpolation-filters="sRGB">
<feColorMatrix
id="feColorMatrix25045"
type="saturate"
values="1"
result="fbSourceGraphic" />
<feColorMatrix
id="feColorMatrix25047"
in="fbSourceGraphic"
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
</filter>
<filter
id="filter25049"
inkscape:label="Invert"
x="0"
y="0"
width="1"
height="1"
inkscape:menu="Color"
inkscape:menu-tooltip="Invert colors"
color-interpolation-filters="sRGB">
<feColorMatrix
id="feColorMatrix25051"
type="saturate"
values="1"
result="fbSourceGraphic" />
<feColorMatrix
id="feColorMatrix25053"
in="fbSourceGraphic"
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
</filter>
<filter
id="filter25055"
inkscape:label="Invert"
x="0"
y="0"
width="1"
height="1"
inkscape:menu="Color"
inkscape:menu-tooltip="Invert colors"
color-interpolation-filters="sRGB">
<feColorMatrix
id="feColorMatrix25057"
type="saturate"
values="1"
result="fbSourceGraphic" />
<feColorMatrix
id="feColorMatrix25059"
in="fbSourceGraphic"
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
</filter>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.8284271"
inkscape:cx="-12.356322"
inkscape:cy="57.536221"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="1200"
inkscape:window-height="840"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:window-maximized="0" />
<metadata
id="metadata25075">
<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
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer"
transform="translate(0, 48)">
<g
id="g3181"
transform="matrix(0,1,-1,0,48.0003,-48)">
<g
style="stroke:#000000;stroke-opacity:1;display:inline"
transform="matrix(2,0,0,2,193.25,-374.967)"
id="g4030-1-8">
<path
style="color:#000000;fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
d="m -72.5,173.5 -14,14 14,14"
id="path3165-7-3"
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0" />
</g>
<path
transform="matrix(3.34328,0,0,3.34328,185.28,-623.176)"
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
sodipodi:ry="2.09375"
sodipodi:rx="2.09375"
sodipodi:cy="186.40625"
sodipodi:cx="-38.59375"
id="path4050-2-7-9-4"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
sodipodi:type="arc" />
<path
transform="matrix(3.34328,0,0,3.34328,207.28,-623.176)"
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
sodipodi:ry="2.09375"
sodipodi:rx="2.09375"
sodipodi:cy="186.40625"
sodipodi:cx="-38.59375"
id="path4050-2-7-9-4-8"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
sodipodi:type="arc" />
<path
transform="matrix(2.86565,0,0,2.86565,166.846,-534.143)"
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
sodipodi:ry="2.09375"
sodipodi:rx="2.09375"
sodipodi:cy="186.40625"
sodipodi:cx="-38.59375"
id="path4050-2-7-9-4-0"
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
sodipodi:type="arc" />
<path
transform="matrix(2.86565,0,0,2.86565,188.846,-534.143)"
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
sodipodi:ry="2.09375"
sodipodi:rx="2.09375"
sodipodi:cy="186.40625"
sodipodi:cx="-38.59375"
id="path4050-2-7-9-4-0-9"
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
sodipodi:type="arc" />
<path
transform="matrix(2,0,0,2,-586,-765.967)"
sodipodi:nodetypes="ccccscccsc"
id="path3165-7-3-1"
d="m 317.06251,365.96875 c -0.76948,0.0224 -1.52555,0.35464 -2.0625,0.90625 l -16.125,16.125 16.125,16.125 c 1.11265,1.11265 3.13735,1.11265 4.25,0 1.11265,-1.11264 1.11265,-3.13735 0,-4.25 l -11.875,-11.875 11.875,-11.875 c 0.86584,-0.83655 1.1475,-2.22114 0.6773,-3.32947 -0.47021,-1.10834 -1.66156,-1.86802 -2.8648,-1.82678 z"
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;opacity:0.35;color:#000000;fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
inkscape:connector-curvature="0" />
<path
transform="matrix(2,0,0,2,-586,-765.967)"
sodipodi:nodetypes="ccccccc"
id="path3165-7-3-1-9"
d="m 320.08435,397.03059 c 0.007,-0.79449 -0.27079,-1.59203 -0.83434,-2.15559 L 307.37501,383 m 12.5523,-15.20447 c -0.47021,-1.10834 -1.66156,-1.86802 -2.8648,-1.82678 -0.76948,0.0224 -1.52555,0.35464 -2.0625,0.90625 L 298.87501,383"
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:none;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
inkscape:connector-curvature="0" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@ -6,22 +6,6 @@
<name xml:lang="en">GNOME Shell</name>
<shortdesc xml:lang="en">Next generation GNOME desktop shell</shortdesc>
<description>GNOME Shell provides core user interface functions for the GNOME 3
desktop, like switching to windows and launching applications.
GNOME Shell takes advantage of the capabilities of modern graphics
hardware and introduces innovative user interface concepts to
provide a visually attractive and easy to use experience.
Tarball releases are provided largely for distributions to build
packages. If you are interested in building GNOME Shell from source,
we would recommend building from version control using the build
script described at:
http://live.gnome.org/GnomeShell
Not only will that give you the very latest version of this rapidly
changing project, it will be much easier than get GNOME Shell and
its dependencies to build from tarballs.</description>
<!--
<homepage rdf:resource="http://live.gnome.org/GnomeShell" />
-->
@ -49,7 +33,7 @@ its dependencies to build from tarballs.</description>
<foaf:Person>
<foaf:name>Colin Walters</foaf:name>
<foaf:mbox rdf:resource="mailto:walters@verbum.org" />
<gnome:userid>walters</gnome:userid>
<gnome:userid>cwalters</gnome:userid>
</foaf:Person>
</maintainer>
<maintainer>

View File

@ -2,30 +2,26 @@
jsdir = $(pkgdatadir)/js
nobase_dist_js_DATA = \
misc/config.js \
misc/docInfo.js \
misc/fileUtils.js \
misc/format.js \
misc/gnomeSession.js \
misc/history.js \
misc/modemManager.js \
misc/params.js \
misc/util.js \
misc/telepathy.js \
perf/core.js \
prefs/clockPreferences.js \
ui/altTab.js \
ui/appDisplay.js \
ui/appFavorites.js \
ui/boxpointer.js \
ui/calendar.js \
ui/chrome.js \
ui/ctrlAltTab.js \
ui/dash.js \
ui/dateMenu.js \
ui/dnd.js \
ui/docDisplay.js \
ui/endSessionDialog.js \
ui/environment.js \
ui/extensionSystem.js \
ui/genericDisplay.js \
ui/iconGrid.js \
ui/lightbox.js \
ui/link.js \
@ -34,13 +30,11 @@ nobase_dist_js_DATA = \
ui/magnifierDBus.js \
ui/main.js \
ui/messageTray.js \
ui/modalDialog.js \
ui/notificationDaemon.js \
ui/overview.js \
ui/panel.js \
ui/panelMenu.js \
ui/placeDisplay.js \
ui/polkitAuthenticationAgent.js \
ui/popupMenu.js \
ui/runDialog.js \
ui/scripting.js \
@ -50,18 +44,13 @@ nobase_dist_js_DATA = \
ui/statusIconDispatcher.js \
ui/statusMenu.js \
ui/status/accessibility.js \
ui/status/keyboard.js \
ui/status/network.js \
ui/status/power.js \
ui/status/volume.js \
ui/status/bluetooth.js \
ui/telepathyClient.js \
ui/tweener.js \
ui/viewSelector.js \
ui/windowAttentionHandler.js \
ui/windowManager.js \
ui/workspace.js \
ui/workspaceThumbnail.js \
ui/workspacesView.js \
ui/workspaceSwitcherPopup.js \
ui/xdndHandler.js
ui/workspaceSwitcherPopup.js

View File

@ -1,10 +0,0 @@
/* mode: js2; indent-tabs-mode: nil; tab-size: 4 */
/* The name of this package (not localized) */
const PACKAGE_NAME = '@PACKAGE_NAME@';
/* The version of this package */
const PACKAGE_VERSION = '@PACKAGE_VERSION@';
/* The version of GJS we're linking to */
const GJS_VERSION = '@GJS_VERSION@';
/* 1 if gnome-bluetooth is available, 0 otherwise */
const HAVE_BLUETOOTH = @HAVE_BLUETOOTH@;

View File

@ -29,8 +29,8 @@ DocInfo.prototype = {
return St.TextureCache.get_default().load_recent_thumbnail(size, this.recentInfo);
},
launch : function(workspaceIndex) {
Shell.DocSystem.get_default().open(this.recentInfo, workspaceIndex);
launch : function() {
Shell.DocSystem.get_default().open(this.recentInfo);
},
matchTerms: function(terms) {
@ -60,7 +60,8 @@ function getDocManager() {
}
/**
* DocManager wraps the DocSystem, primarily to expose DocInfo objects.
* DocManager wraps the DocSystem, primarily to expose DocInfo objects
* which conform to the GenericDisplay item API.
*/
function DocManager() {
this._init();

View File

@ -2,13 +2,11 @@
const DBus = imports.dbus;
const Lang = imports.lang;
const Signals = imports.signals;
const PresenceIface = {
name: 'org.gnome.SessionManager.Presence',
methods: [{ name: 'SetStatus',
inSignature: 'u',
outSignature: '' }],
inSignature: 'u' }],
properties: [{ name: 'status',
signature: 'u',
access: 'readwrite' }],
@ -45,82 +43,3 @@ Presence.prototype = {
}
};
DBus.proxifyPrototype(Presence.prototype, PresenceIface);
// Note inhibitors are immutable objects, so they don't
// change at runtime (changes always come in the form
// of new inhibitors)
const InhibitorIface = {
name: 'org.gnome.SessionManager.Inhibitor',
properties: [{ name: 'app_id',
signature: 's',
access: 'readonly' },
{ name: 'client_id',
signature: 's',
access: 'readonly' },
{ name: 'reason',
signature: 's',
access: 'readonly' },
{ name: 'flags',
signature: 'u',
access: 'readonly' },
{ name: 'toplevel_xid',
signature: 'u',
access: 'readonly' },
{ name: 'cookie',
signature: 'u',
access: 'readonly' }],
};
function Inhibitor(objectPath) {
this._init(objectPath);
}
Inhibitor.prototype = {
_init: function(objectPath) {
DBus.session.proxifyObject(this,
"org.gnome.SessionManager",
objectPath);
this.isLoaded = false;
this._loadingPropertiesCount = InhibitorIface.properties.length;
for (let i = 0; i < InhibitorIface.properties.length; i++) {
let propertyName = InhibitorIface.properties[i].name;
this.GetRemote(propertyName, Lang.bind(this,
function(value, exception) {
if (exception)
return;
this[propertyName] = value;
this._loadingPropertiesCount--;
if (this._loadingPropertiesCount == 0) {
this.isLoaded = true;
this.emit("is-loaded");
}
}));
}
},
};
DBus.proxifyPrototype(Inhibitor.prototype, InhibitorIface);
Signals.addSignalMethods(Inhibitor.prototype);
// Not the full interface, only the methods we use
const SessionManagerIface = {
name: 'org.gnome.SessionManager',
methods: [
{ name: 'Logout', inSignature: 'u', outSignature: '' },
{ name: 'Shutdown', inSignature: '', outSignature: '' },
{ name: 'CanShutdown', inSignature: '', outSignature: 'b' }
]
};
function SessionManager() {
this._init();
}
SessionManager.prototype = {
_init: function() {
DBus.session.proxifyObject(this, 'org.gnome.SessionManager', '/org/gnome/SessionManager');
}
};
DBus.proxifyPrototype(SessionManager.prototype, SessionManagerIface);

View File

@ -1,115 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Lang = imports.lang;
const Signals = imports.signals;
const Clutter = imports.gi.Clutter;
const Params = imports.misc.params;
const DEFAULT_LIMIT = 512;
function HistoryManager(params) {
this._init(params);
}
HistoryManager.prototype = {
_init: function(params) {
params = Params.parse(params, { gsettingsKey: null,
limit: DEFAULT_LIMIT,
entry: null });
this._key = params.gsettingsKey;
this._limit = params.limit;
this._historyIndex = 0;
if (this._key) {
this._history = global.settings.get_strv(this._key);
global.settings.connect('changed::' + this._key,
Lang.bind(this, this._historyChanged));
} else {
this._history = [];
}
this._entry = params.entry;
if (this._entry) {
this._entry.connect('key-press-event',
Lang.bind(this, this._onEntryKeyPress));
}
},
_historyChanged: function() {
this._history = global.settings.get_strv(this._key);
this._historyIndex = this._history.length;
},
prevItem: function(text) {
if (this._historyIndex <= 0)
return text;
if (text)
this._history[this._historyIndex] = text;
this._historyIndex--;
return this._indexChanged();
},
nextItem: function(text) {
if (this._historyIndex >= this._history.length)
return text;
if (text)
this._history[this._historyIndex] = text;
this._historyIndex++;
return this._indexChanged();
},
lastItem: function() {
if (this._historyIndex != this._history.length) {
this._historyIndex = this._history.length;
this._indexChanged();
}
return this._historyIndex[this._history.length];
},
addItem: function(input) {
if (this._history.length == 0 ||
this._history[this._history.length - 1] != input) {
this._history.push(input);
this._save();
}
this._historyIndex = this._history.length;
},
_onEntryKeyPress: function(entry, event) {
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_Up) {
this.prevItem(entry.get_text());
return true;
} else if (symbol == Clutter.KEY_Down) {
this.nextItem(entry.get_text());
return true;
}
return false;
},
_indexChanged: function() {
let current = this._history[this._historyIndex] || '';
this.emit('changed', current);
if (this._entry)
this._entry.set_text(current);
return current;
},
_save: function() {
if (this._history.length > this._limit)
this._history.splice(0, this._history.length - this._limit);
if (this._key)
global.settings.set_strv(this._key, this._history);
}
};
Signals.addSignalMethods(HistoryManager.prototype);

View File

@ -1,225 +0,0 @@
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
const DBus = imports.dbus;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
// The following are not the complete interfaces, just the methods we need
// (or may need in the future)
const ModemGsmNetworkInterface = {
name: 'org.freedesktop.ModemManager.Modem.Gsm.Network',
methods: [
{ name: 'GetRegistrationInfo', inSignature: '', outSignature: 'uss' },
{ name: 'GetSignalQuality', inSignature: '', outSignature: 'u' }
],
properties: [
{ name: 'AccessTechnology', signature: 'u', access: 'read' }
],
signals: [
{ name: 'SignalQuality', inSignature: 'u' },
{ name: 'RegistrationInfo', inSignature: 'uss' }
]
};
const ModemGsmNetworkProxy = DBus.makeProxyClass(ModemGsmNetworkInterface);
const ModemCdmaInterface = {
name: 'org.freedesktop.ModemManager.Modem.Cdma',
methods: [
{ name: 'GetSignalQuality', inSignature: '', outSignature: 'u' },
{ name: 'GetServingSystem', inSignature: '', outSignature: 'usu' }
],
signals: [
{ name: 'SignalQuality', inSignature: 'u' }
]
};
const ModemCdmaProxy = DBus.makeProxyClass(ModemCdmaInterface);
let _providersTable;
function _getProvidersTable() {
if (_providersTable)
return _providersTable;
let [providers, countryCodes] = Shell.mobile_providers_parse();
return _providersTable = providers;
}
function ModemGsm() {
this._init.apply(this, arguments);
}
ModemGsm.prototype = {
_init: function(path) {
this._proxy = new ModemGsmNetworkProxy(DBus.system, 'org.freedesktop.ModemManager', path);
this.signal_quality = 0;
this.operator_name = null;
// Code is duplicated because the function have different signatures
this._proxy.connect('SignalQuality', Lang.bind(this, function(proxy, quality) {
this.signal_quality = quality;
this.emit('notify::signal-quality');
}));
this._proxy.connect('RegistrationInfo', Lang.bind(this, function(proxy, status, code, name) {
this.operator_name = this._findOperatorName(name, code);
this.emit('notify::operator-name');
}));
this._proxy.GetRegistrationInfoRemote(Lang.bind(this, function(result, err) {
if (err) {
log(err);
return;
}
let [status, code, name] = result;
this.operator_name = this._findOperatorName(name, code);
this.emit('notify::operator-name');
}));
this._proxy.GetSignalQualityRemote(Lang.bind(this, function(result, err) {
if (err) {
// it will return an error if the device is not connected
this.signal_quality = 0;
} else {
let [quality] = result;
this.signal_quality = quality;
}
this.emit('notify::signal-quality');
}));
},
_findOperatorName: function(name, opCode) {
if (name.length != 0 && (name.length > 6 || name.length < 5)) {
// this looks like a valid name, i.e. not an MCCMNC (that some
// devices return when not yet connected
return name;
}
if (isNaN(parseInt(name))) {
// name is definitely not a MCCMNC, so it may be a name
// after all; return that
return name;
}
let needle;
if (name.length == 0 && opCode)
needle = opCode;
else if (name.length == 6 || name.length == 5)
needle = name;
else // nothing to search
return null;
return this._findProviderForMCCMNC(needle);
},
_findProviderForMCCMNC: function(needle) {
let table = _getProvidersTable();
let needlemcc = needle.substring(0, 3);
let needlemnc = needle.substring(3, needle.length);
let name2, name3;
for (let iter in table) {
let providers = table[iter];
// Search through each country's providers
for (let i = 0; i < providers.length; i++) {
let provider = providers[i];
// Search through MCC/MNC list
let list = provider.get_gsm_mcc_mnc();
for (let j = 0; j < list.length; j++) {
let mccmnc = list[j];
// Match both 2-digit and 3-digit MNC; prefer a
// 3-digit match if found, otherwise a 2-digit one.
if (mccmnc.mcc != needlemcc)
continue; // MCC was wrong
if (!name3 && needle.length == 6 && needlemnc == mccmnc.mnc)
name3 = provider.name;
if (!name2 && needlemnc.substring(0, 2) == mccmnc.mnc.substring(0, 2))
name2 = provider.name;
if (name2 && name3)
break;
}
}
}
return name3 || name2 || null;
}
}
Signals.addSignalMethods(ModemGsm.prototype);
function ModemCdma() {
this._init.apply(this, arguments);
}
ModemCdma.prototype = {
_init: function(path) {
this._proxy = new ModemCdmaProxy(DBus.system, 'org.freedesktop.ModemManager', path);
this.signal_quality = 0;
this.operator_name = null;
this._proxy.connect('SignalQuality', Lang.bind(this, function(proxy, quality) {
this.signal_quality = quality;
this.emit('notify::signal-quality');
// receiving this signal means the device got activated
// and we can finally call GetServingSystem
if (this.operator_name == null)
this._refreshServingSystem();
}));
this._proxy.GetSignalQualityRemote(Lang.bind(this, function(result, err) {
if (err) {
// it will return an error if the device is not connected
this.signal_quality = 0;
} else {
let [quality] = result;
this.signal_quality = quality;
}
this.emit('notify::signal-quality');
}));
},
_refreshServingSystem: function() {
this._proxy.GetServingSystemRemote(Lang.bind(this, function(result, err) {
if (err) {
// it will return an error if the device is not connected
this.operator_name = null;
} else {
let [bandClass, band, id] = result;
if (name.length > 0)
this.operator_name = this._findProviderForSid(id);
else
this.operator_name = null;
}
this.emit('notify::operator-name');
}));
},
_findProviderForSid: function(sid) {
if (sid == 0)
return null;
let table = _getProvidersTable();
// Search through each country
for (let iter in table) {
let providers = table[iter];
// Search through each country's providers
for (let i = 0; i < providers.length; i++) {
let provider = providers[i];
let cdma_sid = provider.get_cdma_sid();
// Search through CDMA SID list
for (let j = 0; j < cdma_sid.length; j++) {
if (cdma_sid[j] == sid)
return provider.name;
}
}
}
return null;
}
};
Signals.addSignalMethods(ModemCdma.prototype);

361
js/misc/telepathy.js Normal file
View File

@ -0,0 +1,361 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const DBus = imports.dbus;
// D-Bus utils
function nameToPath(name) {
return '/' + name.replace(/\./g, '/');
};
function pathToName(path) {
if (path[0] != '/')
throw new Error('not a D-Bus path: ' + path);
return path.substr(1).replace(/\//g, '.');
};
// This is tp_escape_as_identifier() from telepathy-glib
function escapeAsIdentifier(name) {
if (!name)
return '_';
// first char is replaced with _XX if it's non-alpha,
// later chars are replaced with _XX if they're non-alphanumeric
if (name.length == 1) {
return name.replace(/[^a-zA-Z]/, _hexEscape);
} else {
return (name[0].replace(/[^a-zA-Z]/, _hexEscape) +
name.substring(1).replace(/[^a-zA-Z0-9]/g, _hexEscape));
}
}
function _hexEscape(ch) {
return '_' + ch.charCodeAt(0).toString(16);
}
// Telepathy D-Bus interface definitions. Note that most of these are
// incomplete, and only cover the methods/properties/signals that
// we're currently using.
const TELEPATHY = 'org.freedesktop.Telepathy';
const CLIENT_NAME = TELEPATHY + '.Client';
const ClientIface = {
name: CLIENT_NAME,
properties: [
{ name: 'Interfaces',
signature: 'as',
access: 'read' }
]
};
const CLIENT_APPROVER_NAME = TELEPATHY + '.Client.Approver';
const ClientApproverIface = {
name: CLIENT_APPROVER_NAME,
methods: [
{ name: 'AddDispatchOperation',
inSignature: 'a(oa{sv})oa{sv}',
outSignature: '' }
],
properties: [
{ name: 'ApproverChannelFilter',
signature: 'aa{sv}',
access: 'read' }
]
};
const CLIENT_HANDLER_NAME = TELEPATHY + '.Client.Handler';
const ClientHandlerIface = {
name: CLIENT_HANDLER_NAME,
methods: [
{ name: 'HandleChannels',
inSignature: 'ooa(oa{sv})aota{sv}',
outSignature: '' }
],
properties: [
{ name: 'HandlerChannelFilter',
signature: 'aa{sv}',
access: 'read' }
]
};
const CLIENT_OBSERVER_NAME = TELEPATHY + '.Client.Observer';
const ClientObserverIface = {
name: CLIENT_OBSERVER_NAME,
methods: [
{ name: 'ObserveChannels',
inSignature: 'ooa(oa{sv})oaoa{sv}',
outSignature: '' }
],
properties: [
{ name: 'ObserverChannelFilter',
signature: 'aa{sv}',
access: 'read' }
]
};
const CHANNEL_DISPATCH_OPERATION_NAME = TELEPATHY + '.ChannelDispatchOperation';
const ChannelDispatchOperationIface = {
name: CHANNEL_DISPATCH_OPERATION_NAME,
methods: [
{ name: 'HandleWith',
inSignature: 's',
outSignature: '' },
{ name: 'Claim',
inSignature: '',
outSignature: '' }
]
};
let ChannelDispatchOperation = DBus.makeProxyClass(ChannelDispatchOperationIface);
const CONNECTION_NAME = TELEPATHY + '.Connection';
const ConnectionIface = {
name: CONNECTION_NAME,
signals: [
{ name: 'StatusChanged',
inSignature: 'uu' }
]
};
let Connection = DBus.makeProxyClass(ConnectionIface);
const ConnectionStatus = {
CONNECTED: 0,
CONNECTING: 1,
DISCONNECTED: 2
};
const CONNECTION_ALIASING_NAME = CONNECTION_NAME + '.Interface.Aliasing';
const ConnectionAliasingIface = {
name: CONNECTION_ALIASING_NAME,
methods: [
{ name: 'RequestAliases',
inSignature: 'au',
outSignature: 'as'
}
],
signals: [
{ name: 'AliasesChanged',
inSignature: 'a(us)' }
]
};
let ConnectionAliasing = DBus.makeProxyClass(ConnectionAliasingIface);
const CONNECTION_AVATARS_NAME = CONNECTION_NAME + '.Interface.Avatars';
const ConnectionAvatarsIface = {
name: CONNECTION_AVATARS_NAME,
methods: [
{ name: 'GetKnownAvatarTokens',
inSignature: 'au',
outSignature: 'a{us}'
},
{ name: 'RequestAvatars',
inSignature: 'au',
outSignature: ''
}
],
signals: [
{ name: 'AvatarRetrieved',
inSignature: 'usays'
},
{ name: 'AvatarUpdated',
inSignature: 'us'
}
]
};
let ConnectionAvatars = DBus.makeProxyClass(ConnectionAvatarsIface);
const CONNECTION_CONTACTS_NAME = CONNECTION_NAME + '.Interface.Contacts';
const ConnectionContactsIface = {
name: CONNECTION_CONTACTS_NAME,
methods: [
{ name: 'GetContactAttributes',
inSignature: 'auasb',
outSignature: 'a{ua{sv}}'
}
]
};
let ConnectionContacts = DBus.makeProxyClass(ConnectionContactsIface);
const CONNECTION_REQUESTS_NAME = CONNECTION_NAME + '.Interface.Requests';
const ConnectionRequestsIface = {
name: CONNECTION_REQUESTS_NAME,
methods: [
{ name: 'CreateChannel',
inSignature: 'a{sv}',
outSignature: 'oa{sv}'
},
{ name: 'EnsureChannel',
inSignature: 'a{sv}',
outSignature: 'boa{sv}'
}
],
properties: [
{ name: 'Channels',
signature: 'a(oa{sv})',
access: 'read' }
],
signals: [
{ name: 'NewChannels',
inSignature: 'a(oa{sv})'
},
{ name: 'ChannelClosed',
inSignature: 'o'
}
]
};
let ConnectionRequests = DBus.makeProxyClass(ConnectionRequestsIface);
const CONNECTION_SIMPLE_PRESENCE_NAME = CONNECTION_NAME + '.Interface.SimplePresence';
const ConnectionSimplePresenceIface = {
name: CONNECTION_SIMPLE_PRESENCE_NAME,
methods: [
{ name: 'SetPresence',
inSignature: 'ss'
},
{ name: 'GetPresences',
inSignature: 'au',
outSignature: 'a{u(uss)}'
}
],
signals: [
{ name: 'PresencesChanged',
inSignature: 'a{u(uss)}' }
]
};
let ConnectionSimplePresence = DBus.makeProxyClass(ConnectionSimplePresenceIface);
const ConnectionPresenceType = {
UNSET: 0,
OFFLINE: 1,
AVAILABLE: 2,
AWAY: 3,
EXTENDED_AWAY: 4,
HIDDEN: 5,
BUSY: 6,
UNKNOWN: 7,
ERROR: 8
};
const HandleType = {
NONE: 0,
CONTACT: 1,
ROOM: 2,
LIST: 3,
GROUP: 4
};
const CHANNEL_NAME = TELEPATHY + '.Channel';
const ChannelIface = {
name: CHANNEL_NAME,
signals: [
{ name: 'Closed',
inSignature: '' }
]
};
let Channel = DBus.makeProxyClass(ChannelIface);
const CHANNEL_TEXT_NAME = CHANNEL_NAME + '.Type.Text';
const ChannelTextIface = {
name: CHANNEL_TEXT_NAME,
methods: [
{ name: 'ListPendingMessages',
inSignature: 'b',
outSignature: 'a(uuuuus)'
},
{ name: 'AcknowledgePendingMessages',
inSignature: 'au',
outSignature: ''
},
{ name: 'Send',
inSignature: 'us',
outSignature: ''
}
],
signals: [
{ name: 'Received',
inSignature: 'uuuuus' },
{ name: 'Sent',
inSignature: 'uus' }
]
};
let ChannelText = DBus.makeProxyClass(ChannelTextIface);
const ChannelTextMessageType = {
NORMAL: 0,
ACTION: 1,
NOTICE: 2,
AUTO_REPLY: 3,
DELIVERY_REPORT: 4
};
const CHANNEL_CONTACT_LIST_NAME = CHANNEL_NAME + '.Type.ContactList';
// There is no interface associated with ContactList; it's just a
// special kind of Channel.Interface.Group
const CHANNEL_GROUP_NAME = CHANNEL_NAME + '.Interface.Group';
const ChannelGroupIface = {
name: CHANNEL_GROUP_NAME,
properties: [
{ name: 'Members',
signature: 'au',
access: 'read' }
],
signals: [
{ name: 'MembersChanged',
inSignature: 'sauauauauuu' }
]
};
let ChannelGroup = DBus.makeProxyClass(ChannelGroupIface);
const ACCOUNT_MANAGER_NAME = TELEPATHY + '.AccountManager';
const AccountManagerIface = {
name: ACCOUNT_MANAGER_NAME,
properties: [
{ name: 'ValidAccounts',
signature: 'ao',
access: 'read' }
],
signals: [
{ name: 'AccountValidityChanged',
inSignature: 'ob' }
]
};
let AccountManager = DBus.makeProxyClass(AccountManagerIface);
const ACCOUNT_NAME = TELEPATHY + '.Account';
const AccountIface = {
name: ACCOUNT_NAME,
properties: [
{ name: 'Connection',
signature: 'o',
access: 'read' }
]
};
let Account = DBus.makeProxyClass(AccountIface);
const CHANNEL_DISPATCHER_NAME = TELEPATHY + '.ChannelDispatcher';
const ChannelDispatcherIface = {
name: CHANNEL_DISPATCHER_NAME,
methods: [
{ name: 'EnsureChannel',
inSignature: 'oa{sv}xs',
outSignature: 'o' }
]
};
let ChannelDispatcher = DBus.makeProxyClass(ChannelDispatcherIface);
const CHANNEL_REQUEST_NAME = TELEPATHY + '.ChannelRequest';
const ChannelRequestIface = {
name: CHANNEL_REQUEST_NAME,
methods: [
{ name: 'Proceed',
inSignature: '',
outSignature: '' }
],
signals: [
{ name: 'Failed',
signature: 'ss' },
{ name: 'Succeeded',
signature: '' }
]
};
let ChannelRequest = DBus.makeProxyClass(ChannelRequestIface);

View File

@ -1,210 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Gdk = imports.gi.Gdk;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Shell = imports.gi.Shell;
const Main = imports.ui.main;
/* http://daringfireball.net/2010/07/improved_regex_for_matching_urls */
const _urlRegexp = new RegExp('\\b(([a-z][\\w-]+:(/{1,3}|[a-z0-9%])|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,4}/)([^\\s()<>]+|\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\))+(\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\)|[^\\s`!()\\[\\]{};:\'\\".,<>?«»“”‘’]))', 'gi');
// findUrls:
// @str: string to find URLs in
//
// Searches @str for URLs and returns an array of objects with %url
// properties showing the matched URL string, and %pos properties indicating
// the position within @str where the URL was found.
//
// Return value: the list of match objects, as described above
function findUrls(str) {
let res = [], match;
while ((match = _urlRegexp.exec(str)))
res.push({ url: match[0], pos: match.index });
return res;
}
// spawn:
// @argv: an argv array
//
// Runs @argv in the background, handling any errors that occur
// when trying to start the program.
function spawn(argv) {
try {
trySpawn(argv);
} catch (err) {
_handleSpawnError(argv[0], err);
}
}
// spawnCommandLine:
// @command_line: a command line
//
// Runs @command_line in the background, handling any errors that
// occur when trying to parse or start the program.
function spawnCommandLine(command_line) {
try {
let [success, argv] = GLib.shell_parse_argv(command_line);
trySpawn(argv);
} catch (err) {
_handleSpawnError(command_line, err);
}
}
// trySpawn:
// @argv: an argv array
//
// Runs @argv in the background. If launching @argv fails,
// this will throw an error.
function trySpawn(argv)
{
try {
GLib.spawn_async(null, argv, null,
GLib.SpawnFlags.SEARCH_PATH,
null, null);
} catch (err) {
if (err.code == GLib.SpawnError.G_SPAWN_ERROR_NOENT) {
err.message = _("Command not found");
} else {
// The exception from gjs contains an error string like:
// Error invoking GLib.spawn_command_line_async: Failed to
// execute child process "foo" (No such file or directory)
// We are only interested in the part in the parentheses. (And
// we can't pattern match the text, since it gets localized.)
err.message = err.message.replace(/.*\((.+)\)/, '$1');
}
throw err;
}
}
// trySpawnCommandLine:
// @command_line: a command line
//
// Runs @command_line in the background. If launching @command_line
// fails, this will throw an error.
function trySpawnCommandLine(command_line) {
let success, argv;
try {
[success, argv] = GLib.shell_parse_argv(command_line);
} catch (err) {
// Replace "Error invoking GLib.shell_parse_argv: " with
// something nicer
err.message = err.message.replace(/[^:]*: /, _("Could not parse command:") + "\n");
throw err;
}
trySpawn(argv);
}
function _handleSpawnError(command, err) {
let title = _("Execution of '%s' failed:").format(command);
Main.notifyError(title, err.message);
}
// killall:
// @processName: a process name
//
// Kills @processName. If no process with the given name is found,
// this will fail silently.
function killall(processName) {
try {
// pkill is more portable than killall, but on Linux at least
// it won't match if you pass more than 15 characters of the
// process name... However, if you use the '-f' flag to match
// the entire command line, it will work, but we have to be
// careful in that case that we can match
// '/usr/bin/processName' but not 'gedit processName.c' or
// whatever...
let argv = ['pkill', '-f', '^([^ ]*/)?' + processName + '($| )'];
GLib.spawn_sync(null, argv, null, GLib.SpawnFlags.SEARCH_PATH, null, null);
// It might be useful to return success/failure, but we'd need
// a wrapper around WIFEXITED and WEXITSTATUS. Since none of
// the current callers care, we don't bother.
} catch (e) {
logError(e, 'Failed to kill ' + 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(' ');
}

View File

@ -21,77 +21,26 @@ let METRICS = {
overviewFpsSubsequent:
{ description: "Frames rate when going to the overview, second time",
units: "frames / s" },
overviewFps5Windows:
{ description: "Frames rate when going to the overview, 5 windows open",
units: "frames / s" },
overviewFps10Windows:
{ description: "Frames rate when going to the overview, 10 windows open",
units: "frames / s" },
overviewFps5Maximized:
{ description: "Frames rate when going to the overview, 5 maximized windows open",
units: "frames / s" },
overviewFps10Maximized:
{ description: "Frames rate when going to the overview, 10 maximized windows open",
units: "frames / s" },
overviewFps5Alpha:
{ description: "Frames rate when going to the overview, 5 alpha-transparent windows open",
units: "frames / s" },
overviewFps10Alpha:
{ description: "Frames rate when going to the overview, 10 alpha-transparent windows open",
units: "frames / s" },
usedAfterOverview:
{ description: "Malloc'ed bytes after the overview is shown once",
units: "B" },
leakedAfterOverview:
{ description: "Additional malloc'ed bytes the second time the overview is shown",
units: "B" },
applicationsShowTimeFirst:
{ description: "Time to switch to applications view, first time",
units: "us" },
applicationsShowTimeSubsequent:
{ description: "Time to switch to applications view, second time",
units: "us"}
units: "B" }
};
let WINDOW_CONFIGS = [
{ width: 640, height: 480, alpha: false, maximized: false, count: 1, metric: 'overviewFpsSubsequent' },
{ width: 640, height: 480, alpha: false, maximized: false, count: 5, metric: 'overviewFps5Windows' },
{ width: 640, height: 480, alpha: false, maximized: false, count: 10, metric: 'overviewFps10Windows' },
{ width: 640, height: 480, alpha: false, maximized: true, count: 5, metric: 'overviewFps5Maximized' },
{ width: 640, height: 480, alpha: false, maximized: true, count: 10, metric: 'overviewFps10Maximized' },
{ width: 640, height: 480, alpha: true, maximized: false, count: 5, metric: 'overviewFps5Alpha' },
{ width: 640, height: 480, alpha: true, maximized: false, count: 10, metric: 'overviewFps10Alpha' }
];
function run() {
Scripting.defineScriptEvent("overviewShowStart", "Starting to show the overview");
Scripting.defineScriptEvent("overviewShowDone", "Overview finished showing");
Scripting.defineScriptEvent("afterShowHide", "After a show/hide cycle for the overview");
Scripting.defineScriptEvent("applicationsShowStart", "Starting to switch to applications view");
Scripting.defineScriptEvent("applicationsShowDone", "Done switching to applications view");
Main.overview.connect('shown', function() {
Scripting.scriptEvent('overviewShowDone');
});
yield Scripting.sleep(1000);
for (let i = 0; i < 2 * WINDOW_CONFIGS.length; i++) {
// We go to the overview twice for each configuration; the first time
// to calculate the mipmaps for the windows, the second time to get
// a clean set of numbers.
if ((i % 2) == 0) {
let config = WINDOW_CONFIGS[i / 2];
yield Scripting.destroyTestWindows();
for (let k = 0; k < config.count; k++)
yield Scripting.createTestWindow(config.width, config.height, config.alpha, config.maximized);
yield Scripting.waitTestWindows();
yield Scripting.sleep(1000);
yield Scripting.waitLeisure();
}
yield Scripting.waitLeisure();
for (let i = 0; i < 2; i++) {
Scripting.scriptEvent('overviewShowStart');
Main.overview.show();
@ -104,21 +53,6 @@ function run() {
Scripting.collectStatistics();
Scripting.scriptEvent('afterShowHide');
}
yield Scripting.destroyTestWindows();
yield Scripting.sleep(1000);
Main.overview.show();
yield Scripting.waitLeisure();
for (let i = 0; i < 2; i++) {
Scripting.scriptEvent('applicationsShowStart');
Main.overview.viewSelector.switchTab('applications');
yield Scripting.waitLeisure();
Scripting.scriptEvent('applicationsShowDone');
Main.overview.viewSelector.switchTab('windows');
yield Scripting.waitLeisure();
}
}
let showingOverview = false;
@ -130,8 +64,6 @@ let mallocUsedSize = 0;
let overviewShowCount = 0;
let firstOverviewUsedSize;
let haveSwapComplete = false;
let applicationsShowStart;
let applicationsShowCount = 0;
function script_overviewShowStart(time) {
showingOverview = true;
@ -147,18 +79,6 @@ function script_overviewShowDone(time) {
finishedShowingOverview = true;
}
function script_applicationsShowStart(time) {
applicationsShowStart = time;
}
function script_applicationsShowDone(time) {
applicationsShowCount++;
if (applicationsShowCount == 1)
METRICS.applicationsShowTimeFirst.value = time - applicationsShowStart;
else
METRICS.applicationsShowTimeSubsequent.value = time - applicationsShowStart;
}
function script_afterShowHide(time) {
if (overviewShowCount == 1) {
METRICS.usedAfterOverview.value = mallocUsedSize;
@ -193,15 +113,9 @@ function _frameDone(time) {
if (overviewShowCount == 1) {
METRICS.overviewLatencyFirst.value = overviewLatency;
METRICS.overviewFpsFirst.value = fps;
} else if (overviewShowCount == 2) {
} else {
METRICS.overviewLatencySubsequent.value = overviewLatency;
}
// Other than overviewFpsFirst, we collect FPS metrics the second
// we show each window configuration. overviewShowCount is 1,2,3...
if (overviewShowCount % 2 == 0) {
let config = WINDOW_CONFIGS[(overviewShowCount / 2) - 1];
METRICS[config.metric].value = fps;
METRICS.overviewFpsSubsequent.value = fps;
}
}
}

View File

@ -0,0 +1,97 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Signals = imports.signals;
const Gettext = imports.gettext;
const FORMAT_KEY = 'format';
const SHOW_DATE_KEY = 'show-date';
const SHOW_SECONDS_KEY = 'show-seconds';
function ClockPreferences(uiFile) {
this._init(uiFile);
};
ClockPreferences.prototype = {
_init: function(uiFile) {
let builder = new Gtk.Builder();
builder.add_from_file(uiFile);
this._dialog = builder.get_object('prefs-dialog');
this._dialog.connect('response', Lang.bind(this, this._onResponse));
this._12hrRadio = builder.get_object('12hr_radio');
this._24hrRadio = builder.get_object('24hr_radio');
this._dateCheck = builder.get_object('date_check');
this._secondsCheck = builder.get_object('seconds_check');
delete builder;
this._settings = new Gio.Settings({ schema: 'org.gnome.shell.clock' });
this._notifyId = this._settings.connect('changed',
Lang.bind(this,
this._updateDialog));
this._12hrRadio.connect('toggled', Lang.bind(this,
function() {
let format = this._12hrRadio.active ? '12-hour' : '24-hour';
this._settings.set_string(FORMAT_KEY, format);
}));
this._dateCheck.connect('toggled', Lang.bind(this,
function() {
this._settings.set_boolean(SHOW_DATE_KEY,
this._dateCheck.active);
}));
this._secondsCheck.connect('toggled', Lang.bind(this,
function() {
this._settings.set_boolean(SHOW_SECONDS_KEY,
this._secondsCheck.active);
}));
this._updateDialog();
},
show: function() {
this._dialog.show_all();
},
_updateDialog: function() {
let format = this._settings.get_string(FORMAT_KEY);
this._12hrRadio.active = (format == "12-hour");
this._24hrRadio.active = (format == "24-hour");
this._dateCheck.active = this._settings.get_boolean(SHOW_DATE_KEY);
this._secondsCheck.active = this._settings.get_boolean(SHOW_SECONDS_KEY);
},
_onResponse: function() {
this._dialog.destroy();
this._settings.disconnect(this._notifyId);
this.emit('destroy');
}
};
Signals.addSignalMethods(ClockPreferences.prototype);
function main(params) {
if ('progName' in params)
GLib.set_prgname(params['progName']);
if ('localeDir' in params)
Gettext.bindtextdomain('gnome-shell', params['localeDir']);
Gtk.init(null, null);
let clockPrefs = new ClockPreferences(params['uiFile']);
clockPrefs.connect('destroy',
function() {
Gtk.main_quit();
});
clockPrefs.show();
Gtk.main();
}

View File

@ -4,7 +4,6 @@ const Clutter = imports.gi.Clutter;
const Gdk = imports.gi.Gdk;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
@ -16,8 +15,6 @@ const POPUP_APPICON_SIZE = 96;
const POPUP_SCROLL_TIME = 0.10; // seconds
const POPUP_FADE_TIME = 0.1; // seconds
const APP_ICON_HOVER_TIMEOUT = 200; // milliseconds
const DISABLE_HOVER_TIMEOUT = 500; // milliseconds
const THUMBNAIL_DEFAULT_SIZE = 256;
@ -37,8 +34,7 @@ function AltTabPopup() {
AltTabPopup.prototype = {
_init : function() {
this.actor = new Shell.GenericContainer({ name: 'altTabPopup',
reactive: true,
visible: false });
reactive: true });
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
@ -53,8 +49,6 @@ AltTabPopup.prototype = {
this._thumbnailTimeoutId = 0;
this._motionTimeoutId = 0;
this.thumbnailsVisible = false;
// Initially disable hover so we ignore the enter-event if
// the switcher appears underneath the current pointer location
this._disableHover();
@ -87,7 +81,7 @@ AltTabPopup.prototype = {
let [childMinHeight, childNaturalHeight] = this._appSwitcher.actor.get_preferred_height(primary.width - hPadding);
let [childMinWidth, childNaturalWidth] = this._appSwitcher.actor.get_preferred_width(childNaturalHeight);
childBox.x1 = Math.max(primary.x + leftPadding, primary.x + Math.floor((primary.width - childNaturalWidth) / 2));
childBox.x2 = Math.min(primary.x + primary.width - rightPadding, childBox.x1 + childNaturalWidth);
childBox.x2 = Math.min(childBox.x1 + primary.width - hPadding, childBox.x1 + childNaturalWidth);
childBox.y1 = primary.y + Math.floor((primary.height - childNaturalHeight) / 2);
childBox.y2 = childBox.y1 + childNaturalHeight;
this._appSwitcher.actor.allocate(childBox, flags);
@ -97,6 +91,8 @@ AltTabPopup.prototype = {
// those calculations
if (this._thumbnails) {
let icon = this._appIcons[this._currentApp].actor;
// Force a stage relayout to make sure we get the correct position
global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, 0, 0);
let [posX, posY] = icon.get_transformed_position();
let thumbnailCenter = posX + icon.width / 2;
let [childMinWidth, childNaturalWidth] = this._thumbnails.actor.get_preferred_width(-1);
@ -119,7 +115,7 @@ AltTabPopup.prototype = {
}
},
show : function(backward, switch_group) {
show : function(backward) {
let tracker = Shell.WindowTracker.get_default();
let apps = tracker.get_running_apps ('');
@ -130,41 +126,44 @@ AltTabPopup.prototype = {
return false;
this._haveModal = true;
this.actor.connect('key-press-event', Lang.bind(this, this._keyPressEvent));
this.actor.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent));
this._keyPressEventId = global.stage.connect('key-press-event', Lang.bind(this, this._keyPressEvent));
this._keyReleaseEventId = global.stage.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent));
this.actor.connect('button-press-event', Lang.bind(this, this._clickedOutside));
this.actor.connect('scroll-event', Lang.bind(this, this._onScroll));
this._appSwitcher = new AppSwitcher(apps, this);
this._appSwitcher = new AppSwitcher(apps);
this.actor.add_actor(this._appSwitcher.actor);
this._appSwitcher.connect('item-activated', Lang.bind(this, this._appActivated));
this._appSwitcher.connect('item-entered', Lang.bind(this, this._appEntered));
this._appIcons = this._appSwitcher.icons;
// Need to force an allocation so we can figure out whether we
// need to scroll when selecting
this.actor.opacity = 0;
this.actor.show();
this.actor.get_allocation_box();
// Make the initial selection
if (switch_group) {
if (backward) {
this._select(0, this._appIcons[0].cachedWindows.length - 1);
} else {
if (this._appIcons[0].cachedWindows.length > 1)
this._select(0, 1);
else
this._select(0, 0);
}
} else if (this._appIcons.length == 1) {
this._select(0);
if (this._appIcons.length == 1) {
if (!backward && this._appIcons[0].cachedWindows.length > 1) {
// For compatibility with the multi-app case below
this._select(0, 1, true);
} else
this._select(0);
} else if (backward) {
this._select(this._appIcons.length - 1);
} else {
this._select(1);
let firstWindows = this._appIcons[0].cachedWindows;
if (firstWindows.length > 1) {
let curAppNextWindow = firstWindows[1];
let nextAppWindow = this._appIcons[1].cachedWindows[0];
// If the next window of the current app is more-recently-used
// than the first window of the next app, then select it.
if (curAppNextWindow.get_workspace() == global.screen.get_active_workspace() &&
curAppNextWindow.get_user_time() > nextAppWindow.get_user_time())
this._select(0, 1, true);
else
this._select(1);
} else {
this._select(1);
}
}
// There's a race condition; if the user released Alt before
@ -178,6 +177,8 @@ AltTabPopup.prototype = {
return false;
}
this.actor.opacity = 0;
this.actor.show();
Tweener.addTween(this.actor,
{ opacity: 255,
time: POPUP_FADE_TIME,
@ -211,27 +212,42 @@ AltTabPopup.prototype = {
_keyPressEvent : function(actor, event) {
let keysym = event.get_key_symbol();
let event_state = Shell.get_event_state(event);
let backwards = event_state & Clutter.ModifierType.SHIFT_MASK;
let action = global.screen.get_display().get_keybinding_action(event.get_key_code(), event_state);
let shift = (Shell.get_event_state(event) & Clutter.ModifierType.SHIFT_MASK);
// X allows servers to represent Shift+Tab in two different ways
if (shift && keysym == Clutter.Tab)
keysym = Clutter.ISO_Left_Tab;
this._disableHover();
if (keysym == Clutter.Escape) {
if (keysym == Clutter.grave)
this._select(this._currentApp, this._nextWindow());
else if (keysym == Clutter.asciitilde)
this._select(this._currentApp, this._previousWindow());
else if (keysym == Clutter.Escape)
this.destroy();
} else if (action == Meta.KeyBindingAction.SWITCH_GROUP) {
this._select(this._currentApp, backwards ? this._previousWindow() : this._nextWindow());
} else if (action == Meta.KeyBindingAction.SWITCH_WINDOWS) {
this._select(backwards ? this._previousApp() : this._nextApp());
} else if (this._thumbnailsFocused) {
if (keysym == Clutter.Left)
else if (this._thumbnailsFocused) {
if (keysym == Clutter.Tab) {
if (this._currentWindow == this._appIcons[this._currentApp].cachedWindows.length - 1)
this._select(this._nextApp());
else
this._select(this._currentApp, this._nextWindow());
} else if (keysym == Clutter.ISO_Left_Tab) {
if (this._currentWindow == 0 || this._currentWindow == -1)
this._select(this._previousApp());
else
this._select(this._currentApp, this._previousWindow());
} else if (keysym == Clutter.Left)
this._select(this._currentApp, this._previousWindow());
else if (keysym == Clutter.Right)
this._select(this._currentApp, this._nextWindow());
else if (keysym == Clutter.Up)
this._select(this._currentApp, null, true);
} else {
if (keysym == Clutter.Left)
if (keysym == Clutter.Tab)
this._select(this._nextApp());
else if (keysym == Clutter.ISO_Left_Tab)
this._select(this._previousApp());
else if (keysym == Clutter.Left)
this._select(this._previousApp());
else if (keysym == Clutter.Right)
this._select(this._nextApp());
@ -348,35 +364,30 @@ AltTabPopup.prototype = {
this.destroy();
},
_popModal: function() {
if (this._haveModal) {
Main.popModal(this.actor);
this._haveModal = false;
}
},
destroy : function() {
this._popModal();
if (this.actor.visible) {
Tweener.addTween(this.actor,
{ opacity: 0,
time: POPUP_FADE_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
this.actor.destroy();
})
});
} else
this.actor.destroy();
Tweener.addTween(this.actor,
{ opacity: 0,
time: POPUP_FADE_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
this.actor.destroy();
})
});
},
_onDestroy : function() {
this._popModal();
if (this._haveModal)
Main.popModal(this.actor);
if (this._thumbnails)
this._destroyThumbnails();
if (this._keyPressEventId)
global.stage.disconnect(this._keyPressEventId);
if (this._keyReleaseEventId)
global.stage.disconnect(this._keyReleaseEventId);
if (this._motionTimeoutId != 0)
Mainloop.source_remove(this._motionTimeoutId);
if (this._thumbnailTimeoutId != 0)
@ -447,15 +458,11 @@ AltTabPopup.prototype = {
},
_destroyThumbnails : function() {
let thumbnailsActor = this._thumbnails.actor;
Tweener.addTween(thumbnailsActor,
Tweener.addTween(this._thumbnails.actor,
{ opacity: 0,
time: THUMBNAIL_FADE_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
thumbnailsActor.destroy();
this.thumbnailsVisible = false;
})
onComplete: function() { this.destroy(); }
});
this._thumbnails = null;
},
@ -471,8 +478,7 @@ AltTabPopup.prototype = {
Tweener.addTween(this._thumbnails.actor,
{ opacity: 255,
time: THUMBNAIL_FADE_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function () { this.thumbnailsVisible = true; })
transition: 'easeOutQuad'
});
}
};
@ -513,11 +519,16 @@ SwitcherList.prototype = {
this._leftArrow = new St.DrawingArea({ style_class: 'switcher-arrow',
pseudo_class: 'highlighted' });
this._leftArrow.connect('repaint', Lang.bind(this,
function() { _drawArrow(this._leftArrow, St.Side.LEFT); }));
function (area) {
Shell.draw_box_pointer(area, Shell.PointerDirection.LEFT);
}));
this._rightArrow = new St.DrawingArea({ style_class: 'switcher-arrow',
pseudo_class: 'highlighted' });
this._rightArrow.connect('repaint', Lang.bind(this,
function() { _drawArrow(this._rightArrow, St.Side.RIGHT); }));
function (area) {
Shell.draw_box_pointer(area, Shell.PointerDirection.RIGHT);
}));
this.actor.add_actor(this._leftArrow);
this.actor.add_actor(this._rightArrow);
@ -573,30 +584,24 @@ SwitcherList.prototype = {
this._rightArrow.opacity = this._rightGradient.opacity;
},
addItem : function(item, label) {
let bbox = new St.Button({ style_class: 'item-box',
reactive: true });
addItem : function(item) {
let bbox = new St.Clickable({ style_class: 'item-box',
reactive: true });
bbox.set_child(item);
this._list.add_actor(bbox);
let n = this._items.length;
bbox.connect('clicked', Lang.bind(this, function() { this._onItemClicked(n); }));
bbox.connect('enter-event', Lang.bind(this, function() { this._onItemEnter(n); }));
bbox.label_actor = label;
bbox.connect('clicked', Lang.bind(this, function () {
this._itemActivated(n);
}));
bbox.connect('enter-event', Lang.bind(this, function () {
this._itemEntered(n);
}));
this._items.push(bbox);
},
_onItemClicked: function (index) {
this._itemActivated(index);
},
_onItemEnter: function (index) {
this._itemEntered(index);
},
addSeparator: function () {
let box = new St.Bin({ style_class: 'separator' });
this._separator = box;
@ -604,18 +609,16 @@ SwitcherList.prototype = {
},
highlight: function(index, justOutline) {
if (this._highlighted != -1) {
this._items[this._highlighted].remove_style_pseudo_class('outlined');
this._items[this._highlighted].remove_style_pseudo_class('selected');
}
if (this._highlighted != -1)
this._items[this._highlighted].style_class = 'item-box';
this._highlighted = index;
if (this._highlighted != -1) {
if (justOutline)
this._items[this._highlighted].add_style_pseudo_class('outlined');
this._items[this._highlighted].style_class = 'outlined-item-box';
else
this._items[this._highlighted].add_style_pseudo_class('selected');
this._items[this._highlighted].style_class = 'selected-item-box';
}
let monitor = global.get_primary_monitor();
@ -801,7 +804,7 @@ AppIcon.prototype = {
this.actor = new St.BoxLayout({ style_class: 'alt-tab-app',
vertical: true });
this.icon = null;
this._iconBin = new St.Bin({ x_fill: true, y_fill: true });
this._iconBin = new St.Bin();
this.actor.add(this._iconBin, { x_fill: false, y_fill: false } );
this.label = new St.Label({ text: this.app.get_name() });
@ -815,14 +818,14 @@ AppIcon.prototype = {
}
};
function AppSwitcher(apps, altTabPopup) {
this._init(apps, altTabPopup);
function AppSwitcher(apps) {
this._init(apps);
}
AppSwitcher.prototype = {
__proto__ : SwitcherList.prototype,
_init : function(apps, altTabPopup) {
_init : function(apps) {
SwitcherList.prototype._init.call(this, true);
// Construct the AppIcons, sort by time, add to the popup
@ -854,8 +857,6 @@ AppSwitcher.prototype = {
this._curApp = -1;
this._iconSize = 0;
this._altTabPopup = altTabPopup;
this._mouseTimeOutId = 0;
},
_getPreferredHeight: function (actor, forWidth, alloc) {
@ -863,19 +864,17 @@ AppSwitcher.prototype = {
while(this._items.length > 1 && this._items[j].style_class != 'item-box') {
j++;
}
let themeNode = this._items[j].get_theme_node();
let iconPadding = themeNode.get_horizontal_padding();
let iconBorder = themeNode.get_border_width(St.Side.LEFT) + themeNode.get_border_width(St.Side.RIGHT);
let iconPadding = this._items[j].get_theme_node().get_horizontal_padding();
let [iconMinHeight, iconNaturalHeight] = this.icons[j].label.get_preferred_height(-1);
let iconSpacing = iconNaturalHeight + iconPadding + iconBorder;
let iconSpacing = iconNaturalHeight + iconPadding;
let totalSpacing = this._list.spacing * (this._items.length - 1);
if (this._separator)
totalSpacing += this._separator.width + this._list.spacing;
// We just assume the whole screen here due to weirdness happing with the passed width
let primary = global.get_primary_monitor();
let focus = global.get_focus_monitor();
let parentPadding = this.actor.get_parent().get_theme_node().get_horizontal_padding();
let availWidth = primary.width - parentPadding - this.actor.get_theme_node().get_horizontal_padding();
let availWidth = focus.width - parentPadding - this.actor.get_theme_node().get_horizontal_padding();
let height = 0;
for(let i = 0; i < iconSizes.length; i++) {
@ -920,29 +919,6 @@ AppSwitcher.prototype = {
}
},
// We override SwitcherList's _onItemEnter method to delay
// activation when the thumbnail list is open
_onItemEnter: function (index) {
if (this._mouseTimeOutId != 0)
Mainloop.source_remove(this._mouseTimeOutId);
if (this._altTabPopup.thumbnailsVisible) {
this._mouseTimeOutId = Mainloop.timeout_add(APP_ICON_HOVER_TIMEOUT,
Lang.bind(this, function () {
this._enterItem(index);
this._mouseTimeOutId = 0;
return false;
}));
} else
this._itemEntered(index);
},
_enterItem: function(index) {
let [x, y, mask] = global.get_pointer();
let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y);
if (this._items[index].contains(pickedActor))
this._itemEntered(index);
},
// We override SwitcherList's highlight() method to also deal with
// the AppSwitcher->ThumbnailList arrows. Apps with only 1 window
// will hide their arrows by default, but show them when their
@ -971,11 +947,14 @@ AppSwitcher.prototype = {
_addIcon : function(appIcon) {
this.icons.push(appIcon);
this.addItem(appIcon.actor, appIcon.label);
this.addItem(appIcon.actor);
let n = this._arrows.length;
let arrow = new St.DrawingArea({ style_class: 'switcher-arrow' });
arrow.connect('repaint', function() { _drawArrow(arrow, St.Side.BOTTOM); });
arrow.connect('repaint', Lang.bind(this,
function (area) {
Shell.draw_box_pointer(area, Shell.PointerDirection.DOWN);
}));
this._list.add_actor(arrow);
this._arrows.push(arrow);
@ -1041,12 +1020,9 @@ ThumbnailList.prototype = {
this._labels.push(bin);
bin.add_actor(name);
box.add_actor(bin);
this.addItem(box, name);
} else {
this.addItem(box, null);
}
this.addItem(box);
}
},
@ -1064,9 +1040,6 @@ ThumbnailList.prototype = {
for (let i = 0; i < this._thumbnailBins.length; i++) {
let mutterWindow = this._windows[i].get_compositor_private();
if (!mutterWindow)
continue;
let windowTexture = mutterWindow.get_texture ();
let [width, height] = windowTexture.get_size();
let scale = Math.min(1.0, THUMBNAIL_DEFAULT_SIZE / width, availHeight / height);
@ -1084,46 +1057,3 @@ ThumbnailList.prototype = {
this._thumbnailBins = new Array();
}
};
function _drawArrow(area, side) {
let themeNode = area.get_theme_node();
let borderColor = themeNode.get_border_color(side);
let bodyColor = themeNode.get_foreground_color();
let [width, height] = area.get_surface_size ();
let cr = area.get_context();
cr.setLineWidth(1.0);
Clutter.cairo_set_source_color(cr, borderColor);
switch (side) {
case St.Side.TOP:
cr.moveTo(0, height);
cr.lineTo(Math.floor(width * 0.5), 0);
cr.lineTo(width, height);
break;
case St.Side.BOTTOM:
cr.moveTo(width, 0);
cr.lineTo(Math.floor(width * 0.5), height);
cr.lineTo(0, 0);
break;
case St.Side.LEFT:
cr.moveTo(width, height);
cr.lineTo(0, Math.floor(height * 0.5));
cr.lineTo(width, 0);
break;
case St.Side.RIGHT:
cr.moveTo(0, 0);
cr.lineTo(width, Math.floor(height * 0.5));
cr.lineTo(0, height);
break;
}
cr.strokePreserve();
Clutter.cairo_set_source_color(cr, bodyColor);
cr.fill();
}

View File

@ -1,14 +1,14 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Shell = imports.gi.Shell;
const Lang = imports.lang;
const Signals = imports.signals;
const Meta = imports.gi.Meta;
const St = imports.gi.St;
const Mainloop = imports.mainloop;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const AppFavorites = imports.ui.appFavorites;
const DND = imports.ui.dnd;
@ -21,9 +21,7 @@ const Tweener = imports.ui.tweener;
const Workspace = imports.ui.workspace;
const Params = imports.misc.params;
const MAX_APPLICATION_WORK_MILLIS = 75;
const MENU_POPUP_TIMEOUT = 600;
const SCROLL_TIME = 0.1;
function AlphabeticalView() {
this._init();
@ -31,34 +29,10 @@ function AlphabeticalView() {
AlphabeticalView.prototype = {
_init: function() {
this._grid = new IconGrid.IconGrid({ xAlign: St.Align.START });
this.actor = new St.BoxLayout({ vertical: true });
this._grid = new IconGrid.IconGrid();
this._appSystem = Shell.AppSystem.get_default();
this._pendingAppLaterId = 0;
this._apps = [];
this._filterApp = null;
let box = new St.BoxLayout({ vertical: true });
box.add(this._grid.actor, { y_align: St.Align.START, expand: true });
this.actor = new St.ScrollView({ x_fill: true,
y_fill: false,
y_align: St.Align.START,
style_class: 'vfade' });
this.actor.add_actor(box);
this.actor.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
this.actor.connect('notify::mapped', Lang.bind(this,
function() {
if (!this.actor.mapped)
return;
let adjustment = this.actor.vscroll.adjustment;
let direction = Overview.SwipeScrollDirection.VERTICAL;
Main.overview.setScrollAdjustment(adjustment, direction);
// Reset scroll on mapping
adjustment.value = 0;
}));
this.actor.add(this._grid.actor, { y_align: St.Align.START, expand: true });
},
_removeAll: function() {
@ -66,75 +40,20 @@ AlphabeticalView.prototype = {
this._apps = [];
},
_addApp: function(appInfo) {
let appIcon = new AppWellIcon(this._appSystem.get_app(appInfo.get_id()));
_addApp: function(app) {
let appIcon = new AppWellIcon(this._appSystem.get_app(app.get_id()));
appIcon.connect('launching', Lang.bind(this, function() {
this.emit('launching');
}));
appIcon._draggable.connect('drag-begin', Lang.bind(this, function() {
this.emit('drag-begin');
}));
this._grid.addItem(appIcon.actor);
appIcon.actor.connect('key-focus-in', Lang.bind(this, this._ensureIconVisible));
appIcon._appInfo = appInfo;
if (this._filterApp && !this._filterApp(appInfo))
appIcon.actor.hide();
this._apps.push(appIcon);
},
_ensureIconVisible: function(icon) {
let adjustment = this.actor.vscroll.adjustment;
let [value, lower, upper, stepIncrement, pageIncrement, pageSize] = adjustment.get_values();
let offset = 0;
let vfade = this.actor.get_effect("vfade");
if (vfade)
offset = vfade.fade_offset;
// If this gets called as part of a right-click, the actor
// will be needs_allocation, and so "icon.y" would return 0
let box = icon.get_allocation_box();
if (box.y1 < value + offset)
value = Math.max(0, box.y1 - offset);
else if (box.y2 > value + pageSize - offset)
value = Math.min(upper, box.y2 + offset - pageSize);
else
return;
Tweener.addTween(adjustment,
{ value: value,
time: SCROLL_TIME,
transition: 'easeOutQuad' });
},
setFilter: function(filter) {
this._filterApp = filter;
for (let i = 0; i < this._apps.length; i++)
this._apps[i].actor.visible = filter(this._apps[i]._appInfo);
},
// Create actors for the applications in an idle to avoid blocking
// for too long; see bug 647778
_addPendingApps: function() {
let i;
let startTimeMillis = new Date().getTime();
for (i = 0; i < this._pendingAppIds.length; i++) {
let id = this._pendingAppIds[i];
this._addApp(this._pendingApps[id]);
let currentTimeMillis = new Date().getTime();
if (currentTimeMillis - startTimeMillis > MAX_APPLICATION_WORK_MILLIS)
break;
}
this._pendingAppIds.splice(0, i + 1);
if (this._pendingAppIds.length > 0) {
return true;
} else {
this._pendingAppLaterId = 0;
this._pendingAppIds = null;
this._pendingApps = null;
return false;
}
},
refresh: function(apps) {
let ids = [];
for (let i in apps)
@ -145,15 +64,14 @@ AlphabeticalView.prototype = {
this._removeAll();
this._pendingAppIds = ids;
this._pendingApps = apps;
if (this._pendingAppLaterId)
Meta.later_remove(this._pendingAppLaterId);
this._pendingAppLaterId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
Lang.bind(this, this._addPendingApps));
for (let i = 0; i < ids.length; i++) {
this._addApp(apps[ids[i]]);
}
}
};
Signals.addSignalMethods(AlphabeticalView.prototype);
function ViewByCategories() {
this._init();
}
@ -161,121 +79,59 @@ function ViewByCategories() {
ViewByCategories.prototype = {
_init: function() {
this._appSystem = Shell.AppSystem.get_default();
this.actor = new St.BoxLayout({ style_class: 'all-app' });
this.actor = new St.BoxLayout({ vertical: true });
this.actor._delegate = this;
this._view = new AlphabeticalView();
// categories can be -1 (the All view) or 0...n-1, where n
// is the number of sections
// -2 is a flag to indicate that nothing is selected
// (used only before the actor is mapped the first time)
this._currentCategory = -2;
this._filters = new St.BoxLayout({ vertical: true, reactive: true });
this._filters.connect('scroll-event', Lang.bind(this, this._scrollFilter));
this.actor.add(this._view.actor, { expand: true, x_fill: true, y_fill: true });
this.actor.add(this._filters, { expand: false, y_fill: false, y_align: St.Align.START });
// Always select the "All" filter when switching to the app view
this.actor.connect('notify::mapped', Lang.bind(this,
function() {
if (this.actor.mapped && this._allFilter)
this._selectCategory(-1);
}));
this._sections = [];
// We need a dummy actor to catch the keyboard focus if the
// user Ctrl-Alt-Tabs here before the deferred work creates
// our real contents
this._focusDummy = new St.Bin({ can_focus: true });
this.actor.add(this._focusDummy);
},
_scrollFilter: function(actor, event) {
let direction = event.get_scroll_direction();
if (direction == Clutter.ScrollDirection.UP)
this._selectCategory(Math.max(this._currentCategory - 1, -1))
else if (direction == Clutter.ScrollDirection.DOWN)
this._selectCategory(Math.min(this._currentCategory + 1, this._sections.length - 1));
},
_selectCategory: function(num) {
if (this._currentCategory == num) // nothing to do
return;
this._currentCategory = num;
if (num != -1)
this._allFilter.remove_style_pseudo_class('selected');
else
this._allFilter.add_style_pseudo_class('selected');
this._view.setFilter(Lang.bind(this, function(app) {
if (num == -1)
return true;
return this._sections[num].name == app.get_section();
}));
for (let i = 0; i < this._sections.length; i++) {
if (i == num)
this._sections[i].filterActor.add_style_pseudo_class('selected');
else
this._sections[i].filterActor.remove_style_pseudo_class('selected');
}
},
_addFilter: function(name, num) {
let button = new St.Button({ label: GLib.markup_escape_text (name, -1),
style_class: 'app-filter',
x_align: St.Align.START,
can_focus: true });
this._filters.add(button, { expand: true, x_fill: true, y_fill: false });
button.connect('clicked', Lang.bind(this, function() {
this._selectCategory(num);
}));
if (num != -1)
this._sections[num] = { filterActor: button,
name: name };
else
this._allFilter = button;
},
_removeAll: function() {
this._sections = [];
this._filters.destroy_children();
},
refresh: function(apps) {
_updateSections: function(apps) {
this._removeAll();
let sections = this._appSystem.get_sections();
this._apps = apps;
/* Translators: Filter to display all applications */
this._addFilter(_("All"), -1);
if (!sections)
return;
for (let i = 0; i < sections.length; i++) {
if (i) {
let actor = new St.Bin({ style_class: 'app-section-divider' });
let divider = new St.Bin({ style_class: 'app-section-divider-container',
child: actor,
x_fill: true });
for (let i = 0; i < sections.length; i++)
this._addFilter(sections[i], i);
this.actor.add(divider, { y_fill: false, expand: true });
}
let _apps = apps.filter(function(app) {
return app.get_section() == sections[i];
});
this._sections[i] = { view: new AlphabeticalView(),
apps: _apps,
name: sections[i] };
this._sections[i].view.connect('launching', Lang.bind(this, function() {
this.emit('launching');
}));
this._sections[i].view.connect('drag-begin', Lang.bind(this, function() {
this.emit('drag-begin');
}));
this.actor.add(this._sections[i].view.actor, { y_align: St.Align.START, expand: true });
}
},
this._selectCategory(-1);
this._view.refresh(apps);
_removeAll: function() {
this.actor.destroy_children();
this._sections.forEach(function (section) { section.view.disconnectAll(); });
if (this._focusDummy) {
let focused = this._focusDummy.has_key_focus();
this._focusDummy.destroy();
this._focusDummy = null;
if (focused)
this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
this._sections = [];
},
refresh: function(apps) {
this._updateSections(apps);
for (let i = 0; i < this._sections.length; i++) {
this._sections[i].view.refresh(this._sections[i].apps);
}
}
};
Signals.addSignalMethods(ViewByCategories.prototype);
/* This class represents a display containing a collection of application items.
* The applications are sorted based on their name.
*/
@ -290,8 +146,17 @@ AllAppDisplay.prototype = {
Main.queueDeferredWork(this._workId);
}));
this._scrollView = new St.ScrollView({ x_fill: true,
y_fill: false,
vshadows: true });
this.actor = new St.Bin({ style_class: 'all-app',
y_align: St.Align.START,
child: this._scrollView });
this._appView = new ViewByCategories();
this.actor = new St.Bin({ child: this._appView.actor, x_fill: true, y_fill: true });
this._scrollView.add_actor(this._appView.actor);
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));
},
@ -304,6 +169,8 @@ AllAppDisplay.prototype = {
this._appView.refresh(apps);
}
};
Signals.addSignalMethods(AllAppDisplay.prototype);
function BaseAppSearchProvider() {
this._init();
@ -323,26 +190,17 @@ BaseAppSearchProvider.prototype = {
return null;
return { 'id': resultId,
'name': app.get_name(),
'createIcon': function(size) {
return app.create_icon_texture(size);
}
};
'icon': app.create_icon_texture(Search.RESULT_ICON_SIZE)};
},
activateResult: function(id, params) {
params = Params.parse(params, { workspace: null,
timestamp: null });
activateResult: function(id) {
let app = this._appSys.get_app(id);
app.activate(params.workspace ? params.workspace.index() : -1);
app.activate();
},
dragActivateResult: function(id, params) {
params = Params.parse(params, { workspace: null,
timestamp: null });
dragActivateResult: function(id) {
let app = this._appSys.get_app(id);
app.open_new_window(params.workspace ? params.workspace.index() : -1);
app.open_new_window();
}
};
@ -369,6 +227,10 @@ AppSearchProvider.prototype = {
let app = this._appSys.get_app(resultMeta['id']);
let icon = new AppWellIcon(app);
return icon.actor;
},
expandSearch: function(terms) {
log('TODO expand search');
}
};
@ -380,7 +242,7 @@ PrefsSearchProvider.prototype = {
__proto__: BaseAppSearchProvider.prototype,
_init: function() {
BaseAppSearchProvider.prototype._init.call(this, _("SETTINGS"));
BaseAppSearchProvider.prototype._init.call(this, _("PREFERENCES"));
},
getInitialResultSet: function(terms) {
@ -389,24 +251,30 @@ PrefsSearchProvider.prototype = {
getSubsearchResultSet: function(previousResults, terms) {
return this._appSys.subsearch(true, previousResults, terms);
},
expandSearch: function(terms) {
let controlCenter = this._appSys.load_from_desktop_file('gnomecc.desktop');
controlCenter.launch();
Main.overview.hide();
}
};
function AppIcon(app, params) {
this._init(app, params);
function AppIcon(app) {
this._init(app);
}
AppIcon.prototype = {
__proto__: IconGrid.BaseIcon.prototype,
_init : function(app, params) {
_init : function(app) {
this.app = app;
let label = this.app.get_name();
IconGrid.BaseIcon.prototype._init.call(this,
label,
params);
{ setSizeManually: true });
},
createIcon: function(iconSize) {
@ -414,29 +282,23 @@ AppIcon.prototype = {
}
};
function AppWellIcon(app, iconParams) {
this._init(app, iconParams);
function AppWellIcon(app) {
this._init(app);
}
AppWellIcon.prototype = {
_init : function(app, iconParams) {
_init : function(app) {
this.app = app;
this.actor = new St.Button({ style_class: 'app-well-app',
reactive: true,
button_mask: St.ButtonMask.ONE | St.ButtonMask.TWO,
can_focus: true,
x_fill: true,
y_fill: true });
this.actor = new St.Clickable({ style_class: 'app-well-app',
reactive: true,
x_fill: true,
y_fill: true });
this.actor._delegate = this;
this.icon = new AppIcon(app, iconParams);
this.icon = new AppIcon(app);
this.actor.set_child(this.icon.actor);
this.actor.label_actor = this.icon.label;
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
this.actor.connect('popup-menu', Lang.bind(this, this._onKeyboardPopupMenu));
this._menu = null;
this._menuManager = new PopupMenu.PopupMenuManager(this);
@ -447,15 +309,12 @@ AppWellIcon.prototype = {
this._removeMenuTimeout();
Main.overview.beginItemDrag(this);
}));
this._draggable.connect('drag-cancelled', Lang.bind(this,
function () {
Main.overview.cancelledItemDrag(this);
}));
this._draggable.connect('drag-end', Lang.bind(this,
function () {
Main.overview.endItemDrag(this);
}));
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._menuTimeoutId = 0;
@ -494,34 +353,29 @@ AppWellIcon.prototype = {
Lang.bind(this, function() {
this.popupMenu();
}));
} else if (button == 3) {
this.popupMenu();
return true;
}
return false;
},
_onClicked: function(actor, button) {
_onClicked: function(actor, event) {
this._removeMenuTimeout();
let button = event.get_button();
if (button == 1) {
this._onActivate(Clutter.get_current_event());
this._onActivate(event);
} else if (button == 2) {
// Last workspace is always empty
let launchWorkspace = global.screen.get_workspace_by_index(global.screen.n_workspaces - 1);
launchWorkspace.activate(global.get_current_time());
this.emit('launching');
this.app.open_new_window(-1);
Main.overview.hide();
let newWorkspace = Main.overview.workspaces.addWorkspace();
if (newWorkspace != null) {
newWorkspace.activate(global.get_current_time());
this.emit('launching');
this.app.open_new_window();
Main.overview.hide();
}
} else if (button == 3) {
this.popupMenu();
}
return false;
},
_onKeyboardPopupMenu: function() {
this.popupMenu();
this._menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
},
getId: function() {
return this.app.get_id();
},
@ -532,35 +386,67 @@ AppWellIcon.prototype = {
if (!this._menu) {
this._menu = new AppIconMenu(this);
this._menu.connect('highlight-window', Lang.bind(this, function (menu, window) {
this.highlightWindow(window);
}));
this._menu.connect('activate-window', Lang.bind(this, function (menu, window) {
this.activateWindow(window);
}));
this._menu.connect('popup', Lang.bind(this, function (menu, isPoppedUp) {
if (!isPoppedUp)
if (isPoppedUp) {
this._onMenuPoppedUp();
} else {
this._onMenuPoppedDown();
}
}));
Main.overview.connect('hiding', Lang.bind(this, function () { this._menu.close(); }));
this._menuManager.addMenu(this._menu);
}
this.actor.set_hover(true);
this.actor.show_tooltip();
this._menu.popup();
return false;
},
highlightWindow: function(metaWindow) {
if (this._didActivateWindow)
return;
if (!this._getRunning())
return;
Main.overview.getWorkspacesForWindow(metaWindow).setHighlightWindow(metaWindow);
},
activateWindow: function(metaWindow) {
if (metaWindow) {
this._didActivateWindow = true;
Main.activateWindow(metaWindow);
} else {
Main.overview.hide();
}
},
_onMenuPoppedUp: function() {
if (this._getRunning()) {
Main.overview.getWorkspacesForWindow(null).setApplicationWindowSelection(this.app.get_id());
this._setWindowSelection = true;
this._didActivateWindow = false;
}
},
_onMenuPoppedDown: function() {
this.actor.sync_hover();
if (this._didActivateWindow)
return;
if (!this._setWindowSelection)
return;
Main.overview.getWorkspacesForWindow(null).setApplicationWindowSelection(null);
this._setWindowSelection = false;
},
_getRunning: function() {
return this.app.state != Shell.AppState.STOPPED;
},
_onActivate: function (event) {
@ -569,22 +455,24 @@ AppWellIcon.prototype = {
if (modifiers & Clutter.ModifierType.CONTROL_MASK
&& this.app.state == Shell.AppState.RUNNING) {
this.app.open_new_window(-1);
this.app.open_new_window();
} else {
this.app.activate(-1);
this.app.activate();
}
Main.overview.hide();
},
shellWorkspaceLaunch : function(params) {
params = Params.parse(params, { workspace: null,
timestamp: null });
// called by this._menuManager when it has the grab
menuEventFilter: function(event) {
return this._menu.menuEventFilter(event);
},
this.app.open_new_window(params.workspace ? params.workspace.index() : -1);
shellWorkspaceLaunch : function() {
this.app.open_new_window();
},
getDragActor: function() {
return this.app.create_icon_texture(Main.overview.dash.iconSize);
return this.app.create_icon_texture(this.icon.iconSize);
},
// Returns the original actor that should align with the actor
@ -603,17 +491,11 @@ AppIconMenu.prototype = {
__proto__: PopupMenu.PopupMenu.prototype,
_init: function(source) {
let side = St.Side.LEFT;
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
side = St.Side.RIGHT;
PopupMenu.PopupMenu.prototype._init.call(this, source.actor, 0.5, side, 0);
// We want to keep the item hovered while the menu is up
this.blockSourceEvents = true;
PopupMenu.PopupMenu.prototype._init.call(this, source.actor, St.Align.MIDDLE, St.Side.LEFT, 0);
this._source = source;
this.connect('active-changed', Lang.bind(this, this._onActiveChanged));
this.connect('activate', Lang.bind(this, this._onActivate));
this.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged));
@ -653,12 +535,14 @@ AppIconMenu.prototype = {
let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
this._newWindowMenuItem = this._appendMenuItem(_("New Window"));
this._appendSeparator();
this._newWindowMenuItem = windows.length > 0 ? this._appendMenuItem(_("New Window")) : null;
if (windows.length > 0)
this._appendSeparator();
this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ? _("Remove from Favorites")
: _("Add to Favorites"));
this._highlightedItem = null;
},
_appendSeparator: function () {
@ -682,16 +566,74 @@ AppIconMenu.prototype = {
if (open) {
this.emit('popup', true);
} else {
this._updateHighlight(null);
this.emit('popup', false);
}
},
// called by this._menuManager when it has the grab
menuEventFilter: function(event) {
let eventType = event.type();
// Check if the user is interacting with a window representation
// rather than interacting with the menu
if (eventType == Clutter.EventType.BUTTON_RELEASE) {
let metaWindow = this._findMetaWindowForActor(event.get_source());
if (metaWindow)
this.emit('activate-window', metaWindow);
} else if (eventType == Clutter.EventType.ENTER) {
let metaWindow = this._findMetaWindowForActor(event.get_source());
if (metaWindow)
this._selectMenuItemForWindow(metaWindow, true);
} else if (eventType == Clutter.EventType.LEAVE) {
let metaWindow = this._findMetaWindowForActor(event.get_source());
if (metaWindow)
this._selectMenuItemForWindow(metaWindow, false);
}
return false;
},
_findMetaWindowForActor: function (actor) {
if (actor._delegate instanceof Workspace.WindowClone)
return actor._delegate.metaWindow;
else if (actor.get_meta_window)
return actor.get_meta_window();
return null;
},
_updateHighlight: function (item) {
if (this._highlightedItem)
this.emit('highlight-window', null);
this._highlightedItem = item;
if (this._highlightedItem) {
let window = this._highlightedItem._window;
if (window)
this.emit('highlight-window', window);
}
},
_selectMenuItemForWindow: function (metaWindow, selected) {
let items = this.getMenuItems();
for (let i = 0; i < items.length; i++) {
let item = items[i];
let menuMetaWindow = item._window;
if (menuMetaWindow == metaWindow)
item.setActive(selected);
}
},
_onActiveChanged: function (menu, child) {
this._updateHighlight(child);
},
_onActivate: function (actor, child) {
if (child._window) {
let metaWindow = child._window;
this.emit('activate-window', metaWindow);
} else if (child == this._newWindowMenuItem) {
this._source.app.open_new_window(-1);
this._source.app.open_new_window();
this.emit('activate-window', null);
} else if (child == this._toggleFavoriteMenuItem) {
let favs = AppFavorites.getAppFavorites();

View File

@ -3,6 +3,8 @@
const Shell = imports.gi.Shell;
const Lang = imports.lang;
const Signals = imports.signals;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const Main = imports.ui.main;

View File

@ -2,7 +2,6 @@
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
@ -12,7 +11,7 @@ const POPUP_ANIMATION_TIME = 0.15;
/**
* BoxPointer:
* @side: side to draw the arrow on
* @side: A St.Side type; currently only St.Side.TOP is implemented
* @binProperties: Properties to set on contained bin
*
* An actor which displays a triangle "arrow" pointing to a given
@ -41,80 +40,76 @@ BoxPointer.prototype = {
this._border.connect('repaint', Lang.bind(this, this._drawBorder));
this._container.add_actor(this._border);
this.bin.raise(this._border);
this._xOffset = 0;
this._yOffset = 0;
this._xPosition = 0;
this._yPosition = 0;
},
show: function(animate, onComplete) {
animateAppear: function(onComplete) {
let x = this.actor.x;
let y = this.actor.y;
let themeNode = this.actor.get_theme_node();
let rise = themeNode.get_length('-arrow-rise');
this.opacity = 0;
this.actor.opacity = 0;
this.actor.show();
if (animate) {
switch (this._arrowSide) {
case St.Side.TOP:
this.yOffset = -rise;
break;
case St.Side.BOTTOM:
this.yOffset = rise;
break;
case St.Side.LEFT:
this.xOffset = -rise;
break;
case St.Side.RIGHT:
this.xOffset = rise;
break;
}
switch (this._arrowSide) {
case St.Side.TOP:
this.actor.y -= rise;
break;
case St.Side.BOTTOM:
this.actor.y += rise;
break;
case St.Side.LEFT:
this.actor.x -= rise;
break;
case St.Side.RIGHT:
this.actor.x += rise;
break;
}
Tweener.addTween(this, { opacity: 255,
xOffset: 0,
yOffset: 0,
transition: "linear",
onComplete: onComplete,
time: POPUP_ANIMATION_TIME });
Tweener.addTween(this.actor, { opacity: 255,
x: x,
y: y,
transition: "linear",
onComplete: onComplete,
time: POPUP_ANIMATION_TIME });
},
hide: function(animate, onComplete) {
let xOffset = 0;
let yOffset = 0;
animateDisappear: function(onComplete) {
let x = this.actor.x;
let y = this.actor.y;
let originalX = this.actor.x;
let originalY = this.actor.y;
let themeNode = this.actor.get_theme_node();
let rise = themeNode.get_length('-arrow-rise');
if (animate) {
switch (this._arrowSide) {
case St.Side.TOP:
yOffset = rise;
break;
case St.Side.BOTTOM:
yOffset = -rise;
break;
case St.Side.LEFT:
xOffset = rise;
break;
case St.Side.RIGHT:
xOffset = -rise;
break;
}
switch (this._arrowSide) {
case St.Side.TOP:
y += rise;
break;
case St.Side.BOTTOM:
y -= rise;
break;
case St.Side.LEFT:
x += rise;
break;
case St.Side.RIGHT:
x -= rise;
break;
}
Tweener.addTween(this, { opacity: 0,
xOffset: xOffset,
yOffset: yOffset,
transition: "linear",
time: POPUP_ANIMATION_TIME,
onComplete: Lang.bind(this, function () {
this.actor.hide();
this.xOffset = 0;
this.yOffset = 0;
if (onComplete)
onComplete();
})
});
Tweener.addTween(this.actor, { opacity: 0,
x: x,
y: y,
transition: "linear",
time: POPUP_ANIMATION_TIME,
onComplete: Lang.bind(this, function () {
this.actor.hide();
this.actor.x = originalX;
this.actor.y = originalY;
if (onComplete)
onComplete();
})
});
},
_adjustAllocationForArrow: function(isWidth, alloc) {
@ -177,9 +172,6 @@ BoxPointer.prototype = {
break;
}
this.bin.allocate(childBox, flags);
if (this._sourceActor && this._sourceActor.mapped)
this._reposition(this._sourceActor, this._gap, this._alignment);
},
_drawBorder: function(area) {
@ -193,8 +185,10 @@ BoxPointer.prototype = {
let halfBorder = borderWidth / 2;
let halfBase = Math.floor(base/2);
let borderColor = themeNode.get_color('-arrow-border-color');
let backgroundColor = themeNode.get_color('-arrow-background-color');
let borderColor = new Clutter.Color();
themeNode.get_color('-arrow-border-color', borderColor);
let backgroundColor = new Clutter.Color();
themeNode.get_color('-arrow-background-color', backgroundColor);
let [width, height] = area.get_surface_size();
let [boxWidth, boxHeight] = [width, height];
@ -214,88 +208,46 @@ BoxPointer.prototype = {
cr.translate(rise, 0);
}
let [x1, y1] = [halfBorder, halfBorder];
let [x2, y2] = [boxWidth - halfBorder, boxHeight - halfBorder];
cr.moveTo(borderRadius, halfBorder);
cr.moveTo(x1 + borderRadius, y1);
if (this._arrowSide == St.Side.TOP) {
if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) {
cr.lineTo(this._arrowOrigin, y1 - rise);
cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y1);
} else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y1);
cr.lineTo(this._arrowOrigin, y1 - rise);
} else {
cr.lineTo(this._arrowOrigin - halfBase, y1);
cr.lineTo(this._arrowOrigin, y1 - rise);
cr.lineTo(this._arrowOrigin + halfBase, y1);
}
cr.lineTo(this._arrowOrigin - halfBase, halfBorder);
cr.lineTo(this._arrowOrigin, halfBorder - rise);
cr.lineTo(this._arrowOrigin + halfBase, halfBorder);
}
cr.lineTo(boxWidth - borderRadius, halfBorder);
cr.lineTo(x2 - borderRadius, y1);
// top-right corner
cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius,
cr.arc(boxWidth - borderRadius - halfBorder, borderRadius + halfBorder, borderRadius,
3*Math.PI/2, Math.PI*2);
if (this._arrowSide == St.Side.RIGHT) {
if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
cr.lineTo(x2 + rise, this._arrowOrigin);
cr.lineTo(x2, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase);
} else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
cr.lineTo(x2, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase);
cr.lineTo(x2 + rise, this._arrowOrigin);
} else {
cr.lineTo(x2, this._arrowOrigin - halfBase);
cr.lineTo(x2 + rise, this._arrowOrigin);
cr.lineTo(x2, this._arrowOrigin + halfBase);
}
cr.lineTo(boxWidth - halfBorder, this._arrowOrigin - halfBase);
cr.lineTo(boxWidth - halfBorder + rise, this._arrowOrigin);
cr.lineTo(boxWidth - halfBorder, this._arrowOrigin + halfBase);
}
cr.lineTo(boxWidth - halfBorder, boxHeight - borderRadius);
cr.lineTo(x2, y2 - borderRadius);
// bottom-right corner
cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius,
cr.arc(boxWidth - borderRadius - halfBorder, boxHeight - borderRadius - halfBorder, borderRadius,
0, Math.PI/2);
if (this._arrowSide == St.Side.BOTTOM) {
if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) {
cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y2);
cr.lineTo(this._arrowOrigin, y2 + rise);
} else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
cr.lineTo(this._arrowOrigin, y2 + rise);
cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y2);
} else {
cr.lineTo(this._arrowOrigin + halfBase, y2);
cr.lineTo(this._arrowOrigin, y2 + rise);
cr.lineTo(this._arrowOrigin - halfBase, y2);
}
cr.lineTo(this._arrowOrigin + halfBase, boxHeight - halfBorder);
cr.lineTo(this._arrowOrigin, boxHeight - halfBorder + rise);
cr.lineTo(this._arrowOrigin - halfBase, boxHeight - halfBorder);
}
cr.lineTo(borderRadius, boxHeight - halfBorder);
cr.lineTo(x1 + borderRadius, y2);
// bottom-left corner
cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius,
cr.arc(borderRadius + halfBorder, boxHeight - borderRadius - halfBorder, borderRadius,
Math.PI/2, Math.PI);
if (this._arrowSide == St.Side.LEFT) {
if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
cr.lineTo(x1, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase);
cr.lineTo(x1 - rise, this._arrowOrigin);
} else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
cr.lineTo(x1 - rise, this._arrowOrigin);
cr.lineTo(x1, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase);
} else {
cr.lineTo(x1, this._arrowOrigin + halfBase);
cr.lineTo(x1 - rise, this._arrowOrigin);
cr.lineTo(x1, this._arrowOrigin - halfBase);
}
cr.lineTo(halfBorder, this._arrowOrigin + halfBase);
cr.lineTo(halfBorder - rise, this._arrowOrigin);
cr.lineTo(halfBorder, this._arrowOrigin - halfBase);
}
cr.lineTo(halfBorder, borderRadius);
cr.lineTo(x1, y1 + borderRadius);
// top-left corner
cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius,
cr.arc(borderRadius + halfBorder, borderRadius + halfBorder, borderRadius,
Math.PI, 3*Math.PI/2);
Clutter.cairo_set_source_color(cr, backgroundColor);
@ -310,47 +262,32 @@ BoxPointer.prototype = {
// so that we can query the correct size.
this.actor.show();
this._sourceActor = sourceActor;
this._gap = gap;
this._alignment = alignment;
this._reposition(sourceActor, gap, alignment);
},
_reposition: function(sourceActor, gap, alignment) {
// Position correctly relative to the sourceActor
let sourceNode = sourceActor.get_theme_node();
let sourceContentBox = sourceNode.get_content_box(sourceActor.get_allocation_box());
let sourceAllocation = Shell.util_get_transformed_allocation(sourceActor);
let sourceCenterX = sourceAllocation.x1 + sourceContentBox.x1 + (sourceContentBox.x2 - sourceContentBox.x1) / 2;
let sourceCenterY = sourceAllocation.y1 + sourceContentBox.y1 + (sourceContentBox.y2 - sourceContentBox.y1) / 2;
let [sourceX, sourceY] = sourceActor.get_transformed_position();
let [sourceWidth, sourceHeight] = sourceActor.get_transformed_size();
let [minWidth, minHeight, natWidth, natHeight] = this.actor.get_preferred_size();
// We also want to keep it onscreen, and separated from the
// edge by the same distance as the main part of the box is
// separated from its sourceActor
let primary = global.get_primary_monitor();
let themeNode = this.actor.get_theme_node();
let borderWidth = themeNode.get_length('-arrow-border-width');
let arrowBase = themeNode.get_length('-arrow-base');
let borderRadius = themeNode.get_length('-arrow-border-radius');
let margin = (4 * borderRadius + borderWidth + arrowBase);
let halfMargin = margin / 2;
let arrowRise = this.actor.get_theme_node().get_length('-arrow-rise');
let resX, resY;
switch (this._arrowSide) {
case St.Side.TOP:
resY = sourceAllocation.y2 + gap;
resY = sourceY + sourceHeight + gap;
break;
case St.Side.BOTTOM:
resY = sourceAllocation.y1 - natHeight - gap;
resY = sourceY - natHeight - gap;
break;
case St.Side.LEFT:
resX = sourceAllocation.x2 + gap;
resX = sourceX + sourceWidth + gap;
break;
case St.Side.RIGHT:
resX = sourceAllocation.x1 - natWidth - gap;
resX = sourceX - natWidth - gap;
break;
}
@ -359,21 +296,42 @@ BoxPointer.prototype = {
switch (this._arrowSide) {
case St.Side.TOP:
case St.Side.BOTTOM:
resX = sourceCenterX - (halfMargin + (natWidth - margin) * alignment);
switch (alignment) {
case St.Align.START:
resX = sourceX;
break;
case St.Align.MIDDLE:
resX = sourceX - Math.floor((natWidth - sourceWidth) / 2);
break;
case St.Align.END:
resX = sourceX - (natWidth - sourceWidth);
break;
}
resX = Math.max(resX, primary.x + 10);
resX = Math.min(resX, primary.x + primary.width - (10 + natWidth));
this.setArrowOrigin(sourceCenterX - resX);
resX = Math.min(resX, primary.x + primary.width - natWidth - arrowRise - gap);
resX = Math.max(resX, primary.x);
this.setArrowOrigin((sourceX - resX) + Math.floor(sourceWidth / 2));
break;
case St.Side.LEFT:
case St.Side.RIGHT:
resY = sourceCenterY - (halfMargin + (natHeight - margin) * alignment);
switch (alignment) {
case St.Align.START:
resY = sourceY;
break;
case St.Align.MIDDLE:
resY = sourceY - Math.floor((natHeight - sourceHeight) / 2);
break;
case St.Align.END:
resY = sourceY - (natHeight - sourceHeight);
break;
}
resY = Math.max(resY, primary.y + 10);
resY = Math.min(resY, primary.y + primary.height - (10 + natHeight));
resY = Math.min(resY, primary.y + primary.height - natHeight - arrowRise - gap);
resY = Math.max(resY, primary.y);
this.setArrowOrigin(sourceCenterY - resY);
this.setArrowOrigin((sourceY - resY) + Math.floor(sourceHeight / 2));
break;
}
@ -384,9 +342,9 @@ BoxPointer.prototype = {
parent = parent.get_parent();
}
this._xPosition = Math.floor(x);
this._yPosition = Math.floor(y);
this._shiftActor();
// Actually set the position
this.actor.x = Math.floor(x);
this.actor.y = Math.floor(y);
},
// @origin: Coordinate specifying middle of the arrow, along
@ -397,42 +355,5 @@ BoxPointer.prototype = {
this._arrowOrigin = origin;
this._border.queue_repaint();
}
},
_shiftActor : function() {
// Since the position of the BoxPointer depends on the allocated size
// of the BoxPointer and the position of the source actor, trying
// to position the BoxPoiner via the x/y properties will result in
// allocation loops and warnings. Instead we do the positioning via
// the anchor point, which is independent of allocation, and leave
// x == y == 0.
this.actor.set_anchor_point(-(this._xPosition + this._xOffset),
-(this._yPosition + this._yOffset));
},
set xOffset(offset) {
this._xOffset = offset;
this._shiftActor();
},
get xOffset() {
return this._xOffset;
},
set yOffset(offset) {
this._yOffset = offset;
this._shiftActor();
},
get yOffset() {
return this._yOffset;
},
set opacity(opacity) {
this.actor.opacity = opacity;
},
get opacity() {
return this.actor.opacity;
}
};

View File

@ -1,84 +1,22 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const DBus = imports.dbus;
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const St = imports.gi.St;
const Signals = imports.signals;
const Pango = imports.gi.Pango;
const Gettext_gtk30 = imports.gettext.domain('gtk30');
const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
const Gettext_gtk20 = imports.gettext.domain('gtk20');
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
const CLOCK_FORMAT_KEY = 'clock-format';
function _sameDay(dateA, dateB) {
return (dateA.getDate() == dateB.getDate() &&
dateA.getMonth() == dateB.getMonth() &&
dateA.getYear() == dateB.getYear());
}
function _sameYear(dateA, dateB) {
return (dateA.getYear() == dateB.getYear());
}
/* TODO: maybe needs config - right now we assume that Saturday and
* Sunday are non-work days (not true in e.g. Israel, it's Sunday and
* Monday there)
*/
function _isWorkDay(date) {
return date.getDay() != 0 && date.getDay() != 6;
}
function _getBeginningOfDay(date) {
let ret = new Date(date.getTime());
ret.setHours(0);
ret.setMinutes(0);
ret.setSeconds(0);
ret.setMilliseconds(0);
return ret;
}
function _getEndOfDay(date) {
let ret = new Date(date.getTime());
ret.setHours(23);
ret.setMinutes(59);
ret.setSeconds(59);
ret.setMilliseconds(999);
return ret;
}
function _formatEventTime(event, clockFormat) {
let ret;
if (event.allDay) {
/* Translators: Shown in calendar event list for all day events
* Keep it short, best if you can use less then 10 characters
*/
ret = C_("event list time", "All Day");
} else {
switch (clockFormat) {
case '24h':
/* Translators: Shown in calendar event list, if 24h format */
ret = event.date.toLocaleFormat(C_("event list time", "%H:%M"));
break;
default:
/* explicit fall-through */
case '12h':
/* Transators: Shown in calendar event list, if 12h format */
ret = event.date.toLocaleFormat(C_("event list time", "%l:%M %p"));
break;
}
}
return ret;
}
function _getCalendarWeekForDate(date) {
// Based on the algorithms found here:
// http://en.wikipedia.org/wiki/Talk:ISO_week_date
@ -105,259 +43,12 @@ function _getDigitWidth(actor){
return width;
}
function _getCalendarDayAbbreviation(dayNumber) {
let abbreviations = [
/* Translators: Calendar grid abbreviation for Sunday.
*
* NOTE: These grid abbreviations are always shown together
* and in order, e.g. "S M T W T F S".
*/
C_("grid sunday", "S"),
/* Translators: Calendar grid abbreviation for Monday */
C_("grid monday", "M"),
/* Translators: Calendar grid abbreviation for Tuesday */
C_("grid tuesday", "T"),
/* Translators: Calendar grid abbreviation for Wednesday */
C_("grid wednesday", "W"),
/* Translators: Calendar grid abbreviation for Thursday */
C_("grid thursday", "T"),
/* Translators: Calendar grid abbreviation for Friday */
C_("grid friday", "F"),
/* Translators: Calendar grid abbreviation for Saturday */
C_("grid saturday", "S")
];
return abbreviations[dayNumber];
}
function _getEventDayAbbreviation(dayNumber) {
let abbreviations = [
/* Translators: Event list abbreviation for Sunday.
*
* NOTE: These list abbreviations are normally not shown together
* so they need to be unique (e.g. Tuesday and Thursday cannot
* both be 'T').
*/
C_("list sunday", "Su"),
/* Translators: Event list abbreviation for Monday */
C_("list monday", "M"),
/* Translators: Event list abbreviation for Tuesday */
C_("list tuesday", "T"),
/* Translators: Event list abbreviation for Wednesday */
C_("list wednesday", "W"),
/* Translators: Event list abbreviation for Thursday */
C_("list thursday", "Th"),
/* Translators: Event list abbreviation for Friday */
C_("list friday", "F"),
/* Translators: Event list abbreviation for Saturday */
C_("list saturday", "S")
];
return abbreviations[dayNumber];
}
// Abstraction for an appointment/event in a calendar
function CalendarEvent(date, end, summary, allDay) {
this._init(date, end, summary, allDay);
}
CalendarEvent.prototype = {
_init: function(date, end, summary, allDay) {
this.date = date;
this.end = end;
this.summary = summary;
this.allDay = allDay;
}
};
// Interface for appointments/events - e.g. the contents of a calendar
//
// First, an implementation with no events
function EmptyEventSource() {
function Calendar() {
this._init();
}
EmptyEventSource.prototype = {
_init: function() {
},
requestRange: function(begin, end) {
},
getEvents: function(begin, end) {
let result = [];
return result;
},
hasEvents: function(day) {
return false;
}
};
Signals.addSignalMethods(EmptyEventSource.prototype);
const CalendarServerIface = {
name: 'org.gnome.Shell.CalendarServer',
methods: [{ name: 'GetEvents',
inSignature: 'xxb',
outSignature: 'a(sssbxxa{sv})' }],
signals: [{ name: 'Changed',
inSignature: '' }]
};
const CalendarServer = function () {
this._init();
};
CalendarServer.prototype = {
_init: function() {
DBus.session.proxifyObject(this, 'org.gnome.Shell.CalendarServer', '/org/gnome/Shell/CalendarServer');
}
};
DBus.proxifyPrototype(CalendarServer.prototype, CalendarServerIface);
// an implementation that reads data from a session bus service
function DBusEventSource(owner) {
this._init(owner);
}
function _datesEqual(a, b) {
if (a < b)
return false;
else if (a > b)
return false;
return true;
}
function _dateIntervalsOverlap(a0, a1, b0, b1)
{
if (a1 <= b0)
return false;
else if (b1 <= a0)
return false;
else
return true;
}
DBusEventSource.prototype = {
_init: function(owner) {
this._resetCache();
this._dbusProxy = new CalendarServer(owner);
this._dbusProxy.connect('Changed', Lang.bind(this, this._onChanged));
DBus.session.watch_name('org.gnome.Shell.CalendarServer',
false, // do not launch a name-owner if none exists
Lang.bind(this, this._onNameAppeared),
Lang.bind(this, this._onNameVanished));
},
_resetCache: function() {
this._events = [];
this._lastRequestBegin = null;
this._lastRequestEnd = null;
},
_onNameAppeared: function(owner) {
this._resetCache();
this._loadEvents(true);
},
_onNameVanished: function(oldOwner) {
this._resetCache();
this.emit('changed');
},
_onChanged: function() {
this._loadEvents(false);
},
_onEventsReceived: function(appointments) {
let newEvents = [];
if (appointments != null) {
for (let n = 0; n < appointments.length; n++) {
let a = appointments[n];
let date = new Date(a[4] * 1000);
let end = new Date(a[5] * 1000);
let summary = a[1];
let allDay = a[3];
let event = new CalendarEvent(date, end, summary, allDay);
newEvents.push(event);
}
newEvents.sort(function(event1, event2) {
return event1.date.getTime() - event2.date.getTime();
});
}
this._events = newEvents;
this.emit('changed');
},
_loadEvents: function(forceReload) {
if (this._curRequestBegin && this._curRequestEnd){
let callFlags = 0;
if (forceReload)
callFlags |= DBus.CALL_FLAG_START;
this._dbusProxy.GetEventsRemote(this._curRequestBegin.getTime() / 1000,
this._curRequestEnd.getTime() / 1000,
forceReload,
Lang.bind(this, this._onEventsReceived),
callFlags);
}
},
requestRange: function(begin, end, forceReload) {
if (forceReload || !(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
this._lastRequestBegin = begin;
this._lastRequestEnd = end;
this._curRequestBegin = begin;
this._curRequestEnd = end;
this._loadEvents(forceReload);
}
},
getEvents: function(begin, end) {
let result = [];
for(let n = 0; n < this._events.length; n++) {
let event = this._events[n];
if (_dateIntervalsOverlap (event.date, event.end, begin, end)) {
result.push(event);
}
}
return result;
},
hasEvents: function(day) {
let dayBegin = _getBeginningOfDay(day);
let dayEnd = _getEndOfDay(day);
let events = this.getEvents(dayBegin, dayEnd);
if (events.length == 0)
return false;
return true;
}
};
Signals.addSignalMethods(DBusEventSource.prototype);
// Calendar:
// @eventSource: is an object implementing the EventSource API, e.g. the
// requestRange(), getEvents(), hasEvents() methods and the ::changed signal.
function Calendar(eventSource) {
this._init(eventSource);
}
Calendar.prototype = {
_init: function(eventSource) {
this._eventSource = eventSource;
this._eventSource.connect('changed', Lang.bind(this,
function() {
this._update(false);
}));
_init: function() {
// FIXME: This is actually the fallback method for GTK+ for the week start;
// GTK+ by preference uses nl_langinfo (NL_TIME_FIRST_WEEKDAY). We probably
// should add a C function so we can do the full handling.
@ -369,7 +60,7 @@ Calendar.prototype = {
this._settings.connect('changed::' + SHOW_WEEKDATE_KEY, Lang.bind(this, this._onSettingsChange));
this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY);
let weekStartString = Gettext_gtk30.gettext('calendar:week_start:0');
let weekStartString = Gettext_gtk20.gettext('calendar:week_start:0');
if (weekStartString.indexOf('calendar:week_start:') == 0) {
this._weekStart = parseInt(weekStartString.substring(20));
}
@ -380,8 +71,7 @@ Calendar.prototype = {
}
// Find the ordering for month/year in the calendar heading
this._headerFormatWithoutYear = '%B';
switch (Gettext_gtk30.gettext('calendar:MY')) {
switch (Gettext_gtk20.gettext('calendar:MY')) {
case 'calendar:MY':
this._headerFormat = '%B %Y';
break;
@ -395,7 +85,7 @@ Calendar.prototype = {
}
// Start off with the current date
this._selectedDate = new Date();
this.date = new Date();
this.actor = new St.Table({ homogeneous: false,
style_class: 'calendar',
@ -405,17 +95,14 @@ Calendar.prototype = {
Lang.bind(this, this._onScroll));
this._buildHeader ();
this._update();
},
// Sets the calendar to show a specific date
setDate: function(date, forceReload) {
if (!_sameDay(date, this._selectedDate)) {
this._selectedDate = date;
this._update(forceReload);
this.emit('selected-date-changed', new Date(this._selectedDate));
} else {
if (forceReload)
this._update(forceReload);
setDate: function(date) {
if (!_sameDay(date, this.date)) {
this.date = date;
this._update();
}
},
@ -429,36 +116,45 @@ Calendar.prototype = {
{ row: 0, col: 0, col_span: offsetCols + 7 });
this.actor.connect('style-changed', Lang.bind(this, this._onStyleChange));
let [backlabel, forwardlabel] = ['&lt;', '&gt;'];
if (St.Widget.get_default_direction () == St.TextDirection.RTL) {
[backlabel, forwardlabel] = [forwardlabel, backlabel];
}
let back = new St.Button({ style_class: 'calendar-change-month-back' });
let back = new St.Button({ label: backlabel, style_class: 'calendar-change-month' });
this._topBox.add(back);
back.connect('clicked', Lang.bind(this, this._onPrevMonthButtonClicked));
back.connect('clicked', Lang.bind(this, this._prevMonth));
this._monthLabel = new St.Label({style_class: 'calendar-month-label'});
this._topBox.add(this._monthLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
this._dateLabel = new St.Label();
this._topBox.add(this._dateLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
let forward = new St.Button({ style_class: 'calendar-change-month-forward' });
let forward = new St.Button({ label: forwardlabel, style_class: 'calendar-change-month' });
this._topBox.add(forward);
forward.connect('clicked', Lang.bind(this, this._onNextMonthButtonClicked));
forward.connect('clicked', Lang.bind(this, this._nextMonth));
// Add weekday labels...
//
// We need to figure out the abbreviated localized names for the days of the week;
// we do this by just getting the next 7 days starting from right now and then putting
// them in the right cell in the table. It doesn't matter if we add them in order
let iter = new Date(this._selectedDate);
let iter = new Date(this.date);
iter.setSeconds(0); // Leap second protection. Hah!
iter.setHours(12);
if (this._useWeekdate) {
this._weekdateHeader = new St.Label();
this.actor.add(this._weekdateHeader,
{ row: 1,
col: 0,
x_fill: false, x_align: St.Align.MIDDLE });
this._setWeekdateHeaderWidth();
} else {
this._weekdateHeader = null;
}
for (let i = 0; i < 7; i++) {
// Could use iter.toLocaleFormat('%a') but that normally gives three characters
// and we want, ideally, a single character for e.g. S M T W T F S
let customDayAbbrev = _getCalendarDayAbbreviation(iter.getDay());
let label = new St.Label({ style_class: 'calendar-day-base calendar-day-heading',
text: customDayAbbrev });
this.actor.add(label,
this.actor.add(new St.Label({ text: iter.toLocaleFormat('%a') }),
{ row: 1,
col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7,
x_fill: false, x_align: St.Align.MIDDLE });
x_fill: false, x_align: St.Align.END });
iter.setTime(iter.getTime() + MSECS_IN_DAY);
}
@ -482,72 +178,43 @@ Calendar.prototype = {
switch (event.get_scroll_direction()) {
case Clutter.ScrollDirection.UP:
case Clutter.ScrollDirection.LEFT:
this._onPrevMonthButtonClicked();
this._prevMonth();
break;
case Clutter.ScrollDirection.DOWN:
case Clutter.ScrollDirection.RIGHT:
this._onNextMonthButtonClicked();
this._nextMonth();
break;
}
},
_onPrevMonthButtonClicked: function() {
let newDate = new Date(this._selectedDate);
let oldMonth = newDate.getMonth();
if (oldMonth == 0) {
newDate.setMonth(11);
newDate.setFullYear(newDate.getFullYear() - 1);
if (newDate.getMonth() != 11) {
let day = 32 - new Date(newDate.getFullYear() - 1, 11, 32).getDate();
newDate = new Date(newDate.getFullYear() - 1, 11, day);
}
_prevMonth: function() {
if (this.date.getMonth() == 0) {
this.date.setMonth(11);
this.date.setFullYear(this.date.getFullYear() - 1);
} else {
this.date.setMonth(this.date.getMonth() - 1);
}
else {
newDate.setMonth(oldMonth - 1);
if (newDate.getMonth() != oldMonth - 1) {
let day = 32 - new Date(newDate.getFullYear(), oldMonth - 1, 32).getDate();
newDate = new Date(newDate.getFullYear(), oldMonth - 1, day);
}
}
this.setDate(newDate, false);
this._update();
},
_onNextMonthButtonClicked: function() {
let newDate = new Date(this._selectedDate);
let oldMonth = newDate.getMonth();
if (oldMonth == 11) {
newDate.setMonth(0);
newDate.setFullYear(newDate.getFullYear() + 1);
if (newDate.getMonth() != 0) {
let day = 32 - new Date(newDate.getFullYear() + 1, 0, 32).getDate();
newDate = new Date(newDate.getFullYear() + 1, 0, day);
}
_nextMonth: function() {
if (this.date.getMonth() == 11) {
this.date.setMonth(0);
this.date.setFullYear(this.date.getFullYear() + 1);
} else {
this.date.setMonth(this.date.getMonth() + 1);
}
else {
newDate.setMonth(oldMonth + 1);
if (newDate.getMonth() != oldMonth + 1) {
let day = 32 - new Date(newDate.getFullYear(), oldMonth + 1, 32).getDate();
newDate = new Date(newDate.getFullYear(), oldMonth + 1, day);
}
}
this.setDate(newDate, false);
this._update();
},
_onSettingsChange: function() {
this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY);
this._buildHeader();
this._update(false);
this._update();
},
_update: function(forceReload) {
let now = new Date();
if (_sameYear(this._selectedDate, now))
this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormatWithoutYear);
else
this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormat);
_update: function() {
this._dateLabel.text = this.date.toLocaleFormat(this._headerFormat);
// Remove everything but the topBox and the weekday labels
let children = this.actor.get_children();
@ -555,217 +222,45 @@ Calendar.prototype = {
children[i].destroy();
// Start at the beginning of the week before the start of the month
let beginDate = new Date(this._selectedDate);
beginDate.setDate(1);
beginDate.setSeconds(0);
beginDate.setHours(12);
let daysToWeekStart = (7 + beginDate.getDay() - this._weekStart) % 7;
beginDate.setTime(beginDate.getTime() - daysToWeekStart * MSECS_IN_DAY);
let iter = new Date(this.date);
iter.setDate(1);
iter.setSeconds(0);
iter.setHours(12);
let daysToWeekStart = (7 + iter.getDay() - this._weekStart) % 7;
iter.setTime(iter.getTime() - daysToWeekStart * MSECS_IN_DAY);
let now = new Date();
let iter = new Date(beginDate);
let row = 2;
while (true) {
let button = new St.Button({ label: iter.getDate().toString() });
let iterStr = iter.toUTCString();
button.connect('clicked', Lang.bind(this, function() {
let newlySelectedDate = new Date(iterStr);
this.setDate(newlySelectedDate, false);
}));
let hasEvents = this._eventSource.hasEvents(iter);
let styleClass = 'calendar-day-base calendar-day';
if (_isWorkDay(iter))
styleClass += ' calendar-work-day'
else
styleClass += ' calendar-nonwork-day'
// Hack used in lieu of border-collapse - see gnome-shell.css
if (row == 2)
styleClass = 'calendar-day-top ' + styleClass;
if (iter.getDay() == this._weekStart)
styleClass = 'calendar-day-left ' + styleClass;
let label = new St.Label({ text: iter.getDate().toString() });
if (_sameDay(now, iter))
styleClass += ' calendar-today';
else if (iter.getMonth() != this._selectedDate.getMonth())
styleClass += ' calendar-other-month-day';
if (_sameDay(this._selectedDate, iter))
button.add_style_pseudo_class('active');
if (hasEvents)
styleClass += ' calendar-day-with-events'
button.style_class = styleClass;
label.style_class = 'calendar-day calendar-today';
else if (iter.getMonth() != this.date.getMonth())
label.style_class = 'calendar-day calendar-other-month-day';
else
label.style_class = 'calendar-day';
let offsetCols = this._useWeekdate ? 1 : 0;
this.actor.add(button,
{ row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7 });
this.actor.add(label,
{ row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7,
x_fill: false, x_align: St.Align.END });
if (this._useWeekdate && iter.getDay() == 4) {
let label = new St.Label({ text: _getCalendarWeekForDate(iter).toString(),
style_class: 'calendar-day-base calendar-week-number'});
style_class: 'calendar-day calendar-calendarweek'});
this.actor.add(label,
{ row: row, col: 0, y_align: St.Align.MIDDLE });
{ row: row, col: 0,
x_fill: false, x_align: St.Align.MIDDLE });
}
iter.setTime(iter.getTime() + MSECS_IN_DAY);
if (iter.getDay() == this._weekStart) {
// We stop on the first "first day of the week" after the month we are displaying
if (iter.getMonth() > this._selectedDate.getMonth() || iter.getYear() > this._selectedDate.getYear())
if (iter.getMonth() > this.date.getMonth() || iter.getYear() > this.date.getYear())
break;
row++;
}
}
// Signal to the event source that we are interested in events
// only from this date range
this._eventSource.requestRange(beginDate, iter, forceReload);
}
};
Signals.addSignalMethods(Calendar.prototype);
function EventsList(eventSource) {
this._init(eventSource);
}
EventsList.prototype = {
_init: function(eventSource) {
this.actor = new St.BoxLayout({ vertical: true, style_class: 'events-header-vbox'});
this._date = new Date();
this._eventSource = eventSource;
this._eventSource.connect('changed', Lang.bind(this, this._update));
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
this._desktopSettings.connect('changed', Lang.bind(this, this._update));
let weekStartString = Gettext_gtk30.gettext('calendar:week_start:0');
if (weekStartString.indexOf('calendar:week_start:') == 0) {
this._weekStart = parseInt(weekStartString.substring(20));
}
if (isNaN(this._weekStart) ||
this._weekStart < 0 ||
this._weekStart > 6) {
log('Translation of "calendar:week_start:0" in GTK+ is not correct');
this._weekStart = 0;
}
this._update();
},
_addEvent: function(dayNameBox, timeBox, eventTitleBox, includeDayName, day, time, desc) {
if (includeDayName) {
dayNameBox.add(new St.Label( { style_class: 'events-day-dayname',
text: day } ),
{ x_fill: true } );
}
timeBox.add(new St.Label( { style_class: 'events-day-time',
text: time} ),
{ x_fill: true } );
eventTitleBox.add(new St.Label( { style_class: 'events-day-task',
text: desc} ));
},
_addPeriod: function(header, begin, end, includeDayName, showNothingScheduled) {
let events = this._eventSource.getEvents(begin, end);
let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);;
if (events.length == 0 && !showNothingScheduled)
return;
let vbox = new St.BoxLayout( {vertical: true} );
this.actor.add(vbox);
vbox.add(new St.Label({ style_class: 'events-day-header', text: header }));
let box = new St.BoxLayout({style_class: 'events-header-hbox'});
let dayNameBox = new St.BoxLayout({ vertical: true, style_class: 'events-day-name-box' });
let timeBox = new St.BoxLayout({ vertical: true, style_class: 'events-time-box' });
let eventTitleBox = new St.BoxLayout({ vertical: true, style_class: 'events-event-box' });
box.add(dayNameBox, {x_fill: false});
box.add(timeBox, {x_fill: false});
box.add(eventTitleBox, {expand: true});
vbox.add(box);
for (let n = 0; n < events.length; n++) {
let event = events[n];
let dayString = _getEventDayAbbreviation(event.date.getDay());
let timeString = _formatEventTime(event, clockFormat);
let summaryString = event.summary;
this._addEvent(dayNameBox, timeBox, eventTitleBox, includeDayName, dayString, timeString, summaryString);
}
if (events.length == 0 && showNothingScheduled) {
let now = new Date();
/* Translators: Text to show if there are no events */
let nothingEvent = new CalendarEvent(now, now, _("Nothing Scheduled"), true);
let timeString = _formatEventTime(nothingEvent, clockFormat);
this._addEvent(dayNameBox, timeBox, eventTitleBox, false, "", timeString, nothingEvent.summary);
}
},
_showOtherDay: function(day) {
this.actor.destroy_children();
let dayBegin = _getBeginningOfDay(day);
let dayEnd = _getEndOfDay(day);
let dayString;
let now = new Date();
if (_sameYear(day, now))
/* Translators: Shown on calendar heading when selected day occurs on current year */
dayString = day.toLocaleFormat(C_("calendar heading", "%A, %B %d"));
else
/* Translators: Shown on calendar heading when selected day occurs on different year */
dayString = day.toLocaleFormat(C_("calendar heading", "%A, %B %d, %Y"));
this._addPeriod(dayString, dayBegin, dayEnd, false, true);
},
_showToday: function() {
this.actor.destroy_children();
let now = new Date();
let dayBegin = _getBeginningOfDay(now);
let dayEnd = _getEndOfDay(now);
this._addPeriod(_("Today"), dayBegin, dayEnd, false, true);
let tomorrowBegin = new Date(dayBegin.getTime() + 86400 * 1000);
let tomorrowEnd = new Date(dayEnd.getTime() + 86400 * 1000);
this._addPeriod(_("Tomorrow"), tomorrowBegin, tomorrowEnd, false, true);
if (dayEnd.getDay() <= 4 + this._weekStart) {
/* If now is within the first 5 days we show "This week" and
* include events up until and including Saturday/Sunday
* (depending on whether a week starts on Sunday/Monday).
*/
let thisWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
let thisWeekEnd = new Date(dayEnd.getTime() + (6 + this._weekStart - dayEnd.getDay()) * 86400 * 1000);
this._addPeriod(_("This week"), thisWeekBegin, thisWeekEnd, true, false);
} else {
/* otherwise it's one of the two last days of the week ... show
* "Next week" and include events up until and including *next*
* Saturday/Sunday
*/
let nextWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
let nextWeekEnd = new Date(dayEnd.getTime() + (13 + this._weekStart - dayEnd.getDay()) * 86400 * 1000);
this._addPeriod(_("Next week"), nextWeekBegin, nextWeekEnd, true, false);
}
},
// Sets the event list to show events from a specific date
setDate: function(date) {
if (!_sameDay(date, this._date)) {
this._date = date;
this._update();
}
},
_update: function() {
let today = new Date();
if (_sameDay (this._date, today)) {
this._showToday();
} else {
this._showOtherDay(this._date);
}
}
};

View File

@ -13,7 +13,14 @@ const Params = imports.misc.params;
// normal mode (ie, outside the Overview), that surrounds the main
// workspace content.
const Visibility = {
FULL: 1,
FULLSCREEN: 2,
OVERVIEW: 3
};
const defaultParams = {
visibleInOverview: false,
visibleInFullscreen: false,
affectsStruts: true,
affectsInputRegion: true
@ -30,13 +37,12 @@ Chrome.prototype = {
Main.uiGroup.add_actor(this.actor);
this.actor.connect('allocate', Lang.bind(this, this._allocated));
this._monitors = [];
this._inFullscreen = false;
this._inOverview = false;
this.visibility = Visibility.FULL;
this._trackedActors = [];
global.screen.connect('monitors-changed',
Lang.bind(this, this._monitorsChanged));
global.screen.connect('restacked',
Lang.bind(this, this._windowsRestacked));
@ -49,8 +55,6 @@ Chrome.prototype = {
Main.overview.connect('hidden',
Lang.bind(this, this._overviewHidden));
this._updateMonitors();
this._updateFullscreen();
this._queueUpdateRegions();
},
@ -72,8 +76,11 @@ Chrome.prototype = {
// in its visibility will affect the input region, but NOT the
// struts.
//
// If %visibleInFullscreen is %true, the actor will be visible
// even when a fullscreen window should be covering it.
// If %visibleInOverview is %true in @params, @actor will remain
// visible when the overview is brought up. Otherwise it will
// automatically be hidden. Likewise, if %visibleInFullscreen is
// %true, the actor will be visible even when a fullscreen window
// should be covering it.
//
// If %affectsStruts or %affectsInputRegion is %false, the actor
// will not have the indicated effect.
@ -92,7 +99,7 @@ Chrome.prototype = {
//
// @params can have any of the same values as in addActor(), though
// some possibilities don't make sense (eg, trying to have a
// %visibleInFullscreen child of a non-%visibleInFullscreen parent).
// %visibleInOverview child of a non-%visibleInOverview parent).
// By default, @actor has the same params as its chrome ancestor.
trackActor: function(actor, params) {
let ancestor = actor.get_parent();
@ -185,12 +192,26 @@ Chrome.prototype = {
_updateVisibility: function() {
for (let i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i];
if (!this._inOverview && !actorData.visibleInFullscreen &&
this._findMonitorForActor(actorData.actor).inFullscreen)
if (this._inOverview && !actorData.visibleInOverview)
this.actor.set_skip_paint(actorData.actor, true);
else if (!this._inOverview && this._inFullscreen && !actorData.visibleInFullscreen)
this.actor.set_skip_paint(actorData.actor, true);
else
this.actor.set_skip_paint(actorData.actor, false);
}
let newVisibility;
if (this._inOverview)
newVisibility = Visibility.OVERVIEW;
else if (this._inFullscreen)
newVisibility = Visibility.FULLSCREEN;
else
newVisibility = Visibility.FULL;
if (newVisibility != this.visibility) {
this.visibility = newVisibility;
this.emit('visibility-changed', this.visibility);
}
},
_overviewShowing: function() {
@ -205,77 +226,15 @@ Chrome.prototype = {
this._queueUpdateRegions();
},
_updateMonitors: function() {
let monitors = global.get_monitors();
let primary = global.get_primary_monitor();
this._monitors = monitors;
for (let i = 0; i < monitors.length; i++) {
let monitor = monitors[i];
if (monitor.x == primary.x &&
monitor.y == primary.y &&
monitor.width == primary.width &&
monitor.height == primary.height)
this._primaryMonitor = monitor;
}
},
_findMonitorForRect: function(x, y, w, h) {
// First look at what monitor the center of the rectangle is at
let cx = x + w/2;
let cy = y + h/2;
for (let i = 0; i < this._monitors.length; i++) {
let monitor = this._monitors[i];
if (cx >= monitor.x && cx < monitor.x + monitor.width &&
cy >= monitor.y && cy < monitor.y + monitor.height)
return monitor;
}
// If the center is not on a monitor, return the first overlapping monitor
for (let i = 0; i < this._monitors.length; i++) {
let monitor = this._monitors[i];
if (x + w > monitor.x && x < monitor.x + monitor.width &&
y + h > monitor.y && y < monitor.y + monitor.height)
return monitor;
}
// otherwise on no monitor
return null;
},
_findMonitorForWindow: function(window) {
return this._findMonitorForRect(window.x, window.y, window.width, window.height);
},
// This call guarantees that we return some monitor to simplify usage of it
// In practice all tracked actors should be visible on some monitor anyway
_findMonitorForActor: function(actor) {
let [x, y] = actor.get_transformed_position();
let [w, h] = actor.get_transformed_size();
let monitor = this._findMonitorForRect(x, y, w, h);
if (monitor)
return monitor;
return this._primaryMonitor; // Not on any monitor, pretend its on the primary
},
_monitorsChanged: function() {
this._updateMonitors();
// Update everything that depends on monitor positions
this._updateFullscreen();
this._updateVisibility();
this._queueUpdateRegions();
},
_queueUpdateRegions: function() {
if (!this._updateRegionIdle)
this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this._updateRegions),
Meta.PRIORITY_BEFORE_REDRAW);
},
_updateFullscreen: function() {
let windows = Main.getWindowActorsForWorkspace(global.screen.get_active_workspace_index());
// Reset all monitors to not fullscreen
for (let i = 0; i < this._monitors.length; i++)
this._monitors[i].inFullscreen = false;
_windowsRestacked: function() {
let windows = global.get_window_actors();
let primary = global.get_primary_monitor();
// The chrome layer should be visible unless there is a window
// with layer FULLSCREEN, or a window with layer
@ -289,43 +248,39 @@ Chrome.prototype = {
// @windows is sorted bottom to top.
let wasInFullscreen = this._inFullscreen;
this._inFullscreen = false;
for (let i = windows.length - 1; i > -1; i--) {
let window = windows[i];
let layer = window.get_meta_window().get_layer();
let layer = windows[i].get_meta_window().get_layer();
// There are 3 cases we check here for:
// 1.) Monitor sized window
// 2.) Window with a position somewhere on the primary screen having the _NET_WM_FULLSCREEN flag set
// 3.) Window that is partly off screen (tries to hide its decorations) which might have negative coords
// We check for 1.) and 2.) by checking if the upper right corner is on the primary monitor, but avoid the case
// where it overlaps with the secondary screen (like window.x + window.width == primary.x + primary.width)
// For 3.) we just ignore negative values as they don't really make sense
if (layer == Meta.StackLayer.FULLSCREEN) {
let monitor = this._findMonitorForWindow(window);
if (monitor)
monitor.inFullscreen = true;
if (Math.max(windows[i].x, 0) >= primary.x && Math.max(windows[i].x, 0) < primary.x + primary.width &&
Math.max(windows[i].y, 0) >= primary.y && Math.max(windows[i].y, 0) < primary.y + primary.height) {
this._inFullscreen = true;
break;
}
}
if (layer == Meta.StackLayer.OVERRIDE_REDIRECT) {
let monitor = this._findMonitorForWindow(window);
if (monitor &&
window.x <= monitor.x &&
window.x + window.width >= monitor.x + monitor.width &&
window.y <= monitor.y &&
window.y + window.height >= monitor.y + monitor.height)
monitor.inFullscreen = true;
if (windows[i].x <= primary.x &&
windows[i].x + windows[i].width >= primary.x + primary.width &&
windows[i].y <= primary.y &&
windows[i].y + windows[i].height >= primary.y + primary.height) {
this._inFullscreen = true;
break;
}
} else
break;
}
},
_windowsRestacked: function() {
let wasInFullscreen = [];
for (let i = 0; i < this._monitors.length; i++)
wasInFullscreen[i] = this._monitors[i].inFullscreen;
this._updateFullscreen();
let changed = false;
for (let i = 0; i < wasInFullscreen.length; i++) {
if (wasInFullscreen[i] != this._monitors[i].inFullscreen) {
changed = true;
break;
}
}
if (changed) {
if (this._inFullscreen != wasInFullscreen) {
this._updateVisibility();
this._queueUpdateRegions();
}
@ -357,75 +312,41 @@ Chrome.prototype = {
if (!actorData.affectsStruts)
continue;
// Limit struts to the size of the screen
let x1 = Math.max(x, 0);
let x2 = Math.min(x + w, global.screen_width);
let y1 = Math.max(y, 0);
let y2 = Math.min(y + h, global.screen_height);
// NetWM struts are not really powerful enought to handle
// a multi-monitor scenario, they only describe what happens
// around the outer sides of the full display region. However
// it can describe a partial region along each side, so
// we can support having the struts only affect the
// primary monitor. This should be enough as we only have
// chrome affecting the struts on the primary monitor so
// far.
//
// Metacity wants to know what side of the screen the
// strut is considered to be attached to. If the actor is
// only touching one edge, or is touching the entire
// border of the primary monitor, then it's obvious which
// side to call it. If it's in a corner, we pick a side
// width/height of one edge, then it's obvious which side
// to call it. If it's in a corner, we pick a side
// arbitrarily. If it doesn't touch any edges, or it spans
// the width/height across the middle of the screen, then
// we don't create a strut for it at all.
let side;
let primary = this._primaryMonitor;
if (x1 <= primary.x && x2 >= primary.x + primary.width) {
if (y1 <= primary.y)
if (w >= global.screen_width) {
if (y <= 0)
side = Meta.Side.TOP;
else if (y2 >= primary.y + primary.height)
else if (y + h >= global.screen_height)
side = Meta.Side.BOTTOM;
else
continue;
} else if (y1 <= primary.y && y2 >= primary.y + primary.height) {
if (x1 <= 0)
} else if (h >= global.screen_height) {
if (x <= 0)
side = Meta.Side.LEFT;
else if (x2 >= global.screen_width)
else if (x + w >= global.screen_width)
side = Meta.Side.RIGHT;
else
continue;
} else if (x1 <= 0)
} else if (x <= 0)
side = Meta.Side.LEFT;
else if (y1 <= 0)
else if (y <= 0)
side = Meta.Side.TOP;
else if (x2 >= global.screen_width)
else if (x + w >= global.screen_width)
side = Meta.Side.RIGHT;
else if (y2 >= global.screen_height)
else if (y + h >= global.screen_height)
side = Meta.Side.BOTTOM;
else
continue;
// Ensure that the strut rects goes all the way to the screen edge,
// as this really what mutter expects.
switch (side) {
case Meta.Side.TOP:
y1 = 0;
break;
case Meta.Side.BOTTOM:
y2 = global.screen_height;
break;
case Meta.Side.LEFT:
x1 = 0;
break;
case Meta.Side.RIGHT:
x2 = global.screen_width;
break;
}
let strutRect = new Meta.Rectangle({ x: x1, y: y1, width: x2 - x1, height: y2 - y1});
let strut = new Meta.Strut({ rect: strutRect, side: side });
let strut = new Meta.Strut({ rect: rect, side: side });
struts.push(strut);
}

View File

@ -1,328 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter;
const Gdk = imports.gi.Gdk;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const AltTab = imports.ui.altTab;
const Main = imports.ui.main;
const Params = imports.misc.params;
const Tweener = imports.ui.tweener;
const POPUP_APPICON_SIZE = 96;
const POPUP_FADE_TIME = 0.1; // seconds
const SortGroup = {
TOP: 0,
MIDDLE: 1,
BOTTOM: 2
};
function CtrlAltTabManager() {
this._init();
}
CtrlAltTabManager.prototype = {
_init: function() {
this._items = [];
this._focusManager = St.FocusManager.get_for_stage(global.stage);
},
addGroup: function(root, name, icon, params) {
let item = Params.parse(params, { sortGroup: SortGroup.MIDDLE,
proxy: root,
focusCallback: null });
item.root = root;
item.name = name;
item.iconName = icon;
this._items.push(item);
root.connect('destroy', Lang.bind(this, function() { this.removeGroup(root); }));
this._focusManager.add_group(root);
},
removeGroup: function(root) {
this._focusManager.remove_group(root);
for (let i = 0; i < this._items.length; i++) {
if (this._items[i].root == root) {
this._items.splice(i, 1);
return;
}
}
},
focusGroup: function(item) {
if (global.stage_input_mode == Shell.StageInputMode.NONREACTIVE ||
global.stage_input_mode == Shell.StageInputMode.NORMAL)
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
if (item.window)
Main.activateWindow(item.window);
else if (item.focusCallback)
item.focusCallback();
else
item.root.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
},
// Sort the items into a consistent order; panel first, tray last,
// and everything else in between, sorted by X coordinate, so that
// they will have the same left-to-right ordering in the
// Ctrl-Alt-Tab dialog as they do onscreen.
_sortItems: function(a, b) {
if (a.sortGroup != b.sortGroup)
return a.sortGroup - b.sortGroup;
let y;
if (a.x == undefined) {
if (a.window)
a.x = a.window.get_compositor_private().x;
else
[a.x, y] = a.proxy.get_transformed_position();
}
if (b.x == undefined) {
if (b.window)
b.x = b.window.get_compositor_private().x;
else
[b.x, y] = b.proxy.get_transformed_position();
}
return a.x - b.x;
},
popup: function(backwards) {
// Start with the set of focus groups that are currently mapped
let items = this._items.filter(function (item) { return item.proxy.mapped; });
// And add the windows metacity would show in its Ctrl-Alt-Tab list
if (!Main.overview.visible) {
let screen = global.screen;
let display = screen.get_display();
let windows = display.get_tab_list(Meta.TabList.DOCKS, screen, screen.get_active_workspace ());
let windowTracker = Shell.WindowTracker.get_default();
let textureCache = St.TextureCache.get_default();
for (let i = 0; i < windows.length; i++) {
let icon;
let app = windowTracker.get_window_app(windows[i]);
if (app)
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,
iconActor: icon,
sortGroup: SortGroup.MIDDLE });
}
}
if (!items.length)
return;
items.sort(Lang.bind(this, this._sortItems));
new CtrlAltTabPopup().show(items, backwards);
}
};
function mod(a, b) {
return (a + b) % b;
}
function CtrlAltTabPopup() {
this._init();
}
CtrlAltTabPopup.prototype = {
_init : function() {
this.actor = new Shell.GenericContainer({ name: 'ctrlAltTabPopup',
reactive: true });
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('destroy', Lang.bind(this, this._onDestroy));
this._haveModal = false;
this._selection = 0;
Main.uiGroup.add_actor(this.actor);
},
_getPreferredWidth: function (actor, forHeight, alloc) {
let primary = global.get_primary_monitor();
alloc.min_size = primary.width;
alloc.natural_size = primary.width;
},
_getPreferredHeight: function (actor, forWidth, alloc) {
let primary = global.get_primary_monitor();
alloc.min_size = primary.height;
alloc.natural_size = primary.height;
},
_allocate: function (actor, box, flags) {
let childBox = new Clutter.ActorBox();
let primary = global.get_primary_monitor();
let leftPadding = this.actor.get_theme_node().get_padding(St.Side.LEFT);
let vPadding = this.actor.get_theme_node().get_vertical_padding();
let hPadding = this.actor.get_theme_node().get_horizontal_padding();
let [childMinHeight, childNaturalHeight] = this._switcher.actor.get_preferred_height(primary.width - hPadding);
let [childMinWidth, childNaturalWidth] = this._switcher.actor.get_preferred_width(childNaturalHeight);
childBox.x1 = Math.max(primary.x + leftPadding, primary.x + Math.floor((primary.width - childNaturalWidth) / 2));
childBox.x2 = Math.min(primary.width - hPadding, childBox.x1 + childNaturalWidth);
childBox.y1 = primary.y + Math.floor((primary.height - childNaturalHeight) / 2);
childBox.y2 = childBox.y1 + childNaturalHeight;
this._switcher.actor.allocate(childBox, flags);
},
show : function(items, startBackwards) {
if (!Main.pushModal(this.actor))
return false;
this._haveModal = true;
this._keyPressEventId = this.actor.connect('key-press-event', Lang.bind(this, this._keyPressEvent));
this._keyReleaseEventId = this.actor.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent));
this._items = items;
this._switcher = new CtrlAltTabSwitcher(items);
this.actor.add_actor(this._switcher.actor);
if (startBackwards)
this._selection = this._items.length - 1;
this._select(this._selection);
let [x, y, mods] = global.get_pointer();
if (!(mods & Gdk.ModifierType.MOD1_MASK)) {
this._finish();
return false;
}
this.actor.opacity = 0;
this.actor.show();
Tweener.addTween(this.actor,
{ opacity: 255,
time: POPUP_FADE_TIME,
transition: 'easeOutQuad'
});
return true;
},
_next : function() {
return mod(this._selection + 1, this._items.length);
},
_previous : function() {
return mod(this._selection - 1, this._items.length);
},
_keyPressEvent : function(actor, event) {
let keysym = event.get_key_symbol();
let shift = (Shell.get_event_state(event) & Clutter.ModifierType.SHIFT_MASK);
if (shift && keysym == Clutter.KEY_Tab)
keysym = Clutter.ISO_Left_Tab;
if (keysym == Clutter.KEY_Escape)
this.destroy();
else if (keysym == Clutter.KEY_Tab)
this._select(this._next());
else if (keysym == Clutter.KEY_ISO_Left_Tab)
this._select(this._previous());
else if (keysym == Clutter.KEY_Left)
this._select(this._previous());
else if (keysym == Clutter.KEY_Right)
this._select(this._next());
return true;
},
_keyReleaseEvent : function(actor, event) {
let [x, y, mods] = global.get_pointer();
let state = mods & Clutter.ModifierType.MOD1_MASK;
if (state == 0)
this._finish();
return true;
},
_finish : function() {
this.destroy();
Main.ctrlAltTabManager.focusGroup(this._items[this._selection]);
},
_popModal: function() {
if (this._haveModal) {
Main.popModal(this.actor);
this._haveModal = false;
}
},
destroy : function() {
this._popModal();
Tweener.addTween(this.actor,
{ opacity: 0,
time: POPUP_FADE_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
this.actor.destroy();
})
});
},
_onDestroy : function() {
this._popModal();
if (this._keyPressEventId)
this.actor.disconnect(this._keyPressEventId);
if (this._keyReleaseEventId)
this.actor.disconnect(this._keyReleaseEventId);
},
_select : function(num) {
this._selection = num;
this._switcher.highlight(num);
}
};
function CtrlAltTabSwitcher(items) {
this._init(items);
}
CtrlAltTabSwitcher.prototype = {
__proto__ : AltTab.SwitcherList.prototype,
_init : function(items) {
AltTab.SwitcherList.prototype._init.call(this, true);
for (let i = 0; i < items.length; i++)
this._addIcon(items[i]);
},
_addIcon : function(item) {
let box = new St.BoxLayout({ style_class: 'alt-tab-app',
vertical: true });
let icon = item.iconActor;
if (!icon) {
icon = new St.Icon({ icon_name: item.iconName,
icon_type: St.IconType.SYMBOLIC,
icon_size: POPUP_APPICON_SIZE });
}
box.add(icon, { x_fill: false, y_fill: false } );
let text = new St.Label({ text: item.name });
box.add(text, { x_fill: false });
this.addItem(box, text);
}
};

View File

@ -1,188 +1,34 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter;
const Signals = imports.signals;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const AppDisplay = imports.ui.appDisplay;
const AppFavorites = imports.ui.appFavorites;
const DND = imports.ui.dnd;
const IconGrid = imports.ui.iconGrid;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const Workspace = imports.ui.workspace;
const DASH_ANIMATION_TIME = 0.2;
// A container like StBin, but taking the child's scale into account
// when requesting a size
function DashItemContainer() {
this._init();
}
DashItemContainer.prototype = {
_init: function() {
this.actor = new Shell.GenericContainer({ style_class: 'dash-item-container' });
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._delegate = this;
this.child = null;
this._childScale = 1;
this._childOpacity = 255;
},
_allocate: function(actor, box, flags) {
if (this.child == null)
return;
let availWidth = box.x2 - box.x1;
let availHeight = box.y2 - box.y1;
let [minChildWidth, minChildHeight, natChildWidth, natChildHeight] =
this.child.get_preferred_size();
let [childScaleX, childScaleY] = this.child.get_scale();
let childWidth = Math.min(natChildWidth * childScaleX, availWidth);
let childHeight = Math.min(natChildHeight * childScaleY, availHeight);
let childBox = new Clutter.ActorBox();
childBox.x1 = (availWidth - childWidth) / 2;
childBox.y1 = (availHeight - childHeight) / 2;
childBox.x2 = childBox.x1 + childWidth;
childBox.y2 = childBox.y1 + childHeight;
this.child.allocate(childBox, flags);
},
_getPreferredHeight: function(actor, forWidth, alloc) {
alloc.min_size = 0;
alloc.natural_size = 0;
if (this.child == null)
return;
let [minHeight, natHeight] = this.child.get_preferred_height(forWidth);
alloc.min_size += minHeight * this.child.scale_y;
alloc.natural_size += natHeight * this.child.scale_y;
},
_getPreferredWidth: function(actor, forHeight, alloc) {
alloc.min_size = 0;
alloc.natural_size = 0;
if (this.child == null)
return;
let [minWidth, natWidth] = this.child.get_preferred_width(forHeight);
alloc.min_size = minWidth * this.child.scale_y;
alloc.natural_size = natWidth * this.child.scale_y;
},
setChild: function(actor) {
if (this.child == actor)
return;
this.actor.destroy_children();
this.child = actor;
this.actor.add_actor(this.child);
},
animateIn: function() {
if (this.child == null)
return;
this.childScale = 0;
this.childOpacity = 0;
Tweener.addTween(this,
{ childScale: 1.0,
childOpacity: 255,
time: DASH_ANIMATION_TIME,
transition: 'easeOutQuad'
});
},
animateOutAndDestroy: function() {
if (this.child == null) {
this.actor.destroy();
return;
}
this.childScale = 1.0;
Tweener.addTween(this,
{ childScale: 0.0,
childOpacity: 0,
time: DASH_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this.actor.destroy();
})
});
},
set childScale(scale) {
this._childScale = scale;
if (this.child == null)
return;
this.child.set_scale_with_gravity(scale, scale,
Clutter.Gravity.CENTER);
this.actor.queue_relayout();
},
get childScale() {
return this._childScale;
},
set childOpacity(opacity) {
this._childOpacity = opacity;
if (this.child == null)
return;
this.child.set_opacity(opacity);
this.actor.queue_redraw();
},
get childOpacity() {
return this._childOpacity;
}
};
function RemoveFavoriteIcon() {
this._init();
}
RemoveFavoriteIcon.prototype = {
__proto__: DashItemContainer.prototype,
_init: function() {
DashItemContainer.prototype._init.call(this);
this._iconBin = new St.Bin({ style_class: 'remove-favorite' });
this.actor = new St.Bin({ style_class: 'remove-favorite' });
this._iconActor = null;
this.icon = new IconGrid.BaseIcon(_("Remove"),
{ setSizeManually: true,
showLabel: false,
createIcon: Lang.bind(this, this._createIcon) });
this._iconBin.set_child(this.icon.actor);
this._iconBin._delegate = this;
this.setChild(this._iconBin);
this.hiding = false;
},
animateOutAndDestroy: function() {
DashItemContainer.prototype.animateOutAndDestroy.call(this);
this.hiding = true;
this.actor.set_child(this.icon.actor);
this.actor._delegate = this;
},
_createIcon: function(size) {
@ -193,7 +39,7 @@ RemoveFavoriteIcon.prototype = {
},
setHover: function(hovered) {
this._iconBin.set_hover(hovered);
this.actor.set_hover(hovered);
if (this._iconActor)
this._iconActor.set_hover(hovered);
},
@ -208,7 +54,7 @@ RemoveFavoriteIcon.prototype = {
if (source instanceof AppDisplay.AppWellIcon) {
let appSystem = Shell.AppSystem.get_default();
app = appSystem.get_app(source.getId());
} else if (source.metaWindow) {
} else if (source instanceof Workspace.WindowClone) {
let tracker = Shell.WindowTracker.get_default();
app = tracker.get_window_app(source.metaWindow);
}
@ -226,35 +72,24 @@ RemoveFavoriteIcon.prototype = {
};
function DragPlaceholderItem() {
this._init();
}
DragPlaceholderItem.prototype = {
__proto__: DashItemContainer.prototype,
_init: function() {
DashItemContainer.prototype._init.call(this);
this.setChild(new St.Bin({ style_class: 'dash-placeholder' }));
}
};
function Dash() {
this._init();
}
Dash.prototype = {
_init : function() {
this._placeholderText = null;
this._menus = [];
this._menuDisplays = [];
this._maxHeight = -1;
this.iconSize = 64;
this._shownInitially = false;
this._iconSize = 48;
this._dragPlaceholder = null;
this._dragPlaceholderPos = -1;
this._animatingPlaceholdersCount = 0;
this._favRemoveTarget = null;
this._favorites = [];
this._box = new St.BoxLayout({ name: 'dash',
vertical: true,
clip_to_allocation: true });
@ -276,62 +111,47 @@ Dash.prototype = {
this._appSystem.connect('installed-changed', Lang.bind(this, this._queueRedisplay));
AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._queueRedisplay));
this._tracker.connect('app-state-changed', Lang.bind(this, this._queueRedisplay));
},
Main.overview.connect('item-drag-begin',
Lang.bind(this, this._onDragBegin));
Main.overview.connect('item-drag-end',
Lang.bind(this, this._onDragEnd));
Main.overview.connect('item-drag-cancelled',
Lang.bind(this, this._onDragCancelled));
Main.overview.connect('window-drag-begin',
Lang.bind(this, this._onDragBegin));
Main.overview.connect('window-drag-cancelled',
Lang.bind(this, this._onDragCancelled));
Main.overview.connect('window-drag-end',
Lang.bind(this, this._onDragEnd));
show: function() {
this._itemDragBeginId = Main.overview.connect('item-drag-begin',
Lang.bind(this, this._onDragBegin));
this._itemDragEndId = Main.overview.connect('item-drag-end',
Lang.bind(this, this._onDragEnd));
this._windowDragBeginId = Main.overview.connect('window-drag-begin',
Lang.bind(this, this._onDragBegin));
this._windowDragEndId = Main.overview.connect('window-drag-end',
Lang.bind(this, this._onDragEnd));
},
hide: function() {
Main.overview.disconnect(this._itemDragBeginId);
Main.overview.disconnect(this._itemDragEndId);
Main.overview.disconnect(this._windowDragBeginId);
Main.overview.disconnect(this._windowDragEndId);
},
_onDragBegin: function() {
this._dragCancelled = false;
this._dragMonitor = {
dragMotion: Lang.bind(this, this._onDragMotion)
};
DND.addDragMonitor(this._dragMonitor);
},
_onDragCancelled: function() {
this._dragCancelled = true;
this._endDrag();
},
_onDragEnd: function() {
if (this._dragCancelled)
return;
this._endDrag();
},
_endDrag: function() {
this._clearDragPlaceholder();
if (this._favRemoveTarget) {
this._favRemoveTarget.actor.hide();
this._adjustIconSize();
this._favRemoveTarget.actor.show();
this._favRemoveTarget.animateOutAndDestroy();
this._favRemoveTarget.actor.connect('destroy', Lang.bind(this,
function() {
this._favRemoveTarget = null;
}));
this._favRemoveTarget.actor.destroy();
this._favRemoveTarget = null;
}
DND.removeDragMonitor(this._dragMonitor);
DND.removeMonitor(this._dragMonitor);
},
_onDragMotion: function(dragEvent) {
let app = null;
if (dragEvent.source instanceof AppDisplay.AppWellIcon)
app = this._appSystem.get_app(dragEvent.source.getId());
else if (dragEvent.source.metaWindow)
else if (dragEvent.source instanceof Workspace.WindowClone)
app = this._tracker.get_window_app(dragEvent.source.metaWindow);
else
return DND.DragMotionResult.CONTINUE;
@ -342,15 +162,10 @@ Dash.prototype = {
let srcIsFavorite = (id in favorites);
if (srcIsFavorite &&
dragEvent.source.actor &&
this.actor.contains (dragEvent.source.actor) &&
this._favRemoveTarget == null) {
if (srcIsFavorite && this._favRemoveTarget == null) {
this._favRemoveTarget = new RemoveFavoriteIcon();
this._favRemoveTarget.icon.setIconSize(this.iconSize);
this._favRemoveTarget.icon.setIconSize(this._iconSize);
this._box.add(this._favRemoveTarget.actor);
this._adjustIconSize();
this._favRemoveTarget.animateIn();
}
let favRemoveHovered = false;
@ -378,10 +193,8 @@ Dash.prototype = {
Main.queueDeferredWork(this._workId);
},
_createAppItem: function(app) {
let display = new AppDisplay.AppWellIcon(app,
{ setSizeManually: true,
showLabel: false });
_addApp: function(app) {
let display = new AppDisplay.AppWellIcon(app);
display._draggable.connect('drag-begin',
Lang.bind(this, function() {
display.actor.opacity = 50;
@ -390,227 +203,63 @@ Dash.prototype = {
Lang.bind(this, function() {
display.actor.opacity = 255;
}));
display.actor.set_tooltip_text(app.get_name());
let item = new DashItemContainer();
item.setChild(display.actor);
display.icon.setIconSize(this.iconSize);
return item;
},
_adjustIconSize: function() {
let children = this._box.get_children();
if (children.length == 0) {
this._box.add_style_pseudo_class('empty');
return;
}
this._box.remove_style_pseudo_class('empty');
if (this._maxHeight == -1)
return;
let iconChildren = children.filter(function(actor) {
return actor.visible &&
actor._delegate.child &&
actor._delegate.child._delegate &&
actor._delegate.child._delegate.icon;
});
// Compute the amount of extra space (or missing space) we have
// per icon with the current icon size
let [minHeight, natHeight] = this.actor.get_preferred_height(-1);
let diff = (this._maxHeight - natHeight) / iconChildren.length;
let iconSizes = [ 16, 22, 24, 32, 48, 64 ];
let newIconSize = 16;
for (let i = 0; i < iconSizes.length; i++) {
if (iconSizes[i] < this.iconSize + diff)
newIconSize = iconSizes[i];
}
if (newIconSize == this.iconSize)
return;
let oldIconSize = this.iconSize;
this.iconSize = newIconSize;
let scale = oldIconSize / newIconSize;
for (let i = 0; i < iconChildren.length; i++) {
let icon = iconChildren[i]._delegate.child._delegate.icon;
// Set the new size immediately, to keep the icons' sizes
// in sync with this.iconSize
icon.setIconSize(this.iconSize);
// Don't animate the icon size change when the overview
// is not visible or when initially filling the dash
if (!Main.overview.visible || !this._shownInitially)
continue;
let [targetWidth, targetHeight] = icon.icon.get_size();
// Scale the icon's texture to the previous size and
// tween to the new size
icon.icon.set_size(icon.icon.width * scale,
icon.icon.height * scale);
Tweener.addTween(icon.icon,
{ width: targetWidth,
height: targetHeight,
time: DASH_ANIMATION_TIME,
transition: 'easeOutQuad'
});
}
this._box.add(display.actor);
},
_redisplay: function () {
this._box.hide();
this._box.remove_all();
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
/* hardcode here pending some design about how exactly desktop contexts behave */
let contextId = '';
let running = this._tracker.get_running_apps(contextId);
let runningIds = this._appIdListToHash(running);
let children = this._box.get_children().filter(function(actor) {
return actor._delegate.child &&
actor._delegate.child._delegate &&
actor._delegate.child._delegate.app;
});
// Apps currently in the dash
let oldApps = children.map(function(actor) {
return actor._delegate.child._delegate.app;
});
// Apps supposed to be in the dash
let newApps = [];
for (let id in favorites)
newApps.push(favorites[id]);
for (let id in favorites) {
let app = favorites[id];
this._addApp(app);
}
for (let i = 0; i < running.length; i++) {
let app = running[i];
if (app.get_id() in favorites)
continue;
newApps.push(app);
this._addApp(app);
}
if (this._placeholderText) {
this._placeholderText.destroy();
this._placeholderText = null;
}
// Figure out the actual changes to the list of items; we iterate
// over both the list of items currently in the dash and the list
// of items expected there, and collect additions and removals.
// Moves are both an addition and a removal, where the order of
// the operations depends on whether we encounter the position
// where the item has been added first or the one from where it
// was removed.
// There is an assumption that only one item is moved at a given
// time; when moving several items at once, everything will still
// end up at the right position, but there might be additional
// additions/removals (e.g. it might remove all the launchers
// and add them back in the new order even if a smaller set of
// additions and removals is possible).
// If above assumptions turns out to be a problem, we might need
// to use a more sophisticated algorithm, e.g. Longest Common
// Subsequence as used by diff.
let addedItems = [];
let removedActors = [];
let children = this._box.get_children();
if (children.length == 0) {
this._placeholderText = new St.Label({ text: _("Drag here to add favorites") });
this._box.add_actor(this._placeholderText);
} else if (this._maxHeight > -1) {
let iconSizes = [ 48, 32, 24, 22, 16 ];
let newIndex = 0;
let oldIndex = 0;
while (newIndex < newApps.length || oldIndex < oldApps.length) {
// No change at oldIndex/newIndex
if (oldApps[oldIndex] == newApps[newIndex]) {
oldIndex++;
newIndex++;
continue;
}
for (let i = 0; i < iconSizes.length; i++) {
let minHeight, natHeight;
// App removed at oldIndex
if (oldApps[oldIndex] &&
newApps.indexOf(oldApps[oldIndex]) == -1) {
removedActors.push(children[oldIndex]);
oldIndex++;
continue;
}
this._iconSize = iconSizes[i];
for (let j = 0; j < children.length; j++)
children[j]._delegate.icon.setIconSize(this._iconSize);
// App added at newIndex
if (newApps[newIndex] &&
oldApps.indexOf(newApps[newIndex]) == -1) {
addedItems.push({ app: newApps[newIndex],
item: this._createAppItem(newApps[newIndex]),
pos: newIndex });
newIndex++;
continue;
}
[minHeight, natHeight] = this.actor.get_preferred_height(-1);
// App moved
let insertHere = newApps[newIndex + 1] &&
newApps[newIndex + 1] == oldApps[oldIndex];
let alreadyRemoved = removedActors.reduce(function(result, actor) {
let removedApp = actor._delegate.child._delegate.app;
return result || removedApp == newApps[newIndex];
}, false);
if (insertHere || alreadyRemoved) {
let newItem = this._createAppItem(newApps[newIndex]);
addedItems.push({ app: newApps[newIndex],
item: newItem,
pos: newIndex + removedActors.length });
newIndex++;
} else {
removedActors.push(children[oldIndex]);
oldIndex++;
if (natHeight <= this._maxHeight)
break;
}
}
for (let i = 0; i < addedItems.length; i++)
this._box.insert_actor(addedItems[i].item.actor,
addedItems[i].pos);
// Hide removed actors to not take them into account
// when adjusting the icon size ...
for (let i = 0; i < removedActors.length; i++)
removedActors[i].hide();
// ... and do the same for the remove target if necessary
if (this._favRemoveTarget && this._favRemoveTarget.hiding)
this._favRemoveTarget.actor.hide();
this._adjustIconSize();
if (this._favRemoveTarget && this._favRemoveTarget.hiding)
this._favRemoveTarget.actor.show();
// Skip animations on first run when adding the initial set
// of items, to avoid all items zooming in at once
if (!this._shownInitially) {
this._shownInitially = true;
return;
}
for (let i = 0; i < removedActors.length; i++) {
removedActors[i].show();
let item = removedActors[i]._delegate;
// Don't animate item removal when the overview is hidden
if (Main.overview.visible)
item.animateOutAndDestroy();
else
item.actor.destroy();
}
// Don't animate item addition when the overview is hidden
if (!Main.overview.visible)
return;
for (let i = 0; i < addedItems.length; i++)
addedItems[i].item.animateIn();
this._box.show();
},
_clearDragPlaceholder: function() {
if (this._dragPlaceholder) {
this._dragPlaceholder.animateOutAndDestroy();
this._dragPlaceholder.destroy();
this._dragPlaceholder = null;
this._dragPlaceholderPos = -1;
}
@ -620,79 +269,40 @@ Dash.prototype = {
let app = null;
if (source instanceof AppDisplay.AppWellIcon)
app = this._appSystem.get_app(source.getId());
else if (source.metaWindow)
else if (source instanceof Workspace.WindowClone)
app = this._tracker.get_window_app(source.metaWindow);
// Don't allow favoriting of transient apps
if (app == null || app.is_transient())
return DND.DragMotionResult.NO_DROP;
let favorites = AppFavorites.getAppFavorites().getFavorites();
let numFavorites = favorites.length;
let favPos = favorites.indexOf(app);
let children = this._box.get_children();
let numChildren = children.length;
let numFavorites = AppFavorites.getAppFavorites().getFavorites().length;
let numChildren = this._box.get_children().length;
let boxHeight = this._box.height;
// Keep the placeholder out of the index calculation; assuming that
// the remove target has the same size as "normal" items, we don't
// need to do the same adjustment there.
if (this._dragPlaceholder) {
boxHeight -= this._dragPlaceholder.actor.height;
boxHeight -= this._dragPlaceholder.height;
numChildren--;
}
let pos = Math.round(y * numChildren / boxHeight);
if (pos != this._dragPlaceholderPos && pos <= numFavorites) {
if (this._animatingPlaceholdersCount > 0) {
let appChildren = children.filter(function(actor) {
return actor._delegate &&
actor._delegate.child &&
actor._delegate.child._delegate &&
actor._delegate.child._delegate.app;
});
this._dragPlaceholderPos = children.indexOf(appChildren[pos]);
} else {
this._dragPlaceholderPos = pos;
}
// Don't allow positioning before or after self
if (favPos != -1 && (pos == favPos || pos == favPos + 1)) {
if (this._dragPlaceholder) {
this._dragPlaceholder.animateOutAndDestroy();
this._animatingPlaceholdersCount++;
this._dragPlaceholder.actor.connect('destroy',
Lang.bind(this, function() {
this._animatingPlaceholdersCount--;
}));
}
this._dragPlaceholder = null;
return DND.DragMotionResult.CONTINUE;
}
// If the placeholder already exists, we just move
// it, but if we are adding it, expand its size in
// an animation
let fadeIn;
if (this._dragPlaceholder) {
this._dragPlaceholder.actor.destroy();
fadeIn = false;
} else {
fadeIn = true;
}
this._dragPlaceholder = new DragPlaceholderItem();
this._box.insert_actor(this._dragPlaceholder.actor,
this._dragPlaceholderPos);
if (fadeIn)
this._dragPlaceholder.animateIn();
this._dragPlaceholderPos = pos;
if (this._dragPlaceholder)
this._dragPlaceholder.destroy();
this._dragPlaceholder = new St.Bin({ style_class: 'dash-placeholder' });
this._box.insert_actor(this._dragPlaceholder, pos);
}
let srcIsFavorite = (favPos != -1);
let id = app.get_id();
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
let srcIsFavorite = (id in favorites);
if (srcIsFavorite)
return DND.DragMotionResult.MOVE_DROP;
@ -705,7 +315,7 @@ Dash.prototype = {
let app = null;
if (source instanceof AppDisplay.AppWellIcon) {
app = this._appSystem.get_app(source.getId());
} else if (source.metaWindow) {
} else if (source instanceof Workspace.WindowClone) {
app = this._tracker.get_window_app(source.metaWindow);
}
@ -723,11 +333,7 @@ Dash.prototype = {
let favPos = 0;
let children = this._box.get_children();
for (let i = 0; i < this._dragPlaceholderPos; i++) {
if (this._dragPlaceholder &&
children[i] == this._dragPlaceholder.actor)
continue;
let childId = children[i]._delegate.child._delegate.app.get_id();
let childId = children[i]._delegate.app.get_id();
if (childId == id)
continue;
if (childId in favorites)

View File

@ -1,211 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Cairo = imports.cairo;
const Clutter = imports.gi.Clutter;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Util = imports.misc.util;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Calendar = imports.ui.calendar;
// in org.gnome.desktop.interface
const CLOCK_FORMAT_KEY = 'clock-format';
// in org.gnome.shell.clock
const CLOCK_SHOW_DATE_KEY = 'show-date';
const CLOCK_SHOW_SECONDS_KEY = 'show-seconds';
function _onVertSepRepaint (area)
{
let cr = area.get_context();
let themeNode = area.get_theme_node();
let [width, height] = area.get_surface_size();
let stippleColor = themeNode.get_color('-stipple-color');
let stippleWidth = themeNode.get_length('-stipple-width');
let x = Math.floor(width/2) + 0.5;
cr.moveTo(x, 0);
cr.lineTo(x, height);
Clutter.cairo_set_source_color(cr, stippleColor);
cr.setDash([1, 3], 1); // Hard-code for now
cr.setLineWidth(stippleWidth);
cr.stroke();
};
function DateMenuButton() {
this._init();
}
DateMenuButton.prototype = {
__proto__: PanelMenu.Button.prototype,
_init: function() {
let item;
let hbox;
let vbox;
this._eventSource = new Calendar.DBusEventSource();
let menuAlignment = 0.25;
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
menuAlignment = 1.0 - menuAlignment;
PanelMenu.Button.prototype._init.call(this, menuAlignment);
this._clock = new St.Label();
this.actor.set_child(this._clock);
hbox = new St.BoxLayout({name: 'calendarArea'});
this.menu.addActor(hbox);
// Fill up the first column
vbox = new St.BoxLayout({vertical: true});
hbox.add(vbox);
// Date
this._date = new St.Label();
this._date.style_class = 'datemenu-date-label';
vbox.add(this._date);
this._eventList = new Calendar.EventsList(this._eventSource);
// Calendar
this._calendar = new Calendar.Calendar(this._eventSource);
this._calendar.connect('selected-date-changed',
Lang.bind(this, function(calendar, date) {
this._eventList.setDate(date);
}));
vbox.add(this._calendar.actor);
item = new PopupMenu.PopupSeparatorMenuItem();
item.setColumnWidths(1);
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
item = new PopupMenu.PopupMenuItem(_("Date and Time Settings"));
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
item.actor.can_focus = false;
vbox.add(item.actor);
// Add vertical separator
item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
pseudo_class: 'highlighted' });
item.connect('repaint', Lang.bind(this, _onVertSepRepaint));
hbox.add(item);
// Fill up the second column
vbox = new St.BoxLayout({vertical: true});
hbox.add(vbox, { expand: true });
// Event list
vbox.add(this._eventList.actor, { expand: true });
item = new PopupMenu.PopupMenuItem(_("Open Calendar"));
item.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
item.actor.can_focus = false;
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
// Whenever the menu is opened, select today
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
if (isOpen) {
let now = new Date();
/* Passing true to setDate() forces events to be reloaded. We
* want this behavior, because
*
* o It will cause activation of the calendar server which is
* useful if it has crashed
*
* o It will cause the calendar server to reload events which
* is useful if dynamic updates are not supported or not
* properly working
*
* Since this only happens when the menu is opened, the cost
* isn't very big.
*/
this._calendar.setDate(now, true);
// No need to update this._eventList as ::selected-date-changed
// signal will fire
}
}));
// Done with hbox for calendar and event list
// Track changes to clock settings
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
this._clockSettings = new Gio.Settings({ schema: 'org.gnome.shell.clock' });
this._desktopSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
this._clockSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
// Start the clock
this._updateClockAndDate();
},
_updateClockAndDate: function() {
let format = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);
let showDate = this._clockSettings.get_boolean(CLOCK_SHOW_DATE_KEY);
let showSeconds = this._clockSettings.get_boolean(CLOCK_SHOW_SECONDS_KEY);
let clockFormat;
let dateFormat;
switch (format) {
case '24h':
if (showDate)
/* Translators: This is the time format with date used
in 24-hour mode. */
clockFormat = showSeconds ? _("%a %b %e, %R:%S")
: _("%a %b %e, %R");
else
/* Translators: This is the time format without date used
in 24-hour mode. */
clockFormat = showSeconds ? _("%a %R:%S")
: _("%a %R");
break;
case '12h':
default:
if (showDate)
/* Translators: This is a time format with date used
for AM/PM. */
clockFormat = showSeconds ? _("%a %b %e, %l:%M:%S %p")
: _("%a %b %e, %l:%M %p");
else
/* Translators: This is a time format without date used
for AM/PM. */
clockFormat = showSeconds ? _("%a %l:%M:%S %p")
: _("%a %l:%M %p");
break;
}
let displayDate = new Date();
this._clock.set_text(displayDate.toLocaleFormat(clockFormat));
/* Translators: This is the date format to use when the calendar popup is
* shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
*/
dateFormat = _("%A %B %e, %Y");
this._date.set_text(displayDate.toLocaleFormat(dateFormat));
Mainloop.timeout_add_seconds(1, Lang.bind(this, this._updateClockAndDate));
return false;
},
_onPreferencesActivate: function() {
this.menu.close();
Main.overview.hide();
let app = Shell.AppSystem.get_default().get_app('gnome-datetime-panel.desktop');
app.activate(-1);
},
_onOpenCalendarActivate: function() {
this.menu.close();
// TODO: pass the selected day
Util.spawn(['evolution', '-c', 'calendar']);
}
};

View File

@ -26,9 +26,9 @@ const DragMotionResult = {
};
const DRAG_CURSOR_MAP = {
0: Shell.Cursor.DND_UNSUPPORTED_TARGET,
1: Shell.Cursor.DND_COPY,
2: Shell.Cursor.DND_MOVE
0: Shell.Cursor.UNSUPPORTED_TARGET,
1: Shell.Cursor.COPY,
2: Shell.Cursor.MOVE
};
const DragDropResult = {
@ -61,7 +61,7 @@ function addDragMonitor(monitor) {
dragMonitors.push(monitor);
}
function removeDragMonitor(monitor) {
function removeMonitor(monitor) {
for (let i = 0; i < dragMonitors.length; i++)
if (dragMonitors[i] == monitor) {
dragMonitors.splice(i, 1);
@ -87,10 +87,6 @@ _Draggable.prototype = {
this.actor.connect('destroy', Lang.bind(this, function() {
this._actorDestroyed = true;
// If the drag actor is destroyed and we were going to fix
// up its hover state, fix up the parent hover state instead
if (this.actor == this._firstLeaveActor)
this._firstLeaveActor = this._dragOrigParent;
if (this._dragInProgress)
this._cancelDrag(global.get_current_time());
this.disconnectAll();
@ -104,14 +100,6 @@ _Draggable.prototype = {
this._buttonDown = false; // The mouse button has been pressed and has not yet been released.
this._dragInProgress = false; // The drag has been started, and has not been dropped or cancelled yet.
this._animationInProgress = false; // The drag is over and the item is in the process of animating to its original position (snapping back or reverting).
// During the drag, we eat enter/leave events so that actors don't prelight or show
// tooltips. But we remember the actors that we first left/last entered so we can
// fix up the hover state after the drag ends.
this._firstLeaveActor = null;
this._lastEnterActor = null;
this._eventsGrabbed = false;
},
_onButtonPress : function (actor, event) {
@ -122,11 +110,11 @@ _Draggable.prototype = {
return false;
this._buttonDown = true;
// special case St.Button: grabbing the pointer would mess up the
// special case St.Clickable: grabbing the pointer would mess up the
// internal state, so we start the drag manually on hover change
if (this.actor instanceof St.Button)
if (this.actor instanceof St.Clickable)
this.actor.connect('notify::hover',
Lang.bind(this, this._onButtonHoverChanged));
Lang.bind(this, this._onClickableHoverChanged));
else
this._grabActor();
@ -137,8 +125,8 @@ _Draggable.prototype = {
return false;
},
_onButtonHoverChanged: function(button) {
if (button.hover || !button.pressed)
_onClickableHoverChanged: function(button) {
if (button.hover || !button.held)
return;
button.fake_release();
@ -154,26 +142,18 @@ _Draggable.prototype = {
_ungrabActor: function() {
Clutter.ungrab_pointer();
if (!this._onEventId)
return;
this.actor.disconnect(this._onEventId);
this._onEventId = null;
},
_grabEvents: function() {
if (!this._eventsGrabbed) {
Clutter.grab_pointer(_getEventHandlerActor());
Clutter.grab_keyboard(_getEventHandlerActor());
this._eventsGrabbed = true;
}
Clutter.grab_pointer(_getEventHandlerActor());
Clutter.grab_keyboard(_getEventHandlerActor());
},
_ungrabEvents: function() {
if (this._eventsGrabbed) {
Clutter.ungrab_pointer();
Clutter.ungrab_keyboard();
this._eventsGrabbed = false;
}
Clutter.ungrab_pointer();
Clutter.ungrab_keyboard();
},
_onEvent: function(actor, event) {
@ -210,11 +190,6 @@ _Draggable.prototype = {
this._cancelDrag(event.get_time());
return true;
}
} else if (event.type() == Clutter.EventType.LEAVE) {
if (this._firstLeaveActor == null)
this._firstLeaveActor = event.get_source();
} else if (event.type() == Clutter.EventType.ENTER) {
this._lastEnterActor = event.get_source();
}
return false;
@ -238,7 +213,7 @@ _Draggable.prototype = {
if (this._onEventId)
this._ungrabActor();
this._grabEvents();
global.set_cursor(Shell.Cursor.DND_IN_DRAG);
global.set_cursor(Shell.Cursor.IN_DRAG);
this._dragX = this._dragStartX = stageX;
this._dragY = this._dragStartY = stageY;
@ -284,13 +259,13 @@ _Draggable.prototype = {
this._dragOffsetY = actorStageY - this._dragStartY;
// Set the actor's scale such that it will keep the same
// transformed size when it's reparented to the uiGroup
// transformed size when it's reparented to the stage
let [scaledWidth, scaledHeight] = this.actor.get_transformed_size();
this.actor.set_scale(scaledWidth / this.actor.width,
scaledHeight / this.actor.height);
}
this._dragActor.reparent(Main.uiGroup);
this._dragActor.reparent(this.actor.get_stage());
this._dragActor.raise_top();
Shell.util_set_hidden_from_pick(this._dragActor, true);
@ -399,7 +374,7 @@ _Draggable.prototype = {
}
target = target.get_parent();
}
global.set_cursor(Shell.Cursor.DND_IN_DRAG);
global.set_cursor(Shell.Cursor.IN_DRAG);
}
return true;
@ -442,7 +417,7 @@ _Draggable.prototype = {
return true;
// If it accepted the drop without taking the actor,
// handle it ourselves.
if (this._dragActor.get_parent() == Main.uiGroup) {
if (this._dragActor.get_parent() == this._dragActor.get_stage()) {
if (this._restoreOnSuccess) {
this._restoreDragActor(event.get_time());
return true;
@ -465,51 +440,25 @@ _Draggable.prototype = {
return true;
},
// Get position of the drag actor's source if the source is still around,
// or return the original location if the actor itself was being dragged
// or the source is no longer around.
_getRestoreLocation: function() {
let x, y, scale;
let locX = this._snapBackX;
let locY = this._snapBackY;
if (this._dragActorSource && this._dragActorSource.visible) {
// Snap the clone back to its source
[x, y] = this._dragActorSource.get_transformed_position();
let [sourceScaledWidth, sourceScaledHeight] = this._dragActorSource.get_transformed_size();
scale = this._dragActor.width / sourceScaledWidth;
} else if (this._dragOrigParent) {
// Snap the actor back to its original position within
// its parent, adjusting for the fact that the parent
// may have been moved or scaled
let [parentX, parentY] = this._dragOrigParent.get_transformed_position();
let [parentWidth, parentHeight] = this._dragOrigParent.get_size();
let [parentScaledWidth, parentScaledHeight] = this._dragOrigParent.get_transformed_size();
let parentScale = 1.0;
if (parentWidth != 0)
parentScale = parentScaledWidth / parentWidth;
x = parentX + parentScale * this._dragOrigX;
y = parentY + parentScale * this._dragOrigY;
scale = this._dragOrigScale * parentScale;
} else {
// Snap back actor to its original stage position
x = this._snapBackX;
y = this._snapBackY;
scale = this._snapBackScale;
}
return [x, y, scale];
if (this._dragActorSource && this._dragActorSource.visible)
[locX, locY] = this._dragActorSource.get_transformed_position();
return [locX, locY];
},
_cancelDrag: function(eventTime) {
this.emit('drag-cancelled', eventTime);
this._dragInProgress = false;
let [snapBackX, snapBackY, snapBackScale] = this._getRestoreLocation();
let [snapBackX, snapBackY] = this._getRestoreLocation();
if (this._actorDestroyed) {
global.unset_cursor();
if (!this._buttonDown)
this._dragComplete();
this.emit('drag-end', eventTime, false);
if (!this._dragOrigParent)
this._dragActor.destroy();
return;
}
@ -518,8 +467,8 @@ _Draggable.prototype = {
Tweener.addTween(this._dragActor,
{ x: snapBackX,
y: snapBackY,
scale_x: snapBackScale,
scale_y: snapBackScale,
scale_x: this._snapBackScale,
scale_y: this._snapBackScale,
opacity: this._dragOrigOpacity,
time: SNAP_BACK_ANIMATION_TIME,
transition: 'easeOutQuad',
@ -531,11 +480,11 @@ _Draggable.prototype = {
_restoreDragActor: function(eventTime) {
this._dragInProgress = false;
[restoreX, restoreY, restoreScale] = this._getRestoreLocation();
[restoreX, restoreY] = this._getRestoreLocation();
// fade the actor back in at its original location
this._dragActor.set_position(restoreX, restoreY);
this._dragActor.set_scale(restoreScale, restoreScale);
this._dragActor.set_scale(this._snapBackScale, this._snapBackScale);
this._dragActor.opacity = 0;
this._animationInProgress = true;
@ -565,36 +514,12 @@ _Draggable.prototype = {
this._dragComplete();
},
// Actor is an actor we have entered or left during the drag; call
// st_widget_sync_hover on all StWidget ancestors
_syncHover: function(actor) {
while (actor) {
let parent = actor.get_parent();
if (actor instanceof St.Widget)
actor.sync_hover();
actor = parent;
}
},
_dragComplete: function() {
if (!this._actorDestroyed)
Shell.util_set_hidden_from_pick(this._dragActor, false);
this._ungrabEvents();
if (this._firstLeaveActor) {
this._syncHover(this._firstLeaveActor);
this._firstLeaveActor = null;
}
if (this._lastEnterActor) {
this._syncHover(this._lastEnterActor);
this._lastEnterActor = null;
}
Shell.util_set_hidden_from_pick(this._dragActor, false);
this._dragActor = undefined;
currentDraggable = null;
this._ungrabEvents();
}
};

View File

@ -1,9 +1,487 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
const Mainloop = imports.mainloop;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const DocInfo = imports.misc.docInfo;
const Params = imports.misc.params;
const DND = imports.ui.dnd;
const GenericDisplay = imports.ui.genericDisplay;
const Main = imports.ui.main;
const Search = imports.ui.search;
const MAX_DASH_DOCS = 50;
const DASH_DOCS_ICON_SIZE = 16;
const DEFAULT_SPACING = 4;
/* This class represents a single display item containing information about a document.
* We take the current number of seconds in the constructor to avoid looking up the current
* time for every item when they are created in a batch.
*
* docInfo - DocInfo object containing information about the document
* currentSeconds - current number of seconds since the epoch
*/
function DocDisplayItem(docInfo, currentSecs) {
this._init(docInfo, currentSecs);
}
DocDisplayItem.prototype = {
__proto__: GenericDisplay.GenericDisplayItem.prototype,
_init : function(docInfo, currentSecs) {
GenericDisplay.GenericDisplayItem.prototype._init.call(this);
this._docInfo = docInfo;
this._setItemInfo(docInfo.name, '');
this._timeoutTime = -1;
this._resetTimeDisplay(currentSecs);
},
//// Public methods ////
getUpdateTimeoutTime: function() {
return this._timeoutTime;
},
// Update any relative-time based displays for this item.
redisplay: function(currentSecs) {
this._resetTimeDisplay(currentSecs);
},
//// Public method overrides ////
// Opens a document represented by this display item.
launch : function() {
this._docInfo.launch();
},
//// Protected method overrides ////
// Returns an icon for the item.
_createIcon : function() {
return this._docInfo.createIcon(GenericDisplay.ITEM_DISPLAY_ICON_SIZE);
},
// Returns a preview icon for the item.
_createPreviewIcon : function() {
return this._docInfo.createIcon(GenericDisplay.PREVIEW_ICON_SIZE);
},
// Creates and returns a large preview icon, but only if this._docInfo is an image file
// and we were able to generate a pixbuf from it successfully.
_createLargePreviewIcon : function() {
if (this._docInfo.mimeType == null || this._docInfo.mimeType.indexOf('image/') != 0)
return null;
try {
return St.TextureCache.get_default().load_uri_sync(St.TextureCachePolicy.NONE,
this._docInfo.uri, -1, -1);
} catch (e) {
// An exception will be raised when the image format isn't know
/* FIXME: http://bugzilla.gnome.org/show_bug.cgi?id=591480: should
* only ignore GDK_PIXBUF_ERROR_UNKNOWN_TYPE. */
return null;
}
},
//// Drag and Drop ////
shellWorkspaceLaunch: function() {
this.launch();
},
//// Private Methods ////
// Updates the last visited time displayed in the description text for the item.
_resetTimeDisplay: function(currentSecs) {
let lastSecs = this._docInfo.timestamp;
let timeDelta = currentSecs - lastSecs;
let [text, nextUpdate] = global.format_time_relative_pretty(timeDelta);
this._timeoutTime = currentSecs + nextUpdate;
this._setDescriptionText(text);
}
};
/* This class represents a display containing a collection of document items.
* The documents are sorted by how recently they were last visited.
*/
function DocDisplay() {
this._init();
}
DocDisplay.prototype = {
__proto__: GenericDisplay.GenericDisplay.prototype,
_init : function() {
GenericDisplay.GenericDisplay.prototype._init.call(this);
// We keep a single timeout callback for updating last visited times
// for all the items in the display. This avoids creating individual
// callbacks for each item in the display. So proper time updates
// for individual items and item details depend on the item being
// associated with one of the displays.
this._updateTimeoutTargetTime = -1;
this._updateTimeoutId = 0;
this._docManager = DocInfo.getDocManager();
this._docsStale = true;
this._docManager.connect('changed', Lang.bind(this, function(mgr, userData) {
this._docsStale = true;
// Changes in local recent files should not happen when we are in the Overview mode,
// but redisplaying right away is cool when we use Zephyr.
// Also, we might be displaying remote documents, like Google Docs, in the future
// which might be edited by someone else.
this._redisplay(GenericDisplay.RedisplayFlags.NONE);
}));
this.connect('destroy', Lang.bind(this, function (o) {
if (this._updateTimeoutId > 0)
Mainloop.source_remove(this._updateTimeoutId);
}));
},
//// Protected method overrides ////
// Gets the list of recent items from the recent items manager.
_refreshCache : function() {
if (!this._docsStale)
return true;
this._allItems = {};
Lang.copyProperties(this._docManager.getInfosByUri(), this._allItems);
this._docsStale = false;
return false;
},
// Sets the list of the displayed items based on how recently they were last visited.
_setDefaultList : function() {
// It seems to be an implementation detail of the Mozilla JavaScript that object
// properties are returned during the iteration in the same order in which they were
// defined, but it is not a guarantee according to this
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Statements/for...in
// While this._allItems associative array seems to always be ordered by last added,
// as the results of this._recentManager.get_items() based on which it is constructed are,
// we should do the sorting manually because we want the order to be based on last visited.
//
// This function is called each time the search string is set back to '' or we display
// the Overview, so we are doing the sorting over the same items multiple times if the list
// of recent items didn't change. We could store an additional array of doc ids and sort
// them once when they are returned by this._recentManager.get_items() to avoid having to do
// this sorting each time, but the sorting seems to be very fast anyway, so there is no need
// to introduce an additional class variable.
this._matchedItems = {};
this._matchedItemKeys = [];
let docIdsToRemove = [];
for (docId in this._allItems) {
this._matchedItems[docId] = 1;
this._matchedItemKeys.push(docId);
}
for (docId in docIdsToRemove) {
delete this._allItems[docId];
}
this._matchedItemKeys.sort(Lang.bind(this, this._compareItems));
},
// Compares items associated with the item ids based on how recently the items
// were last visited.
// Returns an integer value indicating the result of the comparison.
_compareItems : function(itemIdA, itemIdB) {
let docA = this._allItems[itemIdA];
let docB = this._allItems[itemIdB];
return docB.timestamp - docA.timestamp;
},
// Checks if the item info can be a match for the search string by checking
// the name of the document. Item info is expected to be GtkRecentInfo.
// Returns a boolean flag indicating if itemInfo is a match.
_isInfoMatching : function(itemInfo, search) {
if (!itemInfo.exists())
return false;
if (search == null || search == '')
return true;
let name = itemInfo.name.toLowerCase();
if (name.indexOf(search) >= 0)
return true;
// TODO: we can also check doc URIs, so that
// if you search for a directory name, we display recent files from it
return false;
},
// Creates a DocDisplayItem based on itemInfo, which is expected to be a DocInfo object.
_createDisplayItem: function(itemInfo) {
let currentSecs = new Date().getTime() / 1000;
let docDisplayItem = new DocDisplayItem(itemInfo, currentSecs);
this._updateTimeoutCallback(docDisplayItem, currentSecs);
return docDisplayItem;
},
//// Private Methods ////
// A callback function that redisplays the items, updating their descriptions,
// and sets up a new timeout callback.
_docTimeout: function () {
let currentSecs = new Date().getTime() / 1000;
this._updateTimeoutId = 0;
this._updateTimeoutTargetTime = -1;
for (let docId in this._displayedItems) {
let docDisplayItem = this._displayedItems[docId];
docDisplayItem.redisplay(currentSecs);
this._updateTimeoutCallback(docDisplayItem, currentSecs);
}
return false;
},
// Updates the timeout callback if the timeout time for the docDisplayItem
// is earlier than the target time for the current timeout callback.
_updateTimeoutCallback: function (docDisplayItem, currentSecs) {
let timeoutTime = docDisplayItem.getUpdateTimeoutTime();
if (this._updateTimeoutTargetTime < 0 || timeoutTime < this._updateTimeoutTargetTime) {
if (this._updateTimeoutId > 0)
Mainloop.source_remove(this._updateTimeoutId);
this._updateTimeoutId = Mainloop.timeout_add_seconds(timeoutTime - currentSecs, Lang.bind(this, this._docTimeout));
this._updateTimeoutTargetTime = timeoutTime;
}
}
};
Signals.addSignalMethods(DocDisplay.prototype);
function DashDocDisplayItem(docInfo) {
this._init(docInfo);
}
DashDocDisplayItem.prototype = {
_init: function(docInfo) {
this._info = docInfo;
this._icon = docInfo.createIcon(DASH_DOCS_ICON_SIZE);
this.actor = new St.Clickable({ style_class: 'recent-docs-item',
reactive: true,
x_align: St.Align.START });
let box = new St.BoxLayout({ style_class: 'recent-docs-item-box' });
this.actor.set_child(box);
box.add(this._icon);
let text = new St.Label({ text: docInfo.name });
box.add(text);
this.actor.connect('clicked', Lang.bind(this, function () {
docInfo.launch();
Main.overview.hide();
}));
this.actor._delegate = this;
let draggable = DND.makeDraggable(this.actor);
draggable.connect('drag-begin',
Lang.bind(this, function() {
Main.overview.beginItemDrag(this);
}));
draggable.connect('drag-end',
Lang.bind(this, function() {
Main.overview.endItemDrag(this);
}));
},
getUri: function() {
return this._info.uri;
},
getDragActorSource: function() {
return this._icon;
},
getDragActor: function(stageX, stageY) {
this.dragActor = this._info.createIcon(DASH_DOCS_ICON_SIZE);
return this.dragActor;
},
//// Drag and drop functions ////
shellWorkspaceLaunch: function () {
this._info.launch();
}
};
/**
* Class used to display two column recent documents in the dash
*/
function DashDocDisplay() {
this._init();
}
DashDocDisplay.prototype = {
_init: function() {
this.actor = new Shell.GenericContainer();
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._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));
this._actorsByUri = {};
this._docManager = DocInfo.getDocManager();
this._docManager.connect('changed', Lang.bind(this, this._onDocsChanged));
this._pendingDocsChange = true;
this._checkDocExistence = false;
},
_getPreferredWidth: function(actor, forHeight, alloc) {
let children = actor.get_children();
// We use two columns maximum. Just take the min and natural size of the
// first two items, even though strictly speaking it's not correct; we'd
// need to calculate how many items we could fit for the height, then
// take the biggest preferred width for each column.
// In practice the dash gets a fixed width anyways.
// If we have one child, add its minimum and natural size
if (children.length > 0) {
let [minSize, naturalSize] = children[0].get_preferred_width(forHeight);
alloc.min_size += minSize;
alloc.natural_size += naturalSize;
}
// If we have two, add its size, plus DEFAULT_SPACING
if (children.length > 1) {
let [minSize, naturalSize] = children[1].get_preferred_width(forHeight);
alloc.min_size += DEFAULT_SPACING + minSize;
alloc.natural_size += DEFAULT_SPACING + naturalSize;
}
},
_getPreferredHeight: function(actor, forWidth, alloc) {
let children = actor.get_children();
// The width of an item is our allocated width, minus spacing, divided in half.
this._itemWidth = Math.floor((forWidth - DEFAULT_SPACING) / 2);
let maxNatural = 0;
for (let i = 0; i < children.length; i++) {
let child = children[i];
let [minSize, naturalSize] = child.get_preferred_height(this._itemWidth);
maxNatural = Math.max(maxNatural, naturalSize);
}
this._itemHeight = maxNatural;
let firstColumnChildren = Math.ceil(children.length / 2);
alloc.natural_size = (firstColumnChildren * maxNatural +
(firstColumnChildren - 1) * DEFAULT_SPACING);
},
_allocate: function(actor, box, flags) {
let width = box.x2 - box.x1;
let height = box.y2 - box.y1;
// Make sure this._itemWidth/Height have been computed, even
// if the parent actor didn't check our size before allocating.
// (Not clear if that is required or not as a Clutter
// invariant; this is safe and cheap because of caching.)
actor.get_preferred_height(width);
let children = actor.get_children();
let x = 0;
let y = 0;
let columnIndex = 0;
let i = 0;
// Loop over the children, going vertically down first. When we run
// out of vertical space (our y variable is bigger than box.y2), switch
// to the second column.
while (i < children.length) {
let child = children[i];
if (y + this._itemHeight > box.y2) {
// Is this the second column, or we're in
// the first column and can't even fit one
// item? In that case, break.
if (columnIndex == 1 || i == 0) {
break;
}
// Set x to the halfway point.
columnIndex += 1;
x = x + this._itemWidth + DEFAULT_SPACING;
// And y is back to the top.
y = 0;
// Retry this same item, now that we're in the second column.
// By looping back to the top here, we re-test the size
// again for the second column.
continue;
}
let childBox = new Clutter.ActorBox();
childBox.x1 = x;
childBox.y1 = y;
childBox.x2 = childBox.x1 + this._itemWidth;
childBox.y2 = y + this._itemHeight;
y = childBox.y2 + DEFAULT_SPACING;
child.allocate(childBox, flags);
this.actor.set_skip_paint(child, false);
i++;
}
if (this._checkDocExistence) {
// Now we know how many docs we are displaying, queue a check to see if any of them
// have been deleted. If they are deleted, then we'll get a 'changed' signal; since
// we'll now be displaying items we weren't previously, we'll check again to see
// if they were deleted, and so forth and so on.
// TODO: We should change this to ask for as many as we can fit in the given space:
// https://bugzilla.gnome.org/show_bug.cgi?id=603522#c23
this._docManager.queueExistenceCheck(i);
this._checkDocExistence = false;
}
for (; i < children.length; i++)
this.actor.set_skip_paint(children[i], true);
},
_onDocsChanged: function() {
this._checkDocExistence = true;
Main.queueDeferredWork(this._workId);
},
_redisplay: function() {
// Should be kept alive by the _actorsByUri
this.actor.remove_all();
let docs = this._docManager.getTimestampOrderedInfos();
for (let i = 0; i < docs.length && i < MAX_DASH_DOCS; i++) {
let doc = docs[i];
let display = this._actorsByUri[doc.uri];
if (display) {
this.actor.add_actor(display.actor);
} else {
let display = new DashDocDisplayItem(doc);
this.actor.add_actor(display.actor);
this._actorsByUri[doc.uri] = display;
}
}
// Any unparented actors must have been deleted
for (let uri in this._actorsByUri) {
let display = this._actorsByUri[uri];
if (display.actor.get_parent() == null) {
display.actor.destroy();
delete this._actorsByUri[uri];
}
}
this.emit('changed');
}
};
Signals.addSignalMethods(DashDocDisplay.prototype);
function DocSearchProvider() {
this._init();
@ -23,18 +501,12 @@ DocSearchProvider.prototype = {
return null;
return { 'id': resultId,
'name': docInfo.name,
'createIcon': function(size) {
return docInfo.createIcon(size);
}
};
'icon': docInfo.createIcon(Search.RESULT_ICON_SIZE)};
},
activateResult: function(id, params) {
params = Params.parse(params, { workspace: null,
timestamp: null });
activateResult: function(id) {
let docInfo = this._docManager.lookupByUri(id);
docInfo.launch(params.workspace ? params.workspace.index() : -1);
docInfo.launch();
},
getInitialResultSet: function(terms) {
@ -43,5 +515,9 @@ DocSearchProvider.prototype = {
getSubsearchResultSet: function(previousResults, terms) {
return this._docManager.subsearch(previousResults, terms);
},
expandSearch: function(terms) {
log('TODO expand docs search');
}
};

View File

@ -1,532 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*-
*
* Copyright 2010 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
const DBus = imports.dbus;
const Lang = imports.lang;
const Signals = imports.signals;
const Clutter = imports.gi.Clutter;
const Gdm = imports.gi.Gdm;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Pango = imports.gi.Pango;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const GnomeSession = imports.misc.gnomeSession
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog;
const Tweener = imports.ui.tweener;
let _endSessionDialog = null;
const _ITEM_ICON_SIZE = 48;
const _DIALOG_ICON_SIZE = 32;
const GSM_SESSION_MANAGER_LOGOUT_FORCE = 2;
const EndSessionDialogIface = {
name: 'org.gnome.SessionManager.EndSessionDialog',
methods: [{ name: 'Open',
inSignature: 'uuuao',
outSignature: ''
}
],
signals: [{ name: 'Canceled',
inSignature: '',
}],
properties: []
};
const logoutDialogContent = {
subjectWithUser: _("Log Out %s"),
subject: _("Log Out"),
inhibitedDescription: _("Click Log Out to quit these applications and log out of the system."),
uninhibitedDescriptionWithUser: _("%s will be logged out automatically in %d seconds."),
uninhibitedDescription: _("You will be logged out automatically in %d seconds."),
endDescription: _("Logging out of the system."),
confirmButtons: [{ signal: 'ConfirmedLogout',
label: _("Log Out") }],
iconStyleClass: 'end-session-dialog-logout-icon'
};
const shutdownDialogContent = {
subject: _("Power Off"),
inhibitedDescription: _("Click Power Off to quit these applications and power off the system."),
uninhibitedDescription: _("The system will power off automatically in %d seconds."),
endDescription: _("Powering off the system."),
confirmButtons: [{ signal: 'ConfirmedReboot',
label: _("Restart") },
{ signal: 'ConfirmedShutdown',
label: _("Power Off") }],
iconName: 'system-shutdown',
iconStyleClass: 'end-session-dialog-shutdown-icon'
};
const restartDialogContent = {
subject: _("Restart"),
inhibitedDescription: _("Click Restart to quit these applications and restart the system."),
uninhibitedDescription: _("The system will restart automatically in %d seconds."),
endDescription: _("Restarting the system."),
confirmButtons: [{ signal: 'ConfirmedReboot',
label: _("Restart") }],
iconName: 'system-shutdown',
iconStyleClass: 'end-session-dialog-shutdown-icon'
};
const DialogContent = {
0 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_LOGOUT */: logoutDialogContent,
1 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_SHUTDOWN */: shutdownDialogContent,
2 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_RESTART */: restartDialogContent
};
function findAppFromInhibitor(inhibitor) {
let desktopFile = inhibitor.app_id;
if (!GLib.str_has_suffix(desktopFile, '.desktop'))
desktopFile += '.desktop';
let candidateDesktopFiles = [];
candidateDesktopFiles.push(desktopFile);
candidateDesktopFiles.push('gnome-' + desktopFile);
let appSystem = Shell.AppSystem.get_default();
let app = null;
for (let i = 0; i < candidateDesktopFiles.length; i++) {
try {
app = appSystem.get_app(candidateDesktopFiles[i]);
if (app)
break;
} catch(e) {
// ignore errors
}
}
return app;
}
function ListItem(app, reason) {
this._init(app, reason);
}
ListItem.prototype = {
_init: function(app, reason) {
this._app = app;
this._reason = reason;
if (this._reason == null)
this._reason = '';
let layout = new St.BoxLayout({ vertical: false});
this.actor = new St.Button({ style_class: 'end-session-dialog-app-list-item',
can_focus: true,
child: layout,
reactive: true,
x_align: St.Align.START,
x_fill: true });
this._icon = this._app.create_icon_texture(_ITEM_ICON_SIZE);
let iconBin = new St.Bin({ style_class: 'end-session-dialog-app-list-item-icon',
child: this._icon });
layout.add(iconBin);
let textLayout = new St.BoxLayout({ style_class: 'end-session-dialog-app-list-item-text-box',
vertical: true });
layout.add(textLayout);
this._nameLabel = new St.Label({ text: this._app.get_name(),
style_class: 'end-session-dialog-app-list-item-name' });
textLayout.add(this._nameLabel,
{ expand: false,
x_fill: true });
this._descriptionLabel = new St.Label({ text: this._reason,
style_class: 'end-session-dialog-app-list-item-description' });
textLayout.add(this._descriptionLabel,
{ expand: true,
x_fill: true });
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
},
_onClicked: function() {
this.emit('activate');
this._app.activate(-1);
}
};
Signals.addSignalMethods(ListItem.prototype);
// The logout timer only shows updates every 10 seconds
// until the last 10 seconds, then it shows updates every
// second. This function takes a given time and returns
// what we should show to the user for that time.
function _roundSecondsToInterval(totalSeconds, secondsLeft, interval) {
let time;
time = Math.ceil(secondsLeft);
// Final count down is in decrements of 1
if (time <= interval)
return time;
// Round up higher than last displayable time interval
time += interval - 1;
// Then round down to that time interval
if (time > totalSeconds)
time = Math.ceil(totalSeconds);
else
time -= time % interval;
return time;
}
function _setLabelText(label, text) {
if (text) {
label.set_text(text);
label.show();
} else {
label.set_text('');
label.hide();
}
}
function EndSessionDialog() {
if (_endSessionDialog == null) {
this._init();
DBus.session.exportObject('/org/gnome/SessionManager/EndSessionDialog',
this);
_endSessionDialog = this;
}
return _endSessionDialog;
}
function init() {
// This always returns the same singleton object
// By instantiating it initially, we register the
// bus object, etc.
let dialog = new EndSessionDialog();
}
EndSessionDialog.prototype = {
__proto__: ModalDialog.ModalDialog.prototype,
_init: function() {
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'end-session-dialog' });
this._user = Gdm.UserManager.ref_default().get_user(GLib.get_user_name());
this._secondsLeft = 0;
this._totalSecondsToStayOpen = 0;
this._inhibitors = [];
this.connect('destroy',
Lang.bind(this, this._onDestroy));
this.connect('opened',
Lang.bind(this, this._onOpened));
this._userLoadedId = this._user.connect('notify::is_loaded',
Lang.bind(this, this._updateContent));
this._userChangedId = this._user.connect('changed',
Lang.bind(this, this._updateContent));
let mainContentLayout = new St.BoxLayout({ vertical: false });
this.contentLayout.add(mainContentLayout,
{ x_fill: true,
y_fill: false });
this._iconBin = new St.Bin();
mainContentLayout.add(this._iconBin,
{ x_fill: true,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.START });
let messageLayout = new St.BoxLayout({ vertical: true });
mainContentLayout.add(messageLayout,
{ y_align: St.Align.START });
this._subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject' });
messageLayout.add(this._subjectLabel,
{ y_fill: false,
y_align: St.Align.START });
this._descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description' });
this._descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._descriptionLabel.clutter_text.line_wrap = true;
messageLayout.add(this._descriptionLabel,
{ y_fill: true,
y_align: St.Align.START });
let scrollView = new St.ScrollView({ style_class: 'end-session-dialog-app-list'});
scrollView.set_policy(Gtk.PolicyType.NEVER,
Gtk.PolicyType.AUTOMATIC);
this.contentLayout.add(scrollView,
{ x_fill: true,
y_fill: true });
scrollView.hide();
this._applicationList = new St.BoxLayout({ vertical: true });
scrollView.add_actor(this._applicationList,
{ x_fill: true,
y_fill: true,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
this._applicationList.connect('actor-added',
Lang.bind(this, function() {
if (this._applicationList.get_children().length == 1)
scrollView.show();
}));
this._applicationList.connect('actor-removed',
Lang.bind(this, function() {
if (this._applicationList.get_children().length == 0)
scrollView.hide();
}));
},
_onDestroy: function() {
this._user.disconnect(this._userLoadedId);
this._user.disconnect(this._userChangedId);
},
_setIconFromFile: function(iconFile, styleClass) {
if (styleClass)
this._iconBin.set_style_class_name(styleClass);
this._iconBin.set_style(null);
this._iconBin.child = null;
if (iconFile) {
this._iconBin.show();
this._iconBin.set_style('background-image: url("' + iconFile + '");');
} else {
this._iconBin.hide();
}
},
_setIconFromName: function(iconName, styleClass) {
if (styleClass)
this._iconBin.set_style_class_name(styleClass);
this._iconBin.set_style(null);
if (iconName != null) {
let textureCache = St.TextureCache.get_default();
let icon = textureCache.load_icon_name(this._iconBin.get_theme_node(),
iconName,
St.IconType.SYMBOLIC,
_DIALOG_ICON_SIZE);
this._iconBin.child = icon;
this._iconBin.show();
} else {
this._iconBin.child = null;
this._iconBin.hide();
}
},
_updateContent: function() {
if (this.state != ModalDialog.State.OPENING &&
this.state != ModalDialog.State.OPENED)
return;
let dialogContent = DialogContent[this._type];
let subject = dialogContent.subject;
let description;
if (this._user.is_loaded && !dialogContent.iconName) {
let iconFile = this._user.get_icon_file();
if (GLib.file_test(iconFile, GLib.FileTest.EXISTS))
this._setIconFromFile(iconFile, dialogContent.iconStyleClass);
else
this._setIconFromName('avatar-default', dialogContent.iconStyleClass);
} else if (dialogContent.iconName) {
this._setIconFromName(dialogContent.iconName,
dialogContent.iconStyleClass);
}
if (this._inhibitors.length > 0) {
this._stopTimer();
description = dialogContent.inhibitedDescription;
} else if (this._secondsLeft > 0 && this._inhibitors.length == 0) {
let displayTime = _roundSecondsToInterval(this._totalSecondsToStayOpen,
this._secondsLeft,
10);
if (this._user.is_loaded) {
let realName = this._user.get_real_name();
if (realName != null) {
if (dialogContent.subjectWithUser)
subject = dialogContent.subjectWithUser.format(realName);
if (dialogContent.uninhibitedDescriptionWithUser)
description = dialogContent.uninhibitedDescriptionWithUser.format(realName, displayTime);
else
description = dialogContent.uninhibitedDescription.format(displayTime);
}
}
if (!description)
description = dialogContent.uninhibitedDescription.format(displayTime);
} else {
description = dialogContent.endDescription;
}
_setLabelText(this._subjectLabel, subject);
_setLabelText(this._descriptionLabel, description);
},
_updateButtons: function() {
let dialogContent = DialogContent[this._type];
let buttons = [{ action: Lang.bind(this, this.cancel),
label: _("Cancel"),
key: Clutter.Escape }];
for (let i = 0; i < dialogContent.confirmButtons.length; i++) {
let signal = dialogContent.confirmButtons[i].signal;
let label = dialogContent.confirmButtons[i].label;
buttons.push({ action: Lang.bind(this, function() {
this._confirm(signal);
}),
label: label });
}
this.setButtons(buttons);
},
close: function() {
ModalDialog.ModalDialog.prototype.close.call(this);
DBus.session.emit_signal('/org/gnome/SessionManager/EndSessionDialog',
'org.gnome.SessionManager.EndSessionDialog',
'Closed', '', []);
},
cancel: function() {
this._stopTimer();
DBus.session.emit_signal('/org/gnome/SessionManager/EndSessionDialog',
'org.gnome.SessionManager.EndSessionDialog',
'Canceled', '', []);
this.close(global.get_current_time());
},
_confirm: function(signal) {
this._fadeOutDialog();
this._stopTimer();
DBus.session.emit_signal('/org/gnome/SessionManager/EndSessionDialog',
'org.gnome.SessionManager.EndSessionDialog',
signal, '', []);
},
_onOpened: function() {
if (this._inhibitors.length == 0)
this._startTimer();
},
_startTimer: function() {
this._secondsLeft = this._totalSecondsToStayOpen;
Tweener.addTween(this,
{ _secondsLeft: 0,
time: this._secondsLeft,
transition: 'linear',
onUpdate: Lang.bind(this, this._updateContent),
onComplete: Lang.bind(this, function() {
let dialogContent = DialogContent[this._type];
let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
this._confirm(button.signal);
}),
});
},
_stopTimer: function() {
Tweener.removeTweens(this);
this._secondsLeft = 0;
},
_onInhibitorLoaded: function(inhibitor) {
if (this._inhibitors.indexOf(inhibitor) < 0) {
// Stale inhibitor
return;
}
let app = findAppFromInhibitor(inhibitor);
if (app) {
let item = new ListItem(app, inhibitor.reason);
item.connect('activate',
Lang.bind(this, function() {
this.close(global.get_current_time());
}));
this._applicationList.add(item.actor, { x_fill: true });
this._stopTimer();
} else {
// inhibiting app is a service, not an application
this._inhibitors.splice(this._inhibitors.indexOf(inhibitor), 1);
}
this._updateContent();
},
OpenAsync: function(type, timestamp, totalSecondsToStayOpen, inhibitorObjectPaths, callback) {
this._totalSecondsToStayOpen = totalSecondsToStayOpen;
this._inhibitors = [];
this._applicationList.destroy_children();
this._type = type;
if (!(this._type in DialogContent))
throw new DBus.DBusError('org.gnome.Shell.ModalDialog.TypeError',
"Unknown dialog type requested");
for (let i = 0; i < inhibitorObjectPaths.length; i++) {
let inhibitor = new GnomeSession.Inhibitor(inhibitorObjectPaths[i]);
inhibitor.connect('is-loaded',
Lang.bind(this, function() {
this._onInhibitorLoaded(inhibitor);
}));
this._inhibitors.push(inhibitor);
}
this._updateButtons();
if (!this.open(timestamp))
throw new DBus.DBusError('org.gnome.Shell.ModalDialog.GrabError',
"Cannot grab pointer and keyboard");
this._updateContent();
let signalId = this.connect('opened',
Lang.bind(this, function() {
callback();
this.disconnect(signalId);
}));
}
};
DBus.conformExport(EndSessionDialog.prototype, EndSessionDialogIface);

View File

@ -1,22 +1,14 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
imports.gi.versions.Clutter = '1.0';
imports.gi.versions.Gio = '2.0';
imports.gi.versions.Gdk = '3.0';
imports.gi.versions.GdkPixbuf = '2.0';
imports.gi.versions.Gtk = '3.0';
const Clutter = imports.gi.Clutter;;
const Gettext = imports.gettext;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Gettext_gtk20 = imports.gettext.domain('gtk20');
// We can't import shell JS modules yet, because they may have
// variable initializations, etc, that depend on init() already having
// been run.
const Tweener = imports.ui.tweener;
const Format = imports.misc.format;
// "monkey patch" in some varargs ClutterContainer methods; we need
// to do this per-container class since there is no representation
@ -25,7 +17,7 @@ function _patchContainerClass(containerClass) {
// This one is a straightforward mapping of the C method
containerClass.prototype.child_set = function(actor, props) {
let meta = this.get_child_meta(actor);
for (let prop in props)
for (prop in props)
meta[prop] = props[prop];
};
@ -39,42 +31,44 @@ function _patchContainerClass(containerClass) {
};
}
function init() {
// Add some bindings to the global JS namespace; (gjs keeps the web
// browser convention of having that namespace be called 'window'.)
window.global = Shell.Global.get();
// Replace @method with something that throws an error instead
function _blockMethod(method, replacement, reason) {
let match = method.match(/^(.+)\.([^.]+)$/);
if (!match)
throw new Error('Bad method name "' + method + '"');
let proto = 'imports.gi.' + match[1] + '.prototype';
let property = match[2];
window._ = Gettext.gettext;
window.C_ = Gettext.pgettext;
window.ngettext = Gettext.ngettext;
if (!global.set_property_mutable(proto, property, true))
throw new Error('Bad method name "' + method + '"');
// eval() is evil in general, but we know it's safe here since
// set_property_mutable() would have failed if proto was
// malformed.
let node = eval(proto);
let msg = 'Do not use "' + method + '".';
if (replacement)
msg += ' Use "' + replacement + '" instead.';
if (reason)
msg += ' (' + reason + ')';
node[property] = function() {
throw new Error(msg);
};
global.set_property_mutable(proto, property, false);
}
function init() {
Tweener.init();
String.prototype.format = Format.format;
// Set the default direction for St widgets (this needs to be done before any use of St)
if (Gtk.Widget.get_default_direction() == Gtk.TextDirection.RTL) {
if (Gettext_gtk20.gettext('default:LTR') == 'default:RTL') {
St.Widget.set_default_direction(St.TextDirection.RTL);
}
// Miscellaneous monkeypatching
_patchContainerClass(St.BoxLayout);
_patchContainerClass(St.Table);
Clutter.Actor.prototype.toString = function() {
return St.describe_actor(this);
};
let origToString = Object.prototype.toString;
Object.prototype.toString = function() {
let base = origToString.call(this);
if ('actor' in this && this.actor instanceof Clutter.Actor)
return base.replace(/\]$/, ' delegate for ' + this.actor.toString().substring(1));
else
return base;
};
// Work around https://bugzilla.mozilla.org/show_bug.cgi?id=508783
Date.prototype.toLocaleFormat = function(format) {
return Shell.util_format_date(format, this.getTime());
};
let slowdownEnv = GLib.getenv('GNOME_SHELL_SLOWDOWN_FACTOR');
if (slowdownEnv) {
let factor = parseFloat(slowdownEnv);
@ -82,10 +76,26 @@ function init() {
St.set_slow_down_factor(factor);
}
// OK, now things are initialized enough that we can import shell JS
const Format = imports.misc.format;
const Tweener = imports.ui.tweener;
_patchContainerClass(St.BoxLayout);
_patchContainerClass(St.Table);
Tweener.init();
String.prototype.format = Format.format;
Clutter.Actor.prototype.toString = function() {
return St.describe_actor(this);
};
if (window.global === undefined) // test environment
return;
_blockMethod('Clutter.Event.get_state', 'Shell.get_event_state',
'gjs\'s handling of Clutter.ModifierType is broken. See bug 597292.');
_blockMethod('Gdk.Display.get_device_state', 'global.get_pointer',
'gjs\'s handling of Gdk.ModifierType is broken. See bug 597292.');
_blockMethod('Gdk.Window.get_device_position', 'global.get_pointer',
'gjs\'s handling of Gdk.ModifierType is broken. See bug 597292.');
// Now close the back door to prevent extensions from trying to
// abuse it. We can't actually delete it since
// Shell.Global.prototype itself is read-only.
global.set_property_mutable('imports.gi.Shell.Global.prototype', 'set_property_mutable', true);
Shell.Global.prototype.set_property_mutable = undefined;
}

View File

@ -3,9 +3,6 @@
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Config = imports.misc.config;
const ExtensionState = {
ENABLED: 1,
@ -23,42 +20,11 @@ const ExtensionType = {
const extensionMeta = {};
// Maps uuid -> importer object (extension directory tree)
const extensions = {};
// Arrays of uuids
// Array of uuids
var disabledExtensions;
var enabledExtensions;
// GFile for user extensions
var userExtensionsDir = null;
/**
* versionCheck:
* @required: an array of versions we're compatible with
* @current: the version we have
*
* Check if a component is compatible for an extension.
* @required is an array, and at least one version must match.
* @current must be in the format <major>.<minor>.<point>.<micro>
* <micro> is always ignored
* <point> is ignored if <minor> is even (so you can target the
* whole stable release)
* <minor> and <major> must match
* Each target version must be at least <major> and <minor>
*/
function versionCheck(required, current) {
let currentArray = current.split('.');
let major = currentArray[0];
let minor = currentArray[1];
let point = currentArray[2];
for (let i = 0; i < required.length; i++) {
let requiredArray = required[i].split('.');
if (requiredArray[0] == major &&
requiredArray[1] == minor &&
(requiredArray[2] == point ||
(requiredArray[2] == undefined && parseInt(minor) % 2 == 0)))
return true;
}
return false;
}
function loadExtension(dir, enabled, type) {
let info;
let baseErrorString = 'While loading extension from "' + dir.get_parse_name() + '": ';
@ -69,13 +35,7 @@ function loadExtension(dir, enabled, type) {
return;
}
let metadataContents;
try {
metadataContents = Shell.get_file_contents_utf8_sync(metadataFile.get_path());
} catch (e) {
global.logError(baseErrorString + 'Failed to load metadata.json: ' + e);
return;
}
let [success, metadataContents, len, etag] = metadataFile.load_contents(null);
let meta;
try {
meta = JSON.parse(metadataContents);
@ -83,8 +43,8 @@ function loadExtension(dir, enabled, type) {
global.logError(baseErrorString + 'Failed to parse metadata.json: ' + e);
return;
}
let requiredProperties = ['uuid', 'name', 'description', 'shell-version'];
for (let i = 0; i < requiredProperties.length; i++) {
let requiredProperties = ['uuid', 'name', 'description'];
for (let i = 0; i < requiredProperties; i++) {
let prop = requiredProperties[i];
if (!meta[prop]) {
global.logError(baseErrorString + 'missing "' + prop + '" property in metadata.json');
@ -108,12 +68,6 @@ function loadExtension(dir, enabled, type) {
return;
}
if (!versionCheck(meta['shell-version'], Config.PACKAGE_VERSION) ||
(meta['js-version'] && !versionCheck(meta['js-version'], Config.GJS_VERSION))) {
global.logError(baseErrorString + 'extension is not compatible with current GNOME Shell and/or GJS version');
return;
}
extensionMeta[meta.uuid] = meta;
extensionMeta[meta.uuid].type = type;
extensionMeta[meta.uuid].path = dir.get_path();
@ -158,7 +112,7 @@ function loadExtension(dir, enabled, type) {
return;
}
try {
extensionModule.main(meta);
extensionModule.main();
} catch (e) {
if (stylesheetPath != null)
theme.unload_stylesheet(stylesheetPath);
@ -179,28 +133,17 @@ function init() {
}
disabledExtensions = global.settings.get_strv('disabled-extensions', -1);
enabledExtensions = global.settings.get_strv('enabled-extensions', -1);
}
function _loadExtensionsIn(dir, type) {
let fileEnum;
let fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
let file, info;
try {
fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
} catch (e) {
global.logError('' + e);
return;
}
while ((info = fileEnum.next_file(null)) != null) {
let fileType = info.get_file_type();
if (fileType != Gio.FileType.DIRECTORY)
continue;
let name = info.get_name();
// Enable all but disabled extensions if enabledExtensions is not set.
// If it is set, enable one those, except they are disabled as well.
let enabled = (enabledExtensions.length == 0 || enabledExtensions.indexOf(name) >= 0)
&& disabledExtensions.indexOf(name) < 0;
let enabled = disabledExtensions.indexOf(name) < 0;
let child = dir.get_child(name);
loadExtension(child, enabled, type);
}

693
js/ui/genericDisplay.js Normal file
View File

@ -0,0 +1,693 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Signals = imports.signals;
const St = imports.gi.St;
const DND = imports.ui.dnd;
const Main = imports.ui.main;
const RedisplayFlags = { NONE: 0,
FULL: 1 << 1,
SUBSEARCH: 1 << 2,
IMMEDIATE: 1 << 3 };
// Used by subclasses
const ITEM_DISPLAY_ICON_SIZE = 48;
const PREVIEW_ICON_SIZE = 96;
/* This is a virtual class that represents a single display item containing
* a name, a description, and an icon. It allows selecting an item and represents
* it by highlighting it with a different background color than the default.
*/
function GenericDisplayItem() {
this._init();
}
GenericDisplayItem.prototype = {
_init: function() {
this.actor = new St.BoxLayout({ style_class: 'generic-display-item',
reactive: true });
this.actor._delegate = this;
this.actor.connect('button-release-event',
Lang.bind(this,
function() {
// Activates the item by launching it
this.emit('activate');
return true;
}));
let draggable = DND.makeDraggable(this.actor);
draggable.connect('drag-begin',
Lang.bind(this, function() {
Main.overview.beginItemDrag(this);
}));
draggable.connect('drag-end',
Lang.bind(this, function() {
Main.overview.endItemDrag(this);
}));
this._iconBin = new St.Bin();
this.actor.add(this._iconBin);
this._infoText = new St.BoxLayout({ style_class: 'generic-display-item-text',
vertical: true });
this.actor.add(this._infoText, { expand: true, y_fill: false });
this._name = null;
this._description = null;
this._icon = null;
this._initialLoadComplete = false;
// An array of details description actors that we create over time for the item.
// It is used for updating the description text inside the details actor when
// the description text for the item is updated.
this._detailsDescriptions = [];
},
//// Draggable object interface ////
// Returns a cloned texture of the item's icon to represent the item as it
// is being dragged.
getDragActor: function(stageX, stageY) {
return this._createIcon();
},
// Returns the item icon, a separate copy of which is used to
// represent the item as it is being dragged. This is used to
// determine a snap-back location for the drag icon if it does
// not get accepted by any drop target.
getDragActorSource: function() {
return this._icon;
},
//// Public methods ////
// Highlights the item by setting a different background color than the default
// if isSelected is true, removes the highlighting otherwise.
markSelected: function(isSelected) {
if (isSelected)
this.actor.add_style_pseudo_class('selected');
else
this.actor.remove_style_pseudo_class('selected');
},
/*
* Returns an actor containing item details. In the future details can have more information than what
* the preview pop-up has and be item-type specific.
*/
createDetailsActor: function() {
let details = new St.BoxLayout({ style_class: 'generic-display-container',
vertical: true });
let mainDetails = new St.BoxLayout({ style_class: 'generic-display-container' });
// Inner box with name and description
let textDetails = new St.BoxLayout({ style_class: 'generic-display-details',
vertical: true });
let detailsName = new St.Label({ style_class: 'generic-display-details-name',
text: this._name.text });
textDetails.add(detailsName);
let detailsDescription = new St.Label({ text: this._description.text });
textDetails.add(detailsDescription);
this._detailsDescriptions.push(detailsDescription);
mainDetails.add(textDetails, { expand: true });
let previewIcon = this._createPreviewIcon();
let largePreviewIcon = this._createLargePreviewIcon();
if (previewIcon != null && largePreviewIcon == null) {
mainDetails.insert_actor(previewIcon, 0);
}
details.add(mainDetails);
if (largePreviewIcon != null) {
details.add(largePreviewIcon);
}
return details;
},
// Destroys the item.
destroy: function() {
this.actor.destroy();
},
//// Pure virtual public methods ////
// Performes an action associated with launching this item, such as opening a file or an application.
launch: function() {
throw new Error('Not implemented');
},
//// Protected methods ////
/*
* Creates the graphical elements for the item based on the item information.
*
* nameText - name of the item
* descriptionText - short description of the item
*/
_setItemInfo: function(nameText, descriptionText) {
if (this._name != null) {
// this also removes this._name from the parent container,
// so we don't need to call this.actor.remove_actor(this._name) directly
this._name.destroy();
this._name = null;
}
if (this._description != null) {
this._description.destroy();
this._description = null;
}
if (this._icon != null) {
// though we get the icon from elsewhere, we assume its ownership here,
// and therefore should be responsible for distroying it
this._icon.destroy();
this._icon = null;
}
this._icon = this._createIcon();
this._iconBin.set_child(this._icon);
this._name = new St.Label({ style_class: 'generic-display-item-name',
text: nameText });
this._infoText.add(this._name);
this._description = new St.Label({ style_class: 'generic-display-item-description',
text: descriptionText ? descriptionText : '' });
this._infoText.add(this._description);
},
// Sets the description text for the item, including the description text
// in the details actors that have been created for the item.
_setDescriptionText: function(text) {
this._description.text = text;
for (let i = 0; i < this._detailsDescriptions.length; i++) {
let detailsDescription = this._detailsDescriptions[i];
if (detailsDescription != null) {
detailsDescription.text = text;
}
}
},
//// Virtual protected methods ////
// Creates and returns a large preview icon, but only if we have a detailed image.
_createLargePreviewIcon : function() {
return null;
},
//// Pure virtual protected methods ////
// Returns an icon for the item.
_createIcon: function() {
throw new Error('Not implemented');
},
// Returns a preview icon for the item.
_createPreviewIcon: function() {
throw new Error('Not implemented');
}
//// Private methods ////
};
Signals.addSignalMethods(GenericDisplayItem.prototype);
/* This is a virtual class that represents a display containing a collection of items
* that can be filtered with a search string.
*/
function GenericDisplay() {
this._init();
}
GenericDisplay.prototype = {
_init : function() {
this._search = '';
this._expanded = false;
this.actor = new St.ScrollView({ x_fill: true,
y_fill: false,
vshadows: true });
this._list = new St.BoxLayout({ style_class: 'generic-display-container',
vertical: true });
this.actor.add_actor(this._list);
this.actor.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
this._pendingRedisplay = RedisplayFlags.NONE;
this.actor.connect('notify::mapped', Lang.bind(this, this._onMappedNotify));
// map<itemId, Object> where Object represents the item info
this._allItems = {};
// set<itemId>
this._matchedItems = {};
// sorted array of items matched by search
this._matchedItemKeys = [];
// map<itemId, GenericDisplayItem>
this._displayedItems = {};
this._openDetailIndex = -1;
this._selectedIndex = -1;
},
//// Public methods ////
// Sets the search string and displays the matching items.
setSearch: function(text) {
let lowertext = text.toLowerCase();
if (lowertext == this._search) {
return;
}
let flags = RedisplayFlags.IMMEDIATE;
if (this._search != '') {
// Because we combine search terms with OR, we have to be sure that no new term
// was introduced before deciding that the new search results will be a subset of
// the existing search results.
if (lowertext.indexOf(this._search) == 0 &&
lowertext.split(/\s+/).length == this._search.split(/\s+/).length) {
flags |= RedisplayFlags.SUBSEARCH;
}
}
this._search = lowertext;
this._redisplay(flags);
},
// Launches the item that is currently selected, closing the Overview
activateSelected: function() {
if (this._selectedIndex != -1) {
let selected = this._findDisplayedByIndex(this._selectedIndex);
selected.launch();
this.unsetSelected();
Main.overview.hide();
}
},
// Moves the selection one up. If the selection was already on the top item, it's moved
// to the bottom one. Returns true if the selection actually moved up, false if it wrapped
// around to the bottom.
selectUp: function() {
let count = this._getVisibleCount();
let selectedUp = true;
let prev = this._selectedIndex - 1;
if (this._selectedIndex <= 0) {
prev = count - 1;
selectedUp = false;
}
this._selectIndex(prev);
return selectedUp;
},
// Moves the selection one down. If the selection was already on the bottom item, it's moved
// to the top one. Returns true if the selection actually moved down, false if it wrapped
// around to the top.
selectDown: function() {
let count = this._getVisibleCount();
let selectedDown = true;
let next = this._selectedIndex + 1;
if (this._selectedIndex == count - 1) {
next = 0;
selectedDown = false;
}
this._selectIndex(next);
return selectedDown;
},
// Selects the first item among the displayed items.
selectFirstItem: function() {
if (this.hasItems())
this._selectIndex(0);
},
// Selects the last item among the displayed items.
selectLastItem: function() {
let count = this._getVisibleCount();
if (this.hasItems())
this._selectIndex(count - 1);
},
// Returns true if the display has some item selected.
hasSelected: function() {
return this._selectedIndex != -1;
},
// Removes selection if some display item is selected.
unsetSelected: function() {
this._selectIndex(-1);
},
// Returns true if the display has any displayed items.
hasItems: function() {
// TODO: figure out why this._list.displayedCount is returning a
// positive number when this._mathedItems.length is 0
// This can be triggered if a search string is entered for which there are no matches.
// log('this._mathedItems.length: ' + this._matchedItems.length + ' this._list.displayedCount ' + this._list.displayedCount);
return this._matchedItemKeys.length > 0;
},
getMatchedItemsCount: function() {
return this._matchedItemKeys.length;
},
// Load the initial state
load: function() {
this._redisplay(RedisplayFlags.FULL);
},
// Should be called when the display is closed
resetState: function() {
this._filterReset();
this._openDetailIndex = -1;
this.actor.get_vscroll_bar().get_adjustment().value = 0;
},
// Returns an actor which acts as a sidebar; this is used for
// the applications category view
getNavigationArea: function () {
return null;
},
createDetailsForIndex: function(index) {
let item = this._findDisplayedByIndex(index);
return item.createDetailsActor();
},
//// Protected methods ////
_recreateDisplayItems: function() {
this._removeAllDisplayItems();
this._setDefaultList();
for (let itemId in this._allItems) {
this._addDisplayItem(itemId);
}
},
// Creates a display item based on the information associated with itemId
// and adds it to the list of displayed items, but does not yet display it.
_addDisplayItem : function(itemId) {
if (this._displayedItems.hasOwnProperty(itemId)) {
log('Tried adding a display item for ' + itemId + ', but an item with this item id is already among displayed items.');
return;
}
let itemInfo = this._allItems[itemId];
let displayItem = this._createDisplayItem(itemInfo);
displayItem.connect('activate',
Lang.bind(this,
function() {
// update the selection
this._selectIndex(this._list.get_children().indexOf(displayItem.actor));
this.activateSelected();
}));
displayItem.connect('show-details',
Lang.bind(this,
function() {
let index = this._list.get_children().indexOf(displayItem.actor);
/* Close the details pane if already open */
if (index == this._openDetailIndex) {
this._openDetailIndex = -1;
this.emit('show-details', -1);
} else {
this._openDetailIndex = index;
this.emit('show-details', index);
}
}));
this._displayedItems[itemId] = displayItem;
},
// Removes an item identifed by the itemId from the displayed items.
_removeDisplayItem: function(itemId) {
let children = this._list.get_children();
let count = children.length;
let displayItem = this._displayedItems[itemId];
let displayItemIndex = children.indexOf(displayItem.actor);
if (this.hasSelected() && count == 1) {
this.unsetSelected();
} else if (this.hasSelected() && displayItemIndex < this._selectedIndex) {
this.selectUp();
}
displayItem.destroy();
delete this._displayedItems[itemId];
},
// Removes all displayed items.
_removeAllDisplayItems: function() {
this.unsetSelected();
for (itemId in this._displayedItems)
this._removeDisplayItem(itemId);
},
// Return true if there's an active search or other constraint
// on the list
_filterActive: function() {
return this._search != '';
},
// Called when we are resetting state
_filterReset: function() {
this.unsetSelected();
},
_compareSearchMatch: function(a, b) {
let countA = this._matchedItems[a];
let countB = this._matchedItems[b];
if (countA > countB)
return -1;
else if (countA < countB)
return 1;
else
return this._compareItems(a, b);
},
_setMatches: function(matches) {
this._matchedItems = matches;
this._matchedItemKeys = [];
for (let itemId in this._matchedItems) {
this._matchedItemKeys.push(itemId);
}
this._matchedItemKeys.sort(Lang.bind(this, this._compareSearchMatch));
},
/**
* _redisplaySubSearch:
* A somewhat more optimized function called when we know
* that we're going to be displaying a subset of the items
* we already had, in the same order. In that case, we can
* just hide the actors that shouldn't be shown.
*/
_redisplaySubSearch: function() {
let matches = this._getSearchMatchedItems(true);
// Just hide all from the old set,
// we'll show the ones we want below
for (let itemId in this._displayedItems) {
let item = this._displayedItems[itemId];
item.actor.hide();
}
this._setMatches(matches);
for (let itemId in matches) {
let item = this._displayedItems[itemId];
item.actor.show();
}
this._list.queue_relayout();
},
_redisplayReordering: function() {
if (!this._filterActive()) {
this._setDefaultList();
} else {
this._setMatches(this._getSearchMatchedItems(false));
}
this._list.remove_all();
for (let i = 0; i < this._matchedItemKeys.length; i++) {
let itemId = this._matchedItemKeys[i];
let item = this._displayedItems[itemId];
item.actor.show();
this._list.add_actor(item.actor);
}
},
/*
* Updates the displayed items, applying the search string if one exists.
* @flags: Flags controlling redisplay behavior as follows:
* SUBSEARCH - Indicates that the current _search is a superstring of the previous
* one, which implies we only need to re-search through previous results.
* FULL - Indicates that we need recreate all displayed items.
* IMMEDIATE - Do the full redisplay even if we're not mapped. This is useful
* if you want to get the number of matched items and show/hide a section based on
* that number.
*/
_redisplay: function(flags) {
let immediate = (flags & RedisplayFlags.IMMEDIATE) != 0;
if (!immediate && !this.actor.mapped) {
this._pendingRedisplay |= flags;
return;
}
let isSubSearch = (flags & RedisplayFlags.SUBSEARCH) != 0;
let fullReload = (flags & RedisplayFlags.FULL) != 0;
let hadSelected = this.hasSelected();
this.unsetSelected();
if (!this._initialLoadComplete)
fullReload = true;
if (!this._refreshCache())
fullReload = true;
if (fullReload) {
this._recreateDisplayItems();
this._initialLoadComplete = true;
}
if (isSubSearch) {
this._redisplaySubSearch();
} else {
this._redisplayReordering();
}
if (hadSelected) {
this._selectedIndex = -1;
this.selectFirstItem();
}
this.emit('redisplayed');
},
//// Pure virtual protected methods ////
// Performs the steps needed to have the latest information about the items.
// Implementation should return %true if we are up to date, and %false
// if a full reload occurred.
_refreshCache: function() {
throw new Error('Not implemented');
},
// Sets the list of the displayed items based on the default sorting order.
// The default sorting order is specific to each implementing class.
_setDefaultList: function() {
throw new Error('Not implemented');
},
// Compares items associated with the item ids based on the order in which the
// items should be displayed.
// Intended to be used as a compareFunction for array.sort().
// Returns an integer value indicating the result of the comparison.
_compareItems: function(itemIdA, itemIdB) {
throw new Error('Not implemented');
},
// Checks if the item info can be a match for the search string.
// Returns a boolean flag indicating if that's the case.
_isInfoMatching: function(itemInfo, search) {
throw new Error('Not implemented');
},
// Creates a display item based on itemInfo.
_createDisplayItem: function(itemInfo) {
throw new Error('Not implemented');
},
//// Private methods ////
_getItemSearchScore: function(itemId, terms) {
let item = this._allItems[itemId];
let score = 0;
for (let i = 0; i < terms.length; i++) {
let term = terms[i];
if (this._isInfoMatching(item, term)) {
score++;
}
}
return score;
},
_getSearchMatchedItems: function(isSubSearch) {
// Break the search up into terms, and search for each
// individual term. Keep track of the number of terms
// each item matched.
let terms = this._search.split(/\s+/);
let matchScores = {};
if (isSubSearch) {
for (let i = 0; i < this._matchedItemKeys.length; i++) {
let itemId = this._matchedItemKeys[i];
let score = this._getItemSearchScore(itemId, terms);
if (score > 0)
matchScores[itemId] = score;
}
} else {
for (let itemId in this._displayedItems) {
let score = this._getItemSearchScore(itemId, terms);
if (score > 0)
matchScores[itemId] = score;
}
}
return matchScores;
},
// Returns a display item based on its index in the ordering of the
// display children.
_findDisplayedByIndex: function(index) {
let actor = this._list.get_children()[index];
return this._findDisplayedByActor(actor);
},
// Returns a display item based on the actor that represents it in
// the display.
_findDisplayedByActor: function(actor) {
for (itemId in this._displayedItems) {
let item = this._displayedItems[itemId];
if (item.actor == actor) {
return item;
}
}
return null;
},
// Selects (e.g. highlights) a display item at the provided index,
// updates this.selectedItemDetails actor, and emits 'selected' signal.
_selectIndex: function(index) {
// Cleanup from the previous item
if (this.hasSelected()) {
this._findDisplayedByIndex(this._selectedIndex).markSelected(false);
}
this._selectedIndex = index;
if (index < 0)
return;
// Mark the new item as selected and create its details pane
let item = this._findDisplayedByIndex(index);
item.markSelected(true);
this.emit('selected');
},
_getVisibleCount: function() {
return this._list.get_n_children();
},
_onMappedNotify: function () {
let mapped = this.actor.mapped;
if (mapped && this._pendingRedisplay > RedisplayFlags.NONE)
this._redisplay(this._pendingRedisplay);
this._pendingRedisplay = RedisplayFlags.NONE;
}
};
Signals.addSignalMethods(GenericDisplay.prototype);

View File

@ -17,8 +17,7 @@ function BaseIcon(label, createIcon) {
BaseIcon.prototype = {
_init : function(label, params) {
params = Params.parse(params, { createIcon: null,
setSizeManually: false,
showLabel: true });
setSizeManually: false });
this.actor = new St.Bin({ style_class: 'overview-icon',
x_fill: true,
y_fill: true });
@ -41,52 +40,42 @@ BaseIcon.prototype = {
box.add_actor(this._iconBin);
if (params.showLabel) {
this.label = new St.Label({ text: label });
box.add_actor(this.label);
} else {
this.label = null;
}
this._name = new St.Label({ text: label });
box.add_actor(this._name);
if (params.createIcon)
this.createIcon = params.createIcon;
this._setSizeManually = params.setSizeManually;
this.icon = null;
this.icon = this.createIcon(this.iconSize);
this._iconBin.set_child(this.icon);
},
_allocate: function(actor, box, flags) {
let availWidth = box.x2 - box.x1;
let availHeight = box.y2 - box.y1;
let iconSize = availHeight;
let [labelMinHeight, labelNatHeight] = this._name.get_preferred_height(-1);
let [iconMinHeight, iconNatHeight] = this._iconBin.get_preferred_height(-1);
let [iconMinWidth, iconNatWidth] = this._iconBin.get_preferred_width(-1);
let preferredHeight = iconNatHeight;
let preferredHeight = labelNatHeight + this._spacing + iconNatHeight;
let labelHeight = availHeight >= preferredHeight ? labelNatHeight
: labelMinHeight;
let iconSize = availHeight - this._spacing - labelHeight;
let iconPadding = (availWidth - iconSize) / 2;
let childBox = new Clutter.ActorBox();
if (this.label) {
let [labelMinHeight, labelNatHeight] = this.label.get_preferred_height(-1);
preferredHeight += this._spacing + labelNatHeight;
let labelHeight = availHeight >= preferredHeight ? labelNatHeight
: labelMinHeight;
iconSize -= this._spacing + labelHeight;
childBox.x1 = 0;
childBox.x2 = availWidth;
childBox.y1 = iconSize + this._spacing;
childBox.y2 = childBox.y1 + labelHeight;
this.label.allocate(childBox, flags);
}
childBox.x1 = Math.floor((availWidth - iconNatWidth) / 2);
childBox.y1 = Math.floor((iconSize - iconNatHeight) / 2);
childBox.x2 = childBox.x1 + iconNatWidth;
childBox.y2 = childBox.y1 + iconNatHeight;
childBox.x1 = iconPadding;
childBox.y1 = 0;
childBox.x2 = availWidth - iconPadding;
childBox.y2 = iconSize;
this._iconBin.allocate(childBox, flags);
childBox.x1 = 0;
childBox.x2 = availWidth;
childBox.y1 = iconSize + this._spacing;
childBox.y2 = childBox.y1 + labelHeight;
this._name.allocate(childBox, flags);
},
_getPreferredWidth: function(actor, forHeight, alloc) {
@ -95,14 +84,9 @@ BaseIcon.prototype = {
_getPreferredHeight: function(actor, forWidth, alloc) {
let [iconMinHeight, iconNatHeight] = this._iconBin.get_preferred_height(forWidth);
alloc.min_size = iconMinHeight;
alloc.natural_size = iconNatHeight;
if (this.label) {
let [labelMinHeight, labelNatHeight] = this.label.get_preferred_height(forWidth);
alloc.min_size += this._spacing + labelMinHeight;
alloc.natural_size += this._spacing + labelNatHeight;
}
let [labelMinHeight, labelNatHeight] = this._name.get_preferred_height(forWidth);
alloc.min_size = iconMinHeight + this._spacing + labelMinHeight;
alloc.natural_size = iconNatHeight + this._spacing + labelNatHeight;
},
// This can be overridden by a subclass, or by the createIcon
@ -115,23 +99,16 @@ BaseIcon.prototype = {
if (!this._setSizeManually)
throw new Error('setSizeManually has to be set to use setIconsize');
this._setIconSize(size);
},
_setIconSize: function(size) {
if (size == this.iconSize)
return;
this._createIconTexture(size);
},
_createIconTexture: function(size) {
if (this.icon)
this.icon.destroy();
this.icon.destroy();
this.iconSize = size;
this.icon = this.createIcon(this.iconSize);
// The icon returned by createIcon() might actually be smaller than
// the requested icon size (for instance StTextureCache does this
// for fallback icons), so set the size explicitly.
this.icon.set_size(this.iconSize, this.iconSize);
this._iconBin.child = this.icon;
},
@ -139,15 +116,12 @@ BaseIcon.prototype = {
let node = this.actor.get_theme_node();
this._spacing = node.get_length('spacing');
let size;
if (this._setSizeManually) {
size = this.iconSize;
} else {
let [found, len] = node.lookup_length('icon-size', false);
size = found ? len : ICON_SIZE;
}
if (this._setSizeManually)
return;
this._createIconTexture(size);
let len = node.get_length('icon-size');
if (len > 0)
this._setIconSize(len);
}
};
@ -191,16 +165,8 @@ IconGrid.prototype = {
alloc.natural_size = nColumns * this._item_size + totalSpacing;
},
_getVisibleChildren: function() {
let children = this._grid.get_children();
children = children.filter(function(actor) {
return actor.visible;
});
return children;
},
_getPreferredHeight: function (grid, forWidth, alloc) {
let children = this._getVisibleChildren();
let children = this._grid.get_children();
let [nColumns, usedWidth] = this._computeLayout(forWidth);
let nRows;
if (nColumns > 0)
@ -216,7 +182,7 @@ IconGrid.prototype = {
},
_allocate: function (grid, box, flags) {
let children = this._getVisibleChildren();
let children = this._grid.get_children();
let availWidth = box.x2 - box.x1;
let availHeight = box.y2 - box.y1;
@ -281,11 +247,8 @@ IconGrid.prototype = {
}
},
childrenInRow: function(rowWidth) {
return this._computeLayout(rowWidth)[0];
},
_computeLayout: function (forWidth) {
let children = this._grid.get_children();
let nColumns = 0;
let usedWidth = 0;
while ((this._colLimit == null || nColumns < this._colLimit) &&

View File

@ -1,19 +1,18 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter;
const Cogl = imports.gi.Cogl;
const GConf = imports.gi.GConf;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Pango = imports.gi.Pango;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const History = imports.misc.history;
const ExtensionSystem = imports.ui.extensionSystem;
const Link = imports.ui.link;
const Tweener = imports.ui.tweener;
@ -26,7 +25,6 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
'const Mainloop = imports.mainloop; ' +
'const Meta = imports.gi.Meta; ' +
'const Shell = imports.gi.Shell; ' +
'const Tp = imports.gi.TelepathyGLib; ' +
'const Main = imports.ui.main; ' +
'const Lang = imports.lang; ' +
'const Tweener = imports.ui.tweener; ' +
@ -38,8 +36,6 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
'const it = Main.lookingGlass.getIt(); ' +
'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ';
const HISTORY_KEY = 'looking-glass-history';
function Notebook() {
this._init();
}
@ -99,19 +95,12 @@ Notebook.prototype = {
selectIndex: function(index) {
if (index == this._selectedIndex)
return;
this._unselect();
if (index < 0) {
this._unselect();
this.emit('selection', null);
return;
}
// Focus the new tab before unmapping the old one
let tabData = this._tabs[index];
if (!tabData.scrollView.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false))
this.actor.grab_key_focus();
this._unselect();
tabData.labelBox.add_style_pseudo_class('selected');
tabData.scrollView.show();
this._selectedIndex = index;
@ -370,30 +359,6 @@ ObjInspector.prototype = {
}
};
function addBorderPaintHook(actor) {
let signalId = actor.connect_after('paint',
function () {
let color = new Cogl.Color();
color.init_from_4ub(0xff, 0, 0, 0xc4);
Cogl.set_source_color(color);
let geom = actor.get_allocation_geometry();
let width = 2;
// clockwise order
Cogl.rectangle(0, 0, geom.width, width);
Cogl.rectangle(geom.width - width, width,
geom.width, geom.height);
Cogl.rectangle(0, geom.height,
geom.width - width, geom.height - width);
Cogl.rectangle(0, geom.height - width,
width, width);
});
actor.queue_redraw();
return signalId;
}
function Inspector() {
this._init();
}
@ -433,9 +398,6 @@ Inspector.prototype = {
},
_allocate: function(actor, box, flags) {
if (!this._eventHandler)
return;
let primary = global.get_primary_monitor();
let [minWidth, minHeight, natWidth, natHeight] =
@ -453,7 +415,6 @@ Inspector.prototype = {
Clutter.ungrab_pointer(this._eventHandler);
Clutter.ungrab_keyboard(this._eventHandler);
this._eventHandler.destroy();
this._eventHandler = null;
this.emit('closed');
},
@ -529,13 +490,10 @@ Inspector.prototype = {
let position = '[inspect x: ' + stageX + ' y: ' + stageY + ']';
this._displayText.text = '';
this._displayText.text = position + ' ' + this._target;
if (this._borderPaintTarget != this._target) {
if (this._borderPaintTarget != null)
this._borderPaintTarget.disconnect(this._borderPaintId);
this._borderPaintTarget = this._target;
this._borderPaintId = addBorderPaintHook(this._target);
}
if (this._borderPaintTarget != null)
this._borderPaintTarget.disconnect(this._borderPaintId);
this._borderPaintTarget = this._target;
this._borderPaintId = Shell.add_hook_paint_red_border(this._target);
}
};
@ -582,53 +540,6 @@ ErrorLog.prototype = {
}
};
function Memory() {
this._init();
}
Memory.prototype = {
_init: function() {
this.actor = new St.BoxLayout({ vertical: true });
this._glibc_uordblks = new St.Label();
this.actor.add(this._glibc_uordblks);
this._js_bytes = new St.Label();
this.actor.add(this._js_bytes);
this._gjs_boxed = new St.Label();
this.actor.add(this._gjs_boxed);
this._gjs_gobject = new St.Label();
this.actor.add(this._gjs_gobject);
this._gjs_function = new St.Label();
this.actor.add(this._gjs_function);
this._gjs_closure = new St.Label();
this.actor.add(this._gjs_closure);
this._gcbutton = new St.Button({ label: 'Full GC',
style_class: 'lg-obj-inspector-button' });
this._gcbutton.connect('clicked', Lang.bind(this, function () { global.gc(); this._renderText(); }));
this.actor.add(this._gcbutton, { x_align: St.Align.START,
x_fill: false });
this.actor.connect('notify::mapped', Lang.bind(this, this._renderText));
},
_renderText: function() {
if (!this.actor.mapped)
return;
let memInfo = global.get_memory_info();
this._glibc_uordblks.text = 'glibc_uordblks: ' + memInfo.glibc_uordblks;
this._js_bytes.text = 'js bytes: ' + memInfo.js_bytes;
this._gjs_boxed.text = 'gjs_boxed: ' + memInfo.gjs_boxed;
this._gjs_gobject.text = 'gjs_gobject: ' + memInfo.gjs_gobject;
this._gjs_function.text = 'gjs_function: ' + memInfo.gjs_function;
this._gjs_closure.text = 'gjs_closure: ' + memInfo.gjs_closure;
}
};
function Extensions() {
this._init();
}
@ -728,10 +639,18 @@ function LookingGlass() {
LookingGlass.prototype = {
_init : function() {
this._idleHistorySaveId = 0;
let historyPath = global.userdatadir + '/lookingglass-history.txt';
this._historyFile = Gio.file_new_for_path(historyPath);
this._savedText = null;
this._historyNavIndex = -1;
this._history = [];
this._borderPaintTarget = null;
this._borderPaintId = 0;
this._borderDestroyId = 0;
this._readHistory();
this._open = false;
this._offset = 0;
@ -744,11 +663,11 @@ LookingGlass.prototype = {
style_class: 'lg-dialog',
vertical: true,
visible: false });
this.actor.connect('key-press-event', Lang.bind(this, this._globalKeyPressEvent));
this._interfaceSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
this._interfaceSettings.connect('changed::monospace-font-name',
Lang.bind(this, this._updateFont));
let gconf = GConf.Client.get_default();
gconf.add_dir('/desktop/gnome/interface', GConf.ClientPreloadType.PRELOAD_NONE);
gconf.notify_add('/desktop/gnome/interface/monospace_font_name',
Lang.bind(this, this._updateFont));
this._updateFont();
Main.uiGroup.add_actor(this.actor);
@ -760,7 +679,6 @@ LookingGlass.prototype = {
let toolbar = new St.BoxLayout({ name: 'Toolbar' });
this.actor.add_actor(toolbar);
let inspectIcon = new St.Icon({ icon_name: 'gtk-color-picker',
icon_type: St.IconType.FULLCOLOR,
icon_size: 24 });
toolbar.add_actor(inspectIcon);
inspectIcon.reactive = true;
@ -798,7 +716,12 @@ LookingGlass.prototype = {
let label = new St.Label({ text: 'js>>> ' });
entryArea.add(label);
this._entry = new St.Entry({ can_focus: true });
this._entry = new St.Entry();
/* unmapping the edit box will un-focus it, undo that */
notebook.connect('selection', Lang.bind(this, function (nb, child) {
if (child == this._evalBox)
global.stage.set_key_focus(this._entry);
}));
entryArea.add(this._entry, { expand: true });
this._windowList = new WindowList();
@ -811,9 +734,6 @@ LookingGlass.prototype = {
this._errorLog = new ErrorLog();
notebook.appendPage('Errors', this._errorLog.actor);
this._memory = new Memory();
notebook.appendPage('Memory', this._memory.actor);
this._extensions = new Extensions();
notebook.appendPage('Extensions', this._extensions.actor);
@ -827,15 +747,39 @@ LookingGlass.prototype = {
if (text == '')
return true;
this._evaluate(text);
this._historyNavIndex = -1;
return true;
}));
this._history = new History.HistoryManager({ gsettingsKey: HISTORY_KEY,
entry: this._entry.clutter_text });
this._entry.clutter_text.connect('key-press-event', Lang.bind(this, function(o, e) {
let symbol = e.get_key_symbol();
if (symbol == Clutter.Up) {
if (this._historyNavIndex >= this._history.length - 1)
return true;
this._historyNavIndex++;
if (this._historyNavIndex == 0)
this._savedText = this._entry.text;
this._entry.text = this._history[this._history.length - this._historyNavIndex - 1];
return true;
} else if (symbol == Clutter.Down) {
if (this._historyNavIndex <= 0)
return true;
this._historyNavIndex--;
if (this._historyNavIndex < 0)
this._entry.text = this._savedText;
else
this._entry.text = this._history[this._history.length - this._historyNavIndex - 1];
return true;
} else {
this._historyNavIndex = -1;
this._savedText = null;
return false;
}
}));
},
_updateFont: function() {
let fontName = this._interfaceSettings.get_string('monospace-font-name');
let gconf = GConf.Client.get_default();
let fontName = gconf.get_string('/desktop/gnome/interface/monospace_font_name');
// This is mishandled by the scanner - should by Pango.FontDescription_from_string(fontName);
// https://bugzilla.gnome.org/show_bug.cgi?id=595889
let fontDesc = Pango.font_description_from_string(fontName);
@ -846,6 +790,29 @@ LookingGlass.prototype = {
+ 'font-family: "' + fontDesc.get_family() + '";';
},
_readHistory: function () {
if (!this._historyFile.query_exists(null))
return;
let [result, contents, length, etag] = this._historyFile.load_contents(null);
this._history = contents.split('\n').filter(function (e) { return e != ''; });
},
_queueHistorySave: function() {
if (this._idleHistorySaveId > 0)
return;
this._idleHistorySaveId = Mainloop.timeout_add_seconds(5, Lang.bind(this, this._doSaveHistory));
},
_doSaveHistory: function () {
this._idleHistorySaveId = false;
let output = this._historyFile.replace(null, true, Gio.FileCreateFlags.NONE, null);
let dataOut = new Gio.DataOutputStream({ base_stream: output });
dataOut.put_string(this._history.join('\n'), null);
dataOut.put_string('\n', null);
dataOut.close(null);
return false;
},
_pushResult: function(command, obj) {
let index = this._results.length + this._offset;
let result = new Result('>>> ' + command, obj, index);
@ -857,7 +824,7 @@ LookingGlass.prototype = {
}
if (obj instanceof Clutter.Actor) {
this._borderPaintTarget = obj;
this._borderPaintId = addBorderPaintHook(obj);
this._borderPaintId = Shell.add_hook_paint_red_border(obj);
this._borderDestroyId = obj.connect('destroy', Lang.bind(this, function () {
this._borderDestroyId = 0;
this._borderPaintTarget = null;
@ -876,7 +843,8 @@ LookingGlass.prototype = {
},
_evaluate : function(command) {
this._history.addItem(command);
this._history.push(command);
this._queueHistorySave();
let fullCmd = commandHeader + command;
@ -957,17 +925,20 @@ LookingGlass.prototype = {
if (this._open)
return;
if (!Main.pushModal(this._entry))
if (!Main.pushModal(this.actor))
return;
this._notebook.selectIndex(0);
this._keyPressEventId = global.stage.connect('key-press-event',
Lang.bind(this, this._globalKeyPressEvent));
this.actor.show();
this.actor.lower(Main.chrome.actor);
this._open = true;
this._history.lastItem();
Tweener.removeTweens(this.actor);
global.stage.set_key_focus(this._entry);
// We inverse compensate for the slow-down so you can change the factor
// through LookingGlass without long waits.
Tweener.addTween(this.actor, { time: 0.5 / St.get_slow_down_factor(),
@ -980,8 +951,12 @@ LookingGlass.prototype = {
if (!this._open)
return;
if (this._keyPressEventId)
global.stage.disconnect(this._keyPressEventId);
this._objInspector.actor.hide();
this._historyNavIndex = -1;
this._open = false;
Tweener.removeTweens(this.actor);
@ -991,7 +966,7 @@ LookingGlass.prototype = {
this._borderPaintTarget = null;
}
Main.popModal(this._entry);
Main.popModal(this.actor);
Tweener.addTween(this.actor, { time: 0.5 / St.get_slow_down_factor(),
transition: 'easeOutQuad',

File diff suppressed because it is too large Load Diff

View File

@ -112,11 +112,6 @@ ShellMagnifier.prototype = {
* [left, top, right, bottom].
* @viewPort Array of integers, [left, top, right, bottom] that defines
* the position of the ZoomRegion on screen.
*
* FIXME: The arguments here are redundant, since the width and height of
* the ROI are determined by the viewport and magnification factors.
* We ignore the passed in width and height.
*
* @return The newly created ZoomRegion.
*/
createZoomRegion: function(xMagFactor, yMagFactor, roi, viewPort) {

View File

@ -1,5 +1,11 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
imports.gi.versions.Clutter = '1.0';
imports.gi.versions.Gio = '2.0';
imports.gi.versions.Gdk = '3.0';
imports.gi.versions.GdkPixbuf = '2.0';
imports.gi.versions.Gtk = '3.0';
const Clutter = imports.gi.Clutter;
const DBus = imports.dbus;
const Gdk = imports.gi.Gdk;
@ -13,9 +19,6 @@ const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Chrome = imports.ui.chrome;
const CtrlAltTab = imports.ui.ctrlAltTab;
const EndSessionDialog = imports.ui.endSessionDialog;
const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent;
const Environment = imports.ui.environment;
const ExtensionSystem = imports.ui.extensionSystem;
const MessageTray = imports.ui.messageTray;
@ -31,16 +34,13 @@ const ShellDBus = imports.ui.shellDBus;
const TelepathyClient = imports.ui.telepathyClient;
const WindowManager = imports.ui.windowManager;
const Magnifier = imports.ui.magnifier;
const XdndHandler = imports.ui.xdndHandler;
const StatusIconDispatcher = imports.ui.statusIconDispatcher;
const Util = imports.misc.util;
const DEFAULT_BACKGROUND_COLOR = new Clutter.Color();
DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff);
let chrome = null;
let panel = null;
let hotCorners = [];
let placesManager = null;
let overview = null;
let runDialog = null;
@ -50,34 +50,33 @@ let messageTray = null;
let notificationDaemon = null;
let windowAttentionHandler = null;
let telepathyClient = null;
let ctrlAltTabManager = null;
let recorder = null;
let shellDBusService = null;
let modalCount = 0;
let modalActorFocusStack = [];
let uiGroup = null;
let magnifier = null;
let xdndHandler = null;
let statusIconDispatcher = null;
let _errorLogStack = [];
let _startDate;
let _defaultCssStylesheet = null;
let _cssStylesheet = null;
let background = null;
function start() {
// Monkey patch utility functions into the global proxy;
// Add a binding for 'global' in the global JS namespace; (gjs
// keeps the web browser convention of having that namespace be
// called 'window'.)
window.global = Shell.Global.get();
// Now monkey patch utility functions into the global proxy;
// This is easier and faster than indirecting down into global
// if we want to call back up into JS.
global.logError = _logError;
global.log = _logDebug;
// Chain up async errors reported from C
global.connect('notify-error', function (global, msg, detail) { notifyError(msg, detail); });
Gio.DesktopAppInfo.set_desktop_env('GNOME');
global.grab_dbus_service();
shellDBusService = new ShellDBus.GnomeShell();
// Force a connection now; dbus.js will do this internally
// if we use its name acquisition stuff but we aren't right
@ -85,9 +84,7 @@ function start() {
// back into sync ones.
DBus.session.flush();
// Load the calendar server. Note that we are careful about
// not loading any events until the user presses the clock
global.launch_calendar_server();
Environment.init();
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
// also initialize ShellAppSystem first. ShellAppSystem
@ -106,8 +103,10 @@ function start() {
global.stage.color = DEFAULT_BACKGROUND_COLOR;
global.stage.no_clear_hint = true;
_defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
loadTheme();
let themeContext = St.ThemeContext.get_for_stage (global.stage);
let stylesheetPath = global.datadir + '/theme/gnome-shell.css';
let theme = new St.Theme ({ application_stylesheet: stylesheetPath });
themeContext.set_theme (theme);
let shellwm = global.window_manager;
shellwm.takeover_keybinding('panel_main_menu');
@ -121,14 +120,11 @@ function start() {
// Set up stage hierarchy to group all UI actors under one container.
uiGroup = new Clutter.Group();
St.set_ui_root(global.stage, uiGroup);
global.window_group.reparent(uiGroup);
global.overlay_group.reparent(uiGroup);
global.stage.add_actor(uiGroup);
placesManager = new PlaceDisplay.PlacesManager();
xdndHandler = new XdndHandler.XdndHandler();
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
overview = new Overview.Overview();
chrome = new Chrome.Chrome();
magnifier = new Magnifier.Magnifier();
@ -140,9 +136,6 @@ function start() {
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
telepathyClient = new TelepathyClient.Client();
overview.init();
statusIconDispatcher.start(messageTray.actor);
_startDate = new Date();
let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
@ -169,24 +162,11 @@ function start() {
}
});
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, false, -1, 1);
// Provide the bus object for gnome-session to
// initiate logouts.
EndSessionDialog.init();
// Attempt to become a PolicyKit authentication agent
PolkitAuthenticationAgent.init()
global.screen.connect('monitors-changed', _relayout);
global.gdk_screen.connect('monitors-changed', _relayout);
ExtensionSystem.init();
ExtensionSystem.loadExtensions();
// Perform initial relayout here
_relayout();
panel.startStatusArea();
panel.startupAnimation();
let display = global.screen.get_display();
@ -194,250 +174,20 @@ function start() {
global.stage.connect('captured-event', _globalKeyPressHandler);
// Perform initial relayout here
_relayout();
_log('info', 'loaded at ' + _startDate);
log('GNOME Shell started at ' + _startDate);
Mainloop.idle_add(_removeUnusedWorkspaces);
let perfModuleName = GLib.getenv("SHELL_PERF_MODULE");
if (perfModuleName) {
let perfOutput = GLib.getenv("SHELL_PERF_OUTPUT");
let module = eval('imports.perf.' + perfModuleName + ';');
Scripting.runPerfScript(module, perfOutput);
}
global.screen.connect('notify::n-workspaces', _nWorkspacesChanged);
global.screen.connect('window-entered-monitor', _windowEnteredMonitor);
global.screen.connect('window-left-monitor', _windowLeftMonitor);
global.screen.connect('restacked', _windowsRestacked);
_nWorkspacesChanged();
}
let _workspaces = [];
let _checkWorkspacesId = 0;
/*
* When the last window closed on a workspace is a dialog or splash
* screen, we assume that it might be an initial window shown before
* the main window of an application, and give the app a grace period
* where it can map another window before we remove the workspace.
*/
const LAST_WINDOW_GRACE_TIME = 1000;
function _checkWorkspaces() {
let i;
let emptyWorkspaces = [];
for (i = 0; i < _workspaces.length; i++) {
let lastRemoved = _workspaces[i]._lastRemovedWindow;
if (lastRemoved &&
(lastRemoved.get_window_type() == Meta.WindowType.SPLASHSCREEN ||
lastRemoved.get_window_type() == Meta.WindowType.DIALOG ||
lastRemoved.get_window_type() == Meta.WindowType.MODAL_DIALOG))
emptyWorkspaces[i] = false;
else
emptyWorkspaces[i] = true;
}
let windows = global.get_window_actors();
for (i = 0; i < windows.length; i++) {
let win = windows[i];
if (win.get_meta_window().is_on_all_workspaces())
continue;
let workspaceIndex = win.get_workspace();
emptyWorkspaces[workspaceIndex] = false;
}
// If we don't have an empty workspace at the end, add one
if (!emptyWorkspaces[emptyWorkspaces.length -1]) {
global.screen.append_new_workspace(false, global.get_current_time());
emptyWorkspaces.push(false);
}
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
let removingCurrentWorkspace = (emptyWorkspaces[activeWorkspaceIndex] &&
activeWorkspaceIndex < emptyWorkspaces.length - 1);
// Don't enter the overview when removing multiple empty workspaces at startup
let showOverview = (removingCurrentWorkspace &&
!emptyWorkspaces.every(function(x) { return x; }));
if (removingCurrentWorkspace) {
// "Merge" the empty workspace we are removing with the one at the end
wm.blockAnimations();
}
// Delete other empty workspaces; do it from the end to avoid index changes
for (i = emptyWorkspaces.length - 2; i >= 0; i--) {
if (emptyWorkspaces[i])
global.screen.remove_workspace(_workspaces[i], global.get_current_time());
}
if (removingCurrentWorkspace) {
global.screen.get_workspace_by_index(global.screen.n_workspaces - 1).activate(global.get_current_time());
wm.unblockAnimations();
if (!overview.visible && showOverview)
overview.show();
}
_checkWorkspacesId = 0;
return false;
}
function _windowRemoved(workspace, window) {
workspace._lastRemovedWindow = window;
_queueCheckWorkspaces();
Mainloop.timeout_add(LAST_WINDOW_GRACE_TIME, function() {
if (workspace._lastRemovedWindow == window) {
workspace._lastRemovedWindow = null;
_queueCheckWorkspaces();
}
});
}
function _windowLeftMonitor(metaScreen, monitorIndex, metaWin) {
// If the window left the primary monitor, that
// might make that workspace empty
if (monitorIndex == global.get_primary_monitor_index())
_queueCheckWorkspaces();
}
function _windowEnteredMonitor(metaScreen, monitorIndex, metaWin) {
// If the window entered the primary monitor, that
// might make that workspace non-empty
if (monitorIndex == global.get_primary_monitor_index())
_queueCheckWorkspaces();
}
function _windowsRestacked() {
// Figure out where the pointer is in case we lost track of
// it during a grab. (In particular, if a trayicon popup menu
// is dismissed, see if we need to close the message tray.)
global.sync_pointer();
}
function _queueCheckWorkspaces() {
if (_checkWorkspacesId == 0)
_checkWorkspacesId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, _checkWorkspaces);
}
function _nWorkspacesChanged() {
let oldNumWorkspaces = _workspaces.length;
let newNumWorkspaces = global.screen.n_workspaces;
if (oldNumWorkspaces == newNumWorkspaces)
return false;
let lostWorkspaces = [];
if (newNumWorkspaces > oldNumWorkspaces) {
let w;
// Assume workspaces are only added at the end
for (w = oldNumWorkspaces; w < newNumWorkspaces; w++)
_workspaces[w] = global.screen.get_workspace_by_index(w);
for (w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
let workspace = _workspaces[w];
workspace._windowAddedId = workspace.connect('window-added', _queueCheckWorkspaces);
workspace._windowRemovedId = workspace.connect('window-removed', _windowRemoved);
}
} else {
// Assume workspaces are only removed sequentially
// (e.g. 2,3,4 - not 2,4,7)
let removedIndex;
let removedNum = oldNumWorkspaces - newNumWorkspaces;
for (let w = 0; w < oldNumWorkspaces; w++) {
let workspace = global.screen.get_workspace_by_index(w);
if (_workspaces[w] != workspace) {
removedIndex = w;
break;
}
}
let lostWorkspaces = _workspaces.splice(removedIndex, removedNum);
lostWorkspaces.forEach(function(workspace) {
workspace.disconnect(workspace._windowAddedId);
workspace.disconnect(workspace._windowRemovedId);
});
}
_queueCheckWorkspaces();
return false;
}
/**
* getThemeStylesheet:
*
* Get the theme CSS file that the shell will load
*
* Returns: A file path that contains the theme CSS,
* null if using the default
*/
function getThemeStylesheet()
{
return _cssStylesheet;
}
/**
* setThemeStylesheet:
* @cssStylesheet: A file path that contains the theme CSS,
* set it to null to use the default
*
* Set the theme CSS file that the shell will load
*/
function setThemeStylesheet(cssStylesheet)
{
_cssStylesheet = cssStylesheet;
}
/**
* loadTheme:
*
* Reloads the theme CSS file
*/
function loadTheme() {
let themeContext = St.ThemeContext.get_for_stage (global.stage);
let previousTheme = themeContext.get_theme();
let cssStylesheet = _defaultCssStylesheet;
if (_cssStylesheet != null)
cssStylesheet = _cssStylesheet;
let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
if (previousTheme) {
let customStylesheets = previousTheme.get_custom_stylesheets();
for (let i = 0; i < customStylesheets.length; i++)
theme.load_stylesheet(customStylesheets[i]);
}
themeContext.set_theme (theme);
}
/**
* notifyError:
* @msg: An error message
* @details: Additional information
*
* See shell_global_notify_problem().
*/
function notifyError(msg, details) {
// Also print to stderr so it's logged somewhere
if (details)
log("error: " + msg + ": " + details);
else
log("error: " + msg)
let source = new MessageTray.SystemNotificationSource();
messageTray.add(source);
let notification = new MessageTray.Notification(source, msg, details);
notification.setTransient(true);
source.notify(notification);
}
/**
@ -482,90 +232,45 @@ function _getAndClearErrorStack() {
}
function _relayout() {
let monitors = global.get_monitors();
// destroy old corners
for (let i = 0; i < hotCorners.length; i++)
hotCorners[i].destroy();
hotCorners = [];
let primary = global.get_primary_monitor();
for (let i = 0; i < monitors.length; i++) {
let monitor = monitors[i];
let isPrimary = (monitor.x == primary.x &&
monitor.y == primary.y &&
monitor.width == primary.width &&
monitor.height == primary.height);
let cornerX = monitor.x;
let cornerY = monitor.y;
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
cornerX += monitor.width;
let haveTopLeftCorner = true;
/* Check if we have a top left (right for RTL) corner.
* I.e. if there is no monitor directly above or to the left(right) */
let besideX;
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
besideX = monitor.x + 1;
else
besideX = cornerX - 1;
let besideY = cornerY;
let aboveX = cornerX;
let aboveY = cornerY - 1;
for (let j = 0; j < monitors.length; j++) {
if (i == j)
continue;
let otherMonitor = monitors[j];
if (besideX >= otherMonitor.x &&
besideX < otherMonitor.x + otherMonitor.width &&
besideY >= otherMonitor.y &&
besideY < otherMonitor.y + otherMonitor.height) {
haveTopLeftCorner = false;
break;
}
if (aboveX >= otherMonitor.x &&
aboveX < otherMonitor.x + otherMonitor.width &&
aboveY >= otherMonitor.y &&
aboveY < otherMonitor.y + otherMonitor.height) {
haveTopLeftCorner = false;
break;
}
}
/* We only want hot corners where there is a natural top-left
* corner, and on the primary monitor */
if (!isPrimary && !haveTopLeftCorner)
continue;
let corner = new Panel.HotCorner(isPrimary ? panel.button : null);
hotCorners.push(corner);
corner.actor.set_position(cornerX, cornerY);
if (isPrimary)
panel.setHotCorner(corner);
}
panel.relayout();
panel.actor.set_position(primary.x, primary.y);
panel.actor.set_size(primary.width, Panel.PANEL_HEIGHT);
overview.relayout();
// To avoid updating the position and size of the workspaces
// in the overview, we just hide the overview. The positions
// will be updated when it is next shown.
// will be updated when it is next shown. We do the same for
// the calendar popdown.
overview.hide();
panel.hideCalendar();
}
function isWindowActorDisplayedOnWorkspace(win, workspaceIndex) {
return win.get_workspace() == workspaceIndex ||
(win.get_meta_window() && win.get_meta_window().is_on_all_workspaces());
}
// metacity-clutter currently uses the same prefs as plain metacity,
// which probably means we'll be starting out with multiple workspaces;
// remove any unused ones. (We do this from an idle handler, because
// global.get_window_actors() still returns NULL at the point when start()
// is called.)
function _removeUnusedWorkspaces() {
function getWindowActorsForWorkspace(workspaceIndex) {
return global.get_window_actors().filter(function (win) {
return isWindowActorDisplayedOnWorkspace(win, workspaceIndex);
});
let windows = global.get_window_actors();
let maxWorkspace = 0;
for (let i = 0; i < windows.length; i++) {
let win = windows[i];
if (!win.get_meta_window().is_on_all_workspaces() &&
win.get_workspace() > maxWorkspace) {
maxWorkspace = win.get_workspace();
}
}
let screen = global.screen;
if (screen.n_workspaces > maxWorkspace) {
for (let w = screen.n_workspaces - 1; w > maxWorkspace; w--) {
let workspace = screen.get_workspace_by_index(w);
screen.remove_workspace(workspace, 0);
}
}
return false;
}
// This function encapsulates hacks to make certain global keybindings
@ -573,67 +278,57 @@ function getWindowActorsForWorkspace(workspaceIndex) {
// are disabled with a global grab. (When there is a global grab, then
// all key events will be delivered to the stage, so ::captured-event
// on the stage can be used for global keybindings.)
//
// We expect to need to conditionally enable just a few keybindings
// depending on circumstance; the main hackiness here is that we are
// assuming that keybindings have their default values; really we
// should be asking Mutter to resolve the key into an action and then
// base our handling based on the action.
function _globalKeyPressHandler(actor, event) {
if (modalCount == 0)
return false;
if (event.type() != Clutter.EventType.KEY_PRESS)
if (event.type() != Clutter.EventType.KEY_RELEASE)
return false;
let symbol = event.get_key_symbol();
let keyCode = event.get_key_code();
let modifierState = Shell.get_event_state(event);
// Check the overview key first, this isn't a Meta.KeyBindingAction yet
if (symbol == Clutter.Super_L || symbol == Clutter.Super_R) {
// The super key is the default for triggering the overview, and should
// get us out of the overview when we are already in it.
if (overview.visible)
overview.hide();
return true;
}
// Whitelist some of the Metacity actions
let display = global.screen.get_display();
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
// This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType
let action = display.get_keybinding_action(keyCode, modifierState);
// The screenshot action should always be available (even if a
// modal dialog is present)
if (action == Meta.KeyBindingAction.COMMAND_SCREENSHOT) {
let gconf = GConf.Client.get_default();
let command = gconf.get_string('/apps/metacity/keybinding_commands/command_screenshot');
if (command != null && command != '')
Util.spawnCommandLine(command);
return true;
}
// Other bindings are only available when the overview is up and
// no modal dialog is present.
if (!overview.visible || modalCount > 1)
return false;
// This isn't a Meta.KeyBindingAction yet
if (symbol == Clutter.Super_L || symbol == Clutter.Super_R) {
overview.hide();
return true;
}
switch (action) {
// left/right would effectively act as synonyms for up/down if we enabled them;
// but that could be considered confusing; we also disable them in the main view.
//
// case Meta.KeyBindingAction.WORKSPACE_LEFT:
// wm.actionMoveWorkspaceLeft();
// return true;
// case Meta.KeyBindingAction.WORKSPACE_RIGHT:
// wm.actionMoveWorkspaceRight();
// return true;
case Meta.KeyBindingAction.WORKSPACE_UP:
wm.actionMoveWorkspaceUp();
case Meta.KeyBindingAction.COMMAND_SCREENSHOT:
let gconf = GConf.Client.get_default();
let command = gconf.get_string('/apps/metacity/keybinding_commands/command_screenshot');
if (command != null && command != '') {
let [ok, len, args] = GLib.shell_parse_argv(command);
let p = new Shell.Process({'args' : args});
p.run();
}
return true;
case Meta.KeyBindingAction.WORKSPACE_DOWN:
wm.actionMoveWorkspaceDown();
case Meta.KeyBindingAction.WORKSPACE_LEFT:
wm.actionMoveWorkspaceLeft();
return true;
case Meta.KeyBindingAction.WORKSPACE_RIGHT:
wm.actionMoveWorkspaceRight();
return true;
case Meta.KeyBindingAction.PANEL_RUN_DIALOG:
case Meta.KeyBindingAction.COMMAND_2:
getRunDialog().open();
return true;
case Meta.KeyBindingAction.PANEL_MAIN_MENU:
overview.hide();
return true;
case Meta.KeyBindingAction.SWITCH_PANELS:
ctrlAltTabManager.popup(modifierState & Clutter.ModifierType.SHIFT_MASK);
return true;
}
return false;
@ -641,8 +336,10 @@ function _globalKeyPressHandler(actor, event) {
function _findModal(actor) {
for (let i = 0; i < modalActorFocusStack.length; i++) {
if (modalActorFocusStack[i].actor == actor)
let [stackActor, stackFocus] = modalActorFocusStack[i];
if (stackActor == actor) {
return i;
}
}
return -1;
}
@ -650,29 +347,21 @@ function _findModal(actor) {
/**
* pushModal:
* @actor: #ClutterActor which will be given keyboard focus
* @timestamp: optional timestamp
*
* Ensure we are in a mode where all keyboard and mouse input goes to
* the stage, and focus @actor. Multiple calls to this function act in
* a stacking fashion; the effect will be undone when an equal number
* of popModal() invocations have been made.
* the stage. Multiple calls to this function act in a stacking fashion;
* the effect will be undone when an equal number of popModal() invocations
* have been made.
*
* Next, record the current Clutter keyboard focus on a stack. If the
* modal stack returns to this actor, reset the focus to the actor
* which was focused at the time pushModal() was invoked.
*
* @timestamp is optionally used to associate the call with a specific user
* initiated event. If not provided then the value of
* global.get_current_time() is assumed.
* Next, record the current Clutter keyboard focus on a stack. If the modal stack
* returns to this actor, reset the focus to the actor which was focused
* at the time pushModal() was invoked.
*
* Returns: true iff we successfully acquired a grab or already had one
*/
function pushModal(actor, timestamp) {
if (timestamp == undefined)
timestamp = global.get_current_time();
function pushModal(actor) {
if (modalCount == 0) {
if (!global.begin_modal(timestamp)) {
if (!global.begin_modal(global.get_current_time())) {
log('pushModal: invocation of begin_modal failed');
return false;
}
@ -681,80 +370,52 @@ function pushModal(actor, timestamp) {
global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
modalCount += 1;
let actorDestroyId = actor.connect('destroy', function() {
actor.connect('destroy', function() {
let index = _findModal(actor);
if (index >= 0)
modalActorFocusStack.splice(index, 1);
});
let curFocus = global.stage.get_key_focus();
let curFocusDestroyId;
if (curFocus != null) {
curFocusDestroyId = curFocus.connect('destroy', function() {
curFocus.connect('destroy', function() {
let index = _findModal(actor);
if (index >= 0)
modalActorFocusStack[index].actor = null;
modalActorFocusStack[index][1] = null;
});
}
modalActorFocusStack.push({ actor: actor,
focus: curFocus,
destroyId: actorDestroyId,
focusDestroyId: curFocusDestroyId });
modalActorFocusStack.push([actor, curFocus]);
global.stage.set_key_focus(actor);
global.stage.set_key_focus(null);
return true;
}
/**
* popModal:
* @actor: #ClutterActor passed to original invocation of pushModal().
* @timestamp: optional timestamp
*
* Reverse the effect of pushModal(). If this invocation is undoing
* the topmost invocation, then the focus will be restored to the
* previous focus at the time when pushModal() was invoked.
*
* @timestamp is optionally used to associate the call with a specific user
* initiated event. If not provided then the value of
* global.get_current_time() is assumed.
*/
function popModal(actor, timestamp) {
if (timestamp == undefined)
timestamp = global.get_current_time();
let focusIndex = _findModal(actor);
if (focusIndex < 0) {
global.stage.set_key_focus(null);
global.end_modal(timestamp);
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
throw new Error('incorrect pop');
}
function popModal(actor) {
modalCount -= 1;
let record = modalActorFocusStack[focusIndex];
record.actor.disconnect(record.destroyId);
if (focusIndex == modalActorFocusStack.length - 1) {
if (record.focus)
record.focus.disconnect(record.focusDestroyId);
global.stage.set_key_focus(record.focus);
} else {
let t = modalActorFocusStack[modalActorFocusStack.length - 1];
if (t.focus)
t.focus.disconnect(t.focusDestroyId);
// Remove from the middle, shift the focus chain up
for (let i = modalActorFocusStack.length - 1; i > focusIndex; i--) {
modalActorFocusStack[i].focus = modalActorFocusStack[i - 1].focus;
modalActorFocusStack[i].focusDestroyId = modalActorFocusStack[i - 1].focusDestroyId;
let focusIndex = _findModal(actor);
if (focusIndex >= 0) {
if (focusIndex == modalActorFocusStack.length - 1) {
let [stackActor, stackFocus] = modalActorFocusStack[focusIndex];
global.stage.set_key_focus(stackFocus);
} else {
// Remove from the middle, shift the focus chain up
for (let i = focusIndex; i < modalActorFocusStack.length - 1; i++) {
modalActorFocusStack[i + 1][1] = modalActorFocusStack[i][1];
}
}
modalActorFocusStack.splice(focusIndex, 1);
}
modalActorFocusStack.splice(focusIndex, 1);
if (modalCount > 0)
return;
global.end_modal(timestamp);
global.end_modal(global.get_current_time());
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,276 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter;
const Gdk = imports.gi.Gdk;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Pango = imports.gi.Pango;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const Params = imports.misc.params;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const OPEN_AND_CLOSE_TIME = 0.1;
const FADE_OUT_DIALOG_TIME = 1.0;
const State = {
OPENED: 0,
CLOSED: 1,
OPENING: 2,
CLOSING: 3,
FADED_OUT: 4
};
function ModalDialog() {
this._init();
}
ModalDialog.prototype = {
_init: function(params) {
params = Params.parse(params, { styleClass: null });
this.state = State.CLOSED;
this._hasModal = false;
this._group = new St.Group({ visible: false,
x: 0,
y: 0 });
Main.uiGroup.add_actor(this._group);
let constraint = new Clutter.BindConstraint({ source: global.stage,
coordinate: Clutter.BindCoordinate.POSITION | Clutter.BindCoordinate.SIZE });
this._group.add_constraint(constraint);
this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy));
this._actionKeys = {};
this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
this._lightbox = new Lightbox.Lightbox(this._group,
{ inhibitEvents: true });
this._backgroundBin = new St.Bin();
this._group.add_actor(this._backgroundBin);
this._lightbox.highlight(this._backgroundBin);
this._backgroundStack = new Shell.Stack();
this._backgroundBin.child = this._backgroundStack;
this._eventBlocker = new Clutter.Group({ reactive: true });
this._backgroundStack.add_actor(this._eventBlocker);
this._dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
vertical: true });
if (params.styleClass != null) {
this._dialogLayout.add_style_class_name(params.styleClass);
}
this._backgroundStack.add_actor(this._dialogLayout);
this.contentLayout = new St.BoxLayout({ vertical: true });
this._dialogLayout.add(this.contentLayout,
{ x_fill: true,
y_fill: true,
x_align: St.Align.MIDDLE,
y_align: St.Align.START });
this._buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
opacity: 220,
vertical: false });
this._dialogLayout.add(this._buttonLayout,
{ expand: true,
x_align: St.Align.MIDDLE,
y_align: St.Align.END });
global.focus_manager.add_group(this._dialogLayout);
this._initialKeyFocus = this._dialogLayout;
this._savedKeyFocus = null;
},
setButtons: function(buttons) {
this._buttonLayout.destroy_children();
this._actionKeys = {};
let i = 0;
for (let index in buttons) {
let buttonInfo = buttons[index];
let label = buttonInfo['label'];
let action = buttonInfo['action'];
let key = buttonInfo['key'];
let button = new St.Button({ style_class: 'modal-dialog-button',
reactive: true,
can_focus: true,
label: label });
let x_alignment;
if (buttons.length == 1)
x_alignment = St.Align.END;
else if (i == 0)
x_alignment = St.Align.START;
else if (i == buttons.length - 1)
x_alignment = St.Align.END;
else
x_alignment = St.Align.MIDDLE;
this._initialKeyFocus = button;
this._buttonLayout.add(button,
{ expand: true,
x_fill: false,
y_fill: false,
x_align: x_alignment,
y_align: St.Align.MIDDLE });
button.connect('clicked', action);
if (key)
this._actionKeys[key] = action;
i++;
}
},
_onKeyPressEvent: function(object, keyPressEvent) {
let symbol = keyPressEvent.get_key_symbol();
let action = this._actionKeys[symbol];
if (action)
action();
},
_onGroupDestroy: function() {
this.emit('destroy');
},
_fadeOpen: function() {
let monitor = global.get_focus_monitor();
this._backgroundBin.set_position(monitor.x, monitor.y);
this._backgroundBin.set_size(monitor.width, monitor.height);
this.state = State.OPENING;
this._dialogLayout.opacity = 255;
this._lightbox.show();
this._group.opacity = 0;
this._group.show();
Tweener.addTween(this._group,
{ opacity: 255,
time: OPEN_AND_CLOSE_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
this.state = State.OPENED;
this.emit('opened');
})
});
},
setInitialKeyFocus: function(actor) {
this._initialKeyFocus = actor;
},
open: function(timestamp) {
if (this.state == State.OPENED || this.state == State.OPENING)
return true;
if (!this.pushModal(timestamp))
return false;
this._fadeOpen();
return true;
},
close: function(timestamp) {
if (this.state == State.CLOSED || this.state == State.CLOSING)
return;
this.state = State.CLOSING;
this.popModal(timestamp);
this._savedKeyFocus = null;
Tweener.addTween(this._group,
{ opacity: 0,
time: OPEN_AND_CLOSE_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
this.state = State.CLOSED;
this._group.hide();
})
});
},
// Drop modal status without closing the dialog; this makes the
// dialog insensitive as well, so it needs to be followed shortly
// by either a close() or a pushModal()
popModal: function(timestamp) {
if (!this._hasModal)
return;
let focus = global.stage.key_focus;
if (focus && this._group.contains(focus))
this._savedKeyFocus = focus;
else
this._savedKeyFocus = null;
Main.popModal(this._group, timestamp);
global.gdk_screen.get_display().sync();
this._hasModal = false;
this._eventBlocker.raise_top();
},
pushModal: function (timestamp) {
if (this._hasModal)
return true;
if (!Main.pushModal(this._group, timestamp))
return false;
this._hasModal = true;
if (this._savedKeyFocus) {
this._savedKeyFocus.grab_key_focus();
this._savedKeyFocus = null;
} else
this._initialKeyFocus.grab_key_focus();
this._eventBlocker.lower_bottom();
return true;
},
// This method is like close, but fades the dialog out much slower,
// and leaves the lightbox in place. Once in the faded out state,
// the dialog can be brought back by an open call, or the lightbox
// can be dismissed by a close call.
//
// The main point of this method is to give some indication to the user
// that the dialog reponse has been acknowledged but will take a few
// moments before being processed.
// e.g., if a user clicked "Log Out" then the dialog should go away
// imediately, but the lightbox should remain until the logout is
// complete.
_fadeOutDialog: function(timestamp) {
if (this.state == State.CLOSED || this.state == State.CLOSING)
return;
if (this.state == State.FADED_OUT)
return;
this.popModal(timestamp);
Tweener.addTween(this._dialogLayout,
{ opacity: 0,
time: FADE_OUT_DIALOG_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
this.state = State.FADED_OUT;
})
});
}
};
Signals.addSignalMethods(ModalDialog.prototype);

View File

@ -1,18 +1,17 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter;
const DBus = imports.dbus;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Mainloop = imports.mainloop;
const St = imports.gi.St;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const Config = imports.misc.config;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const Params = imports.misc.params;
const Util = imports.misc.util;
let nextNotificationId = 1;
@ -76,11 +75,11 @@ const Urgency = {
const rewriteRules = {
'XChat': [
{ pattern: /^XChat: Private message from: (\S*) \(.*\)$/,
replacement: '<$1>' },
replacement: '&lt;$1&gt;' },
{ pattern: /^XChat: New public message from: (\S*) \((.*)\)$/,
replacement: '$2 <$1>' },
replacement: '$2 &lt;$1&gt;' },
{ pattern: /^XChat: Highlighted message from: (\S*) \((.*)\)$/,
replacement: '$2 <$1>' }
replacement: '$2 &lt;$1&gt;' }
]
};
@ -92,6 +91,16 @@ NotificationDaemon.prototype = {
_init: function() {
DBus.session.exportObject('/org/freedesktop/Notifications', this);
this._everAcquiredName = false;
DBus.session.acquire_name('org.freedesktop.Notifications',
// We pass MANY_INSTANCES so that if
// notification-daemon is running, we'll
// get queued behind it and then get the
// name after killing it below
DBus.MANY_INSTANCES,
Lang.bind(this, this._acquiredName),
Lang.bind(this, this._lostName));
this._sources = {};
this._senderToPid = {};
this._notifications = {};
@ -106,6 +115,31 @@ NotificationDaemon.prototype = {
Lang.bind(this, this._onFocusAppChanged));
},
_acquiredName: function() {
this._everAcquiredName = true;
},
_lostName: function() {
if (this._everAcquiredName)
log('Lost name org.freedesktop.Notifications!');
else if (GLib.getenv('GNOME_SHELL_NO_REPLACE'))
log('Failed to acquire org.freedesktop.Notifications');
else {
log('Failed to acquire org.freedesktop.Notifications; trying again');
// kill the notification-daemon. pkill is more portable
// than killall, but on Linux at least it won't match if
// you pass more than 15 characters of the process name...
// However, if you use the '-f' flag to match the entire
// command line, it will work, but we have to be careful
// in that case that we don't match 'gedit
// notification-daemon.c' or whatever...
let p = new Shell.Process({ args: ['pkill', '-f',
'^([^ ]*/)?(notification-daemon|notify-osd)$']});
p.run();
}
},
_iconForNotificationData: function(icon, hints, size) {
let textureCache = St.TextureCache.get_default();
@ -119,9 +153,9 @@ NotificationDaemon.prototype = {
return new St.Icon({ icon_name: icon,
icon_type: St.IconType.FULLCOLOR,
icon_size: size });
} else if (hints['image-data']) {
} else if (hints.icon_data) {
let [width, height, rowStride, hasAlpha,
bitsPerSample, nChannels, data] = hints['image-data'];
bitsPerSample, nChannels, data] = hints.icon_data;
return textureCache.load_from_raw(data, data.length, hasAlpha,
width, height, rowStride, size);
} else {
@ -141,46 +175,14 @@ NotificationDaemon.prototype = {
}
},
// Returns the source associated with ndata.notification if it is set.
// Otherwise, returns the source associated with the pid if one is
// stored in this._sources and the notification is not transient.
// Otherwise, creates a new source as long as pid is provided.
//
// Either a pid or ndata.notification is needed to retrieve or
// create a source.
_getSource: function(title, pid, ndata) {
if (!pid && !(ndata && ndata.notification))
return null;
// We use notification's source for the notifications we still have
// around that are getting replaced because we don't keep sources
// for transient notifications in this._sources, but we still want
// the notification associated with them to get replaced correctly.
if (ndata && ndata.notification)
return ndata.notification.source;
let isForTransientNotification = (ndata && ndata.hints['transient'] == true);
// We don't want to override a persistent notification
// with a transient one from the same sender, so we
// always create a new source object for new transient notifications
// and never add it to this._sources .
if (!isForTransientNotification && this._sources[pid]) {
let source = this._sources[pid];
source.setTitle(title);
return source;
}
_newSource: function(title, pid) {
let source = new Source(title, pid);
source.setTransient(isForTransientNotification);
this._sources[pid] = source;
if (!isForTransientNotification) {
this._sources[pid] = source;
source.connect('destroy', Lang.bind(this,
function() {
delete this._sources[pid];
}));
}
source.connect('destroy', Lang.bind(this,
function() {
delete this._sources[pid];
}));
Main.messageTray.add(source);
return source;
@ -190,11 +192,9 @@ NotificationDaemon.prototype = {
actions, hints, timeout) {
let id;
// Filter out chat and presence notifications from Empathy, since we
// Filter out notifications from Empathy, since we
// handle that information from telepathyClient.js
if (appName == 'Empathy' && (hints['category'] == 'im.received' ||
hints['category'] == 'presence.online' ||
hints['category'] == 'presence.offline')) {
if (appName == 'Empathy') {
// Ignore replacesId since we already sent back a
// NotificationClosed for that id.
id = nextNotificationId++;
@ -205,6 +205,8 @@ NotificationDaemon.prototype = {
return id;
}
summary = GLib.markup_escape_text(summary, -1);
let rewrites = rewriteRules[appName];
if (rewrites) {
for (let i = 0; i < rewrites.length; i++) {
@ -216,15 +218,6 @@ NotificationDaemon.prototype = {
hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
// Be compatible with the various hints for image data
// 'image-data' is the latest name of this hint, introduced in 1.2
if (!hints['image-data']) {
if (hints['image_data'])
hints['image-data'] = hints['image_data']; // version 1.1 of the spec
else if (hints['icon_data'])
hints['image-data'] = hints['icon_data']; // previous versions of the spec
}
let ndata = { appName: appName,
icon: icon,
summary: summary,
@ -243,8 +236,7 @@ NotificationDaemon.prototype = {
let sender = DBus.getCurrentMessageContext().sender;
let pid = this._senderToPid[sender];
let source = this._getSource(appName, pid, ndata);
let source = pid ? this._sources[pid] : null;
if (source) {
this._notifyForSource(source, ndata);
@ -265,23 +257,16 @@ NotificationDaemon.prototype = {
if (!ndata)
return;
source = this._getSource(appName, pid, ndata);
this._senderToPid[sender] = pid;
source = this._sources[pid];
if (!source)
source = this._newSource(appName, pid);
source.connect('destroy', Lang.bind(this,
function() {
delete this._senderToPid[sender];
}));
// We only store sender-pid entries for persistent sources.
// Removing the entries once the source is destroyed
// would result in the entries associated with transient
// sources removed once the notification is shown anyway.
// However, keeping these pairs would mean that we would
// possibly remove an entry associated with a persistent
// source when a transient source for the same sender is
// distroyed.
if (!source.isTransient) {
this._senderToPid[sender] = pid;
source.connect('destroy', Lang.bind(this,
function() {
delete this._senderToPid[sender];
}));
}
this._notifyForSource(source, ndata);
}));
@ -296,34 +281,19 @@ NotificationDaemon.prototype = {
let iconActor = this._iconForNotificationData(icon, hints, source.ICON_SIZE);
if (notification == null) {
notification = new MessageTray.Notification(source, summary, body,
{ icon: iconActor,
bannerMarkup: true });
notification = new MessageTray.Notification(source, summary, body, { icon: iconActor });
ndata.notification = notification;
notification.connect('clicked', Lang.bind(this,
function(n) {
this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
}));
notification.connect('destroy', Lang.bind(this,
function(n, reason) {
function(n) {
delete this._notifications[id];
let notificationClosedReason;
switch (reason) {
case MessageTray.NotificationDestroyedReason.EXPIRED:
notificationClosedReason = NotificationClosedReason.EXPIRED;
break;
case MessageTray.NotificationDestroyedReason.DISMISSED:
notificationClosedReason = NotificationClosedReason.DISMISSED;
break;
case MessageTray.NotificationDestroyedReason.SOURCE_CLOSED:
notificationClosedReason = NotificationClosedReason.APP_CLOSED;
break;
}
this._emitNotificationClosed(id, notificationClosedReason);
}));
notification.connect('action-invoked', Lang.bind(this,
function(n, actionId) {
this._emitActionInvoked(id, actionId);
}));
notification.connect('action-invoked', Lang.bind(this, this._actionInvoked, source, id));
} else {
notification.update(summary, body, { icon: iconActor,
bannerMarkup: true,
clear: true });
}
@ -332,33 +302,21 @@ NotificationDaemon.prototype = {
for (let i = 0; i < actions.length - 1; i += 2)
notification.addButton(actions[i], actions[i + 1]);
}
switch (hints.urgency) {
case Urgency.LOW:
notification.setUrgency(MessageTray.Urgency.LOW);
break;
case Urgency.NORMAL:
notification.setUrgency(MessageTray.Urgency.NORMAL);
break;
case Urgency.CRITICAL:
notification.setUrgency(MessageTray.Urgency.CRITICAL);
break;
}
notification.setResident(hints.resident == true);
// 'transient' is a reserved keyword in JS, so we have to retrieve the value
// of the 'transient' hint with hints['transient'] rather than hints.transient
notification.setTransient(hints['transient'] == true);
notification.setUrgent(hints.urgency == Urgency.CRITICAL);
let sourceIconActor = source.useNotificationIcon ? this._iconForNotificationData(icon, hints, source.ICON_SIZE) : null;
source.processNotification(notification, sourceIconActor);
source.notify(notification, sourceIconActor);
},
CloseNotification: function(id) {
let ndata = this._notifications[id];
if (ndata) {
if (ndata.notification)
ndata.notification.destroy(MessageTray.NotificationDestroyedReason.SOURCE_CLOSED);
ndata.notification.destroy();
delete this._notifications[id];
}
this._emitNotificationClosed(id, NotificationClosedReason.APP_CLOSED);
},
GetCapabilities: function() {
@ -378,10 +336,10 @@ NotificationDaemon.prototype = {
GetServerInformation: function() {
return [
Config.PACKAGE_NAME,
'GNOME Shell',
'GNOME',
Config.PACKAGE_VERSION,
'1.2'
'0.1', // FIXME, get this from somewhere
'1.0'
];
},
@ -393,12 +351,17 @@ NotificationDaemon.prototype = {
for (let id in this._sources) {
let source = this._sources[id];
if (source.app == tracker.focus_app) {
source.destroyNonResidentNotifications();
source.activated();
return;
}
}
},
_actionInvoked: function(notification, action, source, id) {
source.activated();
this._emitActionInvoked(id, action);
},
_emitNotificationClosed: function(id, reason) {
DBus.session.emit_signal('/org/freedesktop/Notifications',
'org.freedesktop.Notifications',
@ -414,7 +377,9 @@ NotificationDaemon.prototype = {
},
_onTrayIconAdded: function(o, icon) {
let source = this._getSource(icon.title || icon.wm_class || _("Unknown"), icon.pid, null);
let source = this._sources[icon.pid];
if (!source)
source = this._newSource(icon.title || icon.wm_class || _("Unknown"), icon.pid);
source.setTrayIcon(icon);
},
@ -438,74 +403,30 @@ Source.prototype = {
MessageTray.Source.prototype._init.call(this, title);
this._pid = pid;
this._appStateChangedId = 0;
this._setApp();
if (this.app)
this.title = this.app.get_name();
else
this.useNotificationIcon = true;
this._trayIcon = null;
this._isTrayIcon = false;
},
processNotification: function(notification, icon) {
notify: function(notification, icon) {
if (!this.app)
this._setApp();
if (!this.app && icon)
this._setSummaryIcon(icon);
let tracker = Shell.WindowTracker.get_default();
if (notification.resident && this.app && tracker.focus_app == this.app)
this.pushNotification(notification);
else
this.notify(notification);
},
handleSummaryClick: function() {
if (!this._trayIcon)
return false;
let event = Clutter.get_current_event();
if (event.type() != Clutter.EventType.BUTTON_RELEASE)
return false;
// Left clicks are passed through only where there aren't unacknowledged
// notifications, so it possible to open them in summary mode; right
// clicks are always forwarded, as the right click menu is not useful for
// tray icons
if (event.get_button() == 1 &&
this.notifications.length > 0)
return false;
if (Main.overview.visible) {
// We can't just connect to Main.overview's 'hidden' signal,
// because it's emitted *before* it calls popModal()...
let id = global.connect('notify::stage-input-mode', Lang.bind(this,
function () {
global.disconnect(id);
this._trayIcon.click(event);
}));
Main.overview.hide();
} else {
this._trayIcon.click(event);
}
return true;
MessageTray.Source.prototype.notify.call(this, notification);
},
_setApp: function() {
if (this.app)
return;
this.app = Shell.WindowTracker.get_default().get_app_from_pid(this._pid);
if (!this.app)
return;
// We only update the app if this.app is null, so we can't disconnect the old this._appStateChangedId
// even if it were non-zero for some reason.
this._appStateChangedId = this.app.connect('notify::state', Lang.bind(this, this._appStateChanged));
// Only override the icon if we were previously using
// notification-based icons (ie, not a trayicon) or if it was unset before
if (!this._trayIcon) {
// notification-based icons (ie, not a trayicon)
if (this.useNotificationIcon) {
this.useNotificationIcon = false;
this._setSummaryIcon(this.app.create_icon_texture (this.ICON_SIZE));
}
@ -514,29 +435,17 @@ Source.prototype = {
setTrayIcon: function(icon) {
this._setSummaryIcon(icon);
this.useNotificationIcon = false;
this._trayIcon = icon;
this._isTrayIcon = true;
},
open: function(notification) {
this.destroyNonResidentNotifications();
_notificationClicked: function(notification) {
notification.destroy();
this.openApp();
this.activated();
},
_lastNotificationRemoved: function() {
if (!this._trayIcon)
this.destroy();
},
_appStateChanged: function() {
// Destroy notification sources when their apps exit.
// The app exiting would normally result in a tray icon being removed,
// so the associated source would be destroyed through the code path
// that handles the tray icon being removed. We should not destroy
// the source associated with a tray icon when the application state
// is Shell.AppState.STOPPED because running applications that have
// no open windows would also have that state. This is often the case
// for applications that use tray icons.
if (!this._trayIcon && this.app.get_state() == Shell.AppState.STOPPED)
activated: function() {
if (!this._isTrayIcon)
this.destroy();
},
@ -549,13 +458,5 @@ Source.prototype = {
let mostRecentWindow = windows[0];
Main.activateWindow(mostRecentWindow);
}
},
destroy: function() {
if (this.app && this._appStateChangedId) {
this.app.disconnect(this._appStateChangedId);
this._appStateChangedId = 0;
}
MessageTray.Source.prototype.destroy.call(this);
}
};

View File

@ -1,19 +1,18 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter;
const Gtk = imports.gi.Gtk;
const Meta = imports.gi.Meta;
const Mainloop = imports.mainloop;
const Signals = imports.signals;
const Lang = imports.lang;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Gdk = imports.gi.Gdk;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const AppDisplay = imports.ui.appDisplay;
const Dash = imports.ui.dash;
const DND = imports.ui.dnd;
const DocDisplay = imports.ui.docDisplay;
const GenericDisplay = imports.ui.genericDisplay;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
@ -22,7 +21,6 @@ const PlaceDisplay = imports.ui.placeDisplay;
const Tweener = imports.ui.tweener;
const ViewSelector = imports.ui.viewSelector;
const WorkspacesView = imports.ui.workspacesView;
const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
// Time for initial animation going into Overview mode
const ANIMATION_TIME = 0.25;
@ -30,19 +28,31 @@ const ANIMATION_TIME = 0.25;
// We split the screen vertically between the dash and the view selector.
const DASH_SPLIT_FRACTION = 0.1;
const DND_WINDOW_SWITCH_TIMEOUT = 1250;
const SHELL_INFO_HIDE_TIMEOUT = 10;
const SwipeScrollDirection = {
NONE: 0,
HORIZONTAL: 1,
VERTICAL: 2
};
function Source() {
this._init();
}
const SwipeScrollResult = {
CANCEL: 0,
SWIPE: 1,
CLICK: 2
};
Source.prototype = {
__proto__: MessageTray.Source.prototype,
_init: function() {
MessageTray.Source.prototype._init.call(this,
"System Information");
this._setSummaryIcon(this.createNotificationIcon());
},
createNotificationIcon: function() {
return new St.Icon({ icon_name: 'info',
icon_type: St.IconType.FULLCOLOR,
icon_size: this.ICON_SIZE });
},
_notificationClicked: function() {
this.destroy();
}
}
function ShellInfo() {
this._init();
@ -51,10 +61,14 @@ function ShellInfo() {
ShellInfo.prototype = {
_init: function() {
this._source = null;
this._timeoutId = 0;
this._undoCallback = null;
},
_onUndoClicked: function() {
Mainloop.source_remove(this._timeoutId);
this._timeoutId = 0;
if (this._undoCallback)
this._undoCallback();
this._undoCallback = null;
@ -63,9 +77,22 @@ ShellInfo.prototype = {
this._source.destroy();
},
_onTimeout: function() {
this._timeoutId = 0;
if (this._source)
this._source.destroy();
return false;
},
setMessage: function(text, undoCallback, undoLabel) {
if (this._timeoutId)
Mainloop.source_remove(this._timeoutId);
this._timeoutId = Mainloop.timeout_add_seconds(SHELL_INFO_HIDE_TIMEOUT,
Lang.bind(this, this._onTimeout));
if (this._source == null) {
this._source = new MessageTray.SystemNotificationSource();
this._source = new Source();
this._source.connect('destroy', Lang.bind(this,
function() {
this._source = null;
@ -73,15 +100,11 @@ ShellInfo.prototype = {
Main.messageTray.add(this._source);
}
let notification = null;
if (this._source.notifications.length == 0) {
let notification = this._source.notification;
if (notification == null)
notification = new MessageTray.Notification(this._source, text, null);
} else {
notification = this._source.notifications[0];
else
notification.update(text, null, { clear: true });
}
notification.setTransient(true);
this._undoCallback = undoCallback;
if (undoCallback) {
@ -101,19 +124,18 @@ function Overview() {
Overview.prototype = {
_init : function() {
this._desktopFade = new St.Bin();
global.overlay_group.add_actor(this._desktopFade);
// The actual global.background_actor is inside global.window_group,
// which is hidden when displaying the overview, so we display a clone.
this._background = new Clutter.Clone({ source: global.background_actor });
this._background.hide();
global.overlay_group.add_actor(this._background);
this._desktopFade = new St.Bin();
global.overlay_group.add_actor(this._desktopFade);
this._spacing = 0;
this._group = new St.Group({ name: 'overview',
reactive: true });
this._group = new St.Group({ name: 'overview' });
this._group._delegate = this;
this._group.connect('style-changed',
Lang.bind(this, function() {
@ -125,17 +147,11 @@ Overview.prototype = {
}
}));
this._scrollDirection = SwipeScrollDirection.NONE;
this._scrollAdjustment = null;
this._capturedEventId = 0;
this._buttonPressId = 0;
this.shellInfo = new ShellInfo();
this._workspacesDisplay = null;
this.visible = false; // animating to overview, in overview, animating out
this._shown = false; // show() and not hide()
this._shownTemporarily = false; // showTemporarily() and not hideTemporarily()
this._modal = false; // have a modal grab
this.visible = false;
this.animationInProgress = false;
this._hideInProgress = false;
@ -151,277 +167,31 @@ Overview.prototype = {
this._group.hide();
global.overlay_group.add_actor(this._group);
this._coverPane.hide();
// XDND
this._dragMonitor = {
dragMotion: Lang.bind(this, this._onDragMotion)
};
Main.xdndHandler.connect('drag-begin', Lang.bind(this, this._onDragBegin));
Main.xdndHandler.connect('drag-end', Lang.bind(this, this._onDragEnd));
this._windowSwitchTimeoutId = 0;
this._windowSwitchTimestamp = 0;
this._lastActiveWorkspaceIndex = -1;
this._lastHoveredWindow = null;
this._needsFakePointerEvent = false;
this.workspaces = null;
},
// The members we construct that are implemented in JS might
// want to access the overview as Main.overview to connect
// signal handlers and so forth. So we create them after
// construction in this init() method.
init: function() {
this.shellInfo = new ShellInfo();
this.viewSelector = new ViewSelector.ViewSelector();
this._group.add_actor(this.viewSelector.actor);
this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay();
this.viewSelector.addViewTab('windows', _("Windows"), this._workspacesDisplay.actor, 'text-x-generic');
this.viewSelector.addViewTab("Windows", this._workspacesDisplay.actor);
let appView = new AppDisplay.AllAppDisplay();
this.viewSelector.addViewTab('applications', _("Applications"), appView.actor, 'system-run');
this.viewSelector.addViewTab("Applications", appView.actor);
// Default search providers
this.viewSelector.addSearchProvider(new AppDisplay.AppSearchProvider());
this.viewSelector.addSearchProvider(new AppDisplay.PrefsSearchProvider());
this.viewSelector.addSearchProvider(new PlaceDisplay.PlaceSearchProvider());
this.viewSelector.addSearchProvider(new DocDisplay.DocSearchProvider());
this.viewSelector.addSearchProvider(new WorkspacesView.WindowSearchProvider());
// TODO - recalculate everything when desktop size changes
this.dash = new Dash.Dash();
this._group.add_actor(this.dash.actor);
this.dash.actor.add_constraint(this.viewSelector.constrainY);
this.dash.actor.add_constraint(this.viewSelector.constrainHeight);
this._dash = new Dash.Dash();
this._group.add_actor(this._dash.actor);
this._dash.actor.add_constraint(this.viewSelector.constrainY);
this._dash.actor.add_constraint(this.viewSelector.constrainHeight);
// Translators: this is the name of the dock/favorites area on
// the left of the overview
Main.ctrlAltTabManager.addGroup(this.dash.actor, _("Dash"), 'user-bookmarks');
this._coverPane.lower_bottom();
},
_onDragBegin: function() {
DND.addDragMonitor(this._dragMonitor);
// Remember the workspace we started from
this._lastActiveWorkspaceIndex = global.screen.get_active_workspace_index();
},
_onDragEnd: function(time) {
// In case the drag was canceled while in the overview
// we have to go back to where we started and hide
// the overview
if (this._shownTemporarily) {
global.screen.get_workspace_by_index(this._lastActiveWorkspaceIndex).activate(time);
this.hideTemporarily();
}
this._resetWindowSwitchTimeout();
this._lastHoveredWindow = null;
DND.removeDragMonitor(this._dragMonitor);
this.endItemDrag();
},
_resetWindowSwitchTimeout: function() {
if (this._windowSwitchTimeoutId != 0) {
Mainloop.source_remove(this._windowSwitchTimeoutId);
this._windowSwitchTimeoutId = 0;
this._needsFakePointerEvent = false;
}
},
_fakePointerEvent: function() {
let display = Gdk.Display.get_default();
let deviceManager = display.get_device_manager();
let pointer = deviceManager.get_client_pointer();
let [screen, pointerX, pointerY] = pointer.get_position();
pointer.warp(screen, pointerX, pointerY);
},
_onDragMotion: function(dragEvent) {
let targetIsWindow = dragEvent.targetActor &&
dragEvent.targetActor._delegate &&
dragEvent.targetActor._delegate.metaWindow &&
!(dragEvent.targetActor._delegate instanceof WorkspaceThumbnail.WindowClone);
this._windowSwitchTimestamp = global.get_current_time();
if (targetIsWindow &&
dragEvent.targetActor._delegate.metaWindow == this._lastHoveredWindow)
return DND.DragMotionResult.CONTINUE;
this._lastHoveredWindow = null;
this._resetWindowSwitchTimeout();
if (targetIsWindow) {
this._lastHoveredWindow = dragEvent.targetActor._delegate.metaWindow;
this._windowSwitchTimeoutId = Mainloop.timeout_add(DND_WINDOW_SWITCH_TIMEOUT,
Lang.bind(this, function() {
this._needsFakePointerEvent = true;
Main.activateWindow(dragEvent.targetActor._delegate.metaWindow,
this._windowSwitchTimestamp);
this.hideTemporarily();
this._lastHoveredWindow = null;
}));
}
return DND.DragMotionResult.CONTINUE;
},
setScrollAdjustment: function(adjustment, direction) {
this._scrollAdjustment = adjustment;
if (this._scrollAdjustment == null)
this._scrollDirection = SwipeScrollDirection.NONE;
else
this._scrollDirection = direction;
},
_onButtonPress: function(actor, event) {
if (this._scrollDirection == SwipeScrollDirection.NONE
|| event.get_button() != 1)
return;
let [stageX, stageY] = event.get_coords();
this._dragStartX = this._dragX = stageX;
this._dragStartY = this._dragY = stageY;
this._dragStartValue = this._scrollAdjustment.value;
this._lastMotionTime = -1; // used to track "stopping" while swipe-scrolling
this._capturedEventId = global.stage.connect('captured-event',
Lang.bind(this, this._onCapturedEvent));
this.emit('swipe-scroll-begin');
},
_onCapturedEvent: function(actor, event) {
let stageX, stageY;
let threshold = Gtk.Settings.get_default().gtk_dnd_drag_threshold;
switch(event.type()) {
case Clutter.EventType.BUTTON_RELEASE:
[stageX, stageY] = event.get_coords();
// default to snapping back to the original value
let newValue = this._dragStartValue;
let minValue = this._scrollAdjustment.lower;
let maxValue = this._scrollAdjustment.upper - this._scrollAdjustment.page_size;
let direction;
if (this._scrollDirection == SwipeScrollDirection.HORIZONTAL) {
direction = stageX > this._dragStartX ? -1 : 1;
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
direction *= -1;
} else {
direction = stageY > this._dragStartY ? -1 : 1;
}
// We default to scroll a full page size; both the first
// and the last page may be smaller though, so we need to
// adjust difference in those cases.
let difference = direction * this._scrollAdjustment.page_size;
if (this._dragStartValue + difference > maxValue)
difference = maxValue - this._dragStartValue;
else if (this._dragStartValue + difference < minValue)
difference = minValue - this._dragStartValue;
// If the user has moved more than half the scroll
// difference, we want to "settle" to the new value
// even if the user stops dragging rather "throws" by
// releasing during the drag.
let distance = this._dragStartValue - this._scrollAdjustment.value;
let noStop = Math.abs(distance / difference) > 0.5;
// We detect if the user is stopped by comparing the
// timestamp of the button release with the timestamp of
// the last motion. Experimentally, a difference of 0 or 1
// millisecond indicates that the mouse is in motion, a
// larger difference indicates that the mouse is stopped.
if ((this._lastMotionTime > 0 &&
this._lastMotionTime > event.get_time() - 2) ||
noStop) {
if (this._dragStartValue + difference >= minValue &&
this._dragStartValue + difference <= maxValue)
newValue += difference;
}
let result;
// See if the user has moved the mouse enough to trigger
// a drag
if (Math.abs(stageX - this._dragStartX) < threshold &&
Math.abs(stageY - this._dragStartY) < threshold) {
// no motion? It's a click!
result = SwipeScrollResult.CLICK;
this.emit('swipe-scroll-end', result);
} else {
if (newValue == this._dragStartValue)
result = SwipeScrollResult.CANCEL;
else
result = SwipeScrollResult.SWIPE;
// The event capture handler is disconnected
// while scrolling to the final position, so
// to avoid undesired prelights we raise
// the cover pane.
this._coverPane.raise_top();
this._coverPane.show();
Tweener.addTween(this._scrollAdjustment,
{ value: newValue,
time: ANIMATION_TIME,
transition: 'easeOutQuad',
onCompleteScope: this,
onComplete: function() {
this._coverPane.hide();
this.emit('swipe-scroll-end',
result);
}
});
}
global.stage.disconnect(this._capturedEventId);
this._capturedEventId = 0;
return result != SwipeScrollResult.CLICK;
case Clutter.EventType.MOTION:
[stageX, stageY] = event.get_coords();
let dx = this._dragX - stageX;
let dy = this._dragY - stageY;
let primary = global.get_primary_monitor();
this._dragX = stageX;
this._dragY = stageY;
this._lastMotionTime = event.get_time();
// See if the user has moved the mouse enough to trigger
// a drag
if (Math.abs(stageX - this._dragStartX) < threshold &&
Math.abs(stageY - this._dragStartY) < threshold)
return true;
if (this._scrollDirection == SwipeScrollDirection.HORIZONTAL) {
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
this._scrollAdjustment.value -= (dx / primary.width) * this._scrollAdjustment.page_size;
else
this._scrollAdjustment.value += (dx / primary.width) * this._scrollAdjustment.page_size;
} else {
this._scrollAdjustment.value += (dy / primary.height) * this._scrollAdjustment.page_size;
}
return true;
// Block enter/leave events to avoid prelights
// during swipe-scroll
case Clutter.EventType.ENTER:
case Clutter.EventType.LEAVE:
return true;
}
return false;
this.workspaces = null;
},
_getDesktopClone: function() {
@ -442,7 +212,7 @@ Overview.prototype = {
let primary = global.get_primary_monitor();
let rtl = (St.Widget.get_default_direction () == St.TextDirection.RTL);
let contentY = Main.panel.actor.height;
let contentY = Panel.PANEL_HEIGHT;
let contentHeight = primary.height - contentY - Main.messageTray.actor.height;
this._group.set_position(primary.x, primary.y);
@ -451,21 +221,21 @@ Overview.prototype = {
this._coverPane.set_position(0, contentY);
this._coverPane.set_size(primary.width, contentHeight);
let dashWidth = Math.round(DASH_SPLIT_FRACTION * primary.width);
let viewWidth = primary.width - dashWidth - this._spacing;
let viewWidth = (1.0 - DASH_SPLIT_FRACTION) * primary.width - this._spacing;
let viewHeight = contentHeight - 2 * this._spacing;
let viewY = contentY + this._spacing;
let viewX = rtl ? 0 : dashWidth + this._spacing;
let viewX = rtl ? 0
: Math.floor(DASH_SPLIT_FRACTION * primary.width) + this._spacing;
// Set the dash's x position - y is handled by a constraint
let dashX;
if (rtl) {
this.dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
this._dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
dashX = primary.width;
} else {
dashX = 0;
}
this.dash.actor.set_x(dashX);
this._dash.actor.set_x(dashX);
this.viewSelector.actor.set_position(viewX, viewY);
this.viewSelector.actor.set_size(viewWidth, viewHeight);
@ -477,10 +247,6 @@ Overview.prototype = {
this.emit('item-drag-begin');
},
cancelledItemDrag: function(source) {
this.emit('item-drag-cancelled');
},
endItemDrag: function(source) {
this.emit('item-drag-end');
},
@ -489,33 +255,39 @@ Overview.prototype = {
this.emit('window-drag-begin');
},
cancelledWindowDrag: function(source) {
this.emit('window-drag-cancelled');
},
endWindowDrag: function(source) {
this.emit('window-drag-end');
},
// show:
//
// Animates the overview visible and grabs mouse and keyboard input
show : function() {
if (this._shown)
return;
// Do this manually instead of using _syncInputMode, to handle failure
if (!Main.pushModal(this._group))
return;
this._modal = true;
this._animateVisible();
this._shown = true;
this._buttonPressId = this._group.connect('button-press-event',
Lang.bind(this, this._onButtonPress));
// Returns the scale the Overview has when we just start zooming out
// to overview mode. That is, when just the active workspace is showing.
getZoomedInScale : function() {
return 1 / this.workspaces.getScale();
},
_animateVisible: function() {
if (this.visible || this.animationInProgress)
// Returns the position the Overview has when we just start zooming out
// to overview mode. That is, when just the active workspace is showing.
getZoomedInPosition : function() {
let [posX, posY] = this.workspaces.getActiveWorkspacePosition();
let scale = this.getZoomedInScale();
return [- posX * scale, - posY * scale];
},
// Returns the current scale of the Overview.
getScale : function() {
return this.workspaces.actor.scaleX;
},
// Returns the current position of the Overview.
getPosition : function() {
return [this.workspaces.actor.x, this.workspaces.actor.y];
},
show : function() {
if (this.visible)
return;
if (!Main.pushModal(this.viewSelector.actor))
return;
this.visible = true;
@ -532,7 +304,9 @@ Overview.prototype = {
this._group.show();
this._background.show();
this.viewSelector.show();
this._workspacesDisplay.show();
this._dash.show();
this.workspaces = this._workspacesDisplay.workspacesView;
global.overlay_group.add_actor(this.workspaces.actor);
@ -550,108 +324,38 @@ Overview.prototype = {
});
}
this._group.opacity = 0;
Tweener.addTween(this._group,
{ opacity: 255,
// Create a zoom out effect. First scale the workspaces view up and
// position it so that the active workspace fills up the whole screen,
// then transform it to its normal dimensions and position.
// The opposite transition is used in hide().
this.workspaces.actor.scaleX = this.workspaces.actor.scaleY = this.getZoomedInScale();
[this.workspaces.actor.x, this.workspaces.actor.y] = this.getZoomedInPosition();
let primary = global.get_primary_monitor();
Tweener.addTween(this.workspaces.actor,
{ x: primary.x - this._group.x,
y: primary.y - this._group.y,
scaleX: 1,
scaleY: 1,
transition: 'easeOutQuad',
time: ANIMATION_TIME,
onComplete: this._showDone,
onCompleteScope: this
});
// Make the other elements fade in.
this._group.opacity = 0;
Tweener.addTween(this._group,
{ opacity: 255,
transition: 'easeOutQuad',
time: ANIMATION_TIME
});
this._coverPane.raise_top();
this._coverPane.show();
this.emit('showing');
},
// showTemporarily:
//
// Animates the overview visible without grabbing mouse and keyboard input;
// if show() has already been called, this has no immediate effect, but
// will result in the overview not being hidden until hideTemporarily() is
// called.
showTemporarily: function() {
if (this._shownTemporarily)
return;
this._syncInputMode();
this._animateVisible();
this._shownTemporarily = true;
},
// hide:
//
// Reverses the effect of show()
hide: function() {
if (!this._shown)
return;
if (!this._shownTemporarily)
this._animateNotVisible();
this._shown = false;
this._syncInputMode();
if (this._buttonPressId > 0)
this._group.disconnect(this._buttonPressId);
this._buttonPressId = 0;
},
// hideTemporarily:
//
// Reverses the effect of showTemporarily()
hideTemporarily: function() {
if (!this._shownTemporarily)
return;
if (!this._shown)
this._animateNotVisible();
this._shownTemporarily = false;
this._syncInputMode();
},
toggle: function() {
if (this._shown)
this.hide();
else
this.show();
},
//// Private methods ////
_syncInputMode: function() {
// We delay input mode changes during animation so that when removing the
// overview we don't have a problem with the release of a press/release
// going to an application.
if (this.animationInProgress)
return;
if (this._shown) {
if (!this._modal) {
if (Main.pushModal(this._group))
this._modal = true;
else
this.hide();
}
} else if (this._shownTemporarily) {
if (this._modal) {
Main.popModal(this._group);
this._modal = false;
}
global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
} else {
if (this._modal) {
Main.popModal(this._group);
this._modal = false;
}
else if (global.stage_input_mode == Shell.StageInputMode.FULLSCREEN)
global.stage_input_mode = Shell.StageInputMode.NORMAL;
}
},
_animateNotVisible: function() {
if (!this.visible || this.animationInProgress)
if (!this.visible || this._hideInProgress)
return;
this.animationInProgress = true;
@ -668,32 +372,63 @@ Overview.prototype = {
this.workspaces.hide();
// Make other elements fade out.
Tweener.addTween(this._group,
{ opacity: 0,
// Create a zoom in effect by transforming the workspaces view so that
// the active workspace fills up the whole screen. The opposite
// transition is used in show().
let scale = this.getZoomedInScale();
let [posX, posY] = this.getZoomedInPosition();
Tweener.addTween(this.workspaces.actor,
{ x: posX,
y: posY,
scaleX: scale,
scaleY: scale,
transition: 'easeOutQuad',
time: ANIMATION_TIME,
onComplete: this._hideDone,
onCompleteScope: this
});
// Make other elements fade out.
Tweener.addTween(this._group,
{ opacity: 0,
transition: 'easeOutQuad',
time: ANIMATION_TIME
});
this._coverPane.raise_top();
this._coverPane.show();
this.emit('hiding');
},
toggle: function() {
if (this.visible)
this.hide();
else
this.show();
},
/**
* getWorkspacesForWindow:
* @metaWindow: A #MetaWindow
*
* Returns the Workspaces object associated with the given window.
* This method is not be accessible if the overview is not open
* and will return %null.
*/
getWorkspacesForWindow: function(metaWindow) {
return this.workspaces;
},
//// Private methods ////
_showDone: function() {
if (this._hideInProgress)
return;
this.animationInProgress = false;
this._desktopFade.hide();
this._coverPane.hide();
this._coverPane.lower_bottom();
this.emit('shown');
// Handle any calls to hide* while we were showing
if (!this._shown && !this._shownTemporarily)
this._animateNotVisible();
this._syncInputMode();
global.sync_pointer();
},
_hideDone: function() {
@ -703,6 +438,8 @@ Overview.prototype = {
this.workspaces = null;
this._workspacesDisplay.hide();
this.viewSelector.hide();
this._dash.hide();
this._desktopFade.hide();
this._background.hide();
@ -712,20 +449,10 @@ Overview.prototype = {
this.animationInProgress = false;
this._hideInProgress = false;
this._coverPane.hide();
this._coverPane.lower_bottom();
Main.popModal(this.viewSelector.actor);
this.emit('hidden');
// Handle any calls to show* while we were hiding
if (this._shown || this._shownTemporarily)
this._animateVisible();
this._syncInputMode();
// Fake a pointer event if requested
if (this._needsFakePointerEvent) {
this._fakePointerEvent();
this._needsFakePointerEvent = false;
}
}
};
Signals.addSignalMethods(Overview.prototype);

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,7 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter;
const Gtk = imports.gi.Gtk;
const St = imports.gi.St;
const Lang = imports.lang;
const PopupMenu = imports.ui.popupMenu;
const Main = imports.ui.main;
@ -22,63 +20,40 @@ Button.prototype = {
track_hover: true });
this.actor._delegate = this;
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress));
this.menu = new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP, 0);
this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPress));
this.menu = new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP, /* FIXME */ 0);
this.menu.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged));
this.menu.actor.connect('key-press-event', Lang.bind(this, this._onMenuKeyPress));
Main.chrome.addActor(this.menu.actor, { affectsStruts: false });
Main.chrome.addActor(this.menu.actor, { visibleInOverview: true,
affectsStruts: false });
this.menu.actor.hide();
},
_onButtonPress: function(actor, event) {
if (!this.menu.isOpen) {
// Setting the max-height won't do any good if the minimum height of the
// menu is higher then the screen; it's useful if part of the menu is
// scrollable so the minimum height is smaller than the natural height
let monitor = global.get_primary_monitor();
this.menu.actor.style = ('max-height: ' +
Math.round(monitor.height - Main.panel.actor.height) +
'px;');
}
this.menu.toggle();
},
_onSourceKeyPress: function(actor, event) {
_onKeyPress: function(actor, event) {
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
this.menu.toggle();
return true;
} else if (symbol == Clutter.KEY_Escape && this.menu.isOpen) {
this.menu.close();
return true;
} else if (symbol == Clutter.KEY_Down) {
if (!this.menu.isOpen)
this.menu.toggle();
this.menu.actor.navigate_focus(this.actor, Gtk.DirectionType.DOWN, false);
this.menu.activateFirst();
return true;
} else
return false;
},
_onMenuKeyPress: function(actor, event) {
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
let focusManager = St.FocusManager.get_for_stage(global.stage);
let group = focusManager.get_group(this.actor);
if (group) {
let direction = (symbol == Clutter.KEY_Left) ? Gtk.DirectionType.LEFT : Gtk.DirectionType.RIGHT;
group.navigate_focus(this.actor, direction, false);
return true;
}
}
return false;
},
_onOpenStateChanged: function(menu, open) {
if (open)
this.actor.add_style_pseudo_class('active');
else
this.actor.remove_style_pseudo_class('active');
if (open) {
this.actor.add_style_pseudo_class('pressed');
let focus = global.stage.get_key_focus();
if (!focus || (focus != this.actor && !menu.contains(focus)))
this.actor.grab_key_focus();
} else
this.actor.remove_style_pseudo_class('pressed');
}
};
@ -96,7 +71,7 @@ SystemStatusButton.prototype = {
__proto__: Button.prototype,
_init: function(iconName,tooltipText) {
Button.prototype._init.call(this, 0.0);
Button.prototype._init.call(this, St.Align.START);
this._iconActor = new St.Icon({ icon_name: iconName,
icon_type: St.IconType.SYMBOLIC,
style_class: 'system-status-icon' });

View File

@ -1,5 +1,6 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const GConf = imports.gi.GConf;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Shell = imports.gi.Shell;
@ -7,12 +8,17 @@ const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Signals = imports.signals;
const St = imports.gi.St;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const DND = imports.ui.dnd;
const Main = imports.ui.main;
const Params = imports.misc.params;
const Search = imports.ui.search;
const Util = imports.misc.util;
const NAUTILUS_PREFS_DIR = '/apps/nautilus/preferences';
const DESKTOP_IS_HOME_KEY = NAUTILUS_PREFS_DIR + '/desktop_is_home_dir';
const PLACES_ICON_SIZE = 16;
/**
* Represents a place object, which is most normally a bookmark entry,
@ -57,21 +63,6 @@ PlaceInfo.prototype = {
}
};
// Helper function to translate launch parameters into a GAppLaunchContext
function _makeLaunchContext(params)
{
params = Params.parse(params, { workspace: null,
timestamp: null });
let launchContext = global.create_app_launch_context();
if (params.workspace != null)
launchContext.set_desktop(params.workspace.index());
if (params.timestamp != null)
launchContext.set_timestamp(params.timestamp);
return launchContext;
}
function PlaceDeviceInfo(mount) {
this._init(mount);
}
@ -91,9 +82,9 @@ PlaceDeviceInfo.prototype = {
return St.TextureCache.get_default().load_gicon(null, icon, size);
},
launch: function(params) {
launch: function() {
Gio.app_info_launch_default_for_uri(this._mount.get_root().get_uri(),
_makeLaunchContext(params));
global.create_app_launch_context());
},
isRemovable: function() {
@ -125,15 +116,20 @@ PlaceDeviceInfo.prototype = {
}
};
function PlacesManager() {
this._init();
}
PlacesManager.prototype = {
_init: function() {
let gconf = GConf.Client.get_default();
gconf.add_dir(NAUTILUS_PREFS_DIR, GConf.ClientPreloadType.PRELOAD_NONE);
this._defaultPlaces = [];
this._mounts = [];
this._bookmarks = [];
this._isDesktopHome = gconf.get_bool(DESKTOP_IS_HOME_KEY);
let homeFile = Gio.file_new_for_path (GLib.get_home_dir());
let homeUri = homeFile.get_uri();
@ -143,8 +139,8 @@ PlacesManager.prototype = {
function(size) {
return St.TextureCache.get_default().load_gicon(null, homeIcon, size);
},
function(params) {
Gio.app_info_launch_default_for_uri(homeUri, _makeLaunchContext(params));
function() {
Gio.app_info_launch_default_for_uri(homeUri, global.create_app_launch_context());
});
let desktopPath = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP);
@ -156,8 +152,8 @@ PlacesManager.prototype = {
function(size) {
return St.TextureCache.get_default().load_gicon(null, desktopIcon, size);
},
function(params) {
Gio.app_info_launch_default_for_uri(desktopUri, _makeLaunchContext(params));
function() {
Gio.app_info_launch_default_for_uri(desktopUri, global.create_app_launch_context());
});
this._connect = new PlaceInfo('special:connect', _("Connect to..."),
@ -166,16 +162,40 @@ PlacesManager.prototype = {
icon_type: St.IconType.FULLCOLOR,
icon_size: size });
},
function (params) {
// BUG: nautilus-connect-server doesn't have a desktop file, so we can't
// launch it with the workspace from params. It's probably pretty rare
// and odd to drag this place onto a workspace in any case
Util.spawn(['nautilus-connect-server']);
function () {
new Shell.Process({ args: ['nautilus-connect-server'] }).run();
});
let networkApp = null;
try {
networkApp = Shell.AppSystem.get_default().load_from_desktop_file('gnome-network-scheme.desktop');
} catch(e) {
try {
networkApp = Shell.AppSystem.get_default().load_from_desktop_file('network-scheme.desktop');
} catch(e) {
log('Cannot create "Network" item, .desktop file not found or corrupt.');
}
}
if (networkApp != null) {
this._network = new PlaceInfo('special:network', networkApp.get_name(),
function(size) {
return networkApp.create_icon_texture(size);
},
function () {
networkApp.launch();
});
}
this._defaultPlaces.push(this._home);
this._defaultPlaces.push(this._desktopMenu);
this._desktopMenuIndex = this._defaultPlaces.length;
if (!this._isDesktopHome)
this._defaultPlaces.push(this._desktopMenu);
if (this._network)
this._defaultPlaces.push(this._network);
this._defaultPlaces.push(this._connect);
/*
@ -209,6 +229,9 @@ PlacesManager.prototype = {
}));
this._reloadBookmarks();
gconf.notify_add(DESKTOP_IS_HOME_KEY, Lang.bind(this, this._updateDesktopMenuVisibility));
},
_updateDevices: function() {
@ -268,7 +291,10 @@ PlacesManager.prototype = {
if (!GLib.file_test(this._bookmarksPath, GLib.FileTest.EXISTS))
return;
let bookmarksContent = Shell.get_file_contents_utf8_sync(this._bookmarksPath);
let [success, bookmarksContent, len] = GLib.file_get_contents(this._bookmarksPath);
if (!success)
return;
let bookmarks = bookmarksContent.split('\n');
@ -303,8 +329,8 @@ PlacesManager.prototype = {
function(size) {
return St.TextureCache.get_default().load_gicon(null, icon, size);
},
function(params) {
Gio.app_info_launch_default_for_uri(bookmark, _makeLaunchContext(params));
function() {
Gio.app_info_launch_default_for_uri(bookmark, global.create_app_launch_context());
});
this._bookmarks.push(item);
}
@ -314,6 +340,21 @@ PlacesManager.prototype = {
this.emit('places-updated');
},
_updateDesktopMenuVisibility: function() {
let gconf = GConf.Client.get_default();
this._isDesktopHome = gconf.get_boolean(DESKTOP_IS_HOME_KEY);
if (this._isDesktopHome)
this._removeById(this._defaultPlaces, 'special:desktop');
else
this._defaultPlaces.splice(this._desktopMenuIndex, 0,
this._desktopMenu);
/* See comment in _updateDevices for explanation why there are two signals. */
this.emit('defaults-updated');
this.emit('places-updated');
},
_addMount: function(mount) {
let devItem = new PlaceDeviceInfo(mount);
this._mounts.push(devItem);
@ -361,8 +402,150 @@ PlacesManager.prototype = {
sourceArray.splice(this._lookupIndexById(sourceArray, id), 1);
}
};
Signals.addSignalMethods(PlacesManager.prototype);
/**
* An entry in the places menu.
* @info The corresponding PlaceInfo to populate this entry.
*/
function DashPlaceDisplayItem(info) {
this._init(info);
}
DashPlaceDisplayItem.prototype = {
_init: function(info) {
this.name = info.name;
this._info = info;
this._icon = info.iconFactory(PLACES_ICON_SIZE);
this.actor = new St.Clickable({ style_class: 'places-item',
reactive: true,
x_align: St.Align.START,
x_fill: true });
let box = new St.BoxLayout({ style_class: 'places-item-box' });
this.actor.set_child(box);
let bin = new St.Bin({ child: this._icon });
box.add(bin);
let text = new St.Label({ text: info.name });
box.add(text, { expand: true, x_fill: true });
if (info.isRemovable()) {
let removeIcon = new St.Icon({ icon_name: 'media-eject',
icon_type: St.IconType.FULLCOLOR,
icon_size: PLACES_ICON_SIZE });
let removeIconBox = new St.Clickable({ child: removeIcon,
reactive: true });
box.add(removeIconBox);
removeIconBox.connect('clicked',
Lang.bind(this, function() {
this._info.remove();
}));
}
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
this.actor._delegate = this;
this._draggable = DND.makeDraggable(this.actor);
this._draggable.connect('drag-begin',
Lang.bind(this, function() {
Main.overview.beginItemDrag(this);
}));
this._draggable.connect('drag-end',
Lang.bind(this, function() {
Main.overview.endItemDrag(this);
}));
},
_onClicked: function(b) {
this._info.launch();
Main.overview.hide();
},
getDragActorSource: function() {
return this._icon;
},
getDragActor: function(stageX, stageY) {
return this._info.iconFactory(PLACES_ICON_SIZE);
},
//// Drag and drop methods ////
shellWorkspaceLaunch: function() {
this._info.launch();
}
};
function DashPlaceDisplay() {
this._init();
}
DashPlaceDisplay.prototype = {
_init: function() {
// Places is divided semi-arbitrarily into left and right; a grid would
// look better in that there would be an even number of items left+right,
// but it seems like we want some sort of differentiation between actions
// like "Connect to server..." and regular folders
this.actor = new St.Table({ style_class: 'places-section',
homogeneous: true });
this._defaultsList = [];
this._bookmarksList = [];
this._mountsList = [];
Main.placesManager.connect('defaults-updated', Lang.bind(this, this._updateDefaults));
Main.placesManager.connect('bookmarks-updated', Lang.bind(this, this._updateBookmarks));
Main.placesManager.connect('mounts-updated', Lang.bind(this, this._updateMounts));
this._updateDefaults();
this._updateMounts();
this._updateBookmarks();
},
_updateDefaults: function() {
for (let i = 0; i < this._defaultsList.length; i++)
this._defaultsList[i].destroy();
this._defaultsList = [];
let places = Main.placesManager.getDefaultPlaces();
for (let i = 0; i < places.length; i++) {
this._defaultsList[i] = new DashPlaceDisplayItem(places[i]).actor;
this.actor.add(this._defaultsList[i], {row: i, col: 0});
}
this._updateMounts();
},
_updateMounts: function() {
for (let i = 0; i < this._mountsList.length; i++)
this._mountsList[i].destroy();
this._mountsList = [];
let places = Main.placesManager.getMounts();
for (let i = 0; i < places.length; i++) {
this._mountsList[i] = new DashPlaceDisplayItem(places[i]).actor;
this.actor.add(this._mountsList[i], {row: this._defaultsList.length + i, col: 0});
}
},
_updateBookmarks: function() {
for (let i = 0; i < this._bookmarksList.length; i++)
this._bookmarksList[i].destroy();
this._bookmarksList = [];
let places = Main.placesManager.getBookmarks();
for (let i = 0; i < places.length; i ++) {
this._bookmarksList[i] = new DashPlaceDisplayItem(places[i]).actor;
this.actor.add(this._bookmarksList[i], {row: i, col: 1});
}
}
};
Signals.addSignalMethods(DashPlaceDisplay.prototype);
function PlaceSearchProvider() {
this._init();
@ -381,15 +564,12 @@ PlaceSearchProvider.prototype = {
return null;
return { 'id': resultId,
'name': placeInfo.name,
'createIcon': function(size) {
return placeInfo.iconFactory(size);
}
};
'icon': placeInfo.iconFactory(Search.RESULT_ICON_SIZE) };
},
activateResult: function(id, params) {
activateResult: function(id) {
let placeInfo = Main.placesManager.lookupPlaceById(id);
placeInfo.launch(params);
placeInfo.launch();
},
_compareResultMeta: function (idA, idB) {

View File

@ -1,410 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*-
*
* Copyright 2010 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
const Lang = imports.lang;
const Signals = imports.signals;
const Shell = imports.gi.Shell;
const Clutter = imports.gi.Clutter;
const St = imports.gi.St;
const Pango = imports.gi.Pango;
const Gdm = imports.gi.Gdm;
const Gio = imports.gi.Gio;
const Mainloop = imports.mainloop;
const Polkit = imports.gi.Polkit;
const PolkitAgent = imports.gi.PolkitAgent;
const ModalDialog = imports.ui.modalDialog;
function AuthenticationDialog(actionId, message, cookie, userNames) {
this._init(actionId, message, cookie, userNames);
}
AuthenticationDialog.prototype = {
__proto__: ModalDialog.ModalDialog.prototype,
_init: function(actionId, message, cookie, userNames) {
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'polkit-dialog' });
this.actionId = actionId;
this.message = message;
this.userNames = userNames;
this._wasDismissed = false;
this._completed = false;
let mainContentBox = new St.BoxLayout({ style_class: 'polkit-dialog-main-layout',
vertical: false });
this.contentLayout.add(mainContentBox,
{ x_fill: true,
y_fill: true });
let icon = new St.Icon({ icon_name: 'dialog-password-symbolic' });
mainContentBox.add(icon,
{ x_fill: true,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.START });
let messageBox = new St.BoxLayout({ style_class: 'polkit-dialog-message-layout',
vertical: true });
mainContentBox.add(messageBox,
{ y_align: St.Align.START });
this._subjectLabel = new St.Label({ style_class: 'polkit-dialog-headline',
text: _("Authentication Required") });
messageBox.add(this._subjectLabel,
{ y_fill: false,
y_align: St.Align.START });
this._descriptionLabel = new St.Label({ style_class: 'polkit-dialog-description',
text: message });
this._descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._descriptionLabel.clutter_text.line_wrap = true;
messageBox.add(this._descriptionLabel,
{ y_fill: true,
y_align: St.Align.START });
if (userNames.length > 1) {
log('polkitAuthenticationAgent: Received ' + userNames.length +
' identities that can be used for authentication. Only ' +
'considering the first one.');
}
let userName = userNames[0];
this._user = Gdm.UserManager.ref_default().get_user(userName);
let userRealName = this._user.get_real_name()
this._userLoadedId = this._user.connect('notify::is_loaded',
Lang.bind(this, this._onUserChanged));
this._userChangedId = this._user.connect('changed',
Lang.bind(this, this._onUserChanged));
// Special case 'root'
let userIsRoot = false;
if (userName == 'root') {
userIsRoot = true;
userRealName = _("Administrator");
}
if (userIsRoot) {
let userLabel = new St.Label(({ style_class: 'polkit-dialog-user-root-label',
text: userRealName }));
messageBox.add(userLabel);
} else {
let userBox = new St.BoxLayout({ style_class: 'polkit-dialog-user-layout',
vertical: false });
messageBox.add(userBox);
this._userIcon = new St.Icon();
this._userIcon.hide();
userBox.add(this._userIcon,
{ x_fill: true,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.START });
let userLabel = new St.Label(({ style_class: 'polkit-dialog-user-label',
text: userRealName }));
userBox.add(userLabel,
{ x_fill: true,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
}
this._onUserChanged();
this._passwordBox = new St.BoxLayout({ vertical: false });
messageBox.add(this._passwordBox);
this._passwordLabel = new St.Label(({ style_class: 'polkit-dialog-password-label' }));
this._passwordBox.add(this._passwordLabel);
this._passwordEntry = new St.Entry({ style_class: 'polkit-dialog-password-entry',
text: "",
can_focus: true});
this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivate));
this._passwordBox.add(this._passwordEntry,
{expand: true });
this._passwordBox.hide();
this._errorMessageLabel = new St.Label({ style_class: 'polkit-dialog-error-label' });
this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._errorMessageLabel.clutter_text.line_wrap = true;
messageBox.add(this._errorMessageLabel);
this._errorMessageLabel.hide();
this._infoMessageLabel = new St.Label({ style_class: 'polkit-dialog-info-label' });
this._infoMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._infoMessageLabel.clutter_text.line_wrap = true;
messageBox.add(this._infoMessageLabel);
this._infoMessageLabel.hide();
/* text is intentionally non-blank otherwise the height is not the same as for
* infoMessage and errorMessageLabel - but it is still invisible because
* gnome-shell.css sets the color to be transparent
*/
this._nullMessageLabel = new St.Label({ style_class: 'polkit-dialog-null-label',
text: 'abc'});
this._nullMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._nullMessageLabel.clutter_text.line_wrap = true;
messageBox.add(this._nullMessageLabel);
this._nullMessageLabel.show();
this.setButtons([{ label: _("Cancel"),
action: Lang.bind(this, this.cancel),
key: Clutter.Escape
},
{ label: _("Authenticate"),
action: Lang.bind(this, this._onAuthenticateButtonPressed)
}]);
this._doneEmitted = false;
this._identityToAuth = Polkit.UnixUser.new_for_name(userName);
this._cookie = cookie;
this._session = new PolkitAgent.Session({ identity: this._identityToAuth,
cookie: this._cookie });
this._session.connect('completed', Lang.bind(this, this._onSessionCompleted));
this._session.connect('request', Lang.bind(this, this._onSessionRequest));
this._session.connect('show-error', Lang.bind(this, this._onSessionShowError));
this._session.connect('show-info', Lang.bind(this, this._onSessionShowInfo));
// Delay focus grab to avoid ModalDialog stealing focus with
// its buttons
this.connect('opened',
Lang.bind(this, function() {
this._passwordEntry.grab_key_focus();
}));
},
startAuthentication: function() {
this._session.initiate();
},
_ensureOpen: function() {
// NOTE: ModalDialog.open() is safe to call if the dialog is
// already open - it just returns true without side-effects
if (!this.open(global.get_current_time())) {
// This can fail if e.g. unable to get input grab
//
// In an ideal world this wouldn't happen (because the
// Shell is in complete control of the session) but that's
// just not how things work right now.
//
// One way to make this happen is by running 'sleep 3;
// pkexec bash' and then opening a popup menu.
//
// We could add retrying if this turns out to be a problem
log('polkitAuthenticationAgent: Failed to show modal dialog.' +
' Dismissing authentication request for action-id ' + this.actionId +
' cookie ' + this._cookie);
this._emitDone(false, true);
}
},
_emitDone: function(keepVisible, dismissed) {
if (!this._doneEmitted) {
this._doneEmitted = true;
this.emit('done', keepVisible, dismissed);
}
},
_onEntryActivate: function() {
let response = this._passwordEntry.get_text();
this._session.response(response);
// When the user responds, dismiss already shown info and
// error texts (if any)
this._errorMessageLabel.hide();
this._infoMessageLabel.hide();
this._nullMessageLabel.show();
},
_onAuthenticateButtonPressed: function() {
this._onEntryActivate();
},
_onSessionCompleted: function(session, gainedAuthorization) {
if (this._completed)
return;
this._completed = true;
if (!gainedAuthorization) {
/* Unless we are showing an existing error message from the PAM
* module (the PAM module could be reporting the authentication
* error providing authentication-method specific information),
* show "Sorry, that didn't work. Please try again."
*/
if (!this._errorMessageLabel.visible && !this._wasDismissed) {
/* Translators: "that didn't work" refers to the fact that the
* requested authentication was not gained; this can happen
* because of an authentication error (like invalid password),
* for instance. */
this._errorMessageLabel.set_text(_("Sorry, that didn\'t work. Please try again."));
this._errorMessageLabel.show();
this._infoMessageLabel.hide();
this._nullMessageLabel.hide();
}
}
this._emitDone(!gainedAuthorization, false);
},
_onSessionRequest: function(session, request, echo_on) {
// Cheap localization trick
if (request == 'Password:')
this._passwordLabel.set_text(_("Password:"));
else
this._passwordLabel.set_text(request);
if (echo_on)
this._passwordEntry.clutter_text.set_password_char('');
else
this._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
this._passwordBox.show();
this._passwordEntry.set_text('');
this._passwordEntry.grab_key_focus();
this._ensureOpen();
},
_onSessionShowError: function(session, text) {
this._passwordEntry.set_text('');
this._errorMessageLabel.set_text(text);
this._errorMessageLabel.show();
this._infoMessageLabel.hide();
this._nullMessageLabel.hide();
this._ensureOpen();
},
_onSessionShowInfo: function(session, text) {
this._passwordEntry.set_text('');
this._infoMessageLabel.set_text(text);
this._infoMessageLabel.show();
this._errorMessageLabel.hide();
this._nullMessageLabel.hide();
this._ensureOpen();
},
destroySession: function() {
if (this._session) {
if (!this._completed)
this._session.cancel();
this._session = null;
}
},
_onUserChanged: function() {
if (this._user.is_loaded) {
if (this._userIcon) {
let iconFileName = this._user.get_icon_file();
let iconFile = Gio.file_new_for_path(iconFileName);
let icon;
if (iconFile.query_exists(null)) {
icon = new Gio.FileIcon({file: iconFile});
} else {
icon = new Gio.ThemedIcon({name: 'avatar-default'});
}
this._userIcon.set_gicon (icon);
this._userIcon.show();
}
}
},
cancel: function() {
this._wasDismissed = true;
this.close(global.get_current_time());
this._emitDone(false, true);
},
};
Signals.addSignalMethods(AuthenticationDialog.prototype);
function AuthenticationAgent() {
this._init();
}
AuthenticationAgent.prototype = {
_init: function() {
this._native = new Shell.PolkitAuthenticationAgent();
this._native.connect('initiate', Lang.bind(this, this._onInitiate));
this._native.connect('cancel', Lang.bind(this, this._onCancel));
this._currentDialog = null;
this._isCompleting = false;
},
_onInitiate: function(nativeAgent, actionId, message, iconName, cookie, userNames) {
this._currentDialog = new AuthenticationDialog(actionId, message, cookie, userNames);
// We actually don't want to open the dialog until we know for
// sure that we're going to interact with the user. For
// example, if the password for the identity to auth is blank
// (which it will be on a live CD) then there will be no
// conversation at all... of course, we don't *know* that
// until we actually try it.
//
// See https://bugzilla.gnome.org/show_bug.cgi?id=643062 for more
// discussion.
this._currentDialog.connect('done', Lang.bind(this, this._onDialogDone));
this._currentDialog.startAuthentication();
},
_onCancel: function(nativeAgent) {
this._completeRequest(false, false);
},
_onDialogDone: function(dialog, keepVisible, dismissed) {
this._completeRequest(keepVisible, dismissed);
},
_reallyCompleteRequest: function(dismissed) {
this._currentDialog.close();
this._currentDialog.destroySession();
this._currentDialog = null;
this._isCompleting = false;
this._native.complete(dismissed)
},
_completeRequest: function(keepVisible, wasDismissed) {
if (this._isCompleting)
return;
this._isCompleting = true;
if (keepVisible) {
// Give the user 2 seconds to read 'Authentication Failure' before
// dismissing the dialog
Mainloop.timeout_add(2000,
Lang.bind(this,
function() {
this._reallyCompleteRequest(wasDismissed);
}));
} else {
this._reallyCompleteRequest(wasDismissed);
}
}
}
function init() {
let agent = new AuthenticationAgent();
}

File diff suppressed because it is too large Load Diff

View File

@ -8,26 +8,19 @@ const Meta = imports.gi.Meta;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const FileUtils = imports.misc.fileUtils;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog;
const Tweener = imports.ui.tweener;
const Util = imports.misc.util;
const History = imports.misc.history;
const MAX_FILE_DELETED_BEFORE_INVALID = 10;
const HISTORY_KEY = 'command-history';
const HISTORY_LIMIT = 512;
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
const DISABLE_COMMAND_LINE_KEY = 'disable-command-line';
const TERMINAL_SCHEMA = 'org.gnome.desktop.default-applications.terminal';
const EXEC_KEY = 'exec';
const EXEC_ARG_KEY = 'exec-arg';
const DIALOG_GROW_TIME = 0.1;
const DIALOG_FADE_TIME = 0.1;
function CommandCompleter() {
this._init();
@ -69,6 +62,25 @@ CommandCompleter.prototype = {
this._update(0);
},
_onGetEnumerateComplete : function(obj, res) {
this._enumerator = obj.enumerate_children_finish(res);
this._enumerator.next_files_async(100, GLib.PRIORITY_LOW, null, Lang.bind(this, this._onNextFileComplete));
},
_onNextFileComplete : function(obj, res) {
let files = obj.next_files_finish(res);
for (let i = 0; i < files.length; i++) {
this._childs[this._i].push(files[i].get_name());
}
if (files.length) {
this._enumerator.next_files_async(100, GLib.PRIORITY_LOW, null, Lang.bind(this, this._onNextFileComplete));
} else {
this._enumerator.close(null);
this._enumerator = null;
this._update(this._i + 1);
}
},
update : function() {
if (this._valid)
return;
@ -88,12 +100,7 @@ CommandCompleter.prototype = {
}
let file = Gio.file_new_for_path(this._paths[i]);
this._childs[this._i] = [];
FileUtils.listDirAsync(file, Lang.bind(this, function (files) {
for (let i = 0; i < files.length; i++) {
this._childs[this._i].push(files[i].get_name());
}
this._update(this._i + 1);
}));
file.enumerate_children_async(Gio.FILE_ATTRIBUTE_STANDARD_NAME, Gio.FileQueryInfoFlags.NONE, GLib.PRIORITY_LOW, null, Lang.bind(this, this._onGetEnumerateComplete));
},
_onChanged : function(m, f, of, type) {
@ -168,17 +175,22 @@ function RunDialog() {
}
RunDialog.prototype = {
__proto__: ModalDialog.ModalDialog.prototype,
_init : function() {
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'run-dialog' });
this._isOpen = false;
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
this._terminalSettings = new Gio.Settings({ schema: TERMINAL_SCHEMA });
global.settings.connect('changed::development-tools', Lang.bind(this, function () {
this._enableInternalCommands = global.settings.get_boolean('development-tools');
}));
this._enableInternalCommands = global.settings.get_boolean('development-tools');
this._history = global.settings.get_strv(HISTORY_KEY);
this._historyIndex = -1;
global.settings.connect('changed::' + HISTORY_KEY, Lang.bind(this, function() {
this._history = global.settings.get_strv(HISTORY_KEY);
this._historyIndex = this._history.length;
}));
this._internalCommands = { 'lg':
Lang.bind(this, function() {
Main.createLookingGlass().open();
@ -194,67 +206,76 @@ __proto__: ModalDialog.ModalDialog.prototype,
}),
'debugexit': Lang.bind(this, function() {
Meta.quit(Meta.ExitCode.ERROR);
}),
// rt is short for "reload theme"
'rt': Lang.bind(this, function() {
Main.loadTheme();
Meta.exit(Meta.ExitCode.ERROR);
})
};
// All actors are inside _group. We create it initially
// hidden then show it in show()
this._group = new Clutter.Group({ visible: false,
x: 0, y: 0 });
Main.uiGroup.add_actor(this._group);
this._lightbox = new Lightbox.Lightbox(this._group,
{ inhibitEvents: true });
this._box = new St.Bin({ x_align: St.Align.MIDDLE,
y_align: St.Align.MIDDLE });
this._group.add_actor(this._box);
this._lightbox.highlight(this._box);
let dialogBox = new St.BoxLayout({ style_class: 'run-dialog', vertical: true });
this._box.set_child(dialogBox);
let label = new St.Label({ style_class: 'run-dialog-label',
text: _("Please enter a command:") });
this.contentLayout.add(label, { y_align: St.Align.START });
dialogBox.add(label, { expand: true, y_fill: false });
let entry = new St.Entry({ style_class: 'run-dialog-entry' });
this._entryText = entry.clutter_text;
this.contentLayout.add(entry, { y_align: St.Align.START });
this.setInitialKeyFocus(this._entryText);
dialogBox.add(entry, { expand: true });
this._errorBox = new St.BoxLayout({ style_class: 'run-dialog-error-box' });
this._errorBox = new St.BoxLayout();
this.contentLayout.add(this._errorBox, { expand: true });
dialogBox.add(this._errorBox, { expand: true });
let errorIcon = new St.Icon({ icon_name: 'dialog-error', icon_size: 24, style_class: 'run-dialog-error-icon' });
let errorIcon = new St.Button({ style_class: 'run-dialog-error-icon' });
this._errorBox.add(errorIcon, { y_align: St.Align.MIDDLE });
this._errorBox.add(errorIcon);
this._commandError = false;
this._errorMessage = new St.Label({ style_class: 'run-dialog-error-label' });
this._errorMessage.clutter_text.line_wrap = true;
this._errorBox.add(this._errorMessage, { expand: true,
y_align: St.Align.MIDDLE,
y_fill: false });
this._errorBox.add(this._errorMessage, { expand: true });
this._errorBox.hide();
this._pathCompleter = new Gio.FilenameCompleter();
this._commandCompleter = new CommandCompleter();
this._group.connect('notify::visible', Lang.bind(this._commandCompleter, this._commandCompleter.update));
this._history = new History.HistoryManager({ gsettingsKey: HISTORY_KEY,
entry: this._entryText });
this._entryText.connect('key-press-event', Lang.bind(this, function(o, e) {
let symbol = e.get_key_symbol();
if (symbol == Clutter.Down) {
this._setCommandFromHistory(this._historyIndex++);
return true;
}
if (symbol == Clutter.Up) {
this._setCommandFromHistory(this._historyIndex--);
return true;
}
if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) {
this.popModal();
if (Shell.get_event_state(e) & Clutter.ModifierType.CONTROL_MASK)
this._run(o.get_text(), true);
else
this._run(o.get_text(), false);
if (!this._commandError)
this.close();
else {
if (!this.pushModal())
this.close();
}
return true;
}
if (symbol == Clutter.Escape) {
this.close();
@ -300,10 +321,22 @@ __proto__: ModalDialog.ModalDialog.prototype,
}
},
_saveHistory : function() {
if (this._history.length > HISTORY_LIMIT) {
this._history.splice(0, this._history.length - HISTORY_LIMIT);
}
global.settings.set_strv(HISTORY_KEY, this._history);
},
_run : function(input, inTerminal) {
let command = input;
this._history.addItem(input);
if (this._history.length == 0 ||
this._history[this._history.length - 1] != input) {
this._history.push(input);
this._saveHistory();
}
this._commandError = false;
let f;
if (this._enableInternalCommands)
@ -314,12 +347,11 @@ __proto__: ModalDialog.ModalDialog.prototype,
f();
} else if (input) {
try {
if (inTerminal) {
let exec = this._terminalSettings.get_string(EXEC_KEY);
let exec_arg = this._terminalSettings.get_string(EXEC_ARG_KEY);
command = exec + ' ' + exec_arg + ' ' + input;
}
Util.trySpawnCommandLine(command);
if (inTerminal)
command = 'gnome-terminal -x ' + input;
let [ok, len, args] = GLib.shell_parse_argv(command);
let p = new Shell.Process({ 'args' : args });
p.run();
} catch (e) {
// Mmmh, that failed - see if @input matches an existing file
let path = null;
@ -337,40 +369,84 @@ __proto__: ModalDialog.ModalDialog.prototype,
global.create_app_launch_context());
} else {
this._commandError = true;
// The exception contains an error string like:
// Error invoking Shell.run: Failed to execute child
// process "foo" (No such file or directory)
// We are only interested in the actual error, so parse
//that out.
let m = /.+\((.+)\)/.exec(e);
let errorStr = _("Execution of '%s' failed:").format(command) + '\n' + m[1];
this._errorMessage.set_text(errorStr);
this._errorMessage.set_text(e.message);
if (!this._errorBox.visible) {
let [errorBoxMinHeight, errorBoxNaturalHeight] = this._errorBox.get_preferred_height(-1);
let parentActor = this._errorBox.get_parent();
Tweener.addTween(parentActor,
{ height: parentActor.height + errorBoxNaturalHeight,
time: DIALOG_GROW_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
parentActor.set_height(-1);
this._errorBox.show();
})
});
}
this._errorBox.show();
}
}
}
},
open: function() {
this._history.lastItem();
this._errorBox.hide();
this._entryText.set_text('');
this._commandError = false;
_setCommandFromHistory: function(lastI) {
if (this._historyIndex < 0)
this._historyIndex = 0;
if (this._historyIndex > this._history.length)
this._historyIndex = this._history.length;
if (this._lockdownSettings.get_boolean(DISABLE_COMMAND_LINE_KEY))
return;
ModalDialog.ModalDialog.prototype.open.call(this);
let text = this._entryText.get_text();
if (text) {
this._history[lastI] = text;
}
if (this._history[this._historyIndex]) {
this._entryText.set_text(this._history[this._historyIndex]);
} else
this._entryText.set_text('');
},
open : function() {
if (this._isOpen) // Already shown
return;
if (!Main.pushModal(this._group))
return;
// Position the dialog on the current monitor
let monitor = global.get_focus_monitor();
this._historyIndex = this._history.length;
this._box.set_position(monitor.x, monitor.y);
this._box.set_size(monitor.width, monitor.height);
this._isOpen = true;
this._lightbox.show();
this._group.opacity = 0;
this._group.show();
Tweener.addTween(this._group,
{ opacity: 255,
time: DIALOG_FADE_TIME,
transition: 'easeOutQuad'
});
global.stage.set_key_focus(this._entryText);
},
close : function() {
if (!this._isOpen)
return;
this._isOpen = false;
this._commandError = false;
Main.popModal(this._group);
Tweener.addTween(this._group,
{ opacity: 0,
time: DIALOG_FADE_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this._errorBox.hide();
this._group.hide();
this._entryText.set_text('');
})
});
}
};
Signals.addSignalMethods(RunDialog.prototype);

View File

@ -1,6 +1,5 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const DBus = imports.dbus;
const Gio = imports.gi.Gio;
const Mainloop = imports.mainloop;
@ -69,104 +68,6 @@ function waitLeisure() {
};
}
const PerfHelperIface = {
name: 'org.gnome.Shell.PerfHelper',
methods: [{ name: 'CreateWindow', inSignature: 'iibb', outSignature: '' },
{ name: 'WaitWindows', inSignature: '', outSignature: '' },
{ name: 'DestroyWindows', inSignature: '', outSignature: ''}]
};
const PerfHelper = function () {
this._init();
};
PerfHelper.prototype = {
_init: function() {
DBus.session.proxifyObject(this, 'org.gnome.Shell.PerfHelper', '/org/gnome/Shell/PerfHelper');
}
};
DBus.proxifyPrototype(PerfHelper.prototype, PerfHelperIface);
let _perfHelper = null;
function _getPerfHelper() {
if (_perfHelper == null)
_perfHelper = new PerfHelper();
return _perfHelper;
}
/**
* createTestWindow:
* @width: width of window, in pixels
* @height: height of window, in pixels
* @alpha: whether the window should be alpha transparent
* @maximized: whethe the window should be created maximized
*
* Creates a window using gnome-shell-perf-helper for testing purposes.
* While this function can be used with yield in an automation
* script to pause until the D-Bus call to the helper process returns,
* because of the normal X asynchronous mapping process, to actually wait
* until the window has been mapped and exposed, use waitTestWindows().
*/
function createTestWindow(width, height, alpha, maximized) {
let cb;
let perfHelper = _getPerfHelper();
perfHelper.CreateWindowRemote(width, height, alpha, maximized,
function(result, excp) {
if (cb)
cb();
});
return function(callback) {
cb = callback;
};
}
/**
* waitTestWindows:
*
* Used within an automation script to pause until all windows previously
* created with createTestWindow have been mapped and exposed.
*/
function waitTestWindows() {
let cb;
let perfHelper = _getPerfHelper();
perfHelper.WaitWindowsRemote(function(result, excp) {
if (cb)
cb();
});
return function(callback) {
cb = callback;
};
}
/**
* destroyTestWindows:
*
* Destroys all windows previously created with createTestWindow().
* While this function can be used with yield in an automation
* script to pause until the D-Bus call to the helper process returns,
* this doesn't guarantee that Mutter has actually finished the destroy
* process because of normal X asynchronicity.
*/
function destroyTestWindows() {
let cb;
let perfHelper = _getPerfHelper();
perfHelper.DestroyWindowsRemote(function(result, excp) {
if (cb)
cb();
});
return function(callback) {
cb = callback;
};
}
/**
* defineScriptEvent
* @name: The event will be called script.<name>
@ -246,8 +147,8 @@ function _collect(scriptModule, outputFile) {
Shell.write_string_to_stream(out, '"events":\n');
Shell.PerfLog.get_default().dump_events(out);
let monitors = global.get_monitors();
let primary = global.get_primary_monitor();
let monitors = global.get_monitors()
let primary = global.get_primary_monitor()
Shell.write_string_to_stream(out, ',\n"monitors":\n[');
for (let i = 0; i < monitors.length; i++) {
let monitor = monitors[i];
@ -266,21 +167,7 @@ function _collect(scriptModule, outputFile) {
Shell.write_string_to_stream(out, ',\n"metrics":\n[ ');
let first = true;
for (let name in scriptModule.METRICS) {
let metric = scriptModule.METRICS[name];
// Extra checks here because JSON.stringify generates
// invalid JSON for undefined values
if (metric.description == null) {
log("Error: No description found for metric " + name);
continue;
}
if (metric.units == null) {
log("Error: No units found for metric " + name);
continue;
}
if (metric.value == null) {
log("Error: No value found for metric " + name);
continue;
}
let metric = scriptModule.METRICS[name];
if (!first)
Shell.write_string_to_stream(out, ',\n ');

View File

@ -1,16 +1,8 @@
/* -*- mode: js2; js2-basic-offset: 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 DISABLED_OPEN_SEARCH_PROVIDERS_KEY = 'disabled-open-search-providers';
const RESULT_ICON_SIZE = 48;
// Not currently referenced by the search API, but
// this enumeration can be useful for provider
@ -157,12 +149,11 @@ SearchProvider.prototype = {
},
/**
* getResultMeta:
* getResultInfo:
* @id: Result identifier string
*
* Return an object with 'id', 'name', (both strings) and 'createIcon'
* (function(size) returning a Clutter.Texture) properties which describe
* the given search result.
* Return an object with 'id', 'name', (both strings) and 'icon' (Clutter.Texture)
* properties which describe the given search result.
*/
getResultMeta: function(id) {
throw new Error('Not implemented');
@ -205,105 +196,21 @@ SearchProvider.prototype = {
*/
activateResult: function(id) {
throw new Error('Not implemented');
},
/**
* expandSearch:
*
* Called when the user clicks on the header for this
* search section. Should typically launch an external program
* displaying search results for that item type.
*/
expandSearch: function(terms) {
throw new Error('Not implemented');
}
};
Signals.addSignalMethods(SearchProvider.prototype);
function OpenSearchSystem() {
this._init();
}
OpenSearchSystem.prototype = {
_init: function() {
this._providers = [];
global.settings.connect('changed::' + DISABLED_OPEN_SEARCH_PROVIDERS_KEY, Lang.bind(this, this._refresh));
this._refresh();
},
getProviders: function() {
let res = [];
for (let i = 0; i < this._providers.length; i++)
res.push({ id: i, name: this._providers[i].name });
return res;
},
setSearchTerms: function(terms) {
this._terms = terms;
},
_checkSupportedProviderLanguage: function(provider) {
if (provider.url.search(/{language}/) == -1)
return true;
let langs = GLib.get_language_names();
langs.push('en');
let lang = null;
for (let i = 0; i < langs.length; i++) {
for (let k = 0; k < provider.langs.length; k++) {
if (langs[i] == provider.langs[k])
lang = langs[i];
}
if (lang)
break;
}
provider.lang = lang;
return lang != null;
},
activateResult: function(id, params) {
let searchTerms = this._terms.join(' ');
let url = this._providers[id].url.replace('{searchTerms}', encodeURIComponent(searchTerms));
if (url.match('{language}'))
url = url.replace('{language}', this._providers[id].lang);
try {
Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context());
} catch (e) {
// TODO: remove this after glib will be removed from moduleset
// In the default jhbuild, gio is in our prefix but gvfs is not
Util.spawn(['gvfs-open', url])
}
Main.overview.hide();
},
_addProvider: function(fileName) {
let path = global.datadir + '/search_providers/' + fileName;
let source = Shell.get_file_contents_utf8_sync(path);
let [success, name, url, langs, icon_uri] = Shell.parse_search_provider(source);
let provider ={ name: name,
url: url,
id: this._providers.length,
icon_uri: icon_uri,
langs: langs };
if (this._checkSupportedProviderLanguage(provider)) {
this._providers.push(provider);
this.emit('changed');
}
},
_refresh: function() {
this._providers = [];
let names = global.settings.get_strv(DISABLED_OPEN_SEARCH_PROVIDERS_KEY);
let file = Gio.file_new_for_path(global.datadir + '/search_providers');
FileUtils.listDirAsync(file, Lang.bind(this, function(files) {
for (let i = 0; i < files.length; i++) {
let enabled = true;
let name = files[i].get_name();
for (let k = 0; k < names.length; k++)
if (names[k] == name)
enabled = false;
if (enabled)
this._addProvider(name);
}
}));
}
}
Signals.addSignalMethods(OpenSearchSystem.prototype);
function SearchSystem() {
this._init();
}
@ -334,7 +241,7 @@ SearchSystem.prototype = {
updateSearch: function(searchString) {
searchString = searchString.replace(/^\s+/g, '').replace(/\s+$/g, '');
if (searchString == '')
return [];
return null;
let terms = searchString.split(/\s+/);
let isSubSearch = terms.length == this._previousTerms.length;

View File

@ -2,17 +2,17 @@
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const Gtk = imports.gi.Gtk;
const Meta = imports.gi.Meta;
const St = imports.gi.St;
const DND = imports.ui.dnd;
const IconGrid = imports.ui.iconGrid;
const Main = imports.ui.main;
const Overview = imports.ui.overview;
const Search = imports.ui.search;
const MAX_SEARCH_RESULTS_ROWS = 1;
const MAX_SEARCH_RESULTS_ROWS = 2;
function SearchResult(provider, metaInfo, terms) {
@ -23,12 +23,11 @@ SearchResult.prototype = {
_init: function(provider, metaInfo, terms) {
this.provider = provider;
this.metaInfo = metaInfo;
this.actor = new St.Button({ style_class: 'search-result',
reactive: true,
x_align: St.Align.START,
y_fill: true });
this.actor = new St.Clickable({ style_class: 'search-result',
reactive: true,
x_align: St.Align.START,
y_fill: true });
this.actor._delegate = this;
this._dragActorSource = null;
let content = provider.createResultActor(metaInfo, terms);
if (content == null) {
@ -36,13 +35,10 @@ SearchResult.prototype = {
reactive: true,
track_hover: true });
let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
{ createIcon: this.metaInfo['createIcon'] });
{ createIcon: Lang.bind(this, function(size) {
return this.metaInfo['icon'];
})});
content.set_child(icon.actor);
this._dragActorSource = icon.icon;
this.actor.label_actor = icon.label;
} else {
if (content._delegate && content._delegate.getDragActorSource)
this._dragActorSource = content._delegate.getDragActorSource();
}
this._content = content;
this.actor.set_child(content);
@ -54,10 +50,6 @@ SearchResult.prototype = {
Lang.bind(this, function() {
Main.overview.beginItemDrag(this);
}));
draggable.connect('drag-cancelled',
Lang.bind(this, function() {
Main.overview.cancelledItemDrag(this);
}));
draggable.connect('drag-end',
Lang.bind(this, function() {
Main.overview.endItemDrag(this);
@ -76,26 +68,23 @@ SearchResult.prototype = {
Main.overview.toggle();
},
_onResultClicked: function(actor) {
_onResultClicked: function(actor, event) {
this.activate();
},
getDragActorSource: function() {
if (this._dragActorSource)
return this._dragActorSource;
// not exactly right, but alignment problems are hard to notice
return this._content;
return this.metaInfo['icon'];
},
getDragActor: function(stageX, stageY) {
return this.metaInfo['createIcon'](Main.overview.dash.iconSize);
return new Clutter.Clone({ source: this.metaInfo['icon'] });
},
shellWorkspaceLaunch: function(params) {
shellWorkspaceLaunch: function() {
if (this.provider.dragActivateResult)
this.provider.dragActivateResult(this.metaInfo.id, params);
this.provider.dragActivateResult(this.metaInfo.id);
else
this.provider.activateResult(this.metaInfo.id, params);
this.provider.activateResult(this.metaInfo.id);
}
};
@ -112,30 +101,8 @@ GridSearchResults.prototype = {
this._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.selectionIndex = -1;
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() {
this._tryAddResults();
}));
}));
this._notDisplayedResult = [];
this._terms = [];
},
_tryAddResults: function() {
let canDisplay = this._grid.childrenInRow(this._width) * MAX_SEARCH_RESULTS_ROWS
- this._grid.visibleItemsCount();
for (let i = Math.min(this._notDisplayedResult.length, canDisplay); i > 0; i--) {
let result = this._notDisplayedResult.shift();
let meta = this.provider.getResultMeta(result);
let display = new SearchResult(this.provider, meta, this._terms);
this._grid.addItem(display.actor);
}
},
getVisibleResultCount: function() {
@ -143,15 +110,15 @@ GridSearchResults.prototype = {
},
renderResults: function(results, terms) {
// copy the lists
this._notDisplayedResult = results.slice(0);
this._terms = terms.slice(0);
this._tryAddResults();
for (let i = 0; i < results.length; i++) {
let result = results[i];
let meta = this.provider.getResultMeta(result);
let display = new SearchResult(this.provider, meta, terms);
this._grid.addItem(display.actor);
}
},
clear: function () {
this._terms = [];
this._notDisplayedResult = [];
this._grid.removeAll();
this.selectionIndex = -1;
},
@ -182,41 +149,28 @@ GridSearchResults.prototype = {
};
function SearchResults(searchSystem, openSearchSystem) {
this._init(searchSystem, openSearchSystem);
function SearchResults(searchSystem) {
this._init(searchSystem);
}
SearchResults.prototype = {
_init: function(searchSystem, openSearchSystem) {
_init: function(searchSystem) {
this._searchSystem = searchSystem;
this._openSearchSystem = openSearchSystem;
this.actor = new St.BoxLayout({ name: 'searchResults',
vertical: true });
this.actor = new St.Bin({ name: 'searchResults',
y_align: St.Align.START,
x_align: St.Align.START,
x_fill: true });
this._content = new St.BoxLayout({ name: 'searchResultsContent',
vertical: true });
let scrollView = new St.ScrollView({ x_fill: true,
y_fill: false,
style_class: 'vfade' });
vshadows: true });
scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
scrollView.add_actor(this._content);
this.actor.add(scrollView, { x_fill: true,
y_fill: false,
expand: true,
x_align: St.Align.START,
y_align: St.Align.START });
this.actor.connect('notify::mapped', Lang.bind(this,
function() {
if (!this.actor.mapped)
return;
let adjustment = scrollView.vscroll.adjustment;
let direction = Overview.SwipeScrollDirection.VERTICAL;
Main.overview.setScrollAdjustment(adjustment, direction);
}));
this.actor.set_child(scrollView);
this._statusText = new St.Label({ style_class: 'search-statustext' });
this._content.add(this._statusText);
@ -225,60 +179,23 @@ SearchResults.prototype = {
this._providerMeta = [];
for (let i = 0; i < this._providers.length; i++)
this.createProviderMeta(this._providers[i]);
this._searchProvidersBox = new St.BoxLayout({ style_class: 'search-providers-box' });
this.actor.add(this._searchProvidersBox);
this._openSearchProviders = [];
this._openSearchSystem.connect('changed', Lang.bind(this, this._updateOpenSearchProviderButtons));
this._updateOpenSearchProviderButtons();
},
_updateOpenSearchProviderButtons: function() {
this._selectedOpenSearchButton = -1;
for (let i = 0; i < this._openSearchProviders.length; i++)
this._openSearchProviders[i].actor.destroy();
this._openSearchProviders = this._openSearchSystem.getProviders();
for (let i = 0; i < this._openSearchProviders.length; i++)
this._createOpenSearchProviderButton(this._openSearchProviders[i]);
},
_updateOpenSearchButtonState: function() {
for (let i = 0; i < this._openSearchProviders.length; i++) {
if (i == this._selectedOpenSearchButton)
this._openSearchProviders[i].actor.add_style_pseudo_class('selected');
else
this._openSearchProviders[i].actor.remove_style_pseudo_class('selected');
}
},
_createOpenSearchProviderButton: function(provider) {
let button = new St.Button({ style_class: 'dash-search-button',
reactive: true,
x_fill: true,
y_align: St.Align.MIDDLE });
let bin = new St.Bin({ x_fill: false,
x_align:St.Align.MIDDLE });
button.connect('clicked', Lang.bind(this, function() {
this._openSearchSystem.activateResult(provider.id);
}));
let title = new St.Label({ text: provider.name,
style_class: 'dash-search-button-label' });
button.label_actor = title;
bin.set_child(title);
button.set_child(bin);
provider.actor = button;
this._searchProvidersBox.add(button);
},
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);
let titleButton = new St.Button({ style_class: 'search-section-header',
reactive: true,
x_fill: true,
y_fill: true });
titleButton.connect('clicked', Lang.bind(this, function () { this._onHeaderClicked(provider); }));
providerBox.add(titleButton);
let titleBox = new St.BoxLayout();
titleButton.set_child(titleBox);
let title = new St.Label({ text: provider.title });
let count = new St.Label();
titleBox.add(title, { expand: true });
titleBox.add(count);
let resultDisplayBin = new St.Bin({ style_class: 'search-section-results',
x_fill: true,
@ -291,7 +208,8 @@ SearchResults.prototype = {
resultDisplayBin.set_child(resultDisplay.actor);
this._providerMeta.push({ actor: providerBox,
resultDisplay: resultDisplay });
resultDisplay: resultDisplay,
count: count });
this._content.add(providerBox);
},
@ -309,8 +227,6 @@ SearchResults.prototype = {
this._searchSystem.reset();
this._statusText.hide();
this._clearDisplay();
this._selectedOpenSearchButton = -1;
this._updateOpenSearchButtonState();
},
startingSearch: function() {
@ -331,37 +247,30 @@ SearchResults.prototype = {
if (results.length == 0) {
this._statusText.set_text(_("No matching results."));
this._statusText.show();
return true;
} else {
this._selectedOpenSearchButton = -1;
this._updateOpenSearchButtonState();
this._statusText.hide();
}
let terms = this._searchSystem.getTerms();
this._openSearchSystem.setSearchTerms(terms);
// To avoid CSS transitions causing flickering
// of the selection when the first search result
// stays the same, we hide the content while
// filling in the results and setting the initial
// selection.
this._content.hide();
for (let i = 0; i < results.length; i++) {
let [provider, providerResults] = results[i];
let meta = this._metaForProvider(provider);
meta.actor.show();
meta.resultDisplay.renderResults(providerResults, terms);
meta.count.set_text('' + providerResults.length);
}
if (this._selectedOpenSearchButton == -1)
this.selectDown(false);
this._content.show();
this.selectDown(false);
return true;
},
_onHeaderClicked: function(provider) {
provider.expandSearch(this._searchSystem.getTerms());
},
_modifyActorSelection: function(resultDisplay, up) {
let success;
let index = resultDisplay.getSelectionIndex();
@ -375,26 +284,16 @@ SearchResults.prototype = {
},
selectUp: function(recursing) {
if (this._selectedOpenSearchButton == -1) {
for (let i = this._selectedProvider; i >= 0; i--) {
let meta = this._providerMeta[i];
if (!meta.actor.visible)
continue;
let success = this._modifyActorSelection(meta.resultDisplay, true);
if (success) {
this._selectedProvider = i;
return;
}
for (let i = this._selectedProvider; i >= 0; i--) {
let meta = this._providerMeta[i];
if (!meta.actor.visible)
continue;
let success = this._modifyActorSelection(meta.resultDisplay, true);
if (success) {
this._selectedProvider = i;
return;
}
}
if (this._selectedOpenSearchButton == -1)
this._selectedOpenSearchButton = this._openSearchProviders.length;
this._selectedOpenSearchButton--;
this._updateOpenSearchButtonState();
if (this._selectedOpenSearchButton >= 0)
return;
if (this._providerMeta.length > 0 && !recursing) {
this._selectedProvider = this._providerMeta.length - 1;
this.selectUp(true);
@ -403,30 +302,18 @@ SearchResults.prototype = {
selectDown: function(recursing) {
let current = this._selectedProvider;
if (this._selectedOpenSearchButton == -1) {
if (current == -1)
current = 0;
for (let i = current; i < this._providerMeta.length; i++) {
let meta = this._providerMeta[i];
if (!meta.actor.visible)
continue;
let success = this._modifyActorSelection(meta.resultDisplay, false);
if (success) {
this._selectedProvider = i;
return;
}
if (current == -1)
current = 0;
for (let i = current; i < this._providerMeta.length; i++) {
let meta = this._providerMeta[i];
if (!meta.actor.visible)
continue;
let success = this._modifyActorSelection(meta.resultDisplay, false);
if (success) {
this._selectedProvider = i;
return;
}
}
this._selectedOpenSearchButton++;
if (this._selectedOpenSearchButton < this._openSearchProviders.length) {
this._updateOpenSearchButtonState();
return;
}
this._selectedOpenSearchButton = -1;
this._updateOpenSearchButtonState();
if (this._providerMeta.length > 0 && !recursing) {
this._selectedProvider = 0;
this.selectDown(true);
@ -434,13 +321,6 @@ SearchResults.prototype = {
},
activateSelected: function() {
if (this._selectedOpenSearchButton != -1) {
let provider = this._openSearchProviders[this._selectedOpenSearchButton];
this._openSearchSystem.activateResult(provider.id);
Main.overview.hide();
return;
}
let current = this._selectedProvider;
if (current < 0)
return;

View File

@ -13,15 +13,21 @@ const St = imports.gi.St;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Util = imports.misc.util;
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
const KEY_STICKY_KEYS_ENABLED = 'stickykeys-enable';
const KEY_BOUNCE_KEYS_ENABLED = 'bouncekeys-enable';
const KEY_SLOW_KEYS_ENABLED = 'slowkeys-enable';
const KEY_MOUSE_KEYS_ENABLED = 'mousekeys-enable';
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
const A11Y_SCHEMA = "org.gnome.desktop.a11y.keyboard";
const KEY_STICKY_KEYS_ENABLED = "stickykeys-enable";
const KEY_BOUNCE_KEYS_ENABLED = "bouncekeys-enable";
const KEY_SLOW_KEYS_ENABLED = "slowkeys-enable";
const KEY_MOUSE_KEYS_ENABLED = "mousekeys-enable";
const AT_SCREEN_KEYBOARD_SCHEMA = "org.gnome.desktop.default-applications.at.mobility";
const AT_SCREEN_READER_SCHEMA = "org.gnome.desktop.default-applications.at.visual";
const XSETTINGS_SCHEMA = "org.gnome.settings-daemon.plugins.xsettings";
const KEY_DPI = "dpi";
const DPI_LOW_REASONABLE_VALUE = 50;
const DPI_HIGH_REASONABLE_VALUE = 500;
@ -29,16 +35,32 @@ const DPI_HIGH_REASONABLE_VALUE = 500;
const DPI_FACTOR_LARGE = 1.25;
const DPI_FACTOR_LARGER = 1.5;
const DPI_FACTOR_LARGEST = 2.0;
const DPI_DEFAULT = 96;
const KEY_META_DIR = '/apps/metacity/general';
const KEY_VISUAL_BELL = KEY_META_DIR + '/visual_bell';
const KEY_META_DIR = "/apps/metacity/general";
const KEY_VISUAL_BELL = KEY_META_DIR + "/visual_bell";
const DESKTOP_INTERFACE_SCHEMA = 'org.gnome.desktop.interface';
const KEY_GTK_THEME = 'gtk-theme';
const KEY_ICON_THEME = 'icon-theme';
const KEY_TEXT_SCALING_FACTOR = 'text-scaling-factor';
const DESKTOP_INTERFACE_SCHEMA = "org.gnome.desktop.interface";
const KEY_GTK_THEME = "gtk-theme";
const KEY_ICON_THEME = "icon-theme";
const HIGH_CONTRAST_THEME = 'HighContrast';
const HIGH_CONTRAST_THEME = "HighContrast";
function getDPIFromX() {
let screen = global.get_gdk_screen();
if (screen) {
let width_dpi = (screen.get_width() / (screen.get_width_mm() / 25.4));
let height_dpi = (screen.get_height() / (screen.get_height_mm() / 25.4));
if (width_dpi < DPI_LOW_REASONABLE_VALUE
|| width_dpi > DPI_HIGH_REASONABLE_VALUE
|| height_dpi < DPI_LOW_REASONABLE_VALUE
|| height_dpi > DPI_HIGH_REASONABLE_VALUE)
return DPI_DEFAULT;
else
return (width_dpi + height_dpi) / 2;
}
return DPI_DEFAULT;
}
function ATIndicator() {
this._init.apply(this, arguments);
@ -57,20 +79,17 @@ ATIndicator.prototype = {
let highContrast = this._buildHCItem();
this.menu.addMenuItem(highContrast);
let magnifier = this._buildItem(_("Zoom"), APPLICATIONS_SCHEMA,
'screen-magnifier-enabled');
let magnifier = this._buildMagItem();
this.menu.addMenuItem(magnifier);
let textZoom = this._buildFontItem();
this.menu.addMenuItem(textZoom);
// let screenReader = this._buildItem(_("Screen Reader"), APPLICATIONS_SCHEMA,
// 'screen-reader-enabled');
// this.menu.addMenuItem(screenReader);
let screenReader = this._buildItem(_("Screen Reader"), AT_SCREEN_READER_SCHEMA, 'startup');
this.menu.addMenuItem(screenReader);
// let screenKeyboard = this._buildItem(_("Screen Keyboard"), APPLICATIONS_SCHEMA,
// 'screen-keyboard-enabled');
// this.menu.addMenuItem(screenKeyboard);
let screenKeyboard = this._buildItem(_("Screen Keyboard"), AT_SCREEN_KEYBOARD_SCHEMA, 'startup');
this.menu.addMenuItem(screenKeyboard);
let visualBell = this._buildItemGConf(_("Visual Alerts"), client, KEY_VISUAL_BELL);
this.menu.addMenuItem(visualBell);
@ -89,9 +108,8 @@ ATIndicator.prototype = {
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addAction(_("Universal Access Settings"), function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().get_app('gnome-universal-access-panel.desktop');
app.activate(-1);
let p = new Shell.Process({ args: ['gnome-control-center','universal-access'] });
p.run();
});
},
@ -149,12 +167,9 @@ ATIndicator.prototype = {
if (enabled) {
settings.set_string(KEY_GTK_THEME, HIGH_CONTRAST_THEME);
settings.set_string(KEY_ICON_THEME, HIGH_CONTRAST_THEME);
} else if(!hasHC) {
} else {
settings.set_string(KEY_GTK_THEME, gtkTheme);
settings.set_string(KEY_ICON_THEME, iconTheme);
} else {
settings.reset(KEY_GTK_THEME);
settings.reset(KEY_ICON_THEME);
}
});
settings.connect('changed::' + KEY_GTK_THEME, function() {
@ -175,23 +190,42 @@ ATIndicator.prototype = {
},
_buildFontItem: function() {
let settings = new Gio.Settings({ schema: DESKTOP_INTERFACE_SCHEMA });
let settings = new Gio.Settings({ schema: XSETTINGS_SCHEMA });
let factor = settings.get_double(KEY_TEXT_SCALING_FACTOR);
let initial_setting = (factor > 1.0);
// we assume this never changes (which is not true if resolution
// is changed, but we would need XRandR events for that)
let x_value = getDPIFromX();
let user_value;
function on_get() {
user_value = settings.get_double(KEY_DPI);
return (user_value - (DPI_FACTOR_LARGE * x_value) > -1);
}
let initial_setting = on_get();
let default_value = initial_setting ? x_value : user_value;
let widget = this._buildItemExtended(_("Large Text"),
initial_setting,
settings.is_writable(KEY_TEXT_SCALING_FACTOR),
settings.is_writable(KEY_DPI),
function (enabled) {
if (enabled)
settings.set_double(KEY_TEXT_SCALING_FACTOR,
DPI_FACTOR_LARGE);
settings.set_double(KEY_DPI, DPI_FACTOR_LARGE * default_value);
else
settings.reset(KEY_TEXT_SCALING_FACTOR);
settings.set_double(KEY_DPI, default_value);
});
settings.connect('changed::' + KEY_TEXT_SCALING_FACTOR, function() {
let factor = settings.get_double(KEY_TEXT_SCALING_FACTOR);
let active = (factor > 1.0);
settings.connect('changed::' + KEY_DPI, function() {
let active = on_get();
default_value = active ? x_value : user_value;
widget.setToggleState(active);
});
return widget;
},
_buildMagItem: function() {
let mag = Main.magnifier;
let widget = this._buildItemExtended(_("Zoom"),
mag.isActive(),
true,
Lang.bind(mag, mag.setActive));
mag.connect('active-changed', function(magnifier, active) {
widget.setToggleState(active);
});
return widget;

View File

@ -1,525 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter;
const Gdk = imports.gi.Gdk;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const GnomeBluetoothApplet = imports.gi.GnomeBluetoothApplet;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const ConnectionState = {
DISCONNECTED: 0,
CONNECTED: 1,
DISCONNECTING: 2,
CONNECTING: 3
}
function Indicator() {
this._init.apply(this, arguments);
}
Indicator.prototype = {
__proto__: PanelMenu.SystemStatusButton.prototype,
_init: function() {
PanelMenu.SystemStatusButton.prototype._init.call(this, 'bluetooth-disabled', null);
GLib.spawn_command_line_sync ('pkill -f "^bluetooth-applet$"');
this._applet = new GnomeBluetoothApplet.Applet();
this._killswitch = new PopupMenu.PopupSwitchMenuItem(_("Bluetooth"), false);
this._applet.connect('notify::killswitch-state', Lang.bind(this, this._updateKillswitch));
this._killswitch.connect('toggled', Lang.bind(this, function() {
let current_state = this._applet.killswitch_state;
if (current_state != GnomeBluetoothApplet.KillswitchState.HARD_BLOCKED &&
current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER) {
this._applet.killswitch_state = this._killswitch.state ?
GnomeBluetoothApplet.KillswitchState.UNBLOCKED:
GnomeBluetoothApplet.KillswitchState.SOFT_BLOCKED;
} else
this._killswitch.setToggleState(false);
}));
this._discoverable = new PopupMenu.PopupSwitchMenuItem(_("Visibility"), this._applet.discoverable);
this._applet.connect('notify::discoverable', Lang.bind(this, function() {
this._discoverable.setToggleState(this._applet.discoverable);
}));
this._discoverable.connect('toggled', Lang.bind(this, function() {
this._applet.discoverable = this._discoverable.state;
}));
this._updateKillswitch();
this.menu.addMenuItem(this._killswitch);
this.menu.addMenuItem(this._discoverable);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._fullMenuItems = [new PopupMenu.PopupSeparatorMenuItem(),
new PopupMenu.PopupMenuItem(_("Send Files to Device...")),
new PopupMenu.PopupMenuItem(_("Set up a New Device...")),
new PopupMenu.PopupSeparatorMenuItem()];
this._hasDevices = false;
this._deviceSep = this._fullMenuItems[0]; // hidden if no device exists
this._fullMenuItems[1].connect('activate', function() {
GLib.spawn_command_line_async('bluetooth-sendto');
});
this._fullMenuItems[2].connect('activate', function() {
GLib.spawn_command_line_async('bluetooth-wizard');
});
for (let i = 0; i < this._fullMenuItems.length; i++) {
let item = this._fullMenuItems[i];
this.menu.addMenuItem(item);
}
this._deviceItemPosition = 3;
this._deviceItems = [];
this._applet.connect('devices-changed', Lang.bind(this, this._updateDevices));
this._updateDevices();
this._applet.connect('notify::show-full-menu', Lang.bind(this, this._updateFullMenu));
this._updateFullMenu();
this.menu.addAction(_("Bluetooth Settings"), function() {
Main.overview.hide()
let app = Shell.AppSystem.get_default().get_app('bluetooth-properties.desktop');
app.activate(-1);
});
this._applet.connect('pincode-request', Lang.bind(this, this._pinRequest));
this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest));
this._applet.connect('auth-request', Lang.bind(this, this._authRequest));
this._applet.connect('cancel-request', Lang.bind(this, this._cancelRequest));
},
_updateKillswitch: function() {
let current_state = this._applet.killswitch_state;
let on = current_state == GnomeBluetoothApplet.KillswitchState.UNBLOCKED;
let has_adapter = current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER;
let can_toggle = current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER &&
current_state != GnomeBluetoothApplet.KillswitchState.HARD_BLOCKED;
this._killswitch.setToggleState(on);
if (can_toggle)
this._killswitch.setStatus(null);
else
/* TRANSLATORS: this means that bluetooth was disabled by hardware rfkill */
this._killswitch.setStatus(_("hardware disabled"));
if (has_adapter)
this.actor.show();
else
this.actor.hide();
if (on) {
this._discoverable.actor.show();
this.setIcon('bluetooth-active');
} else {
this._discoverable.actor.hide();
this.setIcon('bluetooth-disabled');
}
},
_updateDevices: function() {
let devices = this._applet.get_devices();
let newlist = [ ];
for (let i = 0; i < this._deviceItems.length; i++) {
let item = this._deviceItems[i];
let destroy = true;
for (let j = 0; j < devices.length; j++) {
if (item._device.device_path == devices[j].device_path) {
this._updateDeviceItem(item, devices[j]);
destroy = false;
break;
}
}
if (destroy)
item.destroy();
else
newlist.push(item);
}
this._deviceItems = newlist;
this._hasDevices = newlist.length > 0;
for (let i = 0; i < devices.length; i++) {
let d = devices[i];
if (d._item)
continue;
let item = this._createDeviceItem(d);
if (item) {
this.menu.addMenuItem(item, this._deviceItemPosition + this._deviceItems.length);
this._deviceItems.push(item);
this._hasDevices = true;
}
}
if (this._hasDevices)
this._deviceSep.actor.show();
else
this._deviceSep.actor.hide();
},
_updateDeviceItem: function(item, device) {
if (!device.can_connect && device.capabilities == GnomeBluetoothApplet.Capabilities.NONE) {
item.destroy();
return;
}
let prevDevice = item._device;
let prevCapabilities = prevDevice.capabilities;
let prevCanConnect = prevDevice.can_connect;
// adopt the new device object
item._device = device;
device._item = item;
// update properties
item.label.text = device.alias;
if (prevCapabilities != device.capabilities ||
prevCanConnect != device.can_connect) {
// need to rebuild the submenu
item.menu.removeAll();
this._buildDeviceSubMenu(item, device);
}
// update connected property
if (device.can_connect)
item._connectedMenuitem.setToggleState(device.connected);
},
_createDeviceItem: function(device) {
if (!device.can_connect && device.capabilities == GnomeBluetoothApplet.Capabilities.NONE)
return null;
let item = new PopupMenu.PopupSubMenuMenuItem(device.alias);
// adopt the device object, and add a back link
item._device = device;
device._item = item;
this._buildDeviceSubMenu(item, device);
return item;
},
_buildDeviceSubMenu: function(item, device) {
if (device.can_connect) {
item._connected = device.connected;
item._connectedMenuitem = new PopupMenu.PopupSwitchMenuItem(_("Connection"), device.connected);
item._connectedMenuitem.connect('toggled', Lang.bind(this, function() {
if (item._connected > ConnectionState.CONNECTED) {
// operation already in progress, revert
// (should not happen anyway)
menuitem.setToggleState(menuitem.state);
}
if (item._connected) {
item._connected = ConnectionState.DISCONNECTING;
menuitem.setStatus(_("disconnecting..."));
this._applet.disconnect_device(item._device.device_path, function(applet, success) {
if (success) { // apply
item._connected = ConnectionState.DISCONNECTED;
menuitem.setToggleState(false);
} else { // revert
item._connected = ConnectionState.CONNECTED;
menuitem.setToggleState(true);
}
menuitem.setStatus(null);
});
} else {
item._connected = ConnectionState.CONNECTING;
menuitem.setStatus(_("connecting..."));
this._applet.connect_device(item._device.device_path, function(applet, success) {
if (success) { // apply
item._connected = ConnectionState.CONNECTED;
menuitem.setToggleState(true);
} else { // revert
item._connected = ConnectionState.DISCONNECTED;
menuitem.setToggleState(false);
}
menuitem.setStatus(null);
});
}
}));
item.menu.addMenuItem(item._connectedMenuitem);
}
if (device.capabilities & GnomeBluetoothApplet.Capabilities.OBEX_PUSH) {
item.menu.addAction(_("Send Files..."), Lang.bind(this, function() {
this._applet.send_to_address(device.bdaddr, device.alias);
}));
}
if (device.capabilities & GnomeBluetoothApplet.Capabilities.OBEX_FILE_TRANSFER) {
item.menu.addAction(_("Browse Files..."), Lang.bind(this, function(event) {
this._applet.browse_address(device.bdaddr, event.get_time(),
Lang.bind(this, function(applet, result) {
try {
applet.browse_address_finish(result);
} catch (e) {
this._ensureSource();
this._source.notify(new MessageTray.Notification(this._source,
_("Bluetooth"),
_("Error browsing device"),
{ body: _("The requested device cannot be browsed, error is '%s'").format(e) }));
}
}));
}));
}
switch (device.type) {
case GnomeBluetoothApplet.Type.KEYBOARD:
item.menu.addAction(_("Keyboard Settings"), function() {
GLib.spawn_command_line_async('gnome-control-center keyboard');
});
break;
case GnomeBluetoothApplet.Type.MOUSE:
item.menu.addAction(_("Mouse Settings"), function() {
GLib.spawn_command_line_async('gnome-control-center mouse');
});
break;
case GnomeBluetoothApplet.Type.HEADSET:
case GnomeBluetoothApplet.Type.HEADPHONES:
case GnomeBluetoothApplet.Type.OTHER_AUDIO:
item.menu.addAction(_("Sound Settings"), function() {
GLib.spawn_command_line_async('gnome-control-center sound');
});
break;
default:
break;
}
},
_updateFullMenu: function() {
if (this._applet.show_full_menu) {
this._showAll(this._fullMenuItems);
if (this._hasDevices)
this._showAll(this._deviceItems);
else
this._deviceSep.actor.hide();
} else {
this._hideAll(this._fullMenuItems);
this._hideAll(this._deviceItems);
}
},
_showAll: function(items) {
for (let i = 0; i < items.length; i++)
items[i].actor.show();
},
_hideAll: function(items) {
for (let i = 0; i < items.length; i++)
items[i].actor.hide();
},
_destroyAll: function(items) {
for (let i = 0; i < items.length; i++)
items[i].destroy();
},
_ensureSource: function() {
if (!this._source) {
this._source = new Source();
Main.messageTray.add(this._source);
}
},
_authRequest: function(applet, device_path, name, long_name, uuid) {
this._ensureSource();
this._source.notify(new AuthNotification(this._source, this._applet, device_path, name, long_name, uuid));
},
_confirmRequest: function(applet, device_path, name, long_name, pin) {
this._ensureSource();
this._source.notify(new ConfirmNotification(this._source, this._applet, device_path, name, long_name, pin));
},
_pinRequest: function(applet, device_path, name, long_name, numeric) {
this._ensureSource();
this._source.notify(new PinNotification(this._source, this._applet, device_path, name, long_name, numeric));
},
_cancelRequest: function() {
this._source.destroy();
}
}
function Source() {
this._init.apply(this, arguments);
}
Source.prototype = {
__proto__: MessageTray.Source.prototype,
_init: function() {
MessageTray.Source.prototype._init.call(this, _("Bluetooth"));
this._setSummaryIcon(this.createNotificationIcon());
},
notify: function(notification) {
this._private_destroyId = notification.connect('destroy', Lang.bind(this, function(notification) {
if (this.notification == notification) {
// the destroyed notification is the last for this source
this.notification.disconnect(this._private_destroyId);
this.destroy();
}
}));
MessageTray.Source.prototype.notify.call(this, notification);
},
createNotificationIcon: function() {
return new St.Icon({ icon_name: 'bluetooth-active',
icon_type: St.IconType.SYMBOLIC,
icon_size: this.ICON_SIZE });
}
}
function AuthNotification() {
this._init.apply(this, arguments);
}
AuthNotification.prototype = {
__proto__: MessageTray.Notification.prototype,
_init: function(source, applet, device_path, name, long_name, uuid) {
MessageTray.Notification.prototype._init.call(this,
source,
_("Bluetooth"),
_("Authorization request from %s").format(name),
{ customContent: true });
this.setResident(true);
this._applet = applet;
this._devicePath = device_path;
this.addBody(_("Device %s wants access to the service '%s'").format(long_name, uuid));
this.addButton('always-grant', _("Always grant access"));
this.addButton('grant', _("Grant this time only"));
this.addButton('reject', _("Reject"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
switch (action) {
case 'always-grant':
this._applet.agent_reply_auth(this._devicePath, true, true);
break;
case 'grant':
this._applet.agent_reply_auth(this._devicePath, true, false);
break;
case 'reject':
default:
this._applet.agent_reply_auth(this._devicePath, false, false);
}
this.destroy();
}));
}
}
function ConfirmNotification() {
this._init.apply(this, arguments);
}
ConfirmNotification.prototype = {
__proto__: MessageTray.Notification.prototype,
_init: function(source, applet, device_path, name, long_name, pin) {
MessageTray.Notification.prototype._init.call(this,
source,
_("Bluetooth"),
_("Pairing confirmation for %s").format(name),
{ customContent: true });
this.setResident(true);
this._applet = applet;
this._devicePath = device_path;
this.addBody(_("Device %s wants to pair with this computer").format(long_name));
this.addBody(_("Please confirm whether the PIN '%s' matches the one on the device.").format(pin));
this.addButton('matches', _("Matches"));
this.addButton('does-not-match', _("Does not match"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
if (action == 'matches')
this._applet.agent_reply_confirm(this._devicePath, true);
else
this._applet.agent_reply_confirm(this._devicePath, false);
this.destroy();
}));
}
}
function PinNotification() {
this._init.apply(this, arguments);
}
PinNotification.prototype = {
__proto__: MessageTray.Notification.prototype,
_init: function(source, applet, device_path, name, long_name, numeric) {
MessageTray.Notification.prototype._init.call(this,
source,
_("Bluetooth"),
_("Pairing request for %s").format(name),
{ customContent: true });
this.setResident(true);
this._applet = applet;
this._devicePath = device_path;
this._numeric = numeric;
this.addBody(_("Device %s wants to pair with this computer").format(long_name));
this.addBody(_("Please enter the PIN mentioned on the device."));
this._entry = new St.Entry();
this._entry.connect('key-release-event', Lang.bind(this, function(entry, event) {
let key = event.get_key_symbol();
if (key == Clutter.KEY_Return) {
this.emit('action-invoked', 'ok');
return true;
} else if (key == Clutter.KEY_Escape) {
this.emit('action-invoked', 'cancel');
return true;
}
return false;
}));
this.addActor(this._entry);
this.addButton('ok', _("OK"));
this.addButton('cancel', _("Cancel"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
if (action == 'ok') {
if (this._numeric) {
let num = parseInt(this._entry.text);
if (isNaN(num)) {
// user reply was empty, or was invalid
// cancel the operation
num = -1;
}
this._applet.agent_reply_passkey(this._devicePath, num);
} else
this._applet.agent_reply_pincode(this._devicePath, this._entry.text);
} else {
if (this._numeric)
this._applet.agent_reply_passkey(this._devicePath, -1);
else
this._applet.agent_reply_pincode(this._devicePath, null);
}
this.destroy();
}));
},
grabFocus: function(lockTray) {
MessageTray.Notification.prototype.grabFocus.call(this, lockTray);
global.stage.set_key_focus(this._entry);
}
}

View File

@ -1,203 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter;
const GdkPixbuf = imports.gi.GdkPixbuf;
const Gkbd = imports.gi.Gkbd;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Main = imports.ui.main;
const PopupMenu = imports.ui.popupMenu;
const PanelMenu = imports.ui.panelMenu;
const Util = imports.misc.util;
function LayoutMenuItem() {
this._init.apply(this, arguments);
}
LayoutMenuItem.prototype = {
__proto__: PopupMenu.PopupBaseMenuItem.prototype,
_init: function(config, id, indicator, long_name) {
PopupMenu.PopupBaseMenuItem.prototype._init.call(this);
this._config = config;
this._id = id;
this.label = new St.Label({ text: long_name });
this.indicator = indicator;
this.addActor(this.label);
this.addActor(this.indicator);
},
activate: function(event) {
PopupMenu.PopupBaseMenuItem.prototype.activate.call(this);
this._config.lock_group(this._id);
}
};
function XKBIndicator() {
this._init.call(this);
}
XKBIndicator.prototype = {
__proto__: PanelMenu.Button.prototype,
_init: function() {
PanelMenu.Button.prototype._init.call(this, St.Align.START);
this._container = new Shell.GenericContainer();
this._container.connect('get-preferred-width', Lang.bind(this, this._get_preferred_width));
this._container.connect('get-preferred-height', Lang.bind(this, this._get_preferred_height));
this._container.connect('allocate', Lang.bind(this, this._allocate));
this.actor.set_child(this._container);
this._iconActor = new St.Icon({ icon_name: 'keyboard', icon_type: St.IconType.SYMBOLIC, style_class: 'system-status-icon' });
this._container.add_actor(this._iconActor);
this._labelActors = [ ];
this._layoutItems = [ ];
this._showFlags = false;
this._config = Gkbd.Configuration.get();
this._config.connect('changed', Lang.bind(this, this._sync_config));
this._config.connect('group-changed', Lang.bind(this, this._sync_group));
this._config.start_listen();
this._sync_config();
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addAction(_("Show Keyboard Layout..."), Lang.bind(this, function() {
Main.overview.hide();
Util.spawn(['gkbd-keyboard-display', '-g', String(this._config.get_current_group() + 1)]);
}));
this.menu.addAction(_("Localization Settings"), function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().get_app('gnome-region-panel.desktop');
app.activate(-1);
});
},
_sync_config: function() {
this._showFlags = this._config.if_flags_shown();
if (this._showFlags) {
this._container.set_skip_paint(this._iconActor, false);
} else {
this._container.set_skip_paint(this._iconActor, true);
}
let groups = this._config.get_group_names();
if (groups.length > 1) {
this.actor.show();
} else {
this.menu.close();
this.actor.hide();
}
for (let i = 0; i < this._layoutItems.length; i++)
this._layoutItems[i].destroy();
for (let i = 0; i < this._labelActors.length; i++)
this._labelActors[i].destroy();
let short_names = this._config.get_short_group_names();
this._selectedLayout = null;
this._layoutItems = [ ];
this._selectedLabel = null;
this._labelActors = [ ];
for (let i = 0; i < groups.length; i++) {
let icon_name = this._config.get_group_name(i);
let actor;
if (this._showFlags)
actor = new St.Icon({ icon_name: icon_name, icon_type: St.IconType.SYMBOLIC, style_class: 'popup-menu-icon' });
else
actor = new St.Label({ text: short_names[i] });
let item = new LayoutMenuItem(this._config, i, actor, groups[i]);
item._short_group_name = short_names[i];
item._icon_name = icon_name;
this._layoutItems.push(item);
this.menu.addMenuItem(item, i);
let shortLabel = new St.Label({ text: short_names[i] });
this._labelActors.push(shortLabel);
this._container.add_actor(shortLabel);
this._container.set_skip_paint(shortLabel, true);
}
this._sync_group();
},
_sync_group: function() {
let selected = this._config.get_current_group();
if (this._selectedLayout) {
this._selectedLayout.setShowDot(false);
this._selectedLayout = null;
}
if (this._selectedLabel) {
this._container.set_skip_paint(this._selectedLabel, true);
this._selectedLabel = null;
}
let item = this._layoutItems[selected];
item.setShowDot(true);
this._iconActor.icon_name = item._icon_name;
this._selectedLabel = this._labelActors[selected];
this._container.set_skip_paint(this._selectedLabel, this._showFlags);
this._selectedLayout = item;
},
_get_preferred_width: function(container, for_height, alloc) {
/* Here, and in _get_preferred_height, we need to query for the
height of all children, but we ignore the results for those
we don't actually display. */
let max_min_width = 0, max_natural_width = 0;
if (this._showFlags)
[max_min_width, max_natural_width] = this._iconActor.get_preferred_width(for_height);
for (let i = 0; i < this._labelActors.length; i++) {
let [min_width, natural_width] = this._labelActors[i].get_preferred_width(for_height);
if (!this._showFlags) {
max_min_width = Math.max(max_min_width, min_width);
max_natural_width = Math.max(max_natural_width, natural_width);
}
}
alloc.min_size = max_min_width;
alloc.natural_size = max_natural_width;
},
_get_preferred_height: function(container, for_width, alloc) {
let max_min_height = 0, max_natural_height = 0;
if (this._showFlags)
[max_min_height, max_natural_height] = this._iconActor.get_preferred_height(for_width);
for (let i = 0; i < this._labelActors.length; i++) {
let [min_height, natural_height] = this._labelActors[i].get_preferred_height(for_width);
if (!this._showFlags) {
max_min_height = Math.max(max_min_height, min_height);
max_natural_height = Math.max(max_natural_height, natural_height);
}
}
alloc.min_size = max_min_height;
alloc.natural_size = max_natural_height;
},
_allocate: function(container, box, flags) {
// translate box to (0, 0)
box.x2 -= box.x1;
box.x1 = 0;
box.y2 -= box.y1;
box.y1 = 0;
this._iconActor.allocate_align_fill(box, 0.5, 0, false, false, flags);
for (let i = 0; i < this._labelActors.length; i++)
this._labelActors[i].allocate_align_fill(box, 0.5, 0, false, false, flags);
}
};

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,18 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const DBus = imports.dbus;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Util = imports.misc.util;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const BUS_NAME = 'org.gnome.PowerManager';
const OBJECT_PATH = '/org/gnome/PowerManager';
@ -24,10 +26,7 @@ const UPDeviceType = {
MOUSE: 5,
KEYBOARD: 6,
PDA: 7,
PHONE: 8,
MEDIA_PLAYER: 9,
TABLET: 10,
COMPUTER: 11
PHONE: 8
};
const UPDeviceState = {
@ -47,7 +46,7 @@ const PowerManagerInterface = {
{ name: 'GetPrimaryDevice', inSignature: '', outSignature: '(susbut)' },
],
signals: [
{ name: 'Changed', inSignature: '' },
{ name: 'Changed', outSignature: '' },
],
properties: [
{ name: 'Icon', signature: 's', access: 'read' },
@ -69,21 +68,17 @@ Indicator.prototype = {
this._deviceItems = [ ];
this._hasPrimary = false;
this._primaryDeviceId = null;
this._batteryItem = new PopupMenu.PopupMenuItem('', { reactive: false });
this._primaryPercentage = new St.Label();
this._batteryItem.addActor(this._primaryPercentage, { align: St.Align.END });
this._batteryItem = new PopupMenu.PopupMenuItem('');
this.menu.addMenuItem(this._batteryItem);
this._deviceSep = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(this._deviceSep);
this._otherDevicePosition = 2;
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addAction(_("What's using power..."),function() {
GLib.spawn_command_line_async('gnome-power-statistics --device wakeups');
});
this.menu.addAction(_("Power Settings"),function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().get_app('gnome-power-panel.desktop');
app.activate(-1);
GLib.spawn_command_line_async('gnome-control-center power');
});
this._proxy.connect('Changed', Lang.bind(this, this._devicesChanged));
@ -100,32 +95,10 @@ Indicator.prototype = {
this._deviceSep.actor.hide();
return;
}
let [device_id, device_type, icon, percentage, state, seconds] = device;
let [device_id, device_type, summary, percentage, state, time] = device;
if (device_type == UPDeviceType.BATTERY) {
this._hasPrimary = true;
let time = Math.round(seconds / 60);
if (time == 0) {
// 0 is reported when UPower does not have enough data
// to estimate battery life
this._batteryItem.label.text = _("Estimating...");
} else {
let minutes = time % 60;
let hours = Math.floor(time / 60);
let timestring;
if (time > 60) {
if (minutes == 0) {
timestring = ngettext("%d hour remaining", "%d hours remaining", hours).format(hours);
} else {
/* TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining" */
let template = _("%d %s %d %s remaining");
timestring = template.format (hours, ngettext("hour", "hours", hours), minutes, ngettext("minute", "minutes", minutes));
}
} else
timestring = ngettext("%d minute remaining", "%d minutes remaining", minutes).format(minutes);
this._batteryItem.label.text = timestring;
}
this._primaryPercentage.text = Math.round(percentage) + '%';
this._batteryItem.label.text = summary;
this._batteryItem.actor.show();
if (this._deviceItems.length > 0)
this._deviceSep.actor.show();
@ -157,6 +130,10 @@ Indicator.prototype = {
continue;
let item = new DeviceItem (devices[i]);
item.connect('activate', function() {
let p = new Shell.Process({ args: ['gnome-power-statistics', '--device', device_id] });
p.run();
});
this._deviceItems.push(item);
this.menu.addMenuItem(item, this._otherDevicePosition + position);
position++;
@ -187,8 +164,8 @@ Indicator.prototype = {
_checkError: function(error) {
if (!this._restarted && error && error.message.match(/org\.freedesktop\.DBus\.Error\.(UnknownMethod|InvalidArgs)/)) {
Util.killall('gnome-power-manager');
Util.spawn(['gnome-power-manager']);
GLib.spawn_command_line_sync('pkill -f "^gnome-power-manager$"');
GLib.spawn_command_line_async('gnome-power-manager');
this._restarted = true;
}
}
@ -202,14 +179,26 @@ DeviceItem.prototype = {
__proto__: PopupMenu.PopupBaseMenuItem.prototype,
_init: function(device) {
PopupMenu.PopupBaseMenuItem.prototype._init.call(this, { reactive: false });
PopupMenu.PopupBaseMenuItem.prototype._init.call(this);
let [device_id, device_type, icon, percentage, state, time] = device;
let [device_id, device_type, summary, percentage, state, time] = device;
this._box = new St.BoxLayout({ style_class: 'popup-device-menu-item' });
this._label = new St.Label({ text: this._deviceTypeToString(device_type) });
this._label = new St.Label({ text: summary });
this._icon = new St.Icon({ gicon: Shell.util_icon_from_string(icon),
let icon;
switch (state) {
case UPDeviceState.FULLY_CHARGED:
icon = 'battery-full-charged';
break;
case UPDeviceState.UNKNOWN:
icon = 'battery-missing';
break;
default:
icon = this._percentageToIcon(percentage) + (state == UPDeviceState.CHARGING ? '-charging' : '');
}
this._icon = new St.Icon({ icon_name: icon,
icon_type: St.IconType.SYMBOLIC,
style_class: 'popup-menu-icon' });
@ -217,36 +206,21 @@ DeviceItem.prototype = {
this._box.add_actor(this._label);
this.addActor(this._box);
let percentBin = new St.Bin({ x_align: St.Align.END });
let percentLabel = new St.Label({ text: '%d%%'.format(Math.round(percentage)) });
this.addActor(percentLabel, { align: St.Align.END });
percentBin.child = percentLabel;
this.addActor(percentBin);
},
_deviceTypeToString: function(type) {
switch (type) {
case UPDeviceType.AC_POWER:
return _("AC adapter");
case UPDeviceType.BATTERY:
return _("Laptop battery");
case UPDeviceType.UPS:
return _("UPS");
case UPDeviceType.MONITOR:
return _("Monitor");
case UPDeviceType.MOUSE:
return _("Mouse");
case UPDeviceType.KEYBOARD:
return _("Keyboard");
case UPDeviceType.PDA:
return _("PDA");
case UPDeviceType.PHONE:
return _("Cell phone");
case UPDeviceType.MEDIA_PLAYER:
return _("Media player");
case UPDeviceType.TABLET:
return _("Tablet");
case UPDeviceType.COMPUTER:
return _("Computer");
default:
return _("Unknown");
}
_percentageToIcon: function(p) {
if (p > 60)
return 'battery-full';
if (p > 30)
return 'battery-good';
if (p > 10)
return 'battery-low';
if (p > 0)
return 'battery-caution';
return 'battery-empty';
}
}

View File

@ -9,15 +9,15 @@ const Gvc = imports.gi.Gvc;
const Signals = imports.signals;
const St = imports.gi.St;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Util = imports.misc.util;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const VOLUME_MAX = 65536.0; /* PA_VOLUME_NORM */
const VOLUME_ADJUSTMENT_STEP = 0.05; /* Volume adjustment step in % */
const VOLUME_NOTIFY_ID = 1;
function Indicator() {
this._init.apply(this, arguments);
}
@ -34,17 +34,16 @@ Indicator.prototype = {
this._control.connect('default-source-changed', Lang.bind(this, this._readInput));
this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput));
this._control.connect('stream-removed', Lang.bind(this, this._maybeShowInput));
this._volumeMax = this._control.get_vol_max_norm();
this._volumeMaxAmplified = this._control.get_vol_max_amplified();
this._output = null;
this._outputVolumeId = 0;
this._outputMutedId = 0;
this._outputTitle = new PopupMenu.PopupMenuItem(_("Volume"), { reactive: false });
this._outputSwitch = new PopupMenu.PopupSwitchMenuItem(_("Volume: Muted"), false);
this._outputSwitch.connect('toggled', Lang.bind(this, this._switchToggled, '_output'));
this._outputSlider = new PopupMenu.PopupSliderMenuItem(0);
this._outputSlider.connect('value-changed', Lang.bind(this, this._sliderChanged, '_output'));
this._outputSlider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
this.menu.addMenuItem(this._outputTitle);
this.menu.addMenuItem(this._outputSwitch);
this.menu.addMenuItem(this._outputSlider);
this._separator = new PopupMenu.PopupSeparatorMenuItem();
@ -53,53 +52,36 @@ Indicator.prototype = {
this._input = null;
this._inputVolumeId = 0;
this._inputMutedId = 0;
this._inputTitle = new PopupMenu.PopupMenuItem(_("Microphone"), { reactive: false });
this._inputSwitch = new PopupMenu.PopupSwitchMenuItem(_("Microphone: Muted"), false);
this._inputSwitch.connect('toggled', Lang.bind(this, this._switchToggled, '_input'));
this._inputSlider = new PopupMenu.PopupSliderMenuItem(0);
this._inputSlider.connect('value-changed', Lang.bind(this, this._sliderChanged, '_input'));
this._inputSlider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
this.menu.addMenuItem(this._inputTitle);
this.menu.addMenuItem(this._inputSwitch);
this.menu.addMenuItem(this._inputSlider);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addAction(_("Sound Settings"), function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().get_app('gnome-sound-panel.desktop');
app.activate(-1);
let p = new Shell.Process({ args: ['gnome-control-center', 'sound'] });
p.run();
});
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
this._control.open();
},
_getMaxVolume: function(property) {
if (this[property].get_can_decibel())
return this._volumeMaxAmplified;
else
return this._volumeMax;
},
_onScrollEvent: function(actor, event) {
let direction = event.get_scroll_direction();
let currentVolume = this._output.volume;
let maxVolume = this._getMaxVolume('_output');
if (direction == Clutter.ScrollDirection.DOWN) {
let prev_muted = this._output.is_muted;
this._output.volume = Math.max(0, currentVolume - maxVolume * VOLUME_ADJUSTMENT_STEP);
if (this._output.volume < 1) {
this._output.volume = 0;
if (!prev_muted)
this._output.change_is_muted(true);
}
this._output.volume = Math.max(0, currentVolume - VOLUME_MAX * VOLUME_ADJUSTMENT_STEP);
this._output.push_volume();
}
else if (direction == Clutter.ScrollDirection.UP) {
this._output.volume = Math.min(maxVolume, currentVolume + maxVolume * VOLUME_ADJUSTMENT_STEP);
this._output.change_is_muted(false);
this._output.volume = Math.min(VOLUME_MAX, currentVolume + VOLUME_MAX * VOLUME_ADJUSTMENT_STEP);
this._output.push_volume();
}
this._notifyVolumeChange();
},
_onControlReady: function() {
@ -121,7 +103,8 @@ Indicator.prototype = {
this._mutedChanged (null, null, '_output');
this._volumeChanged (null, null, '_output');
} else {
this._outputSlider.setValue(0);
this._outputSwitch.label.text = _("Volume: Muted");
this._outputSwitch.setToggleState(false);
this.setIcon('audio-volume-muted-symbolic');
}
},
@ -141,7 +124,7 @@ Indicator.prototype = {
this._volumeChanged (null, null, '_input');
} else {
this._separator.actor.hide();
this._inputTitle.actor.hide();
this._inputSwitch.actor.hide();
this._inputSlider.actor.hide();
}
},
@ -164,24 +147,23 @@ Indicator.prototype = {
}
if (showInput) {
this._separator.actor.show();
this._inputTitle.actor.show();
this._inputSwitch.actor.show();
this._inputSlider.actor.show();
} else {
this._separator.actor.hide();
this._inputTitle.actor.hide();
this._inputSwitch.actor.hide();
this._inputSlider.actor.hide();
}
},
_volumeToIcon: function(volume) {
let maxVolume = this._getMaxVolume('_output');
if (volume <= 0) {
return 'audio-volume-muted';
} else {
let n = Math.floor(3 * volume / maxVolume) + 1;
if (n < 2)
let v = volume / VOLUME_MAX;
if (v < 0.33)
return 'audio-volume-low';
if (n >= 3)
if (v > 0.8)
return 'audio-volume-high';
return 'audio-volume-medium';
}
@ -192,30 +174,28 @@ Indicator.prototype = {
log ('Volume slider changed for %s, but %s does not exist'.format(property, property));
return;
}
let volume = value * this._getMaxVolume(property);
let prev_muted = this[property].is_muted;
if (volume < 1) {
this[property].volume = 0;
if (!prev_muted)
this[property].change_is_muted(true);
} else {
this[property].volume = volume;
if (prev_muted)
this[property].change_is_muted(false);
}
this[property].volume = value * VOLUME_MAX;
this[property].push_volume();
},
_notifyVolumeChange: function() {
global.cancel_theme_sound(VOLUME_NOTIFY_ID);
global.play_theme_sound(VOLUME_NOTIFY_ID, 'audio-volume-change');
global.play_theme_sound('audio-volume-change');
},
_switchToggled: function(switchItem, state, property) {
if (this[property] == null) {
log ('Volume mute switch toggled for %s, but %s does not exist'.format(property, property));
return;
}
this[property].change_is_muted(!state);
this._notifyVolumeChange();
},
_mutedChanged: function(object, param_spec, property) {
let muted = this[property].is_muted;
let slider = this[property+'Slider'];
let maxVolume = this._getMaxVolume(property);
slider.setValue(muted ? 0 : (this[property].volume / maxVolume));
let toggleSwitch = this[property+'Switch'];
toggleSwitch.setToggleState(!muted);
this._updateLabel(property);
if (property == '_output') {
if (muted)
this.setIcon('audio-volume-muted');
@ -225,9 +205,18 @@ Indicator.prototype = {
},
_volumeChanged: function(object, param_spec, property) {
let maxVolume = this._getMaxVolume(property);
this[property+'Slider'].setValue(this[property].volume / maxVolume);
this[property+'Slider'].setValue(this[property].volume / VOLUME_MAX);
this._updateLabel(property);
if (property == '_output' && !this._output.is_muted)
this.setIcon(this._volumeToIcon(this._output.volume));
},
_updateLabel: function(property) {
let label;
if (this[property].is_muted)
label = (property == '_output' ? _("Volume: Muted") : _("Microphone: Muted"));
else
label = (property == '_output' ? _("Volume: %3.0f%%") : _("Microphone: %3.0f%%")).format(this[property].volume / VOLUME_MAX * 100);
this[property+'Switch'].label.text = label;
}
};

View File

@ -6,7 +6,6 @@ const Signals = imports.signals;
const MessageTray = imports.ui.messageTray;
const NotificationDaemon = imports.ui.notificationDaemon;
const Util = imports.misc.util;
const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
'bluetooth-applet': 'bluetooth',
@ -16,11 +15,7 @@ const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
'nm-applet': 'network',
'gnome-power-manager': 'battery',
'keyboard': 'keyboard',
'a11y-keyboard': 'a11y',
'kbd-scrolllock': 'keyboard',
'kbd-numlock': 'keyboard',
'kbd-capslock': 'keyboard',
'ibus-ui-gtk': 'input-method'
'gnome-settings-daemon': 'display'
};
function StatusIconDispatcher() {
@ -32,16 +27,14 @@ StatusIconDispatcher.prototype = {
this._traymanager = new Shell.TrayManager();
this._traymanager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded));
this._traymanager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
this._traymanager.manage_stage(global.stage);
// Yet-another-Ubuntu-workaround - we have to kill their
// app-indicators, so that applications fall back to normal
// status icons
// http://bugzilla.gnome.org/show_bug.cgi=id=621382
Util.killall('indicator-application-service');
},
start: function(themeWidget) {
this._traymanager.manage_stage(global.stage, themeWidget);
let p = new Shell.Process({ args: ['pkill', '-f', '^([^ ]*/)?indicator-application-service$']});
p.run();
},
_onTrayIconAdded: function(o, icon) {

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