Compare commits

..

39 Commits

Author SHA1 Message Date
83f37da1c1 [StBoxLayout] initialize variable
Fix a missing initialization of 'i' when iterating over children.

https://bugzilla.gnome.org/show_bug.cgi?id=595995
http://bugzilla.moblin.org/show_bug.cgi?id=6311
2009-09-30 14:28:18 -04:00
c2706add36 StButton: Fix property enumeration names
Property enumeration names should correspond exactly to the property names;
in particular the ACTIVE vs :checked disparity was confusing reading the
code.

http://bugzilla.moblin.org/show_bug.cgi?id=6504
2009-09-30 10:21:36 -04:00
0a187b7222 testcommon.css: Don't theme all buttons
StButton is used for many things - scrollbar steppers, etc. Theming
all buttons to look like push-buttons breaks that. So in testcommon.css
just theme a .push-button class to look vaguely button-like.

https://bugzilla.gnome.org/show_bug.cgi?id=596432
2009-09-30 10:21:35 -04:00
3abe92d15d Port StTable to StThemeNode
Convert the StTable code from StStylable to StThemeNode. The
:row-spacing and :col-spacing GObject properties are converted
into spacing-rows and spacing-columns style properties.

A new interactive test is added for StTable.

https://bugzilla.gnome.org/show_bug.cgi?id=596811
2009-09-30 00:12:55 -04:00
45b4d0384c Handle adding children to StTable from Javascript
Remove the StTable specific methods to add actors:

 st_table_add_actor()
 st_table_add_actor_with_properties()

Since they shadow the generic ClutterContainer add_actor() method,
and patch in our add() convenience function as we do for
StBoxLayout.

https://bugzilla.gnome.org/show_bug.cgi?id=596811
2009-09-30 00:12:55 -04:00
083eed140c Import MxTable as StTable
Import table code from Mx library

https://bugzilla.gnome.org/show_bug.cgi?id=596811
2009-09-30 00:12:55 -04:00
1283f0b160 Turn StBoxLayout:spacing into a style property
Remove the StBoxLayout:spacing GObject property, and instead make
BoxLayout look up the spacing from the CSS style. This makes it
consistent with padding and will allow the use of units. (The
removal of the GObject property entirely instead of making it an
override is consistent with how we handle color, font, padding, etc.)

https://bugzilla.gnome.org/show_bug.cgi?id=596803
2009-09-29 23:06:18 -04:00
3bbdc1e1e1 lookingGlass: Get font from GConf
Instead of using "Monospace", pick the users configured monospace font
name up from GConf. (This is a nice touch, but is more done here to
demonstrate that we can do it rather than for any great utility.)

https://bugzilla.gnome.org/show_bug.cgi?id=591245
2009-09-29 19:58:31 -04:00
25f1246b6f Port LookingGlass console to ST widgets
* Style aspects like colors and fonts are moved into gnome-shell.css.
* Scrolling is adding using StScrollView.

Based on a patch from Colin Walters
https://bugzilla.gnome.org/show_bug.cgi?id=591245
2009-09-29 19:58:31 -04:00
a37c86636b Add clutter-text properties to StEntry and StLabel
Add clutter-text properties to allow getting access to the underlying
ClutterText actor. This corresponds to the get_clutter_text() methods.

The PROP_LABEL and PROP_ENTRY enum values are renamed to PROP_TEXT to
match the names of the properties that they correspond to, and the
properties of StEntry are reordered into alphabetical order.

Based on a patch from Colin Walters
https://bugzilla.gnome.org/show_bug.cgi?id=591245
http://bugzilla.moblin.org/show_bug.cgi?id=6313
2009-09-29 19:58:31 -04:00
4057cfaa17 Fix interaction of borders/background and scrolling
StBoxLayout: Make consistent that the area scrolled and clipped
to is the content area (excluding borders and padding.) Translate
back appropriately when chaining up so that the parent background
is drawn at the right place and picking on the box (if it's reactive)
picks at the right place on the screen.

clip-to-allocation is removed from StScrollView since it's just
not right - if the child has any non-moving elements, like headers or
borders, it will need to set a narrower clip. And even if the entire
child scrolls, we want to clip to an arrow that excludes the scrollbars.

https://bugzilla.gnome.org/show_bug.cgi?id=595997
2009-09-29 19:58:30 -04:00
76443e91cd Allocate children as wide as the scrolled area
When we are scrolling a vertical box horizontally , children should be
allocated horizontally as wide as the full horizontal scrolled area,
not just to the size of the "viewport". Similarly for a horizontal box.

http://bugzilla.moblin.org/show_bug.cgi?id=6312
https://bugzilla.gnome.org/show_bug.cgi?id=595996
2009-09-29 19:58:30 -04:00
d3c4c1f5ed Allow StBoxLayout to shrink down to its minimum size
When a StBoxLayout is allocated a size less than its natural size,
think "shrink" needs to be divided among the children that have
a smaller minimum size than natural size.

This is done by preferentially shrinking the children that are most
expanded from their minimum size and then increasing that set of
children until we've found enough total shrink.

A new method is used of allocating children at integral sizes - instead
of rounding the per-child extra amount to an integer (which causes
cumulative round-off errors), compute the position as we go along in
floats and round individually for each child widget.

Extend the box-layout test to include of a test of a box being set
to various widths, starting quite narrow.

http://bugzilla.moblin.org/show_bug.cgi?id=6311
https://bugzilla.gnome.org/show_bug.cgi?id=595995
2009-09-29 19:58:30 -04:00
040ddf077c Don't count not-visible children among expand children
When counting how many children we should divide extra space among,
don't count not-visible children.

http://bugzilla.moblin.org/show_bug.cgi?id=6310
https://bugzilla.gnome.org/show_bug.cgi?id=595995
2009-09-29 19:58:30 -04:00
f313d38458 Don't use the default stage when setting up adjustments
If the actor isn't in a stage, then setting up the adjustment
based on the actor's size (which we can't compute) and the
size of the default stage (which isn't relevant), doesn't make
sense. Just use arbitrary default values.

The adjustments will be updated to reasonable values when first
the box is first allocated.

It's not entirely clear to me why we ever want to compute the
adjustment settings this way; perhaps we should always use
default values.

http://bugzilla.moblin.org/show_bug.cgi?id=6307
https://bugzilla.gnome.org/show_bug.cgi?id=595996
2009-09-29 19:58:30 -04:00
304b48a15d Match CSS for background extents
The CSS specification says that the background extends to the
edge of the border (settable in CSS3 with border-clip), make
BigRectangle match this by computing an "effective border color"
as 'border OVER background'.

(If we don't want this behavior - e.g., to be able to use the
transparent borders as margins, then alternatively transparent
border handling would have to be fixed in st-widget.c, since
prior to this transparent and translucent borders were handled
differently.)

https://bugzilla.gnome.org/show_bug.cgi?id=595993
2009-09-29 19:58:30 -04:00
d92b1d8da2 Rename StThemeImage to StBorderImage
The current CSS3 border-image is close to a superset of what we were
doing for -hippo-background-image. Woot! rename StThemeImage to
StBorderImage and change parsing to look for:

 border-image: <url> <number>...

Rather than

 -st-background-image: <url> <length>...

percentanges for the border sizes are not currently supported, neither
are the keywords for handling of the middle part. We always do 'stretch'
for now.

https://bugzilla.gnome.org/show_bug.cgi?id=595990
2009-09-29 19:58:30 -04:00
df3ac4b25e Add support for colored borders
Use BigRectangle to draw the border and background if there's
a border width or border radius and no border image. (Only
uniform borders are supported for now with some deviations
from the CSS model noted in the comments.)

The background color and image parameters are removed from
StWidget's draw_background() method since they were not used
for StButton (the only current user) and the encapsulation
break that they presented caused some minor problems.

Add a test case for borders, and also use borders to style
the buttons in the 'inline-style' test case.

https://bugzilla.gnome.org/show_bug.cgi?id=595993
2009-09-29 19:58:30 -04:00
0ce05a04c8 Centralize computations of border and padding into StThemeNode
Rather than repeating the computation of borders in many different
widget subclasses, add helper functions:

 st_theme_node_adjust_for_height()
 st_theme_node_adjust_preferred_width()
 st_theme_node_adjust_for_width()
 st_theme_node_adjust_preferred_height()
 st_theme_node_get_content_box()

That are used in get_preferred_width()/get_preferred_height() and
allocate() methods to consistently apply the necessary adjustments.
This allows removing the StPadding type.

Queueing a relayout when the borders/padding change is moved from
st_widget_real_style_changed() to the invoking code to allow access
to the old StThemeNode for comparison. (Should this be added as
a parameter to the signal?)

Borders are included in the geometry adjustments, but borders
are not yet drawn.

https://bugzilla.gnome.org/show_bug.cgi?id=595993
2009-09-29 19:58:30 -04:00
c1c4adda02 StThemeNode: Add border-radius support
Add support for parsing and caching the border-radius property.
Different radii for the 4 corners are supported; elliptical corners
are not supported.

https://bugzilla.gnome.org/show_bug.cgi?id=595993
2009-09-29 19:58:29 -04:00
595242c389 Fix problems with 4-sided padding: specifiers
The test for identifying such a specifier was wrong, and the last
value was assigned to the wrong sides.

https://bugzilla.gnome.org/show_bug.cgi?id=595990
2009-09-29 19:58:29 -04:00
7e678ef0d2 Add support for inline styles
Add support for passing an inline-style string when creating a
StThemeNode.

Hook this up to a new 'style' property of StWidget.

Add a test case that demonstrates using this to update font sizes
on the fly.

https://bugzilla.gnome.org/show_bug.cgi?id=595991
2009-09-29 19:58:29 -04:00
ebbf304899 run-test.sh: support running tests under gdb
As with the 'gnome-shell' -g/--debug can be passed to run under
the debugger.

https://bugzilla.gnome.org/show_bug.cgi?id=595987
2009-09-29 19:58:29 -04:00
2077485827 Port our imported parts of Mx to ShellTheme
ShellTheme replaces both StStyle and ccss_stylesheet_t.

The interface StStylable is replaced by usage of ShellThemeNode.
A concrete node class allows some significant optimizations of property
inheritance that would have been much more difficult to achieve with
the highly abstract pair of StStylable and ccss_node_t.

Some operations that were previously on StStylable (like the
::style-changed signal) are directly on NtkWidget.

Custom properties are no longer registered as param-specs; instead you
call directly into shell theme node to look up a length or color:

shell_theme_node_get_length (theme_node, "border-spacing", FALSE, &spacing);

The dependency on libccss is dropped, while preserving all existing
functionality and adding proper parsing and inheritance of font properties
and proper inheritance for the 'color' property.

Some more javascript tests for CSS functionality are added; workarounds for
a CSS bug where *.some-class was needed instead of .some-class are removed.

https://bugzilla.gnome.org/show_bug.cgi?id=595990
2009-09-29 19:58:29 -04:00
0c0a0c66e2 Add emacs mode-lines to ST sources
To each .c and .h file, add:

 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */

'gnu' is the default anyways for Emacs, but indent-tabs-mode is not,
so this sets things up to correspond to the policy of no-tabs.

http://bugzilla.moblin.org/show_bug.cgi?id=6467
2009-09-29 19:58:23 -04:00
2412a89445 Import stylesheet code from hippo-canvas
Import:

  HippoCanvasTheme      => StTheme
  HippoCanvasThemeImage => StThemeImage
  HippoCanvasStyle      => StThemeNode

StThemeContext is a new class managing the theme for a stage and
global properties like resolution.

test-theme.c is a newly written test program to do verification of the
style matching and property handling rules.

Various changes are made in the import:

 - Comprehensive reindentation
 - guint32 pixels replaced with ClutterColor
 - General pseudo-class support added
 - Old-fashioned (non-bordered) background image support added, though
   with no support for repeat, etc.
 - Bug fixes for problems revealed by test program

https://bugzilla.gnome.org/show_bug.cgi?id=595990
2009-09-29 19:58:21 -04:00
789e268264 Fix installation and distribution of stylesheet data
Install and distribute gnome-shell.css and theme images. They are moved
down from $datadir to $datadir/theme to avoid a weirdness where we have
images in $datadir and then also in $datadir/images.

(Also moved in the source tree to avoid adding another difference between
installed and uninstalled operation.)

https://bugzilla.gnome.org/show_bug.cgi?id=595989
2009-09-29 19:58:21 -04:00
7507d10223 Extend distcheck for files in Git to all files
Instead of just checking that we distribute all Javascript files, check
that we distribute everything that is in Git.

The toplevel Makefile.am has a variable DIST_EXCLUDE that lists patterns
of files that we actually don't want to distribute.

Remove several stale C files that we are no longer using.

https://bugzilla.gnome.org/show_bug.cgi?id=595988
2009-09-29 19:58:21 -04:00
b18a8ebcae Add some structure for interactive tests of UI components
js/ui/environment.js: Split out initial UI setup (Tweener initialization,
  ClutterContainer monkey-patching) into a separate file we can import from tests.

tests/: Directory for various types of tests
tests/run-test.sh: Shell script that to run tests with an appropriate
  environment set up.

tests/testcommon/: Common modules and data for tests
tests/interactive/: Interactive tests

tests/interactive/box-layout.js: A sample test of StLayout

https://bugzilla.gnome.org/show_bug.cgi?id=595987
2009-09-29 19:58:21 -04:00
ed07413c20 Monkey-patch in ClutterContainer methods for StBoxLayout
Setting options for children added to StBoxLayout is not convenient
since we are missing the varargs methods of clutter_container.

Patch in:

 child_set() - set properties of a child
 add() - add a child and set properties (this is different from
         clutter_container_add()! I think the deviation is
         with avoiding the awkward name add_with_properties()
         which is what might be expected. ClutterContainer
         currently doesn't have a method like this at all.)

The code is written to allow patching into multiple ClutterContainer
classes but for now only StBoxLayout is patched, since it's the only
container we are using where we need to set options as properties.

https://bugzilla.gnome.org/show_bug.cgi?id=595419
2009-09-29 19:58:21 -04:00
f94eab803b Add GObject Introspection annotations
Add GObject Introspection annotations to methods where needed, in
particular adding (transfer none) to return values that don't transfer
ownership.

st_texture_cache_get_actor() and st_texture_cache_get_texture()
are annotated as (transfer none) since they return a newly
created *floating* texture.

https://bugzilla.gnome.org/show_bug.cgi?id=591245
2009-09-29 19:58:20 -04:00
0315a6e4a8 Import MxEntry, MxLabel, MxClipboard
For now this commit introduces an external dependency on clutter-imcontext.

https://bugzilla.gnome.org/show_bug.cgi?id=591245
2009-09-29 19:58:20 -04:00
48085dd428 Import MxBoxLayout, MxBoxLayoutChild
https://bugzilla.gnome.org/show_bug.cgi?id=591245
2009-09-29 19:58:20 -04:00
099b73a0c4 Add css for scrolling
Port bits of Mx's default.css into gnome-shell.css, and use some
hand-rolled .pngs with colors from
http://live.gnome.org/GnomeShell/DesignerPlayground/ExpandedViewMockups

https://bugzilla.gnome.org/show_bug.cgi?id=591245
2009-09-29 19:58:20 -04:00
b8d46422d5 Load gnome-shell.css at startup
https://bugzilla.gnome.org/show_bug.cgi?id=591245
2009-09-29 19:58:20 -04:00
459bdfba78 Add a "datadir" property
Will be used to load stylesheets from main.js.

https://bugzilla.gnome.org/show_bug.cgi?id=591245
2009-09-29 19:58:20 -04:00
66414ea3f6 Remove hardcoded '28' from StScrollView
https://bugzilla.gnome.org/show_bug.cgi?id=591245
2009-09-29 19:58:20 -04:00
d453067e24 Import MxScrollView and dependencies
https://bugzilla.gnome.org/show_bug.cgi?id=591245
2009-09-29 19:58:19 -04:00
ae320a26fc Import Mx core as ST
Import the core MxWidget/MxBin and their dependencies; we use the
namespace "St" (Shell Toolkit) because it is the same length as Mx
so enabling easy sharing of code, but makes it clear that this is
a friendly fork and not a literal import.

Based on a patch by Colin Walters <walters@verbum.org>

https://bugzilla.gnome.org/show_bug.cgi?id=591245
2009-09-29 19:58:12 -04:00
168 changed files with 7863 additions and 16462 deletions

2
.gitignore vendored
View File

@ -42,5 +42,3 @@ src/test-theme
stamp-h1
tests/run-test.sh
xmldocs.make
*~
*.patch

View File

@ -1,4 +1,4 @@
AC_INIT(gnome-shell, 2.28.1)
AC_INIT(gnome-shell, 2.27.3)
AC_CONFIG_AUX_DIR(config)
@ -57,7 +57,7 @@ PKG_CHECK_MODULES(MUTTER_PLUGIN, gio-unix-2.0 gtk+-2.0 dbus-glib-1 mutter-plugin
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(ST, clutter-1.0 gtk+-2.0 libcroco-0.6)
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-2.0 clutter-imcontext-0.1 libcroco-0.6)
PKG_CHECK_MODULES(BIG, clutter-1.0 gtk+-2.0 librsvg-2.0)
PKG_CHECK_MODULES(GDMUSER, dbus-glib-1 gtk+-2.0)
PKG_CHECK_MODULES(TRAY, gtk+-2.0)

View File

@ -16,22 +16,21 @@ imagesdir = $(pkgdatadir)/images
dist_images_DATA = \
add-workspace.svg \
app-well-glow.png \
back.svg \
close.svg \
close-black.svg \
info.svg \
magnifier.svg \
remove-workspace.svg
themedir = $(pkgdatadir)/theme
dist_theme_DATA = \
theme/gnome-shell.css \
theme/close.svg \
theme/close-window.svg \
theme/scroll-button-down.png \
theme/scroll-button-down-hover.png \
theme/scroll-button-up.png \
theme/scroll-button-up-hover.png \
theme/scroll-vhandle.png \
theme/section-back.svg \
theme/section-more.svg
theme/scroll-vhandle.png
schemadir = @GCONF_SCHEMA_FILE_DIR@
schema_DATA = gnome-shell.schemas

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -88,21 +88,6 @@
</locale>
</schema>
<schema>
<key>/schemas/desktop/gnome/shell/disabled_extensions</key>
<applyto>/desktop/gnome/shell/disabled_extensions</applyto>
<owner>gnome-shell</owner>
<type>list</type>
<list_type>string</list_type>
<default>[]</default>
<locale name="C">
<short>Uuids of extensions to disable</short>
<long>
GNOME Shell extensions have a uuid property; this key lists extensions which should not be loaded.
</long>
</locale>
</schema>
</schemalist>
</gconfschemafile>

74
data/info.svg Normal file
View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.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"
sodipodi:docname="info_16.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
id="metadata2389"><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="defs2387"><linearGradient
id="linearGradient3710"><stop
style="stop-color:#c4c4c4;stop-opacity:1;"
offset="0"
id="stop3712" /><stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="1"
id="stop3714" /></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="perspective2391" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3710"
id="linearGradient3716"
x1="7.9066148"
y1="15.937743"
x2="7.9377432"
y2="0.031128405"
gradientUnits="userSpaceOnUse" /></defs><sodipodi:namedview
inkscape:window-height="713"
inkscape:window-width="722"
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="8.154146"
inkscape:window-x="20"
inkscape:window-y="20"
inkscape:current-layer="Foreground" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M7,3h2v2H7V3z M5.5,12H7V8H5.5V7H9v5h1.5v1h-5V12z M0,8c0-4.418,3.582-8,8-8 s8,3.582,8,8s-3.582,8-8,8S0,12.418,0,8z"
id="path2384"
style="fill-opacity:1;fill:url(#linearGradient3716)" />
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -1,76 +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="22"
height="22"
viewBox="0 0 16 16"
enable-background="new 0 0 16 16"
xml:space="preserve"
sodipodi:version="0.32"
inkscape:version="0.46"
sodipodi:docname="close-window.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 /></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="999"
inkscape:window-width="1680"
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="false"
inkscape:zoom="25.648691"
inkscape:cx="8.8097603"
inkscape:cy="9.0472789"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:current-layer="Foreground"
showguides="true"
inkscape:guide-bbox="true" />
<g
id="g3175"><path
sodipodi:nodetypes="csssc"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.59217799;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path2394"
d="M 0.83987936,8.0425327 C 0.83987936,4.0805265 4.0712155,0.86823453 8.0567103,0.86823453 C 12.042205,0.86823453 15.273542,4.0805265 15.273542,8.0425327 C 15.273542,12.004539 12.042205,15.216831 8.0567103,15.216831 C 4.0712155,15.216831 0.83987936,12.004539 0.83987936,8.0425327 z"
clip-rule="evenodd" /><g
id="g3172"><path
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.67127273;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 5.4242673,5.3313047 L 10.515414,10.421272 L 10.714004,10.646491"
id="path3152" /></g></g><path
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.67127273;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 5.4402527,10.650392 L 10.688082,5.3573033"
id="path3154"
sodipodi:nodetypes="cc" /></svg>

Before

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -17,17 +17,9 @@
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*/
.shell-link {
color: #0000ff;
text-decoration: underline;
}
.shell-link:hover {
color: #0000e0;
}
StScrollBar
{
background-color: #354761;
padding: 0px;
}
@ -69,291 +61,6 @@ StScrollBar StButton#vhandle:hover
border-image: url("scroll-vhandle.png") 5;
}
StTooltip {
border: 1px solid rgba(79,111,173,0.9);
border-radius: 5px;
padding: 4px;
background-color: rgba(79,111,173,0.9);
color: #ffffff;
}
/* Panel */
#panel {
color: #ffffff;
font-size: 16px;
background-gradient-direction: vertical;
background-gradient-start: #161616;
background-gradient-end: #000000;
}
#panelLeft, #panelCenter, #panelRight {
spacing: 4px;
}
#panelLeft {
padding-right: 4px;
}
#panelRight {
padding-left: 4px;
}
.panel-button:pressed {
background-color: rgba(50,76,111,0.98);
border-radius: 4px;
}
#appMenu {
spacing: 4px;
}
.app-menu-icon {
width: 24px;
height: 24px;
}
.panel-button {
padding: 4px 12px 3px;
border-radius: 5px;
font: 16px sans-serif;
}
.panel-button:active, .panel-button:checked {
background-color: #314a6c;
}
#panelStatus {
spacing: 4px;
}
/* Overlay */
.workspaces {
color: white;
}
.window-caption {
background: rgba(0,0,0,0.8);
border: 1px solid rgba(128,128,128,0.40);
border-radius: 10px;
font-size: 12px;
padding: 2px 8px;
-shell-caption-spacing: 4px;
}
.window-close {
background-image: url("close-window.svg");
height: 24px;
width: 24px;
-shell-close-overlap: 16px;
}
/* Dash */
#dash {
color: #5f5f5f;
background-color: rgba(0,0,0,0.75);
padding: 0px 14px;
}
#dashSections {
spacing: 12px;
}
#searchEntry {
padding: 4px;
border-bottom: 1px solid #262626;
}
#searchEntry:active {
background-color: #c4c4c4;
}
.dash-section {
spacing: 8px;
}
.section-header {
border: 1px solid #262626;
background-gradient-direction: vertical;
background-gradient-start: #161616;
background-gradient-end: #000000;
font-weight: bold;
font-size: 12px;
}
.section-header-inner {
border: 1px solid #000000;
padding: 0px 4px;
spacing: 4px;
}
.section-text-content {
padding: 4px 0px;
}
.section-header-back {
padding: 0px 4px 0px 0px;
border-right: 1px solid #262626;
}
.section-header-back-image {
background-image: url("section-back.svg");
width: 12px;
height: 16px;
}
.section-count {
}
.dash-section-content {
font-size: 14px;
color: #ffffff;
spacing: 8px;
}
.more-link {
}
.more-link-expander {
background-image: url("section-more.svg");
width: 9px;
height: 9px;
}
.dash-pane {
background-color: rgba(0,0,0,0.95);
border: 1px solid #262626;
padding: 4px;
spacing: 4px;
}
.dash-pane-close {
background-image: url("close.svg");
width: 16px;
height: 16px;
}
.dash-search-section-header {
padding: 6px 0px;
spacing: 4px;
}
.dash-search-section-results {
color: #ffffff;
padding-left: 4px;
}
.dash-search-section-list-results {
spacing: 4px;
}
.dash-search-result-content {
padding: 2px;
}
.dash-search-result-content:selected {
padding: 1px;
border: 1px solid #262626;
}
/* GenericDisplay */
.generic-display-container {
spacing: 4px;
}
.generic-display-item {
height: 50px;
border-radius: 4px;
color: #ffffff;
font-size: 14px;
spacing: 4px;
}
.generic-display-item:selected {
background-color: rgba(79,111,173,0.66);
}
.generic-display-item-text {
spacing: 4px;
}
.generic-display-item-description {
font-size: 12px;
color: #bababa;
}
.generic-display-details {
font-size: 14px;
color: #ffffff;
}
.generic-display-details-name {
font-weight: bold;
}
/* Apps */
#dashAppWell {
spacing: 2px;
-shell-grid-item-size: 74px;
}
.app-well-app {
border: 1px solid #080808;
border-radius: 2px;
padding: 2px;
width: 74px;
height: 74px;
font-size: 12px;
}
.app-well-app:hover {
border: 1px solid #202020;
}
.app-well-app:active {
background-color: #1e1e1e;
border: 1px solid #5f5f5f;
}
.app-well-app-glow {
-shell-glow-extend-vertical: 3px;
-shell-glow-shrink-horizontal: 3px;
}
.app-well-menu {
border: 1px solid #5f5f5f;
border-radius: 4px;
padding: 4px;
background-color: rgba(0,0,0,0.9);
color: #ffffff;
-shell-arrow-width: 12px;
-shell-menu-spacing: 4px;
}
.app-well-menu-item:hover {
background-color: #1e1e1e;
}
.app-well-menu-separator {
padding-top: 1px;
border-bottom: 1px solid #5f5f5f;
height: 1px;
}
/* Places */
.places-actions {
spacing: 4px;
}
#placesDevices {
padding-top: 4px;
}
/* LookingGlass */
#LookingGlassDialog
@ -373,19 +80,6 @@ StTooltip {
border-radius: 4px;
}
#LookingGlassDialog .labels {
spacing: 4px;
}
#LookingGlassDialog .notebook-tab {
padding: 2px;
}
#LookingGlassDialog .notebook-tab:selected {
border: 1px solid #88ff66;
padding: 1px;
}
#LookingGlassDialog StLabel
{
color: #88ff66;
@ -401,138 +95,3 @@ StTooltip {
padding: 4px;
spacing: 4px;
}
#lookingGlassExtensions {
padding: 4px;
}
.lg-extension-list {
padding: 4px;
spacing: 6px;
}
.lg-extension {
border: 1px solid #6f6f6f;
border-radius: 4px;
padding: 4px;
}
.lg-extension-name {
font-weight: bold;
}
.lg-extension-actions {
spacing: 6px;
}
/* Calendar popup */
#calendarPopup {
border-radius: 5px;
background: rgba(0,0,0,0.9);
border: 1px solid rgba(128,128,128,0.45);
color: white;
}
#calendarPopup .calendar {
padding: 10px;
}
.calendar {
spacing-rows: 5px;
spacing-columns: 3px;
}
.calendar-change-month {
padding: 2px;
}
.calendar-change-month:hover {
background: #314a6c;
border-radius: 5px;
}
.calendar-change-month:active {
background: #213050;
border-radius: 5px;
}
.calendar-day {
padding: 1px 2px;
}
.calendar-today {
font-weight: bold;
background: #ffffff;
color: black;
border-radius: 5px;
}
.calendar-other-month-day {
color: #cccccc;
}
/* Message Tray */
#message-tray {
background-gradient-direction: vertical;
background-gradient-start: rgba(0,0,0,0.01);
background-gradient-end: rgba(0,0,0,0.95);
height: 28px;
}
#notification {
border-radius: 5px;
background: rgba(0,0,0,0.9);
color: white;
padding: 2px 10px;
spacing: 10px;
}
#summary-mode {
spacing: 10px;
padding: 2px 4px;
}
/* App Switcher */
.switcher-list {
background: rgba(0,0,0,0.8);
border: 1px solid rgba(128,128,128,0.40);
border-radius: 8px;
padding: 18px;
font: 12px sans-serif;
color: white;
}
.switcher-list .item-box {
padding: 8px;
border-radius: 4px;
}
.switcher-list .thumbnail-box {
padding: 2px;
spacing: 4px;
}
.switcher-list .thumbnail {
width: 256px;
height: 256px;
}
.switcher-list .outlined-item-box {
padding: 6px;
border: 2px solid rgba(85,85,85,1.0);
border-radius: 4px;
}
.switcher-list .selected-item-box {
padding: 8px;
border-radius: 4px;
background: rgba(255,255,255,0.33);
}
.switcher-list .separator {
width: 1px;
background: rgba(255,255,255,0.33);
}

View File

@ -1,87 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="5.8600588"
height="9"
id="svg3647"
version="1.1"
inkscape:version="0.46+devel"
sodipodi:docname="New document 6">
<defs
id="defs3649">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective3655" />
<inkscape:perspective
id="perspective3603"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.35"
inkscape:cx="112.21575"
inkscape:cy="-32.642856"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="609"
inkscape:window-height="501"
inkscape:window-x="164"
inkscape:window-y="26"
inkscape:window-maximized="0" />
<metadata
id="metadata3652">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-262.78425,-490.71933)">
<path
transform="matrix(0,0.98149546,-0.71467449,0,506.02358,412.28296)"
d="M 88.830127,340 80.169873,340 84.5,332.5 88.830127,340 z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="true"
sodipodi:arg2="1.5707963"
sodipodi:arg1="0.52359878"
sodipodi:r2="2.5"
sodipodi:r1="5"
sodipodi:cy="337.5"
sodipodi:cx="84.5"
sodipodi:sides="3"
id="path5497-5"
style="fill:#5f5f5f;fill-opacity:1;stroke:#5f5f5f;stroke-width:0.59699643;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
sodipodi:type="star" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -1,6 +1,4 @@
jsmiscdir = $(pkgdatadir)/js/misc
dist_jsmisc_DATA = \
docInfo.js \
format.js \
params.js
docInfo.js

View File

@ -7,7 +7,6 @@ const Shell = imports.gi.Shell;
const Lang = imports.lang;
const Signals = imports.signals;
const Search = imports.ui.search;
const Main = imports.ui.main;
const THUMBNAIL_ICON_MARGIN = 2;
@ -18,43 +17,69 @@ function DocInfo(recentInfo) {
DocInfo.prototype = {
_init : function(recentInfo) {
this.recentInfo = 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._lowerName = this.name.toLowerCase();
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);
return Shell.TextureCache.get_default().load_recent_thumbnail(size, this._recentInfo);
},
launch : function() {
Shell.DocSystem.get_default().open(this.recentInfo);
},
// While using Gio.app_info_launch_default_for_uri() would be
// shorter in terms of lines of code, we are not doing so
// because that would duplicate the work of retrieving the
// mime type.
matchTerms: function(terms) {
let mtype = Search.MatchType.NONE;
for (let i = 0; i < terms.length; i++) {
let term = terms[i];
let idx = this._lowerName.indexOf(term);
if (idx == 0) {
if (mtype != Search.MatchType.NONE)
return Search.MatchType.MULTIPLE;
mtype = Search.MatchType.PREFIX;
} else if (idx > 0) {
if (mtype != Search.MatchType.NONE)
return Search.MatchType.MULTIPLE;
mtype = Search.MatchType.SUBSTRING;
let appInfo = Gio.app_info_get_default_for_type(this.mimeType, true);
if (appInfo != null) {
appInfo.launch_uris([this.uri], Main.createAppLaunchContext());
} else {
log("Failed to get default application info for mime type " + this.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);
if (success) {
log("Will open a document with the following command: " + appExec);
// TODO: Change this once better support for creating
// GAppInfo is added to GtkRecentInfo, as right now
// this relies on the fact that the file uri is
// already a part of appExec, so we don't supply any
// files to appInfo.launch().
// The 'command line' passed to
// create_from_command_line is allowed to contain
// '%<something>' macros that are expanded to file
// name / icon name, etc, so we need to escape % as %%
appExec = appExec.replace(/%/g, "%%");
let appInfo = Gio.app_info_create_from_commandline(appExec, null, 0, null);
// The point of passing an app launch context to
// launch() is mostly to get startup notification and
// associated benefits like the app appearing on the
// right desktop; but it doesn't really work for now
// because with the way we create the appInfo we
// aren't reading the application's desktop file, and
// thus don't find the StartupNotify=true in it. So,
// despite passing the app launch context, no startup
// notification occurs.
appInfo.launch([], Main.createAppLaunchContext());
} else {
continue;
log("Failed to get application info for " + this.uri);
}
}
return mtype;
},
exists : function() {
return this._recentInfo.exists();
}
};
@ -66,86 +91,50 @@ function getDocManager() {
return docManagerInstance;
}
/**
* DocManager wraps the DocSystem, primarily to expose DocInfo objects
* which conform to the GenericDisplay item API.
*/
function DocManager() {
this._init();
}
DocManager.prototype = {
_init: function() {
this._docSystem = Shell.DocSystem.get_default();
this._infosByTimestamp = [];
this._infosByUri = {};
this._docSystem.connect('changed', Lang.bind(this, this._reload));
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._docSystem.get_all();
this._infosByTimestamp = [];
this._infosByUri = {};
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);
this._infosByTimestamp.push(docInfo);
this._infosByUri[docInfo.uri] = docInfo;
// we use GtkRecentInfo URI as an item Id
newItems[docInfo.uri] = docInfo;
}
this.emit('changed');
},
getTimestampOrderedInfos: function() {
return this._infosByTimestamp;
},
getInfosByUri: function() {
return this._infosByUri;
},
lookupByUri: function(uri) {
return this._infosByUri[uri];
},
queueExistenceCheck: function(count) {
return this._docSystem.queue_existence_check(count);
},
initialSearch: function(terms) {
let multipleMatches = [];
let prefixMatches = [];
let substringMatches = [];
for (let i = 0; i < this._infosByTimestamp.length; i++) {
let item = this._infosByTimestamp[i];
let mtype = item.matchTerms(terms);
if (mtype == Search.MatchType.MULTIPLE)
multipleMatches.push(item.uri);
else if (mtype == Search.MatchType.PREFIX)
prefixMatches.push(item.uri);
else if (mtype == Search.MatchType.SUBSTRING)
substringMatches.push(item.uri);
}
return multipleMatches.concat(prefixMatches.concat(substringMatches));
},
subsearch: function(previousResults, terms) {
let multipleMatches = [];
let prefixMatches = [];
let substringMatches = [];
for (let i = 0; i < previousResults.length; i++) {
let uri = previousResults[i];
let item = this._infosByUri[uri];
let mtype = item.matchTerms(terms);
if (mtype == Search.MatchType.MULTIPLE)
multipleMatches.push(uri);
else if (mtype == Search.MatchType.PREFIX)
prefixMatches.push(uri);
else if (mtype == Search.MatchType.SUBSTRING)
substringMatches.push(uri);
let deleted = {};
for (var uri in this._items) {
if (!(uri in newItems))
deleted[uri] = this._items[uri];
}
return multipleMatches.concat(prefixMatches.concat(substringMatches));
/* 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;
}
}

View File

@ -1,44 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
* This function is intended to extend the String object and provide
* an String.format API for string formatting.
* It has to be set up using String.prototype.format = Format.format;
* Usage:
* "somestring %s %d".format('hello', 5);
* It supports %s, %d and %f, for %f it also support precisions like
* "%.2f".format(1.526)
*/
function format() {
let str = this;
let i = 0;
let args = arguments;
return str.replace(/%(?:\.([0-9]+))?(.)/g, function (str, precisionGroup, genericGroup) {
if (precisionGroup != '' && genericGroup != 'f')
throw new Error("Precision can only be specified for 'f'");
switch (genericGroup) {
case '%':
return '%';
break;
case 's':
return args[i++].toString();
break;
case 'd':
return parseInt(args[i++]);
break;
case 'f':
if (precisionGroup == '')
return parseFloat(args[i++]);
else
return parseFloat(args[i++]).toFixed(parseInt(precisionGroup));
break;
default:
throw new Error('Unsupported conversion character %' + genericGroup);
}
return ""; // Suppress warning
});
}

View File

@ -1,33 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// parse:
// @params: caller-provided parameter object, or %null
// @default: function-provided defaults object
// @allowExtras: whether or not to allow properties not in @default
//
// Examines @params and fills in default values from @defaults for
// any properties in @defaults that don't appear in @params. If
// @allowExtras is not %true, it will throw an error if @params
// contains any properties that aren't in @defaults.
//
// If @params is %null, this returns @defaults.
//
// Return value: the updated params
function parse(params, defaults, allowExtras) {
if (!params)
return defaults;
if (!allowExtras) {
for (let prop in params) {
if (!(prop in defaults))
throw new Error('Unrecognized parameter "' + prop + '"');
}
}
for (let prop in defaults) {
if (!(prop in params))
params[prop] = defaults[prop];
}
return params;
}

View File

@ -3,29 +3,24 @@ jsuidir = $(pkgdatadir)/js/ui
dist_jsui_DATA = \
altTab.js \
appDisplay.js \
appFavorites.js \
calendar.js \
appIcon.js \
button.js \
chrome.js \
dash.js \
dnd.js \
docDisplay.js \
environment.js \
extensionSystem.js \
genericDisplay.js \
lightbox.js \
link.js \
lookingGlass.js \
main.js \
messageTray.js \
notificationDaemon.js \
overview.js \
panel.js \
placeDisplay.js \
places.js \
runDialog.js \
search.js \
shellDBus.js \
sidebar.js \
statusMenu.js \
tweener.js \
widget.js \
widgetBox.js \

View File

@ -4,35 +4,24 @@ const Big = imports.gi.Big;
const Clutter = imports.gi.Clutter;
const Gdk = imports.gi.Gdk;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Pango = imports.gi.Pango;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
const AppIcon = imports.ui.appIcon;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const POPUP_ARROW_COLOR = new Clutter.Color();
POPUP_ARROW_COLOR.from_pixel(0xffffffff);
const POPUP_UNFOCUSED_ARROW_COLOR = new Clutter.Color();
POPUP_UNFOCUSED_ARROW_COLOR.from_pixel(0x808080ff);
const TRANSPARENT_COLOR = new Clutter.Color();
TRANSPARENT_COLOR.from_pixel(0x00000000);
const POPUP_BG_COLOR = new Clutter.Color();
POPUP_BG_COLOR.from_pixel(0x00000080);
const POPUP_APPICON_BORDER_COLOR = new Clutter.Color();
POPUP_APPICON_BORDER_COLOR.from_pixel(0xffffffff);
const POPUP_APPICON_SIZE = 96;
const POPUP_LIST_SPACING = 8;
const POPUP_GRID_SPACING = 8;
const POPUP_ICON_SIZE = 48;
const POPUP_NUM_COLUMNS = 5;
const DISABLE_HOVER_TIMEOUT = 500; // milliseconds
const THUMBNAIL_SIZE = 256;
const THUMBNAIL_POPUP_TIME = 500; // milliseconds
const THUMBNAIL_FADE_TIME = 0.2; // seconds
function mod(a, b) {
return (a + b) % b;
}
const POPUP_POINTER_SELECTION_THRESHOLD = 3;
function AltTabPopup() {
this._init();
@ -40,31 +29,61 @@ function AltTabPopup() {
AltTabPopup.prototype = {
_init : function() {
this.actor = new Clutter.Group({ reactive: true,
x: 0,
y: 0,
width: global.screen_width,
height: global.screen_height });
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,
reactive: true });
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
// 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,
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._icons = [];
this._currentWindows = [];
this._haveModal = false;
this._currentApp = 0;
this._currentWindow = 0;
this._thumbnailTimeoutId = 0;
this._motionTimeoutId = 0;
// Initially disable hover so we ignore the enter-event if
// the switcher appears underneath the current pointer location
this._disableHover();
this._selected = 0;
this._highlightedWindow = null;
this._toplevels = global.window_group.get_children();
global.stage.add_actor(this.actor);
},
show : function(backward) {
let tracker = Shell.WindowTracker.get_default();
let apps = tracker.get_running_apps ("");
_addIcon : function(appIcon) {
appIcon.connect('activate', Lang.bind(this, this._appClicked));
appIcon.connect('activate-window', Lang.bind(this, this._windowClicked));
appIcon.connect('highlight-window', Lang.bind(this, this._windowHovered));
appIcon.connect('menu-popped-up', Lang.bind(this, this._menuPoppedUp));
appIcon.connect('menu-popped-down', Lang.bind(this, this._menuPoppedDown));
appIcon.actor.connect('enter-event', Lang.bind(this, this._iconEntered));
// FIXME?
appIcon.actor.border = 2;
appIcon.highlight_border_color = POPUP_APPICON_BORDER_COLOR;
this._icons.push(appIcon);
this._currentWindows.push(appIcon.windows[0]);
// Add it to the grid
if (!this._gridRow || this._gridRow.get_children().length == POPUP_NUM_COLUMNS) {
this._gridRow = new Big.Box({ spacing: POPUP_GRID_SPACING,
orientation: Big.BoxOrientation.HORIZONTAL });
this._grid.append(this._gridRow, Big.BoxPackFlags.NONE);
}
this._gridRow.append(appIcon.actor, Big.BoxPackFlags.NONE);
},
show : function(initialSelection) {
let appMonitor = Shell.AppMonitor.get_default();
let apps = appMonitor.get_running_apps ("");
if (!apps.length)
return false;
@ -76,52 +95,36 @@ AltTabPopup.prototype = {
this._keyPressEventId = global.stage.connect('key-press-event', Lang.bind(this, this._keyPressEvent));
this._keyReleaseEventId = global.stage.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent));
this.actor.connect('button-press-event', Lang.bind(this, this._clickedOutside));
this.actor.connect('scroll-event', Lang.bind(this, this._onScroll));
this._motionEventId = this.actor.connect('motion-event', Lang.bind(this, this._mouseMoved));
this._mouseActive = false;
this._mouseMovement = 0;
this._appSwitcher = new AppSwitcher(apps);
this.actor.add_actor(this._appSwitcher.actor);
this._appSwitcher.connect('item-activated', Lang.bind(this, this._appActivated));
this._appSwitcher.connect('item-entered', Lang.bind(this, this._appEntered));
// Contruct the AppIcons, sort by time, add to the popup
let icons = [];
for (let i = 0; i < apps.length; i++)
icons.push(new AppIcon.AppIcon(apps[i], AppIcon.MenuType.BELOW));
icons.sort(Lang.bind(this, this._sortAppIcon));
for (let i = 0; i < icons.length; i++)
this._addIcon(icons[i]);
let primary = global.get_primary_monitor();
this._appSwitcher.actor.x = primary.x + Math.floor((primary.width - this._appSwitcher.actor.width) / 2);
this._appSwitcher.actor.y = primary.y + Math.floor((primary.height - this._appSwitcher.actor.height) / 2);
// Need to specify explicit width and height because the
// window_group may not actually cover the whole screen
this._lightbox = new Lightbox.Lightbox(global.window_group,
global.screen_width,
global.screen_height);
this._appIcons = this._appSwitcher.icons;
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);
// Make the initial selection
if (this._appIcons.length == 1) {
if (!backward && this._appIcons[0].cachedWindows.length > 1) {
// For compatibility with the multi-app case below
this._select(0, 1, true);
} else
this._select(0);
} else if (backward) {
this._select(this._appIcons.length - 1);
} else {
let firstWindows = this._appIcons[0].cachedWindows;
if (firstWindows.length > 1) {
let curAppNextWindow = firstWindows[1];
let nextAppWindow = this._appIcons[1].cachedWindows[0];
// If the next window of the current app is more-recently-used
// than the first window of the next app, then select it.
if (curAppNextWindow.get_workspace() == global.screen.get_active_workspace() &&
curAppNextWindow.get_user_time() > nextAppWindow.get_user_time())
this._select(0, 1, true);
else
this._select(1);
} else
this._select(1);
}
this._updateSelection(initialSelection);
// There's a race condition; if the user released Alt before
// we got the grab, then we won't be notified. (See
// https://bugzilla.gnome.org/show_bug.cgi?id=596695 for
// details.) So we check now. (Have to do this after updating
// selection.)
let mods = global.get_modifier_keys();
// details.) So we check now. (Have to do this after calling
// _updateSelection.)
let [screen, x, y, mods] = Gdk.Display.get_default().get_pointer();
if (!(mods & Gdk.ModifierType.MOD1_MASK)) {
this._finish();
return false;
@ -130,59 +133,40 @@ AltTabPopup.prototype = {
return true;
},
_nextApp : function() {
return mod(this._currentApp + 1, this._appIcons.length);
},
_previousApp : function() {
return mod(this._currentApp - 1, this._appIcons.length);
_hasVisibleWindows : function(appIcon) {
for (let i = 0; i < appIcon.windows.length; i++) {
if (appIcon.windows[i].showing_on_its_workspace())
return true;
}
return false;
},
_nextWindow : function() {
return mod(this._currentWindow + 1,
this._appIcons[this._currentApp].cachedWindows.length);
},
_previousWindow : function() {
return mod(this._currentWindow - 1,
this._appIcons[this._currentApp].cachedWindows.length);
_sortAppIcon : function(appIcon1, appIcon2) {
let vis1 = this._hasVisibleWindows(appIcon1);
let vis2 = this._hasVisibleWindows(appIcon2);
if (vis1 && !vis2) {
return -1;
} else if (vis2 && !vis1) {
return 1;
} else {
// The app's most-recently-used window is first
// in its list
return (appIcon2.windows[0].get_user_time() -
appIcon1.windows[0].get_user_time());
}
},
_keyPressEvent : function(actor, event) {
let keysym = event.get_key_symbol();
let shift = (Shell.get_event_state(event) & Clutter.ModifierType.SHIFT_MASK);
let backwards = (event.get_state() & Clutter.ModifierType.SHIFT_MASK);
this._disableHover();
// The WASD stuff is for debugging in Xephyr, where the arrow
// keys aren't mapped correctly
if (keysym == Clutter.grave)
this._select(this._currentApp, shift ? this._previousWindow() : this._nextWindow());
if (keysym == Clutter.Tab)
this._updateSelection(backwards ? -1 : 1);
else if (keysym == Clutter.grave)
this._updateWindowSelection(backwards ? -1 : 1);
else if (keysym == Clutter.Escape)
this.destroy();
else if (this._thumbnailsFocused) {
if (keysym == Clutter.Tab) {
if (shift && this._currentWindow == 0)
this._select(this._previousApp());
else if (!shift && this._currentWindow == this._appIcons[this._currentApp].cachedWindows.length - 1)
this._select(this._nextApp());
else
this._select(this._currentApp, shift ? this._previousWindow() : this._nextWindow());
} else if (keysym == Clutter.Left || keysym == Clutter.a)
this._select(this._currentApp, this._previousWindow());
else if (keysym == Clutter.Right || keysym == Clutter.d)
this._select(this._currentApp, this._nextWindow());
else if (keysym == Clutter.Up || keysym == Clutter.w)
this._select(this._currentApp, null, true);
} else {
if (keysym == Clutter.Tab)
this._select(shift ? this._previousApp() : this._nextApp());
else if (keysym == Clutter.Left || keysym == Clutter.a)
this._select(this._previousApp());
else if (keysym == Clutter.Right || keysym == Clutter.d)
this._select(this._nextApp());
else if (keysym == Clutter.Down || keysym == Clutter.s)
this._select(this._currentApp, this._currentWindow);
}
return true;
},
@ -196,88 +180,48 @@ AltTabPopup.prototype = {
return true;
},
_onScroll : function(actor, event) {
let direction = event.get_scroll_direction();
if (direction == Clutter.ScrollDirection.UP) {
if (this._thumbnailsFocused) {
if (this._currentWindow == 0)
this._select(this._previousApp());
else
this._select(this._currentApp, this._previousWindow());
} else {
let nwindows = this._appIcons[this._currentApp].cachedWindows.length;
if (nwindows > 1)
this._select(this._currentApp, nwindows - 1);
else
this._select(this._previousApp());
}
} else if (direction == Clutter.ScrollDirection.DOWN) {
if (this._thumbnailsFocused) {
if (this._currentWindow == this._appIcons[this._currentApp].cachedWindows.length - 1)
this._select(this._nextApp());
else
this._select(this._currentApp, this._nextWindow());
} else {
let nwindows = this._appIcons[this._currentApp].cachedWindows.length;
if (nwindows > 1)
this._select(this._currentApp, 0);
else
this._select(this._nextApp());
_appClicked : function(icon) {
Main.activateWindow(icon.windows[0]);
this.destroy();
},
_windowClicked : function(icon, window) {
if (window)
Main.activateWindow(window);
this.destroy();
},
_windowHovered : function(icon, window) {
if (window)
this._highlightWindow(window);
},
_mouseMoved : function(actor, event) {
if (++this._mouseMovement < POPUP_POINTER_SELECTION_THRESHOLD)
return;
this.actor.disconnect(this._motionEventId);
this._mouseActive = true;
actor = event.get_source();
while (actor) {
if (actor._delegate instanceof AppIcon.AppIcon) {
this._iconEntered(actor, event);
return;
}
actor = actor.get_parent();
}
},
_clickedOutside : function(actor, event) {
this.destroy();
},
_appActivated : function(appSwitcher, n) {
// If the user clicks on the selected app, activate the
// selected window; otherwise (eg, they click on an app while
// !mouseActive) activate the first window of the clicked-on
// app.
let window = (n == this._currentApp) ? this._currentWindow : 0;
Main.activateWindow(this._appIcons[n].cachedWindows[window]);
this.destroy();
},
_appEntered : function(appSwitcher, n) {
if (!this._mouseActive)
return;
this._select(n);
},
_windowActivated : function(thumbnailList, n) {
Main.activateWindow(this._appIcons[this._currentApp].cachedWindows[n]);
this.destroy();
},
_windowEntered : function(thumbnailList, n) {
if (!this._mouseActive)
return;
this._select(this._currentApp, n);
},
_disableHover : function() {
this._mouseActive = false;
if (this._motionTimeoutId != 0)
Mainloop.source_remove(this._motionTimeoutId);
this._motionTimeoutId = Mainloop.timeout_add(DISABLE_HOVER_TIMEOUT, Lang.bind(this, this._mouseTimedOut));
},
_mouseTimedOut : function() {
this._motionTimeoutId = 0;
this._mouseActive = true;
_iconEntered : function(actor, event) {
let index = this._icons.indexOf(actor._delegate);
if (this._mouseActive)
this._updateSelection(index - this._selected);
},
_finish : function() {
let app = this._appIcons[this._currentApp];
let window = app.cachedWindows[this._currentWindow];
Main.activateWindow(window);
if (this._highlightedWindow)
Main.activateWindow(this._highlightedWindow);
this.destroy();
},
@ -289,486 +233,55 @@ AltTabPopup.prototype = {
if (this._haveModal)
Main.popModal(this.actor);
if (this._lightbox)
this._lightbox.destroy();
if (this._keyPressEventId)
global.stage.disconnect(this._keyPressEventId);
if (this._keyReleaseEventId)
global.stage.disconnect(this._keyReleaseEventId);
if (this._motionTimeoutId != 0)
Mainloop.source_remove(this._motionTimeoutId);
if (this._thumbnailTimeoutId != 0)
Mainloop.source_remove(this._thumbnailTimeoutId);
},
/**
* _select:
* @app: index of the app to select
* @window: (optional) index of which of @app's windows to select
* @forceAppFocus: optional flag, see below
*
* Selects the indicated @app, and optional @window, and sets
* this._thumbnailsFocused appropriately to indicate whether the
* arrow keys should act on the app list or the thumbnail list.
*
* If @app is specified and @window is unspecified or %null, then
* the app is highlighted (ie, given a light background), and the
* current thumbnail list, if any, is destroyed. If @app has
* multiple windows, and @forceAppFocus is not %true, then a
* timeout is started to open a thumbnail list.
*
* If @app and @window are specified (and @forceAppFocus is not),
* then @app will be outlined, a thumbnail list will be created
* and focused (if it hasn't been already), and the @window'th
* window in it will be highlighted.
*
* If @app and @window are specified and @forceAppFocus is %true,
* then @app will be highlighted, and @window outlined, and the
* app list will have the keyboard focus.
*/
_select : function(app, window, forceAppFocus) {
if (app != this._currentApp || window == null) {
if (this._thumbnails)
this._destroyThumbnails();
}
_updateSelection : function(delta) {
this._icons[this._selected].setHighlight(false);
if (delta != 0 && this._selectedMenu)
this._selectedMenu.popdown();
if (this._thumbnailTimeoutId != 0) {
Mainloop.source_remove(this._thumbnailTimeoutId);
this._thumbnailTimeoutId = 0;
}
this._selected = (this._selected + this._icons.length + delta) % this._icons.length;
this._icons[this._selected].setHighlight(true);
this._thumbnailsFocused = (window != null) && !forceAppFocus;
this._currentApp = app;
this._currentWindow = window ? window : 0;
this._appSwitcher.highlight(app, this._thumbnailsFocused);
if (window != null) {
if (!this._thumbnails)
this._createThumbnails();
this._currentWindow = window;
this._thumbnails.highlight(window, forceAppFocus);
} else if (this._appIcons[this._currentApp].cachedWindows.length > 1 &&
!forceAppFocus) {
this._thumbnailTimeoutId = Mainloop.timeout_add (
THUMBNAIL_POPUP_TIME,
Lang.bind(this, function () {
this._select(this._currentApp, 0, true);
return false;
}));
}
this._highlightWindow(this._currentWindows[this._selected]);
},
_destroyThumbnails : function() {
Tweener.addTween(this._thumbnails.actor,
{ opacity: 0,
time: THUMBNAIL_FADE_TIME,
transition: "easeOutQuad",
onComplete: function() { this.destroy(); }
});
this._thumbnails = null;
_menuPoppedUp : function(icon, menu) {
this._selectedMenu = menu;
},
_createThumbnails : function() {
this._thumbnails = new ThumbnailList (this._appIcons[this._currentApp].cachedWindows);
this._thumbnails.connect('item-activated', Lang.bind(this, this._windowActivated));
this._thumbnails.connect('item-entered', Lang.bind(this, this._windowEntered));
this.actor.add_actor(this._thumbnails.actor);
let thumbnailCenter;
if (this._thumbnails.actor.width < this._appSwitcher.actor.width) {
// Center the thumbnails under the corresponding AppIcon.
// If this is being called when the switcher is first
// being brought up, then nothing will have been assigned
// an allocation yet, and the get_transformed_position()
// call will return 0,0.
// (http://bugzilla.openedhand.com/show_bug.cgi?id=1115).
// Calling clutter_actor_get_allocation_box() would force
// it to properly allocate itself, but we can't call that
// because it has an out-caller-allocates arg. So we use
// clutter_stage_get_actor_at_pos(), which will force a
// reallocation as a side effect.
global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, 0, 0);
let icon = this._appIcons[this._currentApp].actor;
let [stageX, stageY] = icon.get_transformed_position();
thumbnailCenter = stageX + icon.width / 2;
} else {
// Center the thumbnails on the monitor
let primary = global.get_primary_monitor();
thumbnailCenter = primary.x + primary.width / 2;
}
this._thumbnails.actor.x = Math.floor(thumbnailCenter - this._thumbnails.actor.width / 2);
this._thumbnails.actor.y = this._appSwitcher.actor.y + this._appSwitcher.actor.height + POPUP_LIST_SPACING;
this._thumbnails.actor.opacity = 0;
Tweener.addTween(this._thumbnails.actor,
{ opacity: 255,
time: THUMBNAIL_FADE_TIME,
transition: "easeOutQuad"
});
}
};
function SwitcherList(squareItems) {
this._init(squareItems);
}
SwitcherList.prototype = {
_init : function(squareItems) {
this.actor = new St.Bin({ style_class: 'switcher-list' });
// Here we use a GenericContainer so that we can force all the
// children except the separator to have the same width.
this._list = new Shell.GenericContainer();
this._list.spacing = POPUP_LIST_SPACING;
this._list.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this._list.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this._list.connect('allocate', Lang.bind(this, this._allocate));
this.actor.add_actor(this._list);
this._items = [];
this._highlighted = -1;
this._separator = null;
this._squareItems = squareItems;
_menuPoppedDown : function(icon, menu) {
this._selectedMenu = null;
},
addItem : function(item) {
let bbox = new St.Clickable({ style_class: 'item-box',
reactive: true });
_updateWindowSelection : function(delta) {
let icon = this._icons[this._selected];
bbox.set_child(item);
this._list.add_actor(bbox);
if (!this._selectedMenu)
icon.popupMenu();
if (!this._selectedMenu)
return;
let n = this._items.length;
bbox.connect('clicked', Lang.bind(this, function () {
this._itemActivated(n);
}));
bbox.connect('enter-event', Lang.bind(this, function () {
this._itemEntered(n);
}));
this._items.push(bbox);
},
addSeparator: function () {
let box = new St.Bin({ style_class: 'separator' })
this._separator = box;
this._list.add_actor(box);
},
highlight: function(index, justOutline) {
if (this._highlighted != -1)
this._items[this._highlighted].style_class = 'item-box';
this._highlighted = index;
if (this._highlighted != -1) {
if (justOutline)
this._items[this._highlighted].style_class = 'outlined-item-box';
else
this._items[this._highlighted].style_class = 'selected-item-box';
}
},
_itemActivated: function(n) {
this.emit('item-activated', n);
},
_itemEntered: function(n) {
this.emit('item-entered', n);
},
_maxChildWidth: function (forHeight) {
let maxChildMin = 0;
let maxChildNat = 0;
for (let i = 0; i < this._items.length; i++) {
let [childMin, childNat] = this._items[i].get_preferred_width(forHeight);
maxChildMin = Math.max(childMin, maxChildMin);
maxChildNat = Math.max(childNat, maxChildNat);
if (this._squareItems) {
let [childMin, childNat] = this._items[i].get_preferred_height(-1);
maxChildMin = Math.max(childMin, maxChildMin);
maxChildNat = Math.max(childNat, maxChildNat);
let next = 0;
for (let i = 0; i < icon.windows.length; i++) {
if (icon.windows[i] == this._highlightedWindow) {
next = (i + icon.windows.length + delta) % icon.windows.length;
break;
}
}
return [maxChildMin, maxChildNat];
this._selectedMenu.selectWindow(icon.windows[next]);
},
_getPreferredWidth: function (actor, forHeight, alloc) {
let [maxChildMin, maxChildNat] = this._maxChildWidth(forHeight);
let separatorWidth = 0;
if (this._separator) {
let [sepMin, sepNat] = this._separator.get_preferred_width(forHeight);
separatorWidth = sepNat + this._list.spacing;
}
let totalSpacing = this._list.spacing * (this._items.length - 1);
alloc.min_size = this._items.length * maxChildMin + separatorWidth + totalSpacing;
alloc.nat_size = this._items.length * maxChildNat + separatorWidth + totalSpacing;
},
_getPreferredHeight: function (actor, forWidth, alloc) {
let maxChildMin = 0;
let maxChildNat = 0;
for (let i = 0; i < this._items.length; i++) {
let [childMin, childNat] = this._items[i].get_preferred_height(-1);
maxChildMin = Math.max(childMin, maxChildMin);
maxChildNat = Math.max(childNat, maxChildNat);
}
if (this._squareItems) {
let [childMin, childNat] = this._maxChildWidth(-1);
maxChildMin = Math.max(childMin, maxChildMin);
maxChildNat = Math.max(childNat, maxChildNat);
}
alloc.min_size = maxChildMin;
alloc.nat_size = maxChildNat;
},
_allocate: function (actor, box, flags) {
let childHeight = box.y2 - box.y1;
let [maxChildMin, maxChildNat] = this._maxChildWidth(childHeight);
let totalSpacing = this._list.spacing * (this._items.length - 1);
let separatorWidth = 0;
if (this._separator) {
let [sepMin, sepNat] = this._separator.get_preferred_width(childHeight);
separatorWidth = sepNat;
totalSpacing += this._list.spacing;
}
let childWidth = Math.floor(Math.max(0, box.x2 - box.x1 - totalSpacing - separatorWidth) / this._items.length);
let x = 0;
let children = this._list.get_children();
let childBox = new Clutter.ActorBox();
for (let i = 0; i < children.length; i++) {
if (this._items.indexOf(children[i]) != -1) {
let [childMin, childNat] = children[i].get_preferred_height(childWidth);
let vSpacing = (childHeight - childNat) / 2;
childBox.x1 = x;
childBox.y1 = vSpacing;
childBox.x2 = x + childWidth;
childBox.y2 = childBox.y1 + childNat;
children[i].allocate(childBox, flags);
x += this._list.spacing + childWidth;
} else if (children[i] == this._separator) {
// We want the separator to be more compact than the rest.
childBox.x1 = x;
childBox.y1 = 0;
childBox.x2 = x + separatorWidth;
childBox.y2 = childHeight;
children[i].allocate(childBox, flags);
x += this._list.spacing + separatorWidth;
} else {
// Something else, eg, AppSwitcher's arrows;
// we don't allocate it.
}
}
}
};
Signals.addSignalMethods(SwitcherList.prototype);
function AppIcon(app) {
this._init(app);
}
AppIcon.prototype = {
_init: function(app) {
this.app = app;
this.actor = new St.BoxLayout({ style_class: "alt-tab-app",
vertical: true });
this._icon = this.app.create_icon_texture(POPUP_APPICON_SIZE);
this.actor.add(this._icon, { x_fill: false, y_fill: false } );
this._label = new St.Label({ text: this.app.get_name() });
this.actor.add(this._label, { x_fill: false });
}
}
function AppSwitcher(apps) {
this._init(apps);
}
AppSwitcher.prototype = {
__proto__ : SwitcherList.prototype,
_init : function(apps) {
SwitcherList.prototype._init.call(this, true);
// Construct the AppIcons, sort by time, add to the popup
let activeWorkspace = global.screen.get_active_workspace();
let workspaceIcons = [];
let otherIcons = [];
for (let i = 0; i < apps.length; i++) {
let appIcon = new AppIcon(apps[i]);
// Cache the window list now; we don't handle dynamic changes here,
// and we don't want to be continually retrieving it
appIcon.cachedWindows = appIcon.app.get_windows();
if (this._hasWindowsOnWorkspace(appIcon, activeWorkspace))
workspaceIcons.push(appIcon);
else
otherIcons.push(appIcon);
}
workspaceIcons.sort(Lang.bind(this, this._sortAppIcon));
otherIcons.sort(Lang.bind(this, this._sortAppIcon));
this.icons = [];
this._arrows = [];
for (let i = 0; i < workspaceIcons.length; i++)
this._addIcon(workspaceIcons[i]);
if (workspaceIcons.length > 0 && otherIcons.length > 0)
this.addSeparator();
for (let i = 0; i < otherIcons.length; i++)
this._addIcon(otherIcons[i]);
this._curApp = -1;
},
_allocate: function (actor, box, flags) {
// Allocate the main list items
SwitcherList.prototype._allocate.call(this, actor, box, flags);
let arrowHeight = Math.floor(this.actor.get_theme_node().get_padding(St.Side.BOTTOM) / 3);
let arrowWidth = arrowHeight * 2;
// Now allocate each arrow underneath its item
let childBox = new Clutter.ActorBox();
for (let i = 0; i < this._items.length; i++) {
let itemBox = this._items[i].allocation;
childBox.x1 = Math.floor(itemBox.x1 + (itemBox.x2 - itemBox.x1 - arrowWidth) / 2);
childBox.x2 = childBox.x1 + arrowWidth;
childBox.y1 = itemBox.y2 + arrowHeight;
childBox.y2 = childBox.y1 + arrowHeight;
this._arrows[i].allocate(childBox, flags);
}
},
// We override SwitcherList's highlight() method to also deal with
// the AppSwitcher->ThumbnailList arrows. Apps with only 1 window
// will hide their arrows by default, but show them when their
// thumbnails are visible (ie, when the app icon is supposed to be
// in justOutline mode). Apps with multiple windows will normally
// show a dim arrow, but show a bright arrow when they are
// highlighted; their redraw handler will use the right color
// based on this._curApp; we just need to do a queue_relayout() to
// force it to redraw. (queue_redraw() doesn't work because
// ShellDrawingArea only redraws on allocate.)
highlight : function(n, justOutline) {
if (this._curApp != -1) {
if (this.icons[this._curApp].cachedWindows.length == 1)
this._arrows[this._curApp].hide();
else
this._arrows[this._curApp].queue_relayout();
}
SwitcherList.prototype.highlight.call(this, n, justOutline);
this._curApp = n;
if (this._curApp != -1) {
if (justOutline && this.icons[this._curApp].cachedWindows.length == 1)
this._arrows[this._curApp].show();
else
this._arrows[this._curApp].queue_relayout();
}
},
_addIcon : function(appIcon) {
this.icons.push(appIcon);
this.addItem(appIcon.actor);
let n = this._arrows.length;
let arrow = new St.DrawingArea();
arrow.connect('redraw', Lang.bind(this,
function (area, texture) {
Shell.draw_box_pointer(texture, Shell.PointerDirection.DOWN,
TRANSPARENT_COLOR,
this._curApp == n ? POPUP_ARROW_COLOR : POPUP_UNFOCUSED_ARROW_COLOR);
}));
this._list.add_actor(arrow);
this._arrows.push(arrow);
if (appIcon.cachedWindows.length == 1)
arrow.hide();
},
_hasWindowsOnWorkspace: function(appIcon, workspace) {
let windows = appIcon.cachedWindows;
for (let i = 0; i < windows.length; i++) {
if (windows[i].get_workspace() == workspace)
return true;
}
return false;
},
_sortAppIcon : function(appIcon1, appIcon2) {
return appIcon1.app.compare(appIcon2.app);
}
};
function ThumbnailList(windows) {
this._init(windows);
}
ThumbnailList.prototype = {
__proto__ : SwitcherList.prototype,
_init : function(windows) {
SwitcherList.prototype._init.call(this);
let activeWorkspace = global.screen.get_active_workspace();
// We fake the value of "separatorAdded" when the app has no window
// on the current workspace, to avoid displaying a useless separator in
// that case.
let separatorAdded = windows.length == 0 || windows[0].get_workspace() != activeWorkspace;
for (let i = 0; i < windows.length; i++) {
if (!separatorAdded && windows[i].get_workspace() != activeWorkspace) {
this.addSeparator();
separatorAdded = true;
}
let mutterWindow = windows[i].get_compositor_private();
let windowTexture = mutterWindow.get_texture ();
let [width, height] = windowTexture.get_size();
let scale = Math.min(1.0, THUMBNAIL_SIZE / width, THUMBNAIL_SIZE / height);
let box = new St.BoxLayout({ style_class: "thumbnail-box",
vertical: true });
let bin = new St.Bin({ style_class: "thumbnail" });
let clone = new Clutter.Clone ({ source: windowTexture,
reactive: true,
width: width * scale,
height: height * scale });
bin.add_actor(clone);
box.add_actor(bin);
let title = windows[i].get_title();
if (title) {
let name = new St.Label({ text: title });
// St.Label doesn't support text-align so use a Bin
let bin = new St.Bin({ x_align: St.Align.MIDDLE });
bin.add_actor(name);
box.add_actor(bin);
}
this.addItem(box);
}
_highlightWindow : function(metaWin) {
this._highlightedWindow = metaWin;
this._currentWindows[this._selected] = metaWin;
this._lightbox.highlight(this._highlightedWindow.get_compositor_private());
}
};

File diff suppressed because it is too large Load Diff

View File

@ -1,90 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Shell = imports.gi.Shell;
const Lang = imports.lang;
const Signals = imports.signals;
function AppFavorites() {
this._init();
}
AppFavorites.prototype = {
FAVORITE_APPS_KEY: 'favorite_apps',
_init: function() {
this._favorites = {};
this._gconf = Shell.GConf.get_default();
this._gconf.connect('changed::' + this.FAVORITE_APPS_KEY, Lang.bind(this, this._onFavsChanged));
this._reload();
},
_onFavsChanged: function() {
this._reload();
this.emit('changed');
},
_reload: function() {
let ids = Shell.GConf.get_default().get_string_list('favorite_apps');
let appSys = Shell.AppSystem.get_default();
let apps = ids.map(function (id) {
return appSys.get_app(id);
}).filter(function (app) {
return app != null;
});
this._favorites = {};
for (let i = 0; i < apps.length; i++) {
let app = apps[i];
this._favorites[app.get_id()] = app;
}
},
_getIds: function() {
let ret = [];
for (let id in this._favorites)
ret.push(id);
return ret;
},
getFavoriteMap: function() {
return this._favorites;
},
getFavorites: function() {
let ret = [];
for (let id in this._favorites)
ret.push(this._favorites[id]);
return ret;
},
isFavorite: function(appId) {
return appId in this._favorites;
},
addFavorite: function(appId) {
if (appId in this._favorites)
return;
let app = Shell.AppSystem.get_default().get_app(appId);
if (!app)
return;
let ids = this._getIds();
ids.push(appId);
this._gconf.set_string_list(this.FAVORITE_APPS_KEY, ids);
this._favorites[appId] = app;
},
removeFavorite: function(appId) {
if (!appId in this._favorites)
return;
let ids = this._getIds().filter(function (id) { return id != appId; });
this._gconf.set_string_list(this.FAVORITE_APPS_KEY, ids);
}
};
Signals.addSignalMethods(AppFavorites.prototype);
var appFavoritesInstance = null;
function getAppFavorites() {
if (appFavoritesInstance == null)
appFavoritesInstance = new AppFavorites();
return appFavoritesInstance;
}

View File

@ -8,12 +8,10 @@ const Mainloop = imports.mainloop;
const Pango = imports.gi.Pango;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const GenericDisplay = imports.ui.genericDisplay;
const AppFavorites = imports.ui.appFavorites;
const Main = imports.ui.main;
const Workspaces = imports.ui.workspaces;
@ -22,7 +20,7 @@ GLOW_COLOR.from_pixel(0x4f6ba4ff);
const GLOW_PADDING_HORIZONTAL = 3;
const GLOW_PADDING_VERTICAL = 3;
const APPICON_DEFAULT_ICON_SIZE = 48;
const APPICON_ICON_SIZE = 48;
const APPICON_PADDING = 1;
const APPICON_BORDER_WIDTH = 1;
@ -51,19 +49,14 @@ TRANSPARENT_COLOR.from_pixel(0x00000000);
const MenuType = { NONE: 0, ON_RIGHT: 1, BELOW: 2 };
function AppIcon(params) {
this._init(params);
function AppIcon(appInfo, menuType) {
this._init(appInfo, menuType || MenuType.NONE);
}
AppIcon.prototype = {
_init : function(params) {
this.app = params.app;
if (!this.app)
throw new Error('AppIcon constructor requires "app" param');
this._menuType = ('menuType' in params) ? params.menuType : MenuType.NONE;
this._iconSize = ('size' in params) ? params.size : APPICON_DEFAULT_ICON_SIZE;
this._showGlow = ('glow' in params) ? params.glow : false;
_init : function(appInfo, menuType) {
this.appInfo = appInfo;
this._menuType = menuType;
this.actor = new Shell.ButtonBox({ orientation: Big.BoxOrientation.VERTICAL,
border: APPICON_BORDER_WIDTH,
@ -71,25 +64,30 @@ AppIcon.prototype = {
padding: APPICON_PADDING,
reactive: true });
this.actor._delegate = this;
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this.highlight_border_color = APPICON_DEFAULT_BORDER_COLOR;
if (this._menuType != MenuType.NONE) {
if (menuType != MenuType.NONE) {
this.windows = Shell.AppMonitor.get_default().get_windows_for_app(appInfo.get_id());
for (let i = 0; i < this.windows.length; i++) {
this.windows[i].connect('notify::user-time', Lang.bind(this, this._resortWindows));
}
this._resortWindows();
this.actor.connect('button-press-event', Lang.bind(this, this._updateMenuOnButtonPress));
this.actor.connect('notify::hover', Lang.bind(this, this._updateMenuOnHoverChanged));
this.actor.connect('activate', Lang.bind(this, this._updateMenuOnActivate));
this._menuTimeoutId = 0;
this._menu = null;
}
} else
this.windows = [];
let iconBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
x_align: Big.BoxAlignment.CENTER,
y_align: Big.BoxAlignment.CENTER,
width: this._iconSize,
height: this._iconSize });
this.icon = this.app.create_icon_texture(this._iconSize);
width: APPICON_ICON_SIZE,
height: APPICON_ICON_SIZE });
this.icon = appInfo.create_icon_texture(APPICON_ICON_SIZE);
iconBox.append(this.icon, Big.BoxPackFlags.NONE);
this.actor.append(iconBox, Big.BoxPackFlags.EXPAND);
@ -100,21 +98,22 @@ AppIcon.prototype = {
nameBox.connect('allocate', Lang.bind(this, this._nameBoxAllocate));
this._nameBox = nameBox;
this._name = new St.Label({ style_class: "app-icon-label",
text: this.app.get_name() });
this._name.clutter_text.line_alignment = Pango.Alignment.CENTER;
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);
if (this._showGlow) {
this._glowBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL });
this._nameBox.add_actor(this._glowBox);
this._glowBox.lower(this._name);
this._appWindowChangedId = this.app.connect('windows-changed', Lang.bind(this, this._rerenderGlow));
this._rerenderGlow();
} else {
this._glowBox = null;
this._appWindowChangedId = 0;
this._glowBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL });
let glowPath = GLib.filename_to_uri(global.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);
},
@ -159,30 +158,25 @@ AppIcon.prototype = {
}
},
_onDestroy: function() {
if (this._appWindowChangedId > 0)
this.app.disconnect(this._appWindowChangedId);
},
_resortWindows: function() {
this.windows.sort(function (a, b) {
let visA = a.showing_on_its_workspace();
let visB = b.showing_on_its_workspace();
_rerenderGlow: function() {
if (!this._showGlow)
return;
this._glowBox.get_children().forEach(function (a) { a.destroy(); });
let glowPath = GLib.filename_to_uri(global.imagedir + 'app-well-glow.png', '');
let windows = this.app.get_windows();
for (let i = 0; i < 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);
}
if (visA && !visB)
return -1;
else if (visB && !visA)
return 1;
else
return b.get_user_time() - a.get_user_time();
});
},
// AppIcon itself is not a draggable, but if you want to make
// a subclass of it draggable, you can use this method to create
// a drag actor
createDragActor: function() {
return this.app.create_icon_texture(this._iconSize);
return this.appInfo.create_icon_texture(APPICON_ICON_SIZE);
},
setHighlight: function(highlight) {
@ -211,19 +205,14 @@ AppIcon.prototype = {
},
_updateMenuOnButtonPress: function(actor, event) {
let button = event.get_button();
if (button == 1) {
if (this._menuTimeoutId != 0)
Mainloop.source_remove(this._menuTimeoutId);
this._menuTimeoutId = Mainloop.timeout_add(APPICON_MENU_POPUP_TIMEOUT_MS,
Lang.bind(this, function () { this.popupMenu(button); }));
} else if (button == 3) {
this.popupMenu(button);
}
if (this._menuTimeoutId != 0)
Mainloop.source_remove(this._menuTimeoutId);
this._menuTimeoutId = Mainloop.timeout_add(APPICON_MENU_POPUP_TIMEOUT_MS,
Lang.bind(this, this.popupMenu));
return false;
},
popupMenu: function(activatingButton) {
popupMenu: function() {
if (this._menuTimeoutId != 0) {
Mainloop.source_remove(this._menuTimeoutId);
this._menuTimeoutId = 0;
@ -247,7 +236,7 @@ AppIcon.prototype = {
}));
}
this._menu.popup(activatingButton);
this._menu.popup();
return false;
},
@ -307,10 +296,10 @@ AppIconMenu.prototype = {
this._windowContainer.connect('leave-event', Lang.bind(this, this._onMenuLeave));
this._windowContainer.connect('button-release-event', Lang.bind(this, this._onMenuButtonRelease));
this._arrow = new St.DrawingArea();
this._arrow = new Shell.DrawingArea();
this._arrow.connect('redraw', Lang.bind(this, function (area, texture) {
Shell.draw_box_pointer(texture,
this._type == MenuType.ON_RIGHT ? Shell.PointerDirection.LEFT : Shell.PointerDirection.UP,
this._type == MenuType.ON_RIGHT ? Clutter.Gravity.WEST : Clutter.Gravity.NORTH,
source.highlight_border_color,
APPICON_MENU_BACKGROUND_COLOR);
}));
@ -382,52 +371,38 @@ AppIconMenu.prototype = {
_redisplay: function() {
this._windowContainer.remove_all();
let windows = this._source.app.get_windows();
let windows = this._source.windows;
this._windowContainer.show();
let iconsDiffer = false;
let texCache = Shell.TextureCache.get_default();
if (windows.length > 0) {
let firstIcon = windows[0].mini_icon;
for (let i = 1; i < windows.length; i++) {
if (!texCache.pixbuf_equal(windows[i].mini_icon, firstIcon)) {
iconsDiffer = true;
break;
}
let firstIcon = windows[0].mini_icon;
for (let i = 1; i < windows.length; i++) {
if (!texCache.pixbuf_equal(windows[i].mini_icon, firstIcon)) {
iconsDiffer = true;
break;
}
}
// Display the app windows menu items and the separator between windows
// of the current desktop and other windows.
let activeWorkspace = global.screen.get_active_workspace();
let separatorShown = windows.length > 0 && windows[0].get_workspace() != activeWorkspace;
for (let i = 0; i < windows.length; i++) {
if (!separatorShown && windows[i].get_workspace() != activeWorkspace) {
this._appendSeparator();
separatorShown = true;
}
let currentWorkspaceWindows = windows.filter(function (w) {
return w.get_workspace() == activeWorkspace;
});
let otherWorkspaceWindows = windows.filter(function (w) {
return w.get_workspace() != activeWorkspace;
});
let icon = null;
if (iconsDiffer)
icon = Shell.TextureCache.get_default().bind_pixbuf_property(windows[i], "mini-icon");
let box = this._appendMenuItem(icon, windows[i].title);
box._window = windows[i];
this._appendWindows(currentWorkspaceWindows, iconsDiffer);
if (currentWorkspaceWindows.length > 0 && otherWorkspaceWindows.length > 0) {
this._appendSeparator();
}
this._appendWindows(otherWorkspaceWindows, iconsDiffer);
if (windows.length > 0)
this._appendSeparator();
this._appendSeparator();
let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
this._newWindowMenuItem = windows.length > 0 ? this._appendMenuItem(null, _("New Window")) : null;
if (windows.length > 0)
this._appendSeparator();
this._toggleFavoriteMenuItem = this._appendMenuItem(null, isFavorite ? _("Remove from Favorites")
: _("Add to Favorites"));
this._newWindowMenuItem = this._appendMenuItem(null, _("New Window"));
this._highlightedItem = null;
},
@ -466,13 +441,26 @@ AppIconMenu.prototype = {
return box;
},
popup: function(activatingButton) {
_appendWindows: function (windows, iconsDiffer) {
for (let i = 0; i < windows.length; i++) {
let metaWindow = windows[i];
let icon = null;
if (iconsDiffer) {
icon = Shell.TextureCache.get_default().bind_pixbuf_property(metaWindow, "mini-icon");
}
let box = this._appendMenuItem(icon, metaWindow.title);
box._window = metaWindow;
}
},
popup: function() {
let [stageX, stageY] = this._source.actor.get_transformed_position();
let [stageWidth, stageHeight] = this._source.actor.get_transformed_size();
this._redisplay();
this._windowContainer.popup(activatingButton, global.get_current_time());
this._windowContainer.popup(0, Main.currentTime());
this.emit('popup', true);
@ -569,15 +557,8 @@ AppIconMenu.prototype = {
let metaWindow = child._window;
this.emit('activate-window', metaWindow);
} else if (child == this._newWindowMenuItem) {
this._source.app.launch();
this._source.appInfo.launch();
this.emit('activate-window', null);
} else if (child == this._toggleFavoriteMenuItem) {
let favs = AppFavorites.getAppFavorites();
let isFavorite = favs.isFavorite(this._source.app.get_id());
if (isFavorite)
favs.removeFavorite(this._source.app.get_id());
else
favs.addFavorite(this._source.app.get_id());
}
this.popdown();
},

172
js/ui/button.js Normal file
View File

@ -0,0 +1,172 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
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);
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);
}
Button.prototype = {
_init : function(widgetOrText, buttonColor, pressedButtonColor, textColor, font) {
this._buttonColor = buttonColor
if (buttonColor == null)
this._buttonColor = DEFAULT_BUTTON_COLOR;
this._pressedButtonColor = pressedButtonColor
if (pressedButtonColor == null)
this._pressedButtonColor = DEFAULT_PRESSED_BUTTON_COLOR;
this._textColor = textColor;
if (textColor == null)
this._textColor = DEFAULT_TEXT_COLOR;
this._font = font;
if (font == null)
this._font = DEFAULT_FONT;
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
});
if (typeof widgetOrText == 'string') {
this._widget = new Clutter.Text({ font_name: this._font,
color: this._textColor,
text: widgetOrText });
} else {
this._widget = widgetOrText;
}
this.actor.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));
},
_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(event.get_related()) != -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(event.get_related()) != -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();
}
},
/// 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" });
}
};

View File

@ -1,177 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const St = imports.gi.St;
const Gettext_gtk20 = imports.gettext.domain('gtk20');
const MSECS_IN_DAY = 24 * 60 * 60 * 1000;
function _sameDay(dateA, dateB) {
return (dateA.getDate() == dateB.getDate() &&
dateA.getMonth() == dateB.getMonth() &&
dateA.getYear() == dateB.getYear());
}
function Calendar() {
this._init();
};
Calendar.prototype = {
_init: function() {
// FIXME: This is actually the fallback method for GTK+ for the week start;
// GTK+ by preference uses nl_langinfo (NL_TIME_FIRST_WEEKDAY). We probably
// should add a C function so we can do the full handling.
this._weekStart = NaN;
let weekStartString = Gettext_gtk20.gettext("calendar:week_start:0");
if (weekStartString.indexOf("calendar:week_start:") == 0) {
this._weekStart = parseInt(weekStartString.substring(20));
}
if (isNaN(this._weekStart) || this._weekStart < 0 || this._weekStart > 6) {
log("Translation of 'calendar:week_start:0' in GTK+ is not correct");
this.weekStart = 0;
}
// Find the ordering for month/year in the calendar heading
switch (Gettext_gtk20.gettext("calendar:MY")) {
case "calendar:MY":
this._headerFormat = "%B %Y";
break;
case "calendar:YM":
this._headerFormat = "%Y %B";
break;
default:
log("Translation of 'calendar:MY' in GTK+ is not correct");
this._headerFormat = "%B %Y";
break;
}
// Start off with the current date
this.date = new Date();
this.actor = new St.Table({ homogeneous: false,
style_class: "calendar",
reactive: true });
this.actor.connect('scroll-event',
Lang.bind(this, this._onScroll));
// Top line of the calendar '<| September 2009 |>'
this._topBox = new St.BoxLayout();
this.actor.add(this._topBox,
{ row: 0, col: 0, col_span: 7 });
let back = new St.Button({ label: "&lt;", style_class: 'calendar-change-month' });
this._topBox.add(back);
back.connect("clicked", Lang.bind(this, this._prevMonth));
this._dateLabel = new St.Label();
this._topBox.add(this._dateLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
let forward = new St.Button({ label: "&gt;", style_class: 'calendar-change-month' });
this._topBox.add(forward);
forward.connect("clicked", Lang.bind(this, this._nextMonth));
// We need to figure out the abbreviated localized names for the days of the week;
// we do this by just getting the next 7 days starting from right now and then putting
// them in the right cell in the table. It doesn't matter if we add them in order
let iter = new Date(this.date);
iter.setSeconds(0); // Leap second protection. Hah!
iter.setHours(12);
for (let i = 0; i < 7; i++) {
this.actor.add(new St.Label({ text: iter.toLocaleFormat("%a") }),
{ row: 1,
col: (7 + iter.getDay() - this._weekStart) % 7,
x_fill: false, x_align: 1.0 });
iter.setTime(iter.getTime() + MSECS_IN_DAY);
}
// All the children after this are days, and get removed when we update the calendar
this._firstDayIndex = this.actor.get_children().length;
this._update();
},
// Sets the calendar to show a specific date
setDate: function(date) {
if (!_sameDay(date, this.date)) {
this.date = date;
this._update();
}
},
_onScroll : function(actor, event) {
switch (event.get_scroll_direction()) {
case Clutter.ScrollDirection.UP:
case Clutter.ScrollDirection.LEFT:
this._prevMonth();
break;
case Clutter.ScrollDirection.DOWN:
case Clutter.ScrollDirection.RIGHT:
this._nextMonth();
break;
}
},
_prevMonth: function() {
if (this.date.getMonth() == 0) {
this.date.setMonth(11);
this.date.setFullYear(this.date.getFullYear() - 1);
} else {
this.date.setMonth(this.date.getMonth() - 1);
}
this._update();
},
_nextMonth: function() {
if (this.date.getMonth() == 11) {
this.date.setMonth(0);
this.date.setFullYear(this.date.getFullYear() + 1);
} else {
this.date.setMonth(this.date.getMonth() + 1);
}
this._update();
},
_update: function() {
this._dateLabel.text = this.date.toLocaleFormat(this._headerFormat);
// Remove everything but the topBox and the weekday labels
let children = this.actor.get_children();
for (let i = this._firstDayIndex; i < children.length; i++)
children[i].destroy();
// Start at the beginning of the week before the start of the month
let iter = new Date(this.date);
iter.setDate(1);
iter.setSeconds(0);
iter.setHours(12);
iter.setTime(iter.getTime() - (iter.getDay() - this._weekStart) * MSECS_IN_DAY);
let now = new Date();
let row = 2;
while (true) {
let label = new St.Label({ text: iter.getDate().toString() });
if (_sameDay(now, iter))
label.style_class = "calendar-day calendar-today";
else if (iter.getMonth() != this.date.getMonth())
label.style_class = "calendar-day calendar-other-month-day";
else
label.style_class = "calendar-day";
this.actor.add(label,
{ row: row, col: (7 + iter.getDay() - this._weekStart) % 7,
x_fill: false, x_align: 1.0 });
iter.setTime(iter.getTime() + MSECS_IN_DAY);
if (iter.getDay() == this._weekStart) {
// We stop on the first "first day of the week" after the month we are displaying
if (iter.getMonth() > this.date.getMonth() || iter.getYear() > this.date.getYear())
break;
row++;
}
}
}
};

View File

@ -7,7 +7,6 @@ const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Main = imports.ui.main;
const Params = imports.misc.params;
// This manages the shell "chrome"; the UI that's visible in the
// normal mode (ie, outside the Overview), that surrounds the main
@ -55,7 +54,7 @@ Chrome.prototype = {
// addActor:
// @actor: an actor to add to the chrome layer
// @params: (optional) additional params
// @shapeActor: optional "shape actor".
//
// Adds @actor to the chrome layer and extends the input region
// and window manager struts to include it. (Window manager struts
@ -65,45 +64,59 @@ Chrome.prototype = {
// in its visibility will affect the input region, but NOT the
// struts.
//
// If %visibleInOverview is %true in @params, @actor will remain
// visible when the overview is brought up. Otherwise it will
// automatically be hidden. If %affectsStruts or %affectsInputRegion
// is %false, the actor will not have the indicated effect.
addActor: function(actor, params) {
params = Params.parse(params, { visibleInOverview: false,
affectsStruts: true,
affectsInputRegion: true });
// If @shapeActor is provided, it will be used instead of @actor
// for the input region/strut shape. (This lets you have things like
// drop shadows in @actor that don't affect the struts.) It must
// be a child of @actor. Alternatively, you can pass %null for
// @shapeActor to indicate that @actor should not affect the input
// region or struts at all.
addActor: function(actor, shapeActor) {
if (shapeActor === undefined)
shapeActor = actor;
else if (shapeActor && !this._verifyAncestry(shapeActor, actor))
throw new Error('shapeActor is not a descendent of actor');
if (params.visibleInOverview)
this.actor.add_actor(actor);
else
this.nonOverviewActor.add_actor(actor);
this.nonOverviewActor.add_actor(actor);
this._trackActor(actor, params.affectsInputRegion, params.affectsStruts);
if (shapeActor)
this._trackActor(shapeActor, true, true);
},
// trackActor:
// @actor: a descendant of the chrome to begin tracking
// @params: parameters describing how to track @actor
// setVisibleInOverview:
// @actor: an actor in the chrome layer
// @visible: Overview visibility
//
// Tells the chrome to track @actor, which must be a descendant
// of an actor added via addActor(). This can be used to extend the
// struts or input region to cover specific children.
trackActor: function(actor, params) {
// By default, actors in the chrome layer are automatically hidden
// when the Overview is shown. This can be used to override that
// behavior
setVisibleInOverview: function(actor, visible) {
if (!this._verifyAncestry(actor, this.actor))
throw new Error('actor is not a descendent of the chrome layer');
params = Params.parse(params, { affectsStruts: true,
affectsInputRegion: true });
this._trackActor(actor, params.affectsInputRegion, params.affectsStruts);
if (visible)
actor.reparent(this.actor);
else
actor.reparent(this.nonOverviewActor);
},
// untrackActor:
// @actor: an actor previously tracked via trackActor()
// addInputRegionActor:
// @actor: an actor to add to the stage input region
//
// Undoes the effect of trackActor()
untrackActor: function(actor) {
this._untrackActor(actor);
// Adds @actor to the stage input region, as with addActor(), but
// for actors that are already descendants of the chrome layer.
addInputRegionActor: function(actor) {
if (!this._verifyAncestry(actor, this.actor))
throw new Error('actor is not a descendent of the chrome layer');
this._trackActor(actor, true, false);
},
// removeInputRegionActor:
// @actor: an actor previously added to the stage input region
//
// Undoes the effect of addInputRegionActor()
removeInputRegionActor: function(actor) {
this._untrackActor(actor, true, false);
},
// removeActor:
@ -115,7 +128,7 @@ Chrome.prototype = {
this.nonOverviewActor.remove_actor(actor);
else
this.actor.remove_actor(actor);
this._untrackActor(actor);
this._untrackActor(actor, true, true);
},
_findActor: function(actor) {
@ -129,13 +142,23 @@ Chrome.prototype = {
_trackActor: function(actor, inputRegion, strut) {
let actorData;
let i = this._findActor(actor);
if (this._findActor(actor) != -1)
throw new Error('trying to re-track existing chrome actor');
if (i != -1) {
actorData = this._trackedActors[i];
if (inputRegion)
actorData.inputRegion++;
if (strut)
actorData.strut++;
if (!inputRegion && !strut)
actorData.children++;
return;
}
actorData = { actor: actor,
inputRegion: inputRegion,
strut: strut };
inputRegion: inputRegion ? 1 : 0,
strut: strut ? 1 : 0,
children: 0 };
actorData.visibleId = actor.connect('notify::visible',
Lang.bind(this, this._queueUpdateRegions));
@ -143,31 +166,54 @@ Chrome.prototype = {
Lang.bind(this, this._queueUpdateRegions));
actorData.parentSetId = actor.connect('parent-set',
Lang.bind(this, this._actorReparented));
// Note that destroying actor will unset its parent, so we don't
// need to connect to 'destroy' too.
this._trackedActors.push(actorData);
this._queueUpdateRegions();
actor = actor.get_parent();
if (actor != this.actor && actor != this.nonOverviewActor)
this._trackActor(actor, false, false);
if (inputRegion || strut)
this._queueUpdateRegions();
},
_untrackActor: function(actor) {
_untrackActor: function(actor, inputRegion, strut) {
let i = this._findActor(actor);
if (i == -1)
return;
let actorData = this._trackedActors[i];
this._trackedActors.splice(i, 1);
actor.disconnect(actorData.visibleId);
actor.disconnect(actorData.allocationId);
actor.disconnect(actorData.parentSetId);
if (inputRegion)
actorData.inputRegion--;
if (strut)
actorData.strut--;
if (!inputRegion && !strut)
actorData.children--;
this._queueUpdateRegions();
if (actorData.inputRegion <= 0 && actorData.strut <= 0 && actorData.children <= 0) {
this._trackedActors.splice(i, 1);
actor.disconnect(actorData.visibleId);
actor.disconnect(actorData.allocationId);
actor.disconnect(actorData.parentSetId);
actor = actor.get_parent();
if (actor && actor != this.actor && actor != this.nonOverviewActor)
this._untrackActor(actor, false, false);
}
if (inputRegion || strut)
this._queueUpdateRegions();
},
_actorReparented: function(actor, oldParent) {
if (!this._verifyAncestry(actor, this.actor))
this._untrackActor(actor);
if (this._verifyAncestry(actor, this.actor)) {
let newParent = actor.get_parent();
if (newParent != this.actor && newParent != this.nonOverviewActor)
this._trackActor(newParent, false, false);
}
if (oldParent != this.actor && oldParent != this.nonOverviewActor)
this._untrackActor(oldParent, false, false);
},
_overviewShowing: function() {
@ -191,7 +237,6 @@ Chrome.prototype = {
_windowsRestacked: function() {
let windows = global.get_windows();
let primary = global.get_primary_monitor();
// The chrome layer should be visible unless there is a window
// with layer FULLSCREEN, or a window with layer
@ -209,15 +254,17 @@ Chrome.prototype = {
for (let i = windows.length - 1; i > -1; i--) {
let layer = windows[i].get_meta_window().get_layer();
if (layer == Meta.StackLayer.OVERRIDE_REDIRECT ||
layer == Meta.StackLayer.FULLSCREEN) {
if (windows[i].x <= primary.x &&
windows[i].x + windows[i].width >= primary.x + primary.width &&
windows[i].y <= primary.y &&
windows[i].y + windows[i].height >= primary.y + primary.height) {
if (layer == Meta.StackLayer.OVERRIDE_REDIRECT) {
if (windows[i].x <= 0 &&
windows[i].x + windows[i].width >= global.screen_width &&
windows[i].y <= 0 &&
windows[i].y + windows[i].height >= global.screen_height) {
this._obscuredByFullscreen = true;
break;
}
} else if (layer == Meta.StackLayer.FULLSCREEN) {
this._obscuredByFullscreen = true;
break;
} else
break;
}

File diff suppressed because it is too large Load Diff

View File

@ -8,18 +8,13 @@ const Lang = imports.lang;
const Pango = imports.gi.Pango;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
const Mainloop = imports.mainloop;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const DocInfo = imports.misc.docInfo;
const DND = imports.ui.dnd;
const GenericDisplay = imports.ui.genericDisplay;
const Main = imports.ui.main;
const Search = imports.ui.search;
const MAX_DASH_DOCS = 50;
const DASH_DOCS_ICON_SIZE = 16;
const DEFAULT_SPACING = 4;
@ -116,19 +111,19 @@ DocDisplayItem.prototype = {
/* This class represents a display containing a collection of document items.
* The documents are sorted by how recently they were last visited.
*/
function DocDisplay(flags) {
this._init(flags);
function DocDisplay() {
this._init();
}
DocDisplay.prototype = {
__proto__: GenericDisplay.GenericDisplay.prototype,
_init : function(flags) {
GenericDisplay.GenericDisplay.prototype._init.call(this, flags);
_init : function() {
GenericDisplay.GenericDisplay.prototype._init.call(this);
// We keep a single timeout callback for updating last visited times
// for all the items in the display. This avoids creating individual
// callbacks for each item in the display. So proper time updates
// for individual items and item details depend on the item being
// for individual items and item details depend on the item being
// associated with one of the displays.
this._updateTimeoutTargetTime = -1;
this._updateTimeoutId = 0;
@ -141,7 +136,7 @@ DocDisplay.prototype = {
// but redisplaying right away is cool when we use Zephyr.
// Also, we might be displaying remote documents, like Google Docs, in the future
// which might be edited by someone else.
this._redisplay(GenericDisplay.RedisplayFlags.NONE);
this._redisplay(false);
}));
this.connect('destroy', Lang.bind(this, function (o) {
@ -156,8 +151,7 @@ DocDisplay.prototype = {
_refreshCache : function() {
if (!this._docsStale)
return true;
this._allItems = {};
Lang.copyProperties(this._docManager.getInfosByUri(), this._allItems);
this._allItems = this._docManager.getItems();
this._docsStale = false;
return false;
},
@ -182,8 +176,13 @@ DocDisplay.prototype = {
this._matchedItemKeys = [];
let docIdsToRemove = [];
for (docId in this._allItems) {
this._matchedItems[docId] = 1;
this._matchedItemKeys.push(docId);
// this._allItems[docId].exists() checks if the resource still exists
if (this._allItems[docId].exists()) {
this._matchedItems[docId] = 1;
this._matchedItemKeys.push(docId);
} else {
docIdsToRemove.push(docId);
}
}
for (docId in docIdsToRemove) {
@ -275,21 +274,18 @@ DashDocDisplayItem.prototype = {
Main.overview.hide();
}));
this.actor._delegate = this;
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 St.Label({ style_class: 'dash-recent-docs-item',
text: docInfo.name });
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);
},
getUri: function() {
return this._info.uri;
this.actor._delegate = this;
},
getDragActorSource: function() {
@ -321,14 +317,12 @@ DashDocDisplay.prototype = {
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this.actor.connect('allocate', Lang.bind(this, this._allocate));
this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));
this._actorsByUri = {};
this._docManager = DocInfo.getDocManager();
this._docManager.connect('changed', Lang.bind(this, this._onDocsChanged));
this._pendingDocsChange = true;
this._checkDocExistence = false;
this._docManager.connect('changed', Lang.bind(this, function(mgr) {
this._redisplay();
}));
this._redisplay();
},
_getPreferredWidth: function(actor, forHeight, alloc) {
@ -360,19 +354,19 @@ DashDocDisplay.prototype = {
// Two columns, where we go vertically down first. So just take
// the height of half of the children as our preferred height.
let firstColumnChildren = Math.ceil(children.length / 2);
let firstColumnChildren = children.length / 2;
let natural = 0;
alloc.min_size = 0;
for (let i = 0; i < firstColumnChildren; i++) {
let child = children[i];
let [minSize, naturalSize] = child.get_preferred_height(-1);
natural += naturalSize;
let [minSize, naturalSize] = child.get_preferred_height(forWidth);
alloc.natural_size += naturalSize;
if (i > 0 && i < children.length - 1) {
natural += DEFAULT_SPACING;
alloc.min_size += DEFAULT_SPACING;
alloc.natural_size += DEFAULT_SPACING;
}
}
alloc.natural_size = natural;
},
_allocate: function(actor, box, flags) {
@ -390,16 +384,14 @@ DashDocDisplay.prototype = {
// Loop over the children, going vertically down first. When we run
// out of vertical space (our y variable is bigger than box.y2), switch
// to the second column.
while (i < children.length) {
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, or we're in
// the first column and can't even fit one
// item? In that case, break.
if (columnIndex == 1 || i == 0) {
// Is this the second column? Ok, break.
if (columnIndex == 1) {
break;
}
// Set x to the halfway point.
@ -407,10 +399,6 @@ DashDocDisplay.prototype = {
x = x + itemWidth + DEFAULT_SPACING;
// And y is back to the top.
y = box.y1;
// Retry this same item, now that we're in the second column.
// By looping back to the top here, we re-test the size
// again for the second column.
continue;
}
let childBox = new Clutter.ActorBox();
@ -423,53 +411,30 @@ DashDocDisplay.prototype = {
child.show();
child.allocate(childBox, flags);
i++;
}
if (this._checkDocExistence) {
// Now we know how many docs we are displaying, queue a check to see if any of them
// have been deleted. If they are deleted, then we'll get a 'changed' signal; since
// we'll now be displaying items we weren't previously, we'll check again to see
// if they were deleted, and so forth and so on.
// TODO: We should change this to ask for as many as we can fit in the given space:
// https://bugzilla.gnome.org/show_bug.cgi?id=603522#c23
this._docManager.queueExistenceCheck(i);
this._checkDocExistence = false;
// Everything else didn't fit, just hide it.
for (; i < children.length; i++) {
children[i].hide();
}
let skipPaint = [];
for (; i < children.length; i++)
this.actor.set_skip_paint(children[i], true);
},
_onDocsChanged: function() {
this._checkDocExistence = true;
Main.queueDeferredWork(this._workId);
},
_redisplay: function() {
// Should be kept alive by the _actorsByUri
this.actor.remove_all();
let docs = this._docManager.getTimestampOrderedInfos();
for (let i = 0; i < docs.length && i < MAX_DASH_DOCS; i++) {
let doc = docs[i];
let display = this._actorsByUri[doc.uri];
if (display) {
this.actor.add_actor(display.actor);
} else {
let display = new DashDocDisplayItem(doc);
this.actor.add_actor(display.actor);
this._actorsByUri[doc.uri] = display;
}
let docs = this._docManager.getItems();
let docUrls = [];
for (let url in docs) {
docUrls.push(url);
}
// Any unparented actors must have been deleted
for (let uri in this._actorsByUri) {
let display = this._actorsByUri[uri];
if (display.actor.get_parent() == null) {
display.actor.destroy();
delete this._actorsByUri[uri];
}
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);
}
this.emit('changed');
}
@ -477,41 +442,3 @@ DashDocDisplay.prototype = {
Signals.addSignalMethods(DashDocDisplay.prototype);
function DocSearchProvider() {
this._init();
}
DocSearchProvider.prototype = {
__proto__: Search.SearchProvider.prototype,
_init: function(name) {
Search.SearchProvider.prototype._init.call(this, _("DOCUMENTS"));
this._docManager = DocInfo.getDocManager();
},
getResultMeta: function(resultId) {
let docInfo = this._docManager.lookupByUri(resultId);
if (!docInfo)
return null;
return { 'id': resultId,
'name': docInfo.name,
'icon': docInfo.createIcon(Search.RESULT_ICON_SIZE)};
},
activateResult: function(id) {
let docInfo = this._docManager.lookupByUri(id);
docInfo.launch();
},
getInitialResultSet: function(terms) {
return this._docManager.initialSearch(terms);
},
getSubsearchResultSet: function(previousResults, terms) {
return this._docManager.subsearch(previousResults, terms);
},
expandSearch: function(terms) {
log("TODO expand docs search");
}
};

View File

@ -1,12 +1,9 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const St = imports.gi.St;
const Gettext_gtk20 = imports.gettext.domain('gtk20');
const Tweener = imports.ui.tweener;
const Format = imports.misc.format;
// "monkey patch" in some varargs ClutterContainer methods; we need
// to do this per-container class since there is no representation
// of interfaces in Javascript
@ -33,10 +30,4 @@ _patchContainerClass(St.Table);
function init() {
Tweener.init();
String.prototype.format = Format.format;
// Set the default direction for St widgets (this needs to be done before any use of St)
if (Gettext_gtk20.gettext("default:LTR") == "default:RTL") {
St.Widget.set_default_direction(St.TextDirection.RTL);
}
}

View File

@ -1,158 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const ExtensionState = {
ENABLED: 1,
DISABLED: 2,
ERROR: 3,
OUT_OF_DATE: 4
};
const ExtensionType = {
SYSTEM: 1,
PER_USER: 2
};
// Maps uuid -> metadata object
const extensionMeta = {};
// Maps uuid -> importer object (extension directory tree)
const extensions = {};
// Array of uuids
var disabledExtensions;
// GFile for user extensions
var userExtensionsDir = null;
function loadExtension(dir, enabled, type) {
let info;
let baseErrorString = 'While loading extension from "' + dir.get_parse_name() + '": ';
let metadataFile = dir.get_child('metadata.json');
if (!metadataFile.query_exists(null)) {
global.logError(baseErrorString + 'Missing metadata.json');
return;
}
let [success, metadataContents, len, etag] = metadataFile.load_contents(null);
let meta;
try {
meta = JSON.parse(metadataContents);
} catch (e) {
global.logError(baseErrorString + 'Failed to parse metadata.json: ' + e);
return;
}
let requiredProperties = ['uuid', 'name', 'description'];
for (let i = 0; i < requiredProperties; i++) {
let prop = requiredProperties[i];
if (!meta[prop]) {
global.logError(baseErrorString + 'missing "' + prop + '" property in metadata.json');
return;
}
}
// Encourage people to add this
if (!meta['url']) {
global.log(baseErrorString + 'Warning: Missing "url" property in metadata.json');
}
let base = dir.get_basename();
if (base != meta.uuid) {
global.logError(baseErrorString + 'uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + base + '"');
return;
}
extensionMeta[meta.uuid] = meta;
extensionMeta[meta.uuid].type = type;
extensionMeta[meta.uuid].path = dir.get_path();
if (!enabled) {
extensionMeta[meta.uuid].state = ExtensionState.DISABLED;
return;
}
// Default to error, we set success as the last step
extensionMeta[meta.uuid].state = ExtensionState.ERROR;
let extensionJs = dir.get_child('extension.js');
if (!extensionJs.query_exists(null)) {
global.logError(baseErrorString + 'Missing extension.js');
return;
}
let stylesheetPath = null;
let themeContext = St.ThemeContext.get_for_stage(global.stage);
let theme = themeContext.get_theme();
let stylesheetFile = dir.get_child('stylesheet.css');
if (stylesheetFile.query_exists(null)) {
try {
theme.load_stylesheet(stylesheetFile.get_path());
} catch (e) {
global.logError(baseErrorString + 'Stylesheet parse error: ' + e);
return;
}
}
let extensionModule;
try {
global.add_extension_importer('imports.ui.extensionSystem.extensions', meta.uuid, dir.get_path());
extensionModule = extensions[meta.uuid].extension;
} catch (e) {
if (stylesheetPath != null)
theme.unload_stylesheet(stylesheetPath);
global.logError(baseErrorString + e);
return;
}
if (!extensionModule.main) {
global.logError(baseErrorString + 'missing \'main\' function');
return;
}
try {
extensionModule.main();
} catch (e) {
if (stylesheetPath != null)
theme.unload_stylesheet(stylesheetPath);
global.logError(baseErrorString + 'Failed to evaluate main function:' + e);
return;
}
extensionMeta[meta.uuid].state = ExtensionState.ENABLED;
global.log('Loaded extension ' + meta.uuid);
}
function init() {
let userConfigPath = GLib.get_user_config_dir();
let userExtensionsPath = GLib.build_filenamev([userConfigPath, 'gnome-shell', 'extensions']);
userExtensionsDir = Gio.file_new_for_path(userExtensionsPath);
try {
userExtensionsDir.make_directory_with_parents(null);
} catch (e) {
global.logError(""+e);
}
disabledExtensions = Shell.GConf.get_default().get_string_list('disabled_extensions');
}
function _loadExtensionsIn(dir, type) {
let fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
let file, info;
while ((info = fileEnum.next_file(null)) != null) {
let fileType = info.get_file_type();
if (fileType != Gio.FileType.DIRECTORY)
continue;
let name = info.get_name();
let enabled = disabledExtensions.indexOf(name) < 0;
let child = dir.get_child(name);
loadExtension(child, enabled, type);
}
fileEnum.close(null);
}
function loadExtensions() {
_loadExtensionsIn(userExtensionsDir, ExtensionType.PER_USER);
let systemDataDirs = GLib.get_system_data_dirs();
for (let i = 0; i < systemDataDirs.length; i++) {
let dirPath = systemDataDirs[i] + '/gnome-shell/extensions';
let dir = Gio.file_new_for_path(dirPath);
if (dir.query_exists(null))
_loadExtensionsIn(dir, ExtensionType.SYSTEM);
}
}

View File

@ -11,18 +11,25 @@ const Meta = imports.gi.Meta;
const Pango = imports.gi.Pango;
const Signals = imports.signals;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Button = imports.ui.button;
const DND = imports.ui.dnd;
const Link = imports.ui.link;
const Main = imports.ui.main;
const RedisplayFlags = { NONE: 0,
RESET_CONTROLS: 1 << 0,
FULL: 1 << 1,
SUBSEARCH: 1 << 2,
IMMEDIATE: 1 << 3 };
SUBSEARCH: 1 << 2 };
const ITEM_DISPLAY_NAME_COLOR = new Clutter.Color();
ITEM_DISPLAY_NAME_COLOR.from_pixel(0xffffffff);
const ITEM_DISPLAY_DESCRIPTION_COLOR = new Clutter.Color();
ITEM_DISPLAY_DESCRIPTION_COLOR.from_pixel(0xffffffbb);
const ITEM_DISPLAY_BACKGROUND_COLOR = new Clutter.Color();
ITEM_DISPLAY_BACKGROUND_COLOR.from_pixel(0x00000000);
const ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR = new Clutter.Color();
ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR.from_pixel(0x4f6fadaa);
const DISPLAY_CONTROL_SELECTED_COLOR = new Clutter.Color();
DISPLAY_CONTROL_SELECTED_COLOR.from_pixel(0x112288ff);
const PREVIEW_BOX_BACKGROUND_COLOR = new Clutter.Color();
@ -30,7 +37,10 @@ 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_RIGHT = 2;
const DEFAULT_COLUMN_GAP = 6;
const PREVIEW_ICON_SIZE = 96;
@ -41,6 +51,8 @@ const PREVIEW_BOX_CORNER_RADIUS = 10;
const PREVIEW_PLACING = 3/4;
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
* it by highlighting it with a different background color than the default.
@ -51,8 +63,12 @@ function GenericDisplayItem() {
GenericDisplayItem.prototype = {
_init: function() {
this.actor = new St.BoxLayout({ style_class: "generic-display-item",
reactive: true });
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 });
this.actor._delegate = this;
this.actor.connect('button-release-event',
@ -64,13 +80,47 @@ GenericDisplayItem.prototype = {
}));
let draggable = DND.makeDraggable(this.actor);
draggable.connect('drag-begin', Lang.bind(this, this._onDragBegin));
this._iconBin = new St.Bin();
this.actor.add(this._iconBin);
this._infoContent = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
spacing: DEFAULT_PADDING });
this.actor.append(this._infoContent, Big.BoxPackFlags.EXPAND);
this._infoText = new St.BoxLayout({ style_class: 'generic-display-item-text',
vertical: true });
this.actor.add(this._infoText, { expand: true, y_fill: false });
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);
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,
// 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._name = null;
this._description = null;
@ -102,10 +152,24 @@ GenericDisplayItem.prototype = {
//// Public methods ////
// Shows the information button when the item was drawn under the mouse pointer.
onDrawnUnderPointer: function() {
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) {
this.actor.set_style_pseudo_class(isSelected ? "selected" : null);
let color;
if (isSelected) {
color = ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR;
this._informationButton.forceShow(true);
}
else {
color = ITEM_DISPLAY_BACKGROUND_COLOR;
this._informationButton.forceShow(false);
}
this.actor.background_color = color;
},
/*
@ -121,14 +185,19 @@ GenericDisplayItem.prototype = {
spacing: PREVIEW_BOX_SPACING });
// Inner box with name and description
let textDetails = new St.BoxLayout({ style_class: 'generic-display-details',
vertical: true });
let detailsName = new St.Label({ style_class: 'generic-display-details-name',
text: this._name.text });
textDetails.add(detailsName);
let textDetails = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
spacing: PREVIEW_BOX_SPACING });
let detailsName = new Clutter.Text({ color: ITEM_DISPLAY_NAME_COLOR,
font_name: "Sans bold 14px",
line_wrap: true,
text: this._name.text });
textDetails.append(detailsName, Big.BoxPackFlags.NONE);
let detailsDescription = new St.Label({ text: this._description.text });
textDetails.add(detailsDescription);
let detailsDescription = new Clutter.Text({ color: ITEM_DISPLAY_NAME_COLOR,
font_name: "Sans 14px",
line_wrap: true,
text: this._description.text });
textDetails.append(detailsDescription, Big.BoxPackFlags.NONE);
this._detailsDescriptions.push(detailsDescription);
@ -154,7 +223,7 @@ GenericDisplayItem.prototype = {
// Destroys the item.
destroy: function() {
this.actor.destroy();
this.actor.destroy();
},
//// Pure virtual public methods ////
@ -191,15 +260,20 @@ GenericDisplayItem.prototype = {
}
this._icon = this._createIcon();
this._iconBin.set_child(this._icon);
this._iconBox.append(this._icon, Big.BoxPackFlags.NONE);
this._name = new St.Label({ style_class: "generic-display-item-name",
text: nameText });
this._infoText.add(this._name);
this._name = new Clutter.Text({ color: ITEM_DISPLAY_NAME_COLOR,
font_name: "Sans 14px",
ellipsize: Pango.EllipsizeMode.END,
text: nameText });
this._infoText.append(this._name, Big.BoxPackFlags.EXPAND);
this._description = new St.Label({ style_class: "generic-display-item-description",
text: descriptionText ? descriptionText : "" });
this._infoText.add(this._description);
this._description = new Clutter.Text({ color: ITEM_DISPLAY_DESCRIPTION_COLOR,
font_name: "Sans 12px",
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
@ -231,43 +305,42 @@ GenericDisplayItem.prototype = {
// Returns a preview icon for the item.
_createPreviewIcon: function() {
throw new Error("Not implemented");
}
},
//// Private methods ////
// 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();
}
};
Signals.addSignalMethods(GenericDisplayItem.prototype);
const GenericDisplayFlags = {
DISABLE_VSCROLLING: 1 << 0
}
/* This is a virtual class that represents a display containing a collection of items
* that can be filtered with a search string.
*/
function GenericDisplay(flags) {
this._init(flags);
function GenericDisplay() {
this._init();
}
GenericDisplay.prototype = {
_init : function(flags) {
let disableVScrolling = (flags & GenericDisplayFlags.DISABLE_VSCROLLING) != 0;
_init : function() {
this._search = '';
this._expanded = false;
if (disableVScrolling) {
this.actor = this._list = new Shell.OverflowList({ spacing: 6,
item_height: 50 });
} else {
this.actor = new St.ScrollView({ x_fill: true, y_fill: true });
this.actor.get_hscroll_bar().hide();
this._list = new St.BoxLayout({ style_class: 'generic-display-container',
vertical: true });
this.actor.add_actor(this._list);
}
this._maxItemsPerPage = null;
this._list = new Shell.OverflowList({ spacing: 6.0,
item_height: ITEM_DISPLAY_HEIGHT });
this._pendingRedisplay = RedisplayFlags.NONE;
this.actor.connect('notify::mapped', Lang.bind(this, this._onMappedNotify));
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);
}));
// map<itemId, Object> where Object represents the item info
this._allItems = {};
@ -279,6 +352,13 @@ GenericDisplay.prototype = {
this._displayedItems = {};
this._openDetailIndex = -1;
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;
this.displayControl = new Big.Box({ background_color: ITEM_DISPLAY_BACKGROUND_COLOR,
spacing: 12,
orientation: Big.BoxOrientation.HORIZONTAL});
},
//// Public methods ////
@ -286,18 +366,12 @@ GenericDisplay.prototype = {
// Sets the search string and displays the matching items.
setSearch: function(text) {
let lowertext = text.toLowerCase();
if (lowertext == this._search) {
if (lowertext == this._search)
return;
}
let flags = RedisplayFlags.IMMEDIATE;
let flags = RedisplayFlags.RESET_CONTROLS;
if (this._search != '') {
// Because we combine search terms with OR, we have to be sure that no new term
// was introduced before deciding that the new search results will be a subset of
// the existing search results.
if (lowertext.indexOf(this._search) == 0 &&
lowertext.split(/\s+/).length == this._search.split(/\s+/).length) {
if (lowertext.indexOf(this._search) == 0)
flags |= RedisplayFlags.SUBSEARCH;
}
}
this._search = lowertext;
this._redisplay(flags);
@ -317,7 +391,7 @@ 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._getVisibleCount();
let count = this._list.displayedCount;
let selectedUp = true;
let prev = this._selectedIndex - 1;
if (this._selectedIndex <= 0) {
@ -332,7 +406,7 @@ 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._getVisibleCount();
let count = this._list.displayedCount;
let selectedDown = true;
let next = this._selectedIndex + 1;
if (this._selectedIndex == count - 1) {
@ -351,7 +425,7 @@ GenericDisplay.prototype = {
// Selects the last item among the displayed items.
selectLastItem: function() {
let count = this._getVisibleCount();
let count = this._list.displayedCount;
if (this.hasItems())
this._selectIndex(count - 1);
},
@ -388,8 +462,6 @@ GenericDisplay.prototype = {
resetState: function() {
this._filterReset();
this._openDetailIndex = -1;
if (!(this.actor instanceof Shell.OverflowList))
this.actor.get_vscroll_bar().get_adjustment().value = 0;
},
// Returns an actor which acts as a sidebar; this is used for
@ -403,18 +475,26 @@ GenericDisplay.prototype = {
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 ////
_recreateDisplayItems: function() {
_redisplayFull: function() {
this._removeAllDisplayItems();
this._setDefaultList();
for (let itemId in this._allItems) {
this._addDisplayItem(itemId);
}
},
// Creates a display item based on the information associated with itemId
// and adds it to the list of displayed items, but does not yet display it.
// and adds it to the displayed items.
_addDisplayItem : function(itemId) {
if (this._displayedItems.hasOwnProperty(itemId)) {
log("Tried adding a display item for " + itemId + ", but an item with this item id is already among displayed items.");
@ -428,14 +508,14 @@ GenericDisplay.prototype = {
Lang.bind(this,
function() {
// update the selection
this._selectIndex(this._list.get_children().indexOf(displayItem.actor));
this._selectIndex(this._list.get_actor_index(displayItem.actor));
this.activateSelected();
}));
displayItem.connect('show-details',
Lang.bind(this,
function() {
let index = this._list.get_children().indexOf(displayItem.actor);
let index = this._list.get_actor_index(displayItem.actor);
/* Close the details pane if already open */
if (index == this._openDetailIndex) {
this._openDetailIndex = -1;
@ -445,15 +525,15 @@ GenericDisplay.prototype = {
this.emit('show-details', index);
}
}));
this._list.add_actor(displayItem.actor);
this._displayedItems[itemId] = displayItem;
},
// Removes an item identifed by the itemId from the displayed items.
_removeDisplayItem: function(itemId) {
let children = this._list.get_children();
let count = children.length;
let count = this._list.displayedCount;
let displayItem = this._displayedItems[itemId];
let displayItemIndex = children.indexOf(displayItem.actor);
let displayItemIndex = this._list.get_actor_index(displayItem.actor);
if (this.hasSelected() && count == 1) {
this.unsetSelected();
@ -548,51 +628,62 @@ GenericDisplay.prototype = {
/*
* Updates the displayed items, applying the search string if one exists.
* @flags: Flags controlling redisplay behavior as follows:
* RESET_CONTROLS - indicates if the page selection should be reset when displaying the matching results.
* We reset the page selection when the change in results was initiated by the user by
* entering a different search criteria or by viewing the results list in a different
* size mode, but we keep the page selection the same if the results got updated on
* their own while the user was browsing through the result pages.
* SUBSEARCH - Indicates that the current _search is a superstring of the previous
* one, which implies we only need to re-search through previous results.
* FULL - Indicates that we need recreate all displayed items.
* IMMEDIATE - Do the full redisplay even if we're not mapped. This is useful
* if you want to get the number of matched items and show/hide a section based on
* that number.
*/
_redisplay: function(flags) {
let immediate = (flags & RedisplayFlags.IMMEDIATE) != 0;
if (!immediate && !this.actor.mapped) {
this._pendingRedisplay |= flags;
return;
}
let isSubSearch = (flags & RedisplayFlags.SUBSEARCH) != 0;
let fullReload = (flags & RedisplayFlags.FULL) != 0;
let resetPage = (flags & RedisplayFlags.RESET_CONTROLS) > 0;
let isSubSearch = (flags & RedisplayFlags.SUBSEARCH) > 0;
let fullReload = (flags & RedisplayFlags.FULL) > 0;
let hadSelected = this.hasSelected();
this.unsetSelected();
if (!this._initialLoadComplete)
if (!this._initialLoadComplete || !this._refreshCache())
fullReload = true;
if (!this._refreshCache())
fullReload = true;
if (fullReload) {
this._recreateDisplayItems();
this._initialLoadComplete = true;
}
if (isSubSearch) {
this._redisplayFull();
} if (isSubSearch) {
this._redisplaySubSearch();
} else {
this._redisplayReordering();
}
if (resetPage)
this._list.page = 0;
if (hadSelected) {
this._selectedIndex = -1;
this.selectFirstItem();
}
Mainloop.idle_add(Lang.bind(this, this._checkInformationIcon),
Meta.PRIORITY_BEFORE_REDRAW);
this.emit('redisplayed');
},
// 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.
_checkInformationIcon: function() {
let [child, x, y, mask] = Gdk.Screen.get_default().get_root_window().get_pointer();
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;
},
//// Pure virtual protected methods ////
// Performs the steps needed to have the latest information about the items.
@ -665,14 +756,59 @@ GenericDisplay.prototype = {
return matchScores;
},
/*
* Updates the display control to reflect the matched items set and the page selected.
*
* resetDisplayControl - indicates if the display control should be re-created because
* the results or the space allocated for them changed. If it's false,
* the existing display control is used and only the page links are
* updated to reflect the current page selection.
*/
_updateDisplayControl: function(resetDisplayControl) {
if (resetDisplayControl) {
this.displayControl.remove_all();
let nPages = this._list.n_pages;
// Don't show the page indicator if there is only one page.
if (nPages == 1)
return;
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,
font_name: "Sans Bold 16px",
text: (i+1) + "",
reactive: (i == pageNumber) ? 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;
pageControl.connect('clicked',
Lang.bind(this,
function(o, event) {
this.displayPage(pageNumberLocalScope);
}));
}
} else {
let pageControlActors = this.displayControl.get_children();
for (let i = 0; i < pageControlActors.length; i++) {
let pageControlActor = pageControlActors[i];
if (i == this._list.page) {
pageControlActor.color = DISPLAY_CONTROL_SELECTED_COLOR;
pageControlActor.reactive = false;
} else {
pageControlActor.color = ITEM_DISPLAY_DESCRIPTION_COLOR;
pageControlActor.reactive = true;
}
}
}
if (this.hasSelected()) {
this.selectFirstItem();
}
},
// Returns a display item based on its index in the ordering of the
// display children.
_findDisplayedByIndex: function(index) {
let actor;
if (this.actor instanceof Shell.OverflowList)
actor = this.actor.get_displayed_actor(index);
else
actor = this._list.get_children()[index];
let actor = this._list.get_displayed_actor(index);
return this._findDisplayedByActor(actor);
},
@ -704,20 +840,6 @@ GenericDisplay.prototype = {
let item = this._findDisplayedByIndex(index);
item.markSelected(true);
this.emit('selected');
},
_getVisibleCount: function() {
if (this.actor instanceof Shell.OverflowList)
return this._list.displayed_count;
return this._list.get_n_children();
},
_onMappedNotify: function () {
let mapped = this.actor.mapped;
if (mapped && this._pendingRedisplay > RedisplayFlags.NONE)
this._redisplay(this._pendingRedisplay);
this._pendingRedisplay = RedisplayFlags.NONE;
}
};

View File

@ -3,21 +3,78 @@
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Signals = imports.signals;
const St = imports.gi.St;
// Link is a clickable link. Right now it just handles properly capturing
// press and release events and short-circuiting the button handling in
// ClutterText, but more features like different colors for hover/pressed states
// or a different mouse cursor could be implemented.
//
// The properties passed in are forwarded to the Clutter.Text() constructor,
// so can include, 'text', 'font_name', etc.
function Link(props) {
this._init(props);
}
Link.prototype = {
_init : function(props) {
let realProps = { reactive: true,
style_class: 'shell-link' };
let realProps = { reactive: true };
// The user can pass in reactive: false to override the above and get
// a non-reactive link (a link to the current page, perhaps)
Lang.copyProperties(props, realProps);
Lang.copyProperties(props, realProps);
this.actor = new St.Button(realProps);
this.actor = new Clutter.Text(realProps);
this.actor._delegate = this;
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
this.actor.connect('button-release-event', Lang.bind(this, this._onButtonRelease));
this.actor.connect('enter-event', Lang.bind(this, this._onEnter));
this.actor.connect('leave-event', Lang.bind(this, this._onLeave));
this._buttonDown = false;
this._havePointer = false;
},
// Update the text of the link
setText : function(text) {
this.actor.text = text;
},
// We want to react on buttonDown, but if we override button-release-event for
// ClutterText, but not button-press-event, we get a stuck grab. Tracking
// buttonDown and doing the grab isn't really necessary, but doing it makes
// the behavior perfectly correct if the user clicks on one actor, drags
// to another and releases - that should not trigger either actor.
_onButtonPress : function(actor, event) {
this._buttonDown = true;
this._havePointer = true; // Hack to work around poor enter/leave tracking in Clutter
Clutter.grab_pointer(actor);
return true;
},
_onButtonRelease : function(actor, event) {
if (this._buttonDown) {
this._buttonDown = false;
Clutter.ungrab_pointer(actor);
if (this._havePointer)
this.emit('clicked');
}
return true;
},
_onEnter : function(actor, event) {
if (event.get_source() == actor)
this._havePointer = true;
return false;
},
_onLeave : function(actor, event) {
if (event.get_source() == actor)
this._havePointer = false;
return false;
}
};

View File

@ -9,11 +9,7 @@ const Shell = imports.gi.Shell;
const Signals = imports.signals;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const ExtensionSystem = imports.ui.extensionSystem;
const Link = imports.ui.link;
const Tweener = imports.ui.tweener;
const Main = imports.ui.main;
@ -43,39 +39,33 @@ Notebook.prototype = {
_init: function() {
this.actor = new St.BoxLayout({ vertical: true });
this.tabControls = new St.BoxLayout({ style_class: "labels" });
this.tabControls = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
spacing: 4, padding: 2 });
this._selectedIndex = -1;
this._tabs = [];
},
appendPage: function(name, child) {
let labelBox = new St.BoxLayout({ style_class: "notebook-tab" });
let label = new St.Button({ label: name });
label.connect('clicked', Lang.bind(this, function () {
let labelOuterBox = new Big.Box({ padding: 2 });
let labelBox = new St.BoxLayout({ reactive: true });
labelOuterBox.append(labelBox, Big.BoxPackFlags.NONE);
let label = new St.Label({ text: name });
labelBox.connect('button-press-event', Lang.bind(this, function () {
this.selectChild(child);
return true;
}));
labelBox.add(label, { expand: true });
this.tabControls.add(labelBox);
this.tabControls.append(labelOuterBox, Big.BoxPackFlags.NONE);
let scrollview = new St.ScrollView({ x_fill: true, y_fill: true });
scrollview.get_hscroll_bar().hide();
scrollview.add_actor(child);
let tabData = { child: child,
labelBox: labelBox,
label: label,
scrollView: scrollview,
_scrollToBottom: false };
this._tabs.push(tabData);
this._tabs.push([child, labelBox, scrollview]);
scrollview.hide();
this.actor.add(scrollview, { expand: true });
let vAdjust = scrollview.vscroll.adjustment;
vAdjust.connect('changed', Lang.bind(this, function () { this._onAdjustScopeChanged(tabData); }));
vAdjust.connect('notify::value', Lang.bind(this, function() { this._onAdjustValueChanged(tabData); }));
if (this._selectedIndex == -1)
this.selectIndex(0);
},
@ -83,9 +73,10 @@ Notebook.prototype = {
_unselect: function() {
if (this._selectedIndex < 0)
return;
let tabData = this._tabs[this._selectedIndex];
tabData.labelBox.set_style_pseudo_class(null);
tabData.scrollView.hide();
let [child, labelBox, scrollview] = this._tabs[this._selectedIndex];
labelBox.padding = 2;
labelBox.border = 0;
scrollview.hide();
this._selectedIndex = -1;
},
@ -97,11 +88,12 @@ Notebook.prototype = {
this.emit('selection', null);
return;
}
let tabData = this._tabs[index];
tabData.labelBox.set_style_pseudo_class('selected');
tabData.scrollView.show();
let [child, labelBox, scrollview] = this._tabs[index];
labelBox.padding = 1;
labelBox.border = 1;
scrollview.show();
this._selectedIndex = index;
this.emit('selection', tabData.child);
this.emit('selection', child);
},
selectChild: function(child) {
@ -109,32 +101,13 @@ Notebook.prototype = {
this.selectIndex(-1);
else {
for (let i = 0; i < this._tabs.length; i++) {
let tabData = this._tabs[i];
if (tabData.child == child) {
let [tabChild, labelBox, scrollview] = this._tabs[i];
if (tabChild == child) {
this.selectIndex(i);
return;
}
}
}
},
scrollToBottom: function(index) {
let tabData = this._tabs[index];
tabData._scrollToBottom = true;
},
_onAdjustValueChanged: function (tabData) {
let vAdjust = tabData.scrollView.vscroll.adjustment;
if (vAdjust.value < (vAdjust.upper - vAdjust.lower - 0.5))
tabData._scrolltoBottom = false;
},
_onAdjustScopeChanged: function (tabData) {
if (!tabData._scrollToBottom)
return;
let vAdjust = tabData.scrollView.vscroll.adjustment;
vAdjust.value = vAdjust.upper - vAdjust.page_size;
}
}
Signals.addSignalMethods(Notebook.prototype);
@ -258,13 +231,12 @@ function Inspector() {
Inspector.prototype = {
_init: function() {
let width = 150;
let primary = global.get_primary_monitor();
let eventHandler = new St.BoxLayout({ name: "LookingGlassDialog",
vertical: false,
y: primary.y + Math.floor(primary.height / 2),
y: Math.floor(global.stage.height/2),
reactive: true });
eventHandler.connect('notify::allocation', Lang.bind(this, function () {
eventHandler.x = primary.x + Math.floor((primary.width - eventHandler.width) / 2);
eventHandler.x = Math.floor((global.stage.width)/2 - (eventHandler.width)/2);
}));
global.stage.add_actor(eventHandler);
let displayText = new St.Label();
@ -308,135 +280,6 @@ Inspector.prototype = {
Signals.addSignalMethods(Inspector.prototype);
function ErrorLog() {
this._init();
}
ErrorLog.prototype = {
_init: function() {
this.actor = new St.BoxLayout();
this.text = new St.Label();
this.actor.add(this.text);
this.text.clutter_text.line_wrap = true;
this.actor.connect('notify::mapped', Lang.bind(this, this._renderText));
},
_formatTime: function(d){
function pad(n) { return n < 10 ? '0' + n : n };
return d.getUTCFullYear()+'-'
+ pad(d.getUTCMonth()+1)+'-'
+ pad(d.getUTCDate())+'T'
+ pad(d.getUTCHours())+':'
+ pad(d.getUTCMinutes())+':'
+ pad(d.getUTCSeconds())+'Z'
},
_renderText: function() {
if (!this.actor.mapped)
return;
let text = this.text.text;
let stack = Main._getAndClearErrorStack();
for (let i = 0; i < stack.length; i++) {
let logItem = stack[i];
text += logItem.category + " t=" + this._formatTime(new Date(logItem.timestamp)) + " " + logItem.message + "\n";
}
this.text.text = text;
}
}
function Extensions() {
this._init();
}
Extensions.prototype = {
_init: function() {
this.actor = new St.BoxLayout({ vertical: true,
name: 'lookingGlassExtensions' });
this._noExtensions = new St.Label({ style_class: 'lg-extensions-none',
text: _("No extensions installed") });
this._extensionsList = new St.BoxLayout({ vertical: true,
style_class: 'lg-extensions-list' });
this.actor.add(this._extensionsList);
this._loadExtensionList();
},
_loadExtensionList: function() {
let extensions = ExtensionSystem.extensionMeta;
let totalExtensions = 0;
for (let uuid in extensions) {
let extensionDisplay = this._createExtensionDisplay(extensions[uuid]);
this._extensionsList.add(extensionDisplay);
totalExtensions++;
}
if (totalExtensions == 0) {
this._extensionsList.add(this._noExtensions);
}
},
_onViewSource: function (actor) {
let meta = actor._extensionMeta;
let file = Gio.file_new_for_path(meta.path);
let uri = file.get_uri();
Gio.app_info_launch_default_for_uri(uri, global.create_app_launch_context());
Main.lookingGlass.close();
},
_onWebPage: function (actor) {
let meta = actor._extensionMeta;
Gio.app_info_launch_default_for_uri(meta.url, global.create_app_launch_context());
Main.lookingGlass.close();
},
_stateToString: function(extensionState) {
switch (extensionState) {
case ExtensionSystem.ExtensionState.ENABLED:
return _("Enabled");
case ExtensionSystem.ExtensionState.DISABLED:
return _("Disabled");
case ExtensionSystem.ExtensionState.ERROR:
return _("Error");
case ExtensionSystem.ExtensionState.OUT_OF_DATE:
return _("Out of date");
}
return "Unknown"; // Not translated, shouldn't appear
},
_createExtensionDisplay: function(meta) {
let box = new St.BoxLayout({ style_class: 'lg-extension', vertical: true });
let name = new St.Label({ style_class: 'lg-extension-name',
text: meta.name });
box.add(name, { expand: true });
let description = new St.Label({ style_class: 'lg-extension-description',
text: meta.description });
box.add(description, { expand: true });
let metaBox = new St.BoxLayout();
box.add(metaBox);
let stateString = this._stateToString(meta.state);
let state = new St.Label({ style_class: 'lg-extension-state',
text: this._stateToString(meta.state) });
let actionsContainer = new St.Bin({ x_align: St.Align.END });
metaBox.add(actionsContainer);
let actionsBox = new St.BoxLayout({ style_class: 'lg-extension-actions' });
actionsContainer.set_child(actionsBox);
let viewsource = new Link.Link({ label: _("View Source") });
viewsource.actor._extensionMeta = meta;
viewsource.actor.connect('clicked', Lang.bind(this, this._onViewSource));
actionsBox.add(viewsource.actor);
if (meta.url) {
let webpage = new Link.Link({ label: _("Web Page") });
webpage.actor._extensionMeta = meta;
webpage.actor.connect('clicked', Lang.bind(this, this._onWebPage));
actionsBox.add(webpage.actor);
}
return box;
}
};
function LookingGlass() {
this._init();
}
@ -449,10 +292,6 @@ LookingGlass.prototype = {
this._savedText = null;
this._historyNavIndex = -1;
this._history = [];
this._borderPaintTarget = null;
this._borderPaintId = 0;
this._borderDestroyId = 0;
this._readHistory();
this._open = false;
@ -497,7 +336,6 @@ LookingGlass.prototype = {
}));
let notebook = new Notebook();
this._notebook = notebook;
this.actor.add(notebook.actor, { expand: true });
let emptyBox = new St.Bin();
@ -535,12 +373,6 @@ LookingGlass.prototype = {
notebook.selectIndex(0);
}));
this._errorLog = new ErrorLog();
notebook.appendPage('Errors', this._errorLog.actor);
this._extensions = new Extensions();
notebook.appendPage('Extensions', this._extensions.actor);
this._entry.clutter_text.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
@ -556,7 +388,10 @@ LookingGlass.prototype = {
}));
this._entry.clutter_text.connect('key-press-event', Lang.bind(this, function(o, e) {
let symbol = e.get_key_symbol();
if (symbol == Clutter.Up) {
if (symbol == Clutter.Escape) {
this.close();
return true;
} else if (symbol == Clutter.Up) {
if (this._historyNavIndex >= this._history.length - 1)
return true;
this._historyNavIndex++;
@ -623,18 +458,6 @@ LookingGlass.prototype = {
this._results.push(result);
this._resultsArea.append(result.actor, Big.BoxPackFlags.NONE);
this._propInspector.setTarget(obj);
if (this._borderPaintTarget != null) {
this._borderPaintTarget.disconnect(this._borderPaintId);
this._borderPaintTarget = null;
}
if (obj instanceof Clutter.Actor) {
this._borderPaintTarget = obj;
this._borderPaintId = Shell.add_hook_paint_red_border(obj);
this._borderDestroyId = obj.connect('destroy', Lang.bind(this, function () {
this._borderDestroyId = 0;
this._borderPaintTarget = null;
}));
}
let children = this._resultsArea.get_children();
if (children.length > this._maxItems) {
this._results.shift();
@ -642,9 +465,6 @@ LookingGlass.prototype = {
this._offset++;
}
this._it = obj;
// Scroll to bottom
this._notebook.scrollToBottom(0);
},
_evaluate : function(command) {
@ -681,11 +501,11 @@ LookingGlass.prototype = {
},
_resizeTo: function(actor) {
let primary = global.get_primary_monitor();
let myWidth = primary.width * 0.7;
let myHeight = primary.height * 0.7;
let stage = global.stage;
let myWidth = stage.width * 0.7;
let myHeight = stage.height * 0.7;
let [srcX, srcY] = actor.get_transformed_position();
this.actor.x = srcX + (primary.width - myWidth) / 2;
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;
@ -701,16 +521,6 @@ LookingGlass.prototype = {
this._resizeTo(actor);
},
// Handle key events which are relevant for all tabs of the LookingGlass
_globalKeyPressEvent : function(actor, event) {
let symbol = event.get_key_symbol();
if (symbol == Clutter.Escape) {
this.close();
return true;
}
return false;
},
open : function() {
if (this._open)
return;
@ -718,9 +528,6 @@ LookingGlass.prototype = {
if (!Main.pushModal(this.actor))
return;
this._keyPressEventId = global.stage.connect('key-press-event',
Lang.bind(this, this._globalKeyPressEvent));
this.actor.show();
this.actor.lower(Main.chrome.actor);
this._open = true;
@ -739,19 +546,10 @@ LookingGlass.prototype = {
if (!this._open)
return;
if (this._keyPressEventId)
global.stage.disconnect(this._keyPressEventId);
this._historyNavIndex = -1;
this._open = false;
Tweener.removeTweens(this.actor);
if (this._borderPaintTarget != null) {
this._borderPaintTarget.disconnect(this._borderPaintId);
this._borderPaintTarget.disconnect(this._borderDestroyId);
this._borderPaintTarget = null;
}
Main.popModal(this.actor);
Tweener.addTween(this.actor, { time: 0.5,

View File

@ -14,14 +14,10 @@ const St = imports.gi.St;
const Chrome = imports.ui.chrome;
const Environment = imports.ui.environment;
const ExtensionSystem = imports.ui.extensionSystem;
const MessageTray = imports.ui.messageTray;
const Overview = imports.ui.overview;
const Panel = imports.ui.panel;
const PlaceDisplay = imports.ui.placeDisplay;
const RunDialog = imports.ui.runDialog;
const LookingGlass = imports.ui.lookingGlass;
const NotificationDaemon = imports.ui.notificationDaemon;
const ShellDBus = imports.ui.shellDBus;
const Sidebar = imports.ui.sidebar;
const WindowManager = imports.ui.windowManager;
@ -32,20 +28,14 @@ DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff);
let chrome = null;
let panel = null;
let sidebar = null;
let placesManager = null;
let overview = null;
let runDialog = null;
let lookingGlass = null;
let wm = null;
let notificationDaemon = null;
let notificationPopup = null;
let messageTray = null;
let recorder = null;
let shellDBusService = null;
let modalCount = 0;
let modalActorFocusStack = [];
let _errorLogStack = [];
let _startDate;
function start() {
// Add a binding for "global" in the global JS namespace; (gjs
@ -53,12 +43,6 @@ function start() {
// called "window".)
window.global = Shell.Global.get();
// Now monkey patch utility functions into the global proxy;
// This is easier and faster than indirecting down into global
// if we want to call back up into JS.
global.logError = _logError;
global.log = _logDebug;
Gio.DesktopAppInfo.set_desktop_env("GNOME");
global.grab_dbus_service();
@ -71,16 +55,15 @@ function start() {
Environment.init();
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
// Ensure ShellAppMonitor is initialized; this will
// also initialize ShellAppSystem first. ShellAppSystem
// needs to load all the .desktop files, and ShellWindowTracker
// 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.WindowTracker.get_default();
Shell.AppUsage.get_default();
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
@ -112,17 +95,11 @@ function start() {
getRunDialog().open();
});
placesManager = new PlaceDisplay.PlacesManager();
overview = new Overview.Overview();
chrome = new Chrome.Chrome();
panel = new Panel.Panel();
sidebar = new Sidebar.Sidebar();
wm = new WindowManager.WindowManager();
notificationDaemon = new NotificationDaemon.NotificationDaemon();
notificationPopup = new MessageTray.Notification();
messageTray = new MessageTray.MessageTray();
_startDate = new Date();
global.screen.connect('toggle-recording', function() {
if (recorder == null) {
@ -138,9 +115,6 @@ function start() {
_relayout();
ExtensionSystem.init();
ExtensionSystem.loadExtensions();
panel.startupAnimation();
let display = global.screen.get_display();
@ -149,56 +123,11 @@ function start() {
global.stage.connect('captured-event', _globalKeyPressHandler);
_log('info', 'loaded at ' + _startDate);
Mainloop.idle_add(_removeUnusedWorkspaces);
}
/**
* _log:
* @category: string message type ('info', 'error')
* @msg: A message string
* ...: Any further arguments are converted into JSON notation,
* and appended to the log message, separated by spaces.
*
* Log a message into the LookingGlass error
* stream. This is primarily intended for use by the
* extension system as well as debugging.
*/
function _log(category, msg) {
let text = msg;
if (arguments.length > 2) {
text += ': ';
for (let i = 2; i < arguments.length; i++) {
text += JSON.stringify(arguments[i]);
if (i < arguments.length - 1)
text += " ";
}
}
_errorLogStack.push({timestamp: new Date().getTime(),
category: category,
message: text });
}
function _logError(msg) {
return _log('error', msg);
}
function _logDebug(msg) {
return _log('debug', msg);
}
// Used by the error display in lookingGlass.js
function _getAndClearErrorStack() {
let errors = _errorLogStack;
_errorLogStack = [];
return errors;
}
function _relayout() {
let primary = global.get_primary_monitor();
panel.actor.set_position(primary.x, primary.y);
panel.actor.set_size(primary.width, Panel.PANEL_HEIGHT);
panel.actor.set_size(global.screen_width, Panel.PANEL_HEIGHT);
overview.relayout();
}
@ -270,7 +199,7 @@ function _globalKeyPressHandler(actor, event) {
overview.hide();
return true;
} else if (symbol == Clutter.F2 && (Shell.get_event_state(event) & Clutter.ModifierType.MOD1_MASK)) {
} else if (symbol == Clutter.F2 && (event.get_state() & Clutter.ModifierType.MOD1_MASK)) {
getRunDialog().open();
}
}
@ -305,7 +234,7 @@ function _findModal(actor) {
*/
function pushModal(actor) {
if (modalCount == 0) {
if (!global.begin_modal(global.get_current_time())) {
if (!global.begin_modal(currentTime())) {
log("pushModal: invocation of begin_modal failed");
return false;
}
@ -358,7 +287,7 @@ function popModal(actor) {
if (modalCount > 0)
return;
global.end_modal(global.get_current_time());
global.end_modal(currentTime());
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
}
@ -377,6 +306,45 @@ function getRunDialog() {
return runDialog;
}
function createAppLaunchContext() {
let context = new Gdk.AppLaunchContext();
context.set_timestamp(currentTime());
// Make sure that the app is opened on the current workspace even if
// the user switches before it starts
context.set_desktop(global.screen.get_active_workspace_index());
return context;
}
/**
* currentTime:
*
* Gets the current X server time from the current Clutter, Gdk, or X
* event. If called from outside an event handler, this may return
* %Clutter.CURRENT_TIME (aka 0), or it may return a slightly
* out-of-date timestamp.
*/
function currentTime() {
// meta_display_get_current_time() will return the correct time
// when handling an X or Gdk event, but will return CurrentTime
// from some Clutter event callbacks.
//
// clutter_get_current_event_time() will return the correct time
// from a Clutter event callback, but may return an out-of-date
// timestamp if called at other times.
//
// So we try meta_display_get_current_time() first, since we
// can recognize a "wrong" answer from that, and then fall back
// to clutter_get_current_event_time().
let time = global.screen.get_display().get_current_time();
if (time != Clutter.CURRENT_TIME)
return time;
return Clutter.get_current_event_time();
}
/**
* activateWindow:
* @window: the Meta.Window to activate
@ -389,7 +357,7 @@ function activateWindow(window, time) {
let windowWorkspaceNum = window.get_workspace().index();
if (!time)
time = global.get_current_time();
time = currentTime();
if (windowWorkspaceNum != activeWorkspaceNum) {
let workspace = global.screen.get_workspace_by_index(windowWorkspaceNum);
@ -398,120 +366,3 @@ function activateWindow(window, time) {
window.activate(time);
}
}
// TODO - replace this timeout with some system to guess when the user might
// be e.g. just reading the screen and not likely to interact.
const DEFERRED_TIMEOUT_SECONDS = 20;
var _deferredWorkData = {};
// Work scheduled for some point in the future
var _deferredWorkQueue = [];
// Work we need to process before the next redraw
var _beforeRedrawQueue = [];
// Counter to assign work ids
var _deferredWorkSequence = 0;
var _deferredTimeoutId = 0;
function _runDeferredWork(workId) {
if (!_deferredWorkData[workId])
return;
let index = _deferredWorkQueue.indexOf(workId);
if (index < 0)
return;
_deferredWorkQueue.splice(index, 1);
_deferredWorkData[workId].callback();
if (_deferredWorkQueue.length == 0 && _deferredTimeoutId > 0) {
Mainloop.source_remove(_deferredTimeoutId);
_deferredTimeoutId = 0;
}
}
function _runAllDeferredWork() {
while (_deferredWorkQueue.length > 0)
_runDeferredWork(_deferredWorkQueue[0]);
}
function _runBeforeRedrawQueue() {
for (let i = 0; i < _beforeRedrawQueue.length; i++) {
let workId = _beforeRedrawQueue[i];
_runDeferredWork(workId);
}
_beforeRedrawQueue = [];
}
function _queueBeforeRedraw(workId) {
_beforeRedrawQueue.push(workId);
if (_beforeRedrawQueue.length == 1) {
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, function () {
_runBeforeRedrawQueue();
return false;
}, null);
}
}
/**
* initializeDeferredWork:
* @actor: A #ClutterActor
* @callback: Function to invoke to perform work
*
* This function sets up a callback to be invoked when either the
* given actor is mapped, or after some period of time when the machine
* is idle. This is useful if your actor isn't always visible on the
* screen (for example, all actors in the overview), and you don't want
* to consume resources updating if the actor isn't actually going to be
* displaying to the user.
*
* Note that queueDeferredWork is called by default immediately on
* initialization as well, under the assumption that new actors
* will need it.
*
* Returns: A string work identifer
*/
function initializeDeferredWork(actor, callback, props) {
// Turn into a string so we can use as an object property
let workId = "" + (++_deferredWorkSequence);
_deferredWorkData[workId] = { 'actor': actor,
'callback': callback };
actor.connect('notify::mapped', function () {
if (!(actor.mapped && _deferredWorkQueue.indexOf(workId) >= 0))
return;
_queueBeforeRedraw(workId);
});
actor.connect('destroy', function() {
let index = _deferredWorkQueue.indexOf(workId);
if (index >= 0)
_deferredWorkQueue.splice(index, 1);
delete _deferredWorkData[workId];
});
queueDeferredWork(workId);
return workId;
}
/**
* queueDeferredWork:
* @workId: work identifier
*
* Ensure that the work identified by @workId will be
* run on map or timeout. You should call this function
* for example when data being displayed by the actor has
* changed.
*/
function queueDeferredWork(workId) {
let data = _deferredWorkData[workId];
if (!data) {
global.logError("invalid work id ", workId);
return;
}
if (_deferredWorkQueue.indexOf(workId) < 0)
_deferredWorkQueue.push(workId);
if (data.actor.mapped) {
_queueBeforeRedraw(workId);
return;
} else if (_deferredTimeoutId == 0) {
_deferredTimeoutId = Mainloop.timeout_add_seconds(DEFERRED_TIMEOUT_SECONDS, function () {
_runAllDeferredWork();
_deferredTimeoutId = 0;
return false;
});
}
}

View File

@ -1,271 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Pango = imports.gi.Pango;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Signals = imports.signals;
const Tweener = imports.ui.tweener;
const Main = imports.ui.main;
const ANIMATION_TIME = 0.2;
const NOTIFICATION_TIMEOUT = 4;
const MESSAGE_TRAY_TIMEOUT = 0.2;
const ICON_SIZE = 24;
function Notification(icon, text) {
this._init(icon, text);
}
Notification.prototype = {
_init: function(icon, text) {
this.icon = icon;
this.text = text;
}
}
function NotificationBox() {
this._init();
}
NotificationBox.prototype = {
_init: function() {
this.actor = new St.BoxLayout({ name: 'notification' });
this._iconBox = new St.Bin();
this.actor.add(this._iconBox);
this._textBox = new Shell.GenericContainer();
this._textBox.connect('get-preferred-width', Lang.bind(this, this._textBoxGetPreferredWidth));
this._textBox.connect('get-preferred-height', Lang.bind(this, this._textBoxGetPreferredHeight));
this._textBox.connect('allocate', Lang.bind(this, this._textBoxAllocate));
this.actor.add(this._textBox, { expand: true, x_fill: false, y_fill: false, y_align: St.Align.MIDDLE });
this._text = new St.Label();
this._text.clutter_text.line_wrap = true;
this._text.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._textBox.add_actor(this._text);
},
_textBoxGetPreferredWidth: function (actor, forHeight, alloc) {
let [min, nat] = this._text.get_preferred_width(forHeight);
alloc.min_size = alloc.nat_size = Math.min(nat, global.screen_width / 2);
},
_textBoxGetPreferredHeight: function (actor, forWidth, alloc) {
// St.BoxLayout passes -1 for @forWidth, which isn't what we want.
let prefWidth = {};
this._textBoxGetPreferredWidth(this._textBox, -1, prefWidth);
[alloc.min_size, alloc.nat_size] = this._text.get_preferred_height(prefWidth.nat_size);
log('for width ' + prefWidth.nat_size + ', height ' + alloc.nat_size);
},
_textBoxAllocate: function (actor, box, flags) {
let childBox = new Clutter.ActorBox();
childBox.x1 = childBox.y1 = 0;
childBox.x2 = box.x2 - box.x1;
childBox.y2 = box.y2 - box.y1;
this._text.allocate(childBox, flags);
},
setContent: function(notification) {
this._iconBox.child = notification.icon;
// Support <b>, <i>, and <u>, escape anything else
// so it displays as raw markup.
let markup = notification.text.replace(/<(\/?[^biu]>|[^>\/][^>])/g, "&lt;$1");
this._text.clutter_text.set_markup(markup);
}
};
function Source(id, createIcon) {
this._init(id, createIcon);
}
Source.prototype = {
_init: function(id, createIcon) {
this.id = id;
if (createIcon)
this.createIcon = createIcon;
},
// This can be overridden by a subclass, or by the createIcon
// parameter to _init()
createIcon: function(size) {
throw new Error('no implementation of createIcon in ' + this);
},
notify: function(text) {
Main.messageTray.showNotification(new Notification(this.createIcon(ICON_SIZE), text));
},
clicked: function() {
this.emit('clicked');
},
destroy: function() {
this.emit('destroy');
}
};
Signals.addSignalMethods(Source.prototype);
function MessageTray() {
this._init();
}
MessageTray.prototype = {
_init: function() {
this.actor = new St.BoxLayout({ name: 'message-tray',
reactive: true });
let primary = global.get_primary_monitor();
this.actor.x = 0;
this.actor.y = primary.height - 1;
this.actor.width = primary.width;
this._summaryBin = new St.Bin({ x_align: St.Align.END });
this.actor.add(this._summaryBin, { expand: true });
this._summaryBin.hide();
this._notificationBox = new NotificationBox();
this._notificationQueue = [];
this.actor.add(this._notificationBox.actor);
this._notificationBox.actor.hide();
Main.chrome.addActor(this.actor, { affectsStruts: false });
this.actor.connect('enter-event',
Lang.bind(this, this._onMessageTrayEntered));
this.actor.connect('leave-event',
Lang.bind(this, this._onMessageTrayLeft));
this._isShowing = false;
this.actor.show();
this._summary = new St.BoxLayout({ name: 'summary-mode' });
this._summaryBin.child = this._summary;
this._sources = {};
this._icons = {};
},
contains: function(source) {
return this._sources.hasOwnProperty(source.id);
},
add: function(source) {
if (this.contains(source)) {
log('Trying to re-add source ' + source.id);
return;
}
let iconBox = new St.Bin({ reactive: true });
iconBox.child = source.createIcon(ICON_SIZE);
this._summary.insert_actor(iconBox, 0);
this._icons[source.id] = iconBox;
this._sources[source.id] = source;
iconBox.connect('button-release-event', Lang.bind(this,
function () {
source.clicked();
}));
source.connect('destroy', Lang.bind(this,
function () {
this.remove(source);
}));
},
remove: function(source) {
if (!this.contains(source))
return;
this._summary.remove_actor(this._icons[source.id]);
delete this._icons[source.id];
delete this._sources[source.id];
},
getSource: function(id) {
return this._sources[id];
},
_onMessageTrayEntered: function() {
// Don't hide the message tray after a timeout if the user has moved the mouse over it.
// We might have a timeout in place if the user moved the mouse away from the message tray for a very short period of time
// or if we are showing a notification.
if (this._hideTimeoutId > 0)
Mainloop.source_remove(this._hideTimeoutId);
if (this._isShowing)
return;
// If the message tray was not already showing, we'll show it in the summary mode.
this._summaryBin.show();
this._show();
},
_onMessageTrayLeft: function() {
if (!this._isShowing)
return;
// We wait just a little before hiding the message tray in case the user will quickly move the mouse back over it.
this._hideTimeoutId = Mainloop.timeout_add(MESSAGE_TRAY_TIMEOUT * 1000, Lang.bind(this, this._hide));
},
_show: function() {
this._isShowing = true;
let primary = global.get_primary_monitor();
Tweener.addTween(this.actor,
{ y: primary.height - this.actor.height,
time: ANIMATION_TIME,
transition: "easeOutQuad"
});
},
_hide: function() {
this._hideTimeoutId = 0;
let primary = global.get_primary_monitor();
Tweener.addTween(this.actor,
{ y: primary.height - 1,
time: ANIMATION_TIME,
transition: "easeOutQuad",
onComplete: this._hideComplete,
onCompleteScope: this
});
return false;
},
_hideComplete: function() {
this._isShowing = false;
this._summaryBin.hide();
this._notificationBox.actor.hide();
if (this._notificationQueue.length > 0)
this.showNotification(this._notificationQueue.shift());
},
showNotification: function(notification) {
if (this._isShowing) {
this._notificationQueue.push(notification);
return;
}
this._notificationBox.setContent(notification);
this._notificationBox.actor.x = Math.round((this.actor.width - this._notificationBox.actor.width) / 2);
this._notificationBox.actor.show();
// Because we set up the timeout before we do the animation, we add ANIMATION_TIME to NOTIFICATION_TIMEOUT, so that
// NOTIFICATION_TIMEOUT represents the time the notifiation is fully shown.
this._hideTimeoutId = Mainloop.timeout_add((NOTIFICATION_TIMEOUT + ANIMATION_TIME) * 1000, Lang.bind(this, this._hide));
this._show();
}
};

View File

@ -1,218 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const DBus = imports.dbus;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Mainloop = imports.mainloop;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const Params = imports.misc.params;
let nextNotificationId = 1;
const NotificationDaemonIface = {
name: 'org.freedesktop.Notifications',
methods: [{ name: 'Notify',
inSignature: 'susssasa{sv}i',
outSignature: 'u'
},
{ name: 'CloseNotification',
inSignature: 'u',
outSignature: ''
},
{ name: 'GetCapabilities',
inSignature: '',
outSignature: 'as'
},
{ name: 'GetServerInformation',
inSignature: '',
outSignature: 'ssss'
}],
signals: [{ name: 'NotificationClosed',
inSignature: 'uu' },
{ name: 'ActionInvoked',
inSignature: 'us' }]
};
const NotificationClosedReason = {
EXPIRED: 1,
DISMISSED: 2,
APP_CLOSED: 3,
UNDEFINED: 4
};
const Urgency = {
LOW: 0,
NORMAL: 1,
CRITICAL: 2
};
function NotificationDaemon() {
this._init();
}
NotificationDaemon.prototype = {
_init: function() {
DBus.session.exportObject('/org/freedesktop/Notifications', this);
this._everAcquiredName = false;
DBus.session.acquire_name('org.freedesktop.Notifications',
// We pass MANY_INSTANCES so that if
// notification-daemon is running, we'll
// get queued behind it and then get the
// name after killing it below
DBus.MANY_INSTANCES,
Lang.bind(this, this._acquiredName),
Lang.bind(this, this._lostName));
},
_acquiredName: function() {
this._everAcquiredName = true;
},
_lostName: function() {
if (this._everAcquiredName)
log('Lost name org.freedesktop.Notifications!');
else if (GLib.getenv('GNOME_SHELL_NO_REPLACE'))
log('Failed to acquire org.freedesktop.Notifications');
else {
log('Failed to acquire org.freedesktop.Notifications; trying again');
// kill the notification-daemon. pkill is more portable
// than killall, but on Linux at least it won't match if
// you pass more than 15 characters of the process name...
// However, if you use the "-f" flag to match the entire
// command line, it will work, but we have to be careful
// in that case that we don't match "gedit
// notification-daemon.c" or whatever...
let p = new Shell.Process({ args: ['pkill', '-f',
'^([^ ]*/)?(notification-daemon|notify-osd)$']});
p.run();
}
},
_sourceId: function(id) {
return 'notification-' + id;
},
Notify: function(appName, replacesId, icon, summary, body,
actions, hints, timeout) {
let id, source = null;
if (replacesId != 0) {
id = replacesId;
source = Main.messageTray.getSource(this._sourceId(id));
// source may be null if the current source was destroyed
// right as the client sent the new notification
}
if (source == null) {
id = nextNotificationId++;
source = new Source(this._sourceId(id), icon, hints);
Main.messageTray.add(source);
source.connect('clicked', Lang.bind(this,
function() {
source.destroy();
this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
}));
}
summary = GLib.markup_escape_text(summary, -1);
if (body)
source.notify('<b>' + summary + '</b>: ' + body);
else
source.notify('<b>' + summary + '</b>');
return id;
},
CloseNotification: function(id) {
let source = Main.messageTray.getSource(this._sourceId(id));
if (source)
source.destroy();
this._emitNotificationClosed(id, NotificationClosedReason.APP_CLOSED);
},
GetCapabilities: function() {
return [
// 'actions',
'body',
// 'body-hyperlinks',
// 'body-images',
'body-markup',
// 'icon-multi',
'icon-static'
// 'sound',
];
},
GetServerInformation: function() {
return [
'GNOME Shell',
'GNOME',
'0.1', // FIXME, get this from somewhere
'1.0'
];
},
_emitNotificationClosed: function(id, reason) {
DBus.session.emit_signal('/org/freedesktop/Notifications',
'org.freedesktop.Notifications',
'NotificationClosed', 'uu',
[id, reason]);
}
};
DBus.conformExport(NotificationDaemon.prototype, NotificationDaemonIface);
function Source(sourceId, icon, hints) {
this._init(sourceId, icon, hints);
}
Source.prototype = {
__proto__: MessageTray.Source.prototype,
_init: function(sourceId, icon, hints) {
MessageTray.Source.prototype._init.call(this, sourceId);
hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
this._icon = icon;
this._iconData = hints.icon_data;
this._urgency = hints.urgency;
},
createIcon: function(size) {
let textureCache = Shell.TextureCache.get_default();
if (this._icon) {
if (this._icon.substr(0, 7) == 'file://')
return textureCache.load_uri_async(this._icon, size, size);
else if (this._icon[0] == '/') {
let uri = GLib.filename_to_uri(this._icon, null);
return textureCache.load_uri_async(uri, size, size);
} else
return textureCache.load_icon_name(this._icon, size);
} else if (this._iconData) {
let [width, height, rowStride, hasAlpha,
bitsPerSample, nChannels, data] = this._iconData;
return textureCache.load_from_raw(data, data.length, hasAlpha,
width, height, rowStride, size);
} else {
let stockIcon;
switch (this._urgency) {
case Urgency.LOW:
case Urgency.NORMAL:
stockIcon = 'gtk-dialog-info';
break;
case Urgency.CRITICAL:
stockIcon = 'gtk-dialog-error';
break;
}
return textureCache.load_icon_name(stockIcon, size);
}
}
};

View File

@ -12,6 +12,7 @@ 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;
@ -122,7 +123,8 @@ Overview.prototype = {
// Container to hold popup pane chrome.
this._paneContainer = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
spacing: 6 });
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) {
@ -140,68 +142,63 @@ Overview.prototype = {
},
_recalculateGridSizes: function () {
let primary = global.get_primary_monitor();
wideScreen = (primary.width/primary.height > WIDE_SCREEN_CUT_OFF_RATIO) &&
(primary.height >= WIDE_SCREEN_MINIMUM_HEIGHT);
wideScreen = (global.screen_width/global.screen_height > WIDE_SCREEN_CUT_OFF_RATIO) &&
(global.screen_height >= WIDE_SCREEN_MINIMUM_HEIGHT);
// We divide the screen into an imaginary grid which helps us determine the layout of
// different visual components.
if (wideScreen) {
displayGridColumnWidth = Math.floor(primary.width / COLUMNS_WIDE_SCREEN);
displayGridRowHeight = Math.floor(primary.height / ROWS_WIDE_SCREEN);
displayGridColumnWidth = global.screen_width / COLUMNS_WIDE_SCREEN;
displayGridRowHeight = global.screen_height / ROWS_WIDE_SCREEN;
} else {
displayGridColumnWidth = Math.floor(primary.width / COLUMNS_REGULAR_SCREEN);
displayGridRowHeight = Math.floor(primary.height / ROWS_REGULAR_SCREEN);
displayGridColumnWidth = global.screen_width / COLUMNS_REGULAR_SCREEN;
displayGridRowHeight = global.screen_height / ROWS_REGULAR_SCREEN;
}
},
relayout: function () {
let primary = global.get_primary_monitor();
this._group.set_position(primary.x, primary.y);
let screenHeight = global.screen_height;
let screenWidth = global.screen_width;
let contentY = Panel.PANEL_HEIGHT;
let contentHeight = primary.height - contentY;
let contentHeight = screenHeight - contentY;
this._coverPane.set_position(0, contentY);
this._coverPane.set_size(primary.width, contentHeight);
this._coverPane.set_size(screenWidth, contentHeight);
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 (primary.height / primary.width)
// We scale the vertical padding by (screenHeight / screenWidth)
// so that the workspace preserves its aspect ratio.
this._workspacesHeight = Math.floor(displayGridRowHeight * workspaceRowsUsed
- WORKSPACE_GRID_PADDING * (primary.height / primary.width) * 2);
this._workspacesHeight = displayGridRowHeight * workspaceRowsUsed
- WORKSPACE_GRID_PADDING * (screenHeight / screenWidth) * 2;
this._workspacesX = displayGridColumnWidth + WORKSPACE_GRID_PADDING;
this._workspacesY = Math.floor(displayGridRowHeight + WORKSPACE_GRID_PADDING * (primary.height / primary.width));
this._workspacesY = displayGridRowHeight + WORKSPACE_GRID_PADDING * (screenHeight / screenWidth);
this._dash.actor.set_position(0, contentY);
this._dash.actor.set_size(displayGridColumnWidth, contentHeight);
this._dash.searchArea.height = this._workspacesY - contentY;
this._dash.sectionArea.height = this._workspacesHeight;
this._dash.searchResults.actor.height = this._workspacesHeight;
// place the 'Add Workspace' button in the bottom row of the grid
addRemoveButtonSize = Math.floor(displayGridRowHeight * 3/5);
this._addButtonX = this._workspacesX + this._workspacesWidth - addRemoveButtonSize;
this._addButtonY = primary.height - Math.floor(displayGridRowHeight * 4/5);
this._addButtonY = screenHeight - Math.floor(displayGridRowHeight * 4/5);
// The parent (this._group) is positioned at the top left of the primary monitor
// while this._backOver occupies the entire screen.
this._backOver.set_position(- primary.x, - primary.y);
this._backOver.set_size(global.screen_width, global.screen_height);
this._backOver.set_position(0, contentY);
this._backOver.set_size(global.screen_width, contentHeight);
this._paneContainer.set_position(this._dash.actor.x + this._dash.actor.width + DEFAULT_PADDING,
this._workspacesY);
contentY);
// Dynamic width
this._paneContainer.height = this._workspacesHeight;
this._paneContainer.height = contentHeight;
this._transparentBackground.set_position(this._paneContainer.x, this._paneContainer.y);
this._transparentBackground.set_size(primary.width - this._paneContainer.x,
this._transparentBackground.set_size(global.screen_width - this._paneContainer.x,
this._paneContainer.height);
if (this._activeDisplayPane != null)
@ -227,7 +224,6 @@ Overview.prototype = {
this._activeDisplayPane.close();
return true;
}));
this._workspaces.actor.opacity = 64;
} else if (pane == this._activeDisplayPane) {
this._activeDisplayPane = null;
if (backgroundEventId != null) {
@ -236,7 +232,6 @@ Overview.prototype = {
}
this._transparentBackground.lower_bottom();
this._paneContainer.lower_bottom();
this._workspaces.actor.opacity = 255;
}
}));
},
@ -248,7 +243,7 @@ Overview.prototype = {
// 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.AppIcon) {
|| source instanceof AppDisplay.BaseWellItem) {
if (this._activeDisplayPane != null)
this._activeDisplayPane.close();
return true;
@ -327,10 +322,9 @@ Overview.prototype = {
// The opposite transition is used in hide().
this._group.scaleX = this._group.scaleY = this.getZoomedInScale();
[this._group.x, this._group.y] = this.getZoomedInPosition();
let primary = global.get_primary_monitor();
Tweener.addTween(this._group,
{ x: primary.x,
y: primary.y,
{ x: 0,
y: 0,
scaleX: 1,
scaleY: 1,
transition: 'easeOutQuad',
@ -361,10 +355,6 @@ Overview.prototype = {
this._activeDisplayPane.close();
this._workspaces.hide();
this._addButton.actor.destroy();
this._addButton.actor = null;
this._addButton = null;
// 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().
@ -457,7 +447,7 @@ Overview.prototype = {
},
_addNewWorkspace: function() {
global.screen.append_new_workspace(false, global.get_current_time());
global.screen.append_new_workspace(false, Main.currentTime());
},
_acceptNewWorkspaceDrop: function(source, dropActor, x, y, time) {

View File

@ -7,22 +7,45 @@ const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Tweener = imports.ui.tweener;
const Signals = imports.signals;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const Calendar = imports.ui.calendar;
const Button = imports.ui.button;
const Main = imports.ui.main;
const StatusMenu = imports.ui.statusMenu;
const PANEL_HEIGHT = 26;
const TRAY_HEIGHT = PANEL_HEIGHT - 1;
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);
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.
const PANEL_BUTTON_COLOR = new Clutter.Color();
PANEL_BUTTON_COLOR.from_pixel(0x00000000);
// Lighten pressed buttons; darkening has no effect on a black background.
const PRESSED_BUTTON_BACKGROUND_COLOR = new Clutter.Color();
PRESSED_BUTTON_BACKGROUND_COLOR.from_pixel(0x324c6ffa);
const DEFAULT_FONT = 'Sans 16px';
const TRAY_PADDING = 0;
// See comments around _recomputeTraySize
const TRAY_SPACING = 14;
const TRAY_SPACING_MIN = 8;
@ -30,21 +53,12 @@ const TRAY_SPACING_MIN = 8;
// Used for the tray icon container with gtk pre-2.16, which doesn't
// fully support tray icon transparency
const TRAY_BACKGROUND_COLOR = new Clutter.Color();
TRAY_BACKGROUND_COLOR.from_pixel(0x0b0b0bff);
TRAY_BACKGROUND_COLOR.from_pixel(0xefefefff);
const TRAY_BORDER_COLOR = new Clutter.Color();
TRAY_BORDER_COLOR.from_pixel(0x00000033);
const TRAY_CORNER_RADIUS = 5;
const TRAY_BORDER_WIDTH = 0;
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
const STANDARD_TRAY_ICON_ORDER = ['keyboard', 'volume', 'bluetooth', 'network', 'battery']
const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
'bluetooth-applet': 'bluetooth',
'gnome-volume-control-applet': 'volume',
'nm-applet': 'network',
'gnome-power-manager': 'battery'
};
function AppPanelMenu() {
this._init();
@ -58,14 +72,24 @@ AppPanelMenu.prototype = {
this._activeSequence = null;
this._startupSequences = {};
this.actor = new St.BoxLayout({ name: 'appMenu' });
this._iconBox = new St.Bin({ name: 'appMenuIcon' });
this.actor.add(this._iconBox);
this._label = new St.Label();
this.actor.add(this._label, { expand: true, y_fill: false });
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 St.BoxLayout();
this.actor.add(this._startupBox);
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;
@ -74,26 +98,34 @@ AppPanelMenu.prototype = {
this.actor.opacity = 192;
}));
let tracker = Shell.WindowTracker.get_default();
tracker.connect('notify::focus-app', Lang.bind(this, this._sync));
tracker.connect('startup-sequence-changed', Lang.bind(this, this._sync));
// For now just resync on all running state changes; this is mainly to handle
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.
tracker.connect('app-running-changed', Lang.bind(this, this._sync));
appMonitor.connect('app-added', Lang.bind(this, this._sync));
appMonitor.connect('app-removed', Lang.bind(this, this._sync));
this._sync();
},
_sync: function() {
let tracker = Shell.WindowTracker.get_default();
let appMonitor = Shell.AppMonitor.get_default();
let focusedApp = tracker.focus_app;
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 = tracker.get_startup_sequences();
let sequences = appMonitor.get_startup_sequences();
if (sequences.length > 0)
lastSequence = sequences[sequences.length - 1];
}
@ -109,22 +141,19 @@ AppPanelMenu.prototype = {
this._focusedApp = focusedApp;
this._activeSequence = lastSequence;
if (this._iconBox.child != null)
this._iconBox.child.destroy();
this._iconBox.remove_all();
this._iconBox.hide();
this._label.set_text('');
this._label.text = '';
if (this._focusedApp != null) {
let icon = this._focusedApp.create_icon_texture(PANEL_ICON_SIZE);
this._iconBox.set_child(icon);
let icon = focusedApp.create_icon_texture(PANEL_ICON_SIZE);
this._iconBox.append(icon, Big.BoxPackFlags.NONE);
this._iconBox.show();
let appName = this._focusedApp.get_name();
// Use _set_text to work around http://bugzilla.openedhand.com/show_bug.cgi?id=1851
this._label.set_text(appName);
this._label.text = focusedApp.get_name();
} else if (this._activeSequence != null) {
let icon = this._activeSequence.create_icon(PANEL_ICON_SIZE);
this._iconBox.set_child(icon);
this._iconBox.append(icon, Big.BoxPackFlags.NONE);
this._iconBox.show();
this._label.set_text(this._activeSequence.get_name());
this._label.text = this._activeSequence.get_name();
}
this.emit('changed');
@ -140,17 +169,30 @@ function Panel() {
Panel.prototype = {
_init : function() {
this.actor = new St.BoxLayout({ name: 'panel' });
this.actor._delegate = this;
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);
this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
this._centerBox = new St.BoxLayout({ name: 'panelCenter' });
this._rightBox = new St.BoxLayout({ name: 'panelRight' });
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 });
/* This box container ensures that the centerBox is positioned in the *absolute*
* center, but can be pushed aside if necessary. */
this._boxContainer = new Shell.GenericContainer();
this.actor.add(this._boxContainer, { expand: true });
this.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);
@ -222,16 +264,13 @@ Panel.prototype = {
this._rightBox.allocate(childBox, flags);
}));
/* Button on the left side of the panel. */
/* Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview". */
let label = new St.Label({ text: _("Activities") });
this.button = new St.Clickable({ name: 'panelActivities',
style_class: 'panel-button',
reactive: true });
this.button.set_child(label);
this.button.height = PANEL_HEIGHT;
/* left side */
this._leftBox.add(this.button);
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
@ -239,22 +278,16 @@ Panel.prototype = {
// multiple times due to an accidental jitter.
this._hotCornerEntered = false;
this._hotCornerEnvirons = new Clutter.Rectangle({ x: 0,
y: 0,
width: 3,
this._hotCornerEnvirons = new Clutter.Rectangle({ width: 3,
height: 3,
opacity: 0,
reactive: true });
this._hotCorner = new Clutter.Rectangle({ x: 0,
y: 0,
width: 1,
this._hotCorner = new Clutter.Rectangle({ width: 1,
height: 1,
opacity: 0,
reactive: true });
this._hotCornerActivationTime = 0;
this._hotCornerEnvirons.connect('leave-event',
Lang.bind(this, this._onHotCornerEnvironsLeft));
// Clicking on the hot corner environs should result in the same bahavior
@ -272,35 +305,29 @@ Panel.prototype = {
this._hotCorner.connect('leave-event',
Lang.bind(this, this._onHotCornerLeft));
this._leftBox.add(this._hotCornerEnvirons);
this._leftBox.add(this._hotCorner);
this._leftBox.append(this._hotCornerEnvirons, Big.BoxPackFlags.FIXED);
this._leftBox.append(this._hotCorner, Big.BoxPackFlags.FIXED);
let appMenu = new AppPanelMenu();
this._leftBox.add(appMenu.actor);
this._leftBox.append(appMenu.actor, Big.BoxPackFlags.NONE);
/* center */
let clockButton = new St.Button({ style_class: "panel-button",
toggle_mode: true,
x_fill: true,
y_fill: true });
this._centerBox.add(clockButton, { y_fill: false });
clockButton.connect('clicked', Lang.bind(this, this._toggleCalendar));
this._clock = new St.Label();
clockButton.set_child(this._clock);
this._calendarPopup = null;
this._clock = new Clutter.Text({ font_name: DEFAULT_FONT,
color: PANEL_FOREGROUND_COLOR,
text: "" });
this._centerBox.append(this._clock, Big.BoxPackFlags.NONE);
/* right */
// 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.CENTER });
this._rightBox.add(trayContainer);
y_align: Big.BoxAlignment.START });
this._rightBox.append(trayContainer, Big.BoxPackFlags.NONE);
let trayBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
height: PANEL_ICON_SIZE,
height: TRAY_HEIGHT,
padding: TRAY_PADDING,
spacing: TRAY_SPACING });
this._trayBox = trayBox;
@ -318,7 +345,14 @@ Panel.prototype = {
trayContainer.append(trayBox, Big.BoxPackFlags.NONE);
this._traymanager = new Shell.TrayManager({ bg_color: TRAY_BACKGROUND_COLOR });
this._traymanager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded));
this._traymanager.connect('tray-icon-added',
Lang.bind(this, 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) {
trayBox.remove_actor(icon);
@ -329,48 +363,58 @@ Panel.prototype = {
}));
this._traymanager.manage_stage(global.stage);
let statusmenu = this._statusmenu = new StatusMenu.StatusMenu();
let statusbutton = new St.Clickable({ name: 'panelStatus',
style_class: 'panel-button',
reactive: true });
statusbutton.set_child(statusmenu.actor);
statusbutton.height = PANEL_HEIGHT;
statusbutton.connect('clicked', function (b, event) {
statusmenu.toggle(event);
// The statusmenu might not pop up if it couldn't get a pointer grab
if (statusmenu.isActive())
statusbutton.active = true;
return true;
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.add(statusbutton);
this._rightBox.append(statusbutton.actor, Big.BoxPackFlags.NONE);
// We get a deactivated event when the popup disappears
this._statusmenu.connect('deactivated', function (sm) {
statusbutton.active = false;
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
// to switch to.
this.button.connect('clicked', Lang.bind(this, function(b, event) {
if (!Main.overview.animationInProgress) {
this._maybeToggleOverviewOnClick();
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
// and to be released when it is exited regardless of how it was triggered.
Main.overview.connect('showing', Lang.bind(this, function() {
this.button.active = true;
this.button.actor.active = true;
}));
Main.overview.connect('hiding', Lang.bind(this, function() {
this.button.active = false;
this.button.actor.active = false;
}));
Main.chrome.addActor(this.actor, { visibleInOverview: true });
Main.chrome.addActor(this.actor);
Main.chrome.setVisibleInOverview(this.actor, true);
// Start the clock
this._updateClock();
@ -385,38 +429,6 @@ Panel.prototype = {
});
},
_onTrayIconAdded: function(o, icon, wmClass) {
let role = STANDARD_TRAY_ICON_IMPLEMENTATIONS[wmClass];
if (!role) {
// Unknown icons go first in undefined order
this._trayBox.prepend(icon, Big.BoxPackFlags.NONE);
} else {
icon._role = role;
// Figure out the index in our well-known order for this icon
let position = STANDARD_TRAY_ICON_ORDER.indexOf(role);
icon._rolePosition = position;
let children = this._trayBox.get_children();
let i;
// Walk children backwards, until we find one that isn't
// well-known, or one where we should follow
for (i = children.length - 1; i >= 0; i--) {
let rolePosition = children[i]._rolePosition;
if (!rolePosition || position > rolePosition) {
this._trayBox.insert_after(icon, children[i], Big.BoxPackFlags.NONE);
break;
}
}
if (i == -1) {
// If we didn't find a position, we must be first
this._trayBox.prepend(icon, Big.BoxPackFlags.NONE);
}
}
// Make sure the trayBox is shown.
this._trayBox.show();
this._recomputeTraySize();
},
// 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.
@ -442,21 +454,10 @@ Panel.prototype = {
return false;
},
_toggleCalendar: function(clockButton) {
if (clockButton.checked) {
if (this._calendarPopup == null)
this._calendarPopup = new CalendarPopup();
this._calendarPopup.show();
} else {
this._calendarPopup.hide();
}
},
_onHotCornerEntered : function() {
if (!this._hotCornerEntered) {
this._hotCornerEntered = true;
if (!Main.overview.animationInProgress) {
this._hotCornerActivationTime = Date.now() / 1000;
Main.overview.toggle();
}
}
@ -465,7 +466,7 @@ Panel.prototype = {
_onHotCornerClicked : function() {
if (!Main.overview.animationInProgress) {
this._maybeToggleOverviewOnClick();
Main.overview.toggle();
}
return false;
},
@ -482,61 +483,5 @@ Panel.prototype = {
this._hotCornerEntered = false;
}
return false;
},
// Toggles the overview unless this is the first click on the Activities button within the HOT_CORNER_ACTIVATION_TIMEOUT time
// of the hot corner being triggered. This check avoids opening and closing the overview if the user both triggered the hot corner
// and clicked the Activities button.
_maybeToggleOverviewOnClick: function() {
if (this._hotCornerActivationTime == 0 || Date.now() / 1000 - this._hotCornerActivationTime > HOT_CORNER_ACTIVATION_TIMEOUT)
Main.overview.toggle();
this._hotCornerActivationTime = 0;
}
};
function CalendarPopup() {
this._init();
}
CalendarPopup.prototype = {
_init: function() {
let panelActor = Main.panel.actor;
this.actor = new St.BoxLayout({ name: 'calendarPopup' });
this.calendar = new Calendar.Calendar();
this.actor.add(this.calendar.actor);
Main.chrome.addActor(this.actor, { visibleInOverview: true,
affectsStruts: false });
this.actor.y = (panelActor.y + panelActor.height - this.actor.height);
},
show: function() {
let panelActor = Main.panel.actor;
// Reset the calendar to today's date
this.calendar.setDate(new Date());
this.actor.x = Math.round(panelActor.x + (panelActor.width - this.actor.width) / 2);
this.actor.lower(panelActor);
this.actor.show();
Tweener.addTween(this.actor,
{ y: panelActor.y + panelActor.height,
time: 0.2,
transition: "easeOutQuad"
});
},
hide: function() {
let panelActor = Main.panel.actor;
Tweener.addTween(this.actor,
{ y: panelActor.y + panelActor.height - this.actor.height,
time: 0.2,
transition: "easeOutQuad",
onComplete: function() { this.actor.hide(); },
onCompleteScope: this
});
}
};

View File

@ -1,574 +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 St = imports.gi.St;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const DND = imports.ui.dnd;
const Main = imports.ui.main;
const Search = imports.ui.search;
const NAUTILUS_PREFS_DIR = '/apps/nautilus/preferences';
const DESKTOP_IS_HOME_KEY = NAUTILUS_PREFS_DIR + '/desktop_is_home_dir';
const PLACES_ICON_SIZE = 16;
/**
* Represents a place object, which is most normally a bookmark entry,
* a mount/volume, or a special place like the Home Folder, Computer, and Network.
*
* @name: String title
* @iconFactory: A JavaScript callback which will create an icon texture given a size parameter
* @launch: A JavaScript callback to launch the entry
*/
function PlaceInfo(id, name, iconFactory, launch) {
this._init(id, name, iconFactory, launch);
}
PlaceInfo.prototype = {
_init: function(id, name, iconFactory, launch) {
this.id = id;
this.name = name;
this._lowerName = name.toLowerCase();
this.iconFactory = iconFactory;
this.launch = launch;
},
matchTerms: function(terms) {
let mtype = Search.MatchType.NONE;
for (let i = 0; i < terms.length; i++) {
let term = terms[i];
let idx = this._lowerName.indexOf(term);
if (idx == 0)
return Search.MatchType.PREFIX;
else if (idx > 0)
mtype = Search.MatchType.SUBSTRING;
}
return mtype;
},
isRemovable: function() {
return false;
}
}
function PlaceDeviceInfo(mount) {
this._init(mount);
}
PlaceDeviceInfo.prototype = {
__proto__: PlaceInfo.prototype,
_init: function(mount) {
this._mount = mount;
this.name = mount.get_name();
this._lowerName = this.name.toLowerCase();
this.id = "mount:" + mount.get_root().get_uri();
},
iconFactory: function(size) {
let icon = this._mount.get_icon();
return Shell.TextureCache.get_default().load_gicon(icon, size);
},
launch: function() {
Gio.app_info_launch_default_for_uri(this._mount.get_root().get_uri(),
global.create_app_launch_context());
},
isRemovable: function() {
return this._mount.can_unmount();
},
remove: function() {
if (!this.isRemovable())
return;
this._mount.unmount(0, null, Lang.bind(this, this._removeFinish), null);
},
_removeFinish: function(o, res, data) {
this._mount.unmount_finish(res);
}
}
function PlacesManager() {
this._init();
}
PlacesManager.prototype = {
_init: function() {
let gconf = Shell.GConf.get_default();
gconf.watch_directory(NAUTILUS_PREFS_DIR);
this._defaultPlaces = [];
this._mounts = [];
this._bookmarks = [];
this._isDesktopHome = false;
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);
this._home = new PlaceInfo('special:home', homeLabel,
function(size) {
return Shell.TextureCache.get_default().load_gicon(homeIcon, size);
},
function() {
Gio.app_info_launch_default_for_uri(homeUri, global.create_app_launch_context());
});
let desktopPath = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP);
let desktopFile = Gio.file_new_for_path (desktopPath);
let desktopUri = desktopFile.get_uri();
let desktopLabel = Shell.util_get_label_for_uri (desktopUri);
let desktopIcon = Shell.util_get_icon_for_uri (desktopUri);
this._desktopMenu = new PlaceInfo('special:desktop', desktopLabel,
function(size) {
return Shell.TextureCache.get_default().load_gicon(desktopIcon, size);
},
function() {
Gio.app_info_launch_default_for_uri(desktopUri, global.create_app_launch_context());
});
this._connect = new PlaceInfo('special:connect', _("Connect to..."),
function (size) {
return Shell.TextureCache.get_default().load_icon_name("applications-internet", size);
},
function () {
new Shell.Process({ args: ['nautilus-connect-server'] }).run();
});
let networkApp = null;
try {
networkApp = Shell.AppSystem.get_default().load_from_desktop_file('gnome-network-scheme.desktop');
} catch(e) {
try {
networkApp = Shell.AppSystem.get_default().load_from_desktop_file('network-scheme.desktop');
} catch(e) {
log("Cannot create \"Network\" item, .desktop file not found or corrupt.");
}
}
if (networkApp != null) {
this._network = new PlaceInfo('special:network', networkApp.get_name(),
function(size) {
return networkApp.create_icon_texture(size);
},
function () {
networkApp.launch();
});
}
this._defaultPlaces.push(this._home);
if (!this._isDesktopHome)
this._defaultPlaces.push(this._desktopMenu);
if (this._network)
this._defaultPlaces.push(this._network);
this._defaultPlaces.push(this._connect);
/*
* Show devices, code more or less ported from nautilus-places-sidebar.c
*/
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();
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();
this._updateDesktopMenuVisibility();
gconf.connect('changed::' + DESKTOP_IS_HOME_KEY, Lang.bind(this, this._updateDesktopMenuVisibility));
},
_updateDevices: function() {
this._mounts = [];
/* 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]);
}
/* We emit two signals, one for a generic 'all places' update
* and the other for one specific to mounts. We do this because
* clients like PlaceDisplay may only care about places in general
* being updated while clients like DashPlaceDisplay care which
* specific type of place got updated.
*/
this.emit('mounts-updated');
this.emit('places-updated');
},
_reloadBookmarks: function() {
this._bookmarks = [];
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 PlaceInfo('bookmark:' + bookmark, label,
function(size) {
return Shell.TextureCache.get_default().load_gicon(icon, size);
},
function() {
Gio.app_info_launch_default_for_uri(bookmark, global.create_app_launch_context());
});
this._bookmarks.push(item);
}
/* See comment in _updateDevices for explanation why there are two signals. */
this.emit('bookmarks-updated');
this.emit('places-updated');
},
_updateDesktopMenuVisibility: function() {
let gconf = Shell.GConf.get_default();
this._isDesktopHome = gconf.get_boolean(DESKTOP_IS_HOME_KEY);
/* See comment in _updateDevices for explanation why there are two signals. */
this.emit('defaults-updated');
this.emit('places-updated');
},
_addMount: function(mount) {
let devItem = new PlaceDeviceInfo(mount);
this._mounts.push(devItem);
},
getAllPlaces: function () {
return this.getDefaultPlaces().concat(this.getBookmarks(), this.getMounts());
},
getDefaultPlaces: function () {
return this._defaultPlaces;
},
getBookmarks: function () {
return this._bookmarks;
},
getMounts: function () {
return this._mounts;
},
_lookupById: function(sourceArray, id) {
for (let i = 0; i < sourceArray.length; i++) {
let place = sourceArray[i];
if (place.id == id)
return place;
}
return null;
},
lookupPlaceById: function(id) {
let colonIdx = id.indexOf(':');
let type = id.substring(0, colonIdx);
let sourceArray = null;
if (type == 'special')
sourceArray = this._defaultPlaces;
else if (type == 'mount')
sourceArray = this._mounts;
else if (type == 'bookmark')
sourceArray = this._bookmarks;
return this._lookupById(sourceArray, id);
}
};
Signals.addSignalMethods(PlacesManager.prototype);
/**
* An entry in the places menu.
* @info The corresponding PlaceInfo to populate this entry.
*/
function DashPlaceDisplayItem(info) {
this._init(info);
}
DashPlaceDisplayItem.prototype = {
_init: function(info) {
this.name = info.name;
this._info = info;
this._icon = info.iconFactory(PLACES_ICON_SIZE);
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
spacing: 4 });
let text = new St.Button({ style_class: 'places-item',
label: info.name,
x_align: St.Align.START });
text.connect('clicked', Lang.bind(this, this._onClicked));
let iconBox = new St.Bin({ child: this._icon, reactive: true });
iconBox.connect('button-release-event',
Lang.bind(this, this._onClicked));
this.actor.append(iconBox, Big.BoxPackFlags.NONE);
this.actor.append(text, Big.BoxPackFlags.EXPAND);
if (info.isRemovable()) {
let removeIcon = Shell.TextureCache.get_default().load_icon_name ('media-eject', PLACES_ICON_SIZE);
let removeIconBox = new St.Button({ child: removeIcon,
reactive: true });
this.actor.append(removeIconBox, Big.BoxPackFlags.NONE);
removeIconBox.connect('clicked',
Lang.bind(this, function() {
this._info.remove();
}));
}
this.actor._delegate = this;
let draggable = DND.makeDraggable(this.actor);
},
_onClicked: function(b) {
this._info.launch();
Main.overview.hide();
},
getDragActorSource: function() {
return this._icon;
},
getDragActor: function(stageX, stageY) {
return this._info.iconFactory(PLACES_ICON_SIZE);
},
//// Drag and drop methods ////
shellWorkspaceLaunch: function() {
this._info.launch();
}
};
function DashPlaceDisplay() {
this._init();
}
DashPlaceDisplay.prototype = {
_init: function() {
// Places is divided semi-arbitrarily into left and right; a grid would
// look better in that there would be an even number of items left+right,
// but it seems like we want some sort of differentiation between actions
// like "Connect to server..." and regular folders
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
spacing: 4 });
this._leftBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL });
this.actor.append(this._leftBox, Big.BoxPackFlags.EXPAND);
this._rightBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL });
this.actor.append(this._rightBox, Big.BoxPackFlags.EXPAND);
// Subdivide left into actions and devices
this._actionsBox = new St.BoxLayout({ style_class: 'places-actions',
vertical: true });
this._devBox = new St.BoxLayout({ style_class: 'places-actions',
name: 'placesDevices',
vertical: true });
this._dirsBox = new St.BoxLayout({ style_class: 'places-actions',
vertical: true });
this._leftBox.append(this._actionsBox, Big.BoxPackFlags.NONE);
this._leftBox.append(this._devBox, Big.BoxPackFlags.NONE);
this._rightBox.append(this._dirsBox, Big.BoxPackFlags.NONE);
Main.placesManager.connect('defaults-updated', Lang.bind(this, this._updateDefaults));
Main.placesManager.connect('bookmarks-updated', Lang.bind(this, this._updateBookmarks));
Main.placesManager.connect('mounts-updated', Lang.bind(this, this._updateMounts));
this._updateDefaults();
this._updateMounts();
this._updateBookmarks();
},
_updateDefaults: function() {
this._actionsBox.destroy_children();
let places = Main.placesManager.getDefaultPlaces();
for (let i = 0; i < places.length; i++)
this._actionsBox.add(new DashPlaceDisplayItem(places[i]).actor);
},
_updateMounts: function() {
this._devBox.destroy_children();
let places = Main.placesManager.getMounts();
for (let i = 0; i < places.length; i++)
this._devBox.add(new DashPlaceDisplayItem(places[i]).actor);
},
_updateBookmarks: function() {
this._dirsBox.destroy_children();
let places = Main.placesManager.getBookmarks();
for (let i = 0; i < places.length; i ++)
this._dirsBox.add(new DashPlaceDisplayItem(places[i]).actor);
}
};
Signals.addSignalMethods(DashPlaceDisplay.prototype);
function PlaceSearchProvider() {
this._init();
}
PlaceSearchProvider.prototype = {
__proto__: Search.SearchProvider.prototype,
_init: function() {
Search.SearchProvider.prototype._init.call(this, _("PLACES"));
},
getResultMeta: function(resultId) {
let placeInfo = Main.placesManager.lookupPlaceById(resultId);
if (!placeInfo)
return null;
return { 'id': resultId,
'name': placeInfo.name,
'icon': placeInfo.iconFactory(Search.RESULT_ICON_SIZE) };
},
activateResult: function(id) {
let placeInfo = Main.placesManager.lookupPlaceById(id);
placeInfo.launch();
},
_compareResultMeta: function (idA, idB) {
let infoA = Main.placesManager.lookupPlaceById(idA);
let infoB = Main.placesManager.lookupPlaceById(idB);
return infoA.name.localeCompare(infoB.name);
},
_searchPlaces: function(places, terms) {
let multipleResults = [];
let prefixResults = [];
let substringResults = [];
terms = terms.map(String.toLowerCase);
for (let i = 0; i < places.length; i++) {
let place = places[i];
let mtype = place.matchTerms(terms);
if (mtype == Search.MatchType.MULTIPLE)
multipleResults.push(place.id);
else if (mtype == Search.MatchType.PREFIX)
prefixResults.push(place.id);
else if (mtype == Search.MatchType.SUBSTRING)
substringResults.push(place.id);
}
multipleResults.sort(this._compareResultMeta);
prefixResults.sort(this._compareResultMeta);
substringResults.sort(this._compareResultMeta);
return multipleResults.concat(prefixResults.concat(substringResults));
},
getInitialResultSet: function(terms) {
let places = Main.placesManager.getAllPlaces();
return this._searchPlaces(places, terms);
},
getSubsearchResultSet: function(previousResults, terms) {
let places = previousResults.map(function (id) { return Main.placesManager.lookupPlaceById(id); });
return this._searchPlaces(places, terms);
}
}

278
js/ui/places.js Normal file
View File

@ -0,0 +1,278 @@
/* -*- 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

@ -2,7 +2,6 @@
const Big = imports.gi.Big;
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
@ -25,144 +24,6 @@ const DIALOG_WIDTH = 320;
const DIALOG_PADDING = 6;
const ICON_SIZE = 24;
const ICON_BOX_SIZE = 36;
const MAX_FILE_DELETED_BEFORE_INVALID = 10;
function CommandCompleter() {
this._init();
}
CommandCompleter.prototype = {
_init : function() {
this._changedCount = 0;
this._paths = GLib.getenv('PATH').split(':');
this._valid = false;
this._updateInProgress = false;
this._childs = new Array(this._paths.length);
this._monitors = new Array(this._paths.length);
for (let i = 0; i < this._paths.length; i++) {
this._childs[i] = [];
let file = Gio.file_new_for_path(this._paths[i]);
let info = file.query_info(Gio.FILE_ATTRIBUTE_STANDARD_TYPE, Gio.FileQueryInfoFlags.NONE, null);
if (info.get_attribute_uint32(Gio.FILE_ATTRIBUTE_STANDARD_TYPE) != Gio.FileType.DIRECTORY)
continue;
this._paths[i] = file.get_path();
this._monitors[i] = file.monitor_directory(Gio.FileMonitorFlags.NONE, null);
if (this._monitors[i] != null) {
this._monitors[i].connect("changed", Lang.bind(this, this._onChanged));
}
}
this._update(0);
},
_onGetEnumerateComplete : function(obj, res) {
this._enumerator = obj.enumerate_children_finish(res);
this._enumerator.next_files_async(100, GLib.PRIORITY_LOW, null, Lang.bind(this, this._onNextFileComplete), null);
},
_onNextFileComplete : function(obj, res) {
let files = obj.next_files_finish(res);
for (let i = 0; i < files.length; i++) {
this._childs[this._i].push(files[i].get_name());
}
if (files.length) {
this._enumerator.next_files_async(100, GLib.PRIORITY_LOW, null, Lang.bind(this, this._onNextFileComplete), null);
} else {
this._enumerator.close(null);
this._enumerator = null;
this._update(this._i + 1);
}
},
update : function() {
if (this._valid)
return;
this._update(0);
},
_update : function(i) {
if (i == 0 && this._updateInProgress)
return;
this._updateInProgress = true;
this._changedCount = 0;
this._i = i;
if (i >= this._paths.length) {
this._valid = true;
this._updateInProgress = false;
return;
}
let file = Gio.file_new_for_path(this._paths[i]);
this._childs[this._i] = [];
file.enumerate_children_async(Gio.FILE_ATTRIBUTE_STANDARD_NAME, Gio.FileQueryInfoFlags.NONE, GLib.PRIORITY_LOW, null, Lang.bind(this, this._onGetEnumerateComplete), null);
},
_onChanged : function(m, f, of, type) {
if (!this._valid)
return;
let path = f.get_parent().get_path();
let k = undefined;
for (let i = 0; i < this._paths.length; i++) {
if (this._paths[i] == path)
k = i;
}
if (k === undefined) {
return;
}
if (type == Gio.FileMonitorEvent.CREATED) {
this._childs[k].push(f.get_basename());
}
if (type == Gio.FileMonitorEvent.DELETED) {
this._changedCount++;
if (this._changedCount > MAX_FILE_DELETED_BEFORE_INVALID) {
this._valid = false;
}
let name = f.get_basename();
this._childs[k] = this._childs[k].filter(function(e) {
return e != name;
});
}
if (type == Gio.FileMonitorEvent.UNMOUNTED) {
this._childs[k] = [];
}
},
getCompletion: function(text) {
let common = "";
let notInit = true;
if (!this._valid) {
this._update(0);
return common;
}
function _getCommon(s1, s2) {
let k = 0;
for (; k < s1.length && k < s2.length; k++) {
if (s1[k] != s2[k])
break;
}
if (k == 0)
return "";
return s1.substr(0, k);
}
function _hasPrefix(s1, prefix) {
return s1.indexOf(prefix) == 0;
}
for (let i = 0; i < this._childs.length; i++) {
for (let k = 0; k < this._childs[i].length; k++) {
if (!_hasPrefix(this._childs[i][k], text))
continue;
if (notInit) {
common = this._childs[i][k];
notInit = false;
}
common = _getCommon(common, this._childs[i][k]);
}
}
if (common.length)
return common.substr(text.length);
return common;
}
};
function RunDialog() {
this._init();
@ -199,26 +60,24 @@ RunDialog.prototype = {
// All actors are inside _group. We create it initially
// hidden then show it in show()
this._group = new Clutter.Group({ visible: false,
x: 0,
y: 0,
width: global.screen_width,
height: global.screen_height });
this._group = new Clutter.Group({ visible: false });
global.stage.add_actor(this._group);
let lightbox = new Lightbox.Lightbox(this._group);
this._lightbox = new Lightbox.Lightbox(this._group);
this._boxH = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
x_align: Big.BoxAlignment.CENTER,
y_align: Big.BoxAlignment.CENTER });
let boxH = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
x_align: Big.BoxAlignment.CENTER,
y_align: Big.BoxAlignment.CENTER,
width: global.screen_width,
height: global.screen_height });
this._group.add_actor(this._boxH);
lightbox.highlight(this._boxH);
this._group.add_actor(boxH);
this._lightbox.highlight(boxH);
let boxV = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
y_align: Big.BoxAlignment.CENTER });
this._boxH.append(boxV, Big.BoxPackFlags.NONE);
boxH.append(boxV, Big.BoxPackFlags.NONE);
let dialogBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
@ -228,7 +87,7 @@ RunDialog.prototype = {
padding: DIALOG_PADDING,
width: DIALOG_WIDTH });
this._boxH.append(dialogBox, Big.BoxPackFlags.NONE);
boxH.append(dialogBox, Big.BoxPackFlags.NONE);
let label = new Clutter.Text({ color: BOX_TEXT_COLOR,
font_name: '18px Sans',
@ -276,57 +135,17 @@ RunDialog.prototype = {
this.close();
}));
this._pathCompleter = new Gio.FilenameCompleter();
this._commandCompleter = new CommandCompleter();
this._group.connect('notify::visible', Lang.bind(this._commandCompleter, this._commandCompleter.update));
this._entry.connect('key-press-event', Lang.bind(this, function(o, e) {
let symbol = e.get_key_symbol();
if (symbol == Clutter.Escape) {
this.close();
return true;
}
if (symbol == Clutter.slash) {
// Need preload data before get completion. GFilenameCompleter load content of parent directory.
// Parent directory for /usr/include/ is /usr/. So need to add fake name('a').
let text = o.get_text().concat('/a');
let prefix;
if (text.lastIndexOf(' ') == -1)
prefix = text;
else
prefix = text.substr(text.lastIndexOf(' ') + 1);
this._getCompletion(prefix);
return false;
}
if (symbol == Clutter.Tab) {
let text = o.get_text();
let prefix;
if (text.lastIndexOf(' ') == -1)
prefix = text;
else
prefix = text.substr(text.lastIndexOf(' ') + 1);
let postfix = this._getCompletion(prefix);
if (postfix != null && postfix.length > 0) {
o.insert_text(postfix, -1);
o.set_cursor_position(text.length + postfix.length);
if (postfix[postfix.length - 1] == '/')
this._getCompletion(text + postfix + 'a');
}
return true;
}
return false;
}));
},
_getCompletion : function(text) {
if (text.indexOf('/') != -1) {
return this._pathCompleter.get_completion_suffix(text);
} else {
return this._commandCompleter.getCompletion(text);
}
},
_run : function(command) {
this._commandError = false;
let f;
if (this._enableInternalCommands)
f = this._internalCommands[command];
@ -336,6 +155,7 @@ RunDialog.prototype = {
f();
} else if (command) {
try {
this._commandError = false;
let [ok, len, args] = GLib.shell_parse_argv(command);
let p = new Shell.Process({'args' : args});
p.run();
@ -348,7 +168,7 @@ RunDialog.prototype = {
* We are only interested in the actual error, so parse that out.
*/
let m = /.+\((.+)\)/.exec(e);
let errorStr = _("Execution of '%s' failed:").format(command) + "\n" + m[1];
let errorStr = "Execution of '" + command + "' failed:\n" + m[1];
this._errorMessage.set_text(errorStr);
this._errorBox.show();
}
@ -362,12 +182,6 @@ RunDialog.prototype = {
if (!Main.pushModal(this._group))
return;
// Position the dialog on the current monitor
let monitor = global.get_focus_monitor();
this._boxH.set_position(monitor.x, monitor.y);
this._boxH.set_size(monitor.width, monitor.height);
this._isOpen = true;
this._group.show();
@ -384,7 +198,7 @@ RunDialog.prototype = {
this._commandError = false;
this._group.hide();
this._entry.set_text('');
this._entry.text = '';
Main.popModal(this._group);
}

View File

@ -1,272 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Signals = imports.signals;
const St = imports.gi.St;
const RESULT_ICON_SIZE = 24;
// Not currently referenced by the search API, but
// this enumeration can be useful for provider
// implementations.
const MatchType = {
NONE: 0,
MULTIPLE: 1,
PREFIX: 2,
SUBSTRING: 3
};
function SearchResultDisplay(provider) {
this._init(provider);
}
SearchResultDisplay.prototype = {
_init: function(provider) {
this.provider = provider;
this.actor = null;
this.selectionIndex = -1;
},
/**
* renderResults:
* @results: List of identifier strings
* @terms: List of search term strings
*
* Display the given search matches which resulted
* from the given terms. It's expected that not
* all results will fit in the space for the container
* actor; in this case, show as many as makes sense
* for your result type.
*
* The terms are useful for search match highlighting.
*/
renderResults: function(results, terms) {
throw new Error("not implemented");
},
/**
* clear:
* Remove all results from this display and reset the selection index.
*/
clear: function() {
this.actor.get_children().forEach(function (actor) { actor.destroy(); });
this.selectionIndex = -1;
},
/**
* getSelectionIndex:
*
* Returns the index of the selected actor, or -1 if none.
*/
getSelectionIndex: function() {
return this.selectionIndex;
},
/**
* getVisibleResultCount:
*
* Returns: The number of actors visible.
*/
getVisibleResultCount: function() {
throw new Error("not implemented");
},
/**
* selectIndex:
* @index: Integer index
*
* Move selection to the given index.
* Return true if successful, false if no more results
* available.
*/
selectIndex: function() {
throw new Error("not implemented");
}
};
/**
* SearchProvider:
*
* Subclass this object to add a new result type
* to the search system, then call registerProvider()
* in SearchSystem with an instance.
*/
function SearchProvider(title) {
this._init(title);
}
SearchProvider.prototype = {
_init: function(title) {
this.title = title;
},
/**
* getInitialResultSet:
* @terms: Array of search terms, treated as logical OR
*
* Called when the user first begins a search (most likely
* therefore a single term of length one or two), or when
* a new term is added.
*
* Should return an array of result identifier strings representing
* items which match the given search terms. This
* is expected to be a substring match on the metadata for a given
* item. Ordering of returned results is up to the discretion of the provider,
* but you should follow these heruistics:
*
* * Put items which match multiple search terms before single matches
* * Put items which match on a prefix before non-prefix substring matches
*
* This function should be fast; do not perform unindexed full-text searches
* or network queries.
*/
getInitialResultSet: function(terms) {
throw new Error("not implemented");
},
/**
* getSubsearchResultSet:
* @previousResults: Array of item identifiers
* @newTerms: Updated search terms
*
* Called when a search is performed which is a "subsearch" of
* the previous search; i.e. when every search term has exactly
* one corresponding term in the previous search which is a prefix
* of the new term.
*
* This allows search providers to only search through the previous
* result set, rather than possibly performing a full re-query.
*/
getSubsearchResultSet: function(previousResults, newTerms) {
throw new Error("not implemented");
},
/**
* getResultInfo:
* @id: Result identifier string
*
* Return an object with 'id', 'name', (both strings) and 'icon' (Clutter.Texture)
* properties which describe the given search result.
*/
getResultMeta: function(id) {
throw new Error("not implemented");
},
/**
* createResultContainer:
*
* Search providers may optionally override this to render their
* results in a custom fashion. The default implementation
* will create a vertical list.
*
* Returns: An instance of SearchResultDisplay.
*/
createResultContainerActor: function() {
return null;
},
/**
* createResultActor:
* @resultMeta: Object with result metadata
* @terms: Array of search terms, should be used for highlighting
*
* Search providers may optionally override this to render a
* particular serch result in a custom fashion. The default
* implementation will show the icon next to the name.
*
* The actor should be an instance of St.Widget, with the style class
* 'dash-search-result-content'.
*/
createResultActor: function(resultMeta, terms) {
return null;
},
/**
* activateResult:
* @id: Result identifier string
*
* Called when the user chooses a given result.
*/
activateResult: function(id) {
throw new Error("not implemented");
},
/**
* expandSearch:
*
* Called when the user clicks on the header for this
* search section. Should typically launch an external program
* displaying search results for that item type.
*/
expandSearch: function(terms) {
throw new Error("not implemented");
}
}
Signals.addSignalMethods(SearchProvider.prototype);
function SearchSystem() {
this._init();
}
SearchSystem.prototype = {
_init: function() {
this._providers = [];
this.reset();
},
registerProvider: function (provider) {
this._providers.push(provider);
},
getProviders: function() {
return this._providers;
},
getTerms: function() {
return this._previousTerms;
},
reset: function() {
this._previousTerms = [];
this._previousResults = [];
},
updateSearch: function(searchString) {
searchString = searchString.replace(/^\s+/g, "").replace(/\s+$/g, "");
if (searchString == '')
return null;
let terms = searchString.split(/\s+/);
let isSubSearch = terms.length == this._previousTerms.length;
if (isSubSearch) {
for (let i = 0; i < terms.length; i++) {
if (terms[i].indexOf(this._previousTerms[i]) != 0) {
isSubSearch = false;
break;
}
}
}
let results = [];
if (isSubSearch) {
for (let i = 0; i < this._previousResults.length; i++) {
let [provider, previousResults] = this._previousResults[i];
let providerResults = provider.getSubsearchResultSet(previousResults, terms);
if (providerResults.length > 0)
results.push([provider, providerResults]);
}
} else {
for (let i = 0; i < this._providers.length; i++) {
let provider = this._providers[i];
let providerResults = provider.getInitialResultSet(terms);
if (providerResults.length > 0)
results.push([provider, providerResults]);
}
}
this._previousTerms = terms;
this._previousResults = results;
return results;
}
}
Signals.addSignalMethods(SearchSystem.prototype);

View File

@ -48,9 +48,6 @@ GnomeShell.prototype = {
let success;
try {
returnValue = JSON.stringify(eval(code));
// A hack; DBus doesn't have null/undefined
if (returnValue == undefined)
returnValue = "";
success = true;
} catch (e) {
returnValue = JSON.stringify(e);

View File

@ -69,8 +69,6 @@ Sidebar.prototype = {
Lang.bind(this, this._expandedChanged));
this._gconf.connect('changed::sidebar/visible',
Lang.bind(this, this._visibleChanged));
this._adjustPosition();
},
addWidget: function(widget) {
@ -84,14 +82,6 @@ Sidebar.prototype = {
this.box.append(widgetBox.actor, Big.BoxPackFlags.NONE);
this._widgets.push(widgetBox);
this._adjustPosition();
},
_adjustPosition: function() {
let primary=global.get_primary_monitor();
this.actor.y = Math.max(primary.y + Panel.PANEL_HEIGHT,primary.height/2 - this.actor.height/2);
this.actor.x = primary.x;
},
_visibleChanged: function() {

View File

@ -1,297 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const DBus = imports.dbus;
const Gdm = imports.gi.Gdm;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Signals = imports.signals;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const Panel = imports.ui.panel;
// Adapted from gdm/gui/user-switch-applet/applet.c
//
// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
// Copyright (C) 2008,2009 Red Hat, Inc.
const SIDEBAR_VISIBLE_KEY = 'sidebar/visible';
function StatusMenu() {
this._init();
}
StatusMenu.prototype = {
_init: function() {
this._gdm = Gdm.UserManager.ref_default();
this._user = this._gdm.get_user(GLib.get_user_name());
this._presence = new GnomeSessionPresence();
this.actor = new St.BoxLayout({ name: 'StatusMenu' });
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._iconBox = new St.Bin();
this.actor.add(this._iconBox, { y_align: St.Align.MIDDLE });
let textureCache = Shell.TextureCache.get_default();
// FIXME: these icons are all wrong (likewise in createSubMenu)
this._availableIcon = textureCache.load_icon_name('gtk-yes', 16);
this._busyIcon = textureCache.load_icon_name('gtk-no', 16);
this._invisibleIcon = textureCache.load_icon_name('gtk-close', 16);
this._idleIcon = textureCache.load_icon_name('gtk-media-pause', 16);
this._presence.connect('StatusChanged', Lang.bind(this, this._updatePresenceIcon));
this._presence.getStatus(Lang.bind(this, this._updatePresenceIcon));
this._name = new St.Label({ text: this._user.get_real_name() });
this.actor.add(this._name, { expand: true, y_align: St.Align.MIDDLE });
this._userNameChangedId = this._user.connect('notify::display-name', Lang.bind(this, this._updateUserName));
this._createSubMenu();
this._gdm.connect('users-loaded', Lang.bind(this, this._updateSwitchUser));
this._gdm.connect('user-added', Lang.bind(this, this._updateSwitchUser));
this._gdm.connect('user-removed', Lang.bind(this, this._updateSwitchUser));
},
_onDestroy: function() {
this._user.disconnect(this._userNameChangedId);
},
_updateUserName: function() {
this._name.set_text(this._user.get_real_name());
},
_updateSwitchUser: function() {
let users = this._gdm.list_users();
if (users.length > 1)
this._loginScreenItem.show();
else
this._loginScreenItem.hide();
},
_updatePresenceIcon: function(presence, status) {
if (status == GnomeSessionPresenceStatus.AVAILABLE)
this._iconBox.child = this._availableIcon;
else if (status == GnomeSessionPresenceStatus.BUSY)
this._iconBox.child = this._busyIcon;
else if (status == GnomeSessionPresenceStatus.INVISIBLE)
this._iconBox.child = this._invisibleIcon;
else
this._iconBox.child = this._idleIcon;
},
// The menu
_createImageMenuItem: function(label, iconName, forceIcon) {
let image = new Gtk.Image();
let item = new Gtk.ImageMenuItem({ label: label,
image: image,
always_show_image: forceIcon == true });
item.connect('style-set', Lang.bind(this,
function() {
image.set_from_icon_name(iconName, Gtk.IconSize.MENU);
}));
return item;
},
_createSubMenu: function() {
this._menu = new Gtk.Menu();
this._menu.connect('deactivate', Lang.bind(this, function() { this.emit('deactivated'); }));
let item;
item = this._createImageMenuItem(_('Available'), 'gtk-yes', true);
item.connect('activate', Lang.bind(this, this._setPresenceStatus, GnomeSessionPresenceStatus.AVAILABLE));
this._menu.append(item);
item.show();
item = this._createImageMenuItem(_('Busy'), 'gtk-no', true);
item.connect('activate', Lang.bind(this, this._setPresenceStatus, GnomeSessionPresenceStatus.BUSY));
this._menu.append(item);
item.show();
item = this._createImageMenuItem(_('Invisible'), 'gtk-close', true);
item.connect('activate', Lang.bind(this, this._setPresenceStatus, GnomeSessionPresenceStatus.INVISIBLE));
this._menu.append(item);
item.show();
item = new Gtk.SeparatorMenuItem();
this._menu.append(item);
item.show();
item = this._createImageMenuItem(_('Account Information...'), 'user-info');
item.connect('activate', Lang.bind(this, this._onAccountInformationActivate));
this._menu.append(item);
item.show();
let gconf = Shell.GConf.get_default();
item = new Gtk.CheckMenuItem({ label: _('Sidebar'),
active: gconf.get_boolean(SIDEBAR_VISIBLE_KEY) });
item.connect('activate', Lang.bind(this,
function() {
gconf.set_boolean(SIDEBAR_VISIBLE_KEY, this._sidebarItem.active);
}));
this._menu.append(item);
item.show();
this._sidebarItem = item;
item = this._createImageMenuItem(_('System Preferences...'), 'preferences-desktop');
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
this._menu.append(item);
item.show();
item = new Gtk.SeparatorMenuItem();
this._menu.append(item);
item.show();
item = this._createImageMenuItem(_('Lock Screen'), 'system-lock-screen');
item.connect('activate', Lang.bind(this, this._onLockScreenActivate));
this._menu.append(item);
item.show();
item = this._createImageMenuItem(_('Switch User'), 'system-users');
item.connect('activate', Lang.bind(this, this._onLoginScreenActivate));
this._menu.append(item);
item.show();
this._loginScreenItem = item;
item = this._createImageMenuItem(_('Log Out...'), 'system-log-out');
item.connect('activate', Lang.bind(this, this._onQuitSessionActivate));
this._menu.append(item);
item.show();
item = this._createImageMenuItem(_('Shut Down...'), 'system-shutdown');
item.connect('activate', Lang.bind(this, this._onShutDownActivate));
this._menu.append(item);
item.show();
},
_setPresenceStatus: function(item, status) {
this._presence.setStatus(status);
},
_onAccountInformationActivate: function() {
this._spawn(['gnome-about-me']);
},
_onPreferencesActivate: function() {
this._spawn(['gnome-control-center']);
},
_onLockScreenActivate: function() {
this._spawn(['gnome-screensaver-command', '--lock']);
},
_onLoginScreenActivate: function() {
this._gdm.goto_login_session();
this._onLockScreenActivate();
},
_onQuitSessionActivate: function() {
this._spawn(['gnome-session-save', '--logout-dialog']);
},
_onShutDownActivate: function() {
this._spawn(['gnome-session-save', '--shutdown-dialog']);
},
_spawn: function(args) {
// FIXME: once Shell.Process gets support for signalling
// errors we should pop up an error dialog or something here
// on failure
let p = new Shell.Process({'args' : args});
p.run();
},
// shell_status_menu_toggle:
// @event: event causing the toggle
//
// 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
// isActive().
toggle: function(event) {
if (this._menu.visible)
this._menu.popdown();
else {
// We don't want to overgrab a Mutter grab with the grab
// that GTK+ uses on menus.
if (global.display_is_grabbed())
return;
let [menuWidth, menuHeight] = this._menu.get_size_request ();
let panel;
for (panel = this.actor; panel; panel = panel.get_parent()) {
if (panel._delegate instanceof Panel.Panel)
break;
}
let [panelX, panelY] = panel.get_transformed_position();
let [panelWidth, panelHeight] = panel.get_transformed_size();
let menuX = Math.round(panelX + panelWidth - menuWidth);
let menuY = Math.round(panelY + panelHeight);
Shell.popup_menu(this._menu, event.get_button(), event.get_time(),
menuX, menuY);
}
},
// isActive:
//
// Gets whether the menu is currently popped up
//
// Return value: %true if the menu is currently popped up
isActive: function() {
return this._menu.visible;
}
};
Signals.addSignalMethods(StatusMenu.prototype);
const GnomeSessionPresenceIface = {
name: 'org.gnome.SessionManager.Presence',
methods: [{ name: 'SetStatus',
inSignature: 'u' }],
properties: [{ name: 'status',
signature: 'u',
access: 'readwrite' }],
signals: [{ name: 'StatusChanged',
inSignature: 'u' }]
};
const GnomeSessionPresenceStatus = {
AVAILABLE: 0,
INVISIBLE: 1,
BUSY: 2,
IDLE: 3
};
function GnomeSessionPresence() {
this._init();
}
GnomeSessionPresence.prototype = {
_init: function() {
DBus.session.proxifyObject(this, 'org.gnome.SessionManager', '/org/gnome/SessionManager/Presence', this);
this.connect('StatusChanged', Lang.bind(this, function (proxy, status) { this.status = status; }));
},
getStatus: function(callback) {
this.GetRemote('status', Lang.bind(this,
function(status, ex) {
if (!ex)
callback(this, status);
}));
},
setStatus: function(status) {
this.SetStatusRemote(status);
}
};
DBus.proxifyPrototype(GnomeSessionPresence.prototype, GnomeSessionPresenceIface);

View File

@ -12,7 +12,6 @@ const Signals = imports.signals;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const AppFavorites = imports.ui.appFavorites;
const DocInfo = imports.misc.docInfo;
const COLLAPSED_WIDTH = 24;
@ -319,9 +318,12 @@ AppsWidget.prototype = {
this.collapsedActor = new Big.Box({ spacing: 2});
let appSystem = Shell.AppSystem.get_default();
let apps = AppFavorites.getAppFavorites().getFavorites();
let apps = appSystem.get_favorites();
for (let i = 0; i < apps.length; i++) {
this.addItem(new AppsWidgetInfo(apps[i]));
let app = appSystem.lookup_cached_app(apps[i]);
if (!app)
continue;
this.addItem(new AppsWidgetInfo(app));
}
}
};
@ -354,7 +356,8 @@ RecentDocsWidget.prototype = {
for (i = 0; i < docs.length; i++) {
let docInfo = new DocInfo.DocInfo (docs[i]);
items.push(docInfo);
if (docInfo.exists())
items.push(docInfo);
}
items.sort(function (a,b) { return b.timestamp - a.timestamp; });

View File

@ -331,7 +331,7 @@ WidgetBox.prototype = {
onCompleteScope: this });
this.state = this._widget.state = Widget.STATE_POPPING_OUT;
Main.chrome.trackActor(this._hbox, { affectsStruts: false });
Main.chrome.addInputRegionActor(this._hbox);
},
_popOutComplete: function() {
@ -370,7 +370,7 @@ WidgetBox.prototype = {
this._egroup.hide();
this._ebox.x = 0;
Main.chrome.untrackActor(this._hbox);
Main.chrome.removeInputRegionActor(this._hbox);
},
destroy: function() {

View File

@ -274,7 +274,7 @@ WindowManager.prototype = {
_startAppSwitcher : function(shellwm, binding, window, backwards) {
let tabPopup = new AltTab.AltTabPopup();
if (!tabPopup.show(backwards))
if (!tabPopup.show(backwards ? -1 : 1))
tabPopup.destroy();
}
};

View File

@ -3,14 +3,12 @@
const Big = imports.gi.Big;
const Clutter = imports.gi.Clutter;
const GdkPixbuf = imports.gi.GdkPixbuf;
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 Shell = imports.gi.Shell;
const St = imports.gi.St;
const Signals = imports.signals;
const DND = imports.ui.dnd;
@ -22,6 +20,10 @@ const Tweener = imports.ui.tweener;
const FOCUS_ANIMATION_TIME = 0.15;
const WINDOWCLONE_BG_COLOR = new Clutter.Color();
WINDOWCLONE_BG_COLOR.from_pixel(0x000000f0);
const WINDOWCLONE_TITLE_COLOR = new Clutter.Color();
WINDOWCLONE_TITLE_COLOR.from_pixel(0xffffffff);
const FRAME_COLOR = new Clutter.Color();
FRAME_COLOR.from_pixel(0xffffffff);
@ -109,7 +111,7 @@ WindowClone.prototype = {
this.origX = realWindow.x;
this.origY = realWindow.y;
this._stackAbove = null;
this._title = null;
this.actor.connect('button-release-event',
Lang.bind(this, this._onButtonRelease));
@ -127,20 +129,24 @@ WindowClone.prototype = {
this._draggable.connect('drag-begin', Lang.bind(this, this._onDragBegin));
this._draggable.connect('drag-end', Lang.bind(this, this._onDragEnd));
this._inDrag = false;
this._zooming = false;
},
setStackAbove: function (actor) {
this._stackAbove = actor;
if (this._inDrag || this._zooming)
// We'll fix up the stack after the drag/zooming
return;
this.actor.raise(this._stackAbove);
setVisibleWithChrome: function(visible) {
if (visible) {
this.actor.show();
if (this._title)
this._title.show();
} else {
this.actor.hide();
if (this._title)
this._title.hide();
}
},
destroy: function () {
this.actor.destroy();
if (this._title)
this._title.destroy();
},
_onEnter: function (actor, event) {
@ -150,6 +156,9 @@ WindowClone.prototype = {
return;
this._havePointer = true;
actor.raise_top();
this._updateTitle();
},
_onLeave: function (actor, event) {
@ -159,6 +168,7 @@ WindowClone.prototype = {
return;
this._havePointer = false;
this._updateTitle();
if (this._zoomStep)
this._zoomEnd();
@ -196,9 +206,6 @@ WindowClone.prototype = {
},
_zoomStart : function () {
this._zooming = true;
this.emit('zoom-start');
this._zoomLightbox = new Lightbox.Lightbox(global.stage);
this._zoomLocalOrig = new ScaledPoint(this.actor.x, this.actor.y, this.actor.scale_x, this.actor.scale_y);
@ -225,15 +232,13 @@ WindowClone.prototype = {
},
_zoomEnd : function () {
this._zooming = false;
this.emit('zoom-end');
this.actor.reparent(this._origParent);
this.actor.raise(this._stackAbove);
[this.actor.x, this.actor.y] = this._zoomLocalOrig.getPosition();
[this.actor.scale_x, this.actor.scale_y] = this._zoomLocalOrig.getScale();
this._adjustTitle();
this._zoomLightbox.destroy();
Main.overview.disconnect(this._hideEventId);
@ -252,6 +257,7 @@ WindowClone.prototype = {
_onDragBegin : function (draggable, time) {
this._inDrag = true;
this._updateTitle();
this.emit('drag-begin');
},
@ -266,13 +272,88 @@ WindowClone.prototype = {
// mysteriously present
this._havePointer = false;
// We may not have a parent if DnD completed successfully, in
// which case our clone will shortly be destroyed and replaced
// with a new one on the target workspace.
if (this.actor.get_parent() != null)
this.actor.raise(this._stackAbove);
this.emit('drag-end');
},
// Called by Tweener
onAnimationStart : function () {
this._updateTitle();
},
// Called by Tweener
onAnimationComplete : function () {
this._updateTitle();
this.actor.raise(this.stackAbove);
},
_createTitle : function () {
let window = this.realWindow;
let box = new Big.Box({ background_color : WINDOWCLONE_BG_COLOR,
y_align: Big.BoxAlignment.CENTER,
corner_radius: 5,
padding: 4,
spacing: 4,
orientation: Big.BoxOrientation.HORIZONTAL });
let title = new Clutter.Text({ color: WINDOWCLONE_TITLE_COLOR,
font_name: "Sans 12",
text: this.metaWindow.title,
ellipsize: Pango.EllipsizeMode.END
});
box.append(title, Big.BoxPackFlags.EXPAND);
// Get and cache the expected width (just the icon), with spacing, plus title
box.fullWidth = box.width;
box.hide(); // Hidden by default, show on mouseover
this._title = box;
// Make the title a sibling of the window
this.actor.get_parent().add_actor(box);
},
_adjustTitle : function () {
let title = this._title;
if (!title)
return;
let [cloneScreenWidth, cloneScreenHeight] = this.actor.get_transformed_size();
let [titleScreenWidth, titleScreenHeight] = title.get_transformed_size();
// Titles are supposed to be "full-size", so adjust its
// scale to counteract the scaling of its ancestor actors.
title.set_scale(title.width / titleScreenWidth * title.scale_x,
title.height / titleScreenHeight * title.scale_y);
title.width = Math.min(title.fullWidth, cloneScreenWidth);
let xoff = ((cloneScreenWidth - title.width) / 2) * title.scale_x;
title.set_position(this.actor.x + xoff, this.actor.y);
},
_showTitle : function () {
if (!this._title)
this._createTitle();
this._adjustTitle();
this._title.show();
this._title.raise(this.actor);
},
_hideTitle : function () {
if (!this._title)
return;
this._title.hide();
},
_updateTitle : function () {
let shouldShow = (this._havePointer &&
!this._inDrag &&
!Tweener.isTweening(this.actor));
if (shouldShow)
this._showTitle();
else
this._hideTitle();
}
};
@ -307,212 +388,6 @@ DesktopClone.prototype = {
Signals.addSignalMethods(DesktopClone.prototype);
/**
* @windowClone: Corresponding window clone
* @parentActor: The actor which will be the parent of all overlay items
* such as app icon and window caption
*/
function WindowOverlay(windowClone, parentActor) {
this._init(windowClone, parentActor);
}
WindowOverlay.prototype = {
_init : function(windowClone, parentActor) {
let metaWindow = windowClone.metaWindow;
this._windowClone = windowClone;
this._parentActor = parentActor;
let title = new St.Label({ style_class: "window-caption",
text : metaWindow.title });
title.connect('style-changed',
Lang.bind(this, this._onStyleChanged));
title.clutter_text.ellipsize = Pango.EllipsizeMode.END;
title._spacing = 0;
let button = new St.Bin({ style_class: "window-close",
reactive: true });
button.connect('style-changed',
Lang.bind(this, this._onStyleChanged));
button._overlap = 0;
windowClone.actor.connect('destroy', Lang.bind(this, this._onDestroy));
windowClone.actor.connect('enter-event',
Lang.bind(this, this._onEnter));
windowClone.actor.connect('leave-event',
Lang.bind(this, this._onLeave));
this._idleToggleCloseId = 0;
button.connect('button-release-event',
Lang.bind(this, this._closeWindow));
this._windowAddedId = 0;
windowClone.connect('zoom-start', Lang.bind(this, this.hide));
windowClone.connect('zoom-end', Lang.bind(this, this.show));
button.hide();
this.title = title;
this.closeButton = button;
parentActor.add_actor(this.title);
parentActor.add_actor(this.closeButton);
},
hide: function() {
this.closeButton.hide();
this.title.hide();
},
show: function() {
let [child, x, y, mask] = Gdk.Screen.get_default().get_root_window().get_pointer();
let actor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE,
x, y);
if (actor == this._windowClone.actor) {
this.closeButton.show();
}
this.title.show();
},
fadeIn: function() {
this.title.opacity = 0;
this.title.show();
this.title.raise_top();
Tweener.addTween(this.title,
{ opacity: 255,
time: Overview.ANIMATION_TIME,
transition: "easeOutQuad" });
},
chromeWidth: function () {
return this.closeButton.width - this.closeButton._overlap;
},
chromeHeight: function () {
return this.closeButton.height - this.closeButton._overlap +
this.title.height + this.title._spacing;
},
/**
* @cloneX: x position of windowClone
* @cloneY: y position of windowClone
* @cloneWidth: width of windowClone
* @cloneHeight height of windowClone
*/
// These parameters are not the values retrieved with
// get_transformed_position() and get_transformed_size(),
// as windowClone might be moving.
// See Workspace._fadeInWindowOverlay
updatePositions: function(cloneX, cloneY, cloneWidth, cloneHeight) {
let button = this.closeButton;
let title = this.title;
let buttonX = cloneX + cloneWidth - button._overlap;
let buttonY = cloneY - button.height + button._overlap;
button.set_position(Math.floor(buttonX), Math.floor(buttonY));
if (!title.fullWidth)
title.fullWidth = title.width;
title.width = Math.min(title.fullWidth, cloneWidth);
let titleX = cloneX + (cloneWidth - title.width) / 2;
let titleY = cloneY + cloneHeight + title._spacing;
title.set_position(Math.floor(titleX), Math.floor(titleY));
},
_closeWindow: function(actor, event) {
let metaWindow = this._windowClone.metaWindow;
this._workspace = metaWindow.get_workspace();
this._windowAddedId = this._workspace.connect('window-added',
Lang.bind(this,
this._onWindowAdded));
metaWindow.delete(event.get_time());
},
_onWindowAdded: function(workspace, win) {
let metaWindow = this._windowClone.metaWindow;
if (win.get_transient_for() == metaWindow) {
workspace.disconnect(this._windowAddedId);
this._windowAddedId = 0;
// use an idle handler to avoid mapping problems -
// see comment in Workspace._windowAdded
Mainloop.idle_add(Lang.bind(this,
function() {
this._windowClone.emit('selected');
return false;
}));
}
},
_onDestroy: function() {
if (this._windowAddedId > 0) {
this._workspace.disconnect(this._windowAddedId);
this._windowAddedId = 0;
}
if (this._idleToggleCloseId > 0) {
Mainloop.source_remove(this._idleToggleCloseId);
this._idleToggleCloseId = 0;
}
this.title.destroy();
this.closeButton.destroy();
},
_onEnter: function() {
this.closeButton.raise_top();
this.closeButton.show();
this.emit('show-close-button');
},
_onLeave: function() {
if (this._idleToggleCloseId == 0)
this._idleToggleCloseId = Mainloop.timeout_add(750, Lang.bind(this, this._idleToggleCloseButton));
},
_idleToggleCloseButton: function() {
this._idleToggleCloseId = 0;
let [child, x, y, mask] = Gdk.Screen.get_default().get_root_window().get_pointer();
let actor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE,
x, y);
if (actor != this._windowClone.actor && actor != this.closeButton) {
this.closeButton.hide();
}
return false;
},
hideCloseButton: function() {
if (this._idleToggleCloseId > 0) {
Mainloop.source_remove(this._idleToggleCloseId);
this._idleToggleCloseId = 0;
}
this.closeButton.hide();
},
_onStyleChanged: function() {
let titleNode = this.title.get_theme_node();
let [success, len] = titleNode.get_length('-shell-caption-spacing',
false);
if (success)
this.title._spacing = len;
let closeNode = this.closeButton.get_theme_node();
[success, len] = closeNode.get_length('-shell-close-overlap',
false);
if (success)
this.closeButton._overlap = len;
this._parentActor.queue_relayout();
}
};
Signals.addSignalMethods(WindowOverlay.prototype);
/**
* @workspaceNum: Workspace index
* @parentActor: The actor which will be the parent of this workspace;
@ -564,7 +439,7 @@ Workspace.prototype = {
// Create clones for remaining windows that should be
// visible in the Overview
this._windows = [this._desktop];
this._windowOverlays = [ null ];
this._windowIcons = [ null ];
for (let i = 0; i < windows.length; i++) {
if (this._isOverviewWindow(windows[i])) {
this._addWindowClone(windows[i]);
@ -679,9 +554,6 @@ Workspace.prototype = {
this._lightbox.destroy();
this._lightbox = null;
}
if (this._frame) {
this._frame.set_opacity(showLightbox ? 150 : 255);
}
},
/**
@ -746,47 +618,16 @@ Workspace.prototype = {
this._desktop.actor.height + 2 * FRAME_SIZE / this.actor.scale_y);
},
_isCloneVisible: function(clone) {
return this._showOnlyWindows == null || (clone.metaWindow in this._showOnlyWindows);
},
/**
* _getVisibleClones:
*
* Returns a list WindowClone objects where the clone isn't filtered
* out by any application filter. The clone for the desktop is excluded.
* The returned array will always be newly allocated; it is not in any
* defined order, and thus it's convenient to call .sort() with your
* choice of sorting function.
*/
_getVisibleClones: function() {
let visible = [];
for (let i = 1; i < this._windows.length; i++) {
let clone = this._windows[i];
if (!this._isCloneVisible(clone))
continue;
visible.push(clone);
}
return visible;
},
_getVisibleWindows: function() {
return this._getVisibleClones().map(function (clone) { return clone.metaWindow; });
},
_resetCloneVisibility: function () {
for (let i = 1; i < this._windows.length; i++) {
let clone = this._windows[i];
let overlay = this._windowOverlays[i];
let icon = this._windowIcons[i];
if (!this._isCloneVisible(clone)) {
clone.actor.hide();
overlay.hide();
if (this._showOnlyWindows != null && !(clone.metaWindow in this._showOnlyWindows)) {
clone.setVisibleWithChrome(false);
icon.hide();
} else {
clone.actor.show();
clone.setVisibleWithChrome(true);
}
}
},
@ -932,6 +773,7 @@ Workspace.prototype = {
* fashion by the order in which the windows were created.
*/
_orderWindowsByMotionAndStartup: function(windows, slots) {
let appMonitor = Shell.AppMonitor.get_default();
windows.sort(function(w1, w2) {
return w2.get_stable_sequence() - w1.get_stable_sequence();
});
@ -992,17 +834,14 @@ Workspace.prototype = {
let rect = new Meta.Rectangle();
metaWindow.get_outer_rect(rect);
let chromeHeight = this._windowOverlays[1].chromeHeight() / this.scale;
let chromeWidth = this._windowOverlays[1].chromeWidth() / this.scale;
let desiredWidth = (global.screen_width - chromeWidth) * fraction;
let desiredHeight = (global.screen_height - chromeHeight) * fraction;
let scale = Math.min(desiredWidth / (rect.width + chromeWidth),
desiredHeight / (rect.height + chromeHeight),
let desiredWidth = global.screen_width * fraction;
let desiredHeight = global.screen_height * fraction;
let scale = Math.min(desiredWidth / rect.width,
desiredHeight / rect.height,
1.0 / this.scale);
let x = xCenter - 0.5 * scale * (rect.width + chromeWidth);
let y = yCenter - 0.5 * scale * (rect.height + chromeHeight);
let x = xCenter - 0.5 * scale * rect.width;
let y = yCenter - 0.5 * scale * rect.height;
return [x, y, scale];
},
@ -1014,22 +853,34 @@ Workspace.prototype = {
positionWindows : function(workspaceZooming) {
let totalVisible = 0;
let visibleWindows = this._getVisibleWindows();
let visibleWindows = [];
for (let i = 1; i < this._windows.length; i++) {
let clone = this._windows[i];
if (this._showOnlyWindows != null && !(clone.metaWindow in this._showOnlyWindows))
continue;
visibleWindows.push(clone.metaWindow);
}
// Start the animations
let slots = this._computeAllWindowSlots(visibleWindows.length);
visibleWindows = this._orderWindowsByMotionAndStartup(visibleWindows, slots);
let previousWindow = this._windows[0];
for (let i = 0; i < visibleWindows.length; i++) {
let slot = slots[i];
let metaWindow = visibleWindows[i];
let mainIndex = this._lookupIndex(metaWindow);
let clone = metaWindow._delegate;
let overlay = this._windowOverlays[mainIndex];
let icon = this._windowIcons[mainIndex];
clone.stackAbove = previousWindow.actor;
previousWindow = clone;
let [x, y, scale] = this._computeWindowRelativeLayout(metaWindow, slot);
overlay.hide();
icon.hide();
Tweener.addTween(clone.actor,
{ x: x,
y: y,
@ -1039,62 +890,55 @@ Workspace.prototype = {
time: Overview.ANIMATION_TIME,
transition: "easeOutQuad",
onComplete: Lang.bind(this, function() {
this._fadeInWindowOverlay(clone, overlay);
this._fadeInWindowIcon(clone, icon);
})
});
}
},
syncStacking: function(stackIndices) {
let desktopClone = this._windows[0];
let visibleClones = this._getVisibleClones();
visibleClones.sort(function (a, b) { return stackIndices[a.metaWindow.get_stable_sequence()] - stackIndices[b.metaWindow.get_stable_sequence()]; });
for (let i = 0; i < visibleClones.length; i++) {
let clone = visibleClones[i];
let metaWindow = clone.metaWindow;
if (i == 0) {
clone.setStackAbove(desktopClone.actor);
} else {
let previousClone = visibleClones[i - 1];
clone.setStackAbove(previousClone.actor);
}
}
},
_fadeInWindowOverlay: function(clone, overlay) {
_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;
overlay.updatePositions(cloneX, cloneY, cloneWidth, cloneHeight);
overlay.fadeIn();
// 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" });
},
_fadeInAllOverlays: function() {
_fadeInAllIcons: function () {
for (let i = 1; i < this._windows.length; i++) {
let clone = this._windows[i];
let overlay = this._windowOverlays[i];
let icon = this._windowIcons[i];
if (this._showOnlyWindows != null && !(clone.metaWindow in this._showOnlyWindows))
continue;
this._fadeInWindowOverlay(clone, overlay);
this._fadeInWindowIcon(clone, icon);
}
},
_hideAllOverlays: function() {
for (let i = 1; i< this._windows.length; i++) {
let overlay = this._windowOverlays[i];
overlay.hide();
_hideAllIcons: function () {
for (let i = 1; i < this._windows.length; i++) {
let icon = this._windowIcons[i];
icon.hide();
}
},
@ -1108,9 +952,10 @@ Workspace.prototype = {
return;
let clone = this._windows[index];
let icon = this._windowIcons[index];
this._windows.splice(index, 1);
this._windowOverlays.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
@ -1128,6 +973,7 @@ Workspace.prototype = {
};
}
clone.destroy();
icon.destroy();
this.positionWindows(false);
this.updateRemovable();
@ -1197,7 +1043,7 @@ Workspace.prototype = {
zoomFromOverview : function() {
this.leavingOverview = true;
this._hideAllOverlays();
this._hideAllIcons();
Main.overview.connect('hidden', Lang.bind(this,
this._doneLeavingOverview));
@ -1234,7 +1080,7 @@ Workspace.prototype = {
// Animates grid shrinking/expanding when a row or column
// of workspaces is added or removed
resizeToGrid : function (oldScale) {
this._hideAllOverlays();
this._hideAllIcons();
Tweener.addTween(this.actor,
{ x: this.gridX,
y: this.gridY,
@ -1242,7 +1088,7 @@ Workspace.prototype = {
scale_y: this.scale,
time: Overview.ANIMATION_TIME,
transition: "easeOutQuad",
onComplete: Lang.bind(this, this._fadeInAllOverlays)
onComplete: Lang.bind(this, this._fadeInAllIcons)
});
},
@ -1271,7 +1117,7 @@ Workspace.prototype = {
slideOut : function(onComplete) {
let destX = this.actor.x, destY = this.actor.y;
this._hideAllOverlays();
this._hideAllIcons();
if (this.gridCol > this.gridRow)
destX = global.screen_width;
@ -1316,45 +1162,55 @@ Workspace.prototype = {
// Tests if @win should be shown in the Overview
_isOverviewWindow : function (win) {
let tracker = Shell.WindowTracker.get_default()
return tracker.is_window_interesting(win.get_meta_window());
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;
},
// Create a clone of a (non-desktop) window and add it to the window list
_addWindowClone : function(win) {
let clone = new WindowClone(win);
let overlay = new WindowOverlay(clone, this.parentActor);
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() {
overlay.hide();
icon.hide();
}));
clone.connect('drag-end',
Lang.bind(this, function() {
overlay.show();
icon.show();
}));
this.actor.add_actor(clone.actor);
overlay.connect('show-close-button', Lang.bind(this, this._onShowOverlayClose));
this._windows.push(clone);
this._windowOverlays.push(overlay);
this._windowIcons.push(icon);
return clone;
},
_onShowOverlayClose: function (windowOverlay) {
for (let i = 1; i < this._windowOverlays.length; i++) {
let overlay = this._windowOverlays[i];
if (overlay == windowOverlay)
continue;
overlay.hideCloseButton();
}
},
_computeWindowSlot : function(windowIndex, numberOfWindows) {
if (numberOfWindows in POSITIONS)
return POSITIONS[numberOfWindows][windowIndex];
@ -1430,10 +1286,7 @@ function Workspaces(width, height, x, y) {
Workspaces.prototype = {
_init : function(width, height, x, y) {
this.actor = new St.Bin({ style_class: "workspaces" });
this._actor = new Clutter.Group();
this.actor.add_actor(this._actor);
this.actor = new Clutter.Group();
this._width = width;
this._height = height;
@ -1467,12 +1320,10 @@ Workspaces.prototype = {
// workspaces have been created. This cannot be done first because
// window movement depends on the Workspaces object being accessible
// as an Overview member.
this._overviewShowingId =
Main.overview.connect('showing',
Lang.bind(this, function() {
this._onRestacked();
for (let w = 0; w < this._workspaces.length; w++)
this._workspaces[w].zoomToOverview();
Main.overview.connect('showing',
Lang.bind(this, function() {
for (let w = 0; w < this._workspaces.length; w++)
this._workspaces[w].zoomToOverview();
}));
// Track changes to the number of workspaces
@ -1482,9 +1333,6 @@ Workspaces.prototype = {
this._switchWorkspaceNotifyId =
global.window_manager.connect('switch-workspace',
Lang.bind(this, this._activeWorkspaceChanged));
this._restackedNotifyId =
global.screen.connect('restacked',
Lang.bind(this, this._onRestacked));
},
_lookupWorkspaceForMetaWindow: function (metaWindow) {
@ -1547,11 +1395,10 @@ Workspaces.prototype = {
this._windowSelectionAppId = appId;
let appSys = Shell.AppSystem.get_default();
let appSys = Shell.AppMonitor.get_default();
let showOnlyWindows = {};
let app = appSys.get_app(appId);
let windows = app.get_windows();
let windows = appSys.get_windows_for_app(appId);
for (let i = 0; i < windows.length; i++) {
showOnlyWindows[windows[i]] = 1;
}
@ -1575,6 +1422,9 @@ Workspaces.prototype = {
this._clearApplicationWindowSelection(false);
}
let clone = this._lookupCloneForMetaWindow (metaWindow);
clone.actor.raise_top();
Main.activateWindow(metaWindow, time);
Main.overview.hide();
},
@ -1598,10 +1448,8 @@ Workspaces.prototype = {
this.actor.destroy();
this.actor = null;
Main.overview.disconnect(this._overviewShowingId);
global.screen.disconnect(this._nWorkspacesNotifyId);
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
global.screen.disconnect(this._restackedNotifyId);
},
getScale : function() {
@ -1742,22 +1590,9 @@ Workspaces.prototype = {
},
_addWorkspaceActor : function(workspaceNum) {
let workspace = new Workspace(workspaceNum, this._actor);
let workspace = new Workspace(workspaceNum, this.actor);
this._workspaces[workspaceNum] = workspace;
this._actor.add_actor(workspace.actor);
},
_onRestacked: function() {
let stack = global.get_windows();
let stackIndices = {};
for (let i = 0; i < stack.length; i++) {
// Use the stable sequence for an integer to use as a hash key
stackIndices[stack[i].get_meta_window().get_stable_sequence()] = i;
}
for (let i = 0; i < this._workspaces.length; i++)
this._workspaces[i].syncStacking(stackIndices);
this.actor.add_actor(workspace.actor);
},
// Handles a drop onto the (+) button; assumes the new workspace

View File

@ -3,14 +3,11 @@ ca
cs
da
de
el
en_GB
es
fi
fr
ga
gl
he
hu
it
ko
@ -19,8 +16,6 @@ nl
pa
pl
pt_BR
ro
ru
sl
sv
tr

View File

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

100
po/ar.po
View File

@ -6,8 +6,8 @@ msgid ""
msgstr ""
"Project-Id-Version: HEAD\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-11-08 20:55+0200\n"
"PO-Revision-Date: 2009-11-08 20:55+0300\n"
"POT-Creation-Date: 2009-09-25 04:22+0200\n"
"PO-Revision-Date: 2009-09-25 04:21+0300\n"
"Last-Translator: Khaled Hosny <khaledhosny@eglug.org>\n"
"Language-Team: Arabic <doc@arabeyes.org>\n"
"MIME-Version: 1.0\n"
@ -16,7 +16,7 @@ msgstr ""
"Language: ar\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
"X-Generator: Virtaal 0.4.1\n"
"X-Generator: Virtaal 0.4.0\n"
#: ../data/gnome-shell.desktop.in.in.h:1
msgid "GNOME Shell"
@ -26,106 +26,63 @@ msgstr "صدفة جنوم"
msgid "Window management and application launching"
msgstr "إدارة النوافذ وإطلاق التطبيقات"
#: ../js/ui/appDisplay.js:332
msgid "Frequent"
msgstr "متكرر"
#. left side
#: ../js/ui/panel.js:269
msgid "Activities"
msgstr "الأنشطة"
#: ../js/ui/appDisplay.js:867
msgid "Drag here to add favorites"
msgstr "اسحب إلى هنا ليضاف إلى المفضّلة"
#. Translators: This is a time format.
#: ../js/ui/panel.js:452
msgid "%a %l:%M %p"
msgstr "%A %Ol:%OM %p"
#: ../js/ui/appIcon.js:426
msgid "New Window"
msgstr "نافذة جديدة"
#: ../js/ui/appIcon.js:430
msgid "Remove from Favorites"
msgstr "أزِل من المفضّلة"
#: ../js/ui/appIcon.js:431
msgid "Add to Favorites"
msgstr "أضِف إلى المفضّلة"
#: ../js/ui/dash.js:267
#: ../js/ui/dash.js:283
msgid "Find..."
msgstr "ابحث..."
#: ../js/ui/dash.js:376
msgid "More"
msgstr "المزيد"
#: ../js/ui/dash.js:400
msgid "Browse"
msgstr "استعرض"
#: ../js/ui/dash.js:532
#: ../js/ui/dash.js:536
msgid "(see all)"
msgstr "(انظر الكل)"
#. **** Applications ****
#: ../js/ui/dash.js:711 ../js/ui/dash.js:773
#: ../js/ui/dash.js:753 ../js/ui/dash.js:809
msgid "APPLICATIONS"
msgstr "التطبيقات"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:731
#: ../js/ui/dash.js:773
msgid "PLACES"
msgstr "الأماكن"
#. **** Documents ****
#: ../js/ui/dash.js:738 ../js/ui/dash.js:783
#: ../js/ui/dash.js:780 ../js/ui/dash.js:819
msgid "RECENT DOCUMENTS"
msgstr "المستندات الحديثة"
#. **** Search Results ****
#: ../js/ui/dash.js:763 ../js/ui/dash.js:947
#: ../js/ui/dash.js:799 ../js/ui/dash.js:931
msgid "SEARCH RESULTS"
msgstr "نتائج البحث"
#: ../js/ui/dash.js:778
#: ../js/ui/dash.js:814
msgid "PREFERENCES"
msgstr "التفضيلات"
#. Button on the left side of the panel.
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:274
msgid "Activities"
msgstr "الأنشطة"
#. Translators: This is a time format.
#: ../js/ui/panel.js:491
msgid "%a %l:%M %p"
msgstr "%A %Ol:%OM %p"
#: ../js/ui/places.js:178
msgid "Connect to..."
msgstr "اتّصل ب‍..."
#: ../js/ui/runDialog.js:96
#: ../js/ui/runDialog.js:95
msgid "Please enter a command:"
msgstr "من فضلك اكتب أمرا:"
#: ../js/ui/runDialog.js:173
#, c-format
msgid "Execution of '%s' failed:"
msgstr "فشل تنفيذ '%s':"
#. Translators: This is a time format.
#: ../js/ui/widget.js:163
msgid "%H:%M"
msgstr "%OH:%OM"
#: ../js/ui/widget.js:317
msgid "Applications"
msgstr "التطبيقات"
#: ../js/ui/widget.js:339
msgid "Recent Documents"
msgstr "المستندات الحديثة"
#: ../src/shell-global.c:821
#: ../src/shell-global.c:799
msgid "Less than a minute ago"
msgstr "منذ أقل من دقيقة"
#: ../src/shell-global.c:824
#: ../src/shell-global.c:802
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
@ -136,7 +93,7 @@ msgstr[3] "منذ %d دقائق"
msgstr[4] "منذ %d دقيقة"
msgstr[5] "منذ %d دقيقة"
#: ../src/shell-global.c:827
#: ../src/shell-global.c:805
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
@ -147,7 +104,7 @@ msgstr[3] "منذ %d ساعات"
msgstr[4] "منذ %d ساعة"
msgstr[5] "منذ %d ساعة"
#: ../src/shell-global.c:830
#: ../src/shell-global.c:808
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
@ -158,7 +115,7 @@ msgstr[3] "منذ %d أيام"
msgstr[4] "منذ %d يوما"
msgstr[5] "منذ %d يوم"
#: ../src/shell-global.c:833
#: ../src/shell-global.c:811
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
@ -242,6 +199,3 @@ msgstr "ابحث"
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"
#~ msgid "Browse"
#~ msgstr "استعرض"

105
po/ca.po
View File

@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: HEAD\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-10-10 20:19+0200\n"
"PO-Revision-Date: 2009-10-10 20:22+0100\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"
@ -24,126 +24,82 @@ msgstr "GNOME Shell"
msgid "Window management and application launching"
msgstr "Gestió de finestres i execució d'aplicacions"
#: ../js/ui/appDisplay.js:335
msgid "Frequent"
msgstr "Freqüent"
#. left side
#: ../js/ui/panel.js:271
msgid "Activities"
msgstr "Activitats"
#: ../js/ui/appIcon.js:462
msgid "New Window"
msgstr "Finestra nova"
#. Translators: This is a time format.
#: ../js/ui/panel.js:454
msgid "%a %l:%M %p"
msgstr "%a %H:%M"
#: ../js/ui/appIcon.js:475
msgid "Remove from Favorites"
msgstr "Elimina dels preferits"
#: ../js/ui/appIcon.js:476
msgid "Add to Favorites"
msgstr "Afegeix als preferits"
#: ../js/ui/dash.js:283
#: ../js/ui/dash.js:256
msgid "Find..."
msgstr "Cerca..."
#: ../js/ui/dash.js:400
msgid "More"
msgstr "Més"
#: ../js/ui/dash.js:374
msgid "Browse"
msgstr "Navega"
#: ../js/ui/dash.js:543
#: ../js/ui/dash.js:451
msgid "(see all)"
msgstr "(mostra tot)"
#. **** Applications ****
#: ../js/ui/dash.js:763
#: ../js/ui/dash.js:825
#: ../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:783
#: ../js/ui/dash.js:653
msgid "PLACES"
msgstr "LLOCS"
#. **** Documents ****
#: ../js/ui/dash.js:790
#: ../js/ui/dash.js:835
#: ../js/ui/dash.js:660
#: ../js/ui/dash.js:692
msgid "RECENT DOCUMENTS"
msgstr "DOCUMENTS RECENTS"
#. **** Search Results ****
#: ../js/ui/dash.js:815
#: ../js/ui/dash.js:955
#: ../js/ui/dash.js:679
msgid "SEARCH RESULTS"
msgstr "RESULTATS DE LA CERCA"
#: ../js/ui/dash.js:830
msgid "PREFERENCES"
msgstr "PREFERÈNCIES"
#. Button on the left side of the panel.
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:272
msgid "Activities"
msgstr "Activitats"
#. Translators: This is a time format.
#: ../js/ui/panel.js:464
msgid "%a %l:%M %p"
msgstr "%a %H:%M"
#: ../js/ui/places.js:178
msgid "Connect to..."
msgstr "Connecta a..."
#: ../js/ui/runDialog.js:96
#: ../js/ui/runDialog.js:82
msgid "Please enter a command:"
msgstr "Introduïu una ordre:"
#: ../js/ui/runDialog.js:173
#, c-format
msgid "Execution of '%s' failed:"
msgstr "No s'ha pogut executar «%s»:"
#. Translators: This is a time format.
#: ../js/ui/widget.js:162
msgid "%H:%M"
msgstr "%H:%M"
#: ../js/ui/widget.js:316
msgid "Applications"
msgstr "Aplicacions"
#: ../js/ui/widget.js:341
msgid "Recent Documents"
msgstr "Documents recents"
#: ../src/shell-global.c:812
#: ../src/shell-global.c:840
msgid "Less than a minute ago"
msgstr "Fa menys d'un minut"
#: ../src/shell-global.c:815
#: ../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:818
#: ../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:821
#: ../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:824
#: ../src/shell-global.c:852
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
@ -224,3 +180,10 @@ msgstr "Cerca"
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."

103
po/cs.po
View File

@ -7,15 +7,14 @@ msgid ""
msgstr ""
"Project-Id-Version: gnome-shell\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-11-05 18:03+0100\n"
"PO-Revision-Date: 2009-11-05 18:03+0100\n"
"Last-Translator: Petr Kovar <pknbe@volny.cz>\n"
"POT-Creation-Date: 2009-09-22 13:36+0200\n"
"PO-Revision-Date: 2009-09-22 13:37+0200\n"
"Last-Translator: Andre Klapper <ak-47@gmx.net>, 2009\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"
"X-Generator: Lokalize 1.0\n"
#: ../data/gnome-shell.desktop.in.in.h:1
msgid "GNOME Shell"
@ -25,106 +24,63 @@ msgstr "Prostředí GNOME Shell"
msgid "Window management and application launching"
msgstr "Správa oken a spouštění aplikací"
#: ../js/ui/appDisplay.js:332
msgid "Frequent"
msgstr "Časté"
#. left side
#: ../js/ui/panel.js:269
msgid "Activities"
msgstr "Činnosti"
#: ../js/ui/appDisplay.js:867
msgid "Drag here to add favorites"
msgstr "Oblíbené přidáte přetažením sem"
#. Translators: This is a time format.
#: ../js/ui/panel.js:452
msgid "%a %l:%M %p"
msgstr "%a, %H:%M"
#: ../js/ui/appIcon.js:426
msgid "New Window"
msgstr "Nové okno"
#: ../js/ui/appIcon.js:430
msgid "Remove from Favorites"
msgstr "Odstranit z oblíbených"
#: ../js/ui/appIcon.js:431
msgid "Add to Favorites"
msgstr "Přidat mezi oblíbené"
#: ../js/ui/dash.js:267
#: ../js/ui/dash.js:283
msgid "Find..."
msgstr "Najít..."
#: ../js/ui/dash.js:376
msgid "More"
msgstr "Více"
#: ../js/ui/dash.js:400
msgid "Browse"
msgstr "Procházet"
#: ../js/ui/dash.js:532
#: ../js/ui/dash.js:536
msgid "(see all)"
msgstr "(zobrazit vše)"
#. **** Applications ****
#: ../js/ui/dash.js:711 ../js/ui/dash.js:773
#: ../js/ui/dash.js:753 ../js/ui/dash.js:809
msgid "APPLICATIONS"
msgstr "APLIKACE"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:731
#: ../js/ui/dash.js:773
msgid "PLACES"
msgstr "MÍSTA"
#. **** Documents ****
#: ../js/ui/dash.js:738 ../js/ui/dash.js:783
#: ../js/ui/dash.js:780 ../js/ui/dash.js:819
msgid "RECENT DOCUMENTS"
msgstr "NEDÁVNÉ DOKUMENTY"
#. **** Search Results ****
#: ../js/ui/dash.js:763 ../js/ui/dash.js:947
#: ../js/ui/dash.js:799 ../js/ui/dash.js:931
msgid "SEARCH RESULTS"
msgstr "VÝSLEDKY HLEDÁNÍ"
#: ../js/ui/dash.js:778
#: ../js/ui/dash.js:814
msgid "PREFERENCES"
msgstr "PŘEDVOLBY"
msgstr "NASTAVENÍ"
#. Button on the left side of the panel.
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:274
msgid "Activities"
msgstr "Činnosti"
#. Translators: This is a time format.
#: ../js/ui/panel.js:491
msgid "%a %l:%M %p"
msgstr "%a, %H:%M"
#: ../js/ui/places.js:178
msgid "Connect to..."
msgstr "Připojit se k..."
#: ../js/ui/runDialog.js:96
#: ../js/ui/runDialog.js:101
msgid "Please enter a command:"
msgstr "Zadejte prosím příkaz:"
#: ../js/ui/runDialog.js:173
#, c-format
msgid "Execution of '%s' failed:"
msgstr "Vykonání \"%s\" selhalo:"
#. Translators: This is a time format.
#: ../js/ui/widget.js:163
msgid "%H:%M"
msgstr "%H:%M"
#: ../js/ui/widget.js:317
msgid "Applications"
msgstr "Aplikace"
#: ../js/ui/widget.js:339
msgid "Recent Documents"
msgstr "Nedávné dokumenty"
#: ../src/shell-global.c:821
#: ../src/shell-global.c:799
msgid "Less than a minute ago"
msgstr "Před méně než minutou"
#: ../src/shell-global.c:824
#: ../src/shell-global.c:802
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
@ -132,7 +88,7 @@ msgstr[0] "Před %d minutou"
msgstr[1] "Před %d minutami"
msgstr[2] "Před %d minutami"
#: ../src/shell-global.c:827
#: ../src/shell-global.c:805
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
@ -140,7 +96,7 @@ msgstr[0] "Před %d hodinou"
msgstr[1] "Před %d hodinami"
msgstr[2] "Před %d hodinami"
#: ../src/shell-global.c:830
#: ../src/shell-global.c:808
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
@ -148,7 +104,7 @@ msgstr[0] "Před %d dnem"
msgstr[1] "Před %d dny"
msgstr[2] "Před %d dny"
#: ../src/shell-global.c:833
#: ../src/shell-global.c:811
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
@ -230,9 +186,6 @@ msgstr "Hledat"
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"
#~ msgid "Browse"
#~ msgstr "Procházet"
#~ msgid "Manager"
#~ msgstr "Správce"

View File

@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gnome-shell\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-10-21 22:51+0200\n"
"PO-Revision-Date: 2009-10-18 17:32+0200\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"
@ -24,123 +24,80 @@ msgstr "Skal til GNOME"
msgid "Window management and application launching"
msgstr "Vinduehåndtering og åbning af programmer"
#: ../js/ui/appDisplay.js:332
msgid "Frequent"
msgstr "Ofte"
#. left side
#: ../js/ui/panel.js:271
msgid "Activities"
msgstr "Aktiviteter"
#: ../js/ui/appIcon.js:410
msgid "New Window"
msgstr "Nyt vindue"
#. Translators: This is a time format.
#: ../js/ui/panel.js:454
msgid "%a %l:%M %p"
msgstr "%a %H:%M"
#: ../js/ui/appIcon.js:414
msgid "Remove from Favorites"
msgstr "Fjern fra favoritter"
#: ../js/ui/appIcon.js:415
msgid "Add to Favorites"
msgstr "Tilføj til favoritter"
#: ../js/ui/dash.js:283
#: ../js/ui/dash.js:256
msgid "Find..."
msgstr "Find..."
#: ../js/ui/dash.js:400
msgid "More"
msgstr "Mere"
#: ../js/ui/dash.js:374
msgid "Browse"
msgstr "Gennemse"
#: ../js/ui/dash.js:543
#: ../js/ui/dash.js:511
msgid "(see all)"
msgstr "(se alle)"
#. **** Applications ****
#: ../js/ui/dash.js:725 ../js/ui/dash.js:787
#: ../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:745
#: ../js/ui/dash.js:725
msgid "PLACES"
msgstr "STEDER"
#. **** Documents ****
#: ../js/ui/dash.js:752 ../js/ui/dash.js:797
#: ../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:777 ../js/ui/dash.js:961
#: ../js/ui/dash.js:751 ../js/ui/dash.js:856 ../js/ui/dash.js:882
msgid "SEARCH RESULTS"
msgstr "SØGERESULTATER"
#: ../js/ui/dash.js:792
msgid "PREFERENCES"
msgstr "INDSTILLINGER"
#. Button on the left side of the panel.
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:274
msgid "Activities"
msgstr "Aktiviteter"
#. Translators: This is a time format.
#: ../js/ui/panel.js:491
msgid "%a %l:%M %p"
msgstr "%a %H:%M"
#: ../js/ui/places.js:178
msgid "Connect to..."
msgstr "Forbind til..."
#: ../js/ui/runDialog.js:96
#: ../js/ui/runDialog.js:82
msgid "Please enter a command:"
msgstr "Indtast en kommando:"
#: ../js/ui/runDialog.js:173
#, c-format
msgid "Execution of '%s' failed:"
msgstr "Kørsel af \"%s\" mislykkedes:"
#. Translators: This is a time format.
#: ../js/ui/widget.js:163
msgid "%H:%M"
msgstr "%H:%M"
#: ../js/ui/widget.js:317
msgid "Applications"
msgstr "Programmer"
#: ../js/ui/widget.js:339
msgid "Recent Documents"
msgstr "Seneste dokumenter"
#: ../src/shell-global.c:812
#: ../src/shell-global.c:840
msgid "Less than a minute ago"
msgstr "Mindre end et minut siden"
#: ../src/shell-global.c:815
#: ../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:818
#: ../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:821
#: ../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:824
#: ../src/shell-global.c:852
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
@ -221,9 +178,6 @@ msgstr "Søg"
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"
#~ msgid "Browse"
#~ msgstr "Gennemse"
#~ msgid "Find apps or documents"
#~ msgstr "Find programmer eller dokumenter"

151
po/de.po
View File

@ -4,21 +4,18 @@
#
# Hendrik Brandt <heb@gnome-de.org>, 2009.
# Hendrik Richter <hendrikr@gnome.org>, 2009.
# Christian Kirbach <Christian.Kirbach@googlemail.com>, 2009.
# Mario Blättermann <mariobl@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-10-16 20:05+0000\n"
"PO-Revision-Date: 2009-10-17 23:37+0200\n"
"Last-Translator: Christian Kirbach <Christian.Kirbach@googlemail.com>\n"
"Language-Team: German <gnome-de@gnome.org>\n"
"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: 8bit\n"
"Content-Transfer-Encoding: UTF-8\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: ../data/gnome-shell.desktop.in.in.h:1
@ -29,123 +26,79 @@ msgstr "GNOME-Shell"
msgid "Window management and application launching"
msgstr "Fenster verwalten und Anwendungen starten"
#: ../js/ui/appDisplay.js:335
msgid "Frequent"
msgstr "Häufig"
#: ../js/ui/appIcon.js:407
msgid "New Window"
msgstr "Neues Fenster"
#: ../js/ui/appIcon.js:420
msgid "Remove from Favorites"
msgstr "Aus Favoriten entfernen"
#: ../js/ui/appIcon.js:421
msgid "Add to Favorites"
msgstr "Zu Favoriten hinzufügen"
#: ../js/ui/dash.js:283
msgid "Find..."
msgstr "Suchen …"
#: ../js/ui/dash.js:400
msgid "More"
msgstr "Mehr"
#: ../js/ui/dash.js:543
msgid "(see all)"
msgstr "(alle sehen)"
#. **** Applications ****
#: ../js/ui/dash.js:725 ../js/ui/dash.js:787
msgid "APPLICATIONS"
msgstr "ANWENDUNGEN"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:745
msgid "PLACES"
msgstr "ORTE"
#. **** Documents ****
#: ../js/ui/dash.js:752 ../js/ui/dash.js:797
msgid "RECENT DOCUMENTS"
msgstr "ZULETZT GEÖFFNETE DOKUMENTE"
#. **** Search Results ****
#: ../js/ui/dash.js:777 ../js/ui/dash.js:961
msgid "SEARCH RESULTS"
msgstr "SUCHERGEBNISSE"
#: ../js/ui/dash.js:792
msgid "PREFERENCES"
msgstr "EINSTELLUNGEN"
#. Button on the left side of the panel.
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:273
#. left side
#: ../js/ui/panel.js:266
msgid "Activities"
msgstr "Aktivitäten"
#. Translators: This is a time format.
#: ../js/ui/panel.js:465
#: ../js/ui/panel.js:433
msgid "%a %l:%M %p"
msgstr "%a %H:%M"
#: ../js/ui/places.js:178
msgid "Connect to..."
msgstr "Verbinden mit …"
#: ../js/ui/dash.js:251
msgid "Find apps or documents"
msgstr "Anwendungen oder Dokumente suchen"
#: ../js/ui/runDialog.js:96
#: ../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:"
#: ../js/ui/runDialog.js:173
#, c-format
msgid "Execution of '%s' failed:"
msgstr "Ausführung von »%s« ist gescheitert:"
#: ../src/gdmuser/gdm-user.c:243
msgid "Manager"
msgstr "Verwaltung"
#. Translators: This is a time format.
#: ../js/ui/widget.js:162
msgid "%H:%M"
msgstr "%H:%M"
#: ../src/gdmuser/gdm-user.c:244
msgid "The user manager object this user is controlled by."
msgstr "Das Benutzerverwaltungsobjekt welches diesen Benutzer überwacht."
#: ../js/ui/widget.js:316
msgid "Applications"
msgstr "Anwendungen"
#: ../js/ui/widget.js:341
msgid "Recent Documents"
msgstr "Zuletzt geöffnete Dokumente"
#: ../src/shell-global.c:812
#: ../src/shell-global.c:841
msgid "Less than a minute ago"
msgstr "Vor weniger als einer Minute"
#: ../src/shell-global.c:815
#: ../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:818
#: ../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:821
#: ../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:824
#: ../src/shell-global.c:853
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
@ -227,15 +180,3 @@ msgstr "Suchen"
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"
#~ msgid "Find apps or documents"
#~ msgstr "Anwendungen oder Dokumente suchen"
#~ msgid "Browse"
#~ msgstr "Durchsuchen"
#~ msgid "Manager"
#~ msgstr "Verwaltung"
#~ msgid "The user manager object this user is controlled by."
#~ msgstr "Das Benutzerverwaltungsobjekt welches diesen Benutzer überwacht."

186
po/el.po
View File

@ -1,186 +0,0 @@
# translation of gnome-shell.po.master.po to Greek
# Greek 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.
#
# Jennie Petoumenou <epetoumenou@gmail.com>, 2009.
msgid ""
msgstr ""
"Project-Id-Version: gnome-shell.po.master\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell&component=general\n"
"POT-Creation-Date: 2009-10-04 08:03+0000\n"
"PO-Revision-Date: 2009-10-04 10:25+0200\n"
"Last-Translator: Jennie Petoumenou <epetoumenou@gmail.com>\n"
"Language-Team: Greek <<team AT BLOCKSPAM gnome DOT gr>>\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"
#: ../data/gnome-shell.desktop.in.in.h:1
msgid "GNOME Shell"
msgstr "Κέλυφος GNOME"
#: ../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:461
msgid "%a %l:%M %p"
msgstr "%a %l:%M %p"
#: ../js/ui/dash.js:283
msgid "Find..."
msgstr "Εύρεση..."
#: ../js/ui/dash.js:400
msgid "More"
msgstr "Περισσότερα"
#: ../js/ui/dash.js:543
msgid "(see all)"
msgstr "(εμφάνιση όλων)"
#. **** Applications ****
#: ../js/ui/dash.js:763 ../js/ui/dash.js:825
msgid "APPLICATIONS"
msgstr "ΕΦΑΡΜΟΓΕΣ"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:783
msgid "PLACES"
msgstr "ΤΟΠΟΘΕΣΙΕΣ"
#. **** Documents ****
#: ../js/ui/dash.js:790 ../js/ui/dash.js:835
msgid "RECENT DOCUMENTS"
msgstr "ΠΡΟΣΦΑΤΑ ΕΓΓΡΑΦΑ"
#. **** Search Results ****
#: ../js/ui/dash.js:815 ../js/ui/dash.js:955
msgid "SEARCH RESULTS"
msgstr "ΑΠΟΤΕΛΕΣΜΑΤΑ ΑΝΑΖΗΤΗΣΗΣ"
#: ../js/ui/dash.js:830
msgid "PREFERENCES"
msgstr "ΠΡΟΤΙΜΗΣΕΙΣ"
#: ../js/ui/runDialog.js:96
msgid "Please enter a command:"
msgstr "Παρακαλώ εισάγετε μία εντολή:"
#: ../src/shell-global.c:812
msgid "Less than a minute ago"
msgstr "Λιγότερο από ένα λεπτό πριν"
#: ../src/shell-global.c:815
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "%d λεπτό πριν"
msgstr[1] "%d λεπτά πριν"
#: ../src/shell-global.c:818
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "%d ώρα πριν"
msgstr[1] "%d ώρες πριν"
#: ../src/shell-global.c:821
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "%d ημέρα πριν"
msgstr[1] "%d ημέρες πριν"
#: ../src/shell-global.c:824
#, 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"

236
po/es.po
View File

@ -8,13 +8,13 @@ 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-12-18 15:06+0000\n"
"PO-Revision-Date: 2009-12-19 14:41+0100\n"
"POT-Creation-Date: 2009-09-27 16:05+0000\n"
"PO-Revision-Date: 2009-09-28 21:58+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"
"Content-Transfer-Encoding: UTF-8\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: ../data/gnome-shell.desktop.in.in.h:1
@ -25,132 +25,153 @@ msgstr "GNOME Shell"
msgid "Window management and application launching"
msgstr "Gestión de ventanas e inicio de aplicaciones"
#. **** Applications ****
#: ../js/ui/appDisplay.js:252 ../js/ui/dash.js:852
msgid "APPLICATIONS"
msgstr "APLICACIONES"
#: ../js/ui/appDisplay.js:276
msgid "PREFERENCES"
msgstr "PREFERENCIAS"
#: ../js/ui/appDisplay.js:707 ../js/ui/appIcon.js:425
msgid "New Window"
msgstr "Ventana nueva"
#: ../js/ui/appDisplay.js:711 ../js/ui/appIcon.js:429
msgid "Remove from Favorites"
msgstr "Quitar de los favoritos"
#: ../js/ui/appDisplay.js:712 ../js/ui/appIcon.js:430
msgid "Add to Favorites"
msgstr "Añadir a los favoritos"
#: ../js/ui/appDisplay.js:1064
msgid "Drag here to add favorites"
msgstr "Arrastrar aquí para añadir a los favoritos"
#: ../js/ui/dash.js:240
msgid "Find..."
msgstr "Buscar…"
#: ../js/ui/dash.js:437
#| msgid "Search"
msgid "Searching..."
msgstr "Buscando…"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:872 ../js/ui/placeDisplay.js:471
msgid "PLACES"
msgstr "LUGARES"
#. **** Documents ****
#: ../js/ui/dash.js:879
msgid "RECENT DOCUMENTS"
msgstr "DOCUMENTOS RECIENTES"
#. Button on the left side of the panel.
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:227
#. left side
#: ../js/ui/panel.js:269
msgid "Activities"
msgstr "Actividades"
#. Translators: This is a time format.
#: ../js/ui/panel.js:440
#: ../js/ui/panel.js:452
msgid "%a %l:%M %p"
msgstr "%a %H:%M"
#: ../js/ui/placeDisplay.js:99
msgid "Connect to..."
msgstr "Conectar a…"
#: ../js/ui/dash.js:283
msgid "Find..."
msgstr "Buscar…"
#: ../js/ui/runDialog.js:96
#: ../js/ui/dash.js:400
msgid "More"
msgstr "Más"
#: ../js/ui/dash.js:543
msgid "(see all)"
msgstr "(ver todo)"
#. **** Applications ****
#: ../js/ui/dash.js:763 ../js/ui/dash.js:825
msgid "APPLICATIONS"
msgstr "APLICACIONES"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:783
msgid "PLACES"
msgstr "LUGARES"
#. **** Documents ****
#: ../js/ui/dash.js:790 ../js/ui/dash.js:835
msgid "RECENT DOCUMENTS"
msgstr "DOCUMENTOS RECIENTES"
#. **** Search Results ****
#: ../js/ui/dash.js:815 ../js/ui/dash.js:958
msgid "SEARCH RESULTS"
msgstr "RESULTADOS DE LA BÚSQUEDA"
#: ../js/ui/dash.js:830
msgid "PREFERENCES"
msgstr "PREFERENCIAS"
#: ../js/ui/runDialog.js:94
msgid "Please enter a command:"
msgstr "Introduzca un comando:"
#: ../js/ui/runDialog.js:173
#, c-format
msgid "Execution of '%s' failed:"
msgstr "Falló la ejecución de «%s»:"
#. Translators: This is a time format.
#: ../js/ui/widget.js:163
msgid "%H:%M"
msgstr "%H:%M"
#: ../js/ui/widget.js:317
msgid "Applications"
msgstr "Aplicaciones"
#: ../js/ui/widget.js:339
msgid "Recent Documents"
msgstr "Documentos recientes"
#: ../src/shell-global.c:826
#: ../src/shell-global.c:799
msgid "Less than a minute ago"
msgstr "Hace menos de un minuto"
#: ../src/shell-global.c:829
#: ../src/shell-global.c:802
#, 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:832
#: ../src/shell-global.c:805
#, 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:835
#: ../src/shell-global.c:808
#, 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:838
#: ../src/shell-global.c:811
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
msgstr[0] "Hace %d semana"
msgstr[1] "Hace %d semanas"
#: ../src/shell-uri-util.c:89
#: ../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:104
#: ../src/shell-uri-util.c:102
msgid "File System"
msgstr "Sistema de archivos"
#: ../src/shell-uri-util.c:250
#: ../src/shell-uri-util.c:248
msgid "Search"
msgstr "Buscar"
@ -159,58 +180,11 @@ msgstr "Buscar"
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-uri-util.c:300
#: ../src/shell-uri-util.c:298
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"
#~ msgid "Frequent"
#~ msgstr "Frecuentes"
#~ msgid "More"
#~ msgstr "Más"
#~ msgid "(see all)"
#~ msgstr "(ver todo)"
#~ msgid "SEARCH RESULTS"
#~ msgstr "RESULTADOS DE LA BÚSQUEDA"
#~ msgid "Unknown"
#~ msgstr "Desconocido"
#~ msgid "Can't lock screen: %s"
#~ msgstr "No se puede bloquear la pantalla: %s"
#~ msgid "Can't temporarily set screensaver to blank screen: %s"
#~ msgstr ""
#~ "No se puede establecer temporalmente el salvapantallas a oscurecer "
#~ "pantalla: %s"
#~ msgid "Can't logout: %s"
#~ msgstr "No se puede salir de la sesión: %s"
#~ msgid "Account Information..."
#~ msgstr "Información de la cuenta…"
#~ msgid "Sidebar"
#~ msgstr "Barra lateral"
#~ msgid "System Preferences..."
#~ msgstr "Preferencias del sistema…"
#~ msgid "Lock Screen"
#~ msgstr "Bloquear la pantalla"
#~ msgid "Switch User"
#~ msgstr "Cambiar de usuario"
#~ msgid "Log Out..."
#~ msgstr "Salir…"
#~ msgid "Shut Down..."
#~ msgstr "Apagar…"
#~ msgid "Browse"
#~ msgstr "Examine"

226
po/fi.po
View File

@ -1,226 +0,0 @@
# gnome-shell Finnish translation
# Copyright (C) 2009 Timo Jyrinki
# This file is distributed under the same license as the gnome-shell package.
# Timo Jyrinki <timo.jyrinki@iki.fi>, 2009.
#
msgid ""
msgstr ""
"Project-Id-Version: gnome-shell\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-11-04 11:16+0200\n"
"PO-Revision-Date: 2009-11-04 11:16+0200\n"
"Last-Translator: Timo Jyrinki <timo.jyrinki@iki.fi>\n"
"Language-Team: Finnish <gnome-fi-laatu@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=2; plural=n != 1;\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 "Ikkunanhallinta ja sovelluksien käynnistäminen"
#: ../js/ui/appDisplay.js:332
msgid "Frequent"
msgstr "Usein käytetyt"
#: ../js/ui/appDisplay.js:867
msgid "Drag here to add favorites"
msgstr "Raahaa tähän lisätäksesi suosikkeihin"
#: ../js/ui/appIcon.js:426
msgid "New Window"
msgstr "Uusi ikkuna"
#: ../js/ui/appIcon.js:430
msgid "Remove from Favorites"
msgstr "Poista suosikeista"
#: ../js/ui/appIcon.js:431
msgid "Add to Favorites"
msgstr "Lisää suosikkeihin"
#: ../js/ui/dash.js:283
msgid "Find..."
msgstr "Etsi..."
#: ../js/ui/dash.js:400
msgid "More"
msgstr "Lisää"
#: ../js/ui/dash.js:543
msgid "(see all)"
msgstr "(näytä kaikki)"
#. **** Applications ****
#: ../js/ui/dash.js:725 ../js/ui/dash.js:787
msgid "APPLICATIONS"
msgstr "SOVELLUKSET"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:745
msgid "PLACES"
msgstr "SIJAINNIT"
#. **** Documents ****
#: ../js/ui/dash.js:752 ../js/ui/dash.js:797
msgid "RECENT DOCUMENTS"
msgstr "VIIMEISIMMÄT ASIAKIRJAT"
#. **** Search Results ****
#: ../js/ui/dash.js:777 ../js/ui/dash.js:961
msgid "SEARCH RESULTS"
msgstr "HAKUTULOKSET"
#: ../js/ui/dash.js:792
msgid "PREFERENCES"
msgstr "ASETUKSET"
#. Button on the left side of the panel.
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:274
msgid "Activities"
msgstr "Toiminnot"
#. Translators: This is a time format.
#: ../js/ui/panel.js:491
msgid "%a %l:%M %p"
msgstr "%a %I.%M"
#: ../js/ui/places.js:178
msgid "Connect to..."
msgstr "Yhdistä..."
#: ../js/ui/runDialog.js:96
msgid "Please enter a command:"
msgstr "Syötä komento:"
#: ../js/ui/runDialog.js:173
#, c-format
msgid "Execution of '%s' failed:"
msgstr ""
#. Translators: This is a time format.
#: ../js/ui/widget.js:163
msgid "%H:%M"
msgstr "%I.%M"
#: ../js/ui/widget.js:317
msgid "Applications"
msgstr "Sovellukset"
#: ../js/ui/widget.js:339
msgid "Recent Documents"
msgstr "Viimeisimmät asiakirjat"
#: ../src/shell-global.c:821
msgid "Less than a minute ago"
msgstr "Alle minuutti sitten"
#: ../src/shell-global.c:824
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "%d minuutti sitten"
msgstr[1] "%d minuuttia sitten"
#: ../src/shell-global.c:827
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "%d tunti sitten"
msgstr[1] "%d tuntia sitten"
#: ../src/shell-global.c:830
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "%d päivä sitten"
msgstr[1] "%d päivää sitten"
#: ../src/shell-global.c:833
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
msgstr[0] "%d viikko sitten"
msgstr[1] "%d viikkoa sitten"
#: ../src/shell-status-menu.c:156
msgid "Unknown"
msgstr "Tuntematon"
#: ../src/shell-status-menu.c:212
#, c-format
msgid "Can't lock screen: %s"
msgstr "Näyttöä ei voi lukita: %s"
#: ../src/shell-status-menu.c:227
#, c-format
msgid "Can't temporarily set screensaver to blank screen: %s"
msgstr ""
#: ../src/shell-status-menu.c:351
#, c-format
msgid "Can't logout: %s"
msgstr ""
#: ../src/shell-status-menu.c:492
msgid "Account Information..."
msgstr "Käyttäjätilin tiedot..."
#: ../src/shell-status-menu.c:502
msgid "Sidebar"
msgstr "Sivupalkki"
#: ../src/shell-status-menu.c:510
msgid "System Preferences..."
msgstr "Järjestelmän asetukset"
#: ../src/shell-status-menu.c:525
msgid "Lock Screen"
msgstr "Lukitse näyttö"
#: ../src/shell-status-menu.c:535
msgid "Switch User"
msgstr "Vaihda käyttäjää"
#. Only show switch user if there are other users
#. Log Out
#: ../src/shell-status-menu.c:546
msgid "Log Out..."
msgstr "Kirjaudu ulos..."
#. Shut down
#: ../src/shell-status-menu.c:557
msgid "Shut Down..."
msgstr "Sammuta..."
#: ../src/shell-uri-util.c:87
msgid "Home Folder"
msgstr "Kotikansio"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-uri-util.c:102
msgid "File System"
msgstr "Tiedostojärjestelmä"
#: ../src/shell-uri-util.c:248
msgid "Search"
msgstr "Haku"
#. 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"

102
po/fr.po
View File

@ -6,13 +6,13 @@
#
msgid ""
msgstr ""
"Project-Id-Version: gnome-shell master fr\n"
"Project-Id-Version: HEAD\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&component=general\n"
"POT-Creation-Date: 2009-11-13 17:44+0000\n"
"PO-Revision-Date: 2009-12-05 16:43+0100\n"
"Last-Translator: Pablo Martin-Gomez <pablo.martin-gomez@laposte.net>\n"
"Language-Team: GNOME French Team <gnomefr@traduc.org>\n"
"POT-Creation-Date: 2009-09-09 21:30+0000\n"
"PO-Revision-Date: 2009-09-11 21:40+0200\n"
"Last-Translator: Mathieu Bridon <bochecha@fedoraproject.org>\n"
"Language-Team: GNOME French Team\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@ -26,115 +26,80 @@ msgstr "GNOME Shell"
msgid "Window management and application launching"
msgstr "Gestion des fenêtres et lancement des applications"
#: ../js/ui/appDisplay.js:696
msgid "Drag here to add favorites"
msgstr "Glisser ici pour ajouter aux favoris"
#. left side
#: ../js/ui/panel.js:269
msgid "Activities"
msgstr "Activités"
#: ../js/ui/appIcon.js:425
msgid "New Window"
msgstr "Nouvelle fenêtre"
#. Translators: This is a time format.
#: ../js/ui/panel.js:452
msgid "%a %l:%M %p"
msgstr "%a %H:%M"
#: ../js/ui/appIcon.js:429
msgid "Remove from Favorites"
msgstr "Enlever des favoris"
#: ../js/ui/appIcon.js:430
msgid "Add to Favorites"
msgstr "Ajouter aux favoris"
#: ../js/ui/dash.js:237
#: ../js/ui/dash.js:255
msgid "Find..."
msgstr "Rechercher..."
#: ../js/ui/dash.js:372
msgid "Browse"
msgstr "Parcourir"
#: ../js/ui/dash.js:508
msgid "(see all)"
msgstr "(tout afficher)"
#. **** Applications ****
#: ../js/ui/dash.js:656 ../js/ui/dash.js:718
#: ../js/ui/dash.js:700 ../js/ui/dash.js:756 ../js/ui/dash.js:887
msgid "APPLICATIONS"
msgstr "APPLICATIONS"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:676 ../js/ui/dash.js:733
#: ../js/ui/dash.js:720
msgid "PLACES"
msgstr "RACCOURCIS"
#. **** Documents ****
#: ../js/ui/dash.js:683 ../js/ui/dash.js:728
#: ../js/ui/dash.js:727 ../js/ui/dash.js:768 ../js/ui/dash.js:861
msgid "RECENT DOCUMENTS"
msgstr "DOCUMENTS RÉCENTS"
#. **** Search Results ****
#: ../js/ui/dash.js:708 ../js/ui/dash.js:898
#: ../js/ui/dash.js:746 ../js/ui/dash.js:850 ../js/ui/dash.js:876
msgid "SEARCH RESULTS"
msgstr "RÉSULTATS DE LA RECHERCHE"
#: ../js/ui/dash.js:723
msgid "PREFERENCES"
msgstr "PRÉFÉRENCES"
#. Button on the left side of the panel.
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:274
msgid "Activities"
msgstr "Activités"
#. Translators: This is a time format.
#: ../js/ui/panel.js:491
msgid "%a %l:%M %p"
msgstr "%a %H:%M"
#: ../js/ui/placeDisplay.js:84
msgid "Connect to..."
msgstr "Connexion à..."
#: ../js/ui/runDialog.js:96
#: ../js/ui/runDialog.js:90
msgid "Please enter a command:"
msgstr "Veuillez saisir une commande :"
#: ../js/ui/runDialog.js:173
#, c-format
msgid "Execution of '%s' failed:"
msgstr "Exécution de « %s » impossible :"
#. Translators: This is a time format.
#: ../js/ui/widget.js:163
msgid "%H:%M"
msgstr "%H:%M"
#: ../js/ui/widget.js:317
msgid "Applications"
msgstr "Applications"
#: ../js/ui/widget.js:339
msgid "Recent Documents"
msgstr "Documents récents"
#: ../src/shell-global.c:821
#: ../src/shell-global.c:799
msgid "Less than a minute ago"
msgstr "Il y a moins d'une minute"
#: ../src/shell-global.c:824
#: ../src/shell-global.c:802
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "Il y a %d minute"
msgstr[1] "Il y a %d minutes"
#: ../src/shell-global.c:827
#: ../src/shell-global.c:805
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "Il y a %d heure"
msgstr[1] "Il y a %d heures"
#: ../src/shell-global.c:830
#: ../src/shell-global.c:808
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "Il y a %d jour"
msgstr[1] "Il y a %d jours"
#: ../src/shell-global.c:833
#: ../src/shell-global.c:811
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
@ -215,6 +180,3 @@ msgstr "Recherche"
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s : %2$s"
#~ msgid "Browse"
#~ msgstr "Parcourir"

258
po/gl.po
View File

@ -2,22 +2,19 @@
# 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.
# Anton Meixome <certima@certima.net>, 2009.
# Antón Méixome <meixome@certima.net>, 2009.
#
msgid ""
msgstr ""
"Project-Id-Version: gnome-shell master\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-12-28 17:07+0100\n"
"PO-Revision-Date: 2009-12-28 08:11+0100\n"
"Last-Translator: Antón Méixome <meixome@certima.net>\n"
"Language-Team: Galician Proxecto Trasno <proxecto@trasno.net>\n"
"POT-Creation-Date: 2009-09-15 23:06+0200\n"
"PO-Revision-Date: 2009-09-10 22:32+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"
"Language: gl\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Virtaal 0.4.0\n"
#: ../data/gnome-shell.desktop.in.in.h:1
msgid "GNOME Shell"
@ -25,133 +22,154 @@ msgstr "GNOME Shell"
#: ../data/gnome-shell.desktop.in.in.h:2
msgid "Window management and application launching"
msgstr "Xestor de xanelas e lanzamento de aplicativos"
msgstr "Xestor de xanelas e lanzado de aplicativos"
#. **** Applications ****
#: ../js/ui/appDisplay.js:252 ../js/ui/dash.js:858
msgid "APPLICATIONS"
msgstr "APLICATIVOS"
#: ../js/ui/appDisplay.js:276
msgid "PREFERENCES"
msgstr "PREFERENCIAS"
#: ../js/ui/appDisplay.js:707 ../js/ui/appIcon.js:425
msgid "New Window"
msgstr "Xanela nova"
#: ../js/ui/appDisplay.js:711 ../js/ui/appIcon.js:429
msgid "Remove from Favorites"
msgstr "Eliminar de Favoritos"
#: ../js/ui/appDisplay.js:712 ../js/ui/appIcon.js:430
msgid "Add to Favorites"
msgstr "Engadir a Favoritos"
#: ../js/ui/appDisplay.js:1064
msgid "Drag here to add favorites"
msgstr "Arrastra aquí para engadir favoritos"
#: ../js/ui/dash.js:240
msgid "Find..."
msgstr "Buscar..."
#: ../js/ui/dash.js:437
msgid "Searching..."
msgstr "Buscando..."
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:878 ../js/ui/placeDisplay.js:519
msgid "PLACES"
msgstr "LUGARES"
#. **** Documents ****
#: ../js/ui/dash.js:885
msgid "RECENT DOCUMENTS"
msgstr "DOCUMENTOS RECENTES"
#. Button on the left side of the panel.
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:227
#. left side
#: ../js/ui/panel.js:269
msgid "Activities"
msgstr "Actividades"
#. Translators: This is a time format.
#: ../js/ui/panel.js:440
#: ../js/ui/panel.js:452
msgid "%a %l:%M %p"
msgstr "%a %l:%M %p"
#: ../js/ui/placeDisplay.js:144
msgid "Connect to..."
msgstr "Conectar con..."
#: ../js/ui/dash.js:283
msgid "Find..."
msgstr "Buscar..."
#: ../js/ui/runDialog.js:235
#: ../js/ui/dash.js:400
msgid "Browse"
msgstr "Explorar"
#: ../js/ui/dash.js:536
msgid "(see all)"
msgstr "(ver todos)"
#. **** Applications ****
#: ../js/ui/dash.js:753 ../js/ui/dash.js:809
msgid "APPLICATIONS"
msgstr "APLICATIVOS"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:773
msgid "PLACES"
msgstr "LUGARES"
#. **** Documents ****
#: ../js/ui/dash.js:780 ../js/ui/dash.js:819
msgid "RECENT DOCUMENTS"
msgstr "DOCUMENTOS RECENTES"
#. **** Search Results ****
#: ../js/ui/dash.js:799 ../js/ui/dash.js:931
msgid "SEARCH RESULTS"
msgstr "RESULTADOS DA BÚSQUEDA"
#: ../js/ui/dash.js:814
msgid "PREFERENCES"
msgstr ""
#: ../js/ui/runDialog.js:96
msgid "Please enter a command:"
msgstr "Insira unha orde:"
#: ../js/ui/runDialog.js:351
#, c-format
msgid "Execution of '%s' failed:"
msgstr "Fallou a execución de %s"
#. Translators: This is a time format.
#: ../js/ui/widget.js:163
msgid "%H:%M"
msgstr "%H:%M"
#: ../js/ui/widget.js:317
msgid "Applications"
msgstr "Aplicativos"
#: ../js/ui/widget.js:339
msgid "Recent Documents"
msgstr "Documentos recentes"
#: ../src/shell-global.c:890
#: ../src/shell-global.c:799
msgid "Less than a minute ago"
msgstr "Hai menos dun minuto"
msgstr "Menos de un minuto"
#: ../src/shell-global.c:893
#: ../src/shell-global.c:802
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "hai %d minuto"
msgstr[1] "hai %d minutos"
msgstr[0] "fai %d minuto"
msgstr[1] "fai %d minutos"
#: ../src/shell-global.c:896
#: ../src/shell-global.c:805
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "hai %d hora"
msgstr[1] "hai %d horas"
msgstr[0] "fai %d hora"
msgstr[1] "fai %d horas"
#: ../src/shell-global.c:899
#: ../src/shell-global.c:808
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "hai %d día"
msgstr[1] "hai %d días"
msgstr[0] "fai %d día"
msgstr[1] "fai %d días"
#: ../src/shell-global.c:902
#: ../src/shell-global.c:811
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
msgstr[0] "hai %d semana"
msgstr[1] "hai %d semanas"
msgstr[0] "fai %d semana"
msgstr[1] "fai %d semanas"
#: ../src/shell-uri-util.c:89
#: ../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:104
#: ../src/shell-uri-util.c:102
msgid "File System"
msgstr "Sistema de ficheiros"
#: ../src/shell-uri-util.c:250
#: ../src/shell-uri-util.c:248
msgid "Search"
msgstr "Buscar"
@ -160,55 +178,11 @@ msgstr "Buscar"
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-uri-util.c:300
#: ../src/shell-uri-util.c:298
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"
#~ msgid "SEARCH RESULTS"
#~ msgstr "RESULTADOS DA BUSCA"
#~ msgid "Unknown"
#~ msgstr "Descoñecido"
#~ msgid "Can't lock screen: %s"
#~ msgstr "Non foi posíbel bloquear a pantalla: %s"
#~ msgid "Can't temporarily set screensaver to blank screen: %s"
#~ msgstr ""
#~ "Non foi posíbel estabelecer temporalmente o salvapantallas a unha "
#~ "pantalla en branco: %s"
#~ msgid "Can't logout: %s"
#~ msgstr "Non foi posíbel pechar a sesión: %s"
#~ msgid "Account Information..."
#~ msgstr "Información da conta..."
#~ msgid "Sidebar"
#~ msgstr "Barra lateral"
#~ msgid "System Preferences..."
#~ msgstr "Preferencias do sistema..."
#~ msgid "Lock Screen"
#~ msgstr "Bloquear pantalla"
#~ msgid "Switch User"
#~ msgstr "Cambiar de usuario"
#~ msgid "Log Out..."
#~ msgstr "Saír da sesión..."
#~ msgid "Shut Down..."
#~ msgstr "Apagar..."
#~ msgid "Browse"
#~ msgstr "Explorar"
#~ msgid "(see all)"
#~ msgstr "(ver todos)"
#~ msgid "Find apps or documents"
#~ msgstr "Atopar aplicativos ou documentos"

215
po/he.po
View File

@ -1,215 +0,0 @@
# Hebrew 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.
# liel <lielft@gmail.com>, 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-11-13 17:44+0000\n"
"PO-Revision-Date: 2009-11-28 17:33+0200\n"
"Last-Translator: Liel Fridman <lielft@gmail.com>\n"
"Language-Team: Hebrew <he@li.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"
#: ../data/gnome-shell.desktop.in.in.h:2
msgid "Window management and application launching"
msgstr "ניהול חלונות והרצת יישומים"
#: ../js/ui/appDisplay.js:696
msgid "Drag here to add favorites"
msgstr "יש לגרור פריטים לכאן כדי להוסיף מועדפים"
#: ../js/ui/appIcon.js:425
msgid "New Window"
msgstr "חלון חדש"
#: ../js/ui/appIcon.js:429
msgid "Remove from Favorites"
msgstr "הסר מהמועדפים"
#: ../js/ui/appIcon.js:430
msgid "Add to Favorites"
msgstr "הוסף למועדפים"
#: ../js/ui/dash.js:237
msgid "Find..."
msgstr "חפש..."
#. **** Applications ****
#: ../js/ui/dash.js:656 ../js/ui/dash.js:718
msgid "APPLICATIONS"
msgstr "יישומים"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:676 ../js/ui/dash.js:733
msgid "PLACES"
msgstr "מקומות"
#. **** Documents ****
#: ../js/ui/dash.js:683 ../js/ui/dash.js:728
msgid "RECENT DOCUMENTS"
msgstr "מסמכים אחרונים"
#. **** Search Results ****
#: ../js/ui/dash.js:708 ../js/ui/dash.js:898
msgid "SEARCH RESULTS"
msgstr "תוצאות חיפוש"
#: ../js/ui/dash.js:723
msgid "PREFERENCES"
msgstr "העדפות"
#. Button on the left side of the panel.
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:274
msgid "Activities"
msgstr "פעילויות"
#. Translators: This is a time format.
#: ../js/ui/panel.js:491
msgid "%a %l:%M %p"
msgstr "%a %l:%M %p"
#: ../js/ui/placeDisplay.js:84
msgid "Connect to..."
msgstr "התחבר אל..."
#: ../js/ui/runDialog.js:96
msgid "Please enter a command:"
msgstr "נא להזין פקודה:"
#: ../js/ui/runDialog.js:173
#, c-format
msgid "Execution of '%s' failed:"
msgstr "ההרצה של '%s' נכשלה:"
#. Translators: This is a time format.
#: ../js/ui/widget.js:163
msgid "%H:%M"
msgstr "%H:%M"
#: ../js/ui/widget.js:317
msgid "Applications"
msgstr "יישומים"
#: ../js/ui/widget.js:339
msgid "Recent Documents"
msgstr "מסמכים אחרונים"
#: ../src/shell-global.c:821
msgid "Less than a minute ago"
msgstr "לפני פחות מדקה"
#: ../src/shell-global.c:824
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "לפני דקה"
msgstr[1] "לפני %d דקות"
#: ../src/shell-global.c:827
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "לפני שעה"
msgstr[1] "לפני %d שעות"
#: ../src/shell-global.c:830
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "לפני יום"
msgstr[1] "לפני %d ימים"
#: ../src/shell-global.c:833
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
msgstr[0] "לפני שבוע"
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"

240
po/it.po
View File

@ -6,9 +6,10 @@
msgid ""
msgstr ""
"Project-Id-Version: gnome-shell\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-12-28 21:58+0100\n"
"PO-Revision-Date: 2009-12-28 21:59+0100\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&component=general\n"
"POT-Creation-Date: 2009-09-05 00:19+0000\n"
"PO-Revision-Date: 2009-09-06 18:31+0200\n"
"Last-Translator: Milo Casagrande <milo@ubuntu.com>\n"
"Language-Team: Italian <tp@lists.linux.it>\n"
"MIME-Version: 1.0\n"
@ -24,132 +25,151 @@ msgstr "GNOME Shell"
msgid "Window management and application launching"
msgstr "Gestione finestre e avvio applicazioni"
#. **** Applications ****
#: ../js/ui/appDisplay.js:252 ../js/ui/dash.js:858
msgid "APPLICATIONS"
msgstr "Applicazioni"
#: ../js/ui/appDisplay.js:276
msgid "PREFERENCES"
msgstr "Preferenze"
#: ../js/ui/appDisplay.js:707 ../js/ui/appIcon.js:425
msgid "New Window"
msgstr "Nuova finestra"
#: ../js/ui/appDisplay.js:711 ../js/ui/appIcon.js:429
msgid "Remove from Favorites"
msgstr "Rimuovi dai preferiti"
#: ../js/ui/appDisplay.js:712 ../js/ui/appIcon.js:430
msgid "Add to Favorites"
msgstr "Aggiungi ai preferiti"
#: ../js/ui/appDisplay.js:1064
msgid "Drag here to add favorites"
msgstr "Trascinare qui per aggiungere ai preferiti"
#: ../js/ui/dash.js:240
msgid "Find..."
msgstr "Trova..."
#: ../js/ui/dash.js:437
msgid "Searching..."
msgstr "Ricerca..."
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:878 ../js/ui/placeDisplay.js:519
msgid "PLACES"
msgstr "Risorse"
#. **** Documents ****
#: ../js/ui/dash.js:885
msgid "RECENT DOCUMENTS"
msgstr "Documenti recenti"
#. Button on the left side of the panel.
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:227
#. left side
#: ../js/ui/panel.js:271
msgid "Activities"
msgstr "Attività"
# (ndt) proviamo col k, se non funge, sappiamo il perché...
#. Translators: This is a time format.
#: ../js/ui/panel.js:440
#: ../js/ui/panel.js:454
msgid "%a %l:%M %p"
msgstr "%a %k.%M"
#: ../js/ui/placeDisplay.js:144
msgid "Connect to..."
msgstr "Connetti a..."
#: ../js/ui/dash.js:256
msgid "Find..."
msgstr "Trova..."
#: ../js/ui/runDialog.js:235
#: ../js/ui/dash.js:374
msgid "Browse"
msgstr "Esplora"
#: ../js/ui/dash.js:511
msgid "(see all)"
msgstr "(vedi tutto)"
#. **** Applications ****
#: ../js/ui/dash.js:705 ../js/ui/dash.js:761 ../js/ui/dash.js:893
msgid "APPLICATIONS"
msgstr "Applicazioni"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:725
msgid "PLACES"
msgstr "Risorse"
#. **** Documents ****
#: ../js/ui/dash.js:732 ../js/ui/dash.js:773 ../js/ui/dash.js:867
msgid "RECENT DOCUMENTS"
msgstr "Documenti recenti"
#. **** Search Results ****
#: ../js/ui/dash.js:751 ../js/ui/dash.js:856 ../js/ui/dash.js:882
msgid "SEARCH RESULTS"
msgstr "Risultati ricerca"
#: ../js/ui/runDialog.js:82
msgid "Please enter a command:"
msgstr "Inserire un comando:"
#: ../js/ui/runDialog.js:351
#, c-format
msgid "Execution of '%s' failed:"
msgstr "Esecuzione di «%s» non riuscita:"
#. Translators: This is a time format.
#: ../js/ui/widget.js:163
msgid "%H:%M"
msgstr "%k.%M"
#: ../js/ui/widget.js:317
msgid "Applications"
msgstr "Applicazioni"
#: ../js/ui/widget.js:339
msgid "Recent Documents"
msgstr "Documenti recenti"
#: ../src/shell-global.c:890
#: ../src/shell-global.c:840
msgid "Less than a minute ago"
msgstr "Meno di un minuto fa"
#: ../src/shell-global.c:893
#: ../src/shell-global.c:843
#, 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:896
#: ../src/shell-global.c:846
#, 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:899
#: ../src/shell-global.c:849
#, 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:902
#: ../src/shell-global.c:852
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
msgstr[0] "%d settimana fa"
msgstr[1] "%d settimane fa"
#: ../src/shell-uri-util.c:89
# (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:104
#: ../src/shell-uri-util.c:102
msgid "File System"
msgstr "File system"
#: ../src/shell-uri-util.c:250
#: ../src/shell-uri-util.c:248
msgid "Search"
msgstr "Cerca"
@ -159,55 +179,19 @@ msgstr "Cerca"
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-uri-util.c:300
#: ../src/shell-uri-util.c:298
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"
#~ msgid "Frequent"
#~ msgstr "Frequente"
# (ndt) è da valutare se è troppo lunga, è in una casella di ricerca
#~ msgid "Find apps or documents"
#~ msgstr "Trova programmi e documenti"
#~ msgid "More"
#~ msgstr "Altro"
# (ndt) no idea...
#~ msgid "Manager"
#~ msgstr "Manager"
#~ msgid "(see all)"
#~ msgstr "(vedi tutto)"
#~ msgid "SEARCH RESULTS"
#~ msgstr "Risultati ricerca"
# (ndt) valutare se vada al femminile
#~ msgid "Unknown"
#~ msgstr "Sconosciuto"
#~ msgid "Can't lock screen: %s"
#~ msgstr "Impossibile bloccare lo schermo: %s"
#~ msgid "Can't temporarily set screensaver to blank screen: %s"
#~ msgstr ""
#~ "Impossibile impostare temporaneamente il salva schermo a schermo nero: %s "
#~ msgid "Can't logout: %s"
#~ msgstr "Impossibile terminare la sessione: %s"
#~ msgid "Account Information..."
#~ msgstr "Informazioni account..."
#~ msgid "Sidebar"
#~ msgstr "Barra laterale"
#~ msgid "System Preferences..."
#~ msgstr "Preferenze di sistema..."
#~ msgid "Lock Screen"
#~ msgstr "Blocca schermo"
#~ msgid "Switch User"
#~ msgstr "Cambia utente"
#~ msgid "Log Out..."
#~ msgstr "Termina sessione..."
# (ndt) da valutare... pare che ora anche Windows usi 'Arresta...'...
#~ msgid "Shut Down..."
#~ msgstr "Spegni..."
# (ndt) no idea...
#~ msgid "The user manager object this user is controlled by."
#~ msgstr "L'oggetto user manager che controlla questo utente."

View File

@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gnome-shell 0.4\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-10-03 10:47+0200\n"
"PO-Revision-Date: 2009-10-03 10:48+0200\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"
@ -25,83 +25,70 @@ msgid "Window management and application launching"
msgstr "Vindushåndtering og oppstart av programmer"
#. left side
#: ../js/ui/panel.js:271
#: ../js/ui/panel.js:266
msgid "Activities"
msgstr "Aktiviteter"
#. Translators: This is a time format.
#: ../js/ui/panel.js:461
#: ../js/ui/panel.js:433
msgid "%a %l:%M %p"
msgstr "%a %l:%M"
#: ../js/ui/dash.js:283
msgid "Find..."
msgstr "Finn..."
#: ../js/ui/dash.js:250
msgid "Find apps or documents"
msgstr "Finn programmer eller dokumenter"
#: ../js/ui/dash.js:400
msgid "More"
msgstr "Mer"
#: ../js/ui/dash.js:543
msgid "(see all)"
msgstr "(se alle)"
#: ../js/ui/dash.js:368
msgid "Browse"
msgstr "Bla gjennom"
#. **** Applications ****
#: ../js/ui/dash.js:763 ../js/ui/dash.js:825
#: ../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:783
#: ../js/ui/dash.js:597
msgid "PLACES"
msgstr "STEDER"
#. **** Documents ****
#: ../js/ui/dash.js:790 ../js/ui/dash.js:835
msgid "RECENT DOCUMENTS"
msgstr "SISTE DOKUMENTER"
#. **** Search Results ****
#: ../js/ui/dash.js:815 ../js/ui/dash.js:955
msgid "SEARCH RESULTS"
msgstr "SØKERESULTATER"
#: ../js/ui/dash.js:830
msgid "PREFERENCES"
msgstr "BRUKERVALG"
#: ../js/ui/runDialog.js:96
#: ../js/ui/runDialog.js:75
msgid "Please enter a command:"
msgstr "Oppgi en kommando:"
#: ../src/shell-global.c:812
#: ../src/shell-global.c:841
msgid "Less than a minute ago"
msgstr "Mindre enn ett minutt siden"
#: ../src/shell-global.c:815
#: ../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:818
#: ../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:821
#: ../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:824
#: ../src/shell-global.c:853
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"

View File

@ -8,8 +8,8 @@ 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-10-09 22:10+0000\n"
"PO-Revision-Date: 2009-10-15 06:18+0530\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"
@ -26,123 +26,80 @@ msgstr "ਗਨੋਮ ਸ਼ੈਲ"
msgid "Window management and application launching"
msgstr "ਵਿੰਡੋ ਪਰਬੰਧ ਅਤੇ ਐਪਲੀਕੇਸ਼ਨ ਚਲਾਓ"
#: ../js/ui/appDisplay.js:335
msgid "Frequent"
msgstr "ਅਕਸਰ"
#. left side
#: ../js/ui/panel.js:271
msgid "Activities"
msgstr "ਸਰਗਰਮੀਆਂ"
#: ../js/ui/appIcon.js:462
msgid "New Window"
msgstr "ਨਵੀਂ ਵਿੰਡੋ"
#. Translators: This is a time format.
#: ../js/ui/panel.js:454
msgid "%a %l:%M %p"
msgstr "%a %l:%M %p"
#: ../js/ui/appIcon.js:475
msgid "Remove from Favorites"
msgstr "ਪਸੰਦ ਵਿੱਚੋਂ ਹਟਾਓ"
#: ../js/ui/appIcon.js:476
msgid "Add to Favorites"
msgstr "ਪਸੰਦ 'ਚ ਸ਼ਾਮਲ ਕਰੋ"
#: ../js/ui/dash.js:283
#: ../js/ui/dash.js:256
msgid "Find..."
msgstr "ਖੋਜ..."
#: ../js/ui/dash.js:400
msgid "More"
msgstr "ਹੋਰ"
#: ../js/ui/dash.js:374
msgid "Browse"
msgstr "ਝਲਕ"
#: ../js/ui/dash.js:543
#: ../js/ui/dash.js:451
msgid "(see all)"
msgstr "(ਸਭ ਵੇਖੋ)"
#. **** Applications ****
#: ../js/ui/dash.js:763 ../js/ui/dash.js:825
#: ../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:783
#: ../js/ui/dash.js:653
msgid "PLACES"
msgstr "ਥਾਵਾਂ"
#. **** Documents ****
#: ../js/ui/dash.js:790 ../js/ui/dash.js:835
#: ../js/ui/dash.js:660 ../js/ui/dash.js:692
msgid "RECENT DOCUMENTS"
msgstr "ਤਾਜ਼ਾ ਡੌਕੂਮੈਂਟ"
#. **** Search Results ****
#: ../js/ui/dash.js:815 ../js/ui/dash.js:955
#: ../js/ui/dash.js:679
msgid "SEARCH RESULTS"
msgstr "ਖੋਜ ਨਤੀਜੇ"
#: ../js/ui/dash.js:830
msgid "PREFERENCES"
msgstr "ਪਸੰਦ"
#. Button on the left side of the panel.
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:272
msgid "Activities"
msgstr "ਸਰਗਰਮੀਆਂ"
#. Translators: This is a time format.
#: ../js/ui/panel.js:464
msgid "%a %l:%M %p"
msgstr "%a %l:%M %p"
#: ../js/ui/places.js:178
msgid "Connect to..."
msgstr "...ਨਾਲ ਕੁਨੈਕਟ ਕਰੋ"
#: ../js/ui/runDialog.js:96
#: ../js/ui/runDialog.js:82
msgid "Please enter a command:"
msgstr "ਕਮਾਂਡ ਦਿਓ ਜੀ:"
#: ../js/ui/runDialog.js:173
#, c-format
msgid "Execution of '%s' failed:"
msgstr "'%s' ਚਲਾਉਣ ਲਈ ਫੇਲ੍ਹ:"
#. Translators: This is a time format.
#: ../js/ui/widget.js:162
msgid "%H:%M"
msgstr "%H:%M"
#: ../js/ui/widget.js:316
msgid "Applications"
msgstr "ਐਪਲੀਕੇਸ਼ਨ"
#: ../js/ui/widget.js:341
msgid "Recent Documents"
msgstr "ਤਾਜ਼ਾ ਡੌਕੂਮੈਂਟ"
#: ../src/shell-global.c:812
#: ../src/shell-global.c:840
msgid "Less than a minute ago"
msgstr "ਇੱਕ ਮਿੰਟ ਤੋਂ ਘੱਟ ਚਿਰ ਪਹਿਲਾਂ"
#: ../src/shell-global.c:815
#: ../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:818
#: ../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:821
#: ../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:824
#: ../src/shell-global.c:852
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
@ -223,5 +180,3 @@ msgstr "ਖੋਜ"
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"
#~ msgid "Browse"
#~ msgstr "ਝਲਕ"

View File

@ -7,11 +7,10 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&component=general\n"
"POT-Creation-Date: 2009-11-12 21:33+0000\n"
"PO-Revision-Date: 2009-11-14 11:53-0200\n"
"Last-Translator: Amanda Magalhães <amandinhakee@gmail.com>\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-09-21 08:39-0300\n"
"PO-Revision-Date: 2009-09-20 08:41-0300\n"
"Last-Translator: Rodrigo Flores <mail@rodrigoflores.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"
@ -26,115 +25,84 @@ msgstr "GNOME Shell"
msgid "Window management and application launching"
msgstr "Gerenciamento de janelas e lançador de aplicações"
#: ../js/ui/appDisplay.js:696
msgid "Drag here to add favorites"
msgstr "Arraste até aqui para adicionar aos favoritos"
#. left side
#: ../js/ui/panel.js:269
msgid "Activities"
msgstr "Atividades"
#: ../js/ui/appIcon.js:425
msgid "New Window"
msgstr "Nova janela"
#. Translators: This is a time format.
#: ../js/ui/panel.js:452
msgid "%a %l:%M %p"
msgstr "%a %l:%M %p"
#: ../js/ui/appIcon.js:429
msgid "Remove from Favorites"
msgstr "Remover dos Favoritos"
#: ../js/ui/appIcon.js:430
msgid "Add to Favorites"
msgstr "Adicionar aos Favoritos"
#: ../js/ui/dash.js:237
#: ../js/ui/dash.js:283
msgid "Find..."
msgstr "Procurar..."
msgstr "Encontre..."
#: ../js/ui/dash.js:400
msgid "Browse"
msgstr "Navegar"
#: ../js/ui/dash.js:536
msgid "(see all)"
msgstr "(veja todos)"
#. **** Applications ****
#: ../js/ui/dash.js:656 ../js/ui/dash.js:718
#: ../js/ui/dash.js:753 ../js/ui/dash.js:809
msgid "APPLICATIONS"
msgstr "APLICATIVOS"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:676 ../js/ui/dash.js:733
#: ../js/ui/dash.js:773
msgid "PLACES"
msgstr "LOCAIS"
#. **** Documents ****
#: ../js/ui/dash.js:683 ../js/ui/dash.js:728
#: ../js/ui/dash.js:780 ../js/ui/dash.js:819
msgid "RECENT DOCUMENTS"
msgstr "DOCUMENTOS RECENTES"
#. **** Search Results ****
#: ../js/ui/dash.js:708 ../js/ui/dash.js:898
#: ../js/ui/dash.js:799 ../js/ui/dash.js:931
msgid "SEARCH RESULTS"
msgstr "RESULTADOS DA BUSCA"
#: ../js/ui/dash.js:723
#: ../js/ui/dash.js:814
msgid "PREFERENCES"
msgstr "PREFERÊNCIAS"
#. Button on the left side of the panel.
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:274
msgid "Activities"
msgstr "Atividades"
#. Translators: This is a time format.
#: ../js/ui/panel.js:491
msgid "%a %l:%M %p"
msgstr "%a %l:%M %p"
#: ../js/ui/placeDisplay.js:84
msgid "Connect to..."
msgstr "Conectar ao..."
#: ../js/ui/runDialog.js:96
#: ../js/ui/runDialog.js:101
msgid "Please enter a command:"
msgstr "Por favor digite um comando:"
#: ../js/ui/runDialog.js:173
#, c-format
msgid "Execution of '%s' failed:"
msgstr "A execução de \"%s\" falhou:"
#. Translators: This is a time format.
#: ../js/ui/widget.js:163
msgid "%H:%M"
msgstr "%H:%M"
#: ../js/ui/widget.js:317
msgid "Applications"
msgstr "Aplicações"
#: ../js/ui/widget.js:339
msgid "Recent Documents"
msgstr "Documentos Recentes"
#: ../src/shell-global.c:821
#: ../src/shell-global.c:799
msgid "Less than a minute ago"
msgstr "Menos de um minuto atrás"
#: ../src/shell-global.c:824
#: ../src/shell-global.c:802
#, 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:827
#: ../src/shell-global.c:805
#, 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:830
#: ../src/shell-global.c:808
#, 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:833
#: ../src/shell-global.c:811
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
@ -215,12 +183,6 @@ msgstr "Procurar"
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"
#~ msgid "Browse"
#~ msgstr "Navegar"
#~ msgid "(see all)"
#~ msgstr "(veja todos)"
#~ msgid "Find apps or documents"
#~ msgstr "Localizar aplicativos ou documentos"

232
po/ro.po
View File

@ -1,232 +0,0 @@
# Romanian 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.
# Lucian Adrian Grijincu <lucian.grijincu@gmail.com>, 2009.
msgid ""
msgstr ""
"Project-Id-Version: gnome-shell master\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-10-28 22:25+0200\n"
"PO-Revision-Date: 2009-10-28 22:33+0200\n"
"Last-Translator: Lucian Adrian Grijincu <lucian.grijincu@gmail.com>\n"
"Language-Team: Romanian <gnomero-list@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=3; plural=(n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < "
"20)) ? 1 : 2);;\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 "Administrare de ferestre și lansare de aplicații"
#: ../js/ui/appDisplay.js:332
msgid "Frequent"
msgstr "Frecvent"
#: ../js/ui/appDisplay.js:867
msgid "Drag here to add favorites"
msgstr "Adăugați aici favorite cu mausul"
#: ../js/ui/appIcon.js:426
msgid "New Window"
msgstr "Fereastră nouă"
#: ../js/ui/appIcon.js:430
msgid "Remove from Favorites"
msgstr "Șterge din favorite"
#: ../js/ui/appIcon.js:431
msgid "Add to Favorites"
msgstr "Adaugă la favorite"
#: ../js/ui/dash.js:283
msgid "Find..."
msgstr "Caută..."
#: ../js/ui/dash.js:400
msgid "More"
msgstr "Mai multe"
#: ../js/ui/dash.js:543
msgid "(see all)"
msgstr "(arată tot)"
#. **** Applications ****
#: ../js/ui/dash.js:725 ../js/ui/dash.js:787
msgid "APPLICATIONS"
msgstr "APPLICAȚII"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:745
msgid "PLACES"
msgstr "LOCAȚII"
#. **** Documents ****
#: ../js/ui/dash.js:752 ../js/ui/dash.js:797
msgid "RECENT DOCUMENTS"
msgstr "DOCUMENTE RECENTE"
#. **** Search Results ****
#: ../js/ui/dash.js:777 ../js/ui/dash.js:961
msgid "SEARCH RESULTS"
msgstr "REZULTATELE CĂUTĂRII"
#: ../js/ui/dash.js:792
msgid "PREFERENCES"
msgstr "PREFERINȚE"
#. Button on the left side of the panel.
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:274
msgid "Activities"
msgstr "Activități"
#. Translators: This is a time format.
#: ../js/ui/panel.js:491
msgid "%a %l:%M %p"
msgstr "%a %l:%M %p"
#: ../js/ui/places.js:178
msgid "Connect to..."
msgstr "Conectare la..."
#: ../js/ui/runDialog.js:96
msgid "Please enter a command:"
msgstr "Introduceți o comandă:"
#: ../js/ui/runDialog.js:173
#, c-format
msgid "Execution of '%s' failed:"
msgstr "Execuția comenzii „%s” a eșuat:"
#. Translators: This is a time format.
#: ../js/ui/widget.js:163
msgid "%H:%M"
msgstr "%H:%M"
#: ../js/ui/widget.js:317
msgid "Applications"
msgstr "Aplicații"
#: ../js/ui/widget.js:339
msgid "Recent Documents"
msgstr "Documente recente"
#: ../src/shell-global.c:821
msgid "Less than a minute ago"
msgstr "În ultimul minut"
#: ../src/shell-global.c:824
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "acum un minut"
msgstr[1] "acum %d minute"
msgstr[2] "acum %d de minute"
#: ../src/shell-global.c:827
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "acum o oră"
msgstr[1] "acum %d ore"
msgstr[2] "acum %d de ore"
#: ../src/shell-global.c:830
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "acum o zi"
msgstr[1] "acum %d zile"
msgstr[2] "acum %d de zile"
#: ../src/shell-global.c:833
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
msgstr[0] "acum o săptămână"
msgstr[1] "acum %d săptămâni"
msgstr[2] "acum %d de săptămâni"
#: ../src/shell-status-menu.c:156
msgid "Unknown"
msgstr "Necunoscut"
#: ../src/shell-status-menu.c:212
#, c-format
msgid "Can't lock screen: %s"
msgstr "Nu s-a putut bloca ecranul: %s"
#: ../src/shell-status-menu.c:227
#, c-format
msgid "Can't temporarily set screensaver to blank screen: %s"
msgstr "Nu s-a putut folosi temporar un ecran gol pentru economizorul de "
"ecran: %s"
#: ../src/shell-status-menu.c:351
#, c-format
msgid "Can't logout: %s"
msgstr "Nu se poate ieși din sesiune: %s"
#: ../src/shell-status-menu.c:492
msgid "Account Information..."
msgstr "Informații despre cont..."
#: ../src/shell-status-menu.c:502
msgid "Sidebar"
msgstr "Bară laterală"
#: ../src/shell-status-menu.c:510
msgid "System Preferences..."
msgstr "Preferințe de sistem..."
#: ../src/shell-status-menu.c:525
msgid "Lock Screen"
msgstr "Blocare ecran"
#: ../src/shell-status-menu.c:535
msgid "Switch User"
msgstr "Alt utilizator"
#. Only show switch user if there are other users
#. Log Out
#: ../src/shell-status-menu.c:546
msgid "Log Out..."
msgstr "Ieșire..."
#. Shut down
#: ../src/shell-status-menu.c:557
msgid "Shut Down..."
msgstr "Oprire..."
#: ../src/shell-uri-util.c:87
msgid "Home Folder"
msgstr "Dosar personal"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-uri-util.c:102
msgid "File System"
msgstr "Sistem de fișiere"
#: ../src/shell-uri-util.c:248
msgid "Search"
msgstr "Caută"
#. 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"

233
po/ru.po
View File

@ -1,233 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Andrey Korzinev <fellrond@gmail.com>, 2009.
# Sergey V. Kovylov <serejka@gmail.com>, 2009.
# Marina Zhurakhinskaya <marinaz@redhat.com>, 2009.
#
msgid ""
msgstr ""
"Project-Id-Version: gnome-shell\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell&component=general\n"
"POT-Creation-Date: 2009-10-09 00:27-0400\n"
"PO-Revision-Date: 2009-10-09 00:27-0400\n"
"Last-Translator: Marina Zhurakhinskaya <marinaz@redhat.com>\n"
"Language-Team: Russian <gnome-cyr@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%10==1 && n%100!=11 ? 0 : n%10>=2 && n%"
"10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\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 "Управление окнами и запуск приложений"
#: ../js/ui/appDisplay.js:335
msgid "Frequent"
msgstr "Часто используемые"
#: ../js/ui/appIcon.js:462
msgid "New Window"
msgstr "Новое окно"
#: ../js/ui/appIcon.js:475
msgid "Remove from Favorites"
msgstr "Удалить из избранного"
#: ../js/ui/appIcon.js:476
msgid "Add to Favorites"
msgstr "Добавить в избранное"
#: ../js/ui/dash.js:283
msgid "Find..."
msgstr "Найти…"
#: ../js/ui/dash.js:400
msgid "More"
msgstr "Ещё"
#: ../js/ui/dash.js:543
#: "(увидеть все)" does not fit when it is next to "НЕДАВНИЕ ДОКУМЕНТЫ", so I'm just using "(все)"
msgid "(see all)"
msgstr "(все)"
#. **** Applications ****
#: ../js/ui/dash.js:763 ../js/ui/dash.js:825
msgid "APPLICATIONS"
msgstr "ПРИЛОЖЕНИЯ"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:783
msgid "PLACES"
msgstr "ПАПКИ И РЕСУРСЫ"
#. **** Documents ****
#: ../js/ui/dash.js:790 ../js/ui/dash.js:835
msgid "RECENT DOCUMENTS"
msgstr "НЕДАВНИЕ ДОКУМЕНТЫ"
#. **** Search Results ****
#: ../js/ui/dash.js:815 ../js/ui/dash.js:955
msgid "SEARCH RESULTS"
msgstr "РЕЗУЛЬТАТЫ ПОИСКА"
#: ../js/ui/dash.js:830
msgid "PREFERENCES"
msgstr "НАСТРОЙКИ"
#. Button on the left side of the panel.
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:272
#. Another word that was considered was "Обзор", but it was decided that it doesn't give a full sense of the actions
#. possible when in the overview mode.
msgid "Activities"
msgstr "Действия"
#. Translators: This is a time format.
#: ../js/ui/panel.js:464
msgid "%a %l:%M %p"
msgstr "%a, %H:%M"
#: ../js/ui/places.js:178
msgid "Connect to..."
msgstr "Соединиться с…"
#: ../js/ui/runDialog.js:96
msgid "Please enter a command:"
msgstr "Введите команду:"
#: ../js/ui/runDialog.js:173
#, c-format
msgid "Execution of '%s' failed:"
msgstr "Не удалось выполнить «%s»:"
#. Translators: This is a time format.
#: ../js/ui/widget.js:162
msgid "%H:%M"
msgstr "%H:%M"
#: ../js/ui/widget.js:316
msgid "Applications"
msgstr "Приложения"
#: ../js/ui/widget.js:341
msgid "Recent Documents"
msgstr "Недавние документы"
#: ../src/shell-global.c:812
msgid "Less than a minute ago"
msgstr "Менее минуты назад"
#: ../src/shell-global.c:815
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "%d минуту назад"
msgstr[1] "%d минуты назад"
msgstr[2] "%d минут назад"
#: ../src/shell-global.c:818
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "%d час назад"
msgstr[1] "%d часа назад"
msgstr[2] "%d часов назад"
#: ../src/shell-global.c:821
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "%d день назад"
msgstr[1] "%d дня назад"
msgstr[2] "%d дней назад"
#: ../src/shell-global.c:824
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
msgstr[0] "%d неделю назад"
msgstr[1] "%d недели назад"
msgstr[2] "%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"

230
po/sl.po
View File

@ -1,17 +1,15 @@
# Slovenian translation of gnome-shell package.
# Copyright (C) 2006 Free Software Foundation, Inc.
# Slovenian translation for gnome-shell.
# This file is distributed under the same license as the gnome-shell package.
#
# Matej Urbančič <mateju@svn.gnome.org>, 2009 - 2010.
# Matej Urbančič <mateju@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: 2010-01-05 01:21+0000\n"
"PO-Revision-Date: 2010-01-05 09:09+0100\n"
"POT-Creation-Date: 2009-09-21 22:38+0000\n"
"PO-Revision-Date: 2009-09-22 10:36+0100\n"
"Last-Translator: Matej Urbančič <mateju@svn.gnome.org>\n"
"Language-Team: Slovenian GNOME Translation Team <gnome-si@googlegroups.com>\n"
"Language-Team: Slovenian <gnome-si@googlegroups.com>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@ -29,98 +27,66 @@ msgstr "Gnome lupina"
msgid "Window management and application launching"
msgstr "Upravljanje oken in zaganjanje programov"
#. **** Applications ****
#: ../js/ui/appDisplay.js:252
#: ../js/ui/dash.js:858
msgid "APPLICATIONS"
msgstr "Programi"
#: ../js/ui/appDisplay.js:276
msgid "PREFERENCES"
msgstr "Možnosti"
#: ../js/ui/appDisplay.js:707
#: ../js/ui/appIcon.js:425
msgid "New Window"
msgstr "Novo okno"
#: ../js/ui/appDisplay.js:711
#: ../js/ui/appIcon.js:429
msgid "Remove from Favorites"
msgstr "Odstrani iz priljubljenih"
#: ../js/ui/appDisplay.js:712
#: ../js/ui/appIcon.js:430
msgid "Add to Favorites"
msgstr "Dodaj med priljubljene"
#: ../js/ui/appDisplay.js:1064
msgid "Drag here to add favorites"
msgstr "S potegom na to mesto se izbor doda med priljubljene"
#: ../js/ui/dash.js:240
msgid "Find..."
msgstr "Najdi ..."
#: ../js/ui/dash.js:437
msgid "Searching..."
msgstr "Iskanje ..."
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:878
#: ../js/ui/placeDisplay.js:519
msgid "PLACES"
msgstr "Mesta"
#. **** Documents ****
#: ../js/ui/dash.js:885
msgid "RECENT DOCUMENTS"
msgstr "Nedavni dokumenti"
#. Button on the left side of the panel.
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:227
#. left side
#: ../js/ui/panel.js:269
msgid "Activities"
msgstr "Dejavnosti"
#. Translators: This is a time format.
#: ../js/ui/panel.js:440
#: ../js/ui/panel.js:452
msgid "%a %l:%M %p"
msgstr "%a, %H:%M"
#: ../js/ui/placeDisplay.js:144
msgid "Connect to..."
msgstr "Povezava z ..."
#: ../js/ui/dash.js:283
msgid "Find..."
msgstr "Poišči ..."
#: ../js/ui/runDialog.js:235
msgid "Please enter a command:"
msgstr "Vnos ukaza:"
#: ../js/ui/dash.js:400
msgid "Browse"
msgstr "Prebrskaj"
#: ../js/ui/runDialog.js:351
#, c-format
msgid "Execution of '%s' failed:"
msgstr "Izvajanje '%s' je spodletelo:"
#: ../js/ui/dash.js:536
msgid "(see all)"
msgstr "(poglej vse)"
#. Translators: This is a time format.
#: ../js/ui/widget.js:163
msgid "%H:%M"
msgstr "%H:%M"
#: ../js/ui/widget.js:317
msgid "Applications"
#. **** Applications ****
#: ../js/ui/dash.js:753
#: ../js/ui/dash.js:809
msgid "APPLICATIONS"
msgstr "Programi"
#: ../js/ui/widget.js:339
msgid "Recent Documents"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:773
msgid "PLACES"
msgstr "Mesta"
#. **** Documents ****
#: ../js/ui/dash.js:780
#: ../js/ui/dash.js:819
msgid "RECENT DOCUMENTS"
msgstr "Nedavni dokumenti"
#: ../src/shell-global.c:890
#. **** Search Results ****
#: ../js/ui/dash.js:799
#: ../js/ui/dash.js:931
msgid "SEARCH RESULTS"
msgstr "Rezultati iskanja"
#: ../js/ui/dash.js:814
msgid "PREFERENCES"
msgstr "Lastnosti"
#: ../js/ui/runDialog.js:101
msgid "Please enter a command:"
msgstr "Prosim, vnesite ukaz:"
#: ../src/shell-global.c:799
msgid "Less than a minute ago"
msgstr "Pred manj kot eno minuto"
#: ../src/shell-global.c:893
#: ../src/shell-global.c:802
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
@ -129,7 +95,7 @@ msgstr[1] "Pred %d minuto"
msgstr[2] "Pred %d minutama"
msgstr[3] "Pred %d minutami"
#: ../src/shell-global.c:896
#: ../src/shell-global.c:805
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
@ -138,7 +104,7 @@ msgstr[1] "Pred %d uro"
msgstr[2] "Pred %d urama"
msgstr[3] "Pred %d urami"
#: ../src/shell-global.c:899
#: ../src/shell-global.c:808
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
@ -147,7 +113,7 @@ msgstr[1] "Pred %d dnevom"
msgstr[2] "Pred %d dnevoma"
msgstr[3] "Pred %d dnevi"
#: ../src/shell-global.c:902
#: ../src/shell-global.c:811
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
@ -156,61 +122,77 @@ msgstr[1] "Pred %d tednom"
msgstr[2] "Pred %d tednoma"
msgstr[3] "Pred %d tedni"
#: ../src/shell-uri-util.c:89
#: ../src/shell-status-menu.c:156
msgid "Unknown"
msgstr "Neznano"
#: ../src/shell-status-menu.c:212
#, c-format
msgid "Can't lock screen: %s"
msgstr "Ni mogoče zakleniti zaslona: %s"
#: ../src/shell-status-menu.c:227
#, c-format
msgid "Can't temporarily set screensaver to blank screen: %s"
msgstr "Ni mogoče začasno nastaviti črnega zaslona za ohranjevalnik zaslona: %s"
#: ../src/shell-status-menu.c:351
#, c-format
msgid "Can't logout: %s"
msgstr "Ni se mogoče odjaviti: %s"
#: ../src/shell-status-menu.c:492
msgid "Account Information..."
msgstr "Podrobnosti računa ..."
#: ../src/shell-status-menu.c:502
msgid "Sidebar"
msgstr "_Stranska vrstica"
#: ../src/shell-status-menu.c:510
msgid "System Preferences..."
msgstr "Sistemske lastnosti ..."
#: ../src/shell-status-menu.c:525
msgid "Lock Screen"
msgstr "Zakleni zaslon"
#: ../src/shell-status-menu.c:535
msgid "Switch User"
msgstr "Preklop uporabnika"
#. Only show switch user if there are other users
#. Log Out
#: ../src/shell-status-menu.c:546
msgid "Log Out..."
msgstr "Odjava ..."
#. Shut down
#: ../src/shell-status-menu.c:557
msgid "Shut Down..."
msgstr "Izklop ..."
#: ../src/shell-uri-util.c:87
msgid "Home Folder"
msgstr "Domača mapa"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-uri-util.c:104
#: ../src/shell-uri-util.c:102
msgid "File System"
msgstr "Datotečni sistem"
#: ../src/shell-uri-util.c:250
#: ../src/shell-uri-util.c:248
msgid "Search"
msgstr "Poišči"
msgstr "Iskanje"
#. 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:300
#: ../src/shell-uri-util.c:298
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"
#~ msgid "SEARCH RESULTS"
#~ msgstr "Rezultati iskanja"
#~ msgid "Unknown"
#~ msgstr "Neznano"
#~ msgid "Can't lock screen: %s"
#~ msgstr "Ni mogoče zakleniti zaslona: %s"
#~ msgid "Can't temporarily set screensaver to blank screen: %s"
#~ msgstr ""
#~ "Ni mogoče začasno nastaviti črnega zaslona za ohranjevalnik zaslona: %s"
#~ msgid "Can't logout: %s"
#~ msgstr "Ni se mogoče odjaviti: %s"
#~ msgid "Account Information..."
#~ msgstr "Podrobnosti računa ..."
#~ msgid "Sidebar"
#~ msgstr "Stranska vrstica"
#~ msgid "System Preferences..."
#~ msgstr "Sistemske možnosti ..."
#~ msgid "Lock Screen"
#~ msgstr "Zakleni zaslon"
#~ msgid "Switch User"
#~ msgstr "Preklop uporabnika"
#~ msgid "Log Out..."
#~ msgstr "Odjava ..."
#~ msgid "Shut Down..."
#~ msgstr "Izklopi ..."
#~ msgid "Frequent"
#~ msgstr "Pogosto"
#~ msgid "More"
#~ msgstr "Več"
#~ msgid "(see all)"
#~ msgstr "(poglej vse)"
#~ msgid "Browse"
#~ msgstr "Prebrskaj"

185
po/sv.po
View File

@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gnome-shell\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-12-01 23:52+0100\n"
"PO-Revision-Date: 2009-12-01 23:53+0100\n"
"POT-Creation-Date: 2009-09-18 13:02+0200\n"
"PO-Revision-Date: 2009-09-18 13:02+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"
@ -24,139 +24,154 @@ msgstr "GNOME-skal"
msgid "Window management and application launching"
msgstr "Fönsterhantering och programstarter"
#: ../js/ui/appDisplay.js:580
#: ../js/ui/appIcon.js:425
msgid "New Window"
msgstr "Nytt fönster"
#. left side
#: ../js/ui/panel.js:269
msgid "Activities"
msgstr "Aktiviteter"
#: ../js/ui/appDisplay.js:584
#: ../js/ui/appIcon.js:429
msgid "Remove from Favorites"
msgstr "Ta bort från favoriter"
#. Translators: This is a time format.
#: ../js/ui/panel.js:452
msgid "%a %l:%M %p"
msgstr "%a %H.%M"
#: ../js/ui/appDisplay.js:585
#: ../js/ui/appIcon.js:430
msgid "Add to Favorites"
msgstr "Lägg till som favorit"
#: ../js/ui/appDisplay.js:1029
msgid "Drag here to add favorites"
msgstr "Dra hit för att lägga till favorit"
#: ../js/ui/dash.js:236
#: ../js/ui/dash.js:283
msgid "Find..."
msgstr "Sök..."
#: ../js/ui/dash.js:400
msgid "Browse"
msgstr "Bläddra"
#: ../js/ui/dash.js:536
msgid "(see all)"
msgstr "(se alla)"
#. **** Applications ****
#: ../js/ui/dash.js:620
#: ../js/ui/dash.js:682
#: ../js/ui/dash.js:753
#: ../js/ui/dash.js:809
msgid "APPLICATIONS"
msgstr "PROGRAM"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:640
#: ../js/ui/dash.js:697
#: ../js/ui/dash.js:773
msgid "PLACES"
msgstr "PLATSER"
#. **** Documents ****
#: ../js/ui/dash.js:647
#: ../js/ui/dash.js:692
#: ../js/ui/dash.js:780
#: ../js/ui/dash.js:819
msgid "RECENT DOCUMENTS"
msgstr "SENASTE DOKUMENT"
#. **** Search Results ****
#: ../js/ui/dash.js:672
#: ../js/ui/dash.js:862
#: ../js/ui/dash.js:799
#: ../js/ui/dash.js:931
msgid "SEARCH RESULTS"
msgstr "SÖKRESULTAT"
#: ../js/ui/dash.js:687
#: ../js/ui/dash.js:814
msgid "PREFERENCES"
msgstr "INSTÄLLNINGAR"
#. Button on the left side of the panel.
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:227
msgid "Activities"
msgstr "Aktiviteter"
#. Translators: This is a time format.
#: ../js/ui/panel.js:440
msgid "%a %l:%M %p"
msgstr "%a %H.%M"
#: ../js/ui/placeDisplay.js:84
msgid "Connect to..."
msgstr "Anslut till..."
#: ../js/ui/runDialog.js:96
#: ../js/ui/runDialog.js:101
msgid "Please enter a command:"
msgstr "Ange ett kommando:"
#: ../js/ui/runDialog.js:173
#, c-format
msgid "Execution of '%s' failed:"
msgstr "Körning av \"%s\" misslyckades:"
#. Translators: This is a time format.
#: ../js/ui/widget.js:163
msgid "%H:%M"
msgstr "%H.%M"
#: ../js/ui/widget.js:317
msgid "Applications"
msgstr "Program"
#: ../js/ui/widget.js:339
msgid "Recent Documents"
msgstr "Senaste dokument"
#: ../src/shell-global.c:826
#: ../src/shell-global.c:799
msgid "Less than a minute ago"
msgstr "Mindre än en minut sedan"
#: ../src/shell-global.c:829
#: ../src/shell-global.c:802
#, 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:832
#: ../src/shell-global.c:805
#, 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:835
#: ../src/shell-global.c:808
#, 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:838
#: ../src/shell-global.c:811
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
msgstr[0] "%d vecka sedan"
msgstr[1] "%d veckor sedan"
#: ../src/shell-uri-util.c:89
#: ../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:104
#: ../src/shell-uri-util.c:102
msgid "File System"
msgstr "Filsystem"
#: ../src/shell-uri-util.c:250
#: ../src/shell-uri-util.c:248
msgid "Search"
msgstr "Sök"
@ -165,37 +180,11 @@ msgstr "Sök"
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-uri-util.c:300
#: ../src/shell-uri-util.c:298
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"
#~ msgid "(see all)"
#~ msgstr "(se alla)"
#~ msgid "Unknown"
#~ msgstr "Okänt"
#~ msgid "Can't lock screen: %s"
#~ msgstr "Kan inte låsa skärmen: %s"
#~ 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"
#~ msgid "Can't logout: %s"
#~ msgstr "Kan inte logga ut: %s"
#~ msgid "Account Information..."
#~ msgstr "Kontoinformation..."
#~ msgid "Sidebar"
#~ msgstr "Sidopanel"
#~ msgid "System Preferences..."
#~ msgstr "Systeminställningar..."
#~ msgid "Lock Screen"
#~ msgstr "Lås skärmen"
#~ msgid "Switch User"
#~ msgstr "Växla användare"
#~ msgid "Log Out..."
#~ msgstr "Logga ut..."
#~ msgid "Shut Down..."
#~ msgstr "Stäng av..."
#~ msgid "Browse"
#~ msgstr "Bläddra"
#~ msgid "Find apps or documents"
#~ msgstr "Hitta program eller dokument"
#~ msgid "DOCUMENTS"

View File

@ -18,7 +18,6 @@ st_built_sources = \
BUILT_SOURCES += $(st_built_sources)
EXTRA_DIST += \
st/test-theme.css \
st/st-marshal.list \
st/st-enum-types.h.in \
st/st-enum-types.c.in
@ -72,27 +71,20 @@ st_source_h = \
st/st-box-layout.h \
st/st-box-layout-child.h \
st/st-button.h \
st/st-clickable.h \
st/st-clipboard.h \
st/st-drawing-area.h \
st/st-entry.h \
st/st-im-text.h \
st/st-label.h \
st/st-overflow-box.h \
st/st-private.h \
st/st-scrollable.h \
st/st-scroll-bar.h \
st/st-scroll-view.h \
st/st-subtexture.h \
st/st-table.h \
st/st-table-child.h \
st/st-table-private.h \
st/st-texture-cache.h \
st/st-texture-frame.h \
st/st-theme.h \
st/st-theme-context.h \
st/st-theme-node.h \
st/st-theme-private.h \
st/st-tooltip.h \
st/st-types.h \
st/st-widget.h \
@ -111,13 +103,9 @@ st_source_c = \
st/st-box-layout.c \
st/st-box-layout-child.c \
st/st-button.c \
st/st-clickable.c \
st/st-clipboard.c \
st/st-drawing-area.c \
st/st-entry.c \
st/st-im-text.c \
st/st-label.c \
st/st-overflow-box.c \
st/st-private.c \
st/st-scrollable.c \
st/st-scroll-bar.c \

View File

@ -54,19 +54,18 @@ CLEANFILES += $(SHELL_STAMP_FILES)
libgnome_shell_la_SOURCES = \
$(shell_built_sources) \
gnome-shell-plugin.c \
shell-app.c \
shell-app.h \
shell-app-private.h \
shell-app-monitor.c \
shell-app-monitor.h \
shell-app-system.c \
shell-app-system.h \
shell-app-usage.c \
shell-app-usage.h \
shell-arrow.c \
shell-arrow.h \
shell-doc-system.c \
shell-doc-system.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 \
@ -84,7 +83,8 @@ libgnome_shell_la_SOURCES = \
shell-process.h \
shell-global.c \
shell-global.h \
shell-global-private.h \
shell-status-menu.c \
shell-status-menu.h \
shell-stack.c \
shell-stack.h \
shell-tray-manager.c \
@ -93,14 +93,11 @@ libgnome_shell_la_SOURCES = \
shell-texture-cache.h \
shell-uri-util.c \
shell-uri-util.h \
shell-window-tracker.c \
shell-window-tracker.h \
shell-wm.c \
shell-wm.h
non_gir_sources = \
shell-embedded-window-private.h \
shell-global-private.h
shell-embedded-window-private.h
shell_recorder_sources = \
shell-recorder.c \
@ -161,7 +158,7 @@ libgnome_shell_la_LIBADD = \
libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags)
typelibdir = $(pkglibdir)
typelib_DATA = Shell-0.1.typelib Big-1.0.typelib St-1.0.typelib Gdm-1.0.typelib
typelib_DATA = Shell-0.1.typelib Big-1.0.typelib St-1.0.typelib
Shell-0.1.gir: $(mutter) $(G_IR_SCANNER) Big-1.0.gir St-1.0.gir libgnome-shell.la Makefile
$(AM_V_GEN) $(G_IR_SCANNER) \
@ -169,7 +166,7 @@ Shell-0.1.gir: $(mutter) $(G_IR_SCANNER) Big-1.0.gir St-1.0.gir libgnome-shell.l
--nsversion=0.1 \
--add-include-path=$(MUTTER_LIB_DIR)/mutter/ \
--include=Clutter-1.0 \
--include=Meta-2.28 \
--include=Meta-2.27 \
--libtool="$(LIBTOOL)" \
--add-include-path=$(builddir) \
--include=Big-1.0 \
@ -224,7 +221,7 @@ St-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libst-1.0.la Makefile
$(addprefix $(srcdir)/,$(st_source_h)) \
$(addprefix $(srcdir)/,$(st_source_c)) \
$(srcdir)/st-enum-types.h \
$(st_cflags) \
$(ST_CFLAGS) \
-o $@
CLEANFILES += St-1.0.gir
@ -233,22 +230,3 @@ St-1.0.typelib: St-1.0.gir
$< -o $@
CLEANFILES += St-1.0.typelib
Gdm-1.0.gir: $(mutter) $(G_IR_SCANNER) libgdmuser-1.0.la Makefile
$(AM_V_GEN) $(G_IR_SCANNER) \
--namespace=Gdm \
--nsversion=1.0 \
--include=GObject-2.0 \
--include=GdkPixbuf-2.0 \
--libtool="$(LIBTOOL)" \
--library=libgdmuser-1.0.la \
$(addprefix $(srcdir)/,$(gdmuser_source_h)) \
$(addprefix $(srcdir)/,$(gdmuser_source_c)) \
$(gdmuser_cflags) \
-o $@
CLEANFILES += Gdm-1.0.gir
Gdm-1.0.typelib: libbig-1.0.la Gdm-1.0.gir
$(AM_V_GEN) $(G_IR_COMPILER) Gdm-1.0.gir -o $@
CLEANFILES += Gdm-1.0.typelib

View File

@ -998,7 +998,7 @@ gdm_user_manager_get_user (GdmUserManager *manager,
GdmUser *
gdm_user_manager_get_user_by_uid (GdmUserManager *manager,
gulong uid)
uid_t uid)
{
GdmUser *user;
struct passwd *pwent;

View File

@ -75,7 +75,7 @@ GSList * gdm_user_manager_list_users (GdmUserManager *mana
GdmUser * gdm_user_manager_get_user (GdmUserManager *manager,
const char *user_name);
GdmUser * gdm_user_manager_get_user_by_uid (GdmUserManager *manager,
gulong uid);
uid_t uid);
gboolean gdm_user_manager_activate_user_session (GdmUserManager *manager,
GdmUser *user);

View File

@ -516,7 +516,7 @@ _gdm_user_icon_changed (GdmUser *user)
* Since: 1.0
**/
gulong
uid_t
gdm_user_get_uid (GdmUser *user)
{
g_return_val_if_fail (GDM_IS_USER (user), -1);

View File

@ -39,7 +39,7 @@ typedef struct _GdmUser GdmUser;
GType gdm_user_get_type (void) G_GNUC_CONST;
gulong gdm_user_get_uid (GdmUser *user);
uid_t gdm_user_get_uid (GdmUser *user);
G_CONST_RETURN char *gdm_user_get_user_name (GdmUser *user);
G_CONST_RETURN char *gdm_user_get_real_name (GdmUser *user);
G_CONST_RETURN char *gdm_user_get_home_directory (GdmUser *user);

View File

@ -23,8 +23,6 @@
* 02111-1307, USA.
*/
#include "config.h"
#define MUTTER_BUILDING_PLUGIN 1
#include <mutter-plugin.h>
@ -42,7 +40,7 @@
#include "display.h"
#include "shell-global-private.h"
#include "shell-global.h"
#include "shell-wm.h"
static void gnome_shell_plugin_constructed (GObject *object);
@ -194,8 +192,6 @@ gnome_shell_plugin_constructed (GObject *object)
shell_plugin->gjs_context = gjs_context_new_with_search_path(search_path);
g_strfreev(search_path);
_shell_global_set_gjs_context (shell_global_get (), shell_plugin->gjs_context);
if (!gjs_context_eval (shell_plugin->gjs_context,
"const Main = imports.ui.main; Main.start();",
-1,

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

@ -53,7 +53,9 @@ def start_xephyr():
time.sleep(1)
# Start some windows in our session.
subprocess.Popen(["gnome-terminal"])
subprocess.Popen(["xterm", "-geometry", "+30+30"])
subprocess.Popen(["xlogo", "-geometry", "-0-0"])
subprocess.Popen(["xeyes", "-geometry", "-0+30"])
return xephyr;
@ -210,10 +212,6 @@ parser.add_option("", "--geometry", metavar="GEOMETRY",
default="1024x768");
parser.add_option("-w", "--wide", action="store_true",
help="Use widescreen (1280x800) with Xephyr")
parser.add_option("", "--eval-file", metavar="EVAL_FILE",
help="Evaluate the contents of the given JavaScript file")
parser.add_option("", "--create-extension", action="store_true",
help="Create a new GNOME Shell extension")
options, args = parser.parse_args()
@ -221,105 +219,6 @@ if args:
parser.print_usage()
sys.exit(1)
if options.create_extension:
import json
print
print '''Name should be a very short (ideally descriptive) string.
Examples are: "Click To Focus", "Adblock", "Shell Window Shrinker".
'''
name = raw_input('Name: ').strip()
print
print '''Description is a single-sentence explanation of what your extension does.
Examples are: "Make windows visible on click", "Block advertisement popups"
"Animate windows shrinking on minimize"
'''
description = raw_input('Description: ').strip()
underifier = re.compile('[^A-Za-z]')
sample_uuid = underifier.sub('_', name)
# TODO use evolution data server
hostname = subprocess.Popen(['hostname'], stdout=subprocess.PIPE).communicate()[0].strip()
sample_uuid = sample_uuid + '@' + hostname
print
print '''Uuid is a globally-unique identifier for your extension.
This should be in the format of an email address (foo.bar@extensions.example.com), but
need not be an actual email address, though it's a good idea to base the uuid on your
email address. For example, if your email address is janedoe@example.com, you might
use an extension title clicktofocus@janedoe.example.com.'''
uuid = raw_input('Uuid [%s]: ' % (sample_uuid, )).strip()
if uuid == '':
uuid = sample_uuid
extension_path = os.path.join(os.path.expanduser('~/.config'), 'gnome-shell', 'extensions', uuid)
if os.path.exists(extension_path):
print "Extension path %r already exists" % (extension_path, )
sys.exit(0)
os.makedirs(extension_path)
meta = { 'name': name,
'description': description,
'uuid': uuid }
f = open(os.path.join(extension_path, 'metadata.json'), 'w')
try:
json.dump(meta, f)
except AttributeError:
# For Python versions older than 2.6, try using the json-py module
f.write(json.write(meta) + '\n')
f.close()
extensionjs_path = os.path.join(extension_path, 'extension.js')
f = open(extensionjs_path, 'w')
f.write('''// Sample extension code, makes clicking on the panel show a message
const St = imports.gi.St;
const Mainloop = imports.mainloop;
const Main = imports.ui.main;
function _showHello() {
let text = new St.Label({ style_class: 'helloworld-label', text: "Hello, world!" });
let monitor = global.get_primary_monitor();
global.stage.add_actor(text);
text.set_position(Math.floor (monitor.width / 2 - text.width / 2), Math.floor(monitor.height / 2 - text.height / 2));
Mainloop.timeout_add(3000, function () { text.destroy(); });
}
// Put your extension initialization code here
function main() {
Main.panel.actor.reactive = true;
Main.panel.actor.connect('button-release-event', _showHello);
}
''')
f.close()
f = open(os.path.join(extension_path, 'stylesheet.css'), 'w')
f.write('''/* Example stylesheet */
.helloworld-label {
font-size: 36px;
font-weight: bold;
color: #ffffff;
background-color: rgba(10,10,10,0.7);
border-radius: 5px;
}
''')
f.close()
subprocess.Popen(['gnome-open', extensionjs_path])
sys.exit(0)
if options.eval_file:
import dbus
f = open(options.eval_file)
contents = f.read()
f.close()
session = dbus.SessionBus()
shell = session.get_object('org.gnome.Shell', '/org/gnome/Shell')
shell = dbus.Interface(shell, 'org.gnome.Shell')
result = shell.Eval(contents)
print result
sys.exit(0)
if options.debug_command:
options.debug = True
elif options.debug:
@ -357,9 +256,8 @@ try:
shell = None
if options.xephyr:
xephyr = start_xephyr()
# This makes us not grab the org.gnome.Panel or
# org.freedesktop.Notifications D-Bus names
os.environ['GNOME_SHELL_NO_REPLACE'] = '1'
# This makes us not grab the org.gnome.Panel name
os.environ['GNOME_SHELL_NO_REPLACE_PANEL'] = '1'
shell = start_shell()
else:
xephyr = None

1782
src/shell-app-monitor.c Normal file

File diff suppressed because it is too large Load Diff

66
src/shell-app-monitor.h Normal file
View File

@ -0,0 +1,66 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef __SHELL_APP_MONITOR_H__
#define __SHELL_APP_MONITOR_H__
#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
*/
G_BEGIN_DECLS
typedef struct _ShellAppMonitor ShellAppMonitor;
typedef struct _ShellAppMonitorClass ShellAppMonitorClass;
typedef struct _ShellAppMonitorPrivate ShellAppMonitorPrivate;
#define SHELL_TYPE_APP_MONITOR (shell_app_monitor_get_type ())
#define SHELL_APP_MONITOR(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SHELL_TYPE_APP_MONITOR, ShellAppMonitor))
#define SHELL_APP_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_APP_MONITOR, ShellAppMonitorClass))
#define SHELL_IS_APP_MONITOR(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SHELL_TYPE_APP_MONITOR))
#define SHELL_IS_APP_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_APP_MONITOR))
#define SHELL_APP_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_APP_MONITOR, ShellAppMonitorClass))
struct _ShellAppMonitorClass
{
GObjectClass parent_class;
};
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);
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);
/* 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);
G_END_DECLS
#endif /* __SHELL_APP_MONITOR_H__ */

View File

@ -1,22 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef __SHELL_APP_PRIVATE_H__
#define __SHELL_APP_PRIVATE_H__
#include "shell-app.h"
#include "shell-app-system.h"
G_BEGIN_DECLS
ShellAppInfo *_shell_app_get_info (ShellApp *app);
ShellApp* _shell_app_new_for_window (MetaWindow *window);
ShellApp* _shell_app_new (ShellAppInfo *appinfo);
void _shell_app_add_window (ShellApp *app, MetaWindow *window);
void _shell_app_remove_window (ShellApp *app, MetaWindow *window);
G_END_DECLS
#endif /* __SHELL_APP_PRIVATE_H__ */

View File

@ -1,7 +1,5 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#include "config.h"
#include "shell-app-system.h"
#include <string.h>
@ -12,7 +10,6 @@
#include <gconf/gconf-client.h>
#include <clutter/clutter.h>
#include "shell-app-private.h"
#include "shell-global.h"
#include "shell-texture-cache.h"
#include "display.h"
@ -36,6 +33,7 @@ enum {
enum {
INSTALLED_CHANGED,
FAVORITES_CHANGED,
LAST_SIGNAL
};
@ -45,12 +43,14 @@ struct _ShellAppSystemPrivate {
GMenuTree *apps_tree;
GMenuTree *settings_tree;
GHashTable *app_id_to_info;
GHashTable *app_id_to_app;
GSList *cached_flattened_apps; /* ShellAppInfo */
GSList *cached_app_menus; /* ShellAppMenuEntry */
GSList *cached_settings; /* ShellAppInfo */
GList *cached_favorites; /* utf8 */
gint app_monitor_id;
guint app_change_timeout_id;
@ -60,6 +60,8 @@ static void shell_app_system_finalize (GObject *object);
static gboolean on_tree_changed (gpointer user_data);
static void on_tree_changed_cb (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);
@ -80,10 +82,6 @@ struct _ShellAppInfo {
*/
guint refcount;
char *casefolded_name;
char *name_collation_key;
char *casefolded_description;
GMenuTreeItem *entry;
GKeyFile *keyfile;
@ -105,11 +103,6 @@ shell_app_info_unref (ShellAppInfo *info)
{
if (--info->refcount > 0)
return;
g_free (info->casefolded_name);
g_free (info->name_collation_key);
g_free (info->casefolded_description);
switch (info->type)
{
case SHELL_APP_INFO_TYPE_ENTRY:
@ -135,7 +128,7 @@ shell_app_info_new_from_tree_item (GMenuTreeItem *item)
if (!item)
return NULL;
info = g_slice_alloc0 (sizeof (ShellAppInfo));
info = g_slice_alloc (sizeof (ShellAppInfo));
info->type = SHELL_APP_INFO_TYPE_ENTRY;
info->refcount = 1;
info->entry = gmenu_tree_item_ref (item);
@ -147,7 +140,7 @@ shell_app_info_new_from_window (MetaWindow *window)
{
ShellAppInfo *info;
info = g_slice_alloc0 (sizeof (ShellAppInfo));
info = g_slice_alloc (sizeof (ShellAppInfo));
info->type = SHELL_APP_INFO_TYPE_WINDOW;
info->refcount = 1;
info->window = g_object_ref (window);
@ -165,7 +158,7 @@ shell_app_info_new_from_keyfile_take_ownership (GKeyFile *keyfile,
{
ShellAppInfo *info;
info = g_slice_alloc0 (sizeof (ShellAppInfo));
info = g_slice_alloc (sizeof (ShellAppInfo));
info->type = SHELL_APP_INFO_TYPE_DESKTOP_FILE;
info->refcount = 1;
info->keyfile = keyfile;
@ -173,6 +166,29 @@ shell_app_info_new_from_keyfile_take_ownership (GKeyFile *keyfile,
return info;
}
static gpointer
shell_app_menu_entry_copy (gpointer entryp)
{
ShellAppMenuEntry *entry;
ShellAppMenuEntry *copy;
entry = entryp;
copy = g_new0 (ShellAppMenuEntry, 1);
copy->name = g_strdup (entry->name);
copy->id = g_strdup (entry->id);
copy->icon = g_strdup (entry->icon);
return copy;
}
static void
shell_app_menu_entry_free (gpointer entryp)
{
ShellAppMenuEntry *entry = entryp;
g_free (entry->name);
g_free (entry->id);
g_free (entry->icon);
g_free (entry);
}
static void shell_app_system_class_init(ShellAppSystemClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *)klass;
@ -187,6 +203,14 @@ static void shell_app_system_class_init(ShellAppSystemClass *klass)
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[FAVORITES_CHANGED] =
g_signal_new ("favorites-changed",
SHELL_TYPE_APP_SYSTEM,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ShellAppSystemClass, favorites_changed),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
g_type_class_add_private (gobject_class, sizeof (ShellAppSystemPrivate));
}
@ -202,11 +226,8 @@ shell_app_system_init (ShellAppSystem *self)
ShellAppSystemPrivate);
/* The key is owned by the value */
priv->app_id_to_info = g_hash_table_new_full (g_str_hash, g_str_equal,
NULL, (GDestroyNotify) shell_app_info_unref);
/* Key is owned by info */
priv->app_id_to_app = g_hash_table_new (g_str_hash, g_str_equal);
priv->app_id_to_app = g_hash_table_new_full (g_str_hash, g_str_equal,
NULL, (GDestroyNotify) shell_app_info_unref);
/* For now, we want to pick up Evince, Nautilus, etc. We'll
* handle NODISPLAY semantics at a higher level or investigate them
@ -223,6 +244,10 @@ shell_app_system_init (ShellAppSystem *self)
reread_menus (self);
client = gconf_client_get_default ();
self->priv->app_monitor_id = gconf_client_notify_add (client, SHELL_APP_FAVORITES_KEY,
on_favorite_apps_changed, self, NULL, NULL);
reread_favorite_apps (self);
}
static void
@ -237,23 +262,69 @@ shell_app_system_finalize (GObject *object)
gmenu_tree_unref (priv->apps_tree);
gmenu_tree_unref (priv->settings_tree);
g_hash_table_destroy (priv->app_id_to_info);
g_hash_table_destroy (priv->app_id_to_app);
g_slist_foreach (priv->cached_flattened_apps, (GFunc)shell_app_info_unref, NULL);
g_slist_free (priv->cached_flattened_apps);
priv->cached_flattened_apps = NULL;
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_list_free (priv->cached_favorites);
gconf_client_notify_remove (gconf_client_get_default (), priv->app_monitor_id);
G_OBJECT_CLASS (shell_app_system_parent_class)->finalize(object);
}
static void
reread_directories (ShellAppSystem *self, GSList **cache, GMenuTree *tree)
{
GMenuTreeDirectory *trunk;
GSList *entries;
GSList *iter;
trunk = gmenu_tree_get_root_directory (tree);
entries = gmenu_tree_directory_get_contents (trunk);
g_slist_foreach (*cache, (GFunc)shell_app_menu_entry_free, NULL);
g_slist_free (*cache);
*cache = NULL;
for (iter = entries; iter; iter = iter->next)
{
GMenuTreeItem *item = iter->data;
switch (gmenu_tree_item_get_type (item))
{
case GMENU_TREE_ITEM_DIRECTORY:
{
GMenuTreeDirectory *dir = iter->data;
ShellAppMenuEntry *shell_entry = g_new0 (ShellAppMenuEntry, 1);
shell_entry->name = g_strdup (gmenu_tree_directory_get_name (dir));
shell_entry->id = g_strdup (gmenu_tree_directory_get_menu_id (dir));
shell_entry->icon = g_strdup (gmenu_tree_directory_get_icon (dir));
*cache = g_slist_prepend (*cache, shell_entry);
}
break;
default:
break;
}
gmenu_tree_item_unref (item);
}
*cache = g_slist_reverse (*cache);
g_slist_free (entries);
gmenu_tree_item_unref (trunk);
}
static GSList *
gather_entries_recurse (ShellAppSystem *monitor,
GSList *apps,
GHashTable *unique,
GMenuTreeDirectory *root)
{
GSList *contents;
@ -269,17 +340,13 @@ gather_entries_recurse (ShellAppSystem *monitor,
case GMENU_TREE_ITEM_ENTRY:
{
ShellAppInfo *app = shell_app_info_new_from_tree_item (item);
if (!g_hash_table_lookup (unique, shell_app_info_get_id (app)))
{
apps = g_slist_prepend (apps, app);
g_hash_table_insert (unique, (char*)shell_app_info_get_id (app), app);
}
apps = g_slist_prepend (apps, app);
}
break;
case GMENU_TREE_ITEM_DIRECTORY:
{
GMenuTreeDirectory *dir = (GMenuTreeDirectory*)item;
apps = gather_entries_recurse (monitor, apps, unique, dir);
apps = gather_entries_recurse (monitor, apps, dir);
}
break;
default:
@ -296,7 +363,6 @@ gather_entries_recurse (ShellAppSystem *monitor,
static void
reread_entries (ShellAppSystem *self,
GSList **cache,
GHashTable *unique,
GMenuTree *tree)
{
GMenuTreeDirectory *trunk;
@ -307,51 +373,54 @@ reread_entries (ShellAppSystem *self,
g_slist_free (*cache);
*cache = NULL;
*cache = gather_entries_recurse (self, *cache, unique, trunk);
*cache = gather_entries_recurse (self, *cache, trunk);
gmenu_tree_item_unref (trunk);
}
static void
cache_by_id (ShellAppSystem *self, GSList *apps)
cache_by_id (ShellAppSystem *self, GSList *apps, gboolean ref)
{
GSList *iter;
for (iter = apps; iter; iter = iter->next)
{
ShellAppInfo *info = iter->data;
shell_app_info_ref (info);
if (ref)
shell_app_info_ref (info);
/* the name is owned by the info itself */
g_hash_table_replace (self->priv->app_id_to_info, (char*)shell_app_info_get_id (info),
info);
g_hash_table_insert (self->priv->app_id_to_app, (char*)shell_app_info_get_id (info),
info);
}
}
static void
reread_menus (ShellAppSystem *self)
{
GHashTable *unique = g_hash_table_new (g_str_hash, g_str_equal);
GSList *apps;
GMenuTreeDirectory *trunk;
reread_entries (self, &(self->priv->cached_flattened_apps), unique, self->priv->apps_tree);
g_hash_table_remove_all (unique);
reread_entries (self, &(self->priv->cached_settings), unique, self->priv->settings_tree);
g_hash_table_destroy (unique);
reread_directories (self, &(self->priv->cached_app_menus), self->priv->apps_tree);
g_hash_table_remove_all (self->priv->app_id_to_info);
reread_entries (self, &(self->priv->cached_settings), self->priv->settings_tree);
cache_by_id (self, self->priv->cached_flattened_apps);
cache_by_id (self, self->priv->cached_settings);
/* 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);
}
static gboolean
on_tree_changed (gpointer user_data)
{
ShellAppSystem *self = SHELL_APP_SYSTEM (user_data);
reread_menus (self);
g_signal_emit (self, signals[INSTALLED_CHANGED], 0);
reread_menus (self);
self->priv->app_change_timeout_id = 0;
return FALSE;
}
@ -381,6 +450,63 @@ on_tree_changed_cb (GMenuTree *monitor, gpointer user_data)
self, NULL);
}
static GList *
convert_gconf_value_string_list_to_list_uniquify (GConfValue *value )
{
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);
for (tmp = list ; tmp; tmp = tmp->next)
{
GConfValue *value = tmp->data;
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_destroy (tmp_table);
return g_list_reverse (result);
}
static void
reread_favorite_apps (ShellAppSystem *system)
{
GConfClient *client = gconf_client_get_default ();
GConfValue *val;
val = gconf_client_get (client, SHELL_APP_FAVORITES_KEY, NULL);
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);
gconf_value_free (val);
}
void
on_favorite_apps_changed (GConfClient *client,
guint id,
GConfEntry *entry,
gpointer user_data)
{
ShellAppSystem *system = SHELL_APP_SYSTEM (user_data);
reread_favorite_apps (system);
g_signal_emit (G_OBJECT (system), signals[FAVORITES_CHANGED], 0);
}
GType
shell_app_info_get_type (void)
{
@ -394,19 +520,58 @@ shell_app_info_get_type (void)
return gtype;
}
GType
shell_app_menu_entry_get_type (void)
{
static GType gtype = G_TYPE_INVALID;
if (gtype == G_TYPE_INVALID)
{
gtype = g_boxed_type_register_static ("ShellAppMenuEntry",
shell_app_menu_entry_copy,
shell_app_menu_entry_free);
}
return gtype;
}
/**
* shell_app_system_get_flattened_apps:
* shell_app_system_get_applications_for_menu:
*
* Traverses a toplevel menu, and returns all items under it. Nested items
* are flattened. This value is computed on initial call and cached thereafter
* until the set of installed applications changes.
* are flattened.
*
* Return value: (transfer none) (element-type ShellAppInfo): List of applications
* Return value: (transfer full) (element-type ShellAppInfo): List of applications
*/
GSList *
shell_app_system_get_flattened_apps (ShellAppSystem *self)
shell_app_system_get_applications_for_menu (ShellAppSystem *monitor,
const char *menu)
{
return self->priv->cached_flattened_apps;
char *path;
GMenuTreeDirectory *menu_entry;
GSList *apps;
path = g_strdup_printf ("/%s", menu);
menu_entry = gmenu_tree_get_directory_from_path (monitor->priv->apps_tree, path);
g_free (path);
g_assert (menu_entry != NULL);
apps = gather_entries_recurse (monitor, NULL, menu_entry);
gmenu_tree_item_unref (menu_entry);
return apps;
}
/**
* shell_app_system_get_menus:
*
* Returns a list of toplevel #ShellAppMenuEntry items
*
* Return value: (transfer none) (element-type AppMenuEntry): List of toplevel menus
*/
GSList *
shell_app_system_get_menus (ShellAppSystem *monitor)
{
return monitor->priv->cached_app_menus;
}
/**
@ -438,70 +603,93 @@ shell_app_system_get_default ()
return instance;
}
typedef struct {
ShellAppSystem *appsys;
ShellAppInfo *info;
} ShellAppRef;
/**
* shell_app_system_get_favorites:
*
* 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
*/
GList *
shell_app_system_get_favorites (ShellAppSystem *system)
{
return system->priv->cached_favorites;
}
static void
shell_app_system_on_app_weakref (gpointer data,
GObject *location)
set_gconf_value_string_list (GConfValue *val, GList *items)
{
ShellAppRef *ref = data;
GList *iter;
GSList *tmp = NULL;
g_hash_table_remove (ref->appsys->priv->app_id_to_app, shell_app_info_get_id (ref->info));
shell_app_info_unref (ref->info);
g_free (ref);
for (iter = items; iter; iter = iter->next)
{
const char *str = iter->data;
GConfValue *strval = gconf_value_new (GCONF_VALUE_STRING);
gconf_value_set_string (strval, str);
tmp = g_slist_prepend (tmp, strval);
}
tmp = g_slist_reverse (tmp);
gconf_value_set_list (val, tmp);
g_slist_free (tmp);
}
void
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;
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));
set_gconf_value_string_list (val, system->priv->cached_favorites);
gconf_client_set (client, SHELL_APP_FAVORITES_KEY, val, NULL);
}
void
shell_app_system_remove_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;
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);
gconf_client_set (client, SHELL_APP_FAVORITES_KEY, val, NULL);
}
/**
* shell_app_system_get_app:
* shell_app_system_lookup_app:
*
* Find or create a #ShellApp corresponding to an id; if already cached
* elsewhere in memory, return that instance. Otherwise, create a new
* one.
*
* Return value: (transfer full): The #ShellApp for id, or %NULL if none
* Return value: (transfer full): The #ShellAppInfo for id, or %NULL if none
*/
ShellApp *
shell_app_system_get_app (ShellAppSystem *self,
const char *id)
ShellAppInfo *
shell_app_system_lookup_cached_app (ShellAppSystem *self, const char *id)
{
ShellAppInfo *info;
ShellApp *app;
app = g_hash_table_lookup (self->priv->app_id_to_app, id);
if (app)
return g_object_ref (app);
info = g_hash_table_lookup (self->priv->app_id_to_info, id);
if (!info)
return NULL;
app = _shell_app_new (info);
return app;
}
/* ShellAppSystem ensures we have a unique instance of
* apps per id.
*/
void
_shell_app_system_register_app (ShellAppSystem *self,
ShellApp *app)
{
const char *id;
ShellAppRef *ref;
id = shell_app_get_id (app);
g_return_if_fail (g_hash_table_lookup (self->priv->app_id_to_app, id) == NULL);
ref = g_new0 (ShellAppRef, 1);
ref->appsys = self;
ref->info = shell_app_info_ref (_shell_app_get_info (app));
g_hash_table_insert (self->priv->app_id_to_app, (char*)shell_app_info_get_id (ref->info), app);
g_object_weak_ref (G_OBJECT (app), shell_app_system_on_app_weakref, ref);
info = g_hash_table_lookup (self->priv->app_id_to_app, id);
if (info)
shell_app_info_ref (info);
return info;
}
ShellAppInfo *
@ -565,16 +753,16 @@ shell_app_system_create_from_window (ShellAppSystem *system, MetaWindow *window)
* heuristically determined application identifier
* string, or %NULL if none.
*
* Returns: (transfer full): A #ShellApp for name
* Returns: (transfer full): A #ShellAppInfo for name
*/
ShellApp *
ShellAppInfo *
shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
const char *name)
{
ShellApp *result;
ShellAppInfo *result;
char **vendor_prefixes;
result = shell_app_system_get_app (system, name);
result = shell_app_system_lookup_cached_app (system, name);
if (result != NULL)
return result;
@ -582,7 +770,7 @@ shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
*vendor_prefixes; vendor_prefixes++)
{
char *tmpid = g_strjoin (NULL, *vendor_prefixes, "-", name, NULL);
result = shell_app_system_get_app (system, tmpid);
result = shell_app_system_lookup_cached_app (system, tmpid);
g_free (tmpid);
if (result != NULL)
return result;
@ -591,253 +779,6 @@ shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
return NULL;
}
typedef enum {
MATCH_NONE,
MATCH_MULTIPLE, /* Matches multiple terms */
MATCH_PREFIX, /* Strict prefix */
MATCH_SUBSTRING /* Not prefix, substring */
} ShellAppInfoSearchMatch;
static char *
normalize_and_casefold (const char *str)
{
char *normalized, *result;
if (str == NULL)
return NULL;
normalized = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
result = g_utf8_casefold (normalized, -1);
g_free (normalized);
return result;
}
static void
shell_app_info_init_search_data (ShellAppInfo *info)
{
const char *name;
const char *comment;
g_assert (info->type == SHELL_APP_INFO_TYPE_ENTRY);
name = gmenu_tree_entry_get_name ((GMenuTreeEntry*)info->entry);
info->casefolded_name = normalize_and_casefold (name);
comment = gmenu_tree_entry_get_comment ((GMenuTreeEntry*)info->entry);
info->casefolded_description = normalize_and_casefold (comment);
}
static ShellAppInfoSearchMatch
shell_app_info_match_terms (ShellAppInfo *info,
GSList *terms)
{
GSList *iter;
ShellAppInfoSearchMatch match;
if (G_UNLIKELY(!info->casefolded_name))
shell_app_info_init_search_data (info);
match = MATCH_NONE;
for (iter = terms; iter; iter = iter->next)
{
const char *term = iter->data;
const char *p;
p = strstr (info->casefolded_name, term);
if (p == info->casefolded_name)
{
if (match != MATCH_NONE)
return MATCH_MULTIPLE;
else
match = MATCH_PREFIX;
}
else if (p != NULL)
match = MATCH_SUBSTRING;
if (!info->casefolded_description)
continue;
p = strstr (info->casefolded_description, term);
if (p != NULL)
match = MATCH_SUBSTRING;
}
return match;
}
static gint
shell_app_info_compare (gconstpointer a,
gconstpointer b,
gpointer data)
{
ShellAppSystem *system = data;
const char *id_a = a;
const char *id_b = b;
ShellAppInfo *info_a = g_hash_table_lookup (system->priv->app_id_to_info, id_a);
ShellAppInfo *info_b = g_hash_table_lookup (system->priv->app_id_to_info, id_b);
if (!info_a->name_collation_key)
info_a->name_collation_key = g_utf8_collate_key (gmenu_tree_entry_get_name ((GMenuTreeEntry*)info_a->entry), -1);
if (!info_b->name_collation_key)
info_b->name_collation_key = g_utf8_collate_key (gmenu_tree_entry_get_name ((GMenuTreeEntry*)info_b->entry), -1);
return strcmp (info_a->name_collation_key, info_b->name_collation_key);
}
static GSList *
sort_and_concat_results (ShellAppSystem *system,
GSList *multiple_matches,
GSList *prefix_matches,
GSList *substring_matches)
{
multiple_matches = g_slist_sort_with_data (multiple_matches, shell_app_info_compare, system);
prefix_matches = g_slist_sort_with_data (prefix_matches, shell_app_info_compare, system);
substring_matches = g_slist_sort_with_data (substring_matches, shell_app_info_compare, system);
return g_slist_concat (multiple_matches, g_slist_concat (prefix_matches, substring_matches));
}
/**
* normalize_terms:
* @terms: (element-type utf8): Input search terms
*
* Returns: (element-type utf8) (transfer full): Unicode-normalized and lowercased terms
*/
static GSList *
normalize_terms (GSList *terms)
{
GSList *normalized_terms = NULL;
GSList *iter;
for (iter = terms; iter; iter = iter->next)
{
const char *term = iter->data;
normalized_terms = g_slist_prepend (normalized_terms, normalize_and_casefold (term));
}
return normalized_terms;
}
static inline void
shell_app_system_do_match (ShellAppSystem *system,
ShellAppInfo *info,
GSList *terms,
GSList **multiple_results,
GSList **prefix_results,
GSList **substring_results)
{
const char *id = shell_app_info_get_id (info);
ShellAppInfoSearchMatch match;
if (shell_app_info_get_is_nodisplay (info))
return;
match = shell_app_info_match_terms (info, terms);
switch (match)
{
case MATCH_NONE:
break;
case MATCH_MULTIPLE:
*multiple_results = g_slist_prepend (*multiple_results, (char *) id);
break;
case MATCH_PREFIX:
*prefix_results = g_slist_prepend (*prefix_results, (char *) id);
break;
case MATCH_SUBSTRING:
*substring_results = g_slist_prepend (*substring_results, (char *) id);
break;
}
}
static GSList *
shell_app_system_initial_search_internal (ShellAppSystem *self,
GSList *terms,
GSList *source)
{
GSList *multiple_results = NULL;
GSList *prefix_results = NULL;
GSList *substring_results = NULL;
GSList *iter;
GSList *normalized_terms = normalize_terms (terms);
for (iter = source; iter; iter = iter->next)
{
ShellAppInfo *info = iter->data;
shell_app_system_do_match (self, info, normalized_terms, &multiple_results, &prefix_results, &substring_results);
}
g_slist_foreach (normalized_terms, (GFunc)g_free, NULL);
g_slist_free (normalized_terms);
return sort_and_concat_results (self, multiple_results, prefix_results, substring_results);
}
/**
* shell_app_system_initial_search:
* @self: A #ShellAppSystem
* @prefs: %TRUE iff we should search preferences instead of apps
* @terms: (element-type utf8): List of terms, logical OR
*
* Search through applications for the given search terms. Note that returned
* strings are only valid until a return to the main loop.
*
* Returns: (transfer container) (element-type utf8): List of application identifiers
*/
GSList *
shell_app_system_initial_search (ShellAppSystem *self,
gboolean prefs,
GSList *terms)
{
return shell_app_system_initial_search_internal (self, terms,
prefs ? self->priv->cached_settings : self->priv->cached_flattened_apps);
}
/**
* shell_app_system_subsearch:
* @self: A #ShellAppSystem
* @prefs: %TRUE iff we should search preferences instead of apps
* @previous_results: (element-type utf8): List of previous results
* @terms: (element-type utf8): List of terms, logical OR
*
* Search through a previous result set; for more information, see
* js/ui/search.js. Note the value of @prefs must be
* the same as passed to shell_app_system_initial_search(). Note that returned
* strings are only valid until a return to the main loop.
*
* Returns: (transfer container) (element-type utf8): List of application identifiers
*/
GSList *
shell_app_system_subsearch (ShellAppSystem *system,
gboolean prefs,
GSList *previous_results,
GSList *terms)
{
GSList *iter;
GSList *multiple_results = NULL;
GSList *prefix_results = NULL;
GSList *substring_results = NULL;
GSList *normalized_terms = normalize_terms (terms);
/* Note prefs is deliberately ignored; both apps and prefs are in app_id_to_app,
* but we have the parameter for consistency and in case in the future
* they're not in the same data structure.
*/
for (iter = previous_results; iter; iter = iter->next)
{
const char *id = iter->data;
ShellAppInfo *info;
info = g_hash_table_lookup (system->priv->app_id_to_info, id);
if (!info)
continue;
shell_app_system_do_match (system, info, normalized_terms, &multiple_results, &prefix_results, &substring_results);
}
g_slist_foreach (normalized_terms, (GFunc)g_free, NULL);
g_slist_free (normalized_terms);
/* Note that a shorter term might have matched as a prefix, but
when extended only as a substring, so we have to redo the
sort rather than reusing the existing ordering */
return sort_and_concat_results (system, multiple_results, prefix_results, substring_results);
}
const char *
shell_app_info_get_id (ShellAppInfo *info)
{
@ -869,8 +810,6 @@ shell_app_info_get_name (ShellAppInfo *info)
{
char *title;
g_object_get (info->window, "title", &title, NULL);
if (!title)
title = g_strdup ("");
return title;
}
}

View File

@ -1,11 +1,9 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef __SHELL_APP_SYSTEM_H__
#define __SHELL_APP_SYSTEM_H__
#include <gio/gio.h>
#include <clutter/clutter.h>
#include "shell-app.h"
#include "window.h"
#define SHELL_TYPE_APP_SYSTEM (shell_app_system_get_type ())
@ -37,6 +35,18 @@ struct _ShellAppSystemClass
GType shell_app_system_get_type (void) G_GNUC_CONST;
ShellAppSystem* shell_app_system_get_default(void);
GSList *shell_app_system_get_applications_for_menu (ShellAppSystem *system, const char *menu);
typedef struct _ShellAppMenuEntry ShellAppMenuEntry;
struct _ShellAppMenuEntry {
char *name;
char *id;
char *icon;
};
GType shell_app_menu_entry_get_type (void);
typedef struct _ShellAppInfo ShellAppInfo;
#define SHELL_TYPE_APP_INFO (shell_app_info_get_type ())
@ -65,25 +75,20 @@ gboolean shell_app_info_launch (ShellAppInfo *info,
ShellAppInfo *shell_app_system_load_from_desktop_file (ShellAppSystem *system, const char *filename, GError **error);
ShellApp *shell_app_system_get_app (ShellAppSystem *system, const char *id);
ShellAppInfo *shell_app_system_lookup_cached_app (ShellAppSystem *system, const char *id);
void _shell_app_system_register_app (ShellAppSystem *self, ShellApp *app);
ShellApp *shell_app_system_lookup_heuristic_basename (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);
GSList *shell_app_system_get_flattened_apps (ShellAppSystem *system);
GSList *shell_app_system_get_menus (ShellAppSystem *system);
GSList *shell_app_system_get_all_settings (ShellAppSystem *system);
GSList *shell_app_system_initial_search (ShellAppSystem *system,
gboolean prefs,
GSList *terms);
GList *shell_app_system_get_favorites (ShellAppSystem *system);
GSList *shell_app_system_subsearch (ShellAppSystem *system,
gboolean prefs,
GSList *previous_results,
GSList *terms);
void shell_app_system_add_favorite (ShellAppSystem *system, const char *id);
void shell_app_system_remove_favorite (ShellAppSystem *system, const char *id);
#endif /* __SHELL_APP_SYSTEM_H__ */

View File

@ -1,973 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#include "config.h"
#include <string.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <gdk/gdk.h>
#include <gdk/gdkx.h>
#include <gio/gio.h>
#include <gconf/gconf.h>
#include <gconf/gconf-client.h>
#include <dbus/dbus-glib.h>
#include "shell-app-usage.h"
#include "shell-window-tracker.h"
#include "shell-global.h"
#include "shell-marshal.h"
#include "display.h"
#include "window.h"
#include "group.h"
/* This file includes modified code from
* desktop-data-engine/engine-dbus/hippo-application-monitor.c
* in the functions collecting application usage data.
* Written by Owen Taylor, originally licensed under LGPL 2.1.
* Copyright Red Hat, Inc. 2006-2008
*/
/**
* SECTION:shell-app-usage
* @short_description: Track application usage/state data
*
* This class maintains some usage and state statistics for
* applications by keeping track of the approximate time an application's
* windows are focused, as well as the last workspace it was seen on.
* This time tracking is implemented by watching for focus notifications,
* and computing a time delta between them. Also we watch the
* GNOME Session "StatusChanged" signal which by default is emitted after 5
* minutes to signify idle.
*/
#define APP_MONITOR_GCONF_DIR SHELL_GCONF_DIR"/app_monitor"
#define ENABLE_MONITORING_KEY APP_MONITOR_GCONF_DIR"/enable_monitoring"
#define FOCUS_TIME_MIN_SECONDS 7 /* Need 7 continuous seconds of focus */
#define USAGE_CLEAN_DAYS 7 /* If after 7 days we haven't seen an app, purge it */
/* Data is saved to file SHELL_CONFIG_DIR/DATA_FILENAME */
#define DATA_FILENAME "application_state"
#define IDLE_TIME_TRANSITION_SECONDS 30 /* If we transition to idle, only count
* this many seconds of usage */
/* The ranking algorithm we use is: every time an app score reaches SCORE_MAX,
* divide all scores by 2. Scores are raised by 1 unit every SAVE_APPS_TIMEOUT
* seconds. This mechanism allows the list to update relatively fast when
* a new app is used intensively.
* To keep the list clean, and avoid being Big Brother, apps that have not been
* seen for a week and whose score is below SCORE_MIN are removed.
*/
/* How often we save internally app data, in seconds */
#define SAVE_APPS_TIMEOUT_SECONDS (5 * 60)
/* With this value, an app goes from bottom to top of the
* usage list in 50 hours of use */
#define SCORE_MAX (3600 * 50 / FOCUS_TIME_MIN_SECONDS)
/* If an app's score in lower than this and the app has not been used in a week,
* remove it */
#define SCORE_MIN (SCORE_MAX >> 3)
/* http://www.gnome.org/~mccann/gnome-session/docs/gnome-session.html#org.gnome.SessionManager.Presence */
#define GNOME_SESSION_STATUS_IDLE 3
typedef struct UsageData UsageData;
struct _ShellAppUsage
{
GObject parent;
GFile *configfile;
DBusGProxy *session_proxy;
GdkDisplay *display;
GConfClient *gconf_client;
gulong last_idle;
guint idle_focus_change_id;
guint save_id;
guint gconf_notify;
gboolean currently_idle;
gboolean enable_monitoring;
GSList *previously_running;
long watch_start_time;
ShellApp *watched_app;
/* <char *context, GHashTable<char *appid, UsageData *usage>> */
GHashTable *app_usages_for_context;
};
G_DEFINE_TYPE (ShellAppUsage, shell_app_usage, G_TYPE_OBJECT);
/* Represents an application record for a given context */
struct UsageData
{
/* Whether the application we're tracking is "transient", see
* shell_app_info_is_transient.
*/
gboolean transient;
gdouble score; /* Based on the number of times we'e seen the app and normalized */
long last_seen; /* Used to clear old apps we've only seen a few times */
};
static void shell_app_usage_finalize (GObject *object);
static void on_session_status_changed (DBusGProxy *proxy, guint status, ShellAppUsage *self);
static void on_focus_app_changed (ShellWindowTracker *tracker, GParamSpec *spec, ShellAppUsage *self);
static void ensure_queued_save (ShellAppUsage *self);
static UsageData * get_app_usage_for_context_and_id (ShellAppUsage *self,
const char *context,
const char *appid);
static gboolean idle_save_application_usage (gpointer data);
static void restore_from_file (ShellAppUsage *self);
static void update_enable_monitoring (ShellAppUsage *self);
static void on_enable_monitoring_key_changed (GConfClient *client,
guint connexion_id,
GConfEntry *entry,
gpointer self);
static long
get_time (void)
{
GTimeVal tv;
g_get_current_time (&tv);
return tv.tv_sec;
}
static void
shell_app_usage_class_init (ShellAppUsageClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = shell_app_usage_finalize;
}
static GHashTable *
get_usages_for_context (ShellAppUsage *self,
const char *context)
{
GHashTable *context_usages;
context_usages = g_hash_table_lookup (self->app_usages_for_context, context);
if (context_usages == NULL)
{
context_usages = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
g_hash_table_insert (self->app_usages_for_context, g_strdup (context),
context_usages);
}
return context_usages;
}
static UsageData *
get_app_usage_for_context_and_id (ShellAppUsage *self,
const char *context,
const char *appid)
{
UsageData *usage;
GHashTable *context_usages;
context_usages = get_usages_for_context (self, context);
usage = g_hash_table_lookup (context_usages, appid);
if (usage)
return usage;
usage = g_new0 (UsageData, 1);
g_hash_table_insert (context_usages, g_strdup (appid), usage);
return usage;
}
static UsageData *
get_usage_for_app (ShellAppUsage *self,
ShellApp *app)
{
const char *context;
context = _shell_window_tracker_get_app_context (shell_window_tracker_get_default (), app);
return get_app_usage_for_context_and_id (self, context, shell_app_get_id (app));
}
typedef struct {
gboolean in_context;
GHashTableIter context_iter;
const char *context_id;
GHashTableIter usage_iter;
} UsageIterator;
static void
usage_iterator_init (ShellAppUsage *self,
UsageIterator *iter)
{
iter->in_context = FALSE;
g_hash_table_iter_init (&(iter->context_iter), self->app_usages_for_context);
}
static gboolean
usage_iterator_next (ShellAppUsage *self,
UsageIterator *iter,
const char **context,
const char **id,
UsageData **usage)
{
gpointer key, value;
gboolean next_context;
if (!iter->in_context)
next_context = TRUE;
else if (!g_hash_table_iter_next (&(iter->usage_iter), &key, &value))
next_context = TRUE;
else
next_context = FALSE;
while (next_context)
{
GHashTable *app_usages;
if (!g_hash_table_iter_next (&(iter->context_iter), &key, &value))
return FALSE;
iter->in_context = TRUE;
iter->context_id = key;
app_usages = value;
g_hash_table_iter_init (&(iter->usage_iter), app_usages);
next_context = !g_hash_table_iter_next (&(iter->usage_iter), &key, &value);
}
*context = iter->context_id;
*id = key;
*usage = value;
return TRUE;
}
static void
usage_iterator_remove (ShellAppUsage *self,
UsageIterator *iter)
{
g_assert (iter->in_context);
g_hash_table_iter_remove (&(iter->usage_iter));
}
/* Limit the score to a certain level so that most used apps can change */
static void
normalize_usage (ShellAppUsage *self)
{
UsageIterator iter;
const char *context;
const char *id;
UsageData *usage;
usage_iterator_init (self, &iter);
while (usage_iterator_next (self, &iter, &context, &id, &usage))
{
usage->score /= 2;
}
}
static void
increment_usage_for_app_at_time (ShellAppUsage *self,
ShellApp *app,
long time)
{
UsageData *usage;
guint elapsed;
guint usage_count;
usage = get_usage_for_app (self, app);
usage->last_seen = time;
elapsed = time - self->watch_start_time;
usage_count = elapsed / FOCUS_TIME_MIN_SECONDS;
if (usage_count > 0)
{
usage->score += usage_count;
if (usage->score > SCORE_MAX)
normalize_usage (self);
ensure_queued_save (self);
}
}
static void
increment_usage_for_app (ShellAppUsage *self,
ShellApp *app)
{
long curtime = get_time ();
increment_usage_for_app_at_time (self, app, curtime);
}
static void
on_app_running_changed (ShellWindowTracker *tracker,
ShellApp *app,
gpointer user_data)
{
ShellAppUsage *self = SHELL_APP_USAGE (user_data);
UsageData *usage;
gboolean running;
if (shell_app_is_transient (app))
return;
usage = get_usage_for_app (self, app);
running = shell_app_get_n_windows (app) > 0;
usage->last_seen = get_time ();
}
static void
on_focus_app_changed (ShellWindowTracker *tracker,
GParamSpec *spec,
ShellAppUsage *self)
{
if (self->watched_app != NULL)
increment_usage_for_app (self, self->watched_app);
if (self->watched_app)
g_object_unref (self->watched_app);
g_object_get (tracker, "focus-app", &(self->watched_app), NULL);
self->watch_start_time = get_time ();
}
static void
on_session_status_changed (DBusGProxy *proxy,
guint status,
ShellAppUsage *self)
{
gboolean idle;
idle = (status >= GNOME_SESSION_STATUS_IDLE);
if (self->currently_idle == idle)
return;
self->currently_idle = idle;
if (idle)
{
long end_time;
/* The GNOME Session signal we watch is 5 minutes, but that's a long
* time for this purpose. Instead, just add a base 30 seconds.
*/
if (self->watched_app)
{
end_time = self->watch_start_time + IDLE_TIME_TRANSITION_SECONDS;
increment_usage_for_app_at_time (self, self->watched_app, end_time);
}
}
else
{
/* Transitioning to !idle, reset the start time */
self->watch_start_time = get_time ();
}
}
static void
shell_app_usage_init (ShellAppUsage *self)
{
char *shell_config_dir, *path;
DBusGConnection *session_bus;
ShellWindowTracker *tracker;
self->app_usages_for_context = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_hash_table_destroy);
tracker = shell_window_tracker_get_default ();
g_signal_connect (tracker, "notify::focus-app", G_CALLBACK (on_focus_app_changed), self);
g_signal_connect (tracker, "app-running-changed", G_CALLBACK (on_app_running_changed), self);
session_bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
self->session_proxy = dbus_g_proxy_new_for_name (session_bus, "org.gnome.SessionManager",
"/org/gnome/SessionManager/Presence",
"org.gnome.SessionManager");
dbus_g_proxy_add_signal (self->session_proxy, "StatusChanged",
G_TYPE_UINT, G_TYPE_INVALID, G_TYPE_INVALID);
dbus_g_proxy_connect_signal (self->session_proxy, "StatusChanged",
G_CALLBACK (on_session_status_changed), self, NULL);
self->last_idle = 0;
self->currently_idle = FALSE;
self->enable_monitoring = FALSE;
g_object_get (shell_global_get(), "configdir", &shell_config_dir, NULL),
path = g_build_filename (shell_config_dir, DATA_FILENAME, NULL);
g_free (shell_config_dir);
self->configfile = g_file_new_for_path (path);
g_free (path);
restore_from_file (self);
self->gconf_client = gconf_client_get_default ();
gconf_client_add_dir (self->gconf_client, APP_MONITOR_GCONF_DIR,
GCONF_CLIENT_PRELOAD_NONE, NULL);
self->gconf_notify =
gconf_client_notify_add (self->gconf_client, ENABLE_MONITORING_KEY,
on_enable_monitoring_key_changed, self, NULL, NULL);
update_enable_monitoring (self);
}
static void
shell_app_usage_finalize (GObject *object)
{
ShellAppUsage *self = SHELL_APP_USAGE (object);
if (self->save_id > 0)
g_source_remove (self->save_id);
gconf_client_notify_remove (self->gconf_client, self->gconf_notify);
g_object_unref (self->gconf_client);
g_object_unref (self->configfile);
G_OBJECT_CLASS (shell_app_usage_parent_class)->finalize(object);
}
typedef struct {
ShellAppUsage *usage;
GHashTable *context_usages;
} SortAppsByUsageData;
static int
sort_apps_by_usage (gconstpointer a,
gconstpointer b,
gpointer datap)
{
SortAppsByUsageData *data = datap;
ShellApp *app_a, *app_b;
UsageData *usage_a, *usage_b;
app_a = (ShellApp*)a;
app_b = (ShellApp*)b;
usage_a = g_hash_table_lookup (data->context_usages, shell_app_get_id (app_a));
usage_b = g_hash_table_lookup (data->context_usages, shell_app_get_id (app_b));
return usage_b->score - usage_a->score;
}
/**
* shell_app_usage_get_most_used:
* @usage: the usage instance to request
* @context: Activity identifier
* @max_count: how many applications are requested. Note that the actual
* list size may be less, or NULL if not enough applications are registered.
*
* Get a list of most popular applications for a given context.
*
* Returns: (element-type ShellApp) (transfer full): List of applications
*/
GSList *
shell_app_usage_get_most_used (ShellAppUsage *self,
const char *context,
gint max_count)
{
GSList *apps;
GList *appids, *iter;
GHashTable *usages;
ShellAppSystem *appsys;
SortAppsByUsageData data;
usages = g_hash_table_lookup (self->app_usages_for_context, context);
if (usages == NULL)
return NULL;
appsys = shell_app_system_get_default ();
appids = g_hash_table_get_keys (usages);
apps = NULL;
for (iter = appids; iter; iter = iter->next)
{
const char *appid = iter->data;
ShellApp *app;
app = shell_app_system_get_app (appsys, appid);
if (!app)
continue;
apps = g_slist_prepend (apps, g_object_ref (app));
}
g_list_free (appids);
data.usage = self;
data.context_usages = usages;
apps = g_slist_sort_with_data (apps, sort_apps_by_usage, &data);
return apps;
}
static void
ensure_queued_save (ShellAppUsage *self)
{
if (self->save_id != 0)
return;
self->save_id = g_timeout_add_seconds (SAVE_APPS_TIMEOUT_SECONDS, idle_save_application_usage, self);
}
/* Used to sort highest scores at the top */
static gint
usage_sort_apps (gconstpointer data1,
gconstpointer data2)
{
const UsageData *u1 = data1;
const UsageData *u2 = data2;
if (u1->score > u2->score)
return -1;
else if (u1->score == u2->score)
return 0;
else
return 1;
}
/* Clean up apps we see rarely.
* The logic behind this is that if an app was seen less than SCORE_MIN times
* and not seen for a week, it can probably be forgotten about.
* This should much reduce the size of the list and avoid 'pollution'. */
static gboolean
idle_clean_usage (ShellAppUsage *self)
{
UsageIterator iter;
const char *context;
const char *id;
UsageData *usage;
long current_time;
long week_ago;
current_time = get_time ();
week_ago = current_time - (7 * 24 * 60 * 60);
usage_iterator_init (self, &iter);
while (usage_iterator_next (self, &iter, &context, &id, &usage))
{
if ((usage->score < SCORE_MIN) &&
(usage->last_seen < week_ago))
usage_iterator_remove (self, &iter);
}
return FALSE;
}
static gboolean
write_escaped (GDataOutputStream *stream,
const char *str,
GError **error)
{
gboolean ret;
char *quoted = g_markup_escape_text (str, -1);
ret = g_data_output_stream_put_string (stream, quoted, NULL, error);
g_free (quoted);
return ret;
}
static gboolean
write_attribute_string (GDataOutputStream *stream,
const char *elt_name,
const char *str,
GError **error)
{
gboolean ret = FALSE;
char *elt;
elt = g_strdup_printf (" %s=\"", elt_name);
ret = g_data_output_stream_put_string (stream, elt, NULL, error);
g_free (elt);
if (!ret)
goto out;
ret = write_escaped (stream, str, error);
if (!ret)
goto out;
ret = g_data_output_stream_put_string (stream, "\"", NULL, error);
out:
return ret;
}
static gboolean
write_attribute_uint (GDataOutputStream *stream,
const char *elt_name,
guint value,
GError **error)
{
gboolean ret;
char *buf;
buf = g_strdup_printf ("%u", value);
ret = write_attribute_string (stream, elt_name, buf, error);
g_free (buf);
return ret;
}
static gboolean
write_attribute_double (GDataOutputStream *stream,
const char *elt_name,
double value,
GError **error)
{
gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
gboolean ret;
g_ascii_dtostr (buf, sizeof (buf), value);
ret = write_attribute_string (stream, elt_name, buf, error);
return ret;
}
/* Save app data lists to file */
static gboolean
idle_save_application_usage (gpointer data)
{
ShellAppUsage *self = SHELL_APP_USAGE (data);
UsageIterator iter;
const char *current_context;
const char *context;
const char *id;
UsageData *usage;
GFileOutputStream *output;
GOutputStream *buffered_output;
GDataOutputStream *data_output;
GError *error = NULL;
self->save_id = 0;
/* Parent directory is already created by shell-global */
output = g_file_replace (self->configfile, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error);
if (!output)
{
g_debug ("Could not save applications usage data: %s", error->message);
g_error_free (error);
return FALSE;
}
buffered_output = g_buffered_output_stream_new (G_OUTPUT_STREAM (output));
g_object_unref (output);
data_output = g_data_output_stream_new (G_OUTPUT_STREAM (buffered_output));
g_object_unref (buffered_output);
if (!g_data_output_stream_put_string (data_output, "<?xml version=\"1.0\"?>\n<application-state>\n", NULL, &error))
goto out;
usage_iterator_init (self, &iter);
current_context = NULL;
while (usage_iterator_next (self, &iter, &context, &id, &usage))
{
ShellApp *app;
app = shell_app_system_get_app (shell_app_system_get_default(), id);
if (!app)
continue;
if (context != current_context)
{
if (current_context != NULL)
{
if (!g_data_output_stream_put_string (data_output, " </context>", NULL, &error))
goto out;
}
current_context = context;
if (!g_data_output_stream_put_string (data_output, " <context", NULL, &error))
goto out;
if (!write_attribute_string (data_output, "id", context, &error))
goto out;
if (!g_data_output_stream_put_string (data_output, ">\n", NULL, &error))
goto out;
}
if (!g_data_output_stream_put_string (data_output, " <application", NULL, &error))
goto out;
if (!write_attribute_string (data_output, "id", id, &error))
goto out;
if (!write_attribute_uint (data_output, "open-window-count", shell_app_get_n_windows (app), &error))
goto out;
if (!write_attribute_double (data_output, "score", usage->score, &error))
goto out;
if (!write_attribute_uint (data_output, "last-seen", usage->last_seen, &error))
goto out;
if (!g_data_output_stream_put_string (data_output, "/>\n", NULL, &error))
goto out;
}
if (current_context != NULL)
{
if (!g_data_output_stream_put_string (data_output, " </context>\n", NULL, &error))
goto out;
}
if (!g_data_output_stream_put_string (data_output, "</application-state>\n", NULL, &error))
goto out;
out:
if (!error)
g_output_stream_close_async (G_OUTPUT_STREAM (data_output), 0, NULL, NULL, NULL);
g_object_unref (data_output);
if (error)
{
g_debug ("Could not save applications usage data: %s", error->message);
g_error_free (error);
}
return FALSE;
}
typedef struct {
ShellAppUsage *self;
char *context;
} ParseData;
static void
shell_app_usage_start_element_handler (GMarkupParseContext *context,
const gchar *element_name,
const gchar **attribute_names,
const gchar **attribute_values,
gpointer user_data,
GError **error)
{
ParseData *data = user_data;
if (strcmp (element_name, "application-state") == 0)
{
}
else if (strcmp (element_name, "context") == 0)
{
char *context = NULL;
const char **attribute;
const char **value;
for (attribute = attribute_names, value = attribute_values; *attribute; attribute++, value++)
{
if (strcmp (*attribute, "id") == 0)
context = g_strdup (*value);
}
if (context < 0)
{
g_set_error (error,
G_MARKUP_ERROR,
G_MARKUP_ERROR_PARSE,
"Missing attribute id on <%s> element",
element_name);
return;
}
data->context = context;
}
else if (strcmp (element_name, "application") == 0)
{
const char **attribute;
const char **value;
UsageData *usage;
char *appid = NULL;
GHashTable *usage_table;
for (attribute = attribute_names, value = attribute_values; *attribute; attribute++, value++)
{
if (strcmp (*attribute, "id") == 0)
appid = g_strdup (*value);
}
if (!appid)
{
g_set_error (error,
G_MARKUP_ERROR,
G_MARKUP_ERROR_PARSE,
"Missing attribute id on <%s> element",
element_name);
return;
}
usage_table = get_usages_for_context (data->self, data->context);
usage = g_new0 (UsageData, 1);
g_hash_table_insert (usage_table, appid, usage);
for (attribute = attribute_names, value = attribute_values; *attribute; attribute++, value++)
{
if (strcmp (*attribute, "open-window-count") == 0)
{
guint count = strtoul (*value, NULL, 10);
if (count > 0)
data->self->previously_running = g_slist_prepend (data->self->previously_running,
usage);
}
else if (strcmp (*attribute, "score") == 0)
{
usage->score = g_ascii_strtod (*value, NULL);
}
else if (strcmp (*attribute, "last-seen") == 0)
{
usage->last_seen = (guint) g_ascii_strtoull (*value, NULL, 10);
}
}
}
else
{
g_set_error (error,
G_MARKUP_ERROR,
G_MARKUP_ERROR_PARSE,
"Unknown element <%s>",
element_name);
}
}
static void
shell_app_usage_end_element_handler (GMarkupParseContext *context,
const gchar *element_name,
gpointer user_data,
GError **error)
{
ParseData *data = user_data;
if (strcmp (element_name, "context") == 0)
{
g_free (data->context);
data->context = NULL;
}
}
static void
shell_app_usage_text_handler (GMarkupParseContext *context,
const gchar *text,
gsize text_len,
gpointer user_data,
GError **error)
{
/* do nothing, very very fast */
}
static GMarkupParser app_state_parse_funcs =
{
shell_app_usage_start_element_handler,
shell_app_usage_end_element_handler,
shell_app_usage_text_handler,
NULL,
NULL
};
/* Load data about apps usage from file */
static void
restore_from_file (ShellAppUsage *self)
{
GFileInputStream *input;
ParseData parse_data;
GMarkupParseContext *parse_context;
GError *error = NULL;
char buf[1024];
input = g_file_read (self->configfile, NULL, &error);
if (error)
{
if (error->code != G_IO_ERROR_NOT_FOUND)
g_warning ("Could not load applications usage data: %s", error->message);
g_error_free (error);
return;
}
memset (&parse_data, 0, sizeof (ParseData));
parse_data.self = self;
parse_data.context = NULL;
parse_context = g_markup_parse_context_new (&app_state_parse_funcs, 0, &parse_data, NULL);
while (TRUE)
{
gssize count = g_input_stream_read ((GInputStream*) input, buf, sizeof(buf), NULL, &error);
if (count <= 0)
goto out;
if (!g_markup_parse_context_parse (parse_context, buf, count, &error))
goto out;
}
out:
g_free (parse_data.context);
g_markup_parse_context_free (parse_context);
g_input_stream_close ((GInputStream*)input, NULL, NULL);
g_object_unref (input);
idle_clean_usage (self);
self->previously_running = g_slist_sort (self->previously_running, usage_sort_apps);
if (error)
{
g_warning ("Could not load applications usage data: %s", error->message);
g_error_free (error);
}
}
/* Enable or disable the timers, depending on the value of ENABLE_MONITORING_KEY
* and taking care of the previous state. If selfing is disabled, we still
* report apps usage based on (possibly) saved data, but don't collect data.
*/
static void
update_enable_monitoring (ShellAppUsage *self)
{
GConfValue *value;
gboolean enable;
value = gconf_client_get (self->gconf_client, ENABLE_MONITORING_KEY, NULL);
if (value)
{
enable = gconf_value_get_bool (value);
gconf_value_free (value);
}
else /* Schema is not present, set default value by hand to avoid getting FALSE */
enable = TRUE;
/* Be sure not to start the timers if they were already set */
if (enable && !self->enable_monitoring)
{
on_focus_app_changed (shell_window_tracker_get_default (), NULL, self);
}
/* ...and don't try to stop them if they were not running */
else if (!enable && self->enable_monitoring)
{
if (self->watched_app)
g_object_unref (self->watched_app);
self->watched_app = NULL;
if (self->save_id)
{
g_source_remove (self->save_id);
self->save_id = 0;
}
}
self->enable_monitoring = enable;
}
/* Called when the ENABLE_MONITORING_KEY boolean has changed */
static void
on_enable_monitoring_key_changed (GConfClient *client,
guint connexion_id,
GConfEntry *entry,
gpointer self)
{
update_enable_monitoring ((ShellAppUsage *) self);
}
/**
* shell_app_usage_get_default:
*
* Return Value: (transfer none): The global #ShellAppUsage instance
*/
ShellAppUsage *
shell_app_usage_get_default ()
{
static ShellAppUsage *instance;
if (instance == NULL)
instance = g_object_new (SHELL_TYPE_APP_USAGE, NULL);
return instance;
}

View File

@ -1,36 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef __SHELL_APP_USAGE_H__
#define __SHELL_APP_USAGE_H__
#include "shell-app.h"
#include "shell-window-tracker.h"
G_BEGIN_DECLS
typedef struct _ShellAppUsage ShellAppUsage;
typedef struct _ShellAppUsageClass ShellAppUsageClass;
typedef struct _ShellAppUsagePrivate ShellAppUsagePrivate;
#define SHELL_TYPE_APP_USAGE (shell_app_usage_get_type ())
#define SHELL_APP_USAGE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SHELL_TYPE_APP_USAGE, ShellAppUsage))
#define SHELL_APP_USAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_APP_USAGE, ShellAppUsageClass))
#define SHELL_IS_APP_USAGE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SHELL_TYPE_APP_USAGE))
#define SHELL_IS_APP_USAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_APP_USAGE))
#define SHELL_APP_USAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_APP_USAGE, ShellAppUsageClass))
struct _ShellAppUsageClass
{
GObjectClass parent_class;
};
GType shell_app_usage_get_type (void) G_GNUC_CONST;
ShellAppUsage* shell_app_usage_get_default(void);
GSList *shell_app_usage_get_most_used (ShellAppUsage *monitor,
const char *context,
gint number);
G_END_DECLS
#endif /* __SHELL_APP_USAGE_H__ */

View File

@ -1,379 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#include "config.h"
#include "shell-app-private.h"
#include "shell-global.h"
/**
* SECTION:shell-app
* @short_description: Object representing an application
*
* This object wraps a #ShellAppInfo, providing methods and signals
* primarily useful for running applications.
*/
struct _ShellApp
{
GObject parent;
ShellAppInfo *info;
guint workspace_switch_id;
gboolean window_sort_stale;
GSList *windows;
};
G_DEFINE_TYPE (ShellApp, shell_app, G_TYPE_OBJECT);
enum {
WINDOWS_CHANGED,
LAST_SIGNAL
};
static guint shell_app_signals[LAST_SIGNAL] = { 0 };
const char *
shell_app_get_id (ShellApp *app)
{
return shell_app_info_get_id (app->info);
}
/**
* shell_app_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_create_icon_texture (ShellApp *app,
float size)
{
return shell_app_info_create_icon_texture (app->info, size);
}
char *
shell_app_get_name (ShellApp *app)
{
return shell_app_info_get_name (app->info);
}
char *
shell_app_get_description (ShellApp *app)
{
return shell_app_info_get_description (app->info);
}
gboolean
shell_app_is_transient (ShellApp *app)
{
return shell_app_info_is_transient (app->info);
}
gboolean
shell_app_launch (ShellApp *self,
GError **error)
{
return shell_app_info_launch (self->info, error);
}
/**
* _shell_app_get_info:
*
* Returns: (transfer none): Associated app info
*/
ShellAppInfo *
_shell_app_get_info (ShellApp *app)
{
return app->info;
}
typedef struct {
ShellApp *app;
MetaWorkspace *active_workspace;
} CompareWindowsData;
static int
shell_app_compare_windows (gconstpointer a,
gconstpointer b,
gpointer datap)
{
MetaWindow *win_a = (gpointer)a;
MetaWindow *win_b = (gpointer)b;
CompareWindowsData *data = datap;
gboolean ws_a, ws_b;
gboolean vis_a, vis_b;
ws_a = meta_window_get_workspace (win_a) == data->active_workspace;
ws_b = meta_window_get_workspace (win_b) == data->active_workspace;
if (ws_a && !ws_b)
return -1;
else if (!ws_a && ws_b)
return 1;
vis_a = meta_window_showing_on_its_workspace (win_a);
vis_b = meta_window_showing_on_its_workspace (win_b);
if (vis_a && !vis_b)
return -1;
else if (!vis_a && vis_b)
return 1;
return meta_window_get_user_time (win_b) - meta_window_get_user_time (win_a);
}
/**
* shell_app_get_windows:
* @app:
*
* Get the toplevel, interesting windows which are associated with this
* application. The returned list will be sorted first by whether
* they're on the active workspace, then by whether they're visible,
* and finally by the time the user last interacted with them.
*
* Returns: (transfer none) (element-type MetaWindow): List of windows
*/
GSList *
shell_app_get_windows (ShellApp *app)
{
if (app->window_sort_stale)
{
CompareWindowsData data;
data.app = app;
data.active_workspace = meta_screen_get_active_workspace (shell_global_get_screen (shell_global_get ()));
app->windows = g_slist_sort_with_data (app->windows, shell_app_compare_windows, &data);
app->window_sort_stale = FALSE;
}
return app->windows;
}
guint
shell_app_get_n_windows (ShellApp *app)
{
return g_slist_length (app->windows);
}
static gboolean
shell_app_has_visible_windows (ShellApp *app)
{
GSList *iter;
for (iter = app->windows; iter; iter = iter->next)
{
MetaWindow *window = iter->data;
if (meta_window_showing_on_its_workspace (window))
return TRUE;
}
return FALSE;
}
gboolean
shell_app_is_on_workspace (ShellApp *app,
MetaWorkspace *workspace)
{
GSList *iter;
for (iter = app->windows; iter; iter = iter->next)
{
if (meta_window_get_workspace (iter->data) == workspace)
return TRUE;
}
return FALSE;
}
/**
* shell_app_compare:
* @app:
* @other: A #ShellApp
*
* Compare one #ShellApp instance to another, in the following way:
* - If one of them has visible windows and the other does not, the one
* with visible windows is first.
* - If one has no windows at all (i.e. it's not running) and the other
* does, the one with windows is first.
* - Finally, the application which the user interacted with most recently
* compares earlier.
*/
int
shell_app_compare (ShellApp *app,
ShellApp *other)
{
gboolean vis_app, vis_other;
GSList *windows_app, *windows_other;
vis_app = shell_app_has_visible_windows (app);
vis_other = shell_app_has_visible_windows (other);
if (vis_app && !vis_other)
return -1;
else if (!vis_app && vis_other)
return 1;
if (app->windows && !other->windows)
return -1;
else if (!app->windows && other->windows)
return 1;
windows_app = shell_app_get_windows (app);
windows_other = shell_app_get_windows (other);
return meta_window_get_user_time (windows_other->data) - meta_window_get_user_time (windows_app->data);
}
ShellApp *
_shell_app_new_for_window (MetaWindow *window)
{
ShellApp *app;
app = g_object_new (SHELL_TYPE_APP, NULL);
app->info = shell_app_system_create_from_window (shell_app_system_get_default (), window);
_shell_app_system_register_app (shell_app_system_get_default (), app);
_shell_app_add_window (app, window);
return app;
}
ShellApp *
_shell_app_new (ShellAppInfo *info)
{
ShellApp *app;
app = g_object_new (SHELL_TYPE_APP, NULL);
app->info = shell_app_info_ref (info);
_shell_app_system_register_app (shell_app_system_get_default (), app);
return app;
}
static void
shell_app_on_unmanaged (MetaWindow *window,
ShellApp *app)
{
_shell_app_remove_window (app, window);
}
static void
shell_app_on_user_time_changed (MetaWindow *window,
GParamSpec *pspec,
ShellApp *app)
{
/* Ideally we don't want to emit windows-changed if the sort order
* isn't actually changing. This check catches most of those.
*/
if (window != app->windows->data)
{
app->window_sort_stale = TRUE;
g_signal_emit (app, shell_app_signals[WINDOWS_CHANGED], 0);
}
}
static void
shell_app_on_ws_switch (MetaScreen *screen,
int from,
int to,
MetaMotionDirection direction,
gpointer data)
{
ShellApp *self = SHELL_APP (data);
self->window_sort_stale = TRUE;
g_signal_emit (self, shell_app_signals[WINDOWS_CHANGED], 0);
}
void
_shell_app_add_window (ShellApp *app,
MetaWindow *window)
{
if (g_slist_find (app->windows, window))
return;
app->windows = g_slist_prepend (app->windows, g_object_ref (window));
g_signal_connect (window, "unmanaged", G_CALLBACK(shell_app_on_unmanaged), app);
g_signal_connect (window, "notify::user-time", G_CALLBACK(shell_app_on_user_time_changed), app);
app->window_sort_stale = TRUE;
g_signal_emit (app, shell_app_signals[WINDOWS_CHANGED], 0);
if (app->workspace_switch_id == 0)
{
MetaScreen *screen = shell_global_get_screen (shell_global_get ());
app->workspace_switch_id =
g_signal_connect (screen, "workspace-switched", G_CALLBACK(shell_app_on_ws_switch), app);
}
}
static void
disconnect_workspace_switch (ShellApp *app)
{
MetaScreen *screen;
if (app->workspace_switch_id == 0)
return;
screen = shell_global_get_screen (shell_global_get ());
g_signal_handler_disconnect (screen, app->workspace_switch_id);
app->workspace_switch_id = 0;
}
void
_shell_app_remove_window (ShellApp *app,
MetaWindow *window)
{
if (!g_slist_find (app->windows, window))
return;
g_signal_handlers_disconnect_by_func (window, G_CALLBACK(shell_app_on_unmanaged), app);
g_signal_handlers_disconnect_by_func (window, G_CALLBACK(shell_app_on_user_time_changed), app);
g_object_unref (window);
app->windows = g_slist_remove (app->windows, window);
g_signal_emit (app, shell_app_signals[WINDOWS_CHANGED], 0);
if (app->windows == NULL)
disconnect_workspace_switch (app);
}
static void
shell_app_init (ShellApp *self)
{
}
static void
shell_app_dispose (GObject *object)
{
ShellApp *app = SHELL_APP (object);
if (app->info)
{
shell_app_info_unref (app->info);
app->info = NULL;
}
while (app->windows)
_shell_app_remove_window (app, app->windows->data);
disconnect_workspace_switch (app);
}
static void
shell_app_class_init(ShellAppClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->dispose = shell_app_dispose;
shell_app_signals[WINDOWS_CHANGED] = g_signal_new ("windows-changed",
SHELL_TYPE_APP,
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}

View File

@ -1,48 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef __SHELL_APP_H__
#define __SHELL_APP_H__
#include <clutter/clutter.h>
#include "window.h"
G_BEGIN_DECLS
typedef struct _ShellApp ShellApp;
typedef struct _ShellAppClass ShellAppClass;
typedef struct _ShellAppPrivate ShellAppPrivate;
#define SHELL_TYPE_APP (shell_app_get_type ())
#define SHELL_APP(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SHELL_TYPE_APP, ShellApp))
#define SHELL_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_APP, ShellAppClass))
#define SHELL_IS_APP(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SHELL_TYPE_APP))
#define SHELL_IS_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_APP))
#define SHELL_APP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_APP, ShellAppClass))
struct _ShellAppClass
{
GObjectClass parent_class;
};
GType shell_app_get_type (void) G_GNUC_CONST;
const char *shell_app_get_id (ShellApp *app);
ClutterActor *shell_app_create_icon_texture (ShellApp *app, float size);
char *shell_app_get_name (ShellApp *app);
char *shell_app_get_description (ShellApp *app);
gboolean shell_app_is_transient (ShellApp *app);
gboolean shell_app_launch (ShellApp *info, GError **error);
guint shell_app_get_n_windows (ShellApp *app);
GSList *shell_app_get_windows (ShellApp *app);
gboolean shell_app_is_on_workspace (ShellApp *app, MetaWorkspace *workspace);
int shell_app_compare (ShellApp *app, ShellApp *other);
G_END_DECLS
#endif /* __SHELL_APP_H__ */

View File

@ -1,7 +1,5 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#include "config.h"
#include "shell-arrow.h"
#include <clutter/clutter.h>

View File

@ -1,4 +1,3 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef __SHELL_ARROW_H__
#define __SHELL_ARROW_H__

317
src/shell-button-box.c Normal file
View File

@ -0,0 +1,317 @@
/* -*- 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, event);
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
* @event: Release event which triggered the activation
*
* 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, 1, CLUTTER_TYPE_EVENT);
/**
* 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);
}

35
src/shell-button-box.h Normal file
View File

@ -0,0 +1,35 @@
#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,367 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#include "config.h"
#include "shell-doc-system.h"
#include "shell-global.h"
#include "shell-texture-cache.h"
/**
* SECTION:shell-doc-system
* @short_description: Track recently used documents
*
* Wraps #GtkRecentManager, caching recently used document information, and adds
* APIs for asynchronous queries.
*/
enum {
CHANGED,
DELETED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
struct _ShellDocSystemPrivate {
GtkRecentManager *manager;
GHashTable *infos_by_uri;
GSList *infos_by_timestamp;
guint idle_recent_changed_id;
GHashTable *deleted_infos;
guint idle_emit_deleted_id;
};
G_DEFINE_TYPE(ShellDocSystem, shell_doc_system, G_TYPE_OBJECT);
/**
* shell_doc_system_get_all:
* @self: A #ShellDocSystem
*
* Returns the currently cached set of recent files. Recent files are read initially
* from the underlying #GtkRecentManager, and updated when it changes.
* This function does not perform I/O.
*
* Returns: (transfer none) (element-type GtkRecentInfo): Cached recent file infos
*/
GSList *
shell_doc_system_get_all (ShellDocSystem *self)
{
return self->priv->infos_by_timestamp;
}
/**
* @self: A #ShellDocSystem
* @uri: Url
*
* Returns: (transfer none): Recent file info corresponding to given @uri
*/
GtkRecentInfo *
shell_doc_system_lookup_by_uri (ShellDocSystem *self,
const char *uri)
{
return g_hash_table_lookup (self->priv->infos_by_uri, uri);
}
static gboolean
shell_doc_system_idle_emit_deleted (gpointer data)
{
ShellDocSystem *self = SHELL_DOC_SYSTEM (data);
GHashTableIter iter;
gpointer key, value;
self->priv->idle_emit_deleted_id = 0;
g_hash_table_iter_init (&iter, self->priv->deleted_infos);
while (g_hash_table_iter_next (&iter, &key, &value))
{
GtkRecentInfo *info = key;
g_signal_emit (self, signals[DELETED], 0, info);
}
g_signal_emit (self, signals[CHANGED], 0);
return FALSE;
}
typedef struct {
ShellDocSystem *self;
GtkRecentInfo *info;
} ShellDocSystemRecentQueryData;
static void
on_recent_file_query_result (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
ShellDocSystemRecentQueryData *data = user_data;
ShellDocSystem *self = data->self;
GError *error = NULL;
GFileInfo *fileinfo;
fileinfo = g_file_query_info_finish (G_FILE (source), result, &error);
if (fileinfo)
g_object_unref (fileinfo);
/* This is a strict error check; we don't want to cause recent files to
* vanish for anything potentially transient.
*/
if (error != NULL && error->domain == G_IO_ERROR && error->code == G_IO_ERROR_NOT_FOUND)
{
self->priv->infos_by_timestamp = g_slist_remove (self->priv->infos_by_timestamp, data->info);
g_hash_table_remove (self->priv->infos_by_uri, gtk_recent_info_get_uri (data->info));
g_hash_table_insert (self->priv->deleted_infos, gtk_recent_info_ref (data->info), NULL);
if (self->priv->idle_emit_deleted_id == 0)
self->priv->idle_emit_deleted_id = g_timeout_add (0, shell_doc_system_idle_emit_deleted, self);
}
g_clear_error (&error);
gtk_recent_info_unref (data->info);
g_free (data);
}
/**
* shell_doc_system_queue_existence_check:
* @self: A #ShellDocSystem
* @n_items: Count of items to check for existence, starting from most recent
*
* Asynchronously start a check of a number of recent file for existence;
* any deleted files will be emitted from the #ShellDocSystem::deleted
* signal. Note that this function ignores non-local files; they
* will simply always appear to exist (until they are removed from
* the recent file list manually).
*
* The intent of this function is to be called after a #ShellDocSystem::changed
* signal has been emitted, and a display has shown a subset of those files.
*/
void
shell_doc_system_queue_existence_check (ShellDocSystem *self,
guint n_items)
{
GSList *iter;
guint i;
for (i = 0, iter = self->priv->infos_by_timestamp; i < n_items && iter; i++, iter = iter->next)
{
GtkRecentInfo *info = iter->data;
const char *uri;
GFile *file;
ShellDocSystemRecentQueryData *data;
if (!gtk_recent_info_is_local (info))
continue;
data = g_new0 (ShellDocSystemRecentQueryData, 1);
data->self = self;
data->info = gtk_recent_info_ref (info);
uri = gtk_recent_info_get_uri (info);
file = g_file_new_for_uri (uri);
g_file_query_info_async (file, "standard::type", G_FILE_QUERY_INFO_NONE,
G_PRIORITY_DEFAULT, NULL, on_recent_file_query_result, data);
g_object_unref (file);
}
}
static int
sort_infos_by_timestamp_descending (gconstpointer a,
gconstpointer b)
{
GtkRecentInfo *info_a = (GtkRecentInfo*)a;
GtkRecentInfo *info_b = (GtkRecentInfo*)b;
time_t modified_a, modified_b;
modified_a = gtk_recent_info_get_modified (info_a);
modified_b = gtk_recent_info_get_modified (info_b);
return modified_b - modified_a;
}
static gboolean
idle_handle_recent_changed (gpointer data)
{
ShellDocSystem *self = SHELL_DOC_SYSTEM (data);
GList *items, *iter;
self->priv->idle_recent_changed_id = 0;
g_hash_table_remove_all (self->priv->deleted_infos);
g_hash_table_remove_all (self->priv->infos_by_uri);
g_slist_free (self->priv->infos_by_timestamp);
self->priv->infos_by_timestamp = NULL;
items = gtk_recent_manager_get_items (self->priv->manager);
for (iter = items; iter; iter = iter->next)
{
GtkRecentInfo *info = iter->data;
const char *uri = gtk_recent_info_get_uri (info);
/* uri is owned by the info */
g_hash_table_insert (self->priv->infos_by_uri, (char*) uri, info);
self->priv->infos_by_timestamp = g_slist_prepend (self->priv->infos_by_timestamp, info);
}
g_list_free (items);
self->priv->infos_by_timestamp = g_slist_sort (self->priv->infos_by_timestamp, sort_infos_by_timestamp_descending);
g_signal_emit (self, signals[CHANGED], 0);
return FALSE;
}
static void
shell_doc_system_on_recent_changed (GtkRecentManager *manager,
ShellDocSystem *self)
{
if (self->priv->idle_recent_changed_id != 0)
return;
self->priv->idle_recent_changed_id = g_timeout_add (0, idle_handle_recent_changed, self);
}
/**
* shell_doc_system_open:
* @system: A #ShellDocSystem
* @info: A #GtkRecentInfo
*
* Launch the default application associated with the mime type of
* @info, using its uri.
*/
void
shell_doc_system_open (ShellDocSystem *system,
GtkRecentInfo *info)
{
GFile *file;
GAppInfo *app_info;
gboolean needs_uri;
file = g_file_new_for_uri (gtk_recent_info_get_uri (info));
needs_uri = g_file_get_path (file) == NULL;
g_object_unref (file);
app_info = g_app_info_get_default_for_type (gtk_recent_info_get_mime_type (info), needs_uri);
if (app_info != NULL)
{
GList *uris;
uris = g_list_prepend (NULL, (gpointer)gtk_recent_info_get_uri (info));
g_app_info_launch_uris (app_info, uris, shell_global_create_app_launch_context (shell_global_get ()), NULL);
g_list_free (uris);
}
else
{
char *app_name;
#if GTK_MINOR_VERSION >= 18
const char *app_exec;
#else
char *app_exec;
#endif
char *app_exec_quoted;
guint count;
time_t time;
app_name = gtk_recent_info_last_application (info);
if (gtk_recent_info_get_application_info (info, app_name, &app_exec, &count, &time))
{
GRegex *regex;
GAppLaunchContext *context;
/* TODO: Change this once better support for creating
GAppInfo is added to GtkRecentInfo, as right now
this relies on the fact that the file uri is
already a part of appExec, so we don't supply any
files to app_info.launch().
The 'command line' passed to
create_from_command_line is allowed to contain
'%<something>' macros that are expanded to file
name / icon name, etc, so we need to escape % as %%
*/
regex = g_regex_new ("%", 0, 0, NULL);
app_exec_quoted = g_regex_replace (regex, app_exec, -1, 0, "%%", 0, NULL);
g_regex_unref (regex);
app_info = g_app_info_create_from_commandline (app_exec, NULL, 0, NULL);
/* The point of passing an app launch context to
launch() is mostly to get startup notification and
associated benefits like the app appearing on the
right desktop; but it doesn't really work for now
because with the way we create the appInfo we
aren't reading the application's desktop file, and
thus don't find the StartupNotify=true in it. So,
despite passing the app launch context, no startup
notification occurs.
*/
context = shell_global_create_app_launch_context (shell_global_get ());
g_app_info_launch (app_info, NULL, context, NULL);
g_object_unref (context);
}
g_free (app_name);
}
}
static void
shell_doc_system_class_init(ShellDocSystemClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *)klass;
signals[CHANGED] =
g_signal_new ("changed",
SHELL_TYPE_DOC_SYSTEM,
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[DELETED] =
g_signal_new ("deleted",
SHELL_TYPE_DOC_SYSTEM,
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
g_cclosure_marshal_VOID__BOXED,
G_TYPE_NONE, 1, GTK_TYPE_RECENT_INFO);
g_type_class_add_private (gobject_class, sizeof (ShellDocSystemPrivate));
}
static void
shell_doc_system_init (ShellDocSystem *self)
{
ShellDocSystemPrivate *priv;
self->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
SHELL_TYPE_DOC_SYSTEM,
ShellDocSystemPrivate);
self->priv->manager = gtk_recent_manager_get_default ();
self->priv->deleted_infos = g_hash_table_new_full (NULL, NULL, (GDestroyNotify)gtk_recent_info_unref, NULL);
self->priv->infos_by_uri = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)gtk_recent_info_unref);
g_signal_connect (self->priv->manager, "changed", G_CALLBACK(shell_doc_system_on_recent_changed), self);
shell_doc_system_on_recent_changed (self->priv->manager, self);
}
/**
* shell_doc_system_get_default:
*
* Return Value: (transfer none): The global #ShellDocSystem singleton
*/
ShellDocSystem *
shell_doc_system_get_default ()
{
static ShellDocSystem *instance = NULL;
if (instance == NULL)
instance = g_object_new (SHELL_TYPE_DOC_SYSTEM, NULL);
return instance;
}

View File

@ -1,46 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef __SHELL_DOC_SYSTEM_H__
#define __SHELL_DOC_SYSTEM_H__
#include <gio/gio.h>
#include <gtk/gtk.h>
#define SHELL_TYPE_DOC_SYSTEM (shell_doc_system_get_type ())
#define SHELL_DOC_SYSTEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_DOC_SYSTEM, ShellDocSystem))
#define SHELL_DOC_SYSTEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_DOC_SYSTEM, ShellDocSystemClass))
#define SHELL_IS_DOC_SYSTEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_DOC_SYSTEM))
#define SHELL_IS_DOC_SYSTEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_DOC_SYSTEM))
#define SHELL_DOC_SYSTEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_DOC_SYSTEM, ShellDocSystemClass))
typedef struct _ShellDocSystem ShellDocSystem;
typedef struct _ShellDocSystemClass ShellDocSystemClass;
typedef struct _ShellDocSystemPrivate ShellDocSystemPrivate;
struct _ShellDocSystem
{
GObject parent;
ShellDocSystemPrivate *priv;
};
struct _ShellDocSystemClass
{
GObjectClass parent_class;
};
GType shell_doc_system_get_type (void) G_GNUC_CONST;
ShellDocSystem* shell_doc_system_get_default (void);
GSList *shell_doc_system_get_all (ShellDocSystem *system);
GtkRecentInfo *shell_doc_system_lookup_by_uri (ShellDocSystem *system,
const char *uri);
void shell_doc_system_queue_existence_check (ShellDocSystem *system,
guint n_recent);
void shell_doc_system_open (ShellDocSystem *system,
GtkRecentInfo *info);
#endif /* __SHELL_DOC_SYSTEM_H__ */

102
src/shell-drawing-area.c Normal file
View File

@ -0,0 +1,102 @@
/* -*- 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;
}

37
src/shell-drawing-area.h Normal file
View File

@ -0,0 +1,37 @@
#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,10 +1,104 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#include "config.h"
#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,
@ -53,14 +147,17 @@ shell_draw_clock (ClutterCairoTexture *texture,
}
void
shell_draw_box_pointer (ClutterCairoTexture *texture,
ShellPointerDirection direction,
ClutterColor *border_color,
ClutterColor *background_color)
shell_draw_box_pointer (ClutterCairoTexture *texture,
ClutterGravity pointing_towards,
ClutterColor *border_color,
ClutterColor *background_color)
{
guint width, height;
cairo_t *cr;
g_return_if_fail (pointing_towards == CLUTTER_GRAVITY_NORTH ||
pointing_towards == CLUTTER_GRAVITY_WEST);
clutter_cairo_texture_get_surface_size (texture, &width, &height);
clutter_cairo_texture_clear (texture);
@ -70,34 +167,17 @@ shell_draw_box_pointer (ClutterCairoTexture *texture,
clutter_cairo_set_source_color (cr, border_color);
switch (direction)
if (pointing_towards == CLUTTER_GRAVITY_WEST)
{
cairo_move_to (cr, width, 0);
cairo_line_to (cr, 0, floor (height * 0.5));
cairo_line_to (cr, width, height);
}
else /* CLUTTER_GRAVITY_NORTH */
{
case SHELL_POINTER_UP:
cairo_move_to (cr, 0, height);
cairo_line_to (cr, floor (width * 0.5), 0);
cairo_line_to (cr, width, height);
break;
case SHELL_POINTER_DOWN:
cairo_move_to (cr, width, 0);
cairo_line_to (cr, floor (width * 0.5), height);
cairo_line_to (cr, 0, 0);
break;
case SHELL_POINTER_LEFT:
cairo_move_to (cr, width, height);
cairo_line_to (cr, 0, floor (height * 0.5));
cairo_line_to (cr, width, 0);
break;
case SHELL_POINTER_RIGHT:
cairo_move_to (cr, 0, 0);
cairo_line_to (cr, width, floor (height * 0.5));
cairo_line_to (cr, 0, height);
break;
default:
g_assert_not_reached();
}
cairo_stroke_preserve (cr);

View File

@ -7,17 +7,16 @@
G_BEGIN_DECLS
typedef enum {
SHELL_POINTER_UP,
SHELL_POINTER_DOWN,
SHELL_POINTER_LEFT,
SHELL_POINTER_RIGHT
} ShellPointerDirection;
ClutterCairoTexture *shell_create_vertical_gradient (ClutterColor *top,
ClutterColor *bottom);
void shell_draw_box_pointer (ClutterCairoTexture *texture,
ShellPointerDirection direction,
ClutterColor *border_color,
ClutterColor *background_color);
ClutterCairoTexture *shell_create_horizontal_gradient (ClutterColor *left,
ClutterColor *right);
void shell_draw_box_pointer (ClutterCairoTexture *texture,
ClutterGravity pointing_towards,
ClutterColor *border_color,
ClutterColor *background_color);
void shell_draw_clock (ClutterCairoTexture *texture,
int hour,

View File

@ -1,4 +1,3 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef __SHELL_EMBEDDED_WINDOW_PRIVATE_H__
#define __SHELL_EMBEDDED_WINDOW_PRIVATE_H__

View File

@ -1,7 +1,5 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#include "config.h"
#include <gdk/gdkx.h>
#include <clutter/x11/clutter-x11.h>

View File

@ -1,4 +1,3 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef __SHELL_EMBEDDED_WINDOW_H__
#define __SHELL_EMBEDDED_WINDOW_H__

View File

@ -1,7 +1,5 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#include "config.h"
#include "shell-gconf.h"
#include <gconf/gconf-client.h>
@ -357,7 +355,7 @@ 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) (element-type gboolean): value to set @key to
* @value: (transfer none): value to set @key to
* @error: a #GError, which will be set on error
*
* Sets the value of @key to @value.
@ -379,7 +377,7 @@ 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) (element-type float): value to set @key to
* @value: (transfer none): value to set @key to
* @error: a #GError, which will be set on error
*
* Sets the value of @key to @value.
@ -390,7 +388,7 @@ 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) (element-type utf8): value to set @key to
* @value: (transfer none): value to set @key to
* @error: a #GError, which will be set on error
*
* Sets the value of @key to @value.

View File

@ -94,8 +94,6 @@ function runTestFixedBox() {
}
*/
#include "config.h"
#include "shell-generic-container.h"
#include <clutter/clutter.h>
@ -105,7 +103,7 @@ function runTestFixedBox() {
G_DEFINE_TYPE(ShellGenericContainer, shell_generic_container, CLUTTER_TYPE_GROUP);
struct _ShellGenericContainerPrivate {
GHashTable *skip_paint;
gpointer dummy;
};
/* Signals */
@ -182,119 +180,15 @@ shell_generic_container_get_preferred_height (ClutterActor *actor,
shell_generic_container_allocation_unref (alloc);
}
static void
shell_generic_container_paint (ClutterActor *actor)
{
ShellGenericContainer *self = (ShellGenericContainer*) actor;
GList *iter, *children;
children = clutter_container_get_children ((ClutterContainer*) actor);
for (iter = children; iter; iter = iter->next)
{
ClutterActor *child = iter->data;
if (g_hash_table_lookup (self->priv->skip_paint, child))
continue;
clutter_actor_paint (child);
}
g_list_free (children);
}
static void
shell_generic_container_pick (ClutterActor *actor,
const ClutterColor *color)
{
ShellGenericContainer *self = (ShellGenericContainer*) actor;
GList *iter, *children;
(CLUTTER_ACTOR_CLASS (g_type_class_peek (clutter_actor_get_type ())))->pick (actor, color);
children = clutter_container_get_children ((ClutterContainer*) actor);
for (iter = children; iter; iter = iter->next)
{
ClutterActor *child = iter->data;
if (g_hash_table_lookup (self->priv->skip_paint, child))
continue;
clutter_actor_paint (child);
}
g_list_free (children);
}
static void
on_skip_paint_weakref (gpointer user_data,
GObject *location)
{
ShellGenericContainer *self = SHELL_GENERIC_CONTAINER (user_data);
g_hash_table_remove (self->priv->skip_paint, location);
}
/**
* shell_generic_container_set_skip_paint:
* @container: A #ShellGenericContainer
* @child: Child #ClutterActor
* @skip %TRUE if we should skip painting
*
* Set whether or not we should skip painting @actor. Workaround for
* lack of gjs ability to override _paint vfunc.
*/
void
shell_generic_container_set_skip_paint (ShellGenericContainer *self,
ClutterActor *child,
gboolean skip)
{
gboolean currently_skipping;
currently_skipping = g_hash_table_lookup (self->priv->skip_paint, child) != NULL;
if (!!skip == currently_skipping)
return;
if (!skip)
{
g_object_weak_unref ((GObject*) child, on_skip_paint_weakref, self);
g_hash_table_remove (self->priv->skip_paint, child);
}
else
{
g_object_weak_ref ((GObject*) child, on_skip_paint_weakref, self);
g_hash_table_insert (self->priv->skip_paint, child, child);
}
}
static void
shell_generic_container_dispose (GObject *object)
{
ShellGenericContainer *self = (ShellGenericContainer*) object;
if (self->priv->skip_paint != NULL)
{
g_hash_table_destroy (self->priv->skip_paint);
self->priv->skip_paint = NULL;
}
G_OBJECT_CLASS (shell_generic_container_parent_class)->dispose (object);
}
static void
shell_generic_container_class_init (ShellGenericContainerClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
gobject_class->dispose = shell_generic_container_dispose;
actor_class->get_preferred_width = shell_generic_container_get_preferred_width;
actor_class->get_preferred_height = shell_generic_container_get_preferred_height;
actor_class->allocate = shell_generic_container_allocate;
actor_class->paint = shell_generic_container_paint;
actor_class->pick = shell_generic_container_pick;
shell_generic_container_signals[GET_PREFERRED_WIDTH] =
g_signal_new ("get-preferred-width",
@ -331,7 +225,6 @@ shell_generic_container_init (ShellGenericContainer *area)
{
area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area, SHELL_TYPE_GENERIC_CONTAINER,
ShellGenericContainerPrivate);
area->priv->skip_paint = g_hash_table_new_full (NULL, NULL, (GDestroyNotify)g_object_unref, NULL);
}
GType shell_generic_container_allocation_get_type (void)

View File

@ -1,4 +1,3 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef __SHELL_GENERIC_CONTAINER_H__
#define __SHELL_GENERIC_CONTAINER_H__
@ -42,8 +41,4 @@ struct _ShellGenericContainerClass
GType shell_generic_container_get_type (void) G_GNUC_CONST;
void shell_generic_container_set_skip_paint (ShellGenericContainer *container,
ClutterActor *actor,
gboolean skip);
#endif /* __SHELL_GENERIC_CONTAINER_H__ */

View File

@ -1,14 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef __SHELL_GLOBAL_PRIVATE_H__
#define __SHELL_GLOBAL_PRIVATE_H__
#include "shell-global.h"
#include <gjs/gjs.h>
void _shell_global_set_plugin (ShellGlobal *global,
MutterPlugin *plugin);
void _shell_global_set_gjs_context (ShellGlobal *global,
GjsContext *context);
#endif /* __SHELL_GLOBAL_PRIVATE_H__ */

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