Compare commits

..

48 Commits

Author SHA1 Message Date
Colin Walters
e191636f34 Update for review of e57b7ec335 2009-06-30 15:29:10 -04:00
Colin Walters
39e31a3aa9 Update for comments on b5988a57fa 2009-06-30 15:29:10 -04:00
Colin Walters
8293f3423a Update for review of d5a80d3063 2009-06-30 15:29:10 -04:00
Marina Zhurakhinskaya
858a82fcdd Use a separate icon image as a drag actor instead of using the clone of the icon
Clutter no longer allows using a clone of an actor that is not a part of
the scene graph. This is what used to happen when we created a clone for
the icon of the item that was being dragged, and then closed the More panes
with the original item, removing the icon from the scene graph. This was
also when happened when the user hit Esc while dragging, which prompted the
overlay to close, removing the original icon from the scene graph.

Rename getIcon() methods to createIcon() to better reflect on the fact that
a new icon is created each time the method is called (we do use cache in
some cases).

Remove a stray log message in overlay.js

Fixes http://bugzilla.gnome.org/show_bug.cgi?id=585490
and http://bugzilla.gnome.org/show_bug.cgi?id=585489
2009-06-29 15:08:48 -04:00
Marina Zhurakhinskaya
2ae89f3b36 Merge branch 'master' into my-overlay-design 2009-06-29 14:40:04 -04:00
Marina Zhurakhinskaya
cdee026095 Make workspaces accept a drop of AppDisplay.WellDisplayItem
AppDispplay.WellDisplayItem needed to be added along with the
GenericDisplay.GenericDisplayItem as a type of a drop object
that workspaces accept.
2009-06-29 12:28:21 -04:00
Colin Walters
340fbfe943 Avoid duplicating most used applications in the AppDisplay cache
All used applications should be in the database from the menus
anyways.
2009-06-26 18:35:48 -04:00
Colin Walters
c577951ec9 Fix getMostUsedApps to avoid returning duplicate items 2009-06-26 18:30:28 -04:00
Colin Walters
d588b083d9 Fix invalid function signature and a memory leak
Add the missing GParamSpec to on_n_workspaces_changed.

Also, we don't need to re-dup the appid, since it's already dup'd.
2009-06-26 17:12:55 -04:00
Colin Walters
e57b7ec335 Replace main AppDisplay with AppWell
This is a start towards implementing the 02 overlay design.  The
default applications has moved into GConf.  We keep around an AppDisplay
instance for handling the right side behavior.
2009-06-25 20:32:08 -04:00
Colin Walters
b5988a57fa ShellAppSystem: Add API for looking up and manipulating favorites
The ShellAppSystem now proxies the GConf favorites key.
2009-06-25 20:32:08 -04:00
Colin Walters
d5a80d3063 ShellAppMonitor now works harder to assocate windows with desktop files
Instead of just keeping a heuristic wmclass match, call into the app
system to more strongly associate windows with desktop file IDs.
2009-06-25 20:32:08 -04:00
Marina Zhurakhinskaya
02a8cd5ce2 Use a single ItemResults class instead of AppResults and DocResults
AppResults and DocResults classes were identical with an exception of
the display class they used and the text label for the results. Merged
them into a single ItemResults class that takes these two additional
arguments.
2009-06-25 19:14:29 -04:00
Marina Zhurakhinskaya
aa77762d27 Move the activate and select functionality inside the callbacks
Move the activate and select functionality inside the callbacks for
'button-release-event' signals of the display item and the information
button correspondingly. This way it is more obvious that this is an
event handling code that needs to return a boolean value for whether
the signal has been fully handled by the actor.
2009-06-25 18:01:45 -04:00
Marina Zhurakhinskaya
de3f5dec68 Use TextureCache to load the information icon
Use TextureCache to load the information icon, so that we don't create a
new ClutterTexture for information icons corresponding to each display
item.
2009-06-25 17:24:46 -04:00
Marina Zhurakhinskaya
84865f416d Make sure we show the information button when a new item appears under the pointer
Update the code for checking a display item under the pointer to expect
the item itself rather than its child to be returned by stage_get_actor_at_pos().

This code is now used to display an information button when an item is
drawn under the pointer, so update the comment accordingly.
2009-06-25 15:31:09 -04:00
Marina Zhurakhinskaya
ca3e3df199 Show and hide dash panes instead of adding and removing each time
Add results and details panes up-front, and show and hide them instead
of adding and removing them each time
2009-06-25 14:54:28 -04:00
Marina Zhurakhinskaya
643febebf8 Add a comment about the use of the transparent background and set its opacity to 0
Add a comment about the use of the transparent background to catch clicks
in the workspaces area when the dash panes are being displayed and dismiss
the dash panes.

Set opacity for the background to 0 instead of using a transparent background
color so that Clutter optimizes the drawing of the background actor.
2009-06-25 14:33:24 -04:00
Marina Zhurakhinskaya
6d002c893d Fix up horizontal gradient code and its use
Fix up the comments about the horizontal gradient code and use 8x1 texture
instead of 8x8.

Make sure the values we assign to the three-stop horizontal gradient
require the use of the three stop gradient, with the middle value not being
right between the side values.
2009-06-25 14:32:00 -04:00
Marina Zhurakhinskaya
5eaed34047 Display search results automatically
Display a pane with search results for both applications and documents
automatically when a search string is entered.

Allow viewing search results for the individual section when More link
for applications or documents is clicked.

Move text labels for the applications and documents sections into the
respective classes.
2009-06-24 18:24:48 -04:00
Marina Zhurakhinskaya
8f5b55350f Add back search functionality
Enable typing in the search box and display results in the results pane.
This means that the user has to open the details pane for applications
or documents to view the results for now.

Connect Enter to launch the seleted item.

Connect Escape to clear search, remove results and details panes,
or exit overlay.
2009-06-22 19:06:50 -04:00
Marina Zhurakhinskaya
c600ebb687 Merge branch 'master' into my-overlay-design 2009-06-22 16:14:55 -04:00
Siegfried-Angel Gevatter Pujals
9abc062a64 Hide details panel by default
Don't show the details panel when the overlay is activated,
only when explicitly requested by clicking the info icon.
2009-06-22 16:10:39 -04:00
Marina Zhurakhinskaya
81d0474926 Fix the height allocated to the results sections
The results sections no longer include a label on top of them, so the
height of that label needs to be subtracted when specifying the height
for the sections. This ensures that display controls are positioned
correctly on the bottom of the section.
2009-06-18 20:01:49 -04:00
Marina Zhurakhinskaya
c41902c188 Select an item when information button is clicked, launch on single click
Clicking the information button for an item selects it (i.e. highlights it)
and shows details about the item.
Clicking the rest of the item area launches it.
Item does not become draggable if the dragging is started over the information
icon (i.e. if the user presses the information icon, but releases elsewhere).

Make sure we emit "activated" signal and close the overlay when an item from
one of the results displays is launched.
2009-06-18 19:53:21 -04:00
Marina Zhurakhinskaya
97b9ccbff7 Add an icon for the information link
Use an (i) icon supplied by Jeremy for the information link and display
it on hover. Make sure it is positioned nicely and the text doesn't
overlap with it.
2009-06-18 17:50:56 -04:00
Colin Walters
282daf768f Merge branch 'master' into overlay-design02 2009-06-18 17:26:03 -04:00
Marina Zhurakhinskaya
1b057300b0 Remove pop-up previews
Pop-up previews are not part of the new design and interfere with the information link.

Make sure details display for applications has the appropriate width set.
2009-06-18 17:12:58 -04:00
Colin Walters
75da772d05 Merge branch 'master' into overlay-design02
Conflicts:
	js/ui/appDisplay.js
2009-06-18 13:12:21 -04:00
Marina Zhurakhinskaya
df9cf98826 Make sure at most one item is selected in the overlay
Make sure at most one item is selected in the overlay and we always show
a details pane for the selected item.

Improve the positioning of the search box.

Remove a duplicate variable DASH_PAD and use DASH_SECTION_PADDING everywhere instead.
2009-06-17 18:12:02 -04:00
Colin Walters
d3cb8e5b21 Split out separate AppResults, DocResults classes. Readd search as stub.
Avoid bloating the Dash class by separating out a bit more functionality.
2009-06-17 17:38:08 -04:00
Marina Zhurakhinskaya
71998a6b43 Merge branch 'master' into my-overlay-design 2009-06-17 13:54:39 -04:00
Colin Walters
2fd2293e4a Merge branch 'master' into overlay-design02 2009-06-17 10:12:31 -04:00
Marina Zhurakhinskaya
3528eef655 Change selected item color and make sure only one item is selected at a time
A blue selected item color fits better with the new color scheme than a green one.

Only one item should be selected across all the displays we are showing.
2009-06-16 16:30:42 -04:00
Marina Zhurakhinskaya
f209d6792d Display a pane with item details
Display a pane with item details, such as a full image previews, when an item is single clicked.

Add a placeholder information link that shows up when an item is moused over.
2009-06-16 14:50:38 -04:00
Marina Zhurakhinskaya
614e83476e Merge branch 'master' into my-overlay-design 2009-06-16 13:04:56 -04:00
Marina Zhurakhinskaya
4e2a301ef0 Add icons from Jeremy
These icons are for the "more", "close", and "info" images in the overlay.
2009-06-11 19:00:12 -04:00
Marina Zhurakhinskaya
6def8cf7dd Merge branch 'master' into my-overlay-design 2009-06-11 18:24:42 -04:00
Marina Zhurakhinskaya
1204898d1e Display all recent documents in the results pane
Display all recent documents in the results pane, in addition to the first
few displayed in the main dash. All documents can be viewed with the help
of a paging control.
2009-06-09 16:11:51 -04:00
Marina Zhurakhinskaya
98530afd87 Add a results pane in the overlay
Display the results pane above the workspaces. The results pane is somewhat
transparent and has a blue gradient background. The dash pane is slightly
transparent and also has a blue gradient background.

The results pane shows up when a More control is clicked. It disappears when
a Less control is clicked, an area outside of the dash area is clicked,
an item starts being dragged, or the overlay mode is exited.

Add shell_global_create_horizontal_gradient() to shell-global.[ch]
2009-06-08 19:06:23 -04:00
Marina Zhurakhinskaya
a9fedcce76 Merge branch 'master' into my-overlay-design 2009-06-08 18:02:31 -04:00
Colin Walters
dae1d94258 Merge branch 'master' into overlay-design02 2009-06-04 17:43:48 -04:00
Colin Walters
4a7c328c33 Revert inadvertent commits for future rebasing 2009-06-04 17:42:03 -04:00
Colin Walters
2c0d6fdf89 favorites 2009-06-03 11:49:42 -04:00
Colin Walters
12f896eb94 random hack 2009-06-02 18:18:25 -04:00
Colin Walters
230c91b6d4 Functionality in ShellAppMonitor for tracking active applications
Hook into the metacity core for notification of new/removed windows,
and use the WM_CLASS data to map these to applications.  This is
a first pass; we need further redudancy in the system.
2009-06-02 18:18:25 -04:00
Colin Walters
d7dcfe6a06 Make ShellAppMonitor and ShellAppSystem singletons 2009-06-02 17:21:50 -04:00
Colin Walters
275eba17db Remove a lot of obsolete code from old Sideshow (now Dash) 2009-06-02 13:13:51 -04:00
112 changed files with 6312 additions and 13986 deletions

11
.gitignore vendored
View File

@@ -16,17 +16,7 @@ config.log
config.status
config
configure
data/gnome-shell.desktop
data/gnome-shell.desktop.in
intltool-extract.in
intltool-merge.in
intltool-update.in
libtool
omf.make
po/*.gmo
po/Makefile.in.in
po/POTFILES
po/stamp-it
scripts/launcher.pyc
src/*.gir
src/*.typelib
@@ -39,4 +29,3 @@ src/gnome-shell
src/test-recorder
src/test-recorder.ogg
stamp-h1
xmldocs.make

View File

@@ -1,4 +1,4 @@
SUBDIRS = data js src po
SUBDIRS = data js src
EXTRA_DIST = \
.project \
@@ -7,7 +7,7 @@ EXTRA_DIST = \
distcheck-hook:
@echo "Checking disted javascript against files in git"
@failed=false; \
for f in `cd $(srcdir) && git ls-files js` ; do \
for f in `cd $(srcdir) && git-ls-files js` ; do \
if ! test -e $(distdir)/$$f ; then \
echo File missing from distribution: $$f ; \
failed=true ; \

20
README
View File

@@ -1,20 +0,0 @@
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.
For more information about GNOME Shell, including instructions on how
to build GNOME Shell from source and how to get involved with the project,
see:
http://live.gnome.org/GnomeShell
Bugs should be reported at http://bugzilla.gnome.org against the 'gnome-shell'
product.
License
=======
GNOME Shell is distributed under the terms of the GNU General Public License,
version 2 or later. See the COPYING file for details.

View File

@@ -1,22 +1,8 @@
#!/bin/bash
# Run this to generate all the initial makefiles, etc.
#!/bin/sh
srcdir=`dirname $0`
test -z "$srcdir" && srcdir=.
PKG_NAME="gnome-shell"
REQUIRED_AUTOMAKE_VERSION=1.10
(test -f $srcdir/configure.ac \
&& test -d $srcdir/src) || {
echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
echo " top-level gnome-shell directory"
exit 1
}
which gnome-autogen.sh || {
echo "You need to install gnome-common from GNOME Subversion (or from"
echo "your OS vendor's package manager)."
exit 1
}
USE_GNOME2_MACROS=1 USE_COMMON_DOC_BUILD=yes . gnome-autogen.sh
(cd `dirname $0`;
touch ChangeLog NEWS &&
autoreconf --install --symlink &&
autoreconf &&
./configure --enable-maintainer-mode $@
)

View File

@@ -1,12 +1,10 @@
AC_INIT(gnome-shell, 2.27.2)
AC_INIT(gnome-shell, 0.0.1)
AC_CONFIG_AUX_DIR(config)
AM_INIT_AUTOMAKE([dist-bzip2 no-dist-gzip foreign])
AM_INIT_AUTOMAKE([dist-bzip2 no-dist-gzip])
AM_MAINTAINER_MODE
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])],)
AC_CONFIG_HEADERS(config.h)
AC_DISABLE_STATIC
@@ -22,16 +20,8 @@ AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",
PKG_PROG_PKG_CONFIG(0.16)
IT_PROG_INTLTOOL(0.26)
AM_GLIB_GNU_GETTEXT
AC_PATH_PROG(GCONFTOOL, gconftool-2, no)
AM_GCONF_SOURCE_2
# Get a value to substitute into gnome-shell.in
AM_PATH_PYTHON([2.5])
AC_SUBST(PYTHON)
# We need at least this, since gst_plugin_register_static() was added
# in 0.10.16, but nothing older than 0.10.21 has been tested.
GSTREAMER_MIN_VERSION=0.10.16
@@ -43,23 +33,23 @@ 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 xfixes"
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0)
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-0.9)
else
AC_MSG_RESULT(no)
fi
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
# Collect more than 20 libraries for a prize!
PKG_CHECK_MODULES(MUTTER_PLUGIN, gio-unix-2.0 gtk+-2.0 dbus-glib-1 mutter-plugins
gjs-gi-1.0 libgnome-menu $recorder_modules gconf-2.0
gdk-x11-2.0 clutter-x11-1.0 clutter-glx-1.0
gnome-desktop-2.0 >= 2.26 libstartup-notification-1.0
gobject-introspection-1.0 >= 0.6.5)
PKG_CHECK_MODULES(TIDY, clutter-1.0)
PKG_CHECK_MODULES(BIG, clutter-1.0 gtk+-2.0 librsvg-2.0)
PKG_CHECK_MODULES(MUTTER_PLUGIN, gtk+-2.0 dbus-glib-1 mutter-plugins gjs-gi-1.0 xscrnsaver libgnome-menu $recorder_modules gconf-2.0 gdk-x11-2.0 clutter-x11-0.9 clutter-glx-0.9)
PKG_CHECK_MODULES(TIDY, clutter-0.9)
PKG_CHECK_MODULES(BIG, clutter-0.9 gtk+-2.0 librsvg-2.0)
PKG_CHECK_MODULES(GDMUSER, dbus-glib-1 gtk+-2.0)
PKG_CHECK_MODULES(TRAY, gtk+-2.0)
PKG_CHECK_MODULES(TASKPANEL, libwnck-1.0 dbus-glib-1)
# We require libgnomeui for generating thumbnails for recent files with GnomeThumbnailFactory.
# We'll switch to using GnomeDesktopThumbnailFactory once the branch of gnome-desktop that contains
# it becomes stable.
PKG_CHECK_MODULES(LIBGNOMEUI, libgnomeui-2.0)
MUTTER_BIN_DIR=`$PKG_CONFIG --variable=exec_prefix mutter-plugins`/bin
# FIXME: metacity-plugins.pc should point directly to its .gir file
@@ -90,31 +80,17 @@ AC_SUBST(GIRDIR)
TYPELIBDIR="$($PKG_CONFIG --variable=typelibdir gobject-introspection-1.0)"
AC_SUBST(TYPELIBDIR)
# Stay command-line compatible with the gnome-common configure option. Here
# minimum/yes/maximum are the same, however.
AC_ARG_ENABLE(compile_warnings,
AC_HELP_STRING([--enable-compile-warnings=@<:@no/minimum/yes/maximum/error@:>@],
[Turn on compiler warnings]),,
enable_compile_warnings=error)
changequote(,)dnl
if test "$enable_compile_warnings" != no ; then
if test "x$GCC" = "xyes"; then
case " $CFLAGS " in
*[\ \ ]-Wall[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Wall" ;;
esac
case " $CFLAGS " in
*[\ \ ]-Wmissing-prototypes[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Wmissing-prototypes" ;;
esac
if test "$enable_compile_warnings" = error ; then
case " $CFLAGS " in
*[\ \ ]-Werror[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Werror" ;;
esac
fi
fi
if test "x$GCC" = "xyes"; then
case " $CFLAGS " in
*[\ \ ]-Wall[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Wall" ;;
esac
case " $OBJCFLAGS " in
*[\ \ ]-Wall[\ \ ]*) ;;
*) OBJCFLAGS="$OBJCFLAGS -Wall" ;;
esac
fi
changequote([,])dnl
@@ -128,5 +104,4 @@ AC_OUTPUT([
js/misc/Makefile
js/ui/Makefile
src/Makefile
po/Makefile.in
])

View File

@@ -1,39 +1,14 @@
desktopdir=$(datadir)/applications
desktop_DATA = gnome-shell.desktop
# We substitute in bindir so it works as an autostart
# file when built in a non-system prefix
gnome-shell.desktop.in: gnome-shell.desktop.in.in
$(AM_V_GEN) sed -e "s|@bindir[@]|$(bindir)|" \
-e "s|@VERSION[@]|$(VERSION)|" \
$< > $@ || rm $@
# Placeholder until we add intltool
gnome-shell.desktop: gnome-shell.desktop.in
$(AM_V_GEN) sed s/^_// < $< > $@ || rm $@
imagedir = $(pkgdatadir)/images
dist_image_DATA = \
add-workspace.svg \
back.svg \
close.svg \
close-black.svg \
info.svg \
magnifier.svg \
remove-workspace.svg
schemadir = @GCONF_SCHEMA_FILE_DIR@
schema_DATA = gnome-shell.schemas
install-data-local:
GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule $(srcdir)/$(schema_DATA)
GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule $(top_builddir)/data/$(schema_DATA)
EXTRA_DIST = \
gnome-shell.desktop.in.in \
EXTRA_DIST = \
$(schema_DATA)
CLEANFILES = \
gnome-shell.desktop.in \
$(desktop_DATA)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -1,7 +0,0 @@
<?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="12" height="16" viewBox="0 0 12 16" enable-background="new 0 0 29 18" xml:space="preserve" sodipodi:version="0.32" inkscape:version="0.46+devel" sodipodi:docname="back.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="12.592456" inkscape:cy="8.2696842" inkscape:window-x="145" inkscape:window-y="38" 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>
<path style="fill: rgb(255, 255, 255); fill-opacity: 1; stroke: none;" d="M 10,2 10,14 2,8 10,2 z" id="path43"/></svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -1,66 +0,0 @@
<?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:#000000" />
</svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -1,15 +0,0 @@
[Desktop Entry]
Type=Application
_Name=GNOME Shell
_Comment=Window management and application launching
Exec=@bindir@/gnome-shell
X-GNOME-Bugzilla-Bugzilla=GNOME
X-GNOME-Bugzilla-Product=gnome-shell
X-GNOME-Bugzilla-Component=general
X-GNOME-Bugzilla-Version=@VERSION@
Categories=GNOME;GTK;Utility;Core;
OnlyShowIn=GNOME;
NoDisplay=true
X-GNOME-Autostart-Phase=WindowManager
X-GNOME-Provides=panel;windowmanager;
X-GNOME-Autostart-Notify=true

View File

@@ -20,7 +20,7 @@
<applyto>/desktop/gnome/shell/favorite_apps</applyto>
<owner>gnome-shell</owner>
<type>list</type>
<list_type>string</list_type>
<listtype>string</listtype>
<default>[mozilla-firefox.desktop,evolution.desktop,openoffice.org-writer.desktop]</default>
<locale name="C">
<short>List of desktop file IDs for favorite applications</short>
@@ -30,49 +30,6 @@
</locale>
</schema>
<schema>
<key>/schemas/desktop/gnome/shell/sidebar/visible</key>
<applyto>/desktop/gnome/shell/sidebar/visible</applyto>
<owner>gnome-shell</owner>
<type>bool</type>
<default>false</default>
<locale name="C">
<short>Whether or not to display the sidebar</short>
<long>
Determines whether or not the sidebar is visible.
</long>
</locale>
</schema>
<schema>
<key>/schemas/desktop/gnome/shell/sidebar/expanded</key>
<applyto>/desktop/gnome/shell/sidebar/expanded</applyto>
<owner>gnome-shell</owner>
<type>bool</type>
<default>true</default>
<locale name="C">
<short>Whether the sidebar should be in the expanded (wide) mode</short>
<long>
Controls the expanded/collapsed state of the sidebar.
</long>
</locale>
</schema>
<schema>
<key>/schemas/desktop/gnome/shell/sidebar/widgets</key>
<applyto>/desktop/gnome/shell/sidebar/widgets</applyto>
<owner>gnome-shell</owner>
<type>list</type>
<list_type>string</list_type>
<default>[imports.ui.widget.ClockWidget,imports.ui.widget.AppsWidget,imports.ui.widget.RecentDocsWidget]</default>
<locale name="C">
<short>The widgets to display in the sidebar</short>
<long>
The widgets to display in the sidebar, in order from top to bottom. Each widget "name" is actually a JavaScript expression referring to a widget constructor object.
</long>
</locale>
</schema>
</schemalist>
</gconfschemafile>
</gconfschemafile>

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="29px"
height="18px"
viewBox="0 0 29 18"
enable-background="new 0 0 29 18"
xml:space="preserve"
sodipodi:version="0.32"
inkscape:version="0.46"
sodipodi:docname="search_2.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" /></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="0.0"
guidetolerance="10.0"
gridtolerance="10.0"
objecttolerance="10.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base"
showgrid="false"
inkscape:zoom="19.275862"
inkscape:cx="14.5"
inkscape:cy="9"
inkscape:window-x="40"
inkscape:window-y="40"
inkscape:current-layer="Foreground"><inkscape:grid
type="xygrid"
id="grid2391" /></sodipodi:namedview>
<path
style="fill:#4669a9;fill-opacity:1"
id="path9"
d="" />
<path
id="path3"
style="fill:#3d5a93;fill-opacity:1"
d="M 0,3 C 0,1.343 1.343,0 3,0 L 20,0 C 20.515,0 21.027,0.195 21.42,0.588 L 28.412,7.58 C 29.196,8.364 29.196,9.636 28.412,10.42 L 21.42,17.412 C 21.028,17.804 20.514,18 20,18 L 3,18 C 1.343,18 0,16.657 0,15 L 0,3 zM 13.5,3 C 11.015,3 9,5.015 9,7.5 C 9,8.2423219 9.1815696,8.9452421 9.5,9.5625 L 6.25,12.8125 C 5.931,13.1325 5.9310002,13.64975 6.25,13.96875 L 7.03125,14.78125 C 7.35025,15.10025 7.8674999,15.10025 8.1875,14.78125 L 11.46875,11.5 C 12.080227,11.810879 12.767137,12 13.5,12 C 15.985,12 18,9.985 18,7.5 C 18,5.015 15.985,3 13.5,3 z M 11.25,7.5 C 11.25,6.257 12.257,5.25 13.5,5.25 C 14.743,5.25 15.75,6.257 15.75,7.5 C 15.75,8.743 14.743,9.75 13.5,9.75 C 12.257,9.75 11.25,8.743 11.25,7.5 z" />
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -1,6 +1,5 @@
<?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#"
@@ -9,22 +8,22 @@
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"
version="1.1"
id="Foreground"
x="0px"
y="0px"
width="18"
height="18"
viewBox="0 0 18 18"
width="29px"
height="18px"
viewBox="0 0 29 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:version="0.46"
sodipodi:docname="search_1.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
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs14"><inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 9 : 1"
@@ -35,46 +34,46 @@
inkscape:window-height="728"
inkscape:window-width="1103"
inkscape:pageshadow="2"
inkscape:pageopacity="1"
inkscape:pageopacity="0.0"
guidetolerance="10.0"
gridtolerance="10.0"
objecttolerance="10.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#000000"
pagecolor="#ffffff"
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
showgrid="false"
inkscape:zoom="19.275862"
inkscape:cx="14.5"
inkscape:cy="9"
inkscape:window-x="40"
inkscape:window-y="40"
inkscape:current-layer="Foreground"><inkscape:grid
type="xygrid"
id="grid2391"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" /></sodipodi:namedview>
id="grid2391" /></sodipodi:namedview>
<path
d="M0,3c0-1.657,1.343-3,3-3h17c0.515,0,1.027,0.195,1.42,0.588l6.992,6.992c0.784,0.784,0.784,2.056,0,2.84l-6.992,6.992 C21.028,17.804,20.514,18,20,18H3c-1.657,0-3-1.343-3-3V3z"
id="path3"
style="fill:#151e2f;fill-opacity:1" />
<g
id="g5"
style="fill:#ffffff;fill-opacity:1"
transform="translate(-4,-0.023114)">
style="fill:#4669a9;fill-opacity:1">
<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"
fill="#FFFFFF"
d="M6.246,13.98c-0.319-0.319-0.319-0.837,0-1.157l3.717-3.717c0.319-0.319,0.837-0.319,1.157,0l0.786,0.787 c0.32,0.319,0.32,0.837,0,1.157l-3.717,3.717c-0.32,0.319-0.838,0.319-1.157,0L6.246,13.98L6.246,13.98z"
id="path7"
style="fill:#ffffff;fill-opacity:1" />
style="fill:#4669a9;fill-opacity:1" />
<path
d="M 9.076,11.937"
fill="#FFFFFF"
d="M9.076,11.937"
id="path9"
style="fill:#ffffff;fill-opacity:1" />
style="fill:#4669a9;fill-opacity:1" />
</g>
<path
fill-rule="evenodd"
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"
fill="#FFFFFF"
d="M11.25,7.5c0-1.243,1.007-2.25,2.25-2.25s2.25,1.007,2.25,2.25 s-1.007,2.25-2.25,2.25S11.25,8.743,11.25,7.5z M9,7.5C9,5.015,11.015,3,13.5,3S18,5.015,18,7.5S15.985,12,13.5,12S9,9.985,9,7.5z"
id="path11"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd" />
style="fill:#4669a9;fill-opacity:1" />
</svg>

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -1,53 +0,0 @@
<Project xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
xmlns:foaf="http://xmlns.com/foaf/0.1/"
xmlns:gnome="http://api.gnome.org/doap-extensions#"
xmlns="http://usefulinc.com/ns/doap#">
<name xml:lang="en">GNOME Shell</name>
<shortdesc xml:lang="en">Next generation GNOME desktop shell</shortdesc>
<!--
<homepage rdf:resource="http://live.gnome.org/GnomeShell" />
-->
<mailing-list rdf:resource="http://mail.gnome.org/mailman/listinfo/gnome-shell-list" />
<download-page rdf:resource="http://download.gnome.org/sources/gnome-shell/" />
<bug-database rdf:resource="http://bugzilla.gnome.org/browse.cgi?product=gnome-shell" />
<category rdf:resource="http://api.gnome.org/doap-extensions#desktop" />
<maintainer>
<foaf:Person>
<foaf:name>William Jon McCann</foaf:name>
<foaf:mbox rdf:resource="mailto:jmccann@redhat.com" />
<gnome:userid>mccann</gnome:userid>
</foaf:Person>
</maintainer>
<maintainer>
<foaf:Person>
<foaf:name>Owen Taylor</foaf:name>
<foaf:mbox rdf:resource="mailto:otaylor@redhat.com" />
<gnome:userid>otaylor</gnome:userid>
</foaf:Person>
</maintainer>
<maintainer>
<foaf:Person>
<foaf:name>Colin Walters</foaf:name>
<foaf:mbox rdf:resource="mailto:walters@verbum.org" />
<gnome:userid>cwalters</gnome:userid>
</foaf:Person>
</maintainer>
<maintainer>
<foaf:Person>
<foaf:name>Dan Winship</foaf:name>
<foaf:mbox rdf:resource="mailto:danw@gnome.org" />
<gnome:userid>danw</gnome:userid>
</foaf:Person>
</maintainer>
<maintainer>
<foaf:Person>
<foaf:name>Marina Zhurakhinskaya</foaf:name>
<foaf:mbox rdf:resource="mailto:marinaz@redhat.com" />
<gnome:userid>marinaz</gnome:userid>
</foaf:Person>
</maintainer>
</Project>

View File

@@ -1,4 +1,5 @@
jsmiscdir = $(pkgdatadir)/js/misc
dist_jsmisc_DATA = \
appInfo.js \
docInfo.js

134
js/misc/appInfo.js Normal file
View File

@@ -0,0 +1,134 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Shell = imports.gi.Shell;
const Main = imports.ui.main;
function AppInfo(appId) {
this._init(appId);
}
AppInfo.prototype = {
_init : function(appId) {
this.appId = appId;
this._gAppInfo = Gio.DesktopAppInfo.new(appId);
if (!this._gAppInfo) {
throw new Error('Unknown appId ' + appId);
}
this.id = this._gAppInfo.get_id();
this.name = this._gAppInfo.get_name();
this.description = this._gAppInfo.get_description();
this.executable = this._gAppInfo.get_executable();
this._gicon = this._gAppInfo.get_icon();
},
createIcon : function(size) {
if (this._gicon)
return Shell.TextureCache.get_default().load_gicon(this._gicon, size);
else
return new Clutter.Texture({ width: size, height: size });
},
getIconPath : function(size) {
if (this._gicon) {
let iconTheme = Gtk.IconTheme.get_default();
let previewIconInfo = iconTheme.lookup_by_gicon(this._gicon, size, 0);
if (previewIconInfo)
return previewIconInfo.get_filename();
}
return null;
},
launch : function() {
this._gAppInfo.launch([], Main.createAppLaunchContext());
}
};
var _infos = {};
// getAppInfo:
// @appId: an appId
//
// Gets an #AppInfo for @appId. This is preferable to calling
// new AppInfo() directly, because it caches #AppInfos.
//
// Return value: the new or cached #AppInfo, or %null if @appId
// doesn't point to a valid .desktop file
function getAppInfo(appId) {
let info = _infos[appId];
if (info === undefined) {
try {
info = _infos[appId] = new AppInfo(appId);
} catch (e) {
info = _infos[appId] = null;
}
}
return info;
}
// getTopApps:
// @count: maximum number of apps to retrieve
//
// Gets a list of #AppInfos for the @count most-frequently-used
// applications, with explicitly-chosen favorites first.
//
// Return value: the list of #AppInfo
function getTopApps(count) {
let appMonitor = Shell.AppMonitor.get_default();
let matches = [], alreadyAdded = {};
let favs = getFavorites();
for (let i = 0; i < favs.length && favs.length <= count; i++) {
let appId = favs[i].appId;
if (alreadyAdded[appId])
continue;
alreadyAdded[appId] = true;
matches.push(favs[i]);
}
// Ask for more apps than we need, since the list of recently used
// apps might contain an app we don't have a desktop file for
let apps = appMonitor.get_most_used_apps (0, Math.round(count * 1.5));
for (let i = 0; i < apps.length && matches.length <= count; i++) {
let appId = apps[i] + ".desktop";
if (alreadyAdded[appId])
continue;
alreadyAdded[appId] = true;
let appInfo = getAppInfo(appId);
if (appInfo) {
matches.push(appInfo);
}
}
return matches;
}
function _idListToInfos(ids) {
let infos = [];
for (let i = 0; i < ids.length; i++) {
let display = getAppInfo(ids[i]);
if (display == null)
continue;
infos.push(display);
}
return infos;
}
function getFavorites() {
let system = Shell.AppSystem.get_default();
return _idListToInfos(system.get_favorites());
}
function getRunning() {
let monitor = Shell.AppMonitor.get_default();
return _idListToInfos(monitor.get_running_app_ids());
}

View File

@@ -5,8 +5,6 @@ const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Shell = imports.gi.Shell;
const Lang = imports.lang;
const Signals = imports.signals;
const Main = imports.ui.main;
const THUMBNAIL_ICON_MARGIN = 2;
@@ -18,17 +16,40 @@ function DocInfo(recentInfo) {
DocInfo.prototype = {
_init : function(recentInfo) {
this._recentInfo = recentInfo;
// We actually used get_modified() instead of get_visited()
// here, as GtkRecentInfo doesn't updated get_visited()
// correctly. See http://bugzilla.gnome.org/show_bug.cgi?id=567094
this.timestamp = recentInfo.get_modified().getTime() / 1000;
this.name = recentInfo.get_display_name();
this.uri = recentInfo.get_uri();
this.mimeType = recentInfo.get_mime_type();
},
createIcon : function(size) {
return Shell.TextureCache.get_default().load_recent_thumbnail(size, this._recentInfo);
let icon = new Clutter.Texture();
let iconPixbuf;
if (this.uri.match("^file://"))
iconPixbuf = Shell.get_thumbnail(this.uri, this.mimeType);
if (iconPixbuf) {
// We calculate the width and height of the texture so as
// to preserve the aspect ratio of the thumbnail. Because
// the images generated based on thumbnails don't have an
// internal padding like system icons do, we create a
// slightly smaller texture and then create a group around
// it for padding purposes
let scalingFactor = (size - THUMBNAIL_ICON_MARGIN * 2) / Math.max(iconPixbuf.get_width(), iconPixbuf.get_height());
icon.set_width(Math.ceil(iconPixbuf.get_width() * scalingFactor));
icon.set_height(Math.ceil(iconPixbuf.get_height() * scalingFactor));
Shell.clutter_texture_set_from_pixbuf(icon, iconPixbuf);
let group = new Clutter.Group({ width: size,
height: size });
group.add_actor(icon);
icon.set_position(THUMBNAIL_ICON_MARGIN, THUMBNAIL_ICON_MARGIN);
return group;
} else {
Shell.clutter_texture_set_from_pixbuf(icon, this._recentInfo.get_icon(size));
return icon;
}
},
launch : function() {
@@ -42,7 +63,7 @@ DocInfo.prototype = {
if (appInfo != null) {
appInfo.launch_uris([this.uri], Main.createAppLaunchContext());
} else {
log("Failed to get default application info for mime type " + this.mimeType +
log("Failed to get default application info for mime type " + mimeType +
". Will try to use the last application that registered the document.");
let appName = this._recentInfo.last_application();
let [success, appExec, count, time] = this._recentInfo.get_application_info(appName);
@@ -80,62 +101,14 @@ DocInfo.prototype = {
exists : function() {
return this._recentInfo.exists();
},
lastVisited : function() {
// We actually used get_modified() instead of get_visited()
// here, as GtkRecentInfo doesn't updated get_visited()
// correctly. See
// http://bugzilla.gnome.org/show_bug.cgi?id=567094
return this._recentInfo.get_modified();
}
};
var docManagerInstance = null;
function getDocManager() {
if (docManagerInstance == null)
docManagerInstance = new DocManager();
return docManagerInstance;
}
function DocManager() {
this._init();
}
DocManager.prototype = {
_init: function() {
this._recentManager = Gtk.RecentManager.get_default();
this._items = {};
this._recentManager.connect('changed', Lang.bind(this, function(recentManager) {
this._reload();
this.emit('changed');
}));
this._reload();
},
_reload: function() {
let docs = this._recentManager.get_items();
let newItems = {};
for (let i = 0; i < docs.length; i++) {
let recentInfo = docs[i];
if (!recentInfo.exists())
continue;
let docInfo = new DocInfo(recentInfo);
// we use GtkRecentInfo URI as an item Id
newItems[docInfo.uri] = docInfo;
}
let deleted = {};
for (var uri in this._items) {
if (!(uri in newItems))
deleted[uri] = this._items[uri];
}
/* If we'd cached any thumbnail references that no longer exist,
dump them here */
let texCache = Shell.TextureCache.get_default();
for (var uri in deleted) {
texCache.evict_recent_thumbnail(this._items[uri]);
}
this._items = newItems;
},
getItems: function() {
return this._items;
}
}
Signals.addSignalMethods(DocManager.prototype);

View File

@@ -3,19 +3,15 @@ jsuidir = $(pkgdatadir)/js/ui
dist_jsui_DATA = \
altTab.js \
appDisplay.js \
appIcon.js \
button.js \
chrome.js \
dash.js \
dnd.js \
docDisplay.js \
genericDisplay.js \
link.js \
lookingGlass.js \
main.js \
overview.js \
overlay.js \
panel.js \
places.js \
runDialog.js \
sidebar.js \
tweener.js \

View File

@@ -36,36 +36,38 @@ function AltTabPopup() {
AltTabPopup.prototype = {
_init : function() {
let global = Shell.Global.get();
let global = Shell.Global.get();
this.actor = new Big.Box({ background_color : POPUP_BG_COLOR,
this.actor = new Big.Box({ background_color : POPUP_BG_COLOR,
corner_radius: POPUP_GRID_SPACING,
padding: POPUP_GRID_SPACING,
spacing: POPUP_GRID_SPACING,
orientation: Big.BoxOrientation.VERTICAL });
// Icon grid. TODO: Investigate Nbtk.Grid once that lands. Currently
// just implemented using a chain of Big.Box.
this._grid = new Big.Box({ spacing: POPUP_GRID_SPACING,
// Icon grid. It would be nice to use Tidy.Grid for the this,
// but Tidy.Grid is lame in various ways. (Eg, it seems to
// have a minimum size of 200x200.) So we create a vertical
// Big.Box containing multiple horizontal Big.Boxes.
this._grid = new Big.Box({ spacing: POPUP_GRID_SPACING,
orientation: Big.BoxOrientation.VERTICAL });
let gcenterbox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
x_align: Big.BoxAlignment.CENTER });
gcenterbox.append(this._grid, Big.BoxPackFlags.NONE);
this.actor.append(gcenterbox, Big.BoxPackFlags.NONE);
this.actor.append(gcenterbox, Big.BoxPackFlags.NONE);
// Selected-window label
this._label = new Clutter.Text({ font_name: "Sans 16px",
this._label = new Clutter.Text({ font_name: "Sans 16px",
ellipsize: Pango.EllipsizeMode.END });
let labelbox = new Big.Box({ background_color: POPUP_INDICATOR_COLOR,
corner_radius: POPUP_GRID_SPACING / 2,
padding: POPUP_GRID_SPACING / 2 });
labelbox.append(this._label, Big.BoxPackFlags.NONE);
labelbox.append(this._label, Big.BoxPackFlags.EXPAND);
let lcenterbox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
x_align: Big.BoxAlignment.CENTER,
width: POPUP_LABEL_MAX_WIDTH + POPUP_GRID_SPACING });
lcenterbox.append(labelbox, Big.BoxPackFlags.NONE);
this.actor.append(lcenterbox, Big.BoxPackFlags.NONE);
this.actor.append(lcenterbox, Big.BoxPackFlags.NONE);
// Indicator around selected icon
this._indicator = new Big.Rectangle({ border_width: POPUP_INDICATOR_WIDTH,
@@ -74,10 +76,10 @@ AltTabPopup.prototype = {
color: POPUP_TRANSPARENT });
this.actor.append(this._indicator, Big.BoxPackFlags.FIXED);
this._items = [];
this._items = [];
this._toplevels = global.window_group.get_children();
global.stage.add_actor(this.actor);
global.stage.add_actor(this.actor);
// Dark translucent window used to cover all but the
// currently-selected window while Alt-Tabbing. Actually
@@ -140,7 +142,9 @@ AltTabPopup.prototype = {
},
show : function(initialSelection) {
let global = Shell.Global.get();
let global = Shell.Global.get();
Main.startModal();
global.window_group.add_actor(this._overlay);
this._overlay.raise_top();
@@ -150,16 +154,18 @@ AltTabPopup.prototype = {
time: SHOW_TIME,
transition: "easeOutQuad" });
this.actor.show_all();
this.actor.x = Math.floor((global.screen_width - this.actor.width) / 2);
this.actor.y = Math.floor((global.screen_height - this.actor.height) / 2);
this.actor.show_all();
this.actor.x = (global.screen_width - this.actor.width) / 2;
this.actor.y = (global.screen_height - this.actor.height) / 2;
this.select(initialSelection);
},
destroy : function() {
this.actor.destroy();
this.actor.destroy();
this._overlay.destroy();
Main.endModal();
},
select : function(n) {
@@ -234,7 +240,7 @@ AltTabPopup.prototype = {
},
_adjust_overlay : function() {
let global = Shell.Global.get();
let global = Shell.Global.get();
if (this._selected && this._selected.icon_rect) {
// We want to highlight a specific rectangle within the

View File

@@ -3,28 +3,22 @@
const Big = imports.gi.Big;
const Clutter = imports.gi.Clutter;
const Pango = imports.gi.Pango;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Tidy = imports.gi.Tidy;
const Shell = imports.gi.Shell;
const Lang = imports.lang;
const Signals = imports.signals;
const Mainloop = imports.mainloop;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const AppIcon = imports.ui.appIcon;
const AppInfo = imports.misc.appInfo;
const DND = imports.ui.dnd;
const GenericDisplay = imports.ui.genericDisplay;
const Main = imports.ui.main;
const Workspaces = imports.ui.workspaces;
const ENTERED_MENU_COLOR = new Clutter.Color();
ENTERED_MENU_COLOR.from_pixel(0x00ff0022);
const WELL_DEFAULT_COLUMNS = 4;
const WELL_ITEM_HSPACING = 0;
const WELL_ITEM_VSPACING = 4;
const APP_ICON_SIZE = 48;
const MENU_ICON_SIZE = 24;
const MENU_SPACING = 15;
@@ -34,23 +28,20 @@ const MAX_ITEMS = 30;
/* This class represents a single display item containing information about an application.
*
* appInfo - AppInfo object containing information about the application
* availableWidth - total width available for the item
*/
function AppDisplayItem(appInfo) {
this._init(appInfo);
function AppDisplayItem(appInfo, availableWidth) {
this._init(appInfo, availableWidth);
}
AppDisplayItem.prototype = {
__proto__: GenericDisplay.GenericDisplayItem.prototype,
_init : function(appInfo) {
GenericDisplay.GenericDisplayItem.prototype._init.call(this);
_init : function(appInfo, availableWidth) {
GenericDisplay.GenericDisplayItem.prototype._init.call(this, availableWidth);
this._appInfo = appInfo;
this._setItemInfo(appInfo.get_name(), appInfo.get_description());
},
getId: function() {
return this._appInfo.get_id();
this._setItemInfo(appInfo.name, appInfo.description);
},
//// Public method overrides ////
@@ -64,16 +55,24 @@ AppDisplayItem.prototype = {
// Returns an icon for the item.
_createIcon : function() {
return this._appInfo.create_icon_texture(GenericDisplay.ITEM_DISPLAY_ICON_SIZE);
return this._appInfo.createIcon(GenericDisplay.ITEM_DISPLAY_ICON_SIZE);
},
// Returns a preview icon for the item.
_createPreviewIcon : function() {
return this._appInfo.create_icon_texture(GenericDisplay.PREVIEW_ICON_SIZE);
},
// Ensures the preview icon is created.
_ensurePreviewIconCreated : function() {
if (!this._showPreview || this._previewIcon)
return;
shellWorkspaceLaunch: function() {
this.launch();
let previewIconPath = this._appInfo.getIconPath(GenericDisplay.PREVIEW_ICON_SIZE);
if (previewIconPath) {
try {
this._previewIcon = new Clutter.Texture({ width: GenericDisplay.PREVIEW_ICON_SIZE, height: GenericDisplay.PREVIEW_ICON_SIZE});
this._previewIcon.set_from_file(previewIconPath);
} catch (e) {
// we can get an error here if the file path doesn't exist on the system
log('Error loading AppDisplayItem preview icon ' + e);
}
}
}
};
@@ -114,28 +113,24 @@ MenuItem.prototype = {
}
if (pixbuf != null)
Shell.clutter_texture_set_from_pixbuf(this._icon, pixbuf);
this.actor.append(this._icon, Big.BoxPackFlags.NONE);
this.actor.append(this._icon, 0);
this._text = new Clutter.Text({ color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
font_name: "Sans 14px",
ellipsize: Pango.EllipsizeMode.END,
text: name });
this.actor.append(this._text, Big.BoxPackFlags.EXPAND);
// We use individual boxes for the label and the arrow to ensure that they
// are aligned vertically. Just setting y_align: Big.BoxAlignment.CENTER
// on this.actor does not seem to achieve that.
let labelBox = new Big.Box({ y_align: Big.BoxAlignment.CENTER });
let box = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
y_align: Big.BoxAlignment.CENTER
});
labelBox.append(this._text, Big.BoxPackFlags.NONE);
this.actor.append(labelBox, Big.BoxPackFlags.EXPAND);
let arrowBox = new Big.Box({ y_align: Big.BoxAlignment.CENTER });
this._arrow = new Shell.Arrow({ surface_width: MENU_ICON_SIZE / 2,
surface_height: MENU_ICON_SIZE / 2,
this._arrow = new Shell.Arrow({ surface_width: MENU_ICON_SIZE/2,
surface_height: MENU_ICON_SIZE/2,
direction: Gtk.ArrowType.RIGHT,
opacity: 0 });
arrowBox.append(this._arrow, Big.BoxPackFlags.NONE);
this.actor.append(arrowBox, Big.BoxPackFlags.NONE);
opacity: 0
});
box.append(this._arrow, 0);
this.actor.append(box, 0);
},
getState: function() {
@@ -165,16 +160,19 @@ Signals.addSignalMethods(MenuItem.prototype);
/* This class represents a display containing a collection of application items.
* The applications are sorted based on their popularity by default, and based on
* their name if some search filter is applied.
*
* width - width available for the display
* height - height available for the display
*/
function AppDisplay() {
this._init();
function AppDisplay(width, height, numberOfColumns, columnGap) {
this._init(width, height, numberOfColumns, columnGap);
}
AppDisplay.prototype = {
__proto__: GenericDisplay.GenericDisplay.prototype,
_init : function() {
GenericDisplay.GenericDisplay.prototype._init.call(this);
_init : function(width, height, numberOfColumns, columnGap) {
GenericDisplay.GenericDisplay.prototype._init.call(this, width, height, numberOfColumns, columnGap);
this._menus = [];
this._menuDisplays = [];
@@ -193,15 +191,12 @@ AppDisplay.prototype = {
this._appSystem.connect('favorites-changed', Lang.bind(this, function(appSys) {
this._redisplay(false);
}));
this._appMonitor.connect('app-added', Lang.bind(this, function(monitor) {
this._redisplay(false);
}));
this._appMonitor.connect('app-removed', Lang.bind(this, function(monitor) {
this._appMonitor.connect('changed', Lang.bind(this, function(monitor) {
this._redisplay(false);
}));
// Load the apps now so it doesn't slow down the first
// transition into the Overview
// Load the GAppInfos now so it doesn't slow down the first
// transition into the overlay
this._refreshCache();
this._focusInMenus = true;
@@ -216,7 +211,6 @@ AppDisplay.prototype = {
this.connect('expanded', Lang.bind(this, function (self) {
this._filterReset();
}));
this._filterReset();
},
moveRight: function() {
@@ -236,7 +230,7 @@ AppDisplay.prototype = {
},
// Override genericDisplay.js
getNavigationArea: function() {
getSideArea: function() {
return this._menuDisplay;
},
@@ -257,13 +251,16 @@ AppDisplay.prototype = {
// Protected overrides
_filterActive: function() {
// We always have a filter now since a menu must be selected
return true;
return !!this._search || this._activeMenuIndex >= 0;
},
_filterReset: function() {
GenericDisplay.GenericDisplay.prototype._filterReset.call(this);
this._selectMenuIndex(0);
if (this._activeMenu != null)
this._activeMenu.setState(MENU_UNSELECTED);
this._activeMenuIndex = -1;
this._activeMenu = null;
this._focusInMenu = true;
},
//// Private ////
@@ -278,53 +275,48 @@ AppDisplay.prototype = {
this._menuDisplays[index].setState(MENU_SELECTED);
},
_getMostUsed: function() {
let context = "";
return this._appMonitor.get_most_used_apps(context, 30).map(Lang.bind(this, function (id) {
return this._appSystem.lookup_cached_app(id);
})).filter(function (e) { return e != null });
},
_addMenuItem: function(name, id, icon, index) {
let display = new MenuItem(name, id, icon);
this._menuDisplays.push(display);
display.connect('state-changed', Lang.bind(this, function (display) {
let activated = display.getState() != MENU_UNSELECTED;
if (!activated && display == this._activeMenu) {
this._activeMenuIndex = -1;
this._activeMenu = null;
} else if (activated) {
if (display != this._activeMenu && this._activeMenu != null)
this._activeMenu.setState(MENU_UNSELECTED);
this._activeMenuIndex = index;
this._activeMenu = display;
if (id == null) {
this._activeMenuApps = this._getMostUsed();
} else {
this._activeMenuApps = this._appSystem.get_applications_for_menu(id);
}
}
this._redisplay(true);
}));
this._menuDisplay.append(display.actor, 0);
},
_redisplayMenus: function() {
this._menuDisplay.remove_all();
this._addMenuItem(_("Frequent"), null, 'gtk-select-all');
for (let i = 0; i < this._menus.length; i++) {
let menu = this._menus[i];
this._addMenuItem(menu.name, menu.id, menu.icon, i+1);
let display = new MenuItem(menu.name, menu.id, menu.icon);
this._menuDisplays.push(display);
let menuIndex = i;
display.connect('state-changed', Lang.bind(this, function (display) {
let activated = display.getState() != MENU_UNSELECTED;
if (!activated && display == this._activeMenu) {
this._activeMenuIndex = -1;
this._activeMenu = null;
} else if (activated) {
if (display != this._activeMenu && this._activeMenu != null)
this._activeMenu.setState(MENU_UNSELECTED);
this._activeMenuIndex = menuIndex;
this._activeMenu = display;
this._activeMenuApps = this._appSystem.get_applications_for_menu(menu.id);
}
this._redisplay();
}));
this._menuDisplay.append(display.actor, 0);
}
},
_addAppForId: function(appId) {
let appInfo = AppInfo.getAppInfo(appId);
if (appInfo != null) {
this._addApp(appInfo);
} else {
log("appInfo for " + appId + " was not found.");
}
},
_addApp: function(appInfo) {
let appId = appInfo.get_id();
let appId = appInfo.id;
this._allItems[appId] = appInfo;
// [] is returned if we could not get the categories or the list of categories was empty
let categories = Shell.get_categories_for_desktop_file(appId);
this._appCategories[appId] = categories;
},
//// Protected method overrides ////
// Gets information about all applications by calling Gio.app_info_get_all().
_refreshCache : function() {
let me = this;
@@ -333,21 +325,16 @@ AppDisplay.prototype = {
this._allItems = {};
this._appCategories = {};
this._menus = this._appSystem.get_menus();
// Loop over the toplevel menu items, load the set of desktop file ids
// associated with each one, skipping empty menus
let allMenus = this._appSystem.get_menus();
this._menus = [];
for (let i = 0; i < allMenus.length; i++) {
let menu = allMenus[i];
// associated with each one
for (let i = 0; i < this._menus.length; i++) {
let menu = this._menus[i];
let menuApps = this._appSystem.get_applications_for_menu(menu.id);
let hasVisibleApps = menuApps.some(function (app) { return !app.get_is_nodisplay(); });
if (!hasVisibleApps) {
continue;
}
this._menus.push(menu);
for (let j = 0; j < menuApps.length; j++) {
let app = menuApps[j];
this._addApp(app);
let appId = menuApps[j];
this._addAppForId(appId);
}
}
@@ -355,16 +342,17 @@ AppDisplay.prototype = {
// These show up in search, but not with the rest of apps.
let settings = this._appSystem.get_all_settings();
for (let i = 0; i < settings.length; i++) {
let app = settings[i];
this._addApp(app);
let appId = settings[i];
this._addAppForId(appId);
}
this._appsStale = false;
},
// Stub this out; the app display always has a category selected
// Sets the list of the displayed items based on the most used apps.
_setDefaultList : function() {
this._matchedItems = [];
let matchedInfos = AppInfo.getTopApps(MAX_ITEMS);
this._matchedItems = matchedInfos.map(function(info) { return info.appId; });
},
// Compares items associated with the item ids based on the alphabetical order
@@ -373,17 +361,14 @@ AppDisplay.prototype = {
_compareItems : function(itemIdA, itemIdB) {
let appA = this._allItems[itemIdA];
let appB = this._allItems[itemIdB];
return appA.get_name().localeCompare(appB.get_name());
return appA.name.localeCompare(appB.name);
},
// Checks if the item info can be a match for the search string by checking
// the name, description, execution command, and categories for the application.
// Item info is expected to be Shell.AppInfo.
// the name, description, execution command, and categories for the application.
// Item info is expected to be GAppInfo.
// Returns a boolean flag indicating if itemInfo is a match.
_isInfoMatching : function(itemInfo, search) {
// Don't show nodisplay items here
if (itemInfo.get_is_nodisplay())
return false;
// Search takes precedence; not typically useful to search within a
// menu
if (this._activeMenu == null || search != "")
@@ -393,10 +378,10 @@ AppDisplay.prototype = {
},
_isInfoMatchingMenu : function(itemInfo, search) {
let id = itemInfo.get_id();
let id = itemInfo.id;
for (let i = 0; i < this._activeMenuApps.length; i++) {
let activeApp = this._activeMenuApps[i];
if (activeApp.get_id() == id)
let activeId = this._activeMenuApps[i];
if (activeId == id)
return true;
}
return false;
@@ -406,43 +391,39 @@ AppDisplay.prototype = {
if (search == null || search == '')
return true;
let fold = function(s) {
if (!s)
return s;
return GLib.utf8_casefold(GLib.utf8_normalize(s, -1,
GLib.NormalizeMode.ALL), -1);
};
let name = fold(itemInfo.get_name());
let name = itemInfo.name.toLowerCase();
if (name.indexOf(search) >= 0)
return true;
let description = fold(itemInfo.get_description());
let description = itemInfo.description;
if (description) {
description = description.toLowerCase();
if (description.indexOf(search) >= 0)
return true;
}
let exec = fold(itemInfo.get_executable());
if (exec == null) {
if (itemInfo.executable == null) {
log("Missing an executable for " + itemInfo.name);
} else {
let exec = itemInfo.executable.toLowerCase();
if (exec.indexOf(search) >= 0)
return true;
}
let categories = itemInfo.get_categories();
// we expect this._appCategories.hasOwnProperty(itemInfo.id) to always be true here
let categories = this._appCategories[itemInfo.id];
for (let i = 0; i < categories.length; i++) {
let category = fold(categories[i]);
let category = categories[i].toLowerCase();
if (category.indexOf(search) >= 0)
return true;
}
return false;
},
// Creates an AppDisplayItem based on itemInfo, which is expected be an Shell.AppInfo object.
// Creates an AppDisplayItem based on itemInfo, which is expected be an AppInfo object.
_createDisplayItem: function(itemInfo) {
return new AppDisplayItem(itemInfo);
return new AppDisplayItem(itemInfo, this._columnWidth);
}
};
@@ -453,250 +434,142 @@ function WellDisplayItem(appInfo, isFavorite) {
}
WellDisplayItem.prototype = {
__proto__ : AppIcon.AppIcon.prototype,
_init : function(appInfo, isFavorite) {
AppIcon.AppIcon.prototype._init.call(this, appInfo);
this.appInfo = appInfo;
this.isFavorite = isFavorite;
this.actor = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
width: APP_ICON_SIZE,
reactive: true });
this.actor._delegate = this;
this.actor.connect('button-release-event', Lang.bind(this, function (b, e) {
this._handleActivate();
this.launch();
this.emit('activated');
}));
let draggable = DND.makeDraggable(this.actor);
},
_handleActivate: function () {
if (this._windows.length == 0) {
this.appInfo.launch();
Main.overview.hide();
this._icon = appInfo.createIcon(APP_ICON_SIZE);
this.actor.append(this._icon, Big.BoxPackFlags.NONE);
let count = Shell.AppMonitor.get_default().get_window_count(appInfo.appId);
this._name = new Clutter.Text({ color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
font_name: "Sans 12px",
ellipsize: Pango.EllipsizeMode.END,
text: appInfo.name });
if (count > 0) {
let runningBox = new Big.Box({ /* border_color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
border: 1,
padding: 1 */ });
runningBox.append(this._name, Big.BoxPackFlags.EXPAND);
this.actor.append(runningBox, Big.BoxPackFlags.NONE);
} else {
/* Pick the first window and activate it;
* In the future, we want to have a menu dropdown here. */
let first = this._windows[0];
Main.overview.activateWindow(first, Clutter.get_current_event_time());
this.actor.append(this._name, Big.BoxPackFlags.NONE);
}
},
shellWorkspaceLaunch : function() {
// Here we just always launch the application again, even if we know
// it was already running. For most applications this
// should have the effect of creating a new window, whether that's
// a second process (in the case of Calculator) or IPC to existing
// instance (Firefox). There are a few less-sensical cases such
// as say Pidgin, but ideally what we do there is have the app
// express to us that it doesn't do relaunch=new-window in the
// .desktop file.
// Opens an application represented by this display item.
launch : function() {
this.appInfo.launch();
},
// Draggable interface - FIXME deduplicate with GenericDisplay
getDragActor: function(stageX, stageY) {
return this.appInfo.create_icon_texture(this._icon.height);
this.dragActor = this.appInfo.createIcon(APP_ICON_SIZE);
// If the user dragged from the icon itself, then position
// the dragActor over the original icon. Otherwise center it
// around the pointer
let [iconX, iconY] = this._icon.get_transformed_position();
let [iconWidth, iconHeight] = this._icon.get_transformed_size();
if (stageX > iconX && stageX <= iconX + iconWidth &&
stageY > iconY && stageY <= iconY + iconHeight)
this.dragActor.set_position(iconX, iconY);
else
this.dragActor.set_position(stageX - this.dragActor.width / 2, stageY - this.dragActor.height / 2);
return this.dragActor;
},
// Returns the original icon that is being used as a source for the cloned texture
// that represents the item as it is being dragged.
getDragActorSource: function() {
return this._icon;
},
setWidth: function(width) {
this._nameBox.width = width + GLOW_PADDING * 2;
}
};
function WellGrid() {
this._init();
Signals.addSignalMethods(WellDisplayItem.prototype);
function WellArea(width, isFavorite) {
this._init(width, isFavorite);
}
WellGrid.prototype = {
_init: function() {
this.actor = new Shell.GenericContainer();
WellArea.prototype = {
_init : function(width, isFavorite) {
this.isFavorite = isFavorite;
this._separator = new Big.Box({ height: 1 });
this.actor.add_actor(this._separator);
this._separatorIndex = 0;
this._cachedSeparatorY = 0;
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 = new Tidy.Grid({ width: width });
this.actor._delegate = this;
},
_getPreferredWidth: function (grid, forHeight, alloc) {
let [itemMin, itemNatural] = this._getItemPreferredWidth();
let children = this._getItemChildren();
let nColumns;
if (children.length < WELL_DEFAULT_COLUMNS)
nColumns = children.length;
else
nColumns = WELL_DEFAULT_COLUMNS;
let spacing = Math.max(nColumns - 1, 0) * WELL_ITEM_HSPACING;
alloc.min_size = itemMin * nColumns + spacing;
alloc.natural_size = itemNatural * nColumns + spacing;
redisplay: function (infos) {
let children;
children = this.actor.get_children();
children.forEach(Lang.bind(this, function (v) {
v.destroy();
}));
for (let i = 0; i < infos.length; i++) {
let display = new WellDisplayItem(infos[i], this.isFavorite);
display.connect('activated', Lang.bind(this, function (display) {
this.emit('activated', display);
}));
this.actor.add_actor(display.actor);
};
},
_getPreferredHeight: function (grid, forWidth, alloc) {
let [rows, columns, itemWidth, itemHeight] = this._computeLayout(forWidth);
let totalVerticalSpacing = Math.max(rows - 1, 0) * WELL_ITEM_VSPACING;
// Draggable target interface
acceptDrop : function(source, actor, x, y, time) {
let global = Shell.Global.get();
let [separatorMin, separatorNatural] = this._separator.get_preferred_height(forWidth);
alloc.min_size = alloc.natural_size = rows * itemHeight + totalVerticalSpacing + separatorNatural;
},
_allocate: function (grid, box, flags) {
let children = this._getItemChildren();
let availWidth = box.x2 - box.x1;
let availHeight = box.y2 - box.y1;
let [rows, columns, itemWidth, itemHeight] = this._computeLayout(availWidth);
let [separatorMin, separatorNatural] = this._separator.get_preferred_height(-1);
let x = box.x1;
let y = box.y1;
let columnIndex = 0;
for (let i = 0; i < children.length; i++) {
let [childMinWidth, childNaturalWidth] = children[i].get_preferred_width(-1);
/* Center the item in its allocation horizontally */
let width = Math.min(itemWidth, childNaturalWidth);
let horizSpacing = (itemWidth - width) / 2;
let childBox = new Clutter.ActorBox();
childBox.x1 = Math.floor(x + horizSpacing);
childBox.y1 = y;
childBox.x2 = childBox.x1 + width;
childBox.y2 = childBox.y1 + itemHeight;
children[i].allocate(childBox, flags);
let atSeparator = (i == this._separatorIndex - 1);
columnIndex++;
if (columnIndex == columns || atSeparator) {
columnIndex = 0;
}
if (columnIndex == 0) {
y += itemHeight + WELL_ITEM_VSPACING;
x = box.x1;
} else {
x += itemWidth + WELL_ITEM_HSPACING;
}
if (atSeparator) {
y += separatorNatural + WELL_ITEM_VSPACING;
}
if (!(source instanceof WellDisplayItem)) {
return false;
}
let separatorRowIndex = Math.ceil(this._separatorIndex / columns);
/* Allocate the separator */
let childBox = new Clutter.ActorBox();
childBox.x1 = box.x1;
childBox.y1 = (itemHeight + WELL_ITEM_VSPACING) * separatorRowIndex;
this._cachedSeparatorY = childBox.y1;
childBox.x2 = box.x2;
childBox.y2 = childBox.y1+separatorNatural;
this._separator.allocate(childBox, flags);
},
setSeparatorIndex: function (index) {
this._separatorIndex = index;
this.actor.queue_relayout();
},
removeAll: function () {
let itemChildren = this._getItemChildren();
for (let i = 0; i < itemChildren.length; i++) {
itemChildren[i].destroy();
}
this._separatorIndex = 0;
},
isBeforeSeparator: function(x, y) {
return y < this._cachedSeparatorY;
},
_getItemChildren: function () {
let children = this.actor.get_children();
children.shift();
return children;
},
_computeLayout: function (forWidth) {
let [itemMinWidth, itemNaturalWidth] = this._getItemPreferredWidth();
let columnsNatural;
let i;
let children = this._getItemChildren();
if (children.length == 0)
return [0, WELL_DEFAULT_COLUMNS, 0, 0];
let nColumns;
if (children.length < WELL_DEFAULT_COLUMNS)
nColumns = children.length;
else
nColumns = WELL_DEFAULT_COLUMNS;
if (forWidth >= 0 && forWidth < minWidth) {
log("WellGrid: trying to allocate for width " + forWidth + " but min is " + minWidth);
/* FIXME - we should fall back to fewer than WELL_DEFAULT_COLUMNS here */
}
let horizSpacingTotal = Math.max(nColumns - 1, 0) * WELL_ITEM_HSPACING;
let minWidth = itemMinWidth * nColumns + horizSpacingTotal;
let lastColumnIndex = nColumns - 1;
let separatorColumns = lastColumnIndex - ((lastColumnIndex + this._separatorIndex) % nColumns);
let rows = Math.ceil((children.length + separatorColumns) / nColumns);
let itemWidth;
if (forWidth < 0) {
itemWidth = itemNaturalWidth;
let appSystem = Shell.AppSystem.get_default();
let id = source.appInfo.appId;
if (source.isFavorite && (!this.isFavorite)) {
Mainloop.idle_add(function () {
appSystem.remove_favorite(id);
});
} else if ((!source.isFavorite) && this.isFavorite) {
Mainloop.idle_add(function () {
appSystem.add_favorite(id);
});
} else {
itemWidth = Math.max(forWidth - horizSpacingTotal, 0) / nColumns;
return false;
}
let itemNaturalHeight = 0;
for (let i = 0; i < children.length; i++) {
let [childMin, childNatural] = children[i].get_preferred_height(itemWidth);
if (childNatural > itemNaturalHeight)
itemNaturalHeight = childNatural;
}
return [rows, WELL_DEFAULT_COLUMNS, itemWidth, itemNaturalHeight];
},
_getItemPreferredWidth: function () {
let children = this._getItemChildren();
let minWidth = 0;
let naturalWidth = 0;
for (let i = 0; i < children.length; i++) {
let [childMin, childNatural] = children[i].get_preferred_width(-1);
if (childMin > minWidth)
minWidth = childMin;
if (childNatural > naturalWidth)
naturalWidth = childNatural;
}
return [minWidth, naturalWidth];
return true;
}
}
function AppWell() {
this._init();
Signals.addSignalMethods(WellArea.prototype);
function AppWell(width) {
this._init(width);
}
AppWell.prototype = {
_init : function() {
_init : function(width) {
this._menus = [];
this._menuDisplays = [];
this.actor = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
x_align: Big.BoxAlignment.CENTER });
this.actor._delegate = this;
this._grid = new WellGrid();
this.actor.append(this._grid.actor, Big.BoxPackFlags.EXPAND);
width: width });
this._appSystem = Shell.AppSystem.get_default();
this._appMonitor = Shell.AppMonitor.get_default();
@@ -707,107 +580,44 @@ AppWell.prototype = {
this._appSystem.connect('favorites-changed', Lang.bind(this, function(appSys) {
this._redisplay();
}));
this._appMonitor.connect('window-added', Lang.bind(this, function(monitor) {
this._appMonitor.connect('changed', Lang.bind(this, function(monitor) {
this._redisplay();
}));
this._appMonitor.connect('window-removed', Lang.bind(this, function(monitor) {
this._redisplay();
this._favoritesArea = new WellArea(width, true);
this._favoritesArea.connect('activated', Lang.bind(this, function (a, display) {
this.emit('activated');
}));
this.actor.append(this._favoritesArea.actor, Big.BoxPackFlags.NONE);
this._runningBox = new Big.Box({ border_color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
border: 1,
corner_radius: 3,
padding: GenericDisplay.PREVIEW_BOX_PADDING });
this._runningArea = new WellArea(width, false);
this._runningArea.connect('activated', Lang.bind(this, function (a, display) {
this.emit('activated');
}));
this._runningBox.append(this._runningArea.actor, Big.BoxPackFlags.EXPAND);
this.actor.append(this._runningBox, Big.BoxPackFlags.NONE);
this._redisplay();
},
_lookupApps: function(appIds) {
let result = [];
for (let i = 0; i < appIds.length; i++) {
let id = appIds[i];
let app = this._appSystem.lookup_cached_app(id);
if (!app)
continue;
result.push(app);
}
return result;
},
_arrayValues: function(array) {
return array.reduce(function (values, id, index) {
values[id] = index; return values; }, {});
},
_redisplay: function () {
this._grid.removeAll();
let favoriteIds = this._appSystem.get_favorites();
let favoriteIdsHash = this._arrayValues(favoriteIds);
/* hardcode here pending some design about how exactly desktop contexts behave */
let contextId = "";
let running = this._appMonitor.get_running_apps(contextId).filter(function (e) {
return !(e.get_id() in favoriteIdsHash);
_redisplay: function() {
let arrayToObject = function(a) {
let o = {};
for (let i = 0; i < a.length; i++)
o[a[i]] = 1;
return o;
};
let favorites = AppInfo.getFavorites();
let favoriteIds = arrayToObject(favorites.map(function (e) { return e.appId; }));
let running = AppInfo.getRunning().filter(function (e) {
return !(e.appId in favoriteIds);
});
let favorites = this._lookupApps(favoriteIds);
let displays = []
this._addApps(favorites, true);
this._grid.setSeparatorIndex(favorites.length);
this._addApps(running, false);
this._displays = displays;
},
_addApps: function(apps) {
for (let i = 0; i < apps.length; i++) {
let app = apps[i];
let display = new WellDisplayItem(app, this.isFavorite);
this._grid.actor.add_actor(display.actor);
}
},
// Draggable target interface
acceptDrop : function(source, actor, x, y, time) {
let global = Shell.Global.get();
let appSystem = Shell.AppSystem.get_default();
let app = null;
if (source instanceof WellDisplayItem) {
app = source.appInfo;
} else if (source instanceof AppDisplayItem) {
app = appSystem.lookup_cached_app(source.getId());
} else if (source instanceof Workspaces.WindowClone) {
let appMonitor = Shell.AppMonitor.get_default();
app = appMonitor.get_window_app(source.metaWindow);
}
// Don't allow favoriting of transient apps
if (app == null || app.is_transient()) {
return false;
}
let id = app.get_id();
let favoriteIds = this._appSystem.get_favorites();
let favoriteIdsObject = this._arrayValues(favoriteIds);
let dropIsFavorite = this._grid.isBeforeSeparator(x - this._grid.actor.x,
y - this._grid.actor.y);
let srcIsFavorite = (id in favoriteIdsObject);
if (srcIsFavorite && (!dropIsFavorite)) {
Mainloop.idle_add(function () {
appSystem.remove_favorite(id);
return false;
});
} else if ((!srcIsFavorite) && dropIsFavorite) {
Mainloop.idle_add(function () {
appSystem.add_favorite(id);
return false;
});
} else {
return false;
}
return true;
this._favoritesArea.redisplay(favorites);
this._runningArea.redisplay(running);
}
};

View File

@@ -1,112 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Big = imports.gi.Big;
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Pango = imports.gi.Pango;
const Shell = imports.gi.Shell;
const Lang = imports.lang;
const GenericDisplay = imports.ui.genericDisplay;
const Main = imports.ui.main;
const GLOW_COLOR = new Clutter.Color();
GLOW_COLOR.from_pixel(0x4f6ba4ff);
const GLOW_PADDING_HORIZONTAL = 3;
const GLOW_PADDING_VERTICAL = 3;
const APP_ICON_SIZE = 48;
function AppIcon(appInfo) {
this._init(appInfo);
}
AppIcon.prototype = {
_init : function(appInfo) {
this.appInfo = appInfo;
this.actor = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
corner_radius: 2,
border: 0,
padding: 1,
border_color: GenericDisplay.ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR,
reactive: true });
this.actor._delegate = this;
let iconBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
x_align: Big.BoxAlignment.CENTER,
y_align: Big.BoxAlignment.CENTER });
this._icon = appInfo.create_icon_texture(APP_ICON_SIZE);
iconBox.append(this._icon, Big.BoxPackFlags.NONE);
this.actor.append(iconBox, Big.BoxPackFlags.EXPAND);
this._windows = Shell.AppMonitor.get_default().get_windows_for_app(appInfo.get_id());
let nameBox = new Shell.GenericContainer();
nameBox.connect('get-preferred-width', Lang.bind(this, this._nameBoxGetPreferredWidth));
nameBox.connect('get-preferred-height', Lang.bind(this, this._nameBoxGetPreferredHeight));
nameBox.connect('allocate', Lang.bind(this, this._nameBoxAllocate));
this._nameBox = nameBox;
this._name = new Clutter.Text({ color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
font_name: "Sans 12px",
line_alignment: Pango.Alignment.CENTER,
ellipsize: Pango.EllipsizeMode.END,
text: appInfo.get_name() });
nameBox.add_actor(this._name);
this._glowBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL });
let glowPath = GLib.filename_to_uri(Shell.Global.get().imagedir + 'app-well-glow.png', '');
for (let i = 0; i < this._windows.length && i < 3; i++) {
let glow = Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.FOREVER,
glowPath, -1, -1);
glow.keep_aspect_ratio = false;
this._glowBox.append(glow, Big.BoxPackFlags.EXPAND);
}
this._nameBox.add_actor(this._glowBox);
this._glowBox.lower(this._name);
this.actor.append(nameBox, Big.BoxPackFlags.NONE);
},
_nameBoxGetPreferredWidth: function (nameBox, forHeight, alloc) {
let [min, natural] = this._name.get_preferred_width(forHeight);
alloc.min_size = min + GLOW_PADDING_HORIZONTAL * 2;
alloc.natural_size = natural + GLOW_PADDING_HORIZONTAL * 2;
},
_nameBoxGetPreferredHeight: function (nameBox, forWidth, alloc) {
let [min, natural] = this._name.get_preferred_height(forWidth);
alloc.min_size = min + GLOW_PADDING_VERTICAL * 2;
alloc.natural_size = natural + GLOW_PADDING_VERTICAL * 2;
},
_nameBoxAllocate: function (nameBox, box, flags) {
let childBox = new Clutter.ActorBox();
let [minWidth, naturalWidth] = this._name.get_preferred_width(-1);
let [minHeight, naturalHeight] = this._name.get_preferred_height(-1);
let availWidth = box.x2 - box.x1;
let availHeight = box.y2 - box.y1;
let targetWidth = availWidth;
let xPadding = 0;
if (naturalWidth < availWidth) {
xPadding = Math.floor((availWidth - naturalWidth) / 2);
}
childBox.x1 = xPadding;
childBox.x2 = availWidth - xPadding;
childBox.y1 = GLOW_PADDING_VERTICAL;
childBox.y2 = availHeight - GLOW_PADDING_VERTICAL;
this._name.allocate(childBox, flags);
// Now the glow
if (this._glowBox != null) {
let glowPaddingHoriz = Math.max(0, xPadding - GLOW_PADDING_HORIZONTAL);
glowPaddingHoriz = Math.max(GLOW_PADDING_HORIZONTAL, glowPaddingHoriz);
childBox.x1 = glowPaddingHoriz;
childBox.x2 = availWidth - glowPaddingHoriz;
childBox.y1 = 0;
childBox.y2 = availHeight;
this._glowBox.allocate(childBox, flags);
}
}
};

View File

@@ -2,11 +2,6 @@
const Big = imports.gi.Big;
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const Tweener = imports.ui.tweener;
const DEFAULT_BUTTON_COLOR = new Clutter.Color();
DEFAULT_BUTTON_COLOR.from_pixel(0xeeddcc66);
@@ -14,20 +9,12 @@ DEFAULT_BUTTON_COLOR.from_pixel(0xeeddcc66);
const DEFAULT_PRESSED_BUTTON_COLOR = new Clutter.Color();
DEFAULT_PRESSED_BUTTON_COLOR.from_pixel(0xccbbaa66);
const DEFAULT_TEXT_COLOR = new Clutter.Color();
DEFAULT_TEXT_COLOR.from_pixel(0x000000ff);
const DEFAULT_FONT = 'Sans Bold 16px';
// Padding on the left and right side of the button.
const SIDE_PADDING = 14;
function Button(widget, buttonColor, pressedButtonColor, textColor, font) {
this._init(widget, buttonColor, pressedButtonColor, textColor, font);
function Button(widget, buttonColor, pressedButtonColor, staysPressed, minWidth, minHeight) {
this._init(widget, buttonColor, pressedButtonColor, staysPressed, minWidth, minHeight);
}
Button.prototype = {
_init : function(widgetOrText, buttonColor, pressedButtonColor, textColor, font) {
_init : function(widgetOrText, buttonColor, pressedButtonColor, staysPressed, minWidth, minHeight) {
let me = this;
this._buttonColor = buttonColor
@@ -38,139 +25,90 @@ Button.prototype = {
if (pressedButtonColor == null)
this._pressedButtonColor = DEFAULT_PRESSED_BUTTON_COLOR;
this._textColor = textColor;
if (textColor == null)
this._textColor = DEFAULT_TEXT_COLOR;
this._staysPressed = staysPressed
if (staysPressed == null)
this._staysPressed = false;
this._font = font;
if (font == null)
this._font = DEFAULT_FONT;
if (minWidth == null)
minWidth = 0;
if (minHeight == null)
minHeight = 0;
// if this._staysPressed is true, this._active will be true past the first release of a button, until a subsequent one (the button
// is unpressed) or until release() is called explicitly
this._active = false;
this._isBetweenPressAndRelease = false;
this._mouseIsOverButton = false;
this.actor = new Shell.ButtonBox({ reactive: true,
corner_radius: 5,
padding_left: SIDE_PADDING,
padding_right: SIDE_PADDING,
orientation: Big.BoxOrientation.HORIZONTAL,
y_align: Big.BoxAlignment.CENTER
});
this.button = new Big.Box({ reactive: true,
corner_radius: 5,
padding_left: 4,
padding_right: 4,
orientation: Big.BoxOrientation.HORIZONTAL,
y_align: Big.BoxAlignment.CENTER
});
if (typeof widgetOrText == 'string') {
this._widget = new Clutter.Text({ font_name: this._font,
color: this._textColor,
this._widget = new Clutter.Text({ font_name: "Sans Bold 16px",
text: widgetOrText });
} else {
this._widget = widgetOrText;
}
this.actor.append(this._widget, Big.BoxPackFlags.EXPAND);
this.button.append(this._widget, Big.BoxPackFlags.EXPAND);
this.actor.connect('notify::hover', Lang.bind(this, this._updateColors));
this.actor.connect('notify::pressed', Lang.bind(this, this._updateColors));
this.actor.connect('notify::active', Lang.bind(this, this._updateColors));
this._minWidth = minWidth;
this._minHeight = minHeight;
this.button.connect('button-press-event',
function(o, event) {
me._isBetweenPressAndRelease = true;
me.button.backgroundColor = me._pressedButtonColor;
return false;
});
this.button.connect('button-release-event',
function(o, event) {
me._isBetweenPressAndRelease = false;
if (!me._staysPressed || me._active) {
me.release();
} else {
me._active = true;
}
return false;
});
this.button.connect('enter-event',
function(o, event) {
me._mouseIsOverButton = true;
if (!me._active) {
me.button.backgroundColor = me._buttonColor;
}
return false;
});
this.button.connect('leave-event',
function(o, event) {
me._isBetweenPressAndRelease = false;
me._mouseIsOverButton = false;
if (!me._active) {
me.button.backgroundColor = null;
}
return false;
});
},
_updateColors : function() {
if (this.actor.active || this.actor.pressed)
this.actor.backgroundColor = this._pressedButtonColor;
else if (this.actor.hover)
this.actor.backgroundColor = this._buttonColor;
else
this.actor.backgroundColor = null;
}
};
Signals.addSignalMethods(Button.prototype);
/* Delay before the icon should appear, in seconds after the pointer has entered the parent */
const ANIMATION_TIME = 0.25;
/* This is an icon button that fades in/out when mouse enters/leaves the parent.
* A delay is used before the fading starts. You can force it to be shown if needed.
*
* parent -- used to show/hide the button depending on mouse entering/leaving it
* size -- size in pixels of both the button and the icon it contains
* texture -- optional, must be used if the texture for the icon is already created (else, use setIconFromName)
*/
function iconButton(parent, size, texture) {
this._init(parent, size, texture);
}
iconButton.prototype = {
_init : function(parent, size, texture) {
this._size = size;
if (texture)
this.actor = texture;
else
this.actor = new Clutter.Texture({ width: this._size, height: this._size });
this.actor.set_reactive(true);
this.actor.set_opacity(0);
parent.connect("enter-event", Lang.bind(this, function(actor, event) {
this._shouldHide = false;
// Nothing to do if the cursor has come back from a child of the parent actor
if (actor.get_children().indexOf(Shell.get_event_related(event)) != -1)
return;
this._fadeIn();
}));
parent.connect("leave-event", Lang.bind(this, function(actor, event) {
// Nothing to do if the cursor has merely entered a child of the parent actor
if (actor.get_children().indexOf(Shell.get_event_related(event)) != -1)
return;
// Remember that we should not be visible to hide the button if forceShow is unset
if (this._forceShow) {
this._shouldHide = true;
return;
}
this._fadeOut();
}));
},
/// Private methods ///
setIconFromName : function(iconName) {
let iconTheme = Gtk.IconTheme.get_default();
let iconInfo = iconTheme.lookup_icon(iconName, this._size, 0);
if (!iconInfo)
return;
let iconPath = iconInfo.get_filename();
this.actor.set_from_file(iconPath);
},
// Useful if we want to show the button immediately,
// e.g. in case the mouse is already in the parent when the button is created
show : function() {
this.actor.set_opacity(255);
},
// If show is true, prevents the button from fading out
forceShow : function(show) {
this._forceShow = show;
// Hide the button if it should have been hidden under normal conditions
if (!this._forceShow && this._shouldHide) {
this._fadeOut();
pressIn : function() {
if (!this._isBetweenPressAndRelease && this._staysPressed) {
this._active = true;
this.button.backgroundColor = this._pressedButtonColor;
}
},
/// Private methods ///
_fadeIn : function() {
Tweener.removeTweens(this.actor);
Tweener.addTween(this.actor, { opacity: 255,
time: ANIMATION_TIME,
transition :"easeInQuad" });
},
_fadeOut : function() {
Tweener.removeTweens(this.actor);
Tweener.addTween(this.actor, { opacity: 0,
time: ANIMATION_TIME,
transition :"easeOutQuad" });
release : function() {
if (!this._isBetweenPressAndRelease && this._staysPressed) {
this._active = false;
if (this._mouseIsOverButton) {
this.button.backgroundColor = this._buttonColor;
} else {
this.button.backgroundColor = null;
}
}
}
};

View File

@@ -9,7 +9,7 @@ const Shell = imports.gi.Shell;
const Main = imports.ui.main;
// This manages the shell "chrome"; the UI that's visible in the
// normal mode (ie, outside the Overview), that surrounds the main
// normal mode (ie, outside the overlay), that surrounds the main
// workspace content.
function Chrome() {
@@ -23,8 +23,8 @@ Chrome.prototype = {
// The group itself has zero size so it doesn't interfere with DND
this.actor = new Clutter.Group({ width: 0, height: 0 });
global.stage.add_actor(this.actor);
this.nonOverviewActor = new Clutter.Group();
this.actor.add_actor(this.nonOverviewActor);
this.nonOverlayActor = new Clutter.Group();
this.actor.add_actor(this.nonOverlayActor);
this._obscuredByFullscreen = false;
@@ -37,10 +37,10 @@ Chrome.prototype = {
global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._queueUpdateRegions));
Main.overview.connect('showing',
Lang.bind(this, this._overviewShowing));
Main.overview.connect('hidden',
Lang.bind(this, this._overviewHidden));
Main.overlay.connect('showing',
Lang.bind(this, this._overlayShowing));
Main.overlay.connect('hidden',
Lang.bind(this, this._overlayHidden));
this._queueUpdateRegions();
},
@@ -78,27 +78,27 @@ Chrome.prototype = {
else if (shapeActor && !this._verifyAncestry(shapeActor, actor))
throw new Error('shapeActor is not a descendent of actor');
this.nonOverviewActor.add_actor(actor);
this.nonOverlayActor.add_actor(actor);
if (shapeActor)
this._trackActor(shapeActor, true, true);
},
// setVisibleInOverview:
// setVisibleInOverlay:
// @actor: an actor in the chrome layer
// @visible: Overview visibility
// @visible: overlay visibility
//
// By default, actors in the chrome layer are automatically hidden
// when the Overview is shown. This can be used to override that
// when the overlay is shown. This can be used to override that
// behavior
setVisibleInOverview: function(actor, visible) {
setVisibleInOverlay: function(actor, visible) {
if (!this._verifyAncestry(actor, this.actor))
throw new Error('actor is not a descendent of the chrome layer');
if (visible)
actor.reparent(this.actor);
else
actor.reparent(this.nonOverviewActor);
actor.reparent(this.nonOverlayActor);
},
// addInputRegionActor:
@@ -126,11 +126,9 @@ Chrome.prototype = {
//
// Removes @actor from the chrome layer
removeActor: function(actor) {
if (actor.get_parent() == this.nonOverviewActor)
this.nonOverviewActor.remove_actor(actor);
else
this.actor.remove_actor(actor);
this._untrackActor(actor, true, true);
this.actor.remove_actor(actor);
// We don't have to do anything else; the parent-set handlers
// will do the rest.
},
_findActor: function(actor) {
@@ -172,7 +170,7 @@ Chrome.prototype = {
this._trackedActors.push(actorData);
actor = actor.get_parent();
if (actor != this.actor && actor != this.nonOverviewActor)
if (actor != this.actor)
this._trackActor(actor, false, false);
if (inputRegion || strut)
@@ -200,7 +198,7 @@ Chrome.prototype = {
actor.disconnect(actorData.parentSetId);
actor = actor.get_parent();
if (actor && actor != this.actor && actor != this.nonOverviewActor)
if (actor && actor != this.actor)
this._untrackActor(actor, false, false);
}
@@ -211,30 +209,29 @@ Chrome.prototype = {
_actorReparented: function(actor, oldParent) {
if (this._verifyAncestry(actor, this.actor)) {
let newParent = actor.get_parent();
if (newParent != this.actor && newParent != this.nonOverviewActor)
if (newParent != this.actor)
this._trackActor(newParent, false, false);
}
if (oldParent != this.actor && oldParent != this.nonOverviewActor)
if (oldParent != this.actor)
this._untrackActor(oldParent, false, false);
},
_overviewShowing: function() {
_overlayShowing: function() {
this.actor.show();
this.nonOverviewActor.hide();
this.nonOverlayActor.hide();
this._queueUpdateRegions();
},
_overviewHidden: function() {
_overlayHidden: function() {
if (this._obscuredByFullscreen)
this.actor.hide();
this.nonOverviewActor.show();
this.nonOverlayActor.show();
this._queueUpdateRegions();
},
_queueUpdateRegions: function() {
if (!this._updateRegionIdle)
this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this._updateRegions),
Meta.PRIORITY_BEFORE_REDRAW);
this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this._updateRegions));
},
_windowsRestacked: function() {
@@ -272,7 +269,7 @@ Chrome.prototype = {
break;
}
let shouldBeVisible = !this._obscuredByFullscreen || Main.overview.visible;
let shouldBeVisible = !this._obscuredByFullscreen || Main.overlay.visible;
if (this.actor.visible != shouldBeVisible) {
this.actor.visible = shouldBeVisible;
this._queueUpdateRegions();

View File

@@ -1,907 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Big = imports.gi.Big;
const Clutter = imports.gi.Clutter;
const Gtk = imports.gi.Gtk;
const Mainloop = imports.mainloop;
const Pango = imports.gi.Pango;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const Lang = imports.lang;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const AppDisplay = imports.ui.appDisplay;
const DocDisplay = imports.ui.docDisplay;
const Places = imports.ui.places;
const GenericDisplay = imports.ui.genericDisplay;
const Button = imports.ui.button;
const Main = imports.ui.main;
const DEFAULT_PADDING = 4;
const DEFAULT_SPACING = 4;
const DASH_SECTION_PADDING = 6;
const DASH_SECTION_SPACING = 40;
const DASH_CORNER_RADIUS = 5;
const DASH_PADDING_SIDE = 14;
const BACKGROUND_COLOR = new Clutter.Color();
BACKGROUND_COLOR.from_pixel(0x000000c0);
const PRELIGHT_COLOR = new Clutter.Color();
PRELIGHT_COLOR.from_pixel(0x4f6fadaa);
const TEXT_COLOR = new Clutter.Color();
TEXT_COLOR.from_pixel(0x5f5f5fff);
const BRIGHTER_TEXT_COLOR = new Clutter.Color();
BRIGHTER_TEXT_COLOR.from_pixel(0xbbbbbbff);
const BRIGHT_TEXT_COLOR = new Clutter.Color();
BRIGHT_TEXT_COLOR.from_pixel(0xffffffff);
const SEARCH_TEXT_COLOR = new Clutter.Color();
SEARCH_TEXT_COLOR.from_pixel(0x333333ff);
const SEARCH_CURSOR_COLOR = BRIGHT_TEXT_COLOR;
const HIGHLIGHTED_SEARCH_CURSOR_COLOR = SEARCH_TEXT_COLOR;
const HIGHLIGHTED_SEARCH_BACKGROUND_COLOR = new Clutter.Color();
HIGHLIGHTED_SEARCH_BACKGROUND_COLOR.from_pixel(0xc4c4c4ff);
const SEARCH_BORDER_BOTTOM_COLOR = new Clutter.Color();
SEARCH_BORDER_BOTTOM_COLOR.from_pixel(0x191919ff);
const SECTION_BORDER_COLOR = new Clutter.Color();
SECTION_BORDER_COLOR.from_pixel(0x262626ff);
const SECTION_BORDER = 1;
const SECTION_INNER_BORDER_COLOR = new Clutter.Color();
SECTION_INNER_BORDER_COLOR.from_pixel(0x000000ff);
const SECTION_BACKGROUND_TOP_COLOR = new Clutter.Color();
SECTION_BACKGROUND_TOP_COLOR.from_pixel(0x161616ff);
const SECTION_BACKGROUND_BOTTOM_COLOR = new Clutter.Color();
SECTION_BACKGROUND_BOTTOM_COLOR.from_pixel(0x000000ff);
const SECTION_INNER_SPACING = 8;
const BROWSE_ACTIVATED_BG = new Clutter.Color();
BROWSE_ACTIVATED_BG.from_pixel(0x303030f0);
const PANE_BORDER_COLOR = new Clutter.Color();
PANE_BORDER_COLOR.from_pixel(0x101d3cfa);
const PANE_BORDER_WIDTH = 2;
const PANE_BACKGROUND_COLOR = new Clutter.Color();
PANE_BACKGROUND_COLOR.from_pixel(0x000000f4);
function Pane() {
this._init();
}
Pane.prototype = {
_init: function () {
this._open = false;
this.actor = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
background_color: PANE_BACKGROUND_COLOR,
border: PANE_BORDER_WIDTH,
border_color: PANE_BORDER_COLOR,
padding: DEFAULT_PADDING,
reactive: true });
this.actor.connect('button-press-event', Lang.bind(this, function (a, e) {
// Eat button press events so they don't go through and close the pane
return true;
}));
let chromeTop = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
spacing: 6 });
let global = Shell.Global.get();
let closeIconUri = "file://" + global.imagedir + "close.svg";
let closeIcon = Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.FOREVER,
closeIconUri,
16,
16);
closeIcon.reactive = true;
closeIcon.connect('button-press-event', Lang.bind(this, function (b, e) {
this.close();
return true;
}));
chromeTop.append(closeIcon, Big.BoxPackFlags.END);
this.actor.append(chromeTop, Big.BoxPackFlags.NONE);
this.content = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
spacing: DEFAULT_PADDING });
this.actor.append(this.content, Big.BoxPackFlags.EXPAND);
// Hidden by default
this.actor.hide();
},
open: function () {
if (this._open)
return;
this._open = true;
this.actor.show();
this.emit('open-state-changed', this._open);
},
close: function () {
if (!this._open)
return;
this._open = false;
this.actor.hide();
this.emit('open-state-changed', this._open);
},
destroyContent: function() {
let children = this.content.get_children();
for (let i = 0; i < children.length; i++) {
children[i].destroy();
}
},
toggle: function () {
if (this._open)
this.close();
else
this.open();
}
}
Signals.addSignalMethods(Pane.prototype);
function ResultArea(displayClass, enableNavigation) {
this._init(displayClass, enableNavigation);
}
ResultArea.prototype = {
_init : function(displayClass, enableNavigation) {
this.actor = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL });
this.resultsContainer = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
spacing: DEFAULT_PADDING
});
this.actor.append(this.resultsContainer, Big.BoxPackFlags.EXPAND);
this.navContainer = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL });
this.resultsContainer.append(this.navContainer, Big.BoxPackFlags.NONE);
this.display = new displayClass();
this.navArea = this.display.getNavigationArea();
if (enableNavigation && this.navArea)
this.navContainer.append(this.navArea, Big.BoxPackFlags.EXPAND);
this.resultsContainer.append(this.display.actor, Big.BoxPackFlags.EXPAND);
this.controlBox = new Big.Box({ x_align: Big.BoxAlignment.CENTER });
this.controlBox.append(this.display.displayControl, Big.BoxPackFlags.NONE);
this.actor.append(this.controlBox, Big.BoxPackFlags.NONE);
this.display.load();
}
}
// Utility function shared between ResultPane and the DocDisplay in the main dash.
// Connects to the detail signal of the display, and on-demand creates a new
// pane.
function createPaneForDetails(dash, display) {
let detailPane = null;
display.connect('show-details', Lang.bind(this, function(display, index) {
if (detailPane == null) {
detailPane = new Pane();
detailPane.connect('open-state-changed', Lang.bind(this, function (pane, isOpen) {
if (!isOpen) {
/* Ensure we don't keep around large preview textures */
detailPane.destroyContent();
}
}));
dash._addPane(detailPane);
}
if (index >= 0) {
detailPane.destroyContent();
let details = display.createDetailsForIndex(index);
detailPane.content.append(details, Big.BoxPackFlags.EXPAND);
detailPane.open();
} else {
detailPane.close();
}
}));
return null;
}
function ResultPane(dash) {
this._init(dash);
}
ResultPane.prototype = {
__proto__: Pane.prototype,
_init: function(dash) {
Pane.prototype._init.call(this);
this._dash = dash;
},
// Create an instance of displayClass and pack it into this pane's
// content area. Return the displayClass instance.
packResults: function(displayClass, enableNavigation) {
let resultArea = new ResultArea(displayClass, enableNavigation);
createPaneForDetails(this._dash, resultArea.display);
this.content.append(resultArea.actor, Big.BoxPackFlags.EXPAND);
this.connect('open-state-changed', Lang.bind(this, function(pane, isOpen) {
resultArea.display.resetState();
}));
return resultArea.display;
}
}
function SearchEntry() {
this._init();
}
SearchEntry.prototype = {
_init : function() {
this.actor = new Big.Box({ padding: DEFAULT_PADDING,
border_bottom: SECTION_BORDER,
border_color: SEARCH_BORDER_BOTTOM_COLOR,
corner_radius: DASH_CORNER_RADIUS,
reactive: true });
let box = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
y_align: Big.BoxAlignment.CENTER });
this.actor.append(box, Big.BoxPackFlags.EXPAND);
this.actor.connect('button-press-event', Lang.bind(this, function () {
this._resetTextState(true);
return false;
}));
this.pane = null;
this._defaultText = _("Find...");
let textProperties = { font_name: "Sans 16px" };
let entryProperties = { editable: true,
activatable: true,
single_line_mode: true,
color: SEARCH_TEXT_COLOR,
cursor_color: SEARCH_CURSOR_COLOR };
Lang.copyProperties(textProperties, entryProperties);
this.entry = new Clutter.Text(entryProperties);
this.entry.connect('notify::text', Lang.bind(this, function () {
this._resetTextState(false);
}));
box.append(this.entry, Big.BoxPackFlags.EXPAND);
// Mark as editable just to get a cursor
let defaultTextProperties = { ellipsize: Pango.EllipsizeMode.END,
text: this._defaultText,
editable: true,
color: TEXT_COLOR,
cursor_visible: false,
single_line_mode: true };
Lang.copyProperties(textProperties, defaultTextProperties);
this._defaultText = new Clutter.Text(defaultTextProperties);
box.add_actor(this._defaultText);
this.entry.connect('notify::allocation', Lang.bind(this, function () {
this._repositionDefaultText();
}));
this._iconBox = new Big.Box({ x_align: Big.BoxAlignment.CENTER,
y_align: Big.BoxAlignment.CENTER,
padding_right: 4 });
box.append(this._iconBox, Big.BoxPackFlags.END);
let global = Shell.Global.get();
let magnifierUri = "file://" + global.imagedir + "magnifier.svg";
this._magnifierIcon = Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.FOREVER,
magnifierUri, 18, 18);
let closeUri = "file://" + global.imagedir + "close-black.svg";
this._closeIcon = Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.FOREVER,
closeUri, 18, 18);
this._closeIcon.reactive = true;
this._closeIcon.connect('button-press-event', Lang.bind(this, function () {
// Resetting this.entry.text will trigger notify::text signal which will
// result in this._resetTextState() being called, but we should not rely
// on that not short-circuiting if the text was already empty, so we call
// this._resetTextState() explicitly in that case.
if (this.entry.text == '')
this._resetTextState(false);
else
this.entry.text = '';
// Return true to stop the signal emission, so that this.actor doesn't get
// the button-press-event and re-highlight itself.
return true;
}));
this._repositionDefaultText();
this._resetTextState();
},
setPane: function (pane) {
this._pane = pane;
},
reset: function () {
this.entry.text = '';
},
getText: function () {
return this.entry.text;
},
_resetTextState: function (searchEntryClicked) {
let text = this.getText();
this._iconBox.remove_all();
// We highlight the search box if the user starts typing in it
// or just clicks in it to indicate that the search is active.
if (text != '' || searchEntryClicked) {
if (!searchEntryClicked)
this._defaultText.hide();
this._iconBox.append(this._closeIcon, Big.BoxPackFlags.NONE);
this.actor.background_color = HIGHLIGHTED_SEARCH_BACKGROUND_COLOR;
this.entry.cursor_color = HIGHLIGHTED_SEARCH_CURSOR_COLOR;
} else {
this._defaultText.show();
this._iconBox.append(this._magnifierIcon, Big.BoxPackFlags.NONE);
this.actor.background_color = BACKGROUND_COLOR;
this.entry.cursor_color = SEARCH_CURSOR_COLOR;
}
},
_repositionDefaultText: function () {
// Offset a little to show the cursor
this._defaultText.set_position(this.entry.x + 4, this.entry.y);
this._defaultText.set_size(this.entry.width, this.entry.height);
}
};
Signals.addSignalMethods(SearchEntry.prototype);
function MoreLink() {
this._init();
}
MoreLink.prototype = {
_init : function () {
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
padding_right: DEFAULT_PADDING,
padding_left: DEFAULT_PADDING,
reactive: true,
x_align: Big.BoxAlignment.CENTER,
y_align: Big.BoxAlignment.CENTER,
border_left: SECTION_BORDER,
border_color: SECTION_BORDER_COLOR });
this.pane = null;
let text = new Clutter.Text({ font_name: "Sans 12px",
color: BRIGHT_TEXT_COLOR,
text: _("Browse") });
this.actor.append(text, Big.BoxPackFlags.NONE);
this.actor.connect('button-press-event', Lang.bind(this, function (b, e) {
if (this.pane == null) {
// Ensure the pane is created; the activated handler will call setPane
this.emit('activated');
}
this._pane.toggle();
return true;
}));
},
setPane: function (pane) {
this._pane = pane;
this._pane.connect('open-state-changed', Lang.bind(this, function(pane, isOpen) {
}));
}
}
Signals.addSignalMethods(MoreLink.prototype);
function BackLink() {
this._init();
}
BackLink.prototype = {
_init : function () {
this.actor = new Shell.ButtonBox({ orientation: Big.BoxOrientation.HORIZONTAL,
padding_right: DEFAULT_PADDING,
padding_left: DEFAULT_PADDING,
reactive: true,
x_align: Big.BoxAlignment.CENTER,
y_align: Big.BoxAlignment.CENTER,
border_right: SECTION_BORDER,
border_color: SECTION_BORDER_COLOR });
let global = Shell.Global.get();
let backIconUri = "file://" + global.imagedir + "back.svg";
let backIcon = Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.FOREVER,
backIconUri,
12,
16);
this.actor.append(backIcon, Big.BoxPackFlags.NONE);
}
}
function SectionHeader(title, suppressBrowse) {
this._init(title, suppressBrowse);
}
SectionHeader.prototype = {
_init : function (title, suppressBrowse) {
this.actor = new Big.Box({ border: SECTION_BORDER,
border_color: SECTION_BORDER_COLOR });
this._innerBox = new Big.Box({ border: SECTION_BORDER,
border_color: SECTION_INNER_BORDER_COLOR,
padding_left: DEFAULT_PADDING,
padding_right: DEFAULT_PADDING,
orientation: Big.BoxOrientation.HORIZONTAL,
spacing: DEFAULT_SPACING });
this.actor.append(this._innerBox, Big.BoxPackFlags.EXPAND);
let backgroundGradient = Shell.create_vertical_gradient(SECTION_BACKGROUND_TOP_COLOR,
SECTION_BACKGROUND_BOTTOM_COLOR);
this._innerBox.add_actor(backgroundGradient);
this._innerBox.connect('notify::allocation', Lang.bind(this, function (actor) {
let [width, height] = actor.get_size();
backgroundGradient.set_size(width, height);
}));
this.backLink = new BackLink();
this._innerBox.append(this.backLink.actor, Big.BoxPackFlags.NONE);
this.backLink.actor.hide();
this.backLink.actor.connect('activate', Lang.bind(this, function (actor) {
this.emit('back-link-activated');
}));
let textBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
padding_top: DEFAULT_PADDING,
padding_bottom: DEFAULT_PADDING });
this.text = new Clutter.Text({ color: TEXT_COLOR,
font_name: "Sans Bold 12px",
text: title });
textBox.append(this.text, Big.BoxPackFlags.NONE);
this.countText = new Clutter.Text({ color: TEXT_COLOR,
font_name: 'Sans Bold 14px' });
textBox.append(this.countText, Big.BoxPackFlags.END);
this.countText.hide();
this._innerBox.append(textBox, Big.BoxPackFlags.EXPAND);
if (!suppressBrowse) {
this.moreLink = new MoreLink();
this._innerBox.append(this.moreLink.actor, Big.BoxPackFlags.END);
}
},
setTitle : function(title) {
this.text.text = title;
},
setBackLinkVisible : function(visible) {
if (visible)
this.backLink.actor.show();
else
this.backLink.actor.hide();
},
setCountText : function(countText) {
if (countText == "") {
this.countText.hide();
} else {
this.countText.show();
this.countText.text = countText;
}
}
}
Signals.addSignalMethods(SectionHeader.prototype);
function SearchSectionHeader(title, onClick) {
this._init(title, onClick);
}
SearchSectionHeader.prototype = {
_init : function(title, onClick) {
let box = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
padding_top: DASH_SECTION_PADDING,
padding_bottom: DASH_SECTION_PADDING,
spacing: DEFAULT_SPACING });
let titleText = new Clutter.Text({ color: BRIGHTER_TEXT_COLOR,
font_name: 'Sans Bold 12px',
text: title });
this.tooltip = new Clutter.Text({ color: BRIGHTER_TEXT_COLOR,
font_name: 'Sans 12px',
text: _("(see all)") });
this.countText = new Clutter.Text({ color: BRIGHTER_TEXT_COLOR,
font_name: 'Sans Bold 14px' });
box.append(titleText, Big.BoxPackFlags.NONE);
box.append(this.tooltip, Big.BoxPackFlags.NONE);
box.append(this.countText, Big.BoxPackFlags.END);
this.tooltip.hide();
let button = new Button.Button(box, PRELIGHT_COLOR, BACKGROUND_COLOR,
TEXT_COLOR);
button.actor.height = box.height;
button.actor.padding_left = DEFAULT_PADDING;
button.actor.padding_right = DEFAULT_PADDING;
button.actor.connect('activate', onClick);
button.actor.connect('notify::hover', Lang.bind(this, this._updateTooltip));
this.actor = button.actor;
},
_updateTooltip : function(actor) {
if (actor.hover)
this.tooltip.show();
else
this.tooltip.hide();
}
}
function Section(titleString, suppressBrowse) {
this._init(titleString, suppressBrowse);
}
Section.prototype = {
_init: function(titleString, suppressBrowse) {
this.actor = new Big.Box({ spacing: SECTION_INNER_SPACING });
this.header = new SectionHeader(titleString, suppressBrowse);
this.actor.append(this.header.actor, Big.BoxPackFlags.NONE);
this.content = new Big.Box({spacing: SECTION_INNER_SPACING });
this.actor.append(this.content, Big.BoxPackFlags.EXPAND);
}
}
function Dash() {
this._init();
}
Dash.prototype = {
_init : function() {
let global = Shell.Global.get();
// dash and the popup panes need to be reactive so that the clicks in unoccupied places on them
// are not passed to the transparent background underneath them. This background is used for the workspaces area when
// the additional dash panes are being shown and it handles clicks by closing the additional panes, so that the user
// can interact with the workspaces. However, this behavior is not desirable when the click is actually over a pane.
//
// We have to make the individual panes reactive instead of making the whole dash actor reactive because the width
// of the Group actor ends up including the width of its hidden children, so we were getting a reactive object as
// wide as the details pane that was blocking the clicks to the workspaces underneath it even when the details pane
// was actually hidden.
this.actor = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
background_color: BACKGROUND_COLOR,
corner_radius: DASH_CORNER_RADIUS,
padding_left: DASH_PADDING_SIDE,
padding_right: DASH_PADDING_SIDE,
reactive: true });
// Size for this one explicitly set from overlay.js
this.searchArea = new Big.Box({ y_align: Big.BoxAlignment.CENTER });
this.sectionArea = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
spacing: DASH_SECTION_SPACING });
this.actor.append(this.searchArea, Big.BoxPackFlags.NONE);
this.actor.append(this.sectionArea, Big.BoxPackFlags.NONE);
// The currently active popup display
this._activePane = null;
/***** Search *****/
this._searchActive = false;
this._searchEntry = new SearchEntry();
this.searchArea.append(this._searchEntry.actor, Big.BoxPackFlags.EXPAND);
this._searchTimeoutId = 0;
this._searchEntry.entry.connect('text-changed', Lang.bind(this, function (se, prop) {
let text = this._searchEntry.getText();
text = text.replace(/^\s+/g, "").replace(/\s+$/g, "")
this._searchActive = text != '';
this._updateDashActors();
if (!this._searchActive) {
if (this._searchTimeoutId > 0) {
Mainloop.source_remove(this._searchTimeoutId);
this._searchTimeoutId = 0;
}
return;
}
if (this._searchTimeoutId > 0)
return;
this._searchTimeoutId = Mainloop.timeout_add(150, Lang.bind(this, function() {
this._searchTimeoutId = 0;
let text = this._searchEntry.getText();
text = text.replace(/^\s+/g, "").replace(/\s+$/g, "");
this._appSearchResultArea.display.setSearch(text);
this._docSearchResultArea.display.setSearch(text);
let appsCount = this._appSearchResultArea.display.getMatchedItemsCount() + "";
let docsCount = this._docSearchResultArea.display.getMatchedItemsCount() + "";
this._appSearchHeader.countText.text = appsCount;
this._docSearchHeader.countText.text = docsCount;
if (this._appSearchResultsOnlyShown)
this._searchResultsSection.header.setCountText(appsCount);
else if (this._docSearchResultsOnlyShown)
this._searchResultsSection.header.setCountText(docsCount);
if (this._appSearchResultArea.display.hasItems() && !this._docSearchResultsOnlyShown) {
this._appSearchResultArea.display.selectFirstItem();
this._docSearchResultArea.display.unsetSelected();
} else if (this._docSearchResultArea.display.hasItems() && !this._appSearchResultsOnlyShown) {
this._docSearchResultArea.display.selectFirstItem();
this._appSearchResultArea.display.unsetSelected();
}
return false;
}));
}));
this._searchEntry.entry.connect('activate', Lang.bind(this, function (se) {
// only one of the displays will have an item selected, so it's ok to
// call activateSelected() on all of them
this._appSearchResultArea.display.activateSelected();
this._docSearchResultArea.display.activateSelected();
return true;
}));
this._searchEntry.entry.connect('key-press-event', Lang.bind(this, function (se, e) {
let text = this._searchEntry.getText();
let symbol = Shell.get_event_key_symbol(e);
if (symbol == Clutter.Escape) {
// Escape will keep clearing things back to the desktop.
// If we are showing a particular section of search, go back to all sections.
if (this._appSearchResultsOnlyShown || this._docSearchResultsOnlyShown)
this._showAllSearchSections();
// If we have an active search, we remove it.
else if (this._searchActive)
this._searchEntry.reset();
// Next, if we're in one of the "more" modes or showing the details pane, close them
else if (this._activePane != null)
this._activePane.close();
// Finally, just close the Overview entirely
else
Main.overview.hide();
return true;
} else if (symbol == Clutter.Up) {
if (!this._searchActive)
return true;
// selectUp and selectDown wrap around in their respective displays
// too, but there doesn't seem to be any flickering if we first select
// something in one display, but then unset the selection, and move
// it to the other display, so it's ok to do that.
if (this._appSearchResultArea.display.hasSelected()) {
if (!this._appSearchResultArea.display.selectUp() && this._docSearchResultArea.display.hasItems() && !this._appSearchResultsOnlyShown) {
this._docSearchResultArea.display.selectLastItem();
this._appSearchResultArea.display.unsetSelected();
}
} else if (this._docSearchResultArea.display.hasSelected()) {
if (!this._docSearchResultArea.display.selectUp() && this._appSearchResultArea.display.hasItems() && !this._docSearchResultsOnlyShown) {
this._appSearchResultArea.display.selectLastItem();
this._docSearchResultArea.display.unsetSelected();
}
}
return true;
} else if (symbol == Clutter.Down) {
if (!this._searchActive)
return true;
if (this._appSearchResultArea.display.hasSelected()) {
if (!this._appSearchResultArea.display.selectDown() && this._docSearchResultArea.display.hasItems() && !this._appSearchResultsOnlyShown) {
this._docSearchResultArea.display.selectFirstItem();
this._appSearchResultArea.display.unsetSelected();
}
} else if (this._docSearchResultArea.display.hasSelected()) {
if (!this._docSearchResultArea.display.selectDown() && this._appSearchResultArea.display.hasItems() && !this._docSearchResultsOnlyShown) {
this._appSearchResultArea.display.selectFirstItem();
this._docSearchResultArea.display.unsetSelected();
}
}
return true;
}
return false;
}));
/***** Applications *****/
this._appsSection = new Section(_("APPLICATIONS"));
let appWell = new AppDisplay.AppWell();
this._appsSection.content.append(appWell.actor, Big.BoxPackFlags.EXPAND);
this._moreAppsPane = null;
this._appsSection.header.moreLink.connect('activated', Lang.bind(this, function (link) {
if (this._moreAppsPane == null) {
this._moreAppsPane = new ResultPane(this);
this._moreAppsPane.packResults(AppDisplay.AppDisplay, true);
this._addPane(this._moreAppsPane);
link.setPane(this._moreAppsPane);
}
}));
this.sectionArea.append(this._appsSection.actor, Big.BoxPackFlags.NONE);
/***** Places *****/
/* Translators: This is in the sense of locations for documents,
network locations, etc. */
this._placesSection = new Section(_("PLACES"), true);
let placesDisplay = new Places.Places();
this._placesSection.content.append(placesDisplay.actor, Big.BoxPackFlags.EXPAND);
this.sectionArea.append(this._placesSection.actor, Big.BoxPackFlags.NONE);
/***** Documents *****/
this._docsSection = new Section(_("RECENT DOCUMENTS"));
let docDisplay = new DocDisplay.DashDocDisplay();
this._docsSection.content.append(docDisplay.actor, Big.BoxPackFlags.EXPAND);
this._moreDocsPane = null;
this._docsSection.header.moreLink.connect('activated', Lang.bind(this, function (link) {
if (this._moreDocsPane == null) {
this._moreDocsPane = new ResultPane(this);
this._moreDocsPane.packResults(DocDisplay.DocDisplay, true);
this._addPane(this._moreDocsPane);
link.setPane(this._moreDocsPane);
}
}));
this.sectionArea.append(this._docsSection.actor, Big.BoxPackFlags.EXPAND);
/***** Search Results *****/
this._searchResultsSection = new Section(_("SEARCH RESULTS"), true);
this._searchResultsSection.header.connect('back-link-activated', Lang.bind(this, function () {
if (this._appSearchResultsOnlyShown)
this._toggleOnlyAppSearchShown();
else if (this._docSearchResultsOnlyShown)
this._toggleOnlyDocSearchShown();
}));
this._appSearchResultsOnlyShown = false;
this._appSearchHeader = new SearchSectionHeader(_("APPLICATIONS"),
Lang.bind(this,
function () {
this._toggleOnlyAppSearchShown();
}));
this._searchResultsSection.content.append(this._appSearchHeader.actor, Big.BoxPackFlags.NONE);
this._appSearchResultArea = new ResultArea(AppDisplay.AppDisplay, false);
this._appSearchResultArea.controlBox.hide();
this._searchResultsSection.content.append(this._appSearchResultArea.actor, Big.BoxPackFlags.EXPAND);
createPaneForDetails(this, this._appSearchResultArea.display);
this._docSearchResultsOnlyShown = false;
this._docSearchHeader = new SearchSectionHeader(_("RECENT DOCUMENTS"),
Lang.bind(this,
function () {
this._toggleOnlyDocSearchShown();
}));
this._searchResultsSection.content.append(this._docSearchHeader.actor, Big.BoxPackFlags.NONE);
this._docSearchResultArea = new ResultArea(DocDisplay.DocDisplay, false);
this._docSearchResultArea.controlBox.hide();
this._searchResultsSection.content.append(this._docSearchResultArea.actor, Big.BoxPackFlags.EXPAND);
createPaneForDetails(this, this._docSearchResultArea.display);
this.sectionArea.append(this._searchResultsSection.actor, Big.BoxPackFlags.EXPAND);
this._searchResultsSection.actor.hide();
},
show: function() {
let global = Shell.Global.get();
global.stage.set_key_focus(this._searchEntry.entry);
},
hide: function() {
this._firstSelectAfterOverlayShow = true;
this._searchEntry.reset();
if (this._activePane != null)
this._activePane.close();
},
closePanes: function () {
if (this._activePane != null)
this._activePane.close();
},
_addPane: function(pane) {
pane.connect('open-state-changed', Lang.bind(this, function (pane, isOpen) {
if (isOpen) {
if (pane != this._activePane && this._activePane != null) {
this._activePane.close();
}
this._activePane = pane;
} else if (pane == this._activePane) {
this._activePane = null;
}
}));
Main.overview.addPane(pane);
},
_updateDashActors: function() {
if (!this._searchActive && this._searchResultsSection.actor.visible) {
this._showAllSearchSections();
this._searchResultsSection.actor.hide();
this._appsSection.actor.show();
this._placesSection.actor.show();
this._docsSection.actor.show();
} else if (this._searchActive && !this._searchResultsSection.actor.visible) {
this._searchResultsSection.actor.show();
this._appsSection.actor.hide();
this._placesSection.actor.hide();
this._docsSection.actor.hide();
}
},
_toggleOnlyAppSearchShown: function() {
if (this._appSearchResultsOnlyShown) {
this._setDocSearchShown(true);
} else {
this._setDocSearchShown(false);
}
},
_toggleOnlyDocSearchShown: function() {
if (this._docSearchResultsOnlyShown) {
this._setAppSearchShown(true);
} else {
this._setAppSearchShown(false);
}
},
_setAppSearchShown: function(show) {
if (show) {
this._appSearchHeader.actor.show();
this._appSearchResultArea.actor.show();
this._docSearchResultArea.display.displayPage(0);
this._docSearchResultArea.controlBox.hide();
this._searchResultsSection.header.setTitle(_("SEARCH RESULTS"));
this._searchResultsSection.header.setBackLinkVisible(false);
this._searchResultsSection.header.setCountText("");
this._docSearchHeader.actor.show();
this._docSearchResultsOnlyShown = false;
} else {
this._appSearchHeader.actor.hide();
this._appSearchResultArea.actor.hide();
this._appSearchResultArea.display.unsetSelected();
this._docSearchResultArea.display.selectFirstItem();
this._docSearchResultArea.controlBox.show();
this._searchResultsSection.header.setTitle(_("RECENT DOCUMENTS"));
this._searchResultsSection.header.setBackLinkVisible(true);
let docsCount = this._docSearchResultArea.display.getMatchedItemsCount() + "";
this._searchResultsSection.header.setCountText(docsCount);
this._docSearchHeader.actor.hide();
this._docSearchResultsOnlyShown = true;
}
},
_setDocSearchShown: function(show) {
if (show) {
this._docSearchHeader.actor.show();
this._docSearchResultArea.actor.show();
this._appSearchResultArea.display.displayPage(0);
this._appSearchResultArea.controlBox.hide();
this._searchResultsSection.header.setTitle(_("SEARCH RESULTS"));
this._searchResultsSection.header.setBackLinkVisible(false);
this._searchResultsSection.header.setCountText("");
this._appSearchHeader.actor.show();
this._appSearchResultsOnlyShown = false;
} else {
this._docSearchHeader.actor.hide();
this._docSearchResultArea.actor.hide();
this._docSearchResultArea.display.unsetSelected();
this._appSearchResultArea.display.selectFirstItem();
this._appSearchResultArea.controlBox.show();
this._searchResultsSection.header.setTitle(_("APPLICATIONS"));
this._searchResultsSection.header.setBackLinkVisible(true);
let appsCount = this._appSearchResultArea.display.getMatchedItemsCount() + "";
this._searchResultsSection.header.setCountText(appsCount);
this._appSearchHeader.actor.hide();
this._appSearchResultsOnlyShown = true;
}
},
_showAllSearchSections: function() {
this._setAppSearchShown(true);
this._setDocSearchShown(true);
}
};
Signals.addSignalMethods(Dash.prototype);

View File

@@ -8,17 +8,15 @@ const Tweener = imports.ui.tweener;
const SNAP_BACK_ANIMATION_TIME = 0.25;
function _Draggable(actor, manualMode) {
this._init(actor, manualMode);
function _Draggable(actor) {
this._init(actor);
}
_Draggable.prototype = {
_init : function(actor, manualMode) {
_init : function(actor) {
this.actor = actor;
if (!manualMode)
this.actor.connect('button-press-event',
Lang.bind(this, this._onButtonPress));
this._haveSourceGrab = false;
this.actor.connect('button-press-event',
Lang.bind(this, this._onButtonPress));
},
_onButtonPress : function (actor, event) {
@@ -27,7 +25,6 @@ _Draggable.prototype = {
if (Tweener.getTweenCount(actor))
return false;
this._haveSourceGrab = true;
this._grabActor(actor);
let [stageX, stageY] = event.get_coords();
@@ -69,80 +66,6 @@ _Draggable.prototype = {
return false;
},
/**
* startDrag:
* @actor: Origin actor for drag and drop
* @stageX: X coordinate of event
* @stageY: Y coordinate of event
* @time: Event timestamp
*
* Directly initiate a drag and drop operation from the given actor.
* This function is useful to call if you've specified manualMode
* for the draggable.
*/
startDrag: function (actor, stageX, stageY, time) {
this.emit('drag-begin', time);
this._dragStartX = stageX;
this._dragStartY = stageY;
if (this.actor._delegate && this.actor._delegate.getDragActor) {
this._dragActor = this.actor._delegate.getDragActor(this._dragStartX, this._dragStartY);
// Drag actor does not always have to be the same as actor. For example drag actor
// can be an image that's part of the actor. So to perform "snap back" correctly we need
// to know what was the drag actor source.
if (this.actor._delegate.getDragActorSource) {
this._dragActorSource = this.actor._delegate.getDragActorSource();
// If the user dragged from the source, then position
// the dragActor over it. Otherwise, center it
// around the pointer
let [sourceX, sourceY] = this._dragActorSource.get_transformed_position();
let [sourceWidth, sourceHeight] = this._dragActorSource.get_transformed_size();
let x, y;
if (stageX > sourceX && stageX <= sourceX + sourceWidth &&
stageY > sourceY && stageY <= sourceY + sourceHeight) {
x = sourceX;
y = sourceY;
} else {
x = stageX - this._dragActor.width / 2;
y = stageY - this._dragActor.height / 2;
}
this._dragActor.set_position(x, y);
} else {
this._dragActorSource = this.actor;
}
this._dragOrigParent = undefined;
if (this._haveSourceGrab) {
this._haveSourceGrab = false;
this._ungrabActor(actor);
}
this._grabActor(this._dragActor);
this._dragOffsetX = this._dragActor.x - this._dragStartX;
this._dragOffsetY = this._dragActor.y - this._dragStartY;
} else {
this._dragActor = actor;
this._dragActorSource = undefined;
this._dragOrigParent = actor.get_parent();
this._dragOrigX = this._dragActor.x;
this._dragOrigY = this._dragActor.y;
this._dragOrigScale = this._dragActor.scale_x;
let [actorStageX, actorStageY] = actor.get_transformed_position();
this._dragOffsetX = actorStageX - this._dragStartX;
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 stage
let [scaledWidth, scaledHeight] = actor.get_transformed_size();
actor.set_scale(scaledWidth / actor.width,
scaledHeight / actor.height);
}
this._dragActor.reparent(actor.get_stage());
this._dragActor.raise_top();
},
_onMotion : function (actor, event) {
let [stageX, stageY] = event.get_coords();
@@ -152,7 +75,44 @@ _Draggable.prototype = {
if (!this._dragActor &&
(Math.abs(stageX - this._dragStartX) > threshold ||
Math.abs(stageY - this._dragStartY) > threshold)) {
this.startDrag(actor, stageX, stageY, event.get_time());
this.emit('drag-begin', event.get_time());
if (this.actor._delegate && this.actor._delegate.getDragActor) {
this._dragActor = this.actor._delegate.getDragActor(this._dragStartX, this._dragStartY);
// Drag actor does not always have to be the same as actor. For example drag actor
// can be an image that's part of the actor. So to perform "snap back" correctly we need
// to know what was the drag actor source.
if (this.actor._delegate.getDragActorSource)
this._dragActorSource = this.actor._delegate.getDragActorSource();
else
this._dragActorSource = this.actor;
this._dragOrigParent = undefined;
this._ungrabActor(actor);
this._grabActor(this._dragActor);
this._dragOffsetX = this._dragActor.x - this._dragStartX;
this._dragOffsetY = this._dragActor.y - this._dragStartY;
} else {
this._dragActor = actor;
this._dragActorSource = undefined;
this._dragOrigParent = actor.get_parent();
this._dragOrigX = this._dragActor.x;
this._dragOrigY = this._dragActor.y;
this._dragOrigScale = this._dragActor.scale_x;
let [actorStageX, actorStageY] = actor.get_transformed_position();
this._dragOffsetX = actorStageX - this._dragStartX;
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 stage
let [scaledWidth, scaledHeight] = actor.get_transformed_size();
actor.set_scale(scaledWidth / actor.width,
scaledHeight / actor.height);
}
this._dragActor.reparent(actor.get_stage());
this._dragActor.raise_top();
}
// If we are dragging, update the position
@@ -173,12 +133,12 @@ _Draggable.prototype = {
// We can check the return value of the function and break the loop if it's true if we don't want
// to continue checking the parents.
target._delegate.handleDragOver(this.actor._delegate, actor,
(stageX + this._dragOffsetX - targX) / target.scale_x,
(stageY + this._dragOffsetY - targY) / target.scale_y,
(stageX + this._dragOffsetX + this._xOffset - targX) / target.scale_x,
(stageY + this._dragOffsetY + this._yOffset - targY) / target.scale_y,
event.get_time());
}
target = target.get_parent();
}
}
}
return true;
@@ -193,6 +153,8 @@ _Draggable.prototype = {
if (!dragging)
return false;
this.emit('drag-end', event.get_time());
// Find a drop target
actor.hide();
let [dropX, dropY] = event.get_coords();
@@ -203,15 +165,14 @@ _Draggable.prototype = {
if (target._delegate && target._delegate.acceptDrop) {
let [targX, targY] = target.get_transformed_position();
if (target._delegate.acceptDrop(this.actor._delegate, actor,
(dropX - targX) / target.scale_x,
(dropY - targY) / target.scale_y,
(dropX + this._xOffset - targX) / target.scale_x,
(dropY + this._yOffset - targY) / target.scale_y,
event.get_time())) {
// If it accepted the drop without taking the actor,
// destroy it.
if (actor.get_parent() == actor.get_stage())
actor.destroy();
this.emit('drag-end', event.get_time(), true);
return true;
}
}
@@ -235,32 +196,23 @@ _Draggable.prototype = {
transition: "easeOutQuad",
onComplete: this._onSnapBackComplete,
onCompleteScope: this,
onCompleteParams: [actor, event.get_time()]
onCompleteParams: [actor]
});
return true;
},
_onSnapBackComplete : function (dragActor, eventTime) {
_onSnapBackComplete : function (dragActor) {
if (this._dragOrigParent) {
dragActor.reparent(this._dragOrigParent);
dragActor.set_scale(this._dragOrigScale, this._dragOrigScale);
dragActor.set_position(this._dragOrigX, this._dragOrigY);
} else {
} else
dragActor.destroy();
}
this.emit('drag-end', eventTime, false);
}
};
Signals.addSignalMethods(_Draggable.prototype);
/**
* makeDraggable:
* @actor: Source actor
* @manualMode: If given, do not automatically start drag and drop on click
*
* Create an object which controls drag and drop for the given actor.
*/
function makeDraggable(actor, manualMode) {
return new _Draggable(actor, manualMode);
function makeDraggable(actor) {
return new _Draggable(actor);
}

View File

@@ -1,59 +1,37 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Big = imports.gi.Big;
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Pango = imports.gi.Pango;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const Mainloop = imports.mainloop;
const DocInfo = imports.misc.docInfo;
const DND = imports.ui.dnd;
const GenericDisplay = imports.ui.genericDisplay;
const Main = imports.ui.main;
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
* availableWidth - total width available for the item
*/
function DocDisplayItem(docInfo, currentSecs) {
this._init(docInfo, currentSecs);
function DocDisplayItem(docInfo, availableWidth) {
this._init(docInfo, availableWidth);
}
DocDisplayItem.prototype = {
__proto__: GenericDisplay.GenericDisplayItem.prototype,
_init : function(docInfo, currentSecs) {
GenericDisplay.GenericDisplayItem.prototype._init.call(this);
_init : function(docInfo, availableWidth) {
GenericDisplay.GenericDisplayItem.prototype._init.call(this, availableWidth);
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.
@@ -68,92 +46,66 @@ DocDisplayItem.prototype = {
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);
// Ensures the preview icon is created.
_ensurePreviewIconCreated : function() {
if (!this._previewIcon)
this._previewIcon = 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() {
_createLargePreviewIcon : function(availableWidth, availableHeight) {
if (this._docInfo.mimeType == null || this._docInfo.mimeType.indexOf("image/") != 0)
return null;
try {
return Shell.TextureCache.get_default().load_uri_sync(Shell.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] = Shell.Global.get().format_time_relative_pretty(timeDelta);
this._timeoutTime = currentSecs + nextUpdate;
this._setDescriptionText(text);
return Shell.TextureCache.get_default().load_uri_sync(this._docInfo.uri, availableWidth, availableHeight);
}
};
/* This class represents a display containing a collection of document items.
* The documents are sorted by how recently they were last visited.
*
* width - width available for the display
* height - height available for the display
*/
function DocDisplay() {
this._init();
}
function DocDisplay(width, height, numberOfColumns, columnGap) {
this._init(width, height, numberOfColumns, columnGap);
}
DocDisplay.prototype = {
__proto__: GenericDisplay.GenericDisplay.prototype,
_init : function() {
GenericDisplay.GenericDisplay.prototype._init.call(this);
_init : function(width, height, numberOfColumns, columnGap) {
GenericDisplay.GenericDisplay.prototype._init.call(this, width, height, numberOfColumns, columnGap);
let me = 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._recentManager = Gtk.RecentManager.get_default();
this._docsStale = true;
this._docManager.connect('changed', function(mgr, userData) {
this._recentManager.connect('changed', function(recentManager, userData) {
me._docsStale = true;
// Changes in local recent files should not happen when we are in the Overview mode,
// Changes in local recent files should not happen when we are in the overlay 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.
me._redisplay(false);
me._redisplay(false);
});
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() {
let me = this;
if (!this._docsStale)
return;
this._allItems = this._docManager.getItems();
this._allItems = {};
let docs = this._recentManager.get_items();
for (let i = 0; i < docs.length; i++) {
let recentInfo = docs[i];
let docInfo = new DocInfo.DocInfo(recentInfo);
// we use GtkRecentInfo URI as an item Id
this._allItems[docInfo.uri] = docInfo;
}
this._docsStale = false;
},
@@ -168,7 +120,7 @@ DocDisplay.prototype = {
// 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
// the overlay, 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
@@ -197,7 +149,7 @@ DocDisplay.prototype = {
let docA = this._allItems[itemIdA];
let docB = this._allItems[itemIdB];
return docB.timestamp - docA.timestamp;
return docB.lastVisited() - docA.lastVisited();
},
// Checks if the item info can be a match for the search string by checking
@@ -220,219 +172,8 @@ DocDisplay.prototype = {
// 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;
}
}
return new DocDisplayItem(itemInfo, this._columnWidth);
}
};
Signals.addSignalMethods(DocDisplay.prototype);
function DashDocDisplayItem(docInfo) {
this._init(docInfo);
}
DashDocDisplayItem.prototype = {
_init: function(docInfo) {
this._info = docInfo;
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
spacing: DEFAULT_SPACING,
reactive: true });
this.actor.connect('button-release-event', Lang.bind(this, function () {
docInfo.launch();
Main.overview.hide();
}));
this._icon = docInfo.createIcon(DASH_DOCS_ICON_SIZE);
let iconBox = new Big.Box({ y_align: Big.BoxAlignment.CENTER });
iconBox.append(this._icon, Big.BoxPackFlags.NONE);
this.actor.append(iconBox, Big.BoxPackFlags.NONE);
let name = new Clutter.Text({ font_name: "Sans 14px",
color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
ellipsize: Pango.EllipsizeMode.END,
text: docInfo.name });
this.actor.append(name, Big.BoxPackFlags.EXPAND);
let draggable = DND.makeDraggable(this.actor);
this.actor._delegate = this;
},
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._docManager = DocInfo.getDocManager();
this._docManager.connect('changed', Lang.bind(this, function(mgr) {
this._redisplay();
}));
this._redisplay();
},
_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();
// Two columns, where we go vertically down first. So just take
// the height of half of the children as our preferred height.
let firstColumnChildren = children.length / 2;
alloc.min_size = 0;
for (let i = 0; i < firstColumnChildren; i++) {
let child = children[i];
let [minSize, naturalSize] = child.get_preferred_height(forWidth);
alloc.natural_size += naturalSize;
if (i > 0 && i < children.length - 1) {
alloc.min_size += DEFAULT_SPACING;
alloc.natural_size += DEFAULT_SPACING;
}
}
},
_allocate: function(actor, box, flags) {
let width = box.x2 - box.x1;
let height = box.y2 - box.y1;
let children = actor.get_children();
// The width of an item is our allocated width, minus spacing, divided in half.
let itemWidth = Math.floor((width - DEFAULT_SPACING) / 2);
let x = box.x1;
let y = box.y1;
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.
for (; i < children.length; i++) {
let child = children[i];
let [minSize, naturalSize] = child.get_preferred_height(-1);
if (y + naturalSize > box.y2) {
// Is this the second column? Ok, break.
if (columnIndex == 1) {
break;
}
// Set x to the halfway point.
columnIndex += 1;
x = x + itemWidth + DEFAULT_SPACING;
// And y is back to the top.
y = box.y1;
}
let childBox = new Clutter.ActorBox();
childBox.x1 = x;
childBox.y1 = y;
childBox.x2 = childBox.x1 + itemWidth;
childBox.y2 = y + naturalSize;
y = childBox.y2 + DEFAULT_SPACING;
child.show();
child.allocate(childBox, flags);
}
// Everything else didn't fit, just hide it.
for (; i < children.length; i++) {
children[i].hide();
}
},
_redisplay: function() {
this.actor.remove_all();
let docs = this._docManager.getItems();
let docUrls = [];
for (let url in docs) {
docUrls.push(url);
}
docUrls.sort(function (urlA, urlB) { return docs[urlB].timestamp - docs[urlA].timestamp; });
let textureCache = Shell.TextureCache.get_default();
for (let i = 0; i < docUrls.length; i++) {
let url = docUrls[i];
let docInfo = docs[url];
let display = new DashDocDisplayItem(docInfo);
this.actor.add_actor(display.actor);
}
}
}

View File

@@ -7,15 +7,13 @@ const Gdk = imports.gi.Gdk;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Pango = imports.gi.Pango;
const Signals = imports.signals;
const Shell = imports.gi.Shell;
const Tidy = imports.gi.Tidy;
const Button = imports.ui.button;
const DND = imports.ui.dnd;
const Link = imports.ui.link;
const Main = imports.ui.main;
const ITEM_DISPLAY_NAME_COLOR = new Clutter.Color();
ITEM_DISPLAY_NAME_COLOR.from_pixel(0xffffffff);
@@ -30,18 +28,17 @@ DISPLAY_CONTROL_SELECTED_COLOR.from_pixel(0x112288ff);
const PREVIEW_BOX_BACKGROUND_COLOR = new Clutter.Color();
PREVIEW_BOX_BACKGROUND_COLOR.from_pixel(0xADADADf0);
const DEFAULT_PADDING = 4;
const ITEM_DISPLAY_HEIGHT = 50;
const ITEM_DISPLAY_ICON_SIZE = 48;
const ITEM_DISPLAY_PADDING = 1;
const ITEM_DISPLAY_PADDING_TOP = 1;
const ITEM_DISPLAY_PADDING_RIGHT = 2;
const DEFAULT_COLUMN_GAP = 6;
const LABEL_HEIGHT = 16;
const PREVIEW_ICON_SIZE = 96;
const PREVIEW_BOX_PADDING = 6;
const PREVIEW_BOX_SPACING = DEFAULT_PADDING;
const PREVIEW_BOX_CORNER_RADIUS = 10;
const PREVIEW_BOX_SPACING = 4;
const PREVIEW_BOX_CORNER_RADIUS = 10;
// how far relative to the full item width the preview box should be placed
const PREVIEW_PLACING = 3/4;
const PREVIEW_DETAILS_MIN_WIDTH = PREVIEW_ICON_SIZE * 2;
@@ -49,25 +46,25 @@ const PREVIEW_DETAILS_MIN_WIDTH = PREVIEW_ICON_SIZE * 2;
const INFORMATION_BUTTON_SIZE = 16;
/* 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
* 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.
*
* availableWidth - total width available for the item
*/
function GenericDisplayItem() {
this._init();
function GenericDisplayItem(availableWidth) {
this._init(availableWidth);
}
GenericDisplayItem.prototype = {
_init: function() {
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
spacing: ITEM_DISPLAY_PADDING,
reactive: true,
background_color: ITEM_DISPLAY_BACKGROUND_COLOR,
corner_radius: 4,
height: ITEM_DISPLAY_HEIGHT });
_init: function(availableWidth) {
this._availableWidth = availableWidth;
this.actor = new Clutter.Group({ reactive: true,
width: availableWidth,
height: ITEM_DISPLAY_HEIGHT });
this.actor._delegate = this;
this.actor.connect('button-release-event',
Lang.bind(this,
this.actor.connect('button-release-event',
Lang.bind(this,
function() {
// Activates the item by launching it
this.emit('activate');
@@ -77,55 +74,51 @@ GenericDisplayItem.prototype = {
let draggable = DND.makeDraggable(this.actor);
draggable.connect('drag-begin', Lang.bind(this, this._onDragBegin));
this._infoContent = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
spacing: DEFAULT_PADDING });
this.actor.append(this._infoContent, Big.BoxPackFlags.EXPAND);
this._iconBox = new Big.Box();
this._infoContent.append(this._iconBox, Big.BoxPackFlags.NONE);
this._infoText = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
spacing: DEFAULT_PADDING });
this._infoContent.append(this._infoText, Big.BoxPackFlags.EXPAND);
this._bg = new Big.Box({ background_color: ITEM_DISPLAY_BACKGROUND_COLOR,
corner_radius: 4,
x: 0, y: 0,
width: availableWidth, height: ITEM_DISPLAY_HEIGHT });
this.actor.add_actor(this._bg);
let global = Shell.Global.get();
let infoIconUri = "file://" + global.imagedir + "info.svg";
let infoIcon = Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.FOREVER,
infoIconUri,
INFORMATION_BUTTON_SIZE,
INFORMATION_BUTTON_SIZE);
this._informationButton = new Button.iconButton(this.actor, INFORMATION_BUTTON_SIZE, infoIcon);
let buttonBox = new Big.Box({ width: INFORMATION_BUTTON_SIZE + 2 * DEFAULT_PADDING,
height: INFORMATION_BUTTON_SIZE,
padding_left: DEFAULT_PADDING, padding_right: DEFAULT_PADDING,
y_align: Big.BoxAlignment.CENTER });
buttonBox.append(this._informationButton.actor, Big.BoxPackFlags.NONE);
this.actor.append(buttonBox, Big.BoxPackFlags.END);
// Connecting to the button-press-event for the information button ensures that the actor,
this._informationButton = Shell.TextureCache.get_default().load_uri_sync(infoIconUri,
INFORMATION_BUTTON_SIZE,
INFORMATION_BUTTON_SIZE);
this._informationButton.x = availableWidth - ITEM_DISPLAY_PADDING_RIGHT - INFORMATION_BUTTON_SIZE;
this._informationButton.y = ITEM_DISPLAY_HEIGHT / 2 - INFORMATION_BUTTON_SIZE / 2;
this._informationButton.reactive = true;
// Connecting to the button-press-event for the information button ensures that the actor,
// which is a draggable actor, does not get the button-press-event and doesn't initiate
// the dragging, which then prevents us from getting the button-release-event for the button.
this._informationButton.actor.connect('button-press-event',
Lang.bind(this,
function() {
return true;
}));
this._informationButton.actor.connect('button-release-event',
Lang.bind(this,
function() {
// Selects the item by highlighting it and displaying its details
this.emit('show-details');
return true;
}));
this._informationButton.connect('button-press-event',
Lang.bind(this,
function() {
return true;
}));
this._informationButton.connect('button-release-event',
Lang.bind(this,
function() {
// Selects the item by highlighting it and displaying its details
this.emit('select');
return true;
}));
this._informationButton.hide();
this.actor.add_actor(this._informationButton);
this._informationButton.lower_bottom();
this._name = null;
this._description = null;
this._icon = null;
this._previewIcon = null;
// 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 = [];
this.dragActor = null;
this.actor.connect('enter-event', Lang.bind(this, this._onEnter));
this.actor.connect('leave-event', Lang.bind(this, this._onLeave));
},
//// Draggable object interface ////
@@ -133,9 +126,21 @@ GenericDisplayItem.prototype = {
// 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();
},
this.dragActor = this._createIcon();
// If the user dragged from the icon itself, then position
// the dragActor over the original icon. Otherwise center it
// around the pointer
let [iconX, iconY] = this._icon.get_transformed_position();
let [iconWidth, iconHeight] = this._icon.get_transformed_size();
if (stageX > iconX && stageX <= iconX + iconWidth &&
stageY > iconY && stageY <= iconY + iconHeight)
this.dragActor.set_position(iconX, iconY);
else
this.dragActor.set_position(stageX - this.dragActor.width / 2, stageY - this.dragActor.height / 2);
return this.dragActor;
},
// 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
@@ -148,35 +153,36 @@ GenericDisplayItem.prototype = {
// Shows the information button when the item was drawn under the mouse pointer.
onDrawnUnderPointer: function() {
this._informationButton.show();
this._informationButton.show();
},
// Highlights the item by setting a different background color than the default
// if isSelected is true, removes the highlighting otherwise.
markSelected: function(isSelected) {
let color;
if (isSelected) {
if (isSelected)
color = ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR;
this._informationButton.forceShow(true);
}
else {
else
color = ITEM_DISPLAY_BACKGROUND_COLOR;
this._informationButton.forceShow(false);
}
this.actor.background_color = color;
this._bg.background_color = color;
},
/*
* Returns an actor containing item details. In the future details can have more information than what
* 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() {
*
* availableWidth - width available for displaying details
* availableHeight - height available for displaying details
*/
createDetailsActor: function(availableWidth, availableHeight) {
let details = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
spacing: PREVIEW_BOX_SPACING });
spacing: PREVIEW_BOX_SPACING,
width: availableWidth });
let mainDetails = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
spacing: PREVIEW_BOX_SPACING });
spacing: PREVIEW_BOX_SPACING,
width: availableWidth });
// Inner box with name and description
let textDetails = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
@@ -184,7 +190,7 @@ GenericDisplayItem.prototype = {
let detailsName = new Clutter.Text({ color: ITEM_DISPLAY_NAME_COLOR,
font_name: "Sans bold 14px",
line_wrap: true,
text: this._name.text });
text: this._name.text});
textDetails.append(detailsName, Big.BoxPackFlags.NONE);
let detailsDescription = new Clutter.Text({ color: ITEM_DISPLAY_NAME_COLOR,
@@ -193,15 +199,14 @@ GenericDisplayItem.prototype = {
text: this._description.text });
textDetails.append(detailsDescription, Big.BoxPackFlags.NONE);
this._detailsDescriptions.push(detailsDescription);
mainDetails.append(textDetails, Big.BoxPackFlags.EXPAND);
let previewIcon = this._createPreviewIcon();
let largePreviewIcon = this._createLargePreviewIcon();
this._ensurePreviewIconCreated();
let largePreviewIcon = this._createLargePreviewIcon(availableWidth, Math.max(0, availableHeight - mainDetails.height - PREVIEW_BOX_SPACING));
if (previewIcon != null && largePreviewIcon == null) {
mainDetails.prepend(previewIcon, Big.BoxPackFlags.NONE);
if (this._previewIcon != null && largePreviewIcon == null) {
let previewIconClone = new Clutter.Clone({ source: this._previewIcon });
mainDetails.prepend(previewIconClone, Big.BoxPackFlags.NONE);
}
details.append(mainDetails, Big.BoxPackFlags.NONE);
@@ -215,13 +220,13 @@ GenericDisplayItem.prototype = {
return details;
},
// Destroys the item.
// Destoys 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");
@@ -251,41 +256,39 @@ GenericDisplayItem.prototype = {
// and therefore should be responsible for distroying it
this._icon.destroy();
this._icon = null;
}
// This ensures we'll create a new previewIcon next time we need it
if (this._previewIcon != null) {
this._previewIcon.destroy();
this._previewIcon = null;
}
this._icon = this._createIcon();
this._iconBox.append(this._icon, Big.BoxPackFlags.NONE);
this.actor.add_actor(this._icon);
let textWidth = this._availableWidth - (ITEM_DISPLAY_ICON_SIZE + 4) - INFORMATION_BUTTON_SIZE - ITEM_DISPLAY_PADDING_RIGHT;
this._name = new Clutter.Text({ color: ITEM_DISPLAY_NAME_COLOR,
font_name: "Sans 14px",
width: textWidth,
ellipsize: Pango.EllipsizeMode.END,
text: nameText });
this._infoText.append(this._name, Big.BoxPackFlags.EXPAND);
text: nameText,
x: ITEM_DISPLAY_ICON_SIZE + 4,
y: ITEM_DISPLAY_PADDING_TOP });
this.actor.add_actor(this._name);
this._description = new Clutter.Text({ color: ITEM_DISPLAY_DESCRIPTION_COLOR,
font_name: "Sans 12px",
width: textWidth,
ellipsize: Pango.EllipsizeMode.END,
text: descriptionText ? descriptionText : ""
});
this._infoText.append(this._description, Big.BoxPackFlags.EXPAND);
},
// 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;
}
}
text: descriptionText ? descriptionText : "",
x: this._name.x,
y: this._name.height + 4 });
this.actor.add_actor(this._description);
},
//// Virtual protected methods ////
// Creates and returns a large preview icon, but only if we have a detailed image.
_createLargePreviewIcon : function() {
_createLargePreviewIcon : function(availableWidth, availableHeight) {
return null;
},
@@ -296,18 +299,28 @@ GenericDisplayItem.prototype = {
throw new Error("Not implemented");
},
// Returns a preview icon for the item.
_createPreviewIcon: function() {
// Ensures the preview icon is created.
_ensurePreviewIconCreated: function() {
throw new Error("Not implemented");
},
//// Private methods ////
// Performs actions on mouse enter event for the item. Currently, shows the information button for the item.
_onEnter: function(actor, event) {
this._informationButton.show();
},
// Performs actions on mouse leave event for the item. Currently, hides the information button for the item.
_onLeave: function(actor, event) {
this._informationButton.hide();
},
// Hides the information button once the item starts being dragged.
_onDragBegin : function (draggable, time) {
// For some reason, we are not getting leave-event signal when we are dragging an item,
// so we should remove the link manually.
this._informationButton.actor.hide();
this._informationButton.hide();
}
};
@@ -315,60 +328,82 @@ 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.
*
* width - width available for the display
* height - height available for the display
*/
function GenericDisplay() {
this._init();
function GenericDisplay(width, height, numberOfColumns, columnGap) {
this._init(width, height, numberOfColumns, columnGap);
}
GenericDisplay.prototype = {
_init : function() {
_init : function(width, height, numberOfColumns, columnGap) {
this._search = '';
this._expanded = false;
this._width = null;
this._height = null;
this._columnWidth = null;
this._numberOfColumns = numberOfColumns;
this._columnGap = columnGap;
if (this._columnGap == null)
this._columnGap = DEFAULT_COLUMN_GAP;
this._maxItemsPerPage = null;
this._list = new Shell.OverflowList({ spacing: 6.0,
item_height: ITEM_DISPLAY_HEIGHT });
this._grid = new Tidy.Grid({width: this._width, height: this._height});
this._list.connect('notify::n-pages', Lang.bind(this, function () {
this._updateDisplayControl(true);
}));
this._list.connect('notify::page', Lang.bind(this, function () {
this._updateDisplayControl(false);
}));
this._setDimensionsAndMaxItems(width, 0, height);
this._grid.column_major = true;
this._grid.column_gap = this._columnGap;
// map<itemId, Object> where Object represents the item info
this._allItems = {};
// an array of itemIds of items that match the current request
this._allItems = {};
// an array of itemIds of items that match the current request
// in the order in which the items should be displayed
this._matchedItems = [];
// map<itemId, GenericDisplayItem>
this._displayedItems = {};
this._openDetailIndex = -1;
this._displayedItems = {};
this._displayedItemsCount = 0;
this._pageDisplayed = 0;
this._selectedIndex = -1;
// These two are public - .actor is the normal "actor subclass" property,
// but we also expose a .displayControl actor which is separate.
// See also getNavigationArea.
this.actor = this._list;
// See also getSideArea.
this.actor = this._grid;
this.displayControl = new Big.Box({ background_color: ITEM_DISPLAY_BACKGROUND_COLOR,
spacing: 12,
orientation: Big.BoxOrientation.HORIZONTAL});
this._availableWidthForItemDetails = this._columnWidth;
this._availableHeightForItemDetails = this._height;
this.selectedItemDetails = new Big.Box({});
},
//// Public methods ////
// Sets dimensions available for the item details display.
setAvailableDimensionsForItemDetails: function(availableWidth, availableHeight) {
this._availableWidthForItemDetails = availableWidth;
this._availableHeightForItemDetails = availableHeight;
},
// Returns dimensions available for the item details display.
getAvailableDimensionsForItemDetails: function() {
return [this._availableWidthForItemDetails, this._availableHeightForItemDetails];
},
// Sets the search string and displays the matching items.
setSearch: function(text) {
this._search = text.toLowerCase();
this._redisplay(true);
},
// Launches the item that is currently selected, closing the Overview
// Launches the item that is currently selected and emits 'activated' signal.
activateSelected: function() {
if (this._selectedIndex != -1) {
let selected = this._findDisplayedByIndex(this._selectedIndex);
selected.launch();
this.unsetSelected();
Main.overview.hide();
selected.launch()
this.emit('activated');
}
},
@@ -376,11 +411,10 @@ GenericDisplay.prototype = {
// 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._list.displayedCount;
let selectedUp = true;
let prev = this._selectedIndex - 1;
if (this._selectedIndex <= 0) {
prev = count - 1;
prev = this._displayedItemsCount - 1;
selectedUp = false;
}
this._selectIndex(prev);
@@ -391,10 +425,9 @@ GenericDisplay.prototype = {
// 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._list.displayedCount;
let selectedDown = true;
let next = this._selectedIndex + 1;
if (this._selectedIndex == count - 1) {
if (this._selectedIndex == this._displayedItemsCount - 1) {
next = 0;
selectedDown = false;
}
@@ -410,9 +443,8 @@ GenericDisplay.prototype = {
// Selects the last item among the displayed items.
selectLastItem: function() {
let count = this._list.displayedCount;
if (this.hasItems())
this._selectIndex(count - 1);
this._selectIndex(this._displayedItemsCount - 1);
},
// Returns true if the display has some item selected.
@@ -427,48 +459,48 @@ GenericDisplay.prototype = {
// 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._matchedItems.length > 0;
return this._displayedItemsCount > 0;
},
getMatchedItemsCount: function() {
return this._matchedItems.length;
// Readjusts display layout and the items displayed based on the new dimensions.
setExpanded: function(expanded, baseWidth, expandWidth, height, numberOfColumns) {
this._expanded = expanded;
this._numberOfColumns = numberOfColumns;
this._setDimensionsAndMaxItems(baseWidth, expandWidth, height);
this._grid.width = this._width;
this._grid.height = this._height;
this._pageDisplayed = 0;
this._displayMatchedItems(true);
let gridWidth = this._width;
let sideArea = this.getSideArea();
if (sideArea) {
if (expanded)
sideArea.show();
else
sideArea.hide();
}
this.emit('expanded');
},
// Load the initial state
load: function() {
// Updates the displayed items and makes the display actor visible.
show: function() {
this._grid.show();
this._redisplay(true);
},
// Should be called when the display is closed
resetState: function() {
// Hides the display actor.
hide: function() {
this._grid.hide();
this._filterReset();
this._openDetailIndex = -1;
this._removeAllDisplayItems();
},
// Returns an actor which acts as a sidebar; this is used for
// the applications category view
getNavigationArea: function () {
getSideArea: function () {
return null;
},
createDetailsForIndex: function(index) {
let item = this._findDisplayedByIndex(index);
return item.createDetailsActor();
},
// Displays the page specified by the pageNumber argument.
displayPage: function(pageNumber) {
// Cleanup from the previous selection, but don't unset this._selectedIndex
if (this.hasSelected()) {
this._findDisplayedByIndex(this._selectedIndex).markSelected(false);
}
this._list.page = pageNumber;
},
//// Protected methods ////
/*
@@ -487,7 +519,9 @@ GenericDisplay.prototype = {
let hadSelected = this.hasSelected();
this._removeAllDisplayItems();
for (let i = 0; i < this._matchedItems.length; i++) {
for (let i = this._maxItemsPerPage * this._pageDisplayed; i < this._matchedItems.length && i < this._maxItemsPerPage * (this._pageDisplayed + 1); i++) {
this._addDisplayItem(this._matchedItems[i]);
}
@@ -496,24 +530,26 @@ GenericDisplay.prototype = {
this.selectFirstItem();
}
// Check if the pointer is over one of the items and display the information button if it is.
// We want to do this between finishing our changes to the display and the point where
// the display is redrawn.
Mainloop.idle_add(Lang.bind(this,
function() {
let [child, x, y, mask] = Gdk.Screen.get_default().get_root_window().get_pointer();
let global = Shell.Global.get();
let actor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE,
x, y);
if (actor != null) {
let item = this._findDisplayedByActor(actor);
if (item != null) {
item.onDrawnUnderPointer();
}
}
return false;
}),
Meta.PRIORITY_BEFORE_REDRAW);
this._updateDisplayControl(resetDisplayControl);
// We currently redisplay matching items and raise the sideshow as part of two different callbacks.
// Checking what is under the pointer after a timeout allows us to not merge these callbacks into one, at least for now.
Mainloop.timeout_add(5,
Lang.bind(this,
function() {
// Check if the pointer is over one of the items and display the information button if it is.
let [child, x, y, mask] = Gdk.Screen.get_default().get_root_window().get_pointer();
let global = Shell.Global.get();
let actor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE,
x, y);
if (actor != null) {
let item = this._findDisplayedByActor(actor);
if (item != null) {
item.onDrawnUnderPointer();
}
}
return false;
}));
},
// Creates a display item based on the information associated with itemId
@@ -527,46 +563,40 @@ GenericDisplay.prototype = {
let itemInfo = this._allItems[itemId];
let displayItem = this._createDisplayItem(itemInfo);
displayItem.connect('activate',
displayItem.connect('activate',
Lang.bind(this,
function() {
// update the selection
this._selectIndex(this._list.get_actor_index(displayItem.actor));
this._selectIndex(this._getIndexOfDisplayedActor(displayItem.actor));
this.activateSelected();
}));
displayItem.connect('show-details',
displayItem.connect('select',
Lang.bind(this,
function() {
let index = this._list.get_actor_index(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);
}
// update the selection
this._selectIndex(this._getIndexOfDisplayedActor(displayItem.actor));
}));
this._list.add_actor(displayItem.actor);
this._grid.add_actor(displayItem.actor);
this._displayedItems[itemId] = displayItem;
this._displayedItemsCount++;
},
// Removes an item identifed by the itemId from the displayed items.
_removeDisplayItem: function(itemId) {
let count = this._list.displayedCount;
let displayItem = this._displayedItems[itemId];
let displayItemIndex = this._list.get_actor_index(displayItem.actor);
let displayItemIndex = this._getIndexOfDisplayedActor(displayItem.actor);
if (this.hasSelected() && count == 1) {
if (this.hasSelected() && (this._displayedItemsCount == 1 || !this._grid.visible)) {
this.unsetSelected();
} else if (this.hasSelected() && displayItemIndex < this._selectedIndex) {
this.selectUp();
}
}
displayItem.destroy();
delete this._displayedItems[itemId];
this._displayedItemsCount--;
},
// Removes all displayed items.
@@ -597,6 +627,9 @@ GenericDisplay.prototype = {
* their own while the user was browsing through the result pages.
*/
_redisplay: function(resetPage) {
if (!this._grid.visible)
return;
this._refreshCache();
if (!this._filterActive())
this._setDefaultList();
@@ -604,7 +637,7 @@ GenericDisplay.prototype = {
this._doSearchFilter();
if (resetPage)
this._list.page = 0;
this._pageDisplayed = 0;
this._displayMatchedItems(true);
@@ -645,6 +678,27 @@ GenericDisplay.prototype = {
//// Private methods ////
// Sets this._width, this._height, this._columnWidth, and this._maxItemsPerPage based on the
// space available for the display, number of columns, and the number of items it can fit.
_setDimensionsAndMaxItems: function(baseWidth, expandWidth, height) {
this._width = baseWidth + expandWidth;
let gridWidth;
let sideArea = this.getSideArea();
if (this._expanded && sideArea) {
gridWidth = expandWidth;
sideArea.width = baseWidth;
sideArea.height = this._height;
} else {
gridWidth = this._width;
}
this._columnWidth = (gridWidth - this._columnGap * (this._numberOfColumns - 1)) / this._numberOfColumns;
let maxItemsInColumn = Math.floor(height / ITEM_DISPLAY_HEIGHT);
this._maxItemsPerPage = maxItemsInColumn * this._numberOfColumns;
this._height = maxItemsInColumn * ITEM_DISPLAY_HEIGHT;
this._grid.width = gridWidth;
this._grid.height = this._height;
},
_getSearchMatchedItems: function() {
let matchedItemsForSearch = {};
// Break the search up into terms, and search for each
@@ -694,7 +748,13 @@ GenericDisplay.prototype = {
return 1;
else
return this._compareItems(a, b);
}));
}));
},
// Displays the page specified by the pageNumber argument. The pageNumber is 0-based.
_displayPage: function(pageNumber) {
this._pageDisplayed = pageNumber;
this._displayMatchedItems(false);
},
/*
@@ -708,28 +768,29 @@ GenericDisplay.prototype = {
_updateDisplayControl: function(resetDisplayControl) {
if (resetDisplayControl) {
this.displayControl.remove_all();
let nPages = this._list.n_pages;
let pageNumber = this._list.page;
for (let i = 0; i < nPages; i++) {
let pageControl = new Link.Link({ color: (i == pageNumber) ? DISPLAY_CONTROL_SELECTED_COLOR : ITEM_DISPLAY_DESCRIPTION_COLOR,
let pageNumber = 0;
for (let i = 0; i < this._matchedItems.length; i = i + this._maxItemsPerPage) {
let pageControl = new Link.Link({ color: (pageNumber == this._pageDisplayed) ? DISPLAY_CONTROL_SELECTED_COLOR : ITEM_DISPLAY_DESCRIPTION_COLOR,
font_name: "Sans Bold 16px",
text: (i+1) + "",
reactive: (i == pageNumber) ? false : true});
text: (pageNumber + 1) + "",
height: LABEL_HEIGHT,
reactive: (pageNumber == this._pageDisplayed) ? false : true});
this.displayControl.append(pageControl.actor, Big.BoxPackFlags.NONE);
// we use pageNumberLocalScope to get the page number right in the callback function
let pageNumberLocalScope = i;
let pageNumberLocalScope = pageNumber;
pageControl.connect('clicked',
Lang.bind(this,
function(o, event) {
this.displayPage(pageNumberLocalScope);
this._displayPage(pageNumberLocalScope);
}));
pageNumber ++;
}
} else {
let pageControlActors = this.displayControl.get_children();
for (let i = 0; i < pageControlActors.length; i++) {
for (let i = 0; i < pageControlActors.length; i++) {
let pageControlActor = pageControlActors[i];
if (i == this._list.page) {
if (i == this._pageDisplayed) {
pageControlActor.color = DISPLAY_CONTROL_SELECTED_COLOR;
pageControlActor.reactive = false;
} else {
@@ -738,15 +799,13 @@ GenericDisplay.prototype = {
}
}
}
if (this.hasSelected()) {
this.selectFirstItem();
}
},
// Returns a display item based on its index in the ordering of the
// Returns a display item based on its index in the ordering of the
// display children.
_findDisplayedByIndex: function(index) {
let actor = this._list.get_displayed_actor(index);
let displayedActors = this._grid.get_children();
let actor = displayedActors[index];
return this._findDisplayedByActor(actor);
},
@@ -762,22 +821,39 @@ GenericDisplay.prototype = {
return null;
},
// Returns and index that the actor has in the ordering of the display's
// children.
_getIndexOfDisplayedActor: function(actor) {
let children = this._grid.get_children();
for (let i = 0; i < children.length; i++) {
if (children[i] == actor)
return i;
}
return -1;
},
// 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);
if (this._selectedIndex != -1) {
let prev = this._findDisplayedByIndex(this._selectedIndex);
prev.markSelected(false);
// Calling destroy() gets large image previews released as quickly as
// possible, if we just removed them, they might hang around for a while
// until the actor was garbage collected.
let children = this.selectedItemDetails.get_children();
for (let i = 0; i < children.length; i++)
children[i].destroy();
this.selectedItemDetails.remove_all();
}
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');
if (index != -1 && index < this._displayedItemsCount) {
let item = this._findDisplayedByIndex(index);
item.markSelected(true);
this.selectedItemDetails.append(item.createDetailsActor(this._availableWidthForItemDetails, this._availableHeightForItemDetails), Big.BoxPackFlags.NONE);
this.emit('selected');
}
}
};

View File

@@ -1,589 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Big = imports.gi.Big;
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const Pango = imports.gi.Pango;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Tweener = imports.ui.tweener;
const Main = imports.ui.main;
const LG_BORDER_COLOR = new Clutter.Color();
LG_BORDER_COLOR.from_pixel(0x0000aca0);
const LG_BACKGROUND_COLOR = new Clutter.Color();
LG_BACKGROUND_COLOR.from_pixel(0x000000d5);
const GREY = new Clutter.Color();
GREY.from_pixel(0xAFAFAFFF);
const MATRIX_GREEN = new Clutter.Color();
MATRIX_GREEN.from_pixel(0x88ff66ff);
// FIXME pull from GConf
const MATRIX_FONT = 'Monospace 10';
/* Imports...feel free to add here as needed */
var commandHeader = "const Clutter = imports.gi.Clutter; " +
"const GLib = imports.gi.GLib; " +
"const Gtk = imports.gi.Gtk; " +
"const Mainloop = imports.mainloop; " +
"const Meta = imports.gi.Meta; " +
"const Shell = imports.gi.Shell; " +
"const Main = imports.ui.main; " +
"const Lang = imports.lang; " +
"const Tweener = imports.ui.tweener; " +
/* Utility functions...we should probably be able to use these
* in the shell core code too. */
"const global = Shell.Global.get(); " +
"const stage = global.stage; " +
"const color = function(pixel) { let c= new Clutter.Color(); c.from_pixel(pixel); return c; }; " +
/* Special lookingGlass functions */
"const it = Main.lookingGlass.getIt(); " +
"const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ";
function Notebook() {
this._init();
}
Notebook.prototype = {
_init: function() {
this.actor = new Big.Box();
this.tabControls = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
spacing: 4, padding: 2 });
this._selectedIndex = -1;
this._tabs = [];
},
appendPage: function(name, child) {
let labelOuterBox = new Big.Box({ padding: 2 });
let labelBox = new Big.Box({ padding: 2, border_color: MATRIX_GREEN,
reactive: true });
labelOuterBox.append(labelBox, Big.BoxPackFlags.NONE);
let label = new Clutter.Text({ color: MATRIX_GREEN,
font_name: MATRIX_FONT,
text: name });
labelBox.connect('button-press-event', Lang.bind(this, function () {
this.selectChild(child);
return true;
}));
labelBox.append(label, Big.BoxPackFlags.EXPAND);
this._tabs.push([child, labelBox]);
child.hide();
this.actor.append(child, Big.BoxPackFlags.EXPAND);
this.tabControls.append(labelOuterBox, Big.BoxPackFlags.NONE);
if (this._selectedIndex == -1)
this.selectIndex(0);
},
_unselect: function() {
if (this._selectedIndex < 0)
return;
let [child, labelBox] = this._tabs[this._selectedIndex];
labelBox.padding = 2;
labelBox.border = 0;
child.hide();
this._selectedIndex = -1;
},
selectIndex: function(index) {
if (index == this._selectedIndex)
return;
this._unselect();
if (index < 0) {
this.emit('selection', null);
return;
}
let [child, labelBox] = this._tabs[index];
labelBox.padding = 1;
labelBox.border = 1;
child.show();
this._selectedIndex = index;
this.emit('selection', child);
},
selectChild: function(child) {
if (child == null)
this.selectIndex(-1);
else {
for (let i = 0; i < this._tabs.length; i++) {
let [tabChild, labelBox] = this._tabs[i];
if (tabChild == child) {
this.selectIndex(i);
return;
}
}
}
}
}
Signals.addSignalMethods(Notebook.prototype);
function Result(command, o, index) {
this._init(command, o, index);
}
Result.prototype = {
_init : function(command, o, index) {
this.index = index;
this.o = o;
this.actor = new Big.Box();
let cmdTxt = new Clutter.Text({ color: MATRIX_GREEN,
font_name: MATRIX_FONT,
ellipsize: Pango.EllipsizeMode.END,
text: command });
this.actor.append(cmdTxt, Big.BoxPackFlags.NONE);
let resultTxt = new Clutter.Text({ color: MATRIX_GREEN,
font_name: MATRIX_FONT,
ellipsize: Pango.EllipsizeMode.END,
text: "r(" + index + ") = " + o });
this.actor.append(resultTxt, Big.BoxPackFlags.NONE);
let line = new Big.Box({ border_color: GREY,
border_bottom: 1,
height: 8 });
this.actor.append(line, Big.BoxPackFlags.NONE);
}
}
function ActorHierarchy() {
this._init();
}
ActorHierarchy.prototype = {
_init : function () {
this._previousTarget = null;
this._target = null;
this._parentList = [];
this.actor = new Big.Box({ spacing: 4,
border: 1,
padding: 4,
border_color: GREY });
},
setTarget: function(actor) {
this._previousTarget = this._target;
this.target = actor;
this.actor.remove_all();
if (!(actor instanceof Clutter.Actor))
return;
if (this.target == null)
return;
this._parentList = [];
let parent = actor;
while ((parent = parent.get_parent()) != null) {
this._parentList.push(parent);
let link = new Clutter.Text({ color: MATRIX_GREEN,
font_name: MATRIX_FONT,
reactive: true,
text: "" + parent });
this.actor.append(link, Big.BoxPackFlags.IF_FITS);
let parentTarget = parent;
link.connect('button-press-event', Lang.bind(this, function () {
this._selectByActor(parentTarget);
return true;
}));
}
this.emit('selection', actor);
},
_selectByActor: function(actor) {
let idx = this._parentList.indexOf(actor);
let children = this.actor.get_children();
let link = children[idx];
this.emit('selection', actor);
}
}
Signals.addSignalMethods(ActorHierarchy.prototype);
function PropertyInspector() {
this._init();
}
PropertyInspector.prototype = {
_init : function () {
this._target = null;
this._parentList = [];
this.actor = new Big.Box({ spacing: 4,
border: 1,
padding: 4,
border_color: GREY });
},
setTarget: function(actor) {
this.target = actor;
this.actor.remove_all();
for (let propName in actor) {
let valueStr;
try {
valueStr = "" + actor[propName];
} catch (e) {
valueStr = '<error>';
}
let propText = propName + ": " + valueStr;
let propDisplay = new Clutter.Text({ color: MATRIX_GREEN,
font_name: MATRIX_FONT,
reactive: true,
text: propText });
this.actor.append(propDisplay, Big.BoxPackFlags.IF_FITS);
}
}
}
function Inspector() {
this._init();
}
Inspector.prototype = {
_init: function() {
let global = Shell.Global.get();
let width = 150;
let eventHandler = new Big.Box({ background_color: LG_BACKGROUND_COLOR,
border: 1,
border_color: LG_BORDER_COLOR,
corner_radius: 4,
y: global.stage.height/2,
reactive: true
});
eventHandler.connect('notify::allocation', Lang.bind(this, function () {
eventHandler.x = Math.floor((global.stage.width)/2 - (eventHandler.width)/2);
}));
global.stage.add_actor(eventHandler);
let displayText = new Clutter.Text({ color: MATRIX_GREEN,
font_name: MATRIX_FONT, text: '' });
eventHandler.append(displayText, Big.BoxPackFlags.EXPAND);
let borderPaintTarget = null;
let borderPaintId = null;
eventHandler.connect('destroy', Lang.bind(this, function() {
if (borderPaintTarget != null)
borderPaintTarget.disconnect(borderPaintId);
}));
eventHandler.connect('button-press-event', Lang.bind(this, function (actor, event) {
let global = Shell.Global.get();
Clutter.ungrab_pointer(eventHandler);
let [stageX, stageY] = event.get_coords();
let target = global.stage.get_actor_at_pos(Clutter.PickMode.ALL,
stageX,
stageY);
this.emit('target', target, stageX, stageY);
eventHandler.destroy();
this.emit('closed');
return true;
}));
eventHandler.connect('motion-event', Lang.bind(this, function (actor, event) {
let global = Shell.Global.get();
let [stageX, stageY] = event.get_coords();
let target = global.stage.get_actor_at_pos(Clutter.PickMode.ALL,
stageX,
stageY);
displayText.text = '<inspect x: ' + stageX + ' y: ' + stageY + '> ' + target;
if (borderPaintTarget != null)
borderPaintTarget.disconnect(borderPaintId);
borderPaintTarget = target;
borderPaintId = Shell.add_hook_paint_red_border(target);
return true;
}));
Clutter.grab_pointer(eventHandler);
}
}
Signals.addSignalMethods(Inspector.prototype);
function LookingGlass() {
this._init();
}
LookingGlass.prototype = {
_init : function() {
let global = Shell.Global.get();
this._idleHistorySaveId = 0;
let historyPath = global.configdir + "/lookingglass-history.txt";
this._historyFile = Gio.file_new_for_path(historyPath);
this._savedText = null;
this._historyNavIndex = -1;
this._history = [];
this._readHistory();
this._open = false;
this._offset = 0;
this._results = [];
// TODO replace with scrolling or something better
this._maxItems = 10;
this.actor = new Big.Box({ background_color: LG_BACKGROUND_COLOR,
border: 1,
border_color: LG_BORDER_COLOR,
corner_radius: 4,
padding_top: 8,
padding_left: 4,
padding_right: 4,
padding_bottom: 4,
spacing: 4,
visible: false
});
global.stage.add_actor(this.actor);
let toolbar = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
border: 1, border_color: GREY,
corner_radius: 4 });
this.actor.append(toolbar, Big.BoxPackFlags.NONE);
let inspectIcon = Shell.TextureCache.get_default().load_gicon(new Gio.ThemedIcon({ name: 'gtk-color-picker' }),
24);
toolbar.append(inspectIcon, Big.BoxPackFlags.NONE);
inspectIcon.reactive = true;
inspectIcon.connect('button-press-event', Lang.bind(this, function () {
let inspector = new Inspector();
inspector.connect('target', Lang.bind(this, function(i, target, stageX, stageY) {
this._pushResult('<inspect x:' + stageX + ' y:' + stageY + '>',
target);
this._hierarchy.setTarget(target);
}));
inspector.connect('closed', Lang.bind(this, function() {
this.actor.show();
global.stage.set_key_focus(this._entry);
}));
this.actor.hide();
return true;
}));
let notebook = new Notebook();
this.actor.append(notebook.actor, Big.BoxPackFlags.EXPAND);
toolbar.append(notebook.tabControls, Big.BoxPackFlags.END);
this._evalBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
spacing: 4 });
notebook.appendPage('Evaluator', this._evalBox);
this._resultsArea = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
spacing: 4 });
this._evalBox.append(this._resultsArea, Big.BoxPackFlags.EXPAND);
let entryArea = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL });
this._evalBox.append(entryArea, Big.BoxPackFlags.NONE);
let label = new Clutter.Text({ color: MATRIX_GREEN,
font_name: MATRIX_FONT,
text: 'js>>> ' });
entryArea.append(label, Big.BoxPackFlags.NONE);
this._entry = new Clutter.Text({ color: MATRIX_GREEN,
font_name: MATRIX_FONT,
editable: true,
activatable: true,
singleLineMode: true,
text: ''});
/* kind of a hack */
notebook.connect('selection', Lang.bind(this, function (nb, child) {
if (child == this._evalBox)
global.stage.set_key_focus(this._entry);
}));
entryArea.append(this._entry, Big.BoxPackFlags.EXPAND);
this._hierarchy = new ActorHierarchy();
notebook.appendPage('Hierarchy', this._hierarchy.actor);
this._propInspector = new PropertyInspector();
notebook.appendPage('Properties', this._propInspector.actor);
this._hierarchy.connect('selection', Lang.bind(this, function (h, actor) {
this._pushResult('<parent selection>', actor);
notebook.selectIndex(0);
}));
this._entry.connect('activate', Lang.bind(this, function (o, e) {
let text = o.get_text();
// Ensure we don't get newlines in the command; the history file is
// newline-separated.
text.replace('\n', ' ');
// Strip leading and trailing whitespace
text = text.replace(/^\s+/g, "").replace(/\s+$/g, "");
if (text == '')
return true;
this._evaluate(text);
this._historyNavIndex = -1;
return true;
}));
this._entry.connect('key-press-event', Lang.bind(this, function(o, e) {
let symbol = Shell.get_event_key_symbol(e);
if (symbol == Clutter.Escape) {
this.close();
return true;
} else 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;
}
}));
},
_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);
this._results.push(result);
this._resultsArea.append(result.actor, Big.BoxPackFlags.NONE);
this._propInspector.setTarget(obj);
let children = this._resultsArea.get_children();
if (children.length > this._maxItems) {
this._results.shift();
children[0].destroy();
this._offset++;
}
this._it = obj;
},
_evaluate : function(command) {
this._history.push(command);
this._queueHistorySave();
let fullCmd = commandHeader + command;
let resultObj;
try {
resultObj = eval(fullCmd);
} catch (e) {
resultObj = "<exception " + e + ">";
}
this._pushResult(command, resultObj);
this._hierarchy.setTarget(null);
this._entry.text = '';
},
getIt: function () {
return this._it;
},
getResult: function(idx) {
return this._results[idx - this._offset].o;
},
toggle: function() {
if (this._open)
this.close();
else
this.open();
},
_resizeTo: function(actor) {
let stage = Shell.Global.get().stage;
let stageWidth = stage.width;
let myWidth = stage.width * 0.7;
let myHeight = stage.height * 0.7;
let [srcX, srcY] = actor.get_transformed_position();
this.actor.x = srcX + (stage.width-myWidth)/2;
this._hiddenY = srcY + actor.height - myHeight - 4; // -4 to hide the top corners
this._targetY = this._hiddenY + myHeight;
this.actor.y = this._hiddenY;
this.actor.width = myWidth;
this.actor.height = myHeight;
},
slaveTo: function(actor) {
this._slaveTo = actor;
actor.connect('notify::allocation', Lang.bind(this, function () {
this._resizeTo(actor);
}));
this._resizeTo(actor);
},
open : function() {
if (this._open)
return;
this.actor.show();
this.actor.lower(Main.chrome.actor);
this._open = true;
Tweener.removeTweens(this.actor);
if (!Main.beginModal())
return;
let global = Shell.Global.get();
global.stage.set_key_focus(this._entry);
Tweener.addTween(this.actor, { time: 0.5,
transition: "easeOutQuad",
y: this._targetY
});
},
close : function() {
if (!this._open)
return;
this._historyNavIndex = -1;
this._open = false;
Tweener.removeTweens(this.actor);
Main.endModal();
Tweener.addTween(this.actor, { time: 0.5,
transition: "easeOutQuad",
y: this._hiddenY,
onComplete: Lang.bind(this, function () {
this.actor.hide();
})
});
}
};
Signals.addSignalMethods(LookingGlass.prototype);

View File

@@ -3,7 +3,6 @@
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 Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
@@ -11,10 +10,9 @@ const Shell = imports.gi.Shell;
const Signals = imports.signals;
const Chrome = imports.ui.chrome;
const Overview = imports.ui.overview;
const Overlay = imports.ui.overlay;
const Panel = imports.ui.panel;
const RunDialog = imports.ui.runDialog;
const LookingGlass = imports.ui.lookingGlass;
const Sidebar = imports.ui.sidebar;
const Tweener = imports.ui.tweener;
const WindowManager = imports.ui.windowManager;
@@ -25,9 +23,8 @@ DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff);
let chrome = null;
let panel = null;
let sidebar = null;
let overview = null;
let overlay = null;
let runDialog = null;
let lookingGlass = null;
let wm = null;
let recorder = null;
let inModal = false;
@@ -38,46 +35,49 @@ function start() {
Gio.DesktopAppInfo.set_desktop_env("GNOME");
global.grab_dbus_service();
global.start_task_panel();
Tweener.init();
// Ensure ShellAppMonitor is initialized; this will
// also initialize ShellAppSystem first. ShellAppSystem
// needs to load all the .desktop files, and ShellAppMonitor
// will use those to associate with windows. Right now
// the Monitor doesn't listen for installed app changes
// and recalculate application associations, so to avoid
// races for now we initialize it here. It's better to
// be predictable anyways.
Shell.AppMonitor.get_default();
// The background color really only matters if there is no desktop
// window (say, nautilus) running. We set it mostly so things look good
// when we are running inside Xephyr.
global.stage.color = DEFAULT_BACKGROUND_COLOR;
// Mutter currently hardcodes putting "Yessir. The compositor is running""
// in the Overview. Clear that out.
// in the overlay. Clear that out.
let children = global.overlay_group.get_children();
for (let i = 0; i < children.length; i++)
children[i].destroy();
global.connect('panel-run-dialog', function(panel) {
// Make sure not more than one run dialog is shown.
if (runDialog == null) {
if (!runDialog) {
runDialog = new RunDialog.RunDialog();
let endHandler = function() {
runDialog.destroy();
runDialog = null;
};
runDialog.connect('run', endHandler);
runDialog.connect('cancel', endHandler);
if (!runDialog.show())
endHandler();
}
runDialog.open();
});
overview = new Overview.Overview();
overlay = new Overlay.Overlay();
chrome = new Chrome.Chrome();
panel = new Panel.Panel();
sidebar = new Sidebar.Sidebar();
wm = new WindowManager.WindowManager();
global.screen.connect('toggle-recording', function() {
if (recorder == null) {
// We have to initialize GStreamer first. This isn't done
// inside ShellRecorder to make it usable inside projects
// with other usage of GStreamer.
let Gst = imports.gi.Gst;
Gst.init(null, null);
recorder = new Shell.Recorder({ stage: global.stage });
}
@@ -88,25 +88,13 @@ function start() {
}
});
_relayout();
panel.startupAnimation();
let display = global.screen.get_display();
display.connect('overlay-key', Lang.bind(overview, overview.toggle));
global.connect('panel-main-menu', Lang.bind(overview, overview.toggle));
global.stage.connect('captured-event', _globalKeyPressHandler);
display.connect('overlay-key', Lang.bind(overlay, overlay.toggle));
global.connect('panel-main-menu', Lang.bind(overlay, overlay.toggle));
Mainloop.idle_add(_removeUnusedWorkspaces);
}
function _relayout() {
let global = Shell.Global.get();
panel.actor.set_size(global.screen_width, Panel.PANEL_HEIGHT);
overview.relayout();
}
// 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
@@ -137,60 +125,13 @@ function _removeUnusedWorkspaces() {
return false;
}
// This function encapsulates hacks to make certain global keybindings
// work even when we are in one of our modes where global keybindings
// 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 (!inModal)
return false;
let type = event.type();
if (type == Clutter.EventType.KEY_PRESS) {
let symbol = Shell.get_event_key_symbol (event);
if (symbol == Clutter.Print) {
// We want to be able to take screenshots of the shell at all times
let gconf = Shell.GConf.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;
}
} else if (type == Clutter.EventType.KEY_RELEASE) {
let symbol = Shell.get_event_key_symbol (event);
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;
}
}
return false;
}
// Used to go into a mode where all keyboard and mouse input goes to
// the stage. Returns true if we successfully grabbed the keyboard and
// went modal, false otherwise
function beginModal() {
function startModal() {
let global = Shell.Global.get();
let timestamp = global.screen.get_display().get_current_time();
if (!global.begin_modal(timestamp))
if (!global.grab_keyboard())
return false;
global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
@@ -201,21 +142,12 @@ function beginModal() {
function endModal() {
let global = Shell.Global.get();
let timestamp = global.screen.get_display().get_current_time();
global.end_modal(timestamp);
global.ungrab_keyboard();
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
inModal = false;
}
function createLookingGlass() {
if (lookingGlass == null) {
lookingGlass = new LookingGlass.LookingGlass();
lookingGlass.slaveTo(panel.actor);
}
return lookingGlass;
}
function createAppLaunchContext() {
let global = Shell.Global.get();
let screen = global.screen;

954
js/ui/overlay.js Normal file
View File

@@ -0,0 +1,954 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Big = imports.gi.Big;
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const Lang = imports.lang;
const AppDisplay = imports.ui.appDisplay;
const DocDisplay = imports.ui.docDisplay;
const GenericDisplay = imports.ui.genericDisplay;
const Link = imports.ui.link;
const Main = imports.ui.main;
const Panel = imports.ui.panel;
const Tweener = imports.ui.tweener;
const Workspaces = imports.ui.workspaces;
const ROOT_OVERLAY_COLOR = new Clutter.Color();
ROOT_OVERLAY_COLOR.from_pixel(0x000000bb);
// The factor to scale the overlay wallpaper with. This should not be less
// than 3/2, because the rule of thirds is used for positioning (see below).
const BACKGROUND_SCALE = 2;
const LABEL_HEIGHT = 16;
const DASH_MIN_WIDTH = 250;
const DASH_SECTION_PADDING = 6;
const DASH_SECTION_SPACING = 6;
const DASH_COLUMNS = 1;
const DASH_CORNER_RADIUS = 5;
// This is the height of section components other than the item display.
const DASH_SECTION_MISC_HEIGHT = (LABEL_HEIGHT + DASH_SECTION_SPACING) * 2 + DASH_SECTION_PADDING;
const DASH_SEARCH_BG_COLOR = new Clutter.Color();
DASH_SEARCH_BG_COLOR.from_pixel(0xffffffff);
const DASH_TEXT_COLOR = new Clutter.Color();
DASH_TEXT_COLOR.from_pixel(0xffffffff);
// Time for initial animation going into overlay mode
const ANIMATION_TIME = 0.25;
// We divide the screen into a grid of rows and columns, which we use
// to help us position the overlay components, such as the side panel
// that lists applications and documents, the workspaces display, and
// the button for adding additional workspaces.
// In the regular mode, the side panel takes up one column on the left,
// and the workspaces display takes up the remaining columns.
// In the expanded side panel display mode, the side panel takes up two
// columns, and the workspaces display slides all the way to the right,
// being visible only in the last quarter of the right-most column.
// In the future, this mode will have more components, such as a display
// of documents which were recently opened with a given application, which
// will take up the remaining sections of the display.
const WIDE_SCREEN_CUT_OFF_RATIO = 1.4;
const COLUMNS_REGULAR_SCREEN = 4;
const ROWS_REGULAR_SCREEN = 8;
const COLUMNS_WIDE_SCREEN = 5;
const ROWS_WIDE_SCREEN = 10;
// Padding around workspace grid / Spacing between Dash and Workspaces
const WORKSPACE_GRID_PADDING = 12;
const COLUMNS_FOR_WORKSPACES_REGULAR_SCREEN = 3;
const ROWS_FOR_WORKSPACES_REGULAR_SCREEN = 6;
const COLUMNS_FOR_WORKSPACES_WIDE_SCREEN = 4;
const ROWS_FOR_WORKSPACES_WIDE_SCREEN = 8;
// A multi-state; PENDING is used during animations
const STATE_ACTIVE = true;
const STATE_PENDING_INACTIVE = false;
const STATE_INACTIVE = false;
// The dash has a slightly transparent blue background with a gradient.
const DASH_LEFT_COLOR = new Clutter.Color();
DASH_LEFT_COLOR.from_pixel(0x324c6fbb);
const DASH_MIDDLE_COLOR = new Clutter.Color();
DASH_MIDDLE_COLOR.from_pixel(0x324c6faa);
const DASH_RIGHT_COLOR = new Clutter.Color();
DASH_RIGHT_COLOR.from_pixel(0x324c6fcc);
const DASH_BORDER_COLOR = new Clutter.Color();
DASH_BORDER_COLOR.from_pixel(0x213b5dff);
const DASH_BORDER_WIDTH = 2;
// The results and details panes have a somewhat transparent blue background with a gradient.
const PANE_LEFT_COLOR = new Clutter.Color();
PANE_LEFT_COLOR.from_pixel(0x324c6ff4);
const PANE_MIDDLE_COLOR = new Clutter.Color();
PANE_MIDDLE_COLOR.from_pixel(0x324c6ffa);
const PANE_RIGHT_COLOR = new Clutter.Color();
PANE_RIGHT_COLOR.from_pixel(0x324c6ff4);
const SHADOW_COLOR = new Clutter.Color();
SHADOW_COLOR.from_pixel(0x00000033);
const TRANSPARENT_COLOR = new Clutter.Color();
TRANSPARENT_COLOR.from_pixel(0x00000000);
const SHADOW_WIDTH = 6;
const NUMBER_OF_SECTIONS_IN_SEARCH = 2;
let wideScreen = false;
let displayGridColumnWidth = null;
let displayGridRowHeight = null;
function SearchEntry(width) {
this._init(width);
}
SearchEntry.prototype = {
_init : function(width) {
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
y_align: Big.BoxAlignment.CENTER,
background_color: DASH_SEARCH_BG_COLOR,
corner_radius: 4,
spacing: 4,
padding_left: 4,
padding_right: 4,
width: width,
height: 24
});
let icontheme = Gtk.IconTheme.get_default();
let searchIconTexture = new Clutter.Texture({});
let searchIconPath = icontheme.lookup_icon('gtk-find', 16, 0).get_filename();
searchIconTexture.set_from_file(searchIconPath);
this.actor.append(searchIconTexture, 0);
// We need to initialize the text for the entry to have the cursor displayed
// in it. See http://bugzilla.openedhand.com/show_bug.cgi?id=1365
this.entry = new Clutter.Text({ font_name: "Sans 14px",
editable: true,
activatable: true,
singleLineMode: true,
text: ""
});
this.entry.connect('text-changed', Lang.bind(this, function (e) {
let text = this.entry.text;
}));
this.actor.append(this.entry, Big.BoxPackFlags.EXPAND);
}
};
function ItemResults(resultsWidth, resultsHeight, displayClass, labelText) {
this._init(resultsWidth, resultsHeight, displayClass, labelText);
}
ItemResults.prototype = {
_init: function(resultsWidth, resultsHeight, displayClass, labelText) {
this._resultsWidth = resultsWidth;
this._resultsHeight = resultsHeight;
this.actor = new Big.Box({ height: resultsHeight,
padding: DASH_SECTION_PADDING + DASH_BORDER_WIDTH,
spacing: DASH_SECTION_SPACING });
this._resultsText = new Clutter.Text({ color: DASH_TEXT_COLOR,
font_name: "Sans Bold 14px",
text: labelText });
this.actor.append(this._resultsText, Big.BoxPackFlags.NONE);
// LABEL_HEIGHT is the height of this._resultsText and GenericDisplay.LABEL_HEIGHT is the height
// of the display controls.
this._displayHeight = resultsHeight - LABEL_HEIGHT - GenericDisplay.LABEL_HEIGHT - DASH_SECTION_SPACING * 2;
this.display = new displayClass(resultsWidth, this._displayHeight, DASH_COLUMNS, DASH_SECTION_SPACING);
this.actor.append(this.display.actor, Big.BoxPackFlags.EXPAND);
this.controlBox = new Big.Box({ x_align: Big.BoxAlignment.CENTER });
this.controlBox.append(this.display.displayControl, Big.BoxPackFlags.NONE);
this.actor.append(this.controlBox, Big.BoxPackFlags.END);
},
_setSearchMode: function() {
this.actor.height = this._resultsHeight / NUMBER_OF_SECTIONS_IN_SEARCH;
let displayHeight = this._displayHeight - this._resultsHeight * (NUMBER_OF_SECTIONS_IN_SEARCH - 1) / NUMBER_OF_SECTIONS_IN_SEARCH;
this.display.setExpanded(false, this._resultsWidth, 0, displayHeight, DASH_COLUMNS);
this.actor.remove_all();
this.actor.append(this._resultsText, Big.BoxPackFlags.NONE);
this.actor.append(this.display.actor, Big.BoxPackFlags.EXPAND);
this.actor.append(this.controlBox, Big.BoxPackFlags.END);
},
_unsetSearchMode: function() {
this.actor.height = this._resultsHeight;
this.display.setExpanded(false, this._resultsWidth, 0, this._displayHeight, DASH_COLUMNS);
this.actor.remove_all();
this.actor.append(this._resultsText, Big.BoxPackFlags.NONE);
this.actor.append(this.display.actor, Big.BoxPackFlags.EXPAND);
this.actor.append(this.controlBox, Big.BoxPackFlags.END);
}
}
function Dash() {
this._init();
}
Dash.prototype = {
_init : function() {
let me = this;
this._moreAppsMode = false;
this._moreDocsMode = false;
this._width = displayGridColumnWidth;
this._displayWidth = displayGridColumnWidth - DASH_SECTION_PADDING * 2;
this._resultsWidth = displayGridColumnWidth;
this._detailsWidth = displayGridColumnWidth * 2;
let bottomHeight = DASH_SECTION_PADDING;
let global = Shell.Global.get();
let resultsHeight = global.screen_height - Panel.PANEL_HEIGHT - DASH_SECTION_PADDING - bottomHeight;
let detailsHeight = global.screen_height - Panel.PANEL_HEIGHT - DASH_SECTION_PADDING - bottomHeight;
// The whole dash group needs to be reactive so that the clicks are not passed to the transparent background underneath it.
// This background is used in the workspaces area when the additional dash panes are being shown. It handles clicks in the
// workspaces area by closing these additional dash panes and revealing all workspaces.
this.actor = new Clutter.Group({reactive: true});
this.actor.height = global.screen_height;
let dashPane = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
x: 0,
y: Panel.PANEL_HEIGHT + DASH_SECTION_PADDING,
width: this._width + SHADOW_WIDTH,
height: global.screen_height - Panel.PANEL_HEIGHT - DASH_SECTION_PADDING - bottomHeight});
let dashBackground = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
width: this._width,
height: global.screen_height - Panel.PANEL_HEIGHT - DASH_SECTION_PADDING - bottomHeight,
corner_radius: DASH_CORNER_RADIUS,
border: DASH_BORDER_WIDTH,
border_color: DASH_BORDER_COLOR });
dashPane.append(dashBackground, Big.BoxPackFlags.EXPAND);
let dashLeft = global.create_horizontal_gradient(DASH_LEFT_COLOR,
DASH_MIDDLE_COLOR);
let dashRight = global.create_horizontal_gradient(DASH_MIDDLE_COLOR,
DASH_RIGHT_COLOR);
let dashShadow = global.create_horizontal_gradient(SHADOW_COLOR,
TRANSPARENT_COLOR);
dashShadow.set_width(SHADOW_WIDTH);
dashBackground.append(dashLeft, Big.BoxPackFlags.EXPAND);
dashBackground.append(dashRight, Big.BoxPackFlags.EXPAND);
dashPane.append(dashShadow, Big.BoxPackFlags.NONE);
this.actor.add_actor(dashPane);
this._searchEntry = new SearchEntry(this._width - DASH_SECTION_PADDING * 2 - DASH_BORDER_WIDTH * 2);
this.actor.add_actor(this._searchEntry.actor);
this._searchEntry.actor.set_position(DASH_SECTION_PADDING + DASH_BORDER_WIDTH, dashPane.y + DASH_SECTION_PADDING + DASH_BORDER_WIDTH);
this._searchQueued = false;
this._searchEntry.entry.connect('text-changed', function (se, prop) {
if (me._searchQueued)
return;
me._searchQueued = true;
Mainloop.timeout_add(250, function() {
// Strip leading and trailing whitespace
let text = me._searchEntry.entry.text.replace(/^\s+/g, "").replace(/\s+$/g, "");
me._searchQueued = false;
me._resultsAppsSection.display.setSearch(text);
me._resultsDocsSection.display.setSearch(text);
if (text == '')
me._unsetSearchMode();
else
me._setSearchMode();
return false;
});
});
this._searchEntry.entry.connect('activate', function (se) {
// only one of the displays will have an item selected, so it's ok to
// call activateSelected() on all of them
me._docDisplay.activateSelected();
me._resultsAppsSection.display.activateSelected();
me._resultsDocsSection.display.activateSelected();
return true;
});
this._searchEntry.entry.connect('key-press-event', function (se, e) {
let symbol = Shell.get_event_key_symbol(e);
if (symbol == Clutter.Escape) {
// Escape will keep clearing things back to the desktop. First, if
// we have active text, we remove it.
if (me._searchEntry.entry.text != '')
me._searchEntry.entry.text = '';
// Next, if we're in one of the "more" modes or showing the details pane, close them
else if (me._moreAppsMode || me._moreDocsMode || me._detailsShowing())
me.unsetMoreMode();
// Finally, just close the overlay entirely
else
me.emit('activated');
return true;
} else if (symbol == Clutter.Up) {
// selectUp and selectDown wrap around in their respective displays
// too, but there doesn't seem to be any flickering if we first select
// something in one display, but then unset the selection, and move
// it to the other display, so it's ok to do that.
// TODO: add the right logic
} else if (symbol == Clutter.Down) {
// TODO: add the right logic
}
return false;
});
this._appsText = new Clutter.Text({ color: DASH_TEXT_COLOR,
font_name: "Sans Bold 14px",
text: "Applications",
height: LABEL_HEIGHT});
this._appsSection = new Big.Box({ x: DASH_SECTION_PADDING,
y: this._searchEntry.actor.y + this._searchEntry.actor.height + DASH_SECTION_PADDING,
padding_top: DASH_SECTION_PADDING,
spacing: DASH_SECTION_SPACING});
this._appsSection.append(this._appsText, Big.BoxPackFlags.EXPAND);
this._itemDisplayHeight = global.screen_height - this._appsSection.y - DASH_SECTION_MISC_HEIGHT * 2 - bottomHeight;
this._appsContent = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL });
this._appsSection.append(this._appsContent, Big.BoxPackFlags.EXPAND);
this._appWell = new AppDisplay.AppWell(this._displayWidth);
this._appWell.actor.show();
this._appsContent.append(this._appWell.actor, Big.BoxPackFlags.EXPAND);
let moreAppsBox = new Big.Box({x_align: Big.BoxAlignment.END});
this._moreAppsLink = new Link.Link({ color: DASH_TEXT_COLOR,
font_name: "Sans Bold 14px",
text: "More...",
height: LABEL_HEIGHT });
moreAppsBox.append(this._moreAppsLink.actor, Big.BoxPackFlags.EXPAND);
this._appsSection.append(moreAppsBox, Big.BoxPackFlags.EXPAND);
this.actor.add_actor(this._appsSection);
this._appsSectionDefaultHeight = this._appsSection.height;
this._docsSection = new Big.Box({ x: DASH_SECTION_PADDING,
y: this._appsSection.y + this._appsSection.height,
padding_top: DASH_SECTION_PADDING,
spacing: DASH_SECTION_SPACING});
this._docsText = new Clutter.Text({ color: DASH_TEXT_COLOR,
font_name: "Sans Bold 14px",
text: "Recent Documents",
height: LABEL_HEIGHT});
this._docsSection.append(this._docsText, Big.BoxPackFlags.EXPAND);
this._docDisplay = new DocDisplay.DocDisplay(this._displayWidth, this._itemDisplayHeight - this._appsContent.height, DASH_COLUMNS, DASH_SECTION_PADDING);
this._docsSection.append(this._docDisplay.actor, Big.BoxPackFlags.EXPAND);
let moreDocsBox = new Big.Box({x_align: Big.BoxAlignment.END});
this._moreDocsLink = new Link.Link({ color: DASH_TEXT_COLOR,
font_name: "Sans Bold 14px",
text: "More...",
height: LABEL_HEIGHT });
moreDocsBox.append(this._moreDocsLink.actor, Big.BoxPackFlags.EXPAND);
this._docsSection.append(moreDocsBox, Big.BoxPackFlags.EXPAND);
this.actor.add_actor(this._docsSection);
this._docsSectionDefaultHeight = this._docsSection.height;
// The "More" or search results area
this._resultsAppsSection = new ItemResults(this._displayWidth, resultsHeight, AppDisplay.AppDisplay, "Applications");
this._resultsDocsSection = new ItemResults(this._displayWidth, resultsHeight, DocDisplay.DocDisplay, "Documents");
this._resultsPane = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
x: this._width,
y: Panel.PANEL_HEIGHT + DASH_SECTION_PADDING,
width: this._resultsWidth + SHADOW_WIDTH,
height: resultsHeight });
let resultsBackground = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
width: this._resultsWidth,
height: resultsHeight,
corner_radius: DASH_CORNER_RADIUS,
border: DASH_BORDER_WIDTH,
border_color: DASH_BORDER_COLOR });
this._resultsPane.append(resultsBackground, Big.BoxPackFlags.EXPAND);
let resultsLeft = global.create_horizontal_gradient(PANE_LEFT_COLOR,
PANE_MIDDLE_COLOR);
let resultsRight = global.create_horizontal_gradient(PANE_MIDDLE_COLOR,
PANE_RIGHT_COLOR);
let resultsShadow = global.create_horizontal_gradient(SHADOW_COLOR,
TRANSPARENT_COLOR);
resultsShadow.set_width(SHADOW_WIDTH);
resultsBackground.append(resultsLeft, Big.BoxPackFlags.EXPAND);
resultsBackground.append(resultsRight, Big.BoxPackFlags.EXPAND);
this._resultsPane.append(resultsShadow, Big.BoxPackFlags.NONE);
this.actor.add_actor(this._resultsPane);
this._resultsPane.hide();
this._detailsPane = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
x: this._width,
y: Panel.PANEL_HEIGHT + DASH_SECTION_PADDING,
width: this._detailsWidth + SHADOW_WIDTH,
height: detailsHeight });
this._firstSelectAfterOverlayShow = true;
let detailsBackground = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
width: this._detailsWidth,
height: detailsHeight,
corner_radius: DASH_CORNER_RADIUS,
border: DASH_BORDER_WIDTH,
border_color: DASH_BORDER_COLOR });
this._detailsPane.append(detailsBackground, Big.BoxPackFlags.EXPAND);
let detailsLeft = global.create_horizontal_gradient(PANE_LEFT_COLOR,
PANE_MIDDLE_COLOR);
let detailsRight = global.create_horizontal_gradient(PANE_MIDDLE_COLOR,
PANE_RIGHT_COLOR);
let detailsShadow = global.create_horizontal_gradient(SHADOW_COLOR,
TRANSPARENT_COLOR);
detailsShadow.set_width(SHADOW_WIDTH);
detailsBackground.append(detailsLeft, Big.BoxPackFlags.EXPAND);
detailsBackground.append(detailsRight, Big.BoxPackFlags.EXPAND);
this._detailsPane.append(detailsShadow, Big.BoxPackFlags.NONE);
this._detailsContent = new Big.Box({ padding: DASH_SECTION_PADDING + DASH_BORDER_WIDTH });
this._detailsPane.add_actor(this._detailsContent);
this.actor.add_actor(this._detailsPane);
this._detailsPane.hide();
let itemDetailsAvailableWidth = this._detailsWidth - DASH_SECTION_PADDING * 2 - DASH_BORDER_WIDTH * 2;
let itemDetailsAvailableHeight = detailsHeight - DASH_SECTION_PADDING * 2 - DASH_BORDER_WIDTH * 2;
this._docDisplay.setAvailableDimensionsForItemDetails(itemDetailsAvailableWidth, itemDetailsAvailableHeight);
this._resultsAppsSection.display.setAvailableDimensionsForItemDetails(itemDetailsAvailableWidth, itemDetailsAvailableHeight);
this._resultsDocsSection.display.setAvailableDimensionsForItemDetails(itemDetailsAvailableWidth, itemDetailsAvailableHeight);
/* Proxy the activated signals */
this._appWell.connect('activated', function(well) {
me.emit('activated');
});
this._docDisplay.connect('activated', function(docDisplay) {
me.emit('activated');
});
this._resultsAppsSection.display.connect('activated', function(resultsAppsDisplay) {
me.emit('activated');
});
this._resultsDocsSection.display.connect('activated', function(resultsDocsDisplay) {
me.emit('activated');
});
this._docDisplay.connect('selected', function(docDisplay) {
me._resultsDocsSection.display.unsetSelected();
me._resultsAppsSection.display.unsetSelected();
if (!me._detailsShowing()) {
me._detailsPane.show();
me.emit('panes-displayed');
}
me._detailsContent.remove_all();
me._detailsContent.append(me._docDisplay.selectedItemDetails, Big.BoxPackFlags.NONE);
});
this._resultsDocsSection.display.connect('selected', function(resultsDocDisplay) {
me._docDisplay.unsetSelected();
me._resultsAppsSection.display.unsetSelected();
if (!me._detailsShowing()) {
me._detailsPane.show();
me.emit('panes-displayed');
}
me._detailsContent.remove_all();
me._detailsContent.append(me._resultsDocsSection.display.selectedItemDetails, Big.BoxPackFlags.NONE);
});
this._resultsAppsSection.display.connect('selected', function(resultsAppDisplay) {
me._docDisplay.unsetSelected();
me._resultsDocsSection.display.unsetSelected();
if (!me._detailsShowing()) {
me._detailsPane.show();
me.emit('panes-displayed');
}
me._detailsContent.remove_all();
me._detailsContent.append(me._resultsAppsSection.display.selectedItemDetails, Big.BoxPackFlags.NONE);
});
this._moreAppsLink.connect('clicked',
function(o, event) {
if (me._moreAppsMode) {
me._unsetMoreAppsMode();
} else {
me._setMoreAppsMode();
}
});
this._moreDocsLink.connect('clicked',
function(o, event) {
if (me._moreDocsMode) {
me._unsetMoreDocsMode();
} else {
me._setMoreDocsMode();
}
});
},
show: function() {
let global = Shell.Global.get();
this._appsContent.show();
this._docDisplay.show();
global.stage.set_key_focus(this._searchEntry.entry);
},
hide: function() {
this._firstSelectAfterOverlayShow = true;
this._appsContent.hide();
this._docDisplay.hide();
this._searchEntry.entry.text = '';
this.unsetMoreMode();
},
unsetMoreMode: function() {
this._unsetMoreAppsMode();
this._unsetMoreDocsMode();
if (this._detailsShowing()) {
this._detailsPane.hide();
this.emit('panes-removed');
}
this._unsetSearchMode();
},
// Sets the 'More' mode for browsing applications.
_setMoreAppsMode: function() {
if (this._moreAppsMode)
return;
this._unsetMoreDocsMode();
this._unsetSearchMode();
this._moreAppsMode = true;
this._resultsAppsSection.display.show();
this._resultsPane.add_actor(this._resultsAppsSection.actor);
this._resultsPane.show();
this._moreAppsLink.setText("Less...");
this._detailsPane.x = this._width + this._resultsWidth;
this.emit('panes-displayed');
},
// Unsets the 'More' mode for browsing applications.
_unsetMoreAppsMode: function() {
if (!this._moreAppsMode)
return;
this._moreAppsMode = false;
this._resultsPane.remove_actor(this._resultsAppsSection.actor);
this._resultsAppsSection.display.hide();
this._resultsPane.hide();
this._moreAppsLink.setText("More...");
this._detailsPane.x = this._width;
if (!this._detailsShowing()) {
this.emit('panes-removed');
}
},
// Sets the 'More' mode for browsing documents.
_setMoreDocsMode: function() {
if (this._moreDocsMode)
return;
this._unsetMoreAppsMode();
this._unsetSearchMode();
this._moreDocsMode = true;
this._resultsDocsSection.display.show();
this._resultsPane.add_actor(this._resultsDocsSection.actor);
this._resultsPane.show();
this._moreDocsLink.setText("Less...");
this._detailsPane.x = this._width + this._resultsWidth;
this.emit('panes-displayed');
},
// Unsets the 'More' mode for browsing documents.
_unsetMoreDocsMode: function() {
if (!this._moreDocsMode)
return;
this._moreDocsMode = false;
this._resultsPane.hide();
this._resultsPane.remove_actor(this._resultsDocsSection.actor);
this._resultsDocsSection.display.hide();
this._moreDocsLink.setText("More...");
this._detailsPane.x = this._width;
if (!this._detailsShowing()) {
this.emit('panes-removed');
}
},
_setSearchMode: function() {
if (this._resultsShowing())
return;
this._resultsAppsSection._setSearchMode();
this._resultsAppsSection.display.show();
this._resultsPane.add_actor(this._resultsAppsSection.actor);
this._resultsDocsSection._setSearchMode();
this._resultsDocsSection.display.show();
this._resultsPane.add_actor(this._resultsDocsSection.actor);
this._resultsDocsSection.actor.set_y(this._resultsAppsSection.actor.height);
this._resultsPane.show();
this._detailsPane.x = this._width + this._resultsWidth;
this.emit('panes-displayed');
},
_unsetSearchMode: function() {
if (this._moreDocsMode || this._moreAppsMode || !this._resultsShowing())
return;
this._resultsPane.hide();
this._resultsPane.remove_actor(this._resultsAppsSection.actor);
this._resultsAppsSection.display.hide();
this._resultsAppsSection._unsetSearchMode();
this._resultsPane.remove_actor(this._resultsDocsSection.actor);
this._resultsDocsSection.display.hide();
this._resultsDocsSection._unsetSearchMode();
this._resultsDocsSection.actor.set_y(0);
this._detailsPane.x = this._width;
if (!this._detailsShowing()) {
this.emit('panes-removed');
}
},
_detailsShowing: function() {
return this._detailsPane.visible;
},
_resultsShowing: function() {
return this._resultsPane.visible;
}
};
Signals.addSignalMethods(Dash.prototype);
function Overlay() {
this._init();
}
Overlay.prototype = {
_init : function() {
let me = this;
let global = Shell.Global.get();
wideScreen = (global.screen_width/global.screen_height > WIDE_SCREEN_CUT_OFF_RATIO);
// We divide the screen into an imaginary grid which helps us determine the layout of
// different visual components.
if (wideScreen) {
displayGridColumnWidth = global.screen_width / COLUMNS_WIDE_SCREEN;
displayGridRowHeight = global.screen_height / ROWS_WIDE_SCREEN;
} else {
displayGridColumnWidth = global.screen_width / COLUMNS_REGULAR_SCREEN;
displayGridRowHeight = global.screen_height / ROWS_REGULAR_SCREEN;
}
this._group = new Clutter.Group();
this._group._delegate = this;
this.visible = false;
this._hideInProgress = false;
// A scaled root pixmap actor is used as a background. It is zoomed in
// to the lower right intersection of the lines that divide the image
// evenly in a 3x3 grid. This is based on the rule of thirds, a
// compositional rule of thumb in visual arts. The choice for the
// lower right point is based on a quick survey of GNOME wallpapers.
let background = global.create_root_pixmap_actor();
background.width = global.screen_width * BACKGROUND_SCALE;
background.height = global.screen_height * BACKGROUND_SCALE;
background.x = -global.screen_width * (4 * BACKGROUND_SCALE - 3) / 6;
background.y = -global.screen_height * (4 * BACKGROUND_SCALE - 3) / 6;
this._group.add_actor(background);
// Transparent background is used to catch clicks outside of the dash panes when the panes
// are being displayed and the workspaces area should not be reactive. Catching such a
// click results in the panes being closed and the workspaces area becoming reactive again.
this._transparentBackground = new Clutter.Rectangle({ opacity: 0,
width: global.screen_width,
height: global.screen_height - Panel.PANEL_HEIGHT,
y: Panel.PANEL_HEIGHT,
reactive: true });
this._group.add_actor(this._transparentBackground);
// Draw a semitransparent rectangle over the background for readability.
let backOver = new Clutter.Rectangle({ color: ROOT_OVERLAY_COLOR,
width: global.screen_width,
height: global.screen_height - Panel.PANEL_HEIGHT,
y: Panel.PANEL_HEIGHT });
this._group.add_actor(backOver);
this._group.hide();
global.overlay_group.add_actor(this._group);
// TODO - recalculate everything when desktop size changes
this._dash = new Dash();
this._group.add_actor(this._dash.actor);
this._workspaces = null;
this._buttonEventHandlerId = null;
this._dash.connect('activated', function(dash) {
// TODO - have some sort of animation/effect while
// transitioning to the new app. We definitely need
// startup-notification integration at least.
me.hide();
});
this._dash.connect('panes-displayed', function(dash) {
if (me._buttonEventHandlerId == null) {
me._transparentBackground.raise_top();
me._dash.actor.raise_top();
me._buttonEventHandlerId = me._transparentBackground.connect('button-release-event', function(background) {
me._dash.unsetMoreMode();
return true;
});
}
});
this._dash.connect('panes-removed', function(dash) {
if (me._buttonEventHandlerId != null) {
me._transparentBackground.lower_bottom();
me._transparentBackground.disconnect(me._buttonEventHandlerId);
me._buttonEventHandlerId = null;
}
});
},
//// Draggable target interface ////
// Unsets the expanded display mode if a GenericDisplayItem is being
// dragged over the overlay, i.e. as soon as it starts being dragged.
// This closes the additional panes and allows the user to place
// the item on any workspace.
handleDragOver : function(source, actor, x, y, time) {
if (source instanceof GenericDisplay.GenericDisplayItem) {
this._dash.unsetMoreMode();
return true;
}
return false;
},
//// Public methods ////
show : function() {
if (this.visible)
return;
if (!Main.startModal())
return;
this.visible = true;
let global = Shell.Global.get();
let screenWidth = global.screen_width;
let screenHeight = global.screen_height;
this._dash.show();
let columnsUsed = wideScreen ? COLUMNS_FOR_WORKSPACES_WIDE_SCREEN : COLUMNS_FOR_WORKSPACES_REGULAR_SCREEN;
let rowsUsed = wideScreen ? ROWS_FOR_WORKSPACES_WIDE_SCREEN : ROWS_FOR_WORKSPACES_REGULAR_SCREEN;
let workspacesWidth = displayGridColumnWidth * columnsUsed - WORKSPACE_GRID_PADDING * 2;
// We scale the vertical padding by (screenHeight / screenWidth) so that the workspace preserves its aspect ratio.
let workspacesHeight = displayGridRowHeight * rowsUsed - WORKSPACE_GRID_PADDING * (screenHeight / screenWidth) * 2;
let workspacesX = displayGridColumnWidth + WORKSPACE_GRID_PADDING;
let workspacesY = displayGridRowHeight + WORKSPACE_GRID_PADDING * (screenHeight / screenWidth);
// place the 'Add Workspace' button in the bottom row of the grid
let addButtonSize = Math.floor(displayGridRowHeight * 3/5);
let addButtonX = workspacesX + workspacesWidth - addButtonSize;
let addButtonY = screenHeight - Math.floor(displayGridRowHeight * 4/5);
this._workspaces = new Workspaces.Workspaces(workspacesWidth, workspacesHeight, workspacesX, workspacesY,
addButtonSize, addButtonX, addButtonY);
this._group.add_actor(this._workspaces.actor);
// All the the actors in the window group are completely obscured,
// hiding the group holding them while the overlay is displayed greatly
// increases performance of the overlay especially when there are many
// windows visible.
//
// If we switched to displaying the actors in the overlay rather than
// clones of them, this would obviously no longer be necessary.
global.window_group.hide();
this._group.show();
// Try to make the menu not too visible behind the empty space between
// the workspace previews by sliding in its clipping rectangle.
// We want to finish drawing the Dash just before the top workspace fully
// slides in on the top. Which means that we have more time to wait before
// drawing the dash if the active workspace is displayed on the bottom of
// the workspaces grid, and almost no time to wait if it is displayed in the top
// row of the workspaces grid. The calculations used below try to roughly
// capture the animation ratio for when workspaces are covering the top of the overlay
// vs. when workspaces are already below the top of the overlay, and apply it
// to clipping the dash. The clipping is removed in this._showDone().
this._dash.actor.set_clip(0, 0,
this._workspaces.getFullSizeX(),
this._dash.actor.height);
Tweener.addTween(this._dash.actor,
{ clipWidthRight: this._dash._width + WORKSPACE_GRID_PADDING + this._workspaces.getWidthToTopActiveWorkspace(),
time: ANIMATION_TIME,
transition: "easeOutQuad",
onComplete: this._showDone,
onCompleteScope: this
});
this.emit('showing');
},
hide : function() {
if (!this.visible || this._hideInProgress)
return;
let global = Shell.Global.get();
this._hideInProgress = true;
// lower the Dash, so that workspaces display is on top and covers the Dash while it is sliding out
this._dash.actor.lower(this._workspaces.actor);
this._workspaces.hide();
// Try to make the menu not too visible behind the empty space between
// the workspace previews by sliding in its clipping rectangle.
// The logic used is the same as described in this.show(). If the active workspace
// is displayed in the top row, than almost full animation time is needed for it
// to reach the top of the overlay and cover the Dash fully, while if the
// active workspace is in the lower row, than the top left workspace reaches the
// top of the overlay sooner as it is moving out of the way.
// The clipping is removed in this._hideDone().
this._dash.actor.set_clip(0, 0,
this._dash.actor.width + WORKSPACE_GRID_PADDING + this._workspaces.getWidthToTopActiveWorkspace(),
this._dash.actor.height);
Tweener.addTween(this._dash.actor,
{ clipWidthRight: this._workspaces.getFullSizeX() + this._workspaces.getWidthToTopActiveWorkspace() - global.screen_width,
time: ANIMATION_TIME,
transition: "easeOutQuad",
onComplete: this._hideDone,
onCompleteScope: this
});
this.emit('hiding');
},
toggle: function() {
if (this.visible)
this.hide();
else
this.show();
},
//// Private methods ////
// Raises the Dash to the top, so that we can tell if the pointer is above one of its items.
// We need to do this once the workspaces are shown because the workspaces actor currently covers
// the whole screen, regardless of where the workspaces are actually displayed.
//
// Once we rework the workspaces actor to only cover the area it actually needs, we can
// remove this workaround. Also http://bugzilla.openedhand.com/show_bug.cgi?id=1513 requests being
// able to pick only a reactive actor at a certain position, rather than any actor. Being able
// to do that would allow us to not have to raise the Dash.
_showDone: function() {
if (this._hideInProgress)
return;
this._dash.actor.raise_top();
this._dash.actor.remove_clip();
this.emit('shown');
},
_hideDone: function() {
let global = Shell.Global.get();
global.window_group.show();
this._workspaces.destroy();
this._workspaces = null;
this._dash.actor.remove_clip();
this._dash.hide();
this._group.hide();
this.visible = false;
this._hideInProgress = false;
Main.endModal();
this.emit('hidden');
}
};
Signals.addSignalMethods(Overlay.prototype);
Tweener.registerSpecialProperty("clipHeightBottom", _clipHeightBottomGet, _clipHeightBottomSet);
function _clipHeightBottomGet(actor) {
let [xOffset, yOffset, clipWidth, clipHeight] = actor.get_clip();
return clipHeight;
}
function _clipHeightBottomSet(actor, clipHeight) {
actor.set_clip(0, 0, actor.width, clipHeight);
}
Tweener.registerSpecialProperty("clipHeightTop", _clipHeightTopGet, _clipHeightTopSet);
function _clipHeightTopGet(actor) {
let [xOffset, yOffset, clipWidth, clipHeight] = actor.get_clip();
return clipHeight;
}
function _clipHeightTopSet(actor, clipHeight) {
actor.set_clip(0, actor.height - clipHeight, actor.width, clipHeight);
}
Tweener.registerSpecialProperty("clipWidthRight", _clipWidthRightGet, _clipWidthRightSet);
function _clipWidthRightGet(actor) {
let [xOffset, yOffset, clipWidth, clipHeight] = actor.get_clip();
return clipWidth;
}
function _clipWidthRightSet(actor, clipWidth) {
actor.set_clip(0, 0, clipWidth, actor.height);
}

View File

@@ -1,423 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Big = imports.gi.Big;
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const Lang = imports.lang;
const AppDisplay = imports.ui.appDisplay;
const DocDisplay = imports.ui.docDisplay;
const GenericDisplay = imports.ui.genericDisplay;
const Link = imports.ui.link;
const Main = imports.ui.main;
const Panel = imports.ui.panel;
const Dash = imports.ui.dash;
const Tweener = imports.ui.tweener;
const Workspaces = imports.ui.workspaces;
const ROOT_OVERVIEW_COLOR = new Clutter.Color();
ROOT_OVERVIEW_COLOR.from_pixel(0x000000ff);
// Time for initial animation going into Overview mode
const ANIMATION_TIME = 0.25;
// We divide the screen into a grid of rows and columns, which we use
// to help us position the Overview components, such as the side panel
// that lists applications and documents, the workspaces display, and
// the button for adding additional workspaces.
// In the regular mode, the side panel takes up one column on the left,
// and the workspaces display takes up the remaining columns.
// In the expanded side panel display mode, the side panel takes up two
// columns, and the workspaces display slides all the way to the right,
// being visible only in the last quarter of the right-most column.
// In the future, this mode will have more components, such as a display
// of documents which were recently opened with a given application, which
// will take up the remaining sections of the display.
const WIDE_SCREEN_CUT_OFF_RATIO = 1.4;
const COLUMNS_REGULAR_SCREEN = 4;
const ROWS_REGULAR_SCREEN = 8;
const COLUMNS_WIDE_SCREEN = 5;
const ROWS_WIDE_SCREEN = 10;
const DEFAULT_PADDING = 4;
// Padding around workspace grid / Spacing between Dash and Workspaces
const WORKSPACE_GRID_PADDING = 12;
const COLUMNS_FOR_WORKSPACES_REGULAR_SCREEN = 3;
const ROWS_FOR_WORKSPACES_REGULAR_SCREEN = 6;
const COLUMNS_FOR_WORKSPACES_WIDE_SCREEN = 4;
const ROWS_FOR_WORKSPACES_WIDE_SCREEN = 8;
// A multi-state; PENDING is used during animations
const STATE_ACTIVE = true;
const STATE_PENDING_INACTIVE = false;
const STATE_INACTIVE = false;
const SHADOW_COLOR = new Clutter.Color();
SHADOW_COLOR.from_pixel(0x00000033);
const TRANSPARENT_COLOR = new Clutter.Color();
TRANSPARENT_COLOR.from_pixel(0x00000000);
const SHADOW_WIDTH = 6;
const NUMBER_OF_SECTIONS_IN_SEARCH = 2;
let wideScreen = false;
let displayGridColumnWidth = null;
let displayGridRowHeight = null;
function Overview() {
this._init();
}
Overview.prototype = {
_init : function() {
let me = this;
let global = Shell.Global.get();
this._group = new Clutter.Group();
this._group._delegate = this;
this.visible = false;
this.animationInProgress = false;
this._hideInProgress = false;
this._recalculateGridSizes();
this._activeDisplayPane = null;
// Used to catch any clicks when we have an active pane; see the comments
// in addPane below.
this._transparentBackground = new Clutter.Rectangle({ opacity: 0,
reactive: true });
this._group.add_actor(this._transparentBackground);
// Background color for the Overview
this._backOver = new Clutter.Rectangle({ color: ROOT_OVERVIEW_COLOR });
this._group.add_actor(this._backOver);
this._group.hide();
global.overlay_group.add_actor(this._group);
// TODO - recalculate everything when desktop size changes
this._dash = new Dash.Dash();
this._group.add_actor(this._dash.actor);
// Container to hold popup pane chrome.
this._paneContainer = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
spacing: 6
});
// Note here we explicitly don't set the paneContainer to be reactive yet; that's done
// inside the notify::visible handler on panes.
this._paneContainer.connect('button-release-event', Lang.bind(this, function(background) {
this._activeDisplayPane.close();
return true;
}));
this._group.add_actor(this._paneContainer);
this._transparentBackground.lower_bottom();
this._paneContainer.lower_bottom();
this._workspaces = null;
},
_recalculateGridSizes: function () {
let global = Shell.Global.get();
wideScreen = (global.screen_width/global.screen_height > WIDE_SCREEN_CUT_OFF_RATIO);
// We divide the screen into an imaginary grid which helps us determine the layout of
// different visual components.
if (wideScreen) {
displayGridColumnWidth = global.screen_width / COLUMNS_WIDE_SCREEN;
displayGridRowHeight = global.screen_height / ROWS_WIDE_SCREEN;
} else {
displayGridColumnWidth = global.screen_width / COLUMNS_REGULAR_SCREEN;
displayGridRowHeight = global.screen_height / ROWS_REGULAR_SCREEN;
}
},
relayout: function () {
let global = Shell.Global.get();
let screenHeight = global.screen_height;
let screenWidth = global.screen_width;
let contentHeight = screenHeight - Panel.PANEL_HEIGHT;
let workspaceColumnsUsed = wideScreen ? COLUMNS_FOR_WORKSPACES_WIDE_SCREEN : COLUMNS_FOR_WORKSPACES_REGULAR_SCREEN;
let workspaceRowsUsed = wideScreen ? ROWS_FOR_WORKSPACES_WIDE_SCREEN : ROWS_FOR_WORKSPACES_REGULAR_SCREEN;
this._workspacesWidth = displayGridColumnWidth * workspaceColumnsUsed
- WORKSPACE_GRID_PADDING * 2;
// We scale the vertical padding by (screenHeight / screenWidth)
// so that the workspace preserves its aspect ratio.
this._workspacesHeight = displayGridRowHeight * workspaceRowsUsed
- WORKSPACE_GRID_PADDING * (screenHeight / screenWidth) * 2;
this._workspacesX = displayGridColumnWidth + WORKSPACE_GRID_PADDING;
this._workspacesY = displayGridRowHeight + WORKSPACE_GRID_PADDING * (screenHeight / screenWidth);
let dashY = Panel.PANEL_HEIGHT;
this._dash.actor.set_position(0, dashY);
this._dash.actor.set_size(displayGridColumnWidth, screenHeight - dashY);
this._dash.searchArea.height = this._workspacesY - dashY;
this._dash.sectionArea.height = this._workspacesHeight;
// place the 'Add Workspace' button in the bottom row of the grid
this._addButtonSize = Math.floor(displayGridRowHeight * 3/5);
this._addButtonX = this._workspacesX + this._workspacesWidth - this._addButtonSize;
this._addButtonY = screenHeight - Math.floor(displayGridRowHeight * 4/5);
this._backOver.set_position(0, Panel.PANEL_HEIGHT);
this._backOver.set_size(global.screen_width, contentHeight);
this._paneContainer.set_position(this._dash.actor.x + this._dash.actor.width + DEFAULT_PADDING,
Panel.PANEL_HEIGHT);
// Dynamic width
this._paneContainer.height = contentHeight;
this._transparentBackground.set_position(this._paneContainer.x, this._paneContainer.y);
this._transparentBackground.set_size(global.screen_width - this._paneContainer.x,
this._paneContainer.height);
if (this._activeDisplayPane != null)
this._activeDisplayPane.actor.width = displayGridColumnWidth * 2;
},
addPane: function (pane) {
this._paneContainer.append(pane.actor, Big.BoxPackFlags.NONE);
// When a pane is displayed, we raise the transparent background to the top
// and connect to button-release-event on it, then raise the pane above that.
// The idea here is that clicking anywhere outside the pane should close it.
// When the active pane is closed, undo the effect.
let backgroundEventId = null;
pane.connect('open-state-changed', Lang.bind(this, function (pane, isOpen) {
if (isOpen) {
pane.actor.width = displayGridColumnWidth * 2;
this._activeDisplayPane = pane;
this._transparentBackground.raise_top();
this._paneContainer.raise_top();
if (backgroundEventId != null)
this._transparentBackground.disconnect(backgroundEventId);
backgroundEventId = this._transparentBackground.connect('button-release-event', Lang.bind(this, function () {
this._activeDisplayPane.close();
return true;
}));
} else if (pane == this._activeDisplayPane) {
this._activeDisplayPane = null;
if (backgroundEventId != null) {
this._transparentBackground.disconnect(backgroundEventId);
backgroundEventId = null;
}
this._transparentBackground.lower_bottom();
this._paneContainer.lower_bottom();
}
}));
},
//// Draggable target interface ////
// Closes any active panes if a GenericDisplayItem is being
// dragged over the Overview, i.e. as soon as it starts being dragged.
// This allows the user to place the item on any workspace.
handleDragOver : function(source, actor, x, y, time) {
if (source instanceof GenericDisplay.GenericDisplayItem
|| source instanceof AppDisplay.WellDisplayItem) {
if (this._activeDisplayPane != null)
this._activeDisplayPane.close();
return true;
}
return false;
},
//// Public methods ////
// 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();
},
// 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._group.scaleX;
},
// Returns the current position of the Overview.
getPosition : function() {
return [this._group.x, this._group.y];
},
show : function() {
if (this.visible)
return;
if (!Main.beginModal())
return;
this.visible = true;
this.animationInProgress = true;
let global = Shell.Global.get();
this._dash.show();
/* TODO: make this stuff dynamic */
this._workspaces = new Workspaces.Workspaces(this._workspacesWidth, this._workspacesHeight,
this._workspacesX, this._workspacesY,
this._addButtonSize, this._addButtonX, this._addButtonY);
this._group.add_actor(this._workspaces.actor);
// The workspaces actor is as big as the screen, so we have to raise the dash above it
// for drag and drop to work. In the future we should fix the workspaces to not
// be as big as the screen.
this._dash.actor.raise(this._workspaces.actor);
// All the the actors in the window group are completely obscured,
// hiding the group holding them while the Overview is displayed greatly
// increases performance of the Overview especially when there are many
// windows visible.
//
// If we switched to displaying the actors in the Overview rather than
// clones of them, this would obviously no longer be necessary.
global.window_group.hide();
this._group.show();
// Create a zoom out effect. First scale the Overview group up and
// position it so that the active workspace fills up the whole screen,
// then transform the group to its normal dimensions and position.
// The opposite transition is used in hide().
this._group.scaleX = this._group.scaleY = this.getZoomedInScale();
[this._group.x, this._group.y] = this.getZoomedInPosition();
Tweener.addTween(this._group,
{ x: 0,
y: 0,
scaleX: 1,
scaleY: 1,
transition: 'easeOutQuad',
time: ANIMATION_TIME,
onComplete: this._showDone,
onCompleteScope: this
});
// Make Dash fade in so that it doesn't appear to big.
this._dash.actor.opacity = 0;
Tweener.addTween(this._dash.actor,
{ opacity: 255,
transition: 'easeOutQuad',
time: ANIMATION_TIME
});
this.emit('showing');
},
hide : function() {
if (!this.visible || this._hideInProgress)
return;
let global = Shell.Global.get();
this.animationInProgress = true;
this._hideInProgress = true;
if (this._activeDisplayPane != null)
this._activeDisplayPane.close();
this._workspaces.hide();
// Create a zoom in effect by transforming the Overview group 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._group,
{ x: posX,
y: posY,
scaleX: scale,
scaleY: scale,
transition: 'easeOutQuad',
time: ANIMATION_TIME,
onComplete: this._hideDone,
onCompleteScope: this
});
// Make Dash fade out so that it doesn't appear to big.
Tweener.addTween(this._dash.actor,
{ opacity: 0,
transition: 'easeOutQuad',
time: ANIMATION_TIME
});
this.emit('hiding');
},
toggle: function() {
if (this.visible)
this.hide();
else
this.show();
},
/**
* activateWindow:
* @metaWindow: A #MetaWindow
* @time: Event timestamp integer
*
* Make the given MetaWindow be the focus window, switching
* to the workspace it's on if necessary. This function
* should only be used when the Overview is currently active;
* outside of that, use the relevant methods on MetaDisplay.
*/
activateWindow: function (metaWindow, time) {
this._workspaces.activateWindowFromOverview(metaWindow, time);
this.hide();
},
//// Private methods ////
_showDone: function() {
if (this._hideInProgress)
return;
this.animationInProgress = false;
this.emit('shown');
},
_hideDone: function() {
let global = Shell.Global.get();
global.window_group.show();
this._workspaces.destroy();
this._workspaces = null;
this._dash.hide();
this._group.hide();
this.visible = false;
this.animationInProgress = false;
this._hideInProgress = false;
Main.endModal();
this.emit('hidden');
}
};
Signals.addSignalMethods(Overview.prototype);

View File

@@ -7,48 +7,35 @@ const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Tweener = imports.ui.tweener;
const Signals = imports.signals;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const Button = imports.ui.button;
const Main = imports.ui.main;
const PANEL_HEIGHT = 26;
const PANEL_HEIGHT = 32;
const TRAY_HEIGHT = PANEL_HEIGHT - 1;
const SHADOW_HEIGHT = 6;
const DEFAULT_PADDING = 4;
const PANEL_ICON_SIZE = 24;
const BACKGROUND_TOP = new Clutter.Color();
BACKGROUND_TOP.from_pixel(0x161616ff);
const BACKGROUND_BOTTOM = new Clutter.Color();
BACKGROUND_BOTTOM.from_pixel(0x000000ff);
const PANEL_FOREGROUND_COLOR = new Clutter.Color();
PANEL_FOREGROUND_COLOR.from_pixel(0xffffffff);
const SN_BACKGROUND_COLOR = new Clutter.Color();
SN_BACKGROUND_COLOR.from_pixel(0xffff00a0);
// The panel has a transparent white background with a gradient.
const PANEL_TOP_COLOR = new Clutter.Color();
PANEL_TOP_COLOR.from_pixel(0xffffff99);
const PANEL_MIDDLE_COLOR = new Clutter.Color();
PANEL_MIDDLE_COLOR.from_pixel(0xffffff88);
const PANEL_BOTTOM_COLOR = new Clutter.Color();
PANEL_BOTTOM_COLOR.from_pixel(0xffffffaa);
const SHADOW_COLOR = new Clutter.Color();
SHADOW_COLOR.from_pixel(0x00000033);
const TRANSPARENT_COLOR = new Clutter.Color();
TRANSPARENT_COLOR.from_pixel(0x00000000);
// Don't make the mouse hover effect visible to the user for a menu feel.
// Darken (pressed) buttons; lightening has no effect on white backgrounds.
const PANEL_BUTTON_COLOR = new Clutter.Color();
PANEL_BUTTON_COLOR.from_pixel(0x00000000);
// Lighten pressed buttons; darkening has no effect on a black background.
PANEL_BUTTON_COLOR.from_pixel(0x00000015);
const PRESSED_BUTTON_BACKGROUND_COLOR = new Clutter.Color();
PRESSED_BUTTON_BACKGROUND_COLOR.from_pixel(0x324c6ffa);
const DEFAULT_FONT = 'Sans 16px';
PRESSED_BUTTON_BACKGROUND_COLOR.from_pixel(0x00000030);
const TRAY_PADDING = 0;
// See comments around _recomputeTraySize
const TRAY_SPACING = 14;
const TRAY_SPACING_MIN = 8;
const TRAY_SPACING = 2;
// Used for the tray icon container with gtk pre-2.16, which doesn't
// fully support tray icon transparency
@@ -59,109 +46,6 @@ TRAY_BORDER_COLOR.from_pixel(0x00000033);
const TRAY_CORNER_RADIUS = 5;
const TRAY_BORDER_WIDTH = 0;
function AppPanelMenu() {
this._init();
}
AppPanelMenu.prototype = {
_init: function() {
this._metaDisplay = Shell.Global.get().screen.get_display();
this._focusedApp = null;
this._activeSequence = null;
this._startupSequences = {};
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
spacing: DEFAULT_PADDING,
y_align: Big.BoxAlignment.CENTER });
this._iconBox = new Big.Box({ width: PANEL_ICON_SIZE, height: PANEL_ICON_SIZE,
x_align: Big.BoxAlignment.CENTER,
y_align: Big.BoxAlignment.CENTER });
this.actor.append(this._iconBox, Big.BoxPackFlags.NONE);
let labelBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
y_align: Big.BoxAlignment.CENTER });
this._label = new Clutter.Text({ font_name: DEFAULT_FONT,
color: PANEL_FOREGROUND_COLOR,
text: "" });
labelBox.append(this._label, Big.BoxPackFlags.EXPAND);
this.actor.append(labelBox, Big.BoxPackFlags.NONE);
this._startupBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
y_align: Big.BoxAlignment.CENTER });
this.actor.append(this._startupBox, Big.BoxPackFlags.NONE);
Main.overview.connect('hiding', Lang.bind(this, function () {
this.actor.opacity = 255;
}));
Main.overview.connect('showing', Lang.bind(this, function () {
this.actor.opacity = 192;
}));
this._metaDisplay.connect('notify::focus-window', Lang.bind(this, this._sync));
let appMonitor = Shell.AppMonitor.get_default();
appMonitor.connect('startup-sequence-changed', Lang.bind(this, this._sync));
// For now just resync on application add/remove; this is mainly to handle
// cases where the focused window's application changes without the focus
// changing. An example case is how we map Firefox based on the window
// title which is a dynamic property.
appMonitor.connect('app-added', Lang.bind(this, this._sync));
appMonitor.connect('app-removed', Lang.bind(this, this._sync));
this._sync();
},
_sync: function() {
let appMonitor = Shell.AppMonitor.get_default();
let focusWindow = this._metaDisplay.get_focus_window();
let focusedApp;
if (focusWindow == null) {
focusedApp = null;
} else {
focusedApp = appMonitor.get_window_app(focusWindow);
}
let lastSequence = null;
if (focusedApp == null) {
let sequences = appMonitor.get_startup_sequences();
if (sequences.length > 0)
lastSequence = sequences[sequences.length - 1];
}
// If the currently focused app hasn't changed and the current
// startup sequence hasn't changed, we have nothing to do
if (focusedApp == this._focusedApp
&& ((lastSequence == null && this._activeSequence == null)
|| (lastSequence != null && this._activeSequence != null
&& lastSequence.get_id() == this._activeSequence.get_id())))
return;
this._focusedApp = focusedApp;
this._activeSequence = lastSequence;
this._iconBox.remove_all();
this._iconBox.hide();
this._label.text = '';
if (this._focusedApp != null) {
let icon = focusedApp.create_icon_texture(PANEL_ICON_SIZE);
this._iconBox.append(icon, Big.BoxPackFlags.NONE);
this._iconBox.show();
this._label.text = focusedApp.get_name();
} else if (this._activeSequence != null) {
let icon = this._activeSequence.create_icon(PANEL_ICON_SIZE);
this._iconBox.append(icon, Big.BoxPackFlags.NONE);
this._iconBox.show();
this._label.text = this._activeSequence.get_name();
}
this.emit('changed');
}
}
Signals.addSignalMethods(AppPanelMenu.prototype);
function Panel() {
this._init();
}
@@ -170,168 +54,76 @@ Panel.prototype = {
_init : function() {
let global = Shell.Global.get();
// Put the background under the panel within a group.
this.actor = new Clutter.Group();
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL
});
let backgroundGradient = Shell.create_vertical_gradient(BACKGROUND_TOP,
BACKGROUND_BOTTOM);
this.actor.connect('notify::allocation', Lang.bind(this, function () {
let [width, height] = this.actor.get_size();
backgroundGradient.set_size(width, height);
}));
this.actor.add_actor(backgroundGradient);
// Create backBox, which contains two boxes, backUpper and backLower,
// for the background gradients and one for the shadow. The shadow at
// the bottom has a fixed height (packing flag NONE), and the rest of
// the height above is divided evenly between backUpper and backLower
// (with packing flag EXPAND).
let backBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
x: 0,
y: 0,
width: global.screen_width,
height: PANEL_HEIGHT + SHADOW_HEIGHT });
let backUpper = global.create_vertical_gradient(PANEL_TOP_COLOR,
PANEL_MIDDLE_COLOR);
let backLower = global.create_vertical_gradient(PANEL_MIDDLE_COLOR,
PANEL_BOTTOM_COLOR);
let shadow = global.create_vertical_gradient(SHADOW_COLOR,
TRANSPARENT_COLOR);
shadow.set_height(SHADOW_HEIGHT + 1);
backBox.append(backUpper, Big.BoxPackFlags.EXPAND);
backBox.append(backLower, Big.BoxPackFlags.EXPAND);
backBox.append(shadow, Big.BoxPackFlags.NONE);
this.actor.add_actor(backBox);
this._leftBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
y_align: Big.BoxAlignment.CENTER,
spacing: DEFAULT_PADDING,
padding_right: DEFAULT_PADDING });
this._centerBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
y_align: Big.BoxAlignment.CENTER });
this._rightBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
y_align: Big.BoxAlignment.CENTER,
padding_left: DEFAULT_PADDING });
let box = new Big.Box({ x: 0,
y: 0,
height: PANEL_HEIGHT,
width: global.screen_width,
orientation: Big.BoxOrientation.HORIZONTAL,
spacing: 4 });
/* This box container ensures that the centerBox is positioned in the *absolute*
* center, but can be pushed aside if necessary. */
this._boxContainer = new Shell.GenericContainer();
this.actor.append(this._boxContainer, Big.BoxPackFlags.EXPAND);
this._boxContainer.add_actor(this._leftBox);
this._boxContainer.add_actor(this._centerBox);
this._boxContainer.add_actor(this._rightBox);
this._boxContainer.connect('get-preferred-width', Lang.bind(this, function(box, forHeight, alloc) {
let children = box.get_children();
for (let i = 0; i < children.length; i++) {
let [childMin, childNatural] = children[i].get_preferred_width(forHeight);
alloc.min_size += childMin;
alloc.natural_size += childNatural;
}
}));
this._boxContainer.connect('get-preferred-height', Lang.bind(this, function(box, forWidth, alloc) {
let children = box.get_children();
for (let i = 0; i < children.length; i++) {
let [childMin, childNatural] = children[i].get_preferred_height(forWidth);
if (childMin > alloc.min_size)
alloc.min_size = childMin;
if (childNatural > alloc.natural_size)
alloc.natural_size = childNatural;
}
}));
this._boxContainer.connect('allocate', Lang.bind(this, function(container, box, flags) {
let allocWidth = box.x2 - box.x1;
let allocHeight = box.y2 - box.y1;
let [leftMinWidth, leftNaturalWidth] = this._leftBox.get_preferred_width(-1);
let [centerMinWidth, centerNaturalWidth] = this._centerBox.get_preferred_width(-1);
let [rightMinWidth, rightNaturalWidth] = this._rightBox.get_preferred_width(-1);
let leftWidth, centerWidth, rightWidth;
if (allocWidth < (leftNaturalWidth + centerNaturalWidth + rightNaturalWidth)) {
leftWidth = leftMinWidth;
centerWidth = centerMinWidth;
rightWidth = rightMinWidth;
} else {
leftWidth = leftNaturalWidth;
centerWidth = centerNaturalWidth;
rightWidth = rightNaturalWidth;
}
this.button = new Button.Button("Activities", PANEL_BUTTON_COLOR, PRESSED_BUTTON_BACKGROUND_COLOR, true, null, PANEL_HEIGHT);
let x;
let childBox = new Clutter.ActorBox();
childBox.x1 = box.x1;
childBox.y1 = box.y1;
childBox.x2 = x = childBox.x1 + leftWidth;
childBox.y2 = box.y2;
this._leftBox.allocate(childBox, flags);
box.append(this.button.button, Big.BoxPackFlags.NONE);
let centerNaturalX = Math.floor((box.x2 - box.x1) / 2 - (centerWidth / 2));
/* Check left side */
if (x < centerNaturalX) {
/* We didn't overflow the left, use the natural. */
x = centerNaturalX;
}
/* Check right side */
if (x + centerWidth > (box.x2 - rightWidth)) {
x = box.x2 - rightWidth - centerWidth;
}
childBox = new Clutter.ActorBox();
childBox.x1 = x;
childBox.y1 = box.y1;
childBox.x2 = x = childBox.x1 + centerWidth;
childBox.y2 = box.y2;
this._centerBox.allocate(childBox, flags);
let statusbox = new Big.Box();
let statusmenu = this._statusmenu = new Shell.StatusMenu();
statusbox.append(this._statusmenu, Big.BoxPackFlags.NONE);
let statusbutton = new Button.Button(statusbox, PANEL_BUTTON_COLOR, PRESSED_BUTTON_BACKGROUND_COLOR,
true, null, PANEL_HEIGHT);
statusbutton.button.connect('button-press-event', function (b, e) {
statusmenu.toggle(e);
return false;
});
box.append(statusbutton.button, Big.BoxPackFlags.END);
// We get a deactivated event when the popup disappears
this._statusmenu.connect('deactivated', function (sm) {
statusbutton.release();
});
childBox = new Clutter.ActorBox();
childBox.x1 = box.x2 - rightWidth;
childBox.y1 = box.y1;
childBox.x2 = box.x2;
childBox.y2 = box.y2;
this._rightBox.allocate(childBox, flags);
}));
/* left side */
this.button = new Button.Button(_("Activities"), PANEL_BUTTON_COLOR, PRESSED_BUTTON_BACKGROUND_COLOR,
PANEL_FOREGROUND_COLOR, DEFAULT_FONT);
this.button.actor.height = PANEL_HEIGHT;
this._leftBox.append(this.button.actor, Big.BoxPackFlags.NONE);
// We use this flag to mark the case where the user has entered the
// hot corner and has not left both the hot corner and a surrounding
// guard area (the "environs"). This avoids triggering the hot corner
// multiple times due to an accidental jitter.
this._hotCornerEntered = false;
this._hotCornerEnvirons = new Clutter.Rectangle({ width: 3,
height: 3,
opacity: 0,
reactive: true });
this._hotCorner = new Clutter.Rectangle({ width: 1,
height: 1,
opacity: 0,
reactive: true });
this._hotCornerEnvirons.connect('leave-event',
Lang.bind(this, this._onHotCornerEnvironsLeft));
// Clicking on the hot corner environs should result in the same bahavior
// as clicking on the hot corner.
this._hotCornerEnvirons.connect('button-release-event',
Lang.bind(this, this._onHotCornerClicked));
// In addition to being triggered by the mouse enter event, the hot corner
// can be triggered by clicking on it. This is useful if the user wants to
// undo the effect of triggering the hot corner once in the hot corner.
this._hotCorner.connect('enter-event',
Lang.bind(this, this._onHotCornerEntered));
this._hotCorner.connect('button-release-event',
Lang.bind(this, this._onHotCornerClicked));
this._hotCorner.connect('leave-event',
Lang.bind(this, this._onHotCornerLeft));
this._leftBox.append(this._hotCornerEnvirons, Big.BoxPackFlags.FIXED);
this._leftBox.append(this._hotCorner, Big.BoxPackFlags.FIXED);
let appMenu = new AppPanelMenu();
this._leftBox.append(appMenu.actor, Big.BoxPackFlags.NONE);
/* center */
this._clock = new Clutter.Text({ font_name: DEFAULT_FONT,
color: PANEL_FOREGROUND_COLOR,
this._clock = new Clutter.Text({ font_name: "Sans Bold 16px",
text: "" });
this._centerBox.append(this._clock, Big.BoxPackFlags.NONE);
/* right */
let pad = (PANEL_HEIGHT - this._clock.height) / 2;
let clockbox = new Big.Box({ padding_top: pad,
padding_bottom: pad,
padding_left: 4,
padding_right: 4 });
clockbox.append(this._clock, Big.BoxPackFlags.NONE);
box.append(clockbox, Big.BoxPackFlags.END);
// The tray icons live in trayBox within trayContainer.
// The trayBox is hidden when there are no tray icons.
let trayContainer = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
y_align: Big.BoxAlignment.START });
this._rightBox.append(trayContainer, Big.BoxPackFlags.NONE);
box.append(trayContainer, Big.BoxPackFlags.END);
let trayBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
height: TRAY_HEIGHT,
padding: TRAY_PADDING,
spacing: TRAY_SPACING });
this._trayBox = trayBox;
// gtk+ < 2.16 doesn't have fully-working icon transparency,
// so we want trayBox to be opaque in that case (the icons
@@ -348,100 +140,42 @@ Panel.prototype = {
this._traymanager = new Shell.TrayManager({ bg_color: TRAY_BACKGROUND_COLOR });
this._traymanager.connect('tray-icon-added',
Lang.bind(this, function(o, icon) {
function(o, icon) {
trayBox.append(icon, Big.BoxPackFlags.NONE);
// Make sure the trayBox is shown.
trayBox.show();
this._recomputeTraySize();
}));
});
this._traymanager.connect('tray-icon-removed',
Lang.bind(this, function(o, icon) {
function(o, icon) {
trayBox.remove_actor(icon);
if (trayBox.get_children().length == 0)
trayBox.hide();
this._recomputeTraySize();
}));
});
this._traymanager.manage_stage(global.stage);
let statusbox = new Big.Box();
let statusmenu = this._statusmenu = new Shell.StatusMenu();
statusmenu.get_icon().hide();
statusmenu.get_name().fontName = DEFAULT_FONT;
statusmenu.get_name().color = PANEL_FOREGROUND_COLOR;
statusbox.append(this._statusmenu, Big.BoxPackFlags.NONE);
let statusbutton = new Button.Button(statusbox,
PANEL_BUTTON_COLOR,
PRESSED_BUTTON_BACKGROUND_COLOR,
PANEL_FOREGROUND_COLOR);
statusbutton.actor.height = PANEL_HEIGHT;
statusbutton.actor.connect('button-press-event', function (b, e) {
if (e.get_button() == 1 && e.get_click_count() == 1) {
statusmenu.toggle(e);
// The statusmenu might not pop up if it couldn't get a pointer grab
if (statusmenu.is_active())
statusbutton.actor.active = true;
return true;
} else {
return false;
}
});
this._rightBox.append(statusbutton.actor, Big.BoxPackFlags.NONE);
// We get a deactivated event when the popup disappears
this._statusmenu.connect('deactivated', function (sm) {
statusbutton.actor.active = false;
});
// TODO: decide what to do with the rest of the panel in the Overview mode (make it fade-out, become non-reactive, etc.)
// We get into the Overview mode on button-press-event as opposed to button-release-event because eventually we'll probably
// have the Overview act like a menu that allows the user to release the mouse on the activity the user wants
// TODO: decide what to do with the rest of the panel in the overlay mode (make it fade-out, become non-reactive, etc.)
// We get into the overlay mode on button-press-event as opposed to button-release-event because eventually we'll probably
// have the overlay act like a menu that allows the user to release the mouse on the activity the user wants
// to switch to.
this.button.actor.connect('button-press-event', function(b, e) {
if (e.get_button() == 1 && e.get_click_count() == 1) {
Main.overview.toggle();
return true;
} else {
return false;
}
});
// In addition to pressing the button, the Overview can be entered and exited by other means, such as
// pressing the System key, Alt+F1 or Esc. We want the button to be pressed in when the Overview is entered
this.button.button.connect('button-press-event',
Lang.bind(Main.overlay, Main.overlay.toggle));
// In addition to pressing the button, the overlay can be entered and exited by other means, such as
// pressing the System key, Alt+F1 or Esc. We want the button to be pressed in when the overlay is entered
// and to be released when it is exited regardless of how it was triggered.
Main.overview.connect('showing', Lang.bind(this, function() {
this.button.actor.active = true;
}));
Main.overview.connect('hiding', Lang.bind(this, function() {
this.button.actor.active = false;
}));
Main.overlay.connect('showing', Lang.bind(this.button, this.button.pressIn));
Main.overlay.connect('hiding', Lang.bind(this.button, this.button.release));
Main.chrome.addActor(this.actor);
Main.chrome.setVisibleInOverview(this.actor, true);
this.actor.add_actor(box);
Main.chrome.addActor(this.actor, box);
Main.chrome.setVisibleInOverlay(this.actor, true);
// Start the clock
this._updateClock();
},
startupAnimation: function() {
this.actor.y = -this.actor.height;
Tweener.addTween(this.actor,
{ y: 0,
time: 0.2,
transition: "easeOutQuad"
});
},
// By default, tray icons have a spacing of TRAY_SPACING. However this
// starts to fail if we have too many as can sadly happen; just jump down
// to a spacing of 8 if we're over 6.
// http://bugzilla.gnome.org/show_bug.cgi?id=590495
_recomputeTraySize: function () {
if (this._trayBox.get_children().length > 6)
this._trayBox.spacing = TRAY_SPACING_MIN;
else
this._trayBox.spacing = TRAY_SPACING;
},
_updateClock: function() {
let displayDate = new Date();
let msecRemaining = 60000 - (1000 * displayDate.getSeconds() +
@@ -450,40 +184,8 @@ Panel.prototype = {
displayDate.setMinutes(displayDate.getMinutes() + 1);
msecRemaining += 60000;
}
/* Translators: This is a time format. */
this._clock.set_text(displayDate.toLocaleFormat(_("%a %l:%M %p")));
this._clock.set_text(displayDate.toLocaleFormat("%a %b %e, %l:%M %p"));
Mainloop.timeout_add(msecRemaining, Lang.bind(this, this._updateClock));
return false;
},
_onHotCornerEntered : function() {
if (!this._hotCornerEntered) {
this._hotCornerEntered = true;
if (!Main.overview.animationInProgress) {
Main.overview.toggle();
}
}
return false;
},
_onHotCornerClicked : function() {
if (!Main.overview.animationInProgress) {
Main.overview.toggle();
}
return false;
},
_onHotCornerLeft : function(actor, event) {
if (Shell.get_event_related(event) != this._hotCornerEnvirons) {
this._hotCornerEntered = false;
}
return false;
},
_onHotCornerEnvironsLeft : function(actor, event) {
if (Shell.get_event_related(event) != this._hotCorner) {
this._hotCornerEntered = false;
}
return false;
}
};

View File

@@ -1,278 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Big = imports.gi.Big;
const Clutter = imports.gi.Clutter;
const Pango = imports.gi.Pango;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Shell = imports.gi.Shell;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Signals = imports.signals;
const DND = imports.ui.dnd;
const Main = imports.ui.main;
const GenericDisplay = imports.ui.genericDisplay;
const PLACES_VSPACING = 8;
const PLACES_ICON_SIZE = 16;
/**
* An entry in the places menu.
* @name: String title
* @iconFactory: A JavaScript callback which will create an icon texture
* @onActivate: A JavaScript callback to launch the entry
*/
function PlaceDisplay(name, iconFactory, onActivate) {
this._init(name, iconFactory, onActivate);
}
PlaceDisplay.prototype = {
_init : function(name, iconFactory, onActivate) {
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
reactive: true,
spacing: 4 });
this.actor.connect('button-release-event', Lang.bind(this, function (b, e) {
onActivate(this);
Main.overview.hide();
}));
let text = new Clutter.Text({ font_name: "Sans 14px",
ellipsize: Pango.EllipsizeMode.END,
color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
text: name });
let iconBox = new Big.Box({ y_align: Big.BoxAlignment.CENTER });
this._icon = iconFactory();
iconBox.append(this._icon, Big.BoxPackFlags.NONE);
this.actor.append(iconBox, Big.BoxPackFlags.NONE);
this.actor.append(text, Big.BoxPackFlags.EXPAND);
this._iconFactory = iconFactory;
this._onActivate = onActivate;
this.actor._delegate = this;
let draggable = DND.makeDraggable(this.actor);
},
getDragActorSource: function() {
return this._icon;
},
getDragActor: function(stageX, stageY) {
return this._iconFactory();
},
//// Drag and drop methods ////
shellWorkspaceLaunch : function() {
this._onActivate();
}
};
Signals.addSignalMethods(PlaceDisplay.prototype);
function Places() {
this._init();
}
Places.prototype = {
_init : function() {
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
spacing: 4 });
this._menuBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
spacing: PLACES_VSPACING });
this.actor.append(this._menuBox, Big.BoxPackFlags.EXPAND);
this._devBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
spacing: PLACES_VSPACING });
this._dirsBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
spacing: PLACES_VSPACING });
this.actor.append(this._dirsBox, Big.BoxPackFlags.EXPAND);
let homeFile = Gio.file_new_for_path (GLib.get_home_dir());
let homeUri = homeFile.get_uri();
let homeLabel = Shell.util_get_label_for_uri (homeUri);
let homeIcon = Shell.util_get_icon_for_uri (homeUri);
let home = new PlaceDisplay(homeLabel,
function() {
return Shell.TextureCache.get_default().load_gicon(homeIcon, PLACES_ICON_SIZE);
},
function() {
Gio.app_info_launch_default_for_uri(homeUri, Main.createAppLaunchContext());
});
this._menuBox.append(home.actor, Big.BoxPackFlags.NONE);
/*
* Show devices, code more or less ported from nautilus-places-sidebar.c
*/
this._menuBox.append(this._devBox, Big.BoxPackFlags.NONE);
this._volumeMonitor = Gio.VolumeMonitor.get();
this._volumeMonitor.connect('volume-added', Lang.bind(this, this._updateDevices));
this._volumeMonitor.connect('volume-removed',Lang.bind(this, this._updateDevices));
this._volumeMonitor.connect('volume-changed', Lang.bind(this, this._updateDevices));
this._volumeMonitor.connect('mount-added', Lang.bind(this, this._updateDevices));
this._volumeMonitor.connect('mount-removed', Lang.bind(this, this._updateDevices));
this._volumeMonitor.connect('mount-changed', Lang.bind(this, this._updateDevices));
this._volumeMonitor.connect('drive-connected', Lang.bind(this, this._updateDevices));
this._volumeMonitor.connect('drive-disconnected', Lang.bind(this, this._updateDevices));
this._volumeMonitor.connect('drive-changed', Lang.bind(this, this._updateDevices));
this._updateDevices();
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) {
let network = new PlaceDisplay(networkApp.get_name(),
function() {
return networkApp.create_icon_texture(PLACES_ICON_SIZE);
},
function () {
networkApp.launch();
});
this._menuBox.append(network.actor, Big.BoxPackFlags.NONE);
}
let connect = new PlaceDisplay('Connect to...',
function () {
return Shell.TextureCache.get_default().load_icon_name("applications-internet", PLACES_ICON_SIZE);
},
function () {
new Shell.Process({ args: ['nautilus-connect-server'] }).run();
});
this._menuBox.append(connect.actor, Big.BoxPackFlags.NONE);
this._bookmarksPath = GLib.build_filenamev([GLib.get_home_dir(), ".gtk-bookmarks"]);
this._bookmarksFile = Gio.file_new_for_path(this._bookmarksPath);
let monitor = this._bookmarksFile.monitor_file(Gio.FileMonitorFlags.NONE, null);
this._bookmarkTimeoutId = 0;
monitor.connect('changed', Lang.bind(this, function () {
if (this._bookmarkTimeoutId > 0)
return;
/* Defensive event compression */
this._bookmarkTimeoutId = Mainloop.timeout_add(100, Lang.bind(this, function () {
this._bookmarkTimeoutId = 0;
this._reloadBookmarks();
return false;
}));
}));
this._reloadBookmarks();
},
_reloadBookmarks: function() {
this._dirsBox.remove_all();
if (!GLib.file_test(this._bookmarksPath, GLib.FileTest.EXISTS))
return;
let [success, bookmarksContent, len] = GLib.file_get_contents(this._bookmarksPath);
if (!success)
return;
let bookmarks = bookmarksContent.split('\n');
let bookmarksToLabel = {};
let bookmarksOrder = [];
for (let i = 0; i < bookmarks.length; i++) {
let bookmarkLine = bookmarks[i];
let components = bookmarkLine.split(' ');
let bookmark = components[0];
if (bookmark in bookmarksToLabel)
continue;
let label = null;
if (components.length > 1)
label = components.slice(1).join(' ');
bookmarksToLabel[bookmark] = label;
bookmarksOrder.push(bookmark);
}
for (let i = 0; i < bookmarksOrder.length; i++) {
let bookmark = bookmarksOrder[i];
let label = bookmarksToLabel[bookmark];
let file = Gio.file_new_for_uri(bookmark);
if (!file.query_exists(null))
continue;
if (label == null)
label = Shell.util_get_label_for_uri(bookmark);
if (label == null)
continue;
let icon = Shell.util_get_icon_for_uri(bookmark);
let item = new PlaceDisplay(label,
function() {
return Shell.TextureCache.get_default().load_gicon(icon, PLACES_ICON_SIZE);
},
function() {
Gio.app_info_launch_default_for_uri(bookmark, Main.createAppLaunchContext());
});
this._dirsBox.append(item.actor, Big.BoxPackFlags.NONE);
}
},
_updateDevices: function() {
this._devBox.remove_all();
/* first go through all connected drives */
let drives = this._volumeMonitor.get_connected_drives();
for (let i = 0; i < drives.length; i++) {
let volumes = drives[i].get_volumes();
for(let j = 0; j < volumes.length; j++) {
let mount = volumes[j].get_mount();
if(mount != null) {
this._addMount(mount);
}
}
}
/* add all volumes that is not associated with a drive */
let volumes = this._volumeMonitor.get_volumes();
for(let i = 0; i < volumes.length; i++) {
if(volumes[i].get_drive() != null)
continue;
let mount = volumes[i].get_mount();
if(mount != null) {
this._addMount(mount);
}
}
/* add mounts that have no volume (/etc/mtab mounts, ftp, sftp,...) */
let mounts = this._volumeMonitor.get_mounts();
for(let i = 0; i < mounts.length; i++) {
if(mounts[i].is_shadowed())
continue;
if(mounts[i].get_volume())
continue;
this._addMount(mounts[i]);
}
},
_addMount: function(mount) {
let mountLabel = mount.get_name();
let mountIcon = mount.get_icon();
let root = mount.get_root();
let mountUri = root.get_uri();
let devItem = new PlaceDisplay(mountLabel,
function() {
return Shell.TextureCache.get_default().load_gicon(mountIcon, PLACES_ICON_SIZE);
},
function() {
Gio.app_info_launch_default_for_uri(mountUri, Main.createAppLaunchContext());
});
this._devBox.append(devItem.actor, Big.BoxPackFlags.NONE);
}
};
Signals.addSignalMethods(Places.prototype);

View File

@@ -1,14 +1,8 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Big = imports.gi.Big;
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const Main = imports.ui.main;
@@ -32,26 +26,6 @@ RunDialog.prototype = {
_init : function() {
let global = Shell.Global.get();
this._isOpen = false;
this._internalCommands = { 'lg':
Lang.bind(this, function() {
// Run in an idle to avoid recursive key grab problems
Mainloop.idle_add(function() { Main.createLookingGlass().open(); });
}),
'r': Lang.bind(this, function() {
let global = Shell.Global.get();
global.reexec_self();
}),
// Developer brain backwards compatibility
'restart': Lang.bind(this, function() {
let global = Shell.Global.get();
global.reexec_self();
})
};
// All actors are inside _group. We create it initially
// hidden then show it in show()
this._group = new Clutter.Group({ visible: false });
@@ -69,17 +43,16 @@ RunDialog.prototype = {
(global.screen_height - BOX_HEIGHT) / 2);
this._group.add_actor(boxGroup);
let box = new Big.Box({ background_color: BOX_BACKGROUND_COLOR,
corner_radius: 4,
reactive: false,
width: BOX_WIDTH,
height: BOX_HEIGHT
});
let box = new Clutter.Rectangle({ color: BOX_BACKGROUND_COLOR,
reactive: false,
width: BOX_WIDTH,
height: BOX_HEIGHT,
border_width: 0 });
boxGroup.add_actor(box);
let label = new Clutter.Text({ color: BOX_TEXT_COLOR,
font_name: '18px Sans',
text: _("Please enter a command:") });
text: 'Please enter a command:' });
label.set_position(6, 6);
boxGroup.add_actor(label);
@@ -95,62 +68,70 @@ RunDialog.prototype = {
this._entry.set_position(6, 30);
boxGroup.add_actor(this._entry);
this._entry.connect('activate', Lang.bind(this, function (o, e) {
this._run(o.get_text());
this.close();
let me = this;
this._entry.connect('activate', function (o, e) {
me.hide();
me._run(o.get_text());
return false;
}));
this._entry.connect('key-press-event', Lang.bind(this, function(o, e) {
let symbol = Shell.get_event_key_symbol(e);
if (symbol == Clutter.Escape) {
this.close();
return true;
}
return false;
}));
});
},
_run : function(command) {
let f = this._internalCommands[command];
if (f) {
f();
if (command == 'restart') {
let global = Shell.Global.get();
global.reexec_self();
} else if (command) {
var p = new Shell.Process({'args' : [command]});
try {
let [ok, len, args] = GLib.shell_parse_argv(command);
let p = new Shell.Process({'args' : args});
p.run();
} catch (e) {
// TODO: Give the user direct feedback.
log('Could not run command ' + command + ': ' + e);
log('Could not run command ' + command + '.');
}
}
this.emit('run');
},
open : function() {
if (this._isOpen) // Already shown
return;
show : function() {
let me = this;
if (!Main.beginModal())
return;
this._isOpen = true;
this._group.show();
if (this._group.visible) // Already shown
return false;
if (!Main.startModal())
return false;
this._group.show_all();
this._entry.connect('key-press-event', function(o, e) {
if (Shell.get_event_key_symbol(e) == Clutter.Escape) {
me.hide();
me.emit('cancel');
return true;
} else
return false;
});
let global = Shell.Global.get();
global.stage.set_key_focus(this._entry);
return true;
},
close : function() {
if (!this._isOpen)
hide : function() {
if (!this._group.visible)
return;
this._isOpen = false;
this._group.hide();
this._entry.text = '';
Main.endModal();
},
destroy : function(){
this.hide();
this._group.destroy();
}
};
Signals.addSignalMethods(RunDialog.prototype);

View File

@@ -14,12 +14,10 @@ const WidgetBox = imports.ui.widgetBox;
const SIDEBAR_SPACING = 4;
const SIDEBAR_PADDING = 4;
// The total sidebar width is the widget width plus the widget padding
// (counted twice for the widget box, and once again for the
// out-of-screen padding), plus the empty space between the border of
// the bar and of the windows
const SIDEBAR_COLLAPSED_WIDTH = Widget.COLLAPSED_WIDTH + 3 * WidgetBox.WIDGETBOX_PADDING + SIDEBAR_PADDING;
const SIDEBAR_EXPANDED_WIDTH = Widget.EXPANDED_WIDTH + 3 * WidgetBox.WIDGETBOX_PADDING + SIDEBAR_PADDING;
// The total sidebar width is the widget width plus the widget
// padding, plus the sidebar padding
const SIDEBAR_COLLAPSED_WIDTH = Widget.COLLAPSED_WIDTH + 2 * WidgetBox.WIDGETBOX_PADDING + 2 * SIDEBAR_PADDING;
const SIDEBAR_EXPANDED_WIDTH = Widget.EXPANDED_WIDTH + 2 * WidgetBox.WIDGETBOX_PADDING + 2 * SIDEBAR_PADDING;
// The maximum height of the sidebar would be extending from just
// below the panel to just above the taskbar. Since the taskbar is
@@ -28,6 +26,13 @@ const SIDEBAR_EXPANDED_WIDTH = Widget.EXPANDED_WIDTH + 3 * WidgetBox.WIDGETBOX_P
const HARDCODED_TASKBAR_HEIGHT = 24;
const MAXIMUM_SIDEBAR_HEIGHT = Shell.Global.get().screen_height - Panel.PANEL_HEIGHT - HARDCODED_TASKBAR_HEIGHT;
// FIXME, needs to be configurable, obviously
const default_widgets = [
"imports.ui.widget.ClockWidget",
"imports.ui.widget.AppsWidget",
"imports.ui.widget.DocsWidget"
];
function Sidebar() {
this._init();
}
@@ -43,6 +48,7 @@ Sidebar.prototype = {
this.actor = new Clutter.Group({ x: -WidgetBox.WIDGETBOX_PADDING,
y: Panel.PANEL_HEIGHT,
width: SIDEBAR_EXPANDED_WIDTH });
Main.chrome.addActor(this.actor);
// The actual widgets go into a Big.Box inside this.actor. The
// box's width will vary during the expand/collapse animations,
@@ -52,38 +58,23 @@ Sidebar.prototype = {
// during the animation.
this.box = new Big.Box ({ padding_top: SIDEBAR_PADDING,
padding_bottom: SIDEBAR_PADDING,
padding_right: 0,
padding_right: SIDEBAR_PADDING,
padding_left: 0,
spacing: SIDEBAR_SPACING });
this.actor.add_actor(this.box);
this._gconf = Shell.GConf.get_default();
this._expanded = this._gconf.get_boolean ("sidebar/expanded");
if (!this._expanded)
this.actor.width = SIDEBAR_COLLAPSED_WIDTH;
this._visible = this._gconf.get_boolean ("sidebar/visible");
if (this._visible)
Main.chrome.addActor(this.actor);
this._visible = this.expanded = true;
this._widgets = [];
this.addWidget(new ToggleWidget());
let default_widgets = this._gconf.get_string_list("sidebar/widgets");
this.addWidget(new ToggleWidget(this));
for (let i = 0; i < default_widgets.length; i++)
this.addWidget(default_widgets[i]);
this._gconf.connect('changed::sidebar/expanded',
Lang.bind(this, this._expandedChanged));
this._gconf.connect('changed::sidebar/visible',
Lang.bind(this, this._visibleChanged));
},
addWidget: function(widget) {
let widgetBox;
try {
widgetBox = new WidgetBox.WidgetBox(widget, this._expanded);
widgetBox = new WidgetBox.WidgetBox(widget);
} catch(e) {
logError(e, "Failed to add widget '" + widget + "'");
return;
@@ -93,32 +84,18 @@ Sidebar.prototype = {
this._widgets.push(widgetBox);
},
_visibleChanged: function() {
let visible = this._gconf.get_boolean("sidebar/visible");
if (visible == this._visible)
return;
this._visible = visible;
if (visible)
Main.chrome.addActor(this.actor);
else
Main.chrome.removeActor(this.actor);
show: function() {
this._visible = true;
this.actor.show();
},
_expandedChanged: function() {
let expanded = this._gconf.get_boolean("sidebar/expanded");
if (expanded == this._expanded)
return;
this._expanded = expanded;
if (expanded)
this._expand();
else
this._collapse();
hide: function() {
this._visible = false;
this.actor.hide();
},
_expand: function() {
this._expanded = true;
expand: function() {
this.expanded = true;
for (let i = 0; i < this._widgets.length; i++)
this._widgets[i].expand();
@@ -129,8 +106,8 @@ Sidebar.prototype = {
} });
},
_collapse: function() {
this._expanded = false;
collapse: function() {
this.expanded = false;
for (let i = 0; i < this._widgets.length; i++)
this._widgets[i].collapse();
@@ -153,33 +130,24 @@ Sidebar.prototype = {
const LEFT_DOUBLE_ARROW = "\u00AB";
const RIGHT_DOUBLE_ARROW = "\u00BB";
function ToggleWidget() {
this._init();
function ToggleWidget(sidebar) {
this._init(sidebar);
}
ToggleWidget.prototype = {
__proto__ : Widget.Widget.prototype,
_init : function() {
this._gconf = Shell.GConf.get_default();
_init : function(sidebar) {
this._sidebar = sidebar;
this.actor = new Clutter.Text({ font_name: "Sans Bold 16px",
text: LEFT_DOUBLE_ARROW,
reactive: true });
this.actor.connect('button-release-event',
Lang.bind(this, this._collapse));
Lang.bind(this._sidebar, this._sidebar.collapse));
this.collapsedActor = new Clutter.Text({ font_name: "Sans Bold 16px",
text: RIGHT_DOUBLE_ARROW,
reactive: true });
this.collapsedActor.connect('button-release-event',
Lang.bind(this, this._expand));
},
_collapse : function () {
this._gconf.set_boolean ("sidebar/expanded", false);
},
_expand : function () {
this._gconf.set_boolean ("sidebar/expanded", true);
Lang.bind(this._sidebar, this._sidebar.expand));
}
};

View File

@@ -1,7 +1,6 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Signals = imports.signals;
@@ -42,17 +41,9 @@ const Tweener = imports.tweener.tweener;
// calls any of these is almost certainly wrong anyway, because they
// affect the entire application.)
let slowDownFactor = 1.0;
// Called from Main.start
function init() {
let slowdownEnv = GLib.getenv("GNOME_SHELL_SLOWDOWN_FACTOR");
if (slowdownEnv) {
let factor = parseFloat(slowdownEnv);
if (!isNaN(factor) && factor > 0.0)
slowDownFactor = factor;
}
Tweener.setFrameTicker(new ClutterFrameTicker());
}
@@ -217,10 +208,11 @@ ClutterFrameTicker.prototype = {
this._startTime = -1;
this._currentTime = -1;
this._timeline.connect('new-frame', Lang.bind(this,
let me = this;
this._timeline.connect('new-frame',
function(timeline, frame) {
this._onNewFrame(frame);
}));
me._onNewFrame(frame);
});
},
_onNewFrame : function(frame) {
@@ -233,7 +225,7 @@ ClutterFrameTicker.prototype = {
this._startTime = this._timeline.get_elapsed_time();
// currentTime is in milliseconds
this._currentTime = (this._timeline.get_elapsed_time() - this._startTime) / slowDownFactor;
this._currentTime = this._timeline.get_elapsed_time() - this._startTime;
this.emit('prepare-frame');
},

View File

@@ -9,9 +9,9 @@ const Lang = imports.lang;
const Pango = imports.gi.Pango;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const AppInfo = imports.misc.appInfo;
const DocDisplay = imports.ui.docDisplay;
const DocInfo = imports.misc.docInfo;
const COLLAPSED_WIDTH = 24;
@@ -31,22 +31,12 @@ function Widget() {
Widget.prototype = {
// _init():
//
// Your widget constructor. Your constructor function should look
// like:
//
// function MyWidgetType() {
// this._init.apply(this, arguments);
// }
//
// and your _init method should start by doing:
//
// Widget.Widget.prototype._init.apply(this, arguments);
//
// The _init method must define a field named "actor" containing
// the Clutter.Actor to show in expanded mode. This actor will be
// clipped to Widget.EXPANDED_WIDTH. Most widgets will also define
// a field named "title" containing the title string to show above
// the widget in the sidebar.
// Your widget constructor. Receives no arguments. Must define a
// field named "actor" containing the Clutter.Actor to show in
// expanded mode. This actor will be clipped to
// Widget.EXPANDED_WIDTH. Most widgets will also define a field
// named "title" containing the title string to show above the
// widget in the sidebar.
//
// If you want to have a separate collapsed view, you can define a
// field "collapsedActor" containing the Clutter.Actor to show in
@@ -61,9 +51,6 @@ Widget.prototype = {
// the sidebar is collapsed, the widget's expanded view will pop
// out of the sidebar until either the cursor moves out of it,
// or else the widget calls this.activated() on itself.
_init: function (initialState) {
this.state = initialState;
},
// destroy():
//
@@ -94,22 +81,23 @@ Widget.prototype = {
// state:
//
// A field set on your widget by the sidebar. Will contain one of
// the Widget.STATE_* values. (Eg, Widget.STATE_EXPANDED).
// the Widget.STATE_* values. (Eg, Widget.STATE_EXPANDED). Note
// that this will not be set until *after* _init() is called, so
// you cannot rely on it being set at that point. The widget will
// always initially be in STATE_EXPANDED.
};
Signals.addSignalMethods(Widget.prototype);
function ClockWidget() {
this._init.apply(this, arguments);
this._init();
}
ClockWidget.prototype = {
__proto__ : Widget.prototype,
_init: function() {
Widget.prototype._init.apply(this, arguments);
this.actor = new Clutter.Text({ font_name: "Sans Bold 16px",
text: "",
// Give an explicit height to ensure
@@ -158,15 +146,14 @@ ClockWidget.prototype = {
},
_updateText: function(time) {
// Translators: This is a time format.
this.actor.set_text(time.toLocaleFormat(_("%H:%M")));
this.actor.set_text(time.toLocaleFormat("%H:%M"));
},
_updateCairo: function(time) {
let global = Shell.Global.get();
Shell.draw_clock(this.collapsedActor,
time.getHours() % 12,
time.getMinutes());
global.clutter_cairo_texture_draw_clock(this.collapsedActor,
time.getHours() % 12,
time.getMinutes());
}
};
@@ -181,7 +168,7 @@ const ITEM_NAME_COLOR = new Clutter.Color();
ITEM_NAME_COLOR.from_pixel(0x000000ff);
function LauncherWidget() {
this._init.apply(this, arguments);
this._init();
}
LauncherWidget.prototype = {
@@ -285,61 +272,33 @@ LauncherWidget.prototype = {
}
};
function AppsWidgetInfo(appInfo) {
this._init(appInfo);
}
AppsWidgetInfo.prototype = {
_init : function(appInfo) {
this._info = appInfo;
this.name = appInfo.get_name();
},
createIcon : function(size) {
return this._info.create_icon_texture(size);
},
launch : function() {
this._info.launch();
}
}
function AppsWidget() {
this._init.apply(this, arguments);
this._init();
}
AppsWidget.prototype = {
__proto__ : LauncherWidget.prototype,
_init : function() {
Widget.prototype._init.apply(this, arguments);
this.title = _("Applications");
this.title = "Applications";
this.actor = new Big.Box({ spacing: 2 });
this.collapsedActor = new Big.Box({ spacing: 2});
let appSystem = Shell.AppSystem.get_default();
let apps = appSystem.get_favorites();
for (let i = 0; i < apps.length; i++) {
let app = appSystem.lookup_cached_app(apps[i]);
if (!app)
continue;
this.addItem(new AppsWidgetInfo(app));
}
let apps = AppInfo.getTopApps(5);
for (let i = 0; i < apps.length; i++)
this.addItem(apps[i]);
}
};
function RecentDocsWidget() {
this._init.apply(this, arguments);
function DocsWidget() {
this._init();
}
RecentDocsWidget.prototype = {
DocsWidget.prototype = {
__proto__ : LauncherWidget.prototype,
_init : function() {
Widget.prototype._init.apply(this, arguments);
this.title = _("Recent Documents");
this.title = "Recent Docs";
this.actor = new Big.Box({ spacing: 2 });
this._recentManager = Gtk.RecentManager.get_default();
@@ -361,7 +320,7 @@ RecentDocsWidget.prototype = {
items.push(docInfo);
}
items.sort(function (a,b) { return b.timestamp - a.timestamp; });
items.sort(function (a,b) { return b.lastVisited() - a.lastVisited(); });
for (i = 0; i < Math.min(items.length, 5); i++)
this.addItem(items[i]);
}

View File

@@ -15,24 +15,21 @@ WIDGETBOX_BG_COLOR.from_pixel(0xf0f0f0ff);
const BLACK = new Clutter.Color();
BLACK.from_pixel(0x000000ff);
const WIDGETBOX_PADDING = 2;
const WIDGETBOX_PADDING = 4;
const ANIMATION_TIME = 0.5;
const POP_IN_LAG = 250; /* milliseconds */
function WidgetBox(widget, expanded) {
this._init(widget, expanded);
function WidgetBox(widget) {
this._init(widget);
}
WidgetBox.prototype = {
_init: function(widget, expanded) {
this.state = expanded ? Widget.STATE_EXPANDED : Widget.STATE_COLLAPSED;
if (widget instanceof Widget.Widget) {
_init: function(widget) {
if (widget instanceof Widget.Widget)
this._widget = widget;
this._widget.state = this.state;
} else {
else {
let ctor = this._ctorFromName(widget);
this._widget = new ctor(this.state);
this._widget = new ctor();
}
if (!this._widget.actor)
@@ -40,7 +37,7 @@ WidgetBox.prototype = {
else if (!this._widget.title && !this._widget.collapsedActor)
throw new Error("widget has neither title nor collapsedActor");
this.state = expanded ? Widget.STATE_EXPANDED : Widget.STATE_COLLAPSED;
this.state = this._widget.state = Widget.STATE_EXPANDED;
// The structure of a WidgetBox:
//
@@ -80,15 +77,9 @@ WidgetBox.prototype = {
this.actor = new Clutter.Group();
this._hbox = new Big.Box({ background_color: WIDGETBOX_BG_COLOR,
padding_top: WIDGETBOX_PADDING,
padding_bottom: WIDGETBOX_PADDING,
padding_right: WIDGETBOX_PADDING,
// Left padding is here to make up for
// the X offset used for the sidebar
// to hide its rounded corners
padding_left: 2 * WIDGETBOX_PADDING,
padding: WIDGETBOX_PADDING,
spacing: WIDGETBOX_PADDING,
corner_radius: WIDGETBOX_PADDING,
corner_radius: WIDGETBOX_PADDING / 2,
orientation: Big.BoxOrientation.HORIZONTAL,
reactive: true });
this.actor.add_actor(this._hbox);
@@ -131,6 +122,7 @@ WidgetBox.prototype = {
this._activationHandler = this._widget.connect('activated',
Lang.bind(this, this._activationHandler));
}
this._cgroup.hide();
this._egroup = new Clutter.Group({ clip_to_allocation: true });
this._hbox.append(this._egroup, Big.BoxPackFlags.NONE);
@@ -150,11 +142,6 @@ WidgetBox.prototype = {
}
this._ebox.append(this._widget.actor, Big.BoxPackFlags.NONE);
if (expanded)
this._setWidgetExpanded();
else
this._setWidgetCollapsed();
},
// Given a name like "imports.ui.widget.ClockWidget", turn that
@@ -191,25 +178,16 @@ WidgetBox.prototype = {
this.state = this._widget.state = Widget.STATE_EXPANDING;
},
_setWidgetExpanded: function() {
_expandPart1Complete: function() {
this._cgroup.hide();
this._egroup.show();
this._cbox.x = 0;
if (this._singleActor) {
log(this._widget.actor);
this._widget.actor.unparent();
this._ebox.append(this._widget.actor, Big.BoxPackFlags.NONE);
}
if (this._htitle) {
this._htitle.show();
this._hline.show();
}
},
_expandPart1Complete: function() {
this._cbox.x = 0;
this._setWidgetExpanded();
if (this._widget.expand) {
try {
this._widget.expand();
@@ -218,6 +196,11 @@ WidgetBox.prototype = {
}
}
this._egroup.show();
if (this._htitle) {
this._htitle.show();
this._hline.show();
}
this._ebox.x = -Widget.EXPANDED_WIDTH;
Tweener.addTween(this._ebox, { x: 0,
time: ANIMATION_TIME / 2,
@@ -239,27 +222,19 @@ WidgetBox.prototype = {
this.state = this._widget.state = Widget.STATE_COLLAPSING;
},
_setWidgetCollapsed: function() {
_collapsePart1Complete: function() {
this._egroup.hide();
this._cgroup.show();
if (this._singleActor) {
this._widget.actor.unparent();
this._cbox.append(this._widget.actor, Big.BoxPackFlags.NONE);
}
this._ebox.x = 0;
if (this._htitle) {
this._htitle.hide();
this._hline.hide();
}
if (this._vtitle)
this._cbox.height = this._ebox.height;
},
_collapsePart1Complete: function() {
this._ebox.x = 0;
this._setWidgetCollapsed();
if (this._singleActor) {
log(this._widget.actor);
this._widget.actor.unparent();
this._cbox.append(this._widget.actor, Big.BoxPackFlags.NONE);
}
if (this._widget.collapse) {
try {
@@ -269,7 +244,10 @@ WidgetBox.prototype = {
}
}
this._cgroup.show();
this._cbox.x = -Widget.COLLAPSED_WIDTH;
if (this._vtitle)
this._cbox.height = this._ebox.height;
Tweener.addTween(this._cbox, { x: 0,
time: ANIMATION_TIME / 2,
transition: "easeOutQuad",

View File

@@ -85,7 +85,7 @@ WindowManager.prototype = {
},
_shouldAnimate : function(actor) {
if (Main.overview.visible)
if (Main.overlay.visible)
return false;
if (actor && (actor.get_window_type() != Meta.CompWindowType.NORMAL))
return false;
@@ -145,13 +145,50 @@ WindowManager.prototype = {
},
_maximizeWindow : function(actor, targetX, targetY, targetWidth, targetHeight) {
this._shellwm.completed_maximize(actor);
if (!this._shouldAnimate(actor)) {
this._shellwm.completed_maximize(actor);
return;
}
/* this doesn't work very well, as simply scaling up the existing
* window contents doesn't produce anything like the same results as
* actually maximizing the window.
*/
let scaleX = targetWidth / actor.width;
let scaleY = targetHeight / actor.height;
let anchorX = (actor.x - targetX) * actor.width/(targetWidth - actor.width);
let anchorY = (actor.y - targetY) * actor.height/(targetHeight - actor.height);
actor.move_anchor_point(anchorX, anchorY);
this._maximizing.push(actor);
Tweener.addTween(actor,
{ scale_x: scaleX,
scale_y: scaleY,
time: WINDOW_ANIMATION_TIME,
transition: "easeOutQuad",
onComplete: this._maximizeWindowDone,
onCompleteScope: this,
onCompleteParams: [actor],
onOverwrite: this._maximizeWindowOverwrite,
onOverwriteScope: this,
onOverwriteParams: [actor]
});
},
_maximizeWindowDone : function(actor) {
if (this._removeEffect(this._maximizing, actor)) {
Tweener.removeTweens(actor);
actor.set_scale(1.0, 1.0);
actor.move_anchor_point_from_gravity(Clutter.Gravity.NORTH_WEST);
this._shellwm.completed_maximize(actor);
}
},
_maximizeWindowOverwrite : function(actor) {
if (this._removeEffect(this._maximizing, actor)) {
this._shellwm.completed_maximize(actor);
}
},
_unmaximizeWindow : function(actor, targetX, targetY, targetWidth, targetHeight) {

View File

@@ -11,9 +11,11 @@ const Pango = imports.gi.Pango;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const AppDisplay = imports.ui.appDisplay;
const DND = imports.ui.dnd;
const GenericDisplay = imports.ui.genericDisplay;
const Main = imports.ui.main;
const Overview = imports.ui.overview;
const Overlay = imports.ui.overlay;
const Panel = imports.ui.panel;
const Tweener = imports.ui.tweener;
@@ -33,11 +35,11 @@ FRAME_COLOR.from_pixel(0xffffffff);
// Each triplet is [xCenter, yCenter, scale] where the scale
// is relative to the width of the workspace.
const POSITIONS = {
1: [[0.5, 0.5, 0.95]],
2: [[0.25, 0.5, 0.48], [0.75, 0.5, 0.48]],
3: [[0.25, 0.25, 0.48], [0.75, 0.25, 0.48], [0.5, 0.75, 0.48]],
4: [[0.25, 0.25, 0.47], [0.75, 0.25, 0.47], [0.75, 0.75, 0.47], [0.25, 0.75, 0.47]],
5: [[0.165, 0.25, 0.32], [0.495, 0.25, 0.32], [0.825, 0.25, 0.32], [0.25, 0.75, 0.32], [0.75, 0.75, 0.32]]
1: [[0.5, 0.5, 0.8]],
2: [[0.25, 0.5, 0.4], [0.75, 0.5, 0.4]],
3: [[0.25, 0.25, 0.33], [0.75, 0.25, 0.33], [0.5, 0.75, 0.33]],
4: [[0.25, 0.25, 0.33], [0.75, 0.25, 0.33], [0.75, 0.75, 0.33], [0.25, 0.75, 0.33]],
5: [[0.165, 0.25, 0.28], [0.495, 0.25, 0.28], [0.825, 0.25, 0.28], [0.25, 0.75, 0.4], [0.75, 0.75, 0.4]]
};
// Spacing between workspaces. At the moment, the same spacing is used
@@ -119,10 +121,9 @@ WindowClone.prototype = {
_onDragBegin : function (draggable, time) {
this._inDrag = true;
this._updateTitle();
this.emit('drag-begin');
},
_onDragEnd : function (draggable, time, snapback) {
_onDragEnd : function (draggable, time) {
this._inDrag = false;
// Most likely, the clone is going to move away from the
@@ -132,8 +133,6 @@ WindowClone.prototype = {
// better to have the label mysteriously missing than
// mysteriously present
this._havePointer = false;
this.emit('drag-end');
},
// Called by Tweener
@@ -156,7 +155,16 @@ WindowClone.prototype = {
padding: 4,
spacing: 4,
orientation: Big.BoxOrientation.HORIZONTAL });
let icon = this.metaWindow.mini_icon;
let iconTexture = new Clutter.Texture({ x: this.actor.x,
y: this.actor.y + this.actor.height - 16,
width: 16,
height: 16,
keep_aspect_ratio: true });
Shell.clutter_texture_set_from_pixbuf(iconTexture, icon);
box.append(iconTexture, Big.BoxPackFlags.NONE);
let title = new Clutter.Text({ color: WINDOWCLONE_TITLE_COLOR,
font_name: "Sans 12",
text: this.metaWindow.title,
@@ -250,34 +258,20 @@ DesktopClone.prototype = {
Signals.addSignalMethods(DesktopClone.prototype);
/**
* @workspaceNum: Workspace index
* @parentActor: The actor which will be the parent of this workspace;
* we need this in order to add chrome such as the icons
* on top of the windows without having them be scaled.
*/
function Workspace(workspaceNum, parentActor) {
this._init(workspaceNum, parentActor);
function Workspace(workspaceNum) {
this._init(workspaceNum);
}
Workspace.prototype = {
_init : function(workspaceNum, parentActor) {
_init : function(workspaceNum) {
let me = this;
let global = Shell.Global.get();
this.workspaceNum = workspaceNum;
this._metaWorkspace = global.screen.get_workspace_by_index(workspaceNum);
this.parentActor = parentActor;
this.actor = new Clutter.Group();
this.actor._delegate = this;
// Auto-sizing is unreliable in the presence of ClutterClone, so rather than
// implicitly counting on the workspace actor to be sized to the size of the
// included desktop actor clone, set the size explicitly to the screen size.
// See http://bugzilla.openedhand.com/show_bug.cgi?id=1755
this.actor.width = global.screen_width;
this.actor.height = global.screen_height;
this.scale = 1.0;
let windows = global.get_windows().filter(this._isMyWindow, this);
@@ -297,16 +291,15 @@ Workspace.prototype = {
Lang.bind(this,
function(clone, time) {
this._metaWorkspace.activate(time);
Main.overview.hide();
Main.overlay.hide();
}));
this.actor.add_actor(this._desktop.actor);
// Create clones for remaining windows that should be
// visible in the Overview
// visible in the overlay
this._windows = [this._desktop];
this._windowIcons = [ null ];
for (let i = 0; i < windows.length; i++) {
if (this._isOverviewWindow(windows[i])) {
if (this._isOverlayWindow(windows[i])) {
this._addWindowClone(windows[i]);
}
}
@@ -322,13 +315,12 @@ Workspace.prototype = {
this._frame = null;
this.leavingOverview = false;
this.leavingOverlay = false;
},
updateRemovable : function() {
let global = Shell.Global.get();
let removable = (this._windows.length == 1 /* just desktop */ &&
this.workspaceNum != 0 &&
this.workspaceNum == global.screen.n_workspaces - 1);
if (removable) {
@@ -350,7 +342,7 @@ Workspace.prototype = {
this._removeButton.set_opacity(0);
Tweener.addTween(this._removeButton,
{ opacity: 255,
time: Overview.ANIMATION_TIME,
time: Overlay.ANIMATION_TIME,
transition: "easeOutQuad"
});
}
@@ -361,7 +353,7 @@ Workspace.prototype = {
if (this._visible) {
Tweener.addTween(this._removeButton,
{ opacity: 0,
time: Overview.ANIMATION_TIME,
time: Overlay.ANIMATION_TIME,
transition: "easeOutQuad",
onComplete: this._removeRemoveButton,
onCompleteScope: this
@@ -371,21 +363,6 @@ Workspace.prototype = {
}
},
_lookupIndex: function (metaWindow) {
let index, clone;
for (let i = 0; i < this._windows.length; i++) {
if (this._windows[i].metaWindow == metaWindow) {
return i;
}
}
return -1;
},
lookupCloneForMetaWindow: function (metaWindow) {
let index = this._lookupIndex (metaWindow);
return index < 0 ? null : this._windows[index];
},
_adjustRemoveButton : function() {
this._removeButton.set_scale(1.0 / this.actor.scale_x,
1.0 / this.actor.scale_y);
@@ -402,9 +379,7 @@ Workspace.prototype = {
// Mark the workspace selected/not-selected
setSelected : function(selected) {
let global = Shell.Global.get();
// Don't draw a frame if we only have one workspace
if (selected && global.screen.n_workspaces > 1) {
if (selected) {
if (this._frame)
return;
@@ -417,11 +392,13 @@ Workspace.prototype = {
this._desktop.actor.height + 2 * FRAME_SIZE / this.actor.scale_y);
this._frame.lower_bottom();
this._framePosHandler = this.actor.connect('notify::scale-x', Lang.bind(this, this._updateFramePosition));
this._framePosHandler = this.actor.connect('notify::x', Lang.bind(this, this._updateFramePosition));
this._frameSizeHandler = this.actor.connect('notify::scale-x', Lang.bind(this, this._updateFrameSize));
} else {
if (!this._frame)
return;
this.actor.disconnect(this._framePosHandler);
this.actor.disconnect(this._frameSizeHandler);
this._frame.destroy();
this._frame = null;
}
@@ -430,135 +407,85 @@ Workspace.prototype = {
_updateFramePosition : function() {
this._frame.set_position(this._desktop.actor.x - FRAME_SIZE / this.actor.scale_x,
this._desktop.actor.y - FRAME_SIZE / this.actor.scale_y);
},
_updateFrameSize : function() {
this._frame.set_size(this._desktop.actor.width + 2 * FRAME_SIZE / this.actor.scale_x,
this._desktop.actor.height + 2 * FRAME_SIZE / this.actor.scale_y);
},
// Reposition all windows in their zoomed-to-Overview position. if workspaceZooming
// Reposition all windows in their zoomed-to-overlay position. if workspaceZooming
// is true, then the workspace is moving at the same time and we need to take
// that into account
positionWindows : function(workspaceZooming) {
_positionWindows : function(workspaceZooming) {
let global = Shell.Global.get();
for (let i = 1; i < this._windows.length; i++) {
let clone = this._windows[i];
let icon = this._windowIcons[i];
clone.stackAbove = this._windows[i - 1].actor;
let [xCenter, yCenter, fraction] = this._computeWindowPosition(i);
xCenter = xCenter * global.screen_width;
yCenter = yCenter * global.screen_height;
// clone.actor.width/height aren't reliably set at this point for
// a new window - they're only set when the window contents are
// initially updated prior to painting.
let cloneRect = new Meta.Rectangle();
clone.realWindow.meta_window.get_outer_rect(cloneRect);
let size = Math.max(clone.actor.width, clone.actor.height);
let desiredSize = global.screen_width * fraction;
let scale = Math.min(desiredSize / size, 1.0 / this.scale);
let desiredWidth = global.screen_width * fraction;
let desiredHeight = global.screen_height * fraction;
let scale = Math.min(desiredWidth / cloneRect.width, desiredHeight / cloneRect.height, 1.0 / this.scale);
icon.hide();
Tweener.addTween(clone.actor,
{ x: xCenter - 0.5 * scale * cloneRect.width,
y: yCenter - 0.5 * scale * cloneRect.height,
{ x: xCenter - 0.5 * scale * clone.actor.width,
y: yCenter - 0.5 * scale * clone.actor.height,
scale_x: scale,
scale_y: scale,
workspace_relative: workspaceZooming ? this : null,
time: Overview.ANIMATION_TIME,
transition: "easeOutQuad",
onComplete: Lang.bind(this, function() {
this._fadeInWindowIcon(clone, icon);
})
time: Overlay.ANIMATION_TIME,
transition: "easeOutQuad"
});
}
},
_fadeInWindowIcon: function (clone, icon) {
icon.opacity = 0;
icon.show();
// This is a little messy and complicated because when we
// start the fade-in we may not have done the final positioning
// of the workspaces. (Tweener doesn't necessarily finish
// all animations before calling onComplete callbacks.)
// So we need to manually compute where the window will
// be after the workspace animation finishes.
let [parentX, parentY] = icon.get_parent().get_position();
let [cloneX, cloneY] = clone.actor.get_position();
let [cloneWidth, cloneHeight] = clone.actor.get_size();
cloneX = this.gridX + this.scale * cloneX;
cloneY = this.gridY + this.scale * cloneY;
cloneWidth = this.scale * clone.actor.scale_x * cloneWidth;
cloneHeight = this.scale * clone.actor.scale_y * cloneHeight;
// Note we only round the first part, because we're still going to be
// positioned relative to the parent. By subtracting a possibly
// non-integral parent X/Y we cancel it out.
let x = Math.round(cloneX + cloneWidth - icon.width) - parentX;
let y = Math.round(cloneY + cloneHeight - icon.height) - parentY;
icon.set_position(x, y);
icon.raise(this.actor);
Tweener.addTween(icon,
{ opacity: 255,
time: Overview.ANIMATION_TIME,
transition: "easeOutQuad" });
},
_fadeInAllIcons: function () {
for (let i = 1; i < this._windows.length; i++) {
let clone = this._windows[i];
let icon = this._windowIcons[i];
this._fadeInWindowIcon(clone, icon);
}
},
_hideAllIcons: function () {
for (let i = 1; i < this._windows.length; i++) {
let icon = this._windowIcons[i];
icon.hide();
}
},
_windowRemoved : function(metaWorkspace, metaWin) {
let global = Shell.Global.get();
let win = metaWin.get_compositor_private();
// find the position of the window in our list
let index = this._lookupIndex (metaWin);
let index = - 1, clone;
for (let i = 0; i < this._windows.length; i++) {
if (this._windows[i].metaWindow == metaWin) {
index = i;
clone = this._windows[index];
break;
}
}
if (index == -1)
return;
let clone = this._windows[index];
let icon = this._windowIcons[index];
this._windows.splice(index, 1);
this._windowIcons.splice(index, 1);
// If metaWin.get_compositor_private() returned non-NULL, that
// means the window still exists (and is just being moved to
// another workspace or something), so set its overviewHint
// another workspace or something), so set its overlayHint
// accordingly. (If it returned NULL, then the window is being
// destroyed; we'd like to animate this, but it's too late at
// this point.)
if (win) {
let [stageX, stageY] = clone.actor.get_transformed_position();
let [stageWidth, stageHeight] = clone.actor.get_transformed_size();
win._overviewHint = {
win._overlayHint = {
x: stageX,
y: stageY,
scale: stageWidth / clone.actor.width
};
}
clone.destroy();
icon.destroy();
this.positionWindows(false);
this._positionWindows(false);
this.updateRemovable();
},
_windowAdded : function(metaWorkspace, metaWin) {
if (this.leavingOverview)
if (this.leavingOverlay)
return;
let win = metaWin.get_compositor_private();
@@ -575,69 +502,65 @@ Workspace.prototype = {
return;
}
if (!this._isOverviewWindow(win))
if (!this._isOverlayWindow(win))
return;
let clone = this._addWindowClone(win);
if (win._overviewHint) {
let x = (win._overviewHint.x - this.actor.x) / this.scale;
let y = (win._overviewHint.y - this.actor.y) / this.scale;
let scale = win._overviewHint.scale / this.scale;
delete win._overviewHint;
if (win._overlayHint) {
let x = (win._overlayHint.x - this.actor.x) / this.scale;
let y = (win._overlayHint.y - this.actor.y) / this.scale;
let scale = win._overlayHint.scale / this.scale;
delete win._overlayHint;
clone.actor.set_position (x, y);
clone.actor.set_scale (scale, scale);
}
this.positionWindows(false);
this._positionWindows(false);
this.updateRemovable();
},
// Animate the full-screen to Overview transition.
zoomToOverview : function() {
this.actor.set_position(this.gridX, this.gridY);
this.actor.set_scale(this.scale, this.scale);
// Position and scale the windows.
this.positionWindows(true);
// Fade in the remove button if available, so that it doesn't appear
// too abrubtly and doesn't start at a too big size.
if (this._removeButton) {
Tweener.removeTweens(this._removeButton);
this._removeButton.opacity = 0;
Tweener.addTween(this._removeButton,
{ opacity: 255,
time: Overview.ANIMATION_TIME,
transition: 'easeOutQuad'
});
}
// Animate the full-screen to overlay transition.
zoomToOverlay : function() {
// Move the workspace into size/position
this.actor.set_position(this.fullSizeX, this.fullSizeY);
this.updateInOverlay();
this._visible = true;
},
// Animates the return from Overview mode
zoomFromOverview : function() {
this.leavingOverview = true;
// Animates the display of a workspace and its windows to have the current dimensions and position.
updateInOverlay : function() {
Tweener.addTween(this.actor,
{ x: this.gridX,
y: this.gridY,
scale_x: this.scale,
scale_y: this.scale,
time: Overlay.ANIMATION_TIME,
transition: "easeOutQuad"
});
this._hideAllIcons();
// Likewise for each of the windows in the workspace.
this._positionWindows(true);
},
Main.overview.connect('hidden', Lang.bind(this,
this._doneLeavingOverview));
// Animates the return from overlay mode
zoomFromOverlay : function() {
this.leavingOverlay = true;
Tweener.addTween(this.actor,
{ x: this.fullSizeX,
y: this.fullSizeY,
scale_x: 1.0,
scale_y: 1.0,
time: Overlay.ANIMATION_TIME,
transition: "easeOutQuad",
onComplete: this._doneLeavingOverlay,
onCompleteScope: this
});
// Fade out the remove button if available, so that it doesn't
// disappear too abrubtly and doesn't become too big.
if (this._removeButton) {
Tweener.removeTweens(this._removeButton);
Tweener.addTween(this._removeButton,
{ opacity: 0,
time: Overview.ANIMATION_TIME,
transition: 'easeOutQuad'
});
}
// Position and scale the windows.
for (let i = 1; i < this._windows.length; i++) {
let clone = this._windows[i];
Tweener.addTween(clone.actor,
@@ -646,7 +569,7 @@ Workspace.prototype = {
scale_x: 1.0,
scale_y: 1.0,
workspace_relative: this,
time: Overview.ANIMATION_TIME,
time: Overlay.ANIMATION_TIME,
opacity: 255,
transition: "easeOutQuad"
});
@@ -658,18 +581,16 @@ Workspace.prototype = {
// Animates grid shrinking/expanding when a row or column
// of workspaces is added or removed
resizeToGrid : function (oldScale) {
this._hideAllIcons();
Tweener.addTween(this.actor,
{ x: this.gridX,
y: this.gridY,
scale_x: this.scale,
scale_y: this.scale,
time: Overview.ANIMATION_TIME,
transition: "easeOutQuad",
onComplete: Lang.bind(this, this._fadeInAllIcons)
time: Overlay.ANIMATION_TIME,
transition: "easeOutQuad"
});
},
// Animates the addition of a new (empty) workspace
slideIn : function(oldScale) {
let global = Shell.Global.get();
@@ -686,7 +607,7 @@ Workspace.prototype = {
y: this.gridY,
scale_x: this.scale,
scale_y: this.scale,
time: Overview.ANIMATION_TIME,
time: Overlay.ANIMATION_TIME,
transition: "easeOutQuad"
});
@@ -698,8 +619,6 @@ Workspace.prototype = {
let global = Shell.Global.get();
let destX = this.actor.x, destY = this.actor.y;
this._hideAllIcons();
if (this.gridCol > this.gridRow)
destX = global.screen_width;
else
@@ -709,7 +628,7 @@ Workspace.prototype = {
y: destY,
scale_x: this.scale,
scale_y: this.scale,
time: Overview.ANIMATION_TIME,
time: Overlay.ANIMATION_TIME,
transition: "easeOutQuad",
onComplete: onComplete
});
@@ -732,9 +651,9 @@ Workspace.prototype = {
this._metaWorkspace.disconnect(this._windowRemovedId);
},
// Sets this.leavingOverview flag to false.
_doneLeavingOverview : function() {
this.leavingOverview = false;
// Sets this.leavingOverlay flag to false.
_doneLeavingOverlay : function() {
this.leavingOverlay = false;
},
// Tests if @win belongs to this workspaces
@@ -743,53 +662,25 @@ Workspace.prototype = {
(win.get_meta_window() && win.get_meta_window().is_on_all_workspaces());
},
// Tests if @win should be shown in the Overview
_isOverviewWindow : function (win) {
let appMon = Shell.AppMonitor.get_default()
return appMon.is_window_usage_tracked(win.get_meta_window());
},
_createWindowIcon: function(window) {
let appSys = Shell.AppSystem.get_default();
let appMon = Shell.AppMonitor.get_default()
let appInfo = appMon.get_window_app(window.metaWindow);
let iconTexture = null;
// The design is application based, so prefer the application
// icon here if we have it. FIXME - should move this fallback code
// into ShellAppMonitor.
if (appInfo) {
iconTexture = appInfo.create_icon_texture(48);
} else {
let icon = window.metaWindow.icon;
iconTexture = new Clutter.Texture({ width: 48,
height: 48,
keep_aspect_ratio: true });
Shell.clutter_texture_set_from_pixbuf(iconTexture, icon);
}
return iconTexture;
// Tests if @win should be shown in the overlay
_isOverlayWindow : function (win) {
let wintype = win.get_window_type();
if (wintype == Meta.WindowType.DESKTOP ||
wintype == Meta.WindowType.DOCK)
return false;
return !win.is_override_redirect();
},
// Create a clone of a (non-desktop) window and add it to the window list
_addWindowClone : function(win) {
let icon = this._createWindowIcon(win);
this.parentActor.add_actor(icon);
let clone = new WindowClone(win);
clone.connect('selected',
Lang.bind(this, this._onCloneSelected));
clone.connect('drag-begin',
Lang.bind(this, function() {
icon.hide();
}));
clone.connect('drag-end',
Lang.bind(this, function() {
icon.show();
}));
clone.connect('dragged',
Lang.bind(this, this._onCloneDragged));
this.actor.add_actor(clone.actor);
this._windows.push(clone);
this._windowIcons.push(icon);
return clone;
},
@@ -807,7 +698,7 @@ Workspace.prototype = {
let gridWidth = Math.ceil(Math.sqrt(numberOfWindows));
let gridHeight = Math.ceil(numberOfWindows / gridWidth);
let fraction = 0.95 * (1. / gridWidth);
let fraction = Math.sqrt(.5/(gridWidth * gridHeight));
let xCenter = (.5 / gridWidth) + ((windowIndex) % gridWidth) / gridWidth;
let yCenter = (.5 / gridHeight) + Math.floor((windowIndex / gridWidth)) / gridHeight;
@@ -815,8 +706,20 @@ Workspace.prototype = {
return [xCenter, yCenter, fraction];
},
_onCloneDragged : function (clone, stageX, stageY, time) {
this.emit('window-dragged', clone, stageX, stageY, time);
},
_onCloneSelected : function (clone, time) {
Main.overview.activateWindow(clone.metaWindow, time);
let global = Shell.Global.get();
let activeWorkspace = global.screen.get_active_workspace_index();
if (this.workspaceNum != activeWorkspace) {
let workspace = global.screen.get_workspace_by_index(this.workspaceNum);
workspace.activate_with_focus(clone.metaWindow, time);
} else
clone.metaWindow.activate(time);
Main.overlay.hide();
},
_removeSelf : function(actor, event) {
@@ -839,7 +742,7 @@ Workspace.prototype = {
// Set a hint on the Mutter.Window so its initial position
// in the new workspace will be correct
win._overviewHint = {
win._overlayHint = {
x: actor.x,
y: actor.y,
scale: actor.scale_x
@@ -850,9 +753,9 @@ Workspace.prototype = {
false, // don't create workspace
time);
return true;
} else if (source.shellWorkspaceLaunch) {
} else if (source instanceof GenericDisplay.GenericDisplayItem || source instanceof AppDisplay.WellDisplayItem) {
this._metaWorkspace.activate(time);
source.shellWorkspaceLaunch();
source.launch();
return true;
}
@@ -893,30 +796,27 @@ Workspaces.prototype = {
}
}
activeWorkspace.actor.raise_top();
this._positionWorkspaces(global);
// Save the button size as a global variable so we can us it to create
// matching button sizes for workspace remove buttons.
buttonSize = addButtonSize;
this._positionWorkspaces(global, activeWorkspace);
// Create (+) button
this.addButton = new AddWorkspaceButton(addButtonSize, addButtonX, addButtonY, Lang.bind(this, this._addWorkspaceAcceptDrop));
this.addButton.actor.connect('button-release-event', Lang.bind(this, this._appendNewWorkspace));
this.actor.add_actor(this.addButton.actor);
this.addButton.actor.lower_bottom();
buttonSize = addButtonSize;
this.addButton = new Clutter.Texture({ x: addButtonX,
y: addButtonY,
width: buttonSize,
height: buttonSize,
reactive: true
});
this.addButton.set_from_file(global.imagedir + "add-workspace.svg");
this.addButton.connect('button-release-event', this._appendNewWorkspace);
this.actor.add_actor(this.addButton);
this.addButton.lower_bottom();
let lastWorkspace = this._workspaces[this._workspaces.length - 1];
lastWorkspace.updateRemovable(true);
// Position/scale the desktop windows and their children after the
// workspaces have been created. This cannot be done first because
// window movement depends on the Workspaces object being accessible
// as an Overview member.
Main.overview.connect('showing',
Lang.bind(this, function() {
for (let w = 0; w < this._workspaces.length; w++)
this._workspaces[w].zoomToOverview();
}));
// Position/scale the desktop windows and their children
for (let w = 0; w < this._workspaces.length; w++)
this._workspaces[w].zoomToOverlay();
// Track changes to the number of workspaces
this._nWorkspacesNotifyId =
@@ -927,30 +827,15 @@ Workspaces.prototype = {
Lang.bind(this, this._activeWorkspaceChanged));
},
_lookupCloneForMetaWindow: function (metaWindow) {
for (let i = 0; i < this._workspaces.length; i++) {
let clone = this._workspaces[i].lookupCloneForMetaWindow(metaWindow);
if (clone)
return clone;
}
return null;
},
// Updates position of the workspaces display based on the new coordinates.
// Preserves the old value for the coordinate, if the passed value is null.
updatePosition : function(x, y) {
if (x != null)
this._x = x;
if (y != null)
this._y = y;
// Should only be called from active Overview context
activateWindowFromOverview: function (metaWindow, time) {
let global = Shell.Global.get();
let activeWorkspaceNum = global.screen.get_active_workspace_index();
let windowWorkspaceNum = metaWindow.get_workspace().index();
let clone = this._lookupCloneForMetaWindow (metaWindow);
clone.actor.raise_top();
if (windowWorkspaceNum != activeWorkspaceNum) {
let workspace = global.screen.get_workspace_by_index(windowWorkspaceNum);
workspace.activate_with_focus(metaWindow, time);
} else {
metaWindow.activate(time);
}
this._updateInOverlay();
},
hide : function() {
@@ -958,11 +843,11 @@ Workspaces.prototype = {
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
let activeWorkspace = this._workspaces[activeWorkspaceIndex];
this._positionWorkspaces(global);
this._positionWorkspaces(global, activeWorkspace);
activeWorkspace.actor.raise_top();
for (let w = 0; w < this._workspaces.length; w++)
this._workspaces[w].zoomFromOverview();
this._workspaces[w].zoomFromOverlay();
},
destroy : function() {
@@ -979,17 +864,35 @@ Workspaces.prototype = {
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
},
getScale : function() {
return this._workspaces[0].scale;
getFullSizeX : function() {
return this._workspaces[0].fullSizeX;
},
// Get the grid position of the active workspace.
getActiveWorkspacePosition : function() {
// If j-th workspace in the i-th row is active, returns the full width
// of j workspaces including empty space if i = 1, or the width of one
// workspace.
// Used in overlay.js to determine when it is ok to remove the sideshow
// during animations for entering and leaving the overlay.
getWidthToTopActiveWorkspace : function() {
let global = Shell.Global.get();
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
let activeWorkspace = this._workspaces[activeWorkspaceIndex];
return [activeWorkspace.gridX, activeWorkspace.gridY];
if (activeWorkspace.gridRow == 0)
return (activeWorkspace.gridCol + 1) * global.screen_width + activeWorkspace.gridCol * GRID_SPACING;
else
return global.screen_width;
},
// Updates the workspaces display based on the current dimensions and position.
_updateInOverlay : function() {
let global = Shell.Global.get();
this._positionWorkspaces(global);
// Position/scale the desktop windows and their children
for (let w = 0; w < this._workspaces.length; w++)
this._workspaces[w].updateInOverlay();
},
// Assign grid positions to workspaces. We can't just do a simple
@@ -1002,7 +905,12 @@ Workspaces.prototype = {
// first row.)
//
// FIXME: need to make the metacity internal layout agree with this!
_positionWorkspaces : function(global) {
_positionWorkspaces : function(global, activeWorkspace) {
if (!activeWorkspace) {
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
activeWorkspace = this._workspaces[activeWorkspaceIndex];
}
let gridWidth = Math.ceil(Math.sqrt(this._workspaces.length));
let gridHeight = Math.ceil(this._workspaces.length / gridWidth);
@@ -1037,6 +945,14 @@ Workspaces.prototype = {
}
}
}
// Now figure out their full-size coordinates
for (let w = 0; w < this._workspaces.length; w++) {
let workspace = this._workspaces[w];
workspace.fullSizeX = (workspace.gridCol - activeWorkspace.gridCol) * (global.screen_width + GRID_SPACING);
workspace.fullSizeY = (workspace.gridRow - activeWorkspace.gridRow) * (global.screen_height + GRID_SPACING);
}
},
_workspacesChanged : function() {
@@ -1086,14 +1002,6 @@ Workspaces.prototype = {
this._workspaces[w].resizeToGrid(oldScale);
}
if (newScale != oldScale) {
// The workspace scale affects window size/positioning because we clamp
// window size to a 1:1 ratio and never scale them up
let existingWorkspaces = Math.min(oldNumWorkspaces, newNumWorkspaces);
for (let w = 0; w < existingWorkspaces; w++)
this._workspaces[w].positionWindows(false);
}
if (newNumWorkspaces > oldNumWorkspaces) {
// Slide new workspaces in from offscreen
for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++)
@@ -1107,11 +1015,6 @@ Workspaces.prototype = {
// FIXME: deal with windows on the lost workspaces
}
// Reset the selection state; if we went from > 1 workspace to 1,
// this has the side effect of removing the frame border
let activeIndex = global.screen.get_active_workspace_index();
this._workspaces[activeIndex].setSelected(true);
},
_activeWorkspaceChanged : function(wm, from, to, direction) {
@@ -1120,44 +1023,15 @@ Workspaces.prototype = {
},
_addWorkspaceActor : function(workspaceNum) {
let workspace = new Workspace(workspaceNum, this.actor);
let workspace = new Workspace(workspaceNum);
this._workspaces[workspaceNum] = workspace;
this.actor.add_actor(workspace.actor);
},
_appendNewWorkspace : function() {
_appendNewWorkspace : function(actor, event) {
let global = Shell.Global.get();
global.screen.append_new_workspace(false, global.screen.get_display().get_current_time());
},
// Creates a new workspace and drops the dropActor there
_addWorkspaceAcceptDrop : function(source, dropActor, x, y, time) {
this._appendNewWorkspace();
return this._workspaces[this._workspaces.length - 1].acceptDrop(source, dropActor, x, y, time);
}
};
function AddWorkspaceButton(buttonSize, buttonX, buttonY, acceptDropCallback) {
this._init(buttonSize, buttonX, buttonY, acceptDropCallback);
}
AddWorkspaceButton.prototype = {
_init : function(buttonSize, buttonX, buttonY, acceptDropCallback) {
this.actor = new Clutter.Texture({ x: buttonX,
y: buttonY,
width: buttonSize,
height: buttonSize,
reactive: true });
this._acceptDropCallback = acceptDropCallback;
let global = Shell.Global.get();
this.actor._delegate = this;
this.actor.set_from_file(global.imagedir + 'add-workspace.svg');
},
// Draggable target interface
acceptDrop : function(source, actor, x, y, time) {
return this._acceptDropCallback(source, actor, x, y, time);
global.screen.append_new_workspace(false, event.get_time());
}
};
@@ -1167,49 +1041,28 @@ AddWorkspaceButton.prototype = {
Tweener.registerSpecialPropertyModifier("workspace_relative", _workspaceRelativeModifier, _workspaceRelativeGet);
function _workspaceRelativeModifier(workspace) {
let [startX, startY] = Main.overview.getPosition();
let overviewPosX, overviewPosY, overviewScale;
let endX, endY;
if (!workspace)
return [];
if (workspace.leavingOverview) {
let [zoomedInX, zoomedInY] = Main.overview.getZoomedInPosition();
overviewPosX = { begin: startX, end: zoomedInX };
overviewPosY = { begin: startY, end: zoomedInY };
overviewScale = { begin: Main.overview.getScale(),
end: Main.overview.getZoomedInScale() };
if (workspace.leavingOverlay) {
endX = workspace.fullSizeX;
endY = workspace.fullSizeY;
} else {
overviewPosX = { begin: startX, end: 0 };
overviewPosY = { begin: startY, end: 0 };
overviewScale = { begin: Main.overview.getScale(), end: 1 };
endX = workspace.gridX;
endY = workspace.gridY;
}
return [ { name: "x",
parameters: { workspacePos: workspace.gridX,
overviewPos: overviewPosX,
overviewScale: overviewScale } },
parameters: { begin: workspace.actor.x, end: endX,
cur: function() { return workspace.actor.x; } } },
{ name: "y",
parameters: { workspacePos: workspace.gridY,
overviewPos: overviewPosY,
overviewScale: overviewScale } }
parameters: { begin: workspace.actor.y, end: endY,
cur: function() { return workspace.actor.y; } } }
];
}
function _workspaceRelativeGet(begin, end, time, params) {
let curOverviewPos = (1 - time) * params.overviewPos.begin +
time * params.overviewPos.end;
let curOverviewScale = (1 - time) * params.overviewScale.begin +
time * params.overviewScale.end;
// Calculate the screen position of the window.
let screen = (1 - time) *
((begin + params.workspacePos) * params.overviewScale.begin +
params.overviewPos.begin) +
time *
((end + params.workspacePos) * params.overviewScale.end +
params.overviewPos.end);
// Return the workspace coordinates.
return (screen - curOverviewPos) / curOverviewScale - params.workspacePos;
return (begin + params.begin) + time * (end + params.end - (begin + params.begin)) - params.cur();
}

View File

@@ -1,19 +0,0 @@
ca
cs
da
de
es
fr
ga
gl
hu
it
ko
nb
nl
pa
pl
pt_BR
sv
tr
zh_CN

View File

@@ -1,9 +0,0 @@
data/gnome-shell.desktop.in.in
js/ui/panel.js
js/ui/dash.js
js/ui/overview.js
js/ui/runDialog.js
src/gdmuser/gdm-user.c
src/shell-global.c
src/shell-status-menu.c
src/shell-uri-util.c

View File

@@ -1 +0,0 @@
data/gnome-shell.desktop.in

189
po/ca.po
View File

@@ -1,189 +0,0 @@
# Catalan gnome-shell translation.
# Copyright (C) 2009 Siegfried-Angel Gevatter Pujals <rainct@ubuntu.com>
# This file is distributed under the same license as the gnome-shell package.
#
# Siegfried-Angel Gevatter Pujals <rainct@ubuntu.com>, 2009.
msgid ""
msgstr ""
"Project-Id-Version: HEAD\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-08-30 18:53+0200\n"
"PO-Revision-Date: 2009-08-30 18:57+0100\n"
"Last-Translator: Siegfried-Angel Gevatter Pujals <rainct@ubuntu.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: ../data/gnome-shell.desktop.in.in.h:1
msgid "GNOME Shell"
msgstr "GNOME Shell"
#: ../data/gnome-shell.desktop.in.in.h:2
msgid "Window management and application launching"
msgstr "Gestió de finestres i execució d'aplicacions"
#. left side
#: ../js/ui/panel.js:271
msgid "Activities"
msgstr "Activitats"
#. Translators: This is a time format.
#: ../js/ui/panel.js:454
msgid "%a %l:%M %p"
msgstr "%a %H:%M"
#: ../js/ui/dash.js:256
msgid "Find..."
msgstr "Cerca..."
#: ../js/ui/dash.js:374
msgid "Browse"
msgstr "Navega"
#: ../js/ui/dash.js:451
msgid "(see all)"
msgstr "(mostra tot)"
#. **** Applications ****
#: ../js/ui/dash.js:633
#: ../js/ui/dash.js:681
msgid "APPLICATIONS"
msgstr "APLICACIONS"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:653
msgid "PLACES"
msgstr "LLOCS"
#. **** Documents ****
#: ../js/ui/dash.js:660
#: ../js/ui/dash.js:692
msgid "RECENT DOCUMENTS"
msgstr "DOCUMENTS RECENTS"
#. **** Search Results ****
#: ../js/ui/dash.js:679
msgid "SEARCH RESULTS"
msgstr "RESULTATS DE LA CERCA"
#: ../js/ui/runDialog.js:82
msgid "Please enter a command:"
msgstr "Introduïu una ordre:"
#: ../src/shell-global.c:840
msgid "Less than a minute ago"
msgstr "Fa menys d'un minut"
#: ../src/shell-global.c:843
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "Fa %d minut"
msgstr[1] "Fa %d minuts"
#: ../src/shell-global.c:846
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "Fa %d hora"
msgstr[1] "Fa %d hores"
#: ../src/shell-global.c:849
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "Fa %d dia"
msgstr[1] "Fa %d dies"
#: ../src/shell-global.c:852
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
msgstr[0] "Fa %d setmana"
msgstr[1] "Fa %d setmanes"
#: ../src/shell-status-menu.c:156
msgid "Unknown"
msgstr "Desconegut"
#: ../src/shell-status-menu.c:212
#, c-format
msgid "Can't lock screen: %s"
msgstr "No es pot blocar la pantalla: %s"
#: ../src/shell-status-menu.c:227
#, c-format
msgid "Can't temporarily set screensaver to blank screen: %s"
msgstr "No es pot establir temporalment l'estalvi de pantalla a pantalla negra: %s"
#: ../src/shell-status-menu.c:351
#, c-format
msgid "Can't logout: %s"
msgstr "No es pot sortir: %s"
#: ../src/shell-status-menu.c:492
msgid "Account Information..."
msgstr "Informació del compte..."
#: ../src/shell-status-menu.c:502
msgid "Sidebar"
msgstr "Barra lateral"
#: ../src/shell-status-menu.c:510
msgid "System Preferences..."
msgstr "Preferències del sistema..."
#: ../src/shell-status-menu.c:525
msgid "Lock Screen"
msgstr "Bloca la pantalla"
#: ../src/shell-status-menu.c:535
msgid "Switch User"
msgstr "Canvia d'usuari"
#. Only show switch user if there are other users
#. Log Out
#: ../src/shell-status-menu.c:546
msgid "Log Out..."
msgstr "Surt..."
#. Shut down
#: ../src/shell-status-menu.c:557
msgid "Shut Down..."
msgstr "Atura..."
#: ../src/shell-uri-util.c:87
msgid "Home Folder"
msgstr "Carpeta d'inici"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-uri-util.c:102
msgid "File System"
msgstr "Sistema de fitxers"
#: ../src/shell-uri-util.c:248
msgid "Search"
msgstr "Cerca"
#. Translators: the first string is the name of a gvfs
#. * method, and the second string is a path. For
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-uri-util.c:298
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"
#~ msgid "Find apps or documents"
#~ msgstr "Cerca aplicacions o documents"
#~ msgid "Manager"
#~ msgstr "Gestor"
#~ msgid "The user manager object this user is controlled by."
#~ msgstr "L'objecte gestor d'usuaris que controla a aquest usuari."

189
po/cs.po
View File

@@ -1,189 +0,0 @@
# Czech translation of gnome-shell.
# Copyright (C) 2009 the author(s) of gnome-shell.
# This file is distributed under the same license as the gnome-shell package.
# Andre Klapper <ak-47@gmx.net>, 2009.
# Petr Kovar <pknbe@volny.cz>, 2009.
msgid ""
msgstr ""
"Project-Id-Version: gnome-shell\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-08-30 17:37+0200\n"
"PO-Revision-Date: 2009-08-30 17:33+0200\n"
"Last-Translator: Petr Kovar <pknbe@volny.cz>\n"
"Language-Team: Czech <gnome-cs-list@gnome.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
#: ../data/gnome-shell.desktop.in.in.h:1
msgid "GNOME Shell"
msgstr "Prostředí GNOME Shell"
#: ../data/gnome-shell.desktop.in.in.h:2
msgid "Window management and application launching"
msgstr "Správa oken a spouštění aplikací"
#. left side
#: ../js/ui/panel.js:271
msgid "Activities"
msgstr "Činnosti"
#. Translators: This is a time format.
#: ../js/ui/panel.js:454
msgid "%a %l:%M %p"
msgstr "%a, %H:%M"
#: ../js/ui/dash.js:256
msgid "Find..."
msgstr "Najít..."
#: ../js/ui/dash.js:374
msgid "Browse"
msgstr "Procházet"
#: ../js/ui/dash.js:451
msgid "(see all)"
msgstr "(zobrazit vše)"
#. **** Applications ****
#: ../js/ui/dash.js:633 ../js/ui/dash.js:681
msgid "APPLICATIONS"
msgstr "APLIKACE"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:653
msgid "PLACES"
msgstr "MÍSTA"
#. **** Documents ****
#: ../js/ui/dash.js:660 ../js/ui/dash.js:692
msgid "RECENT DOCUMENTS"
msgstr "NEDÁVNÉ DOKUMENTY"
#. **** Search Results ****
#: ../js/ui/dash.js:679
msgid "SEARCH RESULTS"
msgstr "VÝSLEDKY HLEDÁNÍ"
#: ../js/ui/runDialog.js:82
msgid "Please enter a command:"
msgstr "Zadejte prosím příkaz:"
#: ../src/shell-global.c:840
msgid "Less than a minute ago"
msgstr "Před méně než minutou"
#: ../src/shell-global.c:843
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "Před %d minutou"
msgstr[1] "Před %d minutami"
msgstr[2] "Před %d minutami"
#: ../src/shell-global.c:846
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "Před %d hodinou"
msgstr[1] "Před %d hodinami"
msgstr[2] "Před %d hodinami"
#: ../src/shell-global.c:849
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "Před %d dnem"
msgstr[1] "Před %d dny"
msgstr[2] "Před %d dny"
#: ../src/shell-global.c:852
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
msgstr[0] "Před %d týdnem"
msgstr[1] "Před %d týdny"
msgstr[2] "Před %d týdny"
#: ../src/shell-status-menu.c:156
msgid "Unknown"
msgstr "Neznámé"
#: ../src/shell-status-menu.c:212
#, c-format
msgid "Can't lock screen: %s"
msgstr "Nelze uzamknout obrazovku: %s"
#: ../src/shell-status-menu.c:227
#, c-format
msgid "Can't temporarily set screensaver to blank screen: %s"
msgstr "Šetřič obrazovky nelze dočasně nastavit na prázdnou obrazovku: %s"
#: ../src/shell-status-menu.c:351
#, c-format
msgid "Can't logout: %s"
msgstr "Nelze se odhlásit: %s"
#: ../src/shell-status-menu.c:492
msgid "Account Information..."
msgstr "Informace o účtu..."
#: ../src/shell-status-menu.c:502
msgid "Sidebar"
msgstr "Postranní lišta"
#: ../src/shell-status-menu.c:510
msgid "System Preferences..."
msgstr "Předvolby systému..."
#: ../src/shell-status-menu.c:525
msgid "Lock Screen"
msgstr "Uzamknout obrazovku"
#: ../src/shell-status-menu.c:535
msgid "Switch User"
msgstr "Přepnout uživatele"
#. Only show switch user if there are other users
#. Log Out
#: ../src/shell-status-menu.c:546
msgid "Log Out..."
msgstr "Odhlásit..."
#. Shut down
#: ../src/shell-status-menu.c:557
msgid "Shut Down..."
msgstr "Vypnout..."
#: ../src/shell-uri-util.c:87
msgid "Home Folder"
msgstr "Domovská složka"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-uri-util.c:102
msgid "File System"
msgstr "Systém souborů"
#: ../src/shell-uri-util.c:248
msgid "Search"
msgstr "Hledat"
#. Translators: the first string is the name of a gvfs
#. * method, and the second string is a path. For
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-uri-util.c:298
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"
#~ msgid "Manager"
#~ msgstr "Správce"
#~ msgid "The user manager object this user is controlled by."
#~ msgstr "Objekt správce uživatele, kterým je tento uživatel ovládán."

188
po/da.po
View File

@@ -1,188 +0,0 @@
# Danish translation of gnome-shell
# Copyright (C) 2009 gnome-shell
# This file is distributed under the same license as the gnome-shell package.
# Kris Thomsen <lakristho@gmail.com>, 2009.
#
msgid ""
msgstr ""
"Project-Id-Version: gnome-shell\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-09-04 01:26+0200\n"
"PO-Revision-Date: 2009-09-01 21:48+0200\n"
"Last-Translator: Kris Thomsen <lakristho@gmail.com>\n"
"Language-Team: Danish <dansk@dansk-gruppen.dk>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: ../data/gnome-shell.desktop.in.in.h:1
msgid "GNOME Shell"
msgstr "Skal til GNOME"
#: ../data/gnome-shell.desktop.in.in.h:2
msgid "Window management and application launching"
msgstr "Vinduehåndtering og åbning af programmer"
#. left side
#: ../js/ui/panel.js:271
msgid "Activities"
msgstr "Aktiviteter"
#. Translators: This is a time format.
#: ../js/ui/panel.js:454
msgid "%a %l:%M %p"
msgstr "%a %H:%M"
#: ../js/ui/dash.js:256
msgid "Find..."
msgstr "Find..."
#: ../js/ui/dash.js:374
msgid "Browse"
msgstr "Gennemse"
#: ../js/ui/dash.js:511
msgid "(see all)"
msgstr "(se alle)"
#. **** Applications ****
#: ../js/ui/dash.js:705 ../js/ui/dash.js:761 ../js/ui/dash.js:893
msgid "APPLICATIONS"
msgstr "PROGRAMMER"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:725
msgid "PLACES"
msgstr "STEDER"
#. **** Documents ****
#: ../js/ui/dash.js:732 ../js/ui/dash.js:773 ../js/ui/dash.js:867
msgid "RECENT DOCUMENTS"
msgstr "SENESTE DOKUMENTER"
#. **** Search Results ****
#: ../js/ui/dash.js:751 ../js/ui/dash.js:856 ../js/ui/dash.js:882
msgid "SEARCH RESULTS"
msgstr "SØGERESULTATER"
#: ../js/ui/runDialog.js:82
msgid "Please enter a command:"
msgstr "Indtast en kommando:"
#: ../src/shell-global.c:840
msgid "Less than a minute ago"
msgstr "Mindre end et minut siden"
#: ../src/shell-global.c:843
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "%d minut siden"
msgstr[1] "%d minutter siden"
#: ../src/shell-global.c:846
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "%d time siden"
msgstr[1] "%d timer siden"
#: ../src/shell-global.c:849
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "%d dag siden"
msgstr[1] "%d dage siden"
#: ../src/shell-global.c:852
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
msgstr[0] "%d uge siden"
msgstr[1] "%d uger siden"
#: ../src/shell-status-menu.c:156
msgid "Unknown"
msgstr "Ukendt"
#: ../src/shell-status-menu.c:212
#, c-format
msgid "Can't lock screen: %s"
msgstr "Kan ikke låse skærm: %s"
#: ../src/shell-status-menu.c:227
#, c-format
msgid "Can't temporarily set screensaver to blank screen: %s"
msgstr "Kan ikke midlertidigt sætte pauseskærm til blank skærm: %s"
#: ../src/shell-status-menu.c:351
#, c-format
msgid "Can't logout: %s"
msgstr "Kan ikke logge ud: %s"
#: ../src/shell-status-menu.c:492
msgid "Account Information..."
msgstr "Kontoinformation..."
#: ../src/shell-status-menu.c:502
msgid "Sidebar"
msgstr "Sidebjælke"
#: ../src/shell-status-menu.c:510
msgid "System Preferences..."
msgstr "Systemindstillinger..."
#: ../src/shell-status-menu.c:525
msgid "Lock Screen"
msgstr "Lås skærm"
#: ../src/shell-status-menu.c:535
msgid "Switch User"
msgstr "Skift bruger"
#. Only show switch user if there are other users
#. Log Out
#: ../src/shell-status-menu.c:546
msgid "Log Out..."
msgstr "Log ud..."
#. Shut down
#: ../src/shell-status-menu.c:557
msgid "Shut Down..."
msgstr "Luk ned..."
#: ../src/shell-uri-util.c:87
msgid "Home Folder"
msgstr "Hjemmemappe"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-uri-util.c:102
msgid "File System"
msgstr "Filsystem"
#: ../src/shell-uri-util.c:248
msgid "Search"
msgstr "Søg"
#. Translators: the first string is the name of a gvfs
#. * method, and the second string is a path. For
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-uri-util.c:298
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"
#~ msgid "Find apps or documents"
#~ msgstr "Find programmer eller dokumenter"
#~ msgid "Manager"
#~ msgstr "Håndtering"
#~ msgid "The user manager object this user is controlled by."
#~ msgstr "Brugerhåndteringsobjektet, denne bruger er styret af."

182
po/de.po
View File

@@ -1,182 +0,0 @@
# German gnome-shell translation.
# Copyright (C) 2009 Free Software Foundation, Inc.
# This file is distributed under the same license as the gnome-shell package.
#
# Hendrik Brandt <heb@gnome-de.org>, 2009.
# Hendrik Richter <hendrikr@gnome.org>, 2009.
#
msgid ""
msgstr ""
"Project-Id-Version: HEAD\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-08-20 16:47+0200\n"
"PO-Revision-Date: 2009-08-20 16:49+0200\n"
"Last-Translator: Hendrik Richter <hendrikr@gnome.org>\n"
"Language-Team: Deutsch <gnome-de@gnome.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: UTF-8\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: ../data/gnome-shell.desktop.in.in.h:1
msgid "GNOME Shell"
msgstr "GNOME-Shell"
#: ../data/gnome-shell.desktop.in.in.h:2
msgid "Window management and application launching"
msgstr "Fenster verwalten und Anwendungen starten"
#. left side
#: ../js/ui/panel.js:266
msgid "Activities"
msgstr "Aktivitäten"
#. Translators: This is a time format.
#: ../js/ui/panel.js:433
msgid "%a %l:%M %p"
msgstr "%a %H:%M"
#: ../js/ui/dash.js:251
msgid "Find apps or documents"
msgstr "Anwendungen oder Dokumente suchen"
#: ../js/ui/dash.js:369
msgid "Browse"
msgstr "Durchsuchen"
#. **** Applications ****
#: ../js/ui/dash.js:505 ../js/ui/dash.js:578
msgid "APPLICATIONS"
msgstr "ANWENDUNGEN"
#. **** Documents ****
#: ../js/ui/dash.js:510 ../js/ui/dash.js:605
msgid "RECENT DOCUMENTS"
msgstr "ZULETZT GEÖFFNETE DOKUMENTE"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:598
msgid "PLACES"
msgstr "ORTE"
#: ../js/ui/runDialog.js:75
msgid "Please enter a command:"
msgstr "Bitte geben Sie einen Befehl ein:"
#: ../src/gdmuser/gdm-user.c:243
msgid "Manager"
msgstr "Verwaltung"
#: ../src/gdmuser/gdm-user.c:244
msgid "The user manager object this user is controlled by."
msgstr "Das Benutzerverwaltungsobjekt welches diesen Benutzer überwacht."
#: ../src/shell-global.c:841
msgid "Less than a minute ago"
msgstr "Vor weniger als einer Minute"
#: ../src/shell-global.c:844
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "Vor %d Minute"
msgstr[1] "Vor %d Minuten"
#: ../src/shell-global.c:847
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "Vor %d Stunde"
msgstr[1] "Vor %d Stunden"
#: ../src/shell-global.c:850
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "Vor %d Tag"
msgstr[1] "Vor %d Tagen"
#: ../src/shell-global.c:853
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
msgstr[0] "Vor %d Woche"
msgstr[1] "Vor %d Wochen"
#: ../src/shell-status-menu.c:156
msgid "Unknown"
msgstr "Unbekannt"
#: ../src/shell-status-menu.c:212
#, c-format
msgid "Can't lock screen: %s"
msgstr "Bildschirm kann nicht gesperrt werden: %s"
#: ../src/shell-status-menu.c:227
#, c-format
msgid "Can't temporarily set screensaver to blank screen: %s"
msgstr ""
"Der Bildschirmschoner kann vorübergehend nicht auf einen leeren Schirm "
"gesetzt werden: %s"
#: ../src/shell-status-menu.c:351
#, c-format
msgid "Can't logout: %s"
msgstr "Abmelden ist nicht möglich: %s"
#: ../src/shell-status-menu.c:492
msgid "Account Information..."
msgstr "Benutzerinformationen …"
#: ../src/shell-status-menu.c:502
msgid "Sidebar"
msgstr "Seitenleiste"
#: ../src/shell-status-menu.c:510
msgid "System Preferences..."
msgstr "Systemeinstellungen …"
#: ../src/shell-status-menu.c:525
msgid "Lock Screen"
msgstr "Bildschirm sperren"
#: ../src/shell-status-menu.c:535
msgid "Switch User"
msgstr "Benutzer wechseln"
#. Only show switch user if there are other users
#. Log Out
#: ../src/shell-status-menu.c:546
msgid "Log Out..."
msgstr "Abmelden …"
#. Shut down
#: ../src/shell-status-menu.c:557
msgid "Shut Down..."
msgstr "Ausschalten …"
#: ../src/shell-uri-util.c:87
msgid "Home Folder"
msgstr "Persönlicher Ordner"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-uri-util.c:102
msgid "File System"
msgstr "Dateisystem"
#: ../src/shell-uri-util.c:248
msgid "Search"
msgstr "Suchen"
#. Translators: the first string is the name of a gvfs
#. * method, and the second string is a path. For
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-uri-util.c:298
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"

195
po/es.po
View File

@@ -1,195 +0,0 @@
# Spanish translation of gnome-shell.
# Copyright (C) 2009 gnome-shell's COPYRIGHT HOLDER
# This file is distributed under the same license as the gnome-shell package.
#
# Jorge González <jorgegonz@svn.gnome.org>, 2009.
msgid ""
msgstr ""
"Project-Id-Version: gnome-shell master\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&component=general\n"
"POT-Creation-Date: 2009-08-28 20:35+0000\n"
"PO-Revision-Date: 2009-08-30 11:16+0200\n"
"Last-Translator: Jorge González <jorgegonz@svn.gnome.org>\n"
"Language-Team: Español <gnome-es-list@gnome.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: ../data/gnome-shell.desktop.in.in.h:1
msgid "GNOME Shell"
msgstr "GNOME Shell"
#: ../data/gnome-shell.desktop.in.in.h:2
msgid "Window management and application launching"
msgstr "Gestión de ventanas e inicio de aplicaciones"
#. left side
#: ../js/ui/panel.js:271
msgid "Activities"
msgstr "Actividades"
#. Translators: This is a time format.
#: ../js/ui/panel.js:451
msgid "%a %l:%M %p"
msgstr "%a %H:%M"
#: ../js/ui/dash.js:256
msgid "Find..."
msgstr "Buscar…"
#: ../js/ui/dash.js:374
msgid "Browse"
msgstr "Examine"
#: ../js/ui/dash.js:451
msgid "(see all)"
msgstr "(ver todo)"
#. **** Applications ****
#: ../js/ui/dash.js:634 ../js/ui/dash.js:682
msgid "APPLICATIONS"
msgstr "APLICACIONES"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:654
msgid "PLACES"
msgstr "LUGARES"
#. **** Documents ****
#: ../js/ui/dash.js:661 ../js/ui/dash.js:694
msgid "RECENT DOCUMENTS"
msgstr "DOCUMENTOS RECIENTES"
#. **** Search Results ****
#: ../js/ui/dash.js:680
msgid "SEARCH RESULTS"
msgstr "RESULTADOS DE LA BÚSQUEDA"
#: ../js/ui/runDialog.js:82
msgid "Please enter a command:"
msgstr "Introduzca un comando:"
#: ../src/shell-global.c:840
msgid "Less than a minute ago"
msgstr "Hace menos de un minuto"
#: ../src/shell-global.c:843
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "Hace %d minuto"
msgstr[1] "Hace %d minutos"
#: ../src/shell-global.c:846
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "Hace %d hora"
msgstr[1] "Hace %d horas"
#: ../src/shell-global.c:849
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "Hace %d día"
msgstr[1] "Hace %d días"
#: ../src/shell-global.c:852
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
msgstr[0] "Hace %d semana"
msgstr[1] "Hace %d semanas"
#: ../src/shell-status-menu.c:156
msgid "Unknown"
msgstr "Desconocido"
#: ../src/shell-status-menu.c:212
#, c-format
msgid "Can't lock screen: %s"
msgstr "No se puede bloquear la pantalla: %s"
#: ../src/shell-status-menu.c:227
#, c-format
msgid "Can't temporarily set screensaver to blank screen: %s"
msgstr ""
"No se puede establecer temporalmente el salvapantallas a oscurecer pantalla: "
"%s"
#: ../src/shell-status-menu.c:351
#, c-format
msgid "Can't logout: %s"
msgstr "No se puede salir de la sesión: %s"
#: ../src/shell-status-menu.c:492
msgid "Account Information..."
msgstr "Información de la cuenta…"
#: ../src/shell-status-menu.c:502
msgid "Sidebar"
msgstr "Barra lateral"
#: ../src/shell-status-menu.c:510
msgid "System Preferences..."
msgstr "Preferencias del sistema…"
#: ../src/shell-status-menu.c:525
msgid "Lock Screen"
msgstr "Bloquear la pantalla"
#: ../src/shell-status-menu.c:535
msgid "Switch User"
msgstr "Cambiar de usuario"
#. Only show switch user if there are other users
#. Log Out
#: ../src/shell-status-menu.c:546
msgid "Log Out..."
msgstr "Salir…"
#. Shut down
#: ../src/shell-status-menu.c:557
msgid "Shut Down..."
msgstr "Apagar…"
#: ../src/shell-uri-util.c:87
msgid "Home Folder"
msgstr "Carpeta personal"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-uri-util.c:102
msgid "File System"
msgstr "Sistema de archivos"
#: ../src/shell-uri-util.c:248
msgid "Search"
msgstr "Buscar"
#. Translators: the first string is the name of a gvfs
#. * method, and the second string is a path. For
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-uri-util.c:298
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"
#~ msgid "Find apps or documents"
#~ msgstr "Encuentre aplicaciones o documentos"
#~| msgid "RECENT DOCUMENTS"
#~ msgid "DOCUMENTS"
#~ msgstr "DOCUMENTOS"
#~ msgid "Manager"
#~ msgstr "Gestor"
#~ msgid "The user manager object this user is controlled by."
#~ msgstr "El objeto de gestión de usuarios que controla a este usuario."

View File

@@ -1,21 +0,0 @@
# French translations for gnome-shell package.
# Copyright (C) 2009 THE gnome-shell'S COPYRIGHT HOLDER
# This file is distributed under the same license as the gnome-shell package.
# Colin Walters <walters@verbum.org>, 2009.
#
msgid ""
msgstr ""
"Project-Id-Version: gnome-shell 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-07-27 15:09-0400\n"
"PO-Revision-Date: 2009-07-27 15:23-0400\n"
"Last-Translator: Colin Walters <walters@verbum.org>\n"
"Language-Team: French\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: ../js/ui/panel.js:74
msgid "Activities"
msgstr "Activités"

181
po/ga.po
View File

@@ -1,181 +0,0 @@
# Irish translations for gnome-shell package.
# Copyright (C) 2009 Free Software Foundation, Inc.
# This file is distributed under the same license as the gnome-shell package.
# Seán de Búrca <leftmostcat@gmail.com>, 2009.
#
msgid ""
msgstr ""
"Project-Id-Version: gnome-shell.master\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-08-15 06:01-0600\n"
"PO-Revision-Date: 2009-08-15 06:01-0600\n"
"Last-Translator: Seán de Búrca <leftmostcat@gmail.com>\n"
"Language-Team: Irish <gaeilge-gnulinux@lists.sourceforge.net>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=5; plural=n==1 ? 0 : (n%10==1 || n%10==2) ? 1 : (n%"
"10>=3 && n%10<= 6) ? 2 : ((n%10>=7 && n%10<=9) || n==10) ? 3 : 4;\n"
#. left side
#: ../js/ui/panel.js:266
msgid "Activities"
msgstr "Gníomhartha"
#. Translators: This is a time format.
#: ../js/ui/panel.js:412
msgid "%a %l:%M %p"
msgstr "%a %l:%M %p"
#: ../js/ui/dash.js:235
msgid "Find apps or documents"
msgstr "Aimsigh feidhmchláir nó cáipéisí"
#: ../js/ui/dash.js:336
msgid "Browse"
msgstr "Brabhsáil"
#. **** Applications ****
#: ../js/ui/dash.js:472 ../js/ui/dash.js:545
msgid "APPLICATIONS"
msgstr "FEIDHMCHLÁIR"
#. **** Documents ****
#: ../js/ui/dash.js:477 ../js/ui/dash.js:570
msgid "RECENT DOCUMENTS"
msgstr "CÁIPÉISÍ IS DÉANAÍ"
#. **** Places ****
#: ../js/ui/dash.js:563
msgid "PLACES"
msgstr "ÁITEANNA"
#: ../js/ui/runDialog.js:74
msgid "Please enter a command:"
msgstr "Iontráil ordú, le do thoil:"
#: ../src/gdmuser/gdm-user.c:242
msgid "Manager"
msgstr "Bainisteoir"
#: ../src/gdmuser/gdm-user.c:243
msgid "The user manager object this user is controlled by."
msgstr "An réad bhainisteoir úsáideoirí a rialaíonn an t-úsáideoir seo."
#: ../src/shell-global.c:841
msgid "Less than a minute ago"
msgstr "Níos lú ná nóiméad ó shin"
#: ../src/shell-global.c:844
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "%d nóiméad ó shin"
msgstr[1] "%d nóiméad ó shin"
msgstr[2] "%d nóiméad ó shin"
msgstr[3] "%d nóiméad ó shin"
msgstr[4] "%d nóiméad ó shin"
#: ../src/shell-global.c:847
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "%d uair ó shin"
msgstr[1] "%d uair ó shin"
msgstr[2] "%d uaire ó shin"
msgstr[3] "%d n-uaire ó shin"
msgstr[4] "%d uair ó shin"
#: ../src/shell-global.c:850
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "%d lá ó shin"
msgstr[1] "%d lá ó shin"
msgstr[2] "%d lá ó shin"
msgstr[3] "%d lá ó shin"
msgstr[4] "%d lá ó shin"
#: ../src/shell-global.c:853
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
msgstr[0] "%d seachtain ó shin"
msgstr[1] "%d sheachtain ó shin"
msgstr[2] "%d sheachtain ó shin"
msgstr[3] "%d seachtain ó shin"
msgstr[4] "%d seachtain ó shin"
#: ../src/shell-status-menu.c:156
msgid "Unknown"
msgstr "Anaithnid"
#: ../src/shell-status-menu.c:212
#, c-format
msgid "Can't lock screen: %s"
msgstr "Ní féidir scáileán a chur faoi ghlas: %s"
#: ../src/shell-status-menu.c:227
#, c-format
msgid "Can't temporarily set screensaver to blank screen: %s"
msgstr "Ní féidir spárálaí scáileáin a shocrú chun scáileán a bhánú: %s"
#: ../src/shell-status-menu.c:351
#, c-format
msgid "Can't logout: %s"
msgstr "Ní féidir logáil amach: %s"
#: ../src/shell-status-menu.c:492
msgid "Account Information..."
msgstr "Eolas Cuntais..."
#: ../src/shell-status-menu.c:502
msgid "Sidebar"
msgstr "Barra Taoibh"
#: ../src/shell-status-menu.c:510
msgid "System Preferences..."
msgstr "Sainroghanna an Chórais..."
#: ../src/shell-status-menu.c:525
msgid "Lock Screen"
msgstr "Cuir Scáileán Faoi Ghlas"
#: ../src/shell-status-menu.c:535
msgid "Switch User"
msgstr "Athraigh Úsáideoir"
#. Only show switch user if there are other users
#. Log Out
#: ../src/shell-status-menu.c:546
msgid "Log Out..."
msgstr "Logáil Amach..."
#. Shut down
#: ../src/shell-status-menu.c:557
msgid "Shut Down..."
msgstr "Múch..."
#: ../src/shell-uri-util.c:87
msgid "Home Folder"
msgstr "Fillteán Baile"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-uri-util.c:102
msgid "File System"
msgstr "Córas Comhad"
#: ../src/shell-uri-util.c:248
msgid "Search"
msgstr "Cuardaigh"
#. Translators: the first string is the name of a gvfs
#. * method, and the second string is a path. For
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-uri-util.c:298
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"

184
po/gl.po
View File

@@ -1,184 +0,0 @@
# Galician translation for gnome-shell.
# Copyright (C) 2009 gnome-shell's COPYRIGHT HOLDER
# This file is distributed under the same license as the gnome-shell package.
# Fran Diéguez <fran.dieguez@mabishu.com>, 2009.
#
msgid ""
msgstr ""
"Project-Id-Version: gnome-shell master\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-08-22 16:39+0200\n"
"PO-Revision-Date: 2009-08-18 21:20+0100\n"
"Last-Translator: Fran Diéguez <fran.dieguez@glug.es>\n"
"Language-Team: Galician <gnome@mancomun.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: ../data/gnome-shell.desktop.in.in.h:1
msgid "GNOME Shell"
msgstr "GNOME Shell"
#: ../data/gnome-shell.desktop.in.in.h:2
msgid "Window management and application launching"
msgstr "Xestor de xanelas e lanzado de aplicativos"
#. left side
#: ../js/ui/panel.js:266
msgid "Activities"
msgstr "Actividades"
#. Translators: This is a time format.
#: ../js/ui/panel.js:433
msgid "%a %l:%M %p"
msgstr "%a %l:%M %p"
#: ../js/ui/dash.js:250
msgid "Find apps or documents"
msgstr "Atopar aplicativos ou documentos"
#: ../js/ui/dash.js:368
msgid "Browse"
msgstr "Explorar"
#. **** Applications ****
#: ../js/ui/dash.js:556 ../js/ui/dash.js:606
msgid "APPLICATIONS"
msgstr "APLICATIVOS"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:576
msgid "PLACES"
msgstr "LUGARES"
#. **** Documents ****
#: ../js/ui/dash.js:583
msgid "RECENT DOCUMENTS"
msgstr "DOCUMENTOS RECENTES"
#. **** Search Results ****
#: ../js/ui/dash.js:602
msgid "SEARCH RESULTS"
msgstr ""
#: ../js/ui/dash.js:615
#, fuzzy
msgid "DOCUMENTS"
msgstr "DOCUMENTOS RECENTES"
#: ../js/ui/runDialog.js:75
msgid "Please enter a command:"
msgstr "Insira unha orde:"
#: ../src/shell-global.c:841
msgid "Less than a minute ago"
msgstr "Menos de un minuto"
#: ../src/shell-global.c:844
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "fai %d minuto"
msgstr[1] "fai %d minutos"
#: ../src/shell-global.c:847
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "fai %d hora"
msgstr[1] "fai %d horas"
#: ../src/shell-global.c:850
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "fai %d día"
msgstr[1] "fai %d días"
#: ../src/shell-global.c:853
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
msgstr[0] "fai %d semana"
msgstr[1] "fai %d semanas"
#: ../src/shell-status-menu.c:156
msgid "Unknown"
msgstr "Descoñecido"
#: ../src/shell-status-menu.c:212
#, c-format
msgid "Can't lock screen: %s"
msgstr "Non foi posíbel bloquear a pantalla: %s"
#: ../src/shell-status-menu.c:227
#, c-format
msgid "Can't temporarily set screensaver to blank screen: %s"
msgstr ""
"Non foi posíbel establecer o salvapantallas a unha pantalla en branco: %s"
#: ../src/shell-status-menu.c:351
#, c-format
msgid "Can't logout: %s"
msgstr "Non foi posíbel pechar a sesión: %s"
#: ../src/shell-status-menu.c:492
msgid "Account Information..."
msgstr "Información da conta..."
#: ../src/shell-status-menu.c:502
msgid "Sidebar"
msgstr "Barra lateral"
#: ../src/shell-status-menu.c:510
msgid "System Preferences..."
msgstr "Preferenzas do sistema..."
#: ../src/shell-status-menu.c:525
msgid "Lock Screen"
msgstr "Bloquear pantalla"
#: ../src/shell-status-menu.c:535
msgid "Switch User"
msgstr "Cambiar de usuario"
#. Only show switch user if there are other users
#. Log Out
#: ../src/shell-status-menu.c:546
msgid "Log Out..."
msgstr "Pechar sesión..."
#. Shut down
#: ../src/shell-status-menu.c:557
msgid "Shut Down..."
msgstr "Apagar..."
#: ../src/shell-uri-util.c:87
msgid "Home Folder"
msgstr "Cartafol persoal"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-uri-util.c:102
msgid "File System"
msgstr "Sistema de ficheiros"
#: ../src/shell-uri-util.c:248
msgid "Search"
msgstr "Buscar"
#. Translators: the first string is the name of a gvfs
#. * method, and the second string is a path. For
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-uri-util.c:298
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"
#~ msgid "Manager"
#~ msgstr "Xestor"

180
po/hu.po
View File

@@ -1,180 +0,0 @@
# Hungarian translation of gnome-shell
# Copyright 2009. Free Software Foundation, Inc.
# This file is distributed under the same license as the gnome-shell package.
#
# Gabor Kelemen <kelemeng at gnome dot hu>, 2009.
msgid ""
msgstr ""
"Project-Id-Version: gnome-shell master\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-08-15 11:35+0200\n"
"PO-Revision-Date: 2009-08-15 11:35+0200\n"
"Last-Translator: Gabor Kelemen <kelemeng at gnome dot hu>\n"
"Language-Team: Hungarian <gnome at fsf dot hu>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: KBabel 1.11.4\n"
#. left side
#: ../js/ui/panel.js:266
msgid "Activities"
msgstr "Tevékenységek"
#. Translators: This is a time format.
#: ../js/ui/panel.js:412
msgid "%a %l:%M %p"
msgstr "%a., %k.%M"
#: ../js/ui/dash.js:235
msgid "Find apps or documents"
msgstr "Alkalmazások vagy dokumentumok keresése"
#: ../js/ui/dash.js:336
msgid "Browse"
msgstr "Tallózás"
#. **** Applications ****
#: ../js/ui/dash.js:472 ../js/ui/dash.js:545
msgid "APPLICATIONS"
msgstr "ALKALMAZÁSOK"
#. **** Documents ****
#: ../js/ui/dash.js:477 ../js/ui/dash.js:570
msgid "RECENT DOCUMENTS"
msgstr "LEGUTÓBBI DOKUMENTUMOK"
#. **** Places ****
#: ../js/ui/dash.js:563
msgid "PLACES"
msgstr "HELYEK"
#: ../js/ui/runDialog.js:74
msgid "Please enter a command:"
msgstr "Adjon meg egy parancsot:"
#: ../src/gdmuser/gdm-user.c:242
msgid "Manager"
msgstr "Kezelő"
#: ../src/gdmuser/gdm-user.c:243
msgid "The user manager object this user is controlled by."
msgstr "A felhasználót kezelő felhasználókezelő objektum."
#: ../src/shell-global.c:841
msgid "Less than a minute ago"
msgstr "Kevesebb, mint egy perce"
#: ../src/shell-global.c:844
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "%d perce"
msgstr[1] "%d perce"
#: ../src/shell-global.c:847
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "%d órája"
msgstr[1] "%d órája"
#: ../src/shell-global.c:850
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "%d napja"
msgstr[1] "%d napja"
#: ../src/shell-global.c:853
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
msgstr[0] "%d hete"
msgstr[1] "%d hete"
#: ../src/shell-status-menu.c:156
msgid "Unknown"
msgstr "Ismeretlen"
#: ../src/shell-status-menu.c:212
#, c-format
msgid "Can't lock screen: %s"
msgstr "Nem lehet zárolni a képernyőt: %s "
#: ../src/shell-status-menu.c:227
#, c-format
msgid "Can't temporarily set screensaver to blank screen: %s"
msgstr ""
"Átmenetileg nem lehet beállítani a képernyővédőt a képernyő elsötétítésére: %"
"s"
#: ../src/shell-status-menu.c:351
#, c-format
msgid "Can't logout: %s"
msgstr "Nem lehet kijelentkezni: %s"
#: ../src/shell-status-menu.c:492
msgid "Account Information..."
msgstr "Fiókinformációk…"
#: ../src/shell-status-menu.c:502
msgid "Sidebar"
msgstr "Oldalsáv"
#: ../src/shell-status-menu.c:510
msgid "System Preferences..."
msgstr "Rendszerbeállítások…"
#: ../src/shell-status-menu.c:525
msgid "Lock Screen"
msgstr "Képernyő zárolása"
#: ../src/shell-status-menu.c:535
msgid "Switch User"
msgstr "Felhasználóváltás"
#. Only show switch user if there are other users
#. Log Out
#: ../src/shell-status-menu.c:546
msgid "Log Out..."
msgstr "Kijelentkezés…"
#. Shut down
#: ../src/shell-status-menu.c:557
msgid "Shut Down..."
msgstr "Leállítás…"
#: ../data/gnome-shell.desktop.in.in.h:1
msgid "GNOME Shell"
msgstr "GNOME Shell"
#: ../data/gnome-shell.desktop.in.in.h:2
msgid "Window management and application launching"
msgstr "Ablakkezelés és alkalmazásindítás"
#: ../src/shell-uri-util.c:87
msgid "Home Folder"
msgstr "Saját mappa"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-uri-util.c:102
msgid "File System"
msgstr "Fájlrendszer"
#: ../src/shell-uri-util.c:248
msgid "Search"
msgstr "Oldalsáv"
#. Translators: the first string is the name of a gvfs
#. * method, and the second string is a path. For
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-uri-util.c:298
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"

184
po/it.po
View File

@@ -1,184 +0,0 @@
# Italian translations for gnome-shell package.
# Copyright (C) 2009 the gnome-shell copyright holder
# This file is distributed under the same license as the gnome-shell package.
# Milo Casagrande <milo@ubuntu.com>, 2009.
#
msgid ""
msgstr ""
"Project-Id-Version: gnome-shell\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-08-20 23:25+0200\n"
"PO-Revision-Date: 2009-08-20 23:26+0200\n"
"Last-Translator: Milo Casagrande <milo@ubuntu.com>\n"
"Language-Team: Italian <tp@lists.linux.it>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: ../data/gnome-shell.desktop.in.in.h:1
msgid "GNOME Shell"
msgstr "GNOME Shell"
#: ../data/gnome-shell.desktop.in.in.h:2
msgid "Window management and application launching"
msgstr "Gestione finestre e avvio applicazioni"
#. left side
#: ../js/ui/panel.js:266
msgid "Activities"
msgstr "Attività"
# (ndt) proviamo col k, se non funge, sappiamo il perché...
#. Translators: This is a time format.
#: ../js/ui/panel.js:433
msgid "%a %l:%M %p"
msgstr "%a %k.%M"
# (ndt) è da valutare se è troppo lunga, è in una casella di ricerca
#: ../js/ui/dash.js:251
msgid "Find apps or documents"
msgstr "Trova programmi e documenti"
#: ../js/ui/dash.js:369
msgid "Browse"
msgstr "Esplora"
#. **** Applications ****
#: ../js/ui/dash.js:505 ../js/ui/dash.js:578
msgid "APPLICATIONS"
msgstr "Applicazioni"
#. **** Documents ****
#: ../js/ui/dash.js:510 ../js/ui/dash.js:605
msgid "RECENT DOCUMENTS"
msgstr "Documenti recenti"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:598
msgid "PLACES"
msgstr "Risorse"
#: ../js/ui/runDialog.js:75
msgid "Please enter a command:"
msgstr "Inserire un comando:"
#: ../src/shell-global.c:841
msgid "Less than a minute ago"
msgstr "Meno di un minuto fa"
#: ../src/shell-global.c:844
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "%d minuto fa"
msgstr[1] "%d minuti fa"
#: ../src/shell-global.c:847
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "%d ora fa"
msgstr[1] "%d ore fa"
#: ../src/shell-global.c:850
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "%d giorno fa"
msgstr[1] "%d giorni fa"
#: ../src/shell-global.c:853
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
msgstr[0] "%d settimana fa"
msgstr[1] "%d settimane fa"
# (ndt) valutare se vada al femminile
#: ../src/shell-status-menu.c:156
msgid "Unknown"
msgstr "Sconosciuto"
#: ../src/shell-status-menu.c:212
#, c-format
msgid "Can't lock screen: %s"
msgstr "Impossibile bloccare lo schermo: %s"
#: ../src/shell-status-menu.c:227
#, c-format
msgid "Can't temporarily set screensaver to blank screen: %s"
msgstr ""
"Impossibile impostare temporaneamente il salva schermo a schermo nero: %s "
#: ../src/shell-status-menu.c:351
#, c-format
msgid "Can't logout: %s"
msgstr "Impossibile terminare la sessione: %s"
#: ../src/shell-status-menu.c:492
msgid "Account Information..."
msgstr "Informazioni account..."
#: ../src/shell-status-menu.c:502
msgid "Sidebar"
msgstr "Barra laterale"
#: ../src/shell-status-menu.c:510
msgid "System Preferences..."
msgstr "Preferenze di sistema..."
#: ../src/shell-status-menu.c:525
msgid "Lock Screen"
msgstr "Blocca schermo"
#: ../src/shell-status-menu.c:535
msgid "Switch User"
msgstr "Cambia utente"
#. Only show switch user if there are other users
#. Log Out
#: ../src/shell-status-menu.c:546
msgid "Log Out..."
msgstr "Termina sessione..."
# (ndt) da valutare... pare che ora anche Windows usi 'Arresta...'...
#. Shut down
#: ../src/shell-status-menu.c:557
msgid "Shut Down..."
msgstr "Spegni..."
#: ../src/shell-uri-util.c:87
msgid "Home Folder"
msgstr "Cartella home"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-uri-util.c:102
msgid "File System"
msgstr "File system"
#: ../src/shell-uri-util.c:248
msgid "Search"
msgstr "Cerca"
# (ndt) valutare...
#. Translators: the first string is the name of a gvfs
#. * method, and the second string is a path. For
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-uri-util.c:298
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"
# (ndt) no idea...
#~ msgid "Manager"
#~ msgstr "Manager"
# (ndt) no idea...
#~ msgid "The user manager object this user is controlled by."
#~ msgstr "L'oggetto user manager che controlla questo utente."

166
po/ko.po
View File

@@ -1,166 +0,0 @@
# gnome-shell korean translation.
# Copyright (C) 2009 THE gnome-shell'S COPYRIGHT HOLDER
# This file is distributed under the same license as the gnome-shell package.
# Young-Ho Cha <ganadist@gmail.com>, 2009.
#
msgid ""
msgstr ""
"Project-Id-Version: gnome-shell\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-08-21 10:09+0900\n"
"PO-Revision-Date: 2009-08-21 10:10+0900\n"
"Last-Translator: Young-Ho Cha <ganadist@gmail.com>\n"
"Language-Team: GNOME Korea <gnome-kr-hackers@lists.kldp.net>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#: ../data/gnome-shell.desktop.in.in.h:1
msgid "GNOME Shell"
msgstr "그놈 쉘"
#: ../data/gnome-shell.desktop.in.in.h:2
msgid "Window management and application launching"
msgstr "창 관리와 프로그램 시작"
#. left side
#: ../js/ui/panel.js:266
msgid "Activities"
msgstr ""
#. Translators: This is a time format.
#: ../js/ui/panel.js:433
msgid "%a %l:%M %p"
msgstr "%A %p %l:%M"
#: ../js/ui/dash.js:250
msgid "Find apps or documents"
msgstr "프로그램과 문서를 찾습니다"
#: ../js/ui/dash.js:368
msgid "Browse"
msgstr "찾아보기"
#. **** Applications ****
#: ../js/ui/dash.js:504 ../js/ui/dash.js:577
msgid "APPLICATIONS"
msgstr "프로그램"
#. **** Documents ****
#: ../js/ui/dash.js:509 ../js/ui/dash.js:604
msgid "RECENT DOCUMENTS"
msgstr "최근 문서"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:597
msgid "PLACES"
msgstr "위치"
#: ../js/ui/runDialog.js:75
msgid "Please enter a command:"
msgstr "명령을 입력하십시오:"
#: ../src/shell-global.c:841
msgid "Less than a minute ago"
msgstr "1분 이내"
#: ../src/shell-global.c:844
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "%d분 전"
#: ../src/shell-global.c:847
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "%d시간 전"
#: ../src/shell-global.c:850
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "%d일 전"
#: ../src/shell-global.c:853
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
msgstr[0] "%d주 전"
#: ../src/shell-status-menu.c:156
msgid "Unknown"
msgstr "알 수 없음"
#: ../src/shell-status-menu.c:212
#, c-format
msgid "Can't lock screen: %s"
msgstr "화면을 잠글 수 없습니다: %s"
#: ../src/shell-status-menu.c:227
#, c-format
msgid "Can't temporarily set screensaver to blank screen: %s"
msgstr "임시로 화면보호기를 빈 화면으로 설정할 수 없습니다: %s"
#: ../src/shell-status-menu.c:351
#, c-format
msgid "Can't logout: %s"
msgstr "로그아웃 할 수 없습니다: %s"
#: ../src/shell-status-menu.c:492
msgid "Account Information..."
msgstr "계정 정보..."
#: ../src/shell-status-menu.c:502
msgid "Sidebar"
msgstr "사이드바"
#: ../src/shell-status-menu.c:510
msgid "System Preferences..."
msgstr "시스템 설정..."
#: ../src/shell-status-menu.c:525
msgid "Lock Screen"
msgstr "화면 잠그기"
#: ../src/shell-status-menu.c:535
msgid "Switch User"
msgstr "사용자 바꾸기"
#. Only show switch user if there are other users
#. Log Out
#: ../src/shell-status-menu.c:546
msgid "Log Out..."
msgstr "로그아웃..."
#. Shut down
#: ../src/shell-status-menu.c:557
msgid "Shut Down..."
msgstr "컴퓨터 끄기..."
#: ../src/shell-uri-util.c:87
msgid "Home Folder"
msgstr "내 폴더"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-uri-util.c:102
msgid "File System"
msgstr "파일시스템"
#: ../src/shell-uri-util.c:248
msgid "Search"
msgstr "찾기"
#. Translators: the first string is the name of a gvfs
#. * method, and the second string is a path. For
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-uri-util.c:298
#, c-format
msgid "%1$s: %2$s"
msgstr "%s: %s"

170
po/nb.po
View File

@@ -1,170 +0,0 @@
# Norwegian bokmål translation of gnome-shell.
# Copyright (C) 2009 THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the gnome-shell package.
# Kjartan Maraas <kmaraas@broadpark.no>, 2009.
#
msgid ""
msgstr ""
"Project-Id-Version: gnome-shell 0.4\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-08-21 12:36+0200\n"
"PO-Revision-Date: 2009-08-21 12:44+0200\n"
"Last-Translator: Kjartan Maraas <kmaraas@broadpark.no>\n"
"Language-Team: Norwegian bokmål <i18n-nb@lister.ping.uio.no>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: ../data/gnome-shell.desktop.in.in.h:1
msgid "GNOME Shell"
msgstr "GNOME Shell"
#: ../data/gnome-shell.desktop.in.in.h:2
msgid "Window management and application launching"
msgstr "Vindushåndtering og oppstart av programmer"
#. left side
#: ../js/ui/panel.js:266
msgid "Activities"
msgstr "Aktiviteter"
#. Translators: This is a time format.
#: ../js/ui/panel.js:433
msgid "%a %l:%M %p"
msgstr "%a %l:%M"
#: ../js/ui/dash.js:250
msgid "Find apps or documents"
msgstr "Finn programmer eller dokumenter"
#: ../js/ui/dash.js:368
msgid "Browse"
msgstr "Bla gjennom"
#. **** Applications ****
#: ../js/ui/dash.js:504 ../js/ui/dash.js:577
msgid "APPLICATIONS"
msgstr "PROGRAMMER"
#. **** Documents ****
#: ../js/ui/dash.js:509 ../js/ui/dash.js:604
msgid "RECENT DOCUMENTS"
msgstr "SISTE DOKUMENTER"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:597
msgid "PLACES"
msgstr "STEDER"
#: ../js/ui/runDialog.js:75
msgid "Please enter a command:"
msgstr "Oppgi en kommando:"
#: ../src/shell-global.c:841
msgid "Less than a minute ago"
msgstr "Mindre enn ett minutt siden"
#: ../src/shell-global.c:844
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "%d minutt siden"
msgstr[1] "%d minutter siden"
#: ../src/shell-global.c:847
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "%d time siden"
msgstr[1] "%d timer siden"
#: ../src/shell-global.c:850
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "%d dag siden"
msgstr[1] "%d dager siden"
#: ../src/shell-global.c:853
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
msgstr[0] "%d uke siden"
msgstr[1] "%d uker siden"
#: ../src/shell-status-menu.c:156
msgid "Unknown"
msgstr "Ukjent"
#: ../src/shell-status-menu.c:212
#, c-format
msgid "Can't lock screen: %s"
msgstr "Kan ikke låse skjermen: %s"
#: ../src/shell-status-menu.c:227
#, c-format
msgid "Can't temporarily set screensaver to blank screen: %s"
msgstr "Kan ikke sette skjermsparer midlertidig til blank skjerm: %s"
#: ../src/shell-status-menu.c:351
#, c-format
msgid "Can't logout: %s"
msgstr "Kan ikke logge ut: %s"
#: ../src/shell-status-menu.c:492
msgid "Account Information..."
msgstr "Kontoinformasjon..."
#: ../src/shell-status-menu.c:502
msgid "Sidebar"
msgstr "Sidelinje"
#: ../src/shell-status-menu.c:510
msgid "System Preferences..."
msgstr "Brukervalg for systemet..."
#: ../src/shell-status-menu.c:525
msgid "Lock Screen"
msgstr "Lås skjerm"
#: ../src/shell-status-menu.c:535
msgid "Switch User"
msgstr "Bytt bruker"
#. Only show switch user if there are other users
#. Log Out
#: ../src/shell-status-menu.c:546
msgid "Log Out..."
msgstr "Logg ut..."
#. Shut down
#: ../src/shell-status-menu.c:557
msgid "Shut Down..."
msgstr "Slå av..."
#: ../src/shell-uri-util.c:87
msgid "Home Folder"
msgstr "Hjemmemappe"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-uri-util.c:102
msgid "File System"
msgstr "Filsystem"
#: ../src/shell-uri-util.c:248
msgid "Search"
msgstr "Søk"
#. Translators: the first string is the name of a gvfs
#. * method, and the second string is a path. For
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-uri-util.c:298
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"

179
po/nl.po
View File

@@ -1,179 +0,0 @@
# Dutch translation for gnome-shell
#
# This file is distributed under the same license as the gnome-shell package.
#
# Sander Dijkhuis <sander.dijkhuis@gmail.com>, 2009.
msgid ""
msgstr ""
"Project-Id-Version: gnome-shell master\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-08-29 14:37+0200\n"
"PO-Revision-Date: 2009-08-29 15:10+0200\n"
"Last-Translator: Sander Dijkhuis <sander.dijkhuis@gmail.com>\n"
"Language-Team: Dutch <vertaling@vrijschrift.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: ../data/gnome-shell.desktop.in.in.h:1
msgid "GNOME Shell"
msgstr "GNOME Shell"
#: ../data/gnome-shell.desktop.in.in.h:2
msgid "Window management and application launching"
msgstr "Vensterbeheer en toepassingen starten"
#. left side
#: ../js/ui/panel.js:271
msgid "Activities"
msgstr "Activiteiten"
#. Translators: This is a time format.
#: ../js/ui/panel.js:451
msgid "%a %l:%M %p"
msgstr "%a %k:%M"
#: ../js/ui/dash.js:256
msgid "Find..."
msgstr "Zoeken…"
#: ../js/ui/dash.js:374
msgid "Browse"
msgstr "Bladeren"
#: ../js/ui/dash.js:451
msgid "(see all)"
msgstr "(alles tonen)"
#. **** Applications ****
#: ../js/ui/dash.js:634 ../js/ui/dash.js:682
msgid "APPLICATIONS"
msgstr "TOEPASSINGEN"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:654
msgid "PLACES"
msgstr "LOCATIES"
#. **** Documents ****
#: ../js/ui/dash.js:661 ../js/ui/dash.js:694
msgid "RECENT DOCUMENTS"
msgstr "RECENTE DOCUMENTEN"
#. **** Search Results ****
#: ../js/ui/dash.js:680
msgid "SEARCH RESULTS"
msgstr "ZOEKRESULTATEN"
#: ../js/ui/runDialog.js:82
msgid "Please enter a command:"
msgstr "Voer een opdracht in:"
#: ../src/shell-global.c:840
msgid "Less than a minute ago"
msgstr "Korter dan een minuut geleden"
#: ../src/shell-global.c:843
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "%d minuut geleden"
msgstr[1] "%d minuten geleden"
#: ../src/shell-global.c:846
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "%d uur geleden"
msgstr[1] "%d uur geleden"
#: ../src/shell-global.c:849
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "%d dag geleden"
msgstr[1] "%d dagen geleden"
#: ../src/shell-global.c:852
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
msgstr[0] "%d week geleden"
msgstr[1] "%d weken geleden"
#: ../src/shell-status-menu.c:156
msgid "Unknown"
msgstr "Onbekend"
#: ../src/shell-status-menu.c:212
#, c-format
msgid "Can't lock screen: %s"
msgstr "Kan het scherm niet vergrendelen: %s"
#: ../src/shell-status-menu.c:227
#, c-format
msgid "Can't temporarily set screensaver to blank screen: %s"
msgstr "Kan de schermbeveiliging niet tijdelijk als zwart scherm instellen: %s"
#: ../src/shell-status-menu.c:351
#, c-format
msgid "Can't logout: %s"
msgstr "Kan niet afmelden: %s"
#: ../src/shell-status-menu.c:492
msgid "Account Information..."
msgstr "Account-informatie…"
#: ../src/shell-status-menu.c:502
msgid "Sidebar"
msgstr "Zijbalk"
#: ../src/shell-status-menu.c:510
msgid "System Preferences..."
msgstr "Systeemvoorkeuren…"
#: ../src/shell-status-menu.c:525
msgid "Lock Screen"
msgstr "Scherm vergrendelen"
#: ../src/shell-status-menu.c:535
msgid "Switch User"
msgstr "Gebruiker wisselen"
#. Only show switch user if there are other users
#. Log Out
#: ../src/shell-status-menu.c:546
msgid "Log Out..."
msgstr "Afmelden…"
#. Shut down
#: ../src/shell-status-menu.c:557
msgid "Shut Down..."
msgstr "Afsluiten…"
#: ../src/shell-uri-util.c:87
msgid "Home Folder"
msgstr "Persoonlijke map"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-uri-util.c:102
msgid "File System"
msgstr "Bestandssysteem"
#: ../src/shell-uri-util.c:248
msgid "Search"
msgstr "Zoeken"
#. Translators: the first string is the name of a gvfs
#. * method, and the second string is a path. For
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-uri-util.c:298
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"

182
po/pa.po
View File

@@ -1,182 +0,0 @@
# Punjabi translation for gnome-shell.
# Copyright (C) 2009 gnome-shell's COPYRIGHT HOLDER
# This file is distributed under the same license as the gnome-shell package.
#
# A S Alam <aalam@users.sf.net>, 2009.
msgid ""
msgstr ""
"Project-Id-Version: gnome-shell master\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug."
"cgi?product=gnome-shell&component=general\n"
"POT-Creation-Date: 2009-08-31 22:31+0000\n"
"PO-Revision-Date: 2009-09-01 06:51+0530\n"
"Last-Translator: A S Alam <aalam@users.sf.net>\n"
"Language-Team: Punjabi/Panjabi <punjabi-users@lists.sf.net>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Lokalize 1.0\n"
#: ../data/gnome-shell.desktop.in.in.h:1
msgid "GNOME Shell"
msgstr "ਗਨੋਮ ਸ਼ੈਲ"
#: ../data/gnome-shell.desktop.in.in.h:2
msgid "Window management and application launching"
msgstr "ਵਿੰਡੋ ਪਰਬੰਧ ਅਤੇ ਐਪਲੀਕੇਸ਼ਨ ਚਲਾਓ"
#. left side
#: ../js/ui/panel.js:271
msgid "Activities"
msgstr "ਸਰਗਰਮੀਆਂ"
#. Translators: This is a time format.
#: ../js/ui/panel.js:454
msgid "%a %l:%M %p"
msgstr "%a %l:%M %p"
#: ../js/ui/dash.js:256
msgid "Find..."
msgstr "ਖੋਜ..."
#: ../js/ui/dash.js:374
msgid "Browse"
msgstr "ਝਲਕ"
#: ../js/ui/dash.js:451
msgid "(see all)"
msgstr "(ਸਭ ਵੇਖੋ)"
#. **** Applications ****
#: ../js/ui/dash.js:633 ../js/ui/dash.js:681
msgid "APPLICATIONS"
msgstr "ਐਪਲੀਕੇਸ਼ਨ"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:653
msgid "PLACES"
msgstr "ਥਾਵਾਂ"
#. **** Documents ****
#: ../js/ui/dash.js:660 ../js/ui/dash.js:692
msgid "RECENT DOCUMENTS"
msgstr "ਤਾਜ਼ਾ ਡੌਕੂਮੈਂਟ"
#. **** Search Results ****
#: ../js/ui/dash.js:679
msgid "SEARCH RESULTS"
msgstr "ਖੋਜ ਨਤੀਜੇ"
#: ../js/ui/runDialog.js:82
msgid "Please enter a command:"
msgstr "ਕਮਾਂਡ ਦਿਓ ਜੀ:"
#: ../src/shell-global.c:840
msgid "Less than a minute ago"
msgstr "ਇੱਕ ਮਿੰਟ ਤੋਂ ਘੱਟ ਚਿਰ ਪਹਿਲਾਂ"
#: ../src/shell-global.c:843
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "%d ਮਿੰਟ ਪਹਿਲਾਂ"
msgstr[1] "%d ਮਿੰਟ ਪਹਿਲਾਂ"
#: ../src/shell-global.c:846
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "%d ਘੰਟਾ ਪਹਿਲਾਂ"
msgstr[1] "%d ਘੰਟੇ ਪਹਿਲਾਂ"
#: ../src/shell-global.c:849
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "%d ਦਿਨ ਪਹਿਲਾਂ"
msgstr[1] "%d ਦਿਨ ਪਹਿਲਾਂ"
#: ../src/shell-global.c:852
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
msgstr[0] "%d ਹਫ਼ਤਾ ਪਹਿਲਾਂ"
msgstr[1] "%d ਹਫ਼ਤੇ ਪਹਿਲਾਂ"
#: ../src/shell-status-menu.c:156
msgid "Unknown"
msgstr "ਅਣਜਾਣ"
#: ../src/shell-status-menu.c:212
#, c-format
msgid "Can't lock screen: %s"
msgstr "ਸਕਰੀਨ ਲਾਕ ਨਹੀਂ ਹੋ ਸਕਦੀ: %s"
#: ../src/shell-status-menu.c:227
#, c-format
msgid "Can't temporarily set screensaver to blank screen: %s"
msgstr "ਸਕਰੀਨ ਬੰਦ ਕਰਨ ਲਈ ਆਰਜ਼ੀ ਰੂਪ ਵਿੱਚ ਸਕਰੀਨ-ਸੇਵਰ ਨਹੀਂ ਸੈੱਟ ਕੀਤਾ ਜਾ ਸਕਦਾ: %s"
#: ../src/shell-status-menu.c:351
#, c-format
msgid "Can't logout: %s"
msgstr "ਲਾਗਆਉਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ: %s"
#: ../src/shell-status-menu.c:492
msgid "Account Information..."
msgstr "ਅਕਾਊਂਟ ਜਾਣਕਾਰੀ..."
#: ../src/shell-status-menu.c:502
msgid "Sidebar"
msgstr "ਬਾਹੀ"
#: ../src/shell-status-menu.c:510
msgid "System Preferences..."
msgstr "ਸਿਸਟਮ ਪਸੰਦ..."
#: ../src/shell-status-menu.c:525
msgid "Lock Screen"
msgstr "ਸਕਰੀਨ ਲਾਕ ਕਰੋ"
#: ../src/shell-status-menu.c:535
msgid "Switch User"
msgstr "ਯੂਜ਼ਰ ਬਦਲੋ"
#. Only show switch user if there are other users
#. Log Out
#: ../src/shell-status-menu.c:546
msgid "Log Out..."
msgstr "ਲਾਗਆਉਟ..."
#. Shut down
#: ../src/shell-status-menu.c:557
msgid "Shut Down..."
msgstr "ਬੰਦ ਕਰੋ..."
#: ../src/shell-uri-util.c:87
msgid "Home Folder"
msgstr "ਘਰ ਫੋਲਡਰ"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-uri-util.c:102
msgid "File System"
msgstr "ਫਾਇਲ ਸਿਸਟਮ"
#: ../src/shell-uri-util.c:248
msgid "Search"
msgstr "ਖੋਜ"
#. Translators: the first string is the name of a gvfs
#. * method, and the second string is a path. For
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-uri-util.c:298
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"

187
po/pl.po
View File

@@ -1,187 +0,0 @@
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
# Aviary.pl
# Jeśli masz jakiekolwiek uwagi odnoszące się do tłumaczenia lub chcesz
# pomóc w jego rozwijaniu i pielęgnowaniu, napisz do nas:
# gnomepl@aviary.pl
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
msgid ""
msgstr ""
"Project-Id-Version: gnome-shell\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-08-18 23:25+0200\n"
"PO-Revision-Date: 2009-08-18 23:23+0100\n"
"Last-Translator: Tomasz Dominikowski <dominikowski@gmail.com>\n"
"Language-Team: Polish <gnomepl@aviary.pl>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
"|| n%100>=20) ? 1 : 2);\n"
"X-Poedit-Language: Polish\n"
"X-Poedit-Country: Poland\n"
#: ../data/gnome-shell.desktop.in.in.h:1
msgid "GNOME Shell"
msgstr "Powłoka środowiska GNOME"
#: ../data/gnome-shell.desktop.in.in.h:2
msgid "Window management and application launching"
msgstr "Zarządzanie oknami i uruchamianiem programów"
#. left side
#: ../js/ui/panel.js:266
msgid "Activities"
msgstr "Czynności"
#. Translators: This is a time format.
#: ../js/ui/panel.js:433
msgid "%a %l:%M %p"
msgstr "%a, %H:%M"
#: ../js/ui/dash.js:251
msgid "Find apps or documents"
msgstr "Wyszukuje programy lub dokumenty"
#: ../js/ui/dash.js:369
msgid "Browse"
msgstr "Przeglądaj"
#. **** Applications ****
#: ../js/ui/dash.js:505 ../js/ui/dash.js:578
msgid "APPLICATIONS"
msgstr "Programy"
#. **** Documents ****
#: ../js/ui/dash.js:510 ../js/ui/dash.js:605
msgid "RECENT DOCUMENTS"
msgstr "Ostatnie dokumenty"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:598
msgid "PLACES"
msgstr "Miejsca"
#: ../js/ui/runDialog.js:74
msgid "Please enter a command:"
msgstr "Proszę wprowadzić polecenie:"
#: ../src/gdmuser/gdm-user.c:243
msgid "Manager"
msgstr "Menedżer"
#: ../src/gdmuser/gdm-user.c:244
msgid "The user manager object this user is controlled by."
msgstr "Obiekt menedżera użytkowników, który steruje tym użytkownikiem."
#: ../src/shell-global.c:841
msgid "Less than a minute ago"
msgstr "Mniej niż minutę temu"
#: ../src/shell-global.c:844
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "%d minuta temu"
msgstr[1] "%d minuty temu"
msgstr[2] "%d minut temu"
#: ../src/shell-global.c:847
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "%d godzina temu"
msgstr[1] "%d godziny temu"
msgstr[2] "%d godzin temu"
#: ../src/shell-global.c:850
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "%d dzień temu"
msgstr[1] "%d dni temu"
msgstr[2] "%d dni temu"
#: ../src/shell-global.c:853
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
msgstr[0] "%d tydzień temu"
msgstr[1] "%d tygodnie temu"
msgstr[2] "%d tygodni temu"
#: ../src/shell-status-menu.c:156
msgid "Unknown"
msgstr "Nieznane"
#: ../src/shell-status-menu.c:212
#, c-format
msgid "Can't lock screen: %s"
msgstr "Nie można zablokować ekranu: %s"
#: ../src/shell-status-menu.c:227
#, c-format
msgid "Can't temporarily set screensaver to blank screen: %s"
msgstr ""
"Nie można tymczasowo ustawić wygaszacza ekranu na wygaszenie ekranu: %s"
#: ../src/shell-status-menu.c:351
#, c-format
msgid "Can't logout: %s"
msgstr "Nie można się wylogować: %s"
#: ../src/shell-status-menu.c:492
msgid "Account Information..."
msgstr "Informacje o koncie..."
#: ../src/shell-status-menu.c:502
msgid "Sidebar"
msgstr "Panel boczny"
#: ../src/shell-status-menu.c:510
msgid "System Preferences..."
msgstr "Preferencje systemu..."
#: ../src/shell-status-menu.c:525
msgid "Lock Screen"
msgstr "Zablokuj ekran"
#: ../src/shell-status-menu.c:535
msgid "Switch User"
msgstr "Przełącz użytkownika"
#. Only show switch user if there are other users
#. Log Out
#: ../src/shell-status-menu.c:546
msgid "Log Out..."
msgstr "Wyloguj się..."
#. Shut down
#: ../src/shell-status-menu.c:557
msgid "Shut Down..."
msgstr "Wyłącz komputer..."
#: ../src/shell-uri-util.c:87
msgid "Home Folder"
msgstr "Katalog domowy"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-uri-util.c:102
msgid "File System"
msgstr "System plików"
#: ../src/shell-uri-util.c:248
msgid "Search"
msgstr "Wyszukaj"
#. Translators: the first string is the name of a gvfs
#. * method, and the second string is a path. For
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-uri-util.c:298
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"

View File

@@ -1,144 +0,0 @@
# Portuguese translations for gnome-shell package.
# Copyright (C) 2009 THE gnome-shell'S COPYRIGHT HOLDER
# This file is distributed under the same license as the gnome-shell package.
# Og Maciel <ogmaciel@gnome.org>, 2009.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-08-14 17:53-0400\n"
"PO-Revision-Date: 2009-08-14 17:53-0400\n"
"Last-Translator: Og Maciel <ogmaciel@gnome.org>\n"
"Language-Team: Brazilian Portuguese <gnome-pt_br-list@gnome.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#. left side
#: ../js/ui/panel.js:266
msgid "Activities"
msgstr "Atividades"
#. Translators: This is a time format.
#: ../js/ui/panel.js:412
msgid "%a %l:%M %p"
msgstr "%a %l:%M %p"
#: ../js/ui/dash.js:235
msgid "Find apps or documents"
msgstr "Localizar aplicativos ou documentos"
#: ../js/ui/dash.js:336
msgid "Browse"
msgstr "Navegar"
#. **** Applications ****
#: ../js/ui/dash.js:472 ../js/ui/dash.js:545
msgid "APPLICATIONS"
msgstr "APLICATIVOS"
#. **** Documents ****
#: ../js/ui/dash.js:477 ../js/ui/dash.js:570
msgid "RECENT DOCUMENTS"
msgstr "DOCUMENTOS RECENTES"
#. **** Places ****
#: ../js/ui/dash.js:563
msgid "PLACES"
msgstr "LOCAIS"
#: ../js/ui/runDialog.js:74
msgid "Please enter a command:"
msgstr "Por favor digite um comando:"
#: ../src/gdmuser/gdm-user.c:242
msgid "Manager"
msgstr "Gerenciador"
#: ../src/gdmuser/gdm-user.c:243
msgid "The user manager object this user is controlled by."
msgstr "O objeto gerenciador de usuários que controla este usuário."
#: ../src/shell-global.c:841
msgid "Less than a minute ago"
msgstr "Menos de um minuto atrás"
#: ../src/shell-global.c:844
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "%d minuto atrás"
msgstr[1] "%d minutos atrás"
#: ../src/shell-global.c:847
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "%d hora atrás"
msgstr[1] "%d horas atrás"
#: ../src/shell-global.c:850
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "%d dia atrás"
msgstr[1] "%d dias atrás"
#: ../src/shell-global.c:853
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
msgstr[0] "%d semana atrás"
msgstr[1] "%d semanas atrás"
#: ../src/shell-status-menu.c:156
msgid "Unknown"
msgstr "Desconhecido"
#: ../src/shell-status-menu.c:212
#, c-format
msgid "Can't lock screen: %s"
msgstr "Não foi possível travar a tela: %s"
#: ../src/shell-status-menu.c:227
#, c-format
msgid "Can't temporarily set screensaver to blank screen: %s"
msgstr "Não foi possível definir a proteção de tela para uma tela vazia: %s"
#: ../src/shell-status-menu.c:351
#, c-format
msgid "Can't logout: %s"
msgstr "Não foi possível encerrar a sessão: %s"
#: ../src/shell-status-menu.c:492
msgid "Account Information..."
msgstr "Informação da conta..."
#: ../src/shell-status-menu.c:502
msgid "Sidebar"
msgstr "Barra lateral"
#: ../src/shell-status-menu.c:510
msgid "System Preferences..."
msgstr "Preferências do sistema..."
#: ../src/shell-status-menu.c:525
msgid "Lock Screen"
msgstr "Travar a tela"
#: ../src/shell-status-menu.c:535
msgid "Switch User"
msgstr "Alternar usuário"
#. Only show switch user if there are other users
#. Log Out
#: ../src/shell-status-menu.c:546
msgid "Log Out..."
msgstr "Encerrar sessão..."
#. Shut down
#: ../src/shell-status-menu.c:557
msgid "Shut Down..."
msgstr "Desligar..."

191
po/sv.po
View File

@@ -1,191 +0,0 @@
# Swedish translation for gnome-shell.
# Copyright (C) 2009 Free Software Foundation, Inc.
# This file is distributed under the same license as the gnome-shell package.
# Daniel Nylander <po@danielnylander.se>, 2009.
#
msgid ""
msgstr ""
"Project-Id-Version: gnome-shell\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-09-01 00:31+0200\n"
"PO-Revision-Date: 2009-09-01 00:31+0100\n"
"Last-Translator: Daniel Nylander <po@danielnylander.se>\n"
"Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
#: ../data/gnome-shell.desktop.in.in.h:1
msgid "GNOME Shell"
msgstr "GNOME-skal"
#: ../data/gnome-shell.desktop.in.in.h:2
msgid "Window management and application launching"
msgstr "Fönsterhantering och programstarter"
#. left side
#: ../js/ui/panel.js:271
msgid "Activities"
msgstr "Aktiviteter"
#. Translators: This is a time format.
#: ../js/ui/panel.js:454
msgid "%a %l:%M %p"
msgstr "%a %H.%M"
#: ../js/ui/dash.js:256
msgid "Find..."
msgstr "Sök..."
#: ../js/ui/dash.js:374
msgid "Browse"
msgstr "Bläddra"
#: ../js/ui/dash.js:451
msgid "(see all)"
msgstr "(se alla)"
#. **** Applications ****
#: ../js/ui/dash.js:633
#: ../js/ui/dash.js:681
msgid "APPLICATIONS"
msgstr "PROGRAM"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:653
msgid "PLACES"
msgstr "PLATSER"
#. **** Documents ****
#: ../js/ui/dash.js:660
#: ../js/ui/dash.js:692
msgid "RECENT DOCUMENTS"
msgstr "SENASTE DOKUMENT"
#. **** Search Results ****
#: ../js/ui/dash.js:679
msgid "SEARCH RESULTS"
msgstr "SÖKRESULTAT"
#: ../js/ui/runDialog.js:82
msgid "Please enter a command:"
msgstr "Ange ett kommando:"
#: ../src/shell-global.c:840
msgid "Less than a minute ago"
msgstr "Mindre än en minut sedan"
#: ../src/shell-global.c:843
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "%d minut sedan"
msgstr[1] "%d minuter sedan"
#: ../src/shell-global.c:846
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "%d timme sedan"
msgstr[1] "%d timmar sedan"
#: ../src/shell-global.c:849
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "%d dag sedan"
msgstr[1] "%d dagar sedan"
#: ../src/shell-global.c:852
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
msgstr[0] "%d vecka sedan"
msgstr[1] "%d veckor sedan"
#: ../src/shell-status-menu.c:156
msgid "Unknown"
msgstr "Okänt"
#: ../src/shell-status-menu.c:212
#, c-format
msgid "Can't lock screen: %s"
msgstr "Kan inte låsa skärmen: %s"
#: ../src/shell-status-menu.c:227
#, c-format
msgid "Can't temporarily set screensaver to blank screen: %s"
msgstr "Kan inte temporärt ställa in skärmsläckaren till blank skärm: %s"
#: ../src/shell-status-menu.c:351
#, c-format
msgid "Can't logout: %s"
msgstr "Kan inte logga ut: %s"
#: ../src/shell-status-menu.c:492
msgid "Account Information..."
msgstr "Kontoinformation..."
#: ../src/shell-status-menu.c:502
msgid "Sidebar"
msgstr "Sidopanel"
#: ../src/shell-status-menu.c:510
msgid "System Preferences..."
msgstr "Systeminställningar..."
#: ../src/shell-status-menu.c:525
msgid "Lock Screen"
msgstr "Lås skärmen"
#: ../src/shell-status-menu.c:535
msgid "Switch User"
msgstr "Växla användare"
#. Only show switch user if there are other users
#. Log Out
#: ../src/shell-status-menu.c:546
msgid "Log Out..."
msgstr "Logga ut..."
#. Shut down
#: ../src/shell-status-menu.c:557
msgid "Shut Down..."
msgstr "Stäng av..."
#: ../src/shell-uri-util.c:87
msgid "Home Folder"
msgstr "Hemmapp"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-uri-util.c:102
msgid "File System"
msgstr "Filsystem"
#: ../src/shell-uri-util.c:248
msgid "Search"
msgstr "Sök"
#. Translators: the first string is the name of a gvfs
#. * method, and the second string is a path. For
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-uri-util.c:298
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"
#~ msgid "Find apps or documents"
#~ msgstr "Hitta program eller dokument"
#~ msgid "DOCUMENTS"
#~ msgstr "DOKUMENT"
#~ msgid "Manager"
#~ msgstr "Hanterare"
#~ msgid "The user manager object this user is controlled by."
#~ msgstr "Användarhanteringsobjektet som denna användare styrs av."

166
po/tr.po
View File

@@ -1,166 +0,0 @@
# gnome-shell
# Copyright (C) 2009
# This file is distributed under the same license as the gnome-shell package.
#
# Baris Cicek <baris@teamforce.name.tr>, 2009.
msgid ""
msgstr ""
"Project-Id-Version: gnome-shell\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-08-21 00:41+0300\n"
"PO-Revision-Date: 2009-08-21 00:40+0300\n"
"Last-Translator: Baris Cicek <baris@teamforce.name.tr>\n"
"Language-Team: Turkish <gnome-turk@gnome.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#: ../data/gnome-shell.desktop.in.in.h:1
msgid "GNOME Shell"
msgstr "GNOME Kabuğu"
#: ../data/gnome-shell.desktop.in.in.h:2
msgid "Window management and application launching"
msgstr "Pencere yönetimi ve uygulama başlatma"
#. left side
#: ../js/ui/panel.js:266
msgid "Activities"
msgstr "Etkinlikler"
#. Translators: This is a time format.
#: ../js/ui/panel.js:433
msgid "%a %l:%M %p"
msgstr "%a %H:%M"
#: ../js/ui/dash.js:251
msgid "Find apps or documents"
msgstr "Uygulamalar ve belgeler bul"
#: ../js/ui/dash.js:369
msgid "Browse"
msgstr "Göz at"
#. **** Applications ****
#: ../js/ui/dash.js:505 ../js/ui/dash.js:578
msgid "APPLICATIONS"
msgstr "UYGULAMALAR"
#. **** Documents ****
#: ../js/ui/dash.js:510 ../js/ui/dash.js:605
msgid "RECENT DOCUMENTS"
msgstr "SON ERİŞİLEN BELGELER"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:598
msgid "PLACES"
msgstr "YERLER"
#: ../js/ui/runDialog.js:75
msgid "Please enter a command:"
msgstr "Lütfen bir komut girin:"
#: ../src/shell-global.c:841
msgid "Less than a minute ago"
msgstr "Bir dakikadan daha az süre önce"
#: ../src/shell-global.c:844
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "%d dakika önce"
#: ../src/shell-global.c:847
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "%d saat önce"
#: ../src/shell-global.c:850
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "%d gün önce"
#: ../src/shell-global.c:853
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
msgstr[0] "%d hafta önce"
#: ../src/shell-status-menu.c:156
msgid "Unknown"
msgstr "Bilinmeyen"
#: ../src/shell-status-menu.c:212
#, c-format
msgid "Can't lock screen: %s"
msgstr "Ekran kilitlenemedi: %s"
#: ../src/shell-status-menu.c:227
#, c-format
msgid "Can't temporarily set screensaver to blank screen: %s"
msgstr "Ekran koruyucu geçici olarak boş ekran olarak atanamadı: %s"
#: ../src/shell-status-menu.c:351
#, c-format
msgid "Can't logout: %s"
msgstr "Çıkış yapılamadı: %s"
#: ../src/shell-status-menu.c:492
msgid "Account Information..."
msgstr "Hesap Bilgisi..."
#: ../src/shell-status-menu.c:502
msgid "Sidebar"
msgstr "Kenar Çubuğu"
#: ../src/shell-status-menu.c:510
msgid "System Preferences..."
msgstr "Sistem Tercihleri..."
#: ../src/shell-status-menu.c:525
msgid "Lock Screen"
msgstr "Ekrani Kilitle"
#: ../src/shell-status-menu.c:535
msgid "Switch User"
msgstr "Kullanıcı Değiştir"
#. Only show switch user if there are other users
#. Log Out
#: ../src/shell-status-menu.c:546
msgid "Log Out..."
msgstr "Çıkış..."
#. Shut down
#: ../src/shell-status-menu.c:557
msgid "Shut Down..."
msgstr "Kapat..."
#: ../src/shell-uri-util.c:87
msgid "Home Folder"
msgstr "Ev Klasörü"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-uri-util.c:102
msgid "File System"
msgstr "Dosya Sistemi"
#: ../src/shell-uri-util.c:248
msgid "Search"
msgstr "Arama"
#. Translators: the first string is the name of a gvfs
#. * method, and the second string is a path. For
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-uri-util.c:298
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"

View File

@@ -1,176 +0,0 @@
# Chinese (China) translation for gnome-shell.
# Copyright (C) 2009 gnome-shell's COPYRIGHT HOLDER
# This file is distributed under the same license as the gnome-shell package.
# Ray Wang <raywang@gnome.org>, 2009.
#
msgid ""
msgstr ""
"Project-Id-Version: gnome-shell master\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&component=general\n"
"POT-Creation-Date: 2009-08-29 19:32+0000\n"
"PO-Revision-Date: 2009-08-30 00:08+0800\n"
"Last-Translator: Ray Wang <raywang@gnome.org>\n"
"Language-Team: Chinese (Simplified) <i18n-zh@googlegroups.com>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: utf-8\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#: ../data/gnome-shell.desktop.in.in.h:1
msgid "GNOME Shell"
msgstr "GNOME Shell"
#: ../data/gnome-shell.desktop.in.in.h:2
msgid "Window management and application launching"
msgstr "窗口管理和应用程序启动"
#. left side
#: ../js/ui/panel.js:271
msgid "Activities"
msgstr "活动"
#. Translators: This is a time format.
#: ../js/ui/panel.js:454
msgid "%a %l:%M %p"
msgstr "%A %H:%M"
#: ../js/ui/dash.js:256
msgid "Find..."
msgstr "查找..."
#: ../js/ui/dash.js:374
msgid "Browse"
msgstr "浏览"
#: ../js/ui/dash.js:451
msgid "(see all)"
msgstr "(查看所有)"
#. **** Applications ****
#: ../js/ui/dash.js:633 ../js/ui/dash.js:681
msgid "APPLICATIONS"
msgstr "应用程序"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:653
msgid "PLACES"
msgstr "位置"
#. **** Documents ****
#: ../js/ui/dash.js:660 ../js/ui/dash.js:692
msgid "RECENT DOCUMENTS"
msgstr "最近的文档"
#. **** Search Results ****
#: ../js/ui/dash.js:679
msgid "SEARCH RESULTS"
msgstr "搜索结果"
#: ../js/ui/runDialog.js:82
msgid "Please enter a command:"
msgstr "请输入一个命令:"
#: ../src/shell-global.c:840
msgid "Less than a minute ago"
msgstr "少于一分钟前"
#: ../src/shell-global.c:843
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "%d 分钟前"
#: ../src/shell-global.c:846
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "%d 小时前"
#: ../src/shell-global.c:849
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "%d 天前"
#: ../src/shell-global.c:852
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
msgstr[0] "%d 周前"
#: ../src/shell-status-menu.c:156
msgid "Unknown"
msgstr "未知"
#: ../src/shell-status-menu.c:212
#, c-format
msgid "Can't lock screen: %s"
msgstr "不能锁住屏幕:%s"
#: ../src/shell-status-menu.c:227
#, c-format
msgid "Can't temporarily set screensaver to blank screen: %s"
msgstr "不能临时将屏幕保护设置成空白屏幕:%s"
#: ../src/shell-status-menu.c:351
#, c-format
msgid "Can't logout: %s"
msgstr "不能退出:%s"
#: ../src/shell-status-menu.c:492
msgid "Account Information..."
msgstr "帐户信息..."
#: ../src/shell-status-menu.c:502
msgid "Sidebar"
msgstr "侧边栏"
#: ../src/shell-status-menu.c:510
msgid "System Preferences..."
msgstr "系统首选项..."
#: ../src/shell-status-menu.c:525
msgid "Lock Screen"
msgstr "锁住屏幕"
#: ../src/shell-status-menu.c:535
msgid "Switch User"
msgstr "切换用户"
#. Only show switch user if there are other users
#. Log Out
#: ../src/shell-status-menu.c:546
msgid "Log Out..."
msgstr "退出..."
#. Shut down
#: ../src/shell-status-menu.c:557
msgid "Shut Down..."
msgstr "关机..."
#: ../src/shell-uri-util.c:87
msgid "Home Folder"
msgstr "主文件夹"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-uri-util.c:102
msgid "File System"
msgstr "文件系统"
#: ../src/shell-uri-util.c:248
msgid "Search"
msgstr "搜索"
#. Translators: the first string is the name of a gvfs
#. * method, and the second string is a path. For
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-uri-util.c:298
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"

View File

@@ -33,21 +33,21 @@ big_source_c = \
big-enum-types.h: stamp-big-enum-types.h Makefile
@true
stamp-big-enum-types.h: $(big_source_h) big/big-enum-types.h.in
$(AM_V_GEN) ( cd $(srcdir) && \
( cd $(srcdir) && \
$(GLIB_MKENUMS) \
--template $(srcdir)/big/big-enum-types.h.in \
$(big_source_h) ) >> xgen-beth && \
(cmp -s xgen-beth big-enum-types.h || cp xgen-beth big-enum-types.h) && \
rm -f xgen-beth && \
$(big_source_h) ) >> xgen-teth && \
(cmp xgen-teth big-enum-types.h || cp xgen-teth big-enum-types.h) && \
rm -f xgen-teth && \
echo timestamp > $(@F)
big-enum-types.c: stamp-big-enum-types.h big/big-enum-types.c.in
$(AM_V_GEN) ( cd $(srcdir) && \
( cd $(srcdir) && \
$(GLIB_MKENUMS) \
--template $(srcdir)/big/big-enum-types.c.in \
$(big_source_h) ) >> xgen-betc && \
cp xgen-betc big-enum-types.c && \
rm -f xgen-betc
$(big_source_h) ) >> xgen-tetc && \
cp xgen-tetc big-enum-types.c && \
rm -f xgen-tetc
noinst_LTLIBRARIES += libbig-1.0.la

17
src/Makefile-taskpanel.am Normal file
View File

@@ -0,0 +1,17 @@
gnomeshell_taskpanel_CPPFLAGS = \
-I$(top_srcdir)/src \
-DG_DISABLE_DEPRECATED \
-DWNCK_I_KNOW_THIS_IS_UNSTABLE \
-DG_LOG_DOMAIN=\"gnomeshell-taskpanel\" \
$(TASKPANEL_CFLAGS) \
$(NULL)
gnomeshell_taskpanel_SOURCES = \
gnomeshell-taskpanel.c \
shell-panel-window.c \
shell-panel-window.h \
$(NULL)
gnomeshell_taskpanel_LDADD = $(TASKPANEL_LIBS)
libexec_PROGRAMS += gnomeshell-taskpanel

89
src/Makefile-tidy.am Normal file
View File

@@ -0,0 +1,89 @@
tidy_cflags = \
-I$(top_srcdir)/src \
-DPREFIX=\""$(prefix)"\" \
-DLIBDIR=\""$(libdir)"\" \
-DG_DISABLE_DEPRECATED \
-DG_LOG_DOMAIN=\"Tidy\" \
$(MUTTER_PLUGIN_CFLAGS) \
$(NULL)
tidy_built_sources = \
tidy-enum-types.h \
tidy-enum-types.c \
tidy-marshal.h \
tidy-marshal.c
BUILT_SOURCES += $(tidy_built_sources)
TIDY_STAMP_FILES = stamp-tidy-marshal.h stamp-tidy-enum-types.h
# please, keep this sorted alphabetically
tidy_source_h = \
tidy/tidy-grid.h \
$(NULL)
tidy_source_h_priv = \
$(NULL)
# please, keep this sorted alphabetically
tidy_source_c = \
tidy/tidy-grid.c \
$(NULL)
tidy-marshal.h: stamp-tidy-marshal.h
@true
stamp-tidy-marshal.h: Makefile tidy/tidy-marshal.list
$(GLIB_GENMARSHAL) \
--prefix=_tidy_marshal \
--header \
$(srcdir)/tidy/tidy-marshal.list > xgen-tmh && \
(cmp -s xgen-tmh tidy-marshal.h || cp -f xgen-tmh tidy-marshal.h) && \
rm -f xgen-tmh && \
echo timestamp > $(@F)
tidy-marshal.c: Makefile tidy/tidy-marshal.list
(echo "#include \"tidy-marshal.h\"" ; \
$(GLIB_GENMARSHAL) \
--prefix=_tidy_marshal \
--body \
$(srcdir)/tidy/tidy-marshal.list ) > xgen-tmc && \
cp -f xgen-tmc tidy-marshal.c && \
rm -f xgen-tmc
tidy-enum-types.h: stamp-tidy-enum-types.h Makefile
@true
stamp-tidy-enum-types.h: $(tidy_source_h) tidy/tidy-enum-types.h.in
( cd $(srcdir) && \
$(GLIB_MKENUMS) \
--template $(srcdir)/tidy/tidy-enum-types.h.in \
$(tidy_source_h) ) >> xgen-teth && \
(cmp xgen-teth tidy-enum-types.h || cp xgen-teth tidy-enum-types.h) && \
rm -f xgen-teth && \
echo timestamp > $(@F)
tidy-enum-types.c: stamp-tidy-enum-types.h tidy/tidy-enum-types.c.in
( cd $(srcdir) && \
$(GLIB_MKENUMS) \
--template $(srcdir)/tidy/tidy-enum-types.c.in \
$(tidy_source_h) ) >> xgen-tetc && \
cp xgen-tetc tidy-enum-types.c && \
rm -f xgen-tetc
noinst_LTLIBRARIES += libtidy-1.0.la
libtidy_1_0_la_LIBADD = $(TIDY_LIBS)
libtidy_1_0_la_SOURCES = \
$(tidy_source_c) \
$(tidy_source_h) \
$(tidy_source_h_priv) \
$(tidy_built_sources) \
$(NULL)
libtidy_1_0_la_CPPFLAGS = $(tidy_cflags)
libtidy_1_0_la_LDFLAGS = $(LDADD)
CLEANFILES += $(TIDY_STAMP_FILES) $(BUILT_SOURCES)
EXTRA_DIST += \
tidy/tidy-enum-types.h.in \
tidy/tidy-enum-types.c.in \
tidy/tidy-marshal.list

View File

@@ -24,7 +24,7 @@ tray_source = \
na-marshal.h: stamp-na-marshal.h
@true
stamp-na-marshal.h: Makefile tray/na-marshal.list
$(AM_V_GEN) $(GLIB_GENMARSHAL) \
$(GLIB_GENMARSHAL) \
--prefix=_na_marshal \
--header \
$(srcdir)/tray/na-marshal.list > xgen-tmh && \
@@ -33,7 +33,7 @@ stamp-na-marshal.h: Makefile tray/na-marshal.list
echo timestamp > $(@F)
na-marshal.c: Makefile tray/na-marshal.list
$(AM_V_GEN) (echo "#include \"na-marshal.h\"" ; \
(echo "#include \"na-marshal.h\"" ; \
$(GLIB_GENMARSHAL) \
--prefix=_na_marshal \
--body \

View File

@@ -1,37 +1,34 @@
NULL =
BUILT_SOURCES =
CLEANFILES =
EXTRA_DIST =
EXTRA_DIST =
libexec_PROGRAMS =
noinst_LTLIBRARIES =
.AUTOPARALLEL:
noinst_LTLIBRARIES =
bin_SCRIPTS = gnome-shell
gnome-shell: gnome-shell.in
$(AM_V_GEN) sed -e "s|@MUTTER_BIN_DIR[@]|$(MUTTER_BIN_DIR)|" \
sed -e "s|@MUTTER_BIN_DIR[@]|$(MUTTER_BIN_DIR)|" \
-e "s|@GJS_JS_DIR[@]|$(GJS_JS_DIR)|" \
-e "s|@GJS_JS_NATIVE_DIR[@]|$(GJS_JS_NATIVE_DIR)|" \
-e "s|@libexecdir[@]|$(libexecdir)|" \
-e "s|@libdir[@]|$(libdir)|" \
-e "s|@pkgdatadir[@]|$(pkgdatadir)|" \
-e "s|@PYTHON[@]|$(PYTHON)|" \
-e "s|@sysconfdir[@]|$(sysconfdir)|" \
$< > $@ && chmod a+x $@
CLEANFILES += gnome-shell
EXTRA_DIST += gnome-shell.in
include Makefile-tidy.am
include Makefile-big.am
include Makefile-gdmuser.am
include Makefile-tray.am
include Makefile-taskpanel.am
gnome_shell_cflags = \
$(MUTTER_PLUGIN_CFLAGS) \
$(LIBGNOMEUI_CFLAGS) \
-I$(srcdir)/tray \
-DGETTEXT_PACKAGE=\"gnome-shell\" \
-DLOCALEDIR=\"$(datadir)/locale\" \
-DGETTEXT_PACKAGE=gnome-shell \
-DGNOME_SHELL_DATADIR=\"$(pkgdatadir)\" \
-DGNOME_SHELL_PKGLIBDIR=\"$(pkglibdir)\" \
-DJSDIR=\"$(pkgdatadir)/js\"
@@ -60,37 +57,21 @@ libgnome_shell_la_SOURCES = \
shell-app-system.h \
shell-arrow.c \
shell-arrow.h \
shell-button-box.c \
shell-button-box.h \
shell-drawing.c \
shell-drawing.h \
shell-drawing-area.c \
shell-drawing-area.h \
shell-embedded-window.c \
shell-embedded-window.h \
shell-embedded-window-private.h \
shell-gconf.c \
shell-gconf.h \
shell-generic-container.c \
shell-generic-container.h \
shell-gtk-embed.c \
shell-gtk-embed.h \
shell-overflow-list.c \
shell-overflow-list.h \
shell-process.c \
shell-process.h \
shell-global.c \
shell-global.h \
shell-status-menu.c \
shell-status-menu.h \
shell-stack.c \
shell-stack.h \
shell-tray-manager.c \
shell-tray-manager.h \
shell-texture-cache.c \
shell-texture-cache.h \
shell-uri-util.c \
shell-uri-util.h \
shell-wm.c \
shell-wm.h
@@ -128,22 +109,22 @@ libgnome_shell_la_gir_sources = \
shell-marshal.h: stamp-shell-marshal.h
@true
stamp-shell-marshal.h: Makefile shell-marshal.list
$(AM_V_GEN) $(GLIB_GENMARSHAL) \
$(GLIB_GENMARSHAL) \
--prefix=_shell_marshal \
--header \
$(srcdir)/shell-marshal.list > xgen-smh && \
(cmp -s xgen-smh shell-marshal.h || cp -f xgen-smh shell-marshal.h) && \
rm -f xgen-smh && \
$(srcdir)/shell-marshal.list > xgen-tmh && \
(cmp -s xgen-tmh shell-marshal.h || cp -f xgen-tmh shell-marshal.h) && \
rm -f xgen-tmh && \
echo timestamp > $(@F)
shell-marshal.c: Makefile shell-marshal.list
$(AM_V_GEN) (echo "#include \"shell-marshal.h\"" ; \
(echo "#include \"shell-marshal.h\"" ; \
$(GLIB_GENMARSHAL) \
--prefix=_shell_marshal \
--body \
$(srcdir)/shell-marshal.list ) > xgen-smc && \
cp -f xgen-smc shell-marshal.c && \
rm -f xgen-smc
$(srcdir)/shell-marshal.list ) > xgen-tmc && \
cp -f xgen-tmc shell-marshal.c && \
rm -f xgen-tmc
libgnome_shell_la_LDFLAGS = -avoid-version -module
libgnome_shell_la_LIBADD = \
@@ -151,20 +132,20 @@ libgnome_shell_la_LIBADD = \
$(LIBGNOMEUI_LIBS) \
libbig-1.0.la \
libgdmuser-1.0.la \
libtidy-1.0.la \
libtray.la
libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags)
typelibdir = $(pkglibdir)
typelib_DATA = Shell-0.1.typelib Big-1.0.typelib
typelib_DATA = Shell-0.1.typelib Tidy-1.0.typelib Big-1.0.typelib
Shell-0.1.gir: $(mutter) $(G_IR_SCANNER) Big-1.0.gir libgnome-shell.la Makefile
$(AM_V_GEN) $(G_IR_SCANNER) \
$(G_IR_SCANNER) \
--namespace=Shell \
--nsversion=0.1 \
--add-include-path=$(MUTTER_LIB_DIR)/mutter/ \
--include=Clutter-1.0 \
--include=Clutter-0.9 \
--include=Meta-2.27 \
--libtool="$(LIBTOOL)" \
--add-include-path=$(builddir) \
--include=Big-1.0 \
--program=mutter \
@@ -177,20 +158,33 @@ CLEANFILES += Shell-0.1.gir
# The dependency on libgnome-shell.la here is because g-ir-compiler opens it
# (not the fake library, since we've already done the rewriting)
Shell-0.1.typelib: libgnome-shell.la Shell-0.1.gir Big-1.0.gir
$(AM_V_GEN) LD_LIBRARY_PATH=.$${LD_LIBRARY_PATH:+:$$LD_LIBRARY_PATH} \
$(G_IR_COMPILER) \
--includedir=. \
--includedir=$(MUTTER_LIB_DIR)/mutter/ \
Shell-0.1.gir -o $@
LD_LIBRARY_PATH=$${LD_LIBRARY_PATH:+$$LD_LIBRARY_PATH:}. g-ir-compiler --includedir=$(builddir) --includedir=$(MUTTER_LIB_DIR)/mutter/ Shell-0.1.gir -o $@
CLEANFILES += Shell-0.1.typelib
Big-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libbig-1.0.la $(srcdir)/big-enum-types.h Makefile
$(AM_V_GEN) $(G_IR_SCANNER) \
--namespace=Big \
Tidy-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libtidy-1.0.la Makefile
$(G_IR_SCANNER) \
--namespace=Tidy \
--nsversion=1.0 \
--include=Clutter-1.0 \
--include=Clutter-0.9 \
--program=mutter \
--program-arg=--mutter-plugins=$$(pwd)/libgnome-shell.la \
$(addprefix $(srcdir)/,$(tidy_source_h)) \
$(addprefix $(srcdir)/,$(tidy_source_c)) \
$(srcdir)/tidy-enum-types.h \
$(tidy_cflags) \
-o $@
CLEANFILES += Tidy-1.0.gir
Tidy-1.0.typelib: libtidy-1.0.la Tidy-1.0.gir
LD_LIBRARY_PATH=$${LD_LIBRARY_PATH:+$$LD_LIBRARY_PATH:}. g-ir-compiler Tidy-1.0.gir -o $@
CLEANFILES += Tidy-1.0.typelib
Big-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libbig-1.0.la Makefile
$(G_IR_SCANNER) \
--namespace=Big \
--nsversion=1.0 \
--include=Clutter-0.9 \
--include=GdkPixbuf-2.0 \
--libtool="$(LIBTOOL)" \
--program=mutter \
--program-arg=--mutter-plugins=$$(pwd)/libgnome-shell.la \
$(addprefix $(srcdir)/,$(big_source_h)) \
@@ -201,6 +195,5 @@ Big-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libbig-1.0.la $(srcdir)
CLEANFILES += Big-1.0.gir
Big-1.0.typelib: libbig-1.0.la Big-1.0.gir
$(AM_V_GEN) LD_LIBRARY_PATH=.$${LD_LIBRARY_PATH:+:$$LD_LIBRARY_PATH} \
$(G_IR_COMPILER) Big-1.0.gir -o $@
LD_LIBRARY_PATH=$${LD_LIBRARY_PATH:+$$LD_LIBRARY_PATH:}. g-ir-compiler Big-1.0.gir -o $@
CLEANFILES += Big-1.0.typelib

View File

@@ -0,0 +1,208 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <glib-object.h>
#include <gtk/gtk.h>
#include "gdm-user-chooser-widget.h"
#include "gdm-user-chooser-dialog.h"
#define GDM_USER_CHOOSER_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_USER_CHOOSER_DIALOG, GdmUserChooserDialogPrivate))
struct GdmUserChooserDialogPrivate
{
GtkWidget *chooser_widget;
};
enum {
PROP_0,
};
static void gdm_user_chooser_dialog_class_init (GdmUserChooserDialogClass *klass);
static void gdm_user_chooser_dialog_init (GdmUserChooserDialog *user_chooser_dialog);
static void gdm_user_chooser_dialog_finalize (GObject *object);
G_DEFINE_TYPE (GdmUserChooserDialog, gdm_user_chooser_dialog, GTK_TYPE_DIALOG)
char *
gdm_user_chooser_dialog_get_chosen_user_name (GdmUserChooserDialog *dialog)
{
char *user_name;
g_return_val_if_fail (GDM_IS_USER_CHOOSER_DIALOG (dialog), NULL);
user_name = gdm_user_chooser_widget_get_chosen_user_name (GDM_USER_CHOOSER_WIDGET (dialog->priv->chooser_widget));
return user_name;
}
void
gdm_user_chooser_dialog_set_show_user_other (GdmUserChooserDialog *dialog,
gboolean show_user)
{
g_return_if_fail (GDM_IS_USER_CHOOSER_DIALOG (dialog));
gdm_user_chooser_widget_set_show_user_other (GDM_USER_CHOOSER_WIDGET (dialog->priv->chooser_widget), show_user);
}
void
gdm_user_chooser_dialog_set_show_user_guest (GdmUserChooserDialog *dialog,
gboolean show_user)
{
g_return_if_fail (GDM_IS_USER_CHOOSER_DIALOG (dialog));
gdm_user_chooser_widget_set_show_user_guest (GDM_USER_CHOOSER_WIDGET (dialog->priv->chooser_widget), show_user);
}
void
gdm_user_chooser_dialog_set_show_user_auto (GdmUserChooserDialog *dialog,
gboolean show_user)
{
g_return_if_fail (GDM_IS_USER_CHOOSER_DIALOG (dialog));
gdm_user_chooser_widget_set_show_user_auto (GDM_USER_CHOOSER_WIDGET (dialog->priv->chooser_widget), show_user);
}
static void
gdm_user_chooser_dialog_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
switch (prop_id) {
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gdm_user_chooser_dialog_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
switch (prop_id) {
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static GObject *
gdm_user_chooser_dialog_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties)
{
GdmUserChooserDialog *user_chooser_dialog;
user_chooser_dialog = GDM_USER_CHOOSER_DIALOG (G_OBJECT_CLASS (gdm_user_chooser_dialog_parent_class)->constructor (type,
n_construct_properties,
construct_properties));
return G_OBJECT (user_chooser_dialog);
}
static void
gdm_user_chooser_dialog_dispose (GObject *object)
{
G_OBJECT_CLASS (gdm_user_chooser_dialog_parent_class)->dispose (object);
}
static void
gdm_user_chooser_dialog_class_init (GdmUserChooserDialogClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->get_property = gdm_user_chooser_dialog_get_property;
object_class->set_property = gdm_user_chooser_dialog_set_property;
object_class->constructor = gdm_user_chooser_dialog_constructor;
object_class->dispose = gdm_user_chooser_dialog_dispose;
object_class->finalize = gdm_user_chooser_dialog_finalize;
g_type_class_add_private (klass, sizeof (GdmUserChooserDialogPrivate));
}
static void
on_response (GdmUserChooserDialog *dialog,
gint response_id)
{
switch (response_id) {
default:
break;
}
}
static void
gdm_user_chooser_dialog_init (GdmUserChooserDialog *dialog)
{
dialog->priv = GDM_USER_CHOOSER_DIALOG_GET_PRIVATE (dialog);
dialog->priv->chooser_widget = gdm_user_chooser_widget_new ();
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), dialog->priv->chooser_widget);
gtk_dialog_add_buttons (GTK_DIALOG (dialog),
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OK, GTK_RESPONSE_OK,
NULL);
g_signal_connect (dialog,
"response",
G_CALLBACK (on_response),
dialog);
gtk_widget_show_all (GTK_WIDGET (dialog));
}
static void
gdm_user_chooser_dialog_finalize (GObject *object)
{
GdmUserChooserDialog *user_chooser_dialog;
g_return_if_fail (object != NULL);
g_return_if_fail (GDM_IS_USER_CHOOSER_DIALOG (object));
user_chooser_dialog = GDM_USER_CHOOSER_DIALOG (object);
g_return_if_fail (user_chooser_dialog->priv != NULL);
G_OBJECT_CLASS (gdm_user_chooser_dialog_parent_class)->finalize (object);
}
GtkWidget *
gdm_user_chooser_dialog_new (void)
{
GObject *object;
object = g_object_new (GDM_TYPE_USER_CHOOSER_DIALOG,
NULL);
return GTK_WIDGET (object);
}

View File

@@ -0,0 +1,62 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#ifndef __GDM_USER_CHOOSER_DIALOG_H
#define __GDM_USER_CHOOSER_DIALOG_H
#include <glib-object.h>
#include <gtk/gtkdialog.h>
G_BEGIN_DECLS
#define GDM_TYPE_USER_CHOOSER_DIALOG (gdm_user_chooser_dialog_get_type ())
#define GDM_USER_CHOOSER_DIALOG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_USER_CHOOSER_DIALOG, GdmUserChooserDialog))
#define GDM_USER_CHOOSER_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_USER_CHOOSER_DIALOG, GdmUserChooserDialogClass))
#define GDM_IS_USER_CHOOSER_DIALOG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_USER_CHOOSER_DIALOG))
#define GDM_IS_USER_CHOOSER_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_USER_CHOOSER_DIALOG))
#define GDM_USER_CHOOSER_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_USER_CHOOSER_DIALOG, GdmUserChooserDialogClass))
typedef struct GdmUserChooserDialogPrivate GdmUserChooserDialogPrivate;
typedef struct
{
GtkDialog parent;
GdmUserChooserDialogPrivate *priv;
} GdmUserChooserDialog;
typedef struct
{
GtkDialogClass parent_class;
} GdmUserChooserDialogClass;
GType gdm_user_chooser_dialog_get_type (void);
GtkWidget * gdm_user_chooser_dialog_new (void);
char * gdm_user_chooser_dialog_get_chosen_user_name (GdmUserChooserDialog *dialog);
void gdm_user_chooser_dialog_set_show_other_user (GdmUserChooserDialog *dialog,
gboolean show);
void gdm_user_chooser_dialog_set_show_user_guest (GdmUserChooserDialog *dialog,
gboolean show);
void gdm_user_chooser_dialog_set_show_user_auto (GdmUserChooserDialog *dialog,
gboolean show);
G_END_DECLS
#endif /* __GDM_USER_CHOOSER_DIALOG_H */

View File

@@ -0,0 +1,723 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
* Copyright (C) 2007 Ray Strode <rstrode@redhat.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>
#include <sys/stat.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include <gtk/gtk.h>
#include <gconf/gconf-client.h>
#include "gdm-user-manager.h"
#include "gdm-user-chooser-widget.h"
#define KEY_DISABLE_USER_LIST "/apps/gdm/simple-greeter/disable_user_list"
enum {
USER_NO_DISPLAY = 1 << 0,
USER_ACCOUNT_DISABLED = 1 << 1,
};
#define DEFAULT_USER_ICON "stock_person"
#define GDM_USER_CHOOSER_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_USER_CHOOSER_WIDGET, GdmUserChooserWidgetPrivate))
#define MAX_ICON_SIZE 128
struct GdmUserChooserWidgetPrivate
{
GdmUserManager *manager;
GtkIconTheme *icon_theme;
GdkPixbuf *logged_in_pixbuf;
GdkPixbuf *stock_person_pixbuf;
guint loaded : 1;
guint show_user_other : 1;
guint show_user_guest : 1;
guint show_user_auto : 1;
guint show_normal_users : 1;
guint load_idle_id;
};
enum {
PROP_0,
PROP_SHOW_USER_GUEST,
PROP_SHOW_USER_AUTO,
PROP_SHOW_USER_OTHER,
};
static void gdm_user_chooser_widget_class_init (GdmUserChooserWidgetClass *klass);
static void gdm_user_chooser_widget_init (GdmUserChooserWidget *user_chooser_widget);
static void gdm_user_chooser_widget_finalize (GObject *object);
G_DEFINE_TYPE (GdmUserChooserWidget, gdm_user_chooser_widget, GDM_TYPE_CHOOSER_WIDGET)
static int
get_font_height_for_widget (GtkWidget *widget)
{
PangoFontMetrics *metrics;
PangoContext *context;
int ascent;
int descent;
int height;
gtk_widget_ensure_style (widget);
context = gtk_widget_get_pango_context (widget);
metrics = pango_context_get_metrics (context,
widget->style->font_desc,
pango_context_get_language (context));
ascent = pango_font_metrics_get_ascent (metrics);
descent = pango_font_metrics_get_descent (metrics);
height = PANGO_PIXELS (ascent + descent);
pango_font_metrics_unref (metrics);
return height;
}
static int
get_icon_height_for_widget (GtkWidget *widget)
{
int font_height;
int height;
font_height = get_font_height_for_widget (widget);
height = 3 * font_height;
if (height > MAX_ICON_SIZE) {
height = MAX_ICON_SIZE;
}
g_debug ("GdmUserChooserWidget: font height %d; using icon size %d", font_height, height);
return height;
}
static void
add_user_other (GdmUserChooserWidget *widget)
{
gdm_chooser_widget_add_item (GDM_CHOOSER_WIDGET (widget),
GDM_USER_CHOOSER_USER_OTHER,
NULL,
_("Other..."),
_("Choose a different account"),
0,
FALSE,
TRUE);
}
static void
add_user_guest (GdmUserChooserWidget *widget)
{
gdm_chooser_widget_add_item (GDM_CHOOSER_WIDGET (widget),
GDM_USER_CHOOSER_USER_GUEST,
widget->priv->stock_person_pixbuf,
_("Guest"),
_("Login as a temporary guest"),
0,
FALSE,
TRUE);
}
static void
add_user_auto (GdmUserChooserWidget *widget)
{
gdm_chooser_widget_add_item (GDM_CHOOSER_WIDGET (widget),
GDM_USER_CHOOSER_USER_AUTO,
NULL,
_("Automatic Login"),
_("Automatically login to the system after selecting options"),
0,
FALSE,
TRUE);
}
static void
remove_user_other (GdmUserChooserWidget *widget)
{
gdm_chooser_widget_remove_item (GDM_CHOOSER_WIDGET (widget),
GDM_USER_CHOOSER_USER_OTHER);
}
static void
remove_user_guest (GdmUserChooserWidget *widget)
{
gdm_chooser_widget_remove_item (GDM_CHOOSER_WIDGET (widget),
GDM_USER_CHOOSER_USER_GUEST);
}
static void
remove_user_auto (GdmUserChooserWidget *widget)
{
gdm_chooser_widget_remove_item (GDM_CHOOSER_WIDGET (widget),
GDM_USER_CHOOSER_USER_AUTO);
}
void
gdm_user_chooser_widget_set_show_user_other (GdmUserChooserWidget *widget,
gboolean show_user)
{
g_return_if_fail (GDM_IS_USER_CHOOSER_WIDGET (widget));
if (widget->priv->show_user_other != show_user) {
widget->priv->show_user_other = show_user;
if (show_user) {
add_user_other (widget);
} else {
remove_user_other (widget);
}
}
}
void
gdm_user_chooser_widget_set_show_user_guest (GdmUserChooserWidget *widget,
gboolean show_user)
{
g_return_if_fail (GDM_IS_USER_CHOOSER_WIDGET (widget));
if (widget->priv->show_user_guest != show_user) {
widget->priv->show_user_guest = show_user;
if (show_user) {
add_user_guest (widget);
} else {
remove_user_guest (widget);
}
}
}
void
gdm_user_chooser_widget_set_show_user_auto (GdmUserChooserWidget *widget,
gboolean show_user)
{
g_return_if_fail (GDM_IS_USER_CHOOSER_WIDGET (widget));
if (widget->priv->show_user_auto != show_user) {
widget->priv->show_user_auto = show_user;
if (show_user) {
add_user_auto (widget);
} else {
remove_user_auto (widget);
}
}
}
char *
gdm_user_chooser_widget_get_chosen_user_name (GdmUserChooserWidget *widget)
{
g_return_val_if_fail (GDM_IS_USER_CHOOSER_WIDGET (widget), NULL);
return gdm_chooser_widget_get_active_item (GDM_CHOOSER_WIDGET (widget));
}
void
gdm_user_chooser_widget_set_chosen_user_name (GdmUserChooserWidget *widget,
const char *name)
{
g_return_if_fail (GDM_IS_USER_CHOOSER_WIDGET (widget));
gdm_chooser_widget_set_active_item (GDM_CHOOSER_WIDGET (widget), name);
}
void
gdm_user_chooser_widget_set_show_only_chosen (GdmUserChooserWidget *widget,
gboolean show_only) {
g_return_if_fail (GDM_IS_USER_CHOOSER_WIDGET (widget));
gdm_chooser_widget_set_hide_inactive_items (GDM_CHOOSER_WIDGET (widget),
show_only);
}
static void
gdm_user_chooser_widget_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GdmUserChooserWidget *self;
self = GDM_USER_CHOOSER_WIDGET (object);
switch (prop_id) {
case PROP_SHOW_USER_AUTO:
gdm_user_chooser_widget_set_show_user_auto (self, g_value_get_boolean (value));
break;
case PROP_SHOW_USER_GUEST:
gdm_user_chooser_widget_set_show_user_guest (self, g_value_get_boolean (value));
break;
case PROP_SHOW_USER_OTHER:
gdm_user_chooser_widget_set_show_user_other (self, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gdm_user_chooser_widget_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GdmUserChooserWidget *self;
self = GDM_USER_CHOOSER_WIDGET (object);
switch (prop_id) {
case PROP_SHOW_USER_AUTO:
g_value_set_boolean (value, self->priv->show_user_auto);
break;
case PROP_SHOW_USER_GUEST:
g_value_set_boolean (value, self->priv->show_user_guest);
break;
case PROP_SHOW_USER_OTHER:
g_value_set_boolean (value, self->priv->show_user_other);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static gboolean
is_user_list_disabled (GdmUserChooserWidget *widget)
{
GConfClient *client;
GError *error;
gboolean result;
client = gconf_client_get_default ();
error = NULL;
result = gconf_client_get_bool (client, KEY_DISABLE_USER_LIST, &error);
if (error != NULL) {
g_debug ("GdmUserChooserWidget: unable to get disable-user-list configuration: %s", error->message);
g_error_free (error);
}
g_object_unref (client);
return result;
}
static void
add_user (GdmUserChooserWidget *widget,
GdmUser *user)
{
GdkPixbuf *pixbuf;
char *tooltip;
gboolean is_logged_in;
int size;
if (!widget->priv->show_normal_users) {
return;
}
size = get_icon_height_for_widget (widget);
pixbuf = gdm_user_render_icon (user, size);
if (pixbuf == NULL && widget->priv->stock_person_pixbuf != NULL) {
pixbuf = g_object_ref (widget->priv->stock_person_pixbuf);
}
tooltip = g_strdup_printf (_("Log in as %s"),
gdm_user_get_user_name (user));
is_logged_in = gdm_user_get_num_sessions (user) > 0;
g_debug ("GdmUserChooserWidget: User added name:%s logged-in:%d pixbuf:%p",
gdm_user_get_user_name (user),
is_logged_in,
pixbuf);
gdm_chooser_widget_add_item (GDM_CHOOSER_WIDGET (widget),
gdm_user_get_user_name (user),
pixbuf,
gdm_user_get_real_name (user),
tooltip,
gdm_user_get_login_frequency (user),
is_logged_in,
FALSE);
g_free (tooltip);
if (pixbuf != NULL) {
g_object_unref (pixbuf);
}
}
static void
on_user_added (GdmUserManager *manager,
GdmUser *user,
GdmUserChooserWidget *widget)
{
/* wait for all users to be loaded */
if (! widget->priv->loaded) {
return;
}
add_user (widget, user);
}
static void
on_user_removed (GdmUserManager *manager,
GdmUser *user,
GdmUserChooserWidget *widget)
{
const char *user_name;
g_debug ("GdmUserChooserWidget: User removed: %s", gdm_user_get_user_name (user));
/* wait for all users to be loaded */
if (! widget->priv->loaded) {
return;
}
user_name = gdm_user_get_user_name (user);
gdm_chooser_widget_remove_item (GDM_CHOOSER_WIDGET (widget),
user_name);
}
static void
on_user_is_logged_in_changed (GdmUserManager *manager,
GdmUser *user,
GdmUserChooserWidget *widget)
{
const char *user_name;
gboolean is_logged_in;
g_debug ("GdmUserChooserWidget: User logged in changed: %s", gdm_user_get_user_name (user));
user_name = gdm_user_get_user_name (user);
is_logged_in = gdm_user_get_num_sessions (user) > 0;
gdm_chooser_widget_set_item_in_use (GDM_CHOOSER_WIDGET (widget),
user_name,
is_logged_in);
}
static void
on_user_login_frequency_changed (GdmUserManager *manager,
GdmUser *user,
GdmUserChooserWidget *widget)
{
const char *user_name;
gulong freq;
g_debug ("GdmUserChooserWidget: User login frequency changed: %s", gdm_user_get_user_name (user));
user_name = gdm_user_get_user_name (user);
freq = gdm_user_get_login_frequency (user);
gdm_chooser_widget_set_item_priority (GDM_CHOOSER_WIDGET (widget),
user_name,
freq);
}
static void
on_users_loaded (GdmUserManager *manager,
GdmUserChooserWidget *widget)
{
GSList *users;
widget->priv->loaded = TRUE;
g_debug ("GdmUserChooserWidget: Users loaded");
users = gdm_user_manager_list_users (manager);
while (users != NULL) {
add_user (widget, users->data);
users = g_slist_delete_link (users, users);
}
gtk_widget_grab_focus (GTK_WIDGET (widget));
gdm_chooser_widget_loaded (GDM_CHOOSER_WIDGET (widget));
}
static gboolean
load_users (GdmUserChooserWidget *widget)
{
if (widget->priv->show_normal_users) {
widget->priv->manager = gdm_user_manager_ref_default ();
g_signal_connect (widget->priv->manager,
"user-added",
G_CALLBACK (on_user_added),
widget);
g_signal_connect (widget->priv->manager,
"user-removed",
G_CALLBACK (on_user_removed),
widget);
g_signal_connect (widget->priv->manager,
"users-loaded",
G_CALLBACK (on_users_loaded),
widget);
g_signal_connect (widget->priv->manager,
"user-is-logged-in-changed",
G_CALLBACK (on_user_is_logged_in_changed),
widget);
g_signal_connect (widget->priv->manager,
"user-login-frequency-changed",
G_CALLBACK (on_user_login_frequency_changed),
widget);
} else {
gdm_chooser_widget_loaded (GDM_CHOOSER_WIDGET (widget));
}
widget->priv->load_idle_id = 0;
return FALSE;
}
static GObject *
gdm_user_chooser_widget_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties)
{
GdmUserChooserWidget *widget;
widget = GDM_USER_CHOOSER_WIDGET (G_OBJECT_CLASS (gdm_user_chooser_widget_parent_class)->constructor (type,
n_construct_properties,
construct_properties));
widget->priv->show_normal_users = !is_user_list_disabled (widget);
widget->priv->load_idle_id = g_idle_add ((GSourceFunc)load_users, widget);
return G_OBJECT (widget);
}
static void
gdm_user_chooser_widget_dispose (GObject *object)
{
GdmUserChooserWidget *widget;
widget = GDM_USER_CHOOSER_WIDGET (object);
G_OBJECT_CLASS (gdm_user_chooser_widget_parent_class)->dispose (object);
if (widget->priv->load_idle_id > 0) {
g_source_remove (widget->priv->load_idle_id);
widget->priv->load_idle_id = 0;
}
if (widget->priv->logged_in_pixbuf != NULL) {
g_object_unref (widget->priv->logged_in_pixbuf);
widget->priv->logged_in_pixbuf = NULL;
}
if (widget->priv->stock_person_pixbuf != NULL) {
g_object_unref (widget->priv->stock_person_pixbuf);
widget->priv->stock_person_pixbuf = NULL;
}
}
static void
gdm_user_chooser_widget_class_init (GdmUserChooserWidgetClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->get_property = gdm_user_chooser_widget_get_property;
object_class->set_property = gdm_user_chooser_widget_set_property;
object_class->constructor = gdm_user_chooser_widget_constructor;
object_class->dispose = gdm_user_chooser_widget_dispose;
object_class->finalize = gdm_user_chooser_widget_finalize;
g_object_class_install_property (object_class,
PROP_SHOW_USER_AUTO,
g_param_spec_boolean ("show-user-auto",
"show user auto",
"show user auto",
FALSE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class,
PROP_SHOW_USER_GUEST,
g_param_spec_boolean ("show-user-guest",
"show user guest",
"show user guest",
FALSE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class,
PROP_SHOW_USER_OTHER,
g_param_spec_boolean ("show-user-other",
"show user other",
"show user other",
TRUE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_type_class_add_private (klass, sizeof (GdmUserChooserWidgetPrivate));
}
static GdkPixbuf *
get_stock_person_pixbuf (GdmUserChooserWidget *widget)
{
GdkPixbuf *pixbuf;
int size;
size = get_icon_height_for_widget (widget);
pixbuf = gtk_icon_theme_load_icon (widget->priv->icon_theme,
DEFAULT_USER_ICON,
size,
0,
NULL);
return pixbuf;
}
static GdkPixbuf *
get_logged_in_pixbuf (GdmUserChooserWidget *widget)
{
GdkPixbuf *pixbuf;
int size;
size = get_icon_height_for_widget (widget);
pixbuf = gtk_icon_theme_load_icon (widget->priv->icon_theme,
"emblem-default",
size / 3,
0,
NULL);
return pixbuf;
}
typedef struct {
GdkPixbuf *old_icon;
GdkPixbuf *new_icon;
} IconUpdateData;
static gboolean
update_icons (GdmChooserWidget *widget,
const char *id,
GdkPixbuf **image,
char **name,
char **comment,
gulong *priority,
gboolean *is_in_use,
gboolean *is_separate,
IconUpdateData *data)
{
if (data->old_icon == *image) {
*image = data->new_icon;
return TRUE;
}
return FALSE;
}
static void
load_icons (GdmUserChooserWidget *widget)
{
GdkPixbuf *old_pixbuf;
IconUpdateData data;
if (widget->priv->logged_in_pixbuf != NULL) {
g_object_unref (widget->priv->logged_in_pixbuf);
}
widget->priv->logged_in_pixbuf = get_logged_in_pixbuf (widget);
old_pixbuf = widget->priv->stock_person_pixbuf;
widget->priv->stock_person_pixbuf = get_stock_person_pixbuf (widget);
/* update the icons in the model */
data.old_icon = old_pixbuf;
data.new_icon = widget->priv->stock_person_pixbuf;
gdm_chooser_widget_update_foreach_item (GDM_CHOOSER_WIDGET (widget),
(GdmChooserUpdateForeachFunc)update_icons,
&data);
if (old_pixbuf != NULL) {
g_object_unref (old_pixbuf);
}
}
static void
on_icon_theme_changed (GtkIconTheme *icon_theme,
GdmUserChooserWidget *widget)
{
g_debug ("GdmUserChooserWidget: icon theme changed");
load_icons (widget);
}
static void
setup_icons (GdmUserChooserWidget *widget)
{
if (gtk_widget_has_screen (GTK_WIDGET (widget))) {
widget->priv->icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (widget)));
} else {
widget->priv->icon_theme = gtk_icon_theme_get_default ();
}
if (widget->priv->icon_theme != NULL) {
g_signal_connect (widget->priv->icon_theme,
"changed",
G_CALLBACK (on_icon_theme_changed),
widget);
}
load_icons (widget);
}
static void
gdm_user_chooser_widget_init (GdmUserChooserWidget *widget)
{
widget->priv = GDM_USER_CHOOSER_WIDGET_GET_PRIVATE (widget);
gdm_chooser_widget_set_separator_position (GDM_CHOOSER_WIDGET (widget),
GDM_CHOOSER_WIDGET_POSITION_BOTTOM);
gdm_chooser_widget_set_in_use_message (GDM_CHOOSER_WIDGET (widget),
_("Currently logged in"));
setup_icons (widget);
}
static void
gdm_user_chooser_widget_finalize (GObject *object)
{
GdmUserChooserWidget *widget;
g_return_if_fail (object != NULL);
g_return_if_fail (GDM_IS_USER_CHOOSER_WIDGET (object));
widget = GDM_USER_CHOOSER_WIDGET (object);
g_return_if_fail (widget->priv != NULL);
G_OBJECT_CLASS (gdm_user_chooser_widget_parent_class)->finalize (object);
}
GtkWidget *
gdm_user_chooser_widget_new (void)
{
GObject *object;
object = g_object_new (GDM_TYPE_USER_CHOOSER_WIDGET,
NULL);
return GTK_WIDGET (object);
}

View File

@@ -0,0 +1,70 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#ifndef __GDM_USER_CHOOSER_WIDGET_H
#define __GDM_USER_CHOOSER_WIDGET_H
#include <glib-object.h>
#include "gdm-chooser-widget.h"
G_BEGIN_DECLS
#define GDM_TYPE_USER_CHOOSER_WIDGET (gdm_user_chooser_widget_get_type ())
#define GDM_USER_CHOOSER_WIDGET(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_USER_CHOOSER_WIDGET, GdmUserChooserWidget))
#define GDM_USER_CHOOSER_WIDGET_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_USER_CHOOSER_WIDGET, GdmUserChooserWidgetClass))
#define GDM_IS_USER_CHOOSER_WIDGET(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_USER_CHOOSER_WIDGET))
#define GDM_IS_USER_CHOOSER_WIDGET_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_USER_CHOOSER_WIDGET))
#define GDM_USER_CHOOSER_WIDGET_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_USER_CHOOSER_WIDGET, GdmUserChooserWidgetClass))
typedef struct GdmUserChooserWidgetPrivate GdmUserChooserWidgetPrivate;
typedef struct
{
GdmChooserWidget parent;
GdmUserChooserWidgetPrivate *priv;
} GdmUserChooserWidget;
typedef struct
{
GdmChooserWidgetClass parent_class;
} GdmUserChooserWidgetClass;
#define GDM_USER_CHOOSER_USER_OTHER "__other"
#define GDM_USER_CHOOSER_USER_GUEST "__guest"
#define GDM_USER_CHOOSER_USER_AUTO "__auto"
GType gdm_user_chooser_widget_get_type (void);
GtkWidget * gdm_user_chooser_widget_new (void);
char * gdm_user_chooser_widget_get_chosen_user_name (GdmUserChooserWidget *widget);
void gdm_user_chooser_widget_set_chosen_user_name (GdmUserChooserWidget *widget,
const char *user_name);
void gdm_user_chooser_widget_set_show_only_chosen (GdmUserChooserWidget *widget,
gboolean show_only);
void gdm_user_chooser_widget_set_show_user_other (GdmUserChooserWidget *widget,
gboolean show);
void gdm_user_chooser_widget_set_show_user_guest (GdmUserChooserWidget *widget,
gboolean show);
void gdm_user_chooser_widget_set_show_user_auto (GdmUserChooserWidget *widget,
gboolean show);
G_END_DECLS
#endif /* __GDM_USER_CHOOSER_WIDGET_H */

View File

@@ -35,8 +35,7 @@
#endif /* HAVE_PATHS_H */
#include <glib.h>
/* Note on sync with gdm; need to use -lib here */
#include <glib/gi18n-lib.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include <glib-object.h>
#include <gio/gio.h>

View File

@@ -20,14 +20,12 @@
#include <config.h>
#include <float.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
/* Note on sync with gdm; need to use -lib here */
#include <glib/gi18n-lib.h>
#include <glib/gi18n.h>
#include <gio/gio.h>
#include <gtk/gtk.h>
@@ -240,8 +238,8 @@ gdm_user_class_init (GdmUserClass *class)
g_object_class_install_property (gobject_class,
PROP_MANAGER,
g_param_spec_object ("manager",
"Manager",
"The user manager object this user is controlled by.",
_("Manager"),
_("The user manager object this user is controlled by."),
GDM_TYPE_USER_MANAGER,
(G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY)));
@@ -413,16 +411,13 @@ _gdm_user_update (GdmUser *user,
/* Display Name */
if (pwent->pw_gecos && pwent->pw_gecos[0] != '\0') {
gchar *first_comma;
gchar *real_name_utf8;
real_name_utf8 = g_locale_to_utf8 (pwent->pw_gecos, -1, NULL, NULL, NULL);
first_comma = strchr (real_name_utf8, ',');
first_comma = strchr (pwent->pw_gecos, ',');
if (first_comma) {
real_name = g_strndup (real_name_utf8, first_comma - real_name_utf8);
g_free (real_name_utf8);
real_name = g_strndup (pwent->pw_gecos,
(first_comma - pwent->pw_gecos));
} else {
real_name = real_name_utf8;
real_name = g_strdup (pwent->pw_gecos);
}
if (real_name[0] == '\0') {
@@ -886,7 +881,7 @@ curved_rectangle (cairo_t *cr,
x1 = x0 + width;
y1 = y0 + height;
if (width < FLT_EPSILON || height < FLT_EPSILON) {
if (!width || !height) {
return;
}
@@ -1161,8 +1156,8 @@ gdm_user_render_icon (GdmUser *user,
} else {
pixbuf = NULL;
}
g_free (path);
out:
g_free (path);
if (pixbuf != NULL) {
framed = frame_pixbuf (pixbuf);

View File

@@ -153,9 +153,6 @@ gnome_shell_plugin_constructed (GObject *object)
ClutterBackend *backend;
cairo_font_options_t *font_options;
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
/* Disable text mipmapping; it causes problems on pre-GEM Intel
* drivers and we should just be rendering text at the right
* size rather than scaling it. If we do effects where we dynamically

158
src/gnome-shell.in Normal file → Executable file
View File

@@ -1,4 +1,4 @@
#!@PYTHON@
#!/usr/bin/python
import atexit
import optparse
@@ -14,6 +14,43 @@ import termios
import time
import errno
def find_cmd (cmd_list):
"""
Takes a list of command candidates and returns the first one that exists.
Raises a system exit if none of the commands exist.
"""
for cmd in cmd_list:
if os.path.exists(cmd):
return cmd
raise SystemExit("None of the commands %s exist" % cmd_list)
def pidof(command):
pidof_cmd = find_cmd(["/sbin/pidof", "/bin/pidof", "/usr/bin/pidof"])
pidof = subprocess.Popen([pidof_cmd, command], stdout=subprocess.PIPE)
pids = pidof.communicate()[0].split()
pidof.wait()
# pidof doesn't have a "current user only" option, so we may have
# gotten the pids of other users' processes. Fix that.
for pid in pids:
try:
os.kill(int(pid), 0)
return pid
except Exception, e:
pass
return None
def kill_gnome_panel(pid):
if options.verbose:
print "Terminating panel process %s" % pid
devnull = open("/dev/null", "w")
subprocess.call(["gdb", "-batch-silent",
"-ex", "call panel_session_do_not_restart()",
"-ex", "call exit(0)",
"-p", pid], stdout=devnull, stderr=devnull)
devnull.close()
def start_xephyr():
tmpdir = tempfile.mkdtemp("", "gnome-shell.")
atexit.register(shutil.rmtree, tmpdir)
@@ -85,27 +122,25 @@ def start_shell():
top_dir = os.path.dirname(bin_dir)
plugin = os.path.join(top_dir, 'src', 'libgnome-shell.la')
typelib_dir = os.path.join(top_dir, "src")
taskpanel_dir = os.path.join(top_dir, "src")
js_dir = os.path.join(top_dir, "js")
data_dir = os.path.join(top_dir, "data")
else:
running_from_source_tree = False
plugin = 'libgnome-shell'
js_dir = os.path.join('@pkgdatadir@', 'js')
taskpanel_dir = '@libexecdir@'
# Set up environment
env = dict(os.environ)
env.update({'GNOME_SHELL_JS' : '@GJS_JS_DIR@:@GJS_JS_NATIVE_DIR@:' + js_dir,
'PATH' : '@MUTTER_BIN_DIR@:' + os.environ.get('PATH', ''),
'PATH' : '@MUTTER_BIN_DIR@:' + os.environ.get('PATH', '') + ':' + taskpanel_dir,
'GNOME_DISABLE_CRASH_DIALOG' : '1'})
if running_from_source_tree:
env.update({'GNOME_SHELL_DATADIR' : data_dir,
'GI_TYPELIB_PATH' : typelib_dir})
jhbuild_gconf_source = os.path.join('@sysconfdir@', 'gconf/2/path.jhbuild')
if os.path.exists(jhbuild_gconf_source):
env['GCONF_DEFAULT_SOURCE_PATH'] = jhbuild_gconf_source
# Work around Ubuntu xulrunner bug,
# http://bugzilla.gnome.org/show_bug.cgi?id=573413
pkgconfig = subprocess.Popen(['pkg-config', '--variable=sdkdir', 'mozilla-js'],
@@ -152,47 +187,11 @@ def start_shell():
else:
args = []
args.extend(['mutter', '--mutter-plugins=' + plugin])
if options.replace:
args.append('--replace')
args.extend(['mutter', '--mutter-plugins=' + plugin, '--replace'])
if options.sync:
args.append('--sync')
return subprocess.Popen(args, env=env)
def restore_gnome():
# Do imports lazily to save time and memory
import gio
import gconf
# We don't want to start the new gnome-panel in the current
# directory; $HOME is better for stuff launched from it
os.chdir(os.path.expanduser("~"))
def launch_component(gconf_path):
client = gconf.client_get_default()
component = client.get_string(gconf_path)
if component == None or component == "":
return
# See gnome-session/gsm-util.c:gsm_util_find_desktop_file_for_app_name()
# The one difference is that we don't search the autostart directories,
# and just search normal application search path. (Gio doesnt' know
# how to search the autostart dirs, so we'd have to do that ourselves.)
appinfo = None
try:
appinfo = gio.unix.DesktopAppInfo(component + ".desktop")
except:
try:
appinfo = gio.unix.DesktopAppInfo("gnome-" + component + ".desktop")
except:
pass
if appinfo:
appinfo.launch()
launch_component("/desktop/gnome/session/required_components/windowmanager")
launch_component("/desktop/gnome/session/required_components/panel")
# Main program
@@ -205,8 +204,6 @@ parser.add_option("", "--debug-command", metavar="COMMAND",
help="Command to use for debugging (defaults to 'gdb --args')")
parser.add_option("-v", "--verbose", action="store_true")
parser.add_option("", "--sync", action="store_true")
parser.add_option("", "--xephyr", action="store_true",
help="Run a debugging instance inside Xephyr")
parser.add_option("", "--geometry", metavar="GEOMETRY",
help="Specify Xephyr screen geometry",
default="1024x768");
@@ -227,6 +224,18 @@ elif options.debug:
if options.wide:
options.geometry = "1280x800"
metacity_pid = pidof("metacity")
compiz_pid = pidof("compiz.real") or pidof("compiz")
gnome_panel_pid = pidof("gnome-panel")
# Run in Xephyr if gnome-panel is already running and the user didn't
# specify --replace. Otherwise, run fullscreen
if options.replace:
run_in_xephyr = False
else:
run_in_xephyr = (metacity_pid != None or compiz_pid != None or
gnome_panel_pid != None)
# Figure out whether or not to use GL_EXT_texture_from_pixmap. By default
# we use it iff we aren't running Xephyr, but we allow the user to
# explicitly disable it.
@@ -237,7 +246,7 @@ if 'GNOME_SHELL_DISABLE_TFP' in os.environ and \
use_tfp = False
else:
# tfp does not work correctly in Xephyr
use_tfp = not options.xephyr
use_tfp = not run_in_xephyr
if options.verbose:
print "Starting shell"
@@ -247,25 +256,21 @@ if options.debug:
# later, in case we kill gdb at a bad time
termattrs = termios.tcgetattr(0);
# We only respawn the previous environment on abnormal exit;
# for a clean exit, we assume that gnome-shell was replaced with
# something else.
normal_exit = False
try:
if options.xephyr:
xephyr = start_xephyr()
# This makes us not grab the org.gnome.Panel name
os.environ['GNOME_SHELL_NO_REPLACE_PANEL'] = '1'
shell = start_shell()
if run_in_xephyr:
shell = start_xephyr()
start_shell()
else:
xephyr = None
if gnome_panel_pid is not None:
kill_gnome_panel(gnome_panel_pid)
shell = start_shell()
# Wait for shell to exit
if options.verbose:
print "Waiting for shell to exit"
shell.wait()
if options.verbose:
print "Shell is dead"
except KeyboardInterrupt, e:
try:
@@ -273,29 +278,24 @@ except KeyboardInterrupt, e:
except:
pass
shell.wait()
if options.verbose:
print "Shell killed"
finally:
# Clean up Xephyr if it outlived the shell
if xephyr:
try:
os.kill(xephyr.pid, signal.SIGKILL)
except OSError:
pass
if shell.returncode == 0:
normal_exit = True
if options.verbose:
print "Shell exited normally"
elif shell.returncode < 0:
# Python has no mapping for strsignal; not worth using
# ctypes for this.
print "Shell killed with signal %d" % - shell.returncode
else:
# Normal reason here would be losing connection the X server
if options.verbose:
print "Shell exited with return code %d" % shell.returncode
if options.debug:
termios.tcsetattr(0, termios.TCSANOW, termattrs);
if not options.xephyr and options.replace and not normal_exit:
restore_gnome()
if not run_in_xephyr:
# Restart gnome-panel and window manager
if metacity_pid:
if options.verbose:
print "Restarting Metacity"
subprocess.Popen(["/usr/bin/metacity"])
elif compiz_pid:
if options.verbose:
print "Restarting Compiz"
subprocess.Popen(["/usr/bin/compiz"])
if gnome_panel_pid:
if options.verbose:
print "Restarting gnome-panel"
subprocess.Popen(["/usr/bin/gnome-panel"])

View File

@@ -0,0 +1,90 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#include "shell-panel-window.h"
#include <libwnck/libwnck.h>
#include <stdlib.h>
#include <string.h>
#include <dbus/dbus-glib.h>
static void
on_name_owner_changed (DBusGProxy *proxy,
const char *name,
const char *prev_owner,
const char *new_owner,
gpointer user_data)
{
if (strcmp (name, "org.gnome.Shell") == 0 && new_owner[0] == '\0')
exit (0);
}
static void
monitor_main_shell (const char *shell_name)
{
DBusGConnection *session;
DBusGProxy *driver;
GError *error = NULL;
gboolean have_shell;
session = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
driver = dbus_g_proxy_new_for_name (session,
DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS);
if (!dbus_g_proxy_call (driver, "NameHasOwner", &error, G_TYPE_STRING,
shell_name, G_TYPE_INVALID, G_TYPE_BOOLEAN,
&have_shell, G_TYPE_INVALID))
{
/* Shouldn't happen */
exit (1);
}
if (!have_shell)
{
/* Shell doesn't exist; either crashed or was restarted. Just abort. */
exit (0);
}
dbus_g_proxy_add_signal (driver,
"NameOwnerChanged",
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_INVALID);
dbus_g_proxy_connect_signal (driver,
"NameOwnerChanged",
G_CALLBACK (on_name_owner_changed),
NULL,
NULL);
}
int
main (int argc, char **argv)
{
ShellPanelWindow *panel;
WnckScreen *screen;
WnckTasklist *tasks;
gtk_init (&argc, &argv);
if (argc != 2) {
g_printerr ("Usage: gnomeshell-taskpanel [PARENT_DBUS_SERVICE]\n");
exit (1);
}
monitor_main_shell (argv[1]);
panel = shell_panel_window_new ();
screen = wnck_screen_get_default();
tasks = WNCK_TASKLIST (wnck_tasklist_new (screen));
gtk_container_add (GTK_CONTAINER (panel), GTK_WIDGET (tasks));
gtk_widget_show_all (GTK_WIDGET (panel));
gtk_main ();
exit (0);
}

File diff suppressed because it is too large Load Diff

View File

@@ -5,9 +5,6 @@
#include <glib-object.h>
#include <glib.h>
#include "window.h"
#include "shell-app-system.h"
/*
* This object provides monitoring of system application directories (.desktop files)
* and activity-based statistics about applications usage
@@ -30,36 +27,23 @@ struct _ShellAppMonitorClass
{
GObjectClass parent_class;
void (*apps_changed)(ShellAppMonitor *menuwrapper,
gpointer data);
};
GType shell_app_monitor_get_type (void) G_GNUC_CONST;
ShellAppMonitor* shell_app_monitor_get_default(void);
ShellAppInfo *shell_app_monitor_get_window_app (ShellAppMonitor *monitor, MetaWindow *metawin);
/* Get the most popular applications for a given activity */
GSList *shell_app_monitor_get_most_used_apps (ShellAppMonitor *monitor,
int activity,
gint number);
GList *shell_app_monitor_get_most_used_apps (ShellAppMonitor *monitor,
const char *context,
gint number);
GSList *shell_app_monitor_get_windows_for_app (ShellAppMonitor *monitor, const char *appid);
gboolean shell_app_monitor_is_window_usage_tracked (MetaWindow *window);
guint shell_app_monitor_get_window_count (ShellAppMonitor *monitor, const char *appid);
/* Get whatever's running right now */
GSList *shell_app_monitor_get_running_apps (ShellAppMonitor *monitor, const char *context);
GSList *shell_app_monitor_get_startup_sequences (ShellAppMonitor *monitor);
/* Hidden typedef for SnStartupSequence */
typedef struct _ShellStartupSequence ShellStartupSequence;
#define SHELL_TYPE_STARTUP_SEQUENCE (shell_startup_sequence_get_type ())
GType shell_startup_sequence_get_type (void);
const char *shell_startup_sequence_get_id (ShellStartupSequence *sequence);
const char *shell_startup_sequence_get_name (ShellStartupSequence *sequence);
gboolean shell_startup_sequence_get_completed (ShellStartupSequence *sequence);
ClutterActor *shell_startup_sequence_create_icon (ShellStartupSequence *sequence, guint size);
GList *shell_app_monitor_get_running_app_ids (ShellAppMonitor *monitor);
G_END_DECLS

View File

@@ -4,28 +4,14 @@
#include <string.h>
#include <gio/gio.h>
#include <gio/gdesktopappinfo.h>
#include <gtk/gtk.h>
#include <gconf/gconf.h>
#include <gconf/gconf-client.h>
#include <clutter/clutter.h>
#include "shell-global.h"
#include "shell-texture-cache.h"
#include "display.h"
#define GMENU_I_KNOW_THIS_IS_UNSTABLE
#include <gmenu-tree.h>
#define SHELL_APP_FAVORITES_KEY "/desktop/gnome/shell/favorite_apps"
/* Vendor prefixes are something that can be preprended to a .desktop
* file name. Undo this.
*/
static const char*const known_vendor_prefixes[] = { "gnome",
"fedora",
"mozilla" };
enum {
PROP_0,
@@ -43,129 +29,23 @@ struct _ShellAppSystemPrivate {
GMenuTree *apps_tree;
GMenuTree *settings_tree;
GHashTable *app_id_to_app;
GSList *cached_app_menus; /* ShellAppMenuEntry */
GSList *cached_settings; /* ShellAppInfo */
GSList *cached_setting_ids; /* utf8 */
GList *cached_favorites; /* utf8 */
GHashTable *cached_favorites; /* <utf8,integer> */
gint app_monitor_id;
guint app_change_timeout_id;
};
static void shell_app_system_finalize (GObject *object);
static void on_tree_changed (gpointer user_data);
static void on_tree_changed_cb (GMenuTree *tree, gpointer user_data);
static void on_tree_changed (GMenuTree *tree, gpointer user_data);
static void reread_menus (ShellAppSystem *self);
static void on_favorite_apps_changed (GConfClient *client, guint id, GConfEntry *entry, gpointer user_data);
static void reread_favorite_apps (ShellAppSystem *system);
G_DEFINE_TYPE(ShellAppSystem, shell_app_system, G_TYPE_OBJECT);
typedef enum {
SHELL_APP_INFO_TYPE_ENTRY,
SHELL_APP_INFO_TYPE_DESKTOP_FILE,
SHELL_APP_INFO_TYPE_WINDOW
} ShellAppInfoType;
struct _ShellAppInfo {
ShellAppInfoType type;
/* We need this for two reasons. First, GKeyFile doesn't have a refcount.
* http://bugzilla.gnome.org/show_bug.cgi?id=590808
*
* But more generally we'll always need it so we know when to free this
* structure (short of weak references on each item).
*/
guint refcount;
GMenuTreeItem *entry;
GKeyFile *keyfile;
char *keyfile_path;
MetaWindow *window;
char *window_id;
};
ShellAppInfo*
shell_app_info_ref (ShellAppInfo *info)
{
info->refcount++;
return info;
}
void
shell_app_info_unref (ShellAppInfo *info)
{
if (--info->refcount > 0)
return;
switch (info->type)
{
case SHELL_APP_INFO_TYPE_ENTRY:
gmenu_tree_item_unref (info->entry);
break;
case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
g_key_file_free (info->keyfile);
g_free (info->keyfile_path);
break;
case SHELL_APP_INFO_TYPE_WINDOW:
g_object_unref (info->window);
g_free (info->window_id);
break;
}
g_slice_free (ShellAppInfo, info);
}
static ShellAppInfo *
shell_app_info_new_from_tree_item (GMenuTreeItem *item)
{
ShellAppInfo *info;
if (!item)
return NULL;
info = g_slice_alloc (sizeof (ShellAppInfo));
info->type = SHELL_APP_INFO_TYPE_ENTRY;
info->refcount = 1;
info->entry = gmenu_tree_item_ref (item);
return info;
}
static ShellAppInfo *
shell_app_info_new_from_window (MetaWindow *window)
{
ShellAppInfo *info;
info = g_slice_alloc (sizeof (ShellAppInfo));
info->type = SHELL_APP_INFO_TYPE_WINDOW;
info->refcount = 1;
info->window = g_object_ref (window);
/* For windows, its id is simply its pointer address as a string.
* There are various other alternatives, but the address is unique
* and unchanging, which is pretty much the best we can do.
*/
info->window_id = g_strdup_printf ("window:%p", window);
return info;
}
static ShellAppInfo *
shell_app_info_new_from_keyfile_take_ownership (GKeyFile *keyfile,
const char *path)
{
ShellAppInfo *info;
info = g_slice_alloc (sizeof (ShellAppInfo));
info->type = SHELL_APP_INFO_TYPE_DESKTOP_FILE;
info->refcount = 1;
info->keyfile = keyfile;
info->keyfile_path = g_strdup (path);
return info;
}
static gpointer
shell_app_menu_entry_copy (gpointer entryp)
{
@@ -225,21 +105,15 @@ shell_app_system_init (ShellAppSystem *self)
SHELL_TYPE_APP_SYSTEM,
ShellAppSystemPrivate);
/* The key is owned by the value */
priv->app_id_to_app = g_hash_table_new_full (g_str_hash, g_str_equal,
NULL, (GDestroyNotify) shell_app_info_unref);
priv->cached_favorites = g_hash_table_new_full (g_str_hash, g_str_equal,
(GDestroyNotify)g_free,
NULL);
/* For now, we want to pick up Evince, Nautilus, etc. We'll
* handle NODISPLAY semantics at a higher level or investigate them
* case by case.
*/
priv->apps_tree = gmenu_tree_lookup ("applications.menu", GMENU_TREE_FLAGS_INCLUDE_NODISPLAY);
priv->apps_tree = gmenu_tree_lookup ("applications.menu", GMENU_TREE_FLAGS_NONE);
priv->settings_tree = gmenu_tree_lookup ("settings.menu", GMENU_TREE_FLAGS_NONE);
priv->app_change_timeout_id = 0;
gmenu_tree_add_monitor (priv->apps_tree, on_tree_changed_cb, self);
gmenu_tree_add_monitor (priv->settings_tree, on_tree_changed_cb, self);
gmenu_tree_add_monitor (priv->apps_tree, on_tree_changed, self);
gmenu_tree_add_monitor (priv->settings_tree, on_tree_changed, self);
reread_menus (self);
@@ -256,23 +130,21 @@ shell_app_system_finalize (GObject *object)
ShellAppSystem *self = SHELL_APP_SYSTEM (object);
ShellAppSystemPrivate *priv = self->priv;
gmenu_tree_remove_monitor (priv->apps_tree, on_tree_changed_cb, self);
gmenu_tree_remove_monitor (priv->settings_tree, on_tree_changed_cb, self);
gmenu_tree_remove_monitor (priv->apps_tree, on_tree_changed, self);
gmenu_tree_remove_monitor (priv->settings_tree, on_tree_changed, self);
gmenu_tree_unref (priv->apps_tree);
gmenu_tree_unref (priv->settings_tree);
g_hash_table_destroy (priv->app_id_to_app);
g_slist_foreach (priv->cached_app_menus, (GFunc)shell_app_menu_entry_free, NULL);
g_slist_free (priv->cached_app_menus);
priv->cached_app_menus = NULL;
g_slist_foreach (priv->cached_settings, (GFunc)shell_app_info_unref, NULL);
g_slist_free (priv->cached_settings);
priv->cached_settings = NULL;
g_slist_foreach (priv->cached_setting_ids, (GFunc)g_free, NULL);
g_slist_free (priv->cached_setting_ids);
priv->cached_setting_ids = NULL;
g_list_free (priv->cached_favorites);
g_hash_table_destroy (priv->cached_favorites);
gconf_client_notify_remove (gconf_client_get_default (), priv->app_monitor_id);
@@ -324,7 +196,7 @@ reread_directories (ShellAppSystem *self, GSList **cache, GMenuTree *tree)
static GSList *
gather_entries_recurse (ShellAppSystem *monitor,
GSList *apps,
GSList *ids,
GMenuTreeDirectory *root)
{
GSList *contents;
@@ -339,14 +211,15 @@ gather_entries_recurse (ShellAppSystem *monitor,
{
case GMENU_TREE_ITEM_ENTRY:
{
ShellAppInfo *app = shell_app_info_new_from_tree_item (item);
apps = g_slist_prepend (apps, app);
GMenuTreeEntry *entry = (GMenuTreeEntry *)item;
const char *id = gmenu_tree_entry_get_desktop_file_id (entry);
ids = g_slist_prepend (ids, g_strdup (id));
}
break;
case GMENU_TREE_ITEM_DIRECTORY:
{
GMenuTreeDirectory *dir = (GMenuTreeDirectory*)item;
apps = gather_entries_recurse (monitor, apps, dir);
ids = gather_entries_recurse (monitor, ids, dir);
}
break;
default:
@@ -357,7 +230,7 @@ gather_entries_recurse (ShellAppSystem *monitor,
g_slist_free (contents);
return apps;
return ids;
}
static void
@@ -369,7 +242,7 @@ reread_entries (ShellAppSystem *self,
trunk = gmenu_tree_get_root_directory (tree);
g_slist_foreach (*cache, (GFunc)shell_app_info_unref, NULL);
g_slist_foreach (*cache, (GFunc)g_free, NULL);
g_slist_free (*cache);
*cache = NULL;
@@ -378,84 +251,29 @@ reread_entries (ShellAppSystem *self,
gmenu_tree_item_unref (trunk);
}
static void
cache_by_id (ShellAppSystem *self, GSList *apps, gboolean ref)
{
GSList *iter;
for (iter = apps; iter; iter = iter->next)
{
ShellAppInfo *info = iter->data;
if (ref)
shell_app_info_ref (info);
/* the name is owned by the info itself */
g_hash_table_insert (self->priv->app_id_to_app, (char*)shell_app_info_get_id (info),
info);
}
}
static void
reread_menus (ShellAppSystem *self)
{
GSList *apps;
GMenuTreeDirectory *trunk;
reread_directories (self, &(self->priv->cached_app_menus), self->priv->apps_tree);
reread_entries (self, &(self->priv->cached_settings), self->priv->settings_tree);
/* Now loop over applications.menu and settings.menu, inserting each by desktop file
* ID into a hash */
g_hash_table_remove_all (self->priv->app_id_to_app);
trunk = gmenu_tree_get_root_directory (self->priv->apps_tree);
apps = gather_entries_recurse (self, NULL, trunk);
gmenu_tree_item_unref (trunk);
cache_by_id (self, apps, FALSE);
g_slist_free (apps);
cache_by_id (self, self->priv->cached_settings, TRUE);
reread_entries (self, &(self->priv->cached_setting_ids), self->priv->settings_tree);
}
static void
on_tree_changed (gpointer user_data)
on_tree_changed (GMenuTree *monitor, gpointer user_data)
{
ShellAppSystem *self = SHELL_APP_SYSTEM (user_data);
g_signal_emit (self, signals[INSTALLED_CHANGED], 0);
reread_menus (self);
self->priv->app_change_timeout_id = 0;
}
static void
on_tree_changed_cb (GMenuTree *monitor, gpointer user_data)
{
ShellAppSystem *self = SHELL_APP_SYSTEM (user_data);
/* GMenu currently gives us a separate notification on the entire
* menu tree for each node in the tree that might potentially have
* changed. (See http://bugzilla.gnome.org/show_bug.cgi?id=172046.)
* We need to compress these to avoid doing large extra amounts of
* work.
*
* Even when that bug is fixed, compression is still useful; for one
* thing we want to need to compress across notifications of changes
* to the settings tree. Second we want to compress if multiple
* changes are made to the desktop files at different times but in
* short succession.
*/
if (self->priv->app_change_timeout_id != 0)
return;
self->priv->app_change_timeout_id = g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE, 3000,
(GSourceFunc) on_tree_changed,
self, NULL);
}
static GList *
convert_gconf_value_string_list_to_list_uniquify (GConfValue *value )
copy_gconf_value_string_list_to_hashset (GConfValue *value,
GHashTable *dest)
{
GSList *list;
GSList *tmp;
GList *result = NULL;
GHashTable *tmp_table = g_hash_table_new (g_str_hash, g_str_equal);
list = gconf_value_get_list (value);
@@ -465,16 +283,8 @@ convert_gconf_value_string_list_to_list_uniquify (GConfValue *value )
char *str = g_strdup (gconf_value_get_string (value));
if (!str)
continue;
if (g_hash_table_lookup (tmp_table, str))
{
g_free (str);
continue;
}
g_hash_table_insert (tmp_table, str, GUINT_TO_POINTER(1));
result = g_list_prepend (result, str);
g_hash_table_insert (dest, str, GUINT_TO_POINTER(1));
}
g_hash_table_destroy (tmp_table);
return g_list_reverse (result);
}
static void
@@ -488,9 +298,8 @@ reread_favorite_apps (ShellAppSystem *system)
if (!(val && val->type == GCONF_VALUE_LIST && gconf_value_get_list_type (val) == GCONF_VALUE_STRING))
return;
g_list_foreach (system->priv->cached_favorites, (GFunc) g_free, NULL);
g_list_free (system->priv->cached_favorites);
system->priv->cached_favorites = convert_gconf_value_string_list_to_list_uniquify (val);
g_hash_table_remove_all (system->priv->cached_favorites);
copy_gconf_value_string_list_to_hashset (val, system->priv->cached_favorites);
gconf_value_free (val);
}
@@ -506,19 +315,6 @@ on_favorite_apps_changed (GConfClient *client,
g_signal_emit (G_OBJECT (system), signals[FAVORITES_CHANGED], 0);
}
GType
shell_app_info_get_type (void)
{
static GType gtype = G_TYPE_INVALID;
if (gtype == G_TYPE_INVALID)
{
gtype = g_boxed_type_register_static ("ShellAppInfo",
(GBoxedCopyFunc)shell_app_info_ref,
(GBoxedFreeFunc)shell_app_info_unref);
}
return gtype;
}
GType
shell_app_menu_entry_get_type (void)
{
@@ -538,7 +334,7 @@ shell_app_menu_entry_get_type (void)
* Traverses a toplevel menu, and returns all items under it. Nested items
* are flattened.
*
* Return value: (transfer container) (element-type ShellAppInfo): List of applications
* Return value: (transfer full) (element-type utf8): List of desktop file ids
*/
GSList *
shell_app_system_get_applications_for_menu (ShellAppSystem *monitor,
@@ -563,7 +359,7 @@ shell_app_system_get_applications_for_menu (ShellAppSystem *monitor,
/**
* shell_app_system_get_menus:
*
* Returns a list of toplevel #ShellAppMenuEntry items
* Returns a list of toplevel menu names, like "Accessories", "Programming", etc.
*
* Return value: (transfer none) (element-type AppMenuEntry): List of toplevel menus
*/
@@ -576,14 +372,14 @@ shell_app_system_get_menus (ShellAppSystem *monitor)
/**
* shell_app_system_get_all_settings:
*
* Returns a list of application items under "settings.menu".
* Returns a list of all desktop file ids under "settings.menu".
*
* Return value: (transfer none) (element-type ShellAppInfo): List of applications
* Return value: (transfer none) (element-type utf8): List of desktop file ids
*/
GSList *
shell_app_system_get_all_settings (ShellAppSystem *monitor)
{
return monitor->priv->cached_settings;
return monitor->priv->cached_setting_ids;
}
/**
@@ -608,12 +404,12 @@ shell_app_system_get_default ()
* Return the list of applications which have been explicitly added to the
* favorites.
*
* Return value: (transfer none) (element-type utf8): List of favorite application ids
* Return value: (transfer container) (element-type utf8): List of favorite application ids
*/
GList *
shell_app_system_get_favorites (ShellAppSystem *system)
{
return system->priv->cached_favorites;
return g_hash_table_get_keys (system->priv->cached_favorites);
}
static void
@@ -640,18 +436,17 @@ shell_app_system_add_favorite (ShellAppSystem *system, const char *id)
{
GConfClient *client = gconf_client_get_default ();
GConfValue *val;
GList *iter;
iter = g_list_find_custom (system->priv->cached_favorites, id, (GCompareFunc)strcmp);
if (iter)
return;
GList *favorites;
val = gconf_value_new (GCONF_VALUE_LIST);
gconf_value_set_list_type (val, GCONF_VALUE_STRING);
system->priv->cached_favorites = g_list_append (system->priv->cached_favorites, g_strdup (id));
g_hash_table_insert (system->priv->cached_favorites, g_strdup (id), GUINT_TO_POINTER (1));
favorites = g_hash_table_get_keys (system->priv->cached_favorites);
set_gconf_value_string_list (val, favorites);
g_list_free (favorites);
set_gconf_value_string_list (val, system->priv->cached_favorites);
gconf_client_set (client, SHELL_APP_FAVORITES_KEY, val, NULL);
}
@@ -660,416 +455,102 @@ shell_app_system_remove_favorite (ShellAppSystem *system, const char *id)
{
GConfClient *client = gconf_client_get_default ();
GConfValue *val;
GList *iter;
GList *favorites;
iter = g_list_find_custom (system->priv->cached_favorites, id, (GCompareFunc)strcmp);
if (!iter)
if (!g_hash_table_remove (system->priv->cached_favorites, id))
return;
g_free (iter->data);
system->priv->cached_favorites = g_list_delete_link (system->priv->cached_favorites, iter);
val = gconf_value_new (GCONF_VALUE_LIST);
gconf_value_set_list_type (val, GCONF_VALUE_STRING);
set_gconf_value_string_list (val, system->priv->cached_favorites);
favorites = g_hash_table_get_keys (system->priv->cached_favorites);
set_gconf_value_string_list (val, favorites);
g_list_free (favorites);
gconf_client_set (client, SHELL_APP_FAVORITES_KEY, val, NULL);
}
/**
* shell_app_system_lookup_app:
*
* Return value: (transfer full): The #ShellAppInfo for id, or %NULL if none
*/
ShellAppInfo *
shell_app_system_lookup_cached_app (ShellAppSystem *self, const char *id)
static gboolean
desktop_id_exists (ShellAppSystem *system,
const char *target_id,
GMenuTreeDirectory *root)
{
ShellAppInfo *info;
gboolean found = FALSE;
GSList *contents, *iter;
info = g_hash_table_lookup (self->priv->app_id_to_app, id);
if (info)
shell_app_info_ref (info);
return info;
}
contents = gmenu_tree_directory_get_contents (root);
ShellAppInfo *
shell_app_system_load_from_desktop_file (ShellAppSystem *system,
const char *filename,
GError **error)
{
ShellAppInfo *appinfo;
GKeyFile *keyfile;
char *full_path = NULL;
gboolean success;
keyfile = g_key_file_new ();
if (strchr (filename, '/') != NULL)
for (iter = contents; iter; iter = iter->next)
{
success = g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, error);
full_path = g_strdup (filename);
}
else
{
char *app_path = g_build_filename ("applications", filename, NULL);
success = g_key_file_load_from_data_dirs (keyfile, app_path, &full_path,
G_KEY_FILE_NONE, error);
g_free (app_path);
GMenuTreeItem *item = iter->data;
if (found)
break;
switch (gmenu_tree_item_get_type (item))
{
case GMENU_TREE_ITEM_ENTRY:
{
GMenuTreeEntry *entry = (GMenuTreeEntry *)item;
const char *id = gmenu_tree_entry_get_desktop_file_id (entry);
if (strcmp (id, target_id) == 0)
found = TRUE;
}
break;
case GMENU_TREE_ITEM_DIRECTORY:
{
GMenuTreeDirectory *dir = (GMenuTreeDirectory*)item;
found = desktop_id_exists (system, target_id, dir);
}
break;
default:
break;
}
gmenu_tree_item_unref (item);
}
if (!success)
{
g_key_file_free (keyfile);
g_free (full_path);
return NULL;
}
g_slist_free (contents);
appinfo = shell_app_info_new_from_keyfile_take_ownership (keyfile, full_path);
g_free (full_path);
return appinfo;
return found;
}
/**
* shell_app_system_create_from_window:
*
* In the case where we can't otherwise determine an application
* associated with a window, this function can create a "fake"
* application just backed by information from the window itself.
*
* Return value: (transfer full): A new #ShellAppInfo
*/
ShellAppInfo *
shell_app_system_create_from_window (ShellAppSystem *system, MetaWindow *window)
{
return shell_app_info_new_from_window (window);
}
/**
* shell_app_system_lookup_heuristic_basename:
* shell_app_system_lookup_basename:
* @name: Probable application identifier
*
* Find a valid application corresponding to a given
* Determine whether a valid .desktop file ID corresponding to a given
* heuristically determined application identifier
* string, or %NULL if none.
*
* Returns: (transfer full): A #ShellAppInfo for name
* string.
*/
ShellAppInfo *
shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
const char *name)
char *
shell_app_system_lookup_basename (ShellAppSystem *system,
const char *name)
{
ShellAppInfo *result;
char **vendor_prefixes;
GMenuTreeDirectory *root;
char *result;
result = shell_app_system_lookup_cached_app (system, name);
if (result != NULL)
return result;
root = gmenu_tree_get_directory_from_path (system->priv->apps_tree, "/");
g_assert (root != NULL);
for (vendor_prefixes = (char**)known_vendor_prefixes;
*vendor_prefixes; vendor_prefixes++)
if (desktop_id_exists (system, name, root))
{
char *tmpid = g_strjoin (NULL, *vendor_prefixes, "-", name, NULL);
result = shell_app_system_lookup_cached_app (system, tmpid);
g_free (tmpid);
if (result != NULL)
return result;
result = g_strdup (name);
goto out;
}
return NULL;
}
const char *
shell_app_info_get_id (ShellAppInfo *info)
{
switch (info->type)
{
case SHELL_APP_INFO_TYPE_ENTRY:
return gmenu_tree_entry_get_desktop_file_id ((GMenuTreeEntry*)info->entry);
case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
return info->keyfile_path;
case SHELL_APP_INFO_TYPE_WINDOW:
return info->window_id;
}
g_assert_not_reached ();
return NULL;
}
#define DESKTOP_ENTRY_GROUP "Desktop Entry"
char *
shell_app_info_get_name (ShellAppInfo *info)
{
switch (info->type)
{
case SHELL_APP_INFO_TYPE_ENTRY:
return g_strdup (gmenu_tree_entry_get_name ((GMenuTreeEntry*)info->entry));
case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
return g_key_file_get_locale_string (info->keyfile, DESKTOP_ENTRY_GROUP, "Name", NULL, NULL);
case SHELL_APP_INFO_TYPE_WINDOW:
{
char *title;
g_object_get (info->window, "title", &title, NULL);
return title;
}
}
g_assert_not_reached ();
return NULL;
}
char *
shell_app_info_get_description (ShellAppInfo *info)
{
switch (info->type)
{
case SHELL_APP_INFO_TYPE_ENTRY:
return g_strdup (gmenu_tree_entry_get_comment ((GMenuTreeEntry*)info->entry));
case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
return g_key_file_get_locale_string (info->keyfile, DESKTOP_ENTRY_GROUP, "Comment", NULL, NULL);
case SHELL_APP_INFO_TYPE_WINDOW:
return NULL;
}
g_assert_not_reached ();
return NULL;
}
char *
shell_app_info_get_executable (ShellAppInfo *info)
{
switch (info->type)
{
case SHELL_APP_INFO_TYPE_ENTRY:
return g_strdup (gmenu_tree_entry_get_exec ((GMenuTreeEntry*)info->entry));
case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
return g_key_file_get_string (info->keyfile, DESKTOP_ENTRY_GROUP, "Exec", NULL);
case SHELL_APP_INFO_TYPE_WINDOW:
return NULL;
}
g_assert_not_reached ();
return NULL;
}
char *
shell_app_info_get_desktop_file_path (ShellAppInfo *info)
{
switch (info->type)
{
case SHELL_APP_INFO_TYPE_ENTRY:
return g_strdup (gmenu_tree_entry_get_desktop_file_path ((GMenuTreeEntry*)info->entry));
case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
return g_strdup (info->keyfile_path);
case SHELL_APP_INFO_TYPE_WINDOW:
return NULL;
}
g_assert_not_reached ();
return NULL;
}
static GIcon *
themed_icon_from_name (const char *iconname)
{
GIcon *icon;
if (!iconname)
return NULL;
if (g_path_is_absolute (iconname))
{
GFile *file;
file = g_file_new_for_path (iconname);
icon = G_ICON (g_file_icon_new (file));
g_object_unref (file);
}
else
{
char *tmp_name, *p;
tmp_name = strdup (iconname);
/* Work around a common mistake in desktop files */
if ((p = strrchr (tmp_name, '.')) != NULL &&
(strcmp (p, ".png") == 0 ||
strcmp (p, ".xpm") == 0 ||
strcmp (p, ".svg") == 0))
{
*p = 0;
}
icon = g_themed_icon_new (tmp_name);
g_free (tmp_name);
}
return icon;
}
static GIcon *
shell_app_info_get_icon (ShellAppInfo *info)
{
char *iconname = NULL;
GIcon *icon;
/* This code adapted from gdesktopappinfo.c
* Copyright (C) 2006-2007 Red Hat, Inc.
* Copyright © 2007 Ryan Lortie
* LGPL
/* These are common "vendor prefixes". But using
* WM_CLASS as a source, we don't get the vendor
* prefix. So try stripping them.
*/
result = g_strjoin ("", "gnome-", name, NULL);
if (desktop_id_exists (system, result, root))
goto out;
switch (info->type)
{
case SHELL_APP_INFO_TYPE_ENTRY:
return themed_icon_from_name (gmenu_tree_entry_get_icon ((GMenuTreeEntry*)info->entry));
case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
iconname = g_key_file_get_locale_string (info->keyfile, DESKTOP_ENTRY_GROUP, "Icon", NULL, NULL);
icon = themed_icon_from_name (iconname);
g_free (iconname);
return icon;
break;
case SHELL_APP_INFO_TYPE_WINDOW:
return NULL;
}
g_assert_not_reached ();
return NULL;
}
GSList *
shell_app_info_get_categories (ShellAppInfo *info)
{
return NULL; /* TODO */
}
gboolean
shell_app_info_get_is_nodisplay (ShellAppInfo *info)
{
switch (info->type)
{
case SHELL_APP_INFO_TYPE_ENTRY:
return gmenu_tree_entry_get_is_nodisplay ((GMenuTreeEntry*)info->entry);
case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
case SHELL_APP_INFO_TYPE_WINDOW:
return FALSE;
}
g_assert_not_reached ();
return TRUE;
}
/**
* shell_app_info_is_transient:
*
* A "transient" application is one which represents
* just an open window, i.e. we don't know how to launch it
* again.
*/
gboolean
shell_app_info_is_transient (ShellAppInfo *info)
{
return info->type == SHELL_APP_INFO_TYPE_WINDOW;
}
/**
* shell_app_info_create_icon_texture:
*
* Look up the icon for this application, and create a #ClutterTexture
* for it at the given size.
*
* Return value: (transfer none): A floating #ClutterActor
*/
ClutterActor *
shell_app_info_create_icon_texture (ShellAppInfo *info, float size)
{
GIcon *icon;
ClutterActor *ret;
if (info->type == SHELL_APP_INFO_TYPE_WINDOW)
{
return shell_texture_cache_bind_pixbuf_property (shell_texture_cache_get_default (),
G_OBJECT (info->window),
"icon");
}
icon = shell_app_info_get_icon (info);
if (!icon)
{
ret = clutter_texture_new ();
g_object_set (ret, "opacity", 0, "width", size, "height", size, NULL);
return ret;
}
return shell_texture_cache_load_gicon (shell_texture_cache_get_default (), icon, (int)size);
}
/**
* shell_app_info_launch_full:
* @timestamp: Event timestamp, or 0 for current event timestamp
* @uris: List of uris to pass to application
* @workspace: Start on this workspace, or -1 for default
* @startup_id: (out): Returned startup notification ID, or %NULL if none
* @error: A #GError
*/
gboolean
shell_app_info_launch_full (ShellAppInfo *info,
guint timestamp,
GList *uris,
int workspace,
char **startup_id,
GError **error)
{
GDesktopAppInfo *gapp;
char *filename;
GdkAppLaunchContext *context;
gboolean ret;
ShellGlobal *global;
MetaScreen *screen;
MetaDisplay *display;
if (startup_id)
*startup_id = NULL;
if (info->type == SHELL_APP_INFO_TYPE_WINDOW)
{
/* We can't pass URIs into a window; shouldn't hit this
* code path. If we do, fix the caller to disallow it.
*/
g_return_val_if_fail (uris == NULL, TRUE);
meta_window_activate (info->window, timestamp);
return TRUE;
}
filename = shell_app_info_get_desktop_file_path (info);
gapp = g_desktop_app_info_new_from_filename (filename);
g_free (filename);
if (!gapp)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Not found");
return FALSE;
}
global = shell_global_get ();
screen = shell_global_get_screen (global);
display = meta_screen_get_display (screen);
if (timestamp == 0)
timestamp = clutter_get_current_event_time ();
/* Shell design calls for on application launch, no window is focused,
* and we have startup notification displayed.
*/
meta_display_focus_the_no_focus_window (display, screen, timestamp);
if (workspace < 0)
workspace = meta_screen_get_active_workspace_index (screen);
context = gdk_app_launch_context_new ();
gdk_app_launch_context_set_timestamp (context, timestamp);
gdk_app_launch_context_set_desktop (context, workspace);
ret = g_app_info_launch (G_APP_INFO (gapp), uris, (GAppLaunchContext*) context, error);
g_object_unref (G_OBJECT (gapp));
return ret;
}
gboolean
shell_app_info_launch (ShellAppInfo *info,
GError **error)
{
return shell_app_info_launch_full (info, 0, NULL, -1, NULL, error);
result = g_strjoin ("", "fedora-", name, NULL);
if (desktop_id_exists (system, result, root))
goto out;
out:
gmenu_tree_item_unref (root);
return result;
}

View File

@@ -1,10 +1,7 @@
#ifndef __SHELL_APP_SYSTEM_H__
#define __SHELL_APP_SYSTEM_H__
#include <gio/gio.h>
#include <clutter/clutter.h>
#include "window.h"
#include <glib-object.h>
#define SHELL_TYPE_APP_SYSTEM (shell_app_system_get_type ())
#define SHELL_APP_SYSTEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_APP_SYSTEM, ShellAppSystem))
@@ -47,39 +44,7 @@ struct _ShellAppMenuEntry {
GType shell_app_menu_entry_get_type (void);
typedef struct _ShellAppInfo ShellAppInfo;
#define SHELL_TYPE_APP_INFO (shell_app_info_get_type ())
GType shell_app_info_get_type (void);
ShellAppInfo* shell_app_info_ref (ShellAppInfo *info);
void shell_app_info_unref (ShellAppInfo *info);
const char *shell_app_info_get_id (ShellAppInfo *info);
char *shell_app_info_get_name (ShellAppInfo *info);
char *shell_app_info_get_description (ShellAppInfo *info);
char *shell_app_info_get_executable (ShellAppInfo *info);
char *shell_app_info_get_desktop_file_path (ShellAppInfo *info);
ClutterActor *shell_app_info_create_icon_texture (ShellAppInfo *info, float size);
GSList *shell_app_info_get_categories (ShellAppInfo *info);
gboolean shell_app_info_get_is_nodisplay (ShellAppInfo *info);
gboolean shell_app_info_is_transient (ShellAppInfo *info);
gboolean shell_app_info_launch_full (ShellAppInfo *info,
guint timestamp,
GList *uris,
int workspace,
char **startup_id,
GError **error);
gboolean shell_app_info_launch (ShellAppInfo *info,
GError **error);
ShellAppInfo *shell_app_system_load_from_desktop_file (ShellAppSystem *system, const char *filename, GError **error);
ShellAppInfo *shell_app_system_lookup_cached_app (ShellAppSystem *system, const char *id);
ShellAppInfo *shell_app_system_lookup_heuristic_basename (ShellAppSystem *system, const char *id);
ShellAppInfo *shell_app_system_create_from_window (ShellAppSystem *system, MetaWindow *window);
char * shell_app_system_lookup_basename (ShellAppSystem *system, const char *id);
GSList *shell_app_system_get_menus (ShellAppSystem *system);

View File

@@ -1,316 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/**
* SECTION:shell-button-box
* @short_description: A box with properties useful for implementing buttons
*
* A #BigBox subclass which translates lower-level Clutter button events
* into higher level properties which are useful for implementing "button-like"
* actors.
*/
#include "shell-button-box.h"
G_DEFINE_TYPE(ShellButtonBox, shell_button_box, BIG_TYPE_BOX);
struct _ShellButtonBoxPrivate {
gboolean active;
gboolean held;
gboolean hover;
gboolean pressed;
};
/* Signals */
enum
{
ACTIVATE,
LAST_SIGNAL
};
enum {
PROP_0,
PROP_ACTIVE,
PROP_HOVER,
PROP_PRESSED,
};
static guint shell_button_box_signals [LAST_SIGNAL] = { 0 };
static void
set_active (ShellButtonBox *box,
gboolean active)
{
if (box->priv->active == active)
return;
box->priv->active = active;
g_object_notify (G_OBJECT (box), "active");
}
static void
set_hover (ShellButtonBox *box,
gboolean hover)
{
if (box->priv->hover == hover)
return;
box->priv->hover = hover;
g_object_notify (G_OBJECT (box), "hover");
}
static void
set_pressed (ShellButtonBox *box,
gboolean pressed)
{
if (box->priv->pressed == pressed)
return;
box->priv->pressed = pressed;
g_object_notify (G_OBJECT (box), "pressed");
}
static gboolean
shell_button_box_contains (ShellButtonBox *box,
ClutterActor *actor)
{
while (actor != NULL && actor != (ClutterActor*)box)
{
actor = clutter_actor_get_parent (actor);
}
return actor != NULL;
}
static gboolean
shell_button_box_enter_event (ClutterActor *actor,
ClutterCrossingEvent *event)
{
ShellButtonBox *box = SHELL_BUTTON_BOX (actor);
if (shell_button_box_contains (box, event->related))
return TRUE;
if (!shell_button_box_contains (box, event->source))
return TRUE;
g_object_freeze_notify (G_OBJECT (actor));
if (box->priv->held)
set_pressed (box, TRUE);
set_hover (box, TRUE);
g_object_thaw_notify (G_OBJECT (actor));
return TRUE;
}
static gboolean
shell_button_box_leave_event (ClutterActor *actor,
ClutterCrossingEvent *event)
{
ShellButtonBox *box = SHELL_BUTTON_BOX (actor);
if (shell_button_box_contains (box, event->related))
return TRUE;
set_hover (box, FALSE);
set_pressed (box, FALSE);
return TRUE;
}
static gboolean
shell_button_box_button_press_event (ClutterActor *actor,
ClutterButtonEvent *event)
{
ShellButtonBox *box = SHELL_BUTTON_BOX (actor);
if (event->button != 1 || event->click_count != 1)
return FALSE;
if (box->priv->held)
return TRUE;
if (!shell_button_box_contains (box, event->source))
return FALSE;
box->priv->held = TRUE;
clutter_grab_pointer (CLUTTER_ACTOR (box));
set_pressed (box, TRUE);
return TRUE;
}
static gboolean
shell_button_box_button_release_event (ClutterActor *actor,
ClutterButtonEvent *event)
{
ShellButtonBox *box = SHELL_BUTTON_BOX (actor);
if (event->button != 1 || event->click_count != 1)
return FALSE;
if (!box->priv->held)
return TRUE;
box->priv->held = FALSE;
clutter_ungrab_pointer ();
if (!shell_button_box_contains (box, event->source))
return FALSE;
set_pressed (box, FALSE);
g_signal_emit (G_OBJECT (box), shell_button_box_signals[ACTIVATE], 0);
return TRUE;
}
/**
* shell_button_box_fake_release:
* @box:
*
* If this button box is holding a pointer grab, this function will
* will ungrab it, and reset the pressed state. The effect is
* similar to if the user had released the mouse button, but without
* emitting the activate signal.
*
* This function is useful if for example you want to do something after the user
* is holding the mouse button for a given period of time, breaking the
* grab.
*/
void
shell_button_box_fake_release (ShellButtonBox *box)
{
if (!box->priv->held)
return;
box->priv->held = FALSE;
clutter_ungrab_pointer ();
set_pressed (box, FALSE);
}
static void
shell_button_box_set_property(GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
ShellButtonBox *box = SHELL_BUTTON_BOX (object);
switch (prop_id)
{
case PROP_ACTIVE:
set_active (box, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
shell_button_box_get_property(GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
ShellButtonBox *box = SHELL_BUTTON_BOX (object);
switch (prop_id)
{
case PROP_ACTIVE:
g_value_set_boolean (value, box->priv->active);
break;
case PROP_PRESSED:
g_value_set_boolean (value, box->priv->pressed);
break;
case PROP_HOVER:
g_value_set_boolean (value, box->priv->hover);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
shell_button_box_class_init (ShellButtonBoxClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
gobject_class->get_property = shell_button_box_get_property;
gobject_class->set_property = shell_button_box_set_property;
actor_class->enter_event = shell_button_box_enter_event;
actor_class->leave_event = shell_button_box_leave_event;
actor_class->button_press_event = shell_button_box_button_press_event;
actor_class->button_release_event = shell_button_box_button_release_event;
/**
* ShellButtonBox::activate
* @box: The #ShellButtonBox
*
* This signal is emitted when the button should take the action
* associated with button click+release.
*/
shell_button_box_signals[ACTIVATE] =
g_signal_new ("activate",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
/**
* ShellButtonBox:active
*
* The property allows the button to be used as a "toggle button"; it's up to the
* application to update the active property in response to the activate signal;
* it doesn't happen automatically.
*/
g_object_class_install_property (gobject_class,
PROP_ACTIVE,
g_param_spec_boolean ("active",
"Active",
"Whether the button persistently active",
FALSE,
G_PARAM_READWRITE));
/**
* ShellButtonBox:hover
*
* This property tracks whether the mouse is over the button; note this
* state is independent of whether the button is pressed.
*/
g_object_class_install_property (gobject_class,
PROP_HOVER,
g_param_spec_boolean ("hover",
"Hovering state",
"Whether the mouse is over the button",
FALSE,
G_PARAM_READABLE));
/**
* ShellButtonBox:pressed
*
* This property tracks whether the button should have a "pressed in"
* effect.
*/
g_object_class_install_property (gobject_class,
PROP_PRESSED,
g_param_spec_boolean ("pressed",
"Pressed state",
"Whether the button is currently pressed",
FALSE,
G_PARAM_READABLE));
g_type_class_add_private (gobject_class, sizeof (ShellButtonBoxPrivate));
}
static void
shell_button_box_init (ShellButtonBox *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, SHELL_TYPE_BUTTON_BOX,
ShellButtonBoxPrivate);
}

View File

@@ -1,35 +0,0 @@
#ifndef __SHELL_BUTTON_BOX_H__
#define __SHELL_BUTTON_BOX_H__
#include <clutter/clutter.h>
#include "big/box.h"
#define SHELL_TYPE_BUTTON_BOX (shell_button_box_get_type ())
#define SHELL_BUTTON_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_BUTTON_BOX, ShellButtonBox))
#define SHELL_BUTTON_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_BUTTON_BOX, ShellButtonBoxClass))
#define SHELL_IS_BUTTON_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_BUTTON_BOX))
#define SHELL_IS_BUTTON_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_BUTTON_BOX))
#define SHELL_BUTTON_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_BUTTON_BOX, ShellButtonBoxClass))
typedef struct _ShellButtonBox ShellButtonBox;
typedef struct _ShellButtonBoxClass ShellButtonBoxClass;
typedef struct _ShellButtonBoxPrivate ShellButtonBoxPrivate;
struct _ShellButtonBox
{
BigBox parent;
ShellButtonBoxPrivate *priv;
};
struct _ShellButtonBoxClass
{
BigBoxClass parent_class;
};
GType shell_button_box_get_type (void) G_GNUC_CONST;
void shell_button_box_fake_release (ShellButtonBox *box);
#endif /* __SHELL_BUTTON_BOX_H__ */

View File

@@ -1,102 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/**
* SECTION:shell-drawing-area
* @short_description: A dynamically-sized Cairo drawing area
*
* #ShellDrawingArea is similar to #ClutterCairoTexture in that
* it allows drawing via Cairo; the primary difference is that
* it is dynamically sized. To use, connect to the @redraw
* signal, and inside the signal handler, call
* clutter_cairo_texture_create() to begin drawing.
*/
#include "shell-drawing-area.h"
#include <clutter/clutter.h>
#include <gtk/gtk.h>
#include <cairo.h>
G_DEFINE_TYPE(ShellDrawingArea, shell_drawing_area, CLUTTER_TYPE_GROUP);
struct _ShellDrawingAreaPrivate {
ClutterCairoTexture *texture;
};
/* Signals */
enum
{
REDRAW,
LAST_SIGNAL
};
static guint shell_drawing_area_signals [LAST_SIGNAL] = { 0 };
static void
shell_drawing_area_allocate (ClutterActor *self,
const ClutterActorBox *box,
ClutterAllocationFlags flags)
{
ShellDrawingArea *area = SHELL_DRAWING_AREA (self);
int width = box->x2 - box->x1;
int height = box->y2 - box->y1;
ClutterActorBox child_box;
/* Chain up directly to ClutterActor to set actor->allocation. We explicitly skip our parent class
* ClutterGroup here because we want to override the allocate function. */
(CLUTTER_ACTOR_CLASS (g_type_class_peek (clutter_actor_get_type ())))->allocate (self, box, flags);
child_box.x1 = 0;
child_box.x2 = width;
child_box.y1 = 0;
child_box.y2 = height;
clutter_actor_allocate (CLUTTER_ACTOR (area->priv->texture), &child_box, flags);
if (width > 0 && height > 0)
{
clutter_cairo_texture_set_surface_size (area->priv->texture,
width, height);
g_signal_emit (G_OBJECT (self), shell_drawing_area_signals[REDRAW], 0,
area->priv->texture);
}
}
static void
shell_drawing_area_class_init (ShellDrawingAreaClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
actor_class->allocate = shell_drawing_area_allocate;
shell_drawing_area_signals[REDRAW] =
g_signal_new ("redraw",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ShellDrawingAreaClass, redraw),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1, G_TYPE_OBJECT);
g_type_class_add_private (gobject_class, sizeof (ShellDrawingAreaPrivate));
}
static void
shell_drawing_area_init (ShellDrawingArea *area)
{
area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area, SHELL_TYPE_DRAWING_AREA,
ShellDrawingAreaPrivate);
area->priv->texture = CLUTTER_CAIRO_TEXTURE (clutter_cairo_texture_new (1, 1));
clutter_container_add_actor (CLUTTER_CONTAINER (area), CLUTTER_ACTOR (area->priv->texture));
}
/**
* shell_drawing_area_get_texture:
*
* Return Value: (transfer none):
*/
ClutterCairoTexture *
shell_drawing_area_get_texture (ShellDrawingArea *area)
{
return area->priv->texture;
}

View File

@@ -1,37 +0,0 @@
#ifndef __SHELL_DRAWING_AREA_H__
#define __SHELL_DRAWING_AREA_H__
#include <clutter/clutter.h>
#include <gtk/gtk.h>
#define SHELL_TYPE_DRAWING_AREA (shell_drawing_area_get_type ())
#define SHELL_DRAWING_AREA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_DRAWING_AREA, ShellDrawingArea))
#define SHELL_DRAWING_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_DRAWING_AREA, ShellDrawingAreaClass))
#define SHELL_IS_DRAWING_AREA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_DRAWING_AREA))
#define SHELL_IS_DRAWING_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_DRAWING_AREA))
#define SHELL_DRAWING_AREA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_DRAWING_AREA, ShellDrawingAreaClass))
typedef struct _ShellDrawingArea ShellDrawingArea;
typedef struct _ShellDrawingAreaClass ShellDrawingAreaClass;
typedef struct _ShellDrawingAreaPrivate ShellDrawingAreaPrivate;
struct _ShellDrawingArea
{
ClutterGroup parent;
ShellDrawingAreaPrivate *priv;
};
struct _ShellDrawingAreaClass
{
ClutterGroupClass parent_class;
void (*redraw) (ShellDrawingArea *area, ClutterCairoTexture *texture);
};
GType shell_drawing_area_get_type (void) G_GNUC_CONST;
ClutterCairoTexture *shell_drawing_area_get_texture (ShellDrawingArea *area);
#endif /* __SHELL_DRAWING_AREA_H__ */

View File

@@ -1,182 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#include "shell-drawing.h"
#include <math.h>
/**
* shell_create_vertical_gradient:
* @top: the color at the top
* @bottom: the color at the bottom
*
* Creates a vertical gradient actor.
*
* Return value: (transfer none): a #ClutterCairoTexture actor with the
* gradient. The texture actor is floating, hence (transfer none).
*/
ClutterCairoTexture *
shell_create_vertical_gradient (ClutterColor *top,
ClutterColor *bottom)
{
ClutterCairoTexture *texture;
cairo_t *cr;
cairo_pattern_t *pattern;
/* Draw the gradient on an 8x8 pixel texture. Because the gradient is drawn
* from the uppermost to the lowermost row, after stretching 1/16 of the
* texture height has the top color and 1/16 has the bottom color. The 8
* pixel width is chosen for reasons related to graphics hardware internals.
*/
texture = CLUTTER_CAIRO_TEXTURE (clutter_cairo_texture_new (8, 8));
cr = clutter_cairo_texture_create (texture);
pattern = cairo_pattern_create_linear (0, 0, 0, 8);
cairo_pattern_add_color_stop_rgba (pattern, 0,
top->red / 255.,
top->green / 255.,
top->blue / 255.,
top->alpha / 255.);
cairo_pattern_add_color_stop_rgba (pattern, 1,
bottom->red / 255.,
bottom->green / 255.,
bottom->blue / 255.,
bottom->alpha / 255.);
cairo_set_source (cr, pattern);
cairo_paint (cr);
cairo_pattern_destroy (pattern);
cairo_destroy (cr);
return texture;
}
/**
* shell_create_horizontal_gradient:
* @left: the color on the left
* @right: the color on the right
*
* Creates a horizontal gradient actor.
*
* Return value: (transfer none): a #ClutterCairoTexture actor with the
* gradient. The texture actor is floating, hence (transfer none).
*/
ClutterCairoTexture *
shell_create_horizontal_gradient (ClutterColor *left,
ClutterColor *right)
{
ClutterCairoTexture *texture;
cairo_t *cr;
cairo_pattern_t *pattern;
/* Draw the gradient on an 8x1 pixel texture. Because the gradient is drawn
* from the left to the right column, after stretching 1/16 of the
* texture width has the left side color and 1/16 has the right side color.
* There is no reason to use the 8 pixel height that would be similar to the
* reason we are using the 8 pixel width for the vertical gradient, so we
* are just using the 1 pixel height instead.
*/
texture = CLUTTER_CAIRO_TEXTURE (clutter_cairo_texture_new (8, 1));
cr = clutter_cairo_texture_create (texture);
pattern = cairo_pattern_create_linear (0, 0, 8, 0);
cairo_pattern_add_color_stop_rgba (pattern, 0,
left->red / 255.,
left->green / 255.,
left->blue / 255.,
left->alpha / 255.);
cairo_pattern_add_color_stop_rgba (pattern, 1,
right->red / 255.,
right->green / 255.,
right->blue / 255.,
right->alpha / 255.);
cairo_set_source (cr, pattern);
cairo_paint (cr);
cairo_pattern_destroy (pattern);
cairo_destroy (cr);
return texture;
}
void
shell_draw_clock (ClutterCairoTexture *texture,
int hour,
int minute)
{
cairo_t *cr;
guint width, height;
double xc, yc, radius, hour_radius, minute_radius;
double angle;
clutter_cairo_texture_get_surface_size (texture, &width, &height);
xc = (double)width / 2;
yc = (double)height / 2;
radius = (double)(MIN(width, height)) / 2 - 2;
minute_radius = radius - 3;
hour_radius = radius / 2;
clutter_cairo_texture_clear (texture);
cr = clutter_cairo_texture_create (texture);
cairo_set_line_width (cr, 1.0);
/* Outline */
cairo_arc (cr, xc, yc, radius, 0.0, 2.0 * M_PI);
cairo_stroke (cr);
/* Hour hand. (We add a fraction to @hour for the minutes, then
* convert to radians, and then subtract pi/2 because cairo's origin
* is at 3:00, not 12:00.)
*/
angle = ((hour + minute / 60.0) / 12.0) * 2.0 * M_PI - M_PI / 2.0;
cairo_move_to (cr, xc, yc);
cairo_line_to (cr,
xc + hour_radius * cos (angle),
yc + hour_radius * sin (angle));
cairo_stroke (cr);
/* Minute hand */
angle = (minute / 60.0) * 2.0 * M_PI - M_PI / 2.0;
cairo_move_to (cr, xc, yc);
cairo_line_to (cr,
xc + minute_radius * cos (angle),
yc + minute_radius * sin (angle));
cairo_stroke (cr);
cairo_destroy (cr);
}
static void
hook_paint_red_border (ClutterActor *actor,
gpointer user_data)
{
CoglColor color;
ClutterGeometry geom;
float width = 2;
float x2;
float y2;
cogl_color_set_from_4ub (&color, 0xff, 0, 0, 0xc4);
cogl_set_source_color (&color);
clutter_actor_get_allocation_geometry (actor, &geom);
x2 = geom.x + geom.width;
y2 = geom.y + geom.height;
/** clockwise order **/
cogl_rectangle (geom.x, geom.y,
x2, geom.y + width);
cogl_rectangle (x2 - width, geom.y + width,
x2, y2);
cogl_rectangle (x2 - width, y2,
geom.x, y2 - width);
cogl_rectangle (geom.x + width, y2 - width,
geom.x, geom.y + width);
}
guint
shell_add_hook_paint_red_border (ClutterActor *actor)
{
return g_signal_connect_after (G_OBJECT (actor), "paint",
G_CALLBACK (hook_paint_red_border), NULL);
}

View File

@@ -1,24 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef __SHELL_DRAWING_H__
#define __SHELL_DRAWING_H__
#include <clutter/clutter.h>
G_BEGIN_DECLS
ClutterCairoTexture *shell_create_vertical_gradient (ClutterColor *top,
ClutterColor *bottom);
ClutterCairoTexture *shell_create_horizontal_gradient (ClutterColor *left,
ClutterColor *right);
void shell_draw_clock (ClutterCairoTexture *texture,
int hour,
int minute);
guint shell_add_hook_paint_red_border (ClutterActor *actor);
G_END_DECLS
#endif /* __SHELL_GLOBAL_H__ */

View File

@@ -1,396 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#include "shell-gconf.h"
#include <gconf/gconf-client.h>
#include <string.h>
/**
* ShellGConf:
*
* A wrapper around #GConfClient that cleans up some of its
* non-gjs-bindable bits and makes a few gnome-shell-specific
* assumptions.
*
* For all #ShellGConf methods that take a GConf key path as an
* argument, you can pass either a full path (eg,
* "/desktop/gnome/shell/sidebar/visible"), or just a relative path
* starting from the root of the gnome-shell GConf key hierarchy (eg,
* "sidebar/visible").
*/
struct _ShellGConf
{
GObject parent;
GConfClient *client;
};
G_DEFINE_TYPE (ShellGConf, shell_gconf, G_TYPE_OBJECT);
/* Signals */
enum
{
CHANGED,
LAST_SIGNAL
};
static guint shell_gconf_signals [LAST_SIGNAL] = { 0 };
static void gconf_value_changed (GConfClient *client, const char *key,
GConfValue *new_value, gpointer user_data);
static void
shell_gconf_init (ShellGConf *gconf)
{
gconf->client = gconf_client_get_default ();
gconf_client_add_dir (gconf->client, SHELL_GCONF_DIR,
GCONF_CLIENT_PRELOAD_RECURSIVE, NULL);
g_signal_connect (gconf->client, "value_changed",
G_CALLBACK (gconf_value_changed), gconf);
}
static void
shell_gconf_finalize (GObject *object)
{
ShellGConf *gconf = SHELL_GCONF (object);
g_signal_handlers_disconnect_by_func (gconf->client,
gconf_value_changed, gconf);
g_object_unref (gconf->client);
G_OBJECT_CLASS (shell_gconf_parent_class)->finalize (object);
}
static void
shell_gconf_class_init (ShellGConfClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = shell_gconf_finalize;
/**
* ShellGConf::changed:
* @gconf: the #ShellGConf
*
* Emitted when a key in a watched directory is changed. The signal
* detail indicates which key changed. Eg, connect to
* "changed::sidebar/visible" to be notified when "sidebar/visible"
* changes. For gnome-shell's own GConf keys, the signal detail will
* be the relative path from the top of the gnome-shell GConf
* hierarchy ("/desktop/gnome/shell"). If you want to be notified
* about the value of a non-gnome-shell key, you must first call
* shell_gconf_watch_directory(), and then use the full GConf key path
* as the signal detail.
*/
shell_gconf_signals[CHANGED] =
g_signal_new ("changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
G_STRUCT_OFFSET (ShellGConfClass, changed),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
/**
* shell_gconf_get_default:
*
* Gets the default #ShellGConf
*
* Return value: (transfer none): the default #ShellGConf
*/
ShellGConf *
shell_gconf_get_default (void)
{
static ShellGConf *gconf = NULL;
if (!gconf)
gconf = g_object_new (SHELL_TYPE_GCONF, NULL);
return gconf;
}
/**
* shell_gconf_watch_directory:
* @gconf: a #ShellGConf
* @directory: the path of a GConf directory to watch for changes in
*
* Adds @directory to the list of directories to watch; you must call
* this before connecting to #ShellGConf::changed for a key outside of
* the gnome-shell GConf tree.
*/
void
shell_gconf_watch_directory (ShellGConf *gconf, const char *directory)
{
gconf_client_add_dir (gconf->client, directory,
GCONF_CLIENT_PRELOAD_NONE, NULL);
}
static void
gconf_value_changed (GConfClient *client, const char *key,
GConfValue *new_value, gpointer user_data)
{
ShellGConf *gconf = user_data;
GQuark detail;
if (g_str_has_prefix (key, SHELL_GCONF_DIR "/"))
key += strlen (SHELL_GCONF_DIR "/");
/* This will create a lot of junk quarks, but it's the best we
* can do with gjs's current callback support.
*/
detail = g_quark_from_string (key);
g_signal_emit (gconf, shell_gconf_signals[CHANGED], detail);
}
static char *
resolve_key (const char *key)
{
if (*key == '/')
return g_strdup (key);
else
return g_build_filename (SHELL_GCONF_DIR, key, NULL);
}
#define SIMPLE_GETTER(NAME, TYPE, GCONF_GETTER) \
TYPE \
NAME (ShellGConf *gconf, const char *key, GError **error) \
{ \
char *get_key = resolve_key (key); \
TYPE value; \
\
value = GCONF_GETTER (gconf->client, get_key, error); \
g_free (get_key); \
return value; \
}
#define LIST_GETTER(NAME, ELEMENT_TYPE) \
GSList * \
NAME (ShellGConf *gconf, const char *key, GError **error) \
{ \
char *get_key = resolve_key (key); \
GSList *value; \
\
value = gconf_client_get_list (gconf->client, get_key, \
ELEMENT_TYPE, error); \
g_free (get_key); \
return value; \
}
/**
* shell_gconf_get_boolean:
* @gconf: a #ShellGConf
* @key: a GConf key (as described in the #ShellGConf docs)
* @error: a #GError, which will be set on error
*
* Gets the value of @key, which must be boolean-valued.
*
* Return value: @key's value. If an error occurs, @error will be set
* and the return value is undefined.
**/
SIMPLE_GETTER(shell_gconf_get_boolean, gboolean, gconf_client_get_bool)
/**
* shell_gconf_get_int:
* @gconf: a #ShellGConf
* @key: a GConf key (as described in the #ShellGConf docs)
* @error: a #GError, which will be set on error
*
* Gets the value of @key, which must be integer-valued.
*
* Return value: @key's value. If an error occurs, @error will be set
* and the return value is undefined.
**/
SIMPLE_GETTER(shell_gconf_get_int, int, gconf_client_get_int)
/**
* shell_gconf_get_float:
* @gconf: a #ShellGConf
* @key: a GConf key (as described in the #ShellGConf docs)
* @error: a #GError, which will be set on error
*
* Gets the value of @key, which must be float-valued.
*
* Return value: @key's value. If an error occurs, @error will be set
* and the return value is undefined.
**/
SIMPLE_GETTER(shell_gconf_get_float, float, gconf_client_get_float)
/**
* shell_gconf_get_string:
* @gconf: a #ShellGConf
* @key: a GConf key (as described in the #ShellGConf docs)
* @error: a #GError, which will be set on error
*
* Gets the value of @key, which must be string-valued.
*
* Return value: (transfer full): @key's value, or %NULL if an error
* occurs.
**/
SIMPLE_GETTER(shell_gconf_get_string, char *, gconf_client_get_string)
/**
* shell_gconf_get_boolean_list:
* @gconf: a #ShellGConf
* @key: a GConf key (as described in the #ShellGConf docs)
* @error: a #GError, which will be set on error
*
* Gets the value of @key, which must be boolean-list-valued.
*
* Return value: (element-type gboolean) (transfer full): @key's
* value, or %NULL if an error occurs.
**/
LIST_GETTER(shell_gconf_get_boolean_list, GCONF_VALUE_BOOL)
/**
* shell_gconf_get_int_list:
* @gconf: a #ShellGConf
* @key: a GConf key (as described in the #ShellGConf docs)
* @error: a #GError, which will be set on error
*
* Gets the value of @key, which must be integer-list-valued.
*
* Return value: (element-type int) (transfer full): @key's
* value, or %NULL if an error occurs.
**/
LIST_GETTER(shell_gconf_get_int_list, GCONF_VALUE_INT)
/**
* shell_gconf_get_float_list:
* @gconf: a #ShellGConf
* @key: a GConf key (as described in the #ShellGConf docs)
* @error: a #GError, which will be set on error
*
* Gets the value of @key, which must be float-list-valued.
*
* Return value: (element-type float) (transfer full): @key's
* value, or %NULL if an error occurs.
**/
LIST_GETTER(shell_gconf_get_float_list, GCONF_VALUE_FLOAT)
/**
* shell_gconf_get_string_list:
* @gconf: a #ShellGConf
* @key: a GConf key (as described in the #ShellGConf docs)
* @error: a #GError, which will be set on error
*
* Gets the value of @key, which must be string-list-valued.
*
* Return value: (element-type utf8) (transfer full): @key's
* value, or %NULL if an error occurs.
**/
LIST_GETTER(shell_gconf_get_string_list, GCONF_VALUE_STRING)
#define SIMPLE_SETTER(NAME, TYPE, GCONF_SETTER) \
void \
NAME (ShellGConf *gconf, const char *key, TYPE value, GError **error) \
{ \
char *set_key = resolve_key (key); \
\
GCONF_SETTER (gconf->client, set_key, value, error); \
g_free (set_key); \
}
#define LIST_SETTER(NAME, ELEMENT_TYPE) \
void \
NAME (ShellGConf *gconf, const char *key, \
GSList *value, GError **error) \
{ \
char *set_key = resolve_key (key); \
\
gconf_client_set_list (gconf->client, set_key, ELEMENT_TYPE, \
value, error); \
g_free (set_key); \
}
/**
* shell_gconf_set_boolean:
* @gconf: a #ShellGConf
* @key: a GConf key (as described in the #ShellGConf docs)
* @value: value to set @key to
* @error: a #GError, which will be set on error
*
* Sets the value of @key to @value.
**/
SIMPLE_SETTER(shell_gconf_set_boolean, gboolean, gconf_client_set_bool)
/**
* shell_gconf_set_int:
* @gconf: a #ShellGConf
* @key: a GConf key (as described in the #ShellGConf docs)
* @value: value to set @key to
* @error: a #GError, which will be set on error
*
* Sets the value of @key to @value.
**/
SIMPLE_SETTER(shell_gconf_set_int, int, gconf_client_set_int)
/**
* shell_gconf_set_float:
* @gconf: a #ShellGConf
* @key: a GConf key (as described in the #ShellGConf docs)
* @value: value to set @key to
* @error: a #GError, which will be set on error
*
* Sets the value of @key to @value.
**/
SIMPLE_SETTER(shell_gconf_set_float, float, gconf_client_set_float)
/**
* shell_gconf_set_string:
* @gconf: a #ShellGConf
* @key: a GConf key (as described in the #ShellGConf docs)
* @value: value to set @key to
* @error: a #GError, which will be set on error
*
* Sets the value of @key to @value.
**/
SIMPLE_SETTER(shell_gconf_set_string, const char *, gconf_client_set_string)
/**
* shell_gconf_set_boolean_list:
* @gconf: a #ShellGConf
* @key: a GConf key (as described in the #ShellGConf docs)
* @value: (transfer none): value to set @key to
* @error: a #GError, which will be set on error
*
* Sets the value of @key to @value.
**/
LIST_SETTER(shell_gconf_set_boolean_list, GCONF_VALUE_BOOL)
/**
* shell_gconf_set_int_list:
* @gconf: a #ShellGConf
* @key: a GConf key (as described in the #ShellGConf docs)
* @value: (transfer none): value to set @key to
* @error: a #GError, which will be set on error
*
* Sets the value of @key to @value.
**/
LIST_SETTER(shell_gconf_set_int_list, GCONF_VALUE_INT)
/**
* shell_gconf_set_float_list:
* @gconf: a #ShellGConf
* @key: a GConf key (as described in the #ShellGConf docs)
* @value: (transfer none): value to set @key to
* @error: a #GError, which will be set on error
*
* Sets the value of @key to @value.
**/
LIST_SETTER(shell_gconf_set_float_list, GCONF_VALUE_FLOAT)
/**
* shell_gconf_set_string_list:
* @gconf: a #ShellGConf
* @key: a GConf key (as described in the #ShellGConf docs)
* @value: (transfer none): value to set @key to
* @error: a #GError, which will be set on error
*
* Sets the value of @key to @value.
**/
LIST_SETTER(shell_gconf_set_string_list, GCONF_VALUE_STRING)

View File

@@ -1,95 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef SHELL_GCONF_H
#define SHELL_GCONF_H
#include <glib-object.h>
G_BEGIN_DECLS
typedef struct _ShellGConf ShellGConf;
typedef struct _ShellGConfClass ShellGConfClass;
#define SHELL_TYPE_GCONF (shell_gconf_get_type ())
#define SHELL_GCONF(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SHELL_TYPE_GCONF, ShellGConf))
#define SHELL_GCONF_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_GCONF, ShellGConfClass))
#define SHELL_IS_GCONF(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SHELL_TYPE_GCONF))
#define SHELL_IS_GCONF_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_GCONF))
#define SHELL_GCONF_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_GCONF, ShellGConfClass))
struct _ShellGConfClass
{
GObjectClass parent_class;
/* signals */
void (*changed) (ShellGConf *gconf);
};
#define SHELL_GCONF_DIR "/desktop/gnome/shell"
GType shell_gconf_get_type (void) G_GNUC_CONST;
ShellGConf *shell_gconf_get_default (void);
void shell_gconf_watch_directory (ShellGConf *gconf,
const char *directory);
gboolean shell_gconf_get_boolean (ShellGConf *gconf,
const char *key,
GError **error);
int shell_gconf_get_int (ShellGConf *gconf,
const char *key,
GError **error);
float shell_gconf_get_float (ShellGConf *gconf,
const char *key,
GError **error);
char *shell_gconf_get_string (ShellGConf *gconf,
const char *key,
GError **error);
GSList *shell_gconf_get_boolean_list (ShellGConf *gconf,
const char *key,
GError **error);
GSList *shell_gconf_get_int_list (ShellGConf *gconf,
const char *key,
GError **error);
GSList *shell_gconf_get_float_list (ShellGConf *gconf,
const char *key,
GError **error);
GSList *shell_gconf_get_string_list (ShellGConf *gconf,
const char *key,
GError **error);
void shell_gconf_set_boolean (ShellGConf *gconf,
const char *key,
gboolean value,
GError **error);
void shell_gconf_set_int (ShellGConf *gconf,
const char *key,
int value,
GError **error);
void shell_gconf_set_float (ShellGConf *gconf,
const char *key,
float value,
GError **error);
void shell_gconf_set_string (ShellGConf *gconf,
const char *key,
const char *value,
GError **error);
void shell_gconf_set_boolean_list (ShellGConf *gconf,
const char *key,
GSList *value,
GError **error);
void shell_gconf_set_int_list (ShellGConf *gconf,
const char *key,
GSList *value,
GError **error);
void shell_gconf_set_float_list (ShellGConf *gconf,
const char *key,
GSList *value,
GError **error);
void shell_gconf_set_string_list (ShellGConf *gconf,
const char *key,
GSList *value,
GError **error);
#endif

View File

@@ -1,240 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/**
* SECTION:shell-generic-container
* @short_description: A container class with signals for allocation
*
* #ShellGenericContainer is mainly a workaround for the current
* lack of GObject subclassing + vfunc overrides in gjs. We
* implement the container interface, but proxy the virtual functions
* into signals, which gjs can catch.
*/
/* Example implementation of a horzontal box with PACK_EXPAND for all,
vertically and horizontally centering.
function TestFixedBox() {
this._init();
}
TestFixedBox.prototype = {
_init : function () {
this.actor = new Shell.GenericContainer();
this.spacing = 4;
this.actor.connect('get-preferred-width', Lang.bind(this, function (actor, for_height, alloc) {
let children = this.actor.get_children();
let max_child_min = 0;
let max_child_nat = 0;
for (let i = 0; i < children.length; i++) {
let spacing = i > 0 && i < children.length-1 ? this.spacing : 0;
let [child_min, child_nat] = children[i].get_preferred_width(for_height);
if (child_min > max_child_min)
max_child_min = child_min;
if (child_nat > max_child_nat)
max_child_nat = child_nat;
}
let totalSpacing = this.spacing * Math.abs(children.length - 1);
alloc.min_size = children.length * max_child_min + totalSpacing;
alloc.nat_size = children.length * max_child_nat + totalSpacing;
}));
this.actor.connect('get-preferred-height', Lang.bind(this, function (actor, for_width, alloc) {
let children = this.actor.get_children();
let max_child_min = 0;
let max_child_nat = 0;
for (let i = 0; i < children.length; i++) {
let [child_min, child_nat] = children[i].get_preferred_height(for_width);
if (child_min > max_child_min)
max_child_min = child_min;
if (child_nat > max_child_nat)
max_child_nat = child_nat;
}
alloc.min_size = max_child_min;
alloc.nat_size = max_child_nat;
}));
this.actor.connect('allocate', Lang.bind(this, function (actor, box, flags) {
let children = this.actor.get_children();
let totalSpacing = (this.spacing * Math.abs(children.length - 1));
let child_width = (box.x2 - box.x1 - totalSpacing) / (children.length);
let child_height = box.y2 - box.y1;
let x = box.x1;
for (let i = 0; i < children.length; i++) {
let [child_min, child_nat] = children[i].get_preferred_height(child_width);
let vSpacing = Math.abs(child_height - child_nat) / 2;
let childBox = new Clutter.ActorBox();
childBox.x1 = x;
childBox.y1 = vSpacing;
childBox.x2 = x+child_width;
childBox.y2 = child_height - vSpacing;
children[i].allocate(childBox, flags);
x += this.spacing + child_width;
}
}));
}
}
function runTestFixedBox() {
let testBox = new TestFixedBox();
let c = new Clutter.Color();
c.from_pixel(0xff0000a0);
let r = new Clutter.Rectangle({ width: 50, height: 100, color: c });
testBox.actor.add_actor(r);
r = new Clutter.Rectangle({ width: 90, height: 70, color: c });
testBox.actor.add_actor(r);
r = new Clutter.Rectangle({ width: 90, height: 70, color: c });
testBox.actor.add_actor(r);
r = new Clutter.Rectangle({ width: 30, height: 10, color: c });
testBox.actor.add_actor(r);
c.from_pixel(0x00ff00a0);
let borderBox = new Big.Box({ border: 1, border_color: c });
borderBox.set_position(100, 100);
borderBox.append(testBox.actor, Big.BoxPackFlags.NONE);
Shell.Global.get().stage.add_actor(borderBox);
}
*/
#include "shell-generic-container.h"
#include <clutter/clutter.h>
#include <gtk/gtk.h>
#include <girepository.h>
G_DEFINE_TYPE(ShellGenericContainer, shell_generic_container, CLUTTER_TYPE_GROUP);
struct _ShellGenericContainerPrivate {
gpointer dummy;
};
/* Signals */
enum
{
GET_PREFERRED_WIDTH,
GET_PREFERRED_HEIGHT,
ALLOCATE,
LAST_SIGNAL
};
static guint shell_generic_container_signals [LAST_SIGNAL] = { 0 };
static gpointer
shell_generic_container_allocation_ref (ShellGenericContainerAllocation *alloc)
{
alloc->_refcount++;
return alloc;
}
static void
shell_generic_container_allocation_unref (ShellGenericContainerAllocation *alloc)
{
if (--alloc->_refcount == 0)
{
g_slice_free1 (sizeof (ShellGenericContainerAllocation), alloc);
}
}
static void
shell_generic_container_allocate (ClutterActor *self,
const ClutterActorBox *box,
ClutterAllocationFlags flags)
{
/* chain up to set actor->allocation */
(CLUTTER_ACTOR_CLASS (g_type_class_peek (clutter_actor_get_type ())))->allocate (self, box, flags);
g_signal_emit (G_OBJECT (self), shell_generic_container_signals[ALLOCATE], 0,
box, flags);
}
static void
shell_generic_container_get_preferred_width (ClutterActor *actor,
gfloat for_height,
gfloat *min_width_p,
gfloat *natural_width_p)
{
ShellGenericContainerAllocation *alloc = g_slice_alloc0 (sizeof (ShellGenericContainerAllocation));
alloc->_refcount = 1;
g_signal_emit (G_OBJECT (actor), shell_generic_container_signals[GET_PREFERRED_WIDTH], 0,
for_height, alloc);
if (min_width_p)
*min_width_p = alloc->min_size;
if (natural_width_p)
*natural_width_p = alloc->natural_size;
shell_generic_container_allocation_unref (alloc);
}
static void
shell_generic_container_get_preferred_height (ClutterActor *actor,
gfloat for_width,
gfloat *min_height_p,
gfloat *natural_height_p)
{
ShellGenericContainerAllocation *alloc = g_slice_alloc0 (sizeof (ShellGenericContainerAllocation));
alloc->_refcount = 1;
g_signal_emit (G_OBJECT (actor), shell_generic_container_signals[GET_PREFERRED_HEIGHT], 0,
for_width, alloc);
if (min_height_p)
*min_height_p = alloc->min_size;
if (natural_height_p)
*natural_height_p = alloc->natural_size;
shell_generic_container_allocation_unref (alloc);
}
static void
shell_generic_container_class_init (ShellGenericContainerClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
actor_class->get_preferred_width = shell_generic_container_get_preferred_width;
actor_class->get_preferred_height = shell_generic_container_get_preferred_height;
actor_class->allocate = shell_generic_container_allocate;
shell_generic_container_signals[GET_PREFERRED_WIDTH] =
g_signal_new ("get-preferred-width",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
gi_cclosure_marshal_generic,
G_TYPE_NONE, 2, G_TYPE_FLOAT, SHELL_TYPE_GENERIC_CONTAINER_ALLOCATION);
shell_generic_container_signals[GET_PREFERRED_HEIGHT] =
g_signal_new ("get-preferred-height",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
gi_cclosure_marshal_generic,
G_TYPE_NONE, 2, G_TYPE_FLOAT, SHELL_TYPE_GENERIC_CONTAINER_ALLOCATION);
shell_generic_container_signals[ALLOCATE] =
g_signal_new ("allocate",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
gi_cclosure_marshal_generic,
G_TYPE_NONE, 2, CLUTTER_TYPE_ACTOR_BOX, CLUTTER_TYPE_ALLOCATION_FLAGS);
g_type_class_add_private (gobject_class, sizeof (ShellGenericContainerPrivate));
}
static void
shell_generic_container_init (ShellGenericContainer *area)
{
area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area, SHELL_TYPE_GENERIC_CONTAINER,
ShellGenericContainerPrivate);
}
GType shell_generic_container_allocation_get_type (void)
{
static GType gtype = G_TYPE_INVALID;
if (gtype == G_TYPE_INVALID)
{
gtype = g_boxed_type_register_static ("ShellGenericContainerAllocation",
(GBoxedCopyFunc)shell_generic_container_allocation_ref,
(GBoxedFreeFunc)shell_generic_container_allocation_unref);
}
return gtype;
}

View File

@@ -1,44 +0,0 @@
#ifndef __SHELL_GENERIC_CONTAINER_H__
#define __SHELL_GENERIC_CONTAINER_H__
#include <clutter/clutter.h>
#include <gtk/gtk.h>
#define SHELL_TYPE_GENERIC_CONTAINER (shell_generic_container_get_type ())
#define SHELL_GENERIC_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_GENERIC_CONTAINER, ShellGenericContainer))
#define SHELL_GENERIC_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_GENERIC_CONTAINER, ShellGenericContainerClass))
#define SHELL_IS_GENERIC_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_GENERIC_CONTAINER))
#define SHELL_IS_GENERIC_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_GENERIC_CONTAINER))
#define SHELL_GENERIC_CONTAINER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_GENERIC_CONTAINER, ShellGenericContainerClass))
typedef struct {
float min_size;
float natural_size;
/* <private> */
guint _refcount;
} ShellGenericContainerAllocation;
#define SHELL_TYPE_GENERIC_CONTAINER_ALLOCATION (shell_generic_container_allocation_get_type ())
GType shell_generic_container_allocation_get_type (void);
typedef struct _ShellGenericContainer ShellGenericContainer;
typedef struct _ShellGenericContainerClass ShellGenericContainerClass;
typedef struct _ShellGenericContainerPrivate ShellGenericContainerPrivate;
struct _ShellGenericContainer
{
ClutterGroup parent;
ShellGenericContainerPrivate *priv;
};
struct _ShellGenericContainerClass
{
ClutterGroupClass parent_class;
};
GType shell_generic_container_get_type (void) G_GNUC_CONST;
#endif /* __SHELL_GENERIC_CONTAINER_H__ */

View File

@@ -14,8 +14,7 @@
#include <string.h>
#include <unistd.h>
#include <dbus/dbus-glib.h>
#include <gio/gio.h>
#include <glib/gi18n-lib.h>
#include <libgnomeui/gnome-thumbnail.h>
#include <math.h>
#include <X11/extensions/Xfixes.h>
@@ -40,6 +39,7 @@ struct _ShellGlobal {
MutterPlugin *plugin;
ShellWM *wm;
gboolean keyboard_grabbed;
const char *imagedir;
const char *configdir;
@@ -270,6 +270,49 @@ shell_global_class_init (ShellGlobalClass *klass)
G_PARAM_READABLE));
}
/**
* search_path_init:
*
* search_path_init and get_applications_search_path below were copied from glib/gio/gdesktopappinfo.c
* copyright Red Hat, Inc., written by Alex Larsson, licensed under the LGPL
*
* Return value: location of an array with user and system application directories.
*/
static gpointer
search_path_init (gpointer data)
{
char **args = NULL;
const char * const *data_dirs;
const char *user_data_dir;
int i, length, j;
data_dirs = g_get_system_data_dirs ();
length = g_strv_length ((char **)data_dirs);
args = g_new (char *, length + 2);
j = 0;
user_data_dir = g_get_user_data_dir ();
args[j++] = g_build_filename (user_data_dir, "applications", NULL);
for (i = 0; i < length; i++)
args[j++] = g_build_filename (data_dirs[i],
"applications", NULL);
args[j++] = NULL;
return args;
}
/**
* get_applications_search_path:
*
* Return value: location of an array with user and system application directories.
*/
static const char * const *
get_applications_search_path (void)
{
static GOnce once_init = G_ONCE_INIT;
return g_once (&once_init, search_path_init, NULL);
}
/**
* shell_clutter_texture_set_from_pixbuf:
* texture: #ClutterTexture to be modified
@@ -295,6 +338,135 @@ shell_clutter_texture_set_from_pixbuf (ClutterTexture *texture,
0, NULL);
}
static GnomeThumbnailFactory *thumbnail_factory;
/**
* shell_get_thumbnail:
*
* @uri: URI of the file to thumbnail
*
* @mime_type: Mime-Type of the file to thumbnail
*
* Return value: #GdkPixbuf containing a thumbnail for file @uri
* if the thumbnail exists or can be generated, %NULL otherwise
*/
GdkPixbuf *
shell_get_thumbnail(const gchar *uri,
const gchar *mime_type)
{
char *existing_thumbnail;
GdkPixbuf *pixbuf = NULL;
GError *error = NULL;
GFile *file = NULL;
GFileInfo *file_info = NULL;
GTimeVal mtime_g;
time_t mtime = 0;
file = g_file_new_for_uri (uri);
file_info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED, G_FILE_QUERY_INFO_NONE, NULL, NULL);
g_object_unref (file);
if (file_info) {
g_file_info_get_modification_time (file_info, &mtime_g);
g_object_unref (file_info);
mtime = (time_t) mtime_g.tv_sec;
}
if (thumbnail_factory == NULL)
thumbnail_factory = gnome_thumbnail_factory_new (GNOME_THUMBNAIL_SIZE_NORMAL);
existing_thumbnail = gnome_thumbnail_factory_lookup (thumbnail_factory, uri, mtime);
if (existing_thumbnail != NULL)
{
pixbuf = gdk_pixbuf_new_from_file(existing_thumbnail, &error);
if (error != NULL)
{
g_warning("Could not generate a pixbuf from file %s: %s", existing_thumbnail, error->message);
g_clear_error (&error);
}
}
else if (gnome_thumbnail_factory_has_valid_failed_thumbnail (thumbnail_factory, uri, mtime))
return NULL;
else if (gnome_thumbnail_factory_can_thumbnail (thumbnail_factory, uri, mime_type, mtime))
{
pixbuf = gnome_thumbnail_factory_generate_thumbnail (thumbnail_factory, uri, mime_type);
if (pixbuf)
{
// we need to save the thumbnail so that we don't need to generate it again in the future
gnome_thumbnail_factory_save_thumbnail (thumbnail_factory, pixbuf, uri, mtime);
}
else
{
g_warning ("Could not generate thumbnail for %s", uri);
gnome_thumbnail_factory_create_failed_thumbnail (thumbnail_factory, uri, mtime);
}
}
return pixbuf;
}
/**
* shell_get_categories_for_desktop_file:
*
* @desktop_file_name: name of the desktop file for which to retrieve categories
*
* Return value: (element-type char*) (transfer full): List of categories
*
*/
GSList *
shell_get_categories_for_desktop_file(const char *desktop_file_name)
{
GKeyFile *key_file;
const char * const *search_dirs;
char **categories = NULL;
GSList *categories_list = NULL;
GError *error = NULL;
gsize len;
int i;
key_file = g_key_file_new ();
search_dirs = get_applications_search_path();
g_key_file_load_from_dirs (key_file, desktop_file_name, (const char **)search_dirs, NULL, 0, &error);
if (error != NULL)
{
g_warning ("Error when loading a key file for %s: %s", desktop_file_name, error->message);
g_clear_error (&error);
}
else
{
categories = g_key_file_get_string_list (key_file,
"Desktop Entry",
"Categories",
&len,
&error);
if (error != NULL)
{
// "Categories" is not a required key in the desktop files, so it's ok if we didn't find it
g_clear_error (&error);
}
}
g_key_file_free (key_file);
if (categories == NULL)
return NULL;
// gjs currently does not support returning arrays (other than a NULL value for an array), so we need
// to convert the array we are returning to GSList, returning which gjs supports.
// See http://bugzilla.gnome.org/show_bug.cgi?id=560567 for more info on gjs array support.
for (i = 0; categories[i]; i++)
{
categories_list = g_slist_prepend (categories_list, g_strdup (categories[i]));
}
g_strfreev (categories);
return categories_list;
}
/**
* shell_get_event_key_symbol:
*
@@ -474,66 +646,66 @@ _shell_global_set_plugin (ShellGlobal *global,
}
/**
* shell_global_begin_modal:
* shell_global_grab_keyboard:
* @global: a #ShellGlobal
*
* Grabs the keyboard and mouse to the stage window. The stage will
* receive all keyboard and mouse events until shell_global_end_modal()
* is called. This is used to implement "modes" for the shell, such as the
* overview mode or the "looking glass" debug overlay, that block
* application and normal key shortcuts.
*
* Returns value: %TRUE if we succesfully entered the mode. %FALSE if we couldn't
* enter the mode. Failure may occur because an application has the pointer
* or keyboard grabbed, because Mutter is in a mode itself like moving a
* window or alt-Tab window selection, or because shell_global_begin_modal()
* was previouly called.
* Grab the keyboard to the stage window. The stage will receive
* all keyboard events until shell_global_ungrab_keyboard() is called.
* This is appropriate to do when the desktop goes into a special
* mode where no normal global key shortcuts or application keyboard
* processing should happen.
*/
gboolean
shell_global_begin_modal (ShellGlobal *global,
guint32 timestamp)
{
ClutterStage *stage = CLUTTER_STAGE (mutter_plugin_get_stage (global->plugin));
Window stagewin = clutter_x11_get_stage_window (stage);
return mutter_plugin_begin_modal (global->plugin, stagewin, None, 0, timestamp);
}
/**
* shell_global_end_modal:
* @global: a #ShellGlobal
*
* Undoes the effect of shell_global_begin_modal().
*/
void
shell_global_end_modal (ShellGlobal *global,
guint32 timestamp)
{
mutter_plugin_end_modal (global->plugin, timestamp);
}
/**
* shell_global_display_is_grabbed
* @global: a #ShellGlobal
*
* Determines whether Mutter currently has a grab (keyboard or mouse or
* both) on the display. This could be the result of a current window
* management operation like a window move, or could be from
* shell_global_begin_modal().
*
* This function is useful to for ad-hoc checks to avoid over-grabbing
* the Mutter grab a grab from GTK+. Longer-term we might instead want a
* mechanism to make Mutter use GDK grabs instead of raw XGrabPointer().
*
* Return value: %TRUE if Mutter has a grab on the display
*/
gboolean
shell_global_display_is_grabbed (ShellGlobal *global)
shell_global_grab_keyboard (ShellGlobal *global)
{
MetaScreen *screen = mutter_plugin_get_screen (global->plugin);
MetaDisplay *display = meta_screen_get_display (screen);
Display *xdisplay = meta_display_get_xdisplay (display);
ClutterStage *stage = CLUTTER_STAGE (mutter_plugin_get_stage (global->plugin));
Window stagewin = clutter_x11_get_stage_window (stage);
return meta_display_get_grab_op (display) != META_GRAB_OP_NONE;
/* FIXME: we need to coordinate with the rest of Metacity or we
* may grab the keyboard away from other portions of Metacity
* and leave Metacity in a confused state. An X client is allowed
* to overgrab itself, though not allowed to grab they keyboard
* away from another applications.
*/
if (global->keyboard_grabbed)
return FALSE;
if (XGrabKeyboard (xdisplay, stagewin,
False, /* owner_events - steal events from the rest of metacity */
GrabModeAsync, GrabModeAsync,
CurrentTime) != Success)
return FALSE; /* probably AlreadyGrabbed, some other app has a keyboard grab */
global->keyboard_grabbed = TRUE;
return TRUE;
}
/**
* shell_global_ungrab_keyboard:
* @global: a #ShellGlobal
*
* Undoes the effect of shell_global_grab_keyboard
*/
void
shell_global_ungrab_keyboard (ShellGlobal *global)
{
MetaScreen *screen;
MetaDisplay *display;
Display *xdisplay;
g_return_if_fail (global->keyboard_grabbed);
screen = mutter_plugin_get_screen (global->plugin);
display = meta_screen_get_display (screen);
xdisplay = meta_display_get_xdisplay (display);
XUngrabKeyboard (xdisplay, CurrentTime);
global->keyboard_grabbed = FALSE;
}
/* Code to close all file descriptors before we exec; copied from gspawn.c in GLib.
@@ -696,26 +868,24 @@ shell_global_grab_dbus_service (ShellGlobal *global)
*/
exit (0);
}
/* Also grab org.gnome.Panel to replace any existing panel process,
* unless a special environment variable is passed. The environment
* variable is used by the gnome-shell (no --replace) launcher in
* Xephyr */
if (!g_getenv ("GNOME_SHELL_NO_REPLACE_PANEL"))
{
if (!dbus_g_proxy_call (bus, "RequestName", &error, G_TYPE_STRING,
"org.gnome.Panel", G_TYPE_UINT,
DBUS_NAME_FLAG_REPLACE_EXISTING | DBUS_NAME_FLAG_DO_NOT_QUEUE,
G_TYPE_INVALID, G_TYPE_UINT,
&request_name_result, G_TYPE_INVALID))
{
g_print ("failed to acquire org.gnome.Panel: %s\n", error->message);
exit (1);
}
}
g_object_unref (bus);
}
void
shell_global_start_task_panel (ShellGlobal *global)
{
const char* panel_args[] = {"gnomeshell-taskpanel", SHELL_DBUS_SERVICE, NULL};
GError *error = NULL;
if (!g_spawn_async (NULL, (char**)panel_args, NULL, G_SPAWN_SEARCH_PATH, NULL,
NULL, NULL, &error))
{
g_critical ("failed to execute %s: %s", panel_args[0], error->message);
g_clear_error (&error);
}
}
static void
grab_notify (GtkWidget *widget, gboolean was_grabbed, gpointer user_data)
{
@@ -727,6 +897,102 @@ grab_notify (GtkWidget *widget, gboolean was_grabbed, gpointer user_data)
shell_global_set_stage_input_mode (global, global->input_mode);
}
/**
* shell_global_create_vertical_gradient:
* @top: the color at the top
* @bottom: the color at the bottom
*
* Creates a vertical gradient actor.
*
* Return value: (transfer none): a #ClutterCairoTexture actor with the
* gradient. The texture actor is floating, hence (transfer none).
*/
ClutterCairoTexture *
shell_global_create_vertical_gradient (ClutterColor *top,
ClutterColor *bottom)
{
ClutterCairoTexture *texture;
cairo_t *cr;
cairo_pattern_t *pattern;
/* Draw the gradient on an 8x8 pixel texture. Because the gradient is drawn
* from the uppermost to the lowermost row, after stretching 1/16 of the
* texture height has the top color and 1/16 has the bottom color. The 8
* pixel width is chosen for reasons related to graphics hardware internals.
*/
texture = CLUTTER_CAIRO_TEXTURE (clutter_cairo_texture_new (8, 8));
cr = clutter_cairo_texture_create (texture);
pattern = cairo_pattern_create_linear (0, 0, 0, 8);
cairo_pattern_add_color_stop_rgba (pattern, 0,
top->red / 255.,
top->green / 255.,
top->blue / 255.,
top->alpha / 255.);
cairo_pattern_add_color_stop_rgba (pattern, 1,
bottom->red / 255.,
bottom->green / 255.,
bottom->blue / 255.,
bottom->alpha / 255.);
cairo_set_source (cr, pattern);
cairo_paint (cr);
cairo_pattern_destroy (pattern);
cairo_destroy (cr);
return texture;
}
/**
* shell_global_create_horizontal_gradient:
* @left: the color on the left
* @right: the color on the right
*
* Creates a horizontal gradient actor.
*
* Return value: (transfer none): a #ClutterCairoTexture actor with the
* gradient. The texture actor is floating, hence (transfer none).
*/
ClutterCairoTexture *
shell_global_create_horizontal_gradient (ClutterColor *left,
ClutterColor *right)
{
ClutterCairoTexture *texture;
cairo_t *cr;
cairo_pattern_t *pattern;
/* Draw the gradient on an 8x1 pixel texture. Because the gradient is drawn
* from the left to the right column, after stretching 1/16 of the
* texture width has the left side color and 1/16 has the right side color.
* There is no reason to use the 8 pixel height that would be similar to the
* reason we are using the 8 pixel width for the vertical gradient, so we
* are just using the 1 pixel height instead.
*/
texture = CLUTTER_CAIRO_TEXTURE (clutter_cairo_texture_new (8, 1));
cr = clutter_cairo_texture_create (texture);
pattern = cairo_pattern_create_linear (0, 0, 8, 0);
cairo_pattern_add_color_stop_rgba (pattern, 0,
left->red / 255.,
left->green / 255.,
left->blue / 255.,
left->alpha / 255.);
cairo_pattern_add_color_stop_rgba (pattern, 1,
right->red / 255.,
right->green / 255.,
right->blue / 255.,
right->alpha / 255.);
cairo_set_source (cr, pattern);
cairo_paint (cr);
cairo_pattern_destroy (pattern);
cairo_destroy (cr);
return texture;
}
/*
* Updates the global->root_pixmap actor with the root window's pixmap or fails
* with a warning.
@@ -814,46 +1080,6 @@ root_pixmap_destroy (GObject *sender, gpointer data)
global->root_pixmap = NULL;
}
/**
* shell_global_format_time_relative_pretty:
* @global:
* @delta: Time in seconds since the current time
* @text: (out): Relative human-consumption-only time string
* @next_update: (out): Time in seconds until we should redisplay the time
*
* Format a time value for human consumption only. The passed time
* value is a delta in terms of seconds from the current time.
* This function needs to be in C because of its use of ngettext() which
* is not accessible from JavaScript.
*/
void
shell_global_format_time_relative_pretty (ShellGlobal *global,
guint delta,
char **text,
guint *next_update)
{
#define MINUTE (60)
#define HOUR (MINUTE*60)
#define DAY (HOUR*24)
#define WEEK (DAY*7)
if (delta < MINUTE) {
*text = g_strdup (_("Less than a minute ago"));
*next_update = MINUTE - delta;
} else if (delta < HOUR) {
*text = g_strdup_printf (ngettext ("%d minute ago", "%d minutes ago", delta / MINUTE), delta / MINUTE);
*next_update = MINUTE - (delta % MINUTE);
} else if (delta < DAY) {
*text = g_strdup_printf (ngettext ("%d hour ago", "%d hours ago", delta / HOUR), delta / HOUR);
*next_update = HOUR - (delta % HOUR);
} else if (delta < WEEK) {
*text = g_strdup_printf (ngettext ("%d day ago", "%d days ago", delta / DAY), delta / DAY);
*next_update = DAY - (delta % DAY);
} else {
*text = g_strdup_printf (ngettext ("%d week ago", "%d weeks ago", delta / WEEK), delta / WEEK);
*next_update = WEEK - (delta % WEEK);
}
}
/**
* shell_global_create_root_pixmap_actor:
* @global: a #ShellGlobal
@@ -910,3 +1136,50 @@ shell_global_create_root_pixmap_actor (ShellGlobal *global)
return clutter_clone_new (global->root_pixmap);
}
void
shell_global_clutter_cairo_texture_draw_clock (ClutterCairoTexture *texture,
int hour,
int minute)
{
cairo_t *cr;
guint width, height;
double xc, yc, radius, hour_radius, minute_radius;
double angle;
clutter_cairo_texture_get_surface_size (texture, &width, &height);
xc = (double)width / 2;
yc = (double)height / 2;
radius = (double)(MIN(width, height)) / 2 - 2;
minute_radius = radius - 3;
hour_radius = radius / 2;
clutter_cairo_texture_clear (texture);
cr = clutter_cairo_texture_create (texture);
cairo_set_line_width (cr, 1.0);
/* Outline */
cairo_arc (cr, xc, yc, radius, 0.0, 2.0 * M_PI);
cairo_stroke (cr);
/* Hour hand. (We add a fraction to @hour for the minutes, then
* convert to radians, and then subtract pi/2 because cairo's origin
* is at 3:00, not 12:00.)
*/
angle = ((hour + minute / 60.0) / 12.0) * 2.0 * M_PI - M_PI / 2.0;
cairo_move_to (cr, xc, yc);
cairo_line_to (cr,
xc + hour_radius * cos (angle),
yc + hour_radius * sin (angle));
cairo_stroke (cr);
/* Minute hand */
angle = (minute / 60.0) * 2.0 * M_PI - M_PI / 2.0;
cairo_move_to (cr, xc, yc);
cairo_line_to (cr,
xc + minute_radius * cos (angle),
yc + minute_radius * sin (angle));
cairo_stroke (cr);
cairo_destroy (cr);
}

View File

@@ -36,6 +36,10 @@ GType shell_global_get_type (void) G_GNUC_CONST;
gboolean shell_clutter_texture_set_from_pixbuf (ClutterTexture *texture,
GdkPixbuf *pixbuf);
GdkPixbuf *shell_get_thumbnail(const gchar *uri, const gchar *mime_type);
GSList *shell_get_categories_for_desktop_file(const char *desktop_file_name);
guint16 shell_get_event_key_symbol(ClutterEvent *event);
guint16 shell_get_button_event_click_count(ClutterEvent *event);
@@ -48,6 +52,8 @@ MetaScreen *shell_global_get_screen (ShellGlobal *global);
void shell_global_grab_dbus_service (ShellGlobal *global);
void shell_global_start_task_panel (ShellGlobal *global);
typedef enum {
SHELL_STAGE_INPUT_MODE_NONREACTIVE,
SHELL_STAGE_INPUT_MODE_NORMAL,
@@ -64,19 +70,23 @@ GList *shell_global_get_windows (ShellGlobal *global);
void _shell_global_set_plugin (ShellGlobal *global,
MutterPlugin *plugin);
gboolean shell_global_begin_modal (ShellGlobal *global,
guint32 timestamp);
void shell_global_end_modal (ShellGlobal *global,
guint32 timestamp);
gboolean shell_global_display_is_grabbed (ShellGlobal *global);
gboolean shell_global_grab_keyboard (ShellGlobal *global);
void shell_global_ungrab_keyboard (ShellGlobal *global);
void shell_global_reexec_self (ShellGlobal *global);
void shell_global_format_time_relative_pretty (ShellGlobal *global, guint delta, char **text, guint *update_time);
ClutterCairoTexture *shell_global_create_vertical_gradient (ClutterColor *top,
ClutterColor *bottom);
ClutterCairoTexture *shell_global_create_horizontal_gradient (ClutterColor *left,
ClutterColor *right);
ClutterActor *shell_global_create_root_pixmap_actor (ShellGlobal *global);
void shell_global_clutter_cairo_texture_draw_clock (ClutterCairoTexture *texture,
int hour,
int minute);
G_END_DECLS
#endif /* __SHELL_GLOBAL_H__ */

View File

@@ -1,4 +1,2 @@
VOID:INT,INT,INT
VOID:OBJECT,INT,INT,INT,INT
VOID:BOXED
VOID:BOXED,OBJECT

View File

@@ -1,426 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#include "shell-overflow-list.h"
G_DEFINE_TYPE (ShellOverflowList,
shell_overflow_list,
CLUTTER_TYPE_GROUP);
enum {
PROP_0,
PROP_SPACING,
PROP_ITEM_HEIGHT,
PROP_DISPLAYED_COUNT,
PROP_PAGE,
PROP_N_PAGES
};
struct _ShellOverflowListPrivate {
guint item_height;
guint spacing;
guint page;
guint n_pages;
guint items_per_page;
guint displayed_count;
};
static void
recalc_displayed_count (ShellOverflowList *self)
{
GList *children;
int n_children;
int displayed_count;
int page, n_pages;
children = clutter_container_get_children (CLUTTER_CONTAINER (self));
n_children = g_list_length (children);
g_list_free (children);
page = self->priv->page;
n_pages = self->priv->n_pages;
if (page < n_pages-1)
displayed_count = self->priv->items_per_page;
else if (n_pages > 0)
displayed_count = n_children - (self->priv->items_per_page * (n_pages-1));
else
displayed_count = 0;
if (displayed_count != self->priv->displayed_count)
{
self->priv->displayed_count = displayed_count;
g_object_notify (G_OBJECT (self), "displayed-count");
}
}
static void
shell_overflow_list_set_property(GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
ShellOverflowList *self = SHELL_OVERFLOW_LIST (object);
ShellOverflowListPrivate *priv = self->priv;
switch (prop_id)
{
case PROP_SPACING:
priv->spacing = g_value_get_float (value);
clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
break;
case PROP_ITEM_HEIGHT:
priv->item_height = g_value_get_float (value);
clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
break;
case PROP_PAGE:
priv->page = g_value_get_uint (value);
recalc_displayed_count (self);
clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
shell_overflow_list_get_property(GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
ShellOverflowList *self = SHELL_OVERFLOW_LIST (object);
ShellOverflowListPrivate *priv = self->priv;
switch (prop_id)
{
case PROP_SPACING:
g_value_set_float (value, priv->spacing);
break;
case PROP_ITEM_HEIGHT:
g_value_set_float (value, priv->spacing);
break;
case PROP_DISPLAYED_COUNT:
g_value_set_uint (value, priv->displayed_count);
break;
case PROP_PAGE:
g_value_set_uint (value, priv->page);
break;
case PROP_N_PAGES:
g_value_set_uint (value, priv->n_pages);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
shell_overflow_list_allocate (ClutterActor *actor,
const ClutterActorBox *box,
ClutterAllocationFlags flags)
{
ShellOverflowList *self = SHELL_OVERFLOW_LIST (actor);
ShellOverflowListPrivate *priv = self->priv;
GList *children, *iter;
int n_pages;
int n_children;
int n_fits;
float width;
float curheight;
float avail_height;
gboolean overflow;
/* chain up to set actor->allocation */
(CLUTTER_ACTOR_CLASS (g_type_class_peek (clutter_actor_get_type ())))->allocate (actor, box, flags);
width = box->x2 - box->x1;
curheight = 0;
avail_height = box->y2 - box->y1;
children = clutter_container_get_children (CLUTTER_CONTAINER (self));
n_children = g_list_length (children);
n_fits = 0;
n_pages = 1;
overflow = FALSE;
for (iter = children; iter; iter = iter->next)
{
ClutterActor *actor = CLUTTER_ACTOR (iter->data);
ClutterActorBox child_box;
if (iter != children)
curheight += priv->spacing;
if ((curheight + priv->item_height) > avail_height)
{
overflow = TRUE;
curheight = 0;
n_pages++;
}
else if (!overflow)
n_fits++;
child_box.x1 = 0;
child_box.x2 = width;
child_box.y1 = curheight;
child_box.y2 = child_box.y1 + priv->item_height;
clutter_actor_allocate (actor, &child_box, flags);
curheight += priv->item_height;
}
priv->items_per_page = n_fits;
if (n_pages != priv->n_pages)
{
priv->n_pages = n_pages;
g_object_notify (G_OBJECT (self), "n-pages");
}
recalc_displayed_count (self);
g_list_free (children);
}
static void
shell_overflow_list_paint (ClutterActor *actor)
{
ShellOverflowList *self = SHELL_OVERFLOW_LIST (actor);
ShellOverflowListPrivate *priv = self->priv;
GList *children, *iter;
int i;
children = clutter_container_get_children (CLUTTER_CONTAINER (self));
if (children == NULL)
return;
iter = g_list_nth (children, (priv->page) * priv->items_per_page);
i = 0;
for (;iter && i < priv->items_per_page; iter = iter->next, i++)
{
ClutterActor *actor = CLUTTER_ACTOR (iter->data);
clutter_actor_paint (actor);
}
g_list_free (children);
}
static void
shell_overflow_list_pick (ClutterActor *actor,
const ClutterColor *color)
{
(CLUTTER_ACTOR_CLASS (g_type_class_peek (clutter_actor_get_type ())))->pick (actor, color);
shell_overflow_list_paint (actor);
}
static void
shell_overflow_list_get_preferred_height (ClutterActor *actor,
gfloat for_width,
gfloat *min_height_p,
gfloat *natural_height_p)
{
ShellOverflowList *self = SHELL_OVERFLOW_LIST (actor);
ShellOverflowListPrivate *priv = self->priv;
GList *children;
if (min_height_p)
*min_height_p = 0;
if (natural_height_p)
{
int n_children;
children = clutter_container_get_children (CLUTTER_CONTAINER (self));
n_children = g_list_length (children);
if (n_children == 0)
*natural_height_p = 0;
else
*natural_height_p = (n_children - 1) * (priv->item_height + priv->spacing) + priv->item_height;
g_list_free (children);
}
}
static void
shell_overflow_list_get_preferred_width (ClutterActor *actor,
gfloat for_height,
gfloat *min_width_p,
gfloat *natural_width_p)
{
ShellOverflowList *self = SHELL_OVERFLOW_LIST (actor);
gboolean first = TRUE;
float min = 0, natural = 0;
GList *iter;
GList *children;
children = clutter_container_get_children (CLUTTER_CONTAINER (self));
for (iter = children; iter; iter = iter->next)
{
ClutterActor *child = iter->data;
float child_min, child_natural;
clutter_actor_get_preferred_width (child,
for_height,
&child_min,
&child_natural);
if (first)
{
first = FALSE;
min = child_min;
natural = child_natural;
}
else
{
if (child_min > min)
min = child_min;
if (child_natural > natural)
natural = child_natural;
}
}
if (min_width_p)
*min_width_p = min;
if (natural_width_p)
*natural_width_p = natural;
g_list_free (children);
}
static void
shell_overflow_list_class_init (ShellOverflowListClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
gobject_class->get_property = shell_overflow_list_get_property;
gobject_class->set_property = shell_overflow_list_set_property;
actor_class->get_preferred_width = shell_overflow_list_get_preferred_width;
actor_class->get_preferred_height = shell_overflow_list_get_preferred_height;
actor_class->allocate = shell_overflow_list_allocate;
actor_class->paint = shell_overflow_list_paint;
actor_class->pick = shell_overflow_list_pick;
g_object_class_install_property (gobject_class,
PROP_SPACING,
g_param_spec_float ("spacing",
"Spacing",
"Space between items",
0.0, G_MAXFLOAT, 0.0,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class,
PROP_ITEM_HEIGHT,
g_param_spec_float ("item-height",
"Item height",
"Fixed item height value",
0.0, G_MAXFLOAT, 0.0,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class,
PROP_DISPLAYED_COUNT,
g_param_spec_uint ("displayed-count",
"Displayed count",
"Number of items displayed on current page",
0, G_MAXUINT, 0,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class,
PROP_PAGE,
g_param_spec_uint ("page",
"Page number",
"Page number",
0, G_MAXUINT, 0,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class,
PROP_N_PAGES,
g_param_spec_uint ("n-pages",
"Number of pages",
"Number of pages",
0, G_MAXUINT, 0,
G_PARAM_READABLE));
g_type_class_add_private (gobject_class, sizeof (ShellOverflowListPrivate));
}
static void
shell_overflow_list_init (ShellOverflowList *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
SHELL_TYPE_OVERFLOW_LIST,
ShellOverflowListPrivate);
self->priv->n_pages = 1;
self->priv->page = 0;
}
/**
* shell_overflow_list_get_displayed_actor:
* @self:
* @index: 0-based index for displayed list
*
* Returns the actor at the given index on the current page.
*
* Return value: (transfer none): #ClutterActor at index
*/
ClutterActor *
shell_overflow_list_get_displayed_actor (ShellOverflowList *self,
guint index)
{
GList *children, *iter;
children = clutter_container_get_children (CLUTTER_CONTAINER (self));
if (children == NULL)
return NULL;
iter = g_list_nth (children, index + (self->priv->page * self->priv->items_per_page));
if (!iter)
return NULL;
return iter->data;
}
/**
* shell_overflow_list_get_actor_index:
* @self:
* @actor: a child of the list
*
* Returns the index on the current page of the given actor.
*
* Return value: index of @actor in
* the currently visible page
*/
int
shell_overflow_list_get_actor_index (ShellOverflowList *self,
ClutterActor *actor)
{
GList *children, *iter;
int i;
int result;
children = clutter_container_get_children (CLUTTER_CONTAINER (self));
if (children == NULL)
return -1;
iter = g_list_nth (children, (self->priv->page) * self->priv->items_per_page);
result = -1;
for (i = 0; iter; iter = iter->next, i++)
if (iter->data == actor)
{
result = i;
break;
}
g_list_free (children);
return result;
}

View File

@@ -1,44 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef __SHELL_OVERFLOW_LIST_H__
#define __SHELL_OVERFLOW_LIST_H__
#include <glib-object.h>
#include <glib.h>
#include <clutter/clutter.h>
G_BEGIN_DECLS
typedef struct _ShellOverflowList ShellOverflowList;
typedef struct _ShellOverflowListClass ShellOverflowListClass;
typedef struct _ShellOverflowListPrivate ShellOverflowListPrivate;
#define SHELL_TYPE_OVERFLOW_LIST (shell_overflow_list_get_type ())
#define SHELL_OVERFLOW_LIST(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SHELL_TYPE_OVERFLOW_LIST, ShellOverflowList))
#define SHELL_OVERFLOW_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_OVERFLOW_LIST, ShellOverflowListClass))
#define SHELL_IS_OVERFLOW_LIST(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SHELL_TYPE_OVERFLOW_LIST))
#define SHELL_IS_OVERFLOW_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_OVERFLOW_LIST))
#define SHELL_OVERFLOW_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_OVERFLOW_LIST, ShellOverflowListClass))
struct _ShellOverflowList
{
ClutterGroup parent_instance;
ShellOverflowListPrivate *priv;
};
struct _ShellOverflowListClass
{
ClutterGroupClass parent_class;
ShellOverflowListPrivate *priv;
};
GType shell_overflow_list_get_type (void) G_GNUC_CONST;
ClutterActor *shell_overflow_list_get_displayed_actor (ShellOverflowList *list, guint index);
int shell_overflow_list_get_actor_index (ShellOverflowList *list, ClutterActor *actor);
G_END_DECLS
#endif /* __SHELL_OVERFLOW_LIST_H__ */

View File

@@ -52,6 +52,8 @@ static void shell_process_init (ShellProcess *self)
static void shell_process_dispose (GObject *object)
{
ShellProcess *self = (ShellProcess*)object;
G_OBJECT_CLASS (shell_process_parent_class)->dispose(object);
}

View File

@@ -221,8 +221,7 @@ get_memory_target (void)
return mem_total / 2;
}
/* Skip to the next line and discard what we read */
if (fgets(line_buffer, sizeof(line_buffer), f) == NULL)
break;
fgets(line_buffer, sizeof(line_buffer), f);
}
fclose(f);
@@ -233,9 +232,6 @@ get_memory_target (void)
static void
shell_recorder_init (ShellRecorder *recorder)
{
/* Calling gst_init() is a no-op if GStreamer was previously initialized */
gst_init (NULL, NULL);
shell_recorder_src_register ();
recorder->recording_icon = create_recording_icon ();

View File

@@ -1,160 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/**
* SECTION:shell-stack
* @short_description: Pure "Z-axis" container class
*
* A #ShellStack draws its children on top of each other,
* aligned to the top left. It will be sized in width/height
* according to the largest such dimension of its children, and
* all children will be allocated that size. This differs
* from #ClutterGroup which allocates its children their natural
* size, even if that would overflow the size allocated to the stack.
*/
#include "shell-stack.h"
G_DEFINE_TYPE (ShellStack,
shell_stack,
CLUTTER_TYPE_GROUP);
static void
shell_stack_allocate (ClutterActor *self,
const ClutterActorBox *box,
ClutterAllocationFlags flags)
{
GList *children, *iter;
float width, height;
width = box->x2 - box->x1;
height = box->y2 - box->y1;
/* Chain up directly to ClutterActor to set actor->allocation. We explicitly skip our parent class
* ClutterGroup here because we want to override the allocate function. */
(CLUTTER_ACTOR_CLASS (g_type_class_peek (clutter_actor_get_type ())))->allocate (self, box, flags);
children = clutter_container_get_children (CLUTTER_CONTAINER (self));
for (iter = children; iter; iter = iter->next)
{
ClutterActor *actor = CLUTTER_ACTOR (iter->data);
ClutterActorBox child_box;
child_box.x1 = 0;
child_box.x2 = width;
child_box.y1 = 0;
child_box.y2 = height;
clutter_actor_allocate (actor, &child_box, flags);
}
g_list_free (children);
}
static void
shell_stack_get_preferred_height (ClutterActor *actor,
gfloat for_width,
gfloat *min_height_p,
gfloat *natural_height_p)
{
ShellStack *stack = SHELL_STACK (actor);
gboolean first = TRUE;
float min = 0, natural = 0;
GList *children;
GList *iter;
children = clutter_container_get_children (CLUTTER_CONTAINER (stack));
for (iter = children; iter; iter = iter->next)
{
ClutterActor *child = iter->data;
float child_min, child_natural;
clutter_actor_get_preferred_height (child,
for_width,
&child_min,
&child_natural);
if (first)
{
first = FALSE;
min = child_min;
natural = child_natural;
}
else
{
if (child_min > min)
min = child_min;
if (child_natural > natural)
natural = child_natural;
}
}
if (min_height_p)
*min_height_p = min;
if (natural_height_p)
*natural_height_p = natural;
g_list_free (children);
}
static void
shell_stack_get_preferred_width (ClutterActor *actor,
gfloat for_height,
gfloat *min_width_p,
gfloat *natural_width_p)
{
ShellStack *stack = SHELL_STACK (actor);
gboolean first = TRUE;
float min = 0, natural = 0;
GList *iter;
GList *children;
children = clutter_container_get_children (CLUTTER_CONTAINER (stack));
for (iter = children; iter; iter = iter->next)
{
ClutterActor *child = iter->data;
float child_min, child_natural;
clutter_actor_get_preferred_width (child,
for_height,
&child_min,
&child_natural);
if (first)
{
first = FALSE;
min = child_min;
natural = child_natural;
}
else
{
if (child_min > min)
min = child_min;
if (child_natural > natural)
natural = child_natural;
}
}
if (min_width_p)
*min_width_p = min;
if (natural_width_p)
*natural_width_p = natural;
g_list_free (children);
}
static void
shell_stack_class_init (ShellStackClass *klass)
{
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
actor_class->get_preferred_width = shell_stack_get_preferred_width;
actor_class->get_preferred_height = shell_stack_get_preferred_height;
actor_class->allocate = shell_stack_allocate;
}
static void
shell_stack_init (ShellStack *actor)
{
}

View File

@@ -1,33 +0,0 @@
#ifndef __SHELL_STACK_H__
#define __SHELL_STACK_H__
#include <clutter/clutter.h>
#include <gtk/gtk.h>
#define SHELL_TYPE_STACK (shell_stack_get_type ())
#define SHELL_STACK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_STACK, ShellStack))
#define SHELL_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_STACK, ShellStackClass))
#define SHELL_IS_STACK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_STACK))
#define SHELL_IS_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_STACK))
#define SHELL_STACK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_STACK, ShellStackClass))
typedef struct _ShellStack ShellStack;
typedef struct _ShellStackClass ShellStackClass;
typedef struct _ShellStackPrivate ShellStackPrivate;
struct _ShellStack
{
ClutterGroup parent;
ShellStackPrivate *priv;
};
struct _ShellStackClass
{
ClutterGroupClass parent_class;
};
GType shell_stack_get_type (void) G_GNUC_CONST;
#endif /* __SHELL_STACK_H__ */

View File

@@ -26,7 +26,7 @@
#include <unistd.h>
#include <sys/types.h>
#include <glib/gi18n-lib.h>
#include <glib/gi18n.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
@@ -39,13 +39,10 @@
#include <gdmuser/gdm-user-manager.h>
#include "shell-global.h"
#include "shell-gconf.h"
#define LOCKDOWN_DIR "/desktop/gnome/lockdown"
#define LOCKDOWN_KEY LOCKDOWN_DIR "/disable_user_switching"
#define SIDEBAR_VISIBLE_KEY SHELL_GCONF_DIR "/sidebar/visible"
struct _ShellStatusMenuPrivate {
GConfClient *client;
GdmUserManager *manager;
@@ -57,12 +54,10 @@ struct _ShellStatusMenuPrivate {
GtkWidget *menu;
GtkWidget *account_item;
GtkWidget *sidebar_item;
GtkWidget *control_panel_item;
GtkWidget *lock_screen_item;
GtkWidget *login_screen_item;
GtkWidget *quit_session_item;
GtkWidget *shut_down_item;
guint client_notify_lockdown_id;
@@ -118,9 +113,12 @@ static void
update_name_text (ShellStatusMenu *status)
{
ShellStatusMenuPrivate *priv = status->priv;
char *markup;
clutter_text_set_text (priv->name,
gdm_user_get_real_name (GDM_USER (priv->user)));
markup = g_markup_printf_escaped("<b>%s</b>",
gdm_user_get_real_name (GDM_USER (priv->user)));
clutter_text_set_markup (priv->name, markup);
g_free (markup);
}
static void
@@ -316,18 +314,10 @@ on_account_activate (GtkMenuItem *item,
spawn_external (status, "gnome-about-me");
}
static void
on_sidebar_toggled (GtkCheckMenuItem *item,
ShellStatusMenu *status)
{
gconf_client_set_bool (status->priv->client, SIDEBAR_VISIBLE_KEY,
gtk_check_menu_item_get_active (item), NULL);
}
/* Calls 'gnome-session-save arg' */
static void
gnome_session_save_command (const char *arg)
on_quit_session_activate (GtkMenuItem *item,
ShellStatusMenu *status)
{
char *args[3];
GError *error;
@@ -338,7 +328,7 @@ gnome_session_save_command (const char *arg)
if (args[0] == NULL)
return;
args[1] = (char *)arg;
args[1] = "--logout-dialog";
args[2] = NULL;
screen = gdk_screen_get_default ();
@@ -355,21 +345,6 @@ gnome_session_save_command (const char *arg)
g_free (args[0]);
}
static void
on_quit_session_activate (GtkMenuItem *item,
ShellStatusMenu *status)
{
gnome_session_save_command ("--logout-dialog");
}
static void
on_shut_down_activate (GtkMenuItem *item,
ShellStatusMenu *status)
{
gnome_session_save_command ("--shutdown-dialog");
}
static void
update_switch_user (ShellStatusMenu *status)
{
@@ -455,8 +430,6 @@ menuitem_style_set_cb (GtkWidget *menuitem,
icon_name = "user-info";
else if (menuitem == priv->control_panel_item)
icon_name = "preferences-desktop";
else if (menuitem == priv->shut_down_item)
icon_name = "system-shutdown";
else
icon_name = GTK_STOCK_MISSING_IMAGE;
@@ -499,14 +472,6 @@ create_sub_menu (ShellStatusMenu *status)
G_CALLBACK (on_account_activate), status);
gtk_widget_show (priv->account_item);
priv->sidebar_item = gtk_check_menu_item_new_with_label (_("Sidebar"));
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (priv->sidebar_item),
gconf_client_get_bool (priv->client, SIDEBAR_VISIBLE_KEY, NULL));
gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), priv->sidebar_item);
g_signal_connect (priv->sidebar_item, "toggled",
G_CALLBACK (on_sidebar_toggled), status);
gtk_widget_show (priv->sidebar_item);
priv->control_panel_item = gtk_image_menu_item_new_with_label (_("System Preferences..."));
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (priv->control_panel_item),
gtk_image_new ());
@@ -542,8 +507,7 @@ create_sub_menu (ShellStatusMenu *status)
G_CALLBACK (on_login_screen_activate), status);
/* Only show switch user if there are other users */
/* Log Out */
priv->quit_session_item = gtk_image_menu_item_new_with_label (_("Log Out..."));
priv->quit_session_item = gtk_image_menu_item_new_with_label (_("Quit..."));
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (priv->quit_session_item),
gtk_image_new ());
gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), priv->quit_session_item);
@@ -553,17 +517,6 @@ create_sub_menu (ShellStatusMenu *status)
G_CALLBACK (on_quit_session_activate), status);
gtk_widget_show (priv->quit_session_item);
/* Shut down */
priv->shut_down_item = gtk_image_menu_item_new_with_label (_("Shut Down..."));
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (priv->shut_down_item),
gtk_image_new ());
gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), priv->shut_down_item);
g_signal_connect (priv->shut_down_item, "style-set",
G_CALLBACK (menuitem_style_set_cb), status);
g_signal_connect (priv->shut_down_item, "activate",
G_CALLBACK (on_shut_down_activate), status);
gtk_widget_show (priv->shut_down_item);
g_signal_connect (G_OBJECT (priv->menu), "deactivate",
G_CALLBACK (on_deactivate), status);
}
@@ -635,98 +588,37 @@ shell_status_menu_class_init (ShellStatusMenuClass *klass)
G_TYPE_NONE, 0);
}
ShellStatusMenu *
shell_status_menu_new (void)
{
return g_object_new (SHELL_TYPE_STATUS_MENU, NULL);
}
static void
position_menu (GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer user_data)
{
ShellStatusMenu *status = SHELL_STATUS_MENU (user_data);
ClutterActor *parent;
float src_x, src_y;
float width, height;
int menu_width;
gtk_widget_get_size_request (GTK_WIDGET (menu), &menu_width, NULL);
clutter_actor_get_transformed_position (CLUTTER_ACTOR (status), &src_x, &src_y);
/* Encapsulation breakage: it looks better if the menu is
* aligned with the bottom of the actor's grandparent - the
* panel, rather than with the bottom of the actor. We just
* assume what the hierarchy is and where we are positioned
* in the panel.
*/
parent = clutter_actor_get_parent (CLUTTER_ACTOR (status));
parent = clutter_actor_get_parent (parent);
clutter_actor_get_transformed_position (parent, &src_x, &src_y);
clutter_actor_get_transformed_size (parent, &width, &height);
*x = (gint)(0.5 + src_x + width - menu_width);
*y = (gint)(0.5 + src_y + height);
*x = (gint)(0.5 + src_x);
*y = (gint)(0.5 + src_y);
}
/**
* shell_status_menu_toggle:
* @menu: a #ShellStatusMenu
*
* If the menu is not currently up, pops it up. Otherwise, hides it.
* Popping up may fail if another grab is already active; check with
* shell_status_menu_is_active().
*/
void
shell_status_menu_toggle (ShellStatusMenu *status, ClutterEvent *event)
{
ShellStatusMenuPrivate *priv = status->priv;
if (GTK_WIDGET_VISIBLE (priv->menu))
if (GTK_WIDGET_VISIBLE (GTK_WIDGET (priv->menu)))
{
gtk_menu_popdown (GTK_MENU (priv->menu));
gtk_widget_hide (GTK_WIDGET (priv->menu));
}
else
{
/* We don't want to overgrab a Mutter grab with the grab that GTK+
* uses on menus.
*/
ShellGlobal *global = shell_global_get ();
if (shell_global_display_is_grabbed (global))
return;
gtk_widget_show (GTK_WIDGET (priv->menu));
gtk_menu_popup (GTK_MENU (priv->menu), NULL, NULL, position_menu,
status, 1, event->button.time);
status, 1, event->button.time);
}
}
/**
* shell_status_menu_is_active:
* @menu: a #ShellStatusMenu
*
* Gets whether the menu is currently popped up
*
* Return value: %TRUE if the menu is currently popped up
*/
gboolean
shell_status_menu_is_active (ShellStatusMenu *status)
{
ShellStatusMenuPrivate *priv = status->priv;
return GTK_WIDGET_VISIBLE (priv->menu);
}
/**
* shell_status_menu_get_name:
* @menu: a #ShellStatusMenu
*
* Return value: (transfer none): the #ClutterText actor with the user's name.
*/
ClutterText *
shell_status_menu_get_name (ShellStatusMenu *menu)
{
return menu->priv->name;
}
/**
* shell_status_menu_get_icon:
* @menu: a #ShellStatusMenu
*
* Return value: (transfer none): the #ClutterTexture actor with the user icon.
*/
ClutterTexture *
shell_status_menu_get_icon (ShellStatusMenu *menu)
{
return menu->priv->user_icon;
}

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