Compare commits

...

190 Commits

Author SHA1 Message Date
a1aa58bb64 Bump version to 3.1.90.1
Update NEWS
2011-08-31 11:48:43 -04:00
82ffe233dc Updated Spanish translation 2011-08-30 21:36:47 +02:00
9e16bb85e3 autorun: use the new "media-removable" icon
gnome-icon-theme recently added this icon for removable devices.

https://bugzilla.gnome.org/show_bug.cgi?id=657757
2011-08-30 15:30:10 -04:00
887b41bce3 Updated Spanish translation 2011-08-30 21:24:52 +02:00
63eef286c9 Fixed and updated Russian translation 2011-08-30 20:32:22 +04:00
14e8cba2b1 Add some (element-type) annotations to appease g-i master 2011-08-30 12:07:43 -04:00
3418e6e85e Add support for asynchronous search providers
Some search providers may want to change their results, or may not
want to block on an external service to get their results (DBus, etc.)
Set up an infrastructure to allow search providers to add their search
results at a later time.

Based on a patch by Jasper St. Pierre and Seif Lotfy.

https://bugzilla.gnome.org/show_bug.cgi?id=655220
2011-08-30 11:55:28 -04:00
8f0c980d3c Updated POTFILES.in 2011-08-30 17:39:09 +02:00
c86f3b8d48 build: Make caribou depend on libgee
https://bugzilla.gnome.org/show_bug.cgi?id=657697
2011-08-30 15:14:22 +02:00
8cf6b4c728 don't translate IM status
They are well-known strings defined in the Telepathy spec and so shouldn't be
translated.

https://bugzilla.gnome.org/show_bug.cgi?id=657696
2011-08-30 14:36:02 +02:00
612b9e9faf Fix batch import for loginDialog
Signed-off-by: Marc-Antoine Perennou <Marc-Antoine@Perennou.com>
Signed-off-by: Adel Gadllah <adel.gadllah@gmail.com>
2011-08-30 13:58:02 +02:00
c2c4c26f72 Don't crash displaying contacts with no alias
If a folks individual has no alias we crash when passing in NULL
to strstr(). Fix this by checking for non-null first.
2011-08-30 11:13:35 +02:00
be4d504e27 userMenu: Don't show the default avatar over a newly selected one
When we replace the default avatar image with a real image, we need
to remove the default avatar image.

https://bugzilla.gnome.org/show_bug.cgi?id=657657
2011-08-29 20:12:16 -04:00
9ed0bbb3a9 Bump version to 3.1.90
Update NEWS
2011-08-29 19:24:14 -04:00
e5bc3a2ba8 overview: Compensate for the window's invisible borders
https://bugzilla.gnome.org/show_bug.cgi?id=656335
2011-08-29 19:16:52 -04:00
1dee10c575 Toggle mutter's unredirect features on/off depending on the situation
We disable it when in the overview or when recording a video and otherwise
leave it enabled.

https://bugzilla.gnome.org/show_bug.cgi?id=618497
2011-08-30 00:15:52 +02:00
352fb7b833 search: Allow searching for people in overview mode
This adds contacts search to shell, powered by libfolks.
Changes:

- Add Folks and Gee to the build system
- ShellContactSystem, a backend in C
- ContactDisplay, search frontend in JS

https://bugzilla.gnome.org/show_bug.cgi?id=643018
2011-08-29 17:43:30 -04:00
81cee34c17 Make GridSearchResults take a grid as an optional parameter
This is useful since contact search results use a custom grid

https://bugzilla.gnome.org/show_bug.cgi?id=643018
2011-08-29 17:29:11 -04:00
fa786fd3ef Replace GdmUser with AccountsService
The GdmUser copy+paste code has been superseded by AccountsService,
so kill the former and use the latter.

https://bugzilla.gnome.org/show_bug.cgi?id=650893
2011-08-29 22:53:41 +02:00
0751a90bd9 user-menu: Implement new mockups
The current user status menu allow to set the session status,
which also influences the IM status when signed in with
mission-control. However, the way it is presented to the user
makes it hard to figure out how the statuses interact or that
there are two distinct status in the first place.
Therefore, use a separate control for each status, and update the
overall look to match gnome-contacts.

https://bugzilla.gnome.org/show_bug.cgi?id=652837
2011-08-29 22:11:14 +02:00
709193d680 popup-menu: Expand switch menu items
Given that our menus contain at most two columns, all switch widgets
in menus end up in the last columns, and thus aligned with the right
menu edge.
However, the updated user status menu will contain a section which
ignores the menu's column layout, so the switch might end up in
the middle of the menu if the overall width is determined by said
section.
At least for now, we always want the switch to align with the end,
so just expand switch menu items rather than adding an option.

https://bugzilla.gnome.org/show_bug.cgi?id=652837
2011-08-29 22:11:14 +02:00
d0d82cdf7e popup-menu: Add combo box menu item
Introduce a new menu widget, which displays the active item from
a set of options, and pops up a child menu to allow changing the
active item when activated.

https://bugzilla.gnome.org/show_bug.cgi?id=652837
2011-08-29 22:11:14 +02:00
5d2b7e2c9e popup-menu: Add support for child menus
Allow opening a popup menu from another menu. While the child menu
is open, events on the parent menu are blocked. The parent menu
is kept open when the child menu is closed; the child menu on the
other hand is closed with the parent, e.g. when the focus moves
to another toplevel menu.
This feature will be used to implement combo box menu items.

https://bugzilla.gnome.org/show_bug.cgi?id=652837
2011-08-29 22:11:14 +02:00
07660f7fcf status-menu: Rename to userMenu
We haven't actually been calling the top-right menu "status menu" for
quite some time, so use the upcoming code changes as an excuse for
renaming it to "user menu".
2011-08-29 22:11:09 +02:00
5d138e1b79 appDisplay: Don't show NoDisplay items in the Applications tab 2011-08-29 15:56:07 -04:00
0133c6e174 Favorites: Add gnome-documents to default list
We currenty don't have "finding and reminding" so we add gnome-documents
to the default favorites list to improve the document handling user expirence.

https://bugzilla.gnome.org/show_bug.cgi?id=657520
2011-08-29 21:52:46 +02:00
2054f77e2b Add 'multi-line-notification' class name if the notification image is set
Notifications with images are multi line notification.
2011-08-29 15:21:22 -04:00
e4911e2f7a Specify the style for the 'multi-line-notification' correctly
It was not taking effect before.
2011-08-29 15:21:15 -04:00
eabc2e6781 Updated POTFILES.in 2011-08-29 20:46:20 +02:00
71cf87cbb1 Updated POTFILES.in 2011-08-29 20:21:25 +02:00
155997b5fa Overview: dim the background with the dim-factor property
Doing this rather than overdrawing a black rectangle saves us
(pixels in screen) * 8 bytes of memory bandwidth for every frame we draw going
into the overview.

It also allows us to dim the background on non-primary monitors making the
overall overview appearance consistent across all monitors.

https://bugzilla.gnome.org/show_bug.cgi?id=656433
2011-08-29 19:17:01 +01:00
82ce8fe3ff Use Meta.BackgroundActor instances instead of cloning global.background_actor
Instances of this class share a single CoglTexture behind the scenes which
allows us to show the background with different rendering options without
duplicating the texture data.

https://bugzilla.gnome.org/show_bug.cgi?id=656433
2011-08-29 19:17:01 +01:00
9f1da20161 Add support for gdm greeter session
This commit adds GDM session support.

It provides a user list that talks to GDM,
handles authentication via PAM, etc.

It doesn't currently support fingerprint readers
and smartcards.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-29 14:11:36 -04:00
d4239d570d main: Factor out remaining user session specific bits
The shell has a number of things that are only relevant for
logged in users (e.g. calendar events, telepathy integration, a
user menu, etc).

This commit moves those user session specific bits into their
own functions in preparation for making the shell code ready
for use at login time.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-29 14:11:35 -04:00
1ecbabc69a panel: Dynamically match corner to style of nearest button
Right now the panel code makes the left corner sync up with the
activities button and the right corner sync up with the user menu.
This is fine as long as we have an activities button and a user menu.

The login screen won't have those things, though.

This commit changes the panel corner code to try to figure out which
interface element is the most appropriate to sync up with based on
its position in the panel.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-29 14:11:35 -04:00
7f767c49d8 popupMenu: Raise menu when popping it up
When a menu gets popped up, it should never
pop behind anything else in the shell.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-29 14:11:35 -04:00
aabe56ba79 messageTray: implement showing images in notifications
Images are part of the notification spec, so we should support them.

Marina Zhurakhinskaya provided some code for getting the layout right
for this patch.

https://bugzilla.gnome.org/show_bug.cgi?id=621009
2011-08-29 13:46:47 -04:00
d227ddfc88 keyboard: add an on-screen keyboard
https://bugzilla.gnome.org/show_bug.cgi?id=612662
2011-08-29 12:59:25 -04:00
021d3dadbb layout: add panelBox and trayBox
Have LayoutManager automatically deal with sizing and positioning
boxes for the panel and messageTray relative to the monitors.

Also, now that LayoutManager knows exactly where and how tall the
panel and tray are, have it manage the pointer barriers as well.

https://bugzilla.gnome.org/show_bug.cgi?id=612662
2011-08-29 12:59:25 -04:00
4902a600d5 batch: Add mechanism for doing animation series
In order for transformation animations to look good, they need to be
incremental and have some order to them (e.g., fade out hidden items,
then shrink to close the void left over).

Chaining animations in this way can be error prone and wordy using just
Tweener callbacks.

This commit adds a new set of classes to help:

 - Task.  encapsulates schedulable work to be run in a specific scope.

 - ConsecutiveBatch.  runs a series of tasks in order and completes
                      when the last in the series finishes.

 - ConcurrentBatch.  runs a set of tasks at the same time and completes
                     when the last to finish completes.

 - Hold.  prevents a batch from completing the pending task until
          the hold is released.

The tasks associated with a batch are specified in a list at batch
construction time as either task objects or plain functions.
Batches are task objects, themselves, so they can be nested.

For now, these APIs are temporarily getting staged in a gdm/ specific
subdirectory so they will be available for use by GDM.  They aren't
specific to GDM, or even to doing animations, though, so the API may eventually
move in some form or another to a more general location. Alternatively, the
APIs may ultimately get dropped entirely and replaced by something else.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:44:09 -04:00
db39ba3b9f modalDialog: add mode that leaves shell reactive
A modal dialog in the shell blocks anything but that dialog from
receiving user input. Applications within the session and other
parts of UI are rendered non-reactive.

When GDM gets changed to use the shell for its greeter, the user
list will be presented as a shell dialog. That dialog shouldn't
block access to the panel menus, etc.

This commit adds a shellReactive property that makes the ModalDialog
class continue to block access to applications, but allow the user
to interact with the shell itself.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:36:57 -04:00
239a9e4816 popupMenu: Hide settings menus outside user session
The control-center contains user-pertinent settings
panels. These panels don't make sense to show outside
of a user's session, so hide them for session types other
than SessionType.USER.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:31:34 -04:00
35e99266ba overview: Add dummy mode
We're not going to want an overview at the login screen,
but a lot of code in the shell depends on the overview
existing.

This commit adds a new isDummy constructor property to
allow creating the overview as a non-functional, stub object
that doesn't do anything visible.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:30:52 -04:00
67ae8ed8e9 overview: make shellInfo private
This commit forwards the shellInfo setMessage method
to the overview itself and makes shellInfo private.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:29:41 -04:00
356e4c0967 overview: make dash private
The dash object is currently exposed as a public object.
It's only used outside of the overview for the dash object's
iconSize property though.

This commit makes the dash object private and proxies the dash
iconSize property to the overview.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:25:04 -04:00
80a9d2e7c9 overview: Make viewSelector private
It's only used internally by the overview itself,
and by some performance testing code, so don't
expose it as a public object.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:23:59 -04:00
5088f22388 global: Add concept of "session type"
This commit introduces a "session type" for
gnome-shell.  It essentially defines what
mode of operation the shell runs in
(normal-in-a-users-session mode, or at-the-login-screen mode).

Note this commit only lays the groundwork.  Actually
looking at the key and appropriately differentiating
the UI will happen in subsequent commits.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:22:38 -04:00
4156a4c2d0 shell-global: require init call before shell_global_get()
shell_global_get() currently implicitly instantiates the shell
global singleton the first time it's called.  This means there's
no opportunity to set construction-time properties on the singleton.

This isn't an issue yet, because there aren't any.  We will need it
in the future, though, when we grow a --gdm-mode that gets exposed as
a property through the global singleton.

This commit adds a new _shell_global_init() function that must be
invoked before shell_global_get() can be called.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:20:16 -04:00
b6c2399a17 dateMenu: Make events list optional
Right now, when a user clicks on the panel clock, a menu pops up with a
calendar and a list of events from the user's schedule.  The list of
events only makes sense from within a user's session, however.

As part of the prep work for making the shell a platform for the login
screen, this commit makes the events list optional.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:18:47 -04:00
5be9326192 dateMenu: Force min-width of events area, not whole menu
The theme currently hard codes the minimum size of the calendar
menu to make sure there's a designated area for events
(even if there isn't anything currently scheduled).

A side-effect of the hard coded minimum width is that
if the events area is hidden, the menu ends up much
bigger than the calendar.  We don't currently ever hide
the events area, but we will in the future.

This commit moves the min-width restriction from the menu
specifically to the events area.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:18:13 -04:00
388cfa3695 panelMenu: Separate from ui chrome layer
The chrome layer contains the user interface elements (e.g.,
the panel) that disappear when fullscreen windows get displayed.

Panel menus are currently put in the chrome layer, but don't need
to be, since they are only displayed when the user is interacting
with the shell and not a fullscreen application.

Putting panel menus in the chrome layer does mean they will get
stacked below shell interface elements that aren't in the chrome layer,
though.

This commit changes panel menus to be on the same layer as most other
shell elements, so they get properly stacked above those elements.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:17:13 -04:00
e8914c6699 modalDialog: fade in buttons when first showing them
Right now, if buttons get set on a dialog after it is mapped,
they just pop in instantly.

We shouldn't have any harsh transitions like that, though.

This commit changes the buttons to quickly fade in, instead.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:16:40 -04:00
13bf64a53d popupMenu: Use new convenience method for settings
All the system status menus in the panel offer a
menu item to jump to a relevant part of the
control-center.

This means each status icon has the same, or nearly the
same bit of code to:

- Add a new "action" menu item and listen for its activation.
- Hide the overview if it's showing when the menu item is activated
- Find the relevant control-center panel from its desktop file
- Launch the control-center to the relevant panel

This commit consolidates all those details in a new method,
addSettingsAction.  This refactoring reduces code duplication and
slight inconsistencies in the code resulting from that duplication.
It will also make it easier in subsequent commits to hide settings menu
items when the shell is used in the login screen.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:16:04 -04:00
f96b2ee858 popupMenu: Hide separators when they aren't separating
A separator only makes sense if there are items on both
sides of it. There is quite a lot of code written
throughout the shell that manages the process of showing
and hiding separators as the items around those separators
change.

This commit drops all that code in favor of changes to the menu
implementation to dynamically hide or show separators as
appropriate, so the callers don't have to deal with it.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:15:17 -04:00
b12967b930 st-adjustment: Drop all animation-y stuff
StAdjustment has some non-functional and unused animation vestiges
like the "elastic" property, st_adjustment_interpolate() and
st_adjustment_clamp().

This commit vacuums that stuff up so it doesn't tempt anyone into
trying to use it.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:14:17 -04:00
d896248ff8 Complete transitioning away from nm-applet
Wireless and 3g dialog code has moved to gnome-control-center, so
we can stop calling out to nm-applet. Also, we can now enable the
notifications provided by the shell and kill a bit of code about
auth that is not actually needed.

https://bugzilla.gnome.org/show_bug.cgi?id=650244
2011-08-29 18:11:50 +02:00
2ebdc81c8f Add a system modal dialog for network secrets
Using the new ShellNetworkAgent, show a system modal dialog
(similar to the PolicyKit one) when NetworkManager needs secrets
for connecting to wireless.

https://bugzilla.gnome.org/show_bug.cgi?id=650244
2011-08-29 18:11:50 +02:00
2af5e851b3 Add a new network agent for the Shell
A network agent is a component that stores network secrets (like
wifi passwords) in the session keyring. This commit adds an
implementation of it to be used by the shell network dialogs. It
handles most of the keyring stuff, delegating the UI to upper layers.

https://bugzilla.gnome.org/show_bug.cgi?id=650244
2011-08-29 18:11:50 +02:00
bd9455ec8e telepathyClient: Add notification for account connection errors
Based on initial work from Alban Crequy and Xavier Claessens

https://bugzilla.gnome.org/show_bug.cgi?id=654159
2011-08-29 10:35:09 -04:00
fefee3b49e telepathyClient: Add IM subscription request support
Based on initial work from Guillaume Desmottes

https://bugzilla.gnome.org/show_bug.cgi?id=653941
2011-08-29 10:35:09 -04:00
071c49b7c6 [l10n] Updated German translation 2011-08-28 14:04:58 +02:00
4e9e6e75d3 st-box-layout: Document insertion apis
This commit just adds some brief doc comments
to st_box_layout_insert_actor() and
st_box_layout_insert_before()

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-27 15:25:01 -04:00
a13af7fbcc windowManager: use meta_window_is_attached_dialog()
Use meta_window_is_attached_dialog() so that we only dim/unfold dialog
windows that mutter is actually showing as attached

https://bugzilla.gnome.org/show_bug.cgi?id=646761
2011-08-27 13:14:38 -04:00
4fa8e2b59d extensionSystem: Don't try to make the user extensinons dir if it exists
this removes the "Error invoking Gio.make_directory_with_parents: Error
creating directory: File exists" spam from the Errors tab of the Looking
Glass
2011-08-26 17:34:50 -04:00
90783c7cdf theme: Darken notification entry color a lot
Makes it a lot easier to see what you're typing.
2011-08-26 16:10:13 -04:00
f99b4da4ec KeyboardStatus: disambiguate duplicate short descriptions
If two layouts have the same short description (for example, english
(US) and english (dvorak)), add a subscript for disambiguating
among them.

https://bugzilla.gnome.org/show_bug.cgi?id=650128
2011-08-26 16:52:58 +02:00
a64e0e1f49 VolumeStatus: track PulseAudio state and hide when disconnected
Only show the menu when the associated GvcMixerControl is ready, as
the connection can fail or PulseAudio may not be installed.

https://bugzilla.gnome.org/show_bug.cgi?id=645708
2011-08-25 23:36:22 +02:00
270e82e3db GnomeVolumeControl: track PulseAudio connection state and expose it
Adds get_state() and ::state-changed signals, that replace connecting
and ready, as well as providing indication of when the object was closed
or the connection to PulseAudio failed.

https://bugzilla.gnome.org/show_bug.cgi?id=645708
2011-08-25 23:35:29 +02:00
8f4a4d93f2 panel: Fix ordering of status icons
Otherwise a11y would be added to the right of the power indicator instead of to
the left of keyboard indicator
2011-08-25 17:15:24 -04:00
83265bb12a panel: Start the status area before extensions are loaded
The order of indicators depends on the order of calls to
Panel.addToStatusArea. To have it consistent across enabling and
disabling of extensions, we need to place the core ones first.

https://bugzilla.gnome.org/show_bug.cgi?id=653205
2011-08-25 13:35:49 -04:00
5be8d5f9cf panel: Remove 'display' from the standard icons
This way all standard indicators have a shell implementation
provided, which prevents issues with extensions enabling/disabling
(in particular with xrandr-indicator)

https://bugzilla.gnome.org/show_bug.cgi?id=653205
2011-08-25 13:35:41 -04:00
08126e5a38 panel: Add an easier way of adding items to the system status area
Extensions often want to add items to the system status area, so it
is useful to add a convenience API for it. Also, we now allow
for cleaner destruction of panel objects, by just calling destroy()
on it.
Based on a patch by Jasper St. Pierre.

https://bugzilla.gnome.org/show_bug.cgi?id=653205
2011-08-25 13:34:45 -04:00
b76efe17d6 notificationDaemon: Work around JS interpreter bug
The "id" variable was being sporadically reset to null, and as far as
Florian and I could determine, this is actually a Spidermonkey bug.

The issue has something to do with:

1) use of "let" for the variable
2) Nesting a dynamic closure inside of a for() loop

Work around it here for now - I tried to create a minimized test case
to hand to the Spidermonkey developers, but failed.  A big part of
the problem is it's only sporadically reproducible.
2011-08-25 09:20:00 -04:00
ca2678446d Updated Norwegian bokmål translation 2011-08-24 22:17:14 +02:00
06d906b962 telepathyClient: Add direction containers
Direction containers group all contiguous messages in the same direction into
their own parent container, allowing for smarter styling of similar messages.

https://bugzilla.gnome.org/show_bug.cgi?id=640271
2011-08-24 15:32:15 -04:00
023f5149e5 Updated Galician translations 2011-08-24 21:19:19 +02:00
2e45508529 Updated Spanish translation 2011-08-24 21:10:29 +02:00
6241a8269f extensionSystem: Start using OUT_OF_DATE
We were defining OUT_OF_DATE as a possible state, but never using it
properly.

https://bugzilla.gnome.org/show_bug.cgi?id=654770
2011-08-24 13:58:22 -04:00
465d03ab2c extensionSystem: Add a DOWNLOADING state
https://bugzilla.gnome.org/show_bug.cgi?id=654770
2011-08-24 13:58:22 -04:00
a56cd3c3d6 extensionSystem: Add 'extension-status-changed' signal
https://bugzilla.gnome.org/show_bug.cgi?id=654770
2011-08-24 13:58:22 -04:00
d8a98e5467 extensionSystem: Add install-from-HTTP capability
This adds a new DBus method: InstallExtensionRemote(uuid : s, url : s)

Pass it the UUID of an extension and the URL of a manifest file: the same as a
metadata.json, but with a special key, '__installer', which is an HTTP location
that points to an zip file containing the extension. The Shell will download
and use it to install the extension. In the future, the manifest file may be
used to automatically detect and install updates.

https://bugzilla.gnome.org/show_bug.cgi?id=654770
2011-08-24 13:58:22 -04:00
2d813cbdd8 extensionSystem: Implement new live enable/disable system
The rough sketches of the system are outlined here:

http://mail.gnome.org/archives/gnome-shell-list/2011-June/msg00283.html

Additionally, enable/disable extensions when the 'enabled-extensions' setting
changes. Two new DBus methods, "EnableExtension" and "DisableExtension" allow
users to manipulate this setting quite easily.

https://bugzilla.gnome.org/show_bug.cgi?id=654770
2011-08-24 13:58:22 -04:00
6d3434f3a5 extensionSystem: Remove 'disabled-extensions' blacklist
The two similar keys were hard to manipulate to have specific effects, so just
remove one. Now there is an *explicit* whitelist: all extensions must be in the
'enabled-extensions' for them to be loaded.

https://bugzilla.gnome.org/show_bug.cgi?id=654770
2011-08-24 13:58:22 -04:00
fa0268f35a ShellApp: Avoid crashing during state transition for window-backed apps
During a state transition from running to not-running for
window-backend apps, it's possible we get a request for the icon.
Avoid asserting here and just return an empty image.

https://bugzilla.gnome.org/show_bug.cgi?id=656546
2011-08-24 12:32:33 -04:00
712ea9b9b6 telepathyClient: Use sent timestamp instead of received timestamp
It's generally more useful to see when a person sent a message instead of when
we received it. Also, a recent change in Telepathy made the received timestamp
be 0 for messages we send.

https://bugzilla.gnome.org/show_bug.cgi?id=640271
2011-08-24 11:18:58 -04:00
b7fd78b254 Add screenshot interface
Adds methods to shell_global to allow taking screenshots
save the result into a specified png image.

It exposes three methods via shellDBus applications like
gnome-screenshot:

*) Screenshot (screenshots the whole screen)
*) ScreenshotWindow (screenshots the focused window)
*) ScreenshotArea (screenshots a specific area)

https://bugzilla.gnome.org/show_bug.cgi?id=652952
2011-08-24 16:06:13 +02:00
a2e6b3167b Updated Swedish translation 2011-08-24 09:03:43 +02:00
72037af241 AppDisplay: fix typo that prevented Ctrl+Enter in search
workspace is not a valid variable, the workspace index should be
fetched from the params object.

https://bugzilla.gnome.org/show_bug.cgi?id=657111
2011-08-22 23:28:23 +02:00
62048abbf5 Updated Bulgarian translation 2011-08-22 23:20:37 +03:00
7b61aca956 Updated Persian translation 2011-08-23 00:37:09 +04:30
6709e5e458 NetworkMenu: don't show hidden access points
It is not possible to connect to hidden access points without
knowing the SSID, and it should be done using the control center
panel and the appropriate dialog. At the same time, this should
fix some warnings from libnm-glib and dbus-glib.

https://bugzilla.gnome.org/show_bug.cgi?id=646454
2011-08-22 21:57:29 +02:00
11c8405879 telepathyClient: Delay notification in case it gets acked
The shell should only notify in case no other client handles the message.
Empathy will ack the message if focused, so we don't want to step on its
toes.
2011-08-22 19:18:31 +02:00
6028628261 favorite-apps: Use libreoffice-writer & firefox.desktop
Use firefox.desktop and libreoffice-writer.desktop for favorite-apps
defaults instead of mozilla-firefox and openoffice.org-writer.

https://bugzilla.gnome.org/show_bug.cgi?id=654707
2011-08-22 09:56:30 -04:00
3d8a1537d2 Updated Norwegian bokmål translation 2011-08-21 21:43:36 +02:00
c5b4decdae Updated Norwegian bokmål translation 2011-08-21 21:42:37 +02:00
c7237be3e3 Update Simplified Chinese translation for 3.2 release. 2011-08-21 14:31:57 +00:00
aa39166b74 Updated Indonesian translation, including some strings submitted
by Wibiharto <wibinem@yahoo.com>
2011-08-21 21:28:39 +07:00
e7b9933036 theme: consistent button appearance and behaviour
Update modal dialog, notification and search result buttons to use
the same style. This improves the look of notification buttons
and also ensures that all buttons change appearance when they are
focused or pressed.

Also change all buttons so that their labels are correctly vertically
centered.

https://bugzilla.gnome.org/show_bug.cgi?id=655974
2011-08-19 15:56:55 +01:00
54d51c713c Updated Spanish translation 2011-08-19 12:26:51 +02:00
72e0c2fe11 Updated Hebrew translation. 2011-08-18 23:55:35 +03:00
7458d3ef39 Approve file transfer channels
https://bugzilla.gnome.org/show_bug.cgi?id=653940
2011-08-18 12:35:57 +02:00
52c5f9b144 ApproverSource: takes a gicon rather than an icon name
https://bugzilla.gnome.org/show_bug.cgi?id=653940
2011-08-18 12:35:17 +02:00
8c7085acd4 update translation for Punjabi 2011-08-18 06:58:44 +05:30
083ca7d39b theme - soften notification popups
Some small changes to make notifications fit with the shell aesthetic
and make them match the mockups. Gives them a bigger corner radius,
smaller font and more transparency.

https://bugzilla.gnome.org/show_bug.cgi?id=656732
2011-08-17 13:13:28 +01:00
68e716ae27 theme: new chat input style, smaller meta messages
Match the mockup for chat input style and meta messages - gives
the input box more depth and definition, makes the meta messages
smaller and therefore more distinct and less distracting.

https://bugzilla.gnome.org/show_bug.cgi?id=640271
2011-08-17 12:52:09 +01:00
ef8772916d Revert "network: ignore APs that hide their SSID"
This reverts commit 6a3130e25f.

It would hide those APs forever, even after connecting to them, which users
can do via other means. Thanks to Giovanni Campagna for pointing this.

https://bugzilla.gnome.org/show_bug.cgi?id=654898
2011-08-16 18:39:48 +01:00
b54e374bc5 Updated Galician translations 2011-08-16 19:30:25 +02:00
b3228258ee Updated Lithuanian translation 2011-08-13 22:17:40 +03:00
e5036a458e Updated Traditional Chinese translation(Hong Kong and Taiwan) 2011-08-13 20:19:17 +08:00
c714a66ba3 dateMenu: Watch for a resume, and update the clock
Otherwise it can be very out of date.

https://bugzilla.gnome.org/show_bug.cgi?id=656403
2011-08-12 17:05:57 -04:00
d80b7be6ca Use user-defined calendar application for the date menu calendar button
Use the existing setting

  org.gnome.desktop.default-applications.office.calendar.exec

as calendar application instead of the hard-coded evolution.  Evolution
is still the fallback if that setting is cleared (it defaults to
evolution).

https://bugzilla.gnome.org/show_bug.cgi?id=651190
2011-08-12 12:50:04 -04:00
77de611ec7 dateMenu: Fix some tabs vs spaces 2011-08-12 12:48:32 -04:00
60b54c0052 network: always coerce the SSID to _something_
https://bugzilla.gnome.org/show_bug.cgi?id=654898
2011-08-12 17:26:54 +01:00
6a3130e25f network: ignore APs that hide their SSID
https://bugzilla.gnome.org/show_bug.cgi?id=654898
2011-08-12 17:26:53 +01:00
91cba1f8f4 ShellApp: Make sure that we use a valid timestamp when activating
Otherwise, we'd be comparing against the last_used_time and setting
it to 0.

https://bugzilla.gnome.org/show_bug.cgi?id=656374
2011-08-12 03:21:42 -04:00
22b2661df9 gnome-shell.modules: update to vala 0.13.1
Fixes a crash when compiling the caribou gtk module caused by
a call to async Bus.get_sync() method.
https://bugzilla.gnome.org/show_bug.cgi?id=644275
2011-08-11 13:27:04 -04:00
f8b397a5dc appDisplay: Add missing this
Spotted by Dan Winship <danw@gnome.org>
2011-08-11 10:38:24 -04:00
44b475e746 appDisplay: Fix activation of search results
The id parameter changed to an app.
2011-08-11 10:26:14 -04:00
d25610903a ShellWindowTracker: Rename self variable for consistency
Historically it was monitor, now tracker.

(I want to move more things to self, but that's a different bug).

https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-08-11 10:11:36 -04:00
2efcbaf206 ShellApp: Fix comment about window-backed apps
https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-08-11 10:11:36 -04:00
7f1d2825fd appDisplay: Don't expose "Add as favorite" for window-backed apps
We don't know how to do it.  Similarly, don't allow New Window.

https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-08-11 10:11:22 -04:00
b9edb1dc01 ShellApp: Ensure we set the size of returned texture for window backed apps
Unify the two code paths too.

https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-08-11 05:56:00 -04:00
b0cc778c49 ShellApp: Use stable sequence for id, not pointer address
As danw points out,

  "It's unique during the lifetime of the window, but reasonably likely to be
  reused by another window after this one is destroyed. Using
  meta_window_get_stable_sequence() might be better."

https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-08-11 05:47:38 -04:00
ff840db708 ShellApp: Use global time, not clutter time
This is correct in more circumstances.

https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-08-11 05:45:48 -04:00
11f30e2e09 ShellApp: Use integer for size, not float
We were basically casting it everywhere except for ClutterActor -
let's be consistent with StTextureCache and use integers.

https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-08-11 05:44:19 -04:00
4886275df4 ShellApp: Change activation API
Since almost all of the callers of shell_app_activate were using the
default workspace (by passing -1), remove that parameter.

Add a new shell_app_activate_full() API which takes a workspace as
well as a timestamp; previously we might have been ignoring event
timestamps from elsewhere.

https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-08-11 05:35:23 -04:00
c5de239e25 shell_util_normalize_and_casefold: New utility function
Merge the duplicated copies into shell-util.
2011-08-10 13:00:06 -04:00
10dcc100e9 Kill off ShellAppInfo, move into ShellApp
This dramatically thins down and sanitizes the application code.

The ShellAppSystem changes in a number of ways:
* Preferences are special cased more explicitly; they aren't apps,
  they're shortcuts for an app), and we don't have many of them, so
  don't need e.g. the optimizations in ShellAppSystem for searching.
* get_app() changes to lookup_app() and returns null if an app isn't
  found.  The semantics where it tried to find the .desktop file
  if we didn't know about it were just broken; I am pretty sure no
  caller needs this, and if they do we'll fix them.
* ShellAppSystem maintains two indexes on apps (by desktop file id
  and by GMenuTreeEntry), but is no longer in the business of
  dealing with GMenuTree as far as hierarchy and categories go.  That
  is moved up into js/ui/appDisplay.js.  Actually, it flattens both
  apps and settings.

Also, ShellWindowTracker is now the sole reference-owner for
window-backed apps.  We still do the weird "window:0x1234beef" id
for these apps, but a reference is not stored in ShellAppSystem.

The js/ui/appDisplay.js code is rewritten, and sucks a lot less.
Variable names are clearer:

_apps -> _appIcons
_filterApp -> _visibleApps
_filters -> _categoryBox

Similarly for function names.  We no longer call (for every app) a
recursive lookup in GMenuTree to see if it's in a particular section
on every category switch; it's all cached.

NOTE - this intentionally reverts the incremental loading code from
commit 7813c5b93f.  It's fast enough
here without that.

https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-08-10 12:59:32 -04:00
8ada9b43ae Updated Norwegian bokmål translation 2011-08-10 15:23:32 +02:00
fd77225c3e Updated Spanish translation 2011-08-10 13:27:49 +02:00
7ed3facf8f calendar: Improve week start handling
Add a helper function (mostly copied from gtkcalendar.c) for getting
the first week day for the current locale, using nl_langinfo if
available and falling back to the GTK+ gettext fallback otherwise.

Use that function in the calendar, so that the LC_TIME setting is
used if possible.

https://bugzilla.gnome.org/show_bug.cgi?id=649078
2011-08-10 01:03:26 +02:00
6c97e2a5ab ShellAppUsage: Port to GDBus
(Split out of a larger commit by Colin Walters <walters@verbum.org>)

https://bugzilla.gnome.org/show_bug.cgi?id=648651
2011-08-09 11:46:16 -04:00
f19e8b1e78 ShellGlobal: Remove unused dbus-glib include
https://bugzilla.gnome.org/show_bug.cgi?id=648651
2011-08-09 08:07:34 -04:00
08e669adde layout: Don't create and destroy ripple animations
Instead, create three ripples and keep tweening them. This gives a dramatic
speedup when entering the overview, but means that we can't have the same animation
running twice. In this case, we "reset" the currently running ripple animation, but
it is hard to notice unless looking for it.

https://bugzilla.gnome.org/show_bug.cgi?id=656125
2011-08-08 14:11:09 -04:00
bbd2c02b7f Updated Swedish translation 2011-08-08 08:33:05 +02:00
0d9da86b7e Updated Hebrew translation. 2011-08-06 16:00:28 +03:00
5810fcb14d extensionSystem: Save extension errors per-extension
Extension developers may be confused about why their extensions aren't working:
the LookingGlass isn't a very obvious place, or even which errors are theirs.
To remedy this, save all errors per-UUID which allows them to be retrieved
later.

https://bugzilla.gnome.org/show_bug.cgi?id=654770
2011-08-04 13:40:04 -04:00
2466eb3132 shellDBus: Add a few version parameters
Add ShellVersion, designed for detecting OUT_OF_DATE extensions so they can't
be installed, as well as ApiVersion, designed for backwards-compatibility with
the SweetTooth web-app, which must support all shell versions.

https://bugzilla.gnome.org/show_bug.cgi?id=654770
2011-08-04 13:40:04 -04:00
fc59e222d2 shellDBus: Add ListExtensions() and GetExtensionInfo()
GetExtensionInfo() takes a UUID and returns a JSON object with information
about that extension including their metadata, path and current state.

ListExtensions() takes no arguments and returns a JSON object mapping UUIDs
to the same information objects described above.

https://bugzilla.gnome.org/show_bug.cgi?id=654770
2011-08-04 13:40:04 -04:00
ff983432d9 lookingGlass: Recognize new extensions as they are added
https://bugzilla.gnome.org/show_bug.cgi?id=654770
2011-08-04 13:40:04 -04:00
67b4f9b3a9 workspacesView: belatedly remove an unused variable 2011-08-04 13:37:57 -04:00
f279b6bf7e Update Spanish translation
Forgotten "%p" in 12h time formats.
2011-08-04 12:57:31 +02:00
daec53f4fe Update Spanish translation
The mistery of the combining accutes continues.

Signed-off-by: Diego Escalante Urrelo <descalante@igalia.com>
2011-08-04 12:39:28 +02:00
cb1966612e Don't save unacked messages
Don't bother tracking which messages we need to ACK, just tell Telepathy to
ACK them all.

https://bugzilla.gnome.org/show_bug.cgi?id=654398
2011-08-04 10:53:48 +02:00
cda279f5c2 Updated Spanish translation 2011-08-04 09:31:13 +02:00
31915cdc93 Updated Vietnamese translation 2011-08-04 09:16:04 +07:00
329f803004 po/vi.po: import from Damned Lies 2011-08-04 09:10:38 +07:00
7780c99de6 telepathyClient: Use markup for timestamps
Commit aa1405e4ea introduced <b> tags for timestamps, but forgot to enable
markup with them.
2011-08-03 21:53:15 -04:00
d3ad857ba4 build: gnome-control-center needs latest gnome-menus 2011-08-03 21:46:00 -04:00
aa1405e4ea telepathyClient: Add our own translations for timestamps
As an effort to prevent a string freeze to land timestamps on 3.0, we reused
translations for the calendar. Now that the string freeze is long gone, make
some proper strings.

https://bugzilla.gnome.org/show_bug.cgi?id=640271
2011-08-03 18:14:34 -04:00
aa0a7f7816 Updated Spanish translation 2011-08-03 21:54:48 +02:00
3850edced5 notificationDaemon: fix syntax error 2011-08-03 15:21:00 -04:00
ddd59f2e76 notification-daemon: Add support for 'default' actions
The notification spec supports the concept of a 'default' action:
  "The default action (usually invoked my clicking the notification)
   should have a key named "default". The name can be anything, though
   implementations are free not to display it."
Support this by invoking the 'default' action rather than a emitting
the 'clicked' signal when clicking notifications which specifie a
default action.
Also don't add an action button for the default action.

https://bugzilla.gnome.org/show_bug.cgi?id=655818
2011-08-03 20:07:50 +02:00
10a0f2b614 altTab: do a step transition instead of fading in
Commit 7596fdb460 makes the popup feel slow.
Instead of fading the popup in we wait a bit and show it instantly.

https://bugzilla.gnome.org/show_bug.cgi?id=652346
2011-08-03 16:23:35 +01:00
8bc85d4a79 main: Add Main.notify() for simple system messages
... similar to Main.notifyError(), but don't duplicate the message
on stderr/in the log.

https://bugzilla.gnome.org/show_bug.cgi?id=652718
2011-08-03 17:19:18 +02:00
3dc07d48c5 shell: fail cleanly if XFixesGetCursorImage fails
from Mageia, via Olav Vitters
https://bugzilla.gnome.org/show_bug.cgi?id=653119
2011-08-03 11:12:32 -04:00
c1acf992fa layout: make Chrome an implementation detail of LayoutManager
Make the Chrome object be a private member of LayoutManager, and add
new LayoutManager methods to proxy to the old Chrome methods.

https://bugzilla.gnome.org/show_bug.cgi?id=655813
2011-08-03 09:18:30 -04:00
99149f9c41 layout: merge chrome.js into layout.js
LayoutManager and Chrome are already somewhat intertwined and will be
becoming more so. As a first step in merging them, move the Chrome
object into layout.js (with no other code changes).

https://bugzilla.gnome.org/show_bug.cgi?id=655813
2011-08-03 09:18:30 -04:00
bcd307066a lookingGlass: put this in the chrome layer
Looking Glass is supposed to slide out from underneath the panel.
Rather than fiddling with Main.chrome.actor directly, just add the lg
actor to the chrome, and fix its stacking there.

https://bugzilla.gnome.org/show_bug.cgi?id=655813
2011-08-03 09:18:30 -04:00
446910cb10 messageTray: move the summary notification out of MessageTray.actor
With the old pre-boxpointer summary notifications, it sort of made
sense that the summary notification actor was a child of the message
tray. But there's no reason for that now, and in fact, it ends up
requiring special cases in some places since hovering over the summary
notification counts as hovering over the tray. So, fix this.

https://bugzilla.gnome.org/show_bug.cgi?id=655813
2011-08-03 09:18:18 -04:00
fde200d084 panel: move the corners into the panel actor
Rather than having the panel corners as independent bits of chrome and
manually syncing their positions, put them inside the panel actor, and
update the panel's allocation code to position them correctly.

https://bugzilla.gnome.org/show_bug.cgi?id=655813
2011-08-03 09:17:52 -04:00
a376cd1610 chrome: Make affectsStruts default to false
Since we only want it to be true for the panel, and nothing else.

https://bugzilla.gnome.org/show_bug.cgi?id=655813
2011-08-03 09:17:52 -04:00
dbeab0ef87 St: fix container paint volumes
If a container is not clip-to-allocation, then its get_paint_volume()
needs to include the paint volumes of all of its children, since they
(or their children) may paint outside the container's allocation.

Also, if the superclass get_paint_volume() returns FALSE, then the
subclass should return FALSE too.

https://bugzilla.gnome.org/show_bug.cgi?id=655812
2011-08-03 09:16:55 -04:00
7765d6a08f shell-global: remove "gratuitous" meta_plugin_* calls
MetaPlugin wraps a bunch of compositor (and plain metacity) methods
that we can just call ourselves, so just do that. (Presumably this
dates back to some ancient time when it was imagined that plugins
wouldn't need access to the full metacity API.)

https://bugzilla.gnome.org/show_bug.cgi?id=654639
2011-08-03 09:09:55 -04:00
aed50e2a39 shell-global: add a "display" property
and update callers to fetch that rather than doing
"global.screen.get_display()"

https://bugzilla.gnome.org/show_bug.cgi?id=654639
2011-08-03 09:09:55 -04:00
b262a42458 shell-global: keep better track of screens and displays
Rather than constantly asking mutter for the MetaScreen, and then
figuring out the MetaDisplay/Display/etc from there, just keep track
of everything we care about inside ShellGlobal.

https://bugzilla.gnome.org/show_bug.cgi?id=654639
2011-08-03 09:09:55 -04:00
3fd90dfcb1 polkit: Update style of password entry
Use a subtle gradient background and add a blue border when focused.

https://bugzilla.gnome.org/show_bug.cgi?id=655422
2011-08-03 12:57:42 +02:00
dec55a3291 theme: make modal dialog buttons match the mockups
The buttons should have a glassy transparent look. Also, they should not
be as tall, should light up on hover, and their labels should be white
in order to stand out. Making the labels solid white requires removing the
transparency set in modalDialog.js. Also, add a separate color setting
for the dialog as a whole - this avoids having a white icon.

https://bugzilla.gnome.org/show_bug.cgi?id=655428
2011-08-03 11:23:22 +01:00
f6f3ded842 Added Malay translation 2011-08-03 10:17:22 +02:00
004c5cf287 shell-app-system: Load settings apps
When porting to the new gnome-menus API in commit 8f3bdd4f1, the
initial loading of settings apps was left out, so settings panels
are neither found nor can be launched from the top panel menus.
2011-08-02 22:17:50 +02:00
aba5f2f7b8 Theme: match message tray and panel box pointers
Panel box pointers were recently updated to match the mockups. Message
tray box pointers should look the same. Update the corner radius, stroke
colour and pointer dimensions to match.

https://bugzilla.gnome.org/show_bug.cgi?id=655627
2011-08-01 20:54:42 +01:00
2a96964204 st-container: fix a misspelled variable name 2011-08-01 14:29:15 -04:00
a9fe2a1493 gnome-shell.modules: Import gnome-menus 2011-08-01 14:16:13 -04:00
8f3bdd4f1a Port to new gnome-menus API
Since gnome-menus is now introspectable, eventually we can drop
ShellAppSystem entirely.  For now though, just do the basic port.

https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-08-01 13:42:17 -04:00
4322a20cbe Added StButtonAccessible
Basic skeleton and the proper role (ATK_ROLE_PUSH_BUTTON)
2011-08-01 19:20:08 +02:00
2403fd0680 panelMenu: add a gap between the panel and its menus
The specs call for a 2 pixel gap between the panel and its menus,
though we need to specify this as 4 pixels, since it's relative to the
bottom of the icon/title, not the bottom of the panel (up until now,
the point of the menu arrow was actually overlapping the menu's
highlight underline).

Also, move the gap specification into the CSS, since it makes more
sense there.

https://bugzilla.gnome.org/show_bug.cgi?id=655627
2011-08-01 11:48:02 -04:00
e01baf2a25 Fix critical when setting a NULL label
st_label_set_text() does not accept NULL, and emits a g_critical
in that case.

https://bugzilla.gnome.org/show_bug.cgi?id=654638
2011-08-01 16:40:25 +02:00
d27b37fefe Updated POTFILES.in 2011-08-01 15:42:24 +02:00
acedb60abe Theme: improve menu popup appearance
Make menu popups fully match the mockups by changing the stroke
colour, pointer and corner radius size. This has the benefit of
distinguishing the menu from the background and gives it a subtler
appearance that fits the shell's aesthetic.

https://bugzilla.gnome.org/show_bug.cgi?id=655627
2011-07-30 17:04:19 +01:00
77556d181e Use "Region and Language Settings" label
Since this link in the keyboard menu points to Region and Language
Settings in System Settings, we should be consistent and use that
term instead of "Localization Settings"

Also, this removes ellipsis from "Show Keyboard Layout" since it
doesn't require further input.

https://bugzilla.gnome.org/show_bug.cgi?id=652984
2011-07-29 14:41:08 +02:00
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
124 changed files with 18377 additions and 12946 deletions

136
NEWS
View File

@ -1,3 +1,139 @@
3.1.90.1
========
* Fix typo that was breaking the "Login Screen" mode [Marc-Antoine]
* Fix build with new gobject-introspection [Dan]
* Use a better icon for removable devices [Cosimo; #657757]
* Add support for asynchronous search provides [Philippe, Jasper, Seif; #655220]
* Misc bug fixes [Alex, Guillaume, Jasper; #657657, #657696]
* Misc build fixes [Adel; #657697]
Contributors:
Cosimo Cecchi, Guillaume Desmottes, Adel Gadllah, Alexander Larsson, Seif Lotfy,
Philippe Normand, Marc-Antoine Perennou, Jasper St. Pierre, Dan Winship
Translations:
Jorge González, Daniel Mustieles [es], Stas Solovey [ru]
3.1.90
======
* Add an on-screen keyboard that uses Caribou as a backend
[Nohemi, Dan; #612662]
* Allow searching for people in the overview using libfolks
as the backend [Morten; #643018]
* Add a "Login Screen" mode to be used when GDM is running; this
mode has a stripped down user interface, and also contains the
code to display the user list and authentication. [Ray; #657082]
* Rework user menu to separate out "Do Not Disturb" from the IM
status and to visually match GNOME Contacts. [Florian; #652837]
* Implement displaying images such as cover-art in notifications
[Neha, Marina; #621009]
* Support default actions for notifications [Florian; #655818]
* Networking
- Stop using nm-applet for dialogs; do them as proper system modal
dialogs in the shell code. [Giovanni; #650244]
- Fix handling of hidden access points [Giovanni; #646454]
* Telepathy integration
- Support subscription requests [Guillaume, Xavier; #653941]
- Notify on account connection errors [Alban, Jasper, Xavier; #654159]
- Allow approving file transfers [Guillaume; #653940]
- Improve styling of messages [Jasper; #640271]
* Extension system [Jasper; #654770]
- Support live enabling and disabling of extensions
- Add the ability to install extensions from HTTP
- Enhance D-Bus interface for controlling extensions
- Collect errors separately for each extension
* Add Main.panel.addToStatusArea for extension convenience
[Giovanni, Jasper, Marc-Antoine; #653205]
* Port to the new gnome-menus API. Clean up and speed up
application information loading [Colin; #648149, #656546]
* Use the accountsservice library rather than cut-and-pasted GDM code
[Florian; #650893]
* Add a D-Bus interface to take a screenshot; this will avoid various race
conditions with the current gnome-screenshot approach [Adel; #652952]
* Show numeric indicators to distinguish duplicate keyboard names
[Giovanni; #650128]
* Add GNOME Documents to the favorites list [Adel; #657520]
* Update the clock immediately on resume from suspend [Colin; #656403]
* Remove animation support from StAdjustment [Ray; #657082]
* Support configuration of calendar applications via gsettings
[Tassilo; #651190]
* Don't fade in alt-Tab - wait a bit and show it instantly [Rui; #652346]
* Darken workspace background on all workspaces [Rui; #656433]
* Improve detection of the starting day of the week [Florian; #649078]
* Add StButtonAccessible [Alejandro]
* Visual tweaks to match mockups
[Allan, Dan, Jasper, Marina; #640271, #655627, #655428, #656732]
* Misc bug fixes [Dan, Florian, Giovanni, Guillaume, Jasper, Jeremy, Rui;
#645708, #646761, #653119, #654398, #656125, #654707, #654898, #654638,
#656335, #657111]
* Code cleanups [Colin, Dan, Guillaume, Ray;
#652718, #654639, #648651, #655813, #657082]
* String tweaks [Jasper, Jeremy; #652984, #640271]
* Build fixes [Jasper, Nohemi; #644275, #655812]
Contributors:
Jeremy Bicha, Giovanni Campagna, Xavier Claessens, Alban Crequy,
Guillaume Desmottes, Allan Day, Neha Doijode, Nohemi Fernandez,
Tassilo Horn, Rui Matos, Morten Mjelva, Florian Müllner, Alejandro Piñeiro,
Jasper St. Pierre, Ray Strode, Colin Walters, Dan Winship,
Marina Zhurakhinskaya
Translations:
Ivaylo Valkov [bg], Mario Blättermann [de], Diego Escalante Urrelo,
Jorge González, Daniel Mustieles [es], Arash Mousavi [fa], Fran Dieguez [gl],
Yaron Shahrabani [he], Andika Triwidada, Wibiharto [id],
Aurimas Černius [lt], Umarzuki Bin Mochlis Moktar [ml], Kjartan Maraas [nb],
A S Alam [pa], Daniel Nylander [se], Ngô Chin, Nguyễn Thái Ngọc Duy [vi],
Aron Xu [zh_CN], Chao-Hsiung Liao [zh_HK, zh_TW]
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

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.90.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_SRCDIR([src/shell-global.c])
@ -66,14 +66,15 @@ AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
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
FOLKS_MIN_VERSION=0.5.2
GTK_MIN_VERSION=3.0.0
GIO_MIN_VERSION=2.25.9
GIO_MIN_VERSION=2.29.10
LIBECAL_MIN_VERSION=2.32.0
LIBEDATASERVER_MIN_VERSION=1.2.0
LIBEDATASERVERUI_MIN_VERSION=2.91.6
TELEPATHY_GLIB_MIN_VERSION=0.15.3
TELEPATHY_GLIB_MIN_VERSION=0.15.5
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
POLKIT_MIN_VERSION=0.100
STARTUP_NOTIFICATION_MIN_VERSION=0.11
@ -82,9 +83,10 @@ STARTUP_NOTIFICATION_MIN_VERSION=0.11
PKG_CHECK_MODULES(GNOME_SHELL, gio-2.0 >= $GIO_MIN_VERSION
gio-unix-2.0 dbus-glib-1 libxml-2.0
gtk+-3.0 >= $GTK_MIN_VERSION
folks >= $FOLKS_MIN_VERSION
libmutter >= $MUTTER_MIN_VERSION
gjs-internals-1.0 >= $GJS_MIN_VERSION
libgnome-menu $recorder_modules gconf-2.0
libgnome-menu-3.0 $recorder_modules gconf-2.0
gdk-x11-3.0 libsoup-2.4
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
@ -93,7 +95,8 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-2.0 >= $GIO_MIN_VERSION
libcanberra
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes)
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
libnm-glib libnm-util gnome-keyring-1)
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
@ -105,12 +108,6 @@ AC_SUBST([GJS_VERSION], ["$GJS_VERSION"])
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
JHBUILD_TYPELIBDIR="$INTROSPECTION_TYPELIBDIR"
# NM is the only typelib we use that we don't jhbuild
PKG_CHECK_EXISTS([libnm-glib >= 0.8.999],
[NM_TYPELIBDIR=`$PKG_CONFIG --variable=libdir libnm-glib`/girepository-1.0
if test "$INTROSPECTION_TYPELIBDIR" != "$NM_TYPELIBDIR"; then
JHBUILD_TYPELIBDIR="$JHBUILD_TYPELIBDIR:$NM_TYPELIBDIR"
fi])
AC_SUBST(JHBUILD_TYPELIBDIR)
saved_CFLAGS=$CFLAGS
@ -156,6 +153,17 @@ AC_CHECK_FUNCS(fdwalk)
AC_CHECK_FUNCS(mallinfo)
AC_CHECK_HEADERS([sys/resource.h])
# _NL_TIME_FIRST_WEEKDAY is an enum and not a define
AC_MSG_CHECKING([for _NL_TIME_FIRST_WEEKDAY])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <langinfo.h>]],
[[nl_langinfo(_NL_TIME_FIRST_WEEKDAY);]])],
[langinfo_ok=yes], [langinfo_ok=no])
AC_MSG_RESULT($langinfo_ok)
if test "$langinfo_ok" = "yes"; then
AC_DEFINE([HAVE__NL_TIME_FIRST_WEEKDAY], [1],
[Define if _NL_TIME_FIRST_WEEKDAY is available])
fi
# Sets GLIB_GENMARSHAL and GLIB_MKENUMS
AM_PATH_GLIB_2_0()
G_IR_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0`

View File

@ -29,6 +29,7 @@ dist_theme_DATA = \
theme/dash-placeholder.svg \
theme/filter-selected-ltr.svg \
theme/filter-selected-rtl.svg \
theme/gdm.css \
theme/gnome-shell.css \
theme/panel-border.svg \
theme/panel-button-border.svg \

View File

@ -11,15 +11,6 @@
using the Alt-F2 dialog.
</_description>
</key>
<key name="disabled-extensions" type="as">
<default>[]</default>
<_summary>Uuids of extensions to disable</_summary>
<_description>
GNOME Shell extensions have a uuid property; this key lists extensions
which should not be loaded. This setting overrides enabled-extensions
for extensions that appear in both lists.
</_description>
</key>
<key name="enabled-extensions" type="as">
<default>[]</default>
<_summary>Uuids of extensions to enable</_summary>
@ -40,7 +31,7 @@
</_description>
</key>
<key name="favorite-apps" type="as">
<default>[ 'mozilla-firefox.desktop', 'evolution.desktop', 'empathy.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'openoffice.org-writer.desktop', 'nautilus.desktop' ]</default>
<default>[ 'firefox.desktop', 'evolution.desktop', 'empathy.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'libreoffice-writer.desktop', 'nautilus.desktop', 'gnome-documents.desktop' ]</default>
<_summary>List of desktop file IDs for favorite applications</_summary>
<_description>
The applications corresponding to these identifiers
@ -62,6 +53,7 @@
<child name="clock" schema="org.gnome.shell.clock"/>
<child name="calendar" schema="org.gnome.shell.calendar"/>
<child name="recorder" schema="org.gnome.shell.recorder"/>
<child name="keyboard" schema="org.gnome.shell.keyboard"/>
</schema>
<schema id="org.gnome.shell.calendar" path="/org/gnome/shell/calendar/"
@ -75,6 +67,24 @@
</key>
</schema>
<schema id="org.gnome.shell.keyboard" path="/org/gnome/shell/keyboard/"
gettext-domain="@GETTEXT_PACKAGE@">
<key name="show-keyboard" type="b">
<default>false</default>
<_summary>Show the onscreen keyboard</_summary>
<_description>
If true, display onscreen keyboard.
</_description>
</key>
<key name="keyboard-type" type="s">
<default>'touch'</default>
<_summary>Which keyboard to use</_summary>
<_description>
The type of keyboard to use.
</_description>
</key>
</schema>
<schema id="org.gnome.shell.clock" path="/org/gnome/shell/clock/"
gettext-domain="@GETTEXT_PACKAGE@">
<key name="show-seconds" type="b">

161
data/theme/gdm.css Normal file
View File

@ -0,0 +1,161 @@
/* Copyright 2011, Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* Login Dialog */
.login-dialog-title {
font-size: 14pt;
font-weight: bold;
color: #666666;
padding-bottom: 2em;
}
.login-dialog {
border-radius: 16px;
min-height: 150px;
max-height: 700px;
min-width: 350px;
}
.login-dialog-user-list-view {
-st-vfade-offset: 1em;
}
.login-dialog-user-list {
spacing: 12px;
}
.login-dialog-user-list-item {
color: #666666;
}
.login-dialog-user-list-item:ltr {
padding-right: 1em;
}
.login-dialog-user-list-item:rtl {
padding-left: 1em;
}
.login-dialog-user-list-item .login-dialog-user-list-item-name {
font-size: 20pt;
padding-left: 1em;
color: #666666;
}
.login-dialog-user-list-item:hover .login-dialog-user-list-item-name {
color: white;
}
.login-dialog-user-list-item:focus .login-dialog-user-list-item-name {
color: white;
text-shadow: black 0px 2px 2px;
}
.login-dialog-user-list-item-vertical-layout {
spacing: 2px;
}
.login-dialog-user-list-item .login-dialog-user-list-item-focus-bin {
background-color: rgba(0,0,0,0.0);
height: 2px;
}
.login-dialog-user-list-item:focus .login-dialog-user-list-item-focus-bin {
background-color: #666666;
}
.login-dialog-user-list-item-icon {
border: 2px solid #8b8b8b;
border-radius: 8px;
width: 64px;
height: 64px;
}
.login-dialog-not-listed-button {
padding-top: 2em;
}
.login-dialog-not-listed-label {
font-size: 14pt;
font-weight: bold;
color: #666666;
}
.login-dialog-prompt-layout {
padding-bottom: 64px;
}
.login-dialog-prompt-label {
color: white;
font-size: 20pt;
}
.login-dialog-prompt-entry {
padding: 4px;
border-radius: 4px;
border: 2px solid #5656cc;
color: black;
background-color: white;
caret-color: black;
caret-size: 1px;
}
.login-dialog-session-list {
color: #ffffff;
font-size: 10.5pt;
}
.login-dialog-session-list-button {
padding: 4px;
}
.login-dialog-session-list-button:focus {
background-color: #4c4c4c;
}
.login-dialog-session-list-button:active {
background-color: #4c4c4c;
}
.login-dialog-session-list-button:hover {
font-weight: bold;
}
.login-dialog-session-list-scroll-view {
background-gradient-start: rgba(80,80,80,0.3);
background-gradient-end: rgba(80,80,80,0.7);
background-gradient-direction: vertical;
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.9);
border-radius: 8px;
border: 1px solid rgba(80,80,80,1.0);
padding: .5em;
}
.login-dialog-session-list-item:focus {
background-color: #666666;
}
.login-dialog-session-list-triangle {
padding-right: .5em;
}
.login-dialog-session-list-item-box {
spacing: .25em;
}
.login-dialog-session-list-item-dot {
width: .75em;
height: .75em;
}

View File

@ -93,12 +93,12 @@ StTooltip StLabel {
/* PopupMenu */
.popup-menu-boxpointer {
-arrow-border-radius: 9px;
-arrow-border-radius: 8px;
-arrow-background-color: rgba(0,0,0,0.9);
-arrow-border-width: 2px;
-arrow-border-color: #5f5f5f;
-arrow-base: 30px;
-arrow-rise: 15px;
-arrow-border-color: #a5a5a5;
-arrow-base: 24px;
-arrow-rise: 11px;
}
.popup-menu {
@ -139,6 +139,15 @@ StTooltip StLabel {
border-width: 0px;
}
.popup-combo-menu {
background-color: rgba(0,0,0,0.9);
padding: 1em 0em;
color: #ffffff;
font-size: 10.5pt;
border: 1px solid #5f5f5f;
border-radius: 9px;
}
/* The remaining popup-menu sizing is all done in ems, so that if you
* override .popup-menu.font-size, everything else will scale with it.
*/
@ -158,6 +167,10 @@ StTooltip StLabel {
.popup-image-menu-item {
}
.popup-combobox-item {
spacing: 1em;
}
.popup-separator-menu-item {
-gradient-height: 2px;
-gradient-start: rgba(8,8,8,0);
@ -223,6 +236,39 @@ StTooltip StLabel {
spacing: .5em;
}
/* Shared button properties */
.dash-search-button, .notification-button, .notification-icon-button,
.hotplug-notification-item, .hotplug-resident-eject-button,
.modal-dialog-button {
color: white;
border: 1px solid #8b8b8b;
background-gradient-direction: vertical;
background-gradient-start: rgba(255, 255, 255, 0.2);
background-gradient-end: rgba(255, 255, 255, 0);
}
.dash-search-button:hover, .notification-button:hover,
.notification-icon-button:hover, .hotplug-notification-item:hover,
.hotplug-resident-eject-button:hover, .modal-dialog-button:hover {
background-gradient-start: rgba(255, 255, 255, 0.3);
background-gradient-end: rgba(255, 255, 255, 0.1);
}
.dash-search-button:selected, .notification-button:focus,
.notification-icon-button:focus, .hotplug-notification-item:focus,
.modal-dialog-button:focus {
border: 2px solid #8b8b8b;
}
.dash-search-button:active, .dash-search-button:pressed,
.notification-button:active, .notification-icon-button:active,
.hotplug-notification-item:active, .hotplug-resident-eject-button:active,
.modal-dialog-button:active, .modal-dialog-button:pressed {
background-gradient-start: rgba(255, 255, 255, 0);
background-gradient-end: rgba(255, 255, 255, 0.2);
}
/* Panel */
#panel {
@ -321,6 +367,10 @@ StTooltip StLabel {
icon-shadow: black 0px 2px 2px;
}
.panel-menu {
-boxpointer-gap: 4px
}
/* The rounded panel corners we draw don't
* support transitions, so disable transitions
* for the buttons at the left/right edges
@ -337,6 +387,49 @@ StTooltip StLabel {
spacing: 4px;
}
.status-chooser {
spacing: .4em;
}
.status-chooser .popup-menu-item,
.status-chooser-combo .popup-menu-item {
padding: .4em;
}
.status-chooser-user-icon {
border: 2px solid #8b8b8b;
border-radius: 5px;
width: 48pt;
height: 48pt;
}
.status-chooser-user-icon:hover {
border: 2px solid #bbbbbb;
}
.status-chooser-user-name {
font-weight: bold;
font-size: 1.3em;
}
.status-chooser-combo {
border: 1px solid transparent;
}
.status-chooser-combo.popup-combo-menu {
background-color: rgba(0,0,0,0.7);
padding: .4em 0em;
border-radius: 4px;
border: 1px solid #5f5f5f;
color: #ffffff;
font-size: 10.5pt;
}
.status-chooser-status-item,
.status-chooser-combo > .popup-combobox-item {
spacing: .4em;
}
#legacyTray {
spacing: 14px;
padding-left: 14px;
@ -359,7 +452,6 @@ StTooltip StLabel {
#overview {
spacing: 12px;
background-color: rgba(0,0,0,0.6);
}
.window-caption {
@ -552,27 +644,26 @@ StTooltip StLabel {
spacing: 12px;
}
/* Text labels are an odd number of pixels tall. The uneven top and bottom
* padding compensates for this and ensures that the label is vertically
* centered */
.dash-search-button {
background-gradient-direction: vertical;
background-gradient-start: rgba(255, 255, 255, 0.2);
background-gradient-end: rgba(255, 255, 255, 0);
border: 1px solid #808080;
border-radius: 16px;
height: 32px;
padding-top: 4px;
padding-bottom: 5px;
width: 300px;
font-weight: bold;
}
.dash-search-button:selected,
.dash-search-button:hover {
background-gradient-direction: vertical;
background-gradient-start: rgba(255, 255, 255, 0.4);
background-gradient-end: rgba(255, 255, 255, 0.2);
.dash-search-button:selected {
padding-top: 3px;
padding-bottom: 4px;
width: 298px;
}
.dash-search-button-label {
color: #cccccc;
font-size: 12pt;
color: white;
font-size: 11pt;
}
/* Apps */
@ -582,6 +673,11 @@ StTooltip StLabel {
-shell-grid-item-size: 118px;
}
.contact-grid {
spacing: 36px;
-shell-grid-item-size: 272px; /* 2 * -shell-grid-item-size + spacing */
}
.icon-grid .overview-icon {
icon-size: 96px;
}
@ -648,11 +744,57 @@ StTooltip StLabel {
text-align: center;
}
.contact {
width: 272px; /* Same width as two normal results + spacing */
height: 118px; /* Aspect ratio = 1.75. Normal US business card ratio */
border-radius: 4px;
padding: 3px;
border: 1px rgba(0,0,0,0);
transition-duration: 100;
}
.contact-content {
border-radius: 2px;
padding: 8px;
width: 232px;
height: 84px;
background-color: white;
color: black;
text-align: center;
}
.contact-icon {
border-radius: 4px;
}
.contact-details {
padding: 6px 8px 11px 8px;
}
.contact-details-alias {
font-size: 16px;
padding-bottom: 11px;
}
.contact-details-status {
font-size: 11pt;
}
.contact-details-status-icon {
padding-right: 2px;
}
.contact:hover {
background-color: rgba(255,255,255,0.1);
transition-duration: 100;
}
.app-well-app.running > .overview-icon {
text-shadow: black 0px 2px 2px;
background-image: url("running-indicator.svg");
}
.contact:selected,
.app-well-app:selected > .overview-icon,
.search-result-content:selected > .overview-icon {
background-color: rgba(255,255,255,0.33);
@ -666,6 +808,7 @@ StTooltip StLabel {
transition-duration: 100;
}
.contact:focus,
.app-well-app:focus > .overview-icon,
.search-result-content:focus > .overview-icon {
border: 1px solid #cccccc;
@ -800,9 +943,9 @@ StTooltip StLabel {
/* Calendar popup */
#calendarArea {
/* this is the width of the entire popup */
min-width: 600px;
#calendarEventsArea {
/* this is the width of the second column of the popup */
min-width: 400px;
}
.calendar-vertical-separator {
@ -1021,26 +1164,35 @@ StTooltip StLabel {
}
#notification {
font-size: 12pt;
border-radius: 5px 5px 0px 0px;
background: rgba(0,0,0,0.9);
font-size: 11pt;
border-radius: 10px 10px 0px 0px;
background: rgba(0,0,0,0.8);
padding: 8px 8px 4px 8px;
spacing-rows: 10px;
spacing-columns: 10px;
width: 34em;
}
.multi-line-notification {
#notification.multi-line-notification {
padding-bottom: 8px;
}
/* We use row-span = 2 for the image cell, which prevents its height preferences to be
taken into account during allocation, so its height ends up being limited by the height
of the content in the other rows. To avoid showing a stretched image, we set the minimum
height of the table to be ICON_SIZE + IMAGE_SIZE + spacing-rows = 24 + 125 + 10 = 159 */
.notification-with-image {
min-height: 159px;
}
.summary-boxpointer {
-arrow-border-radius: 9px;
-arrow-border-radius: 8px;
-arrow-background-color: rgba(0,0,0,0.9);
-arrow-border-width: 2px;
-arrow-border-color: #5f5f5f;
-arrow-base: 30px;
-arrow-rise: 15px;
-arrow-border-color: #a5a5a5;
-arrow-base: 24px;
-arrow-rise: 11px;
color: white;
}
.summary-boxpointer #notification {
@ -1094,45 +1246,26 @@ StTooltip StLabel {
}
#notification-actions {
spacing: 5px;
spacing: 10px;
}
.notification-button {
background-color: #3c3c3c;
padding: 2px 14px;
border-radius: 12px;
border: 1px solid #181818;
}
.notification-button:hover {
border: 1px solid #a1a1a1;
border-radius: 18px;
font-size: 11pt;
padding: 4px 42px 5px;
}
.notification-button:focus {
background-color: #666666;
}
.notification-button:active {
border: 1px solid #a1a1a1;
background-color: #2b2b2b;
padding: 3px 41px 4px;
}
.notification-icon-button {
border: 2px rgba(0,0,0,0.0);
border-radius: 5px;
padding: 5px;
}
.notification-icon-button:hover {
border: 2px rgba(161,161,161,0.7);
}
.notification-icon-button:focus {
background: rgba(192,192,192,0.7);
}
.notification-icon-button:active {
background: rgba(128,128,128,0.7);
padding: 4px;
}
.notification-icon-button > StIcon {
@ -1145,23 +1278,13 @@ StTooltip StLabel {
}
.hotplug-notification-item {
background-color: #3c3c3c;
padding: 0px 10px;
border-radius: 8px;
border: 1px solid #181818;
}
.hotplug-notification-item:hover {
border: 1px solid #a1a1a1;
padding: 2px 10px;
border-radius: 18px;
font-size: 10.5pt;
}
.hotplug-notification-item:focus {
background-color: #666666;
}
.hotplug-notification-item:active {
border: 1px solid #a1a1a1;
background-color: #2b2b2b;
padding: 1px 71px 1px 11px;
}
.hotplug-notification-item-icon {
@ -1203,28 +1326,20 @@ StTooltip StLabel {
}
.hotplug-resident-eject-button {
padding: 2px;
border: 1px solid #2b2b2b;
padding: 7px;
border-radius: 5px;
color: #ccc;
}
.hotplug-resident-eject-button:hover {
color: #fff;
background-color: #2b2b2b;
border: 1px solid #a1a1a1;
}
.chat-log-message {
color: #888888;
}
.chat-received {
background-gradient-direction: horizontal;
background-gradient-start: rgba(255, 255, 255, 0.2);
background-gradient-end: rgba(255, 255, 255, 0);
.chat-group-sent, .chat-group-meta {
padding: 8px 0;
}
.chat-sent {
padding-left: 4px;
border-radius: 4px;
}
@ -1235,23 +1350,20 @@ StTooltip StLabel {
}
.chat-sent {
background-gradient-direction: horizontal;
background-gradient-start: rgba(255, 255, 255, 0);
background-gradient-end: rgba(255, 255, 255, 0.2);
padding-left: 4px;
padding-left: 18pt;
border-radius: 4px;
color: #7E7E7E;
}
.chat-sent:rtl {
padding-left: 0px;
padding-right: 4px;
padding-right: 18pt;
}
.chat-meta-message {
padding-left: 4px;
border-radius: 4px;
font-size: 10.5pt;
font-size: 9pt;
color: #bbbbbb;
}
@ -1260,24 +1372,35 @@ StTooltip StLabel {
padding-right: 4px;
}
.subscription-message {
font-style: italic;
}
#notification StEntry {
padding: 4px;
border-radius: 4px;
border: 1px solid #565656;
color: #a8a8a8;
background-color: #404040;
caret-color: #ffffff;
selected-color: black;
border: 1px solid rgba(245,245,245,0.2);
background-gradient-direction: vertical;
background-gradient-start: rgb(200,200,200);
background-gradient-end: white;
transition-duration: 300;
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.6);
caret-color: #a8a8a8;
caret-size: 1px;
}
#notification StEntry:focus {
border: 1px solid #3a3a3a;
color: #545454;
background-color: #e8e8e8;
border: 1px solid #8b8b8b;
color: #333333;
background-gradient-direction: vertical;
background-gradient-start: rgb(200,200,200);
background-gradient-end: white;
caret-color: #545454;
selection-background-color: #bcbcbc;
selected-color: #323232;
box-shadow: 0px 0px 6px 2px rgba(255,255,255,0.9);
selection-background-color: #808080;
}
/* The spacing and padding on the summary is tricky; we want to keep
@ -1501,7 +1624,7 @@ StTooltip StLabel {
border-radius: 24px;
background-color: rgba(0.0, 0.0, 0.0, 0.9);
border: 2px solid #868686;
color: #ffffff;
color: #babdb6;
padding-right: 42px;
padding-left: 42px;
@ -1514,37 +1637,21 @@ StTooltip StLabel {
}
.modal-dialog-button {
border: 1px solid #8b8b8b;
border-radius: 18px;
font-size: 10.5pt;
font-size: 11pt;
color: white;
margin-left: 10px;
margin-right: 10px;
padding-left: 32px;
padding-right: 32px;
padding-top: 8px;
padding-bottom: 8px;
background-gradient-direction: vertical;
background-gradient-start: #29323b;
background-gradient-end: #121a24;
padding: 4px 32px 5px;
}
.modal-dialog-button:active,
.modal-dialog-button:pressed {
border-color: #a5a5a5;
background-gradient-start: #121a24;
background-gradient-end: #29323b;
.modal-dialog-button:disabled {
color: rgb(60, 60, 60);
}
.modal-dialog-button:focus {
border: 2px solid #a5a5a5;
padding-left: 31px;
padding-right: 31px;
padding-top: 7px;
padding-bottom: 7px;
padding: 3px 31px 4px;
}
/* Run Dialog */
@ -1806,9 +1913,16 @@ StTooltip StLabel {
}
.polkit-dialog-password-entry {
background-color: white;
background-gradient-start: rgb(236,236,236);
background-gradient-end: white;
background-gradient-direction: vertical;
color: black;
border-radius: 5px;
border: 2px solid #555753;
}
.polkit-dialog-password-entry:focus {
border: 2px solid #3465a4;
}
.polkit-dialog-error-label {
@ -1829,6 +1943,17 @@ StTooltip StLabel {
padding-bottom: 8px;
}
.network-dialog-show-password-checkbox {
padding-top: 5px;
padding-bottom: 5px;
font-size: 10pt;
color: white;
spacing: 10px;
}
.network-dialog-secret-table {
spacing-rows: 15px;
}
/* Magnifier */
@ -1839,3 +1964,58 @@ StTooltip StLabel {
.magnifier-zoom-region.full-screen {
border-width: 0px;
}
/* On-screen Keyboard */
#keyboard {
background: rgba(0,0,0,0.8);
}
.keyboard-layout {
spacing: 10px;
padding: 10px;
}
.keyboard-row {
spacing: 15px;
}
.keyboard-key {
min-height: 30px;
min-width: 30px;
background-gradient-start: rgba(255,245,245,0.4);
background-gradient-end: rgba(105,105,105,0.1);
background-gradient-direction: vertical;
font-size: 14pt;
font-weight: bold;
border-radius: 10px;
border: 2px solid #a0a0a0;
color: white;
}
.keyboard-key:grayed {
color: #808080;
border-color: #808080;
}
.keyboard-key:checked,
.keyboard-key:hover {
background: #303030;
border: 3px solid white;
}
.keyboard-key:active {
background: #808080;
}
.keyboard-subkeys {
color: white;
padding: 5px;
-arrow-border-radius: 10px;
-arrow-background-color: #090909;
-arrow-border-width: 2px;
-arrow-border-color: white;
-arrow-base: 20px;
-arrow-rise: 10px;
-boxpointer-gap: 5px;
}

View File

@ -2,6 +2,8 @@
jsdir = $(pkgdatadir)/js
nobase_dist_js_DATA = \
gdm/batch.js \
gdm/loginDialog.js \
misc/config.js \
misc/docInfo.js \
misc/fileUtils.js \
@ -20,7 +22,7 @@ nobase_dist_js_DATA = \
ui/autorunManager.js \
ui/boxpointer.js \
ui/calendar.js \
ui/chrome.js \
ui/contactDisplay.js \
ui/ctrlAltTab.js \
ui/dash.js \
ui/dateMenu.js \
@ -30,6 +32,7 @@ nobase_dist_js_DATA = \
ui/environment.js \
ui/extensionSystem.js \
ui/iconGrid.js \
ui/keyboard.js \
ui/layout.js \
ui/lightbox.js \
ui/link.js \
@ -39,6 +42,7 @@ nobase_dist_js_DATA = \
ui/main.js \
ui/messageTray.js \
ui/modalDialog.js \
ui/networkAgent.js \
ui/shellMountOperation.js \
ui/notificationDaemon.js \
ui/overview.js \
@ -53,7 +57,6 @@ nobase_dist_js_DATA = \
ui/searchDisplay.js \
ui/shellDBus.js \
ui/statusIconDispatcher.js \
ui/statusMenu.js \
ui/status/accessibility.js \
ui/status/keyboard.js \
ui/status/network.js \
@ -62,6 +65,7 @@ nobase_dist_js_DATA = \
ui/status/bluetooth.js \
ui/telepathyClient.js \
ui/tweener.js \
ui/userMenu.js \
ui/viewSelector.js \
ui/windowAttentionHandler.js \
ui/windowManager.js \

228
js/gdm/batch.js Normal file
View File

@ -0,0 +1,228 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*-
*
* Copyright 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, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
const Lang = imports.lang;
const Signals = imports.signals;
function Task() {
this._init.apply(this, arguments);
}
Task.prototype = {
_init: function(scope, handler) {
if (scope)
this.scope = scope;
else
this.scope = this;
this.handler = handler;
},
run: function() {
if (this.handler)
return this.handler.call(this.scope);
return null;
},
};
Signals.addSignalMethods(Task.prototype);
function Hold() {
this._init.apply(this, arguments);
}
Hold.prototype = {
__proto__: Task.prototype,
_init: function() {
Task.prototype._init.call(this,
this,
function () {
return this;
});
this._acquisitions = 1;
},
acquire: function() {
if (this._acquisitions <= 0)
throw new Error("Cannot acquire hold after it's been released");
this._acquisitions++;
},
acquireUntilAfter: function(hold) {
if (!hold.isAcquired())
return;
this.acquire();
let signalId = hold.connect('release', Lang.bind(this, function() {
hold.disconnect(signalId);
this.release();
}));
},
release: function() {
this._acquisitions--;
if (this._acquisitions == 0)
this.emit('release');
},
isAcquired: function() {
return this._acquisitions > 0;
}
}
Signals.addSignalMethods(Hold.prototype);
function Batch() {
this._init.apply(this, arguments);
}
Batch.prototype = {
__proto__: Task.prototype,
_init: function(scope, tasks) {
Task.prototype._init.call(this);
this.tasks = [];
for (let i = 0; i < tasks.length; i++) {
let task;
if (tasks[i] instanceof Task) {
task = tasks[i];
} else if (typeof tasks[i] == 'function') {
task = new Task(scope, tasks[i]);
} else {
throw new Error('Batch tasks must be functions or Task, Hold or Batch objects');
}
this.tasks.push(task);
}
},
process: function() {
throw new Error('Not implemented');
},
runTask: function() {
if (!(this._currentTaskIndex in this.tasks)) {
return null;
}
return this.tasks[this._currentTaskIndex].run();
},
_finish: function() {
this.hold.release();
},
nextTask: function() {
this._currentTaskIndex++;
// if the entire batch of tasks is finished, release
// the hold and notify anyone waiting on the batch
if (this._currentTaskIndex >= this.tasks.length) {
this._finish();
return;
}
this.process();
},
_start: function() {
// acquire a hold to get released when the entire
// batch of tasks is finished
this.hold = new Hold();
this._currentTaskIndex = 0;
this.process();
},
run: function() {
this._start();
// hold may be destroyed at this point
// if we're already done running
return this.hold;
},
cancel: function() {
this.tasks = this.tasks.splice(0, this._currentTaskIndex + 1);
}
};
Signals.addSignalMethods(Batch.prototype);
function ConcurrentBatch() {
this._init.apply(this, arguments);
}
ConcurrentBatch.prototype = {
__proto__: Batch.prototype,
_init: function(scope, tasks) {
Batch.prototype._init.call(this, scope, tasks);
},
process: function() {
let hold = this.runTask();
if (hold) {
this.hold.acquireUntilAfter(hold);
}
// Regardless of the state of the just run task,
// fire off the next one, so all the tasks can run
// concurrently.
this.nextTask();
}
};
Signals.addSignalMethods(ConcurrentBatch.prototype);
function ConsecutiveBatch() {
this._init.apply(this, arguments);
}
ConsecutiveBatch.prototype = {
__proto__: Batch.prototype,
_init: function(scope, tasks) {
Batch.prototype._init.call(this, scope, tasks);
},
process: function() {
let hold = this.runTask();
if (hold && hold.isAcquired()) {
// This task is inhibiting the batch. Wait on it
// before processing the next one.
let signalId = hold.connect('release',
Lang.bind(this, function() {
hold.disconnect(signalId);
this.nextTask();
}));
return;
} else {
// This task finished, process the next one
this.nextTask();
}
}
};
Signals.addSignalMethods(ConsecutiveBatch.prototype);

1262
js/gdm/loginDialog.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -113,10 +113,10 @@ function run() {
for (let i = 0; i < 2; i++) {
Scripting.scriptEvent('applicationsShowStart');
Main.overview.viewSelector.switchTab('applications');
Main.overview._viewSelector.switchTab('applications');
yield Scripting.waitLeisure();
Scripting.scriptEvent('applicationsShowDone');
Main.overview.viewSelector.switchTab('windows');
Main.overview._viewSelector.switchTab('windows');
yield Scripting.waitLeisure();
}
}

View File

@ -14,7 +14,7 @@ const Tweener = imports.ui.tweener;
const POPUP_APPICON_SIZE = 96;
const POPUP_SCROLL_TIME = 0.10; // seconds
const POPUP_FADE_IN_TIME = 0.4; // seconds
const POPUP_DELAY_TIMEOUT = 150; // milliseconds
const POPUP_FADE_OUT_TIME = 0.1; // seconds
const APP_ICON_HOVER_TIMEOUT = 200; // milliseconds
@ -53,6 +53,7 @@ AltTabPopup.prototype = {
this._currentWindow = -1;
this._thumbnailTimeoutId = 0;
this._motionTimeoutId = 0;
this._initialDelayTimeoutId = 0;
this.thumbnailsVisible = false;
@ -146,8 +147,6 @@ AltTabPopup.prototype = {
// Need to force an allocation so we can figure out whether we
// need to scroll when selecting
this.actor.opacity = 0;
this.actor.show();
this.actor.get_allocation_box();
// Make the initial selection
@ -183,16 +182,13 @@ 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_IN_TIME,
transition: 'easeInOutExpo'
});
// We delay showing the popup so that fast Alt+Tab users aren't
// disturbed by the popup briefly flashing.
this._initialDelayTimeoutId = Mainloop.timeout_add(POPUP_DELAY_TIMEOUT,
Lang.bind(this, function () {
this.actor.show();
this._initialDelayTimeoutId = 0;
}));
return true;
},
@ -223,7 +219,7 @@ AltTabPopup.prototype = {
let keysym = event.get_key_symbol();
let event_state = Shell.get_event_state(event);
let backwards = event_state & Clutter.ModifierType.SHIFT_MASK;
let action = global.screen.get_display().get_keybinding_action(event.get_key_code(), event_state);
let action = global.display.get_keybinding_action(event.get_key_code(), event_state);
this._disableHover();
@ -395,6 +391,8 @@ AltTabPopup.prototype = {
Mainloop.source_remove(this._motionTimeoutId);
if (this._thumbnailTimeoutId != 0)
Mainloop.source_remove(this._thumbnailTimeoutId);
if (this._initialDelayTimeoutId != 0)
Mainloop.source_remove(this._initialDelayTimeoutId);
},
/**

View File

@ -3,6 +3,7 @@
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const GMenu = imports.gi.GMenu;
const Shell = imports.gi.Shell;
const Lang = imports.lang;
const Signals = imports.signals;
@ -35,8 +36,7 @@ AlphabeticalView.prototype = {
this._appSystem = Shell.AppSystem.get_default();
this._pendingAppLaterId = 0;
this._apps = [];
this._filterApp = null;
this._appIcons = {}; // desktop file id
let box = new St.BoxLayout({ vertical: true });
box.add(this._grid.actor, { y_align: St.Align.START, expand: true });
@ -63,20 +63,17 @@ AlphabeticalView.prototype = {
_removeAll: function() {
this._grid.removeAll();
this._apps = [];
this._appIcons = {};
},
_addApp: function(appInfo) {
let appIcon = new AppWellIcon(this._appSystem.get_app(appInfo.get_id()));
_addApp: function(app) {
var id = app.get_id();
let appIcon = new AppWellIcon(app);
this._grid.addItem(appIcon.actor);
appIcon.actor.connect('key-focus-in', Lang.bind(this, this._ensureIconVisible));
appIcon._appInfo = appInfo;
if (this._filterApp && !this._filterApp(appInfo))
appIcon.actor.hide();
this._apps.push(appIcon);
this._appIcons[id] = appIcon;
},
_ensureIconVisible: function(icon) {
@ -105,52 +102,33 @@ AlphabeticalView.prototype = {
transition: 'easeOutQuad' });
},
setFilter: function(filter) {
this._filterApp = filter;
for (let i = 0; i < this._apps.length; i++)
this._apps[i].actor.visible = filter(this._apps[i]._appInfo);
},
// Create actors for the applications in an idle to avoid blocking
// for too long; see bug 647778
_addPendingApps: function() {
let i;
let startTimeMillis = new Date().getTime();
for (i = 0; i < this._pendingAppIds.length; i++) {
let id = this._pendingAppIds[i];
this._addApp(this._pendingApps[id]);
let currentTimeMillis = new Date().getTime();
if (currentTimeMillis - startTimeMillis > MAX_APPLICATION_WORK_MILLIS)
break;
}
this._pendingAppIds.splice(0, i + 1);
if (this._pendingAppIds.length > 0) {
return true;
setVisibleApps: function(apps) {
if (apps == null) { // null implies "all"
for (var id in this._appIcons) {
var icon = this._appIcons[id];
icon.actor.visible = true;
}
} else {
this._pendingAppLaterId = 0;
this._pendingAppIds = null;
this._pendingApps = null;
return false;
// Set everything to not-visible, then set to visible what we should see
for (var id in this._appIcons) {
var icon = this._appIcons[id];
icon.actor.visible = false;
}
for (var i = 0; i < apps.length; i++) {
var app = apps[i];
var id = app.get_id();
var icon = this._appIcons[id];
icon.actor.visible = true;
}
}
},
refresh: function(apps) {
let ids = [];
for (let i in apps)
ids.push(i);
ids.sort(function(a, b) {
return apps[a].get_name().localeCompare(apps[b].get_name());
});
setAppList: function(apps) {
this._removeAll();
this._pendingAppIds = ids;
this._pendingApps = apps;
if (this._pendingAppLaterId)
Meta.later_remove(this._pendingAppLaterId);
this._pendingAppLaterId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
Lang.bind(this, this._addPendingApps));
for (var i = 0; i < apps.length; i++) {
var app = apps[i];
this._addApp(app);
}
}
};
@ -171,23 +149,24 @@ ViewByCategories.prototype = {
// -2 is a flag to indicate that nothing is selected
// (used only before the actor is mapped the first time)
this._currentCategory = -2;
this._filters = new St.BoxLayout({ vertical: true, reactive: true });
this._filtersBox = new St.ScrollView({ x_fill: false,
y_fill: false,
style_class: 'vfade' });
this._filtersBox.add_actor(this._filters);
this._categories = [];
this._apps = null;
this._categoryBox = new St.BoxLayout({ vertical: true, reactive: true });
this._categoryScroll = new St.ScrollView({ x_fill: false,
y_fill: false,
style_class: 'vfade' });
this._categoryScroll.add_actor(this._categoryBox);
this.actor.add(this._view.actor, { expand: true, x_fill: true, y_fill: true });
this.actor.add(this._filtersBox, { expand: false, y_fill: false, y_align: St.Align.START });
this.actor.add(this._categoryScroll, { expand: false, y_fill: false, y_align: St.Align.START });
// Always select the "All" filter when switching to the app view
this.actor.connect('notify::mapped', Lang.bind(this,
function() {
if (this.actor.mapped && this._allFilter)
if (this.actor.mapped && this._allCategoryButton)
this._selectCategory(-1);
}));
this._sections = [];
// We need a dummy actor to catch the keyboard focus if the
// user Ctrl-Alt-Tabs here before the deferred work creates
// our real contents
@ -201,64 +180,95 @@ ViewByCategories.prototype = {
this._currentCategory = num;
if (num != -1)
this._allFilter.remove_style_pseudo_class('selected');
else
this._allFilter.add_style_pseudo_class('selected');
if (num != -1) {
var category = this._categories[num];
this._allCategoryButton.remove_style_pseudo_class('selected');
this._view.setVisibleApps(category.apps);
} else {
this._allCategoryButton.add_style_pseudo_class('selected');
this._view.setVisibleApps(null);
}
this._view.setFilter(Lang.bind(this, function(app) {
if (num == -1)
return true;
return this._sections[num].name == app.get_section();
}));
for (let i = 0; i < this._sections.length; i++) {
for (var i = 0; i < this._categories.length; i++) {
if (i == num)
this._sections[i].filterActor.add_style_pseudo_class('selected');
this._categories[i].button.add_style_pseudo_class('selected');
else
this._sections[i].filterActor.remove_style_pseudo_class('selected');
this._categories[i].button.remove_style_pseudo_class('selected');
}
},
_addFilter: function(name, num) {
// Recursively load a GMenuTreeDirectory; we could put this in ShellAppSystem too
_loadCategory: function(dir, appList) {
var iter = dir.iter();
var nextType;
while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
if (nextType == GMenu.TreeItemType.ENTRY) {
var entry = iter.get_entry();
var app = this._appSystem.lookup_app_by_tree_entry(entry);
if (!entry.get_app_info().get_nodisplay())
appList.push(app);
} else if (nextType == GMenu.TreeItemType.DIRECTORY) {
this._loadCategory(iter.get_directory(), appList);
}
}
},
_addCategory: function(name, index, dir, allApps) {
let button = new St.Button({ label: GLib.markup_escape_text (name, -1),
style_class: 'app-filter',
x_align: St.Align.START,
can_focus: true });
this._filters.add(button, { expand: true, x_fill: true, y_fill: false });
button.connect('clicked', Lang.bind(this, function() {
this._selectCategory(num);
this._selectCategory(index);
}));
if (num != -1)
this._sections[num] = { filterActor: button,
name: name };
else
this._allFilter = button;
var apps;
if (dir == null) {
apps = allApps;
this._allCategoryButton = button;
} else {
apps = [];
this._loadCategory(dir, apps);
this._categories.push({ apps: apps,
name: name,
button: button });
}
this._categoryBox.add(button, { expand: true, x_fill: true, y_fill: false });
},
_removeAll: function() {
this._sections = [];
this._filters.destroy_children();
this._categories = [];
this._categoryBox.destroy_children();
},
refresh: function(apps) {
refresh: function() {
this._removeAll();
let sections = this._appSystem.get_sections();
this._apps = apps;
var allApps = Shell.AppSystem.get_default().get_all();
allApps.sort(function(a, b) {
return a.compare_by_name(b);
});
/* Translators: Filter to display all applications */
this._addFilter(_("All"), -1);
this._addCategory(_("All"), -1, null, allApps);
if (!sections)
return;
var tree = this._appSystem.get_tree();
var root = tree.get_root_directory();
for (let i = 0; i < sections.length; i++)
this._addFilter(sections[i], i);
var iter = root.iter();
var nextType;
var i = 0;
while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
if (nextType == GMenu.TreeItemType.DIRECTORY) {
var dir = iter.get_directory();
this._addCategory(dir.get_name(), i, dir);
i++;
}
}
this._view.setAppList(allApps);
this._selectCategory(-1);
this._view.refresh(apps);
if (this._focusDummy) {
let focused = this._focusDummy.has_key_focus();
@ -291,60 +301,7 @@ AllAppDisplay.prototype = {
},
_redisplay: function() {
let apps = this._appSystem.get_flattened_apps().filter(function(app) {
return !app.get_is_nodisplay();
});
this._appView.refresh(apps);
}
};
function BaseAppSearchProvider() {
this._init();
}
BaseAppSearchProvider.prototype = {
__proto__: Search.SearchProvider.prototype,
_init: function(name) {
Search.SearchProvider.prototype._init.call(this, name);
this._appSys = Shell.AppSystem.get_default();
},
getResultMeta: function(resultId) {
let app = this._appSys.get_app(resultId);
if (!app)
return null;
return { 'id': resultId,
'name': app.get_name(),
'createIcon': function(size) {
return app.create_icon_texture(size);
}
};
},
activateResult: function(id, params) {
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);
if (openNewWindow)
app.open_new_window(workspace);
else
app.activate(workspace);
},
dragActivateResult: function(id, params) {
params = Params.parse(params, { workspace: null,
timestamp: null });
let app = this._appSys.get_app(id);
app.open_new_window(params.workspace ? params.workspace.index() : -1);
this._appView.refresh();
}
};
@ -353,44 +310,104 @@ function AppSearchProvider() {
}
AppSearchProvider.prototype = {
__proto__: BaseAppSearchProvider.prototype,
__proto__: Search.SearchProvider.prototype,
_init: function() {
BaseAppSearchProvider.prototype._init.call(this, _("APPLICATIONS"));
Search.SearchProvider.prototype._init.call(this, _("APPLICATIONS"));
this._appSys = Shell.AppSystem.get_default();
},
getResultMeta: function(app) {
return { 'id': app,
'name': app.get_name(),
'createIcon': function(size) {
return app.create_icon_texture(size);
}
};
},
getInitialResultSet: function(terms) {
return this._appSys.initial_search(false, terms);
return this._appSys.initial_search(terms);
},
getSubsearchResultSet: function(previousResults, terms) {
return this._appSys.subsearch(false, previousResults, terms);
return this._appSys.subsearch(previousResults, terms);
},
activateResult: function(app, params) {
params = Params.parse(params, { workspace: -1,
timestamp: 0 });
let event = Clutter.get_current_event();
let modifiers = event ? Shell.get_event_state(event) : 0;
let openNewWindow = modifiers & Clutter.ModifierType.CONTROL_MASK;
if (openNewWindow)
app.open_new_window(params.workspace);
else
app.activate_full(params.workspace, params.timestamp);
},
dragActivateResult: function(id, params) {
params = Params.parse(params, { workspace: -1,
timestamp: 0 });
let app = this._appSys.lookup_app(id);
app.open_new_window(workspace);
},
createResultActor: function (resultMeta, terms) {
let app = this._appSys.get_app(resultMeta['id']);
let app = resultMeta['id'];
let icon = new AppWellIcon(app);
return icon.actor;
}
};
function PrefsSearchProvider() {
function SettingsSearchProvider() {
this._init();
}
PrefsSearchProvider.prototype = {
__proto__: BaseAppSearchProvider.prototype,
SettingsSearchProvider.prototype = {
__proto__: Search.SearchProvider.prototype,
_init: function() {
BaseAppSearchProvider.prototype._init.call(this, _("SETTINGS"));
Search.SearchProvider.prototype._init.call(this, _("SETTINGS"));
this._appSys = Shell.AppSystem.get_default();
this._gnomecc = this._appSys.lookup_app('gnome-control-center.desktop');
},
getResultMeta: function(pref) {
return { 'id': pref,
'name': pref.get_name(),
'createIcon': function(size) {
return pref.create_icon_texture(size);
}
};
},
getInitialResultSet: function(terms) {
return this._appSys.initial_search(true, terms);
return this._appSys.search_settings(terms);
},
getSubsearchResultSet: function(previousResults, terms) {
return this._appSys.subsearch(true, previousResults, terms);
return this._appSys.search_settings(terms);
},
activateResult: function(pref, params) {
params = Params.parse(params, { workspace: -1,
timestamp: 0 });
pref.activate_full(params.workspace, params.timestamp);
},
dragActivateResult: function(pref, params) {
this.activateResult(pref, params);
},
createResultActor: function (resultMeta, terms) {
let app = resultMeta['id'];
let icon = new AppWellIcon(app);
return icon.actor;
}
};
@ -416,12 +433,12 @@ AppIcon.prototype = {
}
};
function AppWellIcon(app, iconParams) {
this._init(app, iconParams);
function AppWellIcon(app, iconParams, onActivateOverride) {
this._init(app, iconParams, onActivateOverride);
}
AppWellIcon.prototype = {
_init : function(app, iconParams) {
_init : function(app, iconParams, onActivateOverride) {
this.app = app;
this.actor = new St.Button({ style_class: 'app-well-app',
reactive: true,
@ -436,6 +453,8 @@ AppWellIcon.prototype = {
this.actor.label_actor = this.icon.label;
// A function callback to override the default "app.activate()"; used by preferences
this._onActivateOverride = onActivateOverride;
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
this.actor.connect('popup-menu', Lang.bind(this, this._onKeyboardPopupMenu));
@ -569,24 +588,28 @@ AppWellIcon.prototype = {
this.emit('launching');
let modifiers = Shell.get_event_state(event);
if (modifiers & Clutter.ModifierType.CONTROL_MASK
&& this.app.state == Shell.AppState.RUNNING) {
this.app.open_new_window(-1);
if (this._onActivateOverride) {
this._onActivateOverride(event);
} else {
this.app.activate(-1);
if (modifiers & Clutter.ModifierType.CONTROL_MASK
&& this.app.state == Shell.AppState.RUNNING) {
this.app.open_new_window(-1);
} else {
this.app.activate();
}
}
Main.overview.hide();
},
shellWorkspaceLaunch : function(params) {
params = Params.parse(params, { workspace: null,
timestamp: null });
params = Params.parse(params, { workspace: -1,
timestamp: 0 });
this.app.open_new_window(params.workspace ? params.workspace.index() : -1);
this.app.open_new_window(params.workspace);
},
getDragActor: function() {
return this.app.create_icon_texture(Main.overview.dash.iconSize);
return this.app.create_icon_texture(Main.overview.dashIconSize);
},
// Returns the original actor that should align with the actor
@ -609,7 +632,7 @@ AppIconMenu.prototype = {
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
side = St.Side.RIGHT;
PopupMenu.PopupMenu.prototype._init.call(this, source.actor, 0.5, side, 0);
PopupMenu.PopupMenu.prototype._init.call(this, source.actor, 0.5, side);
// We want to keep the item hovered while the menu is up
this.blockSourceEvents = true;
@ -650,17 +673,18 @@ AppIconMenu.prototype = {
item._window = windows[i];
}
if (windows.length > 0)
if (!this._source.app.is_window_backed()) {
if (windows.length > 0)
this._appendSeparator();
let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
this._newWindowMenuItem = this._appendMenuItem(_("New Window"));
this._appendSeparator();
let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
this._newWindowMenuItem = this._appendMenuItem(_("New Window"));
this._appendSeparator();
this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ? _("Remove from Favorites")
: _("Add to Favorites"));
this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ? _("Remove from Favorites")
: _("Add to Favorites"));
}
},
_appendSeparator: function () {

View File

@ -28,7 +28,7 @@ AppFavorites.prototype = {
let ids = global.settings.get_strv(this.FAVORITE_APPS_KEY);
let appSys = Shell.AppSystem.get_default();
let apps = ids.map(function (id) {
return appSys.get_app(id);
return appSys.lookup_app(id);
}).filter(function (app) {
return app != null;
});
@ -65,7 +65,7 @@ AppFavorites.prototype = {
if (appId in this._favorites)
return false;
let app = Shell.AppSystem.get_default().get_app(appId);
let app = Shell.AppSystem.get_default().lookup_app(appId);
if (!app)
return false;
@ -84,9 +84,9 @@ AppFavorites.prototype = {
if (!this._addFavorite(appId, pos))
return;
let app = Shell.AppSystem.get_default().get_app(appId);
let app = Shell.AppSystem.get_default().lookup_app(appId);
Main.overview.shellInfo.setMessage(_("%s has been added to your favorites.").format(app.get_name()), Lang.bind(this, function () {
Main.overview.setMessage(_("%s has been added to your favorites.").format(app.get_name()), Lang.bind(this, function () {
this._removeFavorite(appId);
}));
},
@ -117,8 +117,8 @@ AppFavorites.prototype = {
if (!this._removeFavorite(appId))
return;
Main.overview.shellInfo.setMessage(_("%s has been removed from your favorites.").format(app.get_name()),
Lang.bind(this, function () {
Main.overview.setMessage(_("%s has been removed from your favorites.").format(app.get_name()),
Lang.bind(this, function () {
this._addFavorite(appId, pos);
}));
}

View File

@ -330,7 +330,8 @@ AutorunResidentSource.prototype = {
},
createNotificationIcon: function(iconSize) {
return new St.Icon ({ icon_name: 'drive-harddisk',
return new St.Icon ({ icon_name: 'media-removable',
icon_type: St.IconType.FULLCOLOR,
icon_size: iconSize ? iconSize : this.ICON_SIZE });
}
}

View File

@ -180,7 +180,7 @@ BoxPointer.prototype = {
this.bin.allocate(childBox, flags);
if (this._sourceActor && this._sourceActor.mapped)
this._reposition(this._sourceActor, this._gap, this._alignment);
this._reposition(this._sourceActor, this._alignment);
},
_drawBorder: function(area) {
@ -306,19 +306,18 @@ BoxPointer.prototype = {
cr.stroke();
},
setPosition: function(sourceActor, gap, alignment) {
setPosition: function(sourceActor, alignment) {
// We need to show it now to force an allocation,
// so that we can query the correct size.
this.actor.show();
this._sourceActor = sourceActor;
this._gap = gap;
this._alignment = alignment;
this._reposition(sourceActor, gap, alignment);
this._reposition(sourceActor, alignment);
},
_reposition: function(sourceActor, gap, alignment) {
_reposition: function(sourceActor, alignment) {
// Position correctly relative to the sourceActor
let sourceNode = sourceActor.get_theme_node();
let sourceContentBox = sourceNode.get_content_box(sourceActor.get_allocation_box());
@ -338,6 +337,9 @@ BoxPointer.prototype = {
let margin = (4 * borderRadius + borderWidth + arrowBase);
let halfMargin = margin / 2;
let themeNode = this.actor.get_theme_node();
let gap = themeNode.get_length('-boxpointer-gap');
let resX, resY;
switch (this._arrowSide) {

View File

@ -351,17 +351,16 @@ function Calendar(eventSource) {
Calendar.prototype = {
_init: function(eventSource) {
this._eventSource = eventSource;
if (eventSource) {
this._eventSource = eventSource;
this._eventSource.connect('changed', Lang.bind(this,
function() {
this._update(false);
}));
this._eventSource.connect('changed', Lang.bind(this,
function() {
this._update(false);
}));
}
// FIXME: This is actually the fallback method for GTK+ for the week start;
// GTK+ by preference uses nl_langinfo (NL_TIME_FIRST_WEEKDAY). We probably
// should add a C function so we can do the full handling.
this._weekStart = NaN;
this._weekStart = Shell.util_get_week_start();
this._weekdate = NaN;
this._digitWidth = NaN;
this._settings = new Gio.Settings({ schema: 'org.gnome.shell.calendar' });
@ -369,16 +368,6 @@ Calendar.prototype = {
this._settings.connect('changed::' + SHOW_WEEKDATE_KEY, Lang.bind(this, this._onSettingsChange));
this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY);
let weekStartString = Gettext_gtk30.gettext('calendar:week_start:0');
if (weekStartString.indexOf('calendar:week_start:') == 0) {
this._weekStart = parseInt(weekStartString.substring(20));
}
if (isNaN(this._weekStart) || this._weekStart < 0 || this._weekStart > 6) {
log('Translation of "calendar:week_start:0" in GTK+ is not correct');
this._weekStart = 0;
}
// Find the ordering for month/year in the calendar heading
this._headerFormatWithoutYear = '%B';
switch (Gettext_gtk30.gettext('calendar:MY')) {
@ -567,13 +556,16 @@ Calendar.prototype = {
while (true) {
let button = new St.Button({ label: iter.getDate().toString() });
if (!this._eventSource)
button.reactive = false;
let iterStr = iter.toUTCString();
button.connect('clicked', Lang.bind(this, function() {
let newlySelectedDate = new Date(iterStr);
this.setDate(newlySelectedDate, false);
}));
let hasEvents = this._eventSource.hasEvents(iter);
let hasEvents = this._eventSource && this._eventSource.hasEvents(iter);
let styleClass = 'calendar-day-base calendar-day';
if (_isWorkDay(iter))
styleClass += ' calendar-work-day'
@ -620,7 +612,8 @@ Calendar.prototype = {
}
// Signal to the event source that we are interested in events
// only from this date range
this._eventSource.requestRange(beginDate, iter, forceReload);
if (this._eventSource)
this._eventSource.requestRange(beginDate, iter, forceReload);
}
};
@ -638,17 +631,7 @@ EventsList.prototype = {
this._eventSource.connect('changed', Lang.bind(this, this._update));
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
this._desktopSettings.connect('changed', Lang.bind(this, this._update));
let weekStartString = Gettext_gtk30.gettext('calendar:week_start:0');
if (weekStartString.indexOf('calendar:week_start:') == 0) {
this._weekStart = parseInt(weekStartString.substring(20));
}
if (isNaN(this._weekStart) ||
this._weekStart < 0 ||
this._weekStart > 6) {
log('Translation of "calendar:week_start:0" in GTK+ is not correct');
this._weekStart = 0;
}
this._weekStart = Shell.util_get_week_start();
this._update();
},
@ -667,6 +650,9 @@ EventsList.prototype = {
},
_addPeriod: function(header, begin, end, includeDayName, showNothingScheduled) {
if (!this._eventSource)
return;
let events = this._eventSource.getEvents(begin, end);
let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);;

View File

@ -1,441 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
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
// workspace content.
const defaultParams = {
visibleInFullscreen: false,
affectsStruts: true,
affectsInputRegion: true
};
function Chrome() {
this._init();
}
Chrome.prototype = {
_init: function() {
// The group itself has zero size so it doesn't interfere with DND
this.actor = new Shell.GenericContainer({ width: 0, height: 0 });
Main.uiGroup.add_actor(this.actor);
this.actor.connect('allocate', Lang.bind(this, this._allocated));
this._monitors = [];
this._inOverview = false;
this._trackedActors = [];
Main.layoutManager.connect('monitors-changed',
Lang.bind(this, this._relayout));
global.screen.connect('restacked',
Lang.bind(this, this._windowsRestacked));
// Need to update struts on new workspaces when they are added
global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._queueUpdateRegions));
Main.overview.connect('showing',
Lang.bind(this, this._overviewShowing));
Main.overview.connect('hidden',
Lang.bind(this, this._overviewHidden));
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) {
let children = this.actor.get_children();
for (let i = 0; i < children.length; i++)
children[i].allocate_preferred_size(flags);
},
// addActor:
// @actor: an actor to add to the chrome layer
// @params: (optional) additional params
//
// Adds @actor to the chrome layer and extends the input region
// and window manager struts to include it. (Window manager struts
// will only be affected if @actor is touching a screen edge.)
// Changes in @actor's size and position will automatically result
// in appropriate changes to the input region and struts. Changes
// in its visibility will affect the input region, but NOT the
// struts.
//
// If %visibleInFullscreen is %true, the actor will be visible
// even when a fullscreen window should be covering it.
//
// If %affectsStruts or %affectsInputRegion is %false, the actor
// will not have the indicated effect.
addActor: function(actor, params) {
this.actor.add_actor(actor);
this._trackActor(actor, params);
},
// trackActor:
// @actor: a descendant of the chrome to begin tracking
// @params: parameters describing how to track @actor
//
// Tells the chrome to track @actor, which must be a descendant
// of an actor added via addActor(). This can be used to extend the
// struts or input region to cover specific children.
//
// @params can have any of the same values as in addActor(), though
// some possibilities don't make sense (eg, trying to have a
// %visibleInFullscreen child of a non-%visibleInFullscreen parent).
// By default, @actor has the same params as its chrome ancestor.
trackActor: function(actor, params) {
let ancestor = actor.get_parent();
let index = this._findActor(ancestor);
while (ancestor && index == -1) {
ancestor = ancestor.get_parent();
index = this._findActor(ancestor);
}
if (!ancestor)
throw new Error('actor is not a descendent of the chrome layer');
let ancestorData = this._trackedActors[index];
if (!params)
params = {};
// We can't use Params.parse here because we want to drop
// the extra values like ancestorData.actor
for (let prop in defaultParams) {
if (!params[prop])
params[prop] = ancestorData[prop];
}
this._trackActor(actor, params);
},
// untrackActor:
// @actor: an actor previously tracked via trackActor()
//
// Undoes the effect of trackActor()
untrackActor: function(actor) {
this._untrackActor(actor);
},
// removeActor:
// @actor: a child of the chrome layer
//
// Removes @actor from the chrome layer
removeActor: function(actor) {
this.actor.remove_actor(actor);
this._untrackActor(actor);
},
_findActor: function(actor) {
for (let i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i];
if (actorData.actor == actor)
return i;
}
return -1;
},
_trackActor: function(actor, params) {
if (this._findActor(actor) != -1)
throw new Error('trying to re-track existing chrome actor');
let actorData = Params.parse(params, defaultParams);
actorData.actor = actor;
actorData.visibleId = actor.connect('notify::visible',
Lang.bind(this, this._queueUpdateRegions));
actorData.allocationId = actor.connect('notify::allocation',
Lang.bind(this, this._queueUpdateRegions));
actorData.parentSetId = actor.connect('parent-set',
Lang.bind(this, this._actorReparented));
// Note that destroying actor will unset its parent, so we don't
// need to connect to 'destroy' too.
this._trackedActors.push(actorData);
this._queueUpdateRegions();
},
_untrackActor: function(actor) {
let i = this._findActor(actor);
if (i == -1)
return;
let actorData = this._trackedActors[i];
this._trackedActors.splice(i, 1);
actor.disconnect(actorData.visibleId);
actor.disconnect(actorData.allocationId);
actor.disconnect(actorData.parentSetId);
this._queueUpdateRegions();
},
_actorReparented: function(actor, oldParent) {
if (!this.actor.contains(actor))
this._untrackActor(actor);
},
_updateVisibility: function() {
for (let i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i];
if (!this._inOverview && !actorData.visibleInFullscreen &&
this._findMonitorForActor(actorData.actor).inFullscreen)
this.actor.set_skip_paint(actorData.actor, true);
else
this.actor.set_skip_paint(actorData.actor, false);
}
},
_overviewShowing: function() {
this._inOverview = true;
this._updateVisibility();
this._queueUpdateRegions();
},
_overviewHidden: function() {
this._inOverview = false;
this._updateVisibility();
this._queueUpdateRegions();
},
_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) {
// First look at what monitor the center of the rectangle is at
let cx = x + w/2;
let cy = y + h/2;
for (let i = 0; i < this._monitors.length; i++) {
let monitor = this._monitors[i];
if (cx >= monitor.x && cx < monitor.x + monitor.width &&
cy >= monitor.y && cy < monitor.y + monitor.height)
return monitor;
}
// If the center is not on a monitor, return the first overlapping monitor
for (let i = 0; i < this._monitors.length; i++) {
let monitor = this._monitors[i];
if (x + w > monitor.x && x < monitor.x + monitor.width &&
y + h > monitor.y && y < monitor.y + monitor.height)
return monitor;
}
// otherwise on no monitor
return null;
},
_findMonitorForWindow: function(window) {
return this._findMonitorForRect(window.x, window.y, window.width, window.height);
},
// This call guarantees that we return some monitor to simplify usage of it
// In practice all tracked actors should be visible on some monitor anyway
_findMonitorForActor: function(actor) {
let [x, y] = actor.get_transformed_position();
let [w, h] = actor.get_transformed_size();
let monitor = this._findMonitorForRect(x, y, w, h);
if (monitor)
return monitor;
return this._primaryMonitor; // Not on any monitor, pretend its on the primary
},
_queueUpdateRegions: function() {
if (!this._updateRegionIdle)
this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this._updateRegions),
Meta.PRIORITY_BEFORE_REDRAW);
},
_updateFullscreen: function() {
let windows = Main.getWindowActorsForWorkspace(global.screen.get_active_workspace_index());
// Reset all monitors to not fullscreen
for (let i = 0; i < this._monitors.length; i++)
this._monitors[i].inFullscreen = false;
// The chrome layer should be visible unless there is a window
// with layer FULLSCREEN, or a window with layer
// OVERRIDE_REDIRECT that covers the whole screen.
// ('override_redirect' is not actually a layer above all
// other windows, but this seems to be how mutter treats it
// currently...) If we wanted to be extra clever, we could
// figure out when an OVERRIDE_REDIRECT window was trying to
// partially overlap us, and then adjust the input region and
// our clip region accordingly...
// @windows is sorted bottom to top.
for (let i = windows.length - 1; i > -1; i--) {
let window = windows[i];
let layer = window.get_meta_window().get_layer();
if (layer == Meta.StackLayer.FULLSCREEN) {
let monitor = this._findMonitorForWindow(window);
if (monitor)
monitor.inFullscreen = true;
}
if (layer == Meta.StackLayer.OVERRIDE_REDIRECT) {
let monitor = this._findMonitorForWindow(window);
if (monitor &&
window.x <= monitor.x &&
window.x + window.width >= monitor.x + monitor.width &&
window.y <= monitor.y &&
window.y + window.height >= monitor.y + monitor.height)
monitor.inFullscreen = true;
} else
break;
}
},
_windowsRestacked: function() {
let wasInFullscreen = [];
for (let i = 0; i < this._monitors.length; i++)
wasInFullscreen[i] = this._monitors[i].inFullscreen;
this._updateFullscreen();
let changed = false;
for (let i = 0; i < wasInFullscreen.length; i++) {
if (wasInFullscreen[i] != this._monitors[i].inFullscreen) {
changed = true;
break;
}
}
if (changed) {
this._updateVisibility();
this._queueUpdateRegions();
}
},
_updateRegions: function() {
let rects = [], struts = [], i;
delete this._updateRegionIdle;
for (i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i];
if (!actorData.affectsInputRegion && !actorData.affectsStruts)
continue;
let [x, y] = actorData.actor.get_transformed_position();
let [w, h] = actorData.actor.get_transformed_size();
x = Math.round(x);
y = Math.round(y);
w = Math.round(w);
h = Math.round(h);
let rect = new Meta.Rectangle({ x: x, y: y, width: w, height: h});
if (actorData.affectsInputRegion &&
actorData.actor.get_paint_visibility() &&
!this.actor.get_skip_paint(actorData.actor))
rects.push(rect);
if (!actorData.affectsStruts)
continue;
// Limit struts to the size of the screen
let x1 = Math.max(x, 0);
let x2 = Math.min(x + w, global.screen_width);
let y1 = Math.max(y, 0);
let y2 = Math.min(y + h, global.screen_height);
// NetWM struts are not really powerful enought to handle
// a multi-monitor scenario, they only describe what happens
// around the outer sides of the full display region. However
// it can describe a partial region along each side, so
// we can support having the struts only affect the
// primary monitor. This should be enough as we only have
// chrome affecting the struts on the primary monitor so
// far.
//
// Metacity wants to know what side of the screen the
// strut is considered to be attached to. If the actor is
// only touching one edge, or is touching the entire
// border of the primary monitor, then it's obvious which
// side to call it. If it's in a corner, we pick a side
// arbitrarily. If it doesn't touch any edges, or it spans
// the width/height across the middle of the screen, then
// we don't create a strut for it at all.
let side;
let primary = this._primaryMonitor;
if (x1 <= primary.x && x2 >= primary.x + primary.width) {
if (y1 <= primary.y)
side = Meta.Side.TOP;
else if (y2 >= primary.y + primary.height)
side = Meta.Side.BOTTOM;
else
continue;
} else if (y1 <= primary.y && y2 >= primary.y + primary.height) {
if (x1 <= 0)
side = Meta.Side.LEFT;
else if (x2 >= global.screen_width)
side = Meta.Side.RIGHT;
else
continue;
} else if (x1 <= 0)
side = Meta.Side.LEFT;
else if (y1 <= 0)
side = Meta.Side.TOP;
else if (x2 >= global.screen_width)
side = Meta.Side.RIGHT;
else if (y2 >= global.screen_height)
side = Meta.Side.BOTTOM;
else
continue;
// Ensure that the strut rects goes all the way to the screen edge,
// as this really what mutter expects.
switch (side) {
case Meta.Side.TOP:
y1 = 0;
break;
case Meta.Side.BOTTOM:
y2 = global.screen_height;
break;
case Meta.Side.LEFT:
x1 = 0;
break;
case Meta.Side.RIGHT:
x2 = global.screen_width;
break;
}
let strutRect = new Meta.Rectangle({ x: x1, y: y1, width: x2 - x1, height: y2 - y1});
let strut = new Meta.Strut({ rect: strutRect, side: side });
struts.push(strut);
}
global.set_stage_input_region(rects);
let screen = global.screen;
for (let w = 0; w < screen.n_workspaces; w++) {
let workspace = screen.get_workspace_by_index(w);
workspace.set_builtin_struts(struts);
}
return false;
}
};
Signals.addSignalMethods(Chrome.prototype);

179
js/ui/contactDisplay.js Normal file
View File

@ -0,0 +1,179 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Folks = imports.gi.Folks
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Util = imports.misc.util;
const IconGrid = imports.ui.iconGrid;
const Search = imports.ui.search;
const SearchDisplay = imports.ui.searchDisplay;
const MAX_SEARCH_RESULTS_ROWS = 1;
const ICON_SIZE = 81;
function launchContact(id) {
Util.spawn(['gnome-contacts', '-i', id]);
}
/* This class represents a shown contact search result in the overview */
function Contact(id) {
this._init(id);
}
Contact.prototype = {
_init: function(id) {
this.individual = Shell.ContactSystem.get_default().get_individual(id);
this.actor = new St.Bin({ style_class: 'contact',
reactive: true,
track_hover: true });
let content = new St.BoxLayout( { style_class: 'contact-content',
vertical: false });
this.actor.set_child(content);
let icon = new St.Icon({ icon_type: St.IconType.FULLCOLOR,
icon_size: ICON_SIZE,
style_class: 'contact-icon' });
if (this.individual.avatar != null)
icon.gicon = this.individual.avatar;
else
icon.icon_name = 'avatar-default';
content.add(icon, { x_fill: true,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
let details = new St.BoxLayout({ style_class: 'contact-details',
vertical: true });
content.add(details, { x_fill: true,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
let aliasText = this.individual.alias || _("Unknown");
let aliasLabel = new St.Label({ text: aliasText,
style_class: 'contact-details-alias' });
details.add(aliasLabel, { x_fill: true,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
let presence = this._createPresence(this.individual.presence_type);
details.add(presence, { x_fill: false,
y_fill: true,
x_align: St.Align.START,
y_align: St.Align.END });
},
_createPresence: function(presence) {
let text;
let iconName;
switch(presence) {
case Folks.PresenceType.AVAILABLE:
text = _("Available");
iconName = 'user-available';
break;
case Folks.PresenceType.AWAY:
case Folks.PresenceType.EXTENDED_AWAY:
text = _("Away");
iconName = 'user-away';
break;
case Folks.PresenceType.BUSY:
text = _("Busy");
iconName = 'user-busy';
break;
default:
text = _("Offline");
iconName = 'user-offline';
}
let icon = new St.Icon({ icon_name: iconName,
icon_type: St.IconType.FULLCOLOR,
icon_size: 16,
style_class: 'contact-details-status-icon' });
let label = new St.Label({ text: text });
let box = new St.BoxLayout({ vertical: false,
style_class: 'contact-details-status' });
box.add(icon, { x_fill: true,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
box.add(label, { x_fill: true,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.START });
return box;
},
createIcon: function(size) {
let tc = St.TextureCache.get_default();
let icon = this.individual.avatar;
if (icon != null) {
return tc.load_gicon(null, icon, size);
} else {
return tc.load_icon_name(null, 'avatar-default', St.IconType.FULLCOLOR, size);
}
},
};
/* Searches for and returns contacts */
function ContactSearchProvider() {
this._init();
}
ContactSearchProvider.prototype = {
__proto__: Search.SearchProvider.prototype,
_init: function() {
Search.SearchProvider.prototype._init.call(this, _("CONTACTS"));
this._contactSys = Shell.ContactSystem.get_default();
},
getResultMeta: function(id) {
let contact = new Contact(id);
return { 'id': id,
'name': contact.alias,
'createIcon': function(size) {
return contact.createIcon(size);
}
};
},
getInitialResultSet: function(terms) {
return this._contactSys.initial_search(terms);
},
getSubsearchResultSet: function(previousResults, terms) {
return this._contactSys.subsearch(previousResults, terms);
},
createResultActor: function(resultMeta, terms) {
let contact = new Contact(resultMeta.id);
return contact.actor;
},
createResultContainerActor: function() {
let grid = new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
xAlign: St.Align.START });
grid.actor.style_class = 'contact-grid';
let actor = new SearchDisplay.GridSearchResults(this, grid);
return actor;
},
activateResult: function(id, params) {
launchContact(id);
}
};

View File

@ -207,7 +207,7 @@ RemoveFavoriteIcon.prototype = {
let app = null;
if (source instanceof AppDisplay.AppWellIcon) {
let appSystem = Shell.AppSystem.get_default();
app = appSystem.get_app(source.getId());
app = appSystem.lookup_app(source.getId());
} else if (source.metaWindow) {
let tracker = Shell.WindowTracker.get_default();
app = tracker.get_window_app(source.metaWindow);
@ -330,7 +330,7 @@ Dash.prototype = {
_onDragMotion: function(dragEvent) {
let app = null;
if (dragEvent.source instanceof AppDisplay.AppWellIcon)
app = this._appSystem.get_app(dragEvent.source.getId());
app = this._appSystem.lookup_app(dragEvent.source.getId());
else if (dragEvent.source.metaWindow)
app = this._tracker.get_window_app(dragEvent.source.metaWindow);
else
@ -437,6 +437,7 @@ Dash.prototype = {
let oldIconSize = this.iconSize;
this.iconSize = newIconSize;
this.emit('icon-size-changed');
let scale = oldIconSize / newIconSize;
for (let i = 0; i < iconChildren.length; i++) {
@ -619,12 +620,12 @@ Dash.prototype = {
handleDragOver : function(source, actor, x, y, time) {
let app = null;
if (source instanceof AppDisplay.AppWellIcon)
app = this._appSystem.get_app(source.getId());
app = this._appSystem.lookup_app(source.getId());
else if (source.metaWindow)
app = this._tracker.get_window_app(source.metaWindow);
// Don't allow favoriting of transient apps
if (app == null || app.is_transient())
if (app == null || app.is_window_backed())
return DND.DragMotionResult.NO_DROP;
let favorites = AppFavorites.getAppFavorites().getFavorites();
@ -704,13 +705,13 @@ Dash.prototype = {
acceptDrop : function(source, actor, x, y, time) {
let app = null;
if (source instanceof AppDisplay.AppWellIcon) {
app = this._appSystem.get_app(source.getId());
app = this._appSystem.lookup_app(source.getId());
} else if (source.metaWindow) {
app = this._tracker.get_window_app(source.metaWindow);
}
// Don't allow favoriting of transient apps
if (app == null || app.is_transient()) {
if (app == null || app.is_window_backed()) {
return false;
}

View File

@ -9,11 +9,13 @@ const Clutter = imports.gi.Clutter;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Params = imports.misc.params;
const Util = imports.misc.util;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Calendar = imports.ui.calendar;
const UPowerGlib = imports.gi.UPowerGlib;
// in org.gnome.desktop.interface
const CLOCK_FORMAT_KEY = 'clock-format';
@ -39,19 +41,19 @@ function _onVertSepRepaint (area)
};
function DateMenuButton() {
this._init();
this._init.apply(this, arguments);
}
DateMenuButton.prototype = {
__proto__: PanelMenu.Button.prototype,
_init: function() {
_init: function(params) {
params = Params.parse(params, { showEvents: true });
let item;
let hbox;
let vbox;
this._eventSource = new Calendar.DBusEventSource();
let menuAlignment = 0.25;
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
menuAlignment = 1.0 - menuAlignment;
@ -60,7 +62,7 @@ DateMenuButton.prototype = {
this._clock = new St.Label();
this.actor.set_child(this._clock);
hbox = new St.BoxLayout({name: 'calendarArea'});
hbox = new St.BoxLayout({name: 'calendarArea' });
this.menu.addActor(hbox);
// Fill up the first column
@ -73,43 +75,58 @@ DateMenuButton.prototype = {
this._date.style_class = 'datemenu-date-label';
vbox.add(this._date);
this._eventList = new Calendar.EventsList(this._eventSource);
if (params.showEvents) {
this._eventSource = new Calendar.DBusEventSource();
this._eventList = new Calendar.EventsList(this._eventSource);
} else {
this._eventSource = null;
this._eventList = null;
}
// Calendar
this._calendar = new Calendar.Calendar(this._eventSource);
this._calendar.connect('selected-date-changed',
Lang.bind(this, function(calendar, date) {
// we know this._eventList is defined here, because selected-data-changed
// only gets emitted when the user clicks a date in the calendar,
// and the calender makes those dates unclickable when instantiated with
// a null event source
this._eventList.setDate(date);
}));
vbox.add(this._calendar.actor);
item = new PopupMenu.PopupSeparatorMenuItem();
item.setColumnWidths(1);
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
item = new PopupMenu.PopupMenuItem(_("Date and Time Settings"));
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
item.actor.can_focus = false;
vbox.add(item.actor);
item = this.menu.addSettingsAction(_("Date and Time Settings"), 'gnome-datetime-panel.desktop');
if (item) {
let separator = new PopupMenu.PopupSeparatorMenuItem();
separator.setColumnWidths(1);
vbox.add(separator.actor, {y_align: St.Align.END, expand: true, y_fill: false});
// Add vertical separator
item.actor.can_focus = false;
item.actor.reparent(vbox);
}
item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
pseudo_class: 'highlighted' });
item.connect('repaint', Lang.bind(this, _onVertSepRepaint));
hbox.add(item);
if (params.showEvents) {
// Add vertical separator
// Fill up the second column
item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
pseudo_class: 'highlighted' });
item.connect('repaint', Lang.bind(this, _onVertSepRepaint));
hbox.add(item);
vbox = new St.BoxLayout({vertical: true});
hbox.add(vbox, { expand: true });
// Fill up the second column
vbox = new St.BoxLayout({name: 'calendarEventsArea',
vertical: true});
hbox.add(vbox, { expand: true });
// Event list
vbox.add(this._eventList.actor, { expand: true });
// Event list
vbox.add(this._eventList.actor, { expand: true });
item = new PopupMenu.PopupMenuItem(_("Open Calendar"));
item.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
item.actor.can_focus = false;
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
item = new PopupMenu.PopupMenuItem(_("Open Calendar"));
item.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
item.actor.can_focus = false;
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
}
// Whenever the menu is opened, select today
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
@ -142,6 +159,10 @@ DateMenuButton.prototype = {
this._desktopSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
this._clockSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
// https://bugzilla.gnome.org/show_bug.cgi?id=655129
this._upClient = new UPowerGlib.Client();
this._upClient.connect('notify-resume', Lang.bind(this, this._updateClockAndDate));
// Start the clock
this._updateClockAndDate();
},
@ -157,12 +178,12 @@ DateMenuButton.prototype = {
switch (format) {
case '24h':
if (showDate)
/* Translators: This is the time format with date used
/* Translators: This is the time format with date used
in 24-hour mode. */
clockFormat = showSeconds ? _("%a %b %e, %R:%S")
: _("%a %b %e, %R");
else
/* Translators: This is the time format without date used
/* Translators: This is the time format without date used
in 24-hour mode. */
clockFormat = showSeconds ? _("%a %R:%S")
: _("%a %R");
@ -170,12 +191,12 @@ DateMenuButton.prototype = {
case '12h':
default:
if (showDate)
/* Translators: This is a time format with date used
/* Translators: This is a time format with date used
for AM/PM. */
clockFormat = showSeconds ? _("%a %b %e, %l:%M:%S %p")
: _("%a %b %e, %l:%M %p");
else
/* Translators: This is a time format without date used
/* Translators: This is a time format without date used
for AM/PM. */
clockFormat = showSeconds ? _("%a %l:%M:%S %p")
: _("%a %l:%M %p");
@ -196,16 +217,26 @@ DateMenuButton.prototype = {
return false;
},
_onPreferencesActivate: function() {
this.menu.close();
Main.overview.hide();
let app = Shell.AppSystem.get_default().get_app('gnome-datetime-panel.desktop');
app.activate(-1);
},
_onOpenCalendarActivate: function() {
this.menu.close();
// TODO: pass the selected day
Util.spawn(['evolution', '-c', 'calendar']);
let calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' });
let tool = calendarSettings.get_string('exec');
if (tool.length == 0 || tool == 'evolution') {
// TODO: pass the selected day
Util.spawn(['evolution', '-c', 'calendar']);
} else {
let needTerm = calendarSettings.get_boolean('needs-term');
if (needTerm) {
let terminalSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.terminal' });
let term = terminalSettings.get_string('exec');
let arg = terminalSettings.get_string('exec-arg');
if (arg != '')
Util.spawn([term, arg, tool]);
else
Util.spawn([term, tool]);
} else {
Util.spawnCommandLine(tool)
}
}
}
};

View File

@ -30,11 +30,11 @@ DocSearchProvider.prototype = {
},
activateResult: function(id, params) {
params = Params.parse(params, { workspace: null,
timestamp: null });
params = Params.parse(params, { workspace: -1,
timestamp: 0 });
let docInfo = this._docManager.lookupByUri(id);
docInfo.launch(params.workspace ? params.workspace.index() : -1);
docInfo.launch(params.workspace);
},
getInitialResultSet: function(terms) {

View File

@ -22,8 +22,8 @@ const DBus = imports.dbus;
const Lang = imports.lang;
const Signals = imports.signals;
const AccountsService = imports.gi.AccountsService;
const Clutter = imports.gi.Clutter;
const Gdm = imports.gi.Gdm;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Pango = imports.gi.Pango;
@ -113,7 +113,7 @@ function findAppFromInhibitor(inhibitor) {
let app = null;
for (let i = 0; i < candidateDesktopFiles.length; i++) {
try {
app = appSystem.get_app(candidateDesktopFiles[i]);
app = appSystem.lookup_app(candidateDesktopFiles[i]);
if (app)
break;
@ -173,7 +173,7 @@ ListItem.prototype = {
_onClicked: function() {
this.emit('activate');
this._app.activate(-1);
this._app.activate();
}
};
Signals.addSignalMethods(ListItem.prototype);
@ -237,7 +237,7 @@ EndSessionDialog.prototype = {
_init: function() {
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'end-session-dialog' });
this._user = Gdm.UserManager.ref_default().get_user(GLib.get_user_name());
this._user = AccountsService.UserManager.get_default().get_user(GLib.get_user_name());
this._secondsLeft = 0;
this._totalSecondsToStayOpen = 0;

View File

@ -1,9 +1,13 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Lang = imports.lang;
const Signals = imports.signals;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Soup = imports.gi.Soup;
const Config = imports.misc.config;
@ -11,7 +15,12 @@ const ExtensionState = {
ENABLED: 1,
DISABLED: 2,
ERROR: 3,
OUT_OF_DATE: 4
OUT_OF_DATE: 4,
DOWNLOADING: 5,
// Used as an error state for operations on unknown extensions,
// should never be in a real extensionMeta object.
UNINSTALLED: 99
};
const ExtensionType = {
@ -19,16 +28,40 @@ const ExtensionType = {
PER_USER: 2
};
const _httpSession = new Soup.SessionAsync();
// The unfortunate state of gjs, gobject-introspection and libsoup
// means that I have to do a hack to add a feature.
// See: https://bugzilla.gnome.org/show_bug.cgi?id=655189 for context.
if (Soup.Session.prototype.add_feature != null)
Soup.Session.prototype.add_feature.call(_httpSession, new Soup.ProxyResolverDefault());
// Maps uuid -> metadata object
const extensionMeta = {};
// Maps uuid -> importer object (extension directory tree)
const extensions = {};
// Maps uuid -> extension state object (returned from init())
const extensionStateObjs = {};
// Arrays of uuids
var disabledExtensions;
var enabledExtensions;
// GFile for user extensions
var userExtensionsDir = null;
// We don't really have a class to add signals on. So, create
// a simple dummy object, add the signal methods, and export those
// publically.
var _signals = {};
Signals.addSignalMethods(_signals);
const connect = Lang.bind(_signals, _signals.connect);
const disconnect = Lang.bind(_signals, _signals.disconnect);
// UUID => Array of error messages
var errors = {};
const ENABLED_EXTENSIONS_KEY = 'enabled-extensions';
/**
* versionCheck:
* @required: an array of versions we're compatible with
@ -59,13 +92,150 @@ function versionCheck(required, current) {
return false;
}
function installExtensionFromManifestURL(uuid, url) {
_httpSession.queue_message(
Soup.Message.new('GET', url),
function(session, message) {
if (message.status_code != Soup.KnownStatusCode.OK) {
logExtensionError(uuid, 'downloading manifest: ' + message.status_code.toString());
return;
}
let manifest;
try {
manifest = JSON.parse(message.response_body.data);
} catch (e) {
logExtensionError(uuid, 'parsing: ' + e.toString());
return;
}
if (uuid != manifest['uuid']) {
logExtensionError(uuid, 'manifest: manifest uuids do not match');
return;
}
let meta = extensionMeta[uuid] = { uuid: uuid,
state: ExtensionState.DOWNLOADING,
error: '' };
_signals.emit('extension-state-changed', meta);
installExtensionFromManifest(manifest, meta);
});
}
function installExtensionFromManifest(manifest, meta) {
let uuid = manifest['uuid'];
let name = manifest['name'];
if (!versionCheck(manifest['shell-version'], Config.PACKAGE_VERSION)) {
meta.state = ExtensionState.OUT_OF_DATE;
logExtensionError(uuid, 'version: ' + name + ' is not compatible with current GNOME Shell version', meta.state);
return;
}
let url = manifest['__installer'];
_httpSession.queue_message(Soup.Message.new('GET', url),
function(session, message) {
gotExtensionZipFile(session, message, uuid);
});
}
function gotExtensionZipFile(session, message, uuid) {
if (message.status_code != Soup.KnownStatusCode.OK) {
logExtensionError(uuid, 'downloading extension: ' + message.status_code);
return;
}
// FIXME: use a GFile mkstemp-type method once one exists
let fd, tmpzip;
try {
[fd, tmpzip] = GLib.file_open_tmp('XXXXXX.shell-extension.zip');
} catch (e) {
logExtensionError(uuid, 'tempfile: ' + e.toString());
return;
}
let stream = new Gio.UnixOutputStream({ fd: fd });
let dir = userExtensionsDir.get_child(uuid);
Shell.write_soup_message_to_stream(stream, message);
stream.close(null);
let [success, pid] = GLib.spawn_async(null,
['unzip', '-uod', dir.get_path(), '--', tmpzip],
null,
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
null);
if (!success) {
logExtensionError(uuid, 'extract: could not extract');
return;
}
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function(pid, status) {
GLib.spawn_close_pid(pid);
loadExtension(dir, true, ExtensionType.PER_USER);
});
}
function disableExtension(uuid) {
let meta = extensionMeta[uuid];
if (!meta)
return;
if (meta.state != ExtensionState.ENABLED)
return;
let extensionState = extensionStateObjs[uuid];
try {
extensionState.disable();
} catch(e) {
logExtensionError(uuid, e.toString());
return;
}
meta.state = ExtensionState.DISABLED;
_signals.emit('extension-state-changed', meta);
}
function enableExtension(uuid) {
let meta = extensionMeta[uuid];
if (!meta)
return;
if (meta.state != ExtensionState.DISABLED)
return;
let extensionState = extensionStateObjs[uuid];
try {
extensionState.enable();
} catch(e) {
logExtensionError(uuid, e.toString());
return;
}
meta.state = ExtensionState.ENABLED;
_signals.emit('extension-state-changed', meta);
}
function logExtensionError(uuid, message, state) {
if (!errors[uuid]) errors[uuid] = [];
errors[uuid].push(message);
global.logError('Extension "%s" had error: %s'.format(uuid, message));
state = state || ExtensionState.ERROR;
_signals.emit('extension-state-changed', { uuid: uuid,
error: message,
state: state });
}
function loadExtension(dir, enabled, type) {
let info;
let baseErrorString = 'While loading extension from "' + dir.get_parse_name() + '": ';
let uuid = dir.get_basename();
let metadataFile = dir.get_child('metadata.json');
if (!metadataFile.query_exists(null)) {
global.logError(baseErrorString + 'Missing metadata.json');
logExtensionError(uuid, 'Missing metadata.json');
return;
}
@ -73,61 +243,65 @@ function loadExtension(dir, enabled, type) {
try {
metadataContents = Shell.get_file_contents_utf8_sync(metadataFile.get_path());
} catch (e) {
global.logError(baseErrorString + 'Failed to load metadata.json: ' + e);
logExtensionError(uuid, 'Failed to load metadata.json: ' + e);
return;
}
let meta;
try {
meta = JSON.parse(metadataContents);
} catch (e) {
global.logError(baseErrorString + 'Failed to parse metadata.json: ' + e);
logExtensionError(uuid, 'Failed to parse metadata.json: ' + e);
return;
}
let requiredProperties = ['uuid', 'name', 'description', 'shell-version'];
for (let i = 0; i < requiredProperties.length; i++) {
let prop = requiredProperties[i];
if (!meta[prop]) {
global.logError(baseErrorString + 'missing "' + prop + '" property in metadata.json');
logExtensionError(uuid, 'missing "' + prop + '" property in metadata.json');
return;
}
}
if (extensions[meta.uuid] != undefined) {
global.logError(baseErrorString + "extension already loaded");
if (extensions[uuid] != undefined) {
logExtensionError(uuid, "extension already loaded");
return;
}
// Encourage people to add this
if (!meta['url']) {
global.log(baseErrorString + 'Warning: Missing "url" property in metadata.json');
global.log('Warning: Missing "url" property in metadata.json');
}
let base = dir.get_basename();
if (base != meta.uuid) {
global.logError(baseErrorString + 'uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + base + '"');
if (uuid != meta.uuid) {
logExtensionError(uuid, 'uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + uuid + '"');
return;
}
if (!versionCheck(meta['shell-version'], Config.PACKAGE_VERSION) ||
(meta['js-version'] && !versionCheck(meta['js-version'], Config.GJS_VERSION))) {
global.logError(baseErrorString + 'extension is not compatible with current GNOME Shell and/or GJS version');
logExtensionError(uuid, 'extension is not compatible with current GNOME Shell and/or GJS version');
return;
}
extensionMeta[meta.uuid] = meta;
extensionMeta[meta.uuid].type = type;
extensionMeta[meta.uuid].path = dir.get_path();
if (!enabled) {
extensionMeta[meta.uuid].state = ExtensionState.DISABLED;
return;
}
extensionMeta[uuid] = meta;
meta.type = type;
meta.path = dir.get_path();
meta.error = '';
// Default to error, we set success as the last step
extensionMeta[meta.uuid].state = ExtensionState.ERROR;
meta.state = ExtensionState.ERROR;
if (!versionCheck(meta['shell-version'], Config.PACKAGE_VERSION) ||
(meta['js-version'] && !versionCheck(meta['js-version'], Config.GJS_VERSION))) {
logExtensionError(uuid, 'extension is not compatible with current GNOME Shell and/or GJS version', ExtensionState.OUT_OF_DATE);
meta.state = ExtensionState.OUT_OF_DATE;
return;
}
let extensionJs = dir.get_child('extension.js');
if (!extensionJs.query_exists(null)) {
global.logError(baseErrorString + 'Missing extension.js');
logExtensionError(uuid, 'Missing extension.js');
return;
}
let stylesheetPath = null;
@ -138,48 +312,94 @@ function loadExtension(dir, enabled, type) {
try {
theme.load_stylesheet(stylesheetFile.get_path());
} catch (e) {
global.logError(baseErrorString + 'Stylesheet parse error: ' + e);
logExtensionError(uuid, 'Stylesheet parse error: ' + e);
return;
}
}
let extensionModule;
let extensionState = null;
try {
global.add_extension_importer('imports.ui.extensionSystem.extensions', meta.uuid, dir.get_path());
extensionModule = extensions[meta.uuid].extension;
} catch (e) {
if (stylesheetPath != null)
theme.unload_stylesheet(stylesheetPath);
global.logError(baseErrorString + e);
logExtensionError(uuid, e);
return;
}
if (!extensionModule.main) {
global.logError(baseErrorString + 'missing \'main\' function');
if (!extensionModule.init) {
logExtensionError(uuid, 'missing \'init\' function');
return;
}
try {
extensionModule.main(meta);
extensionState = extensionModule.init(meta);
} catch (e) {
if (stylesheetPath != null)
theme.unload_stylesheet(stylesheetPath);
global.logError(baseErrorString + 'Failed to evaluate main function:' + e);
logExtensionError(uuid, 'Failed to evaluate init function:' + e);
return;
}
extensionMeta[meta.uuid].state = ExtensionState.ENABLED;
if (!extensionState)
extensionState = extensionModule;
extensionStateObjs[uuid] = extensionState;
if (!extensionState.enable) {
logExtensionError(uuid, 'missing \'enable\' function');
return;
}
if (!extensionState.disable) {
logExtensionError(uuid, 'missing \'disable\' function');
return;
}
meta.state = ExtensionState.DISABLED;
if (enabled)
enableExtension(uuid);
_signals.emit('extension-loaded', meta.uuid);
_signals.emit('extension-state-changed', meta);
global.log('Loaded extension ' + meta.uuid);
}
function onEnabledExtensionsChanged() {
let newEnabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
// Find and enable all the newly enabled extensions: UUIDs found in the
// new setting, but not in the old one.
newEnabledExtensions.filter(function(uuid) {
return enabledExtensions.indexOf(uuid) == -1;
}).forEach(function(uuid) {
enableExtension(uuid);
});
// Find and disable all the newly disabled extensions: UUIDs found in the
// old setting, but not in the new one.
enabledExtensions.filter(function(item) {
return newEnabledExtensions.indexOf(item) == -1;
}).forEach(function(uuid) {
disableExtension(uuid);
});
enabledExtensions = newEnabledExtensions;
}
function init() {
let userExtensionsPath = GLib.build_filenamev([global.userdatadir, 'extensions']);
userExtensionsDir = Gio.file_new_for_path(userExtensionsPath);
try {
userExtensionsDir.make_directory_with_parents(null);
if (!userExtensionsDir.query_exists(null))
userExtensionsDir.make_directory_with_parents(null);
} catch (e) {
global.logError('' + e);
}
disabledExtensions = global.settings.get_strv('disabled-extensions', -1);
enabledExtensions = global.settings.get_strv('enabled-extensions', -1);
global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged);
enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
}
function _loadExtensionsIn(dir, type) {
@ -197,11 +417,8 @@ function _loadExtensionsIn(dir, type) {
if (fileType != Gio.FileType.DIRECTORY)
continue;
let name = info.get_name();
// Enable all but disabled extensions if enabledExtensions is not set.
// If it is set, enable one those, except they are disabled as well.
let enabled = (enabledExtensions.length == 0 || enabledExtensions.indexOf(name) >= 0)
&& disabledExtensions.indexOf(name) < 0;
let child = dir.get_child(name);
let enabled = enabledExtensions.indexOf(name) != -1;
loadExtension(child, enabled, type);
}
fileEnum.close(null);

532
js/ui/keyboard.js Normal file
View File

@ -0,0 +1,532 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Caribou = imports.gi.Caribou;
const Clutter = imports.gi.Clutter;
const DBus = imports.dbus;
const Gdk = imports.gi.Gdk;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const BoxPointer = imports.ui.boxpointer;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const KEYBOARD_SCHEMA = 'org.gnome.shell.keyboard';
const SHOW_KEYBOARD = 'show-keyboard';
const KEYBOARD_TYPE = 'keyboard-type';
// Key constants taken from Antler
// FIXME: ought to be moved into libcaribou
const PRETTY_KEYS = {
'BackSpace': '\u232b',
'space': ' ',
'Return': '\u23ce',
'Caribou_Prefs': '\u2328',
'Caribou_ShiftUp': '\u2b06',
'Caribou_ShiftDown': '\u2b07',
'Caribou_Emoticons': '\u263a',
'Caribou_Symbols': '123',
'Caribou_Symbols_More': '{#*',
'Caribou_Alpha': 'Abc',
'Tab': 'Tab',
'Escape': 'Esc',
'Control_L': 'Ctrl',
'Alt_L': 'Alt'
};
const CaribouKeyboardIface = {
name: 'org.gnome.Caribou.Keyboard',
methods: [ { name: 'Show',
inSignature: 'u',
outSignature: ''
},
{ name: 'Hide',
inSignature: 'u',
outSignature: ''
},
{ name: 'SetCursorLocation',
inSignature: 'iiii',
outSignature: ''
},
{ name: 'SetEntryLocation',
inSignature: 'iiii',
outSignature: ''
} ],
properties: [ { name: 'Name',
signature: 's',
access: 'read' } ]
};
function Key() {
this._init.apply(this, arguments);
}
Key.prototype = {
_init : function(key) {
this._key = key;
this.actor = this._makeKey();
this._extended_keys = this._key.get_extended_keys();
this._extended_keyboard = null;
if (this._key.name == "Control_L" || this._key.name == "Alt_L")
this._key.latch = true;
this._key.connect('key-pressed', Lang.bind(this, function ()
{ this.actor.checked = true }));
this._key.connect('key-released', Lang.bind(this, function ()
{ this.actor.checked = false; }));
if (this._extended_keys.length > 0) {
this._grabbed = false;
this._eventCaptureId = 0;
this._key.connect('notify::show-subkeys', Lang.bind(this, this._onShowSubkeysChanged));
this._boxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
{ x_fill: true,
y_fill: true,
x_align: St.Align.START });
// Adds style to existing keyboard style to avoid repetition
this._boxPointer.actor.add_style_class_name('keyboard-subkeys');
this._getExtendedKeys();
this.actor._extended_keys = this._extended_keyboard;
this._boxPointer.actor.hide();
Main.layoutManager.addChrome(this._boxPointer.actor, { visibleInFullscreen: true });
}
},
_makeKey: function () {
let label = this._key.name;
if (label.length > 1) {
let pretty = PRETTY_KEYS[label];
if (pretty)
label = pretty;
else
label = this._getUnichar(this._key);
}
label = GLib.markup_escape_text(label, -1);
let button = new St.Button ({ label: label,
style_class: 'keyboard-key' });
button.key_width = this._key.width;
button.connect('button-press-event', Lang.bind(this, function () { this._key.press(); }));
button.connect('button-release-event', Lang.bind(this, function () { this._key.release(); }));
return button;
},
_getUnichar: function(key) {
let keyval = key.keyval;
let unichar = Gdk.keyval_to_unicode(keyval);
if (unichar) {
return String.fromCharCode(unichar);
} else {
return key.name;
}
},
_getExtendedKeys: function () {
this._extended_keyboard = new St.BoxLayout({ style_class: 'keyboard-layout',
vertical: false });
for (let i = 0; i < this._extended_keys.length; ++i) {
let extended_key = this._extended_keys[i];
let label = this._getUnichar(extended_key);
let key = new St.Button({ label: label, style_class: 'keyboard-key' });
key.extended_key = extended_key;
key.connect('button-press-event', Lang.bind(this, function () { extended_key.press(); }));
key.connect('button-release-event', Lang.bind(this, function () { extended_key.release(); }));
this._extended_keyboard.add(key);
}
this._boxPointer.bin.add_actor(this._extended_keyboard);
},
_onEventCapture: function (actor, event) {
let source = event.get_source();
let type = event.type();
if ((type == Clutter.EventType.BUTTON_PRESS ||
type == Clutter.EventType.BUTTON_RELEASE) &&
this._extended_keyboard.contains(source)) {
source.extended_key.press();
source.extended_key.release();
return false;
}
if (type == Clutter.EventType.BUTTON_PRESS) {
this._boxPointer.actor.hide();
this._ungrab();
return true;
}
return false;
},
_ungrab: function () {
global.stage.disconnect(this._eventCaptureId);
this._eventCaptureId = 0;
this._grabbed = false;
Main.popModal(this.actor);
},
_onShowSubkeysChanged: function () {
if (this._key.show_subkeys) {
this.actor.fake_release();
this._boxPointer.actor.raise_top();
this._boxPointer.setPosition(this.actor, 0.5);
this._boxPointer.show(true);
this.actor.set_hover(false);
if (!this._grabbed) {
Main.pushModal(this.actor);
this._eventCaptureId = global.stage.connect('captured-event', Lang.bind(this, this._onEventCapture));
this._grabbed = true;
}
this._key.release();
} else {
if (this._grabbed)
this._ungrab();
this._boxPointer.hide(true);
}
}
};
function Keyboard() {
this._init.apply(this, arguments);
}
Keyboard.prototype = {
_init: function () {
DBus.session.exportObject('/org/gnome/Caribou/Keyboard', this);
DBus.session.acquire_name('org.gnome.Caribou.Keyboard', 0, null, null);
this._timestamp = global.get_current_time();
this.actor = new St.BoxLayout({ name: 'keyboard', vertical: true, reactive: true });
Main.layoutManager.keyboardBox.add_actor(this.actor);
Main.layoutManager.trackChrome(this.actor);
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
this._keyboardSettings = new Gio.Settings({ schema: KEYBOARD_SCHEMA });
this._keyboardSettings.connect('changed', Lang.bind(this, this._settingsChanged));
this._settingsChanged();
},
init: function () {
if (this._enableKeyboard)
this._redraw();
},
_settingsChanged: function () {
this._enableKeyboard = this._keyboardSettings.get_boolean(SHOW_KEYBOARD);
if (!this._enableKeyboard && !this._keyboard)
return;
if (this._enableKeyboard && this._keyboard &&
this._keyboard.keyboard_type == this._keyboardSettings.get_string(KEYBOARD_TYPE))
return;
if (this._keyboard)
this._destroyKeyboard();
if (this._enableKeyboard)
this._setupKeyboard();
else
Main.layoutManager.hideKeyboard(true);
},
_destroyKeyboard: function() {
if (this._keyboardNotifyId)
this._keyboard.disconnect(this._keyboardNotifyId);
if (this._focusNotifyId)
global.stage.disconnect(this._focusNotifyId);
this._keyboard = null;
this.actor.destroy_children();
this._destroySource();
},
_setupKeyboard: function() {
this._keyboard = new Caribou.KeyboardModel({ keyboard_type: this._keyboardSettings.get_string(KEYBOARD_TYPE) });
this._groups = {};
this._current_page = null;
// Initialize keyboard key measurements
this._numOfHorizKeys = 0;
this._numOfVertKeys = 0;
this._addKeys();
this._keyboardNotifyId = this._keyboard.connect('notify::active-group', Lang.bind(this, this._onGroupChanged));
this._focusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
this._createSource();
},
_onKeyFocusChanged: function () {
let focus = global.stage.key_focus;
if (focus == global.stage || focus == null)
return;
if (focus instanceof Clutter.Text)
this.show();
else {
if (focus._extended_keys == null)
this.hide();
}
},
_addKeys: function () {
let groups = this._keyboard.get_groups();
for (let i = 0; i < groups.length; ++i) {
let gname = groups[i];
let group = this._keyboard.get_group(gname);
group.connect('notify::active-level', Lang.bind(this, this._onLevelChanged));
let layers = {};
let levels = group.get_levels();
for (let j = 0; j < levels.length; ++j) {
let lname = levels[j];
let level = group.get_level(lname);
let layout = new St.BoxLayout({ style_class: 'keyboard-layout',
vertical: true });
this._loadRows(level, layout);
layers[lname] = layout;
this.actor.add(layout, { x_fill: false });
layout.hide();
}
this._groups[gname] = layers;
}
this._setActiveLayer();
},
_getTrayIcon: function () {
let trayButton = new St.Button ({ label: "tray", style_class: 'keyboard-key' });
trayButton.key_width = 1;
trayButton.connect('button-press-event', Lang.bind(this, function () {
Main.messageTray.toggle();
}));
Main.overview.connect('showing', Lang.bind(this, function () {
trayButton.reactive = false;
trayButton.add_style_pseudo_class('grayed');
}));
Main.overview.connect('hiding', Lang.bind(this, function () {
trayButton.reactive = true;
trayButton.remove_style_pseudo_class('grayed');
}));
return trayButton;
},
_addRows : function (keys, layout) {
let keyboard_row = new St.BoxLayout();
for (let i = 0; i < keys.length; ++i) {
let children = keys[i].get_children();
let right_box = new St.BoxLayout({ style_class: 'keyboard-row' });
let left_box = new St.BoxLayout({ style_class: 'keyboard-row' });
for (let j = 0; j < children.length; ++j) {
if (this._numOfHorizKeys == 0)
this._numOfHorizKeys = children.length;
let key = children[j];
let button = new Key(key);
if (key.align == 'right')
right_box.add(button.actor);
else
left_box.add(button.actor);
if (key.name == "Caribou_Prefs") {
key.connect('key-released', Lang.bind(this, this.hide));
// Add new key for hiding message tray
right_box.add(this._getTrayIcon());
}
}
keyboard_row.add(left_box, { expand: true, x_fill: false, x_align: St.Align.START });
keyboard_row.add(right_box, { expand: true, x_fill: false, x_align: St.Align.END });
}
layout.add(keyboard_row);
},
_loadRows : function (level, layout) {
let rows = level.get_rows();
for (let i = 0; i < rows.length; ++i) {
let row = rows[i];
if (this._numOfVertKeys == 0)
this._numOfVertKeys = rows.length;
this._addRows(row.get_columns(), layout);
}
},
_redraw: function () {
let monitor = Main.layoutManager.bottomMonitor;
let maxHeight = monitor.height / 3;
this.actor.width = monitor.width;
let layout = this._current_page;
let verticalSpacing = layout.get_theme_node().get_length('spacing');
let padding = layout.get_theme_node().get_length('padding');
let box = layout.get_children()[0].get_children()[0];
let horizontalSpacing = box.get_theme_node().get_length('spacing');
let allHorizontalSpacing = (this._numOfHorizKeys - 1) * horizontalSpacing;
let keyWidth = Math.floor((this.actor.width - allHorizontalSpacing - 2 * padding) / this._numOfHorizKeys);
let allVerticalSpacing = (this._numOfVertKeys - 1) * verticalSpacing;
let keyHeight = Math.floor((maxHeight - allVerticalSpacing - 2 * padding) / this._numOfVertKeys);
let keySize = Math.min(keyWidth, keyHeight);
this.actor.height = keySize * this._numOfVertKeys + allVerticalSpacing + 2 * padding;
let rows = this._current_page.get_children();
for (let i = 0; i < rows.length; ++i) {
let keyboard_row = rows[i];
let boxes = keyboard_row.get_children();
for (let j = 0; j < boxes.length; ++j) {
let keys = boxes[j].get_children();
for (let k = 0; k < keys.length; ++k) {
let child = keys[k];
child.width = keySize * child.key_width;
child.height = keySize;
if (child._extended_keys) {
let extended_keys = child._extended_keys.get_children();
for (let n = 0; n < extended_keys.length; ++n) {
let extended_key = extended_keys[n];
extended_key.width = keySize;
extended_key.height = keySize;
}
}
}
}
}
},
_onLevelChanged: function () {
this._setActiveLayer();
this._redraw();
},
_onGroupChanged: function () {
this._setActiveLayer();
this._redraw();
},
_setActiveLayer: function () {
let active_group_name = this._keyboard.active_group;
let active_group = this._keyboard.get_group(active_group_name);
let active_level = active_group.active_level;
let layers = this._groups[active_group_name];
if (this._current_page != null) {
this._current_page.hide();
}
this._current_page = layers[active_level];
this._current_page.show();
},
_createSource: function () {
if (this._source == null) {
this._source = new KeyboardSource(this);
this._source.setTransient(true);
Main.messageTray.add(this._source);
}
},
_destroySource: function () {
if (this._source) {
this._source.destroy();
this._source = null;
}
},
show: function () {
this._redraw();
Main.layoutManager.showKeyboard();
this._destroySource();
},
hide: function () {
Main.layoutManager.hideKeyboard();
this._createSource();
},
_moveTemporarily: function () {
let currentWindow = global.screen.get_display().focus_window;
let rect = currentWindow.get_outer_rect();
let newX = rect.x;
let newY = 3 * this.actor.height / 2;
currentWindow.move_frame(true, newX, newY);
},
_setLocation: function (x, y) {
if (y >= 2 * this.actor.height)
this._moveTemporarily();
},
// D-Bus methods
Show: function(timestamp) {
if (timestamp - this._timestamp < 0)
return;
this._timestamp = timestamp;
this.show();
},
Hide: function(timestamp) {
if (timestamp - this._timestamp < 0)
return;
this._timestamp = timestamp;
this.hide();
},
SetCursorLocation: function(x, y, w, h) {
this._setLocation(x, y);
},
SetEntryLocation: function(x, y, w, h) {
this._setLocation(x, y);
},
get Name() {
return 'gnome-shell';
}
};
DBus.conformExport(Keyboard.prototype, CaribouKeyboardIface);
function KeyboardSource() {
this._init.apply(this, arguments);
}
KeyboardSource.prototype = {
__proto__: MessageTray.Source.prototype,
_init: function(keyboard) {
this._keyboard = keyboard;
MessageTray.Source.prototype._init.call(this, _("Keyboard"));
this._setSummaryIcon(this.createNotificationIcon());
},
createNotificationIcon: function() {
return new St.Icon({ icon_name: 'input-keyboard',
icon_type: St.IconType.SYMBOLIC,
icon_size: this.ICON_SIZE });
},
handleSummaryClick: function() {
let event = Clutter.get_current_event();
if (event.type() != Clutter.EventType.BUTTON_RELEASE)
return false;
this.open();
return true;
},
open: function() {
this._keyboard.show();
}
};

View File

@ -2,13 +2,20 @@
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
const Main = imports.ui.main;
const Params = imports.misc.params;
const ScreenSaver = imports.misc.screenSaver;
const Tweener = imports.ui.tweener;
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
const STARTUP_ANIMATION_TIME = 0.2;
const KEYBOARD_ANIMATION_TIME = 0.5;
function LayoutManager() {
this._init.apply(this, arguments);
@ -21,16 +28,45 @@ LayoutManager.prototype = {
this.primaryMonitor = null;
this.primaryIndex = -1;
this._hotCorners = [];
this._leftPanelBarrier = 0;
this._rightPanelBarrier = 0;
this._trayBarrier = 0;
this._updateMonitors();
this._chrome = new Chrome(this);
this.panelBox = new St.BoxLayout({ name: 'panelBox',
vertical: true });
this.addChrome(this.panelBox, { affectsStruts: true });
this.panelBox.connect('allocation-changed',
Lang.bind(this, this._updatePanelBarriers));
// bottomBox contains the tray and keyboard (which move
// together, since the tray slides up from the top of the
// keyboard when the keyboard is visible).
this._bottomBox = new St.BoxLayout({ name: 'bottomBox',
vertical: true });
this.addChrome(this._bottomBox, { visibleInFullscreen: true });
this.trayBox = new St.BoxLayout({ name: 'trayBox' });
this.trayBox.connect('allocation-changed',
Lang.bind(this, this._updateTrayBarrier));
this._bottomBox.add_actor(this.trayBox);
this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox' });
this._bottomBox.add_actor(this.keyboardBox);
global.screen.connect('monitors-changed',
Lang.bind(this, this._monitorsChanged));
this._monitorsChanged();
},
// This is called by Main after everything else is constructed;
// _updateHotCorners needs access to Main.panel, which didn't exist
// Chrome.init() needs access to Main.overview, which didn't exist
// yet when the LayoutManager was constructed.
init: function() {
global.screen.connect('monitors-changed', Lang.bind(this, this._monitorsChanged));
this._updateHotCorners();
this._chrome.init();
this._startupAnimation();
},
_updateMonitors: function() {
@ -109,12 +145,69 @@ LayoutManager.prototype = {
let corner = new HotCorner();
this._hotCorners.push(corner);
corner.actor.set_position(cornerX, cornerY);
Main.chrome.addActor(corner.actor, { affectsStruts: false });
this._chrome.addActor(corner.actor);
}
},
_updateBoxes: function() {
this.panelBox.set_position(this.primaryMonitor.x, this.primaryMonitor.y);
this.panelBox.set_size(this.primaryMonitor.width, -1);
this._bottomBox.set_position(this.bottomMonitor.x,
this.bottomMonitor.y + this.bottomMonitor.height);
this._bottomBox.set_size(this.bottomMonitor.width, -1);
this.trayBox.width = this.bottomMonitor.width;
// Set trayBox's clip to show things above it, but not below
// it (so it's not visible behind the keyboard). The exact
// height of the clip doesn't matter, as long as it's taller
// than any Notification.actor.
this.trayBox.set_clip(0, -this.bottomMonitor.height,
this.bottomMonitor.width, this.bottomMonitor.height);
},
_updatePanelBarriers: function() {
if (this._leftPanelBarrier)
global.destroy_pointer_barrier(this._leftPanelBarrier);
if (this._rightPanelBarrier)
global.destroy_pointer_barrier(this._rightPanelBarrier);
if (this.panelBox.height) {
let primary = this.primaryMonitor;
this._leftPanelBarrier =
global.create_pointer_barrier(primary.x, primary.y,
primary.x, primary.y + this.panelBox.height,
1 /* BarrierPositiveX */);
this._rightPanelBarrier =
global.create_pointer_barrier(primary.x + primary.width, primary.y,
primary.x + primary.width, primary.y + this.panelBox.height,
4 /* BarrierNegativeX */);
} else {
this._leftPanelBarrier = 0;
this._rightPanelBarrier = 0;
}
},
_updateTrayBarrier: function() {
let monitor = this.bottomMonitor;
if (this._trayBarrier)
global.destroy_pointer_barrier(this._trayBarrier);
if (Main.messageTray) {
this._trayBarrier =
global.create_pointer_barrier(monitor.x + monitor.width, monitor.y + monitor.height - Main.messageTray.actor.height,
monitor.x + monitor.width, monitor.y + monitor.height,
4 /* BarrierNegativeX */);
} else {
this._trayBarrier = 0;
}
},
_monitorsChanged: function() {
this._updateMonitors();
this._updateBoxes();
this._updateHotCorners();
this.emit('monitors-changed');
@ -135,9 +228,7 @@ LayoutManager.prototype = {
},
get focusIndex() {
let screen = global.screen;
let display = screen.get_display();
let focusWindow = display.focus_window;
let focusWindow = global.display.focus_window;
if (focusWindow) {
let wrect = focusWindow.get_outer_rect();
@ -156,6 +247,105 @@ LayoutManager.prototype = {
get focusMonitor() {
return this.monitors[this.focusIndex];
},
_startupAnimation: function() {
this.panelBox.anchor_y = this.panelBox.height;
Tweener.addTween(this.panelBox,
{ anchor_y: 0,
time: STARTUP_ANIMATION_TIME,
transition: 'easeOutQuad'
});
},
showKeyboard: function () {
Main.messageTray.hide();
Tweener.addTween(this._bottomBox,
{ anchor_y: this._bottomBox.height,
time: KEYBOARD_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: this._showKeyboardComplete,
onCompleteScope: this
});
},
_showKeyboardComplete: function() {
// Poke Chrome to update the input shape; it doesn't notice
// anchor point changes
this._chrome.updateRegions();
this._bottomBox.connect('notify::height', Lang.bind(this, function () {
this._bottomBoxAnchor = this._bottomBox.height;
}));
},
hideKeyboard: function (immediate) {
Main.messageTray.hide();
Tweener.addTween(this._bottomBox,
{ anchor_y: 0,
time: immediate ? 0 : KEYBOARD_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: this._showKeyboardComplete,
onCompleteScope: this
});
},
_hideKeyboardComplete: function() {
this._chrome.updateRegions();
},
// addChrome:
// @actor: an actor to add to the chrome layer
// @params: (optional) additional params
//
// Adds @actor to the chrome layer and (unless %affectsInputRegion
// in @params is %false) extends the input region to include it.
// Changes in @actor's size, position, and visibility will
// automatically result in appropriate changes to the input
// region.
//
// If %affectsStruts in @params is %true (and @actor is along a
// screen edge), then @actor's size and position will also affect
// the window manager struts. Changes to @actor's visibility will
// NOT affect whether or not the strut is present, however.
//
// If %visibleInFullscreen in @params is %true, the actor will be
// visible even when a fullscreen window should be covering it.
addChrome: function(actor, params) {
this._chrome.addActor(actor, params);
},
// trackChrome:
// @actor: a descendant of the chrome to begin tracking
// @params: parameters describing how to track @actor
//
// Tells the chrome to track @actor, which must be a descendant
// of an actor added via addChrome(). This can be used to extend the
// struts or input region to cover specific children.
//
// @params can have any of the same values as in addChrome(),
// though some possibilities don't make sense (eg, trying to have
// a %visibleInFullscreen child of a non-%visibleInFullscreen
// parent). By default, @actor has the same params as its chrome
// ancestor.
trackChrome: function(actor, params) {
this._chrome.trackActor(actor, params);
},
// untrackChrome:
// @actor: an actor previously tracked via trackChrome()
//
// Undoes the effect of trackChrome()
untrackChrome: function(actor) {
this._chrome.untrackActor(actor);
},
// removeChrome:
// @actor: a child of the chrome layer
//
// Removes @actor from the chrome layer
removeChrome: function(actor) {
this._chrome.removeActor(actor);
}
};
Signals.addSignalMethods(LayoutManager.prototype);
@ -218,13 +408,22 @@ HotCorner.prototype = {
Lang.bind(this, this._onCornerClicked));
this._corner.connect('leave-event',
Lang.bind(this, this._onCornerLeft));
// Cache the three ripples instead of dynamically creating and destroying them.
this._ripple1 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0 });
this._ripple2 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0 });
this._ripple3 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0 });
Main.uiGroup.add_actor(this._ripple1);
Main.uiGroup.add_actor(this._ripple2);
Main.uiGroup.add_actor(this._ripple3);
},
destroy: function() {
this.actor.destroy();
},
_addRipple : function(delay, time, startScale, startOpacity, finalScale, finalOpacity) {
_animRipple : function(ripple, delay, time, startScale, startOpacity, finalScale) {
// 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
@ -232,25 +431,27 @@ HotCorner.prototype = {
// '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;
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,
ripple.visible = true;
ripple.opacity = 255 * Math.sqrt(startOpacity);
ripple.scale_x = ripple.scale_y = startScale;
let [x, y] = this._corner.get_transformed_position();
ripple.x = x;
ripple.y = y;
Tweener.addTween(ripple, { _opacity: 0,
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);
onComplete: function() { ripple.visible = false; } });
},
rippleAnimation: function() {
@ -258,10 +459,10 @@ HotCorner.prototype = {
// 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);
// delay time scale opacity => scale
this._animRipple(this._ripple1, 0.0, 0.83, 0.25, 1.0, 1.5);
this._animRipple(this._ripple2, 0.05, 1.0, 0.0, 0.7, 1.25);
this._animRipple(this._ripple3, 0.35, 1.0, 0.0, 0.3, 1);
},
handleDragOver: function(source, actor, x, y, time) {
@ -321,3 +522,407 @@ HotCorner.prototype = {
return false;
}
};
// This manages the shell "chrome"; the UI that's visible in the
// normal mode (ie, outside the Overview), that surrounds the main
// workspace content.
const defaultParams = {
visibleInFullscreen: false,
affectsStruts: false,
affectsInputRegion: true
};
function Chrome() {
this._init.apply(this, arguments);
}
Chrome.prototype = {
_init: function(layoutManager) {
this._layoutManager = layoutManager;
// The group itself has zero size so it doesn't interfere with DND
this.actor = new Shell.GenericContainer({ width: 0, height: 0 });
Main.uiGroup.add_actor(this.actor);
this.actor.connect('allocate', Lang.bind(this, this._allocated));
this._monitors = [];
this._inOverview = false;
this._trackedActors = [];
this._layoutManager.connect('monitors-changed',
Lang.bind(this, this._relayout));
global.screen.connect('restacked',
Lang.bind(this, this._windowsRestacked));
// Need to update struts on new workspaces when they are added
global.screen.connect('notify::n-workspaces',
Lang.bind(this, 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();
},
init: function() {
Main.overview.connect('showing',
Lang.bind(this, this._overviewShowing));
Main.overview.connect('hidden',
Lang.bind(this, this._overviewHidden));
},
_allocated: function(actor, box, flags) {
let children = this.actor.get_children();
for (let i = 0; i < children.length; i++)
children[i].allocate_preferred_size(flags);
},
addActor: function(actor, params) {
this.actor.add_actor(actor);
this._trackActor(actor, params);
},
trackActor: function(actor, params) {
let ancestor = actor.get_parent();
let index = this._findActor(ancestor);
while (ancestor && index == -1) {
ancestor = ancestor.get_parent();
index = this._findActor(ancestor);
}
if (!ancestor)
throw new Error('actor is not a descendent of the chrome layer');
let ancestorData = this._trackedActors[index];
if (!params)
params = {};
// We can't use Params.parse here because we want to drop
// the extra values like ancestorData.actor
for (let prop in defaultParams) {
if (!params[prop])
params[prop] = ancestorData[prop];
}
this._trackActor(actor, params);
},
untrackActor: function(actor) {
this._untrackActor(actor);
},
removeActor: function(actor) {
this.actor.remove_actor(actor);
this._untrackActor(actor);
},
_findActor: function(actor) {
for (let i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i];
if (actorData.actor == actor)
return i;
}
return -1;
},
_trackActor: function(actor, params) {
if (this._findActor(actor) != -1)
throw new Error('trying to re-track existing chrome actor');
let actorData = Params.parse(params, defaultParams);
actorData.actor = actor;
actorData.visibleId = actor.connect('notify::visible',
Lang.bind(this, this._queueUpdateRegions));
actorData.allocationId = actor.connect('notify::allocation',
Lang.bind(this, this._queueUpdateRegions));
actorData.parentSetId = actor.connect('parent-set',
Lang.bind(this, this._actorReparented));
// Note that destroying actor will unset its parent, so we don't
// need to connect to 'destroy' too.
this._trackedActors.push(actorData);
this._queueUpdateRegions();
},
_untrackActor: function(actor) {
let i = this._findActor(actor);
if (i == -1)
return;
let actorData = this._trackedActors[i];
this._trackedActors.splice(i, 1);
actor.disconnect(actorData.visibleId);
actor.disconnect(actorData.allocationId);
actor.disconnect(actorData.parentSetId);
this._queueUpdateRegions();
},
_actorReparented: function(actor, oldParent) {
if (!this.actor.contains(actor))
this._untrackActor(actor);
},
_updateVisibility: function() {
for (let i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i];
if (!this._inOverview && !actorData.visibleInFullscreen &&
this._findMonitorForActor(actorData.actor).inFullscreen)
this.actor.set_skip_paint(actorData.actor, true);
else
this.actor.set_skip_paint(actorData.actor, false);
}
},
_overviewShowing: function() {
this._inOverview = true;
this._updateVisibility();
this._queueUpdateRegions();
},
_overviewHidden: function() {
this._inOverview = false;
this._updateVisibility();
this._queueUpdateRegions();
},
_relayout: function() {
this._monitors = this._layoutManager.monitors;
this._primaryMonitor = this._layoutManager.primaryMonitor;
this._updateFullscreen();
this._updateVisibility();
this._queueUpdateRegions();
},
_onScreenSaverActiveChanged: function(proxy, screenSaverActive) {
this.actor.visible = !screenSaverActive;
this._queueUpdateRegions();
},
_findMonitorForRect: function(x, y, w, h) {
// First look at what monitor the center of the rectangle is at
let cx = x + w/2;
let cy = y + h/2;
for (let i = 0; i < this._monitors.length; i++) {
let monitor = this._monitors[i];
if (cx >= monitor.x && cx < monitor.x + monitor.width &&
cy >= monitor.y && cy < monitor.y + monitor.height)
return monitor;
}
// If the center is not on a monitor, return the first overlapping monitor
for (let i = 0; i < this._monitors.length; i++) {
let monitor = this._monitors[i];
if (x + w > monitor.x && x < monitor.x + monitor.width &&
y + h > monitor.y && y < monitor.y + monitor.height)
return monitor;
}
// otherwise on no monitor
return null;
},
_findMonitorForWindow: function(window) {
return this._findMonitorForRect(window.x, window.y, window.width, window.height);
},
// This call guarantees that we return some monitor to simplify usage of it
// In practice all tracked actors should be visible on some monitor anyway
_findMonitorForActor: function(actor) {
let [x, y] = actor.get_transformed_position();
let [w, h] = actor.get_transformed_size();
let monitor = this._findMonitorForRect(x, y, w, h);
if (monitor)
return monitor;
return this._primaryMonitor; // Not on any monitor, pretend its on the primary
},
_queueUpdateRegions: function() {
if (!this._updateRegionIdle)
this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this.updateRegions),
Meta.PRIORITY_BEFORE_REDRAW);
},
_updateFullscreen: function() {
let windows = Main.getWindowActorsForWorkspace(global.screen.get_active_workspace_index());
// Reset all monitors to not fullscreen
for (let i = 0; i < this._monitors.length; i++)
this._monitors[i].inFullscreen = false;
// The chrome layer should be visible unless there is a window
// with layer FULLSCREEN, or a window with layer
// OVERRIDE_REDIRECT that covers the whole screen.
// ('override_redirect' is not actually a layer above all
// other windows, but this seems to be how mutter treats it
// currently...) If we wanted to be extra clever, we could
// figure out when an OVERRIDE_REDIRECT window was trying to
// partially overlap us, and then adjust the input region and
// our clip region accordingly...
// @windows is sorted bottom to top.
for (let i = windows.length - 1; i > -1; i--) {
let window = windows[i];
let layer = window.get_meta_window().get_layer();
// Skip minimized windows
if (!window.showing_on_its_workspace())
continue;
if (layer == Meta.StackLayer.FULLSCREEN) {
let monitor = this._findMonitorForWindow(window);
if (monitor)
monitor.inFullscreen = true;
}
if (layer == Meta.StackLayer.OVERRIDE_REDIRECT) {
let monitor = this._findMonitorForWindow(window);
if (monitor &&
window.x <= monitor.x &&
window.x + window.width >= monitor.x + monitor.width &&
window.y <= monitor.y &&
window.y + window.height >= monitor.y + monitor.height)
monitor.inFullscreen = true;
} else
break;
}
},
_windowsRestacked: function() {
let wasInFullscreen = [];
for (let i = 0; i < this._monitors.length; i++)
wasInFullscreen[i] = this._monitors[i].inFullscreen;
this._updateFullscreen();
let changed = false;
for (let i = 0; i < wasInFullscreen.length; i++) {
if (wasInFullscreen[i] != this._monitors[i].inFullscreen) {
changed = true;
break;
}
}
if (changed) {
this._updateVisibility();
this._queueUpdateRegions();
}
},
updateRegions: function() {
let rects = [], struts = [], i;
if (this._updateRegionIdle) {
Mainloop.source_remove(this._updateRegionIdle);
delete this._updateRegionIdle;
}
for (i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i];
if (!actorData.affectsInputRegion && !actorData.affectsStruts)
continue;
let [x, y] = actorData.actor.get_transformed_position();
let [w, h] = actorData.actor.get_transformed_size();
x = Math.round(x);
y = Math.round(y);
w = Math.round(w);
h = Math.round(h);
let rect = new Meta.Rectangle({ x: x, y: y, width: w, height: h});
if (actorData.affectsInputRegion &&
actorData.actor.get_paint_visibility() &&
!this.actor.get_skip_paint(actorData.actor))
rects.push(rect);
if (!actorData.affectsStruts)
continue;
// Limit struts to the size of the screen
let x1 = Math.max(x, 0);
let x2 = Math.min(x + w, global.screen_width);
let y1 = Math.max(y, 0);
let y2 = Math.min(y + h, global.screen_height);
// NetWM struts are not really powerful enought to handle
// a multi-monitor scenario, they only describe what happens
// around the outer sides of the full display region. However
// it can describe a partial region along each side, so
// we can support having the struts only affect the
// primary monitor. This should be enough as we only have
// chrome affecting the struts on the primary monitor so
// far.
//
// Metacity wants to know what side of the screen the
// strut is considered to be attached to. If the actor is
// only touching one edge, or is touching the entire
// border of the primary monitor, then it's obvious which
// side to call it. If it's in a corner, we pick a side
// arbitrarily. If it doesn't touch any edges, or it spans
// the width/height across the middle of the screen, then
// we don't create a strut for it at all.
let side;
let primary = this._primaryMonitor;
if (x1 <= primary.x && x2 >= primary.x + primary.width) {
if (y1 <= primary.y)
side = Meta.Side.TOP;
else if (y2 >= primary.y + primary.height)
side = Meta.Side.BOTTOM;
else
continue;
} else if (y1 <= primary.y && y2 >= primary.y + primary.height) {
if (x1 <= 0)
side = Meta.Side.LEFT;
else if (x2 >= global.screen_width)
side = Meta.Side.RIGHT;
else
continue;
} else if (x1 <= 0)
side = Meta.Side.LEFT;
else if (y1 <= 0)
side = Meta.Side.TOP;
else if (x2 >= global.screen_width)
side = Meta.Side.RIGHT;
else if (y2 >= global.screen_height)
side = Meta.Side.BOTTOM;
else
continue;
// Ensure that the strut rects goes all the way to the screen edge,
// as this really what mutter expects.
switch (side) {
case Meta.Side.TOP:
y1 = 0;
break;
case Meta.Side.BOTTOM:
y2 = global.screen_height;
break;
case Meta.Side.LEFT:
x1 = 0;
break;
case Meta.Side.RIGHT:
x2 = global.screen_width;
break;
}
let strutRect = new Meta.Rectangle({ x: x1, y: y1, width: x2 - x1, height: y2 - y1});
let strut = new Meta.Strut({ rect: strutRect, side: side });
struts.push(strut);
}
global.set_stage_input_region(rects);
let screen = global.screen;
for (let w = 0; w < screen.n_workspaces; w++) {
let workspace = screen.get_workspace_by_index(w);
workspace.set_builtin_struts(struts);
}
return false;
}
};

View File

@ -222,10 +222,9 @@ function WindowList() {
WindowList.prototype = {
_init : function () {
this.actor = new St.BoxLayout({ name: 'Windows', vertical: true, style: 'spacing: 8px' });
let display = global.screen.get_display();
let tracker = Shell.WindowTracker.get_default();
this._updateId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._updateWindowList));
display.connect('window-created', Lang.bind(this, this._updateWindowList));
global.display.connect('window-created', Lang.bind(this, this._updateWindowList));
tracker.connect('tracked-windows-changed', Lang.bind(this, this._updateWindowList));
},
@ -248,7 +247,7 @@ WindowList.prototype = {
box.add(propsBox);
propsBox.add(new St.Label({ text: 'wmclass: ' + metaWindow.get_wm_class() }));
let app = tracker.get_window_app(metaWindow);
if (app != null && !app.is_transient()) {
if (app != null && !app.is_window_backed()) {
let icon = app.create_icon_texture(22);
let propBox = new St.BoxLayout({ style: 'spacing: 6px; ' });
propsBox.add(propBox);
@ -639,23 +638,32 @@ Extensions.prototype = {
name: 'lookingGlassExtensions' });
this._noExtensions = new St.Label({ style_class: 'lg-extensions-none',
text: _("No extensions installed") });
this._numExtensions = 0;
this._extensionsList = new St.BoxLayout({ vertical: true,
style_class: 'lg-extensions-list' });
this._extensionsList.add(this._noExtensions);
this.actor.add(this._extensionsList);
this._loadExtensionList();
for (let uuid in ExtensionSystem.extensionMeta)
this._loadExtension(null, uuid);
ExtensionSystem.connect('extension-loaded',
Lang.bind(this, this._loadExtension));
},
_loadExtensionList: function() {
let extensions = ExtensionSystem.extensionMeta;
let totalExtensions = 0;
for (let uuid in extensions) {
let extensionDisplay = this._createExtensionDisplay(extensions[uuid]);
this._extensionsList.add(extensionDisplay);
totalExtensions++;
}
if (totalExtensions == 0) {
this._extensionsList.add(this._noExtensions);
}
_loadExtension: function(o, uuid) {
let extension = ExtensionSystem.extensionMeta[uuid];
// There can be cases where we create dummy extension metadata
// that's not really a proper extension. Don't bother with these.
if (!extension.name)
return;
let extensionDisplay = this._createExtensionDisplay(extension);
if (this._numExtensions == 0)
this._extensionsList.remove_actor(this._noExtensions);
this._numExtensions ++;
this._extensionsList.add(extensionDisplay);
},
_onViewSource: function (actor) {
@ -682,6 +690,8 @@ Extensions.prototype = {
return _("Error");
case ExtensionSystem.ExtensionState.OUT_OF_DATE:
return _("Out of date");
case ExtensionSystem.ExtensionState.DOWNLOADING:
return _("Downloading");
}
return 'Unknown'; // Not translated, shouldn't appear
},
@ -692,7 +702,7 @@ Extensions.prototype = {
text: meta.name });
box.add(name, { expand: true });
let description = new St.Label({ style_class: 'lg-extension-description',
text: meta.description });
text: meta.description || 'No description' });
box.add(description, { expand: true });
let metaBox = new St.BoxLayout();
@ -751,7 +761,9 @@ LookingGlass.prototype = {
Lang.bind(this, this._updateFont));
this._updateFont();
Main.uiGroup.add_actor(this.actor);
// we add it to the chrome because we want it to appear to slide
// out from underneath the panel
Main.layoutManager.addChrome(this.actor);
this._objInspector = new ObjInspector();
Main.uiGroup.add_actor(this._objInspector.actor);
@ -962,7 +974,7 @@ LookingGlass.prototype = {
this._notebook.selectIndex(0);
this.actor.show();
this.actor.lower(Main.chrome.actor);
this.actor.lower_bottom();
this._open = true;
this._history.lastItem();
@ -993,6 +1005,7 @@ LookingGlass.prototype = {
Main.popModal(this._entry);
this.actor.lower_bottom();
Tweener.addTween(this.actor, { time: 0.5 / St.get_slow_down_factor(),
transition: 'easeOutQuad',
y: this._hiddenY,

View File

@ -14,12 +14,12 @@ 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;
const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent;
const Environment = imports.ui.environment;
const ExtensionSystem = imports.ui.extensionSystem;
const Keyboard = imports.ui.keyboard;
const MessageTray = imports.ui.messageTray;
const Overview = imports.ui.overview;
const Panel = imports.ui.panel;
@ -27,6 +27,7 @@ const PlaceDisplay = imports.ui.placeDisplay;
const RunDialog = imports.ui.runDialog;
const Layout = imports.ui.layout;
const LookingGlass = imports.ui.lookingGlass;
const NetworkAgent = imports.ui.networkAgent;
const NotificationDaemon = imports.ui.notificationDaemon;
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
const Scripting = imports.ui.scripting;
@ -43,7 +44,6 @@ DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff);
let automountManager = null;
let autorunManager = null;
let chrome = null;
let panel = null;
let hotCorners = [];
let placesManager = null;
@ -64,14 +64,94 @@ let uiGroup = null;
let magnifier = null;
let xdndHandler = null;
let statusIconDispatcher = null;
let keyboard = null;
let layoutManager = null;
let networkAgent = null;
let _errorLogStack = [];
let _startDate;
let _defaultCssStylesheet = null;
let _cssStylesheet = null;
let _gdmCssStylesheet = null;
let background = null;
function _createUserSession() {
// Load the calendar server. Note that we are careful about
// not loading any events until the user presses the clock
global.launch_calendar_server();
placesManager = new PlaceDisplay.PlacesManager();
telepathyClient = new TelepathyClient.Client();
automountManager = new AutomountManager.AutomountManager();
autorunManager = new AutorunManager.AutorunManager();
networkAgent = new NetworkAgent.NetworkAgent();
}
function _createGDMSession() {
// We do this this here instead of at the top to prevent GDM
// related code from getting loaded in normal user sessions
const LoginDialog = imports.gdm.loginDialog;
let loginDialog = new LoginDialog.LoginDialog();
loginDialog.connect('loaded', function() {
loginDialog.open();
});
}
function _initRecorder() {
let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
global.screen.connect('toggle-recording', function() {
if (recorder == null) {
recorder = new Shell.Recorder({ stage: global.stage });
}
if (recorder.is_recording()) {
recorder.pause();
Meta.enable_unredirect_for_screen(global.screen);
} else {
// read the parameters from GSettings always in case they have changed
recorder.set_framerate(recorderSettings.get_int('framerate'));
recorder.set_filename('shell-%d%u-%c.' + recorderSettings.get_string('file-extension'));
let pipeline = recorderSettings.get_string('pipeline');
if (!pipeline.match(/^\s*$/))
recorder.set_pipeline(pipeline);
else
recorder.set_pipeline(null);
Meta.disable_unredirect_for_screen(global.screen);
recorder.record();
}
});
}
function _initUserSession() {
_initRecorder();
keyboard.init();
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, false, -1, 1);
ExtensionSystem.init();
ExtensionSystem.loadExtensions();
let shellwm = global.window_manager;
shellwm.takeover_keybinding('panel_run_dialog');
shellwm.connect('keybinding::panel_run_dialog', function () {
getRunDialog().open();
});
shellwm.takeover_keybinding('panel_main_menu');
shellwm.connect('keybinding::panel_main_menu', function () {
overview.toggle();
});
global.display.connect('overlay-key', Lang.bind(overview, overview.toggle));
}
function start() {
// Monkey patch utility functions into the global proxy;
// This is easier and faster than indirecting down into global
@ -91,10 +171,6 @@ function start() {
// back into sync ones.
DBus.session.flush();
// Load the calendar server. Note that we are careful about
// not loading any events until the user presses the clock
global.launch_calendar_server();
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
// also initialize ShellAppSystem first. ShellAppSystem
// needs to load all the .desktop files, and ShellWindowTracker
@ -113,18 +189,9 @@ function start() {
global.stage.no_clear_hint = true;
_defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
_gdmCssStylesheet = global.datadir + '/theme/gdm.css';
loadTheme();
let shellwm = global.window_manager;
shellwm.takeover_keybinding('panel_main_menu');
shellwm.connect('keybinding::panel_main_menu', function () {
overview.toggle();
});
shellwm.takeover_keybinding('panel_run_dialog');
shellwm.connect('keybinding::panel_run_dialog', function () {
getRunDialog().open();
});
// Set up stage hierarchy to group all UI actors under one container.
uiGroup = new Clutter.Group();
St.set_ui_root(global.stage, uiGroup);
@ -133,54 +200,33 @@ function start() {
global.stage.add_actor(uiGroup);
layoutManager = new Layout.LayoutManager();
placesManager = new PlaceDisplay.PlacesManager();
xdndHandler = new XdndHandler.XdndHandler();
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
overview = new Overview.Overview();
chrome = new Chrome.Chrome();
// This overview object is just a stub for non-user sessions
overview = new Overview.Overview({ isDummy: global.session_type != Shell.SessionType.USER });
magnifier = new Magnifier.Magnifier();
statusIconDispatcher = new StatusIconDispatcher.StatusIconDispatcher();
panel = new Panel.Panel();
wm = new WindowManager.WindowManager();
messageTray = new MessageTray.MessageTray();
keyboard = new Keyboard.Keyboard();
notificationDaemon = new NotificationDaemon.NotificationDaemon();
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
telepathyClient = new TelepathyClient.Client();
automountManager = new AutomountManager.AutomountManager();
autorunManager = new AutorunManager.AutorunManager();
layoutManager.init();
if (global.session_type == Shell.SessionType.USER)
_createUserSession();
else if (global.session_type == Shell.SessionType.GDM)
_createGDMSession();
panel.startStatusArea();
keyboard.init();
overview.init();
if (global.session_type == Shell.SessionType.USER)
_initUserSession();
statusIconDispatcher.start(messageTray.actor);
_startDate = new Date();
let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
global.screen.connect('toggle-recording', function() {
if (recorder == null) {
recorder = new Shell.Recorder({ stage: global.stage });
}
if (recorder.is_recording()) {
recorder.pause();
} else {
// read the parameters from GSettings always in case they have changed
recorder.set_framerate(recorderSettings.get_int('framerate'));
recorder.set_filename('shell-%d%u-%c.' + recorderSettings.get_string('file-extension'));
let pipeline = recorderSettings.get_string('pipeline');
if (!pipeline.match(/^\s*$/))
recorder.set_pipeline(pipeline);
else
recorder.set_pipeline(null);
recorder.record();
}
});
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, false, -1, 1);
// Provide the bus object for gnome-session to
// initiate logouts.
EndSessionDialog.init();
@ -188,14 +234,7 @@ function start() {
// Attempt to become a PolicyKit authentication agent
PolkitAuthenticationAgent.init()
ExtensionSystem.init();
ExtensionSystem.loadExtensions();
panel.startStatusArea();
panel.startupAnimation();
let display = global.screen.get_display();
display.connect('overlay-key', Lang.bind(overview, overview.toggle));
_startDate = new Date();
global.stage.connect('captured-event', _globalKeyPressHandler);
@ -414,6 +453,9 @@ function loadTheme() {
let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
if (global.session_type == Shell.SessionType.GDM)
theme.load_stylesheet(_gdmCssStylesheet);
if (previousTheme) {
let customStylesheets = previousTheme.get_custom_stylesheets();
@ -424,6 +466,19 @@ function loadTheme() {
themeContext.set_theme (theme);
}
/**
* notify:
* @msg: A message
* @details: Additional information
*/
function notify(msg, details) {
let source = new MessageTray.SystemNotificationSource();
messageTray.add(source);
let notification = new MessageTray.Notification(source, msg, details);
notification.setTransient(true);
source.notify(notification);
}
/**
* notifyError:
* @msg: An error message
@ -438,11 +493,7 @@ function notifyError(msg, details) {
else
log("error: " + msg)
let source = new MessageTray.SystemNotificationSource();
messageTray.add(source);
let notification = new MessageTray.Notification(source, msg, details);
notification.setTransient(true);
source.notify(notification);
notify(msg, details);
}
/**
@ -524,9 +575,8 @@ function _globalKeyPressHandler(actor, event) {
let keyCode = event.get_key_code();
let modifierState = Shell.get_event_state(event);
let display = global.screen.get_display();
// This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType
let action = display.get_keybinding_action(keyCode, modifierState);
let action = global.display.get_keybinding_action(keyCode, modifierState);
// The screenshot action should always be available (even if a
// modal dialog is present)
@ -549,6 +599,15 @@ function _globalKeyPressHandler(actor, event) {
return true;
}
if (action == Meta.KeyBindingAction.SWITCH_PANELS) {
ctrlAltTabManager.popup(modifierState & Clutter.ModifierType.SHIFT_MASK);
return true;
}
// None of the other bindings are relevant outside of the user's session
if (global.session_type != Shell.SessionType.USER)
return false;
switch (action) {
// left/right would effectively act as synonyms for up/down if we enabled them;
// but that could be considered confusing; we also disable them in the main view.
@ -572,9 +631,6 @@ function _globalKeyPressHandler(actor, event) {
case Meta.KeyBindingAction.PANEL_MAIN_MENU:
overview.hide();
return true;
case Meta.KeyBindingAction.SWITCH_PANELS:
ctrlAltTabManager.popup(modifierState & Clutter.ModifierType.SHIFT_MASK);
return true;
}
return false;

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) {
@ -229,9 +234,7 @@ FocusGrabber.prototype = {
this.actor = actor;
let metaDisplay = global.screen.get_display();
this._prevFocusedWindow = metaDisplay.focus_window;
this._prevFocusedWindow = global.display.focus_window;
this._prevKeyFocusActor = global.stage.get_key_focus();
if (!Main.overview.visible)
@ -266,7 +269,7 @@ FocusGrabber.prototype = {
let source = event.get_source();
switch (event.type()) {
case Clutter.EventType.BUTTON_PRESS:
if (!this.actor.contains(source))
if (!this.actor.contains(source) && !Main.keyboard.actor.contains(source))
this.emit('button-pressed', source);
break;
case Clutter.EventType.KEY_PRESS:
@ -285,8 +288,6 @@ FocusGrabber.prototype = {
if (!this._hasFocus)
return;
let metaDisplay = global.screen.get_display();
if (this._focusActorChangedId > 0) {
global.stage.disconnect(this._focusActorChangedId);
this._focusActorChangedId = 0;
@ -305,8 +306,8 @@ FocusGrabber.prototype = {
this._hasFocus = false;
this.emit('focus-ungrabbed');
if (this._prevFocusedWindow && !metaDisplay.focus_window) {
metaDisplay.set_input_focus_window(this._prevFocusedWindow, false, global.get_current_time());
if (this._prevFocusedWindow && !global.display.focus_window) {
global.display.set_input_focus_window(this._prevFocusedWindow, false, global.get_current_time());
this._prevFocusedWindow = null;
}
if (this._prevKeyFocusActor) {
@ -395,6 +396,8 @@ function Notification(source, title, banner, params) {
}
Notification.prototype = {
IMAGE_SIZE: 125,
_init: function(source, title, banner, params) {
this.source = source;
this.urgency = Urgency.NORMAL;
@ -411,6 +414,7 @@ Notification.prototype = {
this._titleDirection = St.TextDirection.NONE;
this._spacing = 0;
this._scrollPolicy = Gtk.PolicyType.AUTOMATIC;
this._imageBin = null;
source.connect('destroy', Lang.bind(this,
function (source, reason) {
@ -440,9 +444,19 @@ Notification.prototype = {
this._bannerBox.connect('allocate', Lang.bind(this, this._bannerBoxAllocate));
this._table.add(this._bannerBox, { row: 0,
col: 1,
col_span: 2,
x_expand: false,
y_expand: false,
y_fill: false });
// This is an empty cell that overlaps with this._bannerBox cell to ensure
// that this._bannerBox cell expands horizontally, while not forcing the
// this._imageBin that is also in col: 2 to expand horizontally.
this._table.add(new St.Bin(), { row: 0,
col: 2,
y_expand: false,
y_fill: false });
this._titleLabel = new St.Label();
this._bannerBox.add_actor(this._titleLabel);
this._bannerUrlHighlighter = new URLHighlighter();
@ -494,7 +508,10 @@ Notification.prototype = {
this._actionArea = null;
this._buttonBox = null;
}
if (!this._scrollArea && !this._actionArea)
if (this._imageBin && params.clear)
this.unsetImage();
if (!this._scrollArea && !this._actionArea && !this._imageBin)
this._table.remove_style_class_name('multi-line-notification');
this._icon = params.icon || this.source.createNotificationIcon();
@ -558,7 +575,9 @@ Notification.prototype = {
vscrollbar_policy: this._scrollPolicy,
hscrollbar_policy: Gtk.PolicyType.NEVER,
style_class: 'vfade' });
this._table.add(this._scrollArea, { row: 1, col: 1 });
this._table.add(this._scrollArea, { row: 1,
col: 2 });
this._updateLastColumnSettings();
this._contentArea = new St.BoxLayout({ name: 'notification-body',
vertical: true });
this._scrollArea.add_actor(this._contentArea);
@ -635,13 +654,52 @@ Notification.prototype = {
if (!props)
props = {};
props.row = 2;
props.col = 1;
props.col = 2;
this._table.add_style_class_name('multi-line-notification');
this._table.add(this._actionArea, props);
this._updateLastColumnSettings();
this._updated();
},
_updateLastColumnSettings: function() {
if (this._scrollArea)
this._table.child_set(this._scrollArea, { col: this._imageBin ? 2 : 1,
col_span: this._imageBin ? 1 : 2 });
if (this._actionArea)
this._table.child_set(this._actionArea, { col: this._imageBin ? 2 : 1,
col_span: this._imageBin ? 1 : 2 });
},
setImage: function(image) {
if (this._imageBin)
this.unsetImage();
this._imageBin = new St.Bin();
this._imageBin.child = image;
this._imageBin.opacity = 230;
this._table.add_style_class_name('multi-line-notification');
this._table.add_style_class_name('notification-with-image');
this._updateLastColumnSettings();
this._table.add(this._imageBin, { row: 1,
col: 1,
row_span: 2,
x_expand: false,
y_expand: false,
x_fill: false,
y_fill: false });
},
unsetImage: function() {
if (this._imageBin) {
this._table.remove_style_class_name('notification-with-image');
this._table.remove_actor(this._imageBin);
this._imageBin = null;
this._updateLastColumnSettings();
if (!this._scrollArea && !this._actionArea)
this._table.remove_style_class_name('multi-line-notification');
}
},
// addButton:
// @id: the action ID
// @label: the label for the action's button
@ -657,7 +715,9 @@ Notification.prototype = {
let box = new St.BoxLayout({ name: 'notification-actions' });
this.setActionArea(box, { x_expand: false,
y_expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.END });
this._buttonBox = box;
}
@ -1274,12 +1334,11 @@ MessageTray.prototype = {
this._summaryMotionId = 0;
this._summaryBoxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
{ reactive: true,
track_hover: true });
{ reactive: true,
track_hover: true });
this._summaryBoxPointer.actor.style_class = 'summary-boxpointer';
this.actor.add_actor(this._summaryBoxPointer.actor);
this._summaryBoxPointer.actor.lower_bottom();
this._summaryBoxPointer.actor.hide();
Main.layoutManager.addChrome(this._summaryBoxPointer.actor, { visibleInFullscreen: true });
this._summaryBoxPointerItem = null;
this._summaryBoxPointerContentUpdatedId = 0;
@ -1314,6 +1373,7 @@ MessageTray.prototype = {
this._trayState = State.HIDDEN;
this._locked = false;
this._traySummoned = false;
this._useLongerTrayLeftTimeout = false;
this._trayLeftTimeoutId = 0;
this._pointerInTray = false;
@ -1329,10 +1389,10 @@ MessageTray.prototype = {
this._notificationRemoved = false;
this._reNotifyAfterHideNotification = null;
Main.chrome.addActor(this.actor, { affectsStruts: false,
visibleInFullscreen: true });
Main.chrome.trackActor(this._notificationBin);
Main.chrome.trackActor(this._summaryBoxPointer.actor);
Main.layoutManager.trayBox.add_actor(this.actor);
this.actor.y = -1;
Main.layoutManager.trackChrome(this.actor);
Main.layoutManager.trackChrome(this._notificationBin);
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._setSizePosition));
@ -1371,22 +1431,10 @@ MessageTray.prototype = {
_setSizePosition: function() {
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 = monitor.width;
this._summaryBin.x = 0;
this._summaryBin.width = monitor.width;
if (this._pointerBarrier)
global.destroy_pointer_barrier(this._pointerBarrier);
this._pointerBarrier =
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 */);
},
contains: function(source) {
@ -1532,7 +1580,19 @@ MessageTray.prototype = {
if (!this._locked)
return;
this._locked = false;
this._pointerInTray = this.actor.hover && !this._summaryBoxPointer.bin.hover;
this._pointerInTray = this.actor.hover;
this._updateState();
},
toggle: function() {
this._traySummoned = !this._traySummoned;
this._updateState();
},
hide: function() {
this._traySummoned = false;
this.actor.set_hover(false);
this._summary.set_hover(false);
this._updateState();
},
@ -1720,13 +1780,6 @@ MessageTray.prototype = {
if (this._useLongerTrayLeftTimeout && !this._trayLeftTimeoutId)
return;
// Don't do anything if the mouse is over the summary notification as this should be considered as
// leaving the tray. The tray is locked when the summary notification is visible anyway, but we
// should treat the mouse being over the summary notification as the tray being left for collapsing
// any expanded summary item other than the one related to the notification.
if (this._summaryBoxPointer.bin.hover)
return;
this._useLongerTrayLeftTimeout = false;
if (this._trayLeftTimeoutId) {
Mainloop.source_remove(this._trayLeftTimeoutId);
@ -1850,7 +1903,7 @@ MessageTray.prototype = {
}
// Summary
let summarySummoned = this._pointerInSummary || this._overviewVisible;
let summarySummoned = this._pointerInSummary || this._overviewVisible || this._traySummoned;
let summaryPinned = this._summaryTimeoutId != 0 || this._pointerInTray || summarySummoned || this._locked;
let summaryHovered = this._pointerInTray || this._pointerInSummary;
let summaryVisibleWithNoHover = (this._overviewVisible || this._locked) && !summaryHovered;
@ -1944,18 +1997,16 @@ MessageTray.prototype = {
},
_showTray: function() {
let monitor = Main.layoutManager.bottomMonitor;
this._tween(this.actor, '_trayState', State.SHOWN,
{ y: monitor.y + monitor.height - this.actor.height,
{ y: -this.actor.height,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
},
_hideTray: function() {
let monitor = Main.layoutManager.bottomMonitor;
this._tween(this.actor, '_trayState', State.HIDDEN,
{ y: monitor.y + monitor.height - 1,
{ y: -1,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
@ -2043,7 +2094,7 @@ MessageTray.prototype = {
_notificationTimeout: function() {
let [x, y, mods] = global.get_pointer();
if (y > this._lastSeenMouseY + 10 && y < this.actor.y) {
if (y > this._lastSeenMouseY + 10 && !this.actor.hover) {
// The mouse is moving towards the notification, so don't
// hide it yet. (We just create a new timeout (and destroy
// the old one) each time because the bookkeeping is

View File

@ -18,6 +18,7 @@ const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const OPEN_AND_CLOSE_TIME = 0.1;
const FADE_IN_BUTTONS_TIME = 0.33;
const FADE_OUT_DIALOG_TIME = 1.0;
const State = {
@ -34,10 +35,12 @@ function ModalDialog() {
ModalDialog.prototype = {
_init: function(params) {
params = Params.parse(params, { styleClass: null });
params = Params.parse(params, { shellReactive: false,
styleClass: null });
this.state = State.CLOSED;
this._hasModal = false;
this._shellReactive = params.shellReactive;
this._group = new St.Group({ visible: false,
x: 0,
@ -53,26 +56,30 @@ ModalDialog.prototype = {
this._actionKeys = {};
this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
this._lightbox = new Lightbox.Lightbox(this._group,
{ inhibitEvents: true });
this._backgroundBin = new St.Bin();
this._group.add_actor(this._backgroundBin);
this._lightbox.highlight(this._backgroundBin);
this._backgroundStack = new Shell.Stack();
this._backgroundBin.child = this._backgroundStack;
this._eventBlocker = new Clutter.Group({ reactive: true });
this._backgroundStack.add_actor(this._eventBlocker);
this._dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
vertical: true });
if (params.styleClass != null) {
this._dialogLayout.add_style_class_name(params.styleClass);
}
this._backgroundStack.add_actor(this._dialogLayout);
if (!this._shellReactive) {
this._lightbox = new Lightbox.Lightbox(this._group,
{ inhibitEvents: true });
this._lightbox.highlight(this._backgroundBin);
let stack = new Shell.Stack();
this._backgroundBin.child = stack;
this._eventBlocker = new Clutter.Group({ reactive: true });
stack.add_actor(this._eventBlocker);
stack.add_actor(this._dialogLayout);
} else {
this._backgroundBin.child = this._dialogLayout;
}
this.contentLayout = new St.BoxLayout({ vertical: true });
this._dialogLayout.add(this.contentLayout,
@ -82,7 +89,6 @@ ModalDialog.prototype = {
y_align: St.Align.START });
this._buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
opacity: 220,
vertical: false });
this._dialogLayout.add(this._buttonLayout,
{ expand: true,
@ -94,7 +100,13 @@ ModalDialog.prototype = {
this._savedKeyFocus = null;
},
destroy: function() {
this._group.destroy();
},
setButtons: function(buttons) {
let hadChildren = this._buttonLayout.get_children() > 0;
this._buttonLayout.destroy_children();
this._actionKeys = {};
@ -105,10 +117,10 @@ ModalDialog.prototype = {
let action = buttonInfo['action'];
let key = buttonInfo['key'];
let button = new St.Button({ style_class: 'modal-dialog-button',
reactive: true,
can_focus: true,
label: label });
buttonInfo.button = new St.Button({ style_class: 'modal-dialog-button',
reactive: true,
can_focus: true,
label: label });
let x_alignment;
if (buttons.length == 1)
@ -120,20 +132,36 @@ ModalDialog.prototype = {
else
x_alignment = St.Align.MIDDLE;
this._initialKeyFocus = button;
this._buttonLayout.add(button,
this._initialKeyFocus = buttonInfo.button;
this._buttonLayout.add(buttonInfo.button,
{ expand: true,
x_fill: false,
y_fill: false,
x_align: x_alignment,
y_align: St.Align.MIDDLE });
button.connect('clicked', action);
buttonInfo.button.connect('clicked', action);
if (key)
this._actionKeys[key] = action;
i++;
}
// Fade in buttons if there weren't any before
if (!hadChildren && i > 0) {
this._buttonLayout.opacity = 0;
Tweener.addTween(this._buttonLayout,
{ opacity: 255,
time: FADE_IN_BUTTONS_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this.emit('buttons-set');
})
});
} else {
this.emit('buttons-set');
}
},
_onKeyPressEvent: function(object, keyPressEvent) {
@ -157,7 +185,8 @@ ModalDialog.prototype = {
this.state = State.OPENING;
this._dialogLayout.opacity = 255;
this._lightbox.show();
if (this._lightbox)
this._lightbox.show();
this._group.opacity = 0;
this._group.show();
Tweener.addTween(this._group,
@ -223,7 +252,8 @@ ModalDialog.prototype = {
global.gdk_screen.get_display().sync();
this._hasModal = false;
this._eventBlocker.raise_top();
if (!this._shellReactive)
this._eventBlocker.raise_top();
},
pushModal: function (timestamp) {
@ -239,7 +269,8 @@ ModalDialog.prototype = {
} else
this._initialKeyFocus.grab_key_focus();
this._eventBlocker.lower_bottom();
if (!this._shellReactive)
this._eventBlocker.lower_bottom();
return true;
},

400
js/ui/networkAgent.js Normal file
View File

@ -0,0 +1,400 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*-
*
* Copyright 2011 Giovanni Campagna <scampa.giovanni@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
*/
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const NetworkManager = imports.gi.NetworkManager;
const NMClient = imports.gi.NMClient;
const Pango = imports.gi.Pango;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const ModalDialog = imports.ui.modalDialog;
const PopupMenu = imports.ui.popupMenu;
function NetworkSecretDialog() {
this._init.apply(this, arguments);
}
NetworkSecretDialog.prototype = {
__proto__: ModalDialog.ModalDialog.prototype,
_init: function(agent, requestId, connection, settingName, hints) {
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'polkit-dialog' });
this._agent = agent;
this._requestId = requestId;
this._connection = connection;
this._settingName = settingName;
this._hints = hints;
this._content = this._getContent();
let mainContentBox = new St.BoxLayout({ style_class: 'polkit-dialog-main-layout',
vertical: false });
this.contentLayout.add(mainContentBox,
{ x_fill: true,
y_fill: true });
let icon = new St.Icon({ icon_name: 'dialog-password-symbolic' });
mainContentBox.add(icon,
{ x_fill: true,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.START });
let messageBox = new St.BoxLayout({ style_class: 'polkit-dialog-message-layout',
vertical: true });
mainContentBox.add(messageBox,
{ y_align: St.Align.START });
let subjectLabel = new St.Label({ style_class: 'polkit-dialog-headline',
text: this._content.title });
messageBox.add(subjectLabel,
{ y_fill: false,
y_align: St.Align.START });
if (this._content.message != null) {
let descriptionLabel = new St.Label({ style_class: 'polkit-dialog-description',
text: this._content.message,
// HACK: for reasons unknown to me, the label
// is not asked the correct height for width,
// and thus is underallocated
// place a fixed height to avoid overflowing
style: 'height: 3em'
});
descriptionLabel.clutter_text.line_wrap = true;
messageBox.add(descriptionLabel,
{ y_fill: true,
y_align: St.Align.START,
expand: true });
}
let secretTable = new St.Table({ style_class: 'network-dialog-secret-table' });
let pos = 0;
for (let i = 0; i < this._content.secrets.length; i++) {
let secret = this._content.secrets[i];
let label = new St.Label({ style_class: 'polkit-dialog-password-label',
text: secret.label });
let reactive = secret.key != null;
secret.entry = new St.Entry({ style_class: 'polkit-dialog-password-entry',
text: secret.value, can_focus: reactive,
reactive: reactive });
if (secret.validate)
secret.valid = secret.validate(secret);
else // no special validation, just ensure it's not empty
secret.valid = secret.value.length > 0;
if (reactive) {
secret.entry.clutter_text.connect('text-changed', Lang.bind(this, function() {
secret.value = secret.entry.get_text();
if (secret.validate)
secret.valid = secret.validate(secret);
else
secret.valid = secret.value.length > 0;
this._updateOkButton();
}));
} else
secret.valid = true;
secretTable.add(label, { row: pos, col: 0, x_align: St.Align.START, y_align: St.Align.START });
secretTable.add(secret.entry, { row: pos, col: 1, x_expand: true, x_fill: true, y_align: St.Align.END });
pos++;
if (secret.password) {
secret.entry.clutter_text.set_password_char('\u25cf');
// FIXME: need a real checkbox here
let button = new St.Button({ button_mask: St.ButtonMask.ONE,
can_focus: true });
let checkbox = new St.BoxLayout({ vertical: false,
style_class: 'network-dialog-show-password-checkbox'
});
let _switch = new PopupMenu.Switch(false);
checkbox.add(_switch.actor);
checkbox.add(new St.Label({ text: _("Show password") }), { expand: true });
button.connect('clicked', function() {
_switch.toggle();
if (_switch.state)
secret.entry.clutter_text.set_password_char('');
else
secret.entry.clutter_text.set_password_char('\u25cf');
});
button.child = checkbox;
secretTable.add(button, { row: pos, col: 1, x_expand: true, x_fill: true, y_fill: true })
pos++;
}
}
messageBox.add(secretTable);
this._okButton = { label: _("Connect"),
action: Lang.bind(this, this._onOk),
key: Clutter.KEY_Return,
};
this.setButtons([{ label: _("Cancel"),
action: Lang.bind(this, this.cancel),
key: Clutter.KEY_Escape,
},
this._okButton]);
},
_updateOkButton: function() {
let valid = true;
for (let i = 0; i < this._content.secrets.length; i++) {
let secret = this._content.secrets[i];
valid = valid && secret.valid;
}
this._okButton.button.reactive = valid;
this._okButton.button.can_focus = valid;
if (valid)
this._okButton.button.remove_style_pseudo_class('disabled');
else
this._okButton.button.add_style_pseudo_class('disabled');
},
_onOk: function() {
let valid = true;
for (let i = 0; i < this._content.secrets.length; i++) {
let secret = this._content.secrets[i];
valid = valid && secret.valid;
if (secret.key != null)
this._agent.set_password(this._requestId, secret.key, secret.value);
}
if (valid) {
this._agent.respond(this._requestId, false);
this.close(global.get_current_time());
}
// do nothing if not valid
},
cancel: function() {
this._agent.respond(this._requestId, true);
this.close(global.get_current_time());
},
_validateWpaPsk: function(secret) {
let value = secret.value;
if (value.length == 64) {
// must be composed of hexadecimal digits only
for (let i = 0; i < 64; i++) {
if (!((value[i] >= 'a' && value[i] <= 'f')
|| (value[i] >= 'A' && value[i] <= 'F')
|| (value[i] >= '0' && value[i] <= '9')))
return false;
}
return true;
}
return (value.length >= 8 && value.length <= 63);
},
_validateStaticWep: function(secret) {
let value = secret.value;
if (secret.wep_key_type == NetworkManager.WepKeyType.KEY) {
if (value.length == 10 || value.length == 26) {
for (let i = 0; i < value.length; i++) {
if (!((value[i] >= 'a' && value[i] <= 'f')
|| (value[i] >= 'A' && value[i] <= 'F')
|| (value[i] >= '0' && value[i] <= '9')))
return false;
}
} else if (value.length == 5 || value.length == 13) {
for (let i = 0; i < value.length; i++) {
if (!((value[i] >= 'a' && value[i] <= 'z')
|| (value[i] >= 'A' && value[i] <= 'Z')))
return false;
}
} else
return false;
} else if (secret.wep_key_type == NetworkManager.WepKeyType.PASSPHRASE) {
if (value.length < 0 || value.length > 64)
return false;
}
return true;
},
_getWirelessSecrets: function(secrets, wirelessSetting) {
let wirelessSecuritySetting = this._connection.get_setting_wireless_security();
switch (wirelessSecuritySetting.key_mgmt) {
// First the easy ones
case 'wpa-none':
case 'wpa-psk':
secrets.push({ label: _("Password: "), key: 'psk',
value: wirelessSecuritySetting.psk || '',
validate: this._validateWpaPsk, password: true });
break;
case 'none': // static WEP
secrets.push({ label: _("Key: "), key: 'wep-key' + wirelessSecuritySetting.wep_tx_keyidx,
value: wirelessSecuritySetting.get_wep_key(wirelessSecuritySetting.wep_tx_keyidx) || '',
wep_key_type: wirelessSecuritySetting.wep_key_type,
validate: this._validateStaticWep, password: true });
break;
case 'ieee8021x':
if (wirelessSecuritySetting.auth_alg == 'leap') // Cisco LEAP
secrets.push({ label: _("Password: "), key: 'leap-password',
value: wirelessSecuritySetting.leap_password || '', password: true });
else // Dynamic (IEEE 802.1x) WEP
this._get8021xSecrets(secrets);
break;
case 'wpa-eap':
this._get8021xSecrets(secrets);
break;
default:
log('Invalid wireless key management: ' + wirelessSecuritySetting.key_mgmt);
}
},
_get8021xSecrets: function(secrets) {
let ieee8021xSetting = this._connection.get_setting_802_1x();
let phase2method;
switch (ieee8021xSetting.get_eap_method(0)) {
case 'md5':
case 'leap':
case 'ttls':
case 'peap':
// TTLS and PEAP are actually much more complicated, but this complication
// is not visible here since we only care about phase2 authentication
// (and don't even care of which one)
secrets.push({ label: _("Username: "), key: null,
value: ieee8021xSetting.identity || '', password: false });
secrets.push({ label: _("Password: "), key: 'password',
value: ieee8021xSetting.password || '', password: true });
break;
case 'tls':
secrets.push({ label: _("Identity: "), key: null,
value: ieee8021xSetting.identity || '', password: false });
secrets.push({ label: _("Private key password: "), key: 'private-key-password',
value: ieee8021xSetting.private_key_password || '', password: true });
break;
default:
log('Invalid EAP/IEEE802.1x method: ' + ieee8021xSetting.get_eap_method(0));
}
},
_getPPPoESecrets: function(secrets) {
let pppoeSetting = this._connection.get_setting_pppoe();
secrets.push({ label: _("Username: "), key: 'username',
value: pppoeSetting.username || '', password: false });
secrets.push({ label: _("Service: "), key: 'service',
value: pppoeSetting.service || '', password: false });
secrets.push({ label: _("Password: "), key: 'password',
value: pppoeSetting.password || '', password: true });
},
_getMobileSecrets: function(secrets, connectionType) {
let setting;
if (connectionType == 'bluetooth')
setting = this._connection.get_setting_cdma() || this._connection.get_setting_gsm();
else
setting = this._connection.get_setting_by_name(connectionType);
secrets.push({ label: _("Password: "), key: 'password',
value: setting.value || '', password: true });
},
_getContent: function() {
let connectionSetting = this._connection.get_setting_connection();
let connectionType = connectionSetting.get_connection_type();
let wirelessSetting;
let ssid;
let content = { };
content.secrets = [ ];
switch (connectionType) {
case '802-11-wireless':
wirelessSetting = this._connection.get_setting_wireless();
ssid = NetworkManager.utils_ssid_to_utf8(wirelessSetting.get_ssid());
content.title = _("Authentication required by wireless network");
content.message = _("Passwords or encryption keys are required to access the wireless network '%s'.").format(ssid);
this._getWirelessSecrets(content.secrets, wirelessSetting);
break;
case '802-3-ethernet':
content.title = _("Wired 802.1X authentication");
content.message = null;
content.secrets.push({ label: _("Network name: "), key: null,
value: connectionSetting.get_id(), password: false });
this._get8021xSecrets(content.secrets);
break;
case 'pppoe':
content.title = _("DSL authentication");
content.message = null;
this._getPPPoESecrets(content.secrets);
break;
case 'gsm':
if (this._hints.indexOf('pin') != -1) {
let gsmSetting = this._connection.get_setting_gsm();
content.title = _("PIN code required");
content.message = _("PIN code is needed for the mobile broadband device");
content.secrets.push({ label: _("PIN: "), key: 'pin',
value: gsmSetting.pin || '', password: true });
}
// fall through
case 'cdma':
case 'bluetooth':
content.title = _("Mobile broadband network password");
content.message = _("A password is required to connect to '%s'.").format(connectionSetting.get_id());
this._getMobileSecrets(content.secrets);
break;
default:
log('Invalid connection type: ' + connectionType);
};
return content;
}
};
function NetworkAgent() {
this._init.apply(this, arguments);
}
NetworkAgent.prototype = {
_init: function() {
this._native = new Shell.NetworkAgent({ auto_register: true,
identifier: 'org.gnome.Shell.NetworkAgent' });
this._dialogs = { };
this._native.connect('new-request', Lang.bind(this, this._newRequest));
this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest));
},
_newRequest: function(agent, requestId, connection, settingName, hints) {
let dialog = new NetworkSecretDialog(agent, requestId, connection, settingName, hints);
dialog.connect('destroy', Lang.bind(this, function() {
delete this._dialogs[requestId];
}));
this._dialogs[requestId] = dialog;
dialog.open(global.get_current_time());
},
_cancelRequest: function(agent, requestId) {
this._dialogs[requestId].close(global.get_current_time());
this._dialogs[requestId].destroy();
}
};

View File

@ -119,11 +119,6 @@ NotificationDaemon.prototype = {
return new St.Icon({ icon_name: icon,
icon_type: St.IconType.FULLCOLOR,
icon_size: size });
} else if (hints['image-data']) {
let [width, height, rowStride, hasAlpha,
bitsPerSample, nChannels, data] = hints['image-data'];
return textureCache.load_from_raw(data, hasAlpha,
width, height, rowStride, size);
} else {
let stockIcon;
switch (hints.urgency) {
@ -195,6 +190,8 @@ NotificationDaemon.prototype = {
if (appName == 'Empathy' && (hints['category'] == 'im.received' ||
hints['category'] == 'x-empathy.im.room-invitation' ||
hints['category'] == 'x-empathy.call.incoming' ||
hints['category'] == 'x-empathy.call.incoming"' ||
hints['category'] == 'x-empathy.im.subscription-request' ||
hints['category'] == 'presence.online' ||
hints['category'] == 'presence.offline')) {
// Ignore replacesId since we already sent back a
@ -218,14 +215,18 @@ NotificationDaemon.prototype = {
hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
// Be compatible with the various hints for image data
// 'image-data' is the latest name of this hint, introduced in 1.2
if (!hints['image-data']) {
// Be compatible with the various hints for image data and image path
// 'image-data' and 'image-path' are the latest name of these hints, introduced in 1.2
if (!hints['image-path'] && hints['image_path'])
hints['image-path'] = hints['image_path']; // version 1.1 of the spec
if (!hints['image-data'])
if (hints['image_data'])
hints['image-data'] = hints['image_data']; // version 1.1 of the spec
else if (hints['icon_data'])
hints['image-data'] = hints['icon_data']; // previous versions of the spec
}
else if (hints['icon_data'] && !hints['image-path'])
// early versions of the spec; 'icon_data' should only be used if 'image-path' is not available
hints['image-data'] = hints['icon_data'];
let ndata = { appName: appName,
icon: icon,
@ -304,7 +305,7 @@ NotificationDaemon.prototype = {
ndata.notification = notification;
notification.connect('destroy', Lang.bind(this,
function(n, reason) {
delete this._notifications[id];
delete this._notifications[ndata.id];
let notificationClosedReason;
switch (reason) {
case MessageTray.NotificationDestroyedReason.EXPIRED:
@ -317,11 +318,11 @@ NotificationDaemon.prototype = {
notificationClosedReason = NotificationClosedReason.APP_CLOSED;
break;
}
this._emitNotificationClosed(id, notificationClosedReason);
this._emitNotificationClosed(ndata.id, notificationClosedReason);
}));
notification.connect('action-invoked', Lang.bind(this,
function(n, actionId) {
this._emitActionInvoked(id, actionId);
this._emitActionInvoked(ndata.id, actionId);
}));
} else {
notification.update(summary, body, { icon: iconActor,
@ -329,10 +330,34 @@ NotificationDaemon.prototype = {
clear: true });
}
if (hints['image-data'] || hints['image-path']) {
let image = null;
if (hints['image-data']) {
let [width, height, rowStride, hasAlpha,
bitsPerSample, nChannels, data] = hints['image-data'];
image = St.TextureCache.get_default().load_from_raw(data, hasAlpha,
width, height, rowStride, notification.IMAGE_SIZE);
} else if (hints['image-path']) {
image = St.TextureCache.get_default().load_uri_async(GLib.filename_to_uri(hints['image-path'], null),
notification.IMAGE_SIZE,
notification.IMAGE_SIZE);
}
notification.setImage(image);
} else {
notification.unsetImage();
}
if (actions.length) {
notification.setUseActionIcons(hints['action-icons'] == true);
for (let i = 0; i < actions.length - 1; i += 2)
notification.addButton(actions[i], actions[i + 1]);
for (let i = 0; i < actions.length - 1; i += 2) {
if (actions[i] == 'default')
notification.connect('clicked', Lang.bind(this,
function() {
this._emitActionInvoked(ndata.id, "default");
}));
else
notification.addButton(actions[i], actions[i + 1]);
}
}
switch (hints.urgency) {
case Urgency.LOW:
@ -461,9 +486,11 @@ Source.prototype = {
_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)
if (!this.trayIcon && this.app)
this.destroy();
},

View File

@ -11,6 +11,7 @@ const Shell = imports.gi.Shell;
const Gdk = imports.gi.Gdk;
const AppDisplay = imports.ui.appDisplay;
const ContactDisplay = imports.ui.contactDisplay;
const Dash = imports.ui.dash;
const DND = imports.ui.dnd;
const DocDisplay = imports.ui.docDisplay;
@ -18,6 +19,7 @@ const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const Panel = imports.ui.panel;
const Params = imports.misc.params;
const PlaceDisplay = imports.ui.placeDisplay;
const Tweener = imports.ui.tweener;
const ViewSelector = imports.ui.viewSelector;
@ -96,14 +98,30 @@ ShellInfo.prototype = {
};
function Overview() {
this._init();
this._init.apply(this, arguments);
}
Overview.prototype = {
_init : function() {
// The actual global.background_actor is inside global.window_group,
// which is hidden when displaying the overview, so we display a clone.
this._background = new Clutter.Clone({ source: global.background_actor });
_init : function(params) {
params = Params.parse(params, { isDummy: false });
this.isDummy = params.isDummy;
// We only have an overview in user sessions, so
// create a dummy overview in other cases
if (this.isDummy) {
this.animationInProgress = false;
this.visible = false;
this.workspaces = null;
return;
}
// The main BackgroundActor is inside global.window_group which is
// hidden when displaying the overview, so we create a new
// one. Instances of this class share a single CoglTexture behind the
// scenes which allows us to show the background with different
// rendering options without duplicating the texture data.
this._background = Meta.BackgroundActor.new_for_screen(global.screen);
this._background.hide();
global.overlay_group.add_actor(this._background);
@ -175,37 +193,53 @@ Overview.prototype = {
// signal handlers and so forth. So we create them after
// construction in this init() method.
init: function() {
this.shellInfo = new ShellInfo();
if (this.isDummy)
return;
this.viewSelector = new ViewSelector.ViewSelector();
this._group.add_actor(this.viewSelector.actor);
this._shellInfo = new ShellInfo();
this._viewSelector = new ViewSelector.ViewSelector();
this._group.add_actor(this._viewSelector.actor);
this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay();
this.viewSelector.addViewTab('windows', _("Windows"), this._workspacesDisplay.actor, 'text-x-generic');
this._viewSelector.addViewTab('windows', _("Windows"), this._workspacesDisplay.actor, 'text-x-generic');
let appView = new AppDisplay.AllAppDisplay();
this.viewSelector.addViewTab('applications', _("Applications"), appView.actor, 'system-run');
this._viewSelector.addViewTab('applications', _("Applications"), appView.actor, 'system-run');
// Default search providers
this.viewSelector.addSearchProvider(new AppDisplay.AppSearchProvider());
this.viewSelector.addSearchProvider(new AppDisplay.PrefsSearchProvider());
this.viewSelector.addSearchProvider(new PlaceDisplay.PlaceSearchProvider());
this.viewSelector.addSearchProvider(new DocDisplay.DocSearchProvider());
this._viewSelector.addSearchProvider(new AppDisplay.AppSearchProvider());
this._viewSelector.addSearchProvider(new AppDisplay.SettingsSearchProvider());
this._viewSelector.addSearchProvider(new PlaceDisplay.PlaceSearchProvider());
this._viewSelector.addSearchProvider(new DocDisplay.DocSearchProvider());
this._viewSelector.addSearchProvider(new ContactDisplay.ContactSearchProvider());
// TODO - recalculate everything when desktop size changes
this.dash = new Dash.Dash();
this._group.add_actor(this.dash.actor);
this.dash.actor.add_constraint(this.viewSelector.constrainY);
this.dash.actor.add_constraint(this.viewSelector.constrainHeight);
this._dash = new Dash.Dash();
this._group.add_actor(this._dash.actor);
this._dash.actor.add_constraint(this._viewSelector.constrainY);
this._dash.actor.add_constraint(this._viewSelector.constrainHeight);
this.dashIconSize = this._dash.iconSize;
this._dash.connect('icon-size-changed',
Lang.bind(this, function() {
this.dashIconSize = this._dash.iconSize;
}));
// Translators: this is the name of the dock/favorites area on
// the left of the overview
Main.ctrlAltTabManager.addGroup(this.dash.actor, _("Dash"), 'user-bookmarks');
Main.ctrlAltTabManager.addGroup(this._dash.actor, _("Dash"), 'user-bookmarks');
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
this._relayout();
},
setMessage: function(text, undoCallback, undoLabel) {
if (this.isDummy)
return;
this._shellInfo.setMessage(text, undoCallback, undoLabel);
},
_onDragBegin: function() {
DND.addDragMonitor(this._dragMonitor);
// Remember the workspace we started from
@ -275,6 +309,9 @@ Overview.prototype = {
},
setScrollAdjustment: function(adjustment, direction) {
if (this.isDummy)
return;
this._scrollAdjustment = adjustment;
if (this._scrollAdjustment == null)
this._scrollDirection = SwipeScrollDirection.NONE;
@ -467,15 +504,15 @@ Overview.prototype = {
// Set the dash's x position - y is handled by a constraint
let dashX;
if (rtl) {
this.dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
this._dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
dashX = primary.width;
} else {
dashX = 0;
}
this.dash.actor.set_x(dashX);
this._dash.actor.set_x(dashX);
this.viewSelector.actor.set_position(viewX, viewY);
this.viewSelector.actor.set_size(viewWidth, viewHeight);
this._viewSelector.actor.set_position(viewX, viewY);
this._viewSelector.actor.set_size(viewWidth, viewHeight);
},
//// Public methods ////
@ -508,6 +545,8 @@ Overview.prototype = {
//
// Animates the overview visible and grabs mouse and keyboard input
show : function() {
if (this.isDummy)
return;
if (this._shown)
return;
// Do this manually instead of using _syncInputMode, to handle failure
@ -535,6 +574,9 @@ Overview.prototype = {
//
// If we switched to displaying the actors in the Overview rather than
// clones of them, this would obviously no longer be necessary.
//
// Disable unredirection while in the overview
Meta.disable_unredirect_for_screen(global.screen);
global.window_group.hide();
this._group.show();
this._background.show();
@ -566,6 +608,12 @@ Overview.prototype = {
onCompleteScope: this
});
Tweener.addTween(this._background,
{ dim_factor: 0.4,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
this._coverPane.raise_top();
this._coverPane.show();
this.emit('showing');
@ -578,6 +626,9 @@ Overview.prototype = {
// will result in the overview not being hidden until hideTemporarily() is
// called.
showTemporarily: function() {
if (this.isDummy)
return;
if (this._shownTemporarily)
return;
@ -590,6 +641,9 @@ Overview.prototype = {
//
// Reverses the effect of show()
hide: function() {
if (this.isDummy)
return;
if (!this._shown)
return;
@ -608,6 +662,9 @@ Overview.prototype = {
//
// Reverses the effect of showTemporarily()
hideTemporarily: function() {
if (this.isDummy)
return;
if (!this._shownTemporarily)
return;
@ -619,6 +676,9 @@ Overview.prototype = {
},
toggle: function() {
if (this.isDummy)
return;
if (this._shown)
this.hide();
else
@ -684,6 +744,12 @@ Overview.prototype = {
onCompleteScope: this
});
Tweener.addTween(this._background,
{ dim_factor: 1.0,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
this._coverPane.raise_top();
this._coverPane.show();
this.emit('hiding');
@ -704,6 +770,9 @@ Overview.prototype = {
},
_hideDone: function() {
// Re-enable unredirection
Meta.enable_unredirect_for_screen(global.screen);
global.window_group.show();
this.workspaces.destroy();

View File

@ -16,21 +16,19 @@ const Layout = imports.ui.layout;
const Overview = imports.ui.overview;
const PopupMenu = imports.ui.popupMenu;
const PanelMenu = imports.ui.panelMenu;
const StatusMenu = imports.ui.statusMenu;
const UserMenu = imports.ui.userMenu;
const DateMenu = imports.ui.dateMenu;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const PANEL_ICON_SIZE = 24;
const STARTUP_ANIMATION_TIME = 0.2;
const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
const SPINNER_ANIMATION_TIME = 0.2;
const STANDARD_TRAY_ICON_ORDER = ['a11y', 'display', 'keyboard', 'volume', 'bluetooth', 'network', 'battery'];
const STANDARD_TRAY_ICON_ORDER = ['a11y', 'keyboard', 'volume', 'bluetooth', 'network', 'battery'];
const STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION = {
'a11y': imports.ui.status.accessibility.ATIndicator,
'volume': imports.ui.status.volume.Indicator,
@ -47,6 +45,14 @@ try {
log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
}
const GDM_TRAY_ICON_ORDER = ['a11y', 'display', 'keyboard', 'volume', 'battery'];
const GDM_TRAY_ICON_SHELL_IMPLEMENTATION = {
'a11y': imports.ui.status.accessibility.ATIndicator,
'volume': imports.ui.status.volume.Indicator,
'battery': imports.ui.status.power.Indicator,
'keyboard': imports.ui.status.keyboard.XKBIndicator
};
// To make sure the panel corners blend nicely with the panel,
// we draw background and borders the same way, e.g. drawing
// them as filled shapes from the outside inwards instead of
@ -237,7 +243,6 @@ AppMenuButton.prototype = {
_init: function() {
PanelMenu.Button.prototype._init.call(this, 0.0);
this._metaDisplay = global.screen.get_display();
this._startingApps = [];
this._targetApp = null;
@ -698,12 +703,94 @@ function PanelCorner(panel, side) {
}
PanelCorner.prototype = {
_init: function(panel, side) {
this._panel = panel;
_init: function(box, side) {
this._side = side;
this._box = box;
this._box.connect('style-changed', Lang.bind(this, this._boxStyleChanged));
this.actor = new St.DrawingArea({ style_class: 'panel-corner' });
this.actor.connect('style-changed', Lang.bind(this, this._styleChanged));
this.actor.connect('repaint', Lang.bind(this, this._repaint));
this.actor.connect('style-changed', Lang.bind(this, this.relayout));
},
_findRightmostButton: function(container) {
if (!container.get_children)
return null;
let children = container.get_children();
if (!children || children.length == 0)
return null;
// Start at the back and work backward
let index = children.length - 1;
while (!children[index].visible && index >= 0)
index--;
if (index < 0)
return null;
if (!(children[index].has_style_class_name('panel-menu')) &&
!(children[index].has_style_class_name('panel-button')))
return this._findRightmostButton(children[index]);
return children[index];
},
_findLeftmostButton: function(container) {
if (!container.get_children)
return null;
let children = container.get_children();
if (!children || children.length == 0)
return null;
// Start at the front and work forward
let index = 0;
while (!children[index].visible && index < children.length)
index++;
if (index == children.length)
return null;
if (!(children[index].has_style_class_name('panel-menu')) &&
!(children[index].has_style_class_name('panel-button')))
return this._findLeftmostButton(children[index]);
return children[index];
},
_boxStyleChanged: function() {
let button;
if (this._side == St.Side.LEFT)
button = this._findLeftmostButton(this._box);
else if (this._side == St.Side.RIGHT)
button = this._findRightmostButton(this._box);
if (button) {
if (this._button && this._buttonStyleChangedSignalId)
this._button.disconnect(this._buttonStyleChangedSignalId);
this._button = button;
button.connect('destroy', Lang.bind(this,
function() {
if (this._button == button) {
this._button = null;
this._buttonStyleChangedSignalId = 0;
}
}));
// Synchronize the locate button's pseudo classes with this corner
this._buttonStyleChangedSignalId = button.connect('style-changed', Lang.bind(this,
function(actor) {
let pseudoClass = button.get_style_pseudo_class();
this.actor.set_style_pseudo_class(pseudoClass);
}));
}
},
_repaint: function() {
@ -766,20 +853,14 @@ PanelCorner.prototype = {
cr.restore();
},
relayout: function() {
_styleChanged: function() {
let node = this.actor.get_theme_node();
let cornerRadius = node.get_length("-panel-corner-radius");
let innerBorderWidth = node.get_length('-panel-corner-inner-border-width');
this.actor.set_size(cornerRadius,
innerBorderWidth + cornerRadius);
if (this._side == St.Side.LEFT)
this.actor.set_position(this._panel.actor.x,
this._panel.actor.y + this._panel.actor.height - innerBorderWidth);
else
this.actor.set_position(this._panel.actor.x + this._panel.actor.width - cornerRadius,
this._panel.actor.y + this._panel.actor.height - innerBorderWidth);
this.actor.set_size(cornerRadius, innerBorderWidth + cornerRadius);
this.actor.set_anchor_point(0, innerBorderWidth);
}
};
@ -790,9 +871,8 @@ function Panel() {
Panel.prototype = {
_init : function() {
this.actor = new St.BoxLayout({ style_class: 'menu-bar',
name: 'panel',
reactive: true });
this.actor = new Shell.GenericContainer({ name: 'panel',
reactive: true });
this.actor._delegate = this;
this._statusArea = {};
@ -804,113 +884,60 @@ Panel.prototype = {
this.actor.remove_style_class_name('in-overview');
}));
this._leftPointerBarrier = 0;
this._rightPointerBarrier = 0;
if (global.session_type == Shell.SessionType.GDM) {
this._tray_icon_order = GDM_TRAY_ICON_ORDER;
this._tray_icon_shell_implementation = GDM_TRAY_ICON_SHELL_IMPLEMENTATION;
} else {
this._tray_icon_order = STANDARD_TRAY_ICON_ORDER;
this._tray_icon_shell_implementation = STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION;
}
this._menus = new PopupMenu.PopupMenuManager(this);
this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
this.actor.add_actor(this._leftBox);
this._centerBox = new St.BoxLayout({ name: 'panelCenter' });
this.actor.add_actor(this._centerBox);
this._rightBox = new St.BoxLayout({ name: 'panelRight' });
this.actor.add_actor(this._rightBox);
this._leftCorner = new PanelCorner(this, St.Side.LEFT);
this._rightCorner = new PanelCorner(this, St.Side.RIGHT);
if (this.actor.get_direction() == St.TextDirection.RTL)
this._leftCorner = new PanelCorner(this._rightBox, St.Side.LEFT);
else
this._leftCorner = new PanelCorner(this._leftBox, St.Side.LEFT);
/* This box container ensures that the centerBox is positioned in the *absolute*
* center, but can be pushed aside if necessary. */
this._boxContainer = new Shell.GenericContainer();
this.actor.add(this._boxContainer, { expand: true });
this._boxContainer.add_actor(this._leftBox);
this._boxContainer.add_actor(this._centerBox);
this._boxContainer.add_actor(this._rightBox);
this._boxContainer.connect('get-preferred-width', Lang.bind(this, function(box, forHeight, alloc) {
let children = box.get_children();
for (let i = 0; i < children.length; i++) {
let [childMin, childNatural] = children[i].get_preferred_width(forHeight);
alloc.min_size += childMin;
alloc.natural_size += childNatural;
}
}));
this._boxContainer.connect('get-preferred-height', Lang.bind(this, function(box, forWidth, alloc) {
let children = box.get_children();
for (let i = 0; i < children.length; i++) {
let [childMin, childNatural] = children[i].get_preferred_height(forWidth);
if (childMin > alloc.min_size)
alloc.min_size = childMin;
if (childNatural > alloc.natural_size)
alloc.natural_size = childNatural;
}
}));
this._boxContainer.connect('allocate', Lang.bind(this, function(container, box, flags) {
let allocWidth = box.x2 - box.x1;
let allocHeight = box.y2 - box.y1;
let [leftMinWidth, leftNaturalWidth] = this._leftBox.get_preferred_width(-1);
let [centerMinWidth, centerNaturalWidth] = this._centerBox.get_preferred_width(-1);
let [rightMinWidth, rightNaturalWidth] = this._rightBox.get_preferred_width(-1);
this.actor.add_actor(this._leftCorner.actor);
let sideWidth, centerWidth;
centerWidth = centerNaturalWidth;
sideWidth = (allocWidth - centerWidth) / 2;
if (this.actor.get_direction() == St.TextDirection.RTL)
this._rightCorner = new PanelCorner(this._leftBox, St.Side.RIGHT);
else
this._rightCorner = new PanelCorner(this._rightBox, St.Side.RIGHT);
this.actor.add_actor(this._rightCorner.actor);
let childBox = new Clutter.ActorBox();
childBox.y1 = 0;
childBox.y2 = allocHeight;
if (this.actor.get_direction() == St.TextDirection.RTL) {
childBox.x1 = allocWidth - Math.min(Math.floor(sideWidth),
leftNaturalWidth);
childBox.x2 = allocWidth;
} else {
childBox.x1 = 0;
childBox.x2 = Math.min(Math.floor(sideWidth),
leftNaturalWidth);
}
this._leftBox.allocate(childBox, flags);
childBox.x1 = Math.ceil(sideWidth);
childBox.y1 = 0;
childBox.x2 = childBox.x1 + centerWidth;
childBox.y2 = allocHeight;
this._centerBox.allocate(childBox, flags);
childBox.y1 = 0;
childBox.y2 = allocHeight;
if (this.actor.get_direction() == St.TextDirection.RTL) {
childBox.x1 = 0;
childBox.x2 = Math.min(Math.floor(sideWidth),
rightNaturalWidth);
} else {
childBox.x1 = allocWidth - Math.min(Math.floor(sideWidth),
rightNaturalWidth);
childBox.x2 = allocWidth;
}
this._rightBox.allocate(childBox, flags);
}));
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));
/* Button on the left side of the panel. */
this._activitiesButton = new ActivitiesButton();
this._activities = this._activitiesButton.actor;
this._leftBox.add(this._activities);
if (global.session_type == Shell.SessionType.USER) {
this._activitiesButton = new ActivitiesButton();
this._activities = this._activitiesButton.actor;
this._leftBox.add(this._activities);
// 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);
// 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;
let pseudoClass = actor.get_style_pseudo_class();
corner.actor.set_style_pseudo_class(pseudoClass);
}));
this._appMenu = new AppMenuButton();
this._leftBox.add(this._appMenu.actor);
this._menus.addMenu(this._appMenu.menu);
this._appMenu = new AppMenuButton();
this._leftBox.add(this._appMenu.actor);
this._menus.addMenu(this._appMenu.menu);
}
/* center */
this._dateMenu = new DateMenu.DateMenuButton();
if (global.session_type == Shell.SessionType.USER)
this._dateMenu = new DateMenu.DateMenuButton({ showEvents: true });
else
this._dateMenu = new DateMenu.DateMenuButton({ showEvents: false });
this._centerBox.add(this._dateMenu.actor, { y_fill: true });
this._menus.addMenu(this._dateMenu.menu);
@ -926,113 +953,145 @@ Panel.prototype = {
this._rightBox.add(this._trayBox);
this._rightBox.add(this._statusBox);
this._userMenu = new StatusMenu.StatusMenuButton();
this._userMenu.actor.name = 'panelStatus';
this._rightBox.add(this._userMenu.actor);
// Synchronize the buttons pseudo classes with its corner
this._userMenu.actor.connect('style-changed', Lang.bind(this,
function(actor) {
let rtl = actor.get_direction() == St.TextDirection.RTL;
let corner = rtl ? this._leftCorner : this._rightCorner;
let pseudoClass = actor.get_style_pseudo_class();
corner.actor.set_style_pseudo_class(pseudoClass);
}));
if (global.session_type == Shell.SessionType.USER) {
this._userMenu = new UserMenu.UserMenuButton();
this._userMenu.actor.name = 'panelStatus';
this._rightBox.add(this._userMenu.actor);
}
Main.statusIconDispatcher.connect('status-icon-added', Lang.bind(this, this._onTrayIconAdded));
Main.statusIconDispatcher.connect('status-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
Main.chrome.addActor(this.actor);
Main.chrome.addActor(this._leftCorner.actor, { affectsStruts: false,
affectsInputRegion: false });
Main.chrome.addActor(this._rightCorner.actor, { affectsStruts: false,
affectsInputRegion: false });
Main.layoutManager.panelBox.add(this.actor);
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'start-here',
{ sortGroup: CtrlAltTab.SortGroup.TOP });
},
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
this._relayout();
_getPreferredWidth: function(actor, forHeight, alloc) {
alloc.min_size = -1;
alloc.natural_size = Main.layoutManager.primaryMonitor.width;
},
_getPreferredHeight: function(actor, forWidth, alloc) {
// We don't need to implement this; it's forced by the CSS
alloc.min_size = -1;
alloc.natural_size = -1;
},
_allocate: function(actor, box, flags) {
let allocWidth = box.x2 - box.x1;
let allocHeight = box.y2 - box.y1;
let [leftMinWidth, leftNaturalWidth] = this._leftBox.get_preferred_width(-1);
let [centerMinWidth, centerNaturalWidth] = this._centerBox.get_preferred_width(-1);
let [rightMinWidth, rightNaturalWidth] = this._rightBox.get_preferred_width(-1);
let sideWidth, centerWidth;
centerWidth = centerNaturalWidth;
sideWidth = (allocWidth - centerWidth) / 2;
let childBox = new Clutter.ActorBox();
childBox.y1 = 0;
childBox.y2 = allocHeight;
if (this.actor.get_direction() == St.TextDirection.RTL) {
childBox.x1 = allocWidth - Math.min(Math.floor(sideWidth),
leftNaturalWidth);
childBox.x2 = allocWidth;
} else {
childBox.x1 = 0;
childBox.x2 = Math.min(Math.floor(sideWidth),
leftNaturalWidth);
}
this._leftBox.allocate(childBox, flags);
childBox.x1 = Math.ceil(sideWidth);
childBox.y1 = 0;
childBox.x2 = childBox.x1 + centerWidth;
childBox.y2 = allocHeight;
this._centerBox.allocate(childBox, flags);
childBox.y1 = 0;
childBox.y2 = allocHeight;
if (this.actor.get_direction() == St.TextDirection.RTL) {
childBox.x1 = 0;
childBox.x2 = Math.min(Math.floor(sideWidth),
rightNaturalWidth);
} else {
childBox.x1 = allocWidth - Math.min(Math.floor(sideWidth),
rightNaturalWidth);
childBox.x2 = allocWidth;
}
this._rightBox.allocate(childBox, flags);
let [cornerMinWidth, cornerWidth] = this._leftCorner.actor.get_preferred_width(-1);
let [cornerMinHeight, cornerHeight] = this._leftCorner.actor.get_preferred_width(-1);
childBox.x1 = 0;
childBox.x2 = cornerWidth;
childBox.y1 = allocHeight;
childBox.y2 = allocHeight + cornerHeight;
this._leftCorner.actor.allocate(childBox, flags);
let [cornerMinWidth, cornerWidth] = this._rightCorner.actor.get_preferred_width(-1);
let [cornerMinHeight, cornerHeight] = this._rightCorner.actor.get_preferred_width(-1);
childBox.x1 = allocWidth - cornerWidth;
childBox.x2 = allocWidth;
childBox.y1 = allocHeight;
childBox.y2 = allocHeight + cornerHeight;
this._rightCorner.actor.allocate(childBox, flags);
},
startStatusArea: function() {
for (let i = 0; i < STANDARD_TRAY_ICON_ORDER.length; i++) {
let role = STANDARD_TRAY_ICON_ORDER[i];
let constructor = STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION[role];
for (let i = 0; i < this._tray_icon_order.length; i++) {
let role = this._tray_icon_order[i];
let constructor = this._tray_icon_shell_implementation[role];
if (!constructor) {
// This icon is not implemented (this is a bug)
continue;
}
let indicator = new constructor();
this._statusBox.add(indicator.actor);
this._menus.addMenu(indicator.menu);
this._statusArea[role] = indicator;
let indicator = new constructor();
this.addToStatusArea(role, indicator, i);
}
// PopupMenuManager depends on menus being added in order for
// keyboard navigation
this._menus.addMenu(this._userMenu.menu);
if (this._userMenu)
this._menus.addMenu(this._userMenu.menu);
},
startupAnimation: function() {
let oldY = this.actor.y;
this.actor.y = oldY - this.actor.height;
Tweener.addTween(this.actor,
{ y: oldY,
time: STARTUP_ANIMATION_TIME,
transition: 'easeOutQuad'
});
addToStatusArea: function(role, indicator, position) {
if (this._statusArea[role])
throw new Error('Extension point conflict: there is already a status indicator for role ' + role);
let oldCornerY = this._leftCorner.actor.y;
this._leftCorner.actor.y = oldCornerY - this.actor.height;
this._rightCorner.actor.y = oldCornerY - this.actor.height;
Tweener.addTween(this._leftCorner.actor,
{ y: oldCornerY,
time: STARTUP_ANIMATION_TIME,
transition: 'easeOutQuad'
});
Tweener.addTween(this._rightCorner.actor,
{ y: oldCornerY,
time: STARTUP_ANIMATION_TIME,
transition: 'easeOutQuad'
});
},
if (!(indicator instanceof PanelMenu.Button))
throw new TypeError('Status indicator must be an instance of PanelMenu.Button');
_relayout: function() {
let primary = Main.layoutManager.primaryMonitor;
if (!position)
position = 0;
this.actor.set_position(primary.x, primary.y);
this.actor.set_size(primary.width, -1);
this._statusBox.insert_actor(indicator.actor, position);
this._menus.addMenu(indicator.menu);
if (this._leftPointerBarrier)
global.destroy_pointer_barrier(this._leftPointerBarrier);
if (this._rightPointerBarrier)
global.destroy_pointer_barrier(this._rightPointerBarrier);
this._statusArea[role] = indicator;
let destroyId = indicator.connect('destroy', Lang.bind(this, function(emitter) {
this._statusArea[role] = null;
emitter.disconnect(destroyId);
}));
this._leftPointerBarrier =
global.create_pointer_barrier(primary.x, primary.y,
primary.x, primary.y + this.actor.height,
1 /* BarrierPositiveX */);
this._rightPointerBarrier =
global.create_pointer_barrier(primary.x + primary.width, primary.y,
primary.x + primary.width, primary.y + this.actor.height,
4 /* BarrierNegativeX */);
this._leftCorner.relayout();
this._rightCorner.relayout();
return indicator;
},
_onTrayIconAdded: function(o, icon, role) {
icon.height = PANEL_ICON_SIZE;
if (STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION[role]) {
if (this._tray_icon_shell_implementation[role]) {
// This icon is legacy, and replaced by a Shell version
// Hide it
return;
}
// Figure out the index in our well-known order for this icon
let position = STANDARD_TRAY_ICON_ORDER.indexOf(role);
let position = this._tray_icon_order.indexOf(role);
icon._rolePosition = position;
let children = this._trayBox.get_children();
let i;

View File

@ -2,6 +2,7 @@
const Clutter = imports.gi.Clutter;
const Gtk = imports.gi.Gtk;
const Signals = imports.signals;
const St = imports.gi.St;
const Lang = imports.lang;
@ -23,10 +24,11 @@ Button.prototype = {
this.actor._delegate = this;
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress));
this.menu = new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP, 0);
this.menu = new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP);
this.menu.actor.add_style_class_name('panel-menu');
this.menu.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged));
this.menu.actor.connect('key-press-event', Lang.bind(this, this._onMenuKeyPress));
Main.chrome.addActor(this.menu.actor, { affectsStruts: false });
Main.uiGroup.add_actor(this.menu.actor);
this.menu.actor.hide();
},
@ -79,8 +81,18 @@ Button.prototype = {
this.actor.add_style_pseudo_class('active');
else
this.actor.remove_style_pseudo_class('active');
},
destroy: function() {
this.actor._delegate = null;
this.menu.destroy();
this.actor.destroy();
this.emit('destroy');
}
};
Signals.addSignalMethods(Button.prototype);
/* SystemStatusButton:
*

View File

@ -60,13 +60,13 @@ PlaceInfo.prototype = {
// Helper function to translate launch parameters into a GAppLaunchContext
function _makeLaunchContext(params)
{
params = Params.parse(params, { workspace: null,
timestamp: null });
params = Params.parse(params, { workspace: -1,
timestamp: 0 });
let launchContext = global.create_app_launch_context();
if (params.workspace != null)
launchContext.set_desktop(params.workspace.index());
if (params.timestamp != null)
if (params.workspace != -1)
launchContext.set_desktop(params.workspace);
if (params.timestamp != 0)
launchContext.set_timestamp(params.timestamp);
return launchContext;
@ -118,9 +118,9 @@ PlaceDeviceInfo.prototype = {
this._mount.unmount_finish(res);
} catch (e) {
let message = _("Failed to unmount '%s'").format(o.get_name());
Main.overview.shellInfo.setMessage(message,
Lang.bind(this, this.remove),
_("Retry"));
Main.overview.setMessage(message,
Lang.bind(this, this.remove),
_("Retry"));
}
}
};

View File

@ -23,10 +23,10 @@
const Lang = imports.lang;
const Signals = imports.signals;
const Shell = imports.gi.Shell;
const AccountsService = imports.gi.AccountsService;
const Clutter = imports.gi.Clutter;
const St = imports.gi.St;
const Pango = imports.gi.Pango;
const Gdm = imports.gi.Gdm;
const Gio = imports.gi.Gio;
const Mainloop = imports.mainloop;
const Polkit = imports.gi.Polkit;
@ -92,7 +92,7 @@ AuthenticationDialog.prototype = {
let userName = userNames[0];
this._user = Gdm.UserManager.ref_default().get_user(userName);
this._user = AccountsService.UserManager.get_default().get_user(userName);
let userRealName = this._user.get_real_name()
this._userLoadedId = this._user.connect('notify::is_loaded',
Lang.bind(this, this._onUserChanged));

View File

@ -15,6 +15,17 @@ const Tweener = imports.ui.tweener;
const SLIDER_SCROLL_STEP = 0.05; /* Slider scrolling step in % */
function _ensureStyle(actor) {
if (actor.get_children) {
let children = actor.get_children();
for (let i = 0; i < children.length; i++)
_ensureStyle(children[i]);
}
if (actor instanceof St.Widget)
actor.ensure_style();
}
function PopupBaseMenuItem(params) {
this._init(params);
}
@ -706,7 +717,8 @@ PopupSwitchMenuItem.prototype = {
this.addActor(this.label);
this._statusBin = new St.Bin({ x_align: St.Align.END });
this.addActor(this._statusBin, { align: St.Align.END });
this.addActor(this._statusBin,
{ expand: true, span: -1, align: St.Align.END });
this._statusLabel = new St.Label({ text: '',
style_class: 'popup-inactive-menu-item'
@ -800,14 +812,58 @@ PopupMenuBase.prototype = {
this.passEvents = false;
this._activeMenuItem = null;
this._childMenus = [];
},
addAction: function(title, callback) {
var menuItem = new PopupMenuItem(title);
let menuItem = new PopupMenuItem(title);
this.addMenuItem(menuItem);
menuItem.connect('activate', Lang.bind(this, function (menuItem, event) {
callback(event);
}));
return menuItem;
},
addSettingsAction: function(title, desktopFile) {
// Don't allow user settings to get edited unless we're in a user session
if (global.session_type != Shell.SessionType.USER)
return null;
let menuItem = this.addAction(title, function() {
let app = Shell.AppSystem.get_default().lookup_setting(desktopFile);
if (!app) {
log('Settings panel for desktop file ' + desktopFile + ' could not be loaded!');
return;
}
Main.overview.hide();
app.activate();
});
return menuItem;
},
isChildMenu: function(menu) {
return this._childMenus.indexOf(menu) != -1;
},
addChildMenu: function(menu) {
if (this.isChildMenu(menu))
return;
this._childMenus.push(menu);
this.emit('child-menu-added', menu);
},
removeChildMenu: function(menu) {
let index = this._childMenus.indexOf(menu);
if (index == -1)
return;
this._childMenus.splice(index, 1);
this.emit('child-menu-removed', menu);
},
/**
@ -860,6 +916,39 @@ PopupMenuBase.prototype = {
}));
},
_updateSeparatorVisibility: function(menuItem) {
let children = this.box.get_children();
let index = children.indexOf(menuItem.actor);
if (index < 0)
return;
let childBeforeIndex = index - 1;
while (childBeforeIndex >= 0 && !children[childBeforeIndex].visible)
childBeforeIndex--;
if (childBeforeIndex < 0
|| children[childBeforeIndex]._delegate instanceof PopupSeparatorMenuItem) {
menuItem.actor.hide();
return;
}
let childAfterIndex = index + 1;
while (childAfterIndex < children.length && !children[childAfterIndex].visible)
childAfterIndex++;
if (childAfterIndex >= children.length
|| children[childAfterIndex]._delegate instanceof PopupSeparatorMenuItem) {
menuItem.actor.hide();
return;
}
menuItem.actor.show();
},
addMenuItem: function(menuItem, position) {
let before_item = null;
if (position == undefined) {
@ -891,6 +980,14 @@ PopupMenuBase.prototype = {
if (!open)
menuItem.menu.close(false);
});
} else if (menuItem instanceof PopupSeparatorMenuItem) {
this._connectItemSignals(menuItem);
// updateSeparatorVisibility needs to get called any time the
// separator's adjacent siblings change visibility or position.
// open-state-changed isn't exactly that, but doing it in more
// precise ways would require a lot more bookkeeping.
this.connect('open-state-changed', Lang.bind(this, function() { this._updateSeparatorVisibility(menuItem); }));
} else if (menuItem instanceof PopupBaseMenuItem)
this._connectItemSignals(menuItem);
else
@ -954,6 +1051,10 @@ PopupMenuBase.prototype = {
return null;
},
get numMenuItems() {
return this._getMenuItems().length;
},
removeAll: function() {
let children = this._getMenuItems();
for (let i = 0; i < children.length; i++) {
@ -985,12 +1086,11 @@ function PopupMenu() {
PopupMenu.prototype = {
__proto__: PopupMenuBase.prototype,
_init: function(sourceActor, alignment, arrowSide, gap) {
_init: function(sourceActor, alignment, arrowSide) {
PopupMenuBase.prototype._init.call (this, sourceActor, 'popup-menu-content');
this._alignment = alignment;
this._arrowSide = arrowSide;
this._gap = gap;
this._boxPointer = new BoxPointer.BoxPointer(arrowSide,
{ x_fill: true,
@ -1048,9 +1148,11 @@ PopupMenu.prototype = {
this.isOpen = true;
this._boxPointer.setPosition(this.sourceActor, this._gap, this._alignment);
this._boxPointer.setPosition(this.sourceActor, this._alignment);
this._boxPointer.show(animate);
this.actor.raise_top();
this.emit('open-state-changed', true);
},
@ -1318,6 +1420,193 @@ PopupSubMenuMenuItem.prototype = {
}
};
function PopupComboMenu() {
this._init.apply(this, arguments);
}
PopupComboMenu.prototype = {
__proto__: PopupMenuBase.prototype,
_init: function(sourceActor) {
PopupMenuBase.prototype._init.call(this,
sourceActor, 'popup-combo-menu');
this.actor = this.box;
this.actor._delegate = this;
this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
this.actor.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn));
this._activeItemPos = -1;
global.focus_manager.add_group(this.actor);
},
_onKeyPressEvent: function(actor, event) {
if (event.get_key_symbol() == Clutter.Escape) {
this.close(true);
return true;
}
return false;
},
_onKeyFocusIn: function(actor) {
let items = this._getMenuItems();
let activeItem = items[this._activeItemPos];
activeItem.actor.grab_key_focus();
},
open: function() {
if (this.isOpen)
return;
this.isOpen = true;
let [sourceX, sourceY] = this.sourceActor.get_transformed_position();
let items = this._getMenuItems();
let activeItem = items[this._activeItemPos];
this.actor.set_position(sourceX, sourceY - activeItem.actor.y);
this.actor.width = Math.max(this.actor.width, this.sourceActor.width);
this.actor.raise_top();
this.actor.opacity = 0;
this.actor.show();
Tweener.addTween(this.actor,
{ opacity: 255,
transition: 'linear',
time: BoxPointer.POPUP_ANIMATION_TIME });
this.emit('open-state-changed', true);
},
close: function() {
if (!this.isOpen)
return;
this.isOpen = false;
Tweener.addTween(this.actor,
{ opacity: 0,
transition: 'linear',
time: BoxPointer.POPUP_ANIMATION_TIME,
onComplete: Lang.bind(this,
function() {
this.actor.hide();
})
});
this.emit('open-state-changed', false);
},
setActiveItem: function(position) {
this._activeItemPos = position;
},
setItemVisible: function(position, visible) {
if (!visible && position == this._activeItemPos) {
log('Trying to hide the active menu item.');
return;
}
this._getMenuItems()[position].actor.visible = visible;
}
};
function PopupComboBoxMenuItem() {
this._init.apply(this, arguments);
}
PopupComboBoxMenuItem.prototype = {
__proto__: PopupBaseMenuItem.prototype,
_init: function (params) {
PopupBaseMenuItem.prototype._init.call(this, params);
this._itemBox = new Shell.Stack();
this.addActor(this._itemBox);
let expander = new St.Label({ text: '\u2304' });
this.addActor(expander, { align: St.Align.END });
this._menu = new PopupComboMenu(this.actor);
Main.uiGroup.add_actor(this._menu.actor);
this._menu.actor.hide();
if (params.style_class)
this._menu.actor.add_style_class_name(params.style_class);
this._activeItemPos = -1;
this._items = [];
},
_getTopMenu: function() {
let actor = this.actor.get_parent();
while (actor) {
if (actor._delegate &&
(actor._delegate instanceof PopupMenu ||
actor._delegate instanceof PopupComboMenu))
return actor._delegate;
actor = actor.get_parent();
}
return null;
},
activate: function(event) {
let topMenu = this._getTopMenu();
if (!topMenu)
return;
topMenu.addChildMenu(this._menu);
this._menu.toggle();
},
addMenuItem: function(menuItem, position) {
if (position === undefined)
position = this._menu.numMenuItems;
this._menu.addMenuItem(menuItem, position);
_ensureStyle(this._menu.actor);
let item = new St.BoxLayout({ style_class: 'popup-combobox-item' });
let children = menuItem.actor.get_children();
for (let i = 0; i < children.length; i++) {
let clone = new Clutter.Clone({ source: children[i] });
item.add(clone, { y_fill: false });
}
let oldItem = this._items[position];
if (oldItem)
this._itemBox.remove_actor(oldItem);
this._items[position] = item;
this._itemBox.add_actor(item);
menuItem.connect('activate',
Lang.bind(this, this._itemActivated, position));
},
setActiveItem: function(position) {
let item = this._items[position];
if (!item)
return;
if (this._activeItemPos == position)
return;
this._menu.setActiveItem(position);
this._activeItemPos = position;
for (let i = 0; i < this._items.length; i++)
this._items[i].visible = (i == this._activeItemPos);
},
setItemVisible: function(position, visible) {
this._menu.setItemVisible(position, visible);
},
_itemActivated: function(menuItem, event, position) {
this.setActiveItem(position);
this.emit('active-item-changed', position);
}
};
/* Basic implementation of a menu manager.
* Call addMenu to add menus
@ -1337,6 +1626,7 @@ PopupMenuManager.prototype = {
this._keyFocusNotifyId = 0;
this._activeMenu = null;
this._menus = [];
this._menuStack = [];
this._preGrabInputMode = null;
this._grabbedFromKeynav = false;
},
@ -1345,6 +1635,8 @@ PopupMenuManager.prototype = {
let menudata = {
menu: menu,
openStateChangeId: menu.connect('open-state-changed', Lang.bind(this, this._onMenuOpenState)),
childMenuAddedId: menu.connect('child-menu-added', Lang.bind(this, this._onChildMenuAdded)),
childMenuRemovedId: menu.connect('child-menu-removed', Lang.bind(this, this._onChildMenuRemoved)),
destroyId: menu.connect('destroy', Lang.bind(this, this._onMenuDestroy)),
enterId: 0,
focusInId: 0
@ -1372,6 +1664,8 @@ PopupMenuManager.prototype = {
let menudata = this._menus[position];
menu.disconnect(menudata.openStateChangeId);
menu.disconnect(menudata.childMenuAddedId);
menu.disconnect(menudata.childMenuRemovedId);
menu.disconnect(menudata.destroyId);
if (menudata.enterId)
@ -1409,8 +1703,20 @@ PopupMenuManager.prototype = {
},
_onMenuOpenState: function(menu, open) {
if (open)
if (open) {
if (this._activeMenu && this._activeMenu.isChildMenu(menu)) {
this._menuStack.push(this._activeMenu);
menu.actor.grab_key_focus();
}
this._activeMenu = menu;
} else {
if (this._menuStack.length > 0) {
this._activeMenu = this._menuStack.pop();
if (menu.sourceActor)
menu.sourceActor.grab_key_focus();
this._didPop = true;
}
}
// Check what the focus was before calling pushModal/popModal
let focus = global.stage.key_focus;
@ -1443,6 +1749,14 @@ PopupMenuManager.prototype = {
}
},
_onChildMenuAdded: function(menu, childMenu) {
this.addMenu(childMenu);
},
_onChildMenuRemoved: function(menu, childMenu) {
this.removeMenu(childMenu);
},
// change the currently-open menu without dropping grab
_changeMenu: function(newMenu) {
if (this._activeMenu) {
@ -1451,6 +1765,8 @@ PopupMenuManager.prototype = {
// before closing it to keep that from happening
let oldMenu = this._activeMenu;
this._activeMenu = null;
for (let i = this._menuStack.length - 1; i >= 0; i--)
this._menuStack[i].close(false);
oldMenu.close(false);
newMenu.open(false);
} else
@ -1461,6 +1777,15 @@ PopupMenuManager.prototype = {
if (!this.grabbed || menu == this._activeMenu)
return false;
if (this._activeMenu && this._activeMenu.isChildMenu(menu))
return false;
if (this._menuStack.indexOf(menu) != -1)
return false;
if (this._menuStack.length > 0 && this._menuStack[0].isChildMenu(menu))
return false;
this._changeMenu(menu);
return false;
},
@ -1473,6 +1798,8 @@ PopupMenuManager.prototype = {
if (focus) {
if (this._activeMenuContains(focus))
return;
if (this._menuStack.length > 0)
return;
if (focus._delegate && focus._delegate.menu &&
this._findMenu(focus._delegate.menu) != -1)
return;
@ -1531,6 +1858,11 @@ PopupMenuManager.prototype = {
if (this._activeMenu != null && this._activeMenu.passEvents)
return false;
if (this._didPop) {
this._didPop = false;
return true;
}
let activeMenuContains = this._eventIsOnActiveMenu(event);
let eventType = event.type();

View File

@ -112,6 +112,43 @@ function SearchProvider(title) {
SearchProvider.prototype = {
_init: function(title) {
this.title = title;
this.searchSystem = null;
this.searchAsync = false;
},
_asyncCancelled: function() {
},
startAsync: function() {
this.searchAsync = true;
},
tryCancelAsync: function() {
if (!this.searchAsync)
return;
this._asyncCancelled();
this.searchAsync = false;
},
/**
* addItems:
* @items: an array of result identifier strings representing
* items which match the last given search terms.
*
* This should be used for something that requires a bit more
* logic; it's designed to be an asyncronous way to add a result
* to the current search.
*/
addItems: function(items) {
if (!this.searchSystem)
throw new Error('Search provider not registered');
if (!items.length)
return;
this.tryCancelAsync();
this.searchSystem.addProviderItems(this, items);
},
/**
@ -315,6 +352,7 @@ SearchSystem.prototype = {
},
registerProvider: function (provider) {
provider.searchSystem = this;
this._providers.push(provider);
},
@ -331,12 +369,23 @@ SearchSystem.prototype = {
this._previousResults = [];
},
addProviderItems: function(provider, items) {
this.emit('search-updated', provider, items);
},
updateSearch: function(searchString) {
searchString = searchString.replace(/^\s+/g, '').replace(/\s+$/g, '');
if (searchString == '')
return [];
return;
let terms = searchString.split(/\s+/);
this.updateSearchResults(terms);
},
updateSearchResults: function(terms) {
if (!terms)
return;
let isSubSearch = terms.length == this._previousTerms.length;
if (isSubSearch) {
for (let i = 0; i < terms.length; i++) {
@ -349,12 +398,12 @@ SearchSystem.prototype = {
let results = [];
if (isSubSearch) {
for (let i = 0; i < this._previousResults.length; i++) {
for (let i = 0; i < this._providers.length; i++) {
let [provider, previousResults] = this._previousResults[i];
provider.tryCancelAsync();
try {
let providerResults = provider.getSubsearchResultSet(previousResults, terms);
if (providerResults.length > 0)
results.push([provider, providerResults]);
results.push([provider, providerResults]);
} catch (error) {
global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
}
@ -362,10 +411,10 @@ SearchSystem.prototype = {
} else {
for (let i = 0; i < this._providers.length; i++) {
let provider = this._providers[i];
provider.tryCancelAsync();
try {
let providerResults = provider.getInitialResultSet(terms);
if (providerResults.length > 0)
results.push([provider, providerResults]);
results.push([provider, providerResults]);
} catch (error) {
global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
}
@ -374,8 +423,7 @@ SearchSystem.prototype = {
this._previousTerms = terms;
this._previousResults = results;
return results;
}
this.emit('search-completed', results);
},
};
Signals.addSignalMethods(SearchSystem.prototype);

View File

@ -88,7 +88,7 @@ SearchResult.prototype = {
},
getDragActor: function(stageX, stageY) {
return this.metaInfo['createIcon'](Main.overview.dash.iconSize);
return this.metaInfo['createIcon'](Main.overview.dashIconSize);
},
shellWorkspaceLaunch: function(params) {
@ -100,17 +100,17 @@ SearchResult.prototype = {
};
function GridSearchResults(provider) {
this._init(provider);
function GridSearchResults(provider, grid) {
this._init(provider, grid);
}
GridSearchResults.prototype = {
__proto__: Search.SearchResultDisplay.prototype,
_init: function(provider) {
_init: function(provider, grid) {
Search.SearchResultDisplay.prototype._init.call(this, provider);
this._grid = new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
xAlign: St.Align.START });
this._grid = grid || new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
xAlign: St.Align.START });
this.actor = new St.Bin({ x_align: St.Align.START });
this.actor.set_child(this._grid.actor);
@ -189,6 +189,8 @@ function SearchResults(searchSystem, openSearchSystem) {
SearchResults.prototype = {
_init: function(searchSystem, openSearchSystem) {
this._searchSystem = searchSystem;
this._searchSystem.connect('search-updated', Lang.bind(this, this._updateCurrentResults));
this._searchSystem.connect('search-completed', Lang.bind(this, this._updateResults));
this._openSearchSystem = openSearchSystem;
this.actor = new St.BoxLayout({ name: 'searchResults',
@ -223,9 +225,11 @@ SearchResults.prototype = {
this._selectedProvider = -1;
this._providers = this._searchSystem.getProviders();
this._providerMeta = [];
for (let i = 0; i < this._providers.length; i++)
this._providerMetaResults = {};
for (let i = 0; i < this._providers.length; i++) {
this.createProviderMeta(this._providers[i]);
this._providerMetaResults[this.providers[i].title] = [];
}
this._searchProvidersBox = new St.BoxLayout({ style_class: 'search-providers-box' });
this.actor.add(this._searchProvidersBox);
@ -305,6 +309,12 @@ SearchResults.prototype = {
}
},
_clearDisplayForProvider: function(index) {
let meta = this._providerMeta[index];
meta.resultDisplay.clear();
meta.actor.hide();
},
reset: function() {
this._searchSystem.reset();
this._statusText.hide();
@ -319,15 +329,24 @@ SearchResults.prototype = {
this._statusText.show();
},
doSearch: function (searchString) {
this._searchSystem.updateSearch(searchString);
},
_metaForProvider: function(provider) {
return this._providerMeta[this._providers.indexOf(provider)];
},
updateSearch: function (searchString) {
let results = this._searchSystem.updateSearch(searchString);
this._clearDisplay();
_updateCurrentResults: function(searchSystem, provider, results) {
let terms = searchSystem.getTerms();
let meta = this._metaForProvider(provider);
meta.resultDisplay.clear();
meta.actor.show();
meta.resultDisplay.renderResults(results, terms);
return true;
},
_updateResults: function(searchSystem, results) {
if (results.length == 0) {
this._statusText.set_text(_("No matching results."));
this._statusText.show();
@ -337,7 +356,7 @@ SearchResults.prototype = {
this._statusText.hide();
}
let terms = this._searchSystem.getTerms();
let terms = searchSystem.getTerms();
this._openSearchSystem.setSearchTerms(terms);
// To avoid CSS transitions causing flickering
@ -349,9 +368,15 @@ SearchResults.prototype = {
for (let i = 0; i < results.length; i++) {
let [provider, providerResults] = results[i];
let meta = this._metaForProvider(provider);
meta.actor.show();
meta.resultDisplay.renderResults(providerResults, terms);
if (providerResults.length == 0) {
this._clearDisplayForProvider(i);
} else {
this._providerMetaResults[provider.title] = providerResults;
this._clearDisplayForProvider(i);
let meta = this._metaForProvider(provider);
meta.actor.show();
meta.resultDisplay.renderResults(providerResults, terms);
}
}
if (this._selectedOpenSearchButton == -1)

View File

@ -1,7 +1,10 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const DBus = imports.dbus;
const Lang = imports.lang;
const Config = imports.misc.config;
const ExtensionSystem = imports.ui.extensionSystem;
const Main = imports.ui.main;
const GnomeShellIface = {
@ -9,12 +12,55 @@ const GnomeShellIface = {
methods: [{ name: 'Eval',
inSignature: 's',
outSignature: 'bs'
},
{ name: 'ListExtensions',
inSignature: '',
outSignature: 'a{sa{sv}}'
},
{ name: 'GetExtensionInfo',
inSignature: 's',
outSignature: 'a{sv}'
},
{ name: 'GetExtensionErrors',
inSignature: 's',
outSignature: 'as'
},
{ name: 'ScreenshotArea',
inSignature: 'iiiis',
outSignature: 'b'
},
{ name: 'ScreenshotWindow',
inSignature: 'bs',
outSignature: 'b'
},
{ name: 'Screenshot',
inSignature: 's',
outSignature: 'b'
},
{ name: 'EnableExtension',
inSignature: 's',
outSignature: ''
},
{ name: 'DisableExtension',
inSignature: 's',
outSignature: ''
},
{ name: 'InstallRemoteExtension',
inSignature: 's',
outSignature: ''
}
],
signals: [],
signals: [{ name: 'ExtensionStatusChanged',
inSignature: 'sis' }],
properties: [{ name: 'OverviewActive',
signature: 'b',
access: 'readwrite' }]
access: 'readwrite' },
{ name: 'ApiVersion',
signature: 'i',
access: 'read' },
{ name: 'ShellVersion',
signature: 's',
access: 'read' }]
};
function GnomeShell() {
@ -24,6 +70,8 @@ function GnomeShell() {
GnomeShell.prototype = {
_init: function() {
DBus.session.exportObject('/org/gnome/Shell', this);
ExtensionSystem.connect('extension-state-changed',
Lang.bind(this, this._extensionStateChanged));
},
/**
@ -56,6 +104,80 @@ GnomeShell.prototype = {
return [success, returnValue];
},
/**
* ScreenshotArea:
* @x: The X coordinate of the area
* @y: The Y coordinate of the area
* @width: The width of the area
* @height: The height of the area
* @filename: The filename for the screenshot
*
* Takes a screenshot of the passed in area and saves it
* in @filename as png image, it returns a boolean
* indicating whether the operation was successful or not.
*
*/
ScreenshotAreaAsync : function (x, y, width, height, filename, callback) {
global.screenshot_area (x, y, width, height, filename, function (obj, result) { callback(result); });
},
/**
* ScreenshotWindow:
* @include_frame: Whether to include the frame or not
* @filename: The filename for the screenshot
*
* Takes a screenshot of the focused window (optionally omitting the frame)
* and saves it in @filename as png image, it returns a boolean
* indicating whether the operation was successful or not.
*
*/
ScreenshotWindow : function (include_frame, filename) {
return global.screenshot_window (include_frame, filename);
},
/**
* Screenshot:
* @filename: The filename for the screenshot
*
* Takes a screenshot of the whole screen and saves it
* in @filename as png image, it returns a boolean
* indicating whether the operation was successful or not.
*
*/
ScreenshotAsync : function (filename, callback) {
global.screenshot(filename, function (obj, result) { callback(result); });
},
ListExtensions: function() {
return ExtensionSystem.extensionMeta;
},
GetExtensionInfo: function(uuid) {
return ExtensionSystem.extensionMeta[uuid] || {};
},
GetExtensionErrors: function(uuid) {
return ExtensionSystem.errors[uuid] || [];
},
EnableExtension: function(uuid) {
let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY);
if (enabledExtensions.indexOf(uuid) == -1)
enabledExtensions.push(uuid);
global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
},
DisableExtension: function(uuid) {
let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY);
while (enabledExtensions.indexOf(uuid) != -1)
enabledExtensions.splice(enabledExtensions.indexOf(uuid), 1);
global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
},
InstallRemoteExtension: function(uuid, url) {
ExtensionSystem.installExtensionFromManifestURL(uuid, url);
},
get OverviewActive() {
return Main.overview.visible;
},
@ -65,6 +187,17 @@ GnomeShell.prototype = {
Main.overview.show();
else
Main.overview.hide();
},
ApiVersion: 1,
ShellVersion: Config.PACKAGE_VERSION,
_extensionStateChanged: function(_, newState) {
DBus.session.emit_signal('/org/gnome/Shell',
'org.gnome.Shell',
'ExtensionStatusChanged', 'sis',
[newState.uuid, newState.state, newState.error]);
}
};

View File

@ -84,7 +84,7 @@ ListItem.prototype = {
_onClicked: function() {
this.emit('activate');
this._app.activate(-1);
this._app.activate();
}
};
Signals.addSignalMethods(ListItem.prototype);
@ -402,4 +402,4 @@ ShellProcessesDialog.prototype = {
_setButtonsForChoices(this, choices);
}
}
Signals.addSignalMethods(ShellProcessesDialog.prototype);
Signals.addSignalMethods(ShellProcessesDialog.prototype);

View File

@ -88,11 +88,7 @@ ATIndicator.prototype = {
this.menu.addMenuItem(mouseKeys);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addAction(_("Universal Access Settings"), function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().get_app('gnome-universal-access-panel.desktop');
app.activate(-1);
});
this.menu.addSettingsAction(_("Universal Access Settings"), 'gnome-universal-access-panel.desktop');
},
_buildItemExtended: function(string, initial_value, writable, on_set) {

View File

@ -67,7 +67,6 @@ Indicator.prototype = {
new PopupMenu.PopupMenuItem(_("Set up a New Device...")),
new PopupMenu.PopupSeparatorMenuItem()];
this._hasDevices = false;
this._deviceSep = this._fullMenuItems[0]; // hidden if no device exists
this._fullMenuItems[1].connect('activate', function() {
GLib.spawn_command_line_async('bluetooth-sendto');
@ -89,11 +88,7 @@ Indicator.prototype = {
this._applet.connect('notify::show-full-menu', Lang.bind(this, this._updateFullMenu));
this._updateFullMenu();
this.menu.addAction(_("Bluetooth Settings"), function() {
Main.overview.hide()
let app = Shell.AppSystem.get_default().get_app('bluetooth-properties.desktop');
app.activate(-1);
});
this.menu.addSettingsAction(_("Bluetooth Settings"), 'bluetooth-properties.desktop');
this._applet.connect('pincode-request', Lang.bind(this, this._pinRequest));
this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest));
@ -162,10 +157,6 @@ Indicator.prototype = {
this._hasDevices = true;
}
}
if (this._hasDevices)
this._deviceSep.actor.show();
else
this._deviceSep.actor.hide();
},
_updateDeviceItem: function(item, device) {
@ -277,21 +268,15 @@ Indicator.prototype = {
switch (device.type) {
case GnomeBluetoothApplet.Type.KEYBOARD:
item.menu.addAction(_("Keyboard Settings"), function() {
GLib.spawn_command_line_async('gnome-control-center keyboard');
});
item.menu.addSettingsAction(_("Keyboard Settings"), 'gnome-keyboard-panel.desktop');
break;
case GnomeBluetoothApplet.Type.MOUSE:
item.menu.addAction(_("Mouse Settings"), function() {
GLib.spawn_command_line_async('gnome-control-center mouse');
});
item.menu.addSettingsAction(_("Mouse Settings"), 'gnome-mouse-panel.desktop');
break;
case GnomeBluetoothApplet.Type.HEADSET:
case GnomeBluetoothApplet.Type.HEADPHONES:
case GnomeBluetoothApplet.Type.OTHER_AUDIO:
item.menu.addAction(_("Sound Settings"), function() {
GLib.spawn_command_line_async('gnome-control-center sound');
});
item.menu.addSettingsAction(_("Sound Settings"), 'gnome-sound-panel.desktop');
break;
default:
break;
@ -303,8 +288,6 @@ Indicator.prototype = {
this._showAll(this._fullMenuItems);
if (this._hasDevices)
this._showAll(this._deviceItems);
else
this._deviceSep.actor.hide();
} else {
this._hideAll(this._fullMenuItems);
this._hideAll(this._deviceItems);

View File

@ -68,15 +68,33 @@ XKBIndicator.prototype = {
this._sync_config();
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addAction(_("Show Keyboard Layout..."), Lang.bind(this, function() {
this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, function() {
Main.overview.hide();
Util.spawn(['gkbd-keyboard-display', '-g', String(this._config.get_current_group() + 1)]);
}));
this.menu.addAction(_("Localization Settings"), function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().get_app('gnome-region-panel.desktop');
app.activate(-1);
});
this.menu.addSettingsAction(_("Region and Language Settings"), 'gnome-region-panel.desktop');
},
_adjust_group_names: function(names) {
// Disambiguate duplicate names with a subscript
// This is O(N^2) to avoid sorting names
// but N <= 4 so who cares?
for (let i = 0; i < names.length; i++) {
let name = names[i];
let cnt = 0;
for (let j = i + 1; j < names.length; j++) {
if (names[j] == name) {
cnt++;
// U+2081 SUBSCRIPT ONE
names[j] = name + String.fromCharCode(0x2081 + cnt);
}
}
if (cnt != 0)
names[i] = name + '\u2081';
}
return names;
},
_sync_config: function() {
@ -101,7 +119,7 @@ XKBIndicator.prototype = {
for (let i = 0; i < this._labelActors.length; i++)
this._labelActors[i].destroy();
let short_names = this._config.get_short_group_names();
let short_names = this._adjust_group_names(this._config.get_short_group_names());
this._selectedLayout = null;
this._layoutItems = [ ];

View File

@ -45,17 +45,6 @@ const NM80211ApSecurityFlags = NetworkManager['80211ApSecurityFlags'];
// (the remaining are placed into More...)
const NUM_VISIBLE_NETWORKS = 5;
const NMAppletHelperInterface = {
name: 'org.gnome.network_manager_applet',
methods: [
{ name: 'ConnectToHiddenNetwork', inSignature: '', outSignature: '' },
{ name: 'CreateWifiNetwork', inSignature: '', outSignature: '' },
{ name: 'ConnectTo8021xNetwork', inSignature: 'oo', outSignature: '' },
{ name: 'ConnectTo3gNetwork', inSignature: 'o', outSignature: '' }
],
};
const NMAppletProxy = DBus.makeProxyClass(NMAppletHelperInterface);
function macToArray(string) {
return string.split(':').map(function(el) {
return parseInt(el, 16);
@ -102,6 +91,13 @@ function sortAccessPoints(accessPoints) {
});
}
function ssidToLabel(ssid) {
let label = NetworkManager.utils_ssid_to_utf8(ssid);
if (!label)
label = _("<unknown>");
return label;
}
function NMNetworkMenuItem() {
this._init.apply(this, arguments);
}
@ -117,10 +113,7 @@ NMNetworkMenuItem.prototype = {
if (!title) {
let ssid = this.bestAP.get_ssid();
if (ssid)
title = NetworkManager.utils_ssid_to_utf8(ssid);
if (!title)
title = _("<unknown>");
title = ssidToLabel(ssid);
}
this._label = new St.Label({ text: title });
@ -631,15 +624,8 @@ NMDevice.prototype = {
this.emit('network-lost');
}
switch(newstate) {
case NetworkManager.DeviceState.NEED_AUTH:
// FIXME: make this have a real effect
// (currently we rely on a running nm-applet)
this.emit('need-auth');
break;
case NetworkManager.DeviceState.FAILED:
if (newstate == NetworkManager.DeviceState.FAILED) {
this.emit('activation-failed', reason);
break;
}
this._updateStatusItem();
@ -674,7 +660,7 @@ NMDevice.prototype = {
let dev_product = this.device.get_product();
let dev_vendor = this.device.get_vendor();
if (!dev_product || !dev_vendor)
return null;
return '';
let product = Util.fixupPCIDescription(dev_product);
let vendor = Util.fixupPCIDescription(dev_vendor);
@ -748,10 +734,6 @@ NMDeviceModem.prototype = {
this.mobileDevice = null;
this._connectionType = 'ppp';
this._applet_proxy = new NMAppletProxy(DBus.session,
'org.gnome.network_manager_applet',
'/org/gnome/network_manager_applet');
this._capabilities = device.current_capabilities;
if (this._capabilities & NetworkManager.DeviceModemCapabilities.GSM_UMTS) {
is_wwan = true;
@ -853,12 +835,10 @@ NMDeviceModem.prototype = {
},
_createAutomaticConnection: function() {
// Mobile wizard is handled by nm-applet for now...
this._applet_proxy.ConnectTo3gNetworkRemote(this.device.get_path(),
Lang.bind(this, function(results, err) {
if (err)
log(err);
}));
// Mobile wizard is too complex for the shell UI and
// is handled by the network panel
Util.spawn(['gnome-control-center', 'network',
'connect-3g', this.device.get_path()]);
return null;
}
};
@ -970,10 +950,6 @@ NMDeviceWireless.prototype = {
this._overflowItem = null;
this._networks = [ ];
this._applet_proxy = new NMAppletProxy(DBus.session,
'org.gnome.network_manager_applet',
'/org/gnome/network_manager_applet');
// breaking the layers with this, but cannot call
// this.connectionValid until I have a device
this.device = device;
@ -985,6 +961,16 @@ NMDeviceWireless.prototype = {
for (let i = 0; i < accessPoints.length; i++) {
// Access points are grouped by network
let ap = accessPoints[i];
if (ap.get_ssid() == null) {
// hidden access point cannot be added, we need to know
// the SSID and security details to connect
// nevertheless, the access point can acquire a SSID when
// NetworkManager connects to it (via nmcli or the control-center)
ap._notifySsidId = ap.connect('notify::ssid', Lang.bind(this, this._notifySsidCb));
continue;
}
let pos = this._findNetwork(ap);
let obj;
if (pos != -1) {
@ -998,7 +984,7 @@ NMDeviceWireless.prototype = {
item: null,
accessPoints: [ ap ]
};
obj.ssidText = NetworkManager.utils_ssid_to_utf8(obj.ssid);
obj.ssidText = ssidToLabel(obj.ssid);
this._networks.push(obj);
}
@ -1011,8 +997,14 @@ NMDeviceWireless.prototype = {
}
}
}
if (this.device.active_access_point) {
this._activeNetwork = this._networks[this._findNetwork(this.device.active_access_point)];
let networkPos = this._findNetwork(this.device.active_access_point);
if (networkPos == -1) // the connected access point is invisible
this._activeNetwork = null;
else
this._activeNetwork = this._networks[networkPos];
} else {
this._activeNetwork = null;
}
@ -1098,6 +1090,14 @@ NMDeviceWireless.prototype = {
}
},
_notifySsidCb: function(accessPoint) {
if (accessPoint.get_ssid() != null) {
accessPoint.disconnect(accessPoint._notifySsidId);
accessPoint._notifySsidId = 0;
this._accessPointAdded(this.device, accessPoint);
}
},
_activeApChanged: function() {
this._activeNetwork = null;
@ -1105,7 +1105,9 @@ NMDeviceWireless.prototype = {
if (activeAp) {
let pos = this._findNetwork(activeAp);
this._activeNetwork = this._networks[pos];
if (pos != -1)
this._activeNetwork = this._networks[pos];
}
// we don't refresh the view here, setActiveConnection will
@ -1180,6 +1182,9 @@ NMDeviceWireless.prototype = {
},
_findNetwork: function(accessPoint) {
if (accessPoint.get_ssid() == null)
return -1;
for (let i = 0; i < this._networks.length; i++) {
if (this._networkCompare(this._networks[i], accessPoint))
return i;
@ -1188,6 +1193,13 @@ NMDeviceWireless.prototype = {
},
_accessPointAdded: function(device, accessPoint) {
if (accessPoint.get_ssid() == null) {
// This access point is not visible yet
// Wait for it to get a ssid
accessPoint._notifySsidId = accessPoint.connect('notify::ssid', Lang.bind(this, this._notifySsidCb));
return;
}
let pos = this._findNetwork(accessPoint);
let apObj;
let needsupdate = false;
@ -1210,7 +1222,7 @@ NMDeviceWireless.prototype = {
item: null,
accessPoints: [ accessPoint ]
};
apObj.ssidText = NetworkManager.utils_ssid_to_utf8(apObj.ssid);
apObj.ssidText = ssidToLabel(apObj.ssid);
needsupdate = true;
}
@ -1423,13 +1435,12 @@ NMDeviceWireless.prototype = {
},
_createActiveConnectionItem: function() {
let activeAp = this.device.active_access_point;
let icon, title;
if (this._activeConnection._connection) {
let connection = this._activeConnection._connection;
if (activeAp)
this._activeConnectionItem = new NMNetworkMenuItem([ activeAp ], undefined,
{ reactive: false });
if (this._activeNetwork)
this._activeConnectionItem = new NMNetworkMenuItem(this._activeNetwork.accessPoints, undefined,
{ reactive: false });
else
this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(connection._name,
'network-wireless-connected',
@ -1437,9 +1448,9 @@ NMDeviceWireless.prototype = {
} else {
// We cannot read the connection (due to ACL, or API incompatibility), but we still show signal if we have it
let menuItem;
if (activeAp)
this._activeConnectionItem = new NMNetworkMenuItem([ activeAp ], undefined,
{ reactive: false });
if (this._activeNetwork)
this._activeConnectionItem = new NMNetworkMenuItem(this._activeNetwork.accessPoints, undefined,
{ reactive: false });
else
this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(_("Connected (private)"),
'network-wireless-connected',
@ -1486,13 +1497,10 @@ NMDeviceWireless.prototype = {
let accessPoints = sortAccessPoints(apObj.accessPoints);
if ( (accessPoints[0]._secType == NMAccessPointSecurity.WPA2_ENT)
|| (accessPoints[0]._secType == NMAccessPointSecurity.WPA_ENT)) {
// 802.1x-enabled APs get handled by nm-applet for now...
this._applet_proxy.ConnectTo8021xNetworkRemote(this.device.get_path(),
accessPoints[0].dbus_path,
Lang.bind(this, function(results, err) {
if (err)
log(err);
}));
// 802.1x-enabled APs require further configuration, so they're
// handled in gnome-control-center
Util.spawn(['gnome-control-center', 'network', 'connect-8021x-wifi',
this.device.get_path(), accessPoints[0].dbus_path]);
} else {
let connection = this._createAutomaticConnection(apObj);
this._client.add_and_activate_connection(connection, this.device, accessPoints[0].dbus_path, null)
@ -1550,9 +1558,9 @@ NMApplet.prototype = {
this._statusSection.addAction(_("Enable networking"), Lang.bind(this, function() {
this._client.networking_enabled = true;
}));
this._statusSection.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._statusSection.actor.hide();
this.menu.addMenuItem(this._statusSection);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices = { };
@ -1563,9 +1571,9 @@ NMApplet.prototype = {
};
this._devices.wired.section.addMenuItem(this._devices.wired.item);
this._devices.wired.section.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.wired.section.actor.hide();
this.menu.addMenuItem(this._devices.wired.section);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.wireless = {
section: new PopupMenu.PopupMenuSection(),
@ -1573,9 +1581,9 @@ NMApplet.prototype = {
item: this._makeToggleItem('wireless', _("Wireless"))
};
this._devices.wireless.section.addMenuItem(this._devices.wireless.item);
this._devices.wireless.section.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.wireless.section.actor.hide();
this.menu.addMenuItem(this._devices.wireless.section);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.wwan = {
section: new PopupMenu.PopupMenuSection(),
@ -1583,9 +1591,9 @@ NMApplet.prototype = {
item: this._makeToggleItem('wwan', _("Mobile broadband"))
};
this._devices.wwan.section.addMenuItem(this._devices.wwan.item);
this._devices.wwan.section.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.wwan.section.actor.hide();
this.menu.addMenuItem(this._devices.wwan.section);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.vpn = {
section: new PopupMenu.PopupMenuSection(),
@ -1598,15 +1606,10 @@ NMApplet.prototype = {
this._devices.vpn.item.updateForDevice(this._devices.vpn.device);
this._devices.vpn.section.addMenuItem(this._devices.vpn.item);
this._devices.vpn.section.addMenuItem(this._devices.vpn.device.section);
this._devices.vpn.section.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.vpn.section.actor.hide();
this.menu.addMenuItem(this._devices.vpn.section);
this.menu.addAction(_("Network Settings"), function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().get_app('gnome-network-panel.desktop');
app.activate(-1);
});
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addSettingsAction(_("Network Settings"), 'gnome-network-panel.desktop');
this._activeConnections = [ ];
this._connections = [ ];
@ -1660,8 +1663,7 @@ NMApplet.prototype = {
_ensureSource: function() {
if (!this._source) {
this._source = new NMMessageTraySource();
this._source._destroyId = this._source.connect('destroy', Lang.bind(this, function() {
this._source._destroyId = 0;
this._source.connect('destroy', Lang.bind(this, function() {
this._source = null;
}));
Main.messageTray.add(this._source);
@ -1709,6 +1711,28 @@ NMApplet.prototype = {
}
},
_notifyForDevice: function(device, iconName, title, text, urgency) {
if (device._notification)
device._notification.destroy();
/* must call after destroying previous notification,
or this._source will be cleared */
this._ensureSource();
let icon = new St.Icon({ icon_name: iconName,
icon_type: St.IconType.SYMBOLIC,
icon_size: this._source.ICON_SIZE
});
device._notification = new MessageTray.Notification(this._source, title, text,
{ icon: icon });
device._notification.setUrgency(urgency);
device._notification.setTransient(true);
device._notification.connect('destroy', function() {
device._notification = null;
});
this._source.notify(device._notification);
},
_deviceAdded: function(client, device) {
if (device._delegate) {
// already seen, not adding again
@ -1718,42 +1742,29 @@ NMApplet.prototype = {
if (wrapperClass) {
let wrapper = new wrapperClass(this._client, device, this._connections);
// FIXME: these notifications are duplicate with those exposed by nm-applet
// uncomment this code in 3.2, when we'll conflict with and kill nm-applet
/* wrapper._networkLostId = wrapper.connect('network-lost', Lang.bind(this, function(emitter) {
this._ensureSource();
let icon = new St.Icon({ icon_name: 'network-offline',
icon_type: St.IconType.SYMBOLIC,
icon_size: this._source.ICON_SIZE
});
let notification = new MessageTray.Notification(this._source,
_("Connectivity lost"),
_("You're no longer connected to the network"),
{ icon: icon });
this._source.notify(notification);
wrapper._networkLostId = wrapper.connect('network-lost', Lang.bind(this, function(device) {
this._notifyForDevice(device, 'network-offline',
_("Connectivity lost"),
_("You're no longer connected to the network"),
// set critical urgency to popup the notification automatically
MessageTray.Urgency.CRITICAL);
}));
wrapper._activationFailedId = wrapper.connect('activation-failed', Lang.bind(this, function(wrapper, reason) {
this._ensureSource();
let icon = new St.Icon({ icon_name: 'network-error',
icon_type: St.IconType.SYMBOLIC,
icon_size: this._source.ICON_SIZE,
});
let banner;
wrapper._activationFailedId = wrapper.connect('activation-failed', Lang.bind(this, function(device, reason) {
// XXX: nm-applet has no special text depending on reason
// but I'm not sure of this generic message
let notification = new MessageTray.Notification(this._source,
_("Connection failed"),
_("Activation of network connection failed"),
{ icon: icon });
this._source.notify(notification);
})); */
this._notifyForDevice(device, 'network-error',
_("Connection failed"),
_("Activation of network connection failed"),
MessageTray.Urgency.HIGH);
}));
wrapper._deviceStateChangedId = wrapper.connect('state-changed', Lang.bind(this, function(dev) {
this._syncSectionTitle(dev.category);
}));
wrapper._destroyId = wrapper.connect('destroy', function(wrapper) {
//wrapper.disconnect(wrapper._networkLostId);
//wrapper.disconnect(wrapper._activationFailedId);
wrapper.disconnect(wrapper._networkLostId);
wrapper.disconnect(wrapper._activationFailedId);
wrapper.disconnect(wrapper._deviceStateChangedId);
wrapper.disconnect(wrapper._destroyId);
});
let section = this._devices[wrapper.category].section;
let devices = this._devices[wrapper.category].devices;
@ -1798,11 +1809,8 @@ NMApplet.prototype = {
active._primaryDevice.setActiveConnection(null);
active._primaryDevice = null;
}
if (active._notifyStateId) {
active.disconnect(active._notifyStateId);
active._notifyStateId = 0;
}
if (active._inited) {
active.disconnect(active._notifyStateId);
active.disconnect(active._notifyDefaultId);
active.disconnect(active._notifyDefault6Id);
active._inited = false;
@ -1820,14 +1828,7 @@ NMApplet.prototype = {
if (!a._inited) {
a._notifyDefaultId = a.connect('notify::default', Lang.bind(this, this._updateIcon));
a._notifyDefault6Id = a.connect('notify::default6', Lang.bind(this, this._updateIcon));
if (a.state == NetworkManager.ActiveConnectionState.ACTIVATING) // prepare to notify to the user
a._notifyStateId = a.connect('notify::state', Lang.bind(this, this._notifyActiveConnection));
else {
// notify as soon as possible
Mainloop.idle_add(Lang.bind(this, function() {
this._notifyActiveConnection(a);
}));
}
a._notifyStateId = a.connect('notify::state', Lang.bind(this, this._updateIcon));
a._inited = true;
}
@ -1877,63 +1878,6 @@ NMApplet.prototype = {
this._mainConnection = activating || default_ip4 || default_ip6 || this._activeConnections[0] || null;
},
_notifyActiveConnection: function(active) {
// FIXME: duplicate notifications when nm-applet is running
// This code will come back when nm-applet is killed
this._syncNMState();
return;
if (active.state == NetworkManager.ActiveConnectionState.ACTIVATED) {
// notify only connections that are visible
if (active._connection) {
this._ensureSource();
let icon;
let banner;
switch (active._section) {
case NMConnectionCategory.WWAN:
icon = 'network-cellular-signal-excellent';
banner = _("You're now connected to mobile broadband connection '%s'").format(active._connection._name);
break;
case NMConnectionCategory.WIRELESS:
icon = 'network-wireless-signal-excellent';
banner = _("You're now connected to wireless network '%s'").format(active._connection._name);
break;
case NMConnectionCategory.WIRED:
icon = 'network-wired';
banner = _("You're now connected to wired network '%s'").format(active._connection._name);
break;
case NMConnectionCategory.VPN:
icon = 'network-vpn';
banner = _("You're now connected to VPN network '%s'").format(active._connection._name);
break;
default:
// a fallback for a generic 'connected' icon
icon = 'network-transmit-receive';
banner = _("You're now connected to '%s'").format(active._connection._name);
}
let iconActor = new St.Icon({ icon_name: icon,
icon_type: St.IconType.SYMBOLIC,
icon_size: this._source.ICON_SIZE
});
let notification = new MessageTray.Notification(this._source,
_("Connection established"),
banner,
{ icon: iconActor });
this._source.notify(notification);
}
if (active._stateChangeId) {
active.disconnect(active._stateChangeId);
active._stateChangeId = 0;
}
}
this._syncNMState();
},
_readConnections: function() {
let connections = this._settings.list_connections();
for (let i = 0; i < connections.length; i++) {

View File

@ -75,16 +75,11 @@ Indicator.prototype = {
this._batteryItem.addActor(this._primaryPercentage, { align: St.Align.END });
this.menu.addMenuItem(this._batteryItem);
this._deviceSep = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(this._deviceSep);
this._otherDevicePosition = 2;
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._otherDevicePosition = 2;
this.menu.addAction(_("Power Settings"),function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().get_app('gnome-power-panel.desktop');
app.activate(-1);
});
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop');
this._proxy.connect('Changed', Lang.bind(this, this._devicesChanged));
this._devicesChanged();
@ -96,7 +91,6 @@ Indicator.prototype = {
this._hasPrimary = false;
this._primaryDeviceId = null;
this._batteryItem.actor.hide();
this._deviceSep.actor.hide();
return;
}
let [device_id, device_type, icon, percentage, state, seconds] = device;
@ -126,12 +120,9 @@ Indicator.prototype = {
}
this._primaryPercentage.text = Math.round(percentage) + '%';
this._batteryItem.actor.show();
if (this._deviceItems.length > 0)
this._deviceSep.actor.show();
} else {
this._hasPrimary = false;
this._batteryItem.actor.hide();
this._deviceSep.actor.hide();
}
this._primaryDeviceId = device_id;
@ -144,7 +135,6 @@ Indicator.prototype = {
this._deviceItems = [];
if (error) {
this._deviceSep.actor.hide();
return;
}
@ -159,11 +149,6 @@ Indicator.prototype = {
this.menu.addMenuItem(item, this._otherDevicePosition + position);
position++;
}
if (this._hasPrimary && position > 0)
this._deviceSep.actor.show();
else
this._deviceSep.actor.hide();
}));
},

View File

@ -29,7 +29,7 @@ Indicator.prototype = {
PanelMenu.SystemStatusButton.prototype._init.call(this, 'audio-volume-muted', null);
this._control = new Gvc.MixerControl({ name: 'GNOME Shell Volume Control' });
this._control.connect('ready', Lang.bind(this, this._onControlReady));
this._control.connect('state-changed', Lang.bind(this, this._onControlStateChanged));
this._control.connect('default-sink-changed', Lang.bind(this, this._readOutput));
this._control.connect('default-source-changed', Lang.bind(this, this._readInput));
this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput));
@ -47,8 +47,7 @@ Indicator.prototype = {
this.menu.addMenuItem(this._outputTitle);
this.menu.addMenuItem(this._outputSlider);
this._separator = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(this._separator);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._input = null;
this._inputVolumeId = 0;
@ -61,11 +60,7 @@ Indicator.prototype = {
this.menu.addMenuItem(this._inputSlider);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addAction(_("Sound Settings"), function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().get_app('gnome-sound-panel.desktop');
app.activate(-1);
});
this.menu.addSettingsAction(_("Sound Settings"), 'gnome-sound-panel.desktop');
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
this._control.open();
@ -102,9 +97,14 @@ Indicator.prototype = {
this._notifyVolumeChange();
},
_onControlReady: function() {
this._readOutput();
this._readInput();
_onControlStateChanged: function() {
if (this._control.get_state() == Gvc.MixerControlState.READY) {
this._readOutput();
this._readInput();
this.actor.show();
} else {
this.actor.hide();
}
},
_readOutput: function() {
@ -140,7 +140,6 @@ Indicator.prototype = {
this._mutedChanged (null, null, '_input');
this._volumeChanged (null, null, '_input');
} else {
this._separator.actor.hide();
this._inputTitle.actor.hide();
this._inputSlider.actor.hide();
}
@ -163,11 +162,9 @@ Indicator.prototype = {
}
}
if (showInput) {
this._separator.actor.show();
this._inputTitle.actor.show();
this._inputSlider.actor.show();
} else {
this._separator.actor.hide();
this._inputTitle.actor.hide();
this._inputSlider.actor.hide();
}

View File

@ -1,345 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Gdm = imports.gi.Gdm;
const DBus = imports.dbus;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Tp = imports.gi.TelepathyGLib;
const UPowerGlib = imports.gi.UPowerGlib;
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 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';
// Adapted from gdm/gui/user-switch-applet/applet.c
//
// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
// Copyright (C) 2008,2009 Red Hat, Inc.
function StatusMenuButton() {
this._init();
}
StatusMenuButton.prototype = {
__proto__: PanelMenu.Button.prototype,
_init: function() {
PanelMenu.Button.prototype._init.call(this, 0.0);
let box = new St.BoxLayout({ name: 'panelStatusMenu' });
this.actor.set_child(box);
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
this._gdm = Gdm.UserManager.ref_default();
this._gdm.queue_load();
this._user = this._gdm.get_user(GLib.get_user_name());
this._presence = new GnomeSession.Presence();
this._presenceItems = {};
this._session = new GnomeSession.SessionManager();
this._haveShutdown = true;
this._account_mgr = Tp.AccountManager.dup()
this._upClient = new UPowerGlib.Client();
this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy();
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._iconBox = new St.Bin();
box.add(this._iconBox, { y_align: St.Align.MIDDLE, y_fill: false });
let textureCache = St.TextureCache.get_default();
this._availableIcon = new St.Icon({ icon_name: 'user-available', style_class: 'popup-menu-icon' });
this._busyIcon = new St.Icon({ icon_name: 'user-busy', style_class: 'popup-menu-icon' });
this._invisibleIcon = new St.Icon({ icon_name: 'user-invisible', style_class: 'popup-menu-icon' });
this._idleIcon = new St.Icon({ icon_name: 'user-idle', style_class: 'popup-menu-icon' });
this._presence.connect('StatusChanged', Lang.bind(this, this._updatePresenceIcon));
this._presence.getStatus(Lang.bind(this, this._updatePresenceIcon));
this._name = new St.Label();
box.add(this._name, { y_align: St.Align.MIDDLE, y_fill: false });
this._userLoadedId = this._user.connect('notify::is-loaded', Lang.bind(this, this._updateUserName));
this._userChangedId = this._user.connect('changed', Lang.bind(this, this._updateUserName));
this._createSubMenu();
this._gdm.connect('notify::is-loaded', Lang.bind(this, this._updateSwitchUser));
this._gdm.connect('user-added', Lang.bind(this, this._updateSwitchUser));
this._gdm.connect('user-removed', Lang.bind(this, this._updateSwitchUser));
this._lockdownSettings.connect('changed::' + DISABLE_USER_SWITCH_KEY,
Lang.bind(this, this._updateSwitchUser));
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
Lang.bind(this, this._updateLogout));
this._lockdownSettings.connect('changed::' + DISABLE_LOCK_SCREEN_KEY,
Lang.bind(this, this._updateLockScreen));
this._updateSwitchUser();
this._updateLogout();
this._updateLockScreen();
// Whether shutdown is available or not depends on both lockdown
// settings (disable-log-out) and Polkit policy - the latter doesn't
// notify, so we update the menu item each time the menu opens or
// the lockdown setting changes, which should be close enough.
this.menu.connect('open-state-changed', Lang.bind(this,
function(menu, open) {
if (open)
this._updateHaveShutdown();
}));
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
Lang.bind(this, this._updateHaveShutdown));
this._upClient.connect('notify::can-suspend', Lang.bind(this, this._updateSuspendOrPowerOff));
},
_onDestroy: function() {
this._user.disconnect(this._userLoadedId);
this._user.disconnect(this._userChangedId);
},
_updateUserName: function() {
if (this._user.is_loaded)
this._name.set_text(this._user.get_real_name());
else
this._name.set_text("");
},
_updateSessionSeparator: function() {
let sessionItemsVisible = this._loginScreenItem.actor.visible ||
this._logoutItem.actor.visible ||
this._lockScreenItem.actor.visible;
let showSessionSeparator = sessionItemsVisible &&
this._suspendOrPowerOffItem.actor.visible;
let showSettingsSeparator = sessionItemsVisible ||
this._suspendOrPowerOffItem.actor.visible;
if (showSessionSeparator)
this._sessionSeparator.actor.show();
else
this._sessionSeparator.actor.hide();
if (showSettingsSeparator)
this._settingsSeparator.actor.show();
else
this._settingsSeparator.actor.hide();
},
_updateSwitchUser: function() {
let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY);
if (allowSwitch && this._gdm.can_switch ())
this._loginScreenItem.actor.show();
else
this._loginScreenItem.actor.hide();
this._updateSessionSeparator();
},
_updateLogout: function() {
let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY);
if (allowLogout)
this._logoutItem.actor.show();
else
this._logoutItem.actor.hide();
this._updateSessionSeparator();
},
_updateLockScreen: function() {
let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
if (allowLockScreen)
this._lockScreenItem.actor.show();
else
this._lockScreenItem.actor.hide();
this._updateSessionSeparator();
},
_updateHaveShutdown: function() {
this._session.CanShutdownRemote(Lang.bind(this,
function(result, error) {
if (!error) {
this._haveShutdown = result;
this._updateSuspendOrPowerOff();
}
}));
},
_updateSuspendOrPowerOff: function() {
this._haveSuspend = this._upClient.get_can_suspend();
if (!this._suspendOrPowerOffItem)
return;
if (!this._haveShutdown && !this._haveSuspend)
this._suspendOrPowerOffItem.actor.hide();
else
this._suspendOrPowerOffItem.actor.show();
this._updateSessionSeparator();
// If we can't suspend show Power Off... instead
// and disable the alt key
if (!this._haveSuspend) {
this._suspendOrPowerOffItem.updateText(_("Power Off..."), null);
} else if (!this._haveShutdown) {
this._suspendOrPowerOffItem.updateText(_("Suspend"), null);
} else {
this._suspendOrPowerOffItem.updateText(_("Suspend"), _("Power Off..."));
}
},
_updatePresenceIcon: function(presence, status) {
if (status == GnomeSession.PresenceStatus.AVAILABLE)
this._iconBox.child = this._availableIcon;
else if (status == GnomeSession.PresenceStatus.BUSY)
this._iconBox.child = this._busyIcon;
else if (status == GnomeSession.PresenceStatus.INVISIBLE)
this._iconBox.child = this._invisibleIcon;
else
this._iconBox.child = this._idleIcon;
for (let itemStatus in this._presenceItems)
this._presenceItems[itemStatus].setShowDot(itemStatus == status);
},
_createSubMenu: function() {
let item;
item = new PopupMenu.PopupImageMenuItem(_("Available"), 'user-available');
item.connect('activate', Lang.bind(this, this._setPresenceStatus, GnomeSession.PresenceStatus.AVAILABLE));
this.menu.addMenuItem(item);
this._presenceItems[GnomeSession.PresenceStatus.AVAILABLE] = item;
item = new PopupMenu.PopupImageMenuItem(_("Busy"), 'user-busy');
item.connect('activate', Lang.bind(this, this._setPresenceStatus, GnomeSession.PresenceStatus.BUSY));
this.menu.addMenuItem(item);
this._presenceItems[GnomeSession.PresenceStatus.BUSY] = item;
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("My Account"));
item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
this.menu.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("System Settings"));
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
this.menu.addMenuItem(item);
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
this._settingsSeparator = item;
item = new PopupMenu.PopupMenuItem(_("Lock Screen"));
item.connect('activate', Lang.bind(this, this._onLockScreenActivate));
this.menu.addMenuItem(item);
this._lockScreenItem = item;
item = new PopupMenu.PopupMenuItem(_("Switch User"));
item.connect('activate', Lang.bind(this, this._onLoginScreenActivate));
this.menu.addMenuItem(item);
this._loginScreenItem = item;
item = new PopupMenu.PopupMenuItem(_("Log Out..."));
item.connect('activate', Lang.bind(this, this._onQuitSessionActivate));
this.menu.addMenuItem(item);
this._logoutItem = item;
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
this._sessionSeparator = item;
item = new PopupMenu.PopupAlternatingMenuItem(_("Suspend"),
_("Power Off..."));
this.menu.addMenuItem(item);
this._suspendOrPowerOffItem = item;
item.connect('activate', Lang.bind(this, this._onSuspendOrPowerOffActivate));
this._updateSuspendOrPowerOff();
},
_setPresenceStatus: function(item, event, status) {
this._presence.setStatus(status);
this._setIMStatus(status);
},
_onMyAccountActivate: function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().get_app('gnome-user-accounts-panel.desktop');
app.activate(-1);
},
_onPreferencesActivate: function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().get_app('gnome-control-center.desktop');
app.activate(-1);
},
_onLockScreenActivate: function() {
Main.overview.hide();
this._screenSaverProxy.LockRemote();
},
_onLoginScreenActivate: function() {
Main.overview.hide();
// 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() {
Main.overview.hide();
this._session.LogoutRemote(0);
},
_onSuspendOrPowerOffActivate: function() {
Main.overview.hide();
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);
}));
} else {
this._session.ShutdownRemote();
}
},
_setIMStatus: function(session_status) {
let [presence_type, presence_status, msg] = this._account_mgr.get_most_available_presence();
let type, status;
// We change the IM presence only if there are connected accounts
if (presence_type == Tp.ConnectionPresenceType.UNSET ||
presence_type == Tp.ConnectionPresenceType.OFFLINE ||
presence_type == Tp.ConnectionPresenceType.UNKNOWN ||
presence_type == Tp.ConnectionPresenceType.ERROR)
return;
if (session_status == GnomeSession.PresenceStatus.AVAILABLE) {
type = Tp.ConnectionPresenceType.AVAILABLE;
status = "available";
}
else if (session_status == GnomeSession.PresenceStatus.BUSY) {
type = Tp.ConnectionPresenceType.BUSY;
status = "busy";
}
else {
return;
}
this._account_mgr.set_all_requested_presences(type, status, msg);
}
};

View File

@ -1,6 +1,7 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const DBus = imports.dbus;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
@ -11,6 +12,7 @@ const Tpl = imports.gi.TelepathyLogger;
const Tp = imports.gi.TelepathyGLib;
const History = imports.misc.history;
const Params = imports.misc.params;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
@ -43,11 +45,16 @@ let contactFeatures = [Tp.ContactFeature.ALIAS,
function makeMessageFromTpMessage(tpMessage, direction) {
let [text, flags] = tpMessage.to_text();
let timestamp = tpMessage.get_sent_timestamp();
if (timestamp == 0)
timestamp = tpMessage.get_received_timestamp();
return {
messageType: tpMessage.get_message_type(),
text: text,
sender: tpMessage.sender.alias,
timestamp: tpMessage.get_received_timestamp(),
timestamp: timestamp,
direction: direction
};
}
@ -80,8 +87,8 @@ Client.prototype = {
// channel matching its filters is detected.
// The second argument, recover, means _observeChannels will be run
// for any existing channel as well.
let dbus = Tp.DBusDaemon.dup();
this._tpClient = new Shell.TpClient({ 'dbus_daemon': dbus,
this._accountManager = Tp.AccountManager.dup();
this._tpClient = new Shell.TpClient({ 'account-manager': this._accountManager,
'name': 'GnomeShell',
'uniquify-name': true })
this._tpClient.set_observe_channels_func(
@ -91,6 +98,11 @@ Client.prototype = {
this._tpClient.set_handle_channels_func(
Lang.bind(this, this._handleChannels));
// Workaround for gjs not supporting GPtrArray in signals.
// See BGO bug #653941 for context.
this._tpClient.set_contact_list_changed_func(
Lang.bind(this, this._contactListChanged));
// Allow other clients (such as Empathy) to pre-empt our channels if
// needed
this._tpClient.set_delegated_channels_callback(
@ -101,6 +113,22 @@ Client.prototype = {
} catch (e) {
throw new Error('Couldn\'t register Telepathy client. Error: \n' + e);
}
// Watch subscription requests and connection errors
this._subscriptionSource = null;
this._accountSource = null;
let factory = this._accountManager.get_factory();
factory.add_account_features([Tp.Account.get_feature_quark_connection()]);
factory.add_connection_features([Tp.Connection.get_feature_quark_contact_list()]);
factory.add_contact_features([Tp.ContactFeature.SUBSCRIPTION_STATES,
Tp.ContactFeature.ALIAS,
Tp.ContactFeature.AVATAR_DATA]);
this._accountManager.connect('account-validity-changed',
Lang.bind(this, this._accountValidityChanged));
this._accountManager.prepare_async(null, Lang.bind(this, this._accountManagerPrepared));
},
_observeChannels: function(observer, account, conn, channels,
@ -225,7 +253,8 @@ Client.prototype = {
// 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');
let source = new ApproverSource(dispatchOp, _("Invitation"),
Shell.util_icon_from_string('system-users'));
Main.messageTray.add(source);
let notif = new RoomInviteNotification(source, dispatchOp, channel, contacts[0]);
@ -243,6 +272,8 @@ Client.prototype = {
else if (chanType == Tp.IFACE_CHANNEL_TYPE_STREAMED_MEDIA ||
chanType == 'org.freedesktop.Telepathy.Channel.Type.Call.DRAFT')
this._approveCall(account, conn, channel, dispatchOp, context);
else if (chanType == Tp.IFACE_CHANNEL_TYPE_FILE_TRANSFER)
this._approveFileTransfer(account, conn, channel, dispatchOp, context);
},
_approveTextChannel: function(account, conn, channel, dispatchOp, context) {
@ -288,7 +319,9 @@ Client.prototype = {
isVideo = true;
// We got the TpContact
let source = new ApproverSource(dispatchOp, _("Call"), isVideo ? 'camera-web' : 'audio-input-microphone');
let source = new ApproverSource(dispatchOp, _("Call"), isVideo ?
Shell.util_icon_from_string('camera-web') :
Shell.util_icon_from_string('audio-input-microphone'));
Main.messageTray.add(source);
let notif = new AudioVideoNotification(source, dispatchOp, channel, contacts[0], isVideo);
@ -296,9 +329,141 @@ Client.prototype = {
context.accept();
},
_approveFileTransfer: function(account, conn, channel, dispatchOp, context) {
let [targetHandle, targetHandleType] = channel.get_handle();
Shell.get_tp_contacts(conn, [targetHandle],
contactFeatures,
Lang.bind(this, this._createFileTransferSource, channel, context, dispatchOp));
},
_createFileTransferSource: function(connection, contacts, failed, channel, context, dispatchOp) {
if (contacts.length < 1) {
Shell.decline_dispatch_op(context, 'Failed to get file sender');
return;
}
// Use the icon of the file being transferred
let gicon = Gio.content_type_get_icon(channel.get_mime_type());
// We got the TpContact
let source = new ApproverSource(dispatchOp, _("File Transfer"), gicon);
Main.messageTray.add(source);
let notif = new FileTransferNotification(source, dispatchOp, channel, contacts[0]);
source.notify(notif);
context.accept();
},
_delegatedChannelsCb: function(client, channels) {
// Nothing to do as we don't make a distinction between observed and
// handled channels.
},
_accountManagerPrepared: function(am, result) {
am.prepare_finish(result);
let accounts = am.get_valid_accounts();
for (let i = 0; i < accounts.length; i++) {
this._accountValidityChanged(am, accounts[i], true);
}
},
_accountValidityChanged: function(am, account, valid) {
if (!valid)
return;
// It would be better to connect to "status-changed" but we cannot.
// See discussion in https://bugzilla.gnome.org/show_bug.cgi?id=654159
account.connect("notify::connection-status",
Lang.bind(this, this._accountConnectionStatusNotifyCb));
account.connect('notify::connection',
Lang.bind(this, this._connectionChanged));
this._connectionChanged(account);
},
_connectionChanged: function(account) {
let conn = account.get_connection();
if (conn == null)
return;
this._tpClient.grab_contact_list_changed(conn);
if (conn.get_contact_list_state() == Tp.ContactListState.SUCCESS) {
this._contactListChanged(conn, conn.dup_contact_list(), []);
}
},
_contactListChanged: function(conn, added, removed) {
for (let i = 0; i < added.length; i++) {
let contact = added[i];
contact.connect('subscription-states-changed',
Lang.bind(this, this._subscriptionStateChanged));
this._subscriptionStateChanged(contact);
}
},
_subscriptionStateChanged: function(contact) {
if (contact.get_publish_state() != Tp.SubscriptionState.ASK)
return;
/* Implicitly accept publish requests if contact is already subscribed */
if (contact.get_subscribe_state() == Tp.SubscriptionState.YES ||
contact.get_subscribe_state() == Tp.SubscriptionState.ASK) {
contact.authorize_publication_async(function(src, result) {
src.authorize_publication_finish(result)});
return;
}
/* Display notification to ask user to accept/reject request */
let source = this._ensureSubscriptionSource();
Main.messageTray.add(source);
let notif = new SubscriptionRequestNotification(source, contact);
source.notify(notif);
},
_ensureSubscriptionSource: function() {
if (this._subscriptionSource == null) {
this._subscriptionSource = new MultiNotificationSource(
_("Subscription request"), 'gtk-dialog-question');
this._subscriptionSource.connect('destroy', Lang.bind(this, function () {
this._subscriptionSource = null;
}));
}
return this._subscriptionSource;
},
_accountConnectionStatusNotifyCb: function(account) {
let connectionError = account.connection_error;
if (account.connection_status != Tp.ConnectionStatus.DISCONNECTED ||
connectionError == Tp.error_get_dbus_name(Tp.Error.CANCELLED)) {
return;
}
/* Display notification that account failed to connect */
let source = this._ensureAccountSource();
Main.messageTray.add(source);
let notif = new AccountNotification(source, account, connectionError);
source.notify(notif);
},
_ensureAccountSource: function() {
if (this._accountSource == null) {
this._accountSource = new MultiNotificationSource(
_("Connection error"), 'gtk-dialog-error');
this._accountSource.connect('destroy', Lang.bind(this, function () {
this._accountSource = null;
}));
}
return this._accountSource;
}
};
@ -326,6 +491,7 @@ ChatSource.prototype = {
this._notification = new ChatNotification(this);
this._notification.setUrgency(MessageTray.Urgency.HIGH);
this._notifyTimeoutId = 0;
// We ack messages when the message box is collapsed if user has
// interacted with it before and so read the messages:
@ -487,7 +653,22 @@ ChatSource.prototype = {
message = makeMessageFromTpMessage(message, NotificationDirection.RECEIVED);
this._notification.appendMessage(message);
this.notify();
// Wait a bit before notifying for the received message, a handler
// could ack it in the meantime.
if (this._notifyTimeoutId != 0)
Mainloop.source_remove(this._notifyTimeoutId);
this._notifyTimeoutId = Mainloop.timeout_add(500,
Lang.bind(this, this._notifyTimeout));
},
_notifyTimeout: function() {
if (this._pendingMessages.length != 0)
this.notify();
this._notifyTimeoutId = 0;
return false;
},
// This is called for both messages we send from
@ -576,13 +757,10 @@ ChatSource.prototype = {
},
_ackMessages: function() {
if (this._pendingMessages.length == 0)
return;
// Don't clear our messages here, tp-glib will send a
// 'pending-message-removed' for each one.
this._channel.ack_messages_async(this._pendingMessages, Lang.bind(this, function(src, result) {
this._channel.ack_messages_finish(result);}));
this._channel.ack_all_pending_messages_async(Lang.bind(this, function(src, result) {
this._channel.ack_all_pending_messages_finish(result);}));
},
_summaryItemClicked: function(source, button) {
@ -623,6 +801,8 @@ ChatNotification.prototype = {
this._oldMaxScrollAdjustment = 0;
this._createScrollArea();
this._lastGroup = null;
this._lastGroupActor = null;
this._scrollArea.vscroll.adjustment.connect('changed', Lang.bind(this, function(adjustment) {
let currentValue = adjustment.value + adjustment.page_size;
@ -650,12 +830,10 @@ ChatNotification.prototype = {
* @noTimestamp: Whether to add a timestamp. If %true, no timestamp
* will be added, regardless of the difference since the
* last timestamp
* @styles: A list of CSS class names.
*/
appendMessage: function(message, noTimestamp, styles) {
appendMessage: function(message, noTimestamp) {
let messageBody = GLib.markup_escape_text(message.text, -1);
styles = styles || [];
styles.push(message.direction);
let styles = [message.direction];
if (message.messageType == Tp.ChannelTextMessageType.ACTION) {
let senderAlias = GLib.markup_escape_text(message.sender, -1);
@ -668,7 +846,14 @@ ChatNotification.prototype = {
bannerMarkup: true });
}
this._append(messageBody, styles, message.timestamp, noTimestamp);
let group = (message.direction == NotificationDirection.RECEIVED ?
'received' : 'sent');
this._append({ body: messageBody,
group: group,
styles: styles,
timestamp: message.timestamp,
noTimestamp: noTimestamp });
},
_filterMessages: function() {
@ -694,24 +879,65 @@ ChatNotification.prototype = {
for (let i = 0; i < expired.length; i++)
expired[i].actor.destroy();
}
let groups = this._contentArea.get_children();
for (let i = 0; i < groups.length; i ++) {
let group = groups[i];
if (group.get_children().length == 0)
group.destroy();
}
},
_append: function(text, styles, timestamp, noTimestamp) {
/**
* _append:
* @props: An object with the properties:
* body: The text of the message.
* group: The group of the message, one of:
* 'received', 'sent', 'meta'.
* styles: Style class names for the message to have.
* timestamp: The timestamp of the message.
* noTimestamp: suppress timestamp signal?
* childProps: props to add the actor with.
*/
_append: function(props) {
let currentTime = (Date.now() / 1000);
if (!timestamp)
timestamp = currentTime;
props = Params.parse(props, { body: null,
group: null,
styles: [],
timestamp: currentTime,
noTimestamp: false,
childProps: null });
// Reset the old message timeout
if (this._timestampTimeoutId)
Mainloop.source_remove(this._timestampTimeoutId);
let body = this.addBody(text, true);
let highlighter = new MessageTray.URLHighlighter(props.body,
true, // line wrap?
true); // allow markup?
let body = highlighter.actor;
let styles = props.styles;
for (let i = 0; i < styles.length; i ++)
body.add_style_class_name(styles[i]);
this._history.unshift({ actor: body, time: timestamp, realMessage: true });
let group = props.group;
if (group != this._lastGroup) {
let style = 'chat-group-' + group;
this._lastGroup = group;
this._lastGroupActor = new St.BoxLayout({ style_class: style,
vertical: true });
this.addActor(this._lastGroupActor);
}
if (!noTimestamp) {
this._lastGroupActor.add(body, props.childProps);
let timestamp = props.timestamp;
this._history.unshift({ actor: body, time: timestamp,
realMessage: group != 'meta' });
if (!props.noTimestamp) {
if (timestamp < currentTime - SCROLLBACK_IMMEDIATE_TIME)
this.appendTimestamp();
else
@ -738,18 +964,18 @@ ChatNotification.prototype = {
If applicable, replace %X with a strftime format valid for your
locale, without seconds. */
// xgettext:no-c-format
format = _("Sent at %X on %A");
// FIXME: The next two are stolen from calendar.js with the comment to avoid
// a string-freeze break. They should be replaced with better strings
// with 'Sent at', appropriate context and appropriate translator comment.
format = _("Sent at <b>%X</b> on <b>%A</b>");
} else if (date.getYear() == now.getYear()) {
/* Translators: Shown on calendar heading when selected day occurs on current year */
format = C_("calendar heading", "%A, %B %d");
/* Translators: this is a time format in the style of "Wednesday, May 25",
shown when you get a chat message in the same year. */
// xgettext:no-c-format
format = _("Sent on <b>%A</b>, <b>%B %d</b>");
} else {
/* Translators: Shown on calendar heading when selected day occurs on different year */
format = C_("calendar heading", "%A, %B %d, %Y");
/* Translators: this is a time format in the style of "Wednesday, May 25, 2012",
shown when you get a chat message in a different year. */
// xgettext:no-c-format
format = _("Sent on <b>%A</b>, <b>%B %d</b>, %Y");
}
return date.toLocaleFormat(format);
@ -759,11 +985,13 @@ ChatNotification.prototype = {
let lastMessageTime = this._history[0].time;
let lastMessageDate = new Date(lastMessageTime * 1000);
let timeLabel = this.addBody(this._formatTimestamp(lastMessageDate), false, { expand: true, x_fill: false, x_align: St.Align.END });
timeLabel.add_style_class_name('chat-meta-message');
this._history.unshift({ actor: timeLabel, time: lastMessageTime, realMessage: false });
this._timestampTimeoutId = 0;
let timeLabel = this._append({ body: this._formatTimestamp(lastMessageDate),
group: 'meta',
styles: ['chat-meta-message'],
childProps: { expand: true, x_fill: false,
x_align: St.Align.END },
noTimestamp: true,
timestamp: lastMessageTime });
this._filterMessages();
@ -775,9 +1003,10 @@ ChatNotification.prototype = {
this.update(text, null, { customContent: true, titleMarkup: true });
else
this.update(this.source.title, null, { customContent: true });
let label = this.addBody(text, true);
label.add_style_class_name('chat-meta-message');
this._history.unshift({ actor: label, time: (Date.now() / 1000), realMessage: false});
let label = this._append({ body: text,
group: 'meta',
styles: ['chat-meta-message'] });
this._filterMessages();
},
@ -789,9 +1018,11 @@ ChatNotification.prototype = {
/* Translators: this is the other person changing their old IM name to their new
IM name. */
let message = '<i>' + _("%s is now known as %s").format(oldAlias, newAlias) + '</i>';
let label = this.addBody(message, true);
label.add_style_class_name('chat-meta-message');
this._history.unshift({ actor: label, time: (Date.now() / 1000), realMessage: false });
let label = this._append({ body: text,
group: 'meta',
styles: ['chat-meta-message'] });
this.update(newAlias, null, { customContent: true });
this._filterMessages();
@ -844,17 +1075,17 @@ ChatNotification.prototype = {
}
};
function ApproverSource(dispatchOp, text, icon) {
this._init(dispatchOp, text, icon);
function ApproverSource(dispatchOp, text, gicon) {
this._init(dispatchOp, text, gicon);
}
ApproverSource.prototype = {
__proto__: MessageTray.Source.prototype,
_init: function(dispatchOp, text, icon) {
_init: function(dispatchOp, text, gicon) {
MessageTray.Source.prototype._init.call(this, text);
this._icon = icon;
this._gicon = gicon;
this._setSummaryIcon(this.createNotificationIcon());
this._dispatchOp = dispatchOp;
@ -877,7 +1108,7 @@ ApproverSource.prototype = {
},
createNotificationIcon: function() {
return new St.Icon({ icon_name: this._icon,
return new St.Icon({ gicon: this._gicon,
icon_type: St.IconType.FULLCOLOR,
icon_size: this.ICON_SIZE });
}
@ -971,3 +1202,321 @@ AudioVideoNotification.prototype = {
}));
}
};
// File Transfer
function FileTransferNotification(source, dispatchOp, channel, contact) {
this._init(source, dispatchOp, channel, contact);
}
FileTransferNotification.prototype = {
__proto__: MessageTray.Notification.prototype,
_init: function(source, dispatchOp, channel, contact) {
MessageTray.Notification.prototype._init.call(this,
source,
/* To translators: The first parameter is
* the contact's alias and the second one is the
* file name. The string will be something
* like: "Alice is sending you test.ogg"
*/
_("%s is sending you %s").format(contact.get_alias(),
channel.get_filename()),
null,
{ customContent: true });
this.setResident(true);
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();
}));
}
};
// A notification source that can embed multiple notifications
function MultiNotificationSource(title, icon) {
this._init(title, icon);
}
MultiNotificationSource.prototype = {
__proto__: MessageTray.Source.prototype,
_init: function(title, icon) {
MessageTray.Source.prototype._init.call(this, title);
this._icon = icon;
this._setSummaryIcon(this.createNotificationIcon());
this._nbNotifications = 0;
},
notify: function(notification) {
MessageTray.Source.prototype.notify.call(this, notification);
this._nbNotifications += 1;
// Display the source while there is at least one notification
notification.connect('destroy', Lang.bind(this, function () {
this._nbNotifications -= 1;
if (this._nbNotifications == 0)
this.destroy();
}));
},
createNotificationIcon: function() {
return new St.Icon({ gicon: Shell.util_icon_from_string(this._icon),
icon_type: St.IconType.FULLCOLOR,
icon_size: this.ICON_SIZE });
}
};
// Subscription request
function SubscriptionRequestNotification(source, contact) {
this._init(source, contact);
}
SubscriptionRequestNotification.prototype = {
__proto__: MessageTray.Notification.prototype,
_init: function(source, contact) {
MessageTray.Notification.prototype._init.call(this, source,
/* To translators: The parameter is the contact's alias */
_("%s would like permission to see when you are online").format(contact.get_alias()),
null, { customContent: true });
this._contact = contact;
this._connection = contact.get_connection();
let layout = new St.BoxLayout({ vertical: false });
// Display avatar
let iconBox = new St.Bin({ style_class: 'avatar-box' });
iconBox._size = 48;
let textureCache = St.TextureCache.get_default();
let file = contact.get_avatar_file();
if (file) {
let uri = file.get_uri();
iconBox.child = textureCache.load_uri_async(uri, iconBox._size, iconBox._size);
}
else {
iconBox.child = new St.Icon({ icon_name: 'avatar-default',
icon_type: St.IconType.FULLCOLOR,
icon_size: iconBox._size });
}
layout.add(iconBox);
// subscription request message
let label = new St.Label({ style_class: 'subscription-message',
text: contact.get_publish_request() });
layout.add(label);
this.addActor(layout);
this.addButton('decline', _("Decline"));
this.addButton('accept', _("Accept"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
switch (action) {
case 'decline':
contact.remove_async(function(src, result) {
src.remove_finish(result)});
break;
case 'accept':
// Authorize the contact and request to see his status as well
contact.authorize_publication_async(function(src, result) {
src.authorize_publication_finish(result)});
contact.request_subscription_async('', function(src, result) {
src.request_subscription_finish(result)});
break;
}
// rely on _subscriptionStatesChangedCb to destroy the
// notification
}));
this._changedId = contact.connect('subscription-states-changed',
Lang.bind(this, this._subscriptionStatesChangedCb));
this._invalidatedId = this._connection.connect('invalidated',
Lang.bind(this, this.destroy));
},
destroy: function() {
if (this._changedId != 0) {
this._contact.disconnect(this._changedId);
this._changedId = 0;
}
if (this._invalidatedId != 0) {
this._connection.disconnect(this._invalidatedId);
this._invalidatedId = 0;
}
MessageTray.Notification.prototype.destroy.call(this);
},
_subscriptionStatesChangedCb: function(contact, subscribe, publish, msg) {
// Destroy the notification if the subscription request has been
// answered
if (publish != Tp.SubscriptionState.ASK)
this.destroy();
}
};
function AccountNotification(source, account, connectionError) {
this._init(source, account, connectionError);
}
// Messages from empathy/libempathy/empathy-utils.c
// create_errors_to_message_hash()
/* Translator note: these should be the same messages that are
* used in Empathy, so just copy and paste from there. */
let _connectionErrorMessages = {};
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.NETWORK_ERROR)]
= _("Network error");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.AUTHENTICATION_FAILED)]
= _("Authentication failed");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.ENCRYPTION_ERROR)]
= _("Encryption error");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_NOT_PROVIDED)]
= _("Certificate not provided");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_UNTRUSTED)]
= _("Certificate untrusted");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_EXPIRED)]
= _("Certificate expired");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_NOT_ACTIVATED)]
= _("Certificate not activated");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_HOSTNAME_MISMATCH)]
= _("Certificate hostname mismatch");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_FINGERPRINT_MISMATCH)]
= _("Certificate fingerprint mismatch");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_SELF_SIGNED)]
= _("Certificate self-signed");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CANCELLED)]
= _("Status is set to offline");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.ENCRYPTION_NOT_AVAILABLE)]
= _("Encryption is not available");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_INVALID)]
= _("Certificate is invalid");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CONNECTION_REFUSED)]
= _("Connection has been refused");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CONNECTION_FAILED)]
= _("Connection can't be established");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CONNECTION_LOST)]
= _("Connection has been lost");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.ALREADY_CONNECTED)]
= _("This resource is already connected to the server");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CONNECTION_REPLACED)]
= _("Connection has been replaced by a new connection using the "
+ "same resource");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.REGISTRATION_EXISTS)]
= _("The account already exists on the server");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.SERVICE_BUSY)]
= _("Server is currently too busy to handle the connection");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_REVOKED)]
= _("Certificate has been revoked");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_INSECURE)]
= _("Certificate uses an insecure cipher algorithm or is "
+ "cryptographically weak");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_LIMIT_EXCEEDED)]
= _("The length of the server certificate, or the depth of the "
+ "server certificate chain, exceed the limits imposed by the "
+ "cryptography library");
AccountNotification.prototype = {
__proto__: MessageTray.Notification.prototype,
_init: function(source, account, connectionError) {
MessageTray.Notification.prototype._init.call(this, source,
/* translators: argument is the account name, like
* name@jabber.org for example. */
_("Connection to %s failed").format(account.get_display_name()),
null, { customContent: true });
let message;
if (connectionError in _connectionErrorMessages) {
message = _connectionErrorMessages[connectionError];
} else {
message = _("Unknown reason");
}
this._account = account;
this.addBody(message);
this.addButton('reconnect', _("Reconnect"));
this.addButton('edit', _("Edit account"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
switch (action) {
case 'reconnect':
// If it fails again, a new notification should pop up with the
// new error.
account.reconnect_async(null, null);
break;
case 'edit':
let cmd = '/usr/bin/empathy-accounts'
+ ' --select-account=%s'
.format(account.get_path_suffix());
let app_info = Gio.app_info_create_from_commandline(cmd, null, 0,
null);
app_info.launch([], null, null);
break;
}
this.destroy();
}));
this._enabledId = account.connect('notify::enabled',
Lang.bind(this, function() {
if (!account.is_enabled())
this.destroy();
}));
this._invalidatedId = account.connect('invalidated',
Lang.bind(this, this.destroy));
this._connectionStatusId = account.connect('notify::connection-status',
Lang.bind(this, function() {
if (account.connection_status != Tp.ConnectionStatus.DISCONNECTED)
this.destroy();
}));
},
destroy: function() {
if (this._enabledId != 0) {
this._account.disconnect(this._enabledId);
this._enabledId = 0;
}
if (this._invalidatedId != 0) {
this._account.disconnect(this._invalidatedId);
this._invalidatedId = 0;
}
if (this._connectionStatusId != 0) {
this._account.disconnect(this._connectionStatusId);
this._connectionStatusId = 0;
}
MessageTray.Notification.prototype.destroy.call(this);
}
};

646
js/ui/userMenu.js Normal file
View File

@ -0,0 +1,646 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const AccountsService = imports.gi.AccountsService;
const DBus = imports.dbus;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Tp = imports.gi.TelepathyGLib;
const UPowerGlib = imports.gi.UPowerGlib;
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 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 WRAP_WIDTH = 150;
const DIALOG_ICON_SIZE = 64;
const IMStatus = {
AVAILABLE: 0,
BUSY: 1,
HIDDEN: 2,
AWAY: 3,
IDLE: 4,
OFFLINE: 5,
LAST: 6
};
// Adapted from gdm/gui/user-switch-applet/applet.c
//
// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
// Copyright (C) 2008,2009 Red Hat, Inc.
function IMStatusItem(label, iconName) {
this._init(label, iconName);
}
IMStatusItem.prototype = {
__proto__: PopupMenu.PopupBaseMenuItem.prototype,
_init: function(label, iconName) {
PopupMenu.PopupBaseMenuItem.prototype._init.call(this);
this.actor.add_style_class_name('status-chooser-status-item');
this._icon = new St.Icon({ style_class: 'popup-menu-icon' });
this.addActor(this._icon);
if (iconName)
this._icon.icon_name = iconName;
this.label = new St.Label({ text: label });
this.addActor(this.label);
}
};
function IMUserNameItem() {
this._init();
}
IMUserNameItem.prototype = {
__proto__: PopupMenu.PopupBaseMenuItem.prototype,
_init: function() {
PopupMenu.PopupBaseMenuItem.prototype._init.call(this,
{ reactive: false,
style_class: 'status-chooser-user-name' });
this._wrapper = new Shell.GenericContainer();
this._wrapper.connect('get-preferred-width',
Lang.bind(this, this._wrapperGetPreferredWidth));
this._wrapper.connect('get-preferred-height',
Lang.bind(this, this._wrapperGetPreferredHeight));
this._wrapper.connect('allocate',
Lang.bind(this, this._wrapperAllocate));
this.addActor(this._wrapper, { expand: true, span: -1 });
this.label = new St.Label();
this.label.clutter_text.set_line_wrap(true);
this._wrapper.add_actor(this.label);
},
_wrapperGetPreferredWidth: function(actor, forHeight, alloc) {
[alloc.min_size, alloc.natural_size] = this.label.get_preferred_width(-1);
if (alloc.natural_size > WRAP_WIDTH)
alloc.natural_size = WRAP_WIDTH;
},
_wrapperGetPreferredHeight: function(actor, forWidth, alloc) {
let minWidth, natWidth;
[alloc.min_size, alloc.natural_size] = this.label.get_preferred_height(forWidth);
[minWidth, natWidth] = this.label.get_preferred_width(-1);
if (natWidth > WRAP_WIDTH) {
alloc.min_size *= 2;
alloc.natural_size *= 2;
}
},
_wrapperAllocate: function(actor, box, flags) {
let availWidth = box.x2 - box.x1;
let availHeight = box.y2 - box.y1;
this.label.allocate(box, flags);
}
};
function IMStatusChooserItem() {
this._init();
}
IMStatusChooserItem.prototype = {
__proto__: PopupMenu.PopupBaseMenuItem.prototype,
_init: function() {
PopupMenu.PopupBaseMenuItem.prototype._init.call (this,
{ reactive: false,
style_class: 'status-chooser' });
this._iconBin = new St.Button({ style_class: 'status-chooser-user-icon' });
this.addActor(this._iconBin);
this._iconBin.connect('clicked', Lang.bind(this,
function() {
this.activate();
}));
this._section = new PopupMenu.PopupMenuSection();
this.addActor(this._section.actor);
this._name = new IMUserNameItem();
this._section.addMenuItem(this._name);
this._combo = new PopupMenu.PopupComboBoxMenuItem({ style_class: 'status-chooser-combo' });
this._section.addMenuItem(this._combo);
let item;
item = new IMStatusItem(_("Available"), 'user-available');
this._combo.addMenuItem(item, IMStatus.AVAILABLE);
item = new IMStatusItem(_("Busy"), 'user-busy');
this._combo.addMenuItem(item, IMStatus.BUSY);
item = new IMStatusItem(_("Hidden"), 'user-invisible');
this._combo.addMenuItem(item, IMStatus.HIDDEN);
item = new IMStatusItem(_("Away"), 'user-away');
this._combo.addMenuItem(item, IMStatus.AWAY);
item = new IMStatusItem(_("Idle"), 'user-idle');
this._combo.addMenuItem(item, IMStatus.IDLE);
item = new IMStatusItem(_("Unavailable"), 'user-offline');
this._combo.addMenuItem(item, IMStatus.OFFLINE);
this._combo.connect('active-item-changed',
Lang.bind(this, this._changeIMStatus));
this._presence = new GnomeSession.Presence();
this._presence.getStatus(Lang.bind(this, this._sessionStatusChanged));
this._presence.connect('StatusChanged',
Lang.bind(this, this._sessionStatusChanged));
this._previousPresence = undefined;
this._accountMgr = Tp.AccountManager.dup()
this._accountMgr.connect('most-available-presence-changed',
Lang.bind(this, this._IMStatusChanged));
this._accountMgr.prepare_async(null, Lang.bind(this,
function(mgr) {
let [presence, s, msg] = mgr.get_most_available_presence();
this._previousPresence = presence;
this._IMStatusChanged(mgr, presence, s, msg);
}));
this._userManager = AccountsService.UserManager.get_default();
this._user = this._userManager.get_user(GLib.get_user_name());
this._userLoadedId = this._user.connect('notify::is-loaded',
Lang.bind(this,
this._updateUser));
this._userChangedId = this._user.connect('changed',
Lang.bind(this,
this._updateUser));
},
// Override getColumnWidths()/setColumnWidths() to make the item
// independent from the overall column layout of the menu
getColumnWidths: function() {
return [];
},
setColumnWidths: function(widths) {
this._columnWidths = PopupMenu.PopupBaseMenuItem.prototype.getColumnWidths.call(this);
let sectionWidths = this._section.getColumnWidths();
this._section.setColumnWidths(sectionWidths);
},
_updateUser: function() {
let iconFile = null;
if (this._user.is_loaded) {
this._name.label.set_text(this._user.get_real_name());
iconFile = this._user.get_icon_file();
if (!GLib.file_test(iconFile, GLib.FileTest.EXISTS))
iconFile = null;
} else {
this._name.label.set_text("");
}
if (iconFile)
this._setIconFromFile(iconFile);
else
this._setIconFromName('avatar-default');
},
_setIconFromFile: function(iconFile) {
this._iconBin.set_style('background-image: url("' + iconFile + '");');
this._iconBin.child = null;
},
_setIconFromName: function(iconName) {
this._iconBin.set_style(null);
if (iconName != null) {
let textureCache = St.TextureCache.get_default();
let icon = textureCache.load_icon_name(this._iconBin.get_theme_node(),
iconName,
St.IconType.SYMBOLIC,
DIALOG_ICON_SIZE);
this._iconBin.child = icon;
this._iconBin.show();
} else {
this._iconBin.child = null;
this._iconBin.hide();
}
},
_statusForPresence: function(presence) {
switch(presence) {
case Tp.ConnectionPresenceType.AVAILABLE:
return 'available';
case Tp.ConnectionPresenceType.BUSY:
return 'busy';
case Tp.ConnectionPresenceType.OFFLINE:
return 'offline';
case Tp.ConnectionPresenceType.HIDDEN:
return 'hidden';
case Tp.ConnectionPresenceType.AWAY:
return 'away';
case Tp.ConnectionPresenceType.EXTENDED_AWAY:
return 'xa';
default:
return 'unknown';
}
},
_IMStatusChanged: function(accountMgr, presence, status, message) {
if (presence == Tp.ConnectionPresenceType.AVAILABLE)
this._presence.setStatus(GnomeSession.PresenceStatus.AVAILABLE);
if (!this._expectedPresence || presence != this._expectedPresence)
this._previousPresence = presence;
else
this._expectedPresence = undefined;
let activatedItem;
if (presence == Tp.ConnectionPresenceType.AVAILABLE)
activatedItem = IMStatus.AVAILABLE;
else if (presence == Tp.ConnectionPresenceType.BUSY)
activatedItem = IMStatus.BUSY;
else if (presence == Tp.ConnectionPresenceType.HIDDEN)
activatedItem = IMStatus.HIDDEN;
else if (presence == Tp.ConnectionPresenceType.AWAY)
activatedItem = IMStatus.AWAY;
else if (presence == Tp.ConnectionPresenceType.EXTENDED_AWAY)
activatedItem = IMStatus.IDLE;
else
activatedItem = IMStatus.OFFLINE;
this._combo.setActiveItem(activatedItem);
for (let i = 0; i < IMStatus.LAST; i++) {
if (i == IMStatus.AVAILABLE || i == IMStatus.OFFLINE)
continue; // always visible
this._combo.setItemVisible(i, i == activatedItem);
}
},
_changeIMStatus: function(menuItem, id) {
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
let newPresence, status;
if (id == IMStatus.AVAILABLE) {
newPresence = Tp.ConnectionPresenceType.AVAILABLE;
} else if (id == IMStatus.OFFLINE) {
newPresence = Tp.ConnectionPresenceType.OFFLINE;
} else
return;
status = this._statusForPresence(newPresence);
msg = msg ? msg : "";
this._accountMgr.set_all_requested_presences(newPresence, status, msg);
},
_sessionStatusChanged: function(sessionPresence, sessionStatus) {
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
let newPresence, status;
if (sessionStatus == GnomeSession.PresenceStatus.AVAILABLE) {
newPresence = this._previousPresence;
} else if (sessionStatus == GnomeSession.PresenceStatus.BUSY) {
// Only change presence if the current one is "more present" than
// busy, or if coming back from idle
if (presence == Tp.ConnectionPresenceType.AVAILABLE ||
presence == Tp.ConnectionPresenceType.EXTENDED_AWAY) {
newPresence = Tp.ConnectionPresenceType.BUSY;
} else {
return;
}
} else if (sessionStatus == GnomeSession.PresenceStatus.IDLE) {
// Only change presence if the current one is "more present" than
// idle
if (presence != Tp.ConnectionPresenceType.OFFLINE)
newPresence = Tp.ConnectionPresenceType.EXTENDED_AWAY;
else
return;
} else {
return;
}
if (newPresence == undefined)
return;
status = this._statusForPresence(newPresence);
msg = msg ? msg : "";
this._expectedPresence = newPresence;
this._accountMgr.set_all_requested_presences(newPresence, status, msg);
}
};
function UserMenuButton() {
this._init();
}
UserMenuButton.prototype = {
__proto__: PanelMenu.Button.prototype,
_init: function() {
PanelMenu.Button.prototype._init.call(this, 0.0);
let box = new St.BoxLayout({ name: 'panelStatusMenu' });
this.actor.set_child(box);
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
this._userManager = AccountsService.UserManager.get_default();
this._user = this._userManager.get_user(GLib.get_user_name());
this._presence = new GnomeSession.Presence();
this._session = new GnomeSession.SessionManager();
this._haveShutdown = true;
this._account_mgr = Tp.AccountManager.dup()
this._upClient = new UPowerGlib.Client();
this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy();
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._iconBox = new St.Bin();
box.add(this._iconBox, { y_align: St.Align.MIDDLE, y_fill: false });
let textureCache = St.TextureCache.get_default();
this._offlineIcon = new St.Icon({ icon_name: 'user-offline',
style_class: 'popup-menu-icon' });
this._availableIcon = new St.Icon({ icon_name: 'user-available',
style_class: 'popup-menu-icon' });
this._busyIcon = new St.Icon({ icon_name: 'user-busy',
style_class: 'popup-menu-icon' });
this._invisibleIcon = new St.Icon({ icon_name: 'user-invisible',
style_class: 'popup-menu-icon' });
this._awayIcon = new St.Icon({ icon_name: 'user-away',
style_class: 'popup-menu-icon' });
this._idleIcon = new St.Icon({ icon_name: 'user-idle',
style_class: 'popup-menu-icon' });
this._presence.connect('StatusChanged',
Lang.bind(this, this._updateSwitch));
this._presence.getStatus(Lang.bind(this, this._updateSwitch));
this._account_mgr.connect('most-available-presence-changed',
Lang.bind(this, this._updatePresenceIcon));
this._account_mgr.prepare_async(null, Lang.bind(this,
function(mgr) {
let [presence, s, msg] = mgr.get_most_available_presence();
this._updatePresenceIcon(mgr, presence, s, msg);
}));
this._name = new St.Label();
box.add(this._name, { y_align: St.Align.MIDDLE, y_fill: false });
this._userLoadedId = this._user.connect('notify::is-loaded', Lang.bind(this, this._updateUserName));
this._userChangedId = this._user.connect('changed', Lang.bind(this, this._updateUserName));
this._createSubMenu();
this._userManager.connect('notify::is-loaded',
Lang.bind(this, this._updateSwitchUser));
this._userManager.connect('user-added',
Lang.bind(this, this._updateSwitchUser));
this._userManager.connect('user-removed',
Lang.bind(this, this._updateSwitchUser));
this._lockdownSettings.connect('changed::' + DISABLE_USER_SWITCH_KEY,
Lang.bind(this, this._updateSwitchUser));
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
Lang.bind(this, this._updateLogout));
this._lockdownSettings.connect('changed::' + DISABLE_LOCK_SCREEN_KEY,
Lang.bind(this, this._updateLockScreen));
this._updateSwitchUser();
this._updateLogout();
this._updateLockScreen();
// Whether shutdown is available or not depends on both lockdown
// settings (disable-log-out) and Polkit policy - the latter doesn't
// notify, so we update the menu item each time the menu opens or
// the lockdown setting changes, which should be close enough.
this.menu.connect('open-state-changed', Lang.bind(this,
function(menu, open) {
if (open)
this._updateHaveShutdown();
}));
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
Lang.bind(this, this._updateHaveShutdown));
this._upClient.connect('notify::can-suspend', Lang.bind(this, this._updateSuspendOrPowerOff));
},
_onDestroy: function() {
this._user.disconnect(this._userLoadedId);
this._user.disconnect(this._userChangedId);
},
_updateUserName: function() {
if (this._user.is_loaded)
this._name.set_text(this._user.get_real_name());
else
this._name.set_text("");
},
_updateSwitchUser: function() {
let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY);
if (allowSwitch && this._userManager.can_switch ())
this._loginScreenItem.actor.show();
else
this._loginScreenItem.actor.hide();
},
_updateLogout: function() {
let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY);
if (allowLogout)
this._logoutItem.actor.show();
else
this._logoutItem.actor.hide();
},
_updateLockScreen: function() {
let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
if (allowLockScreen)
this._lockScreenItem.actor.show();
else
this._lockScreenItem.actor.hide();
},
_updateHaveShutdown: function() {
this._session.CanShutdownRemote(Lang.bind(this,
function(result, error) {
if (!error) {
this._haveShutdown = result;
this._updateSuspendOrPowerOff();
}
}));
},
_updateSuspendOrPowerOff: function() {
this._haveSuspend = this._upClient.get_can_suspend();
if (!this._suspendOrPowerOffItem)
return;
if (!this._haveShutdown && !this._haveSuspend)
this._suspendOrPowerOffItem.actor.hide();
else
this._suspendOrPowerOffItem.actor.show();
// If we can't suspend show Power Off... instead
// and disable the alt key
if (!this._haveSuspend) {
this._suspendOrPowerOffItem.updateText(_("Power Off..."), null);
} else if (!this._haveShutdown) {
this._suspendOrPowerOffItem.updateText(_("Suspend"), null);
} else {
this._suspendOrPowerOffItem.updateText(_("Suspend"), _("Power Off..."));
}
},
_updateSwitch: function(presence, status) {
let active = status == GnomeSession.PresenceStatus.BUSY;
this._dontDisturbSwitch.setToggleState(active);
},
_updatePresenceIcon: function(accountMgr, presence, status, message) {
if (presence == Tp.ConnectionPresenceType.AVAILABLE)
this._iconBox.child = this._availableIcon;
else if (presence == Tp.ConnectionPresenceType.BUSY)
this._iconBox.child = this._busyIcon;
else if (presence == Tp.ConnectionPresenceType.HIDDEN)
this._iconBox.child = this._invisibleIcon;
else if (presence == Tp.ConnectionPresenceType.AWAY)
this._iconBox.child = this._awayIcon;
else if (presence == Tp.ConnectionPresenceType.EXTENDED_AWAY)
this._iconBox.child = this._idleIcon;
else
this._iconBox.child = this._offlineIcon;
},
_createSubMenu: function() {
let item;
item = new IMStatusChooserItem();
item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
this.menu.addMenuItem(item);
item = new PopupMenu.PopupSwitchMenuItem(_("Do Not Disturb"));
item.connect('activate', Lang.bind(this, this._updatePresenceStatus));
this.menu.addMenuItem(item);
this._dontDisturbSwitch = item;
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("Online Accounts"));
item.connect('activate', Lang.bind(this, this._onOnlineAccountsActivate));
this.menu.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("System Settings"));
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
this.menu.addMenuItem(item);
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("Lock Screen"));
item.connect('activate', Lang.bind(this, this._onLockScreenActivate));
this.menu.addMenuItem(item);
this._lockScreenItem = item;
item = new PopupMenu.PopupMenuItem(_("Switch User"));
item.connect('activate', Lang.bind(this, this._onLoginScreenActivate));
this.menu.addMenuItem(item);
this._loginScreenItem = item;
item = new PopupMenu.PopupMenuItem(_("Log Out..."));
item.connect('activate', Lang.bind(this, this._onQuitSessionActivate));
this.menu.addMenuItem(item);
this._logoutItem = item;
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
item = new PopupMenu.PopupAlternatingMenuItem(_("Suspend"),
_("Power Off..."));
this.menu.addMenuItem(item);
this._suspendOrPowerOffItem = item;
item.connect('activate', Lang.bind(this, this._onSuspendOrPowerOffActivate));
this._updateSuspendOrPowerOff();
},
_updatePresenceStatus: function(item, event) {
let status = item.state ? GnomeSession.PresenceStatus.BUSY
: GnomeSession.PresenceStatus.AVAILABLE;
this._presence.setStatus(status);
},
_onMyAccountActivate: function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().lookup_setting('gnome-user-accounts-panel.desktop');
app.activate();
},
_onOnlineAccountsActivate: function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().lookup_setting('gnome-online-accounts-panel.desktop');
app.activate(-1);
},
_onPreferencesActivate: function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().lookup_app('gnome-control-center.desktop');
app.activate();
},
_onLockScreenActivate: function() {
Main.overview.hide();
this._screenSaverProxy.LockRemote();
},
_onLoginScreenActivate: function() {
Main.overview.hide();
// 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._userManager.goto_login_session();
}));
},
_onQuitSessionActivate: function() {
Main.overview.hide();
this._session.LogoutRemote(0);
},
_onSuspendOrPowerOffActivate: function() {
Main.overview.hide();
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);
}));
} else {
this._session.ShutdownRemote();
}
}
};

View File

@ -297,7 +297,7 @@ SearchTab.prototype = {
_doSearch: function () {
this._searchTimeoutId = 0;
let text = this._text.get_text().replace(/^\s+/g, '').replace(/\s+$/g, '');
this._searchResults.updateSearch(text);
this._searchResults.doSearch(text);
return false;
}

View File

@ -16,8 +16,7 @@ WindowAttentionHandler.prototype = {
this._tracker = Shell.WindowTracker.get_default();
this._tracker.connect('startup-sequence-changed', Lang.bind(this, this._onStartupSequenceChanged));
let display = global.screen.get_display();
display.connect('window-demands-attention', Lang.bind(this, this._onWindowDemandsAttention));
global.display.connect('window-demands-attention', Lang.bind(this, this._onWindowDemandsAttention));
},
_onStartupSequenceChanged : function(tracker) {

View File

@ -239,7 +239,7 @@ WindowManager.prototype = {
_hasAttachedDialogs: function(window, ignoreWindow) {
var count = 0;
window.foreach_transient(function(win) {
if (win != ignoreWindow && win.get_window_type() == Meta.WindowType.MODAL_DIALOG)
if (win != ignoreWindow && win.is_attached_dialog())
count++;
return false;
});
@ -247,7 +247,7 @@ WindowManager.prototype = {
},
_checkDimming: function(window, ignoreWindow) {
let shouldDim = Meta.prefs_get_attach_modal_dialogs() && this._hasAttachedDialogs(window, ignoreWindow);
let shouldDim = this._hasAttachedDialogs(window, ignoreWindow);
if (shouldDim && !window._dimmed) {
window._dimmed = true;
@ -309,9 +309,7 @@ WindowManager.prototype = {
actor._windowType = type;
}));
if (actor.meta_window.get_window_type() == Meta.WindowType.MODAL_DIALOG
&& Meta.prefs_get_attach_modal_dialogs()
&& actor.get_meta_window().get_transient_for()) {
if (actor.meta_window.is_attached_dialog()) {
this._checkDimming(actor.get_meta_window().get_transient_for());
if (this._shouldAnimate()) {
actor.set_scale(1.0, 0.0);
@ -373,7 +371,6 @@ WindowManager.prototype = {
_destroyWindow : function(shellwm, actor) {
let window = actor.meta_window;
let parent = window.get_transient_for();
if (actor._notifyWindowTypeSignalId) {
window.disconnect(actor._notifyWindowTypeSignalId);
actor._notifyWindowTypeSignalId = 0;
@ -383,12 +380,14 @@ WindowManager.prototype = {
return win != window;
});
}
while (window.get_window_type() == Meta.WindowType.MODAL_DIALOG
&& parent) {
if (window.is_attached_dialog()) {
let parent = window.get_transient_for();
this._checkDimming(parent, window);
if (!Meta.prefs_get_attach_modal_dialogs()
|| !this._shouldAnimate())
break;
if (!this._shouldAnimate()) {
shellwm.completed_destroy(actor);
return;
}
actor.set_scale(1.0, 1.0);
actor.show();
this._destroying.push(actor);

View File

@ -94,22 +94,38 @@ function WindowClone(realWindow) {
WindowClone.prototype = {
_init : function(realWindow) {
this.actor = new Clutter.Clone({ source: realWindow.get_texture(),
reactive: true,
x: realWindow.x,
y: realWindow.y });
this.actor._delegate = this;
this.realWindow = realWindow;
this.metaWindow = realWindow.meta_window;
this.metaWindow._delegate = this;
this.origX = realWindow.x;
this.origY = realWindow.y;
let [borderX, borderY] = this._getInvisibleBorderPadding();
this._windowClone = new Clutter.Clone({ source: realWindow.get_texture(),
x: -borderX,
y: -borderY });
this.origX = realWindow.x + borderX;
this.origY = realWindow.y + borderY;
let outerRect = realWindow.meta_window.get_outer_rect();
// The MetaShapedTexture that we clone has a size that includes
// the invisible border; this is inconvenient; rather than trying
// to compensate all over the place we insert a ClutterGroup into
// the hierarchy that is sized to only the visible portion.
this.actor = new Clutter.Group({ reactive: true,
x: this.origX,
y: this.origY,
width: outerRect.width,
height: outerRect.height });
this.actor.add_actor(this._windowClone);
this.actor._delegate = this;
this._stackAbove = null;
this._sizeChangedId = this.realWindow.connect('size-changed', Lang.bind(this, function() {
this.emit('size-changed');
}));
this._sizeChangedId = this.realWindow.connect('size-changed',
Lang.bind(this, this._onRealWindowSizeChanged));
this._realWindowDestroyId = this.realWindow.connect('destroy',
Lang.bind(this, this._disconnectRealWindowSignals));
@ -161,7 +177,7 @@ WindowClone.prototype = {
// will look funny.
if (!this._selected &&
this.metaWindow != global.screen.get_display().focus_window)
this.metaWindow != global.display.focus_window)
this._zoomEnd();
}
},
@ -176,6 +192,32 @@ WindowClone.prototype = {
this._realWindowDestroyId = 0;
},
_getInvisibleBorderPadding: function() {
// We need to adjust the position of the actor because of the
// consequences of invisible borders -- in reality, the texture
// has an extra set of "padding" around it that we need to trim
// down.
// The outer rect paradoxically is the smaller rectangle,
// containing the positions of the visible frame. The input
// rect contains everything, including the invisible border
// padding.
let outerRect = this.metaWindow.get_outer_rect();
let inputRect = this.metaWindow.get_input_rect();
let [borderX, borderY] = [outerRect.x - inputRect.x,
outerRect.y - inputRect.y];
return [borderX, borderY];
},
_onRealWindowSizeChanged: function() {
let [borderX, borderY] = this._getInvisibleBorderPadding();
let outerRect = this.metaWindow.get_outer_rect();
this.actor.set_size(outerRect.width, outerRect.height);
this._windowClone.set_position(-borderX, -borderY);
this.emit('size-changed');
},
_onDestroy: function() {
this._disconnectRealWindowSignals();
@ -1440,7 +1482,7 @@ Workspace.prototype = {
time);
return true;
} else if (source.shellWorkspaceLaunch) {
source.shellWorkspaceLaunch({ workspace: this.metaWorkspace,
source.shellWorkspaceLaunch({ workspace: this.metaWorkspace ? this.metaWorkspace.index() : -1,
timestamp: time });
return true;
}

View File

@ -167,7 +167,7 @@ WorkspaceThumbnail.prototype = {
return true;
}));
this._background = new Clutter.Clone({ source: global.background_actor });
this._background = Meta.BackgroundActor.new_for_screen(global.screen);
this._contents.add_actor(this._background);
let monitor = Main.layoutManager.primaryMonitor;
@ -452,7 +452,7 @@ WorkspaceThumbnail.prototype = {
time);
return true;
} else if (source.shellWorkspaceLaunch) {
source.shellWorkspaceLaunch({ workspace: this.metaWorkspace,
source.shellWorkspaceLaunch({ workspace: this.metaWorkspace ? this.metaWorkspace.index() : -1,
timestamp: time });
return true;
}

View File

@ -551,7 +551,6 @@ WorkspacesDisplay.prototype = {
Lang.bind(this, this._onScrollEvent));
this._monitorIndex = Main.layoutManager.primaryIndex;
this._monitor = Main.layoutManager.monitors[this._monitorIndex];
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
controls.add_actor(this._thumbnailsBox.actor);

View File

@ -33,6 +33,7 @@ kn
lt
lv
mr
ms
nb
nl
nn

View File

@ -1,15 +1,21 @@
data/gnome-shell.desktop.in.in
data/org.gnome.shell.gschema.xml.in
js/gdm/loginDialog.js
js/misc/util.js
js/ui/appDisplay.js
js/ui/appFavorites.js
js/ui/autorunManager.js
js/ui/calendar.js
js/ui/contactDisplay.js
js/ui/dash.js
js/ui/dateMenu.js
js/ui/docDisplay.js
js/ui/endSessionDialog.js
js/ui/keyboard.js
js/ui/lookingGlass.js
js/ui/messageTray.js
js/ui/networkAgent.js
js/ui/notificationDaemon.js
js/ui/overview.js
js/ui/panel.js
js/ui/placeDisplay.js
@ -17,7 +23,7 @@ js/ui/polkitAuthenticationAgent.js
js/ui/popupMenu.js
js/ui/runDialog.js
js/ui/searchDisplay.js
js/ui/statusMenu.js
js/ui/shellMountOperation.js
js/ui/status/accessibility.js
js/ui/status/bluetooth.js
js/ui/status/keyboard.js
@ -25,11 +31,10 @@ js/ui/status/network.js
js/ui/status/power.js
js/ui/status/volume.js
js/ui/telepathyClient.js
js/ui/userMenu.js
js/ui/viewSelector.js
js/ui/windowAttentionHandler.js
js/ui/workspacesView.js
src/gvc/gvc-mixer-control.c
src/gdmuser/gdm-user.c
src/main.c
src/shell-app.c
src/shell-app-system.c

719
po/bg.po

File diff suppressed because it is too large Load Diff

415
po/de.po
View File

@ -17,8 +17,8 @@ msgstr ""
"Project-Id-Version: gnome-shell master\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&keywords=I18N+L10N&component=general\n"
"POT-Creation-Date: 2011-07-14 18:02+0000\n"
"PO-Revision-Date: 2011-07-14 20:25+0100\n"
"POT-Creation-Date: 2011-08-24 17:59+0000\n"
"PO-Revision-Date: 2011-08-28 14:03+0100\n"
"Last-Translator: Mario Blättermann <mariobl@freenet.de>\n"
"Language-Team: Deutsch <gnome-de@gnome.org>\n"
"MIME-Version: 1.0\n"
@ -67,43 +67,32 @@ msgstr ""
"Listen erscheinen."
#: ../data/org.gnome.shell.gschema.xml.in.h:6
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 ""
"Die Erweiterungen der GNOME-Shell besitzen eine UUID-Eigenschaft. Dieser "
"Schlüssel listet Erweiterungen auf, welche nicht geladen werden sollen. Diese "
"Einstellung überschreibt »enabled-extensions« für Erweiterungen, die in "
"beiden Listen erscheinen."
#: ../data/org.gnome.shell.gschema.xml.in.h:7
msgid "History for command (Alt-F2) dialog"
msgstr "Verlauf des Befehlsdialogs (Alt+F2)"
#: ../data/org.gnome.shell.gschema.xml.in.h:8
#: ../data/org.gnome.shell.gschema.xml.in.h:7
msgid "History for the looking glass dialog"
msgstr "Chronik des Dialogs »looking glass«"
#: ../data/org.gnome.shell.gschema.xml.in.h:9
#: ../data/org.gnome.shell.gschema.xml.in.h:8
msgid "If true, display date in the clock, in addition to time."
msgstr "Legt fest, ob das Datum zusätzlich zur Uhrzeit angezeigt wird."
#: ../data/org.gnome.shell.gschema.xml.in.h:10
#: ../data/org.gnome.shell.gschema.xml.in.h:9
msgid "If true, display seconds in time."
msgstr "Legt fest, ob in der Uhrzeit Sekunden angezeigt werden."
#: ../data/org.gnome.shell.gschema.xml.in.h:11
#: ../data/org.gnome.shell.gschema.xml.in.h:10
msgid "If true, display the ISO week date in the calendar."
msgstr ""
"Wenn dieser Wert gesetzt ist, wird der ISO-Wochentag im Kalender angezeigt."
#: ../data/org.gnome.shell.gschema.xml.in.h:12
#: ../data/org.gnome.shell.gschema.xml.in.h:11
msgid "List of desktop file IDs for favorite applications"
msgstr "Liste der Kennungen der Desktop-Dateien für bevorzugte Anwendungen"
# Hier blicke ich überhaupt nicht durch.
#: ../data/org.gnome.shell.gschema.xml.in.h:14
#: ../data/org.gnome.shell.gschema.xml.in.h:13
#, no-c-format
msgid ""
"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
@ -130,19 +119,19 @@ msgstr ""
"mittels des VP8-Codecs aufzeichnet. %T wird als Platzhalter für die vermutete "
"optimale Threadanzahl auf dem System verwendet."
#: ../data/org.gnome.shell.gschema.xml.in.h:15
#: ../data/org.gnome.shell.gschema.xml.in.h:14
msgid "Show date in clock"
msgstr "Datum in der Uhr anzeigen"
#: ../data/org.gnome.shell.gschema.xml.in.h:16
#: ../data/org.gnome.shell.gschema.xml.in.h:15
msgid "Show the week date in the calendar"
msgstr "Wochentag im Kalender anzeigen"
#: ../data/org.gnome.shell.gschema.xml.in.h:17
#: ../data/org.gnome.shell.gschema.xml.in.h:16
msgid "Show time with seconds"
msgstr "Zeit sekundengenau anzeigen"
#: ../data/org.gnome.shell.gschema.xml.in.h:18
#: ../data/org.gnome.shell.gschema.xml.in.h:17
msgid ""
"The applications corresponding to these identifiers will be displayed in the "
"favorites area."
@ -150,7 +139,7 @@ msgstr ""
"Programme, welche auf diese Bezeichner zutreffen, werden im Favoriten-Bereich "
"angezeigt."
#: ../data/org.gnome.shell.gschema.xml.in.h:19
#: ../data/org.gnome.shell.gschema.xml.in.h:18
msgid ""
"The filename for recorded screencasts will be a unique filename based on the "
"current date, and use this extension. It should be changed when recording to "
@ -161,7 +150,7 @@ msgstr ""
"Dateiname sollte geändert werden, wenn Sie in einem anderen Containerformat "
"aufnehmen."
#: ../data/org.gnome.shell.gschema.xml.in.h:20
#: ../data/org.gnome.shell.gschema.xml.in.h:19
msgid ""
"The framerate of the resulting screencast recordered by GNOME Shell's "
"screencast recorder in frames-per-second."
@ -170,11 +159,11 @@ msgstr ""
"der GNOME-Shell aufgezeichnet werden soll, in Einzelbildern pro Sekunde."
# hmm Enkodieren oder Kodieren?
#: ../data/org.gnome.shell.gschema.xml.in.h:21
#: ../data/org.gnome.shell.gschema.xml.in.h:20
msgid "The gstreamer pipeline used to encode the screencast"
msgstr "Die GStreamer-Weiterleitung zur Enkodierung des Screencasts"
#: ../data/org.gnome.shell.gschema.xml.in.h:22
#: ../data/org.gnome.shell.gschema.xml.in.h:21
msgid ""
"The shell normally monitors active applications in order to present the most "
"used ones (e.g. in launchers). While this data will be kept private, you may "
@ -187,19 +176,15 @@ msgstr ""
"deaktivieren, um Ihre Privatsphäre zu schützen. Bitte beachten Sie, dass "
"bereits gespeicherte Daten hiervon nicht beeinflusst werden."
#: ../data/org.gnome.shell.gschema.xml.in.h:23
msgid "Uuids of extensions to disable"
msgstr "UUIDs der zu deaktivierenden Erweiterungen"
#: ../data/org.gnome.shell.gschema.xml.in.h:24
#: ../data/org.gnome.shell.gschema.xml.in.h:22
msgid "Uuids of extensions to enable"
msgstr "UUIDs der zu aktivierenden Erweiterungen"
#: ../data/org.gnome.shell.gschema.xml.in.h:25
#: ../data/org.gnome.shell.gschema.xml.in.h:23
msgid "Whether to collect stats about applications usage"
msgstr "Legt fest, ob Statistiken über Anwendungsnutzung erfasst werden sollen"
#: ../data/org.gnome.shell.gschema.xml.in.h:26
#: ../data/org.gnome.shell.gschema.xml.in.h:24
msgid "disabled OpenSearch providers"
msgstr "deaktivierte OpenSearch-Provider"
@ -219,27 +204,27 @@ msgid "Execution of '%s' failed:"
msgstr "Ausführung von »%s« ist gescheitert:"
#. Translators: Filter to display all applications
#: ../js/ui/appDisplay.js:252
#: ../js/ui/appDisplay.js:253
msgid "All"
msgstr "Alle"
#: ../js/ui/appDisplay.js:351
#: ../js/ui/appDisplay.js:315
msgid "APPLICATIONS"
msgstr "ANWENDUNGEN"
#: ../js/ui/appDisplay.js:377
#: ../js/ui/appDisplay.js:373
msgid "SETTINGS"
msgstr "EINSTELLUNGEN"
#: ../js/ui/appDisplay.js:650
#: ../js/ui/appDisplay.js:681
msgid "New Window"
msgstr "Neues Fenster"
#: ../js/ui/appDisplay.js:653
#: ../js/ui/appDisplay.js:684
msgid "Remove from Favorites"
msgstr "Aus Favoriten entfernen"
#: ../js/ui/appDisplay.js:654
#: ../js/ui/appDisplay.js:685
msgid "Add to Favorites"
msgstr "Zu Favoriten hinzufügen"
@ -253,6 +238,15 @@ msgstr "%s wurde zu Ihren Favoriten hinzugefügt"
msgid "%s has been removed from your favorites."
msgstr "%s wurde aus Ihren Favoriten entfernt"
#: ../js/ui/autorunManager.js:591
#, c-format
msgid "Open with %s"
msgstr "Öffnen mit %s"
#: ../js/ui/autorunManager.js:617
msgid "Eject"
msgstr "Auswerfen"
#. Translators: Shown in calendar event list for all day events
#. * Keep it short, best if you can use less then 10 characters
#.
@ -371,94 +365,94 @@ msgid "S"
msgstr "Sa"
#. Translators: Text to show if there are no events
#: ../js/ui/calendar.js:701
#: ../js/ui/calendar.js:678
msgid "Nothing Scheduled"
msgstr "Nichts geplant"
#. Translators: Shown on calendar heading when selected day occurs on current year
#: ../js/ui/calendar.js:717 ../js/ui/telepathyClient.js:708
#: ../js/ui/calendar.js:694
msgctxt "calendar heading"
msgid "%A, %B %d"
msgstr "%A, %d. %B"
#. Translators: Shown on calendar heading when selected day occurs on different year
#: ../js/ui/calendar.js:720 ../js/ui/telepathyClient.js:711
#: ../js/ui/calendar.js:697
msgctxt "calendar heading"
msgid "%A, %B %d, %Y"
msgstr "%a, %d. %B %Y"
#: ../js/ui/calendar.js:730
#: ../js/ui/calendar.js:707
msgid "Today"
msgstr "Heute"
#: ../js/ui/calendar.js:734
#: ../js/ui/calendar.js:711
msgid "Tomorrow"
msgstr "Morgen"
#: ../js/ui/calendar.js:743
#: ../js/ui/calendar.js:720
msgid "This week"
msgstr "Diese Woche"
#: ../js/ui/calendar.js:751
#: ../js/ui/calendar.js:728
msgid "Next week"
msgstr "Nächste Woche"
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1122
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1123
msgid "Remove"
msgstr "Entfernen"
#: ../js/ui/dateMenu.js:89
#: ../js/ui/dateMenu.js:90
msgid "Date and Time Settings"
msgstr "Einstellungen für Datum und Uhrzeit"
#: ../js/ui/dateMenu.js:109
#: ../js/ui/dateMenu.js:110
msgid "Open Calendar"
msgstr "Kalender öffnen"
#. Translators: This is the time format with date used
#. in 24-hour mode.
#: ../js/ui/dateMenu.js:162
#: ../js/ui/dateMenu.js:167
msgid "%a %b %e, %R:%S"
msgstr "%a, %e. %b, %R:%S"
#: ../js/ui/dateMenu.js:163
#: ../js/ui/dateMenu.js:168
msgid "%a %b %e, %R"
msgstr "%a, %e. %b, %R"
#. Translators: This is the time format without date used
#. in 24-hour mode.
#: ../js/ui/dateMenu.js:167
#: ../js/ui/dateMenu.js:172
msgid "%a %R:%S"
msgstr "%a %R:%S"
#: ../js/ui/dateMenu.js:168
#: ../js/ui/dateMenu.js:173
msgid "%a %R"
msgstr "%a %R"
#. Translators: This is a time format with date used
#. for AM/PM.
#: ../js/ui/dateMenu.js:175
#: ../js/ui/dateMenu.js:180
msgid "%a %b %e, %l:%M:%S %p"
msgstr "%a, %e. %b, %l:%M:%S"
#: ../js/ui/dateMenu.js:176
#: ../js/ui/dateMenu.js:181
msgid "%a %b %e, %l:%M %p"
msgstr "%a, %e. %b, %l:%M"
#. Translators: This is a time format without date used
#. for AM/PM.
#: ../js/ui/dateMenu.js:180
#: ../js/ui/dateMenu.js:185
msgid "%a %l:%M:%S %p"
msgstr "%a %l:%M:%S"
#: ../js/ui/dateMenu.js:181
#: ../js/ui/dateMenu.js:186
msgid "%a %l:%M %p"
msgstr "%a %l:%M"
#. Translators: This is the date format to use when the calendar popup is
#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
#.
#: ../js/ui/dateMenu.js:192
#: ../js/ui/dateMenu.js:197
msgid "%A %B %e, %Y"
msgstr "%A, %e. %B %Y"
@ -539,44 +533,53 @@ msgstr "Neustart des Systems."
msgid "Cancel"
msgstr "Abbrechen"
#: ../js/ui/lookingGlass.js:641
#: ../js/ui/lookingGlass.js:640
msgid "No extensions installed"
msgstr "Keine Erweiterungen installiert"
#: ../js/ui/lookingGlass.js:678
#: ../js/ui/lookingGlass.js:686
msgid "Enabled"
msgstr "Aktiviert"
#. translators:
#. * The device has been disabled
#: ../js/ui/lookingGlass.js:680 ../src/gvc/gvc-mixer-control.c:1091
#: ../js/ui/lookingGlass.js:688 ../src/gvc/gvc-mixer-control.c:1091
msgid "Disabled"
msgstr "Deaktiviert"
#: ../js/ui/lookingGlass.js:682
#: ../js/ui/lookingGlass.js:690
msgid "Error"
msgstr "Fehler"
#: ../js/ui/lookingGlass.js:684
#: ../js/ui/lookingGlass.js:692
msgid "Out of date"
msgstr "Veraltet"
#: ../js/ui/lookingGlass.js:709
#: ../js/ui/lookingGlass.js:694
msgid "Downloading"
msgstr "Herunterladen"
#: ../js/ui/lookingGlass.js:719
msgid "View Source"
msgstr "Quelle zeigen"
#: ../js/ui/lookingGlass.js:715
#: ../js/ui/lookingGlass.js:725
msgid "Web Page"
msgstr "Webseite"
#: ../js/ui/messageTray.js:1115
#: ../js/ui/messageTray.js:1116
msgid "Open"
msgstr "Öffnen"
#: ../js/ui/messageTray.js:2286
#: ../js/ui/messageTray.js:2277
msgid "System Information"
msgstr "Systeminformationen"
#: ../js/ui/notificationDaemon.js:427 ../js/ui/status/power.js:238
#: ../src/shell-app.c:354
msgid "Unknown"
msgstr "Unbekannt"
#: ../js/ui/overview.js:89
msgid "Undo"
msgstr "Rückgängig"
@ -597,18 +600,18 @@ msgid "Dash"
msgstr "Dash"
#. TODO - _quit() doesn't really work on apps in state STARTING yet
#: ../js/ui/panel.js:533
#: ../js/ui/panel.js:531
#, c-format
msgid "Quit %s"
msgstr "%s beenden"
#. 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:914
#. Translators: If there is no suitable word for "Activities"
#. in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:567
msgid "Activities"
msgstr "Aktivitäten"
#: ../js/ui/panel.js:1014
#: ../js/ui/panel.js:876
msgid "Top Bar"
msgstr "Obere Leiste"
@ -674,43 +677,9 @@ msgstr "Suche läuft …"
msgid "No matching results."
msgstr "Keine passenden Ergebnisse."
#: ../js/ui/statusMenu.js:192 ../js/ui/statusMenu.js:196
#: ../js/ui/statusMenu.js:262
msgid "Power Off..."
msgstr "Ausschalten …"
#: ../js/ui/statusMenu.js:194 ../js/ui/statusMenu.js:196
#: ../js/ui/statusMenu.js:261
msgid "Suspend"
msgstr "Bereitschaft"
#: ../js/ui/statusMenu.js:217
msgid "Available"
msgstr "Verfügbar"
#: ../js/ui/statusMenu.js:222
msgid "Busy"
msgstr "Beschäftigt"
#: ../js/ui/statusMenu.js:230
msgid "My Account"
msgstr "Eigenes Konto"
#: ../js/ui/statusMenu.js:234
msgid "System Settings"
msgstr "Systemeinstellungen"
#: ../js/ui/statusMenu.js:242
msgid "Lock Screen"
msgstr "Bildschirm sperren"
#: ../js/ui/statusMenu.js:247
msgid "Switch User"
msgstr "Benutzer wechseln"
#: ../js/ui/statusMenu.js:252
msgid "Log Out..."
msgstr "Abmelden …"
#: ../js/ui/shellMountOperation.js:285
msgid "Wrong password, please try again"
msgstr "Falsches Passwort, bitte versuchen Sie es erneut"
#: ../js/ui/status/accessibility.js:60
msgid "Zoom"
@ -785,11 +754,11 @@ msgstr "Hardware deaktiviert"
msgid "Connection"
msgstr "Verbindung"
#: ../js/ui/status/bluetooth.js:226 ../js/ui/status/network.js:493
#: ../js/ui/status/bluetooth.js:226 ../js/ui/status/network.js:497
msgid "disconnecting..."
msgstr "Verbindungsabbau …"
#: ../js/ui/status/bluetooth.js:239 ../js/ui/status/network.js:499
#: ../js/ui/status/bluetooth.js:239 ../js/ui/status/network.js:503
msgid "connecting..."
msgstr "Verbindungsaufbau …"
@ -841,7 +810,7 @@ msgstr "Immer Zugriff gewähren"
msgid "Grant this time only"
msgstr "Nur dieses Mal gewähren"
#: ../js/ui/status/bluetooth.js:409
#: ../js/ui/status/bluetooth.js:409 ../js/ui/telepathyClient.js:1006
msgid "Reject"
msgstr "Abweisen"
@ -883,148 +852,186 @@ msgid "OK"
msgstr "OK"
#: ../js/ui/status/keyboard.js:71
msgid "Show Keyboard Layout..."
msgstr "Tastaturbelegung zeigen"
msgid "Show Keyboard Layout"
msgstr "Tastaturbelegung zeigen"
#: ../js/ui/status/keyboard.js:75
msgid "Localization Settings"
msgstr "Lokalisierungseinstellungen"
msgid "Region and Language Settings"
msgstr "Einstellungen für Region und Sprache"
#: ../js/ui/status/network.js:123
#: ../js/ui/statusMenu.js:192 ../js/ui/statusMenu.js:196
#: ../js/ui/statusMenu.js:262
msgid "Power Off..."
msgstr "Ausschalten …"
#: ../js/ui/statusMenu.js:194 ../js/ui/statusMenu.js:196
#: ../js/ui/statusMenu.js:261
msgid "Suspend"
msgstr "Bereitschaft"
#: ../js/ui/statusMenu.js:217
msgid "Available"
msgstr "Verfügbar"
#: ../js/ui/statusMenu.js:222
msgid "Busy"
msgstr "Beschäftigt"
#: ../js/ui/statusMenu.js:230
msgid "My Account"
msgstr "Eigenes Konto"
#: ../js/ui/statusMenu.js:234
msgid "System Settings"
msgstr "Systemeinstellungen"
#: ../js/ui/statusMenu.js:242
msgid "Lock Screen"
msgstr "Bildschirm sperren"
#: ../js/ui/statusMenu.js:247
msgid "Switch User"
msgstr "Benutzer wechseln"
#: ../js/ui/statusMenu.js:252
msgid "Log Out..."
msgstr "Abmelden …"
#: ../js/ui/status/network.js:108
msgid "<unknown>"
msgstr "<Unbekannt>"
#. Translators: this indicates that wireless or wwan is disabled by hardware killswitch
#: ../js/ui/status/network.js:292
#: ../js/ui/status/network.js:296
msgid "disabled"
msgstr "Deaktiviert"
#. 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:491
#: ../js/ui/status/network.js:495
msgid "unmanaged"
msgstr "nicht verwaltet"
#. Translators: this is for network connections that require some kind of key or password
#: ../js/ui/status/network.js:502
#: ../js/ui/status/network.js:506
msgid "authentication required"
msgstr "Legitimierung erforderlich"
#. Translators: this is for devices that require some kind of firmware or kernel
#. module, which is missing
#: ../js/ui/status/network.js:512
#: ../js/ui/status/network.js:516
msgid "firmware missing"
msgstr "Firmware fehlt"
#. Translators: this is for wired network devices that are physically disconnected
#: ../js/ui/status/network.js:519
#: ../js/ui/status/network.js:523
msgid "cable unplugged"
msgstr "Kabel nicht angeschlossen"
#. 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:524
#: ../js/ui/status/network.js:528
msgid "unavailable"
msgstr "nicht verfügbar"
#: ../js/ui/status/network.js:526
#: ../js/ui/status/network.js:530
msgid "connection failed"
msgstr "Verbindung gescheitert"
#: ../js/ui/status/network.js:582 ../js/ui/status/network.js:1507
#: ../js/ui/status/network.js:586 ../js/ui/status/network.js:1546
msgid "More..."
msgstr "Mehr ..."
#. 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:1444
#: ../js/ui/status/network.js:622 ../js/ui/status/network.js:1483
msgid "Connected (private)"
msgstr "Verbunden (privat)"
#: ../js/ui/status/network.js:703
#: ../js/ui/status/network.js:707
msgid "Auto Ethernet"
msgstr "Ethernet (automatisch)"
#: ../js/ui/status/network.js:771
#: ../js/ui/status/network.js:775
msgid "Auto broadband"
msgstr "Mobiles Breitband (automatisch)"
#: ../js/ui/status/network.js:774
#: ../js/ui/status/network.js:778
msgid "Auto dial-up"
msgstr "Einwählverbindung (automatisch)"
#. TRANSLATORS: this the automatic wireless connection name (including the network name)
#: ../js/ui/status/network.js:898 ../js/ui/status/network.js:1456
#: ../js/ui/status/network.js:902 ../js/ui/status/network.js:1495
#, c-format
msgid "Auto %s"
msgstr "%s (automatisch)"
#: ../js/ui/status/network.js:900
#: ../js/ui/status/network.js:904
msgid "Auto bluetooth"
msgstr "Bluetooth (automatisch)"
#: ../js/ui/status/network.js:1458
#: ../js/ui/status/network.js:1497
msgid "Auto wireless"
msgstr "Drahtlos (automatisch)"
#: ../js/ui/status/network.js:1550
#: ../js/ui/status/network.js:1589
msgid "Enable networking"
msgstr "Netzwerk aktivieren"
#: ../js/ui/status/network.js:1562
#: ../js/ui/status/network.js:1601
msgid "Wired"
msgstr "Kabelgebunden"
#: ../js/ui/status/network.js:1573
#: ../js/ui/status/network.js:1612
msgid "Wireless"
msgstr "Drahtlos"
#: ../js/ui/status/network.js:1583
#: ../js/ui/status/network.js:1622
msgid "Mobile broadband"
msgstr "Mobiles Breitband"
#: ../js/ui/status/network.js:1593
#: ../js/ui/status/network.js:1632
msgid "VPN Connections"
msgstr "VPN-Verbindungen"
#: ../js/ui/status/network.js:1605
#: ../js/ui/status/network.js:1644
msgid "Network Settings"
msgstr "Netzwerkeinstellungen"
#: ../js/ui/status/network.js:1897
#: ../js/ui/status/network.js:1936
#, c-format
msgid "You're now connected to mobile broadband connection '%s'"
msgstr "Sie sind nun mit dem mobilen Breitbandnetzwerk »%s« verbunden."
#: ../js/ui/status/network.js:1901
#: ../js/ui/status/network.js:1940
#, c-format
msgid "You're now connected to wireless network '%s'"
msgstr "Sie sind nun mit dem Funknetzwerk »%s« verbunden."
#: ../js/ui/status/network.js:1905
#: ../js/ui/status/network.js:1944
#, c-format
msgid "You're now connected to wired network '%s'"
msgstr "Sie sind nun über Kabel mit dem Netzwerk »%s« verbunden."
#: ../js/ui/status/network.js:1909
#: ../js/ui/status/network.js:1948
#, c-format
msgid "You're now connected to VPN network '%s'"
msgstr "Sie sind nun mit dem VPN-Netzwerk »%s« verbunden."
#: ../js/ui/status/network.js:1914
#: ../js/ui/status/network.js:1953
#, c-format
msgid "You're now connected to '%s'"
msgstr "Sie sind nun mit »%s« verbunden."
#: ../js/ui/status/network.js:1922
#: ../js/ui/status/network.js:1961
msgid "Connection established"
msgstr "Verbindung wurde aufgebaut"
#: ../js/ui/status/network.js:2048
#: ../js/ui/status/network.js:2087
msgid "Networking is disabled"
msgstr "Netzwerk ist deaktiviert"
#: ../js/ui/status/network.js:2173
#: ../js/ui/status/network.js:2212
msgid "Network Manager"
msgstr "Netzwerk-Verwaltung"
@ -1114,10 +1121,6 @@ msgstr "Tablet"
msgid "Computer"
msgstr "Rechner"
#: ../js/ui/status/power.js:238 ../src/shell-app-system.c:1088
msgid "Unknown"
msgstr "Unbekannt"
#: ../js/ui/status/volume.js:43
msgid "Volume"
msgstr "Lautstärke"
@ -1129,26 +1132,36 @@ msgstr "Mikrofon"
#. 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
#: ../js/ui/telepathyClient.js:234
msgid "Invitation"
msgstr "Einladung"
#: ../js/ui/telepathyClient.js:500
#. We got the TpContact
#: ../js/ui/telepathyClient.js:300
msgid "Call"
msgstr "Anruf"
#. We got the TpContact
#: ../js/ui/telepathyClient.js:328
msgid "File Transfer"
msgstr "Dateiübertragung"
#: ../js/ui/telepathyClient.js:594
#, c-format
msgid "%s is online."
msgstr "%s ist angemeldet."
#: ../js/ui/telepathyClient.js:505
#: ../js/ui/telepathyClient.js:599
#, c-format
msgid "%s is offline."
msgstr "%s ist abgemeldet."
#: ../js/ui/telepathyClient.js:508
#: ../js/ui/telepathyClient.js:602
#, c-format
msgid "%s is away."
msgstr "»%s« ist abwesend."
#: ../js/ui/telepathyClient.js:511
#: ../js/ui/telepathyClient.js:605
#, c-format
msgid "%s is busy."
msgstr "%s ist beschäftigt."
@ -1156,21 +1169,35 @@ msgstr "%s ist beschäftigt."
#. 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:700
#: ../js/ui/telepathyClient.js:791
#, no-c-format
msgid "Sent at %X on %A"
msgstr "Gesendet am %A um %X "
msgid "Sent at <b>%X</b> on <b>%A</b>"
msgstr "Gesendet am <b>%A</b> um <b>%X</b> "
#. Translators: this is a time format in the style of "Wednesday, May 25",
#. shown when you get a chat message in the same year.
#: ../js/ui/telepathyClient.js:797
#, no-c-format
msgid "Sent on <b>%A</b>, <b>%B %d</b>"
msgstr "Gesendet am <b>%A</b>, <b>%d. %B</b>"
#. Translators: this is a time format in the style of "Wednesday, May 25, 2012",
#. shown when you get a chat message in a different year.
#: ../js/ui/telepathyClient.js:802
#, no-c-format
msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
msgstr "Gesendet am <b>%A</b>, <b>%d. %B</b> %Y"
#. Translators: this is the other person changing their old IM name to their new
#. IM name.
#: ../js/ui/telepathyClient.js:750
#: ../js/ui/telepathyClient.js:843
#, c-format
msgid "%s is now known as %s"
msgstr "%s heißt jetzt %s"
#. translators: argument is a room name like
#. * room@jabber.org for example.
#: ../js/ui/telepathyClient.js:857
#: ../js/ui/telepathyClient.js:950
#, c-format
msgid "Invitation to %s"
msgstr "Einladung zum Betreten von %s"
@ -1178,19 +1205,45 @@ msgstr "Einladung zum Betreten von %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:865
#: ../js/ui/telepathyClient.js:958
#, c-format
msgid "%s is inviting you to join %s"
msgstr "%s lädt Sie ein, %s beizutreten"
#: ../js/ui/telepathyClient.js:867
#: ../js/ui/telepathyClient.js:960 ../js/ui/telepathyClient.js:1049
msgid "Decline"
msgstr "Ablehnen"
#: ../js/ui/telepathyClient.js:868
#: ../js/ui/telepathyClient.js:961 ../js/ui/telepathyClient.js:1050
msgid "Accept"
msgstr "Annehmen"
#. translators: argument is a contact name like Alice for example.
#: ../js/ui/telepathyClient.js:994
#, c-format
msgid "Video call from %s"
msgstr "Video-Anruf von %s"
#. translators: argument is a contact name like Alice for example.
#: ../js/ui/telepathyClient.js:997
#, c-format
msgid "Call from %s"
msgstr "Anruf von %s"
#: ../js/ui/telepathyClient.js:1007
msgid "Answer"
msgstr "Antworten"
#. To translators: The first parameter is
#. * the contact's alias and the second one is the
#. * file name. The string will be something
#. * like: "Alice is sending you test.ogg"
#.
#: ../js/ui/telepathyClient.js:1043
#, c-format
msgid "%s is sending you %s"
msgstr "%s sendet Ihnen %s"
#. Translators: this is the text displayed
#. in the search entry when no search is
#. active; it should not exceed ~30
@ -1199,16 +1252,16 @@ msgstr "Annehmen"
msgid "Type to search..."
msgstr "Suchbegriff eingeben …"
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:257
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:261
msgid "Search"
msgstr "Suchen"
#: ../js/ui/windowAttentionHandler.js:40
#: ../js/ui/windowAttentionHandler.js:39
#, c-format
msgid "%s has finished starting"
msgstr "Start von %s ist abgeschlossen"
#: ../js/ui/windowAttentionHandler.js:42
#: ../js/ui/windowAttentionHandler.js:41
#, c-format
msgid "'%s' is ready"
msgstr "»%s« ist bereit"
@ -1239,7 +1292,7 @@ msgstr "Systemklänge"
msgid "Print version"
msgstr "Version ausgeben"
#: ../src/shell-app.c:464
#: ../src/shell-app.c:580
#, c-format
msgid "Failed to launch '%s'"
msgstr "»%s« konnte nicht gestartet werden"
@ -1256,13 +1309,13 @@ msgstr "Vorgabe"
msgid "Authentication dialog was dismissed by the user"
msgstr "Der Dialog zur Legitimierung wurde vom Benutzer geschlossen"
#: ../src/shell-util.c:96
#: ../src/shell-util.c:100
msgid "Home Folder"
msgstr "Persönlicher Ordner"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-util.c:111
#: ../src/shell-util.c:115
msgid "File System"
msgstr "Dateisystem"
@ -1271,11 +1324,31 @@ msgstr "Dateisystem"
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-util.c:307
#: ../src/shell-util.c:311
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"
#: ../src/shell-util.c:600
msgid "calendar:week_start:0"
msgstr "calendar:week_start:0"
#~ 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 ""
#~ "Die Erweiterungen der GNOME-Shell besitzen eine UUID-Eigenschaft. Dieser "
#~ "Schlüssel listet Erweiterungen auf, welche nicht geladen werden sollen. "
#~ "Diese Einstellung überschreibt »enabled-extensions« für Erweiterungen, die "
#~ "in beiden Listen erscheinen."
#~ msgid "Uuids of extensions to disable"
#~ msgstr "UUIDs der zu deaktivierenden Erweiterungen"
#~ msgid "Localization Settings"
#~ msgstr "Lokalisierungseinstellungen"
#~ msgid "Less than a minute ago"
#~ msgstr "Vor weniger als einer Minute"

886
po/es.po

File diff suppressed because it is too large Load Diff

853
po/fa.po

File diff suppressed because it is too large Load Diff

470
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-07-04 22:20+0200\n"
"PO-Revision-Date: 2011-07-04 22:20+0200\n"
"POT-Creation-Date: 2011-08-24 21:18+0200\n"
"PO-Revision-Date: 2011-08-24 21:19+0200\n"
"Last-Translator: Fran Diéguez <frandieguez@gnome.org>\n"
"Language-Team: Galician <gnome-l10n-gl@gnome.org>\n"
"Language: gl\n"
@ -64,40 +64,30 @@ msgstr ""
"axuste para as extensións que aparecen en ámbalas dúas listas."
#: ../data/org.gnome.shell.gschema.xml.in.h:6
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 ""
"As extensións do GNOME Shell teñen unha propiedade uuid. Esta chave lista as "
"extensións que deberían cargarse. Esta configuración sobrescribe a «enabled-"
"extensions» para as extensións que aparecen en ámbalas dúas listas."
#: ../data/org.gnome.shell.gschema.xml.in.h:7
msgid "History for command (Alt-F2) dialog"
msgstr "Historial do diálogo de orde (Alt-F2)"
#: ../data/org.gnome.shell.gschema.xml.in.h:8
#: ../data/org.gnome.shell.gschema.xml.in.h:7
msgid "History for the looking glass dialog"
msgstr "Historial do diálogo de «looking glass»"
#: ../data/org.gnome.shell.gschema.xml.in.h:9
#: ../data/org.gnome.shell.gschema.xml.in.h:8
msgid "If true, display date in the clock, in addition to time."
msgstr "Se é verdadeiro, móstrase a data no reloxo, ademais da hora."
#: ../data/org.gnome.shell.gschema.xml.in.h:10
#: ../data/org.gnome.shell.gschema.xml.in.h:9
msgid "If true, display seconds in time."
msgstr "Se é verdadeiro, móstranse os segundos na hora."
#: ../data/org.gnome.shell.gschema.xml.in.h:11
#: ../data/org.gnome.shell.gschema.xml.in.h:10
msgid "If true, display the ISO week date in the calendar."
msgstr "Se é verdadeiro, móstrase a data da semana ISO no calendario."
#: ../data/org.gnome.shell.gschema.xml.in.h:12
#: ../data/org.gnome.shell.gschema.xml.in.h:11
msgid "List of desktop file IDs for favorite applications"
msgstr "Mostra os ID de ficheiros desktop para os aplicativos preferidos"
#: ../data/org.gnome.shell.gschema.xml.in.h:14
#: ../data/org.gnome.shell.gschema.xml.in.h:13
#, no-c-format
msgid ""
"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
@ -123,19 +113,19 @@ msgstr ""
"códec VP8. Úsase %T como suposición para o número de fillos óptimos no "
"sistema."
#: ../data/org.gnome.shell.gschema.xml.in.h:15
#: ../data/org.gnome.shell.gschema.xml.in.h:14
msgid "Show date in clock"
msgstr "Mostrar a data no reloxo"
#: ../data/org.gnome.shell.gschema.xml.in.h:16
#: ../data/org.gnome.shell.gschema.xml.in.h:15
msgid "Show the week date in the calendar"
msgstr "Mostrar a data da semana no calendario"
#: ../data/org.gnome.shell.gschema.xml.in.h:17
#: ../data/org.gnome.shell.gschema.xml.in.h:16
msgid "Show time with seconds"
msgstr "Mostrar a hora con segundos"
#: ../data/org.gnome.shell.gschema.xml.in.h:18
#: ../data/org.gnome.shell.gschema.xml.in.h:17
msgid ""
"The applications corresponding to these identifiers will be displayed in the "
"favorites area."
@ -143,7 +133,7 @@ msgstr ""
"Os aplicativos que corresponden a estes identificadores mostraranse na área "
"de preferidos."
#: ../data/org.gnome.shell.gschema.xml.in.h:19
#: ../data/org.gnome.shell.gschema.xml.in.h:18
msgid ""
"The filename for recorded screencasts will be a unique filename based on the "
"current date, and use this extension. It should be changed when recording to "
@ -153,7 +143,7 @@ msgstr ""
"baseado na data actual e usa esta extensión. Debería cambiar ao grabar nun "
"formato de contedor diferente."
#: ../data/org.gnome.shell.gschema.xml.in.h:20
#: ../data/org.gnome.shell.gschema.xml.in.h:19
msgid ""
"The framerate of the resulting screencast recordered by GNOME Shell's "
"screencast recorder in frames-per-second."
@ -161,11 +151,11 @@ msgstr ""
"A taxa de marcos do screencast resultante grabado polo grabador de "
"screencasts de GNOME Shell en marcos-por-segundo."
#: ../data/org.gnome.shell.gschema.xml.in.h:21
#: ../data/org.gnome.shell.gschema.xml.in.h:20
msgid "The gstreamer pipeline used to encode the screencast"
msgstr "A tubería de gstreamer usada para codificar o screencast"
#: ../data/org.gnome.shell.gschema.xml.in.h:22
#: ../data/org.gnome.shell.gschema.xml.in.h:21
msgid ""
"The shell normally monitors active applications in order to present the most "
"used ones (e.g. in launchers). While this data will be kept private, you may "
@ -177,19 +167,15 @@ msgstr ""
"privados, vostede pode desactivar isto por motivos de privacidade. Teña en "
"conta que facendo isto non eliminará os datos gardados."
#: ../data/org.gnome.shell.gschema.xml.in.h:23
msgid "Uuids of extensions to disable"
msgstr "Uuid das extensións que activar"
#: ../data/org.gnome.shell.gschema.xml.in.h:24
#: ../data/org.gnome.shell.gschema.xml.in.h:22
msgid "Uuids of extensions to enable"
msgstr "Uuid das extensións que activar"
#: ../data/org.gnome.shell.gschema.xml.in.h:25
#: ../data/org.gnome.shell.gschema.xml.in.h:23
msgid "Whether to collect stats about applications usage"
msgstr "Indica se recoller estatísticas sobre o uso dos aplicativos"
#: ../data/org.gnome.shell.gschema.xml.in.h:26
#: ../data/org.gnome.shell.gschema.xml.in.h:24
msgid "disabled OpenSearch providers"
msgstr "fornecedores de OpenSearch desactivados"
@ -209,27 +195,27 @@ msgid "Execution of '%s' failed:"
msgstr "Produciuse un fallo na execución de «%s»:"
#. Translators: Filter to display all applications
#: ../js/ui/appDisplay.js:258
#: ../js/ui/appDisplay.js:253
msgid "All"
msgstr "Todos"
#: ../js/ui/appDisplay.js:357
#: ../js/ui/appDisplay.js:315
msgid "APPLICATIONS"
msgstr "APLICATIVOS"
#: ../js/ui/appDisplay.js:383
#: ../js/ui/appDisplay.js:373
msgid "SETTINGS"
msgstr "CONFIGURACIÓN"
#: ../js/ui/appDisplay.js:656
#: ../js/ui/appDisplay.js:681
msgid "New Window"
msgstr "Xanela nova"
#: ../js/ui/appDisplay.js:659
#: ../js/ui/appDisplay.js:684
msgid "Remove from Favorites"
msgstr "Eliminar dos favoritos"
#: ../js/ui/appDisplay.js:660
#: ../js/ui/appDisplay.js:685
msgid "Add to Favorites"
msgstr "Engadir aos favoritos"
@ -243,6 +229,15 @@ msgstr "%s foi engadido aos seus favoritos."
msgid "%s has been removed from your favorites."
msgstr "%s foi eliminado dos seus favoritos."
#: ../js/ui/autorunManager.js:591
#, c-format
msgid "Open with %s"
msgstr "Abrir con %s"
#: ../js/ui/autorunManager.js:617
msgid "Eject"
msgstr "Expulsar"
#. Translators: Shown in calendar event list for all day events
#. * Keep it short, best if you can use less then 10 characters
#.
@ -357,94 +352,94 @@ msgid "S"
msgstr "S"
#. Translators: Text to show if there are no events
#: ../js/ui/calendar.js:701
#: ../js/ui/calendar.js:678
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:623
#: ../js/ui/calendar.js:694
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:626
#: ../js/ui/calendar.js:697
msgctxt "calendar heading"
msgid "%A, %B %d, %Y"
msgstr "%A, %d de %B de %Y"
#: ../js/ui/calendar.js:730
#: ../js/ui/calendar.js:707
msgid "Today"
msgstr "Hoxe"
#: ../js/ui/calendar.js:734
#: ../js/ui/calendar.js:711
msgid "Tomorrow"
msgstr "Mañá"
#: ../js/ui/calendar.js:743
#: ../js/ui/calendar.js:720
msgid "This week"
msgstr "Esta semana"
#: ../js/ui/calendar.js:751
#: ../js/ui/calendar.js:728
msgid "Next week"
msgstr "A vindeira semana"
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1045
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1123
msgid "Remove"
msgstr "Eliminar"
#: ../js/ui/dateMenu.js:89
#: ../js/ui/dateMenu.js:90
msgid "Date and Time Settings"
msgstr "Configuracións do data e hora"
#: ../js/ui/dateMenu.js:109
#: ../js/ui/dateMenu.js:110
msgid "Open Calendar"
msgstr "Abrir o calendario"
#. Translators: This is the time format with date used
#. in 24-hour mode.
#: ../js/ui/dateMenu.js:162
#: ../js/ui/dateMenu.js:167
msgid "%a %b %e, %R:%S"
msgstr "%a %e de %b, %R:%S"
#: ../js/ui/dateMenu.js:163
#: ../js/ui/dateMenu.js:168
msgid "%a %b %e, %R"
msgstr "%a %e de %b, %R"
#. Translators: This is the time format without date used
#. in 24-hour mode.
#: ../js/ui/dateMenu.js:167
#: ../js/ui/dateMenu.js:172
msgid "%a %R:%S"
msgstr "%a %R:%S"
#: ../js/ui/dateMenu.js:168
#: ../js/ui/dateMenu.js:173
msgid "%a %R"
msgstr "%a %R"
#. Translators: This is a time format with date used
#. for AM/PM.
#: ../js/ui/dateMenu.js:175
#: ../js/ui/dateMenu.js:180
msgid "%a %b %e, %l:%M:%S %p"
msgstr "%a %e de %b, %l:%M:%S"
#: ../js/ui/dateMenu.js:176
#: ../js/ui/dateMenu.js:181
msgid "%a %b %e, %l:%M %p"
msgstr "%a %e de %b, %l:%M %p"
#. Translators: This is a time format without date used
#. for AM/PM.
#: ../js/ui/dateMenu.js:180
#: ../js/ui/dateMenu.js:185
msgid "%a %l:%M:%S %p"
msgstr "%a %l:%M:%S %p"
#: ../js/ui/dateMenu.js:181
#: ../js/ui/dateMenu.js:186
msgid "%a %l:%M %p"
msgstr "%a %l:%M %p"
#. Translators: This is the date format to use when the calendar popup is
#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
#.
#: ../js/ui/dateMenu.js:192
#: ../js/ui/dateMenu.js:197
msgid "%A %B %e, %Y"
msgstr "%a, %e de %B, %Y"
@ -523,44 +518,53 @@ msgstr "Reiniciando o computador."
msgid "Cancel"
msgstr "Cancelar"
#: ../js/ui/lookingGlass.js:641
#: ../js/ui/lookingGlass.js:640
msgid "No extensions installed"
msgstr "Non hai ningunha extensión instalada"
#: ../js/ui/lookingGlass.js:678
#: ../js/ui/lookingGlass.js:686
msgid "Enabled"
msgstr "Activado"
#. translators:
#. * The device has been disabled
#: ../js/ui/lookingGlass.js:680 ../src/gvc/gvc-mixer-control.c:1091
#: ../js/ui/lookingGlass.js:688 ../src/gvc/gvc-mixer-control.c:1091
msgid "Disabled"
msgstr "Desactivado"
#: ../js/ui/lookingGlass.js:682
#: ../js/ui/lookingGlass.js:690
msgid "Error"
msgstr "Erro"
#: ../js/ui/lookingGlass.js:684
#: ../js/ui/lookingGlass.js:692
msgid "Out of date"
msgstr "Desactualizado"
#: ../js/ui/lookingGlass.js:709
#: ../js/ui/lookingGlass.js:694
msgid "Downloading"
msgstr "Descargando"
#: ../js/ui/lookingGlass.js:719
msgid "View Source"
msgstr "Ver fonte"
#: ../js/ui/lookingGlass.js:715
#: ../js/ui/lookingGlass.js:725
msgid "Web Page"
msgstr "Páxina web"
#: ../js/ui/messageTray.js:1038
#: ../js/ui/messageTray.js:1116
msgid "Open"
msgstr "Abrir"
#: ../js/ui/messageTray.js:2210
#: ../js/ui/messageTray.js:2277
msgid "System Information"
msgstr "Información do sistema"
#: ../js/ui/notificationDaemon.js:427 ../js/ui/status/power.js:238
#: ../src/shell-app.c:354
msgid "Unknown"
msgstr "Descoñecido"
#: ../js/ui/overview.js:89
msgid "Undo"
msgstr "Desfacer"
@ -580,18 +584,18 @@ msgid "Dash"
msgstr "Panel"
#. TODO - _quit() doesn't really work on apps in state STARTING yet
#: ../js/ui/panel.js:533
#: ../js/ui/panel.js:531
#, c-format
msgid "Quit %s"
msgstr "Saír 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:567
msgid "Activities"
msgstr "Actividades"
#: ../js/ui/panel.js:1013
#: ../js/ui/panel.js:876
msgid "Top Bar"
msgstr "Barra superior"
@ -657,43 +661,9 @@ msgstr "Buscando..."
msgid "No matching results."
msgstr "Non hai resultados que coincidan."
#: ../js/ui/statusMenu.js:202 ../js/ui/statusMenu.js:206
#: ../js/ui/statusMenu.js:272
msgid "Power Off..."
msgstr "Apagar…"
#: ../js/ui/statusMenu.js:204 ../js/ui/statusMenu.js:206
#: ../js/ui/statusMenu.js:271
msgid "Suspend"
msgstr "Suspender"
#: ../js/ui/statusMenu.js:227
msgid "Available"
msgstr "Dispoñíbel"
#: ../js/ui/statusMenu.js:232
msgid "Busy"
msgstr "Ocupado"
#: ../js/ui/statusMenu.js:240
msgid "My Account"
msgstr "A miña conta"
#: ../js/ui/statusMenu.js:244
msgid "System Settings"
msgstr "Configuracións do sistema"
#: ../js/ui/statusMenu.js:252
msgid "Lock Screen"
msgstr "Bloquear pantalla"
#: ../js/ui/statusMenu.js:257
msgid "Switch User"
msgstr "Cambiar de usuario"
#: ../js/ui/statusMenu.js:262
msgid "Log Out..."
msgstr "Saír da sesión…"
#: ../js/ui/shellMountOperation.js:285
msgid "Wrong password, please try again"
msgstr "O contrasinal é incorrecto, ténteo de novo"
#: ../js/ui/status/accessibility.js:60
msgid "Zoom"
@ -768,11 +738,11 @@ msgstr "hardware desactivado"
msgid "Connection"
msgstr "Conexión"
#: ../js/ui/status/bluetooth.js:226 ../js/ui/status/network.js:493
#: ../js/ui/status/bluetooth.js:226 ../js/ui/status/network.js:497
msgid "disconnecting..."
msgstr "conectando…"
#: ../js/ui/status/bluetooth.js:239 ../js/ui/status/network.js:499
#: ../js/ui/status/bluetooth.js:239 ../js/ui/status/network.js:503
msgid "connecting..."
msgstr "conectando…"
@ -823,7 +793,7 @@ msgstr "Conceder acceso sempre"
msgid "Grant this time only"
msgstr "Conceder só esta vez"
#: ../js/ui/status/bluetooth.js:409
#: ../js/ui/status/bluetooth.js:409 ../js/ui/telepathyClient.js:1006
msgid "Reject"
msgstr "Rexeitar"
@ -864,148 +834,186 @@ msgid "OK"
msgstr "Aceptar"
#: ../js/ui/status/keyboard.js:71
msgid "Show Keyboard Layout..."
msgstr "Mostrar a distribución do teclado"
msgid "Show Keyboard Layout"
msgstr "Mostrar a distribucin do teclado"
#: ../js/ui/status/keyboard.js:75
msgid "Localization Settings"
msgstr "Configuracións do son"
msgid "Region and Language Settings"
msgstr "Configuración rexional e de idioma"
#: ../js/ui/status/network.js:123
#: ../js/ui/statusMenu.js:192 ../js/ui/statusMenu.js:196
#: ../js/ui/statusMenu.js:262
msgid "Power Off..."
msgstr "Apagar…"
#: ../js/ui/statusMenu.js:194 ../js/ui/statusMenu.js:196
#: ../js/ui/statusMenu.js:261
msgid "Suspend"
msgstr "Suspender"
#: ../js/ui/statusMenu.js:217
msgid "Available"
msgstr "Dispoñíbel"
#: ../js/ui/statusMenu.js:222
msgid "Busy"
msgstr "Ocupado"
#: ../js/ui/statusMenu.js:230
msgid "My Account"
msgstr "A miña conta"
#: ../js/ui/statusMenu.js:234
msgid "System Settings"
msgstr "Configuracións do sistema"
#: ../js/ui/statusMenu.js:242
msgid "Lock Screen"
msgstr "Bloquear pantalla"
#: ../js/ui/statusMenu.js:247
msgid "Switch User"
msgstr "Cambiar de usuario"
#: ../js/ui/statusMenu.js:252
msgid "Log Out..."
msgstr "Saír da sesión…"
#: ../js/ui/status/network.js:108
msgid "<unknown>"
msgstr "<descoñecida>"
#. Translators: this indicates that wireless or wwan is disabled by hardware killswitch
#: ../js/ui/status/network.js:292
#: ../js/ui/status/network.js:296
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:491
#: ../js/ui/status/network.js:495
msgid "unmanaged"
msgstr "non xestionada"
#. Translators: this is for network connections that require some kind of key or password
#: ../js/ui/status/network.js:502
#: ../js/ui/status/network.js:506
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:512
#: ../js/ui/status/network.js:516
msgid "firmware missing"
msgstr "falta o «firmware»"
#. Translators: this is for wired network devices that are physically disconnected
#: ../js/ui/status/network.js:519
#: ../js/ui/status/network.js:523
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:524
#: ../js/ui/status/network.js:528
msgid "unavailable"
msgstr "non dispoñíbel"
#: ../js/ui/status/network.js:526
#: ../js/ui/status/network.js:530
msgid "connection failed"
msgstr "conexión fallida"
#: ../js/ui/status/network.js:582 ../js/ui/status/network.js:1489
#: ../js/ui/status/network.js:586 ../js/ui/status/network.js:1546
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:618 ../js/ui/status/network.js:1427
#: ../js/ui/status/network.js:622 ../js/ui/status/network.js:1483
msgid "Connected (private)"
msgstr "Conectado (privado)"
#: ../js/ui/status/network.js:703
#: ../js/ui/status/network.js:707
msgid "Auto Ethernet"
msgstr "Ethernet automática"
#: ../js/ui/status/network.js:771
#: ../js/ui/status/network.js:775
msgid "Auto broadband"
msgstr "Banda larga automática"
#: ../js/ui/status/network.js:774
#: ../js/ui/status/network.js:778
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:898 ../js/ui/status/network.js:1439
#: ../js/ui/status/network.js:902 ../js/ui/status/network.js:1495
#, c-format
msgid "Auto %s"
msgstr "%s automática"
#: ../js/ui/status/network.js:900
#: ../js/ui/status/network.js:904
msgid "Auto bluetooth"
msgstr "Bluetooth automática"
#: ../js/ui/status/network.js:1441
#: ../js/ui/status/network.js:1497
msgid "Auto wireless"
msgstr "Sen fíos automática"
#: ../js/ui/status/network.js:1531
#: ../js/ui/status/network.js:1589
msgid "Enable networking"
msgstr "Activar rede"
#: ../js/ui/status/network.js:1543
#: ../js/ui/status/network.js:1601
msgid "Wired"
msgstr "Con fíos"
#: ../js/ui/status/network.js:1554
#: ../js/ui/status/network.js:1612
msgid "Wireless"
msgstr "Sen fíos"
#: ../js/ui/status/network.js:1564
#: ../js/ui/status/network.js:1622
msgid "Mobile broadband"
msgstr "Banda larga móbil"
#: ../js/ui/status/network.js:1574
#: ../js/ui/status/network.js:1632
msgid "VPN Connections"
msgstr "Conexións VPN"
#: ../js/ui/status/network.js:1586
#: ../js/ui/status/network.js:1644
msgid "Network Settings"
msgstr "Configuracións da rede"
#: ../js/ui/status/network.js:1878
#: ../js/ui/status/network.js:1936
#, 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:1882
#: ../js/ui/status/network.js:1940
#, 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:1886
#: ../js/ui/status/network.js:1944
#, 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:1890
#: ../js/ui/status/network.js:1948
#, 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:1895
#: ../js/ui/status/network.js:1953
#, c-format
msgid "You're now connected to '%s'"
msgstr "Vostede está conectado agora a «%s»"
#: ../js/ui/status/network.js:1903
#: ../js/ui/status/network.js:1961
msgid "Connection established"
msgstr "Conexión estabelecida"
#: ../js/ui/status/network.js:2029
#: ../js/ui/status/network.js:2087
msgid "Networking is disabled"
msgstr "A rede está desactivada"
#: ../js/ui/status/network.js:2154
#: ../js/ui/status/network.js:2212
msgid "Network Manager"
msgstr "Xestor de rede"
@ -1015,11 +1023,11 @@ 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: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"
@ -1027,78 +1035,74 @@ 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:120
#: ../js/ui/status/power.js:119
#, c-format
msgid "%d %s %d %s remaining"
msgstr "%d %s %d %s retante"
#: ../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] "%d minuto restante"
msgstr[1] "%d minutos restantes"
#: ../js/ui/status/power.js:227
#: ../js/ui/status/power.js:216
msgid "AC adapter"
msgstr "Adaptador de corrente"
#: ../js/ui/status/power.js:229
#: ../js/ui/status/power.js:218
msgid "Laptop battery"
msgstr "Batería do portátil"
#: ../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 "Monitor"
#: ../js/ui/status/power.js:235
#: ../js/ui/status/power.js:224
msgid "Mouse"
msgstr "Rato"
#: ../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óbil"
#: ../js/ui/status/power.js:243
#: ../js/ui/status/power.js:232
msgid "Media player"
msgstr "Reprodutor multimedia"
#: ../js/ui/status/power.js:245
#: ../js/ui/status/power.js:234
msgid "Tablet"
msgstr "Tablet"
#: ../js/ui/status/power.js:247
#: ../js/ui/status/power.js:236
msgid "Computer"
msgstr "Computador"
#: ../js/ui/status/power.js:249 ../src/shell-app-system.c:1088
msgid "Unknown"
msgstr "Descoñecido"
#: ../js/ui/status/volume.js:43
msgid "Volume"
msgstr "Volume"
@ -1107,22 +1111,39 @@ msgstr "Volume"
msgid "Microphone"
msgstr "Micrófono"
#: ../js/ui/telepathyClient.js:419
#. 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:234
msgid "Invitation"
msgstr "Convite"
#. We got the TpContact
#: ../js/ui/telepathyClient.js:300
msgid "Call"
msgstr "Chamar"
#. We got the TpContact
#: ../js/ui/telepathyClient.js:328
msgid "File Transfer"
msgstr "Transferencia de ficheiro"
#: ../js/ui/telepathyClient.js:594
#, c-format
msgid "%s is online."
msgstr "%s está conectado/a."
#: ../js/ui/telepathyClient.js:424
#: ../js/ui/telepathyClient.js:599
#, c-format
msgid "%s is offline."
msgstr "%s está desconectado/a."
#: ../js/ui/telepathyClient.js:427
#: ../js/ui/telepathyClient.js:602
#, c-format
msgid "%s is away."
msgstr "%s está ausente."
#: ../js/ui/telepathyClient.js:430
#: ../js/ui/telepathyClient.js:605
#, c-format
msgid "%s is busy."
msgstr "%s está ocupado/a."
@ -1130,18 +1151,81 @@ 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:615
#: ../js/ui/telepathyClient.js:791
#, no-c-format
msgid "Sent at %X on %A"
msgstr "Enviado ás %X o %A"
msgid "Sent at <b>%X</b> on <b>%A</b>"
msgstr "Enviado ás <b>%X</b> o <b>%A</b>"
#. Translators: this is a time format in the style of "Wednesday, May 25",
#. shown when you get a chat message in the same year.
#: ../js/ui/telepathyClient.js:797
#, no-c-format
msgid "Sent on <b>%A</b>, <b>%B %d</b>"
msgstr "Enviado ás <b>%X</b> o <b>%B %d</b>"
#. Translators: this is a time format in the style of "Wednesday, May 25, 2012",
#. shown when you get a chat message in a different year.
#: ../js/ui/telepathyClient.js:802
#, no-c-format
msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
msgstr "Enviado ás <b>%X</b> o <b>%B %d</b>, %Y"
#. Translators: this is the other person changing their old IM name to their new
#. IM name.
#: ../js/ui/telepathyClient.js:665
#: ../js/ui/telepathyClient.js:843
#, c-format
msgid "%s is now known as %s"
msgstr "%s é coñecido agora como %s"
#. translators: argument is a room name like
#. * room@jabber.org for example.
#: ../js/ui/telepathyClient.js:950
#, c-format
msgid "Invitation to %s"
msgstr "Convidado 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:958
#, c-format
msgid "%s is inviting you to join %s"
msgstr "%s esta convidando a unirse a %s"
#: ../js/ui/telepathyClient.js:960 ../js/ui/telepathyClient.js:1049
msgid "Decline"
msgstr "Rexeitar"
#: ../js/ui/telepathyClient.js:961 ../js/ui/telepathyClient.js:1050
msgid "Accept"
msgstr "Aceptar"
#. translators: argument is a contact name like Alice for example.
#: ../js/ui/telepathyClient.js:994
#, c-format
msgid "Video call from %s"
msgstr "Videochamada de %s"
#. translators: argument is a contact name like Alice for example.
#: ../js/ui/telepathyClient.js:997
#, c-format
msgid "Call from %s"
msgstr "Chamada de %s"
#: ../js/ui/telepathyClient.js:1007
msgid "Answer"
msgstr "Respostar"
#. To translators: The first parameter is
#. * the contact's alias and the second one is the
#. * file name. The string will be something
#. * like: "Alice is sending you test.ogg"
#.
#: ../js/ui/telepathyClient.js:1043
#, c-format
msgid "%s is sending you %s"
msgstr "%s esta enviándolle %s"
#. Translators: this is the text displayed
#. in the search entry when no search is
#. active; it should not exceed ~30
@ -1150,16 +1234,16 @@ msgstr "%s é coñecido agora como %s"
msgid "Type to search..."
msgstr "Teclear para buscar…"
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:257
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:261
msgid "Search"
msgstr "Buscar"
#: ../js/ui/windowAttentionHandler.js:40
#: ../js/ui/windowAttentionHandler.js:39
#, c-format
msgid "%s has finished starting"
msgstr "%s rematou de iniarse"
#: ../js/ui/windowAttentionHandler.js:42
#: ../js/ui/windowAttentionHandler.js:41
#, c-format
msgid "'%s' is ready"
msgstr "«%s» está preparado"
@ -1186,11 +1270,11 @@ msgstr[1] "%u entradas"
msgid "System Sounds"
msgstr "Sons do sistema"
#: ../src/main.c:445
#: ../src/main.c:466
msgid "Print version"
msgstr "Imprimir versión"
#: ../src/shell-app.c:464
#: ../src/shell-app.c:580
#, c-format
msgid "Failed to launch '%s'"
msgstr "Produciuse un fallo ao iniciar «%s»"
@ -1207,13 +1291,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:96
#: ../src/shell-util.c:100
msgid "Home Folder"
msgstr "Cartafol persoal"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-util.c:111
#: ../src/shell-util.c:115
msgid "File System"
msgstr "Sistema de ficheiros"
@ -1222,11 +1306,31 @@ msgstr "Sistema de ficheiros"
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-util.c:307
#: ../src/shell-util.c:311
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"
#: ../src/shell-util.c:600
msgid "calendar:week_start:0"
msgstr "calendar:week_start:1"
#~ 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 ""
#~ "As extensións do GNOME Shell teñen unha propiedade uuid. Esta chave lista "
#~ "as extensións que deberían cargarse. Esta configuración sobrescribe a "
#~ "«enabled-extensions» para as extensións que aparecen en ámbalas dúas "
#~ "listas."
#~ msgid "Uuids of extensions to disable"
#~ msgstr "Uuid das extensións que activar"
#~ msgid "Localization Settings"
#~ msgstr "Configuracións do son"
#~ msgid "Less than a minute ago"
#~ msgstr "Hai menos dun minuto"

332
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-07-16 17:26+0300\n"
"PO-Revision-Date: 2011-07-16 17:27+0200\n"
"POT-Creation-Date: 2011-08-18 23:52+0300\n"
"PO-Revision-Date: 2011-08-18 23:55+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:252
#: ../js/ui/appDisplay.js:253
msgid "All"
msgstr "הכול"
#: ../js/ui/appDisplay.js:351
#: ../js/ui/appDisplay.js:315
msgid "APPLICATIONS"
msgstr "יישומים"
#: ../js/ui/appDisplay.js:377
#: ../js/ui/appDisplay.js:373
msgid "SETTINGS"
msgstr "הגדרות"
#: ../js/ui/appDisplay.js:650
#: ../js/ui/appDisplay.js:681
msgid "New Window"
msgstr "חלון חדש"
#: ../js/ui/appDisplay.js:653
#: ../js/ui/appDisplay.js:684
msgid "Remove from Favorites"
msgstr "הסרה מהמועדפים"
#: ../js/ui/appDisplay.js:654
#: ../js/ui/appDisplay.js:685
msgid "Add to Favorites"
msgstr "הוספה למועדפים"
@ -238,6 +238,15 @@ msgstr "%s נוסף למועדפים שלך."
msgid "%s has been removed from your favorites."
msgstr "%s הוסר מהמועדפים שלך."
#: ../js/ui/autorunManager.js:591
#, c-format
msgid "Open with %s"
msgstr "פתיחה באמצעות %s"
#: ../js/ui/autorunManager.js:617
msgid "Eject"
msgstr "שליפה"
#. Translators: Shown in calendar event list for all day events
#. * Keep it short, best if you can use less then 10 characters
#.
@ -352,94 +361,94 @@ msgid "S"
msgstr "ש׳"
#. Translators: Text to show if there are no events
#: ../js/ui/calendar.js:701
#: ../js/ui/calendar.js:678
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:749
#: ../js/ui/calendar.js:694
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:752
#: ../js/ui/calendar.js:697
msgctxt "calendar heading"
msgid "%A, %B %d, %Y"
msgstr "%A, ה־%e ב%B, %Y"
#: ../js/ui/calendar.js:730
#: ../js/ui/calendar.js:707
msgid "Today"
msgstr "היום"
#: ../js/ui/calendar.js:734
#: ../js/ui/calendar.js:711
msgid "Tomorrow"
msgstr "מחר"
#: ../js/ui/calendar.js:743
#: ../js/ui/calendar.js:720
msgid "This week"
msgstr "השבוע"
#: ../js/ui/calendar.js:751
#: ../js/ui/calendar.js:728
msgid "Next week"
msgstr "בשבוע הבא"
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1122
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1123
msgid "Remove"
msgstr "הסרה"
#: ../js/ui/dateMenu.js:89
#: ../js/ui/dateMenu.js:90
msgid "Date and Time Settings"
msgstr "הגדרות תאריך ושעה"
#: ../js/ui/dateMenu.js:109
#: ../js/ui/dateMenu.js:110
msgid "Open Calendar"
msgstr "פתיחת היומן"
#. Translators: This is the time format with date used
#. in 24-hour mode.
#: ../js/ui/dateMenu.js:162
#: ../js/ui/dateMenu.js:167
msgid "%a %b %e, %R:%S"
msgstr "%a %b %e, %R:%S"
#: ../js/ui/dateMenu.js:163
#: ../js/ui/dateMenu.js:168
msgid "%a %b %e, %R"
msgstr "%a %b %e, %R"
#. Translators: This is the time format without date used
#. in 24-hour mode.
#: ../js/ui/dateMenu.js:167
#: ../js/ui/dateMenu.js:172
msgid "%a %R:%S"
msgstr "%a %R:%S"
#: ../js/ui/dateMenu.js:168
#: ../js/ui/dateMenu.js:173
msgid "%a %R"
msgstr "%a %R"
#. Translators: This is a time format with date used
#. for AM/PM.
#: ../js/ui/dateMenu.js:175
#: ../js/ui/dateMenu.js:180
msgid "%a %b %e, %l:%M:%S %p"
msgstr "%a %b %e, %l:%M:%S %p"
#: ../js/ui/dateMenu.js:176
#: ../js/ui/dateMenu.js:181
msgid "%a %b %e, %l:%M %p"
msgstr "%a %b %e, %l:%M %p"
#. Translators: This is a time format without date used
#. for AM/PM.
#: ../js/ui/dateMenu.js:180
#: ../js/ui/dateMenu.js:185
msgid "%a %l:%M:%S %p"
msgstr "%a %l:%M:%S %p"
#: ../js/ui/dateMenu.js:181
#: ../js/ui/dateMenu.js:186
msgid "%a %l:%M %p"
msgstr "%a %l:%M %p"
#. Translators: This is the date format to use when the calendar popup is
#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
#.
#: ../js/ui/dateMenu.js:192
#: ../js/ui/dateMenu.js:197
msgid "%A %B %e, %Y"
msgstr "%A ה־%e ב%B, %Y"
@ -514,44 +523,49 @@ msgstr "המערכת מופעלת מחדש"
msgid "Cancel"
msgstr "ביטול"
#: ../js/ui/lookingGlass.js:641
#: ../js/ui/lookingGlass.js:640
msgid "No extensions installed"
msgstr "לא מותקנות הרחבות"
#: ../js/ui/lookingGlass.js:678
#: ../js/ui/lookingGlass.js:686
msgid "Enabled"
msgstr "פעיל"
#. translators:
#. * The device has been disabled
#: ../js/ui/lookingGlass.js:680 ../src/gvc/gvc-mixer-control.c:1091
#: ../js/ui/lookingGlass.js:688 ../src/gvc/gvc-mixer-control.c:1091
msgid "Disabled"
msgstr "מנוטרל"
#: ../js/ui/lookingGlass.js:682
#: ../js/ui/lookingGlass.js:690
msgid "Error"
msgstr "שגיאה"
#: ../js/ui/lookingGlass.js:684
#: ../js/ui/lookingGlass.js:692
msgid "Out of date"
msgstr "לא בתוקף"
#: ../js/ui/lookingGlass.js:709
#: ../js/ui/lookingGlass.js:717
msgid "View Source"
msgstr "צפייה במקור"
#: ../js/ui/lookingGlass.js:715
#: ../js/ui/lookingGlass.js:723
msgid "Web Page"
msgstr "דף אינטרנט"
#: ../js/ui/messageTray.js:1115
#: ../js/ui/messageTray.js:1116
msgid "Open"
msgstr "פתיחה"
#: ../js/ui/messageTray.js:2286
#: ../js/ui/messageTray.js:2277
msgid "System Information"
msgstr "פרטי המערכת"
#: ../js/ui/notificationDaemon.js:427 ../js/ui/status/power.js:238
#: ../src/shell-app.c:340
msgid "Unknown"
msgstr "לא ידוע"
#: ../js/ui/overview.js:89
msgid "Undo"
msgstr "ביטול"
@ -571,18 +585,18 @@ msgid "Dash"
msgstr "חלונית"
#. TODO - _quit() doesn't really work on apps in state STARTING yet
#: ../js/ui/panel.js:532
#: ../js/ui/panel.js:531
#, c-format
msgid "Quit %s"
msgstr "יציאה מ־%s"
#. Translators: If there is no suitable word for "Activities"
#. in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:568
#: ../js/ui/panel.js:567
msgid "Activities"
msgstr "פעילויות"
#: ../js/ui/panel.js:951
#: ../js/ui/panel.js:876
msgid "Top Bar"
msgstr "הסרגל העליון"
@ -648,43 +662,9 @@ msgstr "בחיפוש..."
msgid "No matching results."
msgstr "אין תוצאות תואמות."
#: ../js/ui/statusMenu.js:192 ../js/ui/statusMenu.js:196
#: ../js/ui/statusMenu.js:262
msgid "Power Off..."
msgstr "כיבוי..."
#: ../js/ui/statusMenu.js:194 ../js/ui/statusMenu.js:196
#: ../js/ui/statusMenu.js:261
msgid "Suspend"
msgstr "השהיה"
#: ../js/ui/statusMenu.js:217
msgid "Available"
msgstr "פנוי"
#: ../js/ui/statusMenu.js:222
msgid "Busy"
msgstr "עסוק"
#: ../js/ui/statusMenu.js:230
msgid "My Account"
msgstr "החשבון שלי"
#: ../js/ui/statusMenu.js:234
msgid "System Settings"
msgstr "הגדרות המערכת"
#: ../js/ui/statusMenu.js:242
msgid "Lock Screen"
msgstr "נעילת המסך"
#: ../js/ui/statusMenu.js:247
msgid "Switch User"
msgstr "החלפת משתמש"
#: ../js/ui/statusMenu.js:252
msgid "Log Out..."
msgstr "ניתוק..."
#: ../js/ui/shellMountOperation.js:285
msgid "Wrong password, please try again"
msgstr "ססמה שגויה, נא לנסות שוב"
#: ../js/ui/status/accessibility.js:60
msgid "Zoom"
@ -759,11 +739,11 @@ msgstr "מנוטרל חומרתית"
msgid "Connection"
msgstr "חיבור"
#: ../js/ui/status/bluetooth.js:226 ../js/ui/status/network.js:493
#: ../js/ui/status/bluetooth.js:226 ../js/ui/status/network.js:497
msgid "disconnecting..."
msgstr "בהליכי ניתוק..."
#: ../js/ui/status/bluetooth.js:239 ../js/ui/status/network.js:499
#: ../js/ui/status/bluetooth.js:239 ../js/ui/status/network.js:503
msgid "connecting..."
msgstr "בהתחברות..."
@ -814,7 +794,7 @@ msgstr "תמיד להעניק גישה"
msgid "Grant this time only"
msgstr "הענקת גישה הפעם בלבד"
#: ../js/ui/status/bluetooth.js:409 ../js/ui/telepathyClient.js:954
#: ../js/ui/status/bluetooth.js:409 ../js/ui/telepathyClient.js:985
msgid "Reject"
msgstr "סירוב"
@ -855,148 +835,186 @@ msgid "OK"
msgstr "אישור"
#: ../js/ui/status/keyboard.js:71
msgid "Show Keyboard Layout..."
msgstr "הצגת פריסת המקלדת..."
msgid "Show Keyboard Layout"
msgstr "הצגת פריסת המקלדת"
#: ../js/ui/status/keyboard.js:75
msgid "Localization Settings"
msgstr "הגדרות אזוריות"
msgid "Region and Language Settings"
msgstr "הגדרות אזור ושפה"
#: ../js/ui/status/network.js:123
#: ../js/ui/statusMenu.js:192 ../js/ui/statusMenu.js:196
#: ../js/ui/statusMenu.js:262
msgid "Power Off..."
msgstr "כיבוי..."
#: ../js/ui/statusMenu.js:194 ../js/ui/statusMenu.js:196
#: ../js/ui/statusMenu.js:261
msgid "Suspend"
msgstr "השהיה"
#: ../js/ui/statusMenu.js:217
msgid "Available"
msgstr "פנוי"
#: ../js/ui/statusMenu.js:222
msgid "Busy"
msgstr "עסוק"
#: ../js/ui/statusMenu.js:230
msgid "My Account"
msgstr "החשבון שלי"
#: ../js/ui/statusMenu.js:234
msgid "System Settings"
msgstr "הגדרות המערכת"
#: ../js/ui/statusMenu.js:242
msgid "Lock Screen"
msgstr "נעילת המסך"
#: ../js/ui/statusMenu.js:247
msgid "Switch User"
msgstr "החלפת משתמש"
#: ../js/ui/statusMenu.js:252
msgid "Log Out..."
msgstr "ניתוק..."
#: ../js/ui/status/network.js:108
msgid "<unknown>"
msgstr "<לא ידוע>"
#. Translators: this indicates that wireless or wwan is disabled by hardware killswitch
#: ../js/ui/status/network.js:292
#: ../js/ui/status/network.js:296
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:491
#: ../js/ui/status/network.js:495
msgid "unmanaged"
msgstr "לא מנוהל"
#. Translators: this is for network connections that require some kind of key or password
#: ../js/ui/status/network.js:502
#: ../js/ui/status/network.js:506
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:512
#: ../js/ui/status/network.js:516
msgid "firmware missing"
msgstr "הקושחה חסרה"
#. Translators: this is for wired network devices that are physically disconnected
#: ../js/ui/status/network.js:519
#: ../js/ui/status/network.js:523
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:524
#: ../js/ui/status/network.js:528
msgid "unavailable"
msgstr "לא זמין"
#: ../js/ui/status/network.js:526
#: ../js/ui/status/network.js:530
msgid "connection failed"
msgstr "החיבור נכשל"
#: ../js/ui/status/network.js:582 ../js/ui/status/network.js:1507
#: ../js/ui/status/network.js:586 ../js/ui/status/network.js:1511
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:618 ../js/ui/status/network.js:1444
#: ../js/ui/status/network.js:622 ../js/ui/status/network.js:1448
msgid "Connected (private)"
msgstr "מחובר (פרטי)"
#: ../js/ui/status/network.js:703
#: ../js/ui/status/network.js:707
msgid "Auto Ethernet"
msgstr "אתרנט אוטומטי"
#: ../js/ui/status/network.js:771
#: ../js/ui/status/network.js:775
msgid "Auto broadband"
msgstr "פס רחב אוטומטי"
#: ../js/ui/status/network.js:774
#: ../js/ui/status/network.js:778
msgid "Auto dial-up"
msgstr "חיוג אוטומטי"
#. TRANSLATORS: this the automatic wireless connection name (including the network name)
#: ../js/ui/status/network.js:898 ../js/ui/status/network.js:1456
#: ../js/ui/status/network.js:902 ../js/ui/status/network.js:1460
#, c-format
msgid "Auto %s"
msgstr "%s אוטומטי"
#: ../js/ui/status/network.js:900
#: ../js/ui/status/network.js:904
msgid "Auto bluetooth"
msgstr "Bluetooth אוטומטי"
#: ../js/ui/status/network.js:1458
#: ../js/ui/status/network.js:1462
msgid "Auto wireless"
msgstr "אלחוטי אוטומטי"
#: ../js/ui/status/network.js:1550
#: ../js/ui/status/network.js:1554
msgid "Enable networking"
msgstr "הפעלת תכונת הרשת"
#: ../js/ui/status/network.js:1562
#: ../js/ui/status/network.js:1566
msgid "Wired"
msgstr "קווי"
#: ../js/ui/status/network.js:1573
#: ../js/ui/status/network.js:1577
msgid "Wireless"
msgstr "אלחוטי"
#: ../js/ui/status/network.js:1583
#: ../js/ui/status/network.js:1587
msgid "Mobile broadband"
msgstr "פס־רחב נייד"
#: ../js/ui/status/network.js:1593
#: ../js/ui/status/network.js:1597
msgid "VPN Connections"
msgstr "חיבורי VPN"
#: ../js/ui/status/network.js:1605
#: ../js/ui/status/network.js:1609
msgid "Network Settings"
msgstr "הגדרות הרשת"
#: ../js/ui/status/network.js:1897
#: ../js/ui/status/network.js:1901
#, c-format
msgid "You're now connected to mobile broadband connection '%s'"
msgstr "כרגע ישנו חיבור בינך ובין רשת הפס הרחב הניידת '%s'"
#: ../js/ui/status/network.js:1901
#: ../js/ui/status/network.js:1905
#, c-format
msgid "You're now connected to wireless network '%s'"
msgstr "כרגע ישנו חיבור בינך ובין הרשת האלחוטית '%s'"
#: ../js/ui/status/network.js:1905
#: ../js/ui/status/network.js:1909
#, c-format
msgid "You're now connected to wired network '%s'"
msgstr "כרגע ישנו חיבור בינך ובין הרשת הקווית '%s'"
#: ../js/ui/status/network.js:1909
#: ../js/ui/status/network.js:1913
#, c-format
msgid "You're now connected to VPN network '%s'"
msgstr "כרגע ישנו חיבור בינך ובין רשת ה־VPN '%s'"
#: ../js/ui/status/network.js:1914
#: ../js/ui/status/network.js:1918
#, c-format
msgid "You're now connected to '%s'"
msgstr "כעת ישנו חיבור בינך ובין '%s'"
#: ../js/ui/status/network.js:1922
#: ../js/ui/status/network.js:1926
msgid "Connection established"
msgstr "ההתחברות הצליחה"
#: ../js/ui/status/network.js:2048
#: ../js/ui/status/network.js:2052
msgid "Networking is disabled"
msgstr "תכונת הרשת מנוטרלת"
#: ../js/ui/status/network.js:2173
#: ../js/ui/status/network.js:2177
msgid "Network Manager"
msgstr "מנהל הרשתות"
@ -1090,10 +1108,6 @@ msgstr "טבלת שליטה"
msgid "Computer"
msgstr "מחשב"
#: ../js/ui/status/power.js:238 ../src/shell-app-system.c:1088
msgid "Unknown"
msgstr "לא ידוע"
#: ../js/ui/status/volume.js:43
msgid "Volume"
msgstr "עצמה"
@ -1105,31 +1119,36 @@ msgstr "מיקרופון"
#. 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
#: ../js/ui/telepathyClient.js:229
msgid "Invitation"
msgstr "הזמנה"
#. We got the TpContact
#: ../js/ui/telepathyClient.js:285
#: ../js/ui/telepathyClient.js:295
msgid "Call"
msgstr "התקשרות"
#: ../js/ui/telepathyClient.js:541
#. We got the TpContact
#: ../js/ui/telepathyClient.js:323
msgid "File Transfer"
msgstr "העברת קבצים"
#: ../js/ui/telepathyClient.js:573
#, c-format
msgid "%s is online."
msgstr "%s התחבר/ה."
#: ../js/ui/telepathyClient.js:546
#: ../js/ui/telepathyClient.js:578
#, c-format
msgid "%s is offline."
msgstr "%s התנתק/ה."
#: ../js/ui/telepathyClient.js:549
#: ../js/ui/telepathyClient.js:581
#, c-format
msgid "%s is away."
msgstr "'%s' מרוחק/ת."
#: ../js/ui/telepathyClient.js:552
#: ../js/ui/telepathyClient.js:584
#, c-format
msgid "%s is busy."
msgstr "%s עסוק/ה."
@ -1137,21 +1156,35 @@ 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:741
#: ../js/ui/telepathyClient.js:770
#, no-c-format
msgid "Sent at %X on %A"
msgstr "נשלח ב־%X בשעה %A"
msgid "Sent at <b>%X</b> on <b>%A</b>"
msgstr "נשלח ב־<b>%X</b> בשעה <b>%A</b>"
#. Translators: this is a time format in the style of "Wednesday, May 25",
#. shown when you get a chat message in the same year.
#: ../js/ui/telepathyClient.js:776
#, no-c-format
msgid "Sent on <b>%A</b>, <b>%B %d</b>"
msgstr "נשלח ב<b>%A</b>, <b>ה־%d ב%B</b>"
#. Translators: this is a time format in the style of "Wednesday, May 25, 2012",
#. shown when you get a chat message in a different year.
#: ../js/ui/telepathyClient.js:781
#, no-c-format
msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
msgstr "נשלח ב<b>%A</b>, <b>ה־%d ב%B</b>, %Y"
#. Translators: this is the other person changing their old IM name to their new
#. IM name.
#: ../js/ui/telepathyClient.js:791
#: ../js/ui/telepathyClient.js:822
#, 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
#: ../js/ui/telepathyClient.js:929
#, c-format
msgid "Invitation to %s"
msgstr "הזמנה ל־%s"
@ -1159,35 +1192,45 @@ 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
#: ../js/ui/telepathyClient.js:937
#, c-format
msgid "%s is inviting you to join %s"
msgstr "הוזמנת על ידי %s להצטרף אל %s"
#: ../js/ui/telepathyClient.js:908
#: ../js/ui/telepathyClient.js:939 ../js/ui/telepathyClient.js:1028
msgid "Decline"
msgstr "דחייה"
#: ../js/ui/telepathyClient.js:909
#: ../js/ui/telepathyClient.js:940 ../js/ui/telepathyClient.js:1029
msgid "Accept"
msgstr "אישור"
#. translators: argument is a contact name like Alice for example.
#: ../js/ui/telepathyClient.js:942
#: ../js/ui/telepathyClient.js:973
#, c-format
msgid "Video call from %s"
msgstr "שיחת וידאו מאת %s"
#. translators: argument is a contact name like Alice for example.
#: ../js/ui/telepathyClient.js:945
#: ../js/ui/telepathyClient.js:976
#, c-format
msgid "Call from %s"
msgstr "שיחה מאת %s"
#: ../js/ui/telepathyClient.js:955
#: ../js/ui/telepathyClient.js:986
msgid "Answer"
msgstr "מענה"
#. To translators: The first parameter is
#. * the contact's alias and the second one is the
#. * file name. The string will be something
#. * like: "Alice is sending you test.ogg"
#.
#: ../js/ui/telepathyClient.js:1022
#, c-format
msgid "%s is sending you %s"
msgstr "%s שולח/ת אליך %s"
#. Translators: this is the text displayed
#. in the search entry when no search is
#. active; it should not exceed ~30
@ -1196,16 +1239,16 @@ msgstr "מענה"
msgid "Type to search..."
msgstr "יש להקליד כדי לחפש..."
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:257
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:261
msgid "Search"
msgstr "חיפוש"
#: ../js/ui/windowAttentionHandler.js:40
#: ../js/ui/windowAttentionHandler.js:39
#, c-format
msgid "%s has finished starting"
msgstr "%s סיים את תהליך ההתחלה"
#: ../js/ui/windowAttentionHandler.js:42
#: ../js/ui/windowAttentionHandler.js:41
#, c-format
msgid "'%s' is ready"
msgstr "'%s' מוכן"
@ -1238,7 +1281,7 @@ msgstr "צלילי מערכת"
msgid "Print version"
msgstr "Print version"
#: ../src/shell-app.c:464
#: ../src/shell-app.c:566
#, c-format
msgid "Failed to launch '%s'"
msgstr "אירע כשל בטעינת '%s'"
@ -1255,13 +1298,13 @@ msgstr "בררת מחדל"
msgid "Authentication dialog was dismissed by the user"
msgstr "המשתמש בחר להתעלם מתיבת דו־שיח האימות"
#: ../src/shell-util.c:96
#: ../src/shell-util.c:100
msgid "Home Folder"
msgstr "תיקיית הבית"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-util.c:111
#: ../src/shell-util.c:115
msgid "File System"
msgstr "מערכת הקבצים"
@ -1270,11 +1313,18 @@ msgstr "מערכת הקבצים"
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-util.c:307
#: ../src/shell-util.c:311
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"
#: ../src/shell-util.c:600
msgid "calendar:week_start:0"
msgstr "calendar:week_start:0"
#~ msgid "Localization Settings"
#~ msgstr "הגדרות אזוריות"
#~ msgid "Less than a minute ago"
#~ msgstr "לפני פחות מדקה"

844
po/id.po

File diff suppressed because it is too large Load Diff

870
po/lt.po

File diff suppressed because it is too large Load Diff

1348
po/ms.po Normal file

File diff suppressed because it is too large Load Diff

385
po/nb.po
View File

@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gnome-shell 3.1.x\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-07-18 13:48+0200\n"
"PO-Revision-Date: 2011-07-18 13:50+0200\n"
"POT-Creation-Date: 2011-08-24 22:16+0200\n"
"PO-Revision-Date: 2011-08-24 22:17+0200\n"
"Last-Translator: Kjartan Maraas <kmaraas@gnome.org>\n"
"Language-Team: Norwegian Bokmål <i18n-nb@lister.ping.uio.no>\n"
"Language: \n"
@ -57,40 +57,30 @@ msgstr ""
"innstillingen for utvidelser som finnes i begge listene."
#: ../data/org.gnome.shell.gschema.xml.in.h:6
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. 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"
msgstr "Historikk for kommandodialog (Alt-F2)"
#: ../data/org.gnome.shell.gschema.xml.in.h:8
#: ../data/org.gnome.shell.gschema.xml.in.h:7
msgid "History for the looking glass dialog"
msgstr "Historikk for forstørrelsesglass-dialogen"
#: ../data/org.gnome.shell.gschema.xml.in.h:9
#: ../data/org.gnome.shell.gschema.xml.in.h:8
msgid "If true, display date in the clock, in addition to time."
msgstr "Viser dato i tillegg til tid i klokken hvis «true»."
#: ../data/org.gnome.shell.gschema.xml.in.h:10
#: ../data/org.gnome.shell.gschema.xml.in.h:9
msgid "If true, display seconds in time."
msgstr "Viser sekunder i klokken hvis «true»."
#: ../data/org.gnome.shell.gschema.xml.in.h:11
#: ../data/org.gnome.shell.gschema.xml.in.h:10
msgid "If true, display the ISO week date in the calendar."
msgstr "Viser ISO-ukedato i kalenderen hvis «true»."
#: ../data/org.gnome.shell.gschema.xml.in.h:12
#: ../data/org.gnome.shell.gschema.xml.in.h:11
msgid "List of desktop file IDs for favorite applications"
msgstr "Liste av skrivebordfil-ider for favorittprogrammer"
#: ../data/org.gnome.shell.gschema.xml.in.h:14
#: ../data/org.gnome.shell.gschema.xml.in.h:13
#, fuzzy, no-c-format
msgid ""
"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
@ -103,21 +93,23 @@ 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
#: ../data/org.gnome.shell.gschema.xml.in.h:14
msgid "Show date in clock"
msgstr "Vis dato i klokken"
#: ../data/org.gnome.shell.gschema.xml.in.h:16
#: ../data/org.gnome.shell.gschema.xml.in.h:15
msgid "Show the week date in the calendar"
msgstr "Vis dato for uken i kalender"
#: ../data/org.gnome.shell.gschema.xml.in.h:17
#: ../data/org.gnome.shell.gschema.xml.in.h:16
msgid "Show time with seconds"
msgstr "Vis tid med sekunder"
#: ../data/org.gnome.shell.gschema.xml.in.h:18
#: ../data/org.gnome.shell.gschema.xml.in.h:17
msgid ""
"The applications corresponding to these identifiers will be displayed in the "
"favorites area."
@ -125,7 +117,7 @@ msgstr ""
"Programmene som passer til disse identifikatorene vil bli vist i "
"favorittområdet."
#: ../data/org.gnome.shell.gschema.xml.in.h:19
#: ../data/org.gnome.shell.gschema.xml.in.h:18
msgid ""
"The filename for recorded screencasts will be a unique filename based on the "
"current date, and use this extension. It should be changed when recording to "
@ -135,7 +127,7 @@ msgstr ""
"og bruke denne filendelsen. Den bør endres når du gjør opptak til et nytt "
"oppbevaringsformat."
#: ../data/org.gnome.shell.gschema.xml.in.h:20
#: ../data/org.gnome.shell.gschema.xml.in.h:19
msgid ""
"The framerate of the resulting screencast recordered by GNOME Shell's "
"screencast recorder in frames-per-second."
@ -143,11 +135,11 @@ msgstr ""
"Bildefrekvensen i den ferdige skjermvideoen tatt opp med GNOME Shells "
"skjermvideoopptaker i bilder per sekund."
#: ../data/org.gnome.shell.gschema.xml.in.h:21
#: ../data/org.gnome.shell.gschema.xml.in.h:20
msgid "The gstreamer pipeline used to encode the screencast"
msgstr "Gstreamer-kommandokø brukt til å kode skjermvideoen"
#: ../data/org.gnome.shell.gschema.xml.in.h:22
#: ../data/org.gnome.shell.gschema.xml.in.h:21
msgid ""
"The shell normally monitors active applications in order to present the most "
"used ones (e.g. in launchers). While this data will be kept private, you may "
@ -159,19 +151,15 @@ msgstr ""
"holdt privat, men du kan deaktivere denne lagringen av personvernårsaker. "
"Hvis du slår det av, vil det ikke fjerne allerede lagret informasjon."
#: ../data/org.gnome.shell.gschema.xml.in.h:23
msgid "Uuids of extensions to disable"
msgstr "Uuider på utvidelser som skal slås av"
#: ../data/org.gnome.shell.gschema.xml.in.h:24
#: ../data/org.gnome.shell.gschema.xml.in.h:22
msgid "Uuids of extensions to enable"
msgstr "Uuider på utvidelser som skal slås på"
#: ../data/org.gnome.shell.gschema.xml.in.h:25
#: ../data/org.gnome.shell.gschema.xml.in.h:23
msgid "Whether to collect stats about applications usage"
msgstr "Om det skal samles statistikk om bruk av programmer"
#: ../data/org.gnome.shell.gschema.xml.in.h:26
#: ../data/org.gnome.shell.gschema.xml.in.h:24
msgid "disabled OpenSearch providers"
msgstr "OpenSearch tilbydere slått av"
@ -191,27 +179,27 @@ msgid "Execution of '%s' failed:"
msgstr "Kjøring av «%s» feilet:"
#. Translators: Filter to display all applications
#: ../js/ui/appDisplay.js:252
#: ../js/ui/appDisplay.js:253
msgid "All"
msgstr "Alle"
#: ../js/ui/appDisplay.js:359
#: ../js/ui/appDisplay.js:315
msgid "APPLICATIONS"
msgstr "PROGRAMMER"
#: ../js/ui/appDisplay.js:385
#: ../js/ui/appDisplay.js:373
msgid "SETTINGS"
msgstr "INNSTILLINGER"
#: ../js/ui/appDisplay.js:658
#: ../js/ui/appDisplay.js:681
msgid "New Window"
msgstr "Nytt vindu"
#: ../js/ui/appDisplay.js:661
#: ../js/ui/appDisplay.js:684
msgid "Remove from Favorites"
msgstr "Fjern fra favoritter"
#: ../js/ui/appDisplay.js:662
#: ../js/ui/appDisplay.js:685
msgid "Add to Favorites"
msgstr "Legg til i favoritter"
@ -225,6 +213,15 @@ msgstr "%s ble lagt til i dine favoritter."
msgid "%s has been removed from your favorites."
msgstr "%s ble fjernet fra dine favoritter."
#: ../js/ui/autorunManager.js:591
#, c-format
msgid "Open with %s"
msgstr "Åpne med %s"
#: ../js/ui/autorunManager.js:617
msgid "Eject"
msgstr "Løs ut"
#. Translators: Shown in calendar event list for all day events
#. * Keep it short, best if you can use less then 10 characters
#.
@ -339,94 +336,94 @@ msgid "S"
msgstr "Lø"
#. Translators: Text to show if there are no events
#: ../js/ui/calendar.js:701
#: ../js/ui/calendar.js:678
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:749
#: ../js/ui/calendar.js:694
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:752
#: ../js/ui/calendar.js:697
msgctxt "calendar heading"
msgid "%A, %B %d, %Y"
msgstr "%A %B %d, %Y"
#: ../js/ui/calendar.js:730
#: ../js/ui/calendar.js:707
msgid "Today"
msgstr "I dag"
#: ../js/ui/calendar.js:734
#: ../js/ui/calendar.js:711
msgid "Tomorrow"
msgstr "I morgen"
#: ../js/ui/calendar.js:743
#: ../js/ui/calendar.js:720
msgid "This week"
msgstr "Denne uken"
#: ../js/ui/calendar.js:751
#: ../js/ui/calendar.js:728
msgid "Next week"
msgstr "Neste uke"
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1122
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1123
msgid "Remove"
msgstr "Fjern"
#: ../js/ui/dateMenu.js:89
#: ../js/ui/dateMenu.js:90
msgid "Date and Time Settings"
msgstr "Innstillinger for dato og klokkeslett"
#: ../js/ui/dateMenu.js:109
#: ../js/ui/dateMenu.js:110
msgid "Open Calendar"
msgstr "Åpne kalender"
#. Translators: This is the time format with date used
#. in 24-hour mode.
#: ../js/ui/dateMenu.js:162
#: ../js/ui/dateMenu.js:167
msgid "%a %b %e, %R:%S"
msgstr "%a %e %b, %R.%S"
#: ../js/ui/dateMenu.js:163
#: ../js/ui/dateMenu.js:168
msgid "%a %b %e, %R"
msgstr "%a %e %b, %R"
#. Translators: This is the time format without date used
#. in 24-hour mode.
#: ../js/ui/dateMenu.js:167
#: ../js/ui/dateMenu.js:172
msgid "%a %R:%S"
msgstr "%a %R.%S"
#: ../js/ui/dateMenu.js:168
#: ../js/ui/dateMenu.js:173
msgid "%a %R"
msgstr "%a %R"
#. Translators: This is a time format with date used
#. for AM/PM.
#: ../js/ui/dateMenu.js:175
#: ../js/ui/dateMenu.js:180
msgid "%a %b %e, %l:%M:%S %p"
msgstr "%a %e %b, %l.%M.%S %p"
#: ../js/ui/dateMenu.js:176
#: ../js/ui/dateMenu.js:181
msgid "%a %b %e, %l:%M %p"
msgstr "%a %e %b, %l.%M %p"
#. Translators: This is a time format without date used
#. for AM/PM.
#: ../js/ui/dateMenu.js:180
#: ../js/ui/dateMenu.js:185
msgid "%a %l:%M:%S %p"
msgstr "%a %l.%M.%S %p"
#: ../js/ui/dateMenu.js:181
#: ../js/ui/dateMenu.js:186
msgid "%a %l:%M %p"
msgstr "%a %l.%M %p"
#. Translators: This is the date format to use when the calendar popup is
#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
#.
#: ../js/ui/dateMenu.js:192
#: ../js/ui/dateMenu.js:197
msgid "%A %B %e, %Y"
msgstr "%a %e %B, %Y"
@ -505,44 +502,53 @@ msgstr "Starter systemet på nytt."
msgid "Cancel"
msgstr "Avbryt"
#: ../js/ui/lookingGlass.js:641
#: ../js/ui/lookingGlass.js:640
msgid "No extensions installed"
msgstr "Ingen utvidelser installert"
#: ../js/ui/lookingGlass.js:678
#: ../js/ui/lookingGlass.js:686
msgid "Enabled"
msgstr "Aktivert"
#. translators:
#. * The device has been disabled
#: ../js/ui/lookingGlass.js:680 ../src/gvc/gvc-mixer-control.c:1091
#: ../js/ui/lookingGlass.js:688 ../src/gvc/gvc-mixer-control.c:1091
msgid "Disabled"
msgstr "Deaktivert"
#: ../js/ui/lookingGlass.js:682
#: ../js/ui/lookingGlass.js:690
msgid "Error"
msgstr "Feil"
#: ../js/ui/lookingGlass.js:684
#: ../js/ui/lookingGlass.js:692
msgid "Out of date"
msgstr "Utdatert"
#: ../js/ui/lookingGlass.js:709
#: ../js/ui/lookingGlass.js:694
msgid "Downloading"
msgstr "Laster ned"
#: ../js/ui/lookingGlass.js:719
msgid "View Source"
msgstr "Vis kildekode"
#: ../js/ui/lookingGlass.js:715
#: ../js/ui/lookingGlass.js:725
msgid "Web Page"
msgstr "Nettside"
#: ../js/ui/messageTray.js:1115
#: ../js/ui/messageTray.js:1116
msgid "Open"
msgstr "Åpne"
#: ../js/ui/messageTray.js:2286
#: ../js/ui/messageTray.js:2277
msgid "System Information"
msgstr "Systeminformasjon"
#: ../js/ui/notificationDaemon.js:427 ../js/ui/status/power.js:238
#: ../src/shell-app.c:354
msgid "Unknown"
msgstr "Ukjent"
#: ../js/ui/overview.js:89
msgid "Undo"
msgstr "Angre"
@ -562,18 +568,18 @@ msgid "Dash"
msgstr "Favoritter"
#. TODO - _quit() doesn't really work on apps in state STARTING yet
#: ../js/ui/panel.js:532
#: ../js/ui/panel.js:531
#, c-format
msgid "Quit %s"
msgstr "Avslutt %s"
#. Translators: If there is no suitable word for "Activities"
#. in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:568
#: ../js/ui/panel.js:567
msgid "Activities"
msgstr "Aktiviteter"
#: ../js/ui/panel.js:951
#: ../js/ui/panel.js:876
msgid "Top Bar"
msgstr "Topp-panel"
@ -639,43 +645,9 @@ msgstr "Søker …"
msgid "No matching results."
msgstr "Ingen treff."
#: ../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:194 ../js/ui/statusMenu.js:196
#: ../js/ui/statusMenu.js:261
msgid "Suspend"
msgstr "Hvilemodus"
#: ../js/ui/statusMenu.js:217
msgid "Available"
msgstr "Tilgjengelig"
#: ../js/ui/statusMenu.js:222
msgid "Busy"
msgstr "Opptatt"
#: ../js/ui/statusMenu.js:230
msgid "My Account"
msgstr "Min konto"
#: ../js/ui/statusMenu.js:234
msgid "System Settings"
msgstr "Systeminnstillinger"
#: ../js/ui/statusMenu.js:242
msgid "Lock Screen"
msgstr "Lås skjerm"
#: ../js/ui/statusMenu.js:247
msgid "Switch User"
msgstr "Bytt bruker"
#: ../js/ui/statusMenu.js:252
msgid "Log Out..."
msgstr "Logg ut …"
#: ../js/ui/shellMountOperation.js:285
msgid "Wrong password, please try again"
msgstr "Feil passord. Prøv igjen"
#: ../js/ui/status/accessibility.js:60
msgid "Zoom"
@ -750,11 +722,11 @@ msgstr "maskinvare slått av"
msgid "Connection"
msgstr "Tilkobling"
#: ../js/ui/status/bluetooth.js:226 ../js/ui/status/network.js:493
#: ../js/ui/status/bluetooth.js:226 ../js/ui/status/network.js:497
msgid "disconnecting..."
msgstr "kobler fra …"
#: ../js/ui/status/bluetooth.js:239 ../js/ui/status/network.js:499
#: ../js/ui/status/bluetooth.js:239 ../js/ui/status/network.js:503
msgid "connecting..."
msgstr "kobler til …"
@ -805,7 +777,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/telepathyClient.js:954
#: ../js/ui/status/bluetooth.js:409 ../js/ui/telepathyClient.js:1058
msgid "Reject"
msgstr "Avvis"
@ -846,148 +818,186 @@ msgid "OK"
msgstr "OK"
#: ../js/ui/status/keyboard.js:71
msgid "Show Keyboard Layout..."
msgstr "Vis tastaturutforming"
msgid "Show Keyboard Layout"
msgstr "Vis tastaturutforming"
#: ../js/ui/status/keyboard.js:75
msgid "Localization Settings"
msgstr "Innstillinger for lokalisering"
msgid "Region and Language Settings"
msgstr "Innstillinger for region og språk"
#: ../js/ui/status/network.js:123
#: ../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:194 ../js/ui/statusMenu.js:196
#: ../js/ui/statusMenu.js:261
msgid "Suspend"
msgstr "Hvilemodus"
#: ../js/ui/statusMenu.js:217
msgid "Available"
msgstr "Tilgjengelig"
#: ../js/ui/statusMenu.js:222
msgid "Busy"
msgstr "Opptatt"
#: ../js/ui/statusMenu.js:230
msgid "My Account"
msgstr "Min konto"
#: ../js/ui/statusMenu.js:234
msgid "System Settings"
msgstr "Systeminnstillinger"
#: ../js/ui/statusMenu.js:242
msgid "Lock Screen"
msgstr "Lås skjerm"
#: ../js/ui/statusMenu.js:247
msgid "Switch User"
msgstr "Bytt bruker"
#: ../js/ui/statusMenu.js:252
msgid "Log Out..."
msgstr "Logg ut …"
#: ../js/ui/status/network.js:108
msgid "<unknown>"
msgstr "<ukjent>"
#. Translators: this indicates that wireless or wwan is disabled by hardware killswitch
#: ../js/ui/status/network.js:292
#: ../js/ui/status/network.js:296
msgid "disabled"
msgstr "slått av"
#. 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:491
#: ../js/ui/status/network.js:495
msgid "unmanaged"
msgstr "ikke håndtert"
#. Translators: this is for network connections that require some kind of key or password
#: ../js/ui/status/network.js:502
#: ../js/ui/status/network.js:506
msgid "authentication required"
msgstr "autentisering kreves"
#. Translators: this is for devices that require some kind of firmware or kernel
#. module, which is missing
#: ../js/ui/status/network.js:512
#: ../js/ui/status/network.js:516
msgid "firmware missing"
msgstr "fastvare mangler"
#. Translators: this is for wired network devices that are physically disconnected
#: ../js/ui/status/network.js:519
#: ../js/ui/status/network.js:523
msgid "cable unplugged"
msgstr "kabel koblet fra"
#. 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:524
#: ../js/ui/status/network.js:528
msgid "unavailable"
msgstr "ikke tilgjengelig"
#: ../js/ui/status/network.js:526
#: ../js/ui/status/network.js:530
msgid "connection failed"
msgstr "tilkobling feilet"
#: ../js/ui/status/network.js:582 ../js/ui/status/network.js:1507
#: ../js/ui/status/network.js:586 ../js/ui/status/network.js:1546
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:1444
#: ../js/ui/status/network.js:622 ../js/ui/status/network.js:1483
msgid "Connected (private)"
msgstr "Tilkoblet (privat)"
#: ../js/ui/status/network.js:703
#: ../js/ui/status/network.js:707
msgid "Auto Ethernet"
msgstr "Automatisk Ethernet"
#: ../js/ui/status/network.js:771
#: ../js/ui/status/network.js:775
msgid "Auto broadband"
msgstr "Automatisk bredbånd"
#: ../js/ui/status/network.js:774
#: ../js/ui/status/network.js:778
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:1456
#: ../js/ui/status/network.js:902 ../js/ui/status/network.js:1495
#, c-format
msgid "Auto %s"
msgstr "Automatisk %s"
#: ../js/ui/status/network.js:900
#: ../js/ui/status/network.js:904
msgid "Auto bluetooth"
msgstr "Automatisk Bluetooth"
#: ../js/ui/status/network.js:1458
#: ../js/ui/status/network.js:1497
msgid "Auto wireless"
msgstr "Automatisk trådløst"
#: ../js/ui/status/network.js:1550
#: ../js/ui/status/network.js:1589
msgid "Enable networking"
msgstr "Slå på nettverk"
#: ../js/ui/status/network.js:1562
#: ../js/ui/status/network.js:1601
msgid "Wired"
msgstr "Kablet"
#: ../js/ui/status/network.js:1573
#: ../js/ui/status/network.js:1612
msgid "Wireless"
msgstr "Trådløst"
#: ../js/ui/status/network.js:1583
#: ../js/ui/status/network.js:1622
msgid "Mobile broadband"
msgstr "Mobilt bredbånd"
#: ../js/ui/status/network.js:1593
#: ../js/ui/status/network.js:1632
msgid "VPN Connections"
msgstr "VPN-tilkoblinger"
#: ../js/ui/status/network.js:1605
#: ../js/ui/status/network.js:1644
msgid "Network Settings"
msgstr "Innstillinger for nettverk"
#: ../js/ui/status/network.js:1897
#: ../js/ui/status/network.js:1936
#, 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:1901
#: ../js/ui/status/network.js:1940
#, 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:1905
#: ../js/ui/status/network.js:1944
#, 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:1909
#: ../js/ui/status/network.js:1948
#, 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:1914
#: ../js/ui/status/network.js:1953
#, c-format
msgid "You're now connected to '%s'"
msgstr "Du er nå koblet til «%s»"
#: ../js/ui/status/network.js:1922
#: ../js/ui/status/network.js:1961
msgid "Connection established"
msgstr "Tilkobling etablert"
#: ../js/ui/status/network.js:2048
#: ../js/ui/status/network.js:2087
msgid "Networking is disabled"
msgstr "Nettverk er slått av"
#: ../js/ui/status/network.js:2173
#: ../js/ui/status/network.js:2212
msgid "Network Manager"
msgstr "Nettverkshåndtering"
@ -1077,10 +1087,6 @@ msgstr "Nettbrett"
msgid "Computer"
msgstr "Datamaskin"
#: ../js/ui/status/power.js:238 ../src/shell-app-system.c:1088
msgid "Unknown"
msgstr "Ukjent"
#: ../js/ui/status/volume.js:43
msgid "Volume"
msgstr "Volum"
@ -1092,31 +1098,36 @@ msgstr "Mikrofon"
#. 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
#: ../js/ui/telepathyClient.js:235
msgid "Invitation"
msgstr "Invitasjon"
#. We got the TpContact
#: ../js/ui/telepathyClient.js:291
#: ../js/ui/telepathyClient.js:301
msgid "Call"
msgstr "Ring"
#: ../js/ui/telepathyClient.js:541
#. We got the TpContact
#: ../js/ui/telepathyClient.js:329
msgid "File Transfer"
msgstr "Filoverføring"
#: ../js/ui/telepathyClient.js:595
#, c-format
msgid "%s is online."
msgstr "%s er tilkoblet."
#: ../js/ui/telepathyClient.js:546
#: ../js/ui/telepathyClient.js:600
#, c-format
msgid "%s is offline."
msgstr "%s er frakoblet."
#: ../js/ui/telepathyClient.js:549
#: ../js/ui/telepathyClient.js:603
#, c-format
msgid "%s is away."
msgstr "«%s» er borte."
#: ../js/ui/telepathyClient.js:552
#: ../js/ui/telepathyClient.js:606
#, c-format
msgid "%s is busy."
msgstr "%s er opptatt."
@ -1124,21 +1135,35 @@ 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:741
#: ../js/ui/telepathyClient.js:840
#, no-c-format
msgid "Sent at %X on %A"
msgstr "Sendt %X på %A"
msgid "Sent at <b>%X</b> on <b>%A</b>"
msgstr "Sendt <b>%X</b> på <b>%A</b>"
#. Translators: this is a time format in the style of "Wednesday, May 25",
#. shown when you get a chat message in the same year.
#: ../js/ui/telepathyClient.js:846
#, no-c-format
msgid "Sent on <b>%A</b>, <b>%B %d</b>"
msgstr "Sendt <b>%A</b>, <b>%B %d</b>"
#. Translators: this is a time format in the style of "Wednesday, May 25, 2012",
#. shown when you get a chat message in a different year.
#: ../js/ui/telepathyClient.js:851
#, no-c-format
msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
msgstr "Sendt <b>%A</b>, <b>%B %d</b>, %Y"
#. Translators: this is the other person changing their old IM name to their new
#. IM name.
#: ../js/ui/telepathyClient.js:791
#: ../js/ui/telepathyClient.js:893
#, 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
#: ../js/ui/telepathyClient.js:1002
#, c-format
msgid "Invitation to %s"
msgstr "Invitasjon til %s"
@ -1146,35 +1171,45 @@ 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
#: ../js/ui/telepathyClient.js:1010
#, c-format
msgid "%s is inviting you to join %s"
msgstr "%s inviterer deg til å bli med i %s"
#: ../js/ui/telepathyClient.js:908
#: ../js/ui/telepathyClient.js:1012 ../js/ui/telepathyClient.js:1101
msgid "Decline"
msgstr "Avslå"
#: ../js/ui/telepathyClient.js:909
#: ../js/ui/telepathyClient.js:1013 ../js/ui/telepathyClient.js:1102
msgid "Accept"
msgstr "Godta"
#. translators: argument is a contact name like Alice for example.
#: ../js/ui/telepathyClient.js:942
#: ../js/ui/telepathyClient.js:1046
#, 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
#: ../js/ui/telepathyClient.js:1049
#, c-format
msgid "Call from %s"
msgstr "Samtale fra %s"
#: ../js/ui/telepathyClient.js:955
#: ../js/ui/telepathyClient.js:1059
msgid "Answer"
msgstr "Svar"
#. To translators: The first parameter is
#. * the contact's alias and the second one is the
#. * file name. The string will be something
#. * like: "Alice is sending you test.ogg"
#.
#: ../js/ui/telepathyClient.js:1095
#, c-format
msgid "%s is sending you %s"
msgstr "%s sender deg %s"
#. Translators: this is the text displayed
#. in the search entry when no search is
#. active; it should not exceed ~30
@ -1183,16 +1218,16 @@ msgstr "Svar"
msgid "Type to search..."
msgstr "Skriv for å søke …"
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:257
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:261
msgid "Search"
msgstr "Søk"
#: ../js/ui/windowAttentionHandler.js:40
#: ../js/ui/windowAttentionHandler.js:39
#, c-format
msgid "%s has finished starting"
msgstr "%s er ferdig startet"
#: ../js/ui/windowAttentionHandler.js:42
#: ../js/ui/windowAttentionHandler.js:41
#, c-format
msgid "'%s' is ready"
msgstr "«%s» er klar"
@ -1223,7 +1258,7 @@ msgstr "Systemlyder"
msgid "Print version"
msgstr "Skriv ut versjon"
#: ../src/shell-app.c:464
#: ../src/shell-app.c:580
#, c-format
msgid "Failed to launch '%s'"
msgstr "Klarte ikke å starte «%s»"
@ -1240,13 +1275,13 @@ msgstr "Forvalg"
msgid "Authentication dialog was dismissed by the user"
msgstr "Autentiseringsdialogen ble lukket av brukeren"
#: ../src/shell-util.c:96
#: ../src/shell-util.c:100
msgid "Home Folder"
msgstr "Hjemmemappe"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-util.c:111
#: ../src/shell-util.c:115
msgid "File System"
msgstr "Filsystem"
@ -1255,7 +1290,11 @@ msgstr "Filsystem"
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-util.c:307
#: ../src/shell-util.c:311
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"
#: ../src/shell-util.c:600
msgid "calendar:week_start:0"
msgstr "calendar:week_start:1"

231
po/pa.po
View File

@ -7,8 +7,8 @@ msgstr ""
"Project-Id-Version: gnome-shell master\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&keywords=I18N+L10N&component=general\n"
"POT-Creation-Date: 2011-07-15 16:19+0000\n"
"PO-Revision-Date: 2011-07-18 08:29+0530\n"
"POT-Creation-Date: 2011-08-09 23:03+0000\n"
"PO-Revision-Date: 2011-08-18 06:57+0530\n"
"Last-Translator: A S Alam <aalam@users.sf.net>\n"
"Language-Team: Punjabi/Panjabi <punjabi-users@lists.sf.net>\n"
"MIME-Version: 1.0\n"
@ -47,9 +47,6 @@ msgid "Framerate used for recording screencasts."
msgstr "ਸਕਰੀਨਕਾਸਟ ਰਿਕਾਰਡ ਕਰਨ ਲਈ ਫਰੇਮਰੇਟ ਹੈ।"
#: ../data/org.gnome.shell.gschema.xml.in.h:5
#| msgid ""
#| "GNOME Shell extensions have a uuid property; this key lists extensions "
#| "which should not be loaded."
msgid ""
"GNOME Shell extensions have a uuid property; this key lists extensions which "
"should be loaded. disabled-extensions overrides this setting for extensions "
@ -58,7 +55,8 @@ msgstr ""
"ਗਨੋਮ ਸ਼ੈਲ ਇਕਸਟੈਨਸ਼ਨ ਲਈ ਇੱਕ uuid ਵਿਸ਼ੇਸ਼ਤਾ ਹੈ; ਇਹ ਕੁੰਜੀ ਇਕਸਟੈਨਸ਼ਨ ਦਰਸਾਉਂਦੀ ਹੈ, "
"ਜੋ ਲੋਡ ਹਨ। "
"disabled-extensions ਇਹ ਸੈਟਿੰਗ ਉਹਨਾਂ ਇਕਸਟੈਨਸ਼ਨਾਂ ਨੂੰ ਅਣਡਿੱਠਾ ਕਰਦੀ ਹੈ, ਜੋ ਦੋਵੇਂ "
"ਲਿਸਟਾਂ ਵਿੱਚ ਹੁੰਦੀਆਂ ਹਨ।"
"ਲਿਸਟਾਂ ਵਿੱਚ "
"ਹੁੰਦੀਆਂ ਹਨ।"
#: ../data/org.gnome.shell.gschema.xml.in.h:6
msgid ""
@ -190,7 +188,6 @@ msgid "Uuids of extensions to disable"
msgstr "ਇਕਟੈਨਸ਼ਨ ਦੀ Uuids ਬੰਦ ਹੈ"
#: ../data/org.gnome.shell.gschema.xml.in.h:24
#| msgid "Uuids of extensions to disable"
msgid "Uuids of extensions to enable"
msgstr "ਚਾਲੂ ਕਰਨ ਲਈ ਇਕਟੈਨਸ਼ਨ ਦੀ Uuid"
@ -222,23 +219,23 @@ msgstr "'%s' ਚਲਾਉਣ ਲਈ ਫੇਲ੍ਹ:"
msgid "All"
msgstr "ਸਭ"
#: ../js/ui/appDisplay.js:351
#: ../js/ui/appDisplay.js:359
msgid "APPLICATIONS"
msgstr "ਐਪਲੀਕੇਸ਼ਨ"
#: ../js/ui/appDisplay.js:377
#: ../js/ui/appDisplay.js:385
msgid "SETTINGS"
msgstr "ਸੈਟਿੰਗ"
#: ../js/ui/appDisplay.js:650
#: ../js/ui/appDisplay.js:658
msgid "New Window"
msgstr "ਨਵੀਂ ਵਿੰਡੋ"
#: ../js/ui/appDisplay.js:653
#: ../js/ui/appDisplay.js:661
msgid "Remove from Favorites"
msgstr "ਪਸੰਦ ਵਿੱਚੋਂ ਹਟਾਓ"
#: ../js/ui/appDisplay.js:654
#: ../js/ui/appDisplay.js:662
msgid "Add to Favorites"
msgstr "ਪਸੰਦ 'ਚ ਸ਼ਾਮਲ ਕਰੋ"
@ -252,6 +249,16 @@ msgstr "%s ਨੂੰ ਤੁਹਾਡੀ ਪਸੰਦ ਵਿੱਚ ਸ਼ਾਮ
msgid "%s has been removed from your favorites."
msgstr "%s ਨੂੰ ਤੁਹਾਡੀ ਪਸੰਦ ਤੋਂ ਹਟਾਇਆ ਜਾ ਚੁੱਕਿਆ ਹੈ।"
#: ../js/ui/autorunManager.js:591
#, c-format
msgid "Open with %s"
msgstr "%s ਨਾਲ ਖੋਲ੍ਹੋ"
#: ../js/ui/autorunManager.js:617
#| msgid "Reject"
msgid "Eject"
msgstr "ਬਾਹਰ ਕੱਢੋ"
#. Translators: Shown in calendar event list for all day events
#. * Keep it short, best if you can use less then 10 characters
#.
@ -366,39 +373,39 @@ msgid "S"
msgstr "ਸ਼"
#. Translators: Text to show if there are no events
#: ../js/ui/calendar.js:701
#: ../js/ui/calendar.js:678
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:749
#: ../js/ui/calendar.js:694
msgctxt "calendar heading"
msgid "%A, %B %d"
msgstr "%A, %d %B"
#. Translators: Shown on calendar heading when selected day occurs on different year
#: ../js/ui/calendar.js:720 ../js/ui/telepathyClient.js:752
#: ../js/ui/calendar.js:697
msgctxt "calendar heading"
msgid "%A, %B %d, %Y"
msgstr "%A, %d %B %Y"
#: ../js/ui/calendar.js:730
#: ../js/ui/calendar.js:707
msgid "Today"
msgstr "ਅੱਜ"
#: ../js/ui/calendar.js:734
#: ../js/ui/calendar.js:711
msgid "Tomorrow"
msgstr "ਭਲਕ"
#: ../js/ui/calendar.js:743
#: ../js/ui/calendar.js:720
msgid "This week"
msgstr "ਇਹ ਹਫ਼ਤਾ"
#: ../js/ui/calendar.js:751
#: ../js/ui/calendar.js:728
msgid "Next week"
msgstr "ਹਫ਼ਤਾ ਅੱਗੇ"
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1122
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1123
msgid "Remove"
msgstr "ਹਟਾਓ"
@ -530,44 +537,49 @@ msgstr "ਸਿਸਟਮ ਮੁੜ-ਚਾਲੂ ਕੀਤਾ ਜਾ ਰਿਹਾ
msgid "Cancel"
msgstr "ਰੱਦ ਕਰੋ"
#: ../js/ui/lookingGlass.js:641
#: ../js/ui/lookingGlass.js:640
msgid "No extensions installed"
msgstr "ਕੋਈ ਇਕਸਟੈਨਸ਼ਨ ਇੰਸਟਾਲ ਨਹੀਂ ਹੈ"
#: ../js/ui/lookingGlass.js:678
#: ../js/ui/lookingGlass.js:686
msgid "Enabled"
msgstr "ਚਾਲੂ ਹੈ"
#. translators:
#. * The device has been disabled
#: ../js/ui/lookingGlass.js:680 ../src/gvc/gvc-mixer-control.c:1091
#: ../js/ui/lookingGlass.js:688 ../src/gvc/gvc-mixer-control.c:1091
msgid "Disabled"
msgstr "ਬੰਦ ਹੈ"
#: ../js/ui/lookingGlass.js:682
#: ../js/ui/lookingGlass.js:690
msgid "Error"
msgstr "ਗਲਤੀ"
#: ../js/ui/lookingGlass.js:684
#: ../js/ui/lookingGlass.js:692
msgid "Out of date"
msgstr "ਪੁਰਾਣਾ"
#: ../js/ui/lookingGlass.js:709
#: ../js/ui/lookingGlass.js:717
msgid "View Source"
msgstr "ਸਰੋਤ ਵੇਖੋ"
#: ../js/ui/lookingGlass.js:715
#: ../js/ui/lookingGlass.js:723
msgid "Web Page"
msgstr "ਵੈੱਬ ਪੇਜ਼"
#: ../js/ui/messageTray.js:1115
#: ../js/ui/messageTray.js:1116
msgid "Open"
msgstr "ਖੋਲ੍ਹੋ"
#: ../js/ui/messageTray.js:2286
#: ../js/ui/messageTray.js:2277
msgid "System Information"
msgstr "ਸਿਸਟਮ ਜਾਣਕਾਰੀ"
#: ../js/ui/notificationDaemon.js:426 ../js/ui/status/power.js:238
#: ../src/shell-app-system.c:1115
msgid "Unknown"
msgstr "ਅਣਜਾਣ"
#: ../js/ui/overview.js:89
msgid "Undo"
msgstr "ਵਾਪਸ"
@ -587,18 +599,18 @@ msgid "Dash"
msgstr "ਡੈਸ਼"
#. TODO - _quit() doesn't really work on apps in state STARTING yet
#: ../js/ui/panel.js:532
#: ../js/ui/panel.js:531
#, c-format
msgid "Quit %s"
msgstr "%s ਬੰਦ ਕਰੋ"
#. Translators: If there is no suitable word for "Activities"
#. in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:568
#: ../js/ui/panel.js:567
msgid "Activities"
msgstr "ਸਰਗਰਮੀਆਂ"
#: ../js/ui/panel.js:951
#: ../js/ui/panel.js:876
msgid "Top Bar"
msgstr "ਉੱਤਲੀ ਪੱਟੀ"
@ -664,43 +676,9 @@ msgstr "ਖੋਜ ਜਾਰੀ ਹੈ..."
msgid "No matching results."
msgstr "ਕੋਈ ਨਤੀਜਾ ਨਹੀਂ ਲੱਭਿਆ।"
#: ../js/ui/statusMenu.js:192 ../js/ui/statusMenu.js:196
#: ../js/ui/statusMenu.js:262
msgid "Power Off..."
msgstr "...ਬੰਦ ਕਰੋ"
#: ../js/ui/statusMenu.js:194 ../js/ui/statusMenu.js:196
#: ../js/ui/statusMenu.js:261
msgid "Suspend"
msgstr "ਸਸਪੈਂਡ"
#: ../js/ui/statusMenu.js:217
msgid "Available"
msgstr "ਉਪਲੱਬਧ"
#: ../js/ui/statusMenu.js:222
msgid "Busy"
msgstr "ਰੁਝਿਆ"
#: ../js/ui/statusMenu.js:230
msgid "My Account"
msgstr "ਮੇਰਾ ਅਕਾਊਂਟ"
#: ../js/ui/statusMenu.js:234
msgid "System Settings"
msgstr "ਸਿਸਟਮ ਸੈਟਿੰਗ"
#: ../js/ui/statusMenu.js:242
msgid "Lock Screen"
msgstr "ਸਕਰੀਨ ਲਾਕ ਕਰੋ"
#: ../js/ui/statusMenu.js:247
msgid "Switch User"
msgstr "ਯੂਜ਼ਰ ਬਦਲੋ"
#: ../js/ui/statusMenu.js:252
msgid "Log Out..."
msgstr "...ਲਾਗਆਉਟ"
#: ../js/ui/shellMountOperation.js:285
msgid "Wrong password, please try again"
msgstr "ਗਲਤ ਪਾਸਵਰਡ; ਮੁੜ ਕੋਸ਼ਿਸ਼ ਕਰੋ ਜੀ"
#: ../js/ui/status/accessibility.js:60
msgid "Zoom"
@ -830,7 +808,7 @@ msgstr "ਹਮੇਸ਼ਾ ਪਹੁੰਚ ਮਨਜ਼ੂਰ"
msgid "Grant this time only"
msgstr "ਕੇਵਲ ਇਸ ਸਮੇਂ ਹੀ ਮਨਜ਼ੂਰ"
#: ../js/ui/status/bluetooth.js:409 ../js/ui/telepathyClient.js:954
#: ../js/ui/status/bluetooth.js:409 ../js/ui/telepathyClient.js:953
msgid "Reject"
msgstr "ਨਾ-ਮਨਜ਼ੂਰ"
@ -871,12 +849,52 @@ msgid "OK"
msgstr "ਠੀਕ ਹੈ"
#: ../js/ui/status/keyboard.js:71
msgid "Show Keyboard Layout..."
msgstr "...ਕੀਬੋਰਡ ਲੇਆਉਟ ਵੇਖੋ"
#| msgid "Show Keyboard Layout..."
msgid "Show Keyboard Layout"
msgstr "ਕੀਬੋਰਡ ਲੇਆਉਟ ਵੇਖਾਓ"
#: ../js/ui/status/keyboard.js:75
msgid "Localization Settings"
msgstr "ਲੋਕਲਾਈਜ਼ੇਸ਼ਨ ਸੈਟਿੰਗ"
#| msgid "Date and Time Settings"
msgid "Region and Language Settings"
msgstr "ਖੇਤਰ ਅਤੇ ਭਾਸ਼ਾ ਸੈਟਿੰਗ"
#: ../js/ui/statusMenu.js:192 ../js/ui/statusMenu.js:196
#: ../js/ui/statusMenu.js:262
msgid "Power Off..."
msgstr "...ਬੰਦ ਕਰੋ"
#: ../js/ui/statusMenu.js:194 ../js/ui/statusMenu.js:196
#: ../js/ui/statusMenu.js:261
msgid "Suspend"
msgstr "ਸਸਪੈਂਡ"
#: ../js/ui/statusMenu.js:217
msgid "Available"
msgstr "ਉਪਲੱਬਧ"
#: ../js/ui/statusMenu.js:222
msgid "Busy"
msgstr "ਰੁਝਿਆ"
#: ../js/ui/statusMenu.js:230
msgid "My Account"
msgstr "ਮੇਰਾ ਅਕਾਊਂਟ"
#: ../js/ui/statusMenu.js:234
msgid "System Settings"
msgstr "ਸਿਸਟਮ ਸੈਟਿੰਗ"
#: ../js/ui/statusMenu.js:242
msgid "Lock Screen"
msgstr "ਸਕਰੀਨ ਲਾਕ ਕਰੋ"
#: ../js/ui/statusMenu.js:247
msgid "Switch User"
msgstr "ਯੂਜ਼ਰ ਬਦਲੋ"
#: ../js/ui/statusMenu.js:252
msgid "Log Out..."
msgstr "...ਲਾਗਆਉਟ"
#: ../js/ui/status/network.js:123
msgid "<unknown>"
@ -1102,10 +1120,6 @@ msgstr "ਟੇਬਲੇਟ"
msgid "Computer"
msgstr "ਕੰਪਿਊਟਰ"
#: ../js/ui/status/power.js:238 ../src/shell-app-system.c:1088
msgid "Unknown"
msgstr "ਅਣਜਾਣ"
#: ../js/ui/status/volume.js:43
msgid "Volume"
msgstr "ਆਵਾਜ਼"
@ -1117,13 +1131,12 @@ msgstr "ਮਾਈਕਰੋਫੋਨ"
#. 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
#: ../js/ui/telepathyClient.js:228
msgid "Invitation"
msgstr "ਸੱਦਾ"
#. We got the TpContact
#: ../js/ui/telepathyClient.js:285
#| msgid "Cancel"
#: ../js/ui/telepathyClient.js:291
msgid "Call"
msgstr "ਕਾਲ ਕਰੋ"
@ -1150,21 +1163,36 @@ 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:741
#: ../js/ui/telepathyClient.js:738
#, no-c-format
msgid "Sent at %X on %A"
msgstr "%2$A ਨੂੰ %1$X ਵਜੇ ਭੇਜਿਆ"
#| msgid "Sent at %X on %A"
msgid "Sent at <b>%X</b> on <b>%A</b>"
msgstr "<b>%2$A</b> ਨੂੰ <b>%1$X</b> ਵਜੇ ਭੇਜਿਆ"
#. Translators: this is a time format in the style of "Wednesday, May 25",
#. shown when you get a chat message in the same year.
#: ../js/ui/telepathyClient.js:744
#, no-c-format
msgid "Sent on <b>%A</b>, <b>%B %d</b>"
msgstr " <b>%A</b> ਨੂੰ <b>%B %d</b> ਵਜੇ ਭੇਜਿਆ"
#. Translators: this is a time format in the style of "Wednesday, May 25, 2012",
#. shown when you get a chat message in a different year.
#: ../js/ui/telepathyClient.js:749
#, no-c-format
msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
msgstr "<b>%A</b> <b>%B %d</b>, %Y ਨੂੰ ਭੇਜਿਆ"
#. Translators: this is the other person changing their old IM name to their new
#. IM name.
#: ../js/ui/telepathyClient.js:791
#: ../js/ui/telepathyClient.js:790
#, 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
#: ../js/ui/telepathyClient.js:897
#, c-format
msgid "Invitation to %s"
msgstr "%s ਲਈ ਸੱਦਾ"
@ -1172,32 +1200,32 @@ 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
#: ../js/ui/telepathyClient.js:905
#, c-format
msgid "%s is inviting you to join %s"
msgstr "%s ਤੁਹਾਨੂੰ %s ਜੁਆਇੰਨ ਕਰਨ ਲਈ ਸੱਦ ਰਿਹਾ ਹੈ"
#: ../js/ui/telepathyClient.js:908
#: ../js/ui/telepathyClient.js:907
msgid "Decline"
msgstr "ਇਨਕਾਰ"
#: ../js/ui/telepathyClient.js:909
#: ../js/ui/telepathyClient.js:908
msgid "Accept"
msgstr "ਮਨਜ਼ੂਰ"
#. translators: argument is a contact name like Alice for example.
#: ../js/ui/telepathyClient.js:942
#: ../js/ui/telepathyClient.js:941
#, c-format
msgid "Video call from %s"
msgstr "%s ਵਲੋਂ ਵਿਡੀਓ ਕਾਲ"
#. translators: argument is a contact name like Alice for example.
#: ../js/ui/telepathyClient.js:945
#: ../js/ui/telepathyClient.js:944
#, c-format
msgid "Call from %s"
msgstr "%s ਵਲੋਂ ਕਾਲ"
#: ../js/ui/telepathyClient.js:955
#: ../js/ui/telepathyClient.js:954
msgid "Answer"
msgstr "ਜਵਾਬ"
@ -1209,16 +1237,16 @@ msgstr "ਜਵਾਬ"
msgid "Type to search..."
msgstr "...ਲੱਭਣ ਲਈ ਲਿਖੋ"
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:257
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:261
msgid "Search"
msgstr "ਖੋਜ"
#: ../js/ui/windowAttentionHandler.js:40
#: ../js/ui/windowAttentionHandler.js:39
#, c-format
msgid "%s has finished starting"
msgstr "%s ਸ਼ੁਰੂ ਹੋਣਾ ਖਤਮ ਹੋਇਆ"
#: ../js/ui/windowAttentionHandler.js:42
#: ../js/ui/windowAttentionHandler.js:41
#, c-format
msgid "'%s' is ready"
msgstr "'%s' ਤਿਆਰ ਹੈ"
@ -1266,13 +1294,13 @@ msgstr "ਡਿਫਾਲਟ"
msgid "Authentication dialog was dismissed by the user"
msgstr "ਪਰਮਾਣਕਿਤਾ ਡਾਈਲਾਗ ਯੂਜ਼ਰ ਵਲੋਂ ਰੱਦ ਕੀਤਾ"
#: ../src/shell-util.c:96
#: ../src/shell-util.c:100
msgid "Home Folder"
msgstr "ਘਰ ਫੋਲਡਰ"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-util.c:111
#: ../src/shell-util.c:115
msgid "File System"
msgstr "ਫਾਇਲ ਸਿਸਟਮ"
@ -1281,11 +1309,18 @@ msgstr "ਫਾਇਲ ਸਿਸਟਮ"
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-util.c:307
#: ../src/shell-util.c:311
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"
#: ../src/shell-util.c:586
msgid "calendar:week_start:0"
msgstr "calendar:week_start:1"
#~ msgid "Localization Settings"
#~ msgstr "ਲੋਕਲਾਈਜ਼ੇਸ਼ਨ ਸੈਟਿੰਗ"
#~ msgid "Less than a minute ago"
#~ msgstr "ਇੱਕ ਮਿੰਟ ਤੋਂ ਘੱਟ ਚਿਰ ਪਹਿਲਾਂ"

958
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

575
po/sv.po

File diff suppressed because it is too large Load Diff

748
po/vi.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,35 +0,0 @@
gdmuser_cflags = \
-I$(top_srcdir)/src \
-DPREFIX=\""$(prefix)"\" \
-DLIBDIR=\""$(libdir)"\" \
-DDATADIR=\""$(datadir)"\" \
-DG_DISABLE_DEPRECATED \
-DG_LOG_DOMAIN=\"GdmUser\" \
-DGDM_CACHE_DIR=\""$(localstatedir)/cache/gdm"\" \
$(GDMUSER_CFLAGS) \
$(NULL)
# please, keep this sorted alphabetically
gdmuser_source_h = \
gdmuser/gdm-user.h \
gdmuser/gdm-user-private.h \
gdmuser/gdm-user-manager.h \
$(NULL)
# please, keep this sorted alphabetically
gdmuser_source_c = \
gdmuser/gdm-user.c \
gdmuser/gdm-user-manager.c \
$(NULL)
noinst_LTLIBRARIES += libgdmuser-1.0.la
libgdmuser_1_0_la_LIBADD = $(GDMUSER_LIBS)
libgdmuser_1_0_la_SOURCES = \
$(gdmuser_source_c) \
$(gdmuser_source_h) \
$(NULL)
libgdmuser_1_0_la_CPPFLAGS = $(gdmuser_cflags)
libgdmuser_1_0_la_LDFLAGS = $(LDADD)

View File

@ -28,6 +28,7 @@ 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
@ -36,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
@ -65,9 +67,7 @@ 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
@ -102,6 +102,7 @@ shell_public_headers_h = \
shell-app-system.h \
shell-app-usage.h \
shell-arrow.h \
shell-contact-system.h \
shell-doc-system.h \
shell-embedded-window.h \
shell-generic-container.h \
@ -109,10 +110,11 @@ shell_public_headers_h = \
shell-global.h \
shell-mobile-providers.h \
shell-mount-operation.h \
shell-network-agent.h \
shell-perf-log.h \
shell-slicer.h \
shell-stack.h \
shell-tp-client.h \
shell-tp-client.h \
shell-tray-icon.h \
shell-tray-manager.h \
shell-util.h \
@ -136,6 +138,7 @@ libgnome_shell_la_SOURCES = \
shell-app-system.c \
shell-app-usage.c \
shell-arrow.c \
shell-contact-system.c \
shell-doc-system.c \
shell-embedded-window.c \
shell-generic-container.c \
@ -143,6 +146,7 @@ libgnome_shell_la_SOURCES = \
shell-global.c \
shell-mobile-providers.c \
shell-mount-operation.c \
shell-network-agent.c \
shell-perf-log.c \
shell-polkit-authentication-agent.h \
shell-polkit-authentication-agent.c \
@ -260,7 +264,6 @@ libgnome_shell_la_LIBADD = \
$(GNOME_SHELL_LIBS) \
$(BLUETOOTH_LIBS) \
libst-1.0.la \
libgdmuser-1.0.la \
libtray.la \
libgvc.la \
$(NULL)
@ -268,7 +271,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 Soup-2.4
Shell_0_1_gir_INCLUDES = Clutter-1.0 ClutterX11-1.0 Meta-3.0 TelepathyGLib-0.12 TelepathyLogger-0.2 Soup-2.4 GMenu-3.0 NetworkManager-1.0 NMClient-1.0 Folks-0.6
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)
@ -285,12 +288,3 @@ St_1_0_gir_FILES = $(filter-out %-private.h $(st_non_gir_sources), $(addprefix $
$(addprefix $(srcdir)/,$(st_source_c))
INTROSPECTION_GIRS += St-1.0.gir
CLEANFILES += St-1.0.gir
Gdm-1.0.gir: libgdmuser-1.0.la
Gdm_1_0_gir_INCLUDES = GObject-2.0 GdkPixbuf-2.0
Gdm_1_0_gir_CFLAGS = $(gdmuser_cflags)
Gdm_1_0_gir_LIBS = libgdmuser-1.0.la
Gdm_1_0_gir_FILES = $(filter-out %-private.h, $(addprefix $(srcdir)/,$(gdmuser_source_h))) \
$(addprefix $(srcdir)/,$(gdmuser_source_c))
INTROSPECTION_GIRS += Gdm-1.0.gir
CLEANFILES += Gdm-1.0.gir

File diff suppressed because it is too large Load Diff

View File

@ -1,91 +0,0 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#ifndef __GDM_USER_MANAGER_H__
#define __GDM_USER_MANAGER_H__
#include <glib-object.h>
#include "gdm-user.h"
G_BEGIN_DECLS
#define GDM_TYPE_USER_MANAGER (gdm_user_manager_get_type ())
#define GDM_USER_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_USER_MANAGER, GdmUserManager))
#define GDM_USER_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_USER_MANAGER, GdmUserManagerClass))
#define GDM_IS_USER_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_USER_MANAGER))
#define GDM_IS_USER_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_USER_MANAGER))
#define GDM_USER_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_USER_MANAGER, GdmUserManagerClass))
typedef struct GdmUserManagerPrivate GdmUserManagerPrivate;
typedef struct GdmUserManager GdmUserManager;
typedef struct GdmUserManagerClass GdmUserManagerClass;
typedef enum GdmUserManagerError GdmUserManagerError;
struct GdmUserManager
{
GObject parent;
GdmUserManagerPrivate *priv;
};
struct GdmUserManagerClass
{
GObjectClass parent_class;
void (* user_added) (GdmUserManager *user_manager,
GdmUser *user);
void (* user_removed) (GdmUserManager *user_manager,
GdmUser *user);
void (* user_is_logged_in_changed) (GdmUserManager *user_manager,
GdmUser *user);
void (* user_changed) (GdmUserManager *user_manager,
GdmUser *user);
};
enum GdmUserManagerError
{
GDM_USER_MANAGER_ERROR_GENERAL,
GDM_USER_MANAGER_ERROR_KEY_NOT_FOUND
};
#define GDM_USER_MANAGER_ERROR gdm_user_manager_error_quark ()
GQuark gdm_user_manager_error_quark (void);
GType gdm_user_manager_get_type (void);
GdmUserManager * gdm_user_manager_ref_default (void);
void gdm_user_manager_queue_load (GdmUserManager *manager);
GSList * gdm_user_manager_list_users (GdmUserManager *manager);
GdmUser * gdm_user_manager_get_user (GdmUserManager *manager,
const char *username);
GdmUser * gdm_user_manager_get_user_by_uid (GdmUserManager *manager,
gulong uid);
gboolean gdm_user_manager_activate_user_session (GdmUserManager *manager,
GdmUser *user);
gboolean gdm_user_manager_can_switch (GdmUserManager *manager);
gboolean gdm_user_manager_goto_login_session (GdmUserManager *manager);
G_END_DECLS
#endif /* __GDM_USER_MANAGER_H */

View File

@ -1,49 +0,0 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
*
* 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
*/
/*
* Private interfaces to the GdmUser object
*/
#ifndef __GDM_USER_PRIVATE_H_
#define __GDM_USER_PRIVATE_H_
#include <pwd.h>
#include "gdm-user.h"
G_BEGIN_DECLS
void _gdm_user_update_from_object_path (GdmUser *user,
const char *object_path);
void _gdm_user_update_from_pwent (GdmUser *user,
const struct passwd *pwent);
void _gdm_user_update_login_frequency (GdmUser *user,
guint64 login_frequency);
void _gdm_user_add_session (GdmUser *user,
const char *session_id);
void _gdm_user_remove_session (GdmUser *user,
const char *session_id);
G_END_DECLS
#endif /* !__GDM_USER_PRIVATE__ */

File diff suppressed because it is too large Load Diff

View File

@ -1,64 +0,0 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
* Copyright (C) 2007-2008 William Jon McCann <mccann@jhu.edu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Facade object for user data, owned by GdmUserManager
*/
#ifndef __GDM_USER_H__
#define __GDM_USER_H__
#include <sys/types.h>
#include <gtk/gtk.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
G_BEGIN_DECLS
#define GDM_TYPE_USER (gdm_user_get_type ())
#define GDM_USER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDM_TYPE_USER, GdmUser))
#define GDM_IS_USER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDM_TYPE_USER))
typedef struct _GdmUser GdmUser;
typedef struct _GdmUserClass GdmUserClass;
GType gdm_user_get_type (void) G_GNUC_CONST;
GdmUser *gdm_user_new_from_object_path (const char *path);
const char *gdm_user_get_object_path (GdmUser *user);
gulong gdm_user_get_uid (GdmUser *user);
const char *gdm_user_get_user_name (GdmUser *user);
const char *gdm_user_get_real_name (GdmUser *user);
guint gdm_user_get_num_sessions (GdmUser *user);
gboolean gdm_user_is_logged_in (GdmUser *user);
gulong gdm_user_get_login_frequency (GdmUser *user);
const char *gdm_user_get_icon_file (GdmUser *user);
const char *gdm_user_get_primary_session_id (GdmUser *user);
GdkPixbuf *gdm_user_render_icon (GdmUser *user,
gint icon_size);
gint gdm_user_collate (GdmUser *user1,
GdmUser *user2);
gboolean gdm_user_is_loaded (GdmUser *user);
G_END_DECLS
#endif

View File

@ -33,7 +33,7 @@ const St = imports.gi.St;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
let text;
let text, button;
function _hideHello() {
Main.uiGroup.remove_actor(text);
@ -60,22 +60,28 @@ function _showHello() {
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 });
function init() {
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);
}
function enable() {
Main.panel._rightBox.insert_actor(button, 0);
}
function disable() {
Main.panel._rightBox.remove_actor(button);
}
""",
"stylesheet.css": """

View File

@ -263,6 +263,11 @@ gvc_mixer_card_change_profile (GvcMixerCard *card,
return TRUE;
}
/**
* gvc_mixer_card_get_profiles:
*
* Return value: (transfer none) (element-type GvcMixerCardProfile):
*/
const GList *
gvc_mixer_card_get_profiles (GvcMixerCard *card)
{
@ -281,6 +286,10 @@ sort_profiles (GvcMixerCardProfile *a,
return -1;
}
/**
* gvc_mixer_card_set_profiles:
* @profiles: (transfer full) (element-type GvcMixerCardProfile):
*/
gboolean
gvc_mixer_card_set_profiles (GvcMixerCard *card,
GList *profiles)

View File

@ -81,11 +81,12 @@ struct GvcMixerControlPrivate
GHashTable *cards;
GvcMixerStream *new_default_stream; /* new default stream, used in gvc_mixer_control_set_default_sink () */
GvcMixerControlState state;
};
enum {
CONNECTING,
READY,
STATE_CHANGED,
STREAM_ADDED,
STREAM_REMOVED,
CARD_ADDED,
@ -508,16 +509,17 @@ dec_outstanding (GvcMixerControl *control)
}
if (--control->priv->n_outstanding <= 0) {
g_signal_emit (G_OBJECT (control), signals[READY], 0);
control->priv->state = GVC_STATE_READY;
g_signal_emit (G_OBJECT (control), signals[STATE_CHANGED], 0, GVC_STATE_READY);
}
}
gboolean
gvc_mixer_control_is_ready (GvcMixerControl *control)
GvcMixerControlState
gvc_mixer_control_get_state (GvcMixerControl *control)
{
g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE);
return (control->priv->n_outstanding == 0);
return control->priv->state;
}
@ -1943,7 +1945,8 @@ _pa_context_state_cb (pa_context *context,
break;
case PA_CONTEXT_FAILED:
g_warning ("Connection failed, reconnecting...");
control->priv->state = GVC_STATE_FAILED;
g_signal_emit (control, signals[STATE_CHANGED], 0, GVC_STATE_FAILED);
if (control->priv->reconnect_id == 0)
control->priv->reconnect_id = g_timeout_add_seconds (RECONNECT_DELAY, idle_reconnect, control);
break;
@ -1968,7 +1971,8 @@ gvc_mixer_control_open (GvcMixerControl *control)
_pa_context_state_cb,
control);
g_signal_emit (G_OBJECT (control), signals[CONNECTING], 0);
control->priv->state = GVC_STATE_CONNECTING;
g_signal_emit (G_OBJECT (control), signals[STATE_CHANGED], 0, GVC_STATE_CONNECTING);
res = pa_context_connect (control->priv->pa_context, NULL, (pa_context_flags_t) PA_CONTEXT_NOFAIL, NULL);
if (res < 0) {
g_warning ("Failed to connect context: %s",
@ -1985,6 +1989,9 @@ gvc_mixer_control_close (GvcMixerControl *control)
g_return_val_if_fail (control->priv->pa_context != NULL, FALSE);
pa_context_disconnect (control->priv->pa_context);
control->priv->state = GVC_STATE_CLOSED;
g_signal_emit (G_OBJECT (control), signals[STATE_CHANGED], 0, GVC_STATE_CLOSED);
return TRUE;
}
@ -2125,22 +2132,14 @@ gvc_mixer_control_class_init (GvcMixerControlClass *klass)
NULL,
G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
signals [CONNECTING] =
g_signal_new ("connecting",
signals [STATE_CHANGED] =
g_signal_new ("state-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GvcMixerControlClass, connecting),
G_STRUCT_OFFSET (GvcMixerControlClass, state_changed),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals [READY] =
g_signal_new ("ready",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GvcMixerControlClass, ready),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE, 1, G_TYPE_UINT);
signals [STREAM_ADDED] =
g_signal_new ("stream-added",
G_TYPE_FROM_CLASS (klass),
@ -2212,6 +2211,8 @@ gvc_mixer_control_init (GvcMixerControl *control)
control->priv->cards = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
control->priv->clients = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_free);
control->priv->state = GVC_STATE_CLOSED;
}
static void

View File

@ -27,6 +27,14 @@
G_BEGIN_DECLS
typedef enum
{
GVC_STATE_CLOSED,
GVC_STATE_READY,
GVC_STATE_CONNECTING,
GVC_STATE_FAILED
} GvcMixerControlState;
#define GVC_TYPE_MIXER_CONTROL (gvc_mixer_control_get_type ())
#define GVC_MIXER_CONTROL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_CONTROL, GvcMixerControl))
#define GVC_MIXER_CONTROL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_CONTROL, GvcMixerControlClass))
@ -46,8 +54,8 @@ typedef struct
{
GObjectClass parent_class;
void (*connecting) (GvcMixerControl *control);
void (*ready) (GvcMixerControl *control);
void (*state_changed) (GvcMixerControl *control,
GvcMixerControlState new_state);
void (*stream_added) (GvcMixerControl *control,
guint id);
void (*stream_removed) (GvcMixerControl *control,
@ -68,7 +76,6 @@ GvcMixerControl * gvc_mixer_control_new (const char *name);
gboolean gvc_mixer_control_open (GvcMixerControl *control);
gboolean gvc_mixer_control_close (GvcMixerControl *control);
gboolean gvc_mixer_control_is_ready (GvcMixerControl *control);
GSList * gvc_mixer_control_get_cards (GvcMixerControl *control);
GSList * gvc_mixer_control_get_streams (GvcMixerControl *control);
@ -94,6 +101,8 @@ gboolean gvc_mixer_control_set_default_source (GvcMixerControl *con
gdouble gvc_mixer_control_get_vol_max_norm (GvcMixerControl *control);
gdouble gvc_mixer_control_get_vol_max_amplified (GvcMixerControl *control);
GvcMixerControlState gvc_mixer_control_get_state (GvcMixerControl *control);
G_END_DECLS
#endif /* __GVC_MIXER_CONTROL_H */

View File

@ -508,6 +508,11 @@ gvc_mixer_stream_change_port (GvcMixerStream *stream,
return GVC_MIXER_STREAM_GET_CLASS (stream)->change_port (stream, port);
}
/**
* gvc_mixer_stream_get_ports:
*
* Return value: (transfer none) (element-type GvcMixerStreamPort):
*/
const GList *
gvc_mixer_stream_get_ports (GvcMixerStream *stream)
{
@ -526,6 +531,10 @@ sort_ports (GvcMixerStreamPort *a,
return -1;
}
/**
* gvc_mixer_stream_set_ports:
* @ports: (transfer full) (element-type GvcMixerStreamPort):
*/
gboolean
gvc_mixer_stream_set_ports (GvcMixerStream *stream,
GList *ports)

View File

@ -24,6 +24,7 @@
#include "shell-a11y.h"
#include "shell-global.h"
#include "shell-global-private.h"
#include "shell-perf-log.h"
#include "st.h"
@ -32,6 +33,8 @@ extern GType gnome_shell_plugin_get_type (void);
#define SHELL_DBUS_SERVICE "org.gnome.Shell"
#define MAGNIFIER_DBUS_SERVICE "org.gnome.Magnifier"
static gboolean is_gdm_mode = FALSE;
static void
shell_dbus_init (gboolean replace)
{
@ -466,6 +469,12 @@ GOptionEntry gnome_shell_options[] = {
N_("Print version"),
NULL
},
{
"gdm-mode", 0, 0, G_OPTION_ARG_NONE,
&is_gdm_mode,
N_("Mode used by GDM for login screen"),
NULL
},
{ NULL }
};
@ -474,6 +483,7 @@ main (int argc, char **argv)
{
GOptionContext *ctx;
GError *error = NULL;
ShellSessionType session_type;
int ecode;
TpDebugSender *sender;
@ -524,7 +534,7 @@ main (int argc, char **argv)
/* Disable debug spew from various libraries */
g_log_set_handler ("Gvc", G_LOG_LEVEL_DEBUG,
muted_log_handler, NULL);
g_log_set_handler ("GdmUser", G_LOG_LEVEL_DEBUG,
g_log_set_handler ("AccountsService", G_LOG_LEVEL_DEBUG,
muted_log_handler, NULL);
g_log_set_handler ("Bluetooth", G_LOG_LEVEL_DEBUG | G_LOG_LEVEL_MESSAGE,
muted_log_handler, NULL);
@ -540,7 +550,12 @@ main (int argc, char **argv)
g_log_set_default_handler (default_log_handler, sender);
/* Initialize the global object */
shell_global_get ();
if (is_gdm_mode)
session_type = SHELL_SESSION_GDM;
else
session_type = SHELL_SESSION_USER;
_shell_global_init ("session-type", session_type, NULL);
ecode = meta_run ();

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

@ -10,11 +10,9 @@
G_BEGIN_DECLS
ShellAppInfo *_shell_app_get_info (ShellApp *app);
ShellApp* _shell_app_new_for_window (MetaWindow *window);
ShellApp* _shell_app_new (ShellAppInfo *appinfo);
ShellApp* _shell_app_new (GMenuTreeEntry *entry);
void _shell_app_handle_startup_sequence (ShellApp *app, SnStartupSequence *sequence);
@ -22,6 +20,13 @@ void _shell_app_add_window (ShellApp *app, MetaWindow *window);
void _shell_app_remove_window (ShellApp *app, MetaWindow *window);
void _shell_app_do_match (ShellApp *app,
GSList *terms,
GSList **multiple_prefix_results,
GSList **prefix_results,
GSList **multiple_substring_results,
GSList **substring_results);
G_END_DECLS
#endif /* __SHELL_APP_PRIVATE_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,8 @@
#include <gio/gio.h>
#include <clutter/clutter.h>
#include <meta/window.h>
#define GMENU_I_KNOW_THIS_IS_UNSTABLE
#include <gmenu-tree.h>
#include "shell-app.h"
@ -37,63 +39,33 @@ struct _ShellAppSystemClass
GType shell_app_system_get_type (void) G_GNUC_CONST;
ShellAppSystem *shell_app_system_get_default (void);
typedef struct _ShellAppInfo ShellAppInfo;
GMenuTree *shell_app_system_get_tree (ShellAppSystem *system);
#define SHELL_TYPE_APP_INFO (shell_app_info_get_type ())
GType shell_app_info_get_type (void);
ShellAppInfo *shell_app_info_ref (ShellAppInfo *info);
void shell_app_info_unref (ShellAppInfo *info);
const char *shell_app_info_get_id (ShellAppInfo *info);
char *shell_app_info_get_name (ShellAppInfo *info);
char *shell_app_info_get_description (ShellAppInfo *info);
char *shell_app_info_get_executable (ShellAppInfo *info);
char *shell_app_info_get_desktop_file_path (ShellAppInfo *info);
GIcon *shell_app_info_get_icon (ShellAppInfo *info);
ClutterActor *shell_app_info_create_icon_texture (ShellAppInfo *info,
float size);
char *shell_app_info_get_section (ShellAppInfo *info);
gboolean shell_app_info_get_is_nodisplay (ShellAppInfo *info);
gboolean shell_app_info_is_transient (ShellAppInfo *info);
MetaWindow *shell_app_info_get_source_window (ShellAppInfo *info);
gboolean shell_app_info_launch (ShellAppInfo *info,
GError **error);
gboolean shell_app_info_launch_full (ShellAppInfo *info,
guint timestamp,
GList *uris,
int workspace,
char **startup_id,
GError **error);
ShellApp *shell_app_system_lookup_app (ShellAppSystem *system,
const char *id);
ShellApp *shell_app_system_lookup_app_by_tree_entry (ShellAppSystem *system,
GMenuTreeEntry *entry);
ShellApp *shell_app_system_lookup_app_for_path (ShellAppSystem *system,
const char *desktop_path);
ShellApp *shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
const char *id);
GList *shell_app_system_get_sections (ShellAppSystem *system);
GSList *shell_app_system_get_flattened_apps (ShellAppSystem *system);
GSList *shell_app_system_get_all_settings (ShellAppSystem *system);
ShellApp *shell_app_system_get_app (ShellAppSystem *system,
const char *id);
ShellApp *shell_app_system_get_app_for_path (ShellAppSystem *system,
const char *desktop_path);
ShellApp *shell_app_system_get_app_for_window (ShellAppSystem *self,
MetaWindow *window);
ShellApp *shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
const char *id);
ShellAppInfo *shell_app_system_create_from_window (ShellAppSystem *system,
MetaWindow *window);
GSList *shell_app_system_get_all (ShellAppSystem *system);
GSList *shell_app_system_initial_search (ShellAppSystem *system,
gboolean prefs,
GSList *terms);
GSList *shell_app_system_subsearch (ShellAppSystem *system,
gboolean prefs,
GSList *previous_results,
GSList *terms);
/* internal API */
void _shell_app_system_register_app (ShellAppSystem *self,
ShellApp *app);
GMenuTree *shell_app_system_get_settings_tree (ShellAppSystem *system);
GSList *shell_app_system_search_settings (ShellAppSystem *system,
GSList *terms);
ShellApp *shell_app_system_lookup_setting (ShellAppSystem *system,
const char *id);
#endif /* __SHELL_APP_SYSTEM_H__ */

View File

@ -11,7 +11,6 @@
#include <gdk/gdkx.h>
#include <glib.h>
#include <gio/gio.h>
#include <dbus/dbus-glib.h>
#include <meta/display.h>
#include <meta/group.h>
#include <meta/window.h>
@ -82,7 +81,7 @@ struct _ShellAppUsage
GObject parent;
GFile *configfile;
DBusGProxy *session_proxy;
GDBusProxy *session_proxy;
GdkDisplay *display;
gulong last_idle;
guint idle_focus_change_id;
@ -106,7 +105,7 @@ G_DEFINE_TYPE (ShellAppUsage, shell_app_usage, G_TYPE_OBJECT);
struct UsageData
{
/* Whether the application we're tracking is "transient", see
* shell_app_info_is_transient.
* shell_app_is_window_backed.
*/
gboolean transient;
@ -116,7 +115,7 @@ struct UsageData
static void shell_app_usage_finalize (GObject *object);
static void on_session_status_changed (DBusGProxy *proxy, guint status, ShellAppUsage *self);
static void on_session_status_changed (GDBusProxy *proxy, guint status, ShellAppUsage *self);
static void on_focus_app_changed (ShellWindowTracker *tracker, GParamSpec *spec, ShellAppUsage *self);
static void ensure_queued_save (ShellAppUsage *self);
static UsageData * get_app_usage_for_context_and_id (ShellAppUsage *self,
@ -316,7 +315,7 @@ on_app_state_changed (ShellWindowTracker *tracker,
UsageData *usage;
gboolean running;
if (shell_app_is_transient (app))
if (shell_app_is_window_backed (app))
return;
usage = get_usage_for_app (self, app);
@ -343,7 +342,7 @@ on_focus_app_changed (ShellWindowTracker *tracker,
}
static void
on_session_status_changed (DBusGProxy *proxy,
on_session_status_changed (GDBusProxy *proxy,
guint status,
ShellAppUsage *self)
{
@ -374,12 +373,23 @@ on_session_status_changed (DBusGProxy *proxy,
}
}
static void
session_proxy_signal (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, gpointer user_data)
{
if (g_str_equal (signal_name, "StatusChanged"))
{
guint status;
g_variant_get (parameters, "(u)", &status);
on_session_status_changed (proxy, status, SHELL_APP_USAGE (user_data));
}
}
static void
shell_app_usage_init (ShellAppUsage *self)
{
ShellGlobal *global;
char *shell_userdata_dir, *path;
DBusGConnection *session_bus;
GDBusConnection *session_bus;
ShellWindowTracker *tracker;
global = shell_global_get ();
@ -390,14 +400,17 @@ shell_app_usage_init (ShellAppUsage *self)
g_signal_connect (tracker, "notify::focus-app", G_CALLBACK (on_focus_app_changed), self);
g_signal_connect (tracker, "app-state-changed", G_CALLBACK (on_app_state_changed), self);
session_bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
self->session_proxy = dbus_g_proxy_new_for_name (session_bus, "org.gnome.SessionManager",
"/org/gnome/SessionManager/Presence",
"org.gnome.SessionManager");
dbus_g_proxy_add_signal (self->session_proxy, "StatusChanged",
G_TYPE_UINT, G_TYPE_INVALID, G_TYPE_INVALID);
dbus_g_proxy_connect_signal (self->session_proxy, "StatusChanged",
G_CALLBACK (on_session_status_changed), self, NULL);
session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
self->session_proxy = g_dbus_proxy_new_sync (session_bus,
G_DBUS_PROXY_FLAGS_NONE,
NULL, /* interface info */
"org.gnome.SessionManager",
"/org/gnome/SessionManager/Presence",
"org.gnome.SessionManager",
NULL, /* cancellable */
NULL /* error */);
g_signal_connect (self->session_proxy, "g-signal", G_CALLBACK (session_proxy_signal), self);
g_object_unref (session_bus);
self->last_idle = 0;
self->currently_idle = FALSE;
@ -433,6 +446,8 @@ shell_app_usage_finalize (GObject *object)
g_object_unref (self->configfile);
g_object_unref (self->session_proxy);
G_OBJECT_CLASS (shell_app_usage_parent_class)->finalize(object);
}
@ -494,7 +509,7 @@ shell_app_usage_get_most_used (ShellAppUsage *self,
const char *appid = iter->data;
ShellApp *app;
app = shell_app_system_get_app (appsys, appid);
app = shell_app_system_lookup_app (appsys, appid);
if (!app)
continue;
@ -655,7 +670,7 @@ idle_save_application_usage (gpointer data)
{
ShellApp *app;
app = shell_app_system_get_app (shell_app_system_get_default(), id);
app = shell_app_system_lookup_app (shell_app_system_get_default(), id);
if (!app)
continue;

View File

@ -11,9 +11,18 @@
#include "shell-app-private.h"
#include "shell-enum-types.h"
#include "shell-global.h"
#include "shell-util.h"
#include "shell-window-tracker-private.h"
#include "st.h"
typedef enum {
MATCH_NONE,
MATCH_SUBSTRING, /* Not prefix, substring */
MATCH_MULTIPLE_SUBSTRING, /* Matches multiple criteria with substrings */
MATCH_PREFIX, /* Strict prefix */
MATCH_MULTIPLE_PREFIX, /* Matches multiple criteria, at least one prefix */
} ShellAppSearchMatch;
/* This is mainly a memory usage optimization - the user is going to
* be running far fewer of the applications at one time than they have
* installed. But it also just helps keep the code more logically
@ -38,7 +47,7 @@ typedef struct {
* SECTION:shell-app
* @short_description: Object representing an application
*
* This object wraps a #ShellAppInfo, providing methods and signals
* This object wraps a #GMenuTreeEntry, providing methods and signals
* primarily useful for running applications.
*/
struct _ShellApp
@ -49,9 +58,22 @@ struct _ShellApp
ShellAppState state;
ShellAppInfo *info;
GMenuTreeEntry *entry; /* If NULL, this app is backed by one or more
* MetaWindow. For purposes of app title
* etc., we use the first window added,
* because it's most likely to be what we
* want (e.g. it will be of TYPE_NORMAL from
* the way shell-window-tracker.c works).
*/
ShellAppRunningState *running_state;
char *window_id_string;
char *casefolded_name;
char *name_collation_key;
char *casefolded_description;
char *casefolded_exec;
};
G_DEFINE_TYPE (ShellApp, shell_app, G_TYPE_OBJECT);
@ -93,7 +115,44 @@ shell_app_get_property (GObject *gobject,
const char *
shell_app_get_id (ShellApp *app)
{
return shell_app_info_get_id (app->info);
if (app->entry)
return gmenu_tree_entry_get_desktop_file_id (app->entry);
return app->window_id_string;
}
static MetaWindow *
window_backed_app_get_window (ShellApp *app)
{
g_assert (app->entry == NULL);
g_assert (app->running_state);
g_assert (app->running_state->windows);
return app->running_state->windows->data;
}
static ClutterActor *
window_backed_app_get_icon (ShellApp *app,
int size)
{
MetaWindow *window;
ClutterActor *actor;
/* During a state transition from running to not-running for
* window-backend apps, it's possible we get a request for the icon.
* Avoid asserting here and just return an empty image.
*/
if (app->running_state == NULL)
{
actor = clutter_texture_new ();
g_object_set (actor, "opacity", 0, "width", (float) size, "height", (float) size, NULL);
return actor;
}
window = window_backed_app_get_window (app);
actor = st_texture_cache_bind_pixbuf_property (st_texture_cache_get_default (),
G_OBJECT (window),
"icon");
g_object_set (actor, "width", (float) size, "height", (float) size, NULL);
return actor;
}
/**
@ -106,10 +165,30 @@ shell_app_get_id (ShellApp *app)
*/
ClutterActor *
shell_app_create_icon_texture (ShellApp *app,
float size)
int size)
{
return shell_app_info_create_icon_texture (app->info, size);
GIcon *icon;
ClutterActor *ret;
ret = NULL;
if (app->entry == NULL)
return window_backed_app_get_icon (app, size);
icon = g_app_info_get_icon (G_APP_INFO (gmenu_tree_entry_get_app_info (app->entry)));
if (icon != NULL)
ret = st_texture_cache_load_gicon (st_texture_cache_get_default (), NULL, icon, size);
if (ret == NULL)
{
icon = g_themed_icon_new ("application-x-executable");
ret = st_texture_cache_load_gicon (st_texture_cache_get_default (), NULL, icon, size);
g_object_unref (icon);
}
return ret;
}
typedef struct {
ShellApp *app;
int size;
@ -143,20 +222,19 @@ shell_app_create_faded_icon_cpu (StTextureCache *cache,
info = NULL;
icon = shell_app_info_get_icon (app->info);
icon = g_app_info_get_icon (G_APP_INFO (gmenu_tree_entry_get_app_info (app->entry)));
if (icon != NULL)
{
info = gtk_icon_theme_lookup_by_gicon (gtk_icon_theme_get_default (),
icon, (int) (size + 0.5),
icon, size,
GTK_ICON_LOOKUP_FORCE_SIZE);
g_object_unref (icon);
}
if (info == NULL)
{
icon = g_themed_icon_new ("application-x-executable");
info = gtk_icon_theme_lookup_by_gicon (gtk_icon_theme_get_default (),
icon, (int) (size + 0.5),
icon, size,
GTK_ICON_LOOKUP_FORCE_SIZE);
g_object_unref (icon);
}
@ -222,28 +300,23 @@ shell_app_create_faded_icon_cpu (StTextureCache *cache,
* Return value: (transfer none): A floating #ClutterActor, or %NULL if no icon
*/
ClutterActor *
shell_app_get_faded_icon (ShellApp *app, float size)
shell_app_get_faded_icon (ShellApp *app, int size)
{
MetaWindow *window;
CoglHandle texture;
ClutterActor *result;
char *cache_key;
CreateFadedIconData data;
/* Punt for WINDOW types for now...easier to reuse the property tracking bits,
* and this helps us visually distinguish app-tracked from not.
/* Don't fade for window backed apps for now...easier to reuse the
* property tracking bits, and this helps us visually distinguish
* app-tracked from not.
*/
window = shell_app_info_get_source_window (app->info);
if (window)
{
return st_texture_cache_bind_pixbuf_property (st_texture_cache_get_default (),
G_OBJECT (window),
"icon");
}
if (!app->entry)
return window_backed_app_get_icon (app, size);
cache_key = g_strdup_printf ("faded-icon:%s,size=%f", shell_app_get_id (app), size);
cache_key = g_strdup_printf ("faded-icon:%s,size=%d", shell_app_get_id (app), size);
data.app = app;
data.size = (int) (0.5 + size);
data.size = size;
texture = st_texture_cache_load (st_texture_cache_get_default (),
cache_key,
ST_TEXTURE_CACHE_POLICY_FOREVER,
@ -260,28 +333,49 @@ shell_app_get_faded_icon (ShellApp *app, float size)
else
{
result = clutter_texture_new ();
g_object_set (result, "opacity", 0, "width", size, "height", size, NULL);
g_object_set (result, "opacity", 0, "width", (float) size, "height", (float) size, NULL);
}
return result;
}
char *
const char *
shell_app_get_name (ShellApp *app)
{
return shell_app_info_get_name (app->info);
if (app->entry)
return g_app_info_get_name (G_APP_INFO (gmenu_tree_entry_get_app_info (app->entry)));
else
{
MetaWindow *window = window_backed_app_get_window (app);
const char *name;
name = meta_window_get_wm_class (window);
if (!name)
name = _("Unknown");
return name;
}
}
char *
const char *
shell_app_get_description (ShellApp *app)
{
return shell_app_info_get_description (app->info);
if (app->entry)
return g_app_info_get_description (G_APP_INFO (gmenu_tree_entry_get_app_info (app->entry)));
else
return NULL;
}
/**
* shell_app_is_window_backed:
*
* A window backed application is one which represents just an open
* window, i.e. there's no .desktop file assocation, so we don't know
* how to launch it again.
*/
gboolean
shell_app_is_transient (ShellApp *app)
shell_app_is_window_backed (ShellApp *app)
{
return shell_app_info_is_transient (app->info);
return app->entry == NULL;
}
typedef struct {
@ -435,8 +529,22 @@ shell_app_activate_window (ShellApp *app,
/**
* shell_app_activate:
* @app: a #ShellApp
*
* Like shell_app_activate_full(), but using the default workspace and
* event timestamp.
*/
void
shell_app_activate (ShellApp *app)
{
return shell_app_activate_full (app, -1, 0);
}
/**
* shell_app_activate_full:
* @app: a #ShellApp
* @workspace: launch on this workspace, or -1 for default. Ignored if
* activating an existing window
* @timestamp: Event timestamp
*
* Perform an appropriate default action for operating on this application,
* dependent on its current state. For example, if the application is not
@ -445,24 +553,32 @@ shell_app_activate_window (ShellApp *app,
* recently used transient for that window).
*/
void
shell_app_activate (ShellApp *app,
int workspace)
shell_app_activate_full (ShellApp *app,
int workspace,
guint32 timestamp)
{
ShellGlobal *global;
global = shell_global_get ();
if (timestamp == 0)
timestamp = shell_global_get_current_time (global);
switch (app->state)
{
case SHELL_APP_STATE_STOPPED:
{
GError *error = NULL;
if (!shell_app_info_launch_full (app->info,
0,
NULL,
workspace,
NULL,
&error))
if (!shell_app_launch (app,
timestamp,
NULL,
workspace,
NULL,
&error))
{
char *msg;
msg = g_strdup_printf (_("Failed to launch '%s'"), shell_app_get_name (app));
shell_global_notify_error (shell_global_get (),
shell_global_notify_error (global,
msg,
error->message);
g_free (msg);
@ -473,7 +589,7 @@ shell_app_activate (ShellApp *app,
case SHELL_APP_STATE_STARTING:
break;
case SHELL_APP_STATE_RUNNING:
shell_app_activate_window (app, NULL, shell_global_get_current_time (shell_global_get ()));
shell_app_activate_window (app, NULL, timestamp);
break;
}
}
@ -489,6 +605,8 @@ void
shell_app_open_new_window (ShellApp *app,
int workspace)
{
g_return_if_fail (app->entry != NULL);
/* Here we just always launch the application again, even if we know
* it was already running. For most applications this
* should have the effect of creating a new window, whether that's
@ -497,12 +615,12 @@ shell_app_open_new_window (ShellApp *app,
* as say Pidgin. Ideally, we have the application express to us
* that it supports an explicit new-window action.
*/
shell_app_info_launch_full (app->info,
0,
NULL,
workspace,
NULL,
NULL);
shell_app_launch (app,
0,
NULL,
workspace,
NULL,
NULL);
}
/**
@ -517,17 +635,6 @@ shell_app_get_state (ShellApp *app)
return app->state;
}
/**
* _shell_app_get_info:
*
* Returns: (transfer none): Associated app info
*/
ShellAppInfo *
_shell_app_get_info (ShellApp *app)
{
return app->info;
}
typedef struct {
ShellApp *app;
MetaWorkspace *active_workspace;
@ -697,21 +804,22 @@ _shell_app_new_for_window (MetaWindow *window)
ShellApp *app;
app = g_object_new (SHELL_TYPE_APP, NULL);
app->info = shell_app_system_create_from_window (shell_app_system_get_default (), window);
_shell_app_system_register_app (shell_app_system_get_default (), app);
app->window_id_string = g_strdup_printf ("window:%d", meta_window_get_stable_sequence (window));
_shell_app_add_window (app, window);
return app;
}
ShellApp *
_shell_app_new (ShellAppInfo *info)
_shell_app_new (GMenuTreeEntry *info)
{
ShellApp *app;
app = g_object_new (SHELL_TYPE_APP, NULL);
app->info = shell_app_info_ref (info);
_shell_app_system_register_app (shell_app_system_get_default (), app);
app->entry = gmenu_tree_item_ref (info);
app->name_collation_key = g_utf8_collate_key (shell_app_get_name (app), -1);
return app;
}
@ -922,6 +1030,112 @@ shell_app_request_quit (ShellApp *app)
return TRUE;
}
static void
_gather_pid_callback (GDesktopAppInfo *gapp,
GPid pid,
gpointer data)
{
ShellApp *app;
ShellWindowTracker *tracker;
g_return_if_fail (data != NULL);
app = SHELL_APP (data);
tracker = shell_window_tracker_get_default ();
_shell_window_tracker_add_child_process_app (tracker,
pid,
app);
}
/**
* shell_app_launch:
* @timestamp: Event timestamp, or 0 for current event timestamp
* @uris: (element-type utf8): List of uris to pass to application
* @workspace: Start on this workspace, or -1 for default
* @startup_id: (out): Returned startup notification ID, or %NULL if none
* @error: A #GError
*/
gboolean
shell_app_launch (ShellApp *app,
guint timestamp,
GList *uris,
int workspace,
char **startup_id,
GError **error)
{
GDesktopAppInfo *gapp;
GdkAppLaunchContext *context;
gboolean ret;
ShellGlobal *global;
MetaScreen *screen;
if (startup_id)
*startup_id = NULL;
if (app->entry == NULL)
{
MetaWindow *window = window_backed_app_get_window (app);
/* We can't pass URIs into a window; shouldn't hit this
* code path. If we do, fix the caller to disallow it.
*/
g_return_val_if_fail (uris == NULL, TRUE);
meta_window_activate (window, timestamp);
return TRUE;
}
global = shell_global_get ();
screen = shell_global_get_screen (global);
if (timestamp == 0)
timestamp = shell_global_get_current_time (global);
if (workspace < 0)
workspace = meta_screen_get_active_workspace_index (screen);
context = gdk_app_launch_context_new ();
gdk_app_launch_context_set_timestamp (context, timestamp);
gdk_app_launch_context_set_desktop (context, workspace);
gapp = gmenu_tree_entry_get_app_info (app->entry);
ret = g_desktop_app_info_launch_uris_as_manager (gapp, uris,
G_APP_LAUNCH_CONTEXT (context),
G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
NULL, NULL,
_gather_pid_callback, app,
error);
g_object_unref (context);
return ret;
}
/**
* shell_app_get_app_info:
* @app: a #ShellApp
*
* Returns: (transfer none): The #GDesktopAppInfo for this app, or %NULL if backed by a window
*/
GDesktopAppInfo *
shell_app_get_app_info (ShellApp *app)
{
if (app->entry)
return gmenu_tree_entry_get_app_info (app->entry);
return NULL;
}
/**
* shell_app_get_tree_entry:
* @app: a #ShellApp
*
* Returns: (transfer none): The #GMenuTreeEntry for this app, or %NULL if backed by a window
*/
GMenuTreeEntry *
shell_app_get_tree_entry (ShellApp *app)
{
return app->entry;
}
static void
create_running_state (ShellApp *app)
{
@ -951,6 +1165,157 @@ unref_running_state (ShellAppRunningState *state)
g_slice_free (ShellAppRunningState, state);
}
static char *
trim_exec_line (const char *str)
{
const char *start, *end, *pos;
end = strchr (str, ' ');
if (end == NULL)
end = str + strlen (str);
start = str;
while ((pos = strchr (start, '/')) && pos < end)
start = ++pos;
return g_strndup (start, end - start);
}
static void
shell_app_init_search_data (ShellApp *app)
{
const char *name;
const char *exec;
const char *comment;
char *normalized_exec;
GDesktopAppInfo *appinfo;
appinfo = gmenu_tree_entry_get_app_info (app->entry);
name = g_app_info_get_name (G_APP_INFO (appinfo));
app->casefolded_name = shell_util_normalize_and_casefold (name);
comment = g_app_info_get_description (G_APP_INFO (appinfo));
app->casefolded_description = shell_util_normalize_and_casefold (comment);
exec = g_app_info_get_executable (G_APP_INFO (appinfo));
normalized_exec = shell_util_normalize_and_casefold (exec);
app->casefolded_exec = trim_exec_line (normalized_exec);
g_free (normalized_exec);
}
/**
* shell_app_compare_by_name:
* @app:
* @other:
*
* Order two applications by name.
*
* Returns: -1, 0, or 1; suitable for use as a comparison function for e.g. g_slist_sort()
*/
int
shell_app_compare_by_name (ShellApp *app, ShellApp *other)
{
return strcmp (app->name_collation_key, other->name_collation_key);
}
static ShellAppSearchMatch
_shell_app_match_search_terms (ShellApp *app,
GSList *terms)
{
GSList *iter;
ShellAppSearchMatch match;
if (G_UNLIKELY (!app->casefolded_name))
shell_app_init_search_data (app);
match = MATCH_NONE;
for (iter = terms; iter; iter = iter->next)
{
ShellAppSearchMatch current_match;
const char *term = iter->data;
const char *p;
current_match = MATCH_NONE;
p = strstr (app->casefolded_name, term);
if (p == app->casefolded_name)
current_match = MATCH_PREFIX;
else if (p != NULL)
current_match = MATCH_SUBSTRING;
p = strstr (app->casefolded_exec, term);
if (p != NULL)
{
if (p == app->casefolded_exec)
current_match = (current_match == MATCH_NONE) ? MATCH_PREFIX
: MATCH_MULTIPLE_PREFIX;
else if (current_match < MATCH_PREFIX)
current_match = (current_match == MATCH_NONE) ? MATCH_SUBSTRING
: MATCH_MULTIPLE_SUBSTRING;
}
if (app->casefolded_description && current_match < MATCH_PREFIX)
{
/* Only do substring matches, as prefix matches are not meaningful
* enough for descriptions
*/
p = strstr (app->casefolded_description, term);
if (p != NULL)
current_match = (current_match == MATCH_NONE) ? MATCH_SUBSTRING
: MATCH_MULTIPLE_SUBSTRING;
}
if (current_match == MATCH_NONE)
return current_match;
if (current_match > match)
match = current_match;
}
return match;
}
void
_shell_app_do_match (ShellApp *app,
GSList *terms,
GSList **multiple_prefix_results,
GSList **prefix_results,
GSList **multiple_substring_results,
GSList **substring_results)
{
ShellAppSearchMatch match;
GAppInfo *appinfo;
g_assert (app != NULL);
/* Skip window-backed apps */
appinfo = (GAppInfo*)shell_app_get_app_info (app);
if (appinfo == NULL)
return;
/* Skip not-visible apps */
if (!g_app_info_should_show (appinfo))
return;
match = _shell_app_match_search_terms (app, terms);
switch (match)
{
case MATCH_NONE:
break;
case MATCH_MULTIPLE_PREFIX:
*multiple_prefix_results = g_slist_prepend (*multiple_prefix_results, app);
break;
case MATCH_PREFIX:
*prefix_results = g_slist_prepend (*prefix_results, app);
break;
case MATCH_MULTIPLE_SUBSTRING:
*multiple_substring_results = g_slist_prepend (*multiple_substring_results, app);
break;
case MATCH_SUBSTRING:
*substring_results = g_slist_prepend (*substring_results, app);
break;
}
}
static void
shell_app_init (ShellApp *self)
{
@ -962,10 +1327,10 @@ shell_app_dispose (GObject *object)
{
ShellApp *app = SHELL_APP (object);
if (app->info)
if (app->entry)
{
shell_app_info_unref (app->info);
app->info = NULL;
gmenu_tree_item_unref (app->entry);
app->entry = NULL;
}
if (app->running_state)
@ -977,6 +1342,20 @@ shell_app_dispose (GObject *object)
G_OBJECT_CLASS(shell_app_parent_class)->dispose (object);
}
static void
shell_app_finalize (GObject *object)
{
ShellApp *app = SHELL_APP (object);
g_free (app->window_id_string);
g_free (app->casefolded_name);
g_free (app->name_collation_key);
g_free (app->casefolded_description);
G_OBJECT_CLASS(shell_app_parent_class)->finalize (object);
}
static void
shell_app_class_init(ShellAppClass *klass)
{
@ -984,6 +1363,7 @@ shell_app_class_init(ShellAppClass *klass)
gobject_class->get_property = shell_app_get_property;
gobject_class->dispose = shell_app_dispose;
gobject_class->finalize = shell_app_finalize;
shell_app_signals[WINDOWS_CHANGED] = g_signal_new ("windows-changed",
SHELL_TYPE_APP,

View File

@ -5,6 +5,8 @@
#include <clutter/clutter.h>
#include <gio/gio.h>
#include <meta/window.h>
#define GMENU_I_KNOW_THIS_IS_UNSTABLE
#include <gmenu-tree.h>
G_BEGIN_DECLS
@ -34,17 +36,22 @@ typedef enum {
GType shell_app_get_type (void) G_GNUC_CONST;
const char *shell_app_get_id (ShellApp *app);
GMenuTreeEntry *shell_app_get_tree_entry (ShellApp *app);
GDesktopAppInfo *shell_app_get_app_info (ShellApp *app);
ClutterActor *shell_app_create_icon_texture (ShellApp *app, float size);
ClutterActor *shell_app_get_faded_icon (ShellApp *app, float size);
char *shell_app_get_name (ShellApp *app);
char *shell_app_get_description (ShellApp *app);
gboolean shell_app_is_transient (ShellApp *app);
ClutterActor *shell_app_create_icon_texture (ShellApp *app, int size);
ClutterActor *shell_app_get_faded_icon (ShellApp *app, int size);
const char *shell_app_get_name (ShellApp *app);
const char *shell_app_get_description (ShellApp *app);
gboolean shell_app_is_window_backed (ShellApp *app);
void shell_app_activate_window (ShellApp *app, MetaWindow *window, guint32 timestamp);
void shell_app_activate (ShellApp *app,
int workspace);
void shell_app_activate (ShellApp *app);
void shell_app_activate_full (ShellApp *app,
int workspace,
guint32 timestamp);
void shell_app_open_new_window (ShellApp *app,
int workspace);
@ -61,6 +68,15 @@ GSList *shell_app_get_pids (ShellApp *app);
gboolean shell_app_is_on_workspace (ShellApp *app, MetaWorkspace *workspace);
gboolean shell_app_launch (ShellApp *app,
guint timestamp,
GList *uris,
int workspace,
char **startup_id,
GError **error);
int shell_app_compare_by_name (ShellApp *app, ShellApp *other);
int shell_app_compare (ShellApp *app, ShellApp *other);
G_END_DECLS

353
src/shell-contact-system.c Normal file
View File

@ -0,0 +1,353 @@
/* This implements a complete suite for caching and searching contacts in the
* Shell. We retrieve contacts from libfolks asynchronously and we search
* these for display to the user. */
#include "shell-contact-system.h"
#include <glib.h>
#include <glib/gprintf.h>
#include <gee.h>
#include <clutter/clutter.h>
#include <folks/folks.h>
#include "shell-global.h"
#include "shell-util.h"
#include "st.h"
G_DEFINE_TYPE (ShellContactSystem, shell_contact_system, G_TYPE_OBJECT);
#define ALIAS_PREFIX_MATCH_WEIGHT 100
#define ALIAS_SUBSTRING_MATCH_WEIGHT 90
#define IM_PREFIX_MATCH_WEIGHT 10
#define IM_SUBSTRING_MATCH_WEIGHT 5
/* Callbacks */
static void
prepare_individual_aggregator_cb (GObject *obj,
GAsyncResult *res,
gpointer user_data)
{
FolksIndividualAggregator *aggregator = FOLKS_INDIVIDUAL_AGGREGATOR (obj);
folks_individual_aggregator_prepare_finish (aggregator, res, NULL);
}
/* Internal stuff */
typedef struct {
gchar *key;
guint weight;
} ContactSearchResult;
struct _ShellContactSystemPrivate {
FolksIndividualAggregator *aggregator;
};
static void
shell_contact_system_constructed (GObject *obj)
{
ShellContactSystem *self = SHELL_CONTACT_SYSTEM (obj);
G_OBJECT_CLASS (shell_contact_system_parent_class)->constructed (obj);
/* We intentionally do not care about the "individuals-changed" signal, as
* we don't intend to update searches after they've been performed.
* Therefore, we will simply retrieve the "individuals" property which
* represents a snapshot of the individuals in the aggregator.
*/
self->priv->aggregator = folks_individual_aggregator_new ();
folks_individual_aggregator_prepare (self->priv->aggregator, prepare_individual_aggregator_cb, NULL);
}
static void
shell_contact_system_finalize (GObject *obj)
{
ShellContactSystem *self = SHELL_CONTACT_SYSTEM (obj);
g_object_unref (self->priv->aggregator);
G_OBJECT_CLASS (shell_contact_system_parent_class)->finalize (obj);
}
static void
shell_contact_system_init (ShellContactSystem *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, SHELL_TYPE_CONTACT_SYSTEM, ShellContactSystemPrivate);
}
static void
shell_contact_system_class_init (ShellContactSystemClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = shell_contact_system_constructed;
object_class->finalize = shell_contact_system_finalize;
g_type_class_add_private (object_class, sizeof (ShellContactSystemPrivate));
}
/**
* normalize_terms:
* @terms: (element-type utf8): Input search terms
*
* Returns: (element-type utf8) (transfer full): Unicode-normalized and lowercased terms
*/
static GSList *
normalize_terms (GSList *terms)
{
GSList *normalized_terms = NULL;
GSList *iter;
for (iter = terms; iter; iter = iter->next)
{
const char *term = iter->data;
normalized_terms = g_slist_prepend (normalized_terms, shell_util_normalize_and_casefold (term));
}
return normalized_terms;
}
static guint
do_match (ShellContactSystem *self,
FolksIndividual *individual,
GSList *terms)
{
GSList *term_iter;
guint weight = 0;
char *alias = shell_util_normalize_and_casefold (folks_alias_details_get_alias (FOLKS_ALIAS_DETAILS (individual)));
GeeMultiMap *im_addr_map = folks_im_details_get_im_addresses (FOLKS_IM_DETAILS (individual));
GeeCollection *im_addrs = gee_multi_map_get_values (im_addr_map);
GeeIterator *im_addrs_iter;
gboolean have_alias_prefix = FALSE;
gboolean have_alias_substring = FALSE;
gboolean have_im_prefix = FALSE;
gboolean have_im_substring = FALSE;
for (term_iter = terms; term_iter; term_iter = term_iter->next)
{
const char *term = term_iter->data;
const char *p;
/* Match on alias */
if (alias != NULL)
{
p = strstr (alias, term);
if (p == alias)
have_alias_prefix = TRUE;
else if (p != NULL)
have_alias_substring = TRUE;
}
/* Match on one or more IM addresses */
im_addrs_iter = gee_iterable_iterator (GEE_ITERABLE (im_addrs));
while (gee_iterator_next (im_addrs_iter))
{
const gchar *addr = gee_iterator_get (im_addrs_iter);
p = strstr (addr, term);
if (p == addr)
have_im_prefix = TRUE;
else if (p != NULL)
have_im_substring = TRUE;
}
g_object_unref (im_addrs_iter);
}
if (have_alias_prefix)
weight += ALIAS_PREFIX_MATCH_WEIGHT;
else if (have_alias_substring)
weight += ALIAS_SUBSTRING_MATCH_WEIGHT;
if (have_im_prefix)
weight += IM_PREFIX_MATCH_WEIGHT;
else if (have_im_substring)
weight += IM_SUBSTRING_MATCH_WEIGHT;
g_free (alias);
g_object_unref (im_addrs);
return weight;
}
static gint
compare_results (gconstpointer a,
gconstpointer b)
{
ContactSearchResult *first = (ContactSearchResult *) a;
ContactSearchResult *second = (ContactSearchResult *) b;
if (first->weight > second->weight)
return 1;
else if (first->weight < second->weight)
return -1;
else
return 0;
}
static void
free_result (gpointer data,
gpointer user_data)
{
g_slice_free (ContactSearchResult, data);
}
/* modifies and frees @results */
static GSList *
sort_and_prepare_results (GSList *results)
{
GSList *iter;
GSList *sorted_results = NULL;
results = g_slist_sort (results, compare_results);
for (iter = results; iter; iter = iter->next)
{
ContactSearchResult *result = iter->data;
gchar *id = result->key;
sorted_results = g_slist_prepend (sorted_results, id);
}
g_slist_foreach (results, (GFunc) free_result, NULL);
return sorted_results;
}
/* Methods */
/**
* shell_contact_system_get_default:
*
* Return Value: (transfer none): The global #ShellContactSystem singleton
*/
ShellContactSystem *
shell_contact_system_get_default (void)
{
static ShellContactSystem *instance = NULL;
if (instance == NULL)
instance = g_object_new (SHELL_TYPE_CONTACT_SYSTEM, NULL);
return instance;
}
/**
* shell_contact_system_get_all:
* @self: A #ShellContactSystem
*
* Returns: (transfer none): All individuals
*/
GeeMap *
shell_contact_system_get_all (ShellContactSystem *self)
{
GeeMap *individuals;
g_return_val_if_fail (SHELL_IS_CONTACT_SYSTEM (self), NULL);
individuals = folks_individual_aggregator_get_individuals (self->priv->aggregator);
return individuals;
}
/**
* shell_contact_system_get_individual:
* @self: A #ShellContactSystem
* @id: A #gchar with the ID of the FolksIndividual to be returned.
*
* Returns: (transfer full): A #FolksIndividual or NULL if @id could not be found.
*/
FolksIndividual *
shell_contact_system_get_individual (ShellContactSystem *self,
gchar *id)
{
GeeMap *individuals;
gpointer key, value;
key = (gpointer) id;
g_return_val_if_fail (SHELL_IS_CONTACT_SYSTEM (self), NULL);
individuals = folks_individual_aggregator_get_individuals (self->priv->aggregator);
value = gee_map_get (individuals, key);
return FOLKS_INDIVIDUAL (value);
}
/**
* shell_contact_system_initial_search:
* @shell: A #ShellContactSystem
* @terms: (element-type utf8): List of terms, logical AND
*
* Search through contacts for the given search terms.
*
* Returns: (transfer container) (element-type utf8): List of contact
* identifiers
*/
GSList *
shell_contact_system_initial_search (ShellContactSystem *self,
GSList *terms)
{
FolksIndividual *individual;
GSList *results = NULL;
GeeMap *individuals = NULL;
ContactSearchResult *result;
GeeMapIterator *iter;
gpointer key;
guint weight;
GSList *normalized_terms = normalize_terms (terms);
g_return_val_if_fail (SHELL_IS_CONTACT_SYSTEM (self), NULL);
individuals = folks_individual_aggregator_get_individuals (self->priv->aggregator);
iter = gee_map_map_iterator (individuals);
while (gee_map_iterator_next (iter))
{
individual = gee_map_iterator_get_value (iter);
weight = do_match (self, individual, normalized_terms);
if (weight != 0)
{
key = gee_map_iterator_get_key (iter);
result = g_slice_new (ContactSearchResult);
result->key = (gchar *) key;
result->weight = weight;
results = g_slist_append (results, result);
}
g_object_unref (individual);
}
return sort_and_prepare_results (results);
}
/**
* shell_contact_system_subsearch:
* @shell: A #ShellContactSystem
* @previous_results: (element-type utf8): List of previous results
* @terms: (element-type utf8): List of terms, logical AND
*
* Search through a previous result set; for more information see
* js/ui/search.js.
*
* Returns: (transfer container) (element-type utf8): List of contact
* identifiers
*/
GSList *
shell_contact_system_subsearch (ShellContactSystem *self,
GSList *previous_results,
GSList *terms)
{
return shell_contact_system_initial_search (self, terms);
}

View File

@ -0,0 +1,50 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef __SHELL_CONTACT_SYSTEM_H__
#define __SHELL_CONTACT_SYSTEM_H__
#include <clutter/clutter.h>
#include <gio/gio.h>
#include <folks/folks.h>
#define SHELL_TYPE_CONTACT_SYSTEM (shell_contact_system_get_type ())
#define SHELL_CONTACT_SYSTEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_CONTACT_SYSTEM, ShellContactSystem))
#define SHELL_CONTACT_SYSTEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_CONTACT_SYSTEM, ShellContactSystemClass))
#define SHELL_IS_CONTACT_SYSTEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_CONTACT_SYSTEM))
#define SHELL_IS_CONTACT_SYSTEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_CONTACT_SYSTEM))
#define SHELL_CONTACT_SYSTEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_CONTACT_SYSTEM, ShellContactSystemClass))
typedef struct _ShellContactSystem ShellContactSystem;
typedef struct _ShellContactSystemClass ShellContactSystemClass;
typedef struct _ShellContactSystemPrivate ShellContactSystemPrivate;
struct _ShellContactSystem
{
GObject parent;
ShellContactSystemPrivate *priv;
};
struct _ShellContactSystemClass
{
GObjectClass parent_class;
};
GType shell_contact_system_get_type (void) G_GNUC_CONST;
/* Methods */
ShellContactSystem * shell_contact_system_get_default (void);
GeeMap *shell_contact_system_get_all (ShellContactSystem *self);
FolksIndividual *shell_contact_system_get_individual (ShellContactSystem *self,
gchar *id);
GSList * shell_contact_system_initial_search (ShellContactSystem *shell,
GSList *terms);
GSList * shell_contact_system_subsearch (ShellContactSystem *shell,
GSList *previous_results,
GSList *terms);
#endif /* __SHELL_CONTACT_SYSTEM_H__ */

View File

@ -247,34 +247,6 @@ shell_generic_container_finalize (GObject *object)
G_OBJECT_CLASS (shell_generic_container_parent_class)->finalize (object);
}
/* Based on implementation from clutter-group.c */
static gboolean
shell_generic_container_get_paint_volume (ClutterActor *actor,
ClutterPaintVolume *volume)
{
GList *l, *children;
children = st_container_get_children_list (ST_CONTAINER (actor));
CLUTTER_ACTOR_CLASS (shell_generic_container_parent_class)->get_paint_volume (actor, volume);
for (l = children; l != NULL; l = l->next)
{
ClutterActor *child = l->data;
const ClutterPaintVolume *child_volume;
/* This gets the paint volume of the child transformed into the
* group's coordinate space... */
child_volume = clutter_actor_get_transformed_paint_volume (child, actor);
if (!child_volume)
return FALSE;
clutter_paint_volume_union (volume, child_volume);
}
return TRUE;
}
static void
shell_generic_container_class_init (ShellGenericContainerClass *klass)
{
@ -287,7 +259,6 @@ shell_generic_container_class_init (ShellGenericContainerClass *klass)
actor_class->get_preferred_width = shell_generic_container_get_preferred_width;
actor_class->get_preferred_height = shell_generic_container_get_preferred_height;
actor_class->allocate = shell_generic_container_allocate;
actor_class->get_paint_volume = shell_generic_container_get_paint_volume;
actor_class->paint = shell_generic_container_paint;
actor_class->pick = shell_generic_container_pick;

View File

@ -6,6 +6,8 @@
#include <gjs/gjs.h>
void _shell_global_init (const char *first_property_name,
...);
void _shell_global_set_plugin (ShellGlobal *global,
MetaPlugin *plugin);
@ -13,4 +15,22 @@ GjsContext *_shell_global_get_gjs_context (ShellGlobal *global);
gboolean _shell_global_check_xdnd_event (ShellGlobal *global,
XEvent *xev);
void _shell_global_set_session_type (ShellGlobal *global,
ShellSessionType session_type);
/* Used for async screenshot grabbing */
typedef struct _screenshot_data {
ShellGlobal *global;
char *filename;
int x;
int y;
int width;
int height;
ShellGlobalScreenshotCallback callback;
} _screenshot_data;
#endif /* __SHELL_GLOBAL_PRIVATE_H__ */

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