Compare commits

...

104 Commits
3.1.3 ... 3.1.4

Author SHA1 Message Date
60612cace9 Add markup.js to TEST_JS 2011-07-27 19:13:03 -04:00
f057502834 Require GJS 1.29.15 2011-07-27 19:13:03 -04:00
21a1149532 Always include gnome-shell-jhbuild.in in EXTRA_DIST 2011-07-27 19:13:03 -04:00
6724ca4f63 Bump version to 3.1.4
Update NEWS
2011-07-27 19:13:03 -04:00
7a6c25b3fb chrome: Ignore minimized windows when updating visibility
The current check for fullscreen windows ignores the window's
minimization state, so that chrome which is hidden in fullscreen
will always hide if the window on top of the window stack is
fullscreen, even if it is actually minimized.
Instead, skip minimized windows when looking for fullscreen windows.

https://bugzilla.gnome.org/show_bug.cgi?id=655446
2011-07-27 23:40:49 +02:00
0366e320af NotificationDaemon: only remove the source if notification sender is removed from DBus and the application is set
This ensures that we don't remove "notify-send" sources, senders of which are
removed from DBus immediately.
2011-07-27 17:16:05 -04:00
f03793b825 tests: add a test for MessageTray._fixMarkup
https://bugzilla.gnome.org/show_bug.cgi?id=650298
2011-07-27 09:29:03 -04:00
0907f030b4 tests: fix up typelib include paths
The js modules have so many imports back and forth that it's pretty
much guaranteed that if you import even one of them, you'll end up
importing all of them, including ui.status.bluetooth and
ui.status.network. So fix up the typelib include paths the same way
gnome-shell-jhbuild does, so we can find everything.

https://bugzilla.gnome.org/show_bug.cgi?id=650298
2011-07-27 09:29:03 -04:00
7542e68b6f messageTray: improve bad-markup handling
_fixMarkup() was supposed to be ensuring that the markup we passed to
clutter was correct, but it was validating the syntax incorrectly, and
wasn't checking that the markup was valid (or even well-formed). This
is bad because if you pass bad pango markup to
clutter_text_set_markup(), it will g_warn and drop the string on the
floor.

Fix by fixing up the regexps, and then calling Pango.parse_markup() on
the result, and just xml-escaping everything if parse_markup() fails.

https://bugzilla.gnome.org/show_bug.cgi?id=650298
2011-07-27 09:29:03 -04:00
9003a34285 Updated Slovak translation 2011-07-26 15:12:15 +02:00
3cc27eaabe [l10n] Updated Italian translation 2011-07-23 20:14:16 +02:00
601b081bf3 Updated Russian translation 2011-07-23 21:34:36 +04:00
4405d993c9 main: add logStackTrace(), for debugging
Add Main.logStackTrace(), to print a (javascript) stack trace, which
is often useful when debugging.

https://bugzilla.gnome.org/show_bug.cgi?id=654987
2011-07-20 15:45:58 -04:00
361308eb1b Revert "wip: cross fade the window_group and window clones going in/out of the overview"
This reverts commit 1625591598.
2011-07-20 00:45:53 +01:00
d894dedaf6 Revert "volume.js: make slider menu items activatable"
This reverts commit a50c30a4fd.
2011-07-20 00:45:29 +01:00
d12dd1491f runDialog: catch exceptions from Gio.app_info_launch_default_for_uri()
An uncaught exception here would mean that the dialog wouldn't close.

https://bugzilla.gnome.org/show_bug.cgi?id=653700
2011-07-20 00:25:11 +01:00
1625591598 wip: cross fade the window_group and window clones going in/out of the overview
https://bugzilla.gnome.org/show_bug.cgi?id=653868
2011-07-20 00:25:11 +01:00
a50c30a4fd volume.js: make slider menu items activatable
Keeping the volume menu open after setting the desired volume isn't that
useful and forces a second click (or an Esc press) to dismiss it. Allow for
the sliders to be used with a single click-hold-move-release.

https://bugzilla.gnome.org/show_bug.cgi?id=649586
2011-07-20 00:25:11 +01:00
02bfc74c1e Updated Slovenian translation 2011-07-19 22:27:47 +02:00
a012dd838d Updated Norwegian bokmål translation 2011-07-18 13:50:22 +02:00
e2b634e59a Punjabi Translation is updated 2011-07-18 08:29:25 +05:30
80b98d8787 telepathyClient: Code cleanup
Move a function to where it's used and rename "_createSource"
to reflect that it creates *chat* sources.

https://bugzilla.gnome.org/show_bug.cgi?id=654791
2011-07-17 15:26:52 -04:00
bf2ba83cd5 telepathyClient: Fix undefined variable
https://bugzilla.gnome.org/show_bug.cgi?id=654791
2011-07-17 15:26:52 -04:00
727c43dbab Updated Spanish translation 2011-07-17 12:14:53 +02:00
203dedfb3a viewSelector: Bind <Ctrl>+<Return> for open new window
https://bugzilla.gnome.org/show_bug.cgi?id=613082
2011-07-17 00:50:26 +04:00
8d5d4159a3 Updated Hebrew translation. 2011-07-16 17:27:41 +03:00
cecba0269f Approve audio/video channels
Support the old (StreamedMedia) and new (Call) API for now as the latter is
still used.

https://bugzilla.gnome.org/show_bug.cgi?id=653939
2011-07-15 17:12:41 +01:00
3c878793b5 Updated Hebrew translation. 2011-07-15 10:55:09 +03:00
0549f42030 panel, layout: clean up HotCorner handling
Move the HotCorner class from panel to layout, and make the panel
manage its own HotCorner.

Stick the panel's HotCorner into the Activities button actor (rather
than separately floating above it), so that hover tracking on the
button works properly without needing hacks in HotCorner.

https://bugzilla.gnome.org/show_bug.cgi?id=645759
2011-07-14 15:31:25 -04:00
50f248ec5b panel: make ActivitiesButton a PanelMenu.Button
The fact that everything in the top bar except the activities button
was a menu made various things difficult. Simplify this by making the
activities button be a menu too, but just hack it up a bit so that the
menu associated with the button never actually appears.

Fixes https://bugzilla.gnome.org/show_bug.cgi?id=645759 (Clicking on
Activities with menu up leaves a funny state) and its semi-dup 641253
(panel keynav between Activities and menus is quirky).
2011-07-14 15:31:25 -04:00
544bdb4fb1 panel: abstract ActivitiesButton into its own class
https://bugzilla.gnome.org/show_bug.cgi?id=645759
2011-07-14 15:31:25 -04:00
88b295ff9f [l10n] Updated German translation 2011-07-14 20:27:45 +02:00
602326bb57 main: filter out "tp-glib/proxy-DEBUG" messages 2011-07-14 14:14:18 -04:00
31857cf05f [l10n] Updated German translation 2011-07-14 19:59:20 +02:00
896d8e830c chrome: don't show visibleInOverview chrome when the screensaver is active
visibleInOverview chrome was visible even when the screensaver was
active. Although we may eventually need visibleInScreenSaver, that
should be a separate flag.

Fix this by tracking the screensaver active state, and hiding the chrome
when the screensaver is active.

https://bugzilla.gnome.org/show_bug.cgi?id=654550
2011-07-14 12:51:18 -04:00
5f86e29830 screenSaver: bugfixes
Fix the signal handling; you can't use this.connect('ActiveChanged')
to connect to a D-Bus signal after replacing the signal methods with
the lang.signals versions. Just leave it using the D-Bus signal names,
just like it uses the D-Bus method names.

Also, remove the "_" from "_screenSaverActive", to match what
AutomountManager checks for, and remove getActive(), since it's not
needed.

https://bugzilla.gnome.org/show_bug.cgi?id=654550
2011-07-14 12:51:18 -04:00
579ae59eca Updated Slovenian translation 2011-07-14 14:36:41 +02:00
90db743cc9 Turn RoomInviteSource to a more generic source
All the approving source will be basically the same.

https://bugzilla.gnome.org/show_bug.cgi?id=653939
2011-07-14 09:47:36 +02:00
fe82897064 statusMenu: Fix typo in user switching commit 2011-07-13 17:43:23 -04:00
0f49f36519 Use CLUTTER_CAIRO_FORMAT_ARGB32: rather then doing a byte order check
Clutter 1.7.x introduced CLUTTER_CAIRO_FORMAT_ARGB32: which can be used when
sharing textures/data with cairo without having to do check the
byte order and choose the appropriate format by hand.

https://bugzilla.gnome.org/show_bug.cgi?id=654577
2011-07-13 23:10:38 +02:00
a92b7342ba message-tray: Move check for _actorDestroyed into _setCount()
As _updateCount has been designed to be overwritable by subclasses,
move the check for _actorDestroyed into _setCount(), to fix the
problem described in commit 5f6ac33d5 in derived types as well.
2011-07-13 22:27:15 +02:00
86818e9c52 autorun-manager: Fix 'destroy' handler of resident source
If the resident source is destroyed, it should be recreated
immediately, so that it is available when another volume is
mounted. However, we only connect to the 'destroy' signal
on the original source, not on newly created ones. As a result,
the resident source only works twice, after that it shows up
without icon and an empty notification.

Fix by always connecting to the source's 'destroy' signal.
2011-07-13 21:43:30 +02:00
7a8a00c705 extension-tool: Clean up code creator, update sample
Update the sample to be more up to date with respect to Shell practices,
and make it look a bit prettier. Additionally, change the file extract
code so that it's easier to update and add new files later.

https://bugzilla.gnome.org/show_bug.cgi?id=653206
2011-07-13 15:02:49 -04:00
c8d5e0a51c extension-tool: Use socket.gethostname() instead of an external process
Minor code cleanup.

https://bugzilla.gnome.org/show_bug.cgi?id=653206
2011-07-13 15:02:46 -04:00
524a7ff6fb trivial: update .gitignore 2011-07-13 14:59:45 -04:00
5f6ac33d59 message-tray: Don't update message count after destruction
When trying to update the message count after a summary icon has
been destroyed, the label to display the count is no longer valid
and trying to set its text results in some Clutter warnings.
2011-07-13 20:48:44 +02:00
b4f5e4206d automount: handle the drive-eject-button signal
The implementation is untested, as the signal is not emitted from the
Gdu GVfs volume monitor yet.

https://bugzilla.gnome.org/show_bug.cgi?id=653520
2011-07-13 14:39:36 -04:00
5133c3e010 automount: emit sounds when a drive is connected/disconnected
https://bugzilla.gnome.org/show_bug.cgi?id=653520
2011-07-13 14:39:36 -04:00
0b77ea422a mount-operation: implement ask-password for mounting encrypted volumes
https://bugzilla.gnome.org/show_bug.cgi?id=653520
2011-07-13 14:39:36 -04:00
5c1dd4ea18 autorun: prefer Safe Removal over eject/unmount if possible
Basically do what NautilusPlacesSidebar does with the drive/volume/mount
eject/unmount/stop priorities.

We follow this pattern:
- always prefer Safely Remove if available (i.e. drive.stop())
- fallback to ejecting the mount/volume/drive if that's not possible
- finally, fallback to unmounting the mount if even eject is not
  available

This also means we don't care about the distinction between
Stop/Eject/Unmount at this level. Disk Utility (or Nautilus) are
available for those who want that degree of control, but the common case
here should do the most useful action without presenting the choice.

https://bugzilla.gnome.org/show_bug.cgi?id=653520
2011-07-13 14:39:36 -04:00
e1c687184e mount-operation: implement ask-question
Use a system modal dialogs for mount operation questions.

https://bugzilla.gnome.org/show_bug.cgi?id=653520
2011-07-13 14:39:36 -04:00
297d1356a2 mount-operation: add a ShellMountOperation implementation
Ideally, this would be an entirely-JS implementation, but we have a
couple of issues with gjs and gobject-introspection to work around, so
we need a ShellMountOperation class for the time being.

This first commit implements the show-processes dialog, with a system
modal style very similar to the EndSession dialog.
Implementations of ask-question and ask-password will follow shortly.

https://bugzilla.gnome.org/show_bug.cgi?id=653520
2011-07-13 14:39:36 -04:00
98327b0c13 automount: only autorun volumes marked as allowed
Port code from g-s-d to mark volumes as allowed for autorun, and check
for it when running the notification.

https://bugzilla.gnome.org/show_bug.cgi?id=653520
2011-07-13 14:39:35 -04:00
6786aee5ed autorun: integrate with the shell sniffer process
If possible, use the results from the sniffer process in order to have
less-generic alternatives to the file manager in the proposed autorun
choices.

https://bugzilla.gnome.org/show_bug.cgi?id=653520
2011-07-13 14:39:35 -04:00
0b9c726b4e sniffer: add a mimetype sniffer helper executable
The sniffer is a simple helper process, activated as a DBus service,
that tries to crawl as many files as possible in the provided target
directory (i.e. the new mount's root), for a maximum amount of time -
which is set here to 1.5 seconds (i.e. it will crawl either all the
files in the directory tree, or as many as it can before the specified
timeout expires).

Crawled files are ordered by their content type, and a generic estimation
of the type of files composing the directory is returned to the caller,
using generic 'x-content/*' mimetypes.

The process will then set an autoquit timeout on itself, which can be
disabled by setting the env variable HOTPLUG_SNIFFER_PERSIST for
debugging purposes. The HOTPLUG_SNIFFER_DEBUG env variable can also be
set to enable debugging output.

https://bugzilla.gnome.org/show_bug.cgi?id=653520
2011-07-13 14:39:35 -04:00
436dd6ee8c autorun: follow per content-type gsettings preferences for autorun
Autorun preferences can be fine-tweaked at the content-type level from
the System Settings 'Removable Media' panel.
Use those settings to figure out the default action for newly-mounted
mounts.

https://bugzilla.gnome.org/show_bug.cgi?id=653520
2011-07-13 14:39:35 -04:00
acc053302d automount: add an AutomountManager class
The AutomountManager class is the low-level counterpart of the
previously introduced AutorunManager, and takes care of extracting the
list of valid mounts from a GVolume or a GDrive and mounting them,
provided a number of conditions and requirements are met.

AutomountManager also keeps track of the current session availability
(using the ConsoleKit and gnome-screensaver DBus interfaces) and
inhibits mounting if the current session is locked, or another session
is in use instead.

https://bugzilla.gnome.org/show_bug.cgi?id=653520
2011-07-13 14:39:35 -04:00
534b371d42 autorun: add an AutorunManager class
AutorunManager is a class that takes care of displaying and managing
notifications and UI for storage devices.

When a mount appears and a number of conditions are satisified, a
transient notification will be displayed to immediately interact with
the device. AutorunTransientDispatcher is the object that takes care of
showing/hiding the notification sources as devices appear/disappear.

Likewise, current mounts are kept in a list and presented within a
list in a resident notification, handled by AutorunResidentSource.

https://bugzilla.gnome.org/show_bug.cgi?id=653520
2011-07-13 14:39:35 -04:00
6004e3d2e1 screensaver: factor out a ScreenSaverProxy helper class
This class will be shared between StatusMenu and the upcoming
AutomountManager classes.

https://bugzilla.gnome.org/show_bug.cgi?id=653520
2011-07-13 14:39:35 -04:00
c632074ba7 statusMenu.js: Ensure screensaver is locked before switching users
Somewhat similar to (see bug 637540), we need to lock the screensaver
*before* asking GDM to switch.

https://bugzilla.gnome.org/show_bug.cgi?id=654565
2011-07-13 14:32:26 -04:00
664245d2a6 build: Remove gnome-power-manager
The DBus interface for the battery status is now provided by the
power plugin of gnome-settings-daemon, so there's real reason to
keep building gnome-power-manager.

https://bugzilla.gnome.org/show_bug.cgi?id=654300
2011-07-13 19:45:29 +02:00
fda4fc674d power: Remove _checkError()
On error, we tried to kill and respawn gnome-power-manager, but
as of commit c5676900 the DBus interface provided by g-s-d's power
plugin is used, so the respawning does not have any effect.

https://bugzilla.gnome.org/show_bug.cgi?id=654300
2011-07-13 19:45:29 +02:00
bf28c24c82 build: Remove upower from the moduleset
The upower version in the moduleset is too old for the power plugin
of gnome-settings-daemon. As at least Fedora and Ubuntu have a
recent enough version in their repositories, use that instead of
bumping the version in the moduleset.

https://bugzilla.gnome.org/show_bug.cgi?id=654300
2011-07-13 19:45:29 +02:00
32df0e80ca main: Fix small memory leak in main.c
==17386== 1,669 (88 direct, 1,581 indirect) bytes in 1 blocks are definitely lost in loss record 4,090 of 4,151
==17386==    at 0x4C24AF4: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==17386==    by 0x691B099: g_malloc0 (in /usr/lib/libglib-2.0.so.0.2800.8)
==17386==    by 0x692006A: g_option_context_new (in /usr/lib/libglib-2.0.so.0.2800.8)
==17386==    by 0x5124C57: meta_get_option_context (in /usr/lib/libmutter.so.0.0.0)
==17386==    by 0x401D4F: main (in /usr/bin/gnome-shell)

https://bugzilla.gnome.org/show_bug.cgi?id=654269
2011-07-13 13:36:56 -04:00
297eab738f gnome-shell-jhbuild.in: Fix restore_gnome.
The gconf keys used to restore GNOME aren't in a proper GNOME3 environment.
To mimic what GNOME3 gnome-session does would be extremely complicated, so
just launch the system gnome-shell.

https://bugzilla.gnome.org/show_bug.cgi?id=654527
2011-07-13 13:35:16 -04:00
5819dd3a5a NetworkMenu: take out an item from More... when another is destroyed
When one of the networks in the main menu is removed and we have
a More... submenu, we can take the first out from the submenu and
show it in the main menu.

https://bugzilla.gnome.org/show_bug.cgi?id=647175
2011-07-13 01:33:47 +02:00
a007b1bb2d st-entry: Inherit all colors
https://bugzilla.gnome.org/show_bug.cgi?id=643768
2011-07-12 15:30:59 -04:00
5cb43b6bae theme: Add selected text color
This should improve readability of text that's selected.

https://bugzilla.gnome.org/show_bug.cgi?id=643768
2011-07-12 15:30:59 -04:00
f524138a64 st-entry: Support a different color for selected text
This should improve readability in text entries where the text
color is very close to the color of the selection.

https://bugzilla.gnome.org/show_bug.cgi?id=643768
2011-07-12 15:30:59 -04:00
0aa626b2fb Updated Norwegian bokmål translation 2011-07-12 20:00:31 +02:00
3ae1050f67 Updated Latvian translation. 2011-07-12 18:46:45 +03:00
67736642f3 Fix methods that take array arguments
Length parameter is no longer required in current GJS. Fix that.

https://bugzilla.gnome.org/show_bug.cgi?id=654349
2011-07-11 20:13:35 +02:00
09f3c87d20 Allow other clients to preempt the channels we are handling
This is needed if we are handling an incoming text channel and then user tries
to open a chat with the same contact using Empathy. In this case, the Shell
should delegate the channel back to Empathy and just continue observing it as
it does for usual outgoing channels.

Depends on telepathy-glib 0.15.3 as
tp_base_client_set_delegated_channels_callback() has been added in this
version.

https://bugzilla.gnome.org/show_bug.cgi?id=654237
2011-07-11 14:43:14 +02:00
9deca75de8 update Punjabi Translation 2011-07-10 16:37:37 +05:30
5bc1dede81 tests: Add tests for large rounded corners
https://bugzilla.gnome.org/show_bug.cgi?id=649513
2011-07-09 20:13:47 -04:00
6d53d43766 PopupSwitchMenuItem: don't override switch by creating it twice
Due to an accidental addition line in commit c8670819, all switches in popup
menus accidentally gave the appearance that they were turned off.

Signed-off-by: Marc-Antoine Perennou <Marc-Antoine@Perennou.com>

https://bugzilla.gnome.org/show_bug.cgi?id=654267
2011-07-09 19:26:24 -04:00
325462d9bf St: Take the cairo fallback for large corners
The cogl path pads the corners out to the maximum corner radius to make the
math and painting logic easier. Unfortunately, when the radius exceeds the
actor's halfsize, the padding ends up interfering with other corners, creating
a big mess of rendering errors.

It'd be extremely complicated to fix this properly in the Cogl code,
so take the Cairo fallback.

https://bugzilla.gnome.org/show_bug.cgi?id=649513
2011-07-09 18:05:07 -04:00
ee4ae62946 St: Implement CSS3 specification for reducing border-radius
Currently, any cases of overlapping corners were just ignored and rendered incorrectly.
Implement the corner overlap algorithm as specified by the W3C to fix this.

https://bugzilla.gnome.org/show_bug.cgi?id=649513
2011-07-09 18:04:51 -04:00
153768ef7f NotificationDaemon: remove source if notification sender is removed from DBus
We don't want sources that are no longer associated with a running application
to stick around in the message tray.

Message tray sources were removed when the associated application’s state
changed to Shell.AppState.STOPPED . This caused sources for applications
that were still running, but did not have any open windows to be removed.
Instead, we should use the notification’s sender removal from DBus as an
indicator for when to remove the associated source from the message tray.

https://bugzilla.gnome.org/show_bug.cgi?id=645764
2011-07-08 21:24:42 -04:00
7249a218ba Updated Spanish translation 2011-07-07 22:41:05 +02:00
de348a4c49 telepathyClient: send typing notifications
If we're typing we want to send composing. If we empty the entry we
want to send active. If we're typing but don't type any more for
COMPOSING_STOP_TIMEOUT seconds, we want to send paused. Simple.

This behaviour was stolen from Empathy where it has won many awards.

https://bugzilla.gnome.org/show_bug.cgi?id=650196

Based on patch from Jonny Lamb <jonnylamb@gnome.org>
Signed-off-by: Alban Crequy <alban.crequy@collabora.co.uk>
2011-07-07 16:31:22 +01:00
fda8ffd9ef Display the number of unread messages in the notification icon
https://bugzilla.gnome.org/show_bug.cgi?id=654139
2011-07-07 15:48:33 +02:00
6cb707cc4f messageTray.js: raise an exception only if parseInt(count) returns NaN
0 is a valid value for count so we should accept it.

https://bugzilla.gnome.org/show_bug.cgi?id=654139
2011-07-07 15:47:12 +02:00
998c5f17fc Approve room invitations
We use to rely on Empathy for this but as we plan to allow users to be
connected on IM without having Empathy running the Shell should do it now.

https://bugzilla.gnome.org/show_bug.cgi?id=653740
2011-07-07 11:18:46 +02:00
c34b357051 gnome-shell-build-setup.sh: update libxcb dependency check on Debian
On Debian-based systems, the contents of libxcb-{event1,aux0}-dev
have been transitioned into a single libxcb-util0-dev package.
2011-07-07 01:01:41 +02:00
40f4e92461 messageTray: Use the count to display the number of unread notifications
https://bugzilla.gnome.org/show_bug.cgi?id=649356
2011-07-06 18:43:39 -04:00
c727da823b messageTray: Add an enumerable count for sources to use
https://bugzilla.gnome.org/show_bug.cgi?id=649356
2011-07-06 18:43:31 -04:00
8d1b7962d8 workspace: Fix layout errors caused by window zoom
Commit 64b2b4a7d4 changed the monitor layout handling, resulting
in some layout errors due to a subtle change in memory handling:
when zooming a window in the overview, the available zoom area is
calculated by subtracting the panel height from the primary monitor
area. This area used to be a copy of the monitor rect, but as now
the rect itself is returned, zooming a window on the primary monitor
repeatedly modifies the monitor rect, leading to layout errors in
various parts of the shell.
Fix by using a copy when calculating the available zoom area.

https://bugzilla.gnome.org/show_bug.cgi?id=654105
2011-07-06 22:06:45 +02:00
a6dfe20348 shell-util: Add a helper method for saving files from HTTP
Unfortunately, gjs cannot handle binary C strings directly from
gobject-introspection. Add a simple workaround method in C to help
us save random files from the web.

https://bugzilla.gnome.org/show_bug.cgi?id=653989
2011-07-06 12:46:18 -04:00
ed46390bbc Add dependency on libsoup
https://bugzilla.gnome.org/show_bug.cgi?id=653989
2011-07-06 12:46:13 -04:00
80eb5bf535 js: belatedly add layout.js to Makefile 2011-07-06 11:18:29 -04:00
7596fdb460 altTab: try to avoid showing the switcher when "flipping"
Use a longer fade-in time, but with an inout transition, so that the
dialog starts fading in very slowly and then picks up speed after
150ms or so. That way if the user releases Alt+Tab right away, they'll
never actually see the dialog.

https://bugzilla.gnome.org/show_bug.cgi?id=652346
2011-07-06 09:03:42 -04:00
8db1ff8aef altTab: honor switch_*_backward key binding actions
https://bugzilla.gnome.org/show_bug.cgi?id=650452
2011-07-06 08:45:31 -04:00
1dfffdbc4e layout: deal better with vertically-stacked monitors
If there is a monitor below the primary monitor, then put the message
tray there, rather than necessarily on the primary.

https://bugzilla.gnome.org/show_bug.cgi?id=636963
2011-07-06 08:38:35 -04:00
64b2b4a7d4 layout: new file handling shell layout
Remove ShellGlobal's monitor-related methods, and have
Main.layoutManager provide that information instead. Move
Main._relayout() to LayoutManager, and have other objects connect to
the layout manager's 'monitors-changed' signal to know when the screen
geometry has changed.

https://bugzilla.gnome.org/show_bug.cgi?id=636963
2011-07-06 08:38:35 -04:00
ae35d0e43c panel: pass the Panel object to the PanelCorners
rather than having them refer to it via Main.panel

https://bugzilla.gnome.org/show_bug.cgi?id=636963
2011-07-06 08:38:35 -04:00
b22c5eb167 Implement Telepathy Debug interface (#652816)
This enable debugging using empathy-debugger.
2011-07-06 09:31:18 +02:00
6d5e414863 appDisplay: make the categories list scrollable, if necessary
https://bugzilla.gnome.org/show_bug.cgi?id=651082
2011-07-05 13:49:01 -04:00
3e74dfb66d Updated Galician translations 2011-07-04 22:21:09 +02:00
1245628521 update Punjabi Translation 2011-07-04 23:23:04 +05:30
c567690004 power: Use the new gnome-settings-daemon DBus names now the power plugin has moved there 2011-07-04 17:57:25 +01:00
2feff4207b Updated Hebrew translation. 2011-07-01 19:56:16 +03:00
8c40b6f9a7 rename Source and Notification to make clear they are about chats
We are going to support more Telepathy events hence more Source and
Notification types.

https://bugzilla.gnome.org/show_bug.cgi?id=653740
2011-07-01 08:14:55 +02:00
fc759bff77 Fix version typo in NEWS
3.1.3 news was labelled 3.1.2; fix that post-release to avoid
confusion in the future.
2011-06-30 18:15:34 -04:00
78 changed files with 7528 additions and 2968 deletions

2
.gitignore vendored
View File

@ -49,9 +49,11 @@ src/calendar-server/org.gnome.Shell.CalendarServer.service
src/gnome-shell
src/gnome-shell-calendar-server
src/gnome-shell-extension-tool
src/gnome-shell-hotplug-sniffer
src/gnome-shell-jhbuild
src/gnome-shell-perf-helper
src/gnome-shell-real
src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service
src/run-js-test
src/test-recorder
src/test-recorder.ogg

50
NEWS
View File

@ -1,4 +1,51 @@
3.1.2
3.1.4
=====
* Take over inserted media handling and autorun from gnome-session [Cosimo]
* Message Tray
- Display a count of unread notifications on icons
[Jasper, Guillaume; #649356, #654139]
- Only remove icons when the sender quits from D-Bus, not when it
closes its last window [Neha, Marina; #645764]
- Solve problems switching chats between shell and Empathy
[Guillaume; #654237]
- Fix handling of bad GMarkup in messages [Dan; #650298]
- Never show notifications when the screensaver is active [Dan; #654550]
* Telepathy integrationpp
- Implement Telepathy Debug interface to enable empathy-debugger
[Guillaume; #652816]
- Allow approving room invitations, and audio/video calls
[Guillaume; #653740 #653939]
- Send typing notifications [Jonny; #650196]
* Fix selection highlighting for light-on-dark entries [Jasper; #643768]
* Make control-Return in the overview open a new window [Maxim]
* Delay showing the alt-Tab switcher to reduce visual noise when
flipping betweeen windows [Dan; #652346]
* When we have vertically stacked monitors, put the message tray
on the bottom one [Dan; #636963]
* Fix various problems with keynav and the Activities button
[Dan; #641253 #645759]
* Ensure screensaver is locked when switching users [Colin; #654565]
* Improve extension creation tool [Jasper; #653206]
* Fix compatibility with latest GJS [Giovanni; #654349]
* Code cleanups [Adel, Dan, Jasper; #645759 #654577 #654791 #654987]
* Misc bug fixes [Richard, Dan, Florian, Giovanni, Jasper, Marc-Antoine, Rui;
#647175 #649513 #650452 #651082 #653700 #653989 #654105 #654791 #654267
#654269 #654527 #655446]
* Build fixes [Florian, Siegfried; #654300]
Contributors:
Giovanni Campagna, Cosimo Cecchi, Guillaume Desmottes, Neha Doijode,
Maxim Ermilov, Adel Gadllah, Siegfried-Angel Gevatter Pujals, Richard Hughes,
Jonny Lamb, Rui Matos, Florian Müllner, Marc-Antoine Perennou, Colin Walters,
Dan Winship, Marina Zhurakhinskaya
Translations:
Mario Blättermann, Paul Seyfert [de], Jorge González, Daniel Mustieles [es],
Fran Dieguez [gl], Yaron Shahrabani [he], Luca Ferretti [it],
Rudolfs Mazurs [lv], Kjartan Maraas [nb], A S Alam [pa], Yuri Kozlov [ru],
Michal Štrba, Matej Urbančič [sl]
3.1.3
=====
* Fix problem with "user theme extension" breaking the CSS for other
extensions [Giovanni; #650971]
@ -77,7 +124,6 @@ Translations:
Matej Urbančič [sl], Krishnababu Krothapalli [te], Daniel Korostil [uk],
Aron Xu [zh_CN]
3.0.2
=====
* Network Menu [Dan Williams]

View File

@ -1,5 +1,5 @@
AC_PREREQ(2.63)
AC_INIT([gnome-shell],[3.1.3],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_INIT([gnome-shell],[3.1.4],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_SRCDIR([src/shell-global.c])
@ -64,16 +64,16 @@ fi
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
CLUTTER_MIN_VERSION=1.5.15
CLUTTER_MIN_VERSION=1.7.5
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
GJS_MIN_VERSION=0.7.11
GJS_MIN_VERSION=1.29.15
MUTTER_MIN_VERSION=3.0.0
GTK_MIN_VERSION=3.0.0
GIO_MIN_VERSION=2.25.9
LIBECAL_MIN_VERSION=2.32.0
LIBEDATASERVER_MIN_VERSION=1.2.0
LIBEDATASERVERUI_MIN_VERSION=2.91.6
TELEPATHY_GLIB_MIN_VERSION=0.15.0
TELEPATHY_GLIB_MIN_VERSION=0.15.3
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
POLKIT_MIN_VERSION=0.100
STARTUP_NOTIFICATION_MIN_VERSION=0.11
@ -85,7 +85,7 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-2.0 >= $GIO_MIN_VERSION
libmutter >= $MUTTER_MIN_VERSION
gjs-internals-1.0 >= $GJS_MIN_VERSION
libgnome-menu $recorder_modules gconf-2.0
gdk-x11-3.0
gdk-x11-3.0 libsoup-2.4
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION
@ -97,6 +97,8 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-2.0 >= $GIO_MIN_VERSION
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
GJS_VERSION=`$PKG_CONFIG --modversion gjs-internals-1.0`
AC_DEFINE_UNQUOTED([GJS_VERSION], ["$GJS_VERSION"], [The version of GJS we're linking to])
AC_SUBST([GJS_VERSION], ["$GJS_VERSION"])

View File

@ -264,7 +264,7 @@ StTooltip StLabel {
}
.panel-corner:active,
.panel-corner:checked,
.panel-corner:overview,
.panel-corner:focus {
-panel-corner-inner-border-color: rgba(255,255,255,0.8);
}
@ -301,7 +301,7 @@ StTooltip StLabel {
}
.panel-button:active,
.panel-button:checked,
.panel-button:overview,
.panel-button:focus {
border-image: url("panel-button-border.svg") 10 10 0 2;
background-image: url("panel-button-highlight-wide.svg");
@ -464,6 +464,7 @@ StTooltip StLabel {
background-gradient-start: rgba(5,5,6,0.1);
background-gradient-end: rgba(254,254,254,0.1);
background-gradient-direction: vertical;
selected-color: black;
caret-color: rgb(128, 128, 128);
caret-size: 1px;
width: 250px;
@ -724,6 +725,8 @@ StTooltip StLabel {
.lg-dialog StEntry
{
color: #88ff66;
selection-background-color: #88ff66;
selected-color: black;
}
.lg-obj-inspector-title
@ -1136,6 +1139,83 @@ StTooltip StLabel {
icon-size: 36px;
}
.hotplug-transient-box {
spacing: 6px;
padding: 2px 72px 2px 12px;
}
.hotplug-notification-item {
background-color: #3c3c3c;
padding: 0px 10px;
border-radius: 8px;
border: 1px solid #181818;
}
.hotplug-notification-item:hover {
border: 1px solid #a1a1a1;
}
.hotplug-notification-item:focus {
background-color: #666666;
}
.hotplug-notification-item:active {
border: 1px solid #a1a1a1;
background-color: #2b2b2b;
}
.hotplug-notification-item-icon {
icon-size: 24px;
padding: 2px 5px;
}
.hotplug-resident-box {
spacing: 8px;
}
.hotplug-resident-mount {
spacing: 8px;
border-radius: 4px;
color: #ccc;
}
.hotplug-resident-mount:hover {
background-gradient-direction: horizontal;
background-gradient-start: rgba(255, 255, 255, 0.1);
background-gradient-end: rgba(255, 255, 255, 0);
color: #fff;
}
.hotplug-resident-mount-label {
color: inherit;
padding-left: 6px;
}
.hotplug-resident-mount-icon {
icon-size: 24px;
padding-left: 6px;
}
.hotplug-resident-eject-icon {
icon-size: 16px;
}
.hotplug-resident-eject-button {
padding: 2px;
border: 1px solid #2b2b2b;
border-radius: 5px;
color: #ccc;
}
.hotplug-resident-eject-button:hover {
color: #fff;
background-color: #2b2b2b;
border: 1px solid #a1a1a1;
}
.chat-log-message {
color: #888888;
}
@ -1195,6 +1275,8 @@ StTooltip StLabel {
color: #545454;
background-color: #e8e8e8;
caret-color: #545454;
selection-background-color: #bcbcbc;
selected-color: #323232;
box-shadow: 0px 0px 6px 2px rgba(255,255,255,0.9);
}
@ -1261,6 +1343,16 @@ StTooltip StLabel {
padding-left: 4px;
}
.summary-source-counter {
color: white;
background-color: #3465A4;
text-shadow: black 1px 1px 0;
font-size: 9pt;
border-radius: 1em;
min-height: 1em;
min-width: 1em;
}
.source-title {
font-size: 9pt;
font-weight: bold;
@ -1476,6 +1568,8 @@ StTooltip StLabel {
font-weight: bold;
width: 23em;
color: white;
selection-background-color: white;
selected-color: black;
}
.run-dialog {
@ -1579,6 +1673,90 @@ StTooltip StLabel {
color: #444444;
}
/* ShellMountOperation Dialogs */
.shell-mount-operation-icon {
icon-size: 48px;
}
.mount-password-reask {
color: red;
}
.show-processes-dialog,
.mount-question-dialog {
spacing: 24px;
}
.show-processes-dialog-subject,
.mount-question-dialog-subject {
font-size: 12pt;
font-weight: bold;
color: #666666;
padding-top: 10px;
padding-left: 17px;
padding-bottom: 6px;
}
.show-processes-dialog-subject:rtl,
.mount-question-dialog-subject:rtl {
padding-left: 0px;
padding-right: 17px;
}
.show-processes-dialog-description,
.mount-question-dialog-description {
font-size: 10pt;
color: white;
padding-left: 17px;
width: 28em;
}
.show-processes-dialog-description:rtl,
.mount-question-dialog-description:rtl {
padding-right: 17px;
}
.show-processes-dialog-app-list {
font-size: 10pt;
max-height: 200px;
padding-top: 24px;
padding-left: 49px;
padding-right: 32px;
}
.show-processes-dialog-app-list:rtl {
padding-right: 49px;
padding-left: 32px;
}
.show-processes-dialog-app-list-item {
color: #ccc;
}
.show-processes-dialog-app-list-item:hover {
color: white;
}
.show-processes-dialog-app-list-item:ltr {
padding-right: 1em;
}
.show-processes-dialog-app-list-item:rtl {
padding-left: 1em;
}
.show-processes-dialog-app-list-item-icon:ltr {
padding-right: 17px;
}
.show-processes-dialog-app-list-item-icon:rtl {
padding-left: 17px;
}
.show-processes-dialog-app-list-item-name {
font-size: 10pt;
}
/* PolicyKit Authentication Dialog */
.polkit-dialog {
/* this is the width of the entire modal popup */

View File

@ -10,11 +10,14 @@ nobase_dist_js_DATA = \
misc/history.js \
misc/modemManager.js \
misc/params.js \
misc/screenSaver.js \
misc/util.js \
perf/core.js \
ui/altTab.js \
ui/appDisplay.js \
ui/appFavorites.js \
ui/automountManager.js \
ui/autorunManager.js \
ui/boxpointer.js \
ui/calendar.js \
ui/chrome.js \
@ -27,6 +30,7 @@ nobase_dist_js_DATA = \
ui/environment.js \
ui/extensionSystem.js \
ui/iconGrid.js \
ui/layout.js \
ui/lightbox.js \
ui/link.js \
ui/lookingGlass.js \
@ -35,6 +39,7 @@ nobase_dist_js_DATA = \
ui/main.js \
ui/messageTray.js \
ui/modalDialog.js \
ui/shellMountOperation.js \
ui/notificationDaemon.js \
ui/overview.js \
ui/panel.js \

53
js/misc/screenSaver.js Normal file
View File

@ -0,0 +1,53 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const DBus = imports.dbus;
const Lang = imports.lang;
const ScreenSaverIface = {
name: 'org.gnome.ScreenSaver',
methods: [{ name: 'GetActive',
inSignature: '',
outSignature: 'b' },
{ name: 'Lock',
inSignature: '' },
{ name: 'SetActive',
inSignature: 'b' }],
signals: [{ name: 'ActiveChanged',
inSignature: 'b' }]
};
function ScreenSaverProxy() {
this._init();
}
ScreenSaverProxy.prototype = {
_init: function() {
DBus.session.proxifyObject(this,
'org.gnome.ScreenSaver',
'/org/gnome/ScreenSaver');
DBus.session.watch_name('org.gnome.ScreenSaver',
false, // do not launch a name-owner if none exists
Lang.bind(this, this._onSSAppeared),
Lang.bind(this, this._onSSVanished));
this.screenSaverActive = false;
this.connect('ActiveChanged',
Lang.bind(this, this._onActiveChanged));
},
_onSSAppeared: function(owner) {
this.GetActiveRemote(Lang.bind(this, function(isActive) {
this.screenSaverActive = isActive;
}))
},
_onSSVanished: function(oldOwner) {
this.screenSaverActive = false;
},
_onActiveChanged: function(object, isActive) {
this.screenSaverActive = isActive;
}
};
DBus.proxifyPrototype(ScreenSaverProxy.prototype, ScreenSaverIface);

View File

@ -14,7 +14,8 @@ const Tweener = imports.ui.tweener;
const POPUP_APPICON_SIZE = 96;
const POPUP_SCROLL_TIME = 0.10; // seconds
const POPUP_FADE_TIME = 0.1; // seconds
const POPUP_FADE_IN_TIME = 0.4; // seconds
const POPUP_FADE_OUT_TIME = 0.1; // seconds
const APP_ICON_HOVER_TIMEOUT = 200; // milliseconds
@ -74,7 +75,7 @@ AltTabPopup.prototype = {
_allocate: function (actor, box, flags) {
let childBox = new Clutter.ActorBox();
let primary = global.get_primary_monitor();
let primary = Main.layoutManager.primaryMonitor;
let leftPadding = this.actor.get_theme_node().get_padding(St.Side.LEFT);
let rightPadding = this.actor.get_theme_node().get_padding(St.Side.RIGHT);
@ -119,7 +120,7 @@ AltTabPopup.prototype = {
}
},
show : function(backward, switch_group) {
show : function(backward, binding) {
let tracker = Shell.WindowTracker.get_default();
let apps = tracker.get_running_apps ('');
@ -150,7 +151,7 @@ AltTabPopup.prototype = {
this.actor.get_allocation_box();
// Make the initial selection
if (switch_group) {
if (binding == 'switch_group') {
if (backward) {
this._select(0, this._appIcons[0].cachedWindows.length - 1);
} else {
@ -159,6 +160,10 @@ AltTabPopup.prototype = {
else
this._select(0, 0);
}
} else if (binding == 'switch_group_backward') {
this._select(0, this._appIcons[0].cachedWindows.length - 1);
} else if (binding == 'switch_windows_backward') {
this._select(this._appIcons.length - 1);
} else if (this._appIcons.length == 1) {
this._select(0);
} else if (backward) {
@ -178,10 +183,15 @@ AltTabPopup.prototype = {
return false;
}
// Using easeInOutExpo over 400ms gives us 150ms of "nearly
// invisible" (less than 10% opacity), followed by a 100ms
// tween in (to 90% opacity, with the last 10% coming over the
// next 150ms). So if the user releases Alt quickly after we
// start tweening, they'll never see the switcher.
Tweener.addTween(this.actor,
{ opacity: 255,
time: POPUP_FADE_TIME,
transition: 'easeOutQuad'
time: POPUP_FADE_IN_TIME,
transition: 'easeInOutExpo'
});
return true;
@ -221,8 +231,12 @@ AltTabPopup.prototype = {
this.destroy();
} else if (action == Meta.KeyBindingAction.SWITCH_GROUP) {
this._select(this._currentApp, backwards ? this._previousWindow() : this._nextWindow());
} else if (action == Meta.KeyBindingAction.SWITCH_GROUP_BACKWARD) {
this._select(this._currentApp, this._previousWindow());
} else if (action == Meta.KeyBindingAction.SWITCH_WINDOWS) {
this._select(backwards ? this._previousApp() : this._nextApp());
} else if (action == Meta.KeyBindingAction.SWITCH_WINDOWS_BACKWARD) {
this._select(this._previousApp());
} else if (this._thumbnailsFocused) {
if (keysym == Clutter.Left)
this._select(this._currentApp, this._previousWindow());
@ -360,7 +374,7 @@ AltTabPopup.prototype = {
if (this.actor.visible) {
Tweener.addTween(this.actor,
{ opacity: 0,
time: POPUP_FADE_TIME,
time: POPUP_FADE_OUT_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
@ -618,7 +632,7 @@ SwitcherList.prototype = {
this._items[this._highlighted].add_style_pseudo_class('selected');
}
let monitor = global.get_primary_monitor();
let monitor = Main.layoutManager.primaryMonitor;
let itemSize = this._items[index].allocation.x2 - this._items[index].allocation.x1;
let [posX, posY] = this._items[index].get_transformed_position();
posX += this.actor.x;
@ -646,7 +660,7 @@ SwitcherList.prototype = {
_scrollToRight : function() {
this._scrollableLeft = true;
let monitor = global.get_primary_monitor();
let monitor = Main.layoutManager.primaryMonitor;
let padding = this.actor.get_theme_node().get_horizontal_padding();
let parentPadding = this.actor.get_parent().get_theme_node().get_horizontal_padding();
let x = this._items[this._highlighted].allocation.x2 - monitor.width + padding + parentPadding;
@ -743,7 +757,7 @@ SwitcherList.prototype = {
let children = this._list.get_children();
let childBox = new Clutter.ActorBox();
let primary = global.get_primary_monitor();
let primary = Main.layoutManager.primaryMonitor;
let parentRightPadding = this.actor.get_parent().get_theme_node().get_padding(St.Side.RIGHT);
if (this.actor.allocation.x2 == primary.x + primary.width - parentRightPadding) {
if (this._squareItems)
@ -873,7 +887,7 @@ AppSwitcher.prototype = {
totalSpacing += this._separator.width + this._list.spacing;
// We just assume the whole screen here due to weirdness happing with the passed width
let primary = global.get_primary_monitor();
let primary = Main.layoutManager.primaryMonitor;
let parentPadding = this.actor.get_parent().get_theme_node().get_horizontal_padding();
let availWidth = primary.width - parentPadding - this.actor.get_theme_node().get_horizontal_padding();
let height = 0;

View File

@ -172,10 +172,12 @@ ViewByCategories.prototype = {
// (used only before the actor is mapped the first time)
this._currentCategory = -2;
this._filters = new St.BoxLayout({ vertical: true, reactive: true });
this._filters.connect('scroll-event', Lang.bind(this, this._scrollFilter));
this._filtersBox = new St.ScrollView({ x_fill: false,
y_fill: false,
style_class: 'vfade' });
this._filtersBox.add_actor(this._filters);
this.actor.add(this._view.actor, { expand: true, x_fill: true, y_fill: true });
this.actor.add(this._filters, { expand: false, y_fill: false, y_align: St.Align.START });
this.actor.add(this._filtersBox, { expand: false, y_fill: false, y_align: St.Align.START });
// Always select the "All" filter when switching to the app view
this.actor.connect('notify::mapped', Lang.bind(this,
@ -193,14 +195,6 @@ ViewByCategories.prototype = {
this.actor.add(this._focusDummy);
},
_scrollFilter: function(actor, event) {
let direction = event.get_scroll_direction();
if (direction == Clutter.ScrollDirection.UP)
this._selectCategory(Math.max(this._currentCategory - 1, -1))
else if (direction == Clutter.ScrollDirection.DOWN)
this._selectCategory(Math.min(this._currentCategory + 1, this._sections.length - 1));
},
_selectCategory: function(num) {
if (this._currentCategory == num) // nothing to do
return;
@ -333,8 +327,16 @@ BaseAppSearchProvider.prototype = {
params = Params.parse(params, { workspace: null,
timestamp: null });
let workspace = params.workspace ? params.workspace.index() : -1;
let event = Clutter.get_current_event();
let modifiers = event ? Shell.get_event_state(event) : 0;
let openNewWindow = modifiers & Clutter.ModifierType.CONTROL_MASK;
let app = this._appSys.get_app(id);
app.activate(params.workspace ? params.workspace.index() : -1);
if (openNewWindow)
app.open_new_window(workspace);
else
app.activate(workspace);
},
dragActivateResult: function(id, params) {

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

@ -0,0 +1,278 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Lang = imports.lang;
const DBus = imports.dbus;
const Mainloop = imports.mainloop;
const Gio = imports.gi.Gio;
const Params = imports.misc.params;
const Main = imports.ui.main;
const ShellMountOperation = imports.ui.shellMountOperation;
const ScreenSaver = imports.misc.screenSaver;
// GSettings keys
const SETTINGS_SCHEMA = 'org.gnome.desktop.media-handling';
const SETTING_ENABLE_AUTOMOUNT = 'automount';
const AUTORUN_EXPIRE_TIMEOUT_SECS = 10;
const ConsoleKitSessionIface = {
name: 'org.freedesktop.ConsoleKit.Session',
methods: [{ name: 'IsActive',
inSignature: '',
outSignature: 'b' }],
signals: [{ name: 'ActiveChanged',
inSignature: 'b' }]
};
const ConsoleKitSessionProxy = DBus.makeProxyClass(ConsoleKitSessionIface);
const ConsoleKitManagerIface = {
name: 'org.freedesktop.ConsoleKit.Manager',
methods: [{ name: 'GetCurrentSession',
inSignature: '',
outSignature: 'o' }]
};
function ConsoleKitManager() {
this._init();
};
ConsoleKitManager.prototype = {
_init: function() {
this.sessionActive = true;
DBus.system.proxifyObject(this,
'org.freedesktop.ConsoleKit',
'/org/freedesktop/ConsoleKit/Manager');
DBus.system.watch_name('org.freedesktop.ConsoleKit',
false, // do not launch a name-owner if none exists
Lang.bind(this, this._onManagerAppeared),
Lang.bind(this, this._onManagerVanished));
},
_onManagerAppeared: function(owner) {
this.GetCurrentSessionRemote(Lang.bind(this, this._onCurrentSession));
},
_onManagerVanished: function(oldOwner) {
this.sessionActive = true;
},
_onCurrentSession: function(session) {
this._ckSession = new ConsoleKitSessionProxy(DBus.system, 'org.freedesktop.ConsoleKit', session);
this._ckSession.connect
('ActiveChanged', Lang.bind(this, function(object, isActive) {
this.sessionActive = isActive;
}));
this._ckSession.IsActiveRemote(Lang.bind(this, function(isActive) {
this.sessionActive = isActive;
}));
}
};
DBus.proxifyPrototype(ConsoleKitManager.prototype, ConsoleKitManagerIface);
function AutomountManager() {
this._init();
}
AutomountManager.prototype = {
_init: function() {
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
this._volumeQueue = [];
this.ckListener = new ConsoleKitManager();
this._ssProxy = new ScreenSaver.ScreenSaverProxy();
this._ssProxy.connect('ActiveChanged',
Lang.bind(this,
this._screenSaverActiveChanged));
this._volumeMonitor = Gio.VolumeMonitor.get();
this._volumeMonitor.connect('volume-added',
Lang.bind(this,
this._onVolumeAdded));
this._volumeMonitor.connect('volume-removed',
Lang.bind(this,
this._onVolumeRemoved));
this._volumeMonitor.connect('drive-connected',
Lang.bind(this,
this._onDriveConnected));
this._volumeMonitor.connect('drive-disconnected',
Lang.bind(this,
this._onDriveDisconnected));
this._volumeMonitor.connect('drive-eject-button',
Lang.bind(this,
this._onDriveEjectButton));
Mainloop.idle_add(Lang.bind(this, this._startupMountAll));
},
_screenSaverActiveChanged: function(object, isActive) {
if (!isActive) {
this._volumeQueue.forEach(Lang.bind(this, function(volume) {
this._checkAndMountVolume(volume);
}));
}
// clear the queue anyway
this._volumeQueue = [];
},
_startupMountAll: function() {
let volumes = this._volumeMonitor.get_volumes();
volumes.forEach(Lang.bind(this, function(volume) {
this._checkAndMountVolume(volume, { checkSession: false,
useMountOp: false });
}));
return false;
},
_onDriveConnected: function() {
// if we're not in the current ConsoleKit session,
// or screensaver is active, don't play sounds
if (!this.ckListener.sessionActive)
return;
if (this._ssProxy.screenSaverActive)
return;
global.play_theme_sound(0, 'device-added-media');
},
_onDriveDisconnected: function() {
// if we're not in the current ConsoleKit session,
// or screensaver is active, don't play sounds
if (!this.ckListener.sessionActive)
return;
if (this._ssProxy.screenSaverActive)
return;
global.play_theme_sound(0, 'device-removed-media');
},
_onDriveEjectButton: function(monitor, drive) {
// TODO: this code path is not tested, as the GVfs volume monitor
// doesn't emit this signal just yet.
if (!this.ckListener.sessionActive)
return;
// we force stop/eject in this case, so we don't have to pass a
// mount operation object
if (drive.can_stop()) {
drive.stop
(Gio.MountUnmountFlags.FORCE, null, null,
Lang.bind(this, function(drive, res) {
try {
drive.stop_finish(res);
} catch (e) {
log("Unable to stop the drive after drive-eject-button " + e.toString());
}
}));
} else if (drive.can_eject()) {
drive.eject_with_operation
(Gio.MountUnmountFlags.FORCE, null, null,
Lang.bind(this, function(drive, res) {
try {
drive.eject_with_operation_finish(res);
} catch (e) {
log("Unable to eject the drive after drive-eject-button " + e.toString());
}
}));
}
},
_onVolumeAdded: function(monitor, volume) {
this._checkAndMountVolume(volume);
},
_checkAndMountVolume: function(volume, params) {
params = Params.parse(params, { checkSession: true,
useMountOp: true });
if (params.checkSession) {
// if we're not in the current ConsoleKit session,
// don't attempt automount
if (!this.ckListener.sessionActive)
return;
if (this._ssProxy.screenSaverActive) {
if (this._volumeQueue.indexOf(volume) == -1)
this._volumeQueue.push(volume);
return;
}
}
if (!this._settings.get_boolean(SETTING_ENABLE_AUTOMOUNT) ||
!volume.should_automount() ||
!volume.can_mount()) {
// allow the autorun to run anyway; this can happen if the
// mount gets added programmatically later, even if
// should_automount() or can_mount() are false, like for
// blank optical media.
this._allowAutorun(volume);
this._allowAutorunExpire(volume);
return;
}
if (params.useMountOp) {
let operation = new ShellMountOperation.ShellMountOperation(volume);
this._mountVolume(volume, operation.mountOp);
} else {
this._mountVolume(volume, null);
}
},
_mountVolume: function(volume, operation) {
this._allowAutorun(volume);
volume.mount(0, operation, null,
Lang.bind(this, this._onVolumeMounted));
},
_onVolumeMounted: function(volume, res) {
this._allowAutorunExpire(volume);
try {
volume.mount_finish(res);
} catch (e) {
let string = e.toString();
// FIXME: needs proper error code handling instead of this
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
if (string.indexOf('No key available with this passphrase') != -1)
this._reaskPassword(volume);
else
log('Unable to mount volume ' + volume.get_name() + ': ' + string);
}
},
_onVolumeRemoved: function(monitor, volume) {
this._volumeQueue =
this._volumeQueue.filter(function(element) {
return (element != volume);
});
},
_reaskPassword: function(volume) {
let operation = new ShellMountOperation.ShellMountOperation(volume, { reaskPassword: true });
this._mountVolume(volume, operation.mountOp);
},
_allowAutorun: function(volume) {
volume.allowAutorun = true;
},
_allowAutorunExpire: function(volume) {
Mainloop.timeout_add_seconds(AUTORUN_EXPIRE_TIMEOUT_SECS, function() {
volume.allowAutorun = false;
return false;
});
}
}

634
js/ui/autorunManager.js Normal file
View File

@ -0,0 +1,634 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Lang = imports.lang;
const DBus = imports.dbus;
const Gio = imports.gi.Gio;
const St = imports.gi.St;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const ShellMountOperation = imports.ui.shellMountOperation;
// GSettings keys
const SETTINGS_SCHEMA = 'org.gnome.desktop.media-handling';
const SETTING_DISABLE_AUTORUN = 'autorun-never';
const SETTING_START_APP = 'autorun-x-content-start-app';
const SETTING_IGNORE = 'autorun-x-content-ignore';
const SETTING_OPEN_FOLDER = 'autorun-x-content-open-folder';
const AutorunSetting = {
RUN: 0,
IGNORE: 1,
FILES: 2,
ASK: 3
};
const HOTPLUG_ICON_SIZE = 16;
// misc utils
function ignoreAutorunForMount(mount) {
let root = mount.get_root();
let volume = mount.get_volume();
if ((root.is_native() && !isMountRootHidden(root)) ||
(volume && volume.allowAutorun && volume.should_automount()))
return false;
return true;
}
function isMountRootHidden(root) {
let path = root.get_path();
// skip any mounts in hidden directory hierarchies
return (path.indexOf('/.') != -1);
}
function startAppForMount(app, mount) {
let files = [];
let root = mount.get_root();
let retval = false;
files.push(root);
try {
retval = app.launch(files,
global.create_app_launch_context())
} catch (e) {
log('Unable to launch the application ' + app.get_name()
+ ': ' + e.toString());
}
return retval;
}
/******************************************/
const HotplugSnifferIface = {
name: 'org.gnome.Shell.HotplugSniffer',
methods: [{ name: 'SniffURI',
inSignature: 's',
outSignature: 'as' }]
};
const HotplugSniffer = function() {
this._init();
};
HotplugSniffer.prototype = {
_init: function() {
DBus.session.proxifyObject(this,
'org.gnome.Shell.HotplugSniffer',
'/org/gnome/Shell/HotplugSniffer');
},
};
DBus.proxifyPrototype(HotplugSniffer.prototype, HotplugSnifferIface);
function ContentTypeDiscoverer(callback) {
this._init(callback);
}
ContentTypeDiscoverer.prototype = {
_init: function(callback) {
this._callback = callback;
},
guessContentTypes: function(mount) {
// guess mount's content types using GIO
mount.guess_content_type(false, null,
Lang.bind(this,
this._onContentTypeGuessed));
},
_onContentTypeGuessed: function(mount, res) {
let contentTypes = [];
try {
contentTypes = mount.guess_content_type_finish(res);
} catch (e) {
log('Unable to guess content types on added mount ' + mount.get_name()
+ ': ' + e.toString());
}
if (contentTypes.length) {
this._emitCallback(mount, contentTypes);
} else {
let root = mount.get_root();
let hotplugSniffer = new HotplugSniffer();
hotplugSniffer.SniffURIRemote
(root.get_uri(), DBus.CALL_FLAG_START,
Lang.bind(this, function(contentTypes) {
this._emitCallback(mount, contentTypes);
}));
}
},
_emitCallback: function(mount, contentTypes) {
if (!contentTypes)
contentTypes = [];
// we're not interested in win32 software content types here
contentTypes = contentTypes.filter(function(type) {
return (type != 'x-content/win32-software');
});
let apps = [];
contentTypes.forEach(function(type) {
let app = Gio.app_info_get_default_for_type(type, false);
if (app)
apps.push(app);
});
if (apps.length == 0)
apps.push(Gio.app_info_get_default_for_type('inode/directory', false));
this._callback(mount, apps, contentTypes);
}
}
function AutorunManager() {
this._init();
}
AutorunManager.prototype = {
_init: function() {
this._volumeMonitor = Gio.VolumeMonitor.get();
this._volumeMonitor.connect('mount-added',
Lang.bind(this,
this._onMountAdded));
this._volumeMonitor.connect('mount-removed',
Lang.bind(this,
this._onMountRemoved));
this._transDispatcher = new AutorunTransientDispatcher();
this._createResidentSource();
let mounts = this._volumeMonitor.get_mounts();
mounts.forEach(Lang.bind(this, function (mount) {
let discoverer = new ContentTypeDiscoverer(Lang.bind (this,
function (mount, apps) {
this._residentSource.addMount(mount, apps);
}));
discoverer.guessContentTypes(mount);
}));
},
_createResidentSource: function() {
this._residentSource = new AutorunResidentSource();
this._residentSource.connect('destroy',
Lang.bind(this,
this._createResidentSource));
},
_onMountAdded: function(monitor, mount) {
// don't do anything if our session is not the currently
// active one
if (!Main.automountManager.ckListener.sessionActive)
return;
let discoverer = new ContentTypeDiscoverer(Lang.bind (this,
function (mount, apps, contentTypes) {
this._transDispatcher.addMount(mount, apps, contentTypes);
this._residentSource.addMount(mount, apps);
}));
discoverer.guessContentTypes(mount);
},
_onMountRemoved: function(monitor, mount) {
this._transDispatcher.removeMount(mount);
this._residentSource.removeMount(mount);
},
ejectMount: function(mount) {
let mountOp = new ShellMountOperation.ShellMountOperation(mount);
// first, see if we have a drive
let drive = mount.get_drive();
let volume = mount.get_volume();
if (drive &&
drive.get_start_stop_type() == Gio.DriveStartStopType.SHUTDOWN &&
drive.can_stop()) {
drive.stop(0, mountOp.mountOp, null,
Lang.bind(this, this._onStop));
} else {
if (mount.can_eject()) {
mount.eject_with_operation(0, mountOp.mountOp, null,
Lang.bind(this, this._onEject));
} else if (volume && volume.can_eject()) {
volume.eject_with_operation(0, mountOp.mountOp, null,
Lang.bind(this, this._onEject));
} else if (drive && drive.can_eject()) {
drive.eject_with_operation(0, mountOp.mountOp, null,
Lang.bind(this, this._onEject));
} else if (mount.can_unmount()) {
mount.unmount_with_operation(0, mountOp.mountOp, null,
Lang.bind(this, this._onUnmount));
}
}
},
_onUnmount: function(mount, res) {
try {
mount.unmount_with_operation_finish(res);
} catch (e) {
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
// but we can't access the error code from JS.
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
log('Unable to eject the mount ' + mount.get_name()
+ ': ' + e.toString());
}
},
_onEject: function(source, res) {
try {
source.eject_with_operation_finish(res);
} catch (e) {
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
// but we can't access the error code from JS.
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
log('Unable to eject the drive ' + source.get_name()
+ ': ' + e.toString());
}
},
_onStop: function(drive, res) {
try {
drive.stop_finish(res);
} catch (e) {
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
// but we can't access the error code from JS.
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
log('Unable to stop the drive ' + drive.get_name()
+ ': ' + e.toString());
}
},
}
function AutorunResidentSource() {
this._init();
}
AutorunResidentSource.prototype = {
__proto__: MessageTray.Source.prototype,
_init: function() {
MessageTray.Source.prototype._init.call(this, _('Removable Devices'));
this._mounts = [];
this._notification = new AutorunResidentNotification(this);
this._setSummaryIcon(this.createNotificationIcon(HOTPLUG_ICON_SIZE));
},
addMount: function(mount, apps) {
if (ignoreAutorunForMount(mount))
return;
let filtered = this._mounts.filter(function (element) {
return (element.mount == mount);
});
if (filtered.length != 0)
return;
let element = { mount: mount, apps: apps };
this._mounts.push(element);
this._redisplay();
},
removeMount: function(mount) {
this._mounts =
this._mounts.filter(function (element) {
return (element.mount != mount);
});
this._redisplay();
},
_redisplay: function() {
if (this._mounts.length == 0) {
this._notification.destroy();
this.destroy();
return;
}
this._notification.updateForMounts(this._mounts);
// add ourselves as a source, and push the notification
if (!Main.messageTray.contains(this)) {
Main.messageTray.add(this);
this.pushNotification(this._notification);
}
},
createNotificationIcon: function(iconSize) {
return new St.Icon ({ icon_name: 'drive-harddisk',
icon_size: iconSize ? iconSize : this.ICON_SIZE });
}
}
function AutorunResidentNotification(source) {
this._init(source);
}
AutorunResidentNotification.prototype = {
__proto__: MessageTray.Notification.prototype,
_init: function(source) {
MessageTray.Notification.prototype._init.call(this, source,
source.title, null,
{ customContent: true });
// set the notification as resident
this.setResident(true);
this._layout = new St.BoxLayout ({ style_class: 'hotplug-resident-box',
vertical: true });
this.addActor(this._layout,
{ x_expand: true,
x_fill: true });
},
updateForMounts: function(mounts) {
// remove all the layout content
this._layout.destroy_children();
for (let idx = 0; idx < mounts.length; idx++) {
let element = mounts[idx];
let actor = this._itemForMount(element.mount, element.apps);
this._layout.add(actor, { x_fill: true,
expand: true });
}
},
_itemForMount: function(mount, apps) {
let item = new St.BoxLayout();
// prepare the mount button content
let mountLayout = new St.BoxLayout();
let mountIcon = new St.Icon({ gicon: mount.get_icon(),
style_class: 'hotplug-resident-mount-icon' });
mountLayout.add_actor(mountIcon);
let labelBin = new St.Bin({ y_align: St.Align.MIDDLE });
let mountLabel =
new St.Label({ text: mount.get_name(),
style_class: 'hotplug-resident-mount-label',
track_hover: true,
reactive: true });
labelBin.add_actor(mountLabel);
mountLayout.add_actor(labelBin);
let mountButton = new St.Button({ child: mountLayout,
x_align: St.Align.START,
x_fill: true,
style_class: 'hotplug-resident-mount',
button_mask: St.ButtonMask.ONE });
item.add(mountButton, { x_align: St.Align.START,
expand: true });
let ejectIcon =
new St.Icon({ icon_name: 'media-eject',
style_class: 'hotplug-resident-eject-icon' });
let ejectButton =
new St.Button({ style_class: 'hotplug-resident-eject-button',
button_mask: St.ButtonMask.ONE,
child: ejectIcon });
item.add(ejectButton, { x_align: St.Align.END });
// now connect signals
mountButton.connect('clicked', Lang.bind(this, function(actor, event) {
startAppForMount(apps[0], mount);
}));
ejectButton.connect('clicked', Lang.bind(this, function() {
Main.autorunManager.ejectMount(mount);
}));
return item;
},
}
function AutorunTransientDispatcher() {
this._init();
}
AutorunTransientDispatcher.prototype = {
_init: function() {
this._sources = [];
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
},
_getAutorunSettingForType: function(contentType) {
let runApp = this._settings.get_strv(SETTING_START_APP);
if (runApp.indexOf(contentType) != -1)
return AutorunSetting.RUN;
let ignore = this._settings.get_strv(SETTING_IGNORE);
if (ignore.indexOf(contentType) != -1)
return AutorunSetting.IGNORE;
let openFiles = this._settings.get_strv(SETTING_OPEN_FOLDER);
if (openFiles.indexOf(contentType) != -1)
return AutorunSetting.FILES;
return AutorunSetting.ASK;
},
_getSourceForMount: function(mount) {
let filtered =
this._sources.filter(function (source) {
return (source.mount == mount);
});
// we always make sure not to add two sources for the same
// mount in addMount(), so it's safe to assume filtered.length
// is always either 1 or 0.
if (filtered.length == 1)
return filtered[0];
return null;
},
_addSource: function(mount, apps) {
// if we already have a source showing for this
// mount, return
if (this._getSourceForMount(mount))
return;
// add a new source
this._sources.push(new AutorunTransientSource(mount, apps));
},
addMount: function(mount, apps, contentTypes) {
// if autorun is disabled globally, return
if (this._settings.get_boolean(SETTING_DISABLE_AUTORUN))
return;
// if the mount doesn't want to be autorun, return
if (ignoreAutorunForMount(mount))
return;
let setting = this._getAutorunSettingForType(contentTypes[0]);
// check at the settings for the first content type
// to see whether we should ask
if (setting == AutorunSetting.IGNORE)
return; // return right away
let success = false;
let app = null;
if (setting == AutorunSetting.RUN) {
app = Gio.app_info_get_default_for_type(type, false);
} else if (setting == AutorunSetting.FILES) {
app = Gio.app_info_get_default_for_type('inode/directory', false);
}
if (app)
success = startAppForMount(app, mount);
// we fallback here also in case the settings did not specify 'ask',
// but we failed launching the default app or the default file manager
if (!success)
this._addSource(mount, apps);
},
removeMount: function(mount) {
let source = this._getSourceForMount(mount);
// if we aren't tracking this mount, don't do anything
if (!source)
return;
// destroy the notification source
source.destroy();
}
}
function AutorunTransientSource(mount, apps) {
this._init(mount, apps);
}
AutorunTransientSource.prototype = {
__proto__: MessageTray.Source.prototype,
_init: function(mount, apps) {
MessageTray.Source.prototype._init.call(this, mount.get_name());
this.mount = mount;
this.apps = apps;
this._notification = new AutorunTransientNotification(this);
this._setSummaryIcon(this.createNotificationIcon(this.ICON_SIZE));
// add ourselves as a source, and popup the notification
Main.messageTray.add(this);
this.notify(this._notification);
},
createNotificationIcon: function(iconSize) {
return new St.Icon({ gicon: this.mount.get_icon(),
icon_size: iconSize ? iconSize : this.ICON_SIZE });
}
}
function AutorunTransientNotification(source) {
this._init(source);
}
AutorunTransientNotification.prototype = {
__proto__: MessageTray.Notification.prototype,
_init: function(source) {
MessageTray.Notification.prototype._init.call(this, source,
source.title, null,
{ customContent: true });
this._box = new St.BoxLayout({ style_class: 'hotplug-transient-box',
vertical: true });
this.addActor(this._box);
this._mount = source.mount;
source.apps.forEach(Lang.bind(this, function (app) {
let actor = this._buttonForApp(app);
if (actor)
this._box.add(actor, { x_fill: true,
x_align: St.Align.START });
}));
this._box.add(this._buttonForEject(), { x_fill: true,
x_align: St.Align.START });
// set the notification to transient and urgent, so that it
// expands out
this.setTransient(true);
this.setUrgency(MessageTray.Urgency.CRITICAL);
},
_buttonForApp: function(app) {
let box = new St.BoxLayout();
let icon = new St.Icon({ gicon: app.get_icon(),
style_class: 'hotplug-notification-item-icon' });
box.add(icon);
let label = new St.Bin({ y_align: St.Align.MIDDLE,
child: new St.Label
({ text: _("Open with %s").format(app.get_display_name()) })
});
box.add(label);
let button = new St.Button({ child: box,
x_fill: true,
x_align: St.Align.START,
button_mask: St.ButtonMask.ONE,
style_class: 'hotplug-notification-item' });
button.connect('clicked', Lang.bind(this, function() {
startAppForMount(app, this._mount);
this.destroy();
}));
return button;
},
_buttonForEject: function() {
let box = new St.BoxLayout();
let icon = new St.Icon({ icon_name: 'media-eject',
style_class: 'hotplug-notification-item-icon' });
box.add(icon);
let label = new St.Bin({ y_align: St.Align.MIDDLE,
child: new St.Label
({ text: _("Eject") })
});
box.add(label);
let button = new St.Button({ child: box,
x_fill: true,
x_align: St.Align.START,
button_mask: St.ButtonMask.ONE,
style_class: 'hotplug-notification-item' });
button.connect('clicked', Lang.bind(this, function() {
Main.autorunManager.ejectMount(this._mount);
}));
return button;
}
}

View File

@ -6,6 +6,7 @@ const Meta = imports.gi.Meta;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const POPUP_ANIMATION_TIME = 0.15;
@ -329,7 +330,7 @@ BoxPointer.prototype = {
// We also want to keep it onscreen, and separated from the
// edge by the same distance as the main part of the box is
// separated from its sourceActor
let primary = global.get_primary_monitor();
let primary = Main.layoutManager.primaryMonitor;
let themeNode = this.actor.get_theme_node();
let borderWidth = themeNode.get_length('-arrow-border-width');
let arrowBase = themeNode.get_length('-arrow-base');

View File

@ -8,6 +8,7 @@ const Signals = imports.signals;
const Main = imports.ui.main;
const Params = imports.misc.params;
const ScreenSaver = imports.misc.screenSaver;
// This manages the shell "chrome"; the UI that's visible in the
// normal mode (ie, outside the Overview), that surrounds the main
@ -35,8 +36,8 @@ Chrome.prototype = {
this._trackedActors = [];
global.screen.connect('monitors-changed',
Lang.bind(this, this._monitorsChanged));
Main.layoutManager.connect('monitors-changed',
Lang.bind(this, this._relayout));
global.screen.connect('restacked',
Lang.bind(this, this._windowsRestacked));
@ -49,9 +50,15 @@ Chrome.prototype = {
Main.overview.connect('hidden',
Lang.bind(this, this._overviewHidden));
this._updateMonitors();
this._updateFullscreen();
this._queueUpdateRegions();
this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy();
this._screenSaverProxy.connect('ActiveChanged', Lang.bind(this, this._onScreenSaverActiveChanged));
this._screenSaverProxy.GetActiveRemote(Lang.bind(this,
function(result, err) {
if (!err)
this._onScreenSaverActiveChanged(this._screenSaverProxy, result);
}));
this._relayout();
},
_allocated: function(actor, box, flags) {
@ -205,18 +212,18 @@ Chrome.prototype = {
this._queueUpdateRegions();
},
_updateMonitors: function() {
let monitors = global.get_monitors();
let primary = global.get_primary_monitor();
this._monitors = monitors;
for (let i = 0; i < monitors.length; i++) {
let monitor = monitors[i];
if (monitor.x == primary.x &&
monitor.y == primary.y &&
monitor.width == primary.width &&
monitor.height == primary.height)
this._primaryMonitor = monitor;
}
_relayout: function() {
this._monitors = Main.layoutManager.monitors;
this._primaryMonitor = Main.layoutManager.primaryMonitor;
this._updateFullscreen();
this._updateVisibility();
this._queueUpdateRegions();
},
_onScreenSaverActiveChanged: function(proxy, screenSaverActive) {
this.actor.visible = !screenSaverActive;
this._queueUpdateRegions();
},
_findMonitorForRect: function(x, y, w, h) {
@ -255,15 +262,6 @@ Chrome.prototype = {
return this._primaryMonitor; // Not on any monitor, pretend its on the primary
},
_monitorsChanged: function() {
this._updateMonitors();
// Update everything that depends on monitor positions
this._updateFullscreen();
this._updateVisibility();
this._queueUpdateRegions();
},
_queueUpdateRegions: function() {
if (!this._updateRegionIdle)
this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this._updateRegions),
@ -291,6 +289,11 @@ Chrome.prototype = {
for (let i = windows.length - 1; i > -1; i--) {
let window = windows[i];
// Skip minimized windows
if (!window.showing_on_its_workspace())
continue;
let layer = window.get_meta_window().get_layer();
if (layer == Meta.StackLayer.FULLSCREEN) {

View File

@ -153,14 +153,14 @@ CtrlAltTabPopup.prototype = {
},
_getPreferredWidth: function (actor, forHeight, alloc) {
let primary = global.get_primary_monitor();
let primary = Main.layoutManager.primaryMonitor;
alloc.min_size = primary.width;
alloc.natural_size = primary.width;
},
_getPreferredHeight: function (actor, forWidth, alloc) {
let primary = global.get_primary_monitor();
let primary = Main.layoutManager.primaryMonitor;
alloc.min_size = primary.height;
alloc.natural_size = primary.height;
@ -168,7 +168,7 @@ CtrlAltTabPopup.prototype = {
_allocate: function (actor, box, flags) {
let childBox = new Clutter.ActorBox();
let primary = global.get_primary_monitor();
let primary = Main.layoutManager.primaryMonitor;
let leftPadding = this.actor.get_theme_node().get_padding(St.Side.LEFT);
let vPadding = this.actor.get_theme_node().get_vertical_padding();

323
js/ui/layout.js Normal file
View File

@ -0,0 +1,323 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Signals = imports.signals;
const St = imports.gi.St;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
function LayoutManager() {
this._init.apply(this, arguments);
}
LayoutManager.prototype = {
_init: function () {
this._rtl = (St.Widget.get_default_direction() == St.TextDirection.RTL);
this.monitors = [];
this.primaryMonitor = null;
this.primaryIndex = -1;
this._hotCorners = [];
this._updateMonitors();
},
// This is called by Main after everything else is constructed;
// _updateHotCorners needs access to Main.panel, which didn't exist
// yet when the LayoutManager was constructed.
init: function() {
global.screen.connect('monitors-changed', Lang.bind(this, this._monitorsChanged));
this._updateHotCorners();
},
_updateMonitors: function() {
let screen = global.screen;
this.monitors = [];
let nMonitors = screen.get_n_monitors();
for (let i = 0; i < nMonitors; i++)
this.monitors.push(screen.get_monitor_geometry(i));
if (nMonitors == 1) {
this.primaryIndex = this.bottomIndex = 0;
} else {
// If there are monitors below the primary, then we need
// to split primary from bottom.
this.primaryIndex = this.bottomIndex = screen.get_primary_monitor();
for (let i = 0; i < this.monitors.length; i++) {
let monitor = this.monitors[i];
if (this._isAboveOrBelowPrimary(monitor)) {
if (monitor.y > this.monitors[this.bottomIndex].y)
this.bottomIndex = i;
}
}
}
this.primaryMonitor = this.monitors[this.primaryIndex];
this.bottomMonitor = this.monitors[this.bottomIndex];
},
_updateHotCorners: function() {
// destroy old hot corners
for (let i = 0; i < this._hotCorners.length; i++)
this._hotCorners[i].destroy();
this._hotCorners = [];
// build new hot corners
for (let i = 0; i < this.monitors.length; i++) {
if (i == this.primaryIndex)
continue;
let monitor = this.monitors[i];
let cornerX = this._rtl ? monitor.x + monitor.width : monitor.x;
let cornerY = monitor.y;
let haveTopLeftCorner = true;
// Check if we have a top left (right for RTL) corner.
// I.e. if there is no monitor directly above or to the left(right)
let besideX = this._rtl ? monitor.x + 1 : cornerX - 1;
let besideY = cornerY;
let aboveX = cornerX;
let aboveY = cornerY - 1;
for (let j = 0; j < this.monitors.length; j++) {
if (i == j)
continue;
let otherMonitor = this.monitors[j];
if (besideX >= otherMonitor.x &&
besideX < otherMonitor.x + otherMonitor.width &&
besideY >= otherMonitor.y &&
besideY < otherMonitor.y + otherMonitor.height) {
haveTopLeftCorner = false;
break;
}
if (aboveX >= otherMonitor.x &&
aboveX < otherMonitor.x + otherMonitor.width &&
aboveY >= otherMonitor.y &&
aboveY < otherMonitor.y + otherMonitor.height) {
haveTopLeftCorner = false;
break;
}
}
if (!haveTopLeftCorner)
continue;
let corner = new HotCorner();
this._hotCorners.push(corner);
corner.actor.set_position(cornerX, cornerY);
Main.chrome.addActor(corner.actor, { affectsStruts: false });
}
},
_monitorsChanged: function() {
this._updateMonitors();
this._updateHotCorners();
this.emit('monitors-changed');
},
_isAboveOrBelowPrimary: function(monitor) {
let primary = this.monitors[this.primaryIndex];
let monitorLeft = monitor.x, monitorRight = monitor.x + monitor.width;
let primaryLeft = primary.x, primaryRight = primary.x + primary.width;
if ((monitorLeft >= primaryLeft && monitorLeft <= primaryRight) ||
(monitorRight >= primaryLeft && monitorRight <= primaryRight) ||
(primaryLeft >= monitorLeft && primaryLeft <= monitorRight) ||
(primaryRight >= monitorLeft && primaryRight <= monitorRight))
return true;
return false;
},
get focusIndex() {
let screen = global.screen;
let display = screen.get_display();
let focusWindow = display.focus_window;
if (focusWindow) {
let wrect = focusWindow.get_outer_rect();
for (let i = 0; i < this.monitors.length; i++) {
let monitor = this.monitors[i];
if (monitor.x <= wrect.x && monitor.y <= wrect.y &&
monitor.x + monitor.width > wrect.x &&
monitor.y + monitor.height > wrect.y)
return i;
}
}
return this.primaryIndex;
},
get focusMonitor() {
return this.monitors[this.focusIndex];
}
};
Signals.addSignalMethods(LayoutManager.prototype);
// HotCorner:
//
// This class manages a "hot corner" that can toggle switching to
// overview.
function HotCorner() {
this._init();
}
HotCorner.prototype = {
_init : function() {
// We use this flag to mark the case where the user has entered the
// hot corner and has not left both the hot corner and a surrounding
// guard area (the "environs"). This avoids triggering the hot corner
// multiple times due to an accidental jitter.
this._entered = false;
this.actor = new Clutter.Group({ name: 'hot-corner-environs',
width: 3,
height: 3,
reactive: true });
this._corner = new Clutter.Rectangle({ name: 'hot-corner',
width: 1,
height: 1,
opacity: 0,
reactive: true });
this._corner._delegate = this;
this.actor.add_actor(this._corner);
if (St.Widget.get_default_direction() == St.TextDirection.RTL) {
this._corner.set_position(this.actor.width - this._corner.width, 0);
this.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
} else {
this._corner.set_position(0, 0);
}
this._activationTime = 0;
this.actor.connect('leave-event',
Lang.bind(this, this._onEnvironsLeft));
// Clicking on the hot corner environs should result in the
// same behavior as clicking on the hot corner.
this.actor.connect('button-release-event',
Lang.bind(this, this._onCornerClicked));
// In addition to being triggered by the mouse enter event,
// the hot corner can be triggered by clicking on it. This is
// useful if the user wants to undo the effect of triggering
// the hot corner once in the hot corner.
this._corner.connect('enter-event',
Lang.bind(this, this._onCornerEntered));
this._corner.connect('button-release-event',
Lang.bind(this, this._onCornerClicked));
this._corner.connect('leave-event',
Lang.bind(this, this._onCornerLeft));
},
destroy: function() {
this.actor.destroy();
},
_addRipple : function(delay, time, startScale, startOpacity, finalScale, finalOpacity) {
// We draw a ripple by using a source image and animating it scaling
// outwards and fading away. We want the ripples to move linearly
// or it looks unrealistic, but if the opacity of the ripple goes
// linearly to zero it fades away too quickly, so we use Tweener's
// 'onUpdate' to give a non-linear curve to the fade-away and make
// it more visible in the middle section.
let [x, y] = this._corner.get_transformed_position();
let ripple = new St.BoxLayout({ style_class: 'ripple-box',
opacity: 255 * Math.sqrt(startOpacity),
scale_x: startScale,
scale_y: startScale,
x: x,
y: y });
ripple._opacity = startOpacity;
if (ripple.get_direction() == St.TextDirection.RTL)
ripple.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
Tweener.addTween(ripple, { _opacity: finalOpacity,
scale_x: finalScale,
scale_y: finalScale,
delay: delay,
time: time,
transition: 'linear',
onUpdate: function() { ripple.opacity = 255 * Math.sqrt(ripple._opacity); },
onComplete: function() { ripple.destroy(); } });
Main.uiGroup.add_actor(ripple);
},
rippleAnimation: function() {
// Show three concentric ripples expanding outwards; the exact
// parameters were found by trial and error, so don't look
// for them to make perfect sense mathematically
// delay time scale opacity => scale opacity
this._addRipple(0.0, 0.83, 0.25, 1.0, 1.5, 0.0);
this._addRipple(0.05, 1.0, 0.0, 0.7, 1.25, 0.0);
this._addRipple(0.35, 1.0, 0.0, 0.3, 1, 0.0);
},
handleDragOver: function(source, actor, x, y, time) {
if (source != Main.xdndHandler)
return;
if (!Main.overview.visible && !Main.overview.animationInProgress) {
this.rippleAnimation();
Main.overview.showTemporarily();
Main.overview.beginItemDrag(actor);
}
},
_onCornerEntered : function() {
if (!this._entered) {
this._entered = true;
if (!Main.overview.animationInProgress) {
this._activationTime = Date.now() / 1000;
this.rippleAnimation();
Main.overview.toggle();
}
}
return false;
},
_onCornerClicked : function() {
if (this.shouldToggleOverviewOnClick())
Main.overview.toggle();
return true;
},
_onCornerLeft : function(actor, event) {
if (event.get_related() != this.actor)
this._entered = false;
// Consume event, otherwise this will confuse onEnvironsLeft
return true;
},
_onEnvironsLeft : function(actor, event) {
if (event.get_related() != this._corner)
this._entered = false;
return false;
},
// Checks if the Activities button is currently sensitive to
// clicks. The first call to this function within the
// HOT_CORNER_ACTIVATION_TIMEOUT time of the hot corner being
// triggered will return false. This avoids opening and closing
// the overview if the user both triggered the hot corner and
// clicked the Activities button.
shouldToggleOverviewOnClick: function() {
if (Main.overview.animationInProgress)
return false;
if (this._activationTime == 0 || Date.now() / 1000 - this._activationTime > HOT_CORNER_ACTIVATION_TIMEOUT)
return true;
return false;
}
};

View File

@ -436,7 +436,7 @@ Inspector.prototype = {
if (!this._eventHandler)
return;
let primary = global.get_primary_monitor();
let primary = Main.layoutManager.primaryMonitor;
let [minWidth, minHeight, natWidth, natHeight] =
this._eventHandler.get_preferred_size();
@ -907,7 +907,7 @@ LookingGlass.prototype = {
},
_resizeTo: function(actor) {
let primary = global.get_primary_monitor();
let primary = Main.layoutManager.primaryMonitor;
let myWidth = primary.width * 0.7;
let myHeight = primary.height * 0.7;
let [srcX, srcY] = actor.get_transformed_position();

View File

@ -12,6 +12,8 @@ const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const AutomountManager = imports.ui.automountManager;
const AutorunManager = imports.ui.autorunManager;
const Chrome = imports.ui.chrome;
const CtrlAltTab = imports.ui.ctrlAltTab;
const EndSessionDialog = imports.ui.endSessionDialog;
@ -23,6 +25,7 @@ const Overview = imports.ui.overview;
const Panel = imports.ui.panel;
const PlaceDisplay = imports.ui.placeDisplay;
const RunDialog = imports.ui.runDialog;
const Layout = imports.ui.layout;
const LookingGlass = imports.ui.lookingGlass;
const NotificationDaemon = imports.ui.notificationDaemon;
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
@ -38,6 +41,8 @@ const Util = imports.misc.util;
const DEFAULT_BACKGROUND_COLOR = new Clutter.Color();
DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff);
let automountManager = null;
let autorunManager = null;
let chrome = null;
let panel = null;
let hotCorners = [];
@ -59,6 +64,7 @@ let uiGroup = null;
let magnifier = null;
let xdndHandler = null;
let statusIconDispatcher = null;
let layoutManager = null;
let _errorLogStack = [];
let _startDate;
let _defaultCssStylesheet = null;
@ -126,6 +132,7 @@ function start() {
global.overlay_group.reparent(uiGroup);
global.stage.add_actor(uiGroup);
layoutManager = new Layout.LayoutManager();
placesManager = new PlaceDisplay.PlacesManager();
xdndHandler = new XdndHandler.XdndHandler();
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
@ -139,7 +146,10 @@ function start() {
notificationDaemon = new NotificationDaemon.NotificationDaemon();
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
telepathyClient = new TelepathyClient.Client();
automountManager = new AutomountManager.AutomountManager();
autorunManager = new AutorunManager.AutorunManager();
layoutManager.init();
overview.init();
statusIconDispatcher.start(messageTray.actor);
@ -178,14 +188,9 @@ function start() {
// Attempt to become a PolicyKit authentication agent
PolkitAuthenticationAgent.init()
global.screen.connect('monitors-changed', _relayout);
ExtensionSystem.init();
ExtensionSystem.loadExtensions();
// Perform initial relayout here
_relayout();
panel.startStatusArea();
panel.startupAnimation();
@ -300,14 +305,14 @@ function _windowRemoved(workspace, window) {
function _windowLeftMonitor(metaScreen, monitorIndex, metaWin) {
// If the window left the primary monitor, that
// might make that workspace empty
if (monitorIndex == global.get_primary_monitor_index())
if (monitorIndex == layoutManager.primaryIndex)
_queueCheckWorkspaces();
}
function _windowEnteredMonitor(metaScreen, monitorIndex, metaWin) {
// If the window entered the primary monitor, that
// might make that workspace non-empty
if (monitorIndex == global.get_primary_monitor_index())
if (monitorIndex == layoutManager.primaryIndex)
_queueCheckWorkspaces();
}
@ -481,80 +486,16 @@ function _getAndClearErrorStack() {
return errors;
}
function _relayout() {
let monitors = global.get_monitors();
// destroy old corners
for (let i = 0; i < hotCorners.length; i++)
hotCorners[i].destroy();
hotCorners = [];
let primary = global.get_primary_monitor();
for (let i = 0; i < monitors.length; i++) {
let monitor = monitors[i];
let isPrimary = (monitor.x == primary.x &&
monitor.y == primary.y &&
monitor.width == primary.width &&
monitor.height == primary.height);
let cornerX = monitor.x;
let cornerY = monitor.y;
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
cornerX += monitor.width;
let haveTopLeftCorner = true;
/* Check if we have a top left (right for RTL) corner.
* I.e. if there is no monitor directly above or to the left(right) */
let besideX;
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
besideX = monitor.x + 1;
else
besideX = cornerX - 1;
let besideY = cornerY;
let aboveX = cornerX;
let aboveY = cornerY - 1;
for (let j = 0; j < monitors.length; j++) {
if (i == j)
continue;
let otherMonitor = monitors[j];
if (besideX >= otherMonitor.x &&
besideX < otherMonitor.x + otherMonitor.width &&
besideY >= otherMonitor.y &&
besideY < otherMonitor.y + otherMonitor.height) {
haveTopLeftCorner = false;
break;
}
if (aboveX >= otherMonitor.x &&
aboveX < otherMonitor.x + otherMonitor.width &&
aboveY >= otherMonitor.y &&
aboveY < otherMonitor.y + otherMonitor.height) {
haveTopLeftCorner = false;
break;
}
}
/* We only want hot corners where there is a natural top-left
* corner, and on the primary monitor */
if (!isPrimary && !haveTopLeftCorner)
continue;
let corner = new Panel.HotCorner(isPrimary ? panel.button : null);
hotCorners.push(corner);
corner.actor.set_position(cornerX, cornerY);
if (isPrimary)
panel.setHotCorner(corner);
function logStackTrace(msg) {
try {
throw new Error();
} catch (e) {
// e.stack must have at least two lines, with the first being
// logStackTrace() (which we strip off), and the second being
// our caller.
let trace = e.stack.substr(e.stack.indexOf('\n') + 1);
log(msg ? (msg + '\n' + trace) : trace);
}
panel.relayout();
overview.relayout();
// To avoid updating the position and size of the workspaces
// in the overview, we just hide the overview. The positions
// will be updated when it is next shown.
overview.hide();
}
function isWindowActorDisplayedOnWorkspace(win, workspaceIndex) {

View File

@ -68,14 +68,19 @@ function _fixMarkup(text, allowMarkup) {
// Support &amp;, &quot;, &apos;, &lt; and &gt;, escape all other
// occurrences of '&'.
let _text = text.replace(/&(?!amp;|quot;|apos;|lt;|gt;)/g, '&amp;');
// Support <b>, <i>, and <u>, escape anything else
// so it displays as raw markup.
return _text.replace(/<(\/?[^biu]>|[^>\/][^>])/g, '&lt;$1');
} else {
// Escape everything
let _text = text.replace(/&/g, '&amp;');
return _text.replace(/</g, '&lt;');
_text = _text.replace(/<(?!\/?[biu]>)/g, '&lt;');
try {
Pango.parse_markup(_text, -1, '');
return _text;
} catch (e) {}
}
// !allowMarkup, or invalid markup
return GLib.markup_escape_text(text, -1);
}
function URLHighlighter(text, lineWrap, allowMarkup) {
@ -880,16 +885,87 @@ Source.prototype = {
_init: function(title) {
this.title = title;
this.actor = new Shell.GenericContainer();
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this.actor.connect('allocate', Lang.bind(this, this._allocate));
this.actor.connect('destroy', Lang.bind(this,
function() {
this._actorDestroyed = true;
}));
this._actorDestroyed = false;
this._counterLabel = new St.Label();
this._counterBin = new St.Bin({ style_class: 'summary-source-counter',
child: this._counterLabel });
this._counterBin.hide();
this._iconBin = new St.Bin({ width: this.ICON_SIZE,
height: this.ICON_SIZE,
x_fill: true,
y_fill: true });
this.actor.add_actor(this._iconBin);
this.actor.add_actor(this._counterBin);
this.isTransient = false;
this.isChat = false;
this.notifications = [];
},
_getPreferredWidth: function (actor, forHeight, alloc) {
let [min, nat] = this._iconBin.get_preferred_width(forHeight);
alloc.min_size = min; alloc.nat_size = nat;
},
_getPreferredHeight: function (actor, forWidth, alloc) {
let [min, nat] = this._iconBin.get_preferred_height(forWidth);
alloc.min_size = min; alloc.nat_size = nat;
},
_allocate: function(actor, box, flags) {
// the iconBin should fill our entire box
this._iconBin.allocate(box, flags);
let childBox = new Clutter.ActorBox();
let [minWidth, minHeight, naturalWidth, naturalHeight] = this._counterBin.get_preferred_size();
let direction = this.actor.get_direction();
if (direction == St.TextDirection.LTR) {
// allocate on the right in LTR
childBox.x1 = box.x2 - naturalWidth;
childBox.x2 = box.x2;
} else {
// allocate on the left in RTL
childBox.x1 = 0;
childBox.x2 = naturalWidth;
}
childBox.y1 = box.y2 - naturalHeight;
childBox.y2 = box.y2;
this._counterBin.allocate(childBox, flags);
},
_setCount: function(count, visible) {
if (isNaN(parseInt(count)))
throw new Error("Invalid notification count: " + count);
if (this._actorDestroyed)
return;
this._counterBin.visible = visible;
this._counterLabel.set_text(count.toString());
},
_updateCount: function() {
let count = this.notifications.length;
this._setCount(count, count > 1);
},
setTransient: function(isTransient) {
this.isTransient = isTransient;
},
@ -909,7 +985,7 @@ Source.prototype = {
// Unlike createNotificationIcon, this always returns the same actor;
// there is only one summary icon actor for a Source.
getSummaryIcon: function() {
return this._iconBin;
return this.actor;
},
pushNotification: function(notification) {
@ -928,7 +1004,11 @@ Source.prototype = {
this.notifications.splice(index, 1);
if (this.notifications.length == 0)
this._lastNotificationRemoved();
this._updateCount();
}));
this._updateCount();
},
notify: function(notification) {
@ -965,6 +1045,8 @@ Source.prototype = {
for (let i = this.notifications.length - 1; i >= 0; i--)
if (!this.notifications[i].resident)
this.notifications[i].destroy();
this._updateCount();
},
// Default implementation is to destroy this source, but subclasses can override
@ -1257,7 +1339,7 @@ MessageTray.prototype = {
Main.chrome.trackActor(this._notificationBin);
Main.chrome.trackActor(this._summaryBoxPointer.actor);
global.screen.connect('monitors-changed', Lang.bind(this, this._setSizePosition));
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._setSizePosition));
this._setSizePosition();
@ -1293,20 +1375,20 @@ MessageTray.prototype = {
},
_setSizePosition: function() {
let primary = global.get_primary_monitor();
this.actor.x = primary.x;
this.actor.y = primary.y + primary.height - 1;
this.actor.width = primary.width;
let monitor = Main.layoutManager.bottomMonitor;
this.actor.x = monitor.x;
this.actor.y = monitor.y + monitor.height - 1;
this.actor.width = monitor.width;
this._notificationBin.x = 0;
this._notificationBin.width = primary.width;
this._notificationBin.width = monitor.width;
this._summaryBin.x = 0;
this._summaryBin.width = primary.width;
this._summaryBin.width = monitor.width;
if (this._pointerBarrier)
global.destroy_pointer_barrier(this._pointerBarrier);
this._pointerBarrier =
global.create_pointer_barrier(primary.x + primary.width, primary.y + primary.height - this.actor.height,
primary.x + primary.width, primary.y + primary.height,
global.create_pointer_barrier(monitor.x + monitor.width, monitor.y + monitor.height - this.actor.height,
monitor.x + monitor.width, monitor.y + monitor.height,
4 /* BarrierNegativeX */);
@ -1867,18 +1949,18 @@ MessageTray.prototype = {
},
_showTray: function() {
let primary = global.get_primary_monitor();
let monitor = Main.layoutManager.bottomMonitor;
this._tween(this.actor, '_trayState', State.SHOWN,
{ y: primary.y + primary.height - this.actor.height,
{ y: monitor.y + monitor.height - this.actor.height,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
},
_hideTray: function() {
let primary = global.get_primary_monitor();
let monitor = Main.layoutManager.bottomMonitor;
this._tween(this.actor, '_trayState', State.HIDDEN,
{ y: primary.y + primary.height - 1,
{ y: monitor.y + monitor.height - 1,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
@ -2047,7 +2129,6 @@ MessageTray.prototype = {
},
_showSummary: function(timeout) {
let primary = global.get_primary_monitor();
this._summaryBin.opacity = 0;
this._summaryBin.y = this.actor.height;
this._tween(this._summaryBin, '_summaryState', State.SHOWN,

View File

@ -149,7 +149,7 @@ ModalDialog.prototype = {
},
_fadeOpen: function() {
let monitor = global.get_focus_monitor();
let monitor = Main.layoutManager.focusMonitor;
this._backgroundBin.set_position(monitor.x, monitor.y);
this._backgroundBin.set_size(monitor.width, monitor.height);

View File

@ -122,7 +122,7 @@ NotificationDaemon.prototype = {
} else if (hints['image-data']) {
let [width, height, rowStride, hasAlpha,
bitsPerSample, nChannels, data] = hints['image-data'];
return textureCache.load_from_raw(data, data.length, hasAlpha,
return textureCache.load_from_raw(data, hasAlpha,
width, height, rowStride, size);
} else {
let stockIcon;
@ -148,7 +148,7 @@ NotificationDaemon.prototype = {
//
// Either a pid or ndata.notification is needed to retrieve or
// create a source.
_getSource: function(title, pid, ndata) {
_getSource: function(title, pid, ndata, sender) {
if (!pid && !(ndata && ndata.notification))
return null;
@ -171,7 +171,7 @@ NotificationDaemon.prototype = {
return source;
}
let source = new Source(title, pid);
let source = new Source(title, pid, sender);
source.setTransient(isForTransientNotification);
if (!isForTransientNotification) {
@ -190,9 +190,11 @@ NotificationDaemon.prototype = {
actions, hints, timeout) {
let id;
// Filter out chat and presence notifications from Empathy, since we
// handle that information from telepathyClient.js
// Filter out chat, presence, calls and invitation notifications from
// Empathy, since we handle that information from telepathyClient.js
if (appName == 'Empathy' && (hints['category'] == 'im.received' ||
hints['category'] == 'x-empathy.im.room-invitation' ||
hints['category'] == 'x-empathy.call.incoming' ||
hints['category'] == 'presence.online' ||
hints['category'] == 'presence.offline')) {
// Ignore replacesId since we already sent back a
@ -244,7 +246,7 @@ NotificationDaemon.prototype = {
let sender = DBus.getCurrentMessageContext().sender;
let pid = this._senderToPid[sender];
let source = this._getSource(appName, pid, ndata);
let source = this._getSource(appName, pid, ndata, sender);
if (source) {
this._notifyForSource(source, ndata);
@ -265,7 +267,7 @@ NotificationDaemon.prototype = {
if (!ndata)
return;
source = this._getSource(appName, pid, ndata);
source = this._getSource(appName, pid, ndata, sender);
// We only store sender-pid entries for persistent sources.
// Removing the entries once the source is destroyed
@ -414,7 +416,7 @@ NotificationDaemon.prototype = {
},
_onTrayIconAdded: function(o, icon) {
let source = this._getSource(icon.title || icon.wm_class || _("Unknown"), icon.pid, null);
let source = this._getSource(icon.title || icon.wm_class || _("Unknown"), icon.pid, null, null);
source.setTrayIcon(icon);
},
@ -427,18 +429,28 @@ NotificationDaemon.prototype = {
DBus.conformExport(NotificationDaemon.prototype, NotificationDaemonIface);
function Source(title, pid) {
this._init(title, pid);
function Source(title, pid, sender) {
this._init(title, pid, sender);
}
Source.prototype = {
__proto__: MessageTray.Source.prototype,
_init: function(title, pid) {
_init: function(title, pid, sender) {
MessageTray.Source.prototype._init.call(this, title);
this._pid = pid;
this._appStateChangedId = 0;
if (sender)
// TODO: dbus-glib implementation of watch_name() doesnt return an id to be used for
// unwatch_name() or implement unwatch_name(), however when we move to using GDBus implementation,
// we should save the id here and call unwatch_name() with it in destroy().
// Moving to GDBus is the work in progress: https://bugzilla.gnome.org/show_bug.cgi?id=648651
// and https://bugzilla.gnome.org/show_bug.cgi?id=622921 .
DBus.session.watch_name(sender,
false,
null,
Lang.bind(this, this._onNameVanished));
this._setApp();
if (this.app)
this.title = this.app.get_name();
@ -447,6 +459,16 @@ Source.prototype = {
this._trayIcon = null;
},
_onNameVanished: function() {
// Destroy the notification source when its sender is removed from DBus.
// Only do so if this.app is set to avoid removing "notify-send" sources, senders
// of which аre removed from DBus immediately.
// Sender being removed from DBus would normally result in a tray icon being removed,
// so allow the code path that handles the tray icon being removed to handle that case.
if (!this.trayIcon && this.app)
this.destroy();
},
processNotification: function(notification, icon) {
if (!this.app)
this._setApp();
@ -499,10 +521,6 @@ Source.prototype = {
if (!this.app)
return;
// We only update the app if this.app is null, so we can't disconnect the old this._appStateChangedId
// even if it were non-zero for some reason.
this._appStateChangedId = this.app.connect('notify::state', Lang.bind(this, this._appStateChanged));
// Only override the icon if we were previously using
// notification-based icons (ie, not a trayicon) or if it was unset before
if (!this._trayIcon) {
@ -527,19 +545,6 @@ Source.prototype = {
this.destroy();
},
_appStateChanged: function() {
// Destroy notification sources when their apps exit.
// The app exiting would normally result in a tray icon being removed,
// so the associated source would be destroyed through the code path
// that handles the tray icon being removed. We should not destroy
// the source associated with a tray icon when the application state
// is Shell.AppState.STOPPED because running applications that have
// no open windows would also have that state. This is often the case
// for applications that use tray icons.
if (!this._trayIcon && this.app.get_state() == Shell.AppState.STOPPED)
this.destroy();
},
openApp: function() {
if (this.app == null)
return;
@ -552,10 +557,6 @@ Source.prototype = {
},
destroy: function() {
if (this.app && this._appStateChangedId) {
this.app.disconnect(this._appStateChangedId);
this._appStateChangedId = 0;
}
MessageTray.Source.prototype.destroy.call(this);
}
};

View File

@ -121,7 +121,7 @@ Overview.prototype = {
let spacing = node.get_length('spacing');
if (spacing != this._spacing) {
this._spacing = spacing;
this.relayout();
this._relayout();
}
}));
@ -202,6 +202,8 @@ Overview.prototype = {
// the left of the overview
Main.ctrlAltTabManager.addGroup(this.dash.actor, _("Dash"), 'user-bookmarks');
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
this._relayout();
},
_onDragBegin: function() {
@ -391,7 +393,7 @@ Overview.prototype = {
[stageX, stageY] = event.get_coords();
let dx = this._dragX - stageX;
let dy = this._dragY - stageY;
let primary = global.get_primary_monitor();
let primary = Main.layoutManager.primaryMonitor;
this._dragX = stageX;
this._dragY = stageY;
@ -438,8 +440,13 @@ Overview.prototype = {
return clone;
},
relayout: function () {
let primary = global.get_primary_monitor();
_relayout: function () {
// To avoid updating the position and size of the workspaces
// we just hide the overview. The positions will be updated
// when it is next shown.
this.hide();
let primary = Main.layoutManager.primaryMonitor;
let rtl = (St.Widget.get_default_direction () == St.TextDirection.RTL);
let contentY = Main.panel.actor.height;

View File

@ -12,6 +12,7 @@ const Signals = imports.signals;
const Config = imports.misc.config;
const CtrlAltTab = imports.ui.ctrlAltTab;
const Layout = imports.ui.layout;
const Overview = imports.ui.overview;
const PopupMenu = imports.ui.popupMenu;
const PanelMenu = imports.ui.panelMenu;
@ -24,8 +25,6 @@ const PANEL_ICON_SIZE = 24;
const STARTUP_ANIMATION_TIME = 0.2;
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
@ -544,13 +543,163 @@ AppMenuButton.prototype = {
Signals.addSignalMethods(AppMenuButton.prototype);
// Activities button. Because everything else in the top bar is a
// PanelMenu.Button, it simplifies some things to make this be one too.
// We just hack it up to not actually have a menu attached to it.
function ActivitiesButton() {
this._init.apply(this, arguments);
}
function PanelCorner(side) {
this._init(side);
ActivitiesButton.prototype = {
__proto__: PanelMenu.Button.prototype,
_init: function() {
PanelMenu.Button.prototype._init.call(this, 0.0);
let container = new Shell.GenericContainer();
container.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
container.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
container.connect('allocate', Lang.bind(this, this._allocate));
this.actor.child = container;
this.actor.name = 'panelActivities';
/* Translators: If there is no suitable word for "Activities"
in your language, you can use the word for "Overview". */
this._label = new St.Label({ text: _("Activities") });
container.add_actor(this._label);
this._hotCorner = new Layout.HotCorner();
container.add_actor(this._hotCorner.actor);
// Hack up our menu...
this.menu.open = Lang.bind(this, this._onMenuOpenRequest);
this.menu.close = Lang.bind(this, this._onMenuCloseRequest);
this.menu.toggle = Lang.bind(this, this._onMenuToggleRequest);
this.actor.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
this.actor.connect_after('button-release-event', Lang.bind(this, this._onButtonRelease));
this.actor.connect_after('key-release-event', Lang.bind(this, this._onKeyRelease));
Main.overview.connect('showing', Lang.bind(this, function() {
this.actor.add_style_pseudo_class('overview');
this._escapeMenuGrab();
}));
Main.overview.connect('hiding', Lang.bind(this, function() {
this.actor.remove_style_pseudo_class('overview');
this._escapeMenuGrab();
}));
this._xdndTimeOut = 0;
},
_getPreferredWidth: function(actor, forHeight, alloc) {
[alloc.min_size, alloc.natural_size] = this._label.get_preferred_width(forHeight);
},
_getPreferredHeight: function(actor, forWidth, alloc) {
[alloc.min_size, alloc.natural_size] = this._label.get_preferred_height(forWidth);
},
_allocate: function(actor, box, flags) {
this._label.allocate(box, flags);
// The hot corner needs to be outside any padding/alignment
// that has been imposed on us
let primary = Main.layoutManager.primaryMonitor;
let hotBox = new Clutter.ActorBox();
let ok, x, y;
if (actor.get_direction() == St.TextDirection.LTR) {
[ok, x, y] = actor.transform_stage_point(primary.x, primary.y)
} else {
[ok, x, y] = actor.transform_stage_point(primary.x + primary.width, primary.y);
// hotCorner.actor has northeast gravity, so we don't need
// to adjust x for its width
}
hotBox.x1 = Math.round(x);
hotBox.x2 = hotBox.x1 + this._hotCorner.actor.width;
hotBox.y1 = Math.round(y);
hotBox.y2 = hotBox.y1 + this._hotCorner.actor.height;
this._hotCorner.actor.allocate(hotBox, flags);
},
handleDragOver: function(source, actor, x, y, time) {
if (source != Main.xdndHandler)
return;
if (this._xdndTimeOut != 0)
Mainloop.source_remove(this._xdndTimeOut);
this._xdndTimeOut = Mainloop.timeout_add(BUTTON_DND_ACTIVATION_TIMEOUT,
Lang.bind(this, this._xdndShowOverview, actor));
},
_escapeMenuGrab: function() {
if (this.menu.isOpen)
this.menu.close();
},
_onCapturedEvent: function(actor, event) {
if (event.type() == Clutter.EventType.BUTTON_PRESS) {
if (!this._hotCorner.shouldToggleOverviewOnClick())
return true;
}
return false;
},
_onMenuOpenRequest: function() {
this.menu.isOpen = true;
this.menu.emit('open-state-changed', true);
},
_onMenuCloseRequest: function() {
this.menu.isOpen = false;
this.menu.emit('open-state-changed', false);
},
_onMenuToggleRequest: function() {
this.menu.isOpen = !this.menu.isOpen;
this.menu.emit('open-state-changed', this.menu.isOpen);
},
_onButtonRelease: function() {
if (this.menu.isOpen) {
this.menu.close();
Main.overview.toggle();
}
},
_onKeyRelease: function(actor, event) {
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_space) {
if (this.menu.isOpen)
this.menu.close();
Main.overview.toggle();
}
},
_xdndShowOverview: function(actor) {
let [x, y, mask] = global.get_pointer();
let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
if (pickedActor == this.actor) {
if (!Main.overview.visible && !Main.overview.animationInProgress) {
Main.overview.showTemporarily();
Main.overview.beginItemDrag(actor);
}
}
Mainloop.source_remove(this._xdndTimeOut);
this._xdndTimeOut = 0;
}
};
function PanelCorner(panel, side) {
this._init(panel, side);
}
PanelCorner.prototype = {
_init: function(side) {
_init: function(panel, side) {
this._panel = panel;
this._side = side;
this.actor = new St.DrawingArea({ style_class: 'panel-corner' });
this.actor.connect('repaint', Lang.bind(this, this._repaint));
@ -626,185 +775,14 @@ PanelCorner.prototype = {
this.actor.set_size(cornerRadius,
innerBorderWidth + cornerRadius);
if (this._side == St.Side.LEFT)
this.actor.set_position(Main.panel.actor.x,
Main.panel.actor.y + Main.panel.actor.height - innerBorderWidth);
this.actor.set_position(this._panel.actor.x,
this._panel.actor.y + this._panel.actor.height - innerBorderWidth);
else
this.actor.set_position(Main.panel.actor.x + Main.panel.actor.width - cornerRadius,
Main.panel.actor.y + Main.panel.actor.height - innerBorderWidth);
this.actor.set_position(this._panel.actor.x + this._panel.actor.width - cornerRadius,
this._panel.actor.y + this._panel.actor.height - innerBorderWidth);
}
};
/**
* HotCorner:
*
* This class manages the "hot corner" that can toggle switching to
* overview.
*/
function HotCorner(button) {
this._init(button);
}
HotCorner.prototype = {
_init : function(button) {
// This is the activities button associated with this hot corner,
// if this is on the primary monitor (or null with the corner is
// on a different monitor)
this._button = button;
// We use this flag to mark the case where the user has entered the
// hot corner and has not left both the hot corner and a surrounding
// guard area (the "environs"). This avoids triggering the hot corner
// multiple times due to an accidental jitter.
this._entered = false;
this.actor = new Clutter.Group({ width: 3,
height: 3,
reactive: true });
this._corner = new Clutter.Rectangle({ width: 1,
height: 1,
opacity: 0,
reactive: true });
this.actor.add_actor(this._corner);
if (St.Widget.get_default_direction() == St.TextDirection.RTL) {
this._corner.set_position(this.actor.width - this._corner.width, 0);
this.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
} else {
this._corner.set_position(0, 0);
}
this._activationTime = 0;
this.actor.connect('enter-event',
Lang.bind(this, this._onEnvironsEntered));
this.actor.connect('leave-event',
Lang.bind(this, this._onEnvironsLeft));
// Clicking on the hot corner environs should result in the same bahavior
// as clicking on the hot corner.
this.actor.connect('button-release-event',
Lang.bind(this, this._onCornerClicked));
// In addition to being triggered by the mouse enter event, the hot corner
// can be triggered by clicking on it. This is useful if the user wants to
// undo the effect of triggering the hot corner once in the hot corner.
this._corner.connect('enter-event',
Lang.bind(this, this._onCornerEntered));
this._corner.connect('button-release-event',
Lang.bind(this, this._onCornerClicked));
this._corner.connect('leave-event',
Lang.bind(this, this._onCornerLeft));
this._corner._delegate = this._corner;
this._corner.handleDragOver = Lang.bind(this,
function(source, actor, x, y, time) {
if (source == Main.xdndHandler) {
if(!Main.overview.visible && !Main.overview.animationInProgress) {
this.rippleAnimation();
Main.overview.showTemporarily();
Main.overview.beginItemDrag(actor);
}
}
});
Main.chrome.addActor(this.actor, { affectsStruts: false });
},
destroy: function() {
this.actor.destroy();
},
_addRipple : function(delay, time, startScale, startOpacity, finalScale, finalOpacity) {
// We draw a ripple by using a source image and animating it scaling
// outwards and fading away. We want the ripples to move linearly
// or it looks unrealistic, but if the opacity of the ripple goes
// linearly to zero it fades away too quickly, so we use Tweener's
// 'onUpdate' to give a non-linear curve to the fade-away and make
// it more visible in the middle section.
let [x, y] = this._corner.get_transformed_position();
let ripple = new St.BoxLayout({ style_class: 'ripple-box',
opacity: 255 * Math.sqrt(startOpacity),
scale_x: startScale,
scale_y: startScale,
x: x,
y: y });
ripple._opacity = startOpacity;
if (ripple.get_direction() == St.TextDirection.RTL)
ripple.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
Tweener.addTween(ripple, { _opacity: finalOpacity,
scale_x: finalScale,
scale_y: finalScale,
delay: delay,
time: time,
transition: 'linear',
onUpdate: function() { ripple.opacity = 255 * Math.sqrt(ripple._opacity); },
onComplete: function() { ripple.destroy(); } });
Main.uiGroup.add_actor(ripple);
},
rippleAnimation: function() {
// Show three concentric ripples expanding outwards; the exact
// parameters were found by trial and error, so don't look
// for them to make perfect sense mathematically
// delay time scale opacity => scale opacity
this._addRipple(0.0, 0.83, 0.25, 1.0, 1.5, 0.0);
this._addRipple(0.05, 1.0, 0.0, 0.7, 1.25, 0.0);
this._addRipple(0.35, 1.0, 0.0, 0.3, 1, 0.0);
},
_onEnvironsEntered : function() {
if (this._button)
this._button.hover = true;
},
_onCornerEntered : function() {
if (!this._entered) {
this._entered = true;
if (!Main.overview.animationInProgress) {
this._activationTime = Date.now() / 1000;
this.rippleAnimation();
Main.overview.toggle();
}
}
return false;
},
_onCornerClicked : function() {
if (!Main.overview.animationInProgress)
this.maybeToggleOverviewOnClick();
return true;
},
_onCornerLeft : function(actor, event) {
if (event.get_related() != this.actor)
this._entered = false;
// Consume event, otherwise this will confuse onEnvironsLeft
return true;
},
_onEnvironsLeft : function(actor, event) {
if (this._button)
this._button.hover = false;
if (event.get_related() != this._corner)
this._entered = 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._activationTime == 0 || Date.now() / 1000 - this._activationTime > HOT_CORNER_ACTIVATION_TIMEOUT)
Main.overview.toggle();
this._activationTime = 0;
}
}
function Panel() {
this._init();
@ -834,8 +812,8 @@ Panel.prototype = {
this._centerBox = new St.BoxLayout({ name: 'panelCenter' });
this._rightBox = new St.BoxLayout({ name: 'panelRight' });
this._leftCorner = new PanelCorner(St.Side.LEFT);
this._rightCorner = new PanelCorner(St.Side.RIGHT);
this._leftCorner = new PanelCorner(this, St.Side.LEFT);
this._rightCorner = new PanelCorner(this, St.Side.RIGHT);
/* This box container ensures that the centerBox is positioned in the *absolute*
* center, but can be pushed aside if necessary. */
@ -909,32 +887,16 @@ Panel.prototype = {
}));
/* 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.Button({ name: 'panelActivities',
style_class: 'panel-button',
reactive: true,
can_focus: true });
this._activities = this.button;
this.button.set_child(label);
this.button._delegate = this.button;
this.button._xdndTimeOut = 0;
this.button.handleDragOver = Lang.bind(this,
function(source, actor, x, y, time) {
if (source == Main.xdndHandler) {
if (this.button._xdndTimeOut != 0)
Mainloop.source_remove(this.button._xdndTimeOut);
this.button._xdndTimeOut = Mainloop.timeout_add(BUTTON_DND_ACTIVATION_TIMEOUT,
Lang.bind(this,
function() {
this._xdndShowOverview(actor);
}));
}
});
this._leftBox.add(this.button);
this._activitiesButton = new ActivitiesButton();
this._activities = this._activitiesButton.actor;
this._leftBox.add(this._activities);
// Synchronize the buttons pseudo classes with its corner
this.button.connect('style-changed', Lang.bind(this,
// The activities button has a pretend menu, so as to integrate
// more cleanly with the rest of the panel
this._menus.addMenu(this._activitiesButton.menu);
// Synchronize the button's pseudo classes with its corner
this._activities.connect('style-changed', Lang.bind(this,
function(actor) {
let rtl = actor.get_direction() == St.TextDirection.RTL;
let corner = rtl ? this._rightCorner : this._leftCorner;
@ -942,8 +904,6 @@ Panel.prototype = {
corner.actor.set_style_pseudo_class(pseudoClass);
}));
this._hotCorner = null;
this._appMenu = new AppMenuButton();
this._leftBox.add(this._appMenu.actor);
@ -982,28 +942,6 @@ Panel.prototype = {
Main.statusIconDispatcher.connect('status-icon-added', Lang.bind(this, this._onTrayIconAdded));
Main.statusIconDispatcher.connect('status-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
// 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) {
if (!Main.overview.animationInProgress) {
this._hotCorner.maybeToggleOverviewOnClick();
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.checked = true;
}));
Main.overview.connect('hiding', Lang.bind(this, function() {
this.button.checked = false;
}));
Main.chrome.addActor(this.actor);
Main.chrome.addActor(this._leftCorner.actor, { affectsStruts: false,
affectsInputRegion: false });
@ -1012,33 +950,9 @@ Panel.prototype = {
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'start-here',
{ sortGroup: CtrlAltTab.SortGroup.TOP });
},
_xdndShowOverview: function (actor) {
let [x, y, mask] = global.get_pointer();
let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
if (pickedActor != this.button) {
Mainloop.source_remove(this.button._xdndTimeOut);
this.button._xdndTimeOut = 0;
return;
}
if(!Main.overview.visible && !Main.overview.animationInProgress) {
Main.overview.showTemporarily();
Main.overview.beginItemDrag(actor);
}
Mainloop.source_remove(this.button._xdndTimeOut);
this.button._xdndTimeOut = 0;
},
// While there can be multiple hotcorners (one per monitor), the hot corner
// that is on top of the Activities button is special since it needs special
// coordination with clicking on that button
setHotCorner: function(corner) {
this._hotCorner = corner;
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
this._relayout();
},
startStatusArea: function() {
@ -1085,8 +999,8 @@ Panel.prototype = {
});
},
relayout: function() {
let primary = global.get_primary_monitor();
_relayout: function() {
let primary = Main.layoutManager.primaryMonitor;
this.actor.set_position(primary.x, primary.y);
this.actor.set_size(primary.width, -1);

View File

@ -35,7 +35,7 @@ Button.prototype = {
// Setting the max-height won't do any good if the minimum height of the
// menu is higher then the screen; it's useful if part of the menu is
// scrollable so the minimum height is smaller than the natural height
let monitor = global.get_primary_monitor();
let monitor = Main.layoutManager.primaryMonitor;
this.menu.actor.style = ('max-height: ' +
Math.round(monitor.height - Main.panel.actor.height) +
'px;');

View File

@ -711,7 +711,6 @@ PopupSwitchMenuItem.prototype = {
this._statusLabel = new St.Label({ text: '',
style_class: 'popup-inactive-menu-item'
});
this._switch = new Switch(false);
this._statusBin.child = this._switch.actor;
},
@ -947,6 +946,14 @@ PopupMenuBase.prototype = {
});
},
get firstMenuItem() {
let items = this._getMenuItems();
if (items.length)
return items[0];
else
return null;
},
removeAll: function() {
let children = this._getMenuItems();
for (let i = 0; i < children.length; i++) {

View File

@ -333,33 +333,46 @@ __proto__: ModalDialog.ModalDialog.prototype,
if (GLib.file_test(path, GLib.FileTest.EXISTS)) {
let file = Gio.file_new_for_path(path);
Gio.app_info_launch_default_for_uri(file.get_uri(),
global.create_app_launch_context());
} else {
this._commandError = true;
this._errorMessage.set_text(e.message);
if (!this._errorBox.visible) {
let [errorBoxMinHeight, errorBoxNaturalHeight] = this._errorBox.get_preferred_height(-1);
let parentActor = this._errorBox.get_parent();
Tweener.addTween(parentActor,
{ height: parentActor.height + errorBoxNaturalHeight,
time: DIALOG_GROW_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
parentActor.set_height(-1);
this._errorBox.show();
})
});
try {
Gio.app_info_launch_default_for_uri(file.get_uri(),
global.create_app_launch_context());
} catch (e) {
// The exception from gjs contains an error string like:
// Error invoking Gio.app_info_launch_default_for_uri: No application
// is registered as handling this file
// We are only interested in the part after the first colon.
let message = e.message.replace(/[^:]*: *(.+)/, '$1');
this._showError(message);
}
} else {
this._showError(e.message);
}
}
}
},
_showError : function(message) {
this._commandError = true;
this._errorMessage.set_text(message);
if (!this._errorBox.visible) {
let [errorBoxMinHeight, errorBoxNaturalHeight] = this._errorBox.get_preferred_height(-1);
let parentActor = this._errorBox.get_parent();
Tweener.addTween(parentActor,
{ height: parentActor.height + errorBoxNaturalHeight,
time: DIALOG_GROW_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
parentActor.set_height(-1);
this._errorBox.show();
})
});
}
},
open: function() {
this._history.lastItem();
this._errorBox.hide();

View File

@ -3,10 +3,11 @@
const DBus = imports.dbus;
const Gio = imports.gi.Gio;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Main = imports.ui.main;
// This module provides functionality for driving the shell user interface
// in an automated fashion. The primary current use case for this is
// automated performance testing (see runPerfScript()), but it could
@ -246,18 +247,14 @@ function _collect(scriptModule, outputFile) {
Shell.write_string_to_stream(out, '"events":\n');
Shell.PerfLog.get_default().dump_events(out);
let monitors = global.get_monitors();
let primary = global.get_primary_monitor();
let monitors = Main.layoutManager.monitors;
let primary = Main.layoutManager.primaryIndex;
Shell.write_string_to_stream(out, ',\n"monitors":\n[');
for (let i = 0; i < monitors.length; i++) {
let monitor = monitors[i];
let is_primary = (monitor.x == primary.x &&
monitor.y == primary.y &&
monitor.width == primary.width &&
monitor.height == primary.height);
if (i != 0)
Shell.write_string_to_stream(out, ', ');
Shell.write_string_to_stream(out, '"%s%dx%d+%d+%d"'.format(is_primary ? "*" : "",
Shell.write_string_to_stream(out, '"%s%dx%d+%d+%d"'.format(i == primary ? "*" : "",
monitor.width, monitor.height,
monitor.x, monitor.y));
}

View File

@ -0,0 +1,405 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Lang = imports.lang;
const Signals = imports.signals;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Pango = imports.gi.Pango;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const ModalDialog = imports.ui.modalDialog;
const Params = imports.misc.params;
const LIST_ITEM_ICON_SIZE = 48;
/* ------ Common Utils ------- */
function _setLabelText(label, text) {
if (text) {
label.set_text(text);
label.show();
} else {
label.set_text('');
label.hide();
}
}
function _setButtonsForChoices(dialog, choices) {
let buttons = [];
for (let idx = 0; idx < choices.length; idx++) {
let button = idx;
buttons.unshift({ label: choices[idx],
action: Lang.bind(dialog, function() {
dialog.emit('response', button);
})});
}
dialog.setButtons(buttons);
}
function _setLabelsForMessage(dialog, message) {
let labels = message.split('\n');
_setLabelText(dialog.subjectLabel, labels[0]);
if (labels.length > 1)
_setLabelText(dialog.descriptionLabel, labels[1]);
}
/* -------------------------------------------------------- */
function ListItem(app) {
this._init(app);
}
ListItem.prototype = {
_init: function(app) {
this._app = app;
let layout = new St.BoxLayout({ vertical: false});
this.actor = new St.Button({ style_class: 'show-processes-dialog-app-list-item',
can_focus: true,
child: layout,
reactive: true,
x_align: St.Align.START,
x_fill: true });
this._icon = this._app.create_icon_texture(LIST_ITEM_ICON_SIZE);
let iconBin = new St.Bin({ style_class: 'show-processes-dialog-app-list-item-icon',
child: this._icon });
layout.add(iconBin);
this._nameLabel = new St.Label({ text: this._app.get_name(),
style_class: 'show-processes-dialog-app-list-item-name' });
let labelBin = new St.Bin({ y_align: St.Align.MIDDLE,
child: this._nameLabel });
layout.add(labelBin);
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
},
_onClicked: function() {
this.emit('activate');
this._app.activate(-1);
}
};
Signals.addSignalMethods(ListItem.prototype);
function ShellMountOperation(source, params) {
this._init(source, params);
}
ShellMountOperation.prototype = {
_init: function(source, params) {
params = Params.parse(params, { reaskPassword: false });
this._reaskPassword = params.reaskPassword;
this._dialog = null;
this._processesDialog = null;
this.mountOp = new Shell.MountOperation();
this.mountOp.connect('ask-question',
Lang.bind(this, this._onAskQuestion));
this.mountOp.connect('ask-password',
Lang.bind(this, this._onAskPassword));
this.mountOp.connect('show-processes-2',
Lang.bind(this, this._onShowProcesses2));
this.mountOp.connect('aborted',
Lang.bind(this, this._onAborted));
this._icon = new St.Icon({ gicon: source.get_icon(),
style_class: 'shell-mount-operation-icon' });
},
_onAskQuestion: function(op, message, choices) {
this._dialog = new ShellMountQuestionDialog(this._icon);
this._dialog.connect('response',
Lang.bind(this, function(object, choice) {
this.mountOp.set_choice(choice);
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
this._dialog.close(global.get_current_time());
this._dialog = null;
}));
this._dialog.update(message, choices);
this._dialog.open(global.get_current_time());
},
_onAskPassword: function(op, message) {
this._notificationShowing = true;
this._source = new ShellMountPasswordSource(message, this._icon, this._reaskPassword);
this._source.connect('password-ready',
Lang.bind(this, function(source, password) {
this.mountOp.set_password(password);
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
this._notificationShowing = false;
this._source.destroy();
}));
this._source.connect('destroy',
Lang.bind(this, function() {
if (!this._notificationShowing)
return;
this._notificationShowing = false;
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
}));
},
_onAborted: function(op) {
if (!this._dialog)
return;
this._dialog.close(global.get_current_time());
this._dialog = null;
},
_onShowProcesses2: function(op) {
let processes = op.get_show_processes_pids();
let choices = op.get_show_processes_choices();
let message = op.get_show_processes_message();
if (!this._processesDialog) {
this._processesDialog = new ShellProcessesDialog(this._icon);
this._dialog = this._processesDialog;
this._processesDialog.connect('response',
Lang.bind(this, function(object, choice) {
if (choice == -1) {
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
} else {
this.mountOp.set_choice(choice);
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
}
this._processesDialog.close(global.get_current_time());
this._dialog = null;
}));
this._processesDialog.open(global.get_current_time());
}
this._processesDialog.update(message, processes, choices);
},
}
function ShellMountQuestionDialog(icon) {
this._init(icon);
}
ShellMountQuestionDialog.prototype = {
__proto__: ModalDialog.ModalDialog.prototype,
_init: function(icon) {
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'mount-question-dialog' });
let mainContentLayout = new St.BoxLayout();
this.contentLayout.add(mainContentLayout, { x_fill: true,
y_fill: false });
this._iconBin = new St.Bin({ child: icon });
mainContentLayout.add(this._iconBin,
{ x_fill: true,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
let messageLayout = new St.BoxLayout({ vertical: true });
mainContentLayout.add(messageLayout,
{ y_align: St.Align.START });
this.subjectLabel = new St.Label({ style_class: 'mount-question-dialog-subject' });
messageLayout.add(this.subjectLabel,
{ y_fill: false,
y_align: St.Align.START });
this.descriptionLabel = new St.Label({ style_class: 'mount-question-dialog-description' });
this.descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this.descriptionLabel.clutter_text.line_wrap = true;
messageLayout.add(this.descriptionLabel,
{ y_fill: true,
y_align: St.Align.START });
},
update: function(message, choices) {
_setLabelsForMessage(this, message);
_setButtonsForChoices(this, choices);
}
}
Signals.addSignalMethods(ShellMountQuestionDialog.prototype);
function ShellMountPasswordSource(message, icon, reaskPassword) {
this._init(message, icon, reaskPassword);
}
ShellMountPasswordSource.prototype = {
__proto__: MessageTray.Source.prototype,
_init: function(message, icon, reaskPassword) {
let strings = message.split('\n');
MessageTray.Source.prototype._init.call(this, strings[0]);
this._notification = new ShellMountPasswordNotification(this, strings, icon, reaskPassword);
// add ourselves as a source, and popup the notification
Main.messageTray.add(this);
this.notify(this._notification);
},
}
Signals.addSignalMethods(ShellMountPasswordSource.prototype);
function ShellMountPasswordNotification(source, strings, icon, reaskPassword) {
this._init(source, strings, icon, reaskPassword);
}
ShellMountPasswordNotification.prototype = {
__proto__: MessageTray.Notification.prototype,
_init: function(source, strings, icon, reaskPassword) {
MessageTray.Notification.prototype._init.call(this, source,
strings[0], null,
{ customContent: true,
icon: icon });
// set the notification to transient and urgent, so that it
// expands out
this.setTransient(true);
this.setUrgency(MessageTray.Urgency.CRITICAL);
if (strings[1])
this.addBody(strings[1]);
if (reaskPassword) {
let label = new St.Label({ style_class: 'mount-password-reask',
text: _("Wrong password, please try again") });
this.addActor(label);
}
this._responseEntry = new St.Entry({ style_class: 'mount-password-entry',
can_focus: true });
this.setActionArea(this._responseEntry);
this._responseEntry.clutter_text.connect('activate',
Lang.bind(this, this._onEntryActivated));
this._responseEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
this._responseEntry.grab_key_focus();
},
_onEntryActivated: function() {
let text = this._responseEntry.get_text();
if (text == '')
return;
this.source.emit('password-ready', text);
}
}
function ShellProcessesDialog(icon) {
this._init(icon);
}
ShellProcessesDialog.prototype = {
__proto__: ModalDialog.ModalDialog.prototype,
_init: function(icon) {
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'show-processes-dialog' });
let mainContentLayout = new St.BoxLayout();
this.contentLayout.add(mainContentLayout, { x_fill: true,
y_fill: false });
this._iconBin = new St.Bin({ child: icon });
mainContentLayout.add(this._iconBin,
{ x_fill: true,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
let messageLayout = new St.BoxLayout({ vertical: true });
mainContentLayout.add(messageLayout,
{ y_align: St.Align.START });
this.subjectLabel = new St.Label({ style_class: 'show-processes-dialog-subject' });
messageLayout.add(this.subjectLabel,
{ y_fill: false,
y_align: St.Align.START });
this.descriptionLabel = new St.Label({ style_class: 'show-processes-dialog-description' });
this.descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this.descriptionLabel.clutter_text.line_wrap = true;
messageLayout.add(this.descriptionLabel,
{ y_fill: true,
y_align: St.Align.START });
let scrollView = new St.ScrollView({ style_class: 'show-processes-dialog-app-list'});
scrollView.set_policy(Gtk.PolicyType.NEVER,
Gtk.PolicyType.AUTOMATIC);
this.contentLayout.add(scrollView,
{ x_fill: true,
y_fill: true });
scrollView.hide();
this._applicationList = new St.BoxLayout({ vertical: true });
scrollView.add_actor(this._applicationList,
{ x_fill: true,
y_fill: true,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
this._applicationList.connect('actor-added',
Lang.bind(this, function() {
if (this._applicationList.get_children().length == 1)
scrollView.show();
}));
this._applicationList.connect('actor-removed',
Lang.bind(this, function() {
if (this._applicationList.get_children().length == 0)
scrollView.hide();
}));
},
_setAppsForPids: function(pids) {
// remove all the items
this._applicationList.destroy_children();
pids.forEach(Lang.bind(this, function(pid) {
let tracker = Shell.WindowTracker.get_default();
let app = tracker.get_app_from_pid(pid);
if (!app)
return;
let item = new ListItem(app);
this._applicationList.add(item.actor, { x_fill: true });
item.connect('activate',
Lang.bind(this, function() {
// use -1 to indicate Cancel
this.emit('response', -1);
}));
}));
},
update: function(message, processes, choices) {
this._setAppsForPids(processes);
_setLabelsForMessage(this, message);
_setButtonsForChoices(this, choices);
}
}
Signals.addSignalMethods(ShellProcessesDialog.prototype);

View File

@ -1281,12 +1281,29 @@ NMDeviceWireless.prototype = {
if (apObj.accessPoints.length == 0) {
if (apObj.item)
apObj.item.destroy();
this._networks.splice(pos, 1);
if (this._overflowItem &&
this._overflowItem.menu.length == 0) {
this._overflowItem.destroy();
this._overflowItem = null;
if (this._overflowItem) {
if (!apObj.isMore) {
// we removed an item in the main menu, and we have a more submenu
// we need to extract the first item in more and move it to the submenu
let apObj = this._overflowItem.menu.firstMenuItem;
if (apObj.item) {
apObj.item.destroy();
this._createNetworkItem(apObj, NUM_VISIBLE_NETWORKS-1);
}
}
// This can happen if the removed connection is from the overflow
// menu, or if we just moved the last connection out from the menu
if (this._overflowItem.menu.length == 0) {
this._overflowItem.destroy();
this._overflowItem = null;
}
}
this._networks.splice(pos, 1);
} else if (apObj.item)
apObj.item.updateAccessPoints(apObj.accessPoints);
},
@ -1482,14 +1499,16 @@ NMDeviceWireless.prototype = {
}
}));
}
if (position < NUM_VISIBLE_NETWORKS)
if (position < NUM_VISIBLE_NETWORKS) {
apObj.isMore = false;
this.section.addMenuItem(apObj.item, position);
else {
} else {
if (!this._overflowItem) {
this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More..."));
this.section.addMenuItem(this._overflowItem);
}
this._overflowItem.menu.addMenuItem(apObj.item, position - NUM_VISIBLE_NETWORKS);
apObj.isMore = true;
}
},

View File

@ -12,8 +12,8 @@ const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Util = imports.misc.util;
const BUS_NAME = 'org.gnome.PowerManager';
const OBJECT_PATH = '/org/gnome/PowerManager';
const BUS_NAME = 'org.gnome.SettingsDaemon';
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
const UPDeviceType = {
UNKNOWN: 0,
@ -41,7 +41,7 @@ const UPDeviceState = {
};
const PowerManagerInterface = {
name: 'org.gnome.PowerManager',
name: 'org.gnome.SettingsDaemon.Power',
methods: [
{ name: 'GetDevices', inSignature: '', outSignature: 'a(susbut)' },
{ name: 'GetPrimaryDevice', inSignature: '', outSignature: '(susbut)' },
@ -93,7 +93,6 @@ Indicator.prototype = {
_readPrimaryDevice: function() {
this._proxy.GetPrimaryDeviceRemote(Lang.bind(this, function(device, error) {
if (error) {
this._checkError(error);
this._hasPrimary = false;
this._primaryDeviceId = null;
this._batteryItem.actor.hide();
@ -145,7 +144,6 @@ Indicator.prototype = {
this._deviceItems = [];
if (error) {
this._checkError(error);
this._deviceSep.actor.hide();
return;
}
@ -176,21 +174,12 @@ Indicator.prototype = {
this.setGIcon(gicon);
this.actor.show();
} else {
this._checkError(error);
this.menu.close();
this.actor.hide();
}
}));
this._readPrimaryDevice();
this._readOtherDevices();
},
_checkError: function(error) {
if (!this._restarted && error && error.message.match(/org\.freedesktop\.DBus\.Error\.(UnknownMethod|InvalidArgs)/)) {
Util.killall('gnome-power-manager');
Util.spawn(['gnome-power-manager']);
this._restarted = true;
}
}
};

View File

@ -14,24 +14,14 @@ const GnomeSession = imports.misc.gnomeSession;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const ScreenSaver = imports.misc.screenSaver;
const Util = imports.misc.util;
const BUS_NAME = 'org.gnome.ScreenSaver';
const OBJECT_PATH = '/org/gnome/ScreenSaver';
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
const DISABLE_USER_SWITCH_KEY = 'disable-user-switching';
const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen';
const DISABLE_LOG_OUT_KEY = 'disable-log-out';
const ScreenSaverInterface = {
name: BUS_NAME,
methods: [ { name: 'Lock', inSignature: '' },
{ name: 'SetActive', inSignature: 'b' }]
};
let ScreenSaverProxy = DBus.makeProxyClass(ScreenSaverInterface);
// Adapted from gdm/gui/user-switch-applet/applet.c
//
// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
@ -63,7 +53,7 @@ StatusMenuButton.prototype = {
this._account_mgr = Tp.AccountManager.dup()
this._upClient = new UPowerGlib.Client();
this._screenSaverProxy = new ScreenSaverProxy(DBus.session, BUS_NAME, OBJECT_PATH);
this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy();
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._iconBox = new St.Bin();
@ -301,8 +291,11 @@ StatusMenuButton.prototype = {
_onLoginScreenActivate: function() {
Main.overview.hide();
this._gdm.goto_login_session();
this._onLockScreenActivate();
// Ensure we only move to GDM after the screensaver has activated; in some
// OS configurations, the X server may block event processing on VT switch
this._screenSaverProxy.SetActiveRemote(true, Lang.bind(this, function() {
this._gdm.goto_login_session();
}));
},
_onQuitSessionActivate: function() {
@ -315,6 +308,7 @@ StatusMenuButton.prototype = {
if (this._haveSuspend &&
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
// Ensure we only suspend after the screensaver has activated
this._screenSaverProxy.SetActiveRemote(true, Lang.bind(this, function() {
this._upClient.suspend_sync(null);
}));

View File

@ -24,6 +24,9 @@ const SCROLLBACK_IDLE_LENGTH = 5;
// See Source._displayPendingMessages
const SCROLLBACK_HISTORY_LINES = 10;
// See Notification._onEntryChanged
const COMPOSING_STOP_TIMEOUT = 5;
const NotificationDirection = {
SENT: 'chat-sent',
RECEIVED: 'chat-received'
@ -69,8 +72,9 @@ function Client() {
Client.prototype = {
_init : function() {
// channel path -> Source
this._sources = {};
// channel path -> ChatSource
this._chatSources = {};
this._chatState = Tp.ChannelChatState.ACTIVE;
// Set up a SimpleObserver, which will call _observeChannels whenever a
// channel matching its filters is detected.
@ -87,6 +91,11 @@ Client.prototype = {
this._tpClient.set_handle_channels_func(
Lang.bind(this, this._handleChannels));
// Allow other clients (such as Empathy) to pre-empt our channels if
// needed
this._tpClient.set_delegated_channels_callback(
Lang.bind(this, this._delegatedChannelsCb));
try {
this._tpClient.register();
} catch (e) {
@ -130,20 +139,20 @@ Client.prototype = {
return;
/* We got the TpContact */
this._createSource(account, conn, channel, contacts[0]);
this._createChatSource(account, conn, channel, contacts[0]);
}), null);
}
context.accept();
},
_createSource: function(account, conn, channel, contact) {
if (this._sources[channel.get_object_path()])
_createChatSource: function(account, conn, channel, contact) {
if (this._chatSources[channel.get_object_path()])
return;
let source = new Source(account, conn, channel, contact, this._tpClient);
let source = new ChatSource(account, conn, channel, contact, this._tpClient);
this._sources[channel.get_object_path()] = source;
this._chatSources[channel.get_object_path()] = source;
source.connect('destroy', Lang.bind(this,
function() {
if (this._tpClient.is_handling_channel(channel)) {
@ -154,10 +163,16 @@ Client.prototype = {
});
}
delete this._sources[channel.get_object_path()];
delete this._chatSources[channel.get_object_path()];
}));
},
_handleChannels: function(handler, account, conn, channels,
requests, user_action_time, context) {
this._handlingChannels(account, conn, channels);
context.accept();
},
_handlingChannels: function(account, conn, channels) {
let len = channels.length;
for (let i = 0; i < len; i++) {
@ -171,40 +186,127 @@ Client.prototype = {
if (this._tpClient.is_handling_channel(channel)) {
// We are already handling the channel, display the source
let source = this._sources[channel.get_object_path()];
let source = this._chatSources[channel.get_object_path()];
if (source)
source.notify();
}
}
},
_approveChannels: function(approver, account, conn, channels,
dispatchOp, context) {
// Approve the channels right away as we are going to handle it
dispatchOp.claim_with_async(this._tpClient,
Lang.bind (this, function(dispatchOp, result) {
try {
dispatchOp.claim_with_finish(result);
this._handlingChannels(account, conn, channels);
} catch (err) {
global.logError('Failed to Claim channel: ' + err);
}}));
_displayRoomInvitation: function(conn, channel, dispatchOp, context) {
// We can only approve the rooms if we have been invited to it
let selfHandle = channel.group_get_self_handle();
if (selfHandle == 0) {
Shell.decline_dispatch_op(context, 'Not invited to the room');
return;
}
let [invited, inviter, reason, msg] = channel.group_get_local_pending_info(selfHandle);
if (!invited) {
Shell.decline_dispatch_op(context, 'Not invited to the room');
return;
}
// Request a TpContact for the inviter
Shell.get_tp_contacts(conn, [inviter],
contactFeatures,
Lang.bind(this, this._createRoomInviteSource, channel, context, dispatchOp));
context.delay();
},
_createRoomInviteSource: function(connection, contacts, failed, channel, context, dispatchOp) {
if (contacts.length < 1) {
Shell.decline_dispatch_op(context, 'Failed to get inviter');
return;
}
// We got the TpContact
// FIXME: We don't have a 'chat room' icon (bgo #653737) use
// system-users for now as Empathy does.
let source = new ApproverSource(dispatchOp, _("Invitation"), 'system-users');
Main.messageTray.add(source);
let notif = new RoomInviteNotification(source, dispatchOp, channel, contacts[0]);
source.notify(notif);
context.accept();
},
_handleChannels: function(handler, account, conn, channels,
requests, user_action_time, context) {
this._handlingChannels(account, conn, channels);
_approveChannels: function(approver, account, conn, channels,
dispatchOp, context) {
let channel = channels[0];
let chanType = channel.get_channel_type();
if (chanType == Tp.IFACE_CHANNEL_TYPE_TEXT)
this._approveTextChannel(account, conn, channel, dispatchOp, context);
else if (chanType == Tp.IFACE_CHANNEL_TYPE_STREAMED_MEDIA ||
chanType == 'org.freedesktop.Telepathy.Channel.Type.Call.DRAFT')
this._approveCall(account, conn, channel, dispatchOp, context);
},
_approveTextChannel: function(account, conn, channel, dispatchOp, context) {
let [targetHandle, targetHandleType] = channel.get_handle();
if (targetHandleType == Tp.HandleType.CONTACT) {
// Approve private text channels right away as we are going to handle it
dispatchOp.claim_with_async(this._tpClient,
Lang.bind(this, function(dispatchOp, result) {
try {
dispatchOp.claim_with_finish(result);
this._handlingChannels(account, conn, [channel]);
} catch (err) {
throw new Error('Failed to Claim channel: ' + err);
}}));
context.accept();
} else {
this._displayRoomInvitation(conn, channel, dispatchOp, context);
}
},
_approveCall: function(account, conn, channel, dispatchOp, context) {
let [targetHandle, targetHandleType] = channel.get_handle();
Shell.get_tp_contacts(conn, [targetHandle],
contactFeatures,
Lang.bind(this, this._createAudioVideoSource, channel, context, dispatchOp));
},
_createAudioVideoSource: function(connection, contacts, failed, channel, context, dispatchOp) {
if (contacts.length < 1) {
Shell.decline_dispatch_op(context, 'Failed to get inviter');
return;
}
let isVideo = false;
let props = channel.borrow_immutable_properties();
if (props['org.freedesktop.Telepathy.Channel.Type.Call.DRAFT.InitialVideo'] ||
props[Tp.PROP_CHANNEL_TYPE_STREAMED_MEDIA_INITIAL_VIDEO])
isVideo = true;
// We got the TpContact
let source = new ApproverSource(dispatchOp, _("Call"), isVideo ? 'camera-web' : 'audio-input-microphone');
Main.messageTray.add(source);
let notif = new AudioVideoNotification(source, dispatchOp, channel, contacts[0], isVideo);
source.notify(notif);
context.accept();
},
_delegatedChannelsCb: function(client, channels) {
// Nothing to do as we don't make a distinction between observed and
// handled channels.
}
};
function Source(account, conn, channel, contact, client) {
function ChatSource(account, conn, channel, contact, client) {
this._init(account, conn, channel, contact, client);
}
Source.prototype = {
ChatSource.prototype = {
__proto__: MessageTray.Source.prototype,
_init: function(account, conn, channel, contact, client) {
@ -222,7 +324,7 @@ Source.prototype = {
this._channel = channel;
this._closedId = this._channel.connect('invalidated', Lang.bind(this, this._channelClosed));
this._notification = new Notification(this);
this._notification = new ChatNotification(this);
this._notification.setUrgency(MessageTray.Urgency.HIGH);
// We ack messages when the message box is collapsed if user has
@ -326,6 +428,8 @@ Source.prototype = {
this._pendingMessages.push(message);
}
this._updateCount();
let showTimestamp = false;
for (let i = 0; i < logMessages.length; i++) {
@ -370,11 +474,16 @@ Source.prototype = {
this.destroy();
},
_updateCount: function() {
this._setCount(this._pendingMessages.length, this._pendingMessages.length > 0);
},
_messageReceived: function(channel, message) {
if (message.get_message_type() == Tp.ChannelTextMessageType.DELIVERY_REPORT)
return;
this._pendingMessages.push(message);
this._updateCount();
message = makeMessageFromTpMessage(message, NotificationDirection.RECEIVED);
this._notification.appendMessage(message);
@ -407,6 +516,19 @@ Source.prototype = {
}));
},
setChatState: function(state) {
// We don't want to send COMPOSING every time a letter is typed into
// the entry. We send the state only when it changes. Telepathy/Empathy
// might change it behind our back if the user is using both
// gnome-shell's entry and the Empathy conversation window. We could
// keep track of it with the ChatStateChanged signal but it is good
// enough right now.
if (state != this._chatState) {
this._chatState = state;
this._channel.set_chat_state_async(state, null);
}
},
_presenceChanged: function (contact, presence, status, message) {
let msg, shouldNotify, title;
@ -445,8 +567,10 @@ Source.prototype = {
_pendingRemoved: function(channel, message) {
let idx = this._pendingMessages.indexOf(message);
if (idx >= 0)
if (idx >= 0) {
this._pendingMessages.splice(idx, 1);
this._updateCount();
}
else
throw new Error('Message not in our pending list: ' + message);
},
@ -480,11 +604,11 @@ Source.prototype = {
}
};
function Notification(source) {
function ChatNotification(source) {
this._init(source);
}
Notification.prototype = {
ChatNotification.prototype = {
__proto__: MessageTray.Notification.prototype,
_init: function(source) {
@ -494,6 +618,7 @@ Notification.prototype = {
this._responseEntry = new St.Entry({ style_class: 'chat-response',
can_focus: true });
this._responseEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivated));
this._responseEntry.clutter_text.connect('text-changed', Lang.bind(this, this._onEntryChanged));
this.setActionArea(this._responseEntry);
this._oldMaxScrollAdjustment = 0;
@ -510,6 +635,7 @@ Notification.prototype = {
this._history = [];
this._timestampTimeoutId = 0;
this._composingTimeoutId = 0;
},
/**
@ -682,5 +808,166 @@ Notification.prototype = {
// see Source._messageSent
this._responseEntry.set_text('');
this.source.respond(text);
},
_composingStopTimeout: function() {
this._composingTimeoutId = 0;
this.source.setChatState(Tp.ChannelChatState.PAUSED);
return false;
},
_onEntryChanged: function() {
let text = this._responseEntry.get_text();
// If we're typing, we want to send COMPOSING.
// If we empty the entry, we want to send ACTIVE.
// If we've stopped typing for COMPOSING_STOP_TIMEOUT
// seconds, we want to send PAUSED.
// Remove composing timeout.
if (this._composingTimeoutId > 0) {
Mainloop.source_remove(this._composingTimeoutId);
this._composingTimeoutId = 0;
}
if (text != '') {
this.source.setChatState(Tp.ChannelChatState.COMPOSING);
this._composingTimeoutId = Mainloop.timeout_add_seconds(
COMPOSING_STOP_TIMEOUT,
Lang.bind(this, this._composingStopTimeout));
} else {
this.source.setChatState(Tp.ChannelChatState.ACTIVE);
}
}
};
function ApproverSource(dispatchOp, text, icon) {
this._init(dispatchOp, text, icon);
}
ApproverSource.prototype = {
__proto__: MessageTray.Source.prototype,
_init: function(dispatchOp, text, icon) {
MessageTray.Source.prototype._init.call(this, text);
this._icon = icon;
this._setSummaryIcon(this.createNotificationIcon());
this._dispatchOp = dispatchOp;
// Destroy the source if the channel dispatch operation is invalidated
// as we can't approve any more.
this._invalidId = dispatchOp.connect('invalidated',
Lang.bind(this, function(domain, code, msg) {
this.destroy();
}));
},
destroy: function() {
if (this._invalidId != 0) {
this._dispatchOp.disconnect(this._invalidId);
this._invalidId = 0;
}
MessageTray.Source.prototype.destroy.call(this);
},
createNotificationIcon: function() {
return new St.Icon({ icon_name: this._icon,
icon_type: St.IconType.FULLCOLOR,
icon_size: this.ICON_SIZE });
}
}
function RoomInviteNotification(source, dispatchOp, channel, inviter) {
this._init(source, dispatchOp, channel, inviter);
}
RoomInviteNotification.prototype = {
__proto__: MessageTray.Notification.prototype,
_init: function(source, dispatchOp, channel, inviter) {
MessageTray.Notification.prototype._init.call(this,
source,
/* translators: argument is a room name like
* room@jabber.org for example. */
_("Invitation to %s").format(channel.get_identifier()),
null,
{ customContent: true });
this.setResident(true);
/* translators: first argument is the name of a contact and the second
* one the name of a room. "Alice is inviting you to join room@jabber.org
* for example. */
this.addBody(_("%s is inviting you to join %s").format(inviter.get_alias(), channel.get_identifier()));
this.addButton('decline', _("Decline"));
this.addButton('accept', _("Accept"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
switch (action) {
case 'decline':
dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE,
'', function(src, result) {
src.leave_channels_finish(result)});
break;
case 'accept':
dispatchOp.handle_with_time_async('', global.get_current_time(),
function(src, result) {
src.handle_with_time_finish(result)});
break;
}
this.destroy();
}));
}
};
// Audio Video
function AudioVideoNotification(source, dispatchOp, channel, contact, isVideo) {
this._init(source, dispatchOp, channel, contact, isVideo);
}
AudioVideoNotification.prototype = {
__proto__: MessageTray.Notification.prototype,
_init: function(source, dispatchOp, channel, contact, isVideo) {
let title = '';
if (isVideo)
/* translators: argument is a contact name like Alice for example. */
title = _("Video call from %s").format(contact.get_alias());
else
/* translators: argument is a contact name like Alice for example. */
title = _("Call from %s").format(contact.get_alias());
MessageTray.Notification.prototype._init.call(this,
source,
title,
null,
{ customContent: true });
this.setResident(true);
this.addButton('reject', _("Reject"));
this.addButton('answer', _("Answer"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
switch (action) {
case 'reject':
dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE,
'', function(src, result) {
src.leave_channels_finish(result)});
break;
case 'answer':
dispatchOp.handle_with_time_async('', global.get_current_time(),
function(src, result) {
src.handle_with_time_finish(result)});
break;
}
this.destroy();
}));
}
};

View File

@ -141,13 +141,19 @@ SearchTab.prototype = {
'edit-find');
this._text.connect('text-changed', Lang.bind(this, this._onTextChanged));
this._text.connect('activate', Lang.bind(this, function (se) {
if (this._searchTimeoutId > 0) {
Mainloop.source_remove(this._searchTimeoutId);
this._doSearch();
this._text.connect('key-press-event', Lang.bind(this, function (o, e) {
// We can't connect to 'activate' here because search providers
// might want to do something with the modifiers in activateSelected.
let symbol = e.get_key_symbol();
if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) {
if (this._searchTimeoutId > 0) {
Mainloop.source_remove(this._searchTimeoutId);
this._doSearch();
}
this._searchResults.activateSelected();
return true;
}
this._searchResults.activateSelected();
return true;
return false;
}));
this._entry.connect('notify::mapped', Lang.bind(this, this._onMapped));

View File

@ -119,6 +119,8 @@ WindowManager.prototype = {
this.setKeybindingHandler('switch_to_workspace_down', Lang.bind(this, this._showWorkspaceSwitcher));
this.setKeybindingHandler('switch_windows', Lang.bind(this, this._startAppSwitcher));
this.setKeybindingHandler('switch_group', Lang.bind(this, this._startAppSwitcher));
this.setKeybindingHandler('switch_windows_backward', Lang.bind(this, this._startAppSwitcher));
this.setKeybindingHandler('switch_group_backward', Lang.bind(this, this._startAppSwitcher));
this.setKeybindingHandler('switch_panels', Lang.bind(this, this._startA11ySwitcher));
Main.overview.connect('showing', Lang.bind(this, function() {
@ -180,7 +182,7 @@ WindowManager.prototype = {
*/
this._minimizing.push(actor);
let primary = global.get_primary_monitor();
let primary = Main.layoutManager.primaryMonitor;
let xDest = primary.x;
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
xDest += primary.width;
@ -534,7 +536,7 @@ WindowManager.prototype = {
let tabPopup = new AltTab.AltTabPopup();
if (!tabPopup.show(backwards, binding == 'switch_group'))
if (!tabPopup.show(backwards, binding))
tabPopup.destroy();
},

View File

@ -225,8 +225,12 @@ WindowClone.prototype = {
let [width, height] = this.actor.get_transformed_size();
let monitorIndex = this.metaWindow.get_monitor();
let availArea = global.get_monitors()[monitorIndex];
if (monitorIndex == global.get_primary_monitor_index()) {
let monitor = Main.layoutManager.monitors[monitorIndex];
let availArea = new Meta.Rectangle({ x: monitor.x,
y: monitor.y,
width: monitor.width,
height: monitor.height });
if (monitorIndex == Main.layoutManager.primaryIndex) {
availArea.y += Main.panel.actor.height;
availArea.height -= Main.panel.actor.height;
}
@ -593,7 +597,7 @@ Workspace.prototype = {
this._height = 0;
this.monitorIndex = monitorIndex;
this._monitor = global.get_monitors()[this.monitorIndex];
this._monitor = Main.layoutManager.monitors[this.monitorIndex];
this._windowOverlaysGroup = new Clutter.Group();
// Without this the drop area will be overlapped.
this._windowOverlaysGroup.set_size(0, 0);

View File

@ -56,7 +56,7 @@ WorkspaceSwitcherPopup.prototype = {
_getPreferredHeight : function (actor, forWidth, alloc) {
let children = this._list.get_children();
let primary = global.get_primary_monitor();
let primary = Main.layoutManager.primaryMonitor;
let availHeight = primary.height;
availHeight -= Main.panel.actor.height;
@ -82,7 +82,7 @@ WorkspaceSwitcherPopup.prototype = {
},
_getPreferredWidth : function (actor, forHeight, alloc) {
let primary = global.get_primary_monitor();
let primary = Main.layoutManager.primaryMonitor;
this._childWidth = Math.round(this._childHeight * primary.width / primary.height);
alloc.min_size = this._childWidth;
@ -125,7 +125,7 @@ WorkspaceSwitcherPopup.prototype = {
},
_position: function() {
let primary = global.get_primary_monitor();
let primary = Main.layoutManager.primaryMonitor;
this._container.x = primary.x + Math.floor((primary.width - this._container.width) / 2);
this._container.y = primary.y + Main.panel.actor.height +
Math.floor(((primary.height - Main.panel.actor.height) - this._container.height) / 2);

View File

@ -146,7 +146,7 @@ function WorkspaceThumbnail(metaWorkspace) {
WorkspaceThumbnail.prototype = {
_init : function(metaWorkspace) {
this.metaWorkspace = metaWorkspace;
this.monitorIndex = global.get_primary_monitor_index();
this.monitorIndex = Main.layoutManager.primaryIndex;
this.actor = new St.Group({ reactive: true,
clip_to_allocation: true,
@ -170,7 +170,7 @@ WorkspaceThumbnail.prototype = {
this._background = new Clutter.Clone({ source: global.background_actor });
this._contents.add_actor(this._background);
let monitor = global.get_primary_monitor();
let monitor = Main.layoutManager.primaryMonitor;
this.setPorthole(monitor.x, monitor.y, monitor.width, monitor.height);
let windows = global.get_window_actors().filter(this._isMyWindow, this);
@ -528,7 +528,7 @@ ThumbnailsBox.prototype = {
// The "porthole" is the portion of the screen that we show in the workspaces
let panelHeight = Main.panel.actor.height;
let monitor = global.get_primary_monitor();
let monitor = Main.layoutManager.primaryMonitor;
this._porthole = {
x: monitor.x,
y: monitor.y + panelHeight,

View File

@ -70,10 +70,10 @@ WorkspacesView.prototype = {
this._workspaces[activeWorkspaceIndex].actor.raise_top();
this._extraWorkspaces = [];
let monitors = global.get_monitors();
let monitors = Main.layoutManager.monitors;
let m = 0;
for (let i = 0; i < monitors.length; i++) {
if (i == global.get_primary_monitor_index())
if (i == Main.layoutManager.primaryIndex)
continue;
let ws = new Workspace.Workspace(null, i);
this._extraWorkspaces[m++] = ws;
@ -383,7 +383,7 @@ WorkspacesView.prototype = {
this._extraWorkspaces[i].setReservedSlot(dragEvent.dragActor._delegate);
}
let primary = global.get_primary_monitor();
let primary = Main.layoutManager.primaryMonitor;
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
let topWorkspace, bottomWorkspace;
@ -550,8 +550,8 @@ WorkspacesDisplay.prototype = {
controls.connect('scroll-event',
Lang.bind(this, this._onScrollEvent));
this._monitorIndex = global.get_primary_monitor_index();
this._monitor = global.get_monitors()[this._monitorIndex];
this._monitorIndex = Main.layoutManager.primaryIndex;
this._monitor = Main.layoutManager.monitors[this._monitorIndex];
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
controls.add_actor(this._thumbnailsBox.actor);
@ -567,7 +567,7 @@ WorkspacesDisplay.prototype = {
this._updateAlwaysZoom();
global.screen.connect('monitors-changed', Lang.bind(this, this._updateAlwaysZoom));
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._updateAlwaysZoom));
global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._workspacesChanged));
@ -694,8 +694,8 @@ WorkspacesDisplay.prototype = {
if (this._alwaysZoomOut)
return;
let monitors = global.get_monitors();
let primary = global.get_primary_monitor();
let monitors = Main.layoutManager.monitors;
let primary = Main.layoutManager.primaryMonitor;
/* Look for any monitor to the right of the primary, if there is
* one, we always keep zoom out, otherwise its hard to reach

622
po/de.po

File diff suppressed because it is too large Load Diff

261
po/es.po
View File

@ -2,22 +2,22 @@
# Copyright (C) 2009 gnome-shell's COPYRIGHT HOLDER
# This file is distributed under the same license as the gnome-shell package.
# Benjamín Valero Espinosa <benjavalero@gmail.com>, 2011.
# Jorge González <jorgegonz@svn.gnome.org>, 2009, 2010, 2011.
# Daniel Mustieles <daniel.mustieles@gmail.com>, 2010, 2011.
# Jorge González <jorgegonz@svn.gnome.org>, 2009, 2010, 2011.
#
msgid ""
msgstr ""
"Project-Id-Version: gnome-shell.master\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&keywords=I18N+L10N&component=general\n"
"POT-Creation-Date: 2011-06-27 15:22+0000\n"
"PO-Revision-Date: 2011-06-28 11:17+0200\n"
"Last-Translator: Daniel Mustieles <daniel.mustieles@gmail.com>\n"
"Language-Team: Español <gnome-es-list@gnome.org>\n"
"POT-Creation-Date: 2011-07-15 16:19+0000\n"
"PO-Revision-Date: 2011-07-17 11:59+0200\n"
"Last-Translator: Jorge González <jorgegonz@svn.gnome.org>\n"
"Language-Team: Español; Castellano <gnome-es-list@gnome.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Content-Transfer-Encoding: 8bits\n"
"Plural-Forms: nplurals=2; plural=(n!=1);\n"
#: ../data/gnome-shell.desktop.in.in.h:1
msgid "GNOME Shell"
@ -108,16 +108,16 @@ msgid ""
"at the optimal thread count on the system."
msgstr ""
"Establece la tubería GStreamer usada para codificar grabaciones. Sigue la "
"sintaxis usada para gst-launch. La tubería debería tener un sumidero («sink») "
"de ensamblaje/sesensamblaje donde el vídeo que se está grabando se graba. "
"Generalmente tendrá un origen de ensamblado/desensamblado; la salida de ese "
"punto se escibirá en el archivo de salida. No obstante la tubería también "
"puede tomar parte en su propia salida; esto se puede usar para enviar la "
"salida a un servidor «icecast» a través de shout2send o similar. Cuando no "
"está establecido o lo está a un valor vacío, se usará la tubería "
"predeterminada. Actualmente es «videorate ! vp8enc quality=10 speed=2 threads="
"%T ! queue ! webmmux» y greba en WEBM usando el códec VP8. Se usa %T como "
"suposición para el número de hilos óptimos en el sistema."
"sintaxis usada para gst-launch. La tubería debería tener un sumidero "
"(«sink») de ensamblaje/sesensamblaje donde el vídeo que se está grabando se "
"graba. Generalmente tendrá un origen de ensamblado/desensamblado; la salida "
"de ese punto se escibirá en el archivo de salida. No obstante la tubería "
"también puede tomar parte en su propia salida; esto se puede usar para "
"enviar la salida a un servidor «icecast» a través de shout2send o similar. "
"Cuando no está establecido o lo está a un valor vacío, se usará la tubería "
"predeterminada. Actualmente es «videorate ! vp8enc quality=10 speed=2 "
"threads=%T ! queue ! webmmux» y greba en WEBM usando el códec VP8. Se usa %T "
"como suposición para el número de hilos óptimos en el sistema."
#: ../data/org.gnome.shell.gschema.xml.in.h:15
msgid "Show date in clock"
@ -206,27 +206,27 @@ msgid "Execution of '%s' failed:"
msgstr "Falló la ejecución de «%s»:"
#. Translators: Filter to display all applications
#: ../js/ui/appDisplay.js:258
#: ../js/ui/appDisplay.js:252
msgid "All"
msgstr "Todas"
#: ../js/ui/appDisplay.js:357
#: ../js/ui/appDisplay.js:351
msgid "APPLICATIONS"
msgstr "APLICACIONES"
#: ../js/ui/appDisplay.js:383
#: ../js/ui/appDisplay.js:377
msgid "SETTINGS"
msgstr "CONFIGURACIÓN"
#: ../js/ui/appDisplay.js:656
#: ../js/ui/appDisplay.js:650
msgid "New Window"
msgstr "Ventana nueva"
#: ../js/ui/appDisplay.js:659
#: ../js/ui/appDisplay.js:653
msgid "Remove from Favorites"
msgstr "Quitar de los favoritos"
#: ../js/ui/appDisplay.js:660
#: ../js/ui/appDisplay.js:654
msgid "Add to Favorites"
msgstr "Añadir a los favoritos"
@ -359,13 +359,13 @@ msgid "Nothing Scheduled"
msgstr "Nada programado"
#. Translators: Shown on calendar heading when selected day occurs on current year
#: ../js/ui/calendar.js:717 ../js/ui/telepathyClient.js:564
#: ../js/ui/calendar.js:717 ../js/ui/telepathyClient.js:749
msgctxt "calendar heading"
msgid "%A, %B %d"
msgstr "%A, %d de %B"
#. Translators: Shown on calendar heading when selected day occurs on different year
#: ../js/ui/calendar.js:720 ../js/ui/telepathyClient.js:567
#: ../js/ui/calendar.js:720 ../js/ui/telepathyClient.js:752
msgctxt "calendar heading"
msgid "%A, %B %d, %Y"
msgstr "%A, %d de %B de %Y"
@ -386,7 +386,7 @@ msgstr "Esta semana"
msgid "Next week"
msgstr "La semana que viene"
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1044
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1122
msgid "Remove"
msgstr "Quitar"
@ -549,11 +549,11 @@ msgstr "Ver fuente"
msgid "Web Page"
msgstr "Página web"
#: ../js/ui/messageTray.js:1037
#: ../js/ui/messageTray.js:1115
msgid "Open"
msgstr "Abrir"
#: ../js/ui/messageTray.js:2208
#: ../js/ui/messageTray.js:2286
msgid "System Information"
msgstr "Información del sistema"
@ -576,18 +576,18 @@ msgid "Dash"
msgstr "Tablero"
#. TODO - _quit() doesn't really work on apps in state STARTING yet
#: ../js/ui/panel.js:533
#: ../js/ui/panel.js:532
#, c-format
msgid "Quit %s"
msgstr "Salir de %s"
#. 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:913
#. Translators: If there is no suitable word for "Activities"
#. in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:568
msgid "Activities"
msgstr "Actividades"
#: ../js/ui/panel.js:1015
#: ../js/ui/panel.js:951
msgid "Top Bar"
msgstr "Barra superior"
@ -653,40 +653,41 @@ msgstr "Buscando…"
msgid "No matching results."
msgstr "No se encontró ningún resultado coincidente."
#: ../js/ui/statusMenu.js:160 ../js/ui/statusMenu.js:162
#: ../js/ui/statusMenu.js:227
#: ../js/ui/statusMenu.js:192 ../js/ui/statusMenu.js:196
#: ../js/ui/statusMenu.js:262
msgid "Power Off..."
msgstr "Apagar…"
#: ../js/ui/statusMenu.js:162 ../js/ui/statusMenu.js:226
#: ../js/ui/statusMenu.js:194 ../js/ui/statusMenu.js:196
#: ../js/ui/statusMenu.js:261
msgid "Suspend"
msgstr "Suspender"
#: ../js/ui/statusMenu.js:183
#: ../js/ui/statusMenu.js:217
msgid "Available"
msgstr "Disponible"
#: ../js/ui/statusMenu.js:188
#: ../js/ui/statusMenu.js:222
msgid "Busy"
msgstr "Ocupado"
#: ../js/ui/statusMenu.js:196
#: ../js/ui/statusMenu.js:230
msgid "My Account"
msgstr "Mi cuenta"
#: ../js/ui/statusMenu.js:200
#: ../js/ui/statusMenu.js:234
msgid "System Settings"
msgstr "Configuración del sistema"
#: ../js/ui/statusMenu.js:207
#: ../js/ui/statusMenu.js:242
msgid "Lock Screen"
msgstr "Bloquear la pantalla"
#: ../js/ui/statusMenu.js:212
#: ../js/ui/statusMenu.js:247
msgid "Switch User"
msgstr "Cambiar de usuario"
#: ../js/ui/statusMenu.js:217
#: ../js/ui/statusMenu.js:252
msgid "Log Out..."
msgstr "Cerrar la sesión…"
@ -756,7 +757,6 @@ msgstr "Configuración de Bluetooth"
#. TRANSLATORS: this means that bluetooth was disabled by hardware rfkill
#: ../js/ui/status/bluetooth.js:116
#| msgid "disabled"
msgid "hardware disabled"
msgstr "hardware desactivado"
@ -819,7 +819,7 @@ msgstr "Conceder acceso siempre"
msgid "Grant this time only"
msgstr "Conceder sólo esta vez"
#: ../js/ui/status/bluetooth.js:409
#: ../js/ui/status/bluetooth.js:409 ../js/ui/telepathyClient.js:954
msgid "Reject"
msgstr "Rechazar"
@ -908,13 +908,13 @@ msgstr "no disponible"
msgid "connection failed"
msgstr "falló la conexión"
#: ../js/ui/status/network.js:582 ../js/ui/status/network.js:1489
#: ../js/ui/status/network.js:582 ../js/ui/status/network.js:1507
msgid "More..."
msgstr "Más…"
#. TRANSLATORS: this is the indication that a connection for another logged in user is active,
#. and we cannot access its settings (including the name)
#: ../js/ui/status/network.js:618 ../js/ui/status/network.js:1427
#: ../js/ui/status/network.js:618 ../js/ui/status/network.js:1444
msgid "Connected (private)"
msgstr "Conectada (privada)"
@ -931,7 +931,7 @@ msgid "Auto dial-up"
msgstr "Marcado automático"
#. TRANSLATORS: this the automatic wireless connection name (including the network name)
#: ../js/ui/status/network.js:898 ../js/ui/status/network.js:1439
#: ../js/ui/status/network.js:898 ../js/ui/status/network.js:1456
#, c-format
msgid "Auto %s"
msgstr "%s automática"
@ -940,68 +940,68 @@ msgstr "%s automática"
msgid "Auto bluetooth"
msgstr "Bluetooth automático"
#: ../js/ui/status/network.js:1441
#: ../js/ui/status/network.js:1458
msgid "Auto wireless"
msgstr "Inalámbrica automática"
#: ../js/ui/status/network.js:1531
#: ../js/ui/status/network.js:1550
msgid "Enable networking"
msgstr "Activar red"
#: ../js/ui/status/network.js:1543
#: ../js/ui/status/network.js:1562
msgid "Wired"
msgstr "Cableada"
#: ../js/ui/status/network.js:1554
#: ../js/ui/status/network.js:1573
msgid "Wireless"
msgstr "Inalámbrica"
#: ../js/ui/status/network.js:1564
#: ../js/ui/status/network.js:1583
msgid "Mobile broadband"
msgstr "Banda ancha móvil"
#: ../js/ui/status/network.js:1574
#: ../js/ui/status/network.js:1593
msgid "VPN Connections"
msgstr "Conexiones VPN"
#: ../js/ui/status/network.js:1586
#: ../js/ui/status/network.js:1605
msgid "Network Settings"
msgstr "Configuración de la red"
#: ../js/ui/status/network.js:1878
#: ../js/ui/status/network.js:1897
#, c-format
msgid "You're now connected to mobile broadband connection '%s'"
msgstr "Ahora está conectado a la red de banda ancha móvil «%s»"
#: ../js/ui/status/network.js:1882
#: ../js/ui/status/network.js:1901
#, c-format
msgid "You're now connected to wireless network '%s'"
msgstr "Ahora está conectado a la red inalámbrica «%s»"
#: ../js/ui/status/network.js:1886
#: ../js/ui/status/network.js:1905
#, c-format
msgid "You're now connected to wired network '%s'"
msgstr "Ahora está conectado a la red cableada «%s»"
#: ../js/ui/status/network.js:1890
#: ../js/ui/status/network.js:1909
#, c-format
msgid "You're now connected to VPN network '%s'"
msgstr "Ahora está conectado a la VPN «%s»"
#: ../js/ui/status/network.js:1895
#: ../js/ui/status/network.js:1914
#, c-format
msgid "You're now connected to '%s'"
msgstr "Ahora está conectado a «%s»"
#: ../js/ui/status/network.js:1903
#: ../js/ui/status/network.js:1922
msgid "Connection established"
msgstr "Conexión establecida"
#: ../js/ui/status/network.js:2029
#: ../js/ui/status/network.js:2048
msgid "Networking is disabled"
msgstr "La red está desactivada"
#: ../js/ui/status/network.js:2154
#: ../js/ui/status/network.js:2173
msgid "Network Manager"
msgstr "Gestor de la red"
@ -1011,11 +1011,11 @@ msgstr "Configuración de energía"
#. 0 is reported when UPower does not have enough data
#. to estimate battery life
#: ../js/ui/status/power.js:110
#: ../js/ui/status/power.js:109
msgid "Estimating..."
msgstr "Estimando…"
#: ../js/ui/status/power.js:117
#: ../js/ui/status/power.js:116
#, c-format
msgid "%d hour remaining"
msgid_plural "%d hours remaining"
@ -1023,75 +1023,75 @@ msgstr[0] "Queda %d hora"
msgstr[1] "Queda %d horas"
#. TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining"
#: ../js/ui/status/power.js:120
#: ../js/ui/status/power.js:119
#, c-format
msgid "%d %s %d %s remaining"
msgstr "Quedan %d %s %d %s"
#: ../js/ui/status/power.js:122
#: ../js/ui/status/power.js:121
msgid "hour"
msgid_plural "hours"
msgstr[0] "hora"
msgstr[1] "horas"
#: ../js/ui/status/power.js:122
#: ../js/ui/status/power.js:121
msgid "minute"
msgid_plural "minutes"
msgstr[0] "minuto"
msgstr[1] "minutos"
#: ../js/ui/status/power.js:125
#: ../js/ui/status/power.js:124
#, c-format
msgid "%d minute remaining"
msgid_plural "%d minutes remaining"
msgstr[0] "Queda %d minuto"
msgstr[1] "Quedan %d minutos"
#: ../js/ui/status/power.js:227
#: ../js/ui/status/power.js:216
msgid "AC adapter"
msgstr "Adaptador de corriente"
#: ../js/ui/status/power.js:229
#: ../js/ui/status/power.js:218
msgid "Laptop battery"
msgstr "Batería del portátil"
#: ../js/ui/status/power.js:231
#: ../js/ui/status/power.js:220
msgid "UPS"
msgstr "SAI"
#: ../js/ui/status/power.js:233
#: ../js/ui/status/power.js:222
msgid "Monitor"
msgstr "Monitor"
#: ../js/ui/status/power.js:235
#: ../js/ui/status/power.js:224
msgid "Mouse"
msgstr "Ratón"
#: ../js/ui/status/power.js:237
#: ../js/ui/status/power.js:226
msgid "Keyboard"
msgstr "Teclado"
#: ../js/ui/status/power.js:239
#: ../js/ui/status/power.js:228
msgid "PDA"
msgstr "PDA"
#: ../js/ui/status/power.js:241
#: ../js/ui/status/power.js:230
msgid "Cell phone"
msgstr "Teléfono móvil"
#: ../js/ui/status/power.js:243
#: ../js/ui/status/power.js:232
msgid "Media player"
msgstr "Reproductor multimedia"
#: ../js/ui/status/power.js:245
#: ../js/ui/status/power.js:234
msgid "Tablet"
msgstr "Tableta"
#: ../js/ui/status/power.js:247
#: ../js/ui/status/power.js:236
msgid "Computer"
msgstr "Equipo"
#: ../js/ui/status/power.js:249 ../src/shell-app-system.c:1088
#: ../js/ui/status/power.js:238 ../src/shell-app-system.c:1088
msgid "Unknown"
msgstr "Desconocido"
@ -1103,22 +1103,35 @@ msgstr "Volumen"
msgid "Microphone"
msgstr "Micrófono"
#: ../js/ui/telepathyClient.js:397
#. We got the TpContact
#. FIXME: We don't have a 'chat room' icon (bgo #653737) use
#. system-users for now as Empathy does.
#: ../js/ui/telepathyClient.js:222
msgid "Invitation"
msgstr "Invitación"
#. We got the TpContact
#: ../js/ui/telepathyClient.js:285
#| msgid "Cancel"
msgid "Call"
msgstr "Llamar"
#: ../js/ui/telepathyClient.js:541
#, c-format
msgid "%s is online."
msgstr "%s está conectado/a."
#: ../js/ui/telepathyClient.js:402
#: ../js/ui/telepathyClient.js:546
#, c-format
msgid "%s is offline."
msgstr "%s está desconectado/a."
#: ../js/ui/telepathyClient.js:405
#: ../js/ui/telepathyClient.js:549
#, c-format
msgid "%s is away."
msgstr "%s está ausente."
#: ../js/ui/telepathyClient.js:408
#: ../js/ui/telepathyClient.js:552
#, c-format
msgid "%s is busy."
msgstr "%s está ocupado/a."
@ -1126,18 +1139,57 @@ msgstr "%s está ocupado/a."
#. Translators: this is a time format string followed by a date.
#. If applicable, replace %X with a strftime format valid for your
#. locale, without seconds.
#: ../js/ui/telepathyClient.js:556
#: ../js/ui/telepathyClient.js:741
#, no-c-format
msgid "Sent at %X on %A"
msgstr "Enviado a las %X el %A"
#. Translators: this is the other person changing their old IM name to their new
#. IM name.
#: ../js/ui/telepathyClient.js:606
#: ../js/ui/telepathyClient.js:791
#, c-format
msgid "%s is now known as %s"
msgstr "Ahora %s se llama %s"
#. translators: argument is a room name like
#. * room@jabber.org for example.
#: ../js/ui/telepathyClient.js:898
#, c-format
msgid "Invitation to %s"
msgstr "Invitación a %s"
#. translators: first argument is the name of a contact and the second
#. * one the name of a room. "Alice is inviting you to join room@jabber.org
#. * for example.
#: ../js/ui/telepathyClient.js:906
#, c-format
msgid "%s is inviting you to join %s"
msgstr "%s le está invitando a unirse a %s"
#: ../js/ui/telepathyClient.js:908
msgid "Decline"
msgstr "Rechazar"
#: ../js/ui/telepathyClient.js:909
msgid "Accept"
msgstr "Aceptar"
#. translators: argument is a contact name like Alice for example.
#: ../js/ui/telepathyClient.js:942
#, c-format
msgid "Video call from %s"
msgstr "Videollamada de %s"
#. translators: argument is a contact name like Alice for example.
#: ../js/ui/telepathyClient.js:945
#, c-format
msgid "Call from %s"
msgstr "Llamada de %s"
#: ../js/ui/telepathyClient.js:955
msgid "Answer"
msgstr "Responder"
#. Translators: this is the text displayed
#. in the search entry when no search is
#. active; it should not exceed ~30
@ -1146,7 +1198,7 @@ msgstr "Ahora %s se llama %s"
msgid "Type to search..."
msgstr "Teclear para buscar…"
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:254
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:257
msgid "Search"
msgstr "Buscar"
@ -1182,7 +1234,7 @@ msgstr[1] "%u entradas"
msgid "System Sounds"
msgstr "Sonidos del sistema"
#: ../src/main.c:445
#: ../src/main.c:466
msgid "Print version"
msgstr "Imprimir versión"
@ -1203,13 +1255,13 @@ msgstr "Predeterminada"
msgid "Authentication dialog was dismissed by the user"
msgstr "El usuario rechazó el diálogo de autenticación"
#: ../src/shell-util.c:93
#: ../src/shell-util.c:96
msgid "Home Folder"
msgstr "Carpeta personal"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-util.c:108
#: ../src/shell-util.c:111
msgid "File System"
msgstr "Sistema de archivos"
@ -1218,7 +1270,7 @@ msgstr "Sistema de archivos"
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-util.c:304
#: ../src/shell-util.c:307
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"
@ -1447,8 +1499,8 @@ msgstr "%1$s: %2$s"
#~ "If true and format is either \"12-hour\" or \"24-hour\", display seconds "
#~ "in time."
#~ msgstr ""
#~ "Si es cierta y el formato es «12-horas» o «24-horas», muestra los segundos "
#~ "en la hora."
#~ "Si es cierta y el formato es «12-horas» o «24-horas», muestra los "
#~ "segundos en la hora."
#~ msgid ""
#~ "This key specifies the format used by the panel clock when the format key "
@ -1465,18 +1517,19 @@ msgstr "%1$s: %2$s"
#~ msgid ""
#~ "This key specifies the hour format used by the panel clock. Possible "
#~ "values are \"12-hour\", \"24-hour\", \"unix\" and \"custom\". If set to "
#~ "\"unix\", the clock will display time in seconds since Epoch, i.e. 1970-"
#~ "01-01. If set to \"custom\", the clock will display time according to the "
#~ "format specified in the custom_format key. Note that if set to either "
#~ "\"unix\" or \"custom\", the show_date and show_seconds keys are ignored."
#~ "\"unix\", the clock will display time in seconds since Epoch, i.e. "
#~ "1970-01-01. If set to \"custom\", the clock will display time according "
#~ "to the format specified in the custom_format key. Note that if set to "
#~ "either \"unix\" or \"custom\", the show_date and show_seconds keys are "
#~ "ignored."
#~ msgstr ""
#~ "Esta clave especifica el formato de la hora especificado por el reloj del "
#~ "panel. Los valores posibles son «12-hour» (12 horas), «24-hour» (24 horas), "
#~ "«unix» y «custom» (personalizado).Si se establece a «unix» el reloj mostrará "
#~ "la hora en segundos desde la época (1 de enero de 1970). Si se establece "
#~ "a «custom» el reloj mostrará la hora según el formato especificado en la "
#~ "clave «custom_format». Note que si se establece a «unix» o «custom» se "
#~ "ignoran las claves «show_date» y «show_seconds»."
#~ "panel. Los valores posibles son «12-hour» (12 horas), «24-hour» (24 "
#~ "horas), «unix» y «custom» (personalizado).Si se establece a «unix» el "
#~ "reloj mostrará la hora en segundos desde la época (1 de enero de 1970). "
#~ "Si se establece a «custom» el reloj mostrará la hora según el formato "
#~ "especificado en la clave «custom_format». Note que si se establece a "
#~ "«unix» o «custom» se ignoran las claves «show_date» y «show_seconds»."
#~ msgid "Clock Format"
#~ msgstr "Formato del reloj"

274
po/gl.po
View File

@ -12,8 +12,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gnome-shell master\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-06-06 00:46+0200\n"
"PO-Revision-Date: 2011-06-06 00:49+0200\n"
"POT-Creation-Date: 2011-07-04 22:20+0200\n"
"PO-Revision-Date: 2011-07-04 22:20+0200\n"
"Last-Translator: Fran Diéguez <frandieguez@gnome.org>\n"
"Language-Team: Galician <gnome-l10n-gl@gnome.org>\n"
"Language: gl\n"
@ -362,13 +362,13 @@ msgid "Nothing Scheduled"
msgstr "Nada programado"
#. Translators: Shown on calendar heading when selected day occurs on current year
#: ../js/ui/calendar.js:717 ../js/ui/telepathyClient.js:500
#: ../js/ui/calendar.js:717 ../js/ui/telepathyClient.js:623
msgctxt "calendar heading"
msgid "%A, %B %d"
msgstr "%A, %d de %B"
#. Translators: Shown on calendar heading when selected day occurs on different year
#: ../js/ui/calendar.js:720 ../js/ui/telepathyClient.js:503
#: ../js/ui/calendar.js:720 ../js/ui/telepathyClient.js:626
msgctxt "calendar heading"
msgid "%A, %B %d, %Y"
msgstr "%A, %d de %B de %Y"
@ -389,7 +389,7 @@ msgstr "Esta semana"
msgid "Next week"
msgstr "A vindeira semana"
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1034
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1045
msgid "Remove"
msgstr "Eliminar"
@ -519,45 +519,45 @@ msgid "Restarting the system."
msgstr "Reiniciando o computador."
#: ../js/ui/endSessionDialog.js:410 ../js/ui/polkitAuthenticationAgent.js:170
#: ../js/ui/status/bluetooth.js:488
#: ../js/ui/status/bluetooth.js:497
msgid "Cancel"
msgstr "Cancelar"
#: ../js/ui/lookingGlass.js:594
#: ../js/ui/lookingGlass.js:641
msgid "No extensions installed"
msgstr "Non hai ningunha extensión instalada"
#: ../js/ui/lookingGlass.js:631
#: ../js/ui/lookingGlass.js:678
msgid "Enabled"
msgstr "Activado"
#. translators:
#. * The device has been disabled
#: ../js/ui/lookingGlass.js:633 ../src/gvc/gvc-mixer-control.c:1091
#: ../js/ui/lookingGlass.js:680 ../src/gvc/gvc-mixer-control.c:1091
msgid "Disabled"
msgstr "Desactivado"
#: ../js/ui/lookingGlass.js:635
#: ../js/ui/lookingGlass.js:682
msgid "Error"
msgstr "Erro"
#: ../js/ui/lookingGlass.js:637
#: ../js/ui/lookingGlass.js:684
msgid "Out of date"
msgstr "Desactualizado"
#: ../js/ui/lookingGlass.js:662
#: ../js/ui/lookingGlass.js:709
msgid "View Source"
msgstr "Ver fonte"
#: ../js/ui/lookingGlass.js:668
#: ../js/ui/lookingGlass.js:715
msgid "Web Page"
msgstr "Páxina web"
#: ../js/ui/messageTray.js:1027
#: ../js/ui/messageTray.js:1038
msgid "Open"
msgstr "Abrir"
#: ../js/ui/messageTray.js:2197
#: ../js/ui/messageTray.js:2210
msgid "System Information"
msgstr "Información do sistema"
@ -591,7 +591,7 @@ msgstr "Saír de %s"
msgid "Activities"
msgstr "Actividades"
#: ../js/ui/panel.js:1015
#: ../js/ui/panel.js:1013
msgid "Top Bar"
msgstr "Barra superior"
@ -649,52 +649,53 @@ msgstr "toggle-switch-intl"
msgid "Please enter a command:"
msgstr "Insira unha orde:"
#: ../js/ui/searchDisplay.js:311
#: ../js/ui/searchDisplay.js:318
msgid "Searching..."
msgstr "Buscando..."
#: ../js/ui/searchDisplay.js:325
#: ../js/ui/searchDisplay.js:332
msgid "No matching results."
msgstr "Non hai resultados que coincidan."
#: ../js/ui/statusMenu.js:159 ../js/ui/statusMenu.js:161
#: ../js/ui/statusMenu.js:226
#: ../js/ui/statusMenu.js:202 ../js/ui/statusMenu.js:206
#: ../js/ui/statusMenu.js:272
msgid "Power Off..."
msgstr "Apagar…"
#: ../js/ui/statusMenu.js:161 ../js/ui/statusMenu.js:225
#: ../js/ui/statusMenu.js:204 ../js/ui/statusMenu.js:206
#: ../js/ui/statusMenu.js:271
msgid "Suspend"
msgstr "Suspender"
#: ../js/ui/statusMenu.js:182
#: ../js/ui/statusMenu.js:227
msgid "Available"
msgstr "Dispoñíbel"
#: ../js/ui/statusMenu.js:187
#: ../js/ui/statusMenu.js:232
msgid "Busy"
msgstr "Ocupado"
#: ../js/ui/statusMenu.js:195
#: ../js/ui/statusMenu.js:240
msgid "My Account"
msgstr "A miña conta"
#: ../js/ui/statusMenu.js:199
#: ../js/ui/statusMenu.js:244
msgid "System Settings"
msgstr "Configuracións do sistema"
#: ../js/ui/statusMenu.js:206
#: ../js/ui/statusMenu.js:252
msgid "Lock Screen"
msgstr "Bloquear pantalla"
#: ../js/ui/statusMenu.js:211
#: ../js/ui/statusMenu.js:257
msgid "Switch User"
msgstr "Cambiar de usuario"
#: ../js/ui/statusMenu.js:216
#: ../js/ui/statusMenu.js:262
msgid "Log Out..."
msgstr "Saír da sesión…"
#: ../js/ui/status/accessibility.js:59
#: ../js/ui/status/accessibility.js:60
msgid "Zoom"
msgstr "Ampliación"
@ -704,41 +705,41 @@ msgstr "Ampliación"
#. let screenKeyboard = this._buildItem(_("Screen Keyboard"), APPLICATIONS_SCHEMA,
#. 'screen-keyboard-enabled');
#. this.menu.addMenuItem(screenKeyboard);
#: ../js/ui/status/accessibility.js:74
#: ../js/ui/status/accessibility.js:75
msgid "Visual Alerts"
msgstr "Alertas visuais"
#: ../js/ui/status/accessibility.js:77
#: ../js/ui/status/accessibility.js:78
msgid "Sticky Keys"
msgstr "Teclas persistentes"
#: ../js/ui/status/accessibility.js:80
#: ../js/ui/status/accessibility.js:81
msgid "Slow Keys"
msgstr "Teclas lentas"
#: ../js/ui/status/accessibility.js:83
#: ../js/ui/status/accessibility.js:84
msgid "Bounce Keys"
msgstr "Rebote das teclas"
#: ../js/ui/status/accessibility.js:86
#: ../js/ui/status/accessibility.js:87
msgid "Mouse Keys"
msgstr "Teclas do Rato"
#: ../js/ui/status/accessibility.js:90
#: ../js/ui/status/accessibility.js:91
msgid "Universal Access Settings"
msgstr "Configuracións de acceso universal"
#: ../js/ui/status/accessibility.js:143
#: ../js/ui/status/accessibility.js:145
msgid "High Contrast"
msgstr "Alto contraste"
#: ../js/ui/status/accessibility.js:180
#: ../js/ui/status/accessibility.js:182
msgid "Large Text"
msgstr "Texto máis grande"
#: ../js/ui/status/bluetooth.js:39 ../js/ui/status/bluetooth.js:261
#: ../js/ui/status/bluetooth.js:355 ../js/ui/status/bluetooth.js:389
#: ../js/ui/status/bluetooth.js:429 ../js/ui/status/bluetooth.js:462
#: ../js/ui/status/bluetooth.js:39 ../js/ui/status/bluetooth.js:270
#: ../js/ui/status/bluetooth.js:364 ../js/ui/status/bluetooth.js:398
#: ../js/ui/status/bluetooth.js:438 ../js/ui/status/bluetooth.js:471
msgid "Bluetooth"
msgstr "Bluetooth"
@ -758,102 +759,115 @@ msgstr "Configurar un dispositivo novo…"
msgid "Bluetooth Settings"
msgstr "Configuracións de Bluetooth"
#: ../js/ui/status/bluetooth.js:212
#. TRANSLATORS: this means that bluetooth was disabled by hardware rfkill
#: ../js/ui/status/bluetooth.js:116
msgid "hardware disabled"
msgstr "hardware desactivado"
#: ../js/ui/status/bluetooth.js:217
msgid "Connection"
msgstr "Conexión"
#: ../js/ui/status/bluetooth.js:248
#: ../js/ui/status/bluetooth.js:226 ../js/ui/status/network.js:493
msgid "disconnecting..."
msgstr "conectando…"
#: ../js/ui/status/bluetooth.js:239 ../js/ui/status/network.js:499
msgid "connecting..."
msgstr "conectando…"
#: ../js/ui/status/bluetooth.js:257
msgid "Send Files..."
msgstr "Enviar ficheiros…"
#: ../js/ui/status/bluetooth.js:253
#: ../js/ui/status/bluetooth.js:262
msgid "Browse Files..."
msgstr "Explorar ficheiros…"
#: ../js/ui/status/bluetooth.js:262
#: ../js/ui/status/bluetooth.js:271
msgid "Error browsing device"
msgstr "Produciuse un erro ao explorar o dispositivo"
#: ../js/ui/status/bluetooth.js:263
#: ../js/ui/status/bluetooth.js:272
#, c-format
msgid "The requested device cannot be browsed, error is '%s'"
msgstr "O dispositivo solicitado non pode explorarse, o erro foi «%s»"
#: ../js/ui/status/bluetooth.js:271
#: ../js/ui/status/bluetooth.js:280
msgid "Keyboard Settings"
msgstr "Configuracións do teclado"
#: ../js/ui/status/bluetooth.js:276
#: ../js/ui/status/bluetooth.js:285
msgid "Mouse Settings"
msgstr "Configuracións do rato"
#: ../js/ui/status/bluetooth.js:283 ../js/ui/status/volume.js:63
#: ../js/ui/status/bluetooth.js:292 ../js/ui/status/volume.js:64
msgid "Sound Settings"
msgstr "Configuracións do son"
#: ../js/ui/status/bluetooth.js:390
#: ../js/ui/status/bluetooth.js:399
#, c-format
msgid "Authorization request from %s"
msgstr "Solicitude de autorización de %s"
#: ../js/ui/status/bluetooth.js:396
#: ../js/ui/status/bluetooth.js:405
#, c-format
msgid "Device %s wants access to the service '%s'"
msgstr "O dispositivo %s quere acceder ao servizo «%s»"
#: ../js/ui/status/bluetooth.js:398
#: ../js/ui/status/bluetooth.js:407
msgid "Always grant access"
msgstr "Conceder acceso sempre"
#: ../js/ui/status/bluetooth.js:399
#: ../js/ui/status/bluetooth.js:408
msgid "Grant this time only"
msgstr "Conceder só esta vez"
#: ../js/ui/status/bluetooth.js:400
#: ../js/ui/status/bluetooth.js:409
msgid "Reject"
msgstr "Rexeitar"
#: ../js/ui/status/bluetooth.js:430
#: ../js/ui/status/bluetooth.js:439
#, c-format
msgid "Pairing confirmation for %s"
msgstr "Confirmación de emparellado para «%s»"
#: ../js/ui/status/bluetooth.js:436 ../js/ui/status/bluetooth.js:470
#: ../js/ui/status/bluetooth.js:445 ../js/ui/status/bluetooth.js:479
#, c-format
msgid "Device %s wants to pair with this computer"
msgstr "O dispositivo «%s» quere emparellarse con este equipo"
#: ../js/ui/status/bluetooth.js:437
#: ../js/ui/status/bluetooth.js:446
#, c-format
msgid "Please confirm whether the PIN '%s' matches the one on the device."
msgstr "Confirme que o PIN mostrado en «%s» coincide co do dispositivo."
#: ../js/ui/status/bluetooth.js:439
#: ../js/ui/status/bluetooth.js:448
msgid "Matches"
msgstr "Coincide"
#: ../js/ui/status/bluetooth.js:440
#: ../js/ui/status/bluetooth.js:449
msgid "Does not match"
msgstr "Non coincide"
#: ../js/ui/status/bluetooth.js:463
#: ../js/ui/status/bluetooth.js:472
#, c-format
msgid "Pairing request for %s"
msgstr "Solicitude de emparellamento para «%s»"
#: ../js/ui/status/bluetooth.js:471
#: ../js/ui/status/bluetooth.js:480
msgid "Please enter the PIN mentioned on the device."
msgstr "Introduza o PIN mencionado no dispositivo."
#: ../js/ui/status/bluetooth.js:487
#: ../js/ui/status/bluetooth.js:496
msgid "OK"
msgstr "Aceptar"
#: ../js/ui/status/keyboard.js:70
#: ../js/ui/status/keyboard.js:71
msgid "Show Keyboard Layout..."
msgstr "Mostrar a distribución do teclado…"
#: ../js/ui/status/keyboard.js:73
#: ../js/ui/status/keyboard.js:75
msgid "Localization Settings"
msgstr "Configuracións do son"
@ -862,158 +876,150 @@ msgid "<unknown>"
msgstr "<descoñecida>"
#. Translators: this indicates that wireless or wwan is disabled by hardware killswitch
#: ../js/ui/status/network.js:340
#: ../js/ui/status/network.js:292
msgid "disabled"
msgstr "desactivada"
#. Translators: this is for network devices that are physically present but are not
#. under NetworkManager's control (and thus cannot be used in the menu)
#: ../js/ui/status/network.js:535
#: ../js/ui/status/network.js:491
msgid "unmanaged"
msgstr "non xestionada"
#: ../js/ui/status/network.js:537
msgid "disconnecting..."
msgstr "conectando…"
#: ../js/ui/status/network.js:543
msgid "connecting..."
msgstr "conectando…"
#. Translators: this is for network connections that require some kind of key or password
#: ../js/ui/status/network.js:546
#: ../js/ui/status/network.js:502
msgid "authentication required"
msgstr "autenticación requirida"
#. Translators: this is for devices that require some kind of firmware or kernel
#. module, which is missing
#: ../js/ui/status/network.js:556
#: ../js/ui/status/network.js:512
msgid "firmware missing"
msgstr "falta o «firmware»"
#. Translators: this is for wired network devices that are physically disconnected
#: ../js/ui/status/network.js:563
#: ../js/ui/status/network.js:519
msgid "cable unplugged"
msgstr "cable desconectado"
#. Translators: this is for a network device that cannot be activated (for example it
#. is disabled by rfkill, or it has no coverage
#: ../js/ui/status/network.js:568
#: ../js/ui/status/network.js:524
msgid "unavailable"
msgstr "non dispoñíbel"
#: ../js/ui/status/network.js:570
#: ../js/ui/status/network.js:526
msgid "connection failed"
msgstr "conexión fallida"
#: ../js/ui/status/network.js:582 ../js/ui/status/network.js:1489
msgid "More..."
msgstr "Máis..."
#. TRANSLATORS: this is the indication that a connection for another logged in user is active,
#. and we cannot access its settings (including the name)
#: ../js/ui/status/network.js:651 ../js/ui/status/network.js:1460
#: ../js/ui/status/network.js:618 ../js/ui/status/network.js:1427
msgid "Connected (private)"
msgstr "Conectado (privado)"
#: ../js/ui/status/network.js:736
#: ../js/ui/status/network.js:703
msgid "Auto Ethernet"
msgstr "Ethernet automática"
#: ../js/ui/status/network.js:804
#: ../js/ui/status/network.js:771
msgid "Auto broadband"
msgstr "Banda larga automática"
#: ../js/ui/status/network.js:807
#: ../js/ui/status/network.js:774
msgid "Auto dial-up"
msgstr "Por liña conmutada automática"
#. TRANSLATORS: this the automatic wireless connection name (including the network name)
#: ../js/ui/status/network.js:931 ../js/ui/status/network.js:1472
#: ../js/ui/status/network.js:898 ../js/ui/status/network.js:1439
#, c-format
msgid "Auto %s"
msgstr "%s automática"
#: ../js/ui/status/network.js:933
#: ../js/ui/status/network.js:900
msgid "Auto bluetooth"
msgstr "Bluetooth automática"
#: ../js/ui/status/network.js:1474
#: ../js/ui/status/network.js:1441
msgid "Auto wireless"
msgstr "Sen fíos automática"
#: ../js/ui/status/network.js:1522
msgid "More..."
msgstr "Máis..."
#: ../js/ui/status/network.js:1564
#: ../js/ui/status/network.js:1531
msgid "Enable networking"
msgstr "Activar rede"
#: ../js/ui/status/network.js:1576
#: ../js/ui/status/network.js:1543
msgid "Wired"
msgstr "Con fíos"
#: ../js/ui/status/network.js:1587
#: ../js/ui/status/network.js:1554
msgid "Wireless"
msgstr "Sen fíos"
#: ../js/ui/status/network.js:1597
#: ../js/ui/status/network.js:1564
msgid "Mobile broadband"
msgstr "Banda larga móbil"
#: ../js/ui/status/network.js:1607
#: ../js/ui/status/network.js:1574
msgid "VPN Connections"
msgstr "Conexións VPN"
#: ../js/ui/status/network.js:1619
#: ../js/ui/status/network.js:1586
msgid "Network Settings"
msgstr "Configuracións da rede"
#: ../js/ui/status/network.js:1910
#: ../js/ui/status/network.js:1878
#, c-format
msgid "You're now connected to mobile broadband connection '%s'"
msgstr "Vostede está conectado agora á conexión de banda larga móbil «%s»"
#: ../js/ui/status/network.js:1914
#: ../js/ui/status/network.js:1882
#, c-format
msgid "You're now connected to wireless network '%s'"
msgstr "Vostede está conectado agora á conexión sen fíos «%s»"
#: ../js/ui/status/network.js:1918
#: ../js/ui/status/network.js:1886
#, c-format
msgid "You're now connected to wired network '%s'"
msgstr "Vostede está conectado agora á conexión con fíos «%s»"
#: ../js/ui/status/network.js:1922
#: ../js/ui/status/network.js:1890
#, c-format
msgid "You're now connected to VPN network '%s'"
msgstr "Vostede está conectado agora á conexión VPN «%s»"
#: ../js/ui/status/network.js:1927
#: ../js/ui/status/network.js:1895
#, c-format
msgid "You're now connected to '%s'"
msgstr "Vostede está conectado agora a «%s»"
#: ../js/ui/status/network.js:1935
#: ../js/ui/status/network.js:1903
msgid "Connection established"
msgstr "Conexión estabelecida"
#: ../js/ui/status/network.js:2061
#: ../js/ui/status/network.js:2029
msgid "Networking is disabled"
msgstr "A rede está desactivada"
#: ../js/ui/status/network.js:2186
#: ../js/ui/status/network.js:2154
msgid "Network Manager"
msgstr "Xestor de rede"
#: ../js/ui/status/power.js:82
#: ../js/ui/status/power.js:83
msgid "Power Settings"
msgstr "Configuracións de enerxía"
#. 0 is reported when UPower does not have enough data
#. to estimate battery life
#: ../js/ui/status/power.js:108
#: ../js/ui/status/power.js:110
msgid "Estimating..."
msgstr "Estimando…"
#: ../js/ui/status/power.js:115
#: ../js/ui/status/power.js:117
#, c-format
msgid "%d hour remaining"
msgid_plural "%d hours remaining"
@ -1021,102 +1027,102 @@ msgstr[0] "%d hora restante"
msgstr[1] "%d horas restante"
#. TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining"
#: ../js/ui/status/power.js:118
#: ../js/ui/status/power.js:120
#, c-format
msgid "%d %s %d %s remaining"
msgstr "%d %s %d %s retante"
#: ../js/ui/status/power.js:120
#: ../js/ui/status/power.js:122
msgid "hour"
msgid_plural "hours"
msgstr[0] "hora"
msgstr[1] "horas"
#: ../js/ui/status/power.js:120
#: ../js/ui/status/power.js:122
msgid "minute"
msgid_plural "minutes"
msgstr[0] "minuto"
msgstr[1] "minutos"
#: ../js/ui/status/power.js:123
#: ../js/ui/status/power.js:125
#, c-format
msgid "%d minute remaining"
msgid_plural "%d minutes remaining"
msgstr[0] "%d minuto restante"
msgstr[1] "%d minutos restantes"
#: ../js/ui/status/power.js:225
#: ../js/ui/status/power.js:227
msgid "AC adapter"
msgstr "Adaptador de corrente"
#: ../js/ui/status/power.js:227
#: ../js/ui/status/power.js:229
msgid "Laptop battery"
msgstr "Batería do portátil"
#: ../js/ui/status/power.js:229
#: ../js/ui/status/power.js:231
msgid "UPS"
msgstr "UPS"
#: ../js/ui/status/power.js:231
#: ../js/ui/status/power.js:233
msgid "Monitor"
msgstr "Monitor"
#: ../js/ui/status/power.js:233
#: ../js/ui/status/power.js:235
msgid "Mouse"
msgstr "Rato"
#: ../js/ui/status/power.js:235
#: ../js/ui/status/power.js:237
msgid "Keyboard"
msgstr "Teclado"
#: ../js/ui/status/power.js:237
#: ../js/ui/status/power.js:239
msgid "PDA"
msgstr "PDA"
#: ../js/ui/status/power.js:239
#: ../js/ui/status/power.js:241
msgid "Cell phone"
msgstr "Teléfono móbil"
#: ../js/ui/status/power.js:241
#: ../js/ui/status/power.js:243
msgid "Media player"
msgstr "Reprodutor multimedia"
#: ../js/ui/status/power.js:243
#: ../js/ui/status/power.js:245
msgid "Tablet"
msgstr "Tablet"
#: ../js/ui/status/power.js:245
#: ../js/ui/status/power.js:247
msgid "Computer"
msgstr "Computador"
#: ../js/ui/status/power.js:247 ../src/shell-app-system.c:1088
#: ../js/ui/status/power.js:249 ../src/shell-app-system.c:1088
msgid "Unknown"
msgstr "Descoñecido"
#: ../js/ui/status/volume.js:42
#: ../js/ui/status/volume.js:43
msgid "Volume"
msgstr "Volume"
#: ../js/ui/status/volume.js:55
#: ../js/ui/status/volume.js:56
msgid "Microphone"
msgstr "Micrófono"
#: ../js/ui/telepathyClient.js:339
#: ../js/ui/telepathyClient.js:419
#, c-format
msgid "%s is online."
msgstr "%s está conectado/a."
#: ../js/ui/telepathyClient.js:344
#: ../js/ui/telepathyClient.js:424
#, c-format
msgid "%s is offline."
msgstr "%s está desconectado/a."
#: ../js/ui/telepathyClient.js:347
#: ../js/ui/telepathyClient.js:427
#, c-format
msgid "%s is away."
msgstr "%s está ausente."
#: ../js/ui/telepathyClient.js:350
#: ../js/ui/telepathyClient.js:430
#, c-format
msgid "%s is busy."
msgstr "%s está ocupado/a."
@ -1124,14 +1130,14 @@ msgstr "%s está ocupado/a."
#. Translators: this is a time format string followed by a date.
#. If applicable, replace %X with a strftime format valid for your
#. locale, without seconds.
#: ../js/ui/telepathyClient.js:492
#: ../js/ui/telepathyClient.js:615
#, no-c-format
msgid "Sent at %X on %A"
msgstr "Enviado ás %X o %A"
#. Translators: this is the other person changing their old IM name to their new
#. IM name.
#: ../js/ui/telepathyClient.js:537
#: ../js/ui/telepathyClient.js:665
#, c-format
msgid "%s is now known as %s"
msgstr "%s é coñecido agora como %s"
@ -1144,7 +1150,7 @@ msgstr "%s é coñecido agora como %s"
msgid "Type to search..."
msgstr "Teclear para buscar…"
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:254
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:257
msgid "Search"
msgstr "Buscar"
@ -1201,13 +1207,13 @@ msgstr "Predeterminado"
msgid "Authentication dialog was dismissed by the user"
msgstr "O usuario rexeitou o diálogo de autenticación"
#: ../src/shell-util.c:93
#: ../src/shell-util.c:96
msgid "Home Folder"
msgstr "Cartafol persoal"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-util.c:108
#: ../src/shell-util.c:111
msgid "File System"
msgstr "Sistema de ficheiros"
@ -1216,7 +1222,7 @@ msgstr "Sistema de ficheiros"
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-util.c:304
#: ../src/shell-util.c:307
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"

347
po/he.po
View File

@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gnome-shell master\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-06-04 15:05+0300\n"
"PO-Revision-Date: 2011-06-04 15:06+0200\n"
"POT-Creation-Date: 2011-07-16 17:26+0300\n"
"PO-Revision-Date: 2011-07-16 17:27+0200\n"
"Last-Translator: Yaron Shahrabani <sh.yaron@gmail.com>\n"
"Language-Team: Hebrew <sh.yaron@gmail.com>\n"
"Language: he\n"
@ -204,27 +204,27 @@ msgid "Execution of '%s' failed:"
msgstr "ההרצה של '%s' נכשלה:"
#. Translators: Filter to display all applications
#: ../js/ui/appDisplay.js:258
#: ../js/ui/appDisplay.js:252
msgid "All"
msgstr "הכול"
#: ../js/ui/appDisplay.js:357
#: ../js/ui/appDisplay.js:351
msgid "APPLICATIONS"
msgstr "יישומים"
#: ../js/ui/appDisplay.js:383
#: ../js/ui/appDisplay.js:377
msgid "SETTINGS"
msgstr "הגדרות"
#: ../js/ui/appDisplay.js:656
#: ../js/ui/appDisplay.js:650
msgid "New Window"
msgstr "חלון חדש"
#: ../js/ui/appDisplay.js:659
#: ../js/ui/appDisplay.js:653
msgid "Remove from Favorites"
msgstr "הסרה מהמועדפים"
#: ../js/ui/appDisplay.js:660
#: ../js/ui/appDisplay.js:654
msgid "Add to Favorites"
msgstr "הוספה למועדפים"
@ -357,13 +357,13 @@ msgid "Nothing Scheduled"
msgstr "היומן ריק"
#. Translators: Shown on calendar heading when selected day occurs on current year
#: ../js/ui/calendar.js:717 ../js/ui/telepathyClient.js:500
#: ../js/ui/calendar.js:717 ../js/ui/telepathyClient.js:749
msgctxt "calendar heading"
msgid "%A, %B %d"
msgstr "%A, ה־%e ב%B"
#. Translators: Shown on calendar heading when selected day occurs on different year
#: ../js/ui/calendar.js:720 ../js/ui/telepathyClient.js:503
#: ../js/ui/calendar.js:720 ../js/ui/telepathyClient.js:752
msgctxt "calendar heading"
msgid "%A, %B %d, %Y"
msgstr "%A, ה־%e ב%B, %Y"
@ -384,7 +384,7 @@ msgstr "השבוע"
msgid "Next week"
msgstr "בשבוע הבא"
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1034
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1122
msgid "Remove"
msgstr "הסרה"
@ -510,45 +510,45 @@ msgid "Restarting the system."
msgstr "המערכת מופעלת מחדש"
#: ../js/ui/endSessionDialog.js:410 ../js/ui/polkitAuthenticationAgent.js:170
#: ../js/ui/status/bluetooth.js:488
#: ../js/ui/status/bluetooth.js:497
msgid "Cancel"
msgstr "ביטול"
#: ../js/ui/lookingGlass.js:594
#: ../js/ui/lookingGlass.js:641
msgid "No extensions installed"
msgstr "לא מותקנות הרחבות"
#: ../js/ui/lookingGlass.js:631
#: ../js/ui/lookingGlass.js:678
msgid "Enabled"
msgstr "פעיל"
#. translators:
#. * The device has been disabled
#: ../js/ui/lookingGlass.js:633 ../src/gvc/gvc-mixer-control.c:1091
#: ../js/ui/lookingGlass.js:680 ../src/gvc/gvc-mixer-control.c:1091
msgid "Disabled"
msgstr "מנוטרל"
#: ../js/ui/lookingGlass.js:635
#: ../js/ui/lookingGlass.js:682
msgid "Error"
msgstr "שגיאה"
#: ../js/ui/lookingGlass.js:637
#: ../js/ui/lookingGlass.js:684
msgid "Out of date"
msgstr "לא בתוקף"
#: ../js/ui/lookingGlass.js:662
#: ../js/ui/lookingGlass.js:709
msgid "View Source"
msgstr "צפייה במקור"
#: ../js/ui/lookingGlass.js:668
#: ../js/ui/lookingGlass.js:715
msgid "Web Page"
msgstr "דף אינטרנט"
#: ../js/ui/messageTray.js:1027
#: ../js/ui/messageTray.js:1115
msgid "Open"
msgstr "פתיחה"
#: ../js/ui/messageTray.js:2197
#: ../js/ui/messageTray.js:2286
msgid "System Information"
msgstr "פרטי המערכת"
@ -571,18 +571,18 @@ msgid "Dash"
msgstr "חלונית"
#. TODO - _quit() doesn't really work on apps in state STARTING yet
#: ../js/ui/panel.js:533
#: ../js/ui/panel.js:532
#, c-format
msgid "Quit %s"
msgstr "יציאה מ־%s"
#. 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:913
#. Translators: If there is no suitable word for "Activities"
#. in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:568
msgid "Activities"
msgstr "פעילויות"
#: ../js/ui/panel.js:1015
#: ../js/ui/panel.js:951
msgid "Top Bar"
msgstr "הסרגל העליון"
@ -640,52 +640,53 @@ msgstr "toggle-switch-intl"
msgid "Please enter a command:"
msgstr "נא להזין פקודה:"
#: ../js/ui/searchDisplay.js:311
#: ../js/ui/searchDisplay.js:318
msgid "Searching..."
msgstr "בחיפוש..."
#: ../js/ui/searchDisplay.js:325
#: ../js/ui/searchDisplay.js:332
msgid "No matching results."
msgstr "אין תוצאות תואמות."
#: ../js/ui/statusMenu.js:159 ../js/ui/statusMenu.js:161
#: ../js/ui/statusMenu.js:226
#: ../js/ui/statusMenu.js:192 ../js/ui/statusMenu.js:196
#: ../js/ui/statusMenu.js:262
msgid "Power Off..."
msgstr "כיבוי..."
#: ../js/ui/statusMenu.js:161 ../js/ui/statusMenu.js:225
#: ../js/ui/statusMenu.js:194 ../js/ui/statusMenu.js:196
#: ../js/ui/statusMenu.js:261
msgid "Suspend"
msgstr "השהיה"
#: ../js/ui/statusMenu.js:182
#: ../js/ui/statusMenu.js:217
msgid "Available"
msgstr "פנוי"
#: ../js/ui/statusMenu.js:187
#: ../js/ui/statusMenu.js:222
msgid "Busy"
msgstr "עסוק"
#: ../js/ui/statusMenu.js:195
#: ../js/ui/statusMenu.js:230
msgid "My Account"
msgstr "החשבון שלי"
#: ../js/ui/statusMenu.js:199
#: ../js/ui/statusMenu.js:234
msgid "System Settings"
msgstr "הגדרות המערכת"
#: ../js/ui/statusMenu.js:206
#: ../js/ui/statusMenu.js:242
msgid "Lock Screen"
msgstr "נעילת המסך"
#: ../js/ui/statusMenu.js:211
#: ../js/ui/statusMenu.js:247
msgid "Switch User"
msgstr "החלפת משתמש"
#: ../js/ui/statusMenu.js:216
#: ../js/ui/statusMenu.js:252
msgid "Log Out..."
msgstr "ניתוק..."
#: ../js/ui/status/accessibility.js:59
#: ../js/ui/status/accessibility.js:60
msgid "Zoom"
msgstr "תקריב"
@ -695,41 +696,41 @@ msgstr "תקריב"
#. let screenKeyboard = this._buildItem(_("Screen Keyboard"), APPLICATIONS_SCHEMA,
#. 'screen-keyboard-enabled');
#. this.menu.addMenuItem(screenKeyboard);
#: ../js/ui/status/accessibility.js:74
#: ../js/ui/status/accessibility.js:75
msgid "Visual Alerts"
msgstr "התראות חזותיות"
#: ../js/ui/status/accessibility.js:77
#: ../js/ui/status/accessibility.js:78
msgid "Sticky Keys"
msgstr "מקשים דביקים"
#: ../js/ui/status/accessibility.js:80
#: ../js/ui/status/accessibility.js:81
msgid "Slow Keys"
msgstr "מקשים אטיים"
#: ../js/ui/status/accessibility.js:83
#: ../js/ui/status/accessibility.js:84
msgid "Bounce Keys"
msgstr "מקשים קופצים"
#: ../js/ui/status/accessibility.js:86
#: ../js/ui/status/accessibility.js:87
msgid "Mouse Keys"
msgstr "מקשי עכבר"
#: ../js/ui/status/accessibility.js:90
#: ../js/ui/status/accessibility.js:91
msgid "Universal Access Settings"
msgstr "הגדרות גישה אוניברסלית"
#: ../js/ui/status/accessibility.js:143
#: ../js/ui/status/accessibility.js:145
msgid "High Contrast"
msgstr "ניגודיות גבוהה"
#: ../js/ui/status/accessibility.js:180
#: ../js/ui/status/accessibility.js:182
msgid "Large Text"
msgstr "טקסט גדול"
#: ../js/ui/status/bluetooth.js:39 ../js/ui/status/bluetooth.js:261
#: ../js/ui/status/bluetooth.js:355 ../js/ui/status/bluetooth.js:389
#: ../js/ui/status/bluetooth.js:429 ../js/ui/status/bluetooth.js:462
#: ../js/ui/status/bluetooth.js:39 ../js/ui/status/bluetooth.js:270
#: ../js/ui/status/bluetooth.js:364 ../js/ui/status/bluetooth.js:398
#: ../js/ui/status/bluetooth.js:438 ../js/ui/status/bluetooth.js:471
msgid "Bluetooth"
msgstr "Bluetooth"
@ -749,102 +750,115 @@ msgstr "הגדרת התקן חדש..."
msgid "Bluetooth Settings"
msgstr "הגדרות Bluetooth"
#: ../js/ui/status/bluetooth.js:212
#. TRANSLATORS: this means that bluetooth was disabled by hardware rfkill
#: ../js/ui/status/bluetooth.js:116
msgid "hardware disabled"
msgstr "מנוטרל חומרתית"
#: ../js/ui/status/bluetooth.js:217
msgid "Connection"
msgstr "חיבור"
#: ../js/ui/status/bluetooth.js:248
#: ../js/ui/status/bluetooth.js:226 ../js/ui/status/network.js:493
msgid "disconnecting..."
msgstr "בהליכי ניתוק..."
#: ../js/ui/status/bluetooth.js:239 ../js/ui/status/network.js:499
msgid "connecting..."
msgstr "בהתחברות..."
#: ../js/ui/status/bluetooth.js:257
msgid "Send Files..."
msgstr "שליחת קבצים..."
#: ../js/ui/status/bluetooth.js:253
#: ../js/ui/status/bluetooth.js:262
msgid "Browse Files..."
msgstr "עיון בקבצים..."
#: ../js/ui/status/bluetooth.js:262
#: ../js/ui/status/bluetooth.js:271
msgid "Error browsing device"
msgstr "שגיאה בעיון בהתקן"
#: ../js/ui/status/bluetooth.js:263
#: ../js/ui/status/bluetooth.js:272
#, c-format
msgid "The requested device cannot be browsed, error is '%s'"
msgstr "לא ניתן לעיין בהתקן הנבחר, השגיאה היא '%s'"
#: ../js/ui/status/bluetooth.js:271
#: ../js/ui/status/bluetooth.js:280
msgid "Keyboard Settings"
msgstr "הגדרות מקלדת"
#: ../js/ui/status/bluetooth.js:276
#: ../js/ui/status/bluetooth.js:285
msgid "Mouse Settings"
msgstr "הגדרות עכבר"
#: ../js/ui/status/bluetooth.js:283 ../js/ui/status/volume.js:63
#: ../js/ui/status/bluetooth.js:292 ../js/ui/status/volume.js:64
msgid "Sound Settings"
msgstr "הגדרות שמע"
#: ../js/ui/status/bluetooth.js:390
#: ../js/ui/status/bluetooth.js:399
#, c-format
msgid "Authorization request from %s"
msgstr "בקשת אישור מאת %s"
#: ../js/ui/status/bluetooth.js:396
#: ../js/ui/status/bluetooth.js:405
#, c-format
msgid "Device %s wants access to the service '%s'"
msgstr "ההתקן %s מעוניין לגשת אל השירות '%s'"
#: ../js/ui/status/bluetooth.js:398
#: ../js/ui/status/bluetooth.js:407
msgid "Always grant access"
msgstr "תמיד להעניק גישה"
#: ../js/ui/status/bluetooth.js:399
#: ../js/ui/status/bluetooth.js:408
msgid "Grant this time only"
msgstr "הענקת גישה הפעם בלבד"
#: ../js/ui/status/bluetooth.js:400
#: ../js/ui/status/bluetooth.js:409 ../js/ui/telepathyClient.js:954
msgid "Reject"
msgstr "סירוב"
#: ../js/ui/status/bluetooth.js:430
#: ../js/ui/status/bluetooth.js:439
#, c-format
msgid "Pairing confirmation for %s"
msgstr "אישור צימוד עבור %s"
#: ../js/ui/status/bluetooth.js:436 ../js/ui/status/bluetooth.js:470
#: ../js/ui/status/bluetooth.js:445 ../js/ui/status/bluetooth.js:479
#, c-format
msgid "Device %s wants to pair with this computer"
msgstr "ההתקן %s מעוניין בצימוד עם מחשב זה"
#: ../js/ui/status/bluetooth.js:437
#: ../js/ui/status/bluetooth.js:446
#, c-format
msgid "Please confirm whether the PIN '%s' matches the one on the device."
msgstr "נא לאשר האם קוד ה־PIN '%s' תואם את זה שמופיע בהתקן."
#: ../js/ui/status/bluetooth.js:439
#: ../js/ui/status/bluetooth.js:448
msgid "Matches"
msgstr "התאמות"
#: ../js/ui/status/bluetooth.js:440
#: ../js/ui/status/bluetooth.js:449
msgid "Does not match"
msgstr "אינו תואם"
#: ../js/ui/status/bluetooth.js:463
#: ../js/ui/status/bluetooth.js:472
#, c-format
msgid "Pairing request for %s"
msgstr "בקשת צימוד עבור %s"
#: ../js/ui/status/bluetooth.js:471
#: ../js/ui/status/bluetooth.js:480
msgid "Please enter the PIN mentioned on the device."
msgstr "נא להזין את קוד ה־PIN המוזכר בהתקן."
#: ../js/ui/status/bluetooth.js:487
#: ../js/ui/status/bluetooth.js:496
msgid "OK"
msgstr "אישור"
#: ../js/ui/status/keyboard.js:70
#: ../js/ui/status/keyboard.js:71
msgid "Show Keyboard Layout..."
msgstr "הצגת פריסת המקלדת..."
#: ../js/ui/status/keyboard.js:73
#: ../js/ui/status/keyboard.js:75
msgid "Localization Settings"
msgstr "הגדרות אזוריות"
@ -853,158 +867,150 @@ msgid "<unknown>"
msgstr "<לא ידוע>"
#. Translators: this indicates that wireless or wwan is disabled by hardware killswitch
#: ../js/ui/status/network.js:340
#: ../js/ui/status/network.js:292
msgid "disabled"
msgstr "מנוטרל"
#. Translators: this is for network devices that are physically present but are not
#. under NetworkManager's control (and thus cannot be used in the menu)
#: ../js/ui/status/network.js:535
#: ../js/ui/status/network.js:491
msgid "unmanaged"
msgstr "לא מנוהל"
#: ../js/ui/status/network.js:537
msgid "disconnecting..."
msgstr "בהליכי ניתוק..."
#: ../js/ui/status/network.js:543
msgid "connecting..."
msgstr "בהתחברות..."
#. Translators: this is for network connections that require some kind of key or password
#: ../js/ui/status/network.js:546
#: ../js/ui/status/network.js:502
msgid "authentication required"
msgstr "נדרש אימות"
#. Translators: this is for devices that require some kind of firmware or kernel
#. module, which is missing
#: ../js/ui/status/network.js:556
#: ../js/ui/status/network.js:512
msgid "firmware missing"
msgstr "הקושחה חסרה"
#. Translators: this is for wired network devices that are physically disconnected
#: ../js/ui/status/network.js:563
#: ../js/ui/status/network.js:519
msgid "cable unplugged"
msgstr "הכבל מנותק"
#. Translators: this is for a network device that cannot be activated (for example it
#. is disabled by rfkill, or it has no coverage
#: ../js/ui/status/network.js:568
#: ../js/ui/status/network.js:524
msgid "unavailable"
msgstr "לא זמין"
#: ../js/ui/status/network.js:570
#: ../js/ui/status/network.js:526
msgid "connection failed"
msgstr "החיבור נכשל"
#: ../js/ui/status/network.js:582 ../js/ui/status/network.js:1507
msgid "More..."
msgstr "עוד..."
#. TRANSLATORS: this is the indication that a connection for another logged in user is active,
#. and we cannot access its settings (including the name)
#: ../js/ui/status/network.js:651 ../js/ui/status/network.js:1460
#: ../js/ui/status/network.js:618 ../js/ui/status/network.js:1444
msgid "Connected (private)"
msgstr "מחובר (פרטי)"
#: ../js/ui/status/network.js:736
#: ../js/ui/status/network.js:703
msgid "Auto Ethernet"
msgstr "אתרנט אוטומטי"
#: ../js/ui/status/network.js:804
#: ../js/ui/status/network.js:771
msgid "Auto broadband"
msgstr "פס רחב אוטומטי"
#: ../js/ui/status/network.js:807
#: ../js/ui/status/network.js:774
msgid "Auto dial-up"
msgstr "חיוג אוטומטי"
#. TRANSLATORS: this the automatic wireless connection name (including the network name)
#: ../js/ui/status/network.js:931 ../js/ui/status/network.js:1472
#: ../js/ui/status/network.js:898 ../js/ui/status/network.js:1456
#, c-format
msgid "Auto %s"
msgstr "%s אוטומטי"
#: ../js/ui/status/network.js:933
#: ../js/ui/status/network.js:900
msgid "Auto bluetooth"
msgstr "Bluetooth אוטומטי"
#: ../js/ui/status/network.js:1474
#: ../js/ui/status/network.js:1458
msgid "Auto wireless"
msgstr "אלחוטי אוטומטי"
#: ../js/ui/status/network.js:1522
msgid "More..."
msgstr "עוד..."
#: ../js/ui/status/network.js:1564
#: ../js/ui/status/network.js:1550
msgid "Enable networking"
msgstr "הפעלת תכונת הרשת"
#: ../js/ui/status/network.js:1576
#: ../js/ui/status/network.js:1562
msgid "Wired"
msgstr "קווי"
#: ../js/ui/status/network.js:1587
#: ../js/ui/status/network.js:1573
msgid "Wireless"
msgstr "אלחוטי"
#: ../js/ui/status/network.js:1597
#: ../js/ui/status/network.js:1583
msgid "Mobile broadband"
msgstr "פס־רחב נייד"
#: ../js/ui/status/network.js:1607
#: ../js/ui/status/network.js:1593
msgid "VPN Connections"
msgstr "חיבורי VPN"
#: ../js/ui/status/network.js:1619
#: ../js/ui/status/network.js:1605
msgid "Network Settings"
msgstr "הגדרות הרשת"
#: ../js/ui/status/network.js:1910
#: ../js/ui/status/network.js:1897
#, c-format
msgid "You're now connected to mobile broadband connection '%s'"
msgstr "כרגע ישנו חיבור בינך ובין רשת הפס הרחב הניידת '%s'"
#: ../js/ui/status/network.js:1914
#: ../js/ui/status/network.js:1901
#, c-format
msgid "You're now connected to wireless network '%s'"
msgstr "כרגע ישנו חיבור בינך ובין הרשת האלחוטית '%s'"
#: ../js/ui/status/network.js:1918
#: ../js/ui/status/network.js:1905
#, c-format
msgid "You're now connected to wired network '%s'"
msgstr "כרגע ישנו חיבור בינך ובין הרשת הקווית '%s'"
#: ../js/ui/status/network.js:1922
#: ../js/ui/status/network.js:1909
#, c-format
msgid "You're now connected to VPN network '%s'"
msgstr "כרגע ישנו חיבור בינך ובין רשת ה־VPN '%s'"
#: ../js/ui/status/network.js:1927
#: ../js/ui/status/network.js:1914
#, c-format
msgid "You're now connected to '%s'"
msgstr "כעת ישנו חיבור בינך ובין '%s'"
#: ../js/ui/status/network.js:1935
#: ../js/ui/status/network.js:1922
msgid "Connection established"
msgstr "ההתחברות הצליחה"
#: ../js/ui/status/network.js:2061
#: ../js/ui/status/network.js:2048
msgid "Networking is disabled"
msgstr "תכונת הרשת מנוטרלת"
#: ../js/ui/status/network.js:2186
#: ../js/ui/status/network.js:2173
msgid "Network Manager"
msgstr "מנהל הרשתות"
#: ../js/ui/status/power.js:82
#: ../js/ui/status/power.js:83
msgid "Power Settings"
msgstr "הגדרות צריכת החשמל"
#. 0 is reported when UPower does not have enough data
#. to estimate battery life
#: ../js/ui/status/power.js:108
#: ../js/ui/status/power.js:109
msgid "Estimating..."
msgstr "מתבצע שיערוך..."
#: ../js/ui/status/power.js:115
#: ../js/ui/status/power.js:116
#, c-format
msgid "%d hour remaining"
msgid_plural "%d hours remaining"
@ -1013,26 +1019,26 @@ msgstr[1] "נותרו %d שעות"
msgstr[2] "נותרו שעתיים"
#. TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining"
#: ../js/ui/status/power.js:118
#: ../js/ui/status/power.js:119
#, c-format
msgid "%d %s %d %s remaining"
msgstr "%d %s %d %s נותרו"
#: ../js/ui/status/power.js:120
#: ../js/ui/status/power.js:121
msgid "hour"
msgid_plural "hours"
msgstr[0] "שעה"
msgstr[1] "שעות"
msgstr[2] "שעתיים"
#: ../js/ui/status/power.js:120
#: ../js/ui/status/power.js:121
msgid "minute"
msgid_plural "minutes"
msgstr[0] "דקה"
msgstr[1] "דקות"
msgstr[2] "דקות"
#: ../js/ui/status/power.js:123
#: ../js/ui/status/power.js:124
#, c-format
msgid "%d minute remaining"
msgid_plural "%d minutes remaining"
@ -1040,78 +1046,90 @@ msgstr[0] "דקה אחת נותרה"
msgstr[1] "%d דקות נותרו"
msgstr[2] "שתי דקות נותרו"
#: ../js/ui/status/power.js:225
#: ../js/ui/status/power.js:216
msgid "AC adapter"
msgstr "מתאם חשמל"
#: ../js/ui/status/power.js:227
#: ../js/ui/status/power.js:218
msgid "Laptop battery"
msgstr "סוללת נייד"
#: ../js/ui/status/power.js:229
#: ../js/ui/status/power.js:220
msgid "UPS"
msgstr "אל־פסק"
#: ../js/ui/status/power.js:231
#: ../js/ui/status/power.js:222
msgid "Monitor"
msgstr "צג"
#: ../js/ui/status/power.js:233
#: ../js/ui/status/power.js:224
msgid "Mouse"
msgstr "עכבר"
#: ../js/ui/status/power.js:235
#: ../js/ui/status/power.js:226
msgid "Keyboard"
msgstr "מקלדת"
#: ../js/ui/status/power.js:237
#: ../js/ui/status/power.js:228
msgid "PDA"
msgstr "מחשב כף יד"
#: ../js/ui/status/power.js:239
#: ../js/ui/status/power.js:230
msgid "Cell phone"
msgstr "טלפון סלולרי"
#: ../js/ui/status/power.js:241
#: ../js/ui/status/power.js:232
msgid "Media player"
msgstr "נגן מדיה"
#: ../js/ui/status/power.js:243
#: ../js/ui/status/power.js:234
msgid "Tablet"
msgstr "טבלת שליטה"
#: ../js/ui/status/power.js:245
#: ../js/ui/status/power.js:236
msgid "Computer"
msgstr "מחשב"
#: ../js/ui/status/power.js:247 ../src/shell-app-system.c:1088
#: ../js/ui/status/power.js:238 ../src/shell-app-system.c:1088
msgid "Unknown"
msgstr "לא ידוע"
#: ../js/ui/status/volume.js:42
#: ../js/ui/status/volume.js:43
msgid "Volume"
msgstr "עצמה"
#: ../js/ui/status/volume.js:55
#: ../js/ui/status/volume.js:56
msgid "Microphone"
msgstr "מיקרופון"
#: ../js/ui/telepathyClient.js:339
#. We got the TpContact
#. FIXME: We don't have a 'chat room' icon (bgo #653737) use
#. system-users for now as Empathy does.
#: ../js/ui/telepathyClient.js:222
msgid "Invitation"
msgstr "הזמנה"
#. We got the TpContact
#: ../js/ui/telepathyClient.js:285
msgid "Call"
msgstr "התקשרות"
#: ../js/ui/telepathyClient.js:541
#, c-format
msgid "%s is online."
msgstr "%s התחבר/ה."
#: ../js/ui/telepathyClient.js:344
#: ../js/ui/telepathyClient.js:546
#, c-format
msgid "%s is offline."
msgstr "%s התנתק/ה."
#: ../js/ui/telepathyClient.js:347
#: ../js/ui/telepathyClient.js:549
#, c-format
msgid "%s is away."
msgstr "'%s' מרוחק/ת."
#: ../js/ui/telepathyClient.js:350
#: ../js/ui/telepathyClient.js:552
#, c-format
msgid "%s is busy."
msgstr "%s עסוק/ה."
@ -1119,18 +1137,57 @@ msgstr "%s עסוק/ה."
#. Translators: this is a time format string followed by a date.
#. If applicable, replace %X with a strftime format valid for your
#. locale, without seconds.
#: ../js/ui/telepathyClient.js:492
#: ../js/ui/telepathyClient.js:741
#, no-c-format
msgid "Sent at %X on %A"
msgstr "נשלח ב־%X בשעה %A"
#. Translators: this is the other person changing their old IM name to their new
#. IM name.
#: ../js/ui/telepathyClient.js:537
#: ../js/ui/telepathyClient.js:791
#, c-format
msgid "%s is now known as %s"
msgstr "השם של %s הוחלף ל־%s"
#. translators: argument is a room name like
#. * room@jabber.org for example.
#: ../js/ui/telepathyClient.js:898
#, c-format
msgid "Invitation to %s"
msgstr "הזמנה ל־%s"
#. translators: first argument is the name of a contact and the second
#. * one the name of a room. "Alice is inviting you to join room@jabber.org
#. * for example.
#: ../js/ui/telepathyClient.js:906
#, c-format
msgid "%s is inviting you to join %s"
msgstr "הוזמנת על ידי %s להצטרף אל %s"
#: ../js/ui/telepathyClient.js:908
msgid "Decline"
msgstr "דחייה"
#: ../js/ui/telepathyClient.js:909
msgid "Accept"
msgstr "אישור"
#. translators: argument is a contact name like Alice for example.
#: ../js/ui/telepathyClient.js:942
#, c-format
msgid "Video call from %s"
msgstr "שיחת וידאו מאת %s"
#. translators: argument is a contact name like Alice for example.
#: ../js/ui/telepathyClient.js:945
#, c-format
msgid "Call from %s"
msgstr "שיחה מאת %s"
#: ../js/ui/telepathyClient.js:955
msgid "Answer"
msgstr "מענה"
#. Translators: this is the text displayed
#. in the search entry when no search is
#. active; it should not exceed ~30
@ -1139,7 +1196,7 @@ msgstr "השם של %s הוחלף ל־%s"
msgid "Type to search..."
msgstr "יש להקליד כדי לחפש..."
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:254
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:257
msgid "Search"
msgstr "חיפוש"
@ -1177,7 +1234,7 @@ msgstr[2] "2 קלטים"
msgid "System Sounds"
msgstr "צלילי מערכת"
#: ../src/main.c:445
#: ../src/main.c:466
msgid "Print version"
msgstr "Print version"
@ -1198,13 +1255,13 @@ msgstr "בררת מחדל"
msgid "Authentication dialog was dismissed by the user"
msgstr "המשתמש בחר להתעלם מתיבת דו־שיח האימות"
#: ../src/shell-util.c:93
#: ../src/shell-util.c:96
msgid "Home Folder"
msgstr "תיקיית הבית"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-util.c:108
#: ../src/shell-util.c:111
msgid "File System"
msgstr "מערכת הקבצים"
@ -1213,7 +1270,7 @@ msgstr "מערכת הקבצים"
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-util.c:304
#: ../src/shell-util.c:307
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"

619
po/it.po

File diff suppressed because it is too large Load Diff

609
po/lv.po

File diff suppressed because it is too large Load Diff

221
po/nb.po
View File

@ -6,10 +6,10 @@
# Torstein Adolf Winterseth <kvikende@fsfe.org>, 2010.
msgid ""
msgstr ""
"Project-Id-Version: gnome-shell 2.91.x\n"
"Project-Id-Version: gnome-shell 3.1.x\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-06-28 22:55+0200\n"
"PO-Revision-Date: 2011-06-28 22:56+0200\n"
"POT-Creation-Date: 2011-07-18 13:48+0200\n"
"PO-Revision-Date: 2011-07-18 13:50+0200\n"
"Last-Translator: Kjartan Maraas <kmaraas@gnome.org>\n"
"Language-Team: Norwegian Bokmål <i18n-nb@lister.ping.uio.no>\n"
"Language: \n"
@ -17,7 +17,6 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Lokalize 1.1\n"
#: ../data/gnome-shell.desktop.in.in.h:1
msgid "GNOME Shell"
@ -52,17 +51,20 @@ msgid ""
"GNOME Shell extensions have a uuid property; this key lists extensions which "
"should be loaded. disabled-extensions overrides this setting for extensions "
"that appear in both lists."
msgstr "GNOME Shell-utvidelser har en uuid-egenskap. Denne nøkkelen lister utvidelser som ikke bør lastes. disabled-extensions overstyrer denne innstillingen for utvidelser som finnes i begge listene."
msgstr ""
"GNOME Shell-utvidelser har en uuid-egenskap. Denne nøkkelen lister "
"utvidelser som ikke bør lastes. disabled-extensions overstyrer denne "
"innstillingen for utvidelser som finnes i begge listene."
#: ../data/org.gnome.shell.gschema.xml.in.h:6
#, fuzzy
msgid ""
"GNOME Shell extensions have a uuid property; this key lists extensions which "
"should not be loaded. This setting overrides enabled-extensions for "
"extensions that appear in both lists."
msgstr ""
"GNOME Shell-utvidelser har en uuid-egenskap. Denne nøkkelen lister "
"utvidelser som ikke bør lastes."
"utvidelser som ikke bør lastes. Denne innstillingen overstyrer enabled-"
"extensions for utvidelser som finnes i begge listene."
#: ../data/org.gnome.shell.gschema.xml.in.h:7
msgid "History for command (Alt-F2) dialog"
@ -101,9 +103,7 @@ msgid ""
"'videorate ! vp8enc quality=10 speed=2 threads=%T ! queue ! webmmux' and "
"records to WEBM using the VP8 codec. %T is used as a placeholder for a guess "
"at the optimal thread count on the system."
msgstr ""
"Setter GStreamer-rør som brukes til å kode opptak. Den følger syntaksen som "
"brukes for gst-launch."
msgstr "Setter GStreamer-rør som brukes til å kode opptak. Den følger syntaksen som brukes for gst-launch."
#: ../data/org.gnome.shell.gschema.xml.in.h:15
msgid "Show date in clock"
@ -191,27 +191,27 @@ msgid "Execution of '%s' failed:"
msgstr "Kjøring av «%s» feilet:"
#. Translators: Filter to display all applications
#: ../js/ui/appDisplay.js:258
#: ../js/ui/appDisplay.js:252
msgid "All"
msgstr "Alle"
#: ../js/ui/appDisplay.js:357
#: ../js/ui/appDisplay.js:359
msgid "APPLICATIONS"
msgstr "PROGRAMMER"
#: ../js/ui/appDisplay.js:383
#: ../js/ui/appDisplay.js:385
msgid "SETTINGS"
msgstr "INNSTILLINGER"
#: ../js/ui/appDisplay.js:656
#: ../js/ui/appDisplay.js:658
msgid "New Window"
msgstr "Nytt vindu"
#: ../js/ui/appDisplay.js:659
#: ../js/ui/appDisplay.js:661
msgid "Remove from Favorites"
msgstr "Fjern fra favoritter"
#: ../js/ui/appDisplay.js:660
#: ../js/ui/appDisplay.js:662
msgid "Add to Favorites"
msgstr "Legg til i favoritter"
@ -344,13 +344,13 @@ msgid "Nothing Scheduled"
msgstr "Ingenting planlagt"
#. Translators: Shown on calendar heading when selected day occurs on current year
#: ../js/ui/calendar.js:717 ../js/ui/telepathyClient.js:568
#: ../js/ui/calendar.js:717 ../js/ui/telepathyClient.js:749
msgctxt "calendar heading"
msgid "%A, %B %d"
msgstr "%A %B %d"
#. Translators: Shown on calendar heading when selected day occurs on different year
#: ../js/ui/calendar.js:720 ../js/ui/telepathyClient.js:571
#: ../js/ui/calendar.js:720 ../js/ui/telepathyClient.js:752
msgctxt "calendar heading"
msgid "%A, %B %d, %Y"
msgstr "%A %B %d, %Y"
@ -371,7 +371,7 @@ msgstr "Denne uken"
msgid "Next week"
msgstr "Neste uke"
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1044
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1122
msgid "Remove"
msgstr "Fjern"
@ -535,11 +535,11 @@ msgstr "Vis kildekode"
msgid "Web Page"
msgstr "Nettside"
#: ../js/ui/messageTray.js:1037
#: ../js/ui/messageTray.js:1115
msgid "Open"
msgstr "Åpne"
#: ../js/ui/messageTray.js:2207
#: ../js/ui/messageTray.js:2286
msgid "System Information"
msgstr "Systeminformasjon"
@ -562,18 +562,18 @@ msgid "Dash"
msgstr "Favoritter"
#. TODO - _quit() doesn't really work on apps in state STARTING yet
#: ../js/ui/panel.js:533
#: ../js/ui/panel.js:532
#, c-format
msgid "Quit %s"
msgstr "Avslutt %s"
#. 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:913
#. Translators: If there is no suitable word for "Activities"
#. in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:568
msgid "Activities"
msgstr "Aktiviteter"
#: ../js/ui/panel.js:1013
#: ../js/ui/panel.js:951
msgid "Top Bar"
msgstr "Topp-panel"
@ -639,41 +639,41 @@ msgstr "Søker …"
msgid "No matching results."
msgstr "Ingen treff."
#: ../js/ui/statusMenu.js:202 ../js/ui/statusMenu.js:206
#: ../js/ui/statusMenu.js:272
#: ../js/ui/statusMenu.js:192 ../js/ui/statusMenu.js:196
#: ../js/ui/statusMenu.js:262
msgid "Power Off..."
msgstr "Slå av …"
#: ../js/ui/statusMenu.js:204 ../js/ui/statusMenu.js:206
#: ../js/ui/statusMenu.js:271
#: ../js/ui/statusMenu.js:194 ../js/ui/statusMenu.js:196
#: ../js/ui/statusMenu.js:261
msgid "Suspend"
msgstr "Hvilemodus"
#: ../js/ui/statusMenu.js:227
#: ../js/ui/statusMenu.js:217
msgid "Available"
msgstr "Tilgjengelig"
#: ../js/ui/statusMenu.js:232
#: ../js/ui/statusMenu.js:222
msgid "Busy"
msgstr "Opptatt"
#: ../js/ui/statusMenu.js:240
#: ../js/ui/statusMenu.js:230
msgid "My Account"
msgstr "Min konto"
#: ../js/ui/statusMenu.js:244
#: ../js/ui/statusMenu.js:234
msgid "System Settings"
msgstr "Systeminnstillinger"
#: ../js/ui/statusMenu.js:252
#: ../js/ui/statusMenu.js:242
msgid "Lock Screen"
msgstr "Lås skjerm"
#: ../js/ui/statusMenu.js:257
#: ../js/ui/statusMenu.js:247
msgid "Switch User"
msgstr "Bytt bruker"
#: ../js/ui/statusMenu.js:262
#: ../js/ui/statusMenu.js:252
msgid "Log Out..."
msgstr "Logg ut …"
@ -805,7 +805,7 @@ msgstr "Alltid gi tilgang"
msgid "Grant this time only"
msgstr "Gi tilgang kun denne ene gangen"
#: ../js/ui/status/bluetooth.js:409
#: ../js/ui/status/bluetooth.js:409 ../js/ui/telepathyClient.js:954
msgid "Reject"
msgstr "Avvis"
@ -894,13 +894,13 @@ msgstr "ikke tilgjengelig"
msgid "connection failed"
msgstr "tilkobling feilet"
#: ../js/ui/status/network.js:582 ../js/ui/status/network.js:1489
#: ../js/ui/status/network.js:582 ../js/ui/status/network.js:1507
msgid "More..."
msgstr "Mer …"
#. TRANSLATORS: this is the indication that a connection for another logged in user is active,
#. and we cannot access its settings (including the name)
#: ../js/ui/status/network.js:618 ../js/ui/status/network.js:1427
#: ../js/ui/status/network.js:618 ../js/ui/status/network.js:1444
msgid "Connected (private)"
msgstr "Tilkoblet (privat)"
@ -917,7 +917,7 @@ msgid "Auto dial-up"
msgstr "Automatisk oppringt"
#. TRANSLATORS: this the automatic wireless connection name (including the network name)
#: ../js/ui/status/network.js:898 ../js/ui/status/network.js:1439
#: ../js/ui/status/network.js:898 ../js/ui/status/network.js:1456
#, c-format
msgid "Auto %s"
msgstr "Automatisk %s"
@ -926,68 +926,68 @@ msgstr "Automatisk %s"
msgid "Auto bluetooth"
msgstr "Automatisk Bluetooth"
#: ../js/ui/status/network.js:1441
#: ../js/ui/status/network.js:1458
msgid "Auto wireless"
msgstr "Automatisk trådløst"
#: ../js/ui/status/network.js:1531
#: ../js/ui/status/network.js:1550
msgid "Enable networking"
msgstr "Slå på nettverk"
#: ../js/ui/status/network.js:1543
#: ../js/ui/status/network.js:1562
msgid "Wired"
msgstr "Kablet"
#: ../js/ui/status/network.js:1554
#: ../js/ui/status/network.js:1573
msgid "Wireless"
msgstr "Trådløst"
#: ../js/ui/status/network.js:1564
#: ../js/ui/status/network.js:1583
msgid "Mobile broadband"
msgstr "Mobilt bredbånd"
#: ../js/ui/status/network.js:1574
#: ../js/ui/status/network.js:1593
msgid "VPN Connections"
msgstr "VPN-tilkoblinger"
#: ../js/ui/status/network.js:1586
#: ../js/ui/status/network.js:1605
msgid "Network Settings"
msgstr "Innstillinger for nettverk"
#: ../js/ui/status/network.js:1878
#: ../js/ui/status/network.js:1897
#, c-format
msgid "You're now connected to mobile broadband connection '%s'"
msgstr "Du er nå koblet til mobil bredbåndstilkobling «%s»"
#: ../js/ui/status/network.js:1882
#: ../js/ui/status/network.js:1901
#, c-format
msgid "You're now connected to wireless network '%s'"
msgstr "Du er nå koblet til trådløst nettverk «%s»"
#: ../js/ui/status/network.js:1886
#: ../js/ui/status/network.js:1905
#, c-format
msgid "You're now connected to wired network '%s'"
msgstr "Du er nå koblet til kablet nettverk «%s»"
#: ../js/ui/status/network.js:1890
#: ../js/ui/status/network.js:1909
#, c-format
msgid "You're now connected to VPN network '%s'"
msgstr "Du er nå koblet til virtuelt privat nettverk «%s»"
#: ../js/ui/status/network.js:1895
#: ../js/ui/status/network.js:1914
#, c-format
msgid "You're now connected to '%s'"
msgstr "Du er nå koblet til «%s»"
#: ../js/ui/status/network.js:1903
#: ../js/ui/status/network.js:1922
msgid "Connection established"
msgstr "Tilkobling etablert"
#: ../js/ui/status/network.js:2029
#: ../js/ui/status/network.js:2048
msgid "Networking is disabled"
msgstr "Nettverk er slått av"
#: ../js/ui/status/network.js:2154
#: ../js/ui/status/network.js:2173
msgid "Network Manager"
msgstr "Nettverkshåndtering"
@ -997,11 +997,11 @@ msgstr "Innstillinger for strøm"
#. 0 is reported when UPower does not have enough data
#. to estimate battery life
#: ../js/ui/status/power.js:110
#: ../js/ui/status/power.js:109
msgid "Estimating..."
msgstr "Estimerer …"
#: ../js/ui/status/power.js:117
#: ../js/ui/status/power.js:116
#, c-format
msgid "%d hour remaining"
msgid_plural "%d hours remaining"
@ -1009,75 +1009,75 @@ msgstr[0] "%d time gjenstår"
msgstr[1] "%d timer gjenstår"
#. TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining"
#: ../js/ui/status/power.js:120
#: ../js/ui/status/power.js:119
#, c-format
msgid "%d %s %d %s remaining"
msgstr "%d %s %d %s gjenstår"
#: ../js/ui/status/power.js:122
#: ../js/ui/status/power.js:121
msgid "hour"
msgid_plural "hours"
msgstr[0] "time"
msgstr[1] "timer"
#: ../js/ui/status/power.js:122
#: ../js/ui/status/power.js:121
msgid "minute"
msgid_plural "minutes"
msgstr[0] "minutt"
msgstr[1] "minutter"
#: ../js/ui/status/power.js:125
#: ../js/ui/status/power.js:124
#, c-format
msgid "%d minute remaining"
msgid_plural "%d minutes remaining"
msgstr[0] "%d minutt gjenstår"
msgstr[1] "%d minutter gjenstår"
#: ../js/ui/status/power.js:227
#: ../js/ui/status/power.js:216
msgid "AC adapter"
msgstr "Strømadapter"
#: ../js/ui/status/power.js:229
#: ../js/ui/status/power.js:218
msgid "Laptop battery"
msgstr "Batteri på bærbar"
#: ../js/ui/status/power.js:231
#: ../js/ui/status/power.js:220
msgid "UPS"
msgstr "UPS"
#: ../js/ui/status/power.js:233
#: ../js/ui/status/power.js:222
msgid "Monitor"
msgstr "Skjerm"
#: ../js/ui/status/power.js:235
#: ../js/ui/status/power.js:224
msgid "Mouse"
msgstr "Mus"
#: ../js/ui/status/power.js:237
#: ../js/ui/status/power.js:226
msgid "Keyboard"
msgstr "Tastatur"
#: ../js/ui/status/power.js:239
#: ../js/ui/status/power.js:228
msgid "PDA"
msgstr "PDA"
#: ../js/ui/status/power.js:241
#: ../js/ui/status/power.js:230
msgid "Cell phone"
msgstr "Mobiltelefon"
#: ../js/ui/status/power.js:243
#: ../js/ui/status/power.js:232
msgid "Media player"
msgstr "Medieavspiller"
#: ../js/ui/status/power.js:245
#: ../js/ui/status/power.js:234
msgid "Tablet"
msgstr "Nettbrett"
#: ../js/ui/status/power.js:247
#: ../js/ui/status/power.js:236
msgid "Computer"
msgstr "Datamaskin"
#: ../js/ui/status/power.js:249 ../src/shell-app-system.c:1088
#: ../js/ui/status/power.js:238 ../src/shell-app-system.c:1088
msgid "Unknown"
msgstr "Ukjent"
@ -1089,22 +1089,34 @@ msgstr "Volum"
msgid "Microphone"
msgstr "Mikrofon"
#: ../js/ui/telepathyClient.js:401
#. We got the TpContact
#. FIXME: We don't have a 'chat room' icon (bgo #653737) use
#. system-users for now as Empathy does.
#: ../js/ui/telepathyClient.js:228
msgid "Invitation"
msgstr "Invitasjon"
#. We got the TpContact
#: ../js/ui/telepathyClient.js:291
msgid "Call"
msgstr "Ring"
#: ../js/ui/telepathyClient.js:541
#, c-format
msgid "%s is online."
msgstr "%s er tilkoblet."
#: ../js/ui/telepathyClient.js:406
#: ../js/ui/telepathyClient.js:546
#, c-format
msgid "%s is offline."
msgstr "%s er frakoblet."
#: ../js/ui/telepathyClient.js:409
#: ../js/ui/telepathyClient.js:549
#, c-format
msgid "%s is away."
msgstr "«%s» er borte."
#: ../js/ui/telepathyClient.js:412
#: ../js/ui/telepathyClient.js:552
#, c-format
msgid "%s is busy."
msgstr "%s er opptatt."
@ -1112,18 +1124,57 @@ msgstr "%s er opptatt."
#. Translators: this is a time format string followed by a date.
#. If applicable, replace %X with a strftime format valid for your
#. locale, without seconds.
#: ../js/ui/telepathyClient.js:560
#: ../js/ui/telepathyClient.js:741
#, no-c-format
msgid "Sent at %X on %A"
msgstr "Sendt %X på %A"
#. Translators: this is the other person changing their old IM name to their new
#. IM name.
#: ../js/ui/telepathyClient.js:610
#: ../js/ui/telepathyClient.js:791
#, c-format
msgid "%s is now known as %s"
msgstr "%s er nå kjent som %s"
#. translators: argument is a room name like
#. * room@jabber.org for example.
#: ../js/ui/telepathyClient.js:898
#, c-format
msgid "Invitation to %s"
msgstr "Invitasjon til %s"
#. translators: first argument is the name of a contact and the second
#. * one the name of a room. "Alice is inviting you to join room@jabber.org
#. * for example.
#: ../js/ui/telepathyClient.js:906
#, c-format
msgid "%s is inviting you to join %s"
msgstr "%s inviterer deg til å bli med i %s"
#: ../js/ui/telepathyClient.js:908
msgid "Decline"
msgstr "Avslå"
#: ../js/ui/telepathyClient.js:909
msgid "Accept"
msgstr "Godta"
#. translators: argument is a contact name like Alice for example.
#: ../js/ui/telepathyClient.js:942
#, c-format
msgid "Video call from %s"
msgstr "Videosamtale fra %s"
#. translators: argument is a contact name like Alice for example.
#: ../js/ui/telepathyClient.js:945
#, c-format
msgid "Call from %s"
msgstr "Samtale fra %s"
#: ../js/ui/telepathyClient.js:955
msgid "Answer"
msgstr "Svar"
#. Translators: this is the text displayed
#. in the search entry when no search is
#. active; it should not exceed ~30
@ -1132,7 +1183,7 @@ msgstr "%s er nå kjent som %s"
msgid "Type to search..."
msgstr "Skriv for å søke …"
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:254
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:257
msgid "Search"
msgstr "Søk"
@ -1168,7 +1219,7 @@ msgstr[1] "%u innganger"
msgid "System Sounds"
msgstr "Systemlyder"
#: ../src/main.c:445
#: ../src/main.c:466
msgid "Print version"
msgstr "Skriv ut versjon"
@ -1189,13 +1240,13 @@ msgstr "Forvalg"
msgid "Authentication dialog was dismissed by the user"
msgstr "Autentiseringsdialogen ble lukket av brukeren"
#: ../src/shell-util.c:93
#: ../src/shell-util.c:96
msgid "Home Folder"
msgstr "Hjemmemappe"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-util.c:108
#: ../src/shell-util.c:111
msgid "File System"
msgstr "Filsystem"
@ -1204,7 +1255,7 @@ msgstr "Filsystem"
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-util.c:304
#: ../src/shell-util.c:307
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"

662
po/pa.po

File diff suppressed because it is too large Load Diff

467
po/ru.po

File diff suppressed because it is too large Load Diff

669
po/sk.po

File diff suppressed because it is too large Load Diff

208
po/sl.po
View File

@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gnome-shell master\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell&keywords=I18N+L10N&component=general\n"
"POT-Creation-Date: 2011-06-28 19:42+0000\n"
"PO-Revision-Date: 2011-06-28 22:20+0100\n"
"POT-Creation-Date: 2011-07-18 11:50+0000\n"
"PO-Revision-Date: 2011-07-19 21:45+0100\n"
"Last-Translator: Matej Urbančič <mateju@svn.gnome.org>\n"
"Language-Team: Slovenian GNOME Translation Team <gnome-si@googlegroups.com>\n"
"Language: Slovenian\n"
@ -146,27 +146,27 @@ msgid "Execution of '%s' failed:"
msgstr "Izvedba '%s' je spodletela:"
#. Translators: Filter to display all applications
#: ../js/ui/appDisplay.js:258
#: ../js/ui/appDisplay.js:252
msgid "All"
msgstr "Vse"
#: ../js/ui/appDisplay.js:357
#: ../js/ui/appDisplay.js:359
msgid "APPLICATIONS"
msgstr "Programi"
#: ../js/ui/appDisplay.js:383
#: ../js/ui/appDisplay.js:385
msgid "SETTINGS"
msgstr "NASTAVITVE"
#: ../js/ui/appDisplay.js:656
#: ../js/ui/appDisplay.js:658
msgid "New Window"
msgstr "Novo okno"
#: ../js/ui/appDisplay.js:659
#: ../js/ui/appDisplay.js:661
msgid "Remove from Favorites"
msgstr "Odstrani iz priljubljenih"
#: ../js/ui/appDisplay.js:660
#: ../js/ui/appDisplay.js:662
msgid "Add to Favorites"
msgstr "Dodaj med priljubljene"
@ -300,14 +300,14 @@ msgstr "Nič ni razporejeno"
#. Translators: Shown on calendar heading when selected day occurs on current year
#: ../js/ui/calendar.js:717
#: ../js/ui/telepathyClient.js:568
#: ../js/ui/telepathyClient.js:749
msgctxt "calendar heading"
msgid "%A, %B %d"
msgstr "%A, %d. %m."
#. Translators: Shown on calendar heading when selected day occurs on different year
#: ../js/ui/calendar.js:720
#: ../js/ui/telepathyClient.js:571
#: ../js/ui/telepathyClient.js:752
msgctxt "calendar heading"
msgid "%A, %B %d, %Y"
msgstr "%A, %d %B %Y"
@ -329,7 +329,7 @@ msgid "Next week"
msgstr "Naslednji teden"
#: ../js/ui/dash.js:172
#: ../js/ui/messageTray.js:1044
#: ../js/ui/messageTray.js:1122
msgid "Remove"
msgstr "Odstrani"
@ -494,11 +494,11 @@ msgstr "Poglej vir"
msgid "Web Page"
msgstr "Spletna stran"
#: ../js/ui/messageTray.js:1037
#: ../js/ui/messageTray.js:1115
msgid "Open"
msgstr "Odpri"
#: ../js/ui/messageTray.js:2207
#: ../js/ui/messageTray.js:2286
msgid "System Information"
msgstr "Podrobnosti sistema"
@ -521,18 +521,18 @@ msgid "Dash"
msgstr "Armaturna plošča"
#. TODO - _quit() doesn't really work on apps in state STARTING yet
#: ../js/ui/panel.js:533
#: ../js/ui/panel.js:532
#, c-format
msgid "Quit %s"
msgstr "Končaj %s"
#. 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:913
#. Translators: If there is no suitable word for "Activities"
#. in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:568
msgid "Activities"
msgstr "Dejavnosti"
#: ../js/ui/panel.js:1013
#: ../js/ui/panel.js:951
msgid "Top Bar"
msgstr "Vrhnja vrstica"
@ -598,43 +598,43 @@ msgstr "Iskanje ..."
msgid "No matching results."
msgstr "Ni zadetkov iskanja"
#: ../js/ui/statusMenu.js:202
#: ../js/ui/statusMenu.js:206
#: ../js/ui/statusMenu.js:272
#: ../js/ui/statusMenu.js:192
#: ../js/ui/statusMenu.js:196
#: ../js/ui/statusMenu.js:262
msgid "Power Off..."
msgstr "Izklop ..."
#: ../js/ui/statusMenu.js:204
#: ../js/ui/statusMenu.js:206
#: ../js/ui/statusMenu.js:271
#: ../js/ui/statusMenu.js:194
#: ../js/ui/statusMenu.js:196
#: ../js/ui/statusMenu.js:261
msgid "Suspend"
msgstr "Zaustavi"
#: ../js/ui/statusMenu.js:227
#: ../js/ui/statusMenu.js:217
msgid "Available"
msgstr "Na voljo"
#: ../js/ui/statusMenu.js:232
#: ../js/ui/statusMenu.js:222
msgid "Busy"
msgstr "Zaposleno"
#: ../js/ui/statusMenu.js:240
#: ../js/ui/statusMenu.js:230
msgid "My Account"
msgstr "Račun"
#: ../js/ui/statusMenu.js:244
#: ../js/ui/statusMenu.js:234
msgid "System Settings"
msgstr "Sistemske nastavitve"
#: ../js/ui/statusMenu.js:252
#: ../js/ui/statusMenu.js:242
msgid "Lock Screen"
msgstr "Zakleni zaslon"
#: ../js/ui/statusMenu.js:257
#: ../js/ui/statusMenu.js:247
msgid "Switch User"
msgstr "Preklopi uporabnika"
#: ../js/ui/statusMenu.js:262
#: ../js/ui/statusMenu.js:252
msgid "Log Out..."
msgstr "Odjava ..."
@ -773,6 +773,7 @@ msgid "Grant this time only"
msgstr "Odobri le tokrat"
#: ../js/ui/status/bluetooth.js:409
#: ../js/ui/telepathyClient.js:954
msgid "Reject"
msgstr "Zavrni"
@ -863,14 +864,14 @@ msgid "connection failed"
msgstr "povezovanje je spodletelo"
#: ../js/ui/status/network.js:582
#: ../js/ui/status/network.js:1489
#: ../js/ui/status/network.js:1507
msgid "More..."
msgstr "Več ..."
#. TRANSLATORS: this is the indication that a connection for another logged in user is active,
#. and we cannot access its settings (including the name)
#: ../js/ui/status/network.js:618
#: ../js/ui/status/network.js:1427
#: ../js/ui/status/network.js:1444
msgid "Connected (private)"
msgstr "Povezano (zasebna povezava)"
@ -888,7 +889,7 @@ msgstr "Samodejni klicni dostop"
#. TRANSLATORS: this the automatic wireless connection name (including the network name)
#: ../js/ui/status/network.js:898
#: ../js/ui/status/network.js:1439
#: ../js/ui/status/network.js:1456
#, c-format
msgid "Auto %s"
msgstr "Samodejna povezava z %s"
@ -897,68 +898,68 @@ msgstr "Samodejna povezava z %s"
msgid "Auto bluetooth"
msgstr "Samodejna povezava z Bluetooth"
#: ../js/ui/status/network.js:1441
#: ../js/ui/status/network.js:1458
msgid "Auto wireless"
msgstr "Samodejni brezžični dostop"
#: ../js/ui/status/network.js:1531
#: ../js/ui/status/network.js:1550
msgid "Enable networking"
msgstr "Omogoči omrežje"
#: ../js/ui/status/network.js:1543
#: ../js/ui/status/network.js:1562
msgid "Wired"
msgstr "Žično"
#: ../js/ui/status/network.js:1554
#: ../js/ui/status/network.js:1573
msgid "Wireless"
msgstr "Brezžično"
#: ../js/ui/status/network.js:1564
#: ../js/ui/status/network.js:1583
msgid "Mobile broadband"
msgstr "Mobilni širokopasovni dostop"
#: ../js/ui/status/network.js:1574
#: ../js/ui/status/network.js:1593
msgid "VPN Connections"
msgstr "Povezave VPN"
#: ../js/ui/status/network.js:1586
#: ../js/ui/status/network.js:1605
msgid "Network Settings"
msgstr "Omrežne nastavitve"
#: ../js/ui/status/network.js:1878
#: ../js/ui/status/network.js:1897
#, c-format
msgid "You're now connected to mobile broadband connection '%s'"
msgstr "Vzpostavljena je povezava z mobilnim širokopasovnim omrežjem '%s'."
#: ../js/ui/status/network.js:1882
#: ../js/ui/status/network.js:1901
#, c-format
msgid "You're now connected to wireless network '%s'"
msgstr "Vzpostavljena je povezava z brezžičnim omrežjem '%s'."
#: ../js/ui/status/network.js:1886
#: ../js/ui/status/network.js:1905
#, c-format
msgid "You're now connected to wired network '%s'"
msgstr "Vzpostavljena je povezava z žičnim omrežjem '%s'."
#: ../js/ui/status/network.js:1890
#: ../js/ui/status/network.js:1909
#, c-format
msgid "You're now connected to VPN network '%s'"
msgstr "Vzpostavljena je povezava z omrežjem VPN '%s'"
#: ../js/ui/status/network.js:1895
#: ../js/ui/status/network.js:1914
#, c-format
msgid "You're now connected to '%s'"
msgstr "Vzpostavljena je povezava z '%s'."
#: ../js/ui/status/network.js:1903
#: ../js/ui/status/network.js:1922
msgid "Connection established"
msgstr "Povezava je vzpostavljena"
#: ../js/ui/status/network.js:2029
#: ../js/ui/status/network.js:2048
msgid "Networking is disabled"
msgstr "Omrežje je onemogočeno"
#: ../js/ui/status/network.js:2154
#: ../js/ui/status/network.js:2173
msgid "Network Manager"
msgstr "Upravljalnik omrežij"
@ -968,11 +969,11 @@ msgstr "Upravljanje napajanja"
#. 0 is reported when UPower does not have enough data
#. to estimate battery life
#: ../js/ui/status/power.js:110
#: ../js/ui/status/power.js:109
msgid "Estimating..."
msgstr "Ocenjevanje ...."
#: ../js/ui/status/power.js:117
#: ../js/ui/status/power.js:116
#, c-format
msgid "%d hour remaining"
msgid_plural "%d hours remaining"
@ -982,12 +983,12 @@ msgstr[2] "preostajata še %d uri"
msgstr[3] "preostajajo še %d ure"
#. TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining"
#: ../js/ui/status/power.js:120
#: ../js/ui/status/power.js:119
#, c-format
msgid "%d %s %d %s remaining"
msgstr "Preostaja še %d %s %d %s"
#: ../js/ui/status/power.js:122
#: ../js/ui/status/power.js:121
msgid "hour"
msgid_plural "hours"
msgstr[0] "ur"
@ -995,7 +996,7 @@ msgstr[1] "ura"
msgstr[2] "uri"
msgstr[3] "ure"
#: ../js/ui/status/power.js:122
#: ../js/ui/status/power.js:121
msgid "minute"
msgid_plural "minutes"
msgstr[0] "minut"
@ -1003,7 +1004,7 @@ msgstr[1] "minuta"
msgstr[2] "minuti"
msgstr[3] "minute"
#: ../js/ui/status/power.js:125
#: ../js/ui/status/power.js:124
#, c-format
msgid "%d minute remaining"
msgid_plural "%d minutes remaining"
@ -1012,51 +1013,51 @@ msgstr[1] "preostaja še %d minuta"
msgstr[2] "preostajata še %d minuti"
msgstr[3] "preostajajo še %d minute"
#: ../js/ui/status/power.js:227
#: ../js/ui/status/power.js:216
msgid "AC adapter"
msgstr "Električni prilagodilnik"
#: ../js/ui/status/power.js:229
#: ../js/ui/status/power.js:218
msgid "Laptop battery"
msgstr "Baterija prenosnika"
#: ../js/ui/status/power.js:231
#: ../js/ui/status/power.js:220
msgid "UPS"
msgstr "UPS"
#: ../js/ui/status/power.js:233
#: ../js/ui/status/power.js:222
msgid "Monitor"
msgstr "Zaslon"
#: ../js/ui/status/power.js:235
#: ../js/ui/status/power.js:224
msgid "Mouse"
msgstr "Miška"
#: ../js/ui/status/power.js:237
#: ../js/ui/status/power.js:226
msgid "Keyboard"
msgstr "Tipkovnica"
#: ../js/ui/status/power.js:239
#: ../js/ui/status/power.js:228
msgid "PDA"
msgstr "Dlančnik"
#: ../js/ui/status/power.js:241
#: ../js/ui/status/power.js:230
msgid "Cell phone"
msgstr "Mobilni telefon"
#: ../js/ui/status/power.js:243
#: ../js/ui/status/power.js:232
msgid "Media player"
msgstr "Predstavni predvajalnik"
#: ../js/ui/status/power.js:245
#: ../js/ui/status/power.js:234
msgid "Tablet"
msgstr "Tablični računalnik"
#: ../js/ui/status/power.js:247
#: ../js/ui/status/power.js:236
msgid "Computer"
msgstr "Računalnik"
#: ../js/ui/status/power.js:249
#: ../js/ui/status/power.js:238
#: ../src/shell-app-system.c:1088
msgid "Unknown"
msgstr "Neznano"
@ -1069,22 +1070,34 @@ msgstr "Glasnost"
msgid "Microphone"
msgstr "Mikrofon"
#: ../js/ui/telepathyClient.js:401
#. We got the TpContact
#. FIXME: We don't have a 'chat room' icon (bgo #653737) use
#. system-users for now as Empathy does.
#: ../js/ui/telepathyClient.js:228
msgid "Invitation"
msgstr "Povabilo"
#. We got the TpContact
#: ../js/ui/telepathyClient.js:291
msgid "Call"
msgstr "Pokliči"
#: ../js/ui/telepathyClient.js:541
#, c-format
msgid "%s is online."
msgstr "%s je povezan."
#: ../js/ui/telepathyClient.js:406
#: ../js/ui/telepathyClient.js:546
#, c-format
msgid "%s is offline."
msgstr "%s ni povezan."
#: ../js/ui/telepathyClient.js:409
#: ../js/ui/telepathyClient.js:549
#, c-format
msgid "%s is away."
msgstr "%s je odsoten."
#: ../js/ui/telepathyClient.js:412
#: ../js/ui/telepathyClient.js:552
#, c-format
msgid "%s is busy."
msgstr "%s je zaposlen."
@ -1092,18 +1105,57 @@ msgstr "%s je zaposlen."
#. Translators: this is a time format string followed by a date.
#. If applicable, replace %X with a strftime format valid for your
#. locale, without seconds.
#: ../js/ui/telepathyClient.js:560
#: ../js/ui/telepathyClient.js:741
#, no-c-format
msgid "Sent at %X on %A"
msgstr "Poslano na %X ob %A"
#. Translators: this is the other person changing their old IM name to their new
#. IM name.
#: ../js/ui/telepathyClient.js:610
#: ../js/ui/telepathyClient.js:791
#, c-format
msgid "%s is now known as %s"
msgstr "%s je sedaj znan kot v %s"
#. translators: argument is a room name like
#. * room@jabber.org for example.
#: ../js/ui/telepathyClient.js:898
#, c-format
msgid "Invitation to %s"
msgstr "Povabilo v %s"
#. translators: first argument is the name of a contact and the second
#. * one the name of a room. "Alice is inviting you to join room@jabber.org
#. * for example.
#: ../js/ui/telepathyClient.js:906
#, c-format
msgid "%s is inviting you to join %s"
msgstr "%s vas vabi, da se pridružite v %s"
#: ../js/ui/telepathyClient.js:908
msgid "Decline"
msgstr "Zavrni"
#: ../js/ui/telepathyClient.js:909
msgid "Accept"
msgstr "Sprejmi"
#. translators: argument is a contact name like Alice for example.
#: ../js/ui/telepathyClient.js:942
#, c-format
msgid "Video call from %s"
msgstr "%s želi vzpostaviti video klic"
#. translators: argument is a contact name like Alice for example.
#: ../js/ui/telepathyClient.js:945
#, c-format
msgid "Call from %s"
msgstr "%s kliče"
#: ../js/ui/telepathyClient.js:955
msgid "Answer"
msgstr "Odgovori"
#. Translators: this is the text displayed
#. in the search entry when no search is
#. active; it should not exceed ~30
@ -1113,7 +1165,7 @@ msgid "Type to search..."
msgstr "Vtipkajte za iskanje ..."
#: ../js/ui/viewSelector.js:140
#: ../src/shell-util.c:254
#: ../src/shell-util.c:257
msgid "Search"
msgstr "Poišči"
@ -1153,7 +1205,7 @@ msgstr[3] "%u dovodi naprave"
msgid "System Sounds"
msgstr "Sistemski zvoki"
#: ../src/main.c:445
#: ../src/main.c:466
msgid "Print version"
msgstr "Izpiši različico"
@ -1174,13 +1226,13 @@ msgstr "Privzeto"
msgid "Authentication dialog was dismissed by the user"
msgstr "Uporabnik je zavrnil pogovorno okno overitve"
#: ../src/shell-util.c:93
#: ../src/shell-util.c:96
msgid "Home Folder"
msgstr "Domača mapa"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-util.c:108
#: ../src/shell-util.c:111
msgid "File System"
msgstr "Datotečni sistem"
@ -1189,7 +1241,7 @@ msgstr "Datotečni sistem"
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-util.c:304
#: ../src/shell-util.c:307
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"

View File

@ -1,13 +1,4 @@
servicedir = $(datadir)/dbus-1/services
service_in_files = calendar-server/org.gnome.Shell.CalendarServer.service.in
service_DATA = $(service_in_files:.service.in=.service)
$(service_DATA): $(service_in_files) Makefile
$(AM_V_GEN) \
[ -d $(@D) ] || $(mkdir_p) $(@D) ; \
sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@.tmp && mv $@.tmp $@
CLEANFILES += $(service_DATA)
service_in_files += calendar-server/org.gnome.Shell.CalendarServer.service.in
libexec_PROGRAMS += gnome-shell-calendar-server

View File

@ -0,0 +1,24 @@
service_in_files += hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service.in
libexec_PROGRAMS += gnome-shell-hotplug-sniffer
gnome_shell_hotplug_sniffer_SOURCES = \
hotplug-sniffer/hotplug-mimetypes.h \
hotplug-sniffer/shell-mime-sniffer.h \
hotplug-sniffer/shell-mime-sniffer.c \
hotplug-sniffer/hotplug-sniffer.c \
$(NULL)
gnome_shell_hotplug_sniffer_CFLAGS = \
-I$(top_srcdir)/src \
-DG_DISABLE_DEPRECATED \
$(SHELL_HOTPLUG_SNIFFER_CFLAGS) \
$(NULL)
gnome_shell_hotplug_sniffer_LDFLAGS = \
$(SHELL_HOTPLUG_SNIFFER_LIBS) \
$(NULL)
EXTRA_DIST += \
hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service.in \
$(NULL)

View File

@ -6,6 +6,7 @@ bin_SCRIPTS =
libexec_PROGRAMS =
noinst_LTLIBRARIES =
noinst_PROGRAMS =
service_in_files =
-include $(INTROSPECTION_MAKEFILE)
INTROSPECTION_GIRS =
@ -15,9 +16,19 @@ INTROSPECTION_COMPILER_ARGS = --includedir=$(srcdir) --includedir=$(MUTTER_TYPEL
typelibdir = $(pkglibdir)
typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib)
servicedir = $(datadir)/dbus-1/services
service_DATA = $(service_in_files:.service.in=.service)
$(service_DATA): $(service_in_files) Makefile
$(AM_V_GEN) \
[ -d $(@D) ] || $(mkdir_p) $(@D) ; \
sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@.tmp && mv $@.tmp $@
CLEANFILES += $(service_DATA)
CLEANFILES += $(gir_DATA) $(typelib_DATA)
bin_SCRIPTS += gnome-shell-extension-tool
EXTRA_DIST += gnome-shell-extension-tool.in
bin_PROGRAMS = gnome-shell-real
if USE_JHBUILD_WRAPPER_SCRIPT
@ -26,6 +37,7 @@ bin_SCRIPTS += gnome-shell-jhbuild
else
gnome_shell = gnome-shell-real
endif
EXTRA_DIST += gnome-shell-jhbuild.in
noinst_DATA = gnome-shell
gnome-shell: $(gnome_shell) Makefile
@ -55,13 +67,13 @@ gnome-shell-extension-tool: gnome-shell-extension-tool.in Makefile
$(AM_V_GEN) sed $(generated_script_substitutions) $< > $@.tmp && mv $@.tmp $@ && chmod a+x $@
CLEANFILES += gnome-shell $(bin_SCRIPTS)
EXTRA_DIST += $(bin_SCRIPTS:=.in)
include Makefile-gdmuser.am
include Makefile-st.am
include Makefile-tray.am
include Makefile-gvc.am
include Makefile-calendar-server.am
include Makefile-hotplug-sniffer.am
gnome_shell_cflags = \
$(GNOME_SHELL_CFLAGS) \
@ -97,6 +109,7 @@ shell_public_headers_h = \
shell-gtk-embed.h \
shell-global.h \
shell-mobile-providers.h \
shell-mount-operation.h \
shell-perf-log.h \
shell-slicer.h \
shell-stack.h \
@ -130,6 +143,7 @@ libgnome_shell_la_SOURCES = \
shell-gtk-embed.c \
shell-global.c \
shell-mobile-providers.c \
shell-mount-operation.c \
shell-perf-log.c \
shell-polkit-authentication-agent.h \
shell-polkit-authentication-agent.c \
@ -255,7 +269,7 @@ libgnome_shell_la_LIBADD = \
libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags)
Shell-0.1.gir: libgnome-shell.la St-1.0.gir
Shell_0_1_gir_INCLUDES = Clutter-1.0 ClutterX11-1.0 Meta-3.0 TelepathyGLib-0.12 TelepathyLogger-0.2
Shell_0_1_gir_INCLUDES = Clutter-1.0 ClutterX11-1.0 Meta-3.0 TelepathyGLib-0.12 TelepathyLogger-0.2 Soup-2.4
Shell_0_1_gir_CFLAGS = $(libgnome_shell_la_CPPFLAGS) -I $(srcdir)
Shell_0_1_gir_LIBS = libgnome-shell.la
Shell_0_1_gir_FILES = $(libgnome_shell_la_gir_sources)

View File

@ -3,6 +3,7 @@
import os
import re
import socket
import subprocess
import sys
import optparse
@ -26,6 +27,69 @@ if args:
parser.print_usage()
sys.exit(1)
SAMPLE_EXTENSION_FILES = {
"extension.js": """
const St = imports.gi.St;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
let text;
function _hideHello() {
Main.uiGroup.remove_actor(text);
text = null;
}
function _showHello() {
if (!text) {
text = new St.Label({ style_class: 'helloworld-label', text: "Hello, world!" });
Main.uiGroup.add_actor(text);
}
text.opacity = 255;
let monitor = Main.layoutManager.primaryMonitor;
text.set_position(Math.floor(monitor.width / 2 - text.width / 2),
Math.floor(monitor.height / 2 - text.height / 2));
Tweener.addTween(text,
{ opacity: 0,
time: 2,
transition: 'easeOutQuad',
onComplete: _hideHello });
}
function main() {
let button = new St.Bin({ style_class: 'panel-button',
reactive: true,
can_focus: true,
x_fill: true,
y_fill: false,
track_hover: true });
let icon = new St.Icon({ icon_name: 'system-run',
icon_type: St.IconType.SYMBOLIC,
style_class: 'system-status-icon' });
button.set_child(icon);
button.connect('button-press-event', _showHello);
Main.panel._rightBox.insert_actor(button, 0);
}
""",
"stylesheet.css": """
.helloworld-label {
font-size: 36px;
font-weight: bold;
color: #ffffff;
background-color: rgba(10,10,10,0.7);
border-radius: 5px;
padding: .5em;
}
""",
}
if options.create_extension:
print
print '''Name should be a very short (ideally descriptive) string.
@ -41,7 +105,7 @@ Examples are: "Make windows visible on click", "Block advertisement popups"
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()
hostname = socket.gethostname()
sample_uuid = sample_uuid + '@' + hostname
print
@ -71,41 +135,11 @@ use an extension title clicktofocus@janedoe.example.com.'''
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()
for filename, contents in SAMPLE_EXTENSION_FILES.iteritems():
path = os.path.join(extension_path, filename)
f = open(path, 'w')
f.write(contents)
f.close()
print "Created extension in %r" % (extension_path, )
subprocess.Popen(['gnome-open', extensionjs_path])

View File

@ -532,7 +532,7 @@ def restore_gnome():
component = client.get_string(gconf_path)
if component == None or component == "":
return
return False
# See gnome-session/gsm-util.c:gsm_util_find_desktop_file_for_app_name()
# The one difference is that we don't search the autostart directories,
@ -549,9 +549,15 @@ def restore_gnome():
if appinfo:
appinfo.launch()
return True
return False
launch_component("/desktop/gnome/session/required_components/windowmanager")
launch_component("/desktop/gnome/session/required_components/panel")
# GNOME2 fallback
wm = launch_component("/desktop/gnome/session/required_components/windowmanager")
panel = launch_component("/desktop/gnome/session/required_components/panel")
if not wm and not panel: # Probably GNOME3
subprocess.Popen(['gnome-shell'])
# Main program

View File

@ -0,0 +1,139 @@
#ifndef __HOTPLUG_MIMETYPES_H__
#define __HOTPLUG_MIMETYPES_H__
#include <glib.h>
G_GNUC_UNUSED static const gchar *docs_mimetypes[] = {
"application/vnd.oasis.opendocument.text",
"application/vnd.oasis.opendocument.presentation",
"application/vnd.oasis.opendocument.spreadsheet",
"application/msword",
"application/vnd.ms-excel",
"application/vnd.ms-powerpoint",
"application/rtf",
"application/pdf",
"application/x-bzpdf",
"application/x-gzpdf",
"application/x-xzpdf",
"application/postscript",
"application/x-bzpostscript",
"application/x-gzpostscript",
"image/x-eps",
"image/x-bzeps",
"image/x-gzeps",
"application/x-dvi",
"application/x-bzdvi",
"application/x-gzdvi",
"image/vnd.djvu",
"application/x-cbr",
"application/x-cbz",
"application/x-cb7",
"application/x-cbt",
NULL
};
G_GNUC_UNUSED static const gchar *video_mimetypes[] = {
"application/mxf",
"application/ogg",
"application/ram",
"application/sdp",
"application/vnd.ms-wpl",
"application/vnd.rn-realmedia",
"application/x-extension-m4a",
"application/x-extension-mp4",
"application/x-flash-video",
"application/x-matroska",
"application/x-netshow-channel",
"application/x-ogg",
"application/x-quicktimeplayer",
"application/x-shorten",
"image/vnd.rn-realpix",
"image/x-pict",
"misc/ultravox",
"text/x-google-video-pointer",
"video/3gpp",
"video/dv",
"video/fli",
"video/flv",
"video/mp2t",
"video/mp4",
"video/mp4v-es",
"video/mpeg",
"video/msvideo",
"video/ogg",
"video/quicktime",
"video/vivo",
"video/vnd.divx",
"video/vnd.rn-realvideo",
"video/vnd.vivo",
"video/webm",
"video/x-anim",
"video/x-avi",
"video/x-flc",
"video/x-fli",
"video/x-flic",
"video/x-flv",
"video/x-m4v",
"video/x-matroska",
"video/x-mpeg",
"video/x-ms-asf",
"video/x-ms-asx",
"video/x-msvideo",
"video/x-ms-wm",
"video/x-ms-wmv",
"video/x-ms-wmx",
"video/x-ms-wvx",
"video/x-nsv",
"video/x-ogm+ogg",
"video/x-theora+ogg",
"video/x-totem-stream",
NULL
};
G_GNUC_UNUSED static const gchar *audio_mimetypes[] = {
"audio/3gpp",
"audio/ac3",
"audio/AMR",
"audio/AMR-WB",
"audio/basic",
"audio/flac",
"audio/midi",
"audio/mp2",
"audio/mp4",
"audio/mpeg",
"audio/ogg",
"audio/prs.sid",
"audio/vnd.rn-realaudio",
"audio/x-aiff",
"audio/x-ape",
"audio/x-flac",
"audio/x-gsm",
"audio/x-it",
"audio/x-m4a",
"audio/x-matroska",
"audio/x-mod",
"audio/x-mp3",
"audio/x-mpeg",
"audio/x-ms-asf",
"audio/x-ms-asx",
"audio/x-ms-wax",
"audio/x-ms-wma",
"audio/x-musepack",
"audio/x-pn-aiff",
"audio/x-pn-au",
"audio/x-pn-wav",
"audio/x-pn-windows-acm",
"audio/x-realaudio",
"audio/x-real-audio",
"audio/x-sbc",
"audio/x-speex",
"audio/x-tta",
"audio/x-wav",
"audio/x-wavpack",
"audio/x-vorbis",
"audio/x-vorbis+ogg",
"audio/x-xm",
NULL
};
#endif /* __HOTPLUG_MIMETYPES_H__ */

View File

@ -0,0 +1,319 @@
/*
* Copyright (C) 2011 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* Authors: David Zeuthen <davidz@redhat.com>
* Cosimo Cecchi <cosimoc@redhat.com>
*
*/
#include "shell-mime-sniffer.h"
#include "hotplug-mimetypes.h"
/* Set the environment variable HOTPLUG_SNIFFER_DEBUG to show debug */
static void print_debug (const gchar *str, ...);
#define BUS_NAME "org.gnome.Shell.HotplugSniffer"
#define AUTOQUIT_TIMEOUT 5
static const gchar introspection_xml[] =
"<node>"
" <interface name='org.gnome.Shell.HotplugSniffer'>"
" <method name='SniffURI'>"
" <arg type='s' name='uri' direction='in'/>"
" <arg type='as' name='content_types' direction='out'/>"
" </method>"
" </interface>"
"</node>";
static GDBusNodeInfo *introspection_data = NULL;
static GMainLoop *loop = NULL;
static guint autoquit_id = 0;
static gboolean
autoquit_timeout_cb (gpointer _unused)
{
print_debug ("Timeout reached, quitting...");
autoquit_id = 0;
g_main_loop_quit (loop);
return FALSE;
}
static void
ensure_autoquit_off (void)
{
if (g_getenv ("HOTPLUG_SNIFFER_PERSIST") != NULL)
return;
if (autoquit_id != 0)
{
g_source_remove (autoquit_id);
autoquit_id = 0;
}
}
static void
ensure_autoquit_on (void)
{
if (g_getenv ("HOTPLUG_SNIFFER_PERSIST") != NULL)
return;
autoquit_id =
g_timeout_add_seconds (AUTOQUIT_TIMEOUT,
autoquit_timeout_cb, NULL);
}
typedef struct {
GVariant *parameters;
GDBusMethodInvocation *invocation;
} InvocationData;
static InvocationData *
invocation_data_new (GVariant *params,
GDBusMethodInvocation *invocation)
{
InvocationData *ret;
ret = g_slice_new0 (InvocationData);
ret->parameters = g_variant_ref (params);
ret->invocation = g_object_ref (invocation);
return ret;
}
static void
invocation_data_free (InvocationData *data)
{
g_variant_unref (data->parameters);
g_clear_object (&data->invocation);
g_slice_free (InvocationData, data);
}
static void
sniff_async_ready_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
InvocationData *data = user_data;
gchar **types;
gint idx;
GError *error = NULL;
GVariantBuilder *builder;
GVariant *result;
types = shell_mime_sniffer_sniff_finish (SHELL_MIME_SNIFFER (source),
res, &error);
if (error != NULL)
{
g_dbus_method_invocation_return_gerror (data->invocation, error);
g_error_free (error);
goto out;
}
builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
for (idx = 0; types[idx] != NULL; idx++)
g_variant_builder_add (builder, "s", types[idx]);
result = g_variant_new ("(as)", builder);
g_dbus_method_invocation_return_value (data->invocation, result);
g_variant_unref (result);
g_variant_builder_unref (builder);
g_strfreev (types);
out:
invocation_data_free (data);
ensure_autoquit_on ();
}
static void
handle_sniff_uri (InvocationData *data)
{
ShellMimeSniffer *sniffer;
const gchar *uri;
GFile *file;
ensure_autoquit_off ();
g_variant_get (data->parameters,
"(&s)", &uri,
NULL);
file = g_file_new_for_uri (uri);
print_debug ("Initiating sniff for uri %s", uri);
sniffer = shell_mime_sniffer_new (file);
shell_mime_sniffer_sniff_async (sniffer,
sniff_async_ready_cb,
data);
g_object_unref (sniffer);
g_object_unref (file);
}
static void
handle_method_call (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
InvocationData *data;
data = invocation_data_new (parameters, invocation);
if (g_strcmp0 (method_name, "SniffURI") == 0)
handle_sniff_uri (data);
else
g_assert_not_reached ();
}
static const GDBusInterfaceVTable interface_vtable =
{
handle_method_call,
NULL, /* get_property */
NULL, /* set_property */
};
static void
on_bus_acquired (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
GError *error = NULL;
print_debug ("Connected to the session bus: %s", name);
g_dbus_connection_register_object (connection,
"/org/gnome/Shell/HotplugSniffer",
introspection_data->interfaces[0],
&interface_vtable,
NULL,
NULL,
&error);
if (error != NULL)
{
g_printerr ("Error exporting object on the session bus: %s",
error->message);
g_error_free (error);
_exit(1);
}
print_debug ("Object exported on the session bus");
}
static void
on_name_lost (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
print_debug ("Lost bus name: %s, exiting", name);
g_main_loop_quit (loop);
}
static void
on_name_acquired (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
print_debug ("Acquired bus name: %s", name);
}
int
main (int argc,
char **argv)
{
guint name_owner_id;
g_type_init ();
introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
g_assert (introspection_data != NULL);
ensure_autoquit_on ();
loop = g_main_loop_new (NULL, FALSE);
name_owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
BUS_NAME, 0,
on_bus_acquired,
on_name_acquired,
on_name_lost,
NULL,
NULL);
g_main_loop_run (loop);
if (name_owner_id != 0)
g_bus_unown_name (name_owner_id);
if (loop != NULL)
g_main_loop_unref (loop);
return 0;
}
/* ---------------------------------------------------------------------------------------------------- */
static void
print_debug (const gchar *format, ...)
{
gchar *s;
va_list ap;
gchar timebuf[64];
GTimeVal now;
time_t now_t;
struct tm broken_down;
static volatile gsize once_init_value = 0;
static gboolean show_debug = FALSE;
static guint pid = 0;
if (g_once_init_enter (&once_init_value))
{
show_debug = (g_getenv ("HOTPLUG_SNIFFER_DEBUG") != NULL);
pid = getpid ();
g_once_init_leave (&once_init_value, 1);
}
if (!show_debug)
goto out;
g_get_current_time (&now);
now_t = now.tv_sec;
localtime_r (&now_t, &broken_down);
strftime (timebuf, sizeof timebuf, "%H:%M:%S", &broken_down);
va_start (ap, format);
s = g_strdup_vprintf (format, ap);
va_end (ap);
g_print ("gnome-shell-hotplug-sniffer[%d]: %s.%03d: %s\n", pid, timebuf, (gint) (now.tv_usec / 1000), s);
g_free (s);
out:
;
}

View File

@ -0,0 +1,3 @@
[D-BUS Service]
Name=org.gnome.Shell.HotplugSniffer
Exec=@libexecdir@/gnome-shell-hotplug-sniffer

View File

@ -0,0 +1,607 @@
/*
* Copyright (C) 1999, 2000, 2001 Eazel, Inc.
* Copyright (C) 2011 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* Author: Cosimo Cecchi <cosimoc@redhat.com>
*
* The code for crawling the directory hierarchy is based on
* nautilus/libnautilus-private/nautilus-directory-async.c, with
* the following copyright and author:
*
* Copyright (C) 1999, 2000, 2001 Eazel, Inc.
* Author: Darin Adler <darin@bentspoon.com>
*
*/
#include "shell-mime-sniffer.h"
#include "hotplug-mimetypes.h"
#include <glib/gi18n.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#define LOADER_ATTRS \
G_FILE_ATTRIBUTE_STANDARD_TYPE "," \
G_FILE_ATTRIBUTE_STANDARD_NAME "," \
G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
#define WATCHDOG_TIMEOUT 1500
#define DIRECTORY_LOAD_ITEMS_PER_CALLBACK 100
#define HIGH_SCORE_RATIO 0.10
G_DEFINE_TYPE (ShellMimeSniffer, shell_mime_sniffer, G_TYPE_OBJECT);
enum {
PROP_FILE = 1,
NUM_PROPERTIES
};
static GHashTable *image_type_table = NULL;
static GHashTable *audio_type_table = NULL;
static GHashTable *video_type_table = NULL;
static GHashTable *docs_type_table = NULL;
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
typedef struct {
ShellMimeSniffer *self;
GFile *file;
GFileEnumerator *enumerator;
GList *deep_count_subdirectories;
gint audio_count;
gint image_count;
gint document_count;
gint video_count;
gint total_items;
} DeepCountState;
struct _ShellMimeSnifferPrivate {
GFile *file;
GCancellable *cancellable;
guint watchdog_id;
GSimpleAsyncResult *async_result;
gchar **sniffed_mime;
};
static void deep_count_load (DeepCountState *state,
GFile *file);
static void
init_mimetypes (void)
{
static gsize once_init = 0;
if (g_once_init_enter (&once_init))
{
GSList *formats, *l;
GdkPixbufFormat *format;
gchar **types;
gint idx;
image_type_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
video_type_table = g_hash_table_new (g_str_hash, g_str_equal);
audio_type_table = g_hash_table_new (g_str_hash, g_str_equal);
docs_type_table = g_hash_table_new (g_str_hash, g_str_equal);
formats = gdk_pixbuf_get_formats ();
for (l = formats; l != NULL; l = l->next)
{
format = l->data;
types = gdk_pixbuf_format_get_mime_types (format);
for (idx = 0; types[idx] != NULL; idx++)
g_hash_table_insert (image_type_table, g_strdup (types[idx]), GINT_TO_POINTER (1));
g_strfreev (types);
}
g_slist_free (formats);
for (idx = 0; audio_mimetypes[idx] != NULL; idx++)
g_hash_table_insert (audio_type_table, (gpointer) audio_mimetypes[idx], GINT_TO_POINTER (1));
for (idx = 0; video_mimetypes[idx] != NULL; idx++)
g_hash_table_insert (video_type_table, (gpointer) video_mimetypes[idx], GINT_TO_POINTER (1));
for (idx = 0; docs_mimetypes[idx] != NULL; idx++)
g_hash_table_insert (docs_type_table, (gpointer) docs_mimetypes[idx], GINT_TO_POINTER (1));
g_once_init_leave (&once_init, 1);
}
}
static void
add_content_type_to_cache (DeepCountState *state,
const gchar *content_type)
{
gboolean matched = TRUE;
if (g_hash_table_lookup (image_type_table, content_type))
state->image_count++;
else if (g_hash_table_lookup (video_type_table, content_type))
state->video_count++;
else if (g_hash_table_lookup (docs_type_table, content_type))
state->document_count++;
else if (g_hash_table_lookup (audio_type_table, content_type))
state->audio_count++;
else
matched = FALSE;
if (matched)
state->total_items++;
}
typedef struct {
const gchar *type;
gdouble ratio;
} SniffedResult;
static gint
results_cmp_func (gconstpointer a,
gconstpointer b)
{
const SniffedResult *sniffed_a = a;
const SniffedResult *sniffed_b = b;
if (sniffed_a->ratio < sniffed_b->ratio)
return 1;
if (sniffed_a->ratio > sniffed_b->ratio)
return -1;
return 0;
}
static void
prepare_async_result (DeepCountState *state)
{
ShellMimeSniffer *self = state->self;
GArray *results;
GPtrArray *sniffed_mime;
SniffedResult result;
sniffed_mime = g_ptr_array_new ();
results = g_array_new (TRUE, TRUE, sizeof (SniffedResult));
if (state->total_items == 0)
goto out;
result.type = "x-content/video";
result.ratio = (gdouble) state->video_count / (gdouble) state->total_items;
g_array_append_val (results, result);
result.type = "x-content/audio";
result.ratio = (gdouble) state->audio_count / (gdouble) state->total_items;
g_array_append_val (results, result);
result.type = "x-content/pictures";
result.ratio = (gdouble) state->image_count / (gdouble) state->total_items;
g_array_append_val (results, result);
result.type = "x-content/documents";
result.ratio = (gdouble) state->document_count / (gdouble) state->total_items;
g_array_append_val (results, result);
g_array_sort (results, results_cmp_func);
result = g_array_index (results, SniffedResult, 0);
g_ptr_array_add (sniffed_mime, g_strdup (result.type));
/* if other types score high in ratio, add them, up to three */
result = g_array_index (results, SniffedResult, 1);
if (result.ratio < HIGH_SCORE_RATIO)
goto out;
g_ptr_array_add (sniffed_mime, g_strdup (result.type));
result = g_array_index (results, SniffedResult, 2);
if (result.ratio < HIGH_SCORE_RATIO)
goto out;
g_ptr_array_add (sniffed_mime, g_strdup (result.type));
out:
g_ptr_array_add (sniffed_mime, NULL);
self->priv->sniffed_mime = (gchar **) g_ptr_array_free (sniffed_mime, FALSE);
g_array_free (results, TRUE);
g_simple_async_result_complete_in_idle (self->priv->async_result);
}
/* adapted from nautilus/libnautilus-private/nautilus-directory-async.c */
static void
deep_count_one (DeepCountState *state,
GFileInfo *info)
{
GFile *subdir;
const char *content_type;
if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
{
/* record the fact that we have to descend into this directory */
subdir = g_file_get_child (state->file, g_file_info_get_name (info));
state->deep_count_subdirectories =
g_list_append (state->deep_count_subdirectories, subdir);
}
else
{
content_type = g_file_info_get_content_type (info);
add_content_type_to_cache (state, content_type);
}
}
static void
deep_count_finish (DeepCountState *state)
{
prepare_async_result (state);
if (state->enumerator)
{
if (!g_file_enumerator_is_closed (state->enumerator))
g_file_enumerator_close_async (state->enumerator,
0, NULL, NULL, NULL);
g_object_unref (state->enumerator);
}
g_cancellable_reset (state->self->priv->cancellable);
g_clear_object (&state->file);
g_list_free_full (state->deep_count_subdirectories, g_object_unref);
g_free (state);
}
static void
deep_count_next_dir (DeepCountState *state)
{
GFile *new_file;
g_clear_object (&state->file);
if (state->deep_count_subdirectories != NULL)
{
/* Work on a new directory. */
new_file = state->deep_count_subdirectories->data;
state->deep_count_subdirectories =
g_list_remove (state->deep_count_subdirectories, new_file);
deep_count_load (state, new_file);
g_object_unref (new_file);
}
else
{
deep_count_finish (state);
}
}
static void
deep_count_more_files_callback (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
DeepCountState *state;
GList *files, *l;
GFileInfo *info;
state = user_data;
if (g_cancellable_is_cancelled (state->self->priv->cancellable))
{
deep_count_finish (state);
return;
}
files = g_file_enumerator_next_files_finish (state->enumerator,
res, NULL);
for (l = files; l != NULL; l = l->next)
{
info = l->data;
deep_count_one (state, info);
g_object_unref (info);
}
if (files == NULL)
{
g_file_enumerator_close_async (state->enumerator, 0, NULL, NULL, NULL);
g_object_unref (state->enumerator);
state->enumerator = NULL;
deep_count_next_dir (state);
}
else
{
g_file_enumerator_next_files_async (state->enumerator,
DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
G_PRIORITY_LOW,
state->self->priv->cancellable,
deep_count_more_files_callback,
state);
}
g_list_free (files);
}
static void
deep_count_callback (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
DeepCountState *state;
GFileEnumerator *enumerator;
state = user_data;
if (g_cancellable_is_cancelled (state->self->priv->cancellable))
{
deep_count_finish (state);
return;
}
enumerator = g_file_enumerate_children_finish (G_FILE (source_object),
res, NULL);
if (enumerator == NULL)
{
deep_count_next_dir (state);
}
else
{
state->enumerator = enumerator;
g_file_enumerator_next_files_async (state->enumerator,
DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
G_PRIORITY_LOW,
state->self->priv->cancellable,
deep_count_more_files_callback,
state);
}
}
static void
deep_count_load (DeepCountState *state,
GFile *file)
{
state->file = g_object_ref (file);
g_file_enumerate_children_async (state->file,
LOADER_ATTRS,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, /* flags */
G_PRIORITY_LOW, /* prio */
state->self->priv->cancellable,
deep_count_callback,
state);
}
static void
deep_count_start (ShellMimeSniffer *self)
{
DeepCountState *state;
state = g_new0 (DeepCountState, 1);
state->self = self;
deep_count_load (state, self->priv->file);
}
static void
query_info_async_ready_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
GFileInfo *info;
GError *error = NULL;
ShellMimeSniffer *self = user_data;
info = g_file_query_info_finish (G_FILE (source),
res, &error);
if (error != NULL)
{
g_simple_async_result_take_error (self->priv->async_result,
error);
g_simple_async_result_complete_in_idle (self->priv->async_result);
return;
}
if (g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY)
{
g_simple_async_result_set_error (self->priv->async_result,
G_IO_ERROR,
G_IO_ERROR_NOT_DIRECTORY,
"Not a directory");
g_simple_async_result_complete_in_idle (self->priv->async_result);
return;
}
deep_count_start (self);
}
static gboolean
watchdog_timeout_reached_cb (gpointer user_data)
{
ShellMimeSniffer *self = user_data;
self->priv->watchdog_id = 0;
g_cancellable_cancel (self->priv->cancellable);
return FALSE;
}
static void
start_loading_file (ShellMimeSniffer *self)
{
g_file_query_info_async (self->priv->file,
LOADER_ATTRS,
G_FILE_QUERY_INFO_NONE,
G_PRIORITY_DEFAULT,
self->priv->cancellable,
query_info_async_ready_cb,
self);
}
static void
shell_mime_sniffer_set_file (ShellMimeSniffer *self,
GFile *file)
{
g_clear_object (&self->priv->file);
self->priv->file = g_object_ref (file);
}
static void
shell_mime_sniffer_dispose (GObject *object)
{
ShellMimeSniffer *self = SHELL_MIME_SNIFFER (object);
g_clear_object (&self->priv->file);
g_clear_object (&self->priv->cancellable);
g_clear_object (&self->priv->async_result);
if (self->priv->watchdog_id != 0)
{
g_source_remove (self->priv->watchdog_id);
self->priv->watchdog_id = 0;
}
G_OBJECT_CLASS (shell_mime_sniffer_parent_class)->dispose (object);
}
static void
shell_mime_sniffer_finalize (GObject *object)
{
ShellMimeSniffer *self = SHELL_MIME_SNIFFER (object);
g_strfreev (self->priv->sniffed_mime);
G_OBJECT_CLASS (shell_mime_sniffer_parent_class)->finalize (object);
}
static void
shell_mime_sniffer_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
ShellMimeSniffer *self = SHELL_MIME_SNIFFER (object);
switch (prop_id) {
case PROP_FILE:
g_value_set_object (value, self->priv->file);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
shell_mime_sniffer_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
ShellMimeSniffer *self = SHELL_MIME_SNIFFER (object);
switch (prop_id) {
case PROP_FILE:
shell_mime_sniffer_set_file (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
shell_mime_sniffer_class_init (ShellMimeSnifferClass *klass)
{
GObjectClass *oclass;
oclass = G_OBJECT_CLASS (klass);
oclass->dispose = shell_mime_sniffer_dispose;
oclass->finalize = shell_mime_sniffer_finalize;
oclass->get_property = shell_mime_sniffer_get_property;
oclass->set_property = shell_mime_sniffer_set_property;
properties[PROP_FILE] =
g_param_spec_object ("file",
"File",
"The loaded file",
G_TYPE_FILE,
G_PARAM_READWRITE);
g_type_class_add_private (klass, sizeof (ShellMimeSnifferPrivate));
g_object_class_install_properties (oclass, NUM_PROPERTIES, properties);
}
static void
shell_mime_sniffer_init (ShellMimeSniffer *self)
{
self->priv =
G_TYPE_INSTANCE_GET_PRIVATE (self,
SHELL_TYPE_MIME_SNIFFER,
ShellMimeSnifferPrivate);
init_mimetypes ();
}
ShellMimeSniffer *
shell_mime_sniffer_new (GFile *file)
{
return g_object_new (SHELL_TYPE_MIME_SNIFFER,
"file", file,
NULL);
}
void
shell_mime_sniffer_sniff_async (ShellMimeSniffer *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_assert (self->priv->watchdog_id == 0);
g_assert (self->priv->async_result == NULL);
self->priv->async_result =
g_simple_async_result_new (G_OBJECT (self),
callback, user_data,
shell_mime_sniffer_sniff_finish);
self->priv->cancellable = g_cancellable_new ();
self->priv->watchdog_id =
g_timeout_add (WATCHDOG_TIMEOUT,
watchdog_timeout_reached_cb, self);
start_loading_file (self);
}
gchar **
shell_mime_sniffer_sniff_finish (ShellMimeSniffer *self,
GAsyncResult *res,
GError **error)
{
if (g_simple_async_result_propagate_error (self->priv->async_result, error))
return NULL;
return g_strdupv (self->priv->sniffed_mime);
}

View File

@ -0,0 +1,68 @@
/*
* Copyright (C) 2011 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* Author: Cosimo Cecchi <cosimoc@redhat.com>
*
*/
#ifndef __SHELL_MIME_SNIFFER_H__
#define __SHELL_MIME_SNIFFER_H__
#include <glib-object.h>
#include <gio/gio.h>
G_BEGIN_DECLS
#define SHELL_TYPE_MIME_SNIFFER (shell_mime_sniffer_get_type ())
#define SHELL_MIME_SNIFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_MIME_SNIFFER, ShellMimeSniffer))
#define SHELL_IS_MIME_SNIFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_MIME_SNIFFER))
#define SHELL_MIME_SNIFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_MIME_SNIFFER, ShellMimeSnifferClass))
#define SHELL_IS_MIME_SNIFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_MIME_SNIFFER))
#define SHELL_MIME_SNIFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_MIME_SNIFFER, ShellMimeSnifferClass))
typedef struct _ShellMimeSniffer ShellMimeSniffer;
typedef struct _ShellMimeSnifferPrivate ShellMimeSnifferPrivate;
typedef struct _ShellMimeSnifferClass ShellMimeSnifferClass;
struct _ShellMimeSniffer
{
GObject parent_instance;
ShellMimeSnifferPrivate *priv;
};
struct _ShellMimeSnifferClass
{
GObjectClass parent_class;
};
GType shell_mime_sniffer_get_type (void) G_GNUC_CONST;
ShellMimeSniffer *shell_mime_sniffer_new (GFile *file);
void shell_mime_sniffer_sniff_async (ShellMimeSniffer *self,
GAsyncReadyCallback callback,
gpointer user_data);
gchar ** shell_mime_sniffer_sniff_finish (ShellMimeSniffer *self,
GAsyncResult *res,
GError **error);
G_END_DECLS
#endif /* __SHELL_MIME_SNIFFER_H__ */

View File

@ -19,6 +19,8 @@
#include <meta/main.h>
#include <meta/meta-plugin.h>
#include <meta/prefs.h>
#include <telepathy-glib/debug.h>
#include <telepathy-glib/debug-sender.h>
#include "shell-a11y.h"
#include "shell-global.h"
@ -428,6 +430,25 @@ muted_log_handler (const char *log_domain,
/* Intentionally empty to discard message */
}
static void
default_log_handler (const char *log_domain,
GLogLevelFlags log_level,
const char *message,
gpointer data)
{
TpDebugSender *sender = data;
GTimeVal now;
g_get_current_time (&now);
tp_debug_sender_add_message (sender, &now, log_domain, log_level, message);
/* Filter out telepathy-glib logs, we don't want to flood Shell's output
* with those. */
if (!g_str_has_prefix (log_domain, "tp-glib"))
g_log_default_handler (log_domain, log_level, message, data);
}
static gboolean
print_version (const gchar *option_name,
const gchar *value,
@ -454,6 +475,7 @@ main (int argc, char **argv)
GOptionContext *ctx;
GError *error = NULL;
int ecode;
TpDebugSender *sender;
g_type_init ();
@ -469,6 +491,8 @@ main (int argc, char **argv)
exit (1);
}
g_option_context_free (ctx);
meta_plugin_type_register (gnome_shell_plugin_get_type ());
/* Prevent meta_init() from causing gtk to load gail and at-bridge */
@ -504,6 +528,16 @@ main (int argc, char **argv)
muted_log_handler, NULL);
g_log_set_handler ("Bluetooth", G_LOG_LEVEL_DEBUG | G_LOG_LEVEL_MESSAGE,
muted_log_handler, NULL);
g_log_set_handler ("tp-glib/proxy", G_LOG_LEVEL_DEBUG,
muted_log_handler, NULL);
/* Turn on telepathy-glib debugging but filter it out in
* default_log_handler. This handler also exposes all the logs over D-Bus
* using TpDebugSender. */
tp_debug_set_flags ("all");
sender = tp_debug_sender_dup ();
g_log_set_default_handler (default_log_handler, sender);
/* Initialize the global object */
shell_global_get ();
@ -516,5 +550,7 @@ main (int argc, char **argv)
g_object_unref (shell_global_get ());
}
g_object_unref (sender);
return ecode;
}

View File

@ -32,18 +32,17 @@
#include <clutter/x11/clutter-x11.h>
#include <gdk/gdkx.h>
#include <girepository.h>
#include <gjs/gjs.h>
#include <gtk/gtk.h>
#include "shell-global.h"
#include "shell-global-private.h"
static char **include_path = NULL;
static char *command = NULL;
static GOptionEntry entries[] = {
{ "command", 'c', 0, G_OPTION_ARG_STRING, &command, "Program passed in as a string", "COMMAND" },
{ "include-path", 'I', 0, G_OPTION_ARG_STRING_ARRAY, &include_path, "Add the directory DIR to the list of directories to search for js files.", "DIR" },
{ NULL }
};
@ -131,6 +130,14 @@ main(int argc, char **argv)
clutter_stage_set_title (CLUTTER_STAGE (stage), title);
g_free (title);
#if HAVE_BLUETOOTH
/* The module imports are all so intertwined that if the test
* imports anything in js/ui, it will probably eventually end up
* pulling in ui/status/bluetooth.js. So we need this.
*/
g_irepository_prepend_search_path (BLUETOOTH_DIR);
#endif
/* evaluate the script */
error = NULL;
if (!gjs_context_eval (js_context, script, len,

View File

@ -1193,109 +1193,6 @@ void shell_global_init_xdnd (ShellGlobal *global)
32, PropModeReplace, (const unsigned char *)&stage_win, 1);
}
/**
* shell_global_get_monitors:
* @global: the #ShellGlobal
*
* Gets a list of the bounding boxes of the active screen's monitors.
*
* Return value: (transfer full) (element-type Meta.Rectangle): a list
* of monitor bounding boxes.
*/
GSList *
shell_global_get_monitors (ShellGlobal *global)
{
MetaScreen *screen = shell_global_get_screen (global);
GSList *monitors = NULL;
MetaRectangle rect;
int i;
for (i = meta_screen_get_n_monitors (screen) - 1; i >= 0; i--)
{
meta_screen_get_monitor_geometry (screen, i, &rect);
monitors = g_slist_prepend (monitors,
meta_rectangle_copy (&rect));
}
return monitors;
}
/**
* shell_global_get_primary_monitor:
* @global: the #ShellGlobal
*
* Gets the bounding box of the primary monitor (the one that the
* panel is on).
*
* Return value: the bounding box of the primary monitor
*/
MetaRectangle *
shell_global_get_primary_monitor (ShellGlobal *global)
{
MetaScreen *screen = shell_global_get_screen (global);
MetaRectangle rect;
gint primary = 0;
primary = meta_screen_get_primary_monitor (screen);
meta_screen_get_monitor_geometry (screen, primary, &rect);
return meta_rectangle_copy (&rect);
}
/**
* shell_global_get_primary_monitor_index:
* @global: the #ShellGlobal
*
* Gets the index of the primary monitor (the one that the
* panel is on).
*
* Return value: the index of the primary monitor
*/
int
shell_global_get_primary_monitor_index (ShellGlobal *global)
{
MetaScreen *screen = shell_global_get_screen (global);
return meta_screen_get_primary_monitor (screen);
}
/**
* shell_global_get_focus_monitor:
* @global: the #ShellGlobal
*
* Gets the bounding box of the monitor containing the window that
* currently contains the keyboard focus.
*
* Return value: the bounding box of the focus monitor
*/
MetaRectangle *
shell_global_get_focus_monitor (ShellGlobal *global)
{
MetaScreen *screen = shell_global_get_screen (global);
MetaDisplay *display = meta_screen_get_display (screen);
MetaWindow *focus = meta_display_get_focus_window (display);
MetaRectangle rect, wrect;
int nmonitors, i;
if (focus)
{
meta_window_get_outer_rect (focus, &wrect);
nmonitors = meta_screen_get_n_monitors (screen);
/* Find the monitor that the top-left corner of @focus is on. */
for (i = 0; i < nmonitors; i++)
{
meta_screen_get_monitor_geometry (screen, i, &rect);
if (rect.x <= wrect.x && rect.y <= wrect.y &&
rect.x + rect.width > wrect.x &&
rect.y + rect.height > wrect.y)
return meta_rectangle_copy (&rect);
}
}
return shell_global_get_primary_monitor (global);
}
/**
* shell_global_get_pointer:
* @global: the #ShellGlobal

View File

@ -32,10 +32,6 @@ ShellGlobal *shell_global_get (void);
MetaScreen *shell_global_get_screen (ShellGlobal *global);
GdkScreen *shell_global_get_gdk_screen (ShellGlobal *global);
GList *shell_global_get_window_actors (ShellGlobal *global);
GSList *shell_global_get_monitors (ShellGlobal *global);
MetaRectangle *shell_global_get_primary_monitor (ShellGlobal *global);
int shell_global_get_primary_monitor_index (ShellGlobal *global);
MetaRectangle *shell_global_get_focus_monitor (ShellGlobal *global);
GSettings *shell_global_get_settings (ShellGlobal *global);
guint32 shell_global_get_current_time (ShellGlobal *global);

184
src/shell-mount-operation.c Normal file
View File

@ -0,0 +1,184 @@
/*
* Copyright (C) 2011 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* Author: Cosimo Cecchi <cosimoc@redhat.com>
*
*/
#include "shell-mount-operation.h"
/* This is a dummy class; we would like to be able to subclass the
* object from JS but we can't yet; the default GMountOperation impl
* automatically calls g_mount_operation_reply(UNHANDLED) after an idle,
* in interactive methods. We want to handle the reply outselves
* instead, so we just override the default methods with empty ones,
* except for ask-password, as we don't want to handle that.
*
* Also, we need to workaround the fact that gjs doesn't support type
* annotations for signals yet (so we can't effectively forward e.g.
* the GPid array to JS).
* See https://bugzilla.gnome.org/show_bug.cgi?id=645978
*/
G_DEFINE_TYPE (ShellMountOperation, shell_mount_operation, G_TYPE_MOUNT_OPERATION);
enum {
SHOW_PROCESSES_2,
NUM_SIGNALS
};
static guint signals[NUM_SIGNALS] = { 0, };
struct _ShellMountOperationPrivate {
GArray *pids;
gchar **choices;
gchar *message;
};
static void
shell_mount_operation_init (ShellMountOperation *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, SHELL_TYPE_MOUNT_OPERATION,
ShellMountOperationPrivate);
}
static void
shell_mount_operation_ask_password (GMountOperation *op,
const char *message,
const char *default_user,
const char *default_domain,
GAskPasswordFlags flags)
{
/* do nothing */
}
static void
shell_mount_operation_ask_question (GMountOperation *op,
const char *message,
const char *choices[])
{
/* do nothing */
}
static void
shell_mount_operation_show_processes (GMountOperation *operation,
const gchar *message,
GArray *processes,
const gchar *choices[])
{
ShellMountOperation *self = SHELL_MOUNT_OPERATION (operation);
if (self->priv->pids != NULL)
{
g_array_unref (self->priv->pids);
self->priv->pids = NULL;
}
g_free (self->priv->message);
g_strfreev (self->priv->choices);
/* save the parameters */
self->priv->pids = g_array_ref (processes);
self->priv->choices = g_strdupv ((gchar **) choices);
self->priv->message = g_strdup (message);
g_signal_emit (self, signals[SHOW_PROCESSES_2], 0);
}
static void
shell_mount_operation_finalize (GObject *obj)
{
ShellMountOperation *self = SHELL_MOUNT_OPERATION (obj);
g_strfreev (self->priv->choices);
g_free (self->priv->message);
if (self->priv->pids != NULL)
{
g_array_unref (self->priv->pids);
self->priv->pids = NULL;
}
G_OBJECT_CLASS (shell_mount_operation_parent_class)->finalize (obj);
}
static void
shell_mount_operation_class_init (ShellMountOperationClass *klass)
{
GMountOperationClass *mclass;
GObjectClass *oclass;
mclass = G_MOUNT_OPERATION_CLASS (klass);
mclass->show_processes = shell_mount_operation_show_processes;
mclass->ask_question = shell_mount_operation_ask_question;
mclass->ask_password = shell_mount_operation_ask_password;
oclass = G_OBJECT_CLASS (klass);
oclass->finalize = shell_mount_operation_finalize;
signals[SHOW_PROCESSES_2] =
g_signal_new ("show-processes-2",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0, NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
g_type_class_add_private (klass, sizeof (ShellMountOperationPrivate));
}
GMountOperation *
shell_mount_operation_new (void)
{
return g_object_new (SHELL_TYPE_MOUNT_OPERATION, NULL);
}
/**
* shell_mount_operation_get_show_processes_pids:
* @self: a #ShellMountOperation
*
* Returns: (transfer full) (element-type GPid): a #GArray
*/
GArray *
shell_mount_operation_get_show_processes_pids (ShellMountOperation *self)
{
return g_array_ref (self->priv->pids);
}
/**
* shell_mount_operation_get_show_processes_choices:
* @self: a #ShellMountOperation
*
* Returns: (transfer full):
*/
gchar **
shell_mount_operation_get_show_processes_choices (ShellMountOperation *self)
{
return g_strdupv (self->priv->choices);
}
/**
* shell_mount_operation_get_show_processes_message:
* @self: a #ShellMountOperation
*
* Returns: (transfer full):
*/
gchar *
shell_mount_operation_get_show_processes_message (ShellMountOperation *self)
{
return g_strdup (self->priv->message);
}

View File

@ -0,0 +1,63 @@
/*
* Copyright (C) 2011 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* Author: Cosimo Cecchi <cosimoc@redhat.com>
*
*/
#ifndef __SHELL_MOUNT_OPERATION_H__
#define __SHELL_MOUNT_OPERATION_H__
#include <gio/gio.h>
G_BEGIN_DECLS
#define SHELL_TYPE_MOUNT_OPERATION (shell_mount_operation_get_type ())
#define SHELL_MOUNT_OPERATION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), SHELL_TYPE_MOUNT_OPERATION, ShellMountOperation))
#define SHELL_MOUNT_OPERATION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), SHELL_TYPE_MOUNT_OPERATION, ShellMountOperationClass))
#define SHELL_IS_MOUNT_OPERATION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), SHELL_TYPE_MOUNT_OPERATION))
#define SHELL_IS_MOUNT_OPERATION_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), SHELL_TYPE_MOUNT_OPERATION))
#define SHELL_MOUNT_OPERATION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), SHELL_TYPE_MOUNT_OPERATION, ShellMountOperationClass))
typedef struct _ShellMountOperation ShellMountOperation;
typedef struct _ShellMountOperationClass ShellMountOperationClass;
typedef struct _ShellMountOperationPrivate ShellMountOperationPrivate;
struct _ShellMountOperation
{
GMountOperation parent_instance;
ShellMountOperationPrivate *priv;
};
struct _ShellMountOperationClass
{
GMountOperationClass parent_class;
};
GType shell_mount_operation_get_type (void);
GMountOperation *shell_mount_operation_new (void);
GArray * shell_mount_operation_get_show_processes_pids (ShellMountOperation *self);
gchar ** shell_mount_operation_get_show_processes_choices (ShellMountOperation *self);
gchar * shell_mount_operation_get_show_processes_message (ShellMountOperation *self);
G_END_DECLS
#endif /* __SHELL_MOUNT_OPERATION_H__ */

View File

@ -198,7 +198,7 @@ create_recording_icon (void)
texture = cogl_texture_new_from_data (32, 32,
COGL_TEXTURE_NONE,
COGL_PIXEL_FORMAT_BGRA_8888,
CLUTTER_CAIRO_FORMAT_ARGB32,
COGL_PIXEL_FORMAT_ANY,
cairo_image_surface_get_stride (surface),
cairo_image_surface_get_data (surface));
@ -529,7 +529,7 @@ recorder_record_frame (ShellRecorder *recorder)
cogl_read_pixels (0, 0,
recorder->stage_width, recorder->stage_height,
COGL_READ_PIXELS_COLOR_BUFFER,
COGL_PIXEL_FORMAT_BGRA_8888_PRE,
CLUTTER_CAIRO_FORMAT_ARGB32,
data);
recorder_draw_cursor (recorder, buffer);

View File

@ -101,6 +101,32 @@ shell_tp_client_init (ShellTpClient *self)
/* Approver */
tp_base_client_add_approver_filter (TP_BASE_CLIENT (self), filter);
/* Approve room invitations. We don't handle or observe room channels so
* just register this filter for the approver. */
tp_base_client_take_approver_filter (TP_BASE_CLIENT (self), tp_asv_new (
TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
TP_IFACE_CHANNEL_TYPE_TEXT,
TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT,
TP_HANDLE_TYPE_ROOM,
NULL));
/* Approve calls (StreameMedia and Call.DRAFT). We let Empathy handle the
* call itself. */
tp_base_client_take_approver_filter (TP_BASE_CLIENT (self),
tp_asv_new (
TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT,
NULL));
/* FIXME: use TP_IFACE_CHANNEL_TYPE_CALL once API is undrafted (fdo #24936) */
tp_base_client_take_approver_filter (TP_BASE_CLIENT (self),
tp_asv_new (
TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
"org.freedesktop.Telepathy.Channel.Type.Call.DRAFT",
TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT,
NULL));
/* Handler */
tp_base_client_add_handler_filter (TP_BASE_CLIENT (self), filter);
@ -371,3 +397,15 @@ shell_get_contact_events (TplLogManager *log_manager,
NULL, NULL,
callback, NULL);
}
/* gjs doesn't allow us to craft a GError so we need a C wrapper */
void
shell_decline_dispatch_op (TpAddDispatchOperationContext *context,
const gchar *message)
{
GError *error = g_error_new_literal (TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
message);
tp_add_dispatch_operation_context_fail (context, error);
g_error_free (error);
}

View File

@ -111,5 +111,8 @@ void shell_get_contact_events (TplLogManager *log_manager,
guint num_events,
GAsyncReadyCallback callback);
void shell_decline_dispatch_op (TpAddDispatchOperationContext *context,
const gchar *message);
G_END_DECLS
#endif /* __SHELL_TP_CLIENT_H__ */

View File

@ -560,6 +560,29 @@ shell_get_event_state (ClutterEvent *event)
return state & CLUTTER_MODIFIER_MASK;
}
/**
* shell_write_soup_message_to_stream:
* @stream: a #GOutputStream
* @message: a #SoupMessage
* @error: location to store GError
*
* Write a string to a GOutputStream as binary data. This is a
* workaround for the lack of proper binary strings in GJS.
*/
void
shell_write_soup_message_to_stream (GOutputStream *stream,
SoupMessage *message,
GError **error)
{
SoupMessageBody *body;
body = message->response_body;
g_output_stream_write_all (stream,
body->data, body->length,
NULL, NULL, error);
}
/**
* shell_write_string_to_stream:
* @stream: a #GOutputStream

View File

@ -5,6 +5,7 @@
#include <gio/gio.h>
#include <clutter/clutter.h>
#include <libsoup/soup.h>
G_BEGIN_DECLS
@ -25,6 +26,10 @@ char *shell_util_format_date (const char *format,
ClutterModifierType
shell_get_event_state (ClutterEvent *event);
void shell_write_soup_message_to_stream (GOutputStream *stream,
SoupMessage *message,
GError **error);
gboolean shell_write_string_to_stream (GOutputStream *stream,
const char *str,
GError **error);

View File

@ -232,11 +232,7 @@ xfixes_cursor_reset_image (ShellXFixesCursor *xfixes_cursor)
sprite = cogl_texture_new_from_data (cursor_image->width,
cursor_image->height,
COGL_TEXTURE_NONE,
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
COGL_PIXEL_FORMAT_BGRA_8888_PRE,
#else
COGL_PIXEL_FORMAT_ARGB_8888_PRE,
#endif
CLUTTER_CAIRO_FORMAT_ARGB32,
COGL_PIXEL_FORMAT_ANY,
cursor_image->width * 4, /* stride */
cursor_data);

View File

@ -36,15 +36,6 @@
#include <cairo.h>
/* Cairo stores the data in native byte order as ARGB but Cogl's pixel
formats specify the actual byte order. Therefore we need to use a
different format depending on the architecture */
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
#define PIXEL_FORMAT COGL_PIXEL_FORMAT_BGRA_8888_PRE
#else
#define PIXEL_FORMAT COGL_PIXEL_FORMAT_ARGB_8888_PRE
#endif
G_DEFINE_TYPE(StDrawingArea, st_drawing_area, ST_TYPE_WIDGET);
struct _StDrawingAreaPrivate {
@ -122,7 +113,7 @@ st_drawing_area_paint (ClutterActor *self)
{
priv->texture = cogl_texture_new_with_size (width, height,
COGL_TEXTURE_NONE,
PIXEL_FORMAT);
CLUTTER_CAIRO_FORMAT_ARGB32);
priv->needs_repaint = TRUE;
}
@ -142,7 +133,7 @@ st_drawing_area_paint (ClutterActor *self)
priv->context = NULL;
cogl_texture_set_region (priv->texture, 0, 0, 0, 0, width, height, width, height,
PIXEL_FORMAT,
CLUTTER_CAIRO_FORMAT_ARGB32,
cairo_image_surface_get_stride (surface),
cairo_image_surface_get_data (surface));

View File

@ -200,15 +200,18 @@ st_entry_style_changed (StWidget *self)
st_theme_node_get_foreground_color (theme_node, &color);
clutter_text_set_color (CLUTTER_TEXT (priv->entry), &color);
if (st_theme_node_lookup_length (theme_node, "caret-size", FALSE, &size))
if (st_theme_node_lookup_length (theme_node, "caret-size", TRUE, &size))
clutter_text_set_cursor_size (CLUTTER_TEXT (priv->entry), (int)(.5 + size));
if (st_theme_node_lookup_color (theme_node, "caret-color", FALSE, &color))
if (st_theme_node_lookup_color (theme_node, "caret-color", TRUE, &color))
clutter_text_set_cursor_color (CLUTTER_TEXT (priv->entry), &color);
if (st_theme_node_lookup_color (theme_node, "selection-background-color", FALSE, &color))
if (st_theme_node_lookup_color (theme_node, "selection-background-color", TRUE, &color))
clutter_text_set_selection_color (CLUTTER_TEXT (priv->entry), &color);
if (st_theme_node_lookup_color (theme_node, "selected-color", TRUE, &color))
clutter_text_set_selected_text_color (CLUTTER_TEXT (priv->entry), &color);
font = st_theme_node_get_font (theme_node);
font_string = pango_font_description_to_string (font);
clutter_text_set_font_name (CLUTTER_TEXT (priv->entry), font_string);

View File

@ -167,11 +167,7 @@ create_corner_material (StCornerSpec *corner)
texture = cogl_texture_new_from_data (size, size,
COGL_TEXTURE_NONE,
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
COGL_PIXEL_FORMAT_BGRA_8888_PRE,
#else
COGL_PIXEL_FORMAT_ARGB_8888_PRE,
#endif
CLUTTER_CAIRO_FORMAT_ARGB32,
COGL_PIXEL_FORMAT_ANY,
rowstride,
data);
@ -257,6 +253,57 @@ over (const ClutterColor *source,
unpremultiply (result);
}
/*
* st_theme_node_reduce_border_radius:
* @node: a #StThemeNode
* @corners: (array length=4) (out): reduced corners
*
* Implements the corner overlap algorithm mentioned at
* http://www.w3.org/TR/css3-background/#corner-overlap
*/
static void
st_theme_node_reduce_border_radius (StThemeNode *node,
guint *corners)
{
gfloat scale;
guint sum;
scale = 1.0;
/* top */
sum = node->border_radius[ST_CORNER_TOPLEFT]
+ node->border_radius[ST_CORNER_TOPRIGHT];
if (sum > 0)
scale = MIN (node->alloc_width / sum, scale);
/* right */
sum = node->border_radius[ST_CORNER_TOPRIGHT]
+ node->border_radius[ST_CORNER_BOTTOMRIGHT];
if (sum > 0)
scale = MIN (node->alloc_height / sum, scale);
/* bottom */
sum = node->border_radius[ST_CORNER_BOTTOMLEFT]
+ node->border_radius[ST_CORNER_BOTTOMRIGHT];
if (sum > 0)
scale = MIN (node->alloc_width / sum, scale);
/* left */
sum = node->border_radius[ST_CORNER_BOTTOMLEFT]
+ node->border_radius[ST_CORNER_TOPLEFT];
if (sum > 0)
scale = MIN (node->alloc_height / sum, scale);
corners[ST_CORNER_TOPLEFT] = node->border_radius[ST_CORNER_TOPLEFT] * scale;
corners[ST_CORNER_TOPRIGHT] = node->border_radius[ST_CORNER_TOPRIGHT] * scale;
corners[ST_CORNER_BOTTOMLEFT] = node->border_radius[ST_CORNER_BOTTOMLEFT] * scale;
corners[ST_CORNER_BOTTOMRIGHT] = node->border_radius[ST_CORNER_BOTTOMRIGHT] * scale;
}
static void
st_theme_node_get_corner_border_widths (StThemeNode *node,
StCorner corner_id,
@ -301,13 +348,15 @@ st_theme_node_lookup_corner (StThemeNode *node,
StTextureCache *cache;
StCornerSpec corner;
LoadCornerData data;
guint radius[4];
if (node->border_radius[corner_id] == 0)
return COGL_INVALID_HANDLE;
cache = st_texture_cache_get_default ();
corner.radius = node->border_radius[corner_id];
st_theme_node_reduce_border_radius (node, radius);
corner.radius = radius[corner_id];
corner.color = node->background_color;
st_theme_node_get_corner_border_widths (node, corner_id,
&corner.border_width_1,
@ -877,7 +926,8 @@ st_theme_node_prerender_background (StThemeNode *node)
{
StBorderImage *border_image;
CoglHandle texture;
int radius[4], i;
guint radius[4];
int i;
cairo_t *cr;
cairo_surface_t *surface;
StShadow *shadow_spec;
@ -944,12 +994,10 @@ st_theme_node_prerender_background (StThemeNode *node)
/* TODO - support non-uniform border colors */
get_arbitrary_border_color (node, &border_color);
for (i = 0; i < 4; i++)
{
border_width[i] = st_theme_node_get_border_width (node, i);
st_theme_node_reduce_border_radius (node, radius);
radius[i] = st_theme_node_get_border_radius (node, i);
}
for (i = 0; i < 4; i++)
border_width[i] = st_theme_node_get_border_width (node, i);
/* Note we don't support translucent background images on top
* of gradients. It's strictly either/or.
@ -1198,14 +1246,7 @@ st_theme_node_prerender_background (StThemeNode *node)
texture = cogl_texture_new_from_data (paint_box.x2 - paint_box.x1,
paint_box.y2 - paint_box.y1,
COGL_TEXTURE_NONE,
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
COGL_PIXEL_FORMAT_BGRA_8888_PRE,
#elif G_BYTE_ORDER == G_BIG_ENDIAN
COGL_PIXEL_FORMAT_ARGB_8888_PRE,
#else
COGL_PIXEL_FORMAT_ANY,
#error unknown endianness type
#endif
CLUTTER_CAIRO_FORMAT_ARGB32,
COGL_PIXEL_FORMAT_ANY,
rowstride,
data);
@ -1278,6 +1319,7 @@ st_theme_node_render_resources (StThemeNode *node,
gboolean has_border;
gboolean has_border_radius;
gboolean has_inset_box_shadow;
gboolean has_large_corners;
StShadow *box_shadow_spec;
StShadow *background_image_shadow_spec;
const char *background_image;
@ -1315,6 +1357,27 @@ st_theme_node_render_resources (StThemeNode *node,
else
has_border_radius = FALSE;
/* The cogl code pads each corner to the maximum border radius,
* which results in overlapping corner areas if the radius
* exceeds the actor's halfsize, causing rendering errors.
* Fall back to cairo in these cases. */
has_large_corners = FALSE;
if (has_border_radius) {
guint border_radius[4];
int corner;
st_theme_node_reduce_border_radius (node, border_radius);
for (corner = 0; corner < 4; corner ++) {
if (border_radius[corner] * 2 > height ||
border_radius[corner] * 2 > width) {
has_large_corners = TRUE;
break;
}
}
}
/* Load referenced images from disk and draw anything we need with cairo now */
background_image = st_theme_node_get_background_image (node);
border_image = st_theme_node_get_border_image (node);
@ -1335,7 +1398,8 @@ st_theme_node_render_resources (StThemeNode *node,
/* Use cairo to prerender the node if there is a gradient, or
* background image with borders and/or rounded corners,
* since we can't do those things easily with cogl.
* or large corners, since we can't do those things
* easily with cogl.
*
* FIXME: if we could figure out ahead of time that a
* background image won't overlap with the node borders,
@ -1343,7 +1407,8 @@ st_theme_node_render_resources (StThemeNode *node,
*/
if ((node->background_gradient_type != ST_GRADIENT_NONE)
|| (has_inset_box_shadow && (has_border || node->background_color.alpha > 0))
|| (background_image && (has_border || has_border_radius)))
|| (background_image && (has_border || has_border_radius))
|| has_large_corners)
node->prerendered_texture = st_theme_node_prerender_background (node);
if (node->prerendered_texture)
@ -1457,6 +1522,7 @@ st_theme_node_paint_borders (StThemeNode *node,
{
float width, height;
int border_width[4];
guint border_radius[4];
int max_border_radius = 0;
int max_width_radius[4];
int corner_id, side_id;
@ -1472,6 +1538,8 @@ st_theme_node_paint_borders (StThemeNode *node,
for (side_id = 0; side_id < 4; side_id++)
border_width[side_id] = st_theme_node_get_border_width(node, side_id);
st_theme_node_reduce_border_radius (node, border_radius);
for (corner_id = 0; corner_id < 4; corner_id++)
{
guint border_width_1, border_width_2;
@ -1479,10 +1547,10 @@ st_theme_node_paint_borders (StThemeNode *node,
st_theme_node_get_corner_border_widths (node, corner_id,
&border_width_1, &border_width_2);
if (node->border_radius[corner_id] > max_border_radius)
max_border_radius = node->border_radius[corner_id];
if (border_radius[corner_id] > max_border_radius)
max_border_radius = border_radius[corner_id];
max_width_radius[corner_id] = MAX(MAX(border_width_1, border_width_2),
node->border_radius[corner_id]);
border_radius[corner_id]);
}
/* borders */
@ -1506,8 +1574,8 @@ st_theme_node_paint_borders (StThemeNode *node,
alpha);
/* NORTH */
skip_corner_1 = node->border_radius[ST_CORNER_TOPLEFT] > 0;
skip_corner_2 = node->border_radius[ST_CORNER_TOPRIGHT] > 0;
skip_corner_1 = border_radius[ST_CORNER_TOPLEFT] > 0;
skip_corner_2 = border_radius[ST_CORNER_TOPRIGHT] > 0;
x1 = skip_corner_1 ? max_width_radius[ST_CORNER_TOPLEFT] : 0;
y1 = 0;
@ -1516,8 +1584,8 @@ st_theme_node_paint_borders (StThemeNode *node,
cogl_rectangle (x1, y1, x2, y2);
/* EAST */
skip_corner_1 = node->border_radius[ST_CORNER_TOPRIGHT] > 0;
skip_corner_2 = node->border_radius[ST_CORNER_BOTTOMRIGHT] > 0;
skip_corner_1 = border_radius[ST_CORNER_TOPRIGHT] > 0;
skip_corner_2 = border_radius[ST_CORNER_BOTTOMRIGHT] > 0;
x1 = width - border_width[ST_SIDE_RIGHT];
y1 = skip_corner_1 ? max_width_radius[ST_CORNER_TOPRIGHT]
@ -1528,8 +1596,8 @@ st_theme_node_paint_borders (StThemeNode *node,
cogl_rectangle (x1, y1, x2, y2);
/* SOUTH */
skip_corner_1 = node->border_radius[ST_CORNER_BOTTOMLEFT] > 0;
skip_corner_2 = node->border_radius[ST_CORNER_BOTTOMRIGHT] > 0;
skip_corner_1 = border_radius[ST_CORNER_BOTTOMLEFT] > 0;
skip_corner_2 = border_radius[ST_CORNER_BOTTOMRIGHT] > 0;
x1 = skip_corner_1 ? max_width_radius[ST_CORNER_BOTTOMLEFT] : 0;
y1 = height - border_width[ST_SIDE_BOTTOM];
@ -1539,8 +1607,8 @@ st_theme_node_paint_borders (StThemeNode *node,
cogl_rectangle (x1, y1, x2, y2);
/* WEST */
skip_corner_1 = node->border_radius[ST_CORNER_TOPLEFT] > 0;
skip_corner_2 = node->border_radius[ST_CORNER_BOTTOMLEFT] > 0;
skip_corner_1 = border_radius[ST_CORNER_TOPLEFT] > 0;
skip_corner_2 = border_radius[ST_CORNER_BOTTOMLEFT] > 0;
x1 = 0;
y1 = skip_corner_1 ? max_width_radius[ST_CORNER_TOPLEFT]
@ -1610,32 +1678,32 @@ st_theme_node_paint_borders (StThemeNode *node,
int n_rects;
/* corner texture does not need padding */
if (max_border_radius == node->border_radius[corner_id])
if (max_border_radius == border_radius[corner_id])
continue;
n_rects = node->border_radius[corner_id] == 0 ? 1 : 2;
n_rects = border_radius[corner_id] == 0 ? 1 : 2;
switch (corner_id)
{
case ST_CORNER_TOPLEFT:
verts[0] = border_width[ST_SIDE_LEFT];
verts[1] = MAX(node->border_radius[corner_id],
verts[1] = MAX(border_radius[corner_id],
border_width[ST_SIDE_TOP]);
verts[2] = max_border_radius;
verts[3] = max_border_radius;
if (n_rects == 2)
{
verts[4] = MAX(node->border_radius[corner_id],
verts[4] = MAX(border_radius[corner_id],
border_width[ST_SIDE_LEFT]);
verts[5] = border_width[ST_SIDE_TOP];
verts[6] = max_border_radius;
verts[7] = MAX(node->border_radius[corner_id],
verts[7] = MAX(border_radius[corner_id],
border_width[ST_SIDE_TOP]);
}
break;
case ST_CORNER_TOPRIGHT:
verts[0] = width - max_border_radius;
verts[1] = MAX(node->border_radius[corner_id],
verts[1] = MAX(border_radius[corner_id],
border_width[ST_SIDE_TOP]);
verts[2] = width - border_width[ST_SIDE_RIGHT];
verts[3] = max_border_radius;
@ -1643,9 +1711,9 @@ st_theme_node_paint_borders (StThemeNode *node,
{
verts[4] = width - max_border_radius;
verts[5] = border_width[ST_SIDE_TOP];
verts[6] = width - MAX(node->border_radius[corner_id],
verts[6] = width - MAX(border_radius[corner_id],
border_width[ST_SIDE_RIGHT]);
verts[7] = MAX(node->border_radius[corner_id],
verts[7] = MAX(border_radius[corner_id],
border_width[ST_SIDE_TOP]);
}
break;
@ -1653,14 +1721,14 @@ st_theme_node_paint_borders (StThemeNode *node,
verts[0] = width - max_border_radius;
verts[1] = height - max_border_radius;
verts[2] = width - border_width[ST_SIDE_RIGHT];
verts[3] = height - MAX(node->border_radius[corner_id],
verts[3] = height - MAX(border_radius[corner_id],
border_width[ST_SIDE_BOTTOM]);
if (n_rects == 2)
{
verts[4] = width - max_border_radius;
verts[5] = height - MAX(node->border_radius[corner_id],
verts[5] = height - MAX(border_radius[corner_id],
border_width[ST_SIDE_BOTTOM]);
verts[6] = width - MAX(node->border_radius[corner_id],
verts[6] = width - MAX(border_radius[corner_id],
border_width[ST_SIDE_RIGHT]);
verts[7] = height - border_width[ST_SIDE_BOTTOM];
}
@ -1669,13 +1737,13 @@ st_theme_node_paint_borders (StThemeNode *node,
verts[0] = border_width[ST_SIDE_LEFT];
verts[1] = height - max_border_radius;
verts[2] = max_border_radius;
verts[3] = height - MAX(node->border_radius[corner_id],
verts[3] = height - MAX(border_radius[corner_id],
border_width[ST_SIDE_BOTTOM]);
if (n_rects == 2)
{
verts[4] = MAX(node->border_radius[corner_id],
verts[4] = MAX(border_radius[corner_id],
border_width[ST_SIDE_LEFT]);
verts[5] = height - MAX(node->border_radius[corner_id],
verts[5] = height - MAX(border_radius[corner_id],
border_width[ST_SIDE_BOTTOM]);
verts[6] = max_border_radius;
verts[7] = height - border_width[ST_SIDE_BOTTOM];

View File

@ -18,7 +18,8 @@ TEST_JS = \
testcommon/border-image.png \
testcommon/face-plain.png \
testcommon/ui.js \
unit/format.js
unit/format.js \
unit/markup.js
EXTRA_DIST += $(TEST_JS)
TEST_MISC = \
@ -28,6 +29,7 @@ EXTRA_DIST += $(TEST_MISC)
run-test.sh: run-test.sh.in
$(AM_V_GEN) sed \
-e "s|@MUTTER_TYPELIB_DIR[@]|$(MUTTER_TYPELIB_DIR)|" \
-e "s|@JHBUILD_TYPELIBDIR[@]|$(JHBUILD_TYPELIBDIR)|" \
-e "s|@srcdir[@]|$(srcdir)|" \
$< > $@ && chmod a+x $@

View File

@ -51,6 +51,13 @@ addTestCase(" 5px 10px 15px 0px", true);
addTestCase("10px 15px 0px 5px", true);
addTestCase("15px 0px 5px 10px", true);
// border-radius reduction
// these should all take the cairo fallback,
// so don't bother testing w/ or w/out gradients.
addTestCase("200px 200px 200px 200px", false);
addTestCase("200px 200px 0px 200px", false);
addTestCase("999px 0px 999px 0px", false);
stage.show();
Clutter.main();
stage.destroy();

View File

@ -30,7 +30,7 @@ builddir=`cd $builddir && pwd`
srcdir=$builddir/@srcdir@
srcdir=`cd $srcdir && pwd`
GI_TYPELIB_PATH="@MUTTER_TYPELIB_DIR@:$builddir/../src"
GI_TYPELIB_PATH="$GI_TYPELIB_PATH${GI_TYPELIB_PATH:+:}@MUTTER_TYPELIB_DIR@:@JHBUILD_TYPELIBDIR@:$builddir/../src"
GJS_PATH="$srcdir:$srcdir/../js"
GJS_DEBUG_OUTPUT=stderr
$verbose || GJS_DEBUG_TOPICS="JS ERROR;JS LOG"

142
tests/unit/markup.js Normal file
View File

@ -0,0 +1,142 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// Test cases for MessageTray markup parsing
const JsUnit = imports.jsUnit;
const Pango = imports.gi.Pango;
const Environment = imports.ui.environment;
Environment.init();
const MessageTray = imports.ui.messageTray;
// Assert that @input, assumed to be markup, gets "fixed" to @output,
// which is valid markup. If @output is null, @input is expected to
// convert to itself
function assertConverts(input, output) {
if (!output)
output = input;
let fixed = MessageTray._fixMarkup(input, true);
JsUnit.assertEquals(output, fixed);
let parsed = false;
try {
Pango.parse_markup(fixed, -1, '');
parsed = true;
} catch (e) {}
JsUnit.assertEquals(true, parsed);
}
// Assert that @input, assumed to be plain text, gets escaped to @output,
// which is valid markup.
function assertEscapes(input, output) {
let fixed = MessageTray._fixMarkup(input, false);
JsUnit.assertEquals(output, fixed);
let parsed = false;
try {
Pango.parse_markup(fixed, -1, '');
parsed = true;
} catch (e) {}
JsUnit.assertEquals(true, parsed);
}
// CORRECT MARKUP
assertConverts('foo');
assertEscapes('foo', 'foo');
assertConverts('<b>foo</b>');
assertEscapes('<b>foo</b>', '&lt;b&gt;foo&lt;/b&gt;');
assertConverts('something <i>foo</i>');
assertEscapes('something <i>foo</i>', 'something &lt;i&gt;foo&lt;/i&gt;');
assertConverts('<u>foo</u> something');
assertEscapes('<u>foo</u> something', '&lt;u&gt;foo&lt;/u&gt; something');
assertConverts('<b>bold</b> <i>italic <u>and underlined</u></i>');
assertEscapes('<b>bold</b> <i>italic <u>and underlined</u></i>', '&lt;b&gt;bold&lt;/b&gt; &lt;i&gt;italic &lt;u&gt;and underlined&lt;/u&gt;&lt;/i&gt;');
assertConverts('this &amp; that');
assertEscapes('this &amp; that', 'this &amp;amp; that');
assertConverts('this &lt; that');
assertEscapes('this &lt; that', 'this &amp;lt; that');
assertConverts('this &lt; that &gt; the other');
assertEscapes('this &lt; that &gt; the other', 'this &amp;lt; that &amp;gt; the other');
assertConverts('this &lt;<i>that</i>&gt;');
assertEscapes('this &lt;<i>that</i>&gt;', 'this &amp;lt;&lt;i&gt;that&lt;/i&gt;&amp;gt;');
assertConverts('<b>this</b> > <i>that</i>');
assertEscapes('<b>this</b> > <i>that</i>', '&lt;b&gt;this&lt;/b&gt; &gt; &lt;i&gt;that&lt;/i&gt;');
// PARTIALLY CORRECT MARKUP
// correct bits are kept, incorrect bits are escaped
// unrecognized entity
assertConverts('<b>smile</b> &#9786;!', '<b>smile</b> &amp;#9786;!');
assertEscapes('<b>smile</b> &#9786;!', '&lt;b&gt;smile&lt;/b&gt; &amp;#9786;!');
// stray '&'; this is really a bug, but it's easier to do it this way
assertConverts('<b>this</b> & <i>that</i>', '<b>this</b> &amp; <i>that</i>');
assertEscapes('<b>this</b> & <i>that</i>', '&lt;b&gt;this&lt;/b&gt; &amp; &lt;i&gt;that&lt;/i&gt;');
// likewise with stray '<'
assertConverts('this < that', 'this &lt; that');
assertEscapes('this < that', 'this &lt; that');
assertConverts('<b>this</b> < <i>that</i>', '<b>this</b> &lt; <i>that</i>');
assertEscapes('<b>this</b> < <i>that</i>', '&lt;b&gt;this&lt;/b&gt; &lt; &lt;i&gt;that&lt;/i&gt;');
assertConverts('this < that > the other', 'this &lt; that > the other');
assertEscapes('this < that > the other', 'this &lt; that &gt; the other');
assertConverts('this <<i>that</i>>', 'this &lt;<i>that</i>>');
assertEscapes('this <<i>that</i>>', 'this &lt;&lt;i&gt;that&lt;/i&gt;&gt;');
// unknown tags
assertConverts('<unknown>tag</unknown>', '&lt;unknown>tag&lt;/unknown>');
assertEscapes('<unknown>tag</unknown>', '&lt;unknown&gt;tag&lt;/unknown&gt;');
// make sure we check beyond the first letter
assertConverts('<bunknown>tag</bunknown>', '&lt;bunknown>tag&lt;/bunknown>');
assertEscapes('<bunknown>tag</bunknown>', '&lt;bunknown&gt;tag&lt;/bunknown&gt;');
// with mix of good and bad, we keep the good and escape the bad
assertConverts('<i>known</i> and <unknown>tag</unknown>', '<i>known</i> and &lt;unknown>tag&lt;/unknown>');
assertEscapes('<i>known</i> and <unknown>tag</unknown>', '&lt;i&gt;known&lt;/i&gt; and &lt;unknown&gt;tag&lt;/unknown&gt;');
// FULLY INCORRECT MARKUP
// (fall back to escaping the whole thing)
// tags not matched up
assertConverts('<b>in<i>com</i>plete', '&lt;b&gt;in&lt;i&gt;com&lt;/i&gt;plete');
assertEscapes('<b>in<i>com</i>plete', '&lt;b&gt;in&lt;i&gt;com&lt;/i&gt;plete');
assertConverts('in<i>com</i>plete</b>', 'in&lt;i&gt;com&lt;/i&gt;plete&lt;/b&gt;');
assertEscapes('in<i>com</i>plete</b>', 'in&lt;i&gt;com&lt;/i&gt;plete&lt;/b&gt;');
// we don't support attributes, and it's too complicated to try
// to escape both start and end tags, so we just treat it as bad
assertConverts('<b>good</b> and <b style=\'bad\'>bad</b>', '&lt;b&gt;good&lt;/b&gt; and &lt;b style=&apos;bad&apos;&gt;bad&lt;/b&gt;');
assertEscapes('<b>good</b> and <b style=\'bad\'>bad</b>', '&lt;b&gt;good&lt;/b&gt; and &lt;b style=&apos;bad&apos;&gt;bad&lt;/b&gt;');
// this is just syntactically invalid
assertConverts('<b>unrecognized</b stuff>', '&lt;b&gt;unrecognized&lt;/b stuff&gt;');
assertEscapes('<b>unrecognized</b stuff>', '&lt;b&gt;unrecognized&lt;/b stuff&gt;');
// mismatched tags
assertConverts('<b>mismatched</i>', '&lt;b&gt;mismatched&lt;/i&gt;');
assertEscapes('<b>mismatched</i>', '&lt;b&gt;mismatched&lt;/i&gt;');
assertConverts('<b>mismatched/unknown</bunknown>', '&lt;b&gt;mismatched/unknown&lt;/bunknown&gt;');
assertEscapes('<b>mismatched/unknown</bunknown>', '&lt;b&gt;mismatched/unknown&lt;/bunknown&gt;');

12
tools/build/gnome-shell-build-setup.sh Executable file → Normal file
View File

@ -59,7 +59,7 @@ fi
# Devel packages needed by gnome-shell and its deps:
# dbus-glib, expat, GL, gnome-menus, gstreamer, libffi,
# libjasper, libjpeg, libpng, libpulse, libtiff, libwnck,
# iso-codes, libical, libxml2, ORBit2, pam, python, readline,
# iso-codes, libical, libxml2, ORBit2, pam, python, readline, upower,
# spidermonkey ({mozilla,firefox,xulrunner}-js), startup-notification,
# xdamage, icon-naming-utils, upower, libtool-ltdl, libvorbis,
# libgcrypt, libtasn1, libgnome-keyring, libgtop, cups, xcb, WebKit-gtk
@ -84,9 +84,15 @@ if test "x$system" = xUbuntu -o "x$system" = xDebian -o "x$system" = xLinuxMint
libltdl-dev libvorbis-dev iso-codes libgnome-keyring-dev libusb-1.0-0-dev
libupower-glib-dev libcups2-dev libproxy-dev libdb-dev libproxy-dev
libsqlite3-dev libgudev-1.0-dev libsane-dev libwebkitgtk-3.0-0
libx11-xcb-dev libxcb-event1-dev libxcb-aux0-dev
libx11-xcb-dev libupower-glib-dev
"
if apt-cache show libxcb-util0-dev > /dev/null 2> /dev/null; then
reqd="$reqd libxcb-util0-dev"
else
reqd="$reqd libxcb-event1-dev libxcb-aux0-dev"
fi
if apt-cache show autopoint > /dev/null 2> /dev/null; then
reqd="$reqd autopoint"
fi
@ -119,7 +125,7 @@ if test "x$system" = xFedora ; then
pulseaudio-libs-devel python-devel pygobject2 readline-devel xulrunner-devel
libXdamage-devel libcroco-devel libxml2-devel gstreamer-devel
gstreamer-plugins-base gstreamer-plugins-good glx-utils expat-devel
startup-notification-devel zenity webkitgtk3-devel
startup-notification-devel zenity webkitgtk3-devel upower-devel
icon-naming-utils upower-devel libtool-ltdl-devel libvorbis-devel
iso-codes-devel libgcrypt-devel libtasn1-devel libtasn1-tools libusb1-devel
libgnome-keyring-devel libgtop2-devel cups-devel db4-devel libproxy-devel

View File

@ -295,19 +295,6 @@
</dependencies>
</autotools>
<autotools id="gnome-power-manager">
<branch repo="git.gnome.org" module="gnome-power-manager" />
<dependencies>
<dep package="gtk3"/>
<dep package="glib"/>
<dep package="gconf"/>
<dep package="cairo"/>
<dep package="libnotify"/>
<dep package="libcanberra"/>
<dep package="gnome-control-center"/>
</dependencies>
</autotools>
<autotools id="telepathy-glib" autogenargs="--disable-Werror" >
<branch repo="git.freedesktop.org" module="telepathy/telepathy-glib" />
<dependencies>
@ -342,7 +329,7 @@
<dep package="gnome-bluetooth"/>
<dep package="telepathy-glib"/>
<dep package="telepathy-logger"/>
<dep package="upower"/>
<dep package="libsoup"/>
</dependencies>
</autotools>
@ -360,7 +347,6 @@
<dep package="librsvg"/>
<dep package="gnome-themes-standard"/>
<dep package="gnome-shell"/>
<dep package="gnome-power-manager"/>
</dependencies>
</metamodule>
@ -385,19 +371,6 @@
</suggests>
</tarball>
<tarball id="upower" version="0.9.8">
<source href="http://upower.freedesktop.org/releases/upower-0.9.8.tar.bz2"
hash="sha256:7afaec3cb10ebbc898308c7abd250c27fd58e10379a42e80444d7a4b32f2eb91"
size="462754"/>
<dependencies>
<dep package="polkit"/>
</dependencies>
<suggests>
<dep package="gobject-introspection"/>
<dep package="gudev"/>
</suggests>
</tarball>
<autotools id="libnotify">
<branch repo="git.gnome.org" module="libnotify"/>
<dependencies>