Compare commits

...

162 Commits

Author SHA1 Message Date
d139adc25f authPrompt: support smartcard authentication
This commit detects when a user inserts a smartcard,
and then initiates user verification using the gdm-smartcard
PAM service.

Likewise, if a user removes their smartcard, password verification
(or the user list depending on auth mode and configuration) are initiated

https://bugzilla.gnome.org/show_bug.cgi?id=683437
2013-07-29 20:21:06 -04:00
0d151f8d94 misc: add code to use settings-daemon smartcard service
gnome-settings-daemon monitors smartcard insertion and removal
events on the system and then exports a model of the current
smartcard topology over the bus using the D-Bus ObjectManager interface.

This commit adds the support code needed in gnome-shell to talk to
the gnome-settings-daemon service.

A future commit will use this code to inform the login screen
when a user inserts a smartcard (so it can react appropriately)

https://bugzilla.gnome.org/show_bug.cgi?id=683437
2013-07-29 20:21:06 -04:00
e3fdc2858d misc: add objectManager class
The D-Bus ObjectManager interface is fairly recent addition to the
D-Bus specification. Its purpose is to provide a standardized way
to track objects dynamically coming and going for a service, and
to track capabilities dynamically coming and going for those objects
(by means of interfaces).

This commit adds the requisite code needed to make use of the
ObjectManager interface.

It will ultimately be needed to implement smartcard support in the
login screen.

https://bugzilla.gnome.org/show_bug.cgi?id=683437
2013-07-29 20:21:06 -04:00
05dcd8575c gdmUtil: support disabling password authentication
This commit skips trying password authentication if it's
disallowed, favoring fingerprint login instead.

https://bugzilla.gnome.org/show_bug.cgi?id=683437
2013-07-29 20:21:06 -04:00
214440fde4 authPrompt: emit prompted when given a message
Some pam modules prompt without expecting the user to type
an answer back (e.g. "Please swipe finger").  We need to
emit prompted in this case too, so the the dialog will get shown.

https://bugzilla.gnome.org/show_bug.cgi?id=683437
2013-07-29 20:21:06 -04:00
e3621e13ac gdmUtil: pave way for fingeprint to optionally be default auth service
Currently, fingerprint authentication is always a secondary thing.
If a user wants to swipe their finger when the computer is asking
for a password, so be it.

This commit paves the way for making fingerprint auth optionally
be the main way to authenticate.  Currently there's no way to enable
this, but in a future commit will honor

enable-password-authentication=false

in gsettings.

https://bugzilla.gnome.org/show_bug.cgi?id=683437
2013-07-29 20:21:03 -04:00
df0aace025 util: abstract out default auth service in code
Right now, the primary way a user logs in is with
a password. They can also swipe their finger, if their
fingerprint is enrolled, but it's expected the fingerprint
auth service won't ask questions the user has to respond to
by typing. As such, we ignore questions that comes from
anything but the main auth service: gdm-password.

In the future, if a user inserts a smartcard, we'll want
to treat the gdm-smartcard service as the main auth service,
and let any questions from it get to the user.

This commit tries to prepare for that eventuality by storing
the name of the default auth service away in a _defaultService variable
before verification has begun, and then later checking incoming
queries against that service instead of checking against
string 'gdm-password' directly.

Of course, right now, _defaultService is always gdm-password.

https://bugzilla.gnome.org/show_bug.cgi?id=683437
2013-07-29 18:54:20 -04:00
023f4b31d9 authPrompt: add support for auth without username
This commit introduces a new BeginRequestType enum which gets
passed to the 'reset' signal to specify whether
a username should be provided to the begin() method and changes
the loginDialog to comply.

Currently, the signal only ever gets emitted with

AuthPrompt.BeginRequestType.PROVIDE_USERNAME

but that will change in the future when providing smartcard
support.

https://bugzilla.gnome.org/show_bug.cgi?id=683437
2013-07-29 18:52:25 -04:00
16fba7bd5f unlockDialog: only emit 'failed' on reset after failure/cancel
We currently emit "failed" any time the UserVerifier is reset,
and user verification didn't succeed prior.

A more conceptually clear time to emit "failed" would be if
the UserVerifier is reset and user verification failed prior,
and to emit "failed" if the user cancels unlock.

This commit restructures things to do that. Aside from being
more conceptually clear, it also lays the groundwork for us
to be able to reset the unlock screen without failing.

https://bugzilla.gnome.org/show_bug.cgi?id=683437
2013-07-29 17:40:29 -04:00
ebce362c05 authPrompt: cancel user verification if verifying when reset
authPrompt.reset() currently only leaves the authPrompt in a
sane state if the user isn't verifying.

This commit makes sure to cancel verification if a reset happens
while verification is in process.

https://bugzilla.gnome.org/show_bug.cgi?id=683437
2013-07-29 17:40:29 -04:00
c4ca17d127 util: clear user verifier after cancelling it
If we don't clear it, then the connection to gdm will remain open.

https://bugzilla.gnome.org/show_bug.cgi?id=683437
2013-07-29 17:40:29 -04:00
3455dd1e76 authPrompt: consolidate verifyingUser/userVerified
Right now we have two booleans that specify when user verification
is happening and when it succeeded, respectively.

This commit consolidates them into one AuthPromptStatus enumeration.

This clean up will allow us to check for verification failure more
easily.

https://bugzilla.gnome.org/show_bug.cgi?id=683437
2013-07-29 17:40:29 -04:00
57abfab923 loginDialog: don't ever call _reset directly
The only time we ever call _reset directly is when
detecting changes to disable-user-list.  We can implicitly
trigger a reset for this case, just as easily by calling
this._authPrompt.reset()

This commit makes that change for consistency and to make
it easier to adjust the authprompt workflow later.

https://bugzilla.gnome.org/show_bug.cgi?id=683437
2013-07-29 17:40:29 -04:00
3a13e7484a authPrompt: don't muck with cancelButton in onAskQuestion
onAskQuestion has this code:

    if (this.verifyingUser)
        this.cancelButton.show();
    else
        this.cancelButton.hide();

but onAskQuestion can only be called when this.verifyingUser is true.
Also, cancelButton is public, and it only ever otherwise gets hidden
from callers.

This commit drops mucking with cancelButton visibility, leaving it
entirely up to the callers to deal with.

https://bugzilla.gnome.org/show_bug.cgi?id=683437
2013-07-29 17:40:29 -04:00
a04895383a authPrompt: disassociate from userVerifier when destroyed
Otherwise, it won't get GC'd and we'll end up potentially calling
its signal handlers after destruction.

https://bugzilla.gnome.org/show_bug.cgi?id=683437
2013-07-29 17:40:29 -04:00
43357a1b69 loginDialog: fix up cancel button visibility
This commit makes sure we hide when there's nothing to cancel
to and show it when there's something to cancel to.

https://bugzilla.gnome.org/show_bug.cgi?id=683437
2013-07-29 17:40:29 -04:00
f38d5a9c06 authPrompt: let PAM message wrap if longer than entry
Right now the whole authPrompt spreads out if a PAM message
comes in that longer than the entry.

This commit changes it to wrap instead, by forcing the
auth prompt to be a fixed width (slightly bigger than
the entry width was sized to previously).

https://bugzilla.gnome.org/show_bug.cgi?id=705037
2013-07-29 17:25:41 -04:00
696efcdf20 Tajik translation updated 2013-07-29 11:22:53 +01:00
728c3a72ad network: Introduce a new, rewritten Wi-Fi section
Remove the Wi-Fi chooser from the menu and put it in a dialog instead.
This frees up the submenu to simply have three items: an rfkill toggle,
a button to show the dialog, and a button to show network settings.

Ideally, we'd autodetect the "needs network" case by user initiation
and automatically show the dialog if needed, but lower-level plumbing
is neccessary, so the menu item to show the dialog is an acceptable
compromise instead.

This is a part of the new system status design, see
https://wiki.gnome.org/GnomeShell/Design/Guidelines/SystemStatus/
for design details.

https://bugzilla.gnome.org/show_bug.cgi?id=704670
2013-07-28 15:52:33 -04:00
c8c839a569 network: Make sure that the network menu is insensitive when in the lock screen
Since the network section of the aggregate menu will be shown in the lock
screen, we need to ensure that users can't tweak with network settings or
anything like that.

https://bugzilla.gnome.org/show_bug.cgi?id=704670
2013-07-28 15:40:24 -04:00
6827dacb4b network: Remove syncDescription
Replace it with setDeviceDescription, which gives device wrappers
more control about how to handle description changes.

https://bugzilla.gnome.org/show_bug.cgi?id=704670
2013-07-28 15:40:24 -04:00
246271fd9d network: Implement new designs for VPN/modem submenus
This is a part of the new system status design, see
https://wiki.gnome.org/GnomeShell/Design/Guidelines/SystemStatus/
for design details.

https://bugzilla.gnome.org/show_bug.cgi?id=704670
2013-07-28 15:40:24 -04:00
4305ebc5b0 network: Rework the VPN rewrite to work for modem and bluetooth as well
Replace NMNetworkMenuItem with NMConnectionItem, based on
NMVPNConnectionItem, and replace NMDevice with NMConnectionSection
and NMConnectionDevice.

Since this rips apart NMDevice, and since wi-fi should not be
connection-based, we'll temporarily remove NMDeviceWireless. We'll
add it back in a later commit, along with the new Wi-Fi dialog.

https://bugzilla.gnome.org/show_bug.cgi?id=704670
2013-07-28 15:40:23 -04:00
39bff8bfbc network: Don't pass a list of connections to the devices
Instead, just add them after they're constructed. This allows us to
not have to pass the connections to each device, and prevents issues
with having to enumerate the connections in the middle of construction.

https://bugzilla.gnome.org/show_bug.cgi?id=704670
2013-07-28 15:39:40 -04:00
fc6a0c1006 network: Remove the Wired section
This is a part of the new system status design, see
https://wiki.gnome.org/GnomeShell/Design/Guidelines/SystemStatus/
for design details.

Note that this does have an interesting side effect of not showing
network connectivity status on wired. This is intentional, and error
states will still be shown in the top bar when they happen.

This also means that if you're connected to both wired and wireless,
even though wired is the default route, we'll first notice the wireless
active connection, and we'll show that in the top bar. New NM API that
will help figuring out the active connection of the default device is
being implemented to stop this from happening.

https://bugzilla.gnome.org/show_bug.cgi?id=704670
2013-07-28 15:39:40 -04:00
d6dc9af5ff network: Remove overflow implementation
The code is complicated by requiring overflow, and in order to incrementally
improve the code to match the designs, remove overflow.

In the new design, we'll have a fixed number of menu items, and Wi-Fi
will be done by a separate design, so we can't be too concerned with
the menu not fitting on the screen.

This is a part of the new system status design, see
https://wiki.gnome.org/GnomeShell/Design/Guidelines/SystemStatus/
for design details.

https://bugzilla.gnome.org/show_bug.cgi?id=704670
2013-07-28 15:39:40 -04:00
e62045eb7f network: Remove the enable networking item
This is a part of the new system status design, see
https://wiki.gnome.org/GnomeShell/Design/Guidelines/SystemStatus/
for design details.

Additionally, with this gone, we can clean up how we handle menu
item visibility.

https://bugzilla.gnome.org/show_bug.cgi?id=704670
2013-07-28 15:39:40 -04:00
bda3e53511 network: Remove the global wireless killswitch
"Airplane Mode" is able to be turned on in gnome-control-center,
and it will replace the killswitch UI we have in the menu right
now.

https://bugzilla.gnome.org/show_bug.cgi?id=704670
2013-07-28 15:39:39 -04:00
6295d0fc6c network: Make the device section headers not bold
The new device section headers will be simple text strings
in the new designs.

This is a part of the new system status design, see
https://wiki.gnome.org/GnomeShell/Design/Guidelines/SystemStatus/
for design details.

https://bugzilla.gnome.org/show_bug.cgi?id=704670
2013-07-28 15:39:39 -04:00
3271e65d56 network: Remove separators between device sections
This is not needed in the new design. Doing this allows us to
remove some complex section visibility tracking, also.

https://bugzilla.gnome.org/show_bug.cgi?id=704670
2013-07-28 15:39:39 -04:00
f8225141dc network: Remove the firmware changed signal
According to Dan Williams, if firmware is installed the device
will disappear and reappear, and this is unlikely to change any
time soon. Just make our lives easier by removing the tracking.

https://bugzilla.gnome.org/show_bug.cgi?id=704670
2013-07-28 15:39:39 -04:00
8e9e583b79 network: Fix the AP strength indicator never getting updated
I intended to make a few code cleanups, but I apparently forgot
to hook up _updateAccessPoint. Merge it with _activeApChanged,
which is where the notify::active-access-point signal is actually
hooked up to.

https://bugzilla.gnome.org/show_bug.cgi?id=704670
2013-07-28 15:39:39 -04:00
bbadfce65a Updated Czech translation 2013-07-28 13:12:08 +02:00
9e191d681f Updated Czech translation 2013-07-28 13:10:24 +02:00
a3236997be search: Don't throw if provider directories don't exist
There's no /usr/local/share/gnome-shell/search-providers, so don't
throw if we don't find it.
2013-07-27 10:58:36 -04:00
be961cd60e remoteSearch: Load remote search providers synchronously
As we only reload search providers on startup or when the sort order changes,
and given the small number of search providers we'll actually load, I doubt
we'll see any speed decrease.

The simplicity of synchronous code is also much clearer, and fully avoids
all the possible bugs about in-flight requests or similar.

This also prevents issues with multiple search providers showing up at once,
which happen when multiple requests to reload search providers get called
immediately, with the existing in-flight async requests never cancelled.

https://bugzilla.gnome.org/show_bug.cgi?id=700283
2013-07-26 19:14:40 -04:00
4efd363134 search: Ensure that we correctly remove remote search providers
When we reload the remote search providers, we currently try to remove
all remote providers, and then re-scan. It turns out that we sometimes
remove the wrong providers from the remote provider list, causing us to
have some providers not correctly unloaded.

https://bugzilla.gnome.org/show_bug.cgi?id=700283
2013-07-26 17:39:05 -04:00
7bdee9ce05 Tajik translation updated 2013-07-26 16:42:13 +01:00
7dc9a9bf2f bluetooth: Adapt to new designs for the bluetooth menu
This is a part of the new system status design, see
https://wiki.gnome.org/GnomeShell/Design/Guidelines/SystemStatus/
for design details.

https://bugzilla.gnome.org/show_bug.cgi?id=704368
2013-07-26 04:41:49 -04:00
87245c7b33 authPrompt: Call next button "Unlock" when user switching
When a ShellUserVerifier is asked to verify a user at the login
screen it will transparently first try to reauthenticate the user
against an existing session and then fall back to logging a user
into a new session.  The former is used for user switching.
It's useful to know which type of verification is happening, so
the next button can be made to say "Unlock" instead of "Sign In" when
a user is already signed in.

This commit exports a new "reauthenticating" property on the
ShellUserVerifier that the auth prompt checks when deciding which
label to use for its next button.

https://bugzilla.gnome.org/show_bug.cgi?id=704795
2013-07-25 09:28:22 -04:00
db497a2ecf shellDBus: Export the timestamp of shortcuts through D-Bus
So that apps launched through gnome-settings-daemon can get
focused properly.

https://bugzilla.gnome.org/show_bug.cgi?id=704859
2013-07-25 14:55:25 +02:00
9d250bec05 Updated German translation 2013-07-24 22:10:17 +02:00
1371f69810 Updated POTFILES.in 2013-07-24 20:16:16 +02:00
82ee6aed7f authPrompt: fade out message if user starts to type
If there are no messages in the queue and a user starts to
type then we can safely hide the message label since the
user has probably already read it.

This fixes a weirdness where "Incorrect Password" messages stay
around, even as the user types in the new correct password.

https://bugzilla.gnome.org/show_bug.cgi?id=704817
2013-07-24 13:22:10 -04:00
d730fd0a5f loginDialog: correct typo
commit 7e7295f259 introduced a typo
(LOGIN instead of LOG_IN).

The follow up commit fixes that.
2013-07-24 11:27:53 -04:00
cb4e4bb2db appDisplay: Use hookup_style() to bind app-view-controls spacing
With the monkey-patched ClutterBoxLayout, we no longer need this
code to hook up the 'spacing' property to CSS.

https://bugzilla.gnome.org/show_bug.cgi?id=703905
2013-07-24 15:22:05 +02:00
6fd3c0fbb4 environment: Add some convenience LayoutManager monkey-patching
Similar to our ClutterContainer monkey-patching, we can add some
convenience to existing ClutterLayoutManagers:

 - hookup_style() to bind layoutManager properties to CSS properties
 - child_set() to set child properties

https://bugzilla.gnome.org/show_bug.cgi?id=703905
2013-07-24 15:22:05 +02:00
13170fbb3c notificationDaemon: Fix clicks on legacy icons
Jasper removed the ShellGlobal:stage-input-mode property after its
"last" use was removed. Adapt the (hopefully) really last use of the
property to the recent input changes.

https://bugzilla.gnome.org/show_bug.cgi?id=704095
2013-07-24 15:22:05 +02:00
7e7295f259 authPrompt: move unlock and login user verifier code here
There's quite a bit of duplicated code between the login dialog
and the unlock dialog dealing with the various signals from the
ShellUserVerifier.

This commit moves that duplicated code into the AuthPrompt.

https://bugzilla.gnome.org/show_bug.cgi?id=704707
2013-07-24 06:01:12 -04:00
d30cb2d4d9 gdmUtil: separate AuthPrompt out into its own file
It's cleaner to have it in its own file than to cram it into
util.js, so this commit moves it.

https://bugzilla.gnome.org/show_bug.cgi?id=704707
2013-07-24 06:01:03 -04:00
f4100ac507 message-tray: Really close notifications when the close button is clicked
When the user eclipictly closes a notification, we should really destroy
it not just move it to the tray.

https://bugzilla.gnome.org/show_bug.cgi?id=687016
2013-07-23 19:32:51 +02:00
1a0415a100 Updated Spanish translation 2013-07-22 13:50:38 +02:00
1cc5bb5ec4 shell-app: Fade the app icon on the left in RTL layouts
The point of fading the icon is to make the text displayed over the
icon more legible. In RTL layouts, the text is displayed on the left
of the icon, so fading the right-hand-side of the icon doesn't work
well.

https://bugzilla.gnome.org/show_bug.cgi?id=704583
2013-07-22 07:09:42 -04:00
43661fff76 popupMenu: Fix popup menu layouts in RTL directions
The math for inverting RTL layouts (which was copy/pasted from
ClutterBoxLayout) was incorrect for non-zero child offsets.

https://bugzilla.gnome.org/show_bug.cgi?id=704542
2013-07-22 07:09:19 -04:00
509bdceac2 Updated Czech translation 2013-07-21 21:12:39 +02:00
835cb8caa5 Tajik translation updated 2013-07-21 11:23:25 +05:00
ed32adc9bb Updated POTFILES.in 2013-07-19 20:59:02 +02:00
44f8fecf84 slider: Explicitly grab the device that was clicked
It seems the Clutter bug mentioned has been fixed.

https://bugzilla.gnome.org/show_bug.cgi?id=704368
2013-07-19 05:36:02 -04:00
0cee0e08e9 slider: Make clicking anywhere on the slider menu item pass to the slider
This is a regression from splitting the slider out that never got fixed.
Restore the previous (useful) behavior by adding a public API to the
slider that lets us pass an event through.

https://bugzilla.gnome.org/show_bug.cgi?id=704368
2013-07-19 05:36:02 -04:00
73cd595b73 volume: Implement new volume menu design
This is a part of the new system status design, see
https://wiki.gnome.org/GnomeShell/Design/Guidelines/SystemStatus/
for design details.

Since the designs require that we have a custom layout for the slider
item, this means that the PopupSliderMenuItem is unused, so remove it
as well.

https://bugzilla.gnome.org/show_bug.cgi?id=704368
2013-07-19 05:35:54 -04:00
c50b23e9ca system: Use icons for actions at the bottom of the system menu
This is a part of the new system status design, see
https://wiki.gnome.org/GnomeShell/Design/Guidelines/SystemStatus/
for design details.

https://bugzilla.gnome.org/show_bug.cgi?id=704368
2013-07-19 05:35:39 -04:00
d1a763bc21 system: Move the Switch User and Log Out items to a new submenu
This is a part of the new system status design, see
https://wiki.gnome.org/GnomeShell/Design/Guidelines/SystemStatus/
for design details.

https://bugzilla.gnome.org/show_bug.cgi?id=704368
2013-07-19 05:35:32 -04:00
de8ca5c1b5 system: Remove Install Updates & Restart
This will eventually end up in a redesign of the "end session" dialog.

https://bugzilla.gnome.org/show_bug.cgi?id=704368
2013-07-19 05:35:20 -04:00
14372ba7f3 system: Remove suspend from the features of the system menu
This will eventually go up in a redesign of the "end session" dialog.

https://bugzilla.gnome.org/show_bug.cgi?id=704368
2013-07-19 05:35:02 -04:00
ca861d8ff9 status: Rebrand the user menu as the system menu
As the user menu no longer really shows anything about the user, and just
power menu and settings, it's not really deserving of the "user menu" name,
so rename it to the ever-blandly-named "system menu".

This is a part of the new system status design, see
https://wiki.gnome.org/GnomeShell/Design/Guidelines/SystemStatus/
for design details.

https://bugzilla.gnome.org/show_bug.cgi?id=704368
2013-07-19 05:34:44 -04:00
37a316e66c power: Implement new power menu design
This is a part of the new system status design, see
https://wiki.gnome.org/GnomeShell/Design/Guidelines/SystemStatus/
for design details.

https://bugzilla.gnome.org/show_bug.cgi?id=704368
2013-07-19 05:34:30 -04:00
22c8be6645 power: Use UPowerGlib for constants
This removes the need to define the constants from headers ourselves.

https://bugzilla.gnome.org/show_bug.cgi?id=704368
2013-07-19 05:34:17 -04:00
8cce1b4f6c power: Remove other devices
Simply have one section.

This is a part of the new system status design, see
https://wiki.gnome.org/GnomeShell/Design/Guidelines/SystemStatus/
for design details.

https://bugzilla.gnome.org/show_bug.cgi?id=704368
2013-07-19 05:34:17 -04:00
040bb9354a power: Restyle the power menu for the new design
Remove the icon next to devices, and make the percentage have the
"status" color.

This is a part of the new system status design, see
https://wiki.gnome.org/GnomeShell/Design/Guidelines/SystemStatus/
for design details.

https://bugzilla.gnome.org/show_bug.cgi?id=704368
2013-07-19 05:34:17 -04:00
fb0f9cd1a1 popupMenu: Add a status label and icon to submenu menu items
This will allow us to implement the new submenu designs in the
aggregate menu.

https://bugzilla.gnome.org/show_bug.cgi?id=704368
2013-07-19 05:34:17 -04:00
3816db03f5 popupMenu: Remove combo boxes and child menus
They're no longer used with the removal of the avatar widget.

https://bugzilla.gnome.org/show_bug.cgi?id=704368
2013-07-19 05:34:17 -04:00
d802416dae userMenu: Implement new user menu design
For now, turn it into another system status button with a simple
icon. We'll revamp this when we revamp how panel menus work in
general.

https://bugzilla.gnome.org/show_bug.cgi?id=704368
2013-07-19 05:34:17 -04:00
2cd41773ba status: Remove settings actions
These aren't used in the new system status designs.

This is a part of the new system status design, see
https://wiki.gnome.org/GnomeShell/Design/Guidelines/SystemStatus/
for design details.

https://bugzilla.gnome.org/show_bug.cgi?id=704368
2013-07-19 05:34:17 -04:00
8ce5b087bc userWidget: Rename the default style class of the user widget
As the status chooser is going away, it doesn't make sense to
keep the "status-chooser" name around.

https://bugzilla.gnome.org/show_bug.cgi?id=704368
2013-07-19 05:34:09 -04:00
03fd74da4e [l10n] Updated Italian translation. 2013-07-19 09:27:25 +02:00
ed178b702f windowManager: Actually respect hasWorkspaces
We've long had the hasWorkspaces property, but it doesn't seem like
it was ever used. Implement it so that we don't have workspaces in
initial-setup mode.

Since it's difficult to make it change at runtime with a decent set
of semantics, and we never expect that to happen, don't bother
implementing it dynamically.

https://bugzilla.gnome.org/show_bug.cgi?id=698593
2013-07-18 18:04:15 -04:00
b4567acc6b unlockDialog: Use GdmUtil.AuthPrompt instead of ModalDialog
commit ea02380c15 made the login
screen stop using ModalDialog. It makes sense for the unlock
code to also stop using ModalDialog, too (for similar reasons).

Now that the login screen's auth prompt code has been separated
out, the unlock dialog can use it to get the buttons and spinners
etc, that it was previously getting from ModalDialog.

This commit drops the ModalDialog usage in the unlock dialog, and
makes the unlock dialog use GdmUtil.AuthPrompt instead.

https://bugzilla.gnome.org/show_bug.cgi?id=702308
2013-07-18 16:07:27 -04:00
953f44bcc5 Revert "add annoying delay"
This reverts commit e9531487d9.

This is a testing commit and snuck in on accident.
2013-07-18 15:43:51 -04:00
be4f259b71 util: add shell entry menu to auth prompt
This brings us parity with the unlock dialog, and is a prerequisite
for eventually moving the unlock dialog over to using the auth
prompt.

https://bugzilla.gnome.org/show_bug.cgi?id=702308
2013-07-18 15:41:02 -04:00
e9531487d9 add annoying delay 2013-07-18 15:17:17 -04:00
d715110961 loginDialog: factor auth prompt code out to utils
Right now there is a lot of duplicated code between the unlock
dialog and the login dialog.

This commit moves the login dialog's auth prompt to a separate
class, so that it can (in a subsequent commit) be used by the
unlock dialog.

https://bugzilla.gnome.org/show_bug.cgi?id=702308
2013-07-18 14:36:00 -04:00
3c31908e08 loginDialog: s/button-press-event/key-press-event/
A bug got introduced when moving the login dialog away from modal
dialog, such that it listens for escape key presses in a mouse
event handler instead of a keyboard event handler.

This commit fixes that code to correctly listen for key-press-event
instead of button-press-event.

https://bugzilla.gnome.org/show_bug.cgi?id=702308
2013-07-18 13:12:25 -04:00
09d34a2129 unlockDialog: drop unused variable
https://bugzilla.gnome.org/show_bug.cgi?id=702308
2013-07-18 13:12:21 -04:00
e5e3f3c299 loginDialog: avoid blinking user list when disable-user-list=true
Right now if disable-user-list is true we show it briefly, just so
that we can fade it out to the user entry.

This commit avoids the fade in that case.

https://bugzilla.gnome.org/show_bug.cgi?id=704471
2013-07-18 13:08:31 -04:00
bcd1c793ea Updated Norwegian bokmål translation 2013-07-18 15:29:48 +02:00
185152a74a popMenu: Fix invalid allocation
https://bugzilla.gnome.org/show_bug.cgi?id=704453
2013-07-18 14:28:33 +01:00
ded99b9a09 screenShield: defer deactivation until all messages are shown
Right now when a user types their password to unlock their session
we end up getting an unlock signal from GDM right away.  We then
proceed to deactivate the screensaver before the user has a chance
to read his messages.

This commit makes sure we clear out the message queue before processing
the deactivation request.

https://bugzilla.gnome.org/show_bug.cgi?id=704347
2013-07-18 09:24:01 -04:00
55a04bbf2b unlockDialog: don't unlock explicitly on verification-complete
logind sends out an "unlock" signal separately when
verification completes and we already listen for that,
so we don't need to unlock on verification-complete, too.

https://bugzilla.gnome.org/show_bug.cgi?id=704347
2013-07-18 09:23:15 -04:00
7d5d7453c2 util: drop call that can't do anything
this._clearMessageQueue() is a noop when this.hasPendingMessages is
false so calling it in that case doesn't make sense.

This commit drops that call.

https://bugzilla.gnome.org/show_bug.cgi?id=704347
2013-07-18 09:23:11 -04:00
952f58153f util: Fix no-more-messages signal
Now thas hasPendingMessages is fixed, we need to also fix the associated
signal "no-more-messages"

https://bugzilla.gnome.org/show_bug.cgi?id=704347
2013-07-18 09:23:11 -04:00
d0d981435a appSwitcher: Add option to limit to the current workspace
Add an option to limit the appSwitcher to the current workspace. For users
that use workspaces for task separation this more convient then current
behviour. While having to add an option is unfortunate there is no way to make
both groups happy as workspaces usage differes between different users / types
of users.

https://bugzilla.gnome.org/show_bug.cgi?id=703538
2013-07-18 14:33:41 +02:00
fd83990d8a st-theme-node: reset paint state when theme node changes
https://bugzilla.gnome.org/show_bug.cgi?id=704430
2013-07-18 01:12:44 +01:00
ad0c4caf1c st-theme-node-transition: fix paint state corruption
https://bugzilla.gnome.org/show_bug.cgi?id=704411
2013-07-17 19:38:44 +01:00
126f0ed95d popupMenu: Remove non-all-spanning versions of colspan in popup menus
This simplifies the code considerably, and makes 'expand' behave as expected.

https://bugzilla.gnome.org/show_bug.cgi?id=704336
2013-07-17 12:52:22 -04:00
a2b499c460 popupMenu: Fix closing submenus when clicking on the expander
We need to make sure that we reset the opened submenu when we close the
submenu, not trick the toplevel into thinking a closed submenu is the
currently opened menu.

https://bugzilla.gnome.org/show_bug.cgi?id=704336
2013-07-17 12:52:22 -04:00
5a5b3bf291 popupMenu: Use the parent field for sensitivity chaining
Instead of a signal mess.

https://bugzilla.gnome.org/show_bug.cgi?id=704336
2013-07-17 12:52:22 -04:00
a4a6e7cf53 popupMenu: Fix parenting implementation
I got confused between menus and menu items.

https://bugzilla.gnome.org/show_bug.cgi?id=704336
2013-07-17 12:52:22 -04:00
658db43ad3 popupMenu: Don't count the separator as an item when returning isEmpty
If we have a separator don't use it's possibly-unsynced visibility to
determine if the menu is empty or not.

https://bugzilla.gnome.org/show_bug.cgi?id=70386
2013-07-17 16:40:51 +01:00
cfecd063c9 st-scroll-view: properly check if the scrollbars are visible
We don't set :visible on the scrollbars, but use booleans to track
if they are visible. Thus check the booleans instead of the actor's
properties when allocating the scrollbars.

https://bugzilla.gnome.org/show_bug.cgi?id=704265
2013-07-17 17:33:26 +02:00
a8fe063726 util: Fix hasPendingMessages
While the UserVerifier does indeed have a _userVerifier inside
it, the hasPendingMessages property is on ourselves, not
_userVerifier.

https://bugzilla.gnome.org/show_bug.cgi?id=704347
2013-07-16 16:54:54 -04:00
dcf637edd8 theme: fix spacing for "Log in as another user" button
At some point the Not Listed? button was moved to align it better
in the login screen.  That nudging made the "Log in as another user"
button (which uses the same style class) align worse.

This commit removes the padding at the unlock screen.

https://bugzilla.gnome.org/show_bug.cgi?id=704318
2013-07-16 14:14:26 -04:00
c0345f1088 theme: adjust scroll bar styling to new background color
Since the background color changed, the style of the scroll bar
needs to be adjusted to compensate.

https://bugzilla.gnome.org/show_bug.cgi?id=702305
2013-07-16 15:28:56 +01:00
0cbdfca141 theme: rework lock screen notification layout
Make the notifications wider and adjust the padding so they
aren't as tall. Also make the corner radius consistent with the
rest of the theme.

This makes the notifications look better, is more space
efficient, and is consistent with the latest mockups.

https://bugzilla.gnome.org/show_bug.cgi?id=702305
2013-07-16 15:12:16 +01:00
cb1f696e22 theme: adjust some colors to the new background
The background color is now lighter. That means that text and
button borders also need to be lighter to compensate.

https://bugzilla.gnome.org/show_bug.cgi?id=702305
2013-07-16 13:59:28 +01:00
cf7355e4d0 theme: change the lock screen notification background
The current look is too heavy and looks out of place. Simplify
the background in accordance with the latest mockups.

https://bugzilla.gnome.org/show_bug.cgi?id=702305
2013-07-15 19:09:56 +01:00
dab8c5ea56 popupMenu: Fix bad syntax error 2013-07-15 12:57:51 -04:00
4b889eac32 popupMenu: Ensure that submenus are properly hidden when insensitive
We don't actually propagate sensitivity information to submenus; we
simply make sure that they can never be open when the parent is
insensitive.

https://bugzilla.gnome.org/show_bug.cgi?id=702539
2013-07-15 12:34:23 -04:00
86835db8f2 popupMenu: Propagate sensitivity to menu children
This way, if a parent is insensitive, all children will be, too.
Though PopupSubMenus will be forced closed, PopupMenuSection needs
the propagation.

https://bugzilla.gnome.org/show_bug.cgi?id=702539
2013-07-15 12:34:23 -04:00
bc317bf3f2 popupMenu: Remove 'sensitive' input param
It's hard to implement properly, was broken, and unused. If somebody
really wants it, they can call setSensitive after constructing the item.

https://bugzilla.gnome.org/show_bug.cgi?id=702539
2013-07-15 12:34:22 -04:00
5c036eadf9 popupMenu: Only allow one submenu to be open at a time
When the user opens another submenu, close the first one.

https://bugzilla.gnome.org/show_bug.cgi?id=702539
2013-07-15 12:34:22 -04:00
263474705b popupMenu: Emit open-state-changed at the start of animating a submenu
Doing it at the end has confusing semantics, especially as there is
this point where isOpen is true, but the corresponding open-state-changed
has not been emitted.

https://bugzilla.gnome.org/show_bug.cgi?id=702539
2013-07-15 12:34:22 -04:00
1e781ec78f popupMenu: Remove connectSubMenuSignals
The code here is a bit messy, as the signal disconnection is handled
in two different places. Share code in a better, different way.

https://bugzilla.gnome.org/show_bug.cgi?id=702539
2013-07-15 12:34:22 -04:00
ef1eabf033 popupMenu: Ignore submenus when getting the column widths
The new designs don't want these to be aligned the same way.

https://bugzilla.gnome.org/show_bug.cgi?id=702539
2013-07-15 12:34:22 -04:00
2fa40555e6 popupMenu: Simplify allocation code
Use ClutterActor.allocate_align_fill() so we don't have to do
this math ourselves. At the same time, clean up the RTL handling
so that it's easier to follow.

https://bugzilla.gnome.org/show_bug.cgi?id=702539
2013-07-15 12:34:22 -04:00
2aae272d86 popupMenu: Don't close submenus when the toplevel container is closed
It seems this behavior at one time was intentional, but I (along with
the designers) think it looks ugly having the menu having its insides
shrinking and shifting around while fading out of existence.

There's two cases where we currently explicitly try to animate the
submenu closed -- when an item is clicked inside the submenu, and
when the toplevel closes. This removes both of those.

The user expectation is that submenus will be closed the next time the
toplevel is open even if they were open before, so force submenus closed
when the toplevel finishes fading out, without any animation.

https://bugzilla.gnome.org/show_bug.cgi?id=702539
2013-07-15 12:24:27 -04:00
7db0900cc8 popupMenu: Introduce a way of closing toplevels from sections
As the aggregate menu will be built out of sections from each
of the menus, we need to ensure that activating an item in one
of these sections can close the main menu, even when it is not
a menu item. The new API also needs to be flexible enough to
ensure that animations can be controlled, like the buttons that
lock the screen or launch a new session.

Port the user menu to use this new API as well.

https://bugzilla.gnome.org/show_bug.cgi?id=702539
2013-07-15 12:01:07 -04:00
c1e2d66abd popupMenu: Add a simple way to get the toplevel for a submenu / section
This will be used to avoid some nasty signal propagation when wanting to
rework how sections / submenus work.

https://bugzilla.gnome.org/show_bug.cgi?id=702539
2013-07-15 12:01:07 -04:00
78b1ba56ce calendar: Make the events-list a fixed width
Commit 929636ebd0 removed the fixed width of the calendar, while
commit cb45a38838 only added it back at max-width (resulting in
width changes of the calendar while browsing days).

https://bugzilla.gnome.org/show_bug.cgi?id=704200
2013-07-14 17:32:46 +02:00
5385205b8e l10n: Update Japanese translation 2013-07-14 23:57:58 +09:00
5c8bbb511e Updated slovak translation 2013-07-14 14:37:21 +02:00
fde01f0b71 st-theme-node: invalidate cached size when initializing paint state
https://bugzilla.gnome.org/show_bug.cgi?id=703997
2013-07-13 08:31:18 +01:00
1c04ae3216 st-theme-node: fix obvious size comparison error
https://bugzilla.gnome.org/show_bug.cgi?id=703997
2013-07-13 08:31:14 +01:00
35b4907e52 loginDialog: force user list and prompt to be the same width 2013-07-12 14:38:40 -04:00
2431b8e021 loginDialog: make prompt entry wider
This makes it match mock ups better and looks more visually
pleasing.

https://bugzilla.gnome.org/show_bug.cgi?id=702308
2013-07-12 11:53:09 -04:00
bd5c04b923 loginDialog: drop padding between buttons and entry
Now that we preallocate space for the prompt message there is
a lot of loose space between the entry and the buttons.

This commit helps tighten things up by getting rid
of the large top padding set above the login buttons.

https://bugzilla.gnome.org/show_bug.cgi?id=702308
2013-07-12 11:52:52 -04:00
6e00b6e214 loginDialog: pre-allocate prompt message height
Right now things jump around if a message comes in.
This commit makes sure there's room for a message to start.

https://bugzilla.gnome.org/show_bug.cgi?id=702308
2013-07-12 11:52:39 -04:00
c9b079cbb5 StWidget: use a handler id to disconnect the callback
g_signal_handlers_disconnect_by_func() is much more expensive
than g_signal_handler_disconnect(), so use the latter.

https://bugzilla.gnome.org/show_bug.cgi?id=704077
2013-07-12 13:38:47 +02:00
9163372786 Tajik translation updated 2013-07-12 16:06:48 +05:00
dabcd29fb6 panelMenu: Close menu when hiding the corresponding button
A PanelMenuButton added to the top bar might not be visible at all
times. If it is hidden while the corresponding menu is open, we
currently don't do anything at all, e.g. the menu remains open
pointing to an arbitrary location in the top bar.
Instead, close the menu automatically in that case.

https://bugzilla.gnome.org/show_bug.cgi?id=703540
2013-07-11 17:14:02 +02:00
d36e435801 update Simplified Chinese (zh_CN) translation 2013-07-11 18:11:52 +08:00
a18fb27d0f st-theme-node: let paint states take weak ref on theme nodes
When the St theme is changed, the StThemeContext unrefs all the theme
nodes cached in it's internal hash table, then emits a signal to
notify all theme nodes that the current theme has changed.

The problem is that the first StWidget to catch a theme changed signal
will trigger a "style-changed" signal catched by its children first.
So the theme changed signal can't be processed properly to cleanup
StThemeNodePaintState before recomputing the theme.

This patch adds a weak ref to the StThemeNode in the
StThemeNodePaintState to ensure paint states are properly cleaned up
when the associated StThemeNode is freed.

https://bugzilla.gnome.org/show_bug.cgi?id=703859
2013-07-10 20:27:38 +01:00
2dbe511519 Bump version to 3.9.4
Update NEWS.
2013-07-10 19:50:29 +02:00
e031a5d28b st-theme-node: Update corner textures on allocation size changes
Commit 318283fc70 optimized box-shadow rendering by not recreating
shadow materials on every allocation change. Other handles cannot
be reused and are updated regularly, however the patch missed the
cached corner materials - while those can be reused, we still need
to ensure that the currently used paint state references them.

https://bugzilla.gnome.org/show_bug.cgi?id=703909
2013-07-10 17:35:11 +02:00
f9b32474b0 st-theme-node: Don't update resources on each paint
Up to commit 318283fc70, resources were only updated when
the allocated size had changed. There is no good reason to change
this for theme nodes without box shadows.

https://bugzilla.gnome.org/show_bug.cgi?id=703909
2013-07-10 17:35:11 +02:00
53d268a7ef st: Remove support for fixed positioning in BoxLayout
It is the job of layout containers to arrange their children; having
a hidden feature that *also* allows children to be positioned freely
outside the parent's allocation is just odd.
With the last user of the feature gone, kill it.

https://bugzilla.gnome.org/show_bug.cgi?id=703808
2013-07-09 23:19:20 +02:00
70da558802 overview: Add coverPane to stack instead of BoxLayout
The event catcher that covers the entire primary monitor during
transitions is currently inside a BoxLayout, relying in its
odd support for fixed position actors.
We already have a proper stack widget in place, move it there.

https://bugzilla.gnome.org/show_bug.cgi?id=703808
2013-07-09 23:19:20 +02:00
11215374ff lookingGlass: Use uiGroup as parent instead of panelBox
Currently lookingGlass relies on some odd BoxLayout behavior, which
allows children to use fixed positioning without affecting the parent's
size request. As this behavior is scheduled for removal, add the
looking glass dialog directly to Main.uiGroup.

https://bugzilla.gnome.org/show_bug.cgi?id=703808
2013-07-09 23:19:20 +02:00
cb45a38838 calendar: Fix line-wrapping of calendar events
Commit 929636ebd0 broke line-wrapping of longish calendar events,
add back the required CSS width.

https://bugzilla.gnome.org/show_bug.cgi?id=703893
2013-07-09 23:19:19 +02:00
bb4d430ebf style: Fix extension list style in looking glass
No idea how this got unnoticed since forever, but code and stylesheet
didn't quite agree on the same name ...

https://bugzilla.gnome.org/show_bug.cgi?id=703807
2013-07-09 22:06:13 +02:00
c17f84ca23 style: Fix focused app view controls
Moving from fixed width to horizontal padding for the app view control
buttons broke the focus style, in that buttons may change size on
keyboard focus changes. Fix this by using the correct horizontal padding
when focused.

https://bugzilla.gnome.org/show_bug.cgi?id=703807
2013-07-09 22:06:13 +02:00
0ae1f9ffc7 modalDialog: Minor coding style fix
https://bugzilla.gnome.org/show_bug.cgi?id=703807
2013-07-09 22:06:13 +02:00
d15bcd9845 loginDialog: don't call nonexistent setInitialKeyFocus function
commit ea02380c15 changed the login
dialog to not use ModalDialog anymore.  There's still one lingering
setInitialKeyFocus method call in the source, which will cause an
exception to be thrown when users have their user list disabled.

This commit fixes that.

https://bugzilla.gnome.org/show_bug.cgi?id=703874
2013-07-09 12:52:18 -04:00
f79a11d993 st-theme-node: init cached state properly 2013-07-09 11:25:28 +01:00
9391d9d11b st-theme-node: reuse box-shadow materials between paint states when possible 2013-07-09 11:25:24 +01:00
aee90a3116 st-theme-node: refactor calls to render css box 2013-07-09 11:24:26 +01:00
ffac5279a7 tests: add animated box-shadow test to demo optimized painting 2013-07-09 11:24:26 +01:00
318283fc70 st: optimize box-shadow rendering
Currently the box-shadow is rendering is done like this :

The first time we want to render a node that requires a box-shadow, St
creates an cogl offscreen surface of the size of the allocation and
renders the box into this offscreen buffer using modulation on the
alpha channel, this buffer is then blurred according to the CSS
parameters.

The problem with this method is that every time an StWidget is
resized, its box-shadow offscreen buffer has to be resized and
therefore rendered and blurred.

This patches propose an optimization for this use case by rendering
the box-shadow only once but at a size that is independent of the
StWidget's size. Then every time we need to paint this box-shadow, we
just render this offscreen buffer using a 9-slices.

This method only works when the allocation of the widget is bigger
than the minimum shadow size on which we can apply a 9-slices, that is
given my the radius of the corners. If the allocation is smaller than
this minimum size, we then fallback to the fully render/blur the
shadow (like before this patch).

https://bugzilla.gnome.org/show_bug.cgi?id=689858
2013-07-09 11:24:26 +01:00
3582ba0c77 layout: Don't use the input mode to block events to windows
Instead, use the standard Clutter scene graph and our Chrome system.

This also removes our last use of the input mode, so remove that as
well.

https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-07-08 17:05:17 -04:00
985d0c786c global: Automatically unshape the stage X window when we take a modal
This prevents the "client" from having to do it, and removes one part
of the FULLSCREEN input mode.

https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-07-08 17:05:17 -04:00
9c8c282e08 global: Clean up the code that actually sets the shape on the stage
Instead of having "dummy" setters that do work, split out the parts
that do work into their own function.

https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-07-08 17:05:16 -04:00
93dc7a51c0 Rework window / actor focus handling
The duality of the Clutter's key focus and mutter's window focus has long been
a problem for us in lots of case, and caused us to create large and complicated
hacks to get around the issue, including GrabHelper's focus grab model.

Instead of doing this, tie basic focus management into the core of gnome-shell,
instead of requiring complex "application-level" management to get it done
right.

Do this by making sure that only one of an actor or window can be focused at
the same time, and apply the appropriate logic to drop one or the other,
reactively.

Modals are considered a special case, as we grab all keyboard events, but at
the X level, the client window still has focus. Make sure to not do any input
synchronization when we have a modal.

At the same time, remove the FOCUSED input mode, as it's no longer necessary.

https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-07-08 17:05:16 -04:00
393577ee78 grabHelper: Remove explicitly having to select modal
https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-07-08 16:51:51 -04:00
eef593a34e messageTray: Don't use focus grabs
We can easily implement much of the same behavior ourselves by
keeping track of Clutter's focus events. Reintroduce heavily
modified FocusGrabber to do the work for us.

This will temporarily break when the user selects a window until
we can make gnome-shell automatically set the stage focus.

This also removes our only use of focus grabs, so remove those
as well.

https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-07-08 16:51:50 -04:00
3790e924e9 grabHelper: Rewrite documentation for GrabHelper.grab()
The previous docs were badly maintained. This does not mention
grabFocus grabs, as they'll be removed shortly.

https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-07-08 16:17:01 -04:00
2f165aade8 Updated Vietnamese translation 2013-07-06 18:29:12 +07:00
2c502aec45 po/vi: import from Damned Lies 2013-07-06 18:24:32 +07:00
6bbf246752 Updated German translation 2013-07-05 17:24:23 +02:00
0b3e8e29cf Updated Indonesian translation 2013-07-05 10:23:35 +07:00
d509ab7779 ScreenShield: when the user goes idle, check for active before pushing a modal
We can't assume "isActive implies isModal", so there is a risk
of pushing a modal that nothing else will ever pop, because we
take the early return and don't activate the user active watch.

https://bugzilla.gnome.org/show_bug.cgi?id=700901
2013-07-04 15:16:27 +02:00
502a9aefdc Updated Norwegian bokmål translation 2013-07-04 11:09:57 +02:00
ccba18aa8f network: Fix a recursion issue when updating VPN
_updateIcon should not attempt to sync any active connections, as the
icon-changed signal can be emitted in response to something done during
_syncActiveConnection. In the case of VPN, removeActiveConnection would
cause an icon-changed signal to be emitted immediately, but the state
would not be updated, causing us to call removeActiveConnection over and
over.

Explicitly sync all active connections when we know it needs to be done,
and simply make _updateIcon synchronize with the current device's icon.

https://bugzilla.gnome.org/show_bug.cgi?id=703565
2013-07-03 13:55:08 -04:00
586ebcd5be background: fix asynchronous management of background loading operations
This fixes a blue background being drawn when switching the monitors
configuration using hardware keys
(clone/multimonitor/external/internal).

The problem is that the shell gather all background loading requests
under the same meta_background_load_file_async call using one
GCancellable (the first one to come). So when the shell receives a
batch of 12 or so XRandr events, it creates 12 new background managers
which end up trying to load 12 times the same background picture. All
of these requests are batched into the same
meta_background_load_file_async using the first GCancellable received
on the first request. Unfortunately, when the first request is
cancelled by the following event indicating a new monitor setup, all
of the background picture requests are dropped on the floor, and
nothing gets loaded (hence the blue screen background).

https://bugzilla.gnome.org/show_bug.cgi?id=703001
2013-07-03 17:03:02 +01:00
76 changed files with 6854 additions and 7358 deletions

29
NEWS
View File

@ -1,3 +1,32 @@
3.9.4
=====
* Fix chat entries not being focused when expanded [Jasper; #698778]
* Fix alignment of "Not Listed?" label [Mathieu; #702307]
* Fix alignment of time stamps in chat notifications [Carlos; #687809]
* Round the ends of slider trough [Jasper; #702825]
* Add support for "box-shadow: none" [Cosimo; #702782]
* Keep chrome below popup windows [Florian; #702338]
* Move the session list to a popup menu [Ray; #702818]
* Fix autorun notifications for "non-native" volumes [Matthias; #703418]
* dnd: Speed up by not picking on each motion event [Jasper; #703443]
* Fix management of asynchronous background loading [Lionel; #703001]
* Rework focus handling [Jasper; #700735]
* Optimize box-shadow rendering [Lionel; #689858]
* Remove support for fixed positioning in BoxLayouts [Florian; #703808]
* Misc bug fixes and cleanups [Adel, Jasper, Florian, Ray, Lionel, Emilio;
#702849, #610279, #703132, #703105, #703160, #703126, #703304, #703403,
#698593, #703442, #703565, #700901, #703874, #703807, #703893, #703909]
Contributors:
Mathieu Bridon, Giovanni Campagna, Cosimo Cecchi, Matthias Clasen,
Fran Diéguez, Adel Gadllah, Lionel Landwerlin, Florian Müllner,
Emilio Pozuelo Monfort, Carlos Soriano, Jasper St. Pierre, Ray Strode
Translations:
Baurzhan Muftakhidinov [kk], Marek Černocký [cs], Daniel Mustieles [es],
Fran Diéguez [gl], Kjartan Maraas [nb], Andika Triwidada [id],
Benjamin Steinwender [de], Nguyễn Thái Ngọc Duy [vi], Trần Ngọc Quân [vi]
3.9.3
=====
* Don't push window thumbs when workspace switcher is hidden [Jasper; #701167]

View File

@ -1,5 +1,5 @@
AC_PREREQ(2.63)
AC_INIT([gnome-shell],[3.9.3],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_INIT([gnome-shell],[3.9.4],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_SRCDIR([src/shell-global.c])
@ -63,7 +63,7 @@ AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
CLUTTER_MIN_VERSION=1.13.4
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
GJS_MIN_VERSION=1.35.4
MUTTER_MIN_VERSION=3.9.3
MUTTER_MIN_VERSION=3.9.4
GTK_MIN_VERSION=3.7.9
GIO_MIN_VERSION=2.37.0
LIBECAL_MIN_VERSION=3.5.3

View File

@ -186,6 +186,19 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
</key>
</schema>
<schema id="org.gnome.shell.app-switcher"
path="/org/gnome/shell/app-switcher/"
gettext-domain="@GETTEXT_PACKAGE@">
<key type="b" name="current-workspace-only">
<default>false</default>
<summary>Limit switcher to current workspace.</summary>
<description>
If true, only applications that have windows on the current workspace are shown in the switcher.
Otherwise, all applications are included.
</description>
</key>
</schema>
<enum id="org.gnome.shell.window-switcher.AppIconMode">
<value value="1" nick="thumbnail-only"/>
<value value="2" nick="app-icon-only"/>

View File

@ -189,13 +189,6 @@ StScrollBar StButton#vhandle:active {
border-width: 0px;
}
.popup-combo-menu {
background-color: rgba(0,0,0,0.9);
padding: 1em 0em;
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.
*/
@ -219,10 +212,6 @@ StScrollBar StButton#vhandle:active {
.popup-image-menu-item {
}
.popup-combobox-item {
spacing: 1em;
}
.popup-separator-menu-item {
-gradient-height: 1px;
-gradient-start: rgba(255,255,255,0.0);
@ -236,10 +225,6 @@ StScrollBar StButton#vhandle:active {
font-weight: bold;
}
.popup-device-menu-item {
spacing: .5em;
}
.popup-status-menu-item {
font-weight: normal;
color: #999;
@ -249,19 +234,10 @@ StScrollBar StButton#vhandle:active {
color: white;
}
.popup-subtitle-menu-item, .popup-subtitle-menu-item:insensitive {
font-weight: bold;
color: white;
}
.popup-menu-icon {
icon-size: 1.09em;
}
.popup-battery-percentage {
padding-left: 24px;
}
/* Switches */
.toggle-switch {
width: 65px;
@ -286,10 +262,51 @@ StScrollBar StButton#vhandle:active {
background-size: contain;
}
.nm-menu-item-icons {
/* Network */
.nm-dialog {
max-height: 400px;
}
.nm-dialog-content {
spacing: 8px;
}
.nm-dialog-header-hbox {
spacing: 4px;
}
.nm-dialog-header-icon {
icon-size: 32px;
}
.nm-dialog-scroll-view {
border: 2px solid #666;
border-radius: 6px;
}
.nm-dialog-header {
font-weight: bold;
}
.nm-dialog-item {
font-size: 12pt;
border-bottom: 1px solid #666;
padding: 12px;
}
.nm-dialog-item:checked {
background-color: #333;
}
.nm-dialog-icons {
spacing: .5em;
}
.nm-dialog-icon {
icon-size: 16px;
}
/* Buttons */
.candidate-page-button,
@ -327,10 +344,6 @@ StScrollBar StButton#vhandle:active {
border-width: 2px;
}
.app-view-control:focus {
padding: 3px;
}
.app-view-control:first-child:ltr:focus,
.app-view-control:last-child:rtl:focus {
border-right-width: 1px;
@ -365,7 +378,8 @@ StScrollBar StButton#vhandle:active {
.modal-dialog-button,
.notification-button,
.hotplug-notification-item,
.app-view-controls {
.app-view-controls,
#screenShieldNotifications {
border-radius: 18px;
}
@ -474,10 +488,6 @@ StScrollBar StButton#vhandle:active {
height: 1.86em;
}
#panel.lock-screen {
background-color: rgba(0,0,0,0.3);
}
#panel.unlock-screen,
#panel.login-screen {
background-color: transparent;
@ -612,58 +622,30 @@ StScrollBar StButton#vhandle:active {
spacing: 8px;
}
/* User Menu */
#panelUserMenu {
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;
background-size: contain;
}
.status-chooser-user-icon:hover {
border: 2px solid #bbbbbb;
}
.status-chooser-user-name {
font-weight: bold;
font-size: 1.3em;
min-width: 120pt;
}
.status-chooser-combo {
border: 1px solid transparent;
}
.status-chooser-combo.popup-combo-menu {
padding: .4em 0em;
border-radius: 4px;
border: 1px solid #5f5f5f;
}
.status-chooser-status-item,
.status-chooser-combo .popup-combobox-item {
spacing: .4em;
}
.system-status-icon {
icon-size: 1.09em;
}
.system-switch-user-submenu-icon {
icon-size: 24px;
border: 1px solid #8b8b8b;
}
.system-menu-action {
color: #e6e6e6;
border-radius: 4px;
padding: 6px;
}
.system-menu-action:hover {
color: white;
background-color: rgba(255,255,255,0.1);
}
.system-menu-action > StIcon {
icon-size: 32px;
}
/* Overview */
#overview {
@ -911,6 +893,11 @@ StScrollBar StButton#vhandle:active {
padding: 4px 32px;
}
.app-view-control:focus {
padding: 3px 31px;
}
.search-display > StBoxLayout,
.all-apps > StBoxLayout,
.frequent-apps > StBoxLayout {
@ -1132,7 +1119,7 @@ StScrollBar StButton#vhandle:active {
padding: 4px;
}
.lg-extension-list {
.lg-extensions-list {
padding: 4px;
spacing: 6px;
}
@ -1297,7 +1284,7 @@ StScrollBar StButton#vhandle:active {
}
.events-table {
min-width: 320px;
width: 320px;
spacing-columns: 6pt;
padding: 0 1.4em;
}
@ -2209,6 +2196,18 @@ StScrollBar StButton#vhandle:active {
/* Login Dialog */
.framed-user-icon {
border: 2px solid #8b8b8b;
border-radius: 5px;
width: 48pt;
height: 48pt;
background-size: contain;
}
.framed-user-icon:hover {
border: 2px solid #bbbbbb;
}
.login-dialog-banner {
font-size: 10pt;
font-weight: bold;
@ -2238,6 +2237,10 @@ StScrollBar StButton#vhandle:active {
min-width: 350px;
}
.login-dialog-button-box {
spacing: 5px;
}
.login-dialog-prompt-login-hint-message {
font-size: 10.5pt;
}
@ -2319,6 +2322,9 @@ StScrollBar StButton#vhandle:active {
font-weight: bold;
color: #666666;
padding-top: 1em;
}
.login-dialog-user-selection-box .login-dialog-not-listed-label {
padding-left: 2px;
}
@ -2339,6 +2345,7 @@ StScrollBar StButton#vhandle:active {
padding-top: 24px;
padding-bottom: 12px;
spacing: 8px;
width: 23em;
}
.login-dialog-prompt-label {
@ -2346,10 +2353,6 @@ StScrollBar StButton#vhandle:active {
font-size: 14px;
}
.login-dialog-prompt-entry {
width: 15em;
}
.login-dialog-session-list-button StIcon {
icon-size: 1.25em;
}
@ -2427,6 +2430,11 @@ StScrollBar StButton#vhandle:active {
/* Screen shield */
#panel.lock-screen,
#screenShieldNotifications {
background-color: rgba(0,0,0,0.3);
}
.screen-shield-background {
background: black;
box-shadow: 0px 4px 8px rgba(0,0,0,0.9);
@ -2471,33 +2479,27 @@ StScrollBar StButton#vhandle:active {
}
#screenShieldNotifications {
border-radius: 8px;
background-color: rgba(0.0, 0.0, 0.0, 0.9);
border: 2px solid #868686;
max-height: 500px;
padding: 18px 0;
box-shadow: .5em .5em 20px rgba(0, 0, 0, 0.5);
padding: 12px;
}
.screen-shield-notifications-box {
spacing: 18px;
max-width: 34em;
spacing: 12px;
width: 30em;
}
.screen-shield-notification-source {
padding: 13px 24px;
padding: 3px 6px;
spacing: 5px;
}
.screen-shield-notification-label {
font-size: 1.2em;
font-weight: bold;
padding: 0px 18px;
color: #babdb6;
padding: 0px 0px 0px 12px;
}
.screen-shield-notification-count-text {
padding: 0px 18px;
padding: 0px 0px 0px 12px;
}
/* Remove background from notifications, otherwise
@ -2515,6 +2517,31 @@ StScrollBar StButton#vhandle:active {
padding-bottom: 0px;
}
#screenShieldNotifications .notification-button,
#screenShieldNotifications .notification-icon-button {
border: 1px rgba(255,255,255,0.5);
}
#screenShieldNotifications StScrollBar StBin#trough {
background-color: rgba(0,0,0,0.2);
}
#screenShieldNotifications StScrollBar StButton#vhandle,
#screenShieldNotifications StScrollBar StButton#hhandle {
background-color: rgba(0,0,0,0.3);
border: none;
}
#screenShieldNotifications StScrollBar StButton#vhandle:hover,
#screenShieldNotifications StScrollBar StButton#hhandle {
background-color: rgba(0,0,0,0.6);
}
#screenShieldNotifications StScrollBar StButton#vhandle:active,
#screenShieldNotifications StScrollBar StButton#hhandle {
background-color: rgba(0,0,0,0.8);
}
.input-source-switcher-symbol {
font-size: 34pt;
width: 96px;

View File

@ -17,6 +17,7 @@ misc/config.js: misc/config.js.in Makefile
jsdir = $(pkgdatadir)/js
nobase_dist_js_DATA = \
gdm/authPrompt.js \
gdm/batch.js \
gdm/fingerprint.js \
gdm/loginDialog.js \
@ -33,7 +34,9 @@ nobase_dist_js_DATA = \
misc/jsParse.js \
misc/loginManager.js \
misc/modemManager.js \
misc/objectManager.js \
misc/params.js \
misc/smartcardManager.js \
misc/util.js \
perf/core.js \
ui/altTab.js \
@ -95,10 +98,10 @@ nobase_dist_js_DATA = \
ui/status/power.js \
ui/status/volume.js \
ui/status/bluetooth.js \
ui/status/system.js \
ui/switcherPopup.js \
ui/tweener.js \
ui/unlockDialog.js \
ui/userMenu.js \
ui/userWidget.js \
ui/viewSelector.js \
ui/wanda.js \

506
js/gdm/authPrompt.js Normal file
View File

@ -0,0 +1,506 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Signals = imports.signals;
const St = imports.gi.St;
const Animation = imports.ui.animation;
const Batch = imports.gdm.batch;
const GdmUtil = imports.gdm.util;
const Params = imports.misc.params;
const ShellEntry = imports.ui.shellEntry;
const Tweener = imports.ui.tweener;
const UserWidget = imports.ui.userWidget;
const DEFAULT_BUTTON_WELL_ICON_SIZE = 24;
const DEFAULT_BUTTON_WELL_ANIMATION_DELAY = 1.0;
const DEFAULT_BUTTON_WELL_ANIMATION_TIME = 0.3;
const MESSAGE_FADE_OUT_ANIMATION_TIME = 0.5;
const AuthPromptMode = {
UNLOCK_ONLY: 0,
UNLOCK_OR_LOG_IN: 1
};
const AuthPromptStatus = {
NOT_VERIFYING: 0,
VERIFYING: 1,
VERIFICATION_FAILED: 2,
VERIFICATION_SUCCEEDED: 3
};
const BeginRequestType = {
PROVIDE_USERNAME: 0,
DONT_PROVIDE_USERNAME: 1
};
const AuthPrompt = new Lang.Class({
Name: 'AuthPrompt',
_init: function(gdmClient, mode) {
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
this._gdmClient = gdmClient;
this._mode = mode;
let reauthenticationOnly;
if (this._mode == AuthPromptMode.UNLOCK_ONLY)
reauthenticationOnly = true;
else if (this._mode == AuthPromptMode.UNLOCK_OR_LOG_IN)
reauthenticationOnly = false;
this._userVerifier = new GdmUtil.ShellUserVerifier(this._gdmClient, { reauthenticationOnly: reauthenticationOnly });
this._userVerifier.connect('ask-question', Lang.bind(this, this._onAskQuestion));
this._userVerifier.connect('show-message', Lang.bind(this, this._onShowMessage));
this._userVerifier.connect('verification-failed', Lang.bind(this, this._onVerificationFailed));
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
this._userVerifier.connect('show-login-hint', Lang.bind(this, this._onShowLoginHint));
this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._onHideLoginHint));
this._userVerifier.connect('smartcard-status-changed', Lang.bind(this, this._onSmartcardStatusChanged));
this.smartcardDetected = this._userVerifier.smartcardDetected;
this.connect('next', Lang.bind(this, function() {
this.updateSensitivity(false);
this.startSpinning();
if (this._queryingService) {
this._userVerifier.answerQuery(this._queryingService, this._entry.text);
} else {
this._preemptiveAnswer = this._entry.text;
}
}));
this.actor = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
vertical: true });
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this.actor.connect('key-press-event',
Lang.bind(this, function(actor, event) {
if (event.get_key_symbol() == Clutter.KEY_Escape) {
this.cancel();
}
}));
this._userWell = new St.Bin({ x_fill: true,
x_align: St.Align.START });
this.actor.add(this._userWell,
{ x_align: St.Align.START,
x_fill: true,
y_fill: true,
expand: true });
this._label = new St.Label({ style_class: 'login-dialog-prompt-label' });
this.actor.add(this._label,
{ expand: true,
x_fill: true,
y_fill: true,
x_align: St.Align.START });
this._entry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
can_focus: true });
ShellEntry.addContextMenu(this._entry, { isPassword: true });
this.actor.add(this._entry,
{ expand: true,
x_fill: true,
y_fill: false,
x_align: St.Align.START });
this._entry.grab_key_focus();
this._message = new St.Label({ opacity: 0 });
this._message.clutter_text.line_wrap = true;
this.actor.add(this._message, { x_fill: true });
this._loginHint = new St.Label({ style_class: 'login-dialog-prompt-login-hint-message' });
this.actor.add(this._loginHint);
this._buttonBox = new St.BoxLayout({ style_class: 'login-dialog-button-box',
vertical: false });
this.actor.add(this._buttonBox,
{ expand: true,
x_align: St.Align.MIDDLE,
y_align: St.Align.END });
this._defaultButtonWell = new St.Widget();
this._defaultButtonWellActor = null;
this._initButtons();
let spinnerIcon = global.datadir + '/theme/process-working.svg';
this._spinner = new Animation.AnimatedIcon(spinnerIcon, DEFAULT_BUTTON_WELL_ICON_SIZE);
this._spinner.actor.opacity = 0;
this._spinner.actor.show();
this._defaultButtonWell.add_child(this._spinner.actor);
},
_onDestroy: function() {
this._userVerifier.clear();
this._userVerifier.disconnectAll();
this._userVerifier = null;
},
_initButtons: function() {
this.cancelButton = new St.Button({ style_class: 'modal-dialog-button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
reactive: true,
can_focus: true,
label: _("Cancel") });
this.cancelButton.connect('clicked',
Lang.bind(this, function() {
this.cancel();
}));
this._buttonBox.add(this.cancelButton,
{ expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.END });
this._buttonBox.add(this._defaultButtonWell,
{ expand: true,
x_fill: false,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
this.nextButton = new St.Button({ style_class: 'modal-dialog-button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
reactive: true,
can_focus: true,
label: _("Next") });
this.nextButton.connect('clicked',
Lang.bind(this, function() {
this.emit('next');
}));
this.nextButton.add_style_pseudo_class('default');
this._buttonBox.add(this.nextButton,
{ expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.END });
this._updateNextButtonSensitivity(this._entry.text.length > 0);
this._entry.clutter_text.connect('text-changed',
Lang.bind(this, function() {
if (!this._userVerifier.hasPendingMessages)
this._fadeOutMessage();
this._updateNextButtonSensitivity(this._entry.text.length > 0);
}));
this._entry.clutter_text.connect('activate', Lang.bind(this, function() {
this.emit('next');
}));
},
_onAskQuestion: function(verifier, serviceName, question, passwordChar) {
if (this._preemptiveAnswer) {
this._userVerifier.answerQuery(this._queryingService, this._preemptiveAnswer);
this._preemptiveAnswer = null;
return;
}
if (this._queryingService)
this.clear();
this._queryingService = serviceName;
this.setPasswordChar(passwordChar);
this.setQuestion(question);
if (passwordChar) {
if (this._userVerifier.reauthenticating)
this.nextButton.label = _("Unlock");
else
this.nextButton.label = C_("button", "Sign In");
} else {
this.nextButton.label = _("Next");
}
this.updateSensitivity(true);
this.emit('prompted');
},
_onSmartcardStatusChanged: function() {
this.smartcardDetected = this._userVerifier.smartcardDetected;
// Don't reset on smartcard insertion if we're already verifying
// and the smartcard is the main service
if (this._userVerifier.serviceIsDefault(GdmUtil.SMARTCARD_SERVICE_NAME) &&
this.verificationStatus == AuthPromptStatus.VERIFYING &&
this.smartcardDetected)
return;
if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED)
this.reset();
},
_onShowMessage: function(userVerifier, message, styleClass) {
this.setMessage(message, styleClass);
this.emit('prompted');
},
_onVerificationFailed: function() {
this.clear();
this.updateSensitivity(true);
this.setActorInDefaultButtonWell(null);
this.verificationStatus = AuthPromptStatus.VERIFICATION_FAILED;
},
_onVerificationComplete: function() {
this.verificationStatus = AuthPromptStatus.VERIFICATION_SUCCEEDED;
},
_onReset: function() {
if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED) {
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
this.reset();
}
},
_onShowLoginHint: function(verifier, message) {
this.setHint(message);
},
_onHideLoginHint: function() {
this.setHint(null);
},
addActorToDefaultButtonWell: function(actor) {
this._defaultButtonWell.add_child(actor);
actor.add_constraint(new Clutter.AlignConstraint({ source: this._spinner.actor,
align_axis: Clutter.AlignAxis.BOTH,
factor: 0.5 }));
},
setActorInDefaultButtonWell: function(actor, animate) {
if (!this._defaultButtonWellActor &&
!actor)
return;
let oldActor = this._defaultButtonWellActor;
if (oldActor)
Tweener.removeTweens(oldActor);
let isSpinner;
if (actor == this._spinner.actor)
isSpinner = true;
else
isSpinner = false;
if (this._defaultButtonWellActor != actor && oldActor) {
if (!animate) {
oldActor.opacity = 0;
} else {
Tweener.addTween(oldActor,
{ opacity: 0,
time: DEFAULT_BUTTON_WELL_ANIMATION_TIME,
delay: DEFAULT_BUTTON_WELL_ANIMATION_DELAY,
transition: 'linear',
onCompleteScope: this,
onComplete: function() {
if (isSpinner) {
if (this._spinner)
this._spinner.stop();
}
}
});
}
}
if (actor) {
if (isSpinner)
this._spinner.play();
if (!animate)
actor.opacity = 255;
else
Tweener.addTween(actor,
{ opacity: 255,
time: DEFAULT_BUTTON_WELL_ANIMATION_TIME,
delay: DEFAULT_BUTTON_WELL_ANIMATION_DELAY,
transition: 'linear' });
}
this._defaultButtonWellActor = actor;
},
startSpinning: function() {
this.setActorInDefaultButtonWell(this._spinner.actor, true);
},
stopSpinning: function() {
this.setActorInDefaultButtonWell(null, false);
},
clear: function() {
this._entry.text = '';
this.stopSpinning();
},
setPasswordChar: function(passwordChar) {
this._entry.clutter_text.set_password_char(passwordChar);
this._entry.menu.isPassword = passwordChar != '';
},
setQuestion: function(question) {
this._label.set_text(question);
this._label.show();
this._entry.show();
this._loginHint.opacity = 0;
this._loginHint.show();
this._entry.grab_key_focus();
},
getAnswer: function() {
let text = this._entry.get_text();
return text;
},
_fadeOutMessage: function() {
if (this._message.opacity == 0)
return;
Tweener.removeTweens(this._message);
Tweener.addTween(this._message,
{ opacity: 0,
time: MESSAGE_FADE_OUT_ANIMATION_TIME,
transition: 'easeOutQuad'
});
},
setMessage: function(message, styleClass) {
if (message) {
Tweener.removeTweens(this._message);
this._message.text = message;
this._message.styleClass = styleClass;
this._message.opacity = 255;
} else {
this._message.opacity = 0;
}
},
_updateNextButtonSensitivity: function(sensitive) {
this.nextButton.reactive = sensitive;
this.nextButton.can_focus = sensitive;
},
updateSensitivity: function(sensitive) {
this._updateNextButtonSensitivity(sensitive);
this._entry.reactive = sensitive;
this._entry.clutter_text.editable = sensitive;
},
hide: function() {
this.setActorInDefaultButtonWell(null, true);
this.actor.hide();
this._loginHint.opacity = 0;
this.setUser(null);
this.updateSensitivity(true);
this._entry.set_text('');
},
setUser: function(user) {
if (user) {
let userWidget = new UserWidget.UserWidget(user);
this._userWell.set_child(userWidget.actor);
} else {
this._userWell.set_child(null);
}
},
setHint: function(message) {
if (message) {
this._loginHint.set_text(message)
this._loginHint.opacity = 255;
} else {
this._loginHint.opacity = 0;
this._loginHint.set_text('');
}
},
reset: function() {
let oldStatus = this.verificationStatus;
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
if (oldStatus == AuthPromptStatus.VERIFYING)
this._userVerifier.cancel();
this._queryingService = null;
this.clear();
this._message.opacity = 0;
this.setUser(null);
this.stopSpinning();
this.setHint(null);
if (oldStatus == AuthPromptStatus.VERIFICATION_FAILED)
this.emit('failed');
let beginRequestType;
if (this._mode == AuthPromptMode.UNLOCK_ONLY) {
// The user is constant at the unlock screen
beginRequestType = BeginRequestType.PROVIDE_USERNAME;
} else if (this.smartcardDetected &&
this._userVerifier.serviceIsForeground(GdmUtil.SMARTCARD_SERVICE_NAME)) {
// We don't need to know the username if the user preempted the login screen
// with a smartcard.
beginRequestType = BeginRequestType.DONT_PROVIDE_USERNAME;
} else {
// In all other cases, we should get the username up front.
beginRequestType = BeginRequestType.PROVIDE_USERNAME;
}
this.emit('reset', beginRequestType);
},
addCharacter: function(unichar) {
if (!this._entry.visible)
return;
this._entry.grab_key_focus();
this._entry.clutter_text.insert_unichar(unichar);
},
begin: function(params) {
params = Params.parse(params, { userName: null,
hold: null });
this.updateSensitivity(false);
let hold = params.hold;
if (!hold)
hold = new Batch.Hold();
this._userVerifier.begin(params.userName, hold);
this.verificationStatus = AuthPromptStatus.VERIFYING;
},
finish: function(onComplete) {
if (!this._userVerifier.hasPendingMessages) {
onComplete();
return;
}
let signalId = this._userVerifier.connect('no-more-messages',
Lang.bind(this, function() {
this._userVerifier.disconnect(signalId);
onComplete();
}));
},
cancel: function() {
this.reset();
this.emit('cancelled');
}
});
Signals.addSignalMethods(AuthPrompt.prototype);

View File

@ -32,7 +32,7 @@ const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
const Animation = imports.ui.animation;
const AuthPrompt = imports.gdm.authPrompt;
const Batch = imports.gdm.batch;
const BoxPointer = imports.ui.boxpointer;
const CtrlAltTab = imports.ui.ctrlAltTab;
@ -46,20 +46,11 @@ const UserWidget = imports.ui.userWidget;
const _FADE_ANIMATION_TIME = 0.25;
const _SCROLL_ANIMATION_TIME = 0.5;
const _DEFAULT_BUTTON_WELL_ICON_SIZE = 24;
const _DEFAULT_BUTTON_WELL_ANIMATION_DELAY = 1.0;
const _DEFAULT_BUTTON_WELL_ANIMATION_TIME = 0.3;
const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
const _LOGO_ICON_HEIGHT = 48;
let _loginDialog = null;
const DefaultButtonWellMode = {
NONE: 0,
SESSION_MENU_BUTTON: 1,
SPINNER: 2
};
const UserListItem = new Lang.Class({
Name: 'UserListItem',
@ -410,10 +401,10 @@ const LoginDialog = new Lang.Class({
parentActor.add_child(this.actor);
this._userManager = AccountsService.UserManager.get_default()
this._greeterClient = new Gdm.Client();
let gdmClient = new Gdm.Client();
if (GLib.getenv('GDM_GREETER_TEST') != '1') {
this._greeter = this._greeterClient.get_greeter_sync(null);
this._greeter = gdmClient.get_greeter_sync(null);
this._greeter.connect('default-session-name-changed',
Lang.bind(this, this._onDefaultSessionChanged));
@ -424,15 +415,6 @@ const LoginDialog = new Lang.Class({
Lang.bind(this, this._onTimedLoginRequested));
}
this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient);
this._userVerifier.connect('ask-question', Lang.bind(this, this._askQuestion));
this._userVerifier.connect('show-message', Lang.bind(this, this._showMessage));
this._userVerifier.connect('verification-failed', Lang.bind(this, this._verificationFailed));
this._userVerifier.connect('reset', Lang.bind(this, this._reset));
this._userVerifier.connect('show-login-hint', Lang.bind(this, this._showLoginHint));
this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._hideLoginHint));
this._verifyingUser = false;
this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
this._settings.connect('changed::' + GdmUtil.BANNER_MESSAGE_KEY,
@ -449,7 +431,8 @@ const LoginDialog = new Lang.Class({
Lang.bind(this, this._updateLogoTexture));
this._userSelectionBox = new St.BoxLayout({ style_class: 'login-dialog-user-selection-box',
vertical: true });
vertical: true,
visible: false });
this._userSelectionBox.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
align_axis: Clutter.AlignAxis.BOTH,
factor: 0.5 }));
@ -466,61 +449,18 @@ const LoginDialog = new Lang.Class({
x_fill: true,
y_fill: true });
this._promptBox = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
vertical: true });
this._authPrompt = new AuthPrompt.AuthPrompt(gdmClient, AuthPrompt.AuthPromptMode.UNLOCK_OR_LOG_IN);
this._authPrompt.connect('prompted', Lang.bind(this, this._onPrompted));
this._authPrompt.connect('reset', Lang.bind(this, this._onReset));
this._authPrompt.hide();
this._promptBox.connect('button-press-event',
Lang.bind(this, function(actor, event) {
if (event.get_key_symbol() == Clutter.KEY_Escape) {
this.cancel();
}
}));
this._authPrompt.actor.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
align_axis: Clutter.AlignAxis.BOTH,
factor: 0.5 }));
this._promptBox.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
align_axis: Clutter.AlignAxis.BOTH,
factor: 0.5 }));
this.actor.add_child(this._promptBox);
this._promptUser = new St.Bin({ x_fill: true,
x_align: St.Align.START });
this._promptBox.add(this._promptUser,
{ x_align: St.Align.START,
x_fill: true,
y_fill: true,
expand: true });
this._promptLabel = new St.Label({ style_class: 'login-dialog-prompt-label' });
this._promptBox.add(this._promptLabel,
{ expand: true,
x_fill: true,
y_fill: true,
x_align: St.Align.START });
this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
can_focus: true });
this._promptEntryTextChangedId = 0;
this._promptEntryActivateId = 0;
this._promptBox.add(this._promptEntry,
{ expand: true,
x_fill: true,
y_fill: false,
x_align: St.Align.START });
this._promptMessage = new St.Label({ visible: false });
this._promptBox.add(this._promptMessage, { x_fill: true });
this._promptLoginHint = new St.Label({ style_class: 'login-dialog-prompt-login-hint-message' });
this._promptLoginHint.hide();
this._promptBox.add(this._promptLoginHint);
this._buttonBox = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
vertical: false });
this._promptBox.add(this._buttonBox,
{ expand: true,
x_align: St.Align.MIDDLE,
y_align: St.Align.END });
this._cancelButton = null;
this._signInButton = null;
this._promptBox.hide();
this.actor.add_child(this._authPrompt.actor);
this._userList.actor.add_constraint(new Clutter.BindConstraint({ source: this._authPrompt.actor,
coordinate: Clutter.BindCoordinate.WIDTH }));
// translators: this message is shown below the user list on the
// login screen. It can be activated to reveal an entry for
@ -535,7 +475,12 @@ const LoginDialog = new Lang.Class({
x_align: St.Align.START,
x_fill: true });
this._notListedButton.connect('clicked', Lang.bind(this, this._hideUserListAndLogIn));
this._notListedButton.connect('clicked',
Lang.bind(this, function() {
this._authPrompt.cancelButton.show();
this._hideUserListAskForUsernameAndBeginVerification();
}));
this._notListedButton.hide();
this._userSelectionBox.add(this._notListedButton,
@ -571,8 +516,6 @@ const LoginDialog = new Lang.Class({
this._onUserListActivated(item);
}));
this._defaultButtonWell = new St.Widget();
this._defaultButtonWellMode = DefaultButtonWellMode.NONE;
this._sessionMenuButton = new SessionMenuButton();
this._sessionMenuButton.connect('session-activated',
@ -581,31 +524,18 @@ const LoginDialog = new Lang.Class({
}));
this._sessionMenuButton.actor.opacity = 0;
this._sessionMenuButton.actor.show();
this._defaultButtonWell.add_child(this._sessionMenuButton.actor);
this._authPrompt.addActorToDefaultButtonWell(this._sessionMenuButton.actor);
let spinnerIcon = global.datadir + '/theme/process-working.svg';
this._workSpinner = new Animation.AnimatedIcon(spinnerIcon, _DEFAULT_BUTTON_WELL_ICON_SIZE);
this._workSpinner.actor.opacity = 0;
this._workSpinner.actor.show();
this._defaultButtonWell.add_child(this._workSpinner.actor);
this._sessionMenuButton.actor.add_constraint(new Clutter.AlignConstraint({ source: this._workSpinner.actor,
align_axis: Clutter.AlignAxis.BOTH,
factor: 0.5 }));
},
_updateDisableUserList: function() {
let disableUserList = this._settings.get_boolean(GdmUtil.DISABLE_USER_LIST_KEY);
// If this is the first time around, set initial focus
if (this._disableUserList == undefined && disableUserList)
this.setInitialKeyFocus(this._promptEntry);
if (disableUserList != this._disableUserList) {
this._disableUserList = disableUserList;
if (!this._verifyingUser)
this._reset();
if (this._authPrompt.verificationStatus == AuthPrompt.AuthPromptStatus.NOT_VERIFYING)
this._authPrompt.reset();
}
},
@ -639,123 +569,38 @@ const LoginDialog = new Lang.Class({
this._updateLogoTexture(this._textureCache, this._logoFileUri);
},
_reset: function() {
this._userVerifier.clear();
_onPrompted: function() {
this._sessionMenuButton.updateSensitivity(true);
if (this._shouldShowSessionMenuButton())
this._authPrompt.setActorInDefaultButtonWell(this._sessionMenuButton.actor);
this._showPrompt();
},
_onReset: function(authPrompt, beginRequest) {
this._sessionMenuButton.updateSensitivity(true);
this._updateSensitivity(true);
this._promptMessage.hide();
this._user = null;
this._verifyingUser = false;
if (this._disableUserList)
this._hideUserListAndLogIn();
else
this._showUserList();
},
_getActorForDefaultButtonWellMode: function(mode) {
let actor;
if (mode == DefaultButtonWellMode.NONE)
actor = null;
else if (mode == DefaultButtonWellMode.SPINNER)
actor = this._workSpinner.actor;
else if (mode == DefaultButtonWellMode.SESSION_MENU_BUTTON)
actor = this._sessionMenuButton.actor;
return actor;
},
_setDefaultButtonWellMode: function(mode, immediately) {
if (this._defaultButtonWellMode == DefaultButtonWellMode.NONE &&
mode == DefaultButtonWellMode.NONE)
return;
let oldActor = this._getActorForDefaultButtonWellMode(this._defaultButtonWellMode);
if (oldActor)
Tweener.removeTweens(oldActor);
let actor = this._getActorForDefaultButtonWellMode(mode);
if (this._defaultButtonWellMode != mode && oldActor) {
if (immediately)
oldActor.opacity = 0;
else
Tweener.addTween(oldActor,
{ opacity: 0,
time: _DEFAULT_BUTTON_WELL_ANIMATION_TIME,
delay: _DEFAULT_BUTTON_WELL_ANIMATION_DELAY,
transition: 'linear',
onCompleteScope: this,
onComplete: function() {
if (mode == DefaultButtonWellMode.SPINNER) {
if (this._workSpinner)
this._workSpinner.stop();
}
}
});
if (beginRequest == AuthPrompt.BeginRequestType.PROVIDE_USERNAME) {
if (this._disableUserList) {
this._authPrompt.cancelButton.hide();
this._hideUserListAskForUsernameAndBeginVerification();
} else {
this._showUserList();
}
} else {
this._authPrompt.cancelButton.hide();
this._hideUserListAndBeginVerification();
}
if (actor) {
if (mode == DefaultButtonWellMode.SPINNER)
this._workSpinner.play();
if (immediately)
actor.opacity = 255;
else
Tweener.addTween(actor,
{ opacity: 255,
time: _DEFAULT_BUTTON_WELL_ANIMATION_TIME,
delay: _DEFAULT_BUTTON_WELL_ANIMATION_DELAY,
transition: 'linear' });
}
this._defaultButtonWellMode = mode;
},
_verificationFailed: function() {
this._promptEntry.text = '';
this._updateSensitivity(true);
this._setDefaultButtonWellMode(DefaultButtonWellMode.NONE, true);
},
_onDefaultSessionChanged: function(client, sessionId) {
this._sessionMenuButton.setActiveSession(sessionId);
},
_showMessage: function(userVerifier, message, styleClass) {
if (message) {
this._promptMessage.text = message;
this._promptMessage.styleClass = styleClass;
this._promptMessage.show();
} else {
this._promptMessage.hide();
}
},
_showLoginHint: function(verifier, message) {
this._promptLoginHint.set_text(message)
this._promptLoginHint.show();
this._promptLoginHint.opacity = 255;
},
_hideLoginHint: function() {
this._promptLoginHint.hide();
this._promptLoginHint.set_text('');
},
cancel: function() {
if (this._verifyingUser)
this._userVerifier.cancel();
else
this._reset();
},
_shouldShowSessionMenuButton: function() {
if (this._verifyingUser)
if (this._authPrompt.verifyingUser)
return true;
if (!this._user)
@ -767,158 +612,15 @@ const LoginDialog = new Lang.Class({
return true;
},
_showPrompt: function(forSecret) {
this._promptLabel.show();
this._promptEntry.show();
this._promptLoginHint.opacity = 0;
this._promptLoginHint.show();
this._promptBox.opacity = 0;
this._promptBox.show();
Tweener.addTween(this._promptBox,
_showPrompt: function() {
if (this._authPrompt.actor.visible)
return;
this._authPrompt.actor.opacity = 0;
this._authPrompt.actor.show();
Tweener.addTween(this._authPrompt.actor,
{ opacity: 255,
time: _FADE_ANIMATION_TIME,
transition: 'easeOutQuad' });
if (this._shouldShowSessionMenuButton())
this._setDefaultButtonWellMode(DefaultButtonWellMode.SESSION_MENU_BUTTON, true);
else
this._setDefaultButtonWellMode(DefaultButtonWellMode.NONE, true);
this._promptEntry.grab_key_focus();
let hold = new Batch.Hold();
let tasks = [function() {
this._prepareDialog(forSecret, hold);
},
hold];
let batch = new Batch.ConcurrentBatch(this, tasks);
return batch.run();
},
_prepareDialog: function(forSecret, hold) {
this._buttonBox.visible = true;
this._buttonBox.remove_all_children();
if (!this._disableUserList || this._verifyingUser) {
this._cancelButton = new St.Button({ style_class: 'modal-dialog-button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
reactive: true,
can_focus: true,
label: _("Cancel") });
this._cancelButton.connect('clicked',
Lang.bind(this, function() {
this.cancel();
}));
this._buttonBox.add(this._cancelButton,
{ expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.END });
}
this._buttonBox.add(this._defaultButtonWell,
{ expand: true,
x_fill: false,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
this._signInButton = new St.Button({ style_class: 'modal-dialog-button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
reactive: true,
can_focus: true,
label: forSecret ? C_("button", "Sign In") : _("Next") });
this._signInButton.connect('clicked',
Lang.bind(this, function() {
hold.release();
}));
this._signInButton.add_style_pseudo_class('default');
this._buttonBox.add(this._signInButton,
{ expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.END });
this._updateSignInButtonSensitivity(this._promptEntry.text.length > 0);
this._promptEntryTextChangedId =
this._promptEntry.clutter_text.connect('text-changed',
Lang.bind(this, function() {
this._updateSignInButtonSensitivity(this._promptEntry.text.length > 0);
}));
this._promptEntryActivateId =
this._promptEntry.clutter_text.connect('activate', function() {
hold.release();
});
},
_updateSensitivity: function(sensitive) {
this._promptEntry.reactive = sensitive;
this._promptEntry.clutter_text.editable = sensitive;
this._sessionMenuButton.updateSensitivity(sensitive);
this._updateSignInButtonSensitivity(sensitive);
},
_updateSignInButtonSensitivity: function(sensitive) {
if (this._signInButton) {
this._signInButton.reactive = sensitive;
this._signInButton.can_focus = sensitive;
}
},
_hidePrompt: function() {
if (this._promptEntryTextChangedId > 0) {
this._promptEntry.clutter_text.disconnect(this._promptEntryTextChangedId);
this._promptEntryTextChangedId = 0;
}
if (this._promptEntryActivateId > 0) {
this._promptEntry.clutter_text.disconnect(this._promptEntryActivateId);
this._promptEntryActivateId = 0;
}
this._setDefaultButtonWellMode(DefaultButtonWellMode.NONE, true);
this._promptBox.hide();
this._promptLoginHint.hide();
this._promptUser.set_child(null);
this._updateSensitivity(true);
this._promptEntry.set_text('');
this._sessionMenuButton.close();
this._promptLoginHint.hide();
this._buttonBox.remove_all_children();
this._signInButton = null;
this._cancelButton = null;
},
_askQuestion: function(verifier, serviceName, question, passwordChar) {
this._promptLabel.set_text(question);
this._updateSensitivity(true);
this._promptEntry.set_text('');
this._promptEntry.clutter_text.set_password_char(passwordChar);
let tasks = [function() {
return this._showPrompt(!!passwordChar);
},
function() {
let text = this._promptEntry.get_text();
this._updateSensitivity(false);
this._setDefaultButtonWellMode(DefaultButtonWellMode.SPINNER, false);
this._userVerifier.answerQuery(serviceName, text);
}];
let batch = new Batch.ConsecutiveBatch(this, tasks);
return batch.run();
},
_showRealmLoginHint: function(realmManager, hint) {
@ -931,34 +633,32 @@ const LoginDialog = new Lang.Class({
// Translators: this message is shown below the username entry field
// to clue the user in on how to login to the local network realm
this._showLoginHint(null, _("(e.g., user or %s)").format(hint));
this._authPrompt.setHint(_("(e.g., user or %s)").format(hint));
},
_askForUsernameAndLogIn: function() {
this._promptLabel.set_text(_("Username: "));
this._promptEntry.set_text('');
this._promptEntry.clutter_text.set_password_char('');
_askForUsernameAndBeginVerification: function() {
this._authPrompt.setPasswordChar('');
this._authPrompt.setQuestion(_("Username: "));
let realmManager = new Realmd.Manager();
let signalId = realmManager.connect('login-format-changed',
Lang.bind(this, this._showRealmLoginHint));
let realmSignalId = realmManager.connect('login-format-changed',
Lang.bind(this, this._showRealmLoginHint));
this._showRealmLoginHint(realmManager.loginFormat);
let tasks = [this._showPrompt,
let nextSignalId = this._authPrompt.connect('next',
Lang.bind(this, function() {
this._authPrompt.disconnect(nextSignalId);
this._authPrompt.updateSensitivity(false);
let answer = this._authPrompt.getAnswer();
this._authPrompt.clear();
this._authPrompt.cancelButton.show();
this._authPrompt.startSpinning();
this._authPrompt.begin({ userName: answer });
function() {
let userName = this._promptEntry.get_text();
this._promptEntry.reactive = false;
return this._beginVerificationForUser(userName);
},
function() {
realmManager.disconnect(signalId)
realmManager.release();
}];
let batch = new Batch.ConsecutiveBatch(this, tasks);
return batch.run();
realmManager.disconnect(realmSignalId)
realmManager.release();
}));
this._showPrompt();
},
_startSession: function(serviceName) {
@ -985,15 +685,9 @@ const LoginDialog = new Lang.Class({
},
_onSessionOpened: function(client, serviceName) {
if (!this._userVerifier.hasPendingMessages) {
this._authPrompt.finish(Lang.bind(this, function() {
this._startSession(serviceName);
} else {
let signalId = this._userVerifier.connect('no-more-messages',
Lang.bind(this, function() {
this._userVerifier.disconnect(signalId);
this._startSession(serviceName);
}));
}
}));
},
_waitForItemForUser: function(userName) {
@ -1125,39 +819,42 @@ const LoginDialog = new Lang.Class({
this._userSelectionBox.visible = expanded;
},
_hideUserListAndLogIn: function() {
_hideUserList: function() {
this._setUserListExpanded(false);
GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
this._askForUsernameAndLogIn();
if (this._userSelectionBox.visible)
GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
},
_hideUserListAskForUsernameAndBeginVerification: function() {
this._hideUserList();
this._askForUsernameAndBeginVerification();
},
_hideUserListAndBeginVerification: function() {
this._hideUserList();
this._authPrompt.begin();
},
_showUserList: function() {
this._hidePrompt();
this._authPrompt.hide();
this._sessionMenuButton.close();
this._authPrompt.cancelButton.show();
this._setUserListExpanded(true);
this._notListedButton.show();
this._userList.actor.grab_key_focus();
},
_beginVerificationForUser: function(userName) {
_beginVerificationForItem: function(item) {
this._authPrompt.setUser(item.user);
let userName = item.user.get_user_name();
let hold = new Batch.Hold();
this._userVerifier.begin(userName, hold);
this._verifyingUser = true;
this._authPrompt.begin({ userName: userName,
hold: hold });
return hold;
},
_beginVerificationForItem: function(item) {
let userWidget = new UserWidget.UserWidget(item.user);
this._promptUser.set_child(userWidget.actor);
let tasks = [function() {
let userName = item.user.get_user_name();
return this._beginVerificationForUser(userName);
}];
let batch = new Batch.ConsecutiveBatch(this, tasks);
return batch.run();
},
_onUserListActivated: function(activatedItem) {
let tasks = [function() {
return GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
@ -1216,7 +913,7 @@ const LoginDialog = new Lang.Class({
},
addCharacter: function(unichar) {
this._promptEntry.clutter_text.insert_unichar(unichar);
this._authPrompt.addCharacter(unichar);
},
});
Signals.addSignalMethods(LoginDialog.prototype);

View File

@ -6,20 +6,26 @@ const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Signals = imports.signals;
const St = imports.gi.St;
const Batch = imports.gdm.batch;
const Fprint = imports.gdm.fingerprint;
const Main = imports.ui.main;
const Params = imports.misc.params;
const ShellEntry = imports.ui.shellEntry;
const SmartcardManager = imports.misc.smartcardManager;
const Tweener = imports.ui.tweener;
const PASSWORD_SERVICE_NAME = 'gdm-password';
const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
const SMARTCARD_SERVICE_NAME = 'gdm-smartcard';
const FADE_ANIMATION_TIME = 0.16;
const CLONE_FADE_ANIMATION_TIME = 0.25;
const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
const PASSWORD_AUTHENTICATION_KEY = 'enable-password-authentication';
const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
const SMARTCARD_AUTHENTICATION_KEY = 'enable-smartcard-authentication';
const BANNER_MESSAGE_KEY = 'banner-message-enable';
const BANNER_MESSAGE_TEXT_KEY = 'banner-message-text';
const ALLOWED_FAILURES_KEY = 'allowed-failures';
@ -114,11 +120,34 @@ const ShellUserVerifier = new Lang.Class({
this._client = client;
this._settings = new Gio.Settings({ schema: LOGIN_SCREEN_SCHEMA });
this._settings.connect('changed',
Lang.bind(this, function() {
this._updateDefaultService();
}));
this._updateDefaultService();
this._fprintManager = new Fprint.FprintManager();
this._smartcardManager = SmartcardManager.getSmartcardManager();
// We check for smartcards right away, since an inserted smartcard
// at startup should result in immediately initiating authentication.
// This is different than fingeprint readers, where we only check them
// after a user has been picked.
this._checkForSmartcard();
this._smartcardManager.connect('smartcard-inserted',
Lang.bind(this, function() {
this._checkForSmartcard();
}));
this._smartcardManager.connect('smartcard-removed',
Lang.bind(this, function() {
this._checkForSmartcard();
}));
this._messageQueue = [];
this._messageQueueTimeoutId = 0;
this.hasPendingMessages = false;
this.reauthenticating = false;
this._failCounter = 0;
},
@ -127,6 +156,7 @@ const ShellUserVerifier = new Lang.Class({
this._cancellable = new Gio.Cancellable();
this._hold = hold;
this._userName = userName;
this.reauthenticating = false;
this._checkForFingerprintReader();
@ -144,8 +174,10 @@ const ShellUserVerifier = new Lang.Class({
if (this._cancellable)
this._cancellable.cancel();
if (this._userVerifier)
if (this._userVerifier) {
this._userVerifier.call_cancel_sync(null);
this.clear();
}
},
clear: function() {
@ -163,15 +195,14 @@ const ShellUserVerifier = new Lang.Class({
},
answerQuery: function(serviceName, answer) {
if (!this._userVerifier.hasPendingMessages) {
this._clearMessageQueue();
if (!this.hasPendingMessages) {
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
} else {
let signalId = this._userVerifier.connect('no-more-messages',
Lang.bind(this, function() {
this._userVerifier.disconnect(signalId);
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
}));
let signalId = this.connect('no-more-messages',
Lang.bind(this, function() {
this.disconnect(signalId);
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
}));
}
},
@ -241,6 +272,28 @@ const ShellUserVerifier = new Lang.Class({
}));
},
_checkForSmartcard: function() {
let smartcardDetected;
if (!this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY))
smartcardDetected = false;
else if (this.reauthenticating)
smartcardDetected = this._smartcardManager.hasInsertedLoginToken();
else
smartcardDetected = this._smartcardManager.hasInsertedTokens();
if (smartcardDetected != this.smartcardDetected) {
this.smartcardDetected = smartcardDetected;
if (this.smartcardDetected)
this._preemptingService = SMARTCARD_SERVICE_NAME;
else if (this._preemptingService == SMARTCARD_SERVICE_NAME)
this._preemptingService = null;
this.emit('smartcard-status-changed');
}
},
_reportInitError: function(where, error) {
logError(error, where);
this._hold.release();
@ -266,6 +319,7 @@ const ShellUserVerifier = new Lang.Class({
return;
}
this.reauthenticating = true;
this._connectSignals();
this._beginVerification();
this._hold.release();
@ -296,11 +350,35 @@ const ShellUserVerifier = new Lang.Class({
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
},
_getForegroundService: function() {
if (this._preemptingService)
return this._preemptingService;
return this._defaultService;
},
serviceIsForeground: function(serviceName) {
return serviceName == this._getForegroundService();
},
serviceIsDefault: function(serviceName) {
return serviceName == this._defaultService;
},
_updateDefaultService: function() {
if (this._settings.get_boolean(PASSWORD_AUTHENTICATION_KEY))
this._defaultService = PASSWORD_SERVICE_NAME;
else if (this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY))
this._defaultService = SMARTCARD_SERVICE_NAME;
else if (this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY))
this._defaultService = FINGERPRINT_SERVICE_NAME;
},
_beginVerification: function() {
this._hold.acquire();
if (this._userName) {
this._userVerifier.call_begin_verification_for_user(PASSWORD_SERVICE_NAME,
this._userVerifier.call_begin_verification_for_user(this._getForegroundService(),
this._userName,
this._cancellable,
Lang.bind(this, function(obj, result) {
@ -316,7 +394,7 @@ const ShellUserVerifier = new Lang.Class({
this._hold.release();
}));
if (this._haveFingerprintReader) {
if (this._haveFingerprintReader && !this.serviceIsForeground(FINGERPRINT_SERVICE_NAME)) {
this._hold.acquire();
this._userVerifier.call_begin_verification_for_user(FINGERPRINT_SERVICE_NAME,
@ -336,7 +414,7 @@ const ShellUserVerifier = new Lang.Class({
}));
}
} else {
this._userVerifier.call_begin_verification(PASSWORD_SERVICE_NAME,
this._userVerifier.call_begin_verification(this._getForegroundService(),
this._cancellable,
Lang.bind(this, function(obj, result) {
try {
@ -354,39 +432,36 @@ const ShellUserVerifier = new Lang.Class({
},
_onInfo: function(client, serviceName, info) {
// We don't display fingerprint messages, because they
// have words like UPEK in them. Instead we use the messages
// as a cue to display our own message.
if (serviceName == FINGERPRINT_SERVICE_NAME &&
if (this.serviceIsForeground(serviceName)) {
this._queueMessage(info, 'login-dialog-message-info');
} else if (serviceName == FINGERPRINT_SERVICE_NAME &&
this._haveFingerprintReader) {
// We don't show fingeprint messages directly since it's
// not the main auth service. Instead we use the messages
// as a cue to display our own message.
// Translators: this message is shown below the password entry field
// to indicate the user can swipe their finger instead
this.emit('show-login-hint', _("(or swipe finger)"));
} else if (serviceName == PASSWORD_SERVICE_NAME) {
this._queueMessage(info, 'login-dialog-message-info');
}
},
_onProblem: function(client, serviceName, problem) {
// we don't want to show auth failed messages to
// users who haven't enrolled their fingerprint.
if (serviceName != PASSWORD_SERVICE_NAME)
if (!this.serviceIsForeground(serviceName))
return;
this._queueMessage(problem, 'login-dialog-message-warning');
},
_onInfoQuery: function(client, serviceName, question) {
// We only expect questions to come from the main auth service
if (serviceName != PASSWORD_SERVICE_NAME)
if (!this.serviceIsForeground(serviceName))
return;
this.emit('ask-question', serviceName, question, '');
},
_onSecretInfoQuery: function(client, serviceName, secretQuestion) {
// We only expect secret requests to come from the main auth service
if (serviceName != PASSWORD_SERVICE_NAME)
if (!this.serviceIsForeground(serviceName))
return;
this.emit('ask-question', serviceName, secretQuestion, '\u25cf');
@ -395,6 +470,7 @@ const ShellUserVerifier = new Lang.Class({
_onReset: function() {
// Clear previous attempts to authenticate
this._failCounter = 0;
this._updateDefaultService();
this.emit('reset');
},
@ -423,24 +499,24 @@ const ShellUserVerifier = new Lang.Class({
this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY);
if (canRetry) {
if (!this._userVerifier.hasPendingMessages) {
if (!this.hasPendingMessages) {
this._retry();
} else {
let signalId = this._userVerifier.connect('no-more-messages',
Lang.bind(this, function() {
this._userVerifier.disconnect(signalId);
this._retry();
}));
let signalId = this.connect('no-more-messages',
Lang.bind(this, function() {
this.disconnect(signalId);
this._retry();
}));
}
} else {
if (!this._userVerifier.hasPendingMessages) {
if (!this.hasPendingMessages) {
this._cancelAndReset();
} else {
let signalId = this._userVerifier.connect('no-more-messages',
Lang.bind(this, function() {
this._userVerifier.disconnect(signalId);
this._cancelAndReset();
}));
let signalId = this.connect('no-more-messages',
Lang.bind(this, function() {
this.disconnect(signalId);
this._cancelAndReset();
}));
}
}
@ -451,7 +527,7 @@ const ShellUserVerifier = new Lang.Class({
// if the password service fails, then cancel everything.
// But if, e.g., fingerprint fails, still give
// password authentication a chance to succeed
if (serviceName == PASSWORD_SERVICE_NAME) {
if (this.serviceIsForeground(serviceName)) {
this._verificationFailed(true);
}

275
js/misc/objectManager.js Normal file
View File

@ -0,0 +1,275 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Params = imports.misc.params;
const Signals = imports.signals;
// Specified in the D-Bus specification here:
// http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager
const ObjectManagerIface = <interface name="org.freedesktop.DBus.ObjectManager">
<method name="GetManagedObjects">
<arg name="objects" type="a{oa{sa{sv}}}" direction="out"/>
</method>
<signal name="InterfacesAdded">
<arg name="objectPath" type="o"/>
<arg name="interfaces" type="a{sa{sv}}" />
</signal>
<signal name="InterfacesRemoved">
<arg name="objectPath" type="o"/>
<arg name="interfaces" type="as" />
</signal>
</interface>
const ObjectManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(ObjectManagerIface);
const ObjectManager = new Lang.Class({
Name: 'ObjectManager',
_init: function(params) {
params = Params.parse(params, { connection: null,
name: null,
objectPath: null,
knownInterfaces: null,
cancellable: null,
onLoaded: null });
this._connection = params.connection;
this._serviceName = params.name;
this._managerPath = params.objectPath;
this._cancellable = params.cancellable;
this._managerProxy = new Gio.DBusProxy({ g_connection: this._connection,
g_interface_name: ObjectManagerInfo.name,
g_interface_info: ObjectManagerInfo,
g_name: this._serviceName,
g_object_path: this._managerPath,
g_flags: Gio.DBusProxyFlags.NONE });
this._interfaceInfos = {};
this._objects = {};
this._interfaces = {};
this._pendingProxies = [];
this._onLoaded = params.onLoaded;
if (params.knownInterfaces)
this._registerInterfaces(params.knownInterfaces);
this._managerProxy.init_async(GLib.PRIORITY_DEFAULT,
this._cancellable,
Lang.bind(this, this._onManagerProxyLoaded));
},
_addInterface: function(objectPath, interfaceName, onFinished) {
let info = this._interfaceInfos[interfaceName];
if (!info)
return;
let proxy = new Gio.DBusProxy({ g_connection: this._connection,
g_name: this._serviceName,
g_object_path: objectPath,
g_interface_name: interfaceName,
g_interface_info: info,
g_flags: Gio.DBusProxyFlags.NONE });
this._pendingProxies.push(proxy);
proxy.init_async(GLib.PRIORITY_DEFAULT,
this._cancellable,
Lang.bind(this, function(initable, result) {
let index = this._pendingProxies.indexOf(proxy);
if (index >= 0)
this._pendingProxies.splice(index, 1);
let error = null;
try {
initable.init_finish(result);
} catch(e) {
logError(e, 'could not initialize proxy for interface ' + interfaceName);
if (onFinished)
onFinished();
return;
}
let isNewObject;
if (!this._objects[objectPath]) {
this._objects[objectPath] = {};
isNewObject = true;
} else {
isNewObject = false;
}
this._objects[objectPath][interfaceName] = proxy;
if (!this._interfaces[interfaceName])
this._interfaces[interfaceName] = [];
this._interfaces[interfaceName].push(proxy);
if (isNewObject)
this.emit('object-added', objectPath);
this.emit('interface-added', interfaceName, proxy);
if (onFinished)
onFinished();
}));
},
_removeInterface: function(objectPath, interfaceName) {
if (!this._objects[objectPath])
return;
let proxy = this._objects[objectPath][interfaceName];
if (this._interfaces[interfaceName]) {
let index = this._interfaces[interfaceName].indexOf(proxy);
if (index >= 0)
this._interfaces[interfaceName].splice(index, 1);
if (this._interfaces[interfaceName].length == 0)
delete this._interfaces[interfaceName];
}
this.emit('interface-removed', interfaceName, proxy);
this._objects[objectPath][interfaceName] = null;
if (Object.keys(this._objects[objectPath]).length == 0) {
delete this._objects[objectPath];
this.emit('object-removed', objectPath);
}
},
_onManagerProxyLoaded: function(initable, result) {
let error = null;
try {
initable.init_finish(result);
} catch(e) {
logError(e, 'could not initialize object manager for object ' + params.name);
if (this._onLoaded)
this._onLoaded();
return;
}
this._managerProxy.connectSignal('InterfacesAdded',
Lang.bind(this, function(objectManager, sender, [objectPath, interfaces]) {
let interfaceNames = Object.keys(interfaces);
for (let i = 0; i < interfaceNames.length; i++)
this._addInterface(objectPath, interfaceNames[i]);
}));
this._managerProxy.connectSignal('InterfacesRemoved',
Lang.bind(this, function(objectManager, sender, [objectPath, interfaceNames]) {
for (let i = 0; i < interfaceNames.length; i++)
this._removeInterface(objectPath, interfaceNames[i]);
}));
this._managerProxy.GetManagedObjectsRemote(Lang.bind(this, function(result, error) {
if (!result) {
if (error) {
logError(error, 'could not get remote objects for service ' + this._serviceName + ' path ' + this._managerPath);
}
if (this._onLoaded)
this._onLoaded();
return;
}
let [objects] = result;
if (Object.keys(this._interfaceInfos).length == 0) {
if (this._onLoaded)
this._onLoaded();
return;
}
let numLoadInhibitors = 0;
// First inhibitor is to prevent onLoaded from getting
// called until all interfaces have started being added.
// Subsequent inhibitors are to prevent onLoaded from getting
// called until all interfaces finish getting added.
numLoadInhibitors++;
let objectPaths = Object.keys(objects);
for (let i = 0; i < objectPaths.length; i++) {
let objectPath = objectPaths[i];
let object = objects[objectPath];
let interfaceNames = Object.keys(object);
for (let j = 0; j < interfaceNames.length; j++) {
let interfaceName = interfaceNames[j];
numLoadInhibitors++;
this._addInterface(objectPath,
interfaceName,
Lang.bind(this, function() {
numLoadInhibitors--;
if (numLoadInhibitors == 0) {
if (this._onLoaded)
this._onLoaded();
}
}));
}
}
numLoadInhibitors--;
if (numLoadInhibitors == 0) {
if (this._onLoaded)
this._onLoaded();
}
}));
},
_registerInterfaces: function(interfaces) {
for (let i = 0; i < interfaces.length; i++) {
let info = Gio.DBusInterfaceInfo.new_for_xml(interfaces[i]);
this._interfaceInfos[info.name] = info;
}
},
getProxy: function(objectPath, interfaceName) {
let object = this._objects[objectPath];
if (!object)
return null;
return object[interfaceName];
},
getProxiesForInterface: function(interfaceName) {
let proxyList = this._interfaces[interfaceName];
if (!proxyList)
return [];
return proxyList;
},
getAllProxies: function() {
let proxies = [];
let objectPaths = Object.keys(this._objects);
for (let i = 0; i < objectPaths.length; i++) {
let object = this._objects[objectPaths];
let interfaceNames = Object.keys(object);
for (let j = 0; i < interfaceNames.length; i++) {
let interfaceName = interfaceNames[i];
if (object[interfaceName])
proxies.push(object(interfaceName));
}
}
return proxies;
}
});
Signals.addSignalMethods(ObjectManager.prototype);

142
js/misc/smartcardManager.js Normal file
View File

@ -0,0 +1,142 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const ObjectManager = imports.misc.objectManager;
const _SMARTCARD_SERVICE_DBUS_NAME = "org.gnome.SettingsDaemon.Smartcard";
const SmartcardManagerIface = <interface name="org.gnome.SettingsDaemon.Smartcard.Manager">
<method name="GetLoginToken">
<arg name="token" type="o" direction="out"/>
</method>
<method name="GetInsertedTokens">
<arg name="tokens" type="ao" direction="out"/>
</method>
</interface>;
const SmartcardTokenIface = <interface name="org.gnome.SettingsDaemon.Smartcard.Token">
<property name="Name" type="s" access="read"/>
<property name="Driver" type="o" access="read"/>
<property name="IsInserted" type="b" access="read"/>
<property name="UsedToLogin" type="b" access="read"/>
</interface>;
const SmartcardDriverIface = <interface name="org.gnome.SettingsDaemon.Smartcard.Driver">
<property name="Library" type="s" access="read"/>
<property name="Description" type="s" access="read"/>
</interface>;
let _smartcardManager = null;
function getSmartcardManager() {
if (_smartcardManager == null)
_smartcardManager = new SmartcardManager();
return _smartcardManager;
}
const SmartcardManager = new Lang.Class({
Name: 'SmartcardManager',
_init: function() {
this._objectManager = new ObjectManager.ObjectManager({ connection: Gio.DBus.session,
name: _SMARTCARD_SERVICE_DBUS_NAME,
objectPath: '/org/gnome/SettingsDaemon/Smartcard',
knownInterfaces: [ SmartcardManagerIface,
SmartcardTokenIface,
SmartcardDriverIface ],
onLoaded: Lang.bind(this, this._onLoaded) });
this._insertedTokens = {};
this._removedTokens = {};
this._loginToken = null;
},
_onLoaded: function() {
let tokens = this._objectManager.getProxiesForInterface('org.gnome.SettingsDaemon.Smartcard.Token');
for (let i = 0; i < tokens.length; i++)
this._addToken(tokens[i]);
this._objectManager.connect('interface-added', Lang.bind(this, function(objectManager, interfaceName, proxy) {
if (interfaceName == 'org.gnome.SettingsDaemon.Smartcard.Token')
this._addToken(proxy);
}));
this._objectManager.connect('interface-removed', Lang.bind(this, function(objectManager, interfaceName, proxy) {
if (interfaceName == 'org.gnome.SettingsDaemon.Smartcard.Token')
this._removeToken(proxy);
}));
},
_updateToken: function(token) {
let objectPath = token.get_object_path();
delete this._insertedTokens[objectPath];
delete this._removedTokens[objectPath];
if (token.IsInserted)
this._insertedTokens[objectPath] = token;
else
this._removedTokens[objectPath] = token;
if (token.UsedToLogin)
this._loginToken = token;
},
_addToken: function(token) {
this._updateToken(token);
token.connect('g-properties-changed',
Lang.bind(this, function(proxy, properties) {
if ('IsInserted' in properties.deep_unpack()) {
this._updateToken(token);
if (token.IsInserted) {
this.emit('smartcard-inserted', token.Name);
} else {
this.emit('smartcard-removed', token.Name);
}
}
}));
// Emit a smartcard-inserted at startup if it's already plugged in
if (token.IsInserted)
this.emit('smartcard-inserted', token.Name);
},
_removeToken: function(token) {
let objectPath = token.get_object_path();
if (objectPath) {
if (this._removedTokens[objectPath] == token) {
delete this._removedTokens[objectPath];
}
if (this._insertedTokens[objectPath] == token) {
delete this._insertedTokens[objectPath];
this.emit('smartcard-removed', token.Name);
}
}
token.disconnectAll();
},
hasInsertedTokens: function() {
return Object.keys(this._insertedTokens).length > 0;
},
hasInsertedLoginToken: function() {
if (!this._loginToken)
return false;
if (!this._loginToken.IsInserted)
return false;
return true;
}
});
Signals.addSignalMethods(SmartcardManager.prototype);

View File

@ -436,8 +436,11 @@ const AppSwitcher = new Lang.Class({
this._arrows = [];
let windowTracker = Shell.WindowTracker.get_default();
let settings = new Gio.Settings({ schema: 'org.gnome.shell.app-switcher' });
let workspace = settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace()
: null;
let allWindows = global.display.get_tab_list(Meta.TabList.NORMAL,
global.screen, null);
global.screen, workspace);
// Construct the AppIcons, add to the popup
for (let i = 0; i < apps.length; i++) {
@ -447,7 +450,9 @@ const AppSwitcher = new Lang.Class({
appIcon.cachedWindows = allWindows.filter(function(w) {
return windowTracker.get_window_app (w) == appIcon.app;
});
this._addIcon(appIcon);
if (workspace == null || appIcon.cachedWindows.length > 0) {
this._addIcon(appIcon);
}
}
this._curApp = -1;

View File

@ -358,17 +358,6 @@ const ControlsBoxLayout = Lang.Class({
let totalSpacing = this.spacing * (childrenCount - 1);
return [maxMinWidth * childrenCount + totalSpacing,
maxNaturalWidth * childrenCount + totalSpacing];
},
vfunc_set_container: function(container) {
if(this._styleChangedId) {
this._container.disconnect(this._styleChangedId);
this._styleChangedId = 0;
}
if(container != null)
this._styleChangedId = container.connect('style-changed', Lang.bind(this,
function() { this.spacing = this._container.get_theme_node().get_length('spacing'); }));
this._container = container;
}
});
@ -416,8 +405,9 @@ const AppDisplay = new Lang.Class({
this.actor.add(this._viewStack, { expand: true });
let layout = new ControlsBoxLayout({ homogeneous: true });
this._controls = new St.Widget({ style_class: 'app-view-controls' });
this._controls.set_layout_manager(layout);
this._controls = new St.Widget({ style_class: 'app-view-controls',
layout_manager: layout });
layout.hookup_style(this._controls);
this.actor.add(new St.Bin({ child: this._controls }));

View File

@ -142,23 +142,20 @@ const BackgroundCache = new Lang.Class({
cancellable: null,
onFinished: null });
for (let i = 0; i < this._pendingFileLoads.length; i++) {
if (this._pendingFileLoads[i].filename == params.filename &&
this._pendingFileLoads[i].style == params.style) {
this._pendingFileLoads[i].callers.push({ shouldCopy: true,
monitorIndex: params.monitorIndex,
effects: params.effects,
onFinished: params.onFinished });
return;
}
}
let fileLoad = { filename: params.filename,
style: params.style,
shouldCopy: false,
monitorIndex: params.monitorIndex,
effects: params.effects,
onFinished: params.onFinished,
cancellable: new Gio.Cancellable(), };
this._pendingFileLoads.push(fileLoad);
this._pendingFileLoads.push({ filename: params.filename,
style: params.style,
callers: [{ shouldCopy: false,
monitorIndex: params.monitorIndex,
effects: params.effects,
onFinished: params.onFinished }] });
if (params.cancellable) {
params.cancellable.connect(Lang.bind(this, function(c) {
fileLoad.cancellable.cancel();
}));
}
let content = new Meta.Background({ meta_screen: global.screen,
monitor: params.monitorIndex,
@ -166,9 +163,19 @@ const BackgroundCache = new Lang.Class({
content.load_file_async(params.filename,
params.style,
params.cancellable,
fileLoad.cancellable,
Lang.bind(this,
function(object, result) {
if (fileLoad.cancellable.is_cancelled()) {
if (params.cancellable && params.cancellable.is_cancelled()) {
if (params.onFinished)
params.onFinished(null);
this._removePendingFileLoad(fileLoad);
return;
}
return;
}
try {
content.load_file_finish(result);
@ -178,22 +185,25 @@ const BackgroundCache = new Lang.Class({
content = null;
}
let needsCopy = false;
for (let i = 0; i < this._pendingFileLoads.length; i++) {
let pendingLoad = this._pendingFileLoads[i];
if (pendingLoad.filename != params.filename ||
pendingLoad.style != params.style)
continue;
for (let j = 0; j < pendingLoad.callers.length; j++) {
if (pendingLoad.callers[j].onFinished) {
if (content && pendingLoad.callers[j].shouldCopy) {
content = object.copy(pendingLoad.callers[j].monitorIndex,
pendingLoad.callers[j].effects);
if (pendingLoad.cancellable.is_cancelled())
continue;
}
pendingLoad.callers[j].onFinished(content);
pendingLoad.cancellable.cancel();
if (pendingLoad.onFinished) {
if (content && needsCopy) {
content = object.copy(pendingLoad.monitorIndex,
pendingLoad.effects);
}
needsCopy = true;
pendingLoad.onFinished(content);
}
this._pendingFileLoads.splice(i, 1);
@ -201,6 +211,15 @@ const BackgroundCache = new Lang.Class({
}));
},
_removePendingFileLoad: function(fileLoad) {
for (let i = 0; i < this._pendingFileLoads.length; i++) {
if (this._pendingFileLoads[i].cancellable == fileLoad.cancellable) {
this._pendingFileLoads.splice(i, 1);
break;
}
}
},
getImageContent: function(params) {
params = Params.parse(params, { monitorIndex: 0,
style: null,

View File

@ -58,14 +58,10 @@ const CtrlAltTabManager = new Lang.Class({
},
focusGroup: function(item, timestamp) {
if (item.focusCallback) {
if (item.focusCallback)
item.focusCallback(timestamp);
} else {
if (global.stage_input_mode == Shell.StageInputMode.NORMAL)
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
else
item.root.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
}
},
// Sort the items into a consistent order; panel first, tray last,
@ -136,8 +132,6 @@ const CtrlAltTabManager = new Lang.Class({
},
_focusWindows: function(timestamp) {
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
global.stage.key_focus = null;
global.screen.focus_default_window(timestamp);
}
});

View File

@ -10,6 +10,7 @@ const Clutter = imports.gi.Clutter;;
const Gettext = imports.gettext;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
@ -39,6 +40,22 @@ function _patchContainerClass(containerClass) {
};
}
function _patchLayoutClass(layoutClass, styleProps) {
if (styleProps)
layoutClass.prototype.hookup_style = function(container) {
container.connect('style-changed', Lang.bind(this, function() {
let node = container.get_theme_node();
for (let prop in styleProps)
this[prop] = node.get_length(styleProps[prop]);
}));
};
layoutClass.prototype.child_set = function(actor, props) {
let meta = this.get_child_meta(actor.get_parent(), actor);
for (let prop in props)
meta[prop] = props[prop];
};
}
function _makeLoggingFunc(func) {
return function() {
return func([].join.call(arguments, ', '));
@ -60,6 +77,12 @@ function init() {
_patchContainerClass(St.BoxLayout);
_patchContainerClass(St.Table);
_patchLayoutClass(Clutter.TableLayout, { row_spacing: 'spacing-rows',
column_spacing: 'spacing-columns' });
_patchLayoutClass(Clutter.GridLayout, { row_spacing: 'spacing-rows',
column_spacing: 'spacing-columns' });
_patchLayoutClass(Clutter.BoxLayout, { spacing: 'spacing' });
Clutter.Actor.prototype.toString = function() {
return St.describe_actor(this);
};

View File

@ -32,13 +32,9 @@ const GrabHelper = new Lang.Class({
this._actors = [];
this._capturedEventId = 0;
this._keyFocusNotifyId = 0;
this._focusWindowChangedId = 0;
this._ignoreRelease = false;
this._isUngrabbingCount = 0;
this._modalCount = 0;
this._grabFocusCount = 0;
},
// addActor:
@ -118,38 +114,36 @@ const GrabHelper = new Lang.Class({
// grab:
// @params: A bunch of parameters, see below
//
// Grabs the mouse and keyboard, according to the GrabHelper's
// parameters. If @newFocus is not %null, then the keyboard focus
// is moved to the first #StWidget:can-focus widget inside it.
// The general effect of a "grab" is to ensure that the passed in actor
// and all actors inside the grab get exclusive control of the mouse and
// keyboard, with the grab automatically being dropped if the user tries
// to dismiss it. The actor is passed in through @params.actor.
//
// The grab will automatically be dropped if:
// - The user clicks outside the grabbed actors
// - The user types Escape
// - The keyboard focus is moved outside the grabbed actors
// - A window is focused
// grab() can be called multiple times, with the scope of the grab being
// changed to a different actor every time. A nested grab does not have
// to have its grabbed actor inside the parent grab actors.
//
// If @params.actor is not null, then it will be focused as the
// new actor. If you attempt to grab an already focused actor, the
// request to be focused will be ignored. The actor will not be
// added to the grab stack, so do not call a paired ungrab().
// Grabs can be automatically dropped if the user tries to dismiss it
// in one of two ways: the user clicking outside the currently grabbed
// actor, or the user typing the Escape key.
//
// If @params contains { modal: true }, then grab() will push a modal
// on the owner of the GrabHelper. As long as there is at least one
// { modal: true } actor on the grab stack, the grab will be kept.
// When the last { modal: true } actor is ungrabbed, then the modal
// will be dropped. A modal grab can fail if there is already a grab
// in effect from aother application; in this case the function returns
// false and nothing happens. Non-modal grabs can never fail.
// If the user clicks outside the grabbed actors, and the clicked on
// actor is part of a previous grab in the stack, grabs will be popped
// until that grab is active. However, the click event will not be
// replayed to the actor.
//
// If @params contains { grabFocus: true }, then if you call grab()
// while the shell is outside the overview, it will set the stage
// input mode to %Shell.StageInputMode.FOCUSED, and ungrab() will
// revert it back, and re-focus the previously-focused window (if
// another window hasn't been explicitly focused before then).
// If the user types the Escape key, one grab from the grab stack will
// be popped.
//
// When a grab is popped by user interacting as described above, if you
// pass a callback as @params.onUngrab, it will be called with %true.
//
// If @params.focus is not null, we'll set the key focus directly
// to that actor instead of navigating in @params.actor. This is for
// use cases like menus, where we want to grab the menu actor, but keep
// focus on the clicked on menu item.
grab: function(params) {
params = Params.parse(params, { actor: null,
modal: false,
grabFocus: false,
focus: null,
onUngrab: null });
@ -162,24 +156,18 @@ const GrabHelper = new Lang.Class({
params.savedFocus = focus;
if (params.modal && !this._takeModalGrab())
return false;
if (params.grabFocus && !this._takeFocusGrab(hadFocus))
if (!this._takeModalGrab())
return false;
this._grabStack.push(params);
if (params.focus) {
params.focus.grab_key_focus();
} else if (newFocus && (hadFocus || params.grabFocus)) {
} else if (newFocus && hadFocus) {
if (!newFocus.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false))
newFocus.grab_key_focus();
}
if ((params.grabFocus || params.modal) && !this._capturedEventId)
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
return true;
},
@ -188,6 +176,8 @@ const GrabHelper = new Lang.Class({
if (firstGrab) {
if (!Main.pushModal(this._owner, this._modalParams))
return false;
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
}
this._modalCount++;
@ -199,56 +189,15 @@ const GrabHelper = new Lang.Class({
if (this._modalCount > 0)
return;
global.stage.disconnect(this._capturedEventId);
this._capturedEventId = 0;
this._ignoreRelease = false;
Main.popModal(this._owner);
global.sync_pointer();
},
_takeFocusGrab: function(hadFocus) {
let firstGrab = (this._grabFocusCount == 0);
if (firstGrab) {
let metaDisplay = global.screen.get_display();
this._grabbedFromKeynav = hadFocus;
this._preGrabInputMode = global.stage_input_mode;
if (this._preGrabInputMode == Shell.StageInputMode.NORMAL)
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
this._keyFocusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
this._focusWindowChangedId = metaDisplay.connect('notify::focus-window', Lang.bind(this, this._focusWindowChanged));
}
this._grabFocusCount++;
return true;
},
_releaseFocusGrab: function() {
this._grabFocusCount--;
if (this._grabFocusCount > 0)
return;
if (this._keyFocusNotifyId > 0) {
global.stage.disconnect(this._keyFocusNotifyId);
this._keyFocusNotifyId = 0;
}
if (this._focusWindowChangedId > 0) {
let metaDisplay = global.screen.get_display();
metaDisplay.disconnect(this._focusWindowChangedId);
this._focusWindowChangedId = 0;
}
let prePopInputMode = global.stage_input_mode;
if (this._grabbedFromKeynav) {
if (this._preGrabInputMode == Shell.StageInputMode.FOCUSED &&
prePopInputMode != Shell.StageInputMode.FULLSCREEN)
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
}
global.screen.focus_default_window(global.display.get_current_time_roundtrip());
},
// ignoreRelease:
//
// Make sure that the next button release event evaluated by the
@ -262,10 +211,14 @@ const GrabHelper = new Lang.Class({
// ungrab:
// @params: The parameters for the grab; see below.
//
// Pops an actor from the grab stack, potentially dropping the grab.
// Pops @params.actor from the grab stack, potentially dropping
// the grab. If the actor is not on the grab stack, this call is
// ignored with no ill effects.
//
// If the actor that was popped from the grab stack was not the actor
// That was passed in, this call is ignored.
// If the actor is not at the top of the grab stack, grabs are
// popped until the grabbed actor is at the top of the grab stack.
// The onUngrab callback for every grab is called for every popped
// grab with the parameter %false.
ungrab: function(params) {
params = Params.parse(params, { actor: this.currentGrab.actor,
isUser: false });
@ -274,14 +227,6 @@ const GrabHelper = new Lang.Class({
if (grabStackIndex < 0)
return;
// We may get key focus changes when calling onUngrab, which
// would cause an extra ungrab() on the next actor in the
// stack, which is wrong. Ignore key focus changes during the
// ungrab, and restore the saved key focus ourselves afterwards.
// We use a count as ungrab() may be re-entrant, as onUngrab()
// may ungrab additional actors.
this._isUngrabbingCount++;
let focus = global.stage.key_focus;
let hadFocus = focus && this._isWithinGrabbedActor(focus);
@ -296,18 +241,7 @@ const GrabHelper = new Lang.Class({
if (poppedGrab.onUngrab)
poppedGrab.onUngrab(params.isUser);
if (poppedGrab.modal)
this._releaseModalGrab();
if (poppedGrab.grabFocus)
this._releaseFocusGrab();
}
if (!this.grabbed && this._capturedEventId > 0) {
global.stage.disconnect(this._capturedEventId);
this._capturedEventId = 0;
this._ignoreRelease = false;
this._releaseModalGrab();
}
if (hadFocus) {
@ -315,8 +249,6 @@ const GrabHelper = new Lang.Class({
if (poppedGrab.savedFocus)
poppedGrab.savedFocus.grab_key_focus();
}
this._isUngrabbingCount--;
},
_onCapturedEvent: function(actor, event) {
@ -337,9 +269,6 @@ const GrabHelper = new Lang.Class({
return true;
}
if (!button && this._modalCount == 0)
return false;
if (this._isWithinGrabbedActor(event.get_source()))
return false;
@ -356,21 +285,6 @@ const GrabHelper = new Lang.Class({
return true;
}
return this._modalCount > 0;
return true;
},
_onKeyFocusChanged: function() {
if (this._isUngrabbingCount > 0)
return;
let focus = global.stage.key_focus;
if (!focus || !this._isWithinGrabbedActor(focus))
this.ungrab({ isUser: true });
},
_focusWindowChanged: function() {
let metaDisplay = global.screen.get_display();
if (metaDisplay.focus_window != null)
this.ungrab({ isUser: true });
}
});

View File

@ -528,17 +528,10 @@ const LayoutManager = new Lang.Class({
get focusIndex() {
let i = Main.layoutManager.primaryIndex;
if (global.stage_input_mode == Shell.StageInputMode.FOCUSED ||
global.stage_input_mode == Shell.StageInputMode.FULLSCREEN) {
let focusActor = global.stage.key_focus;
if (focusActor)
i = this.findIndexForActor(focusActor);
} else {
let focusWindow = global.display.focus_window;
if (focusWindow)
i = focusWindow.get_monitor();
}
if (global.stage.key_focus != null)
i = this.findIndexForActor(global.stage.key_focus);
else if (global.display.focus_window != null)
i = global.display.focus_window.get_monitor();
return i;
},
@ -596,9 +589,13 @@ const LayoutManager = new Lang.Class({
// screen. So, we set no_clear_hint at the end of the animation.
_prepareStartupAnimation: function() {
// Set ourselves to FULLSCREEN input mode while the animation is running
// so events don't get delivered to X11 windows (which are distorted by the animation)
global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
// During the initial transition, add a simple actor to block all events,
// so they don't get delivered to X11 windows that have been transformed.
this._coverPane = new Clutter.Actor({ opacity: 0,
width: global.screen_width,
height: global.screen_height,
reactive: true });
this.addChrome(this._coverPane);
if (Main.sessionMode.isGreeter) {
this.panelBox.translation_y = -this.panelBox.height;
@ -668,7 +665,8 @@ const LayoutManager = new Lang.Class({
// we no longer need to clear the stage
global.stage.no_clear_hint = true;
global.stage_input_mode = Shell.StageInputMode.NORMAL;
this._coverPane.destroy();
this._coverPane = null;
this._systemBackground.actor.destroy();
this._systemBackground = null;

View File

@ -849,8 +849,9 @@ const LookingGlass = new Lang.Class({
this._updateFont();
// We want it to appear to slide out from underneath the panel
Main.layoutManager.panelBox.add_actor(this.actor);
this.actor.lower_bottom();
Main.uiGroup.add_actor(this.actor);
Main.uiGroup.set_child_below_sibling(this.actor,
Main.layoutManager.panelBox);
Main.layoutManager.panelBox.connect('allocation-changed',
Lang.bind(this, this._queueResize));
Main.layoutManager.keyboardBox.connect('allocation-changed',
@ -1072,7 +1073,7 @@ const LookingGlass = new Lang.Class({
let availableHeight = primary.height - Main.layoutManager.keyboardBox.height;
let myHeight = Math.min(primary.height * 0.7, availableHeight * 0.9);
this.actor.x = (primary.width - myWidth) / 2;
this._hiddenY = this.actor.get_parent().height - myHeight - 4; // -4 to hide the top corners
this._hiddenY = Main.layoutManager.panelBox.height - myHeight - 4; // -4 to hide the top corners
this._targetY = this._hiddenY + myHeight;
this.actor.y = this._hiddenY;
this.actor.width = myWidth;

View File

@ -356,8 +356,6 @@ function pushModal(actor, params) {
Meta.disable_unredirect_for_screen(global.screen);
}
global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
modalCount += 1;
let actorDestroyId = actor.connect('destroy', function() {
let index = _findModal(actor);
@ -406,7 +404,6 @@ function popModal(actor, timestamp) {
if (focusIndex < 0) {
global.stage.set_key_focus(null);
global.end_modal(timestamp);
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
keybindingMode = Shell.KeyBindingMode.NORMAL;
throw new Error('incorrect pop');
@ -454,7 +451,6 @@ function popModal(actor, timestamp) {
return;
global.end_modal(timestamp);
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
Meta.enable_unredirect_for_screen(global.screen);
keybindingMode = Shell.KeyBindingMode.NORMAL;
}

View File

@ -97,6 +97,65 @@ function _fixMarkup(text, allowMarkup) {
return GLib.markup_escape_text(text, -1);
}
const FocusGrabber = new Lang.Class({
Name: 'FocusGrabber',
_init: function(actor) {
this._actor = actor;
this._prevKeyFocusActor = null;
this._focusActorChangedId = 0;
this._focused = false;
},
grabFocus: function() {
if (this._focused)
return;
this._prevFocusedWindow = global.display.focus_window;
this._prevKeyFocusActor = global.stage.get_key_focus();
this._focusActorChangedId = global.stage.connect('notify::key-focus', Lang.bind(this, this._focusActorChanged));
if (!this._actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false))
this._actor.grab_key_focus();
this._focused = true;
},
_focusUngrabbed: function() {
if (!this._focused)
return false;
if (this._focusActorChangedId > 0) {
global.stage.disconnect(this._focusActorChangedId);
this._focusActorChangedId = 0;
}
this._focused = false;
return true;
},
_focusActorChanged: function() {
let focusedActor = global.stage.get_key_focus();
if (!focusedActor || !this._actor.contains(focusedActor))
this._focusUngrabbed();
},
ungrabFocus: function() {
if (!this._focusUngrabbed())
return;
if (this._prevKeyFocusActor) {
global.stage.set_key_focus(this._prevKeyFocusActor);
this._prevKeyFocusActor = null;
} else {
let focusedActor = global.stage.get_key_focus();
if (focusedActor && this._actor.contains(focusedActor))
global.stage.set_key_focus(null);
}
}
});
const URLHighlighter = new Lang.Class({
Name: 'URLHighlighter',
@ -1365,6 +1424,10 @@ const SummaryItem = new Lang.Class({
this.notificationStackWidget.add_actor(this.notificationStackView);
this.closeButton = Util.makeCloseButton();
this.closeButton.connect('clicked', Lang.bind(this, function() {
source.destroy();
source.emit('done-displaying-content');
}));
this.notificationStackWidget.add_actor(this.closeButton);
this._stackedNotifications = [];
@ -1574,6 +1637,7 @@ const MessageTray = new Lang.Class({
this._notificationBin.set_y_align(Clutter.ActorAlign.START);
this._notificationWidget.add_actor(this._notificationBin);
this._notificationWidget.hide();
this._notificationFocusGrabber = new FocusGrabber(this._notificationWidget);
this._notificationQueue = [];
this._notification = null;
this._notificationClickedId = 0;
@ -1623,7 +1687,6 @@ const MessageTray = new Lang.Class({
{ keybindingMode: Shell.KeyBindingMode.MESSAGE_TRAY });
this._grabHelper.addActor(this._summaryBoxPointer.actor);
this._grabHelper.addActor(this.actor);
this._grabHelper.addActor(this._notificationWidget);
Main.layoutManager.connect('keyboard-visible-changed', Lang.bind(this, this._onKeyboardVisibleChanged));
@ -1749,7 +1812,6 @@ const MessageTray = new Lang.Class({
let [x, y, mask] = global.get_pointer();
this._contextMenu.setPosition(Math.round(x), Math.round(y));
this._grabHelper.grab({ actor: this._contextMenu.actor,
modal: true,
onUngrab: Lang.bind(this, function () {
this._contextMenu.close(BoxPointer.PopupAnimation.FULL);
})
@ -1857,9 +1919,8 @@ const MessageTray = new Lang.Class({
_closeNotification: function() {
if (this._notificationState == State.SHOWN) {
this._closeButton.hide();
this._notificationClosed = true;
this._updateState();
this._notificationClosed = false;
this._notification.emit('done-displaying');
this._notification.destroy();
}
},
@ -2316,7 +2377,6 @@ const MessageTray = new Lang.Class({
_showTray: function() {
if (!this._grabHelper.grab({ actor: this.actor,
modal: true,
onUngrab: Lang.bind(this, this._escapeTray) })) {
this._traySummoned = false;
return false;
@ -2520,19 +2580,7 @@ const MessageTray = new Lang.Class({
},
_hideNotification: function() {
// HACK!
// There seems to be a reentrancy issue in calling .ungrab() here,
// which causes _updateState to be called before _notificationState
// becomes HIDING. That hides the notification again, nullifying the
// object but not setting _notificationState (and that's the weird part)
// As then _notificationState is stuck into SHOWN but _notification
// is null, every new _updateState fails and the message tray is
// lost forever.
//
// See more at https://bugzilla.gnome.org/show_bug.cgi?id=683986
this._notificationState = State.HIDING;
this._grabHelper.ungrab({ actor: this._notification.actor });
this._notificationFocusGrabber.ungrabFocus();
if (this._notificationExpandedId) {
this._notification.disconnect(this._notificationExpandedId);
@ -2630,8 +2678,7 @@ const MessageTray = new Lang.Class({
},
_ensureNotificationFocused: function() {
this._grabHelper.grab({ actor: this._notification.actor,
grabFocus: true });
this._notificationFocusGrabber.grabFocus();
},
_onSourceDoneDisplayingContent: function(source, closeTray) {
@ -2654,11 +2701,9 @@ const MessageTray = new Lang.Class({
let closeButton = summaryItem.closeButton;
closeButton.show();
this._summaryBoxPointerCloseClickedId = closeButton.connect('clicked', Lang.bind(this, this._hideSummaryBoxPointer));
summaryItem.prepareNotificationStackForShowing();
} else if (this._clickedSummaryItemMouseButton == 3) {
child = summaryItem.rightClickMenu;
this._summaryBoxPointerCloseClickedId = 0;
}
// If the user clicked the middle mouse button, or the item
@ -2674,7 +2719,6 @@ const MessageTray = new Lang.Class({
this._summaryBoxPointer.bin.child = child;
this._grabHelper.grab({ actor: this._summaryBoxPointer.bin.child,
modal: true,
onUngrab: Lang.bind(this, this._onSummaryBoxPointerUngrabbed) });
this._summaryBoxPointer.actor.opacity = 0;
@ -2753,10 +2797,7 @@ const MessageTray = new Lang.Class({
this._summaryBoxPointerItem.disconnect(this._summaryBoxPointerContentUpdatedId);
this._summaryBoxPointerContentUpdatedId = 0;
}
if (this._summaryBoxPointerCloseClickedId != 0) {
this._summaryBoxPointerItem.closeButton.disconnect(this._summaryBoxPointerCloseClickedId);
this._summaryBoxPointerCloseClickedId = 0;
}
if (this._sourceDoneDisplayingId) {
this._summaryBoxPointerItem.source.disconnect(this._sourceDoneDisplayingId);
this._sourceDoneDisplayingId = 0;

View File

@ -79,9 +79,8 @@ const ModalDialog = new Lang.Class({
this.dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
vertical: true });
if (params.styleClass != null) {
if (params.styleClass != null)
this.dialogLayout.add_style_class_name(params.styleClass);
}
if (!this._shellReactive) {
this._lightbox = new Lightbox.Lightbox(this._group,
@ -357,8 +356,9 @@ const ModalDialog = new Lang.Class({
if (this._savedKeyFocus) {
this._savedKeyFocus.grab_key_focus();
this._savedKeyFocus = null;
} else
} else {
this._initialKeyFocus.grab_key_focus();
}
if (!this._shellReactive)
this._eventBlocker.lower_bottom();

View File

@ -717,8 +717,8 @@ const Source = new Lang.Class({
this.notifications.length > 0)
return false;
let id = global.connect('notify::stage-input-mode', Lang.bind(this, function () {
global.disconnect(id);
let id = global.stage.connect('deactivate', Lang.bind(this, function () {
global.stage.disconnect(id);
this.trayIcon.click(event);
}));

View File

@ -148,7 +148,7 @@ const Overview = new Lang.Class({
// Dash elements, or mouseover handlers in the workspaces.
this._coverPane = new Clutter.Actor({ opacity: 0,
reactive: true });
this._overview.add_actor(this._coverPane);
this._stack.add_actor(this._coverPane);
this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; }));
this._stack.add_actor(this._overview);

View File

@ -297,13 +297,16 @@ const AppMenuButton = new Lang.Class({
this._updateIconBoxClip();
},
_syncIcon: function() {
let icon = this._targetApp.get_faded_icon(2 * PANEL_ICON_SIZE, this._iconBox.text_direction);
this._iconBox.set_child(icon);
},
_onIconThemeChanged: function() {
if (this._iconBox.child == null)
return;
this._iconBox.child.destroy();
let icon = this._targetApp.get_faded_icon(2 * PANEL_ICON_SIZE);
this._iconBox.set_child(icon);
this._syncIcon();
},
_updateIconBoxClip: function() {
@ -449,7 +452,7 @@ const AppMenuButton = new Lang.Class({
// If the app has just lost focus to the panel, pretend
// nothing happened; otherwise you can't keynav to the
// app menu.
if (global.stage_input_mode == Shell.StageInputMode.FOCUSED)
if (global.stage.key_focus != null)
return;
}
this._sync();
@ -526,12 +529,10 @@ const AppMenuButton = new Lang.Class({
}
this._targetApp = targetApp;
let icon = targetApp.get_faded_icon(2 * PANEL_ICON_SIZE);
this._label.setText(targetApp.get_name());
this.setName(targetApp.get_name());
this._iconBox.set_child(icon);
this._syncIcon();
this._iconBox.show();
if (targetApp.get_state() == Shell.AppState.STARTING ||
@ -857,7 +858,7 @@ const PANEL_ITEM_IMPLEMENTATIONS = {
'lockScreen': imports.ui.status.lockScreenMenu.Indicator,
'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
'powerMenu': imports.gdm.powerMenu.PowerMenuButton,
'userMenu': imports.ui.userMenu.UserMenuButton
'system': imports.ui.status.system.Indicator,
};
if (Config.HAVE_BLUETOOTH)

View File

@ -110,6 +110,7 @@ const Button = new Lang.Class({
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress));
this.actor.connect('notify::visible', Lang.bind(this, this._onVisibilityChanged));
if (dontCreateMenu)
this.menu = new PopupMenu.PopupDummyMenu(this.actor);
@ -183,6 +184,14 @@ const Button = new Lang.Class({
return false;
},
_onVisibilityChanged: function() {
if (!this.menu)
return;
if (!this.actor.visible)
this.menu.close();
},
_onMenuKeyPress: function(actor, event) {
if (global.focus_manager.navigate_from_event(event))
return true;

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,6 @@ const Lang = imports.lang;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const FileUtils = imports.misc.fileUtils;
const Search = imports.ui.search;
const KEY_FILE_GROUP = 'Shell Search Provider';
@ -60,108 +59,114 @@ var SearchProviderProxy = Gio.DBusProxy.makeProxyWrapper(SearchProviderIface);
var SearchProvider2Proxy = Gio.DBusProxy.makeProxyWrapper(SearchProvider2Iface);
function loadRemoteSearchProviders(addProviderCallback) {
let data = { loadedProviders: [],
objectPaths: {},
addProviderCallback: addProviderCallback };
FileUtils.collectFromDatadirsAsync('search-providers',
{ loadedCallback: remoteProvidersLoaded,
processFile: loadRemoteSearchProvider,
data: data
});
}
let objectPaths = {};
let loadedProviders = [];
function loadRemoteSearchProvider(file, info, data) {
let keyfile = new GLib.KeyFile();
let path = file.get_path();
function loadRemoteSearchProvider(file) {
let keyfile = new GLib.KeyFile();
let path = file.get_path();
try {
keyfile.load_from_file(path, 0);
} catch(e) {
return;
}
if (!keyfile.has_group(KEY_FILE_GROUP))
return;
let remoteProvider;
try {
let group = KEY_FILE_GROUP;
let busName = keyfile.get_string(group, 'BusName');
let objectPath = keyfile.get_string(group, 'ObjectPath');
if (data.objectPaths[objectPath])
return;
let appInfo = null;
try {
let desktopId = keyfile.get_string(group, 'DesktopId');
appInfo = Gio.DesktopAppInfo.new(desktopId);
} catch (e) {
log('Ignoring search provider ' + path + ': missing DesktopId');
keyfile.load_from_file(path, 0);
} catch(e) {
return;
}
let version = '1';
if (!keyfile.has_group(KEY_FILE_GROUP))
return;
let remoteProvider;
try {
version = keyfile.get_string(group, 'Version');
} catch (e) {
// ignore error
let group = KEY_FILE_GROUP;
let busName = keyfile.get_string(group, 'BusName');
let objectPath = keyfile.get_string(group, 'ObjectPath');
if (objectPaths[objectPath])
return;
let appInfo = null;
try {
let desktopId = keyfile.get_string(group, 'DesktopId');
appInfo = Gio.DesktopAppInfo.new(desktopId);
} catch (e) {
log('Ignoring search provider ' + path + ': missing DesktopId');
return;
}
let version = '1';
try {
version = keyfile.get_string(group, 'Version');
} catch (e) {
// ignore error
}
if (version >= 2)
remoteProvider = new RemoteSearchProvider2(appInfo, busName, objectPath);
else
remoteProvider = new RemoteSearchProvider(appInfo, busName, objectPath);
objectPaths[objectPath] = remoteProvider;
loadedProviders.push(remoteProvider);
} catch(e) {
log('Failed to add search provider %s: %s'.format(path, e.toString()));
}
if (version >= 2)
remoteProvider = new RemoteSearchProvider2(appInfo, busName, objectPath);
else
remoteProvider = new RemoteSearchProvider(appInfo, busName, objectPath);
data.objectPaths[objectPath] = remoteProvider;
data.loadedProviders.push(remoteProvider);
} catch(e) {
log('Failed to add search provider %s: %s'.format(path, e.toString()));
}
}
function remoteProvidersLoaded(loadState) {
let dataDirs = GLib.get_system_data_dirs();
dataDirs.forEach(function(dataDir) {
let path = GLib.build_filenamev([dataDir, 'gnome-shell', 'search-providers']);
let dir = Gio.File.new_for_path(path);
let fileEnum;
try {
fileEnum = dir.enumerate_children('standard::name,standard::type',
Gio.FileQueryInfoFlags.NONE, null);
} catch (e) {
fileEnum = null;
}
if (fileEnum != null) {
let info;
while ((info = fileEnum.next_file(null)))
loadRemoteSearchProvider(fileEnum.get_child(info));
}
});
let searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
let sortOrder = searchSettings.get_strv('sort-order');
// Special case gnome-control-center to be always active and always first
sortOrder.unshift('gnome-control-center.desktop');
loadState.loadedProviders.sort(
function(providerA, providerB) {
let idxA, idxB;
let appIdA, appIdB;
loadedProviders.sort(function(providerA, providerB) {
let idxA, idxB;
let appIdA, appIdB;
appIdA = providerA.appInfo.get_id();
appIdB = providerB.appInfo.get_id();
appIdA = providerA.appInfo.get_id();
appIdB = providerB.appInfo.get_id();
idxA = sortOrder.indexOf(appIdA);
idxB = sortOrder.indexOf(appIdB);
idxA = sortOrder.indexOf(appIdA);
idxB = sortOrder.indexOf(appIdB);
// if no provider is found in the order, use alphabetical order
if ((idxA == -1) && (idxB == -1)) {
let nameA = providerA.appInfo.get_name();
let nameB = providerB.appInfo.get_name();
// if no provider is found in the order, use alphabetical order
if ((idxA == -1) && (idxB == -1)) {
let nameA = providerA.appInfo.get_name();
let nameB = providerB.appInfo.get_name();
return GLib.utf8_collate(nameA, nameB);
}
return GLib.utf8_collate(nameA, nameB);
}
// if providerA isn't found, it's sorted after providerB
if (idxA == -1)
return 1;
// if providerA isn't found, it's sorted after providerB
if (idxA == -1)
return 1;
// if providerB isn't found, it's sorted after providerA
if (idxB == -1)
return -1;
// if providerB isn't found, it's sorted after providerA
if (idxB == -1)
return -1;
// finally, if both providers are found, return their order in the list
return (idxA - idxB);
});
// finally, if both providers are found, return their order in the list
return (idxA - idxB);
});
loadState.loadedProviders.forEach(
function(provider) {
loadState.addProviderCallback(provider);
});
loadedProviders.forEach(addProviderCallback);
}
const RemoteSearchProvider = new Lang.Class({

View File

@ -23,6 +23,7 @@ const Main = imports.ui.main;
const Overview = imports.ui.overview;
const MessageTray = imports.ui.messageTray;
const ShellDBus = imports.ui.shellDBus;
const SmartcardManager = imports.misc.smartcardManager;
const Tweener = imports.ui.tweener;
const Util = imports.misc.util;
@ -516,6 +517,13 @@ const ScreenShield = new Lang.Class({
this._screenSaverDBus = new ShellDBus.ScreenSaverDBus(this);
this._smartcardManager = SmartcardManager.getSmartcardManager();
this._smartcardManager.connect('smartcard-inserted',
Lang.bind(this, function() {
if (this._isLocked)
this._liftShield(true, 0);
}));
this._inhibitor = null;
this._aboutToSuspend = false;
this._loginManager = LoginManager.getLoginManager();
@ -764,19 +772,6 @@ const ScreenShield = new Lang.Class({
}
}
if (!this._becomeModal()) {
// We could not become modal, so we can't activate the
// screenshield. The user is probably very upset at this
// point, but any application using global grabs is broken
// Just tell him to stop using this app
//
// XXX: another option is to kick the user into the gdm login
// screen, where we're not affected by grabs
Main.notifyError(_("Unable to lock"),
_("Lock was blocked by an application"));
return;
}
if (this._lightbox.actor.visible ||
this._isActive) {
// We're either shown and active, or in the process of
@ -790,6 +785,19 @@ const ScreenShield = new Lang.Class({
return;
}
if (!this._becomeModal()) {
// We could not become modal, so we can't activate the
// screenshield. The user is probably very upset at this
// point, but any application using global grabs is broken
// Just tell him to stop using this app
//
// XXX: another option is to kick the user into the gdm login
// screen, where we're not affected by grabs
Main.notifyError(_("Unable to lock"),
_("Lock was blocked by an application"));
return;
}
this._lightbox.show();
if (this._activationTime == 0)
@ -927,7 +935,6 @@ const ScreenShield = new Lang.Class({
}
this._dialog.connect('failed', Lang.bind(this, this._onUnlockFailed));
this._dialog.connect('unlocked', Lang.bind(this, this._onUnlockSucceded));
}
this._dialog.allowCancel = allowCancel;
@ -937,10 +944,6 @@ const ScreenShield = new Lang.Class({
this._resetLockScreen(true, false);
},
_onUnlockSucceded: function() {
this.deactivate(true);
},
_resetLockScreen: function(animateLockScreen, animateLockDialog) {
// Don't reset the lock screen unless it is completely hidden
// This prevents the shield going down if the lock-delay timeout
@ -1126,6 +1129,12 @@ const ScreenShield = new Lang.Class({
},
deactivate: function(animate) {
this._dialog.finish(Lang.bind(this, function() {
this._finishDeactivate(animate);
}));
},
_finishDeactivate: function(animate) {
this._hideLockScreen(animate, 0);
if (this._hasLockScreen)

View File

@ -31,7 +31,7 @@ const SearchSystem = new Lang.Class({
let remoteIndex = this._remoteProviders.indexOf(provider);
if (remoteIndex != -1)
this._remoteProviders.splice(index, 1);
this._remoteProviders.splice(remoteIndex, 1);
},
getProviders: function() {

View File

@ -60,7 +60,7 @@ const _modes = {
unlockDialog: undefined,
components: ['polkitAgent', 'telepathyClient'],
panel: {
left: ['userMenu'],
left: [],
center: [],
right: ['lockScreen']
},
@ -72,7 +72,7 @@ const _modes = {
unlockDialog: undefined,
components: ['polkitAgent', 'telepathyClient'],
panel: {
left: ['userMenu'],
left: [],
center: [],
right: ['a11y', 'keyboard', 'lockScreen']
},
@ -97,7 +97,7 @@ const _modes = {
left: ['activities', 'appMenu'],
center: ['dateMenu'],
right: ['a11y', 'keyboard', 'volume', 'bluetooth',
'network', 'battery', 'userMenu']
'network', 'battery', 'system']
}
}
};

View File

@ -41,6 +41,7 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
<signal name="AcceleratorActivated">
<arg name="action" type="u" />
<arg name="deviceid" type="u" />
<arg name="timestamp" type="u" />
</signal>
<property name="Mode" type="s" access="read" />
<property name="OverviewActive" type="b" access="readwrite" />
@ -79,8 +80,8 @@ const GnomeShell = new Lang.Class({
this._grabbers = new Hash.Map();
global.display.connect('accelerator-activated', Lang.bind(this,
function(display, action, deviceid) {
this._emitAcceleratorActivated(action, deviceid);
function(display, action, deviceid, timestamp) {
this._emitAcceleratorActivated(action, deviceid, timestamp);
}));
},
@ -166,7 +167,7 @@ const GnomeShell = new Lang.Class({
return invocation.return_value(GLib.Variant.new('(b)', [ungrabSucceeded]));
},
_emitAcceleratorActivated: function(action, deviceid) {
_emitAcceleratorActivated: function(action, deviceid, timestamp) {
let destination = this._grabbedAccelerators.get(action);
if (!destination)
return;
@ -177,7 +178,7 @@ const GnomeShell = new Lang.Class({
this._dbusImpl.get_object_path(),
info ? info.name : null,
'AcceleratorActivated',
GLib.Variant.new('(uu)', [action, deviceid]));
GLib.Variant.new('(uuu)', [action, deviceid, timestamp]));
},
_grabAcceleratorForSender: function(accelerator, flags, sender) {

View File

@ -98,21 +98,24 @@ const Slider = new Lang.Class({
},
_startDragging: function(actor, event) {
if (this._dragging) // don't allow two drags at the same time
this.startDragging(event);
},
startDragging: function(event) {
if (this._dragging)
return false;
this._dragging = true;
// FIXME: we should only grab the specific device that originated
// the event, but for some weird reason events are still delivered
// outside the slider if using clutter_grab_pointer_for_device
Clutter.grab_pointer(this.actor);
let device = event.get_device();
device.grab(this.actor);
this._grabbedDevice = device;
this._releaseId = this.actor.connect('button-release-event', Lang.bind(this, this._endDragging));
this._motionId = this.actor.connect('motion-event', Lang.bind(this, this._motionEvent));
let absX, absY;
[absX, absY] = event.get_coords();
this._moveHandle(absX, absY);
return true;
},
@ -121,7 +124,8 @@ const Slider = new Lang.Class({
this.actor.disconnect(this._releaseId);
this.actor.disconnect(this._motionId);
Clutter.ungrab_pointer();
this._grabbedDevice.ungrab();
this._grabbedDevice = null;
this._dragging = false;
this.emit('drag-end');

View File

@ -68,9 +68,6 @@ const ATIndicator = new Lang.Class({
let mouseKeys = this._buildItem(_("Mouse Keys"), A11Y_SCHEMA, KEY_MOUSE_KEYS_ENABLED);
this.menu.addMenuItem(mouseKeys);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addSettingsAction(_("Universal Access Settings"), 'gnome-universal-access-panel.desktop');
this._syncMenuVisibility();
},

View File

@ -13,13 +13,6 @@ const NotificationDaemon = imports.ui.notificationDaemon;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const ConnectionState = {
DISCONNECTED: 0,
CONNECTED: 1,
DISCONNECTING: 2,
CONNECTING: 3
}
const Indicator = new Lang.Class({
Name: 'BTIndicator',
Extends: PanelMenu.SystemStatusButton,
@ -27,61 +20,18 @@ const Indicator = new Lang.Class({
_init: function() {
this.parent('bluetooth-disabled-symbolic', _("Bluetooth"));
// The Bluetooth menu only appears when Bluetooth is in use,
// so just statically build it with a "Turn Off" menu item.
this._item = new PopupMenu.PopupSubMenuMenuItem(_("Bluetooth"), true);
this._item.icon.icon_name = 'bluetooth-active-symbolic';
this._item.menu.addAction(_("Turn Off"), Lang.bind(this, function() {
this._applet.killswitch_state = GnomeBluetooth.KillswitchState.SOFT_BLOCKED;
}));
this._item.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop');
this._applet = new GnomeBluetoothApplet.Applet();
this._killswitch = new PopupMenu.PopupSwitchMenuItem(_("Bluetooth"), false);
this._applet.connect('notify::killswitch-state', Lang.bind(this, this._updateKillswitch));
this._killswitch.connect('toggled', Lang.bind(this, function() {
let current_state = this._applet.killswitch_state;
if (current_state != GnomeBluetooth.KillswitchState.HARD_BLOCKED &&
current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER) {
this._applet.killswitch_state = this._killswitch.state ?
GnomeBluetooth.KillswitchState.UNBLOCKED:
GnomeBluetooth.KillswitchState.SOFT_BLOCKED;
} else
this._killswitch.setToggleState(false);
}));
this._discoverable = new PopupMenu.PopupSwitchMenuItem(_("Visibility"), this._applet.discoverable);
this._applet.connect('notify::discoverable', Lang.bind(this, function() {
this._discoverable.setToggleState(this._applet.discoverable);
}));
this._discoverable.connect('toggled', Lang.bind(this, function() {
this._applet.discoverable = this._discoverable.state;
}));
this._updateKillswitch();
this.menu.addMenuItem(this._killswitch);
this.menu.addMenuItem(this._discoverable);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._fullMenuItems = [new PopupMenu.PopupSeparatorMenuItem(),
new PopupMenu.PopupMenuItem(_("Send Files to Device…")),
new PopupMenu.PopupMenuItem(_("Set Up a New Device…")),
new PopupMenu.PopupSeparatorMenuItem()];
this._hasDevices = false;
this._fullMenuItems[1].connect('activate', function() {
GLib.spawn_command_line_async('bluetooth-sendto');
});
this._fullMenuItems[2].connect('activate', function() {
GLib.spawn_command_line_async('bluetooth-wizard');
});
for (let i = 0; i < this._fullMenuItems.length; i++) {
let item = this._fullMenuItems[i];
this.menu.addMenuItem(item);
}
this._deviceItemPosition = 3;
this._deviceItems = [];
this._applet.connect('devices-changed', Lang.bind(this, this._updateDevices));
this._updateDevices();
this._applet.connect('notify::show-full-menu', Lang.bind(this, this._updateFullMenu));
this._updateFullMenu();
this.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop');
this._applet.connect('devices-changed', Lang.bind(this, this._sync));
this._sync();
this._applet.connect('pincode-request', Lang.bind(this, this._pinRequest));
this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest));
@ -90,199 +40,18 @@ const Indicator = new Lang.Class({
this._applet.connect('cancel-request', Lang.bind(this, this._cancelRequest));
},
_updateKillswitch: function() {
let current_state = this._applet.killswitch_state;
let on = current_state == GnomeBluetooth.KillswitchState.UNBLOCKED;
let has_adapter = current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER;
let can_toggle = current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER &&
current_state != GnomeBluetooth.KillswitchState.HARD_BLOCKED;
_sync: function() {
let connectedDevices = this._applet.get_devices().filter(function(device) {
return device.connected;
});
let nDevices = connectedDevices.length;
this._killswitch.setToggleState(on);
if (can_toggle)
this._killswitch.setStatus(null);
else
/* TRANSLATORS: this means that bluetooth was disabled by hardware rfkill */
this._killswitch.setStatus(_("hardware disabled"));
let on = nDevices > 0;
this.mainIcon.visible = on;
this.actor.visible = on;
this.actor.visible = has_adapter;
if (on) {
this._discoverable.actor.show();
this.setIcon('bluetooth-active-symbolic');
} else {
this._discoverable.actor.hide();
this.setIcon('bluetooth-disabled-symbolic');
}
},
_updateDevices: function() {
let devices = this._applet.get_devices();
let newlist = [ ];
for (let i = 0; i < this._deviceItems.length; i++) {
let item = this._deviceItems[i];
let destroy = true;
for (let j = 0; j < devices.length; j++) {
if (item._device.device_path == devices[j].device_path) {
this._updateDeviceItem(item, devices[j]);
destroy = false;
break;
}
}
if (destroy)
item.destroy();
else
newlist.push(item);
}
this._deviceItems = newlist;
this._hasDevices = newlist.length > 0;
for (let i = 0; i < devices.length; i++) {
let d = devices[i];
if (d._item)
continue;
let item = this._createDeviceItem(d);
if (item) {
this.menu.addMenuItem(item, this._deviceItemPosition + this._deviceItems.length);
this._deviceItems.push(item);
this._hasDevices = true;
}
}
},
_updateDeviceItem: function(item, device) {
if (!device.can_connect && device.capabilities == GnomeBluetoothApplet.Capabilities.NONE) {
item.destroy();
return;
}
let prevDevice = item._device;
let prevCapabilities = prevDevice.capabilities;
let prevCanConnect = prevDevice.can_connect;
// adopt the new device object
item._device = device;
device._item = item;
// update properties
item.label.text = device.alias;
if (prevCapabilities != device.capabilities ||
prevCanConnect != device.can_connect) {
// need to rebuild the submenu
item.menu.removeAll();
this._buildDeviceSubMenu(item, device);
}
// update connected property
if (device.can_connect)
item._connectedMenuItem.setToggleState(device.connected);
},
_createDeviceItem: function(device) {
if (!device.can_connect && device.capabilities == GnomeBluetoothApplet.Capabilities.NONE)
return null;
let item = new PopupMenu.PopupSubMenuMenuItem(device.alias);
// adopt the device object, and add a back link
item._device = device;
device._item = item;
this._buildDeviceSubMenu(item, device);
return item;
},
_buildDeviceSubMenu: function(item, device) {
if (device.can_connect) {
let menuitem = new PopupMenu.PopupSwitchMenuItem(_("Connection"), device.connected);
item._connected = device.connected;
item._connectedMenuItem = menuitem;
menuitem.connect('toggled', Lang.bind(this, function() {
if (item._connected > ConnectionState.CONNECTED) {
// operation already in progress, revert
// (should not happen anyway)
menuitem.setToggleState(menuitem.state);
}
if (item._connected) {
item._connected = ConnectionState.DISCONNECTING;
menuitem.setStatus(_("disconnecting..."));
this._applet.disconnect_device(item._device.device_path, function(applet, success) {
if (success) { // apply
item._connected = ConnectionState.DISCONNECTED;
menuitem.setToggleState(false);
} else { // revert
item._connected = ConnectionState.CONNECTED;
menuitem.setToggleState(true);
}
menuitem.setStatus(null);
});
} else {
item._connected = ConnectionState.CONNECTING;
menuitem.setStatus(_("connecting..."));
this._applet.connect_device(item._device.device_path, function(applet, success) {
if (success) { // apply
item._connected = ConnectionState.CONNECTED;
menuitem.setToggleState(true);
} else { // revert
item._connected = ConnectionState.DISCONNECTED;
menuitem.setToggleState(false);
}
menuitem.setStatus(null);
});
}
}));
item.menu.addMenuItem(menuitem);
}
if (device.capabilities & GnomeBluetoothApplet.Capabilities.OBEX_PUSH) {
item.menu.addAction(_("Send Files…"), Lang.bind(this, function() {
this._applet.send_to_address(device.bdaddr, device.alias);
}));
}
switch (device.type) {
case GnomeBluetoothApplet.Type.KEYBOARD:
item.menu.addSettingsAction(_("Keyboard Settings"), 'gnome-keyboard-panel.desktop');
break;
case GnomeBluetoothApplet.Type.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.addSettingsAction(_("Sound Settings"), 'gnome-sound-panel.desktop');
break;
default:
break;
}
},
_updateFullMenu: function() {
if (this._applet.show_full_menu) {
this._showAll(this._fullMenuItems);
if (this._hasDevices)
this._showAll(this._deviceItems);
} else {
this._hideAll(this._fullMenuItems);
this._hideAll(this._deviceItems);
}
},
_showAll: function(items) {
for (let i = 0; i < items.length; i++)
items[i].actor.show();
},
_hideAll: function(items) {
for (let i = 0; i < items.length; i++)
items[i].actor.hide();
},
_destroyAll: function(items) {
for (let i = 0; i < items.length; i++)
items[i].destroy();
if (on)
this._item.status.text = ngettext("%d Connected Device", "%d Connected Devices").format(nDevices);
},
_ensureSource: function() {

View File

@ -398,8 +398,6 @@ const InputSourceIndicator = new Lang.Class({
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._sessionUpdated();
this.menu.addSettingsAction(_("Region & Language Settings"), 'gnome-region-panel.desktop');
this._sourcesPerWindow = false;
this._focusWindowNotifyId = 0;
this._overviewShowingId = 0;

File diff suppressed because it is too large Load Diff

View File

@ -2,39 +2,15 @@
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const St = imports.gi.St;
const UPower = imports.gi.UPowerGlib;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const BUS_NAME = 'org.gnome.SettingsDaemon.Power';
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
const UPDeviceType = {
UNKNOWN: 0,
AC_POWER: 1,
BATTERY: 2,
UPS: 3,
MONITOR: 4,
MOUSE: 5,
KEYBOARD: 6,
PDA: 7,
PHONE: 8,
MEDIA_PLAYER: 9,
TABLET: 10,
COMPUTER: 11
};
const UPDeviceState = {
UNKNOWN: 0,
CHARGING: 1,
DISCHARGING: 2,
EMPTY: 3,
FULLY_CHARGED: 4,
PENDING_CHARGE: 5,
PENDING_DISCHARGE: 6
};
const PowerManagerInterface = <interface name="org.gnome.SettingsDaemon.Power">
<method name="GetDevices">
<arg type="a(susdut)" direction="out" />
@ -55,96 +31,73 @@ const Indicator = new Lang.Class({
this.parent('battery-missing-symbolic', _("Battery"));
this._proxy = new PowerManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
Lang.bind(this, function(proxy, error) {
if (error) {
log(error.message);
return;
}
this._proxy.connect('g-properties-changed',
Lang.bind(this, this._devicesChanged));
this._devicesChanged();
}));
Lang.bind(this, function(proxy, error) {
if (error) {
log(error.message);
return;
}
this._proxy.connect('g-properties-changed',
Lang.bind(this, this._sync));
this._sync();
}));
this._deviceItems = [ ];
this._hasPrimary = false;
this._primaryDeviceId = null;
this._item = new PopupMenu.PopupSubMenuMenuItem(_("Battery"), true);
this._item.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop');
this.menu.addMenuItem(this._item);
this._batteryItem = new PopupMenu.PopupMenuItem('', { reactive: false });
this._primaryPercentage = new St.Label({ style_class: 'popup-battery-percentage' });
this._batteryItem.addActor(this._primaryPercentage, { align: St.Align.END });
this.menu.addMenuItem(this._batteryItem);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._otherDevicePosition = 2;
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop');
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._sessionUpdated();
},
_readPrimaryDevice: function() {
_sessionUpdated: function() {
let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
this.menu.setSensitive(sensitive);
},
_statusForDevice: function(device) {
let [device_id, device_type, icon, percentage, state, seconds] = device;
if (state == UPower.DeviceState.FULLY_CHARGED)
return _("Fully Charged");
let time = Math.round(seconds / 60);
if (time == 0) {
// 0 is reported when UPower does not have enough data
// to estimate battery life
return _("Estimating…");
}
let minutes = time % 60;
let hours = Math.floor(time / 60);
if (state == UPower.DeviceState.DISCHARGING) {
// Translators: this is <hours>:<minutes> Remaining (<percentage>)
return _("%d\u2236%d Remaining (%d%%)".format(hours, minutes, percentage));
}
if (state == UPower.DeviceState.CHARGING) {
// Translators: this is <hours>:<minutes> Until Full (<percentage>)
return _("%d\u2236%d Until Full (%d%%)".format(hours, minutes, percentage));
}
// state is one of PENDING_CHARGING, PENDING_DISCHARGING
return _("Estimating…");
},
_syncStatusLabel: function() {
this._proxy.GetPrimaryDeviceRemote(Lang.bind(this, function(result, error) {
if (error) {
this._hasPrimary = false;
this._primaryDeviceId = null;
this._batteryItem.actor.hide();
this._item.actor.hide();
return;
}
let [[device_id, device_type, icon, percentage, state, seconds]] = result;
if (device_type == UPDeviceType.BATTERY) {
this._hasPrimary = true;
let time = Math.round(seconds / 60);
if (time == 0) {
// 0 is reported when UPower does not have enough data
// to estimate battery life
this._batteryItem.label.text = _("Estimating…");
} else {
let minutes = time % 60;
let hours = Math.floor(time / 60);
let timestring;
if (time >= 60) {
if (minutes == 0) {
timestring = ngettext("%d hour remaining", "%d hours remaining", hours).format(hours);
} else {
/* TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining" */
let template = _("%d %s %d %s remaining");
timestring = template.format (hours, ngettext("hour", "hours", hours), minutes, ngettext("minute", "minutes", minutes));
}
} else
timestring = ngettext("%d minute remaining", "%d minutes remaining", minutes).format(minutes);
this._batteryItem.label.text = timestring;
}
this._primaryPercentage.text = C_("percent of battery remaining", "%d%%").format(Math.round(percentage));
this._batteryItem.actor.show();
let [device] = result;
let [device_id, device_type] = device;
if (device_type == UPower.DeviceKind.BATTERY) {
this._item.status.text = this._statusForDevice(device);
this._item.actor.show();
} else {
this._hasPrimary = false;
this._batteryItem.actor.hide();
}
this._primaryDeviceId = device_id;
}));
},
_readOtherDevices: function() {
this._proxy.GetDevicesRemote(Lang.bind(this, function(result, error) {
this._deviceItems.forEach(function(i) { i.destroy(); });
this._deviceItems = [];
if (error) {
return;
}
let position = 0;
let [devices] = result;
for (let i = 0; i < devices.length; i++) {
let [device_id, device_type] = devices[i];
if (device_type == UPDeviceType.AC_POWER || device_id == this._primaryDeviceId)
continue;
let item = new DeviceItem (devices[i]);
this._deviceItems.push(item);
this.menu.addMenuItem(item, this._otherDevicePosition + position);
position++;
this._item.actor.hide();
}
}));
},
@ -156,71 +109,15 @@ const Indicator = new Lang.Class({
if (icon) {
let gicon = Gio.icon_new_for_string(icon);
this.setGIcon(gicon);
this._item.icon.gicon = gicon;
hasIcon = true;
}
this.mainIcon.visible = hasIcon;
this.actor.visible = hasIcon;
},
_devicesChanged: function() {
_sync: function() {
this._syncIcon();
this._readPrimaryDevice();
this._readOtherDevices();
}
});
const DeviceItem = new Lang.Class({
Name: 'DeviceItem',
Extends: PopupMenu.PopupBaseMenuItem,
_init: function(device) {
this.parent({ reactive: false });
let [device_id, device_type, icon, percentage, state, time] = device;
this._box = new St.BoxLayout({ style_class: 'popup-device-menu-item' });
this._label = new St.Label({ text: this._deviceTypeToString(device_type) });
this._icon = new St.Icon({ gicon: Gio.icon_new_for_string(icon),
style_class: 'popup-menu-icon' });
this._box.add_actor(this._icon);
this._box.add_actor(this._label);
this.addActor(this._box);
let percentLabel = new St.Label({ text: C_("percent of battery remaining", "%d%%").format(Math.round(percentage)),
style_class: 'popup-battery-percentage' });
this.addActor(percentLabel, { align: St.Align.END });
//FIXME: ideally we would like to expose this._label and percentLabel
this.actor.label_actor = percentLabel;
},
_deviceTypeToString: function(type) {
switch (type) {
case UPDeviceType.AC_POWER:
return _("AC Adapter");
case UPDeviceType.BATTERY:
return _("Laptop Battery");
case UPDeviceType.UPS:
return _("UPS");
case UPDeviceType.MONITOR:
return _("Monitor");
case UPDeviceType.MOUSE:
return _("Mouse");
case UPDeviceType.KEYBOARD:
return _("Keyboard");
case UPDeviceType.PDA:
return _("PDA");
case UPDeviceType.PHONE:
return _("Cell Phone");
case UPDeviceType.MEDIA_PLAYER:
return _("Media Player");
case UPDeviceType.TABLET:
return _("Tablet");
case UPDeviceType.COMPUTER:
return _("Computer");
default:
return C_("device", "Unknown");
}
this._syncStatusLabel();
}
});

358
js/ui/status/system.js Normal file
View File

@ -0,0 +1,358 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const AccountsService = imports.gi.AccountsService;
const Gdm = imports.gi.Gdm;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Clutter = imports.gi.Clutter;
const BoxPointer = imports.ui.boxpointer;
const GnomeSession = imports.misc.gnomeSession;
const LoginManager = imports.misc.loginManager;
const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Util = imports.misc.util;
const UserWidget = imports.ui.userWidget;
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
const PRIVACY_SCHEMA = 'org.gnome.desktop.privacy'
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 ALWAYS_SHOW_LOG_OUT_KEY = 'always-show-log-out';
const MAX_USERS_IN_SESSION_DIALOG = 5;
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
<property name="Id" type="s" access="read"/>
<property name="Remote" type="b" access="read"/>
<property name="Class" type="s" access="read"/>
<property name="Type" type="s" access="read"/>
<property name="State" type="s" access="read"/>
</interface>;
const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface);
const Indicator = new Lang.Class({
Name: 'SystemIndicator',
Extends: PanelMenu.SystemStatusButton,
_init: function() {
this.parent('system-shutdown-symbolic', _("System"));
this._screenSaverSettings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
this._privacySettings = new Gio.Settings({ schema: PRIVACY_SCHEMA });
this._session = new GnomeSession.SessionManager();
this._haveShutdown = true;
this._loginManager = LoginManager.getLoginManager();
this._userManager = AccountsService.UserManager.get_default();
this._user = this._userManager.get_user(GLib.get_user_name());
this._createSubMenu();
this._userManager.connect('notify::is-loaded',
Lang.bind(this, this._updateMultiUser));
this._userManager.connect('notify::has-multiple-users',
Lang.bind(this, this._updateMultiUser));
this._userManager.connect('user-added',
Lang.bind(this, this._updateMultiUser));
this._userManager.connect('user-removed',
Lang.bind(this, this._updateMultiUser));
this._lockdownSettings.connect('changed::' + DISABLE_USER_SWITCH_KEY,
Lang.bind(this, this._updateMultiUser));
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
Lang.bind(this, this._updateMultiUser));
this._lockdownSettings.connect('changed::' + DISABLE_LOCK_SCREEN_KEY,
Lang.bind(this, this._updateLockScreen));
global.settings.connect('changed::' + ALWAYS_SHOW_LOG_OUT_KEY,
Lang.bind(this, this._updateMultiUser));
this._updateSwitchUser();
this._updateMultiUser();
// 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)
return;
this._updateHaveShutdown();
}));
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
Lang.bind(this, this._updateHaveShutdown));
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._sessionUpdated();
},
_sessionUpdated: function() {
this._updateLockScreen();
this._updatePowerOff();
this._settingsAction.visible = Main.sessionMode.allowSettings;
},
_updateMultiUser: function() {
let shouldShowInMode = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
let hasSwitchUser = this._updateSwitchUser();
let hasLogout = this._updateLogout();
this._switchUserSubMenu.actor.visible = shouldShowInMode && (hasSwitchUser || hasLogout);
},
_updateSwitchUser: function() {
let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY);
let multiUser = this._userManager.can_switch() && this._userManager.has_multiple_users;
let visible = allowSwitch && multiUser;
this._loginScreenItem.actor.visible = visible;
return visible;
},
_updateLogout: function() {
let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY);
let alwaysShow = global.settings.get_boolean(ALWAYS_SHOW_LOG_OUT_KEY);
let systemAccount = this._user.system_account;
let localAccount = this._user.local_account;
let multiUser = this._userManager.has_multiple_users;
let multiSession = Gdm.get_session_ids().length > 1;
let visible = allowLogout && (alwaysShow || multiUser || multiSession || systemAccount || !localAccount);
this._logoutItem.actor.visible = visible;
return visible;
},
_updateSwitchUserSubMenu: function() {
this._switchUserSubMenu.label.text = this._user.get_real_name();
let iconFile = this._user.get_icon_file();
if (iconFile && !GLib.file_test(iconFile, GLib.FileTest.EXISTS))
iconFile = null;
if (iconFile) {
let file = Gio.File.new_for_path(iconFile);
let gicon = new Gio.FileIcon({ file: file });
this._switchUserSubMenu.icon.gicon = gicon;
} else {
this._switchUserSubMenu.icon_name = 'avatar-default-symbolic';
}
},
_updateLockScreen: function() {
let showLock = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
this._lockScreenAction.visible = showLock && allowLockScreen && LoginManager.canLock();
},
_updateHaveShutdown: function() {
this._session.CanShutdownRemote(Lang.bind(this,
function(result, error) {
if (!error) {
this._haveShutdown = result[0];
this._updatePowerOff();
}
}));
},
_updatePowerOff: function() {
this._powerOffAction.visible = this._haveShutdown && !Main.sessionMode.isLocked;
},
_createActionButton: function(iconName, accessibleName) {
let icon = new St.Button({ reactive: true,
can_focus: true,
track_hover: true,
accessible_name: accessibleName,
style_class: 'system-menu-action' });
icon.child = new St.Icon({ icon_name: iconName });
return icon;
},
_createSubMenu: function() {
let item;
this._switchUserSubMenu = new PopupMenu.PopupSubMenuMenuItem('', true);
this._switchUserSubMenu.icon.style_class = 'system-switch-user-submenu-icon';
item = new PopupMenu.PopupMenuItem(_("Switch User"));
item.connect('activate', Lang.bind(this, this._onLoginScreenActivate));
this._switchUserSubMenu.menu.addMenuItem(item);
this._loginScreenItem = item;
item = new PopupMenu.PopupMenuItem(_("Log Out"));
item.connect('activate', Lang.bind(this, this._onQuitSessionActivate));
this._switchUserSubMenu.menu.addMenuItem(item);
this._logoutItem = item;
this._user.connect('notify::is-loaded', Lang.bind(this, this._updateSwitchUserSubMenu));
this._user.connect('changed', Lang.bind(this, this._updateSwitchUserSubMenu));
this.menu.addMenuItem(this._switchUserSubMenu);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
let hbox = new St.BoxLayout({ style_class: 'system-menu-actions-box' });
this._settingsAction = this._createActionButton('preferences-system-symbolic', _("Settings"));
this._settingsAction.connect('clicked', Lang.bind(this, this._onSettingsClicked));
hbox.add(this._settingsAction, { expand: true, x_fill: false });
this._lockScreenAction = this._createActionButton('changes-prevent-symbolic', _("Lock"));
this._lockScreenAction.connect('clicked', Lang.bind(this, this._onLockScreenClicked));
hbox.add(this._lockScreenAction, { expand: true, x_fill: false });
this._powerOffAction = this._createActionButton('system-shutdown-symbolic', _("Power Off"));
this._powerOffAction.connect('clicked', Lang.bind(this, this._onPowerOffClicked));
hbox.add(this._powerOffAction, { expand: true, x_fill: false });
item = new PopupMenu.PopupBaseMenuItem({ reactive: false,
can_focus: false });
item.addActor(hbox, { expand: true });
this.menu.addMenuItem(item);
},
_onSettingsClicked: function() {
this.menu.itemActivated();
let app = Shell.AppSystem.get_default().lookup_app('gnome-control-center.desktop');
Main.overview.hide();
app.activate();
},
_onLockScreenClicked: function() {
this.menu.itemActivated(BoxPointer.PopupAnimation.NONE);
Main.overview.hide();
Main.screenShield.lock(true);
},
_onLoginScreenActivate: function() {
this.menu.itemActivated(BoxPointer.PopupAnimation.NONE);
Main.overview.hide();
if (Main.screenShield)
Main.screenShield.lock(false);
Gdm.goto_login_session_sync(null);
},
_onQuitSessionActivate: function() {
Main.overview.hide();
this._session.LogoutRemote(0);
},
_openSessionWarnDialog: function(sessions) {
let dialog = new ModalDialog.ModalDialog();
let subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject',
text: _("Other users are logged in.") });
dialog.contentLayout.add(subjectLabel, { y_fill: true,
y_align: St.Align.START });
let descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description'});
descriptionLabel.set_text(_("Shutting down might cause them to lose unsaved work."));
descriptionLabel.clutter_text.line_wrap = true;
dialog.contentLayout.add(descriptionLabel, { x_fill: true,
y_fill: true,
y_align: St.Align.START });
let scrollView = new St.ScrollView({ style_class: 'end-session-dialog-app-list' });
scrollView.add_style_class_name('vfade');
scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
dialog.contentLayout.add(scrollView, { x_fill: true, y_fill: true });
let userList = new St.BoxLayout({ vertical: true });
scrollView.add_actor(userList);
for (let i = 0; i < sessions.length; i++) {
let session = sessions[i];
let userEntry = new St.BoxLayout({ style_class: 'login-dialog-user-list-item',
vertical: false });
let avatar = new UserWidget.Avatar(session.user);
avatar.update();
userEntry.add(avatar.actor);
let userLabelText = "";;
let userName = session.user.get_real_name() ?
session.user.get_real_name() : session.username;
if (session.info.remote)
/* Translators: Remote here refers to a remote session, like a ssh login */
userLabelText = _("%s (remote)").format(userName);
else if (session.info.type == "tty")
/* Translators: Console here refers to a tty like a VT console */
userLabelText = _("%s (console)").format(userName);
else
userLabelText = userName;
let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box',
vertical: true });
textLayout.add(new St.Label({ text: userLabelText }),
{ y_fill: false,
y_align: St.Align.MIDDLE,
expand: true });
userEntry.add(textLayout, { expand: true });
userList.add(userEntry, { x_fill: true });
}
let cancelButton = { label: _("Cancel"),
action: function() { dialog.close(); },
key: Clutter.Escape };
let powerOffButton = { label: _("Power Off"), action: Lang.bind(this, function() {
dialog.close();
this._session.ShutdownRemote();
}), default: true };
dialog.setButtons([cancelButton, powerOffButton]);
dialog.open();
},
_onPowerOffClicked: function() {
this.menu.itemActivated();
Main.overview.hide();
this._loginManager.listSessions(Lang.bind(this, function(result) {
let sessions = [];
let n = 0;
for (let i = 0; i < result.length; i++) {
let[id, uid, userName, seat, sessionPath] = result[i];
let proxy = new SystemdLoginSession(Gio.DBus.system,
'org.freedesktop.login1',
sessionPath);
if (proxy.Class != 'user')
continue;
if (proxy.State == 'closing')
continue;
if (proxy.Id == GLib.getenv('XDG_SESSION_ID'))
continue;
sessions.push({ user: this._userManager.get_user(userName),
username: userName,
info: { type: proxy.Type,
remote: proxy.Remote }
});
// limit the number of entries
n++;
if (n == MAX_USERS_IN_SESSION_DIALOG)
break;
}
if (n != 0)
this._openSessionWarnDialog(sessions);
else
this._session.ShutdownRemote();
}));
}
});

View File

@ -9,6 +9,7 @@ const Signals = imports.signals;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Slider = imports.ui.slider;
const VOLUME_NOTIFY_ID = 1;
@ -28,18 +29,23 @@ function getMixerControl() {
const StreamSlider = new Lang.Class({
Name: 'StreamSlider',
_init: function(control, title) {
_init: function(control) {
this._control = control;
this.item = new PopupMenu.PopupMenuSection();
this.item = new PopupMenu.PopupBaseMenuItem({ activate: false });
this._title = new PopupMenu.PopupMenuItem(title, { reactive: false });
this._slider = new PopupMenu.PopupSliderMenuItem(0);
this._slider = new Slider.Slider(0);
this._slider.connect('value-changed', Lang.bind(this, this._sliderChanged));
this._slider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
this.item.addMenuItem(this._title);
this.item.addMenuItem(this._slider);
this._icon = new St.Icon({ style_class: 'popup-menu-icon' });
this.item.addActor(this._icon, { align: St.Align.MIDDLE });
this.item.addActor(this._slider.actor, { expand: true });
this.item.actor.connect('button-press-event', Lang.bind(this, function(actor, event) {
this._slider.startDragging(event);
}));
this._stream = null;
},
@ -83,8 +89,7 @@ const StreamSlider = new Lang.Class({
_updateVisibility: function() {
let visible = this._shouldBeVisible();
this._title.actor.visible = visible;
this._slider.actor.visible = visible;
this.item.actor.visible = visible;
},
scroll: function(event) {
@ -178,11 +183,17 @@ const OutputStreamSlider = new Lang.Class({
this._portChangedId = 0;
},
_updateSliderIcon: function() {
this._icon.icon_name = (this._hasHeadphones ?
'audio-headphones-symbolic' :
'audio-speakers-symbolic');
},
_portChanged: function() {
let hasHeadphones = this._findHeadphones(this._stream);
if (hasHeadphones != this._hasHeadphones) {
this._hasHeadphones = hasHeadphones;
this.emit('headphones-changed', this._hasHeadphones);
this._updateSliderIcon();
}
}
});
@ -191,10 +202,11 @@ const InputStreamSlider = new Lang.Class({
Name: 'InputStreamSlider',
Extends: StreamSlider,
_init: function(control, title) {
this.parent(control, title);
_init: function(control) {
this.parent(control);
this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput));
this._control.connect('stream-removed', Lang.bind(this, this._maybeShowInput));
this._icon.icon_name = 'audio-input-microphone-symbolic';
},
_connectStream: function(stream) {
@ -242,17 +254,13 @@ const VolumeMenu = new Lang.Class({
this._control.connect('default-sink-changed', Lang.bind(this, this._readOutput));
this._control.connect('default-source-changed', Lang.bind(this, this._readInput));
/* Translators: This is the label for audio volume */
this._output = new OutputStreamSlider(this._control, _("Volume"));
this._output = new OutputStreamSlider(this._control);
this._output.connect('stream-updated', Lang.bind(this, function() {
this.emit('icon-changed');
}));
this._output.connect('headphones-changed', Lang.bind(this, function(stream, value) {
this.emit('headphones-changed', value);
}));
this.addMenuItem(this._output.item);
this._input = new InputStreamSlider(this._control, _("Microphone"));
this._input = new InputStreamSlider(this._control);
this.addMenuItem(this._input.item);
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
@ -300,18 +308,9 @@ const Indicator = new Lang.Class({
this.actor.visible = (icon != null);
this.setIcon(icon);
}));
this._volumeMenu.connect('headphones-changed', Lang.bind(this, function(menu, value) {
this._headphoneIcon.visible = value;
}));
this._headphoneIcon = this.addIcon(new Gio.ThemedIcon({ name: 'audio-headphones-symbolic' }));
this._headphoneIcon.visible = false;
this.menu.addMenuItem(this._volumeMenu);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addSettingsAction(_("Sound Settings"), 'gnome-sound-panel.desktop');
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
},

View File

@ -1,6 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const AccountsService = imports.gi.AccountsService;
const Atk = imports.gi.Atk;
const Clutter = imports.gi.Clutter;
const Gdm = imports.gi.Gdm;
const Gio = imports.gi.Gio;
@ -12,13 +13,13 @@ const Signals = imports.signals;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Layout = imports.ui.layout;
const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog;
const Panel = imports.ui.panel;
const ShellEntry = imports.ui.shellEntry;
const Tweener = imports.ui.tweener;
const UserWidget = imports.ui.userWidget;
const AuthPrompt = imports.gdm.authPrompt;
const Batch = imports.gdm.batch;
const GdmUtil = imports.gdm.util;
const LoginDialog = imports.gdm.loginDialog;
@ -28,91 +29,35 @@ const IDLE_TIMEOUT = 2 * 60;
const UnlockDialog = new Lang.Class({
Name: 'UnlockDialog',
Extends: ModalDialog.ModalDialog,
_init: function(parentActor) {
this.parent({ shellReactive: true,
styleClass: 'login-dialog',
keybindingMode: Shell.KeyBindingMode.UNLOCK_SCREEN,
parentActor: parentActor
});
this.actor = new St.Widget({ accessible_role: Atk.Role.WINDOW,
style_class: 'login-dialog',
visible: false });
this.actor.add_constraint(new Layout.MonitorConstraint({ primary: true }));
parentActor.add_child(this.actor);
this._userManager = AccountsService.UserManager.get_default();
this._userName = GLib.get_user_name();
this._user = this._userManager.get_user(this._userName);
this._failCounter = 0;
this._firstQuestion = true;
this._promptBox = new St.BoxLayout({ vertical: true });
this.actor.add_child(this._promptBox);
this._promptBox.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
align_axis: Clutter.AlignAxis.BOTH,
factor: 0.5 }));
this._greeterClient = new Gdm.Client();
this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient, { reauthenticationOnly: true });
this._userVerified = false;
this._authPrompt = new AuthPrompt.AuthPrompt(new Gdm.Client(), AuthPrompt.AuthPromptMode.UNLOCK_ONLY);
this._authPrompt.connect('failed', Lang.bind(this, this._fail));
this._authPrompt.connect('cancelled', Lang.bind(this, this._fail));
this._authPrompt.connect('reset', Lang.bind(this, this._onReset));
this._authPrompt.setPasswordChar('\u25cf');
this._authPrompt.nextButton.label = _("Unlock");
this._userVerifier.connect('ask-question', Lang.bind(this, this._onAskQuestion));
this._userVerifier.connect('show-message', Lang.bind(this, this._showMessage));
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
this._userVerifier.connect('verification-failed', Lang.bind(this, this._onVerificationFailed));
this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
this._userVerifier.connect('show-login-hint', Lang.bind(this, this._showLoginHint));
this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._hideLoginHint));
this._userWidget = new UserWidget.UserWidget(this._user);
this.contentLayout.add_actor(this._userWidget.actor);
this._promptLayout = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
vertical: true });
this._promptLabel = new St.Label({ style_class: 'login-dialog-prompt-label' });
this._promptLayout.add(this._promptLabel,
{ x_align: St.Align.START });
this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
can_focus: true });
this._promptEntry.clutter_text.connect('activate', Lang.bind(this, this._doUnlock));
this._promptEntry.clutter_text.set_password_char('\u25cf');
ShellEntry.addContextMenu(this._promptEntry, { isPassword: true });
this.setInitialKeyFocus(this._promptEntry);
this._promptEntry.clutter_text.connect('text-changed', Lang.bind(this, function() {
this._updateOkButtonSensitivity(this._promptEntry.text.length > 0);
}));
this._promptLayout.add(this._promptEntry,
{ expand: true,
x_fill: true });
this.contentLayout.add_actor(this._promptLayout);
this._promptMessage = new St.Label({ visible: false });
this.contentLayout.add(this._promptMessage, { x_fill: true });
this._promptLoginHint = new St.Label({ style_class: 'login-dialog-prompt-login-hint' });
this._promptLoginHint.hide();
this.contentLayout.add_actor(this._promptLoginHint);
this._promptBox.add_child(this._authPrompt.actor);
this.allowCancel = false;
this.buttonLayout.visible = true;
this.addButton({ label: _("Cancel"),
action: Lang.bind(this, this._escape),
key: Clutter.KEY_Escape },
{ expand: true,
x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
this.placeSpinner({ expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
this._okButton = this.addButton({ label: _("Unlock"),
action: Lang.bind(this, this._doUnlock),
default: true },
{ expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
let screenSaverSettings = new Gio.Settings({ schema: 'org.gnome.desktop.screensaver' });
if (screenSaverSettings.get_boolean('user-switch-enabled')) {
@ -125,174 +70,100 @@ const UnlockDialog = new Lang.Class({
x_align: St.Align.START,
x_fill: true });
this._otherUserButton.connect('clicked', Lang.bind(this, this._otherUserClicked));
this.dialogLayout.add(this._otherUserButton,
{ x_align: St.Align.START,
x_fill: false });
this._promptBox.add_child(this._otherUserButton);
} else {
this._otherUserButton = null;
}
this._authPrompt.reset();
this._updateSensitivity(true);
let batch = new Batch.Hold();
this._userVerifier.begin(this._userName, batch);
Main.ctrlAltTabManager.addGroup(this.dialogLayout, _("Unlock Window"), 'dialog-password-symbolic');
Main.ctrlAltTabManager.addGroup(this.actor, _("Unlock Window"), 'dialog-password-symbolic');
this._idleMonitor = new GnomeDesktop.IdleMonitor();
this._idleWatchId = this._idleMonitor.add_idle_watch(IDLE_TIMEOUT * 1000, Lang.bind(this, this._escape));
},
_updateSensitivity: function(sensitive) {
this._promptEntry.reactive = sensitive;
this._promptEntry.clutter_text.editable = sensitive;
this._updateOkButtonSensitivity(sensitive && this._promptEntry.text.length > 0);
this._authPrompt.updateSensitivity(sensitive);
if (this._otherUserButton) {
this._otherUserButton.reactive = sensitive;
this._otherUserButton.can_focus = sensitive;
}
},
_updateOkButtonSensitivity: function(sensitive) {
this._okButton.reactive = sensitive;
this._okButton.can_focus = sensitive;
_fail: function() {
this.emit('failed');
},
_showMessage: function(userVerifier, message, styleClass) {
if (message) {
this._promptMessage.text = message;
this._promptMessage.styleClass = styleClass;
GdmUtil.fadeInActor(this._promptMessage);
_onReset: function(authPrompt, beginRequest) {
let userName;
if (beginRequest == AuthPrompt.BeginRequestType.PROVIDE_USERNAME) {
this._authPrompt.setUser(this._user);
userName = this._userName;
} else {
GdmUtil.fadeOutActor(this._promptMessage);
}
},
_onAskQuestion: function(verifier, serviceName, question, passwordChar) {
if (this._firstQuestion && this._firstQuestionAnswer) {
this._userVerifier.answerQuery(serviceName, this._firstQuestionAnswer);
this._firstQuestionAnswer = null;
this._firstQuestion = false;
return;
userName = null;
}
this._promptLabel.text = question;
if (!this._firstQuestion)
this._promptEntry.text = '';
else
this._firstQuestion = false;
this._promptEntry.clutter_text.set_password_char(passwordChar);
this._promptEntry.menu.isPassword = passwordChar != '';
this._currentQuery = serviceName;
this._updateSensitivity(true);
this.setWorking(false);
},
_showLoginHint: function(verifier, message) {
this._promptLoginHint.set_text(message)
GdmUtil.fadeInActor(this._promptLoginHint);
},
_hideLoginHint: function() {
GdmUtil.fadeOutActor(this._promptLoginHint);
},
_doUnlock: function() {
if (this._firstQuestion) {
// we haven't received a query yet, so stash the answer
// and make ourself non-reactive
// the actual reply to GDM will be sent as soon as asked
this._firstQuestionAnswer = this._promptEntry.text;
this._updateSensitivity(false);
this.setWorking(true);
return;
}
if (!this._currentQuery)
return;
let query = this._currentQuery;
this._currentQuery = null;
this._updateSensitivity(false);
this.setWorking(true);
this._userVerifier.answerQuery(query, this._promptEntry.text);
},
_finishUnlock: function() {
this._userVerifier.clear();
this.emit('unlocked');
},
_onVerificationComplete: function() {
this._userVerified = true;
if (!this._userVerifier.hasPendingMessages) {
this._finishUnlock();
} else {
let signalId = this._userVerifier.connect('no-more-messages',
Lang.bind(this, function() {
this._userVerifier.disconnect(signalId);
this._finishUnlock();
}));
}
},
_onReset: function() {
if (!this._userVerified) {
this._userVerifier.clear();
this.emit('failed');
}
},
_onVerificationFailed: function() {
this._currentQuery = null;
this._firstQuestion = true;
this._userVerified = false;
this._promptEntry.text = '';
this._promptEntry.clutter_text.set_password_char('\u25cf');
this._promptEntry.menu.isPassword = true;
this._updateSensitivity(false);
this.setWorking(false);
this._authPrompt.begin({ userName: userName });
},
_escape: function() {
if (this.allowCancel) {
this._userVerifier.cancel();
this.emit('failed');
}
if (this.allowCancel)
this._authPrompt.cancel();
},
_otherUserClicked: function(button, event) {
Gdm.goto_login_session_sync(null);
this._userVerifier.cancel();
this.emit('failed');
this._authPrompt.cancel();
},
destroy: function() {
this._userVerifier.clear();
this.popModal();
this.actor.destroy();
if (this._idleWatchId) {
this._idleMonitor.remove_watch(this._idleWatchId);
this._idleWatchId = 0;
}
this.parent();
},
cancel: function() {
this._userVerifier.cancel(null);
this._authPrompt.cancel();
this.destroy();
},
addCharacter: function(unichar) {
this._promptEntry.clutter_text.insert_unichar(unichar);
this._authPrompt.addCharacter(unichar);
},
finish: function(onComplete) {
this._authPrompt.finish(onComplete);
},
open: function(timestamp) {
this.actor.show();
if (this._isModal)
return true;
if (!Main.pushModal(this.actor, { timestamp: timestamp,
keybindingMode: Shell.KeyBindingMode.UNLOCK_SCREEN }))
return false;
this._isModal = true;
return true;
},
popModal: function(timestamp) {
if (this._isModal) {
Main.popModal(this.actor, timestamp);
this._isModal = false;
}
}
});
Signals.addSignalMethods(UnlockDialog.prototype);

View File

@ -1,944 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const AccountsService = imports.gi.AccountsService;
const Gdm = imports.gi.Gdm;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Pango = imports.gi.Pango;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Tp = imports.gi.TelepathyGLib;
const Atk = imports.gi.Atk;
const Clutter = imports.gi.Clutter;
const BoxPointer = imports.ui.boxpointer;
const GnomeSession = imports.misc.gnomeSession;
const LoginManager = imports.misc.loginManager;
const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Util = imports.misc.util;
const UserWidget = imports.ui.userWidget;
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
const PRIVACY_SCHEMA = 'org.gnome.desktop.privacy'
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 ALWAYS_SHOW_LOG_OUT_KEY = 'always-show-log-out';
const SHOW_FULL_NAME_IN_TOP_BAR_KEY = 'show-full-name-in-top-bar';
const MAX_USERS_IN_SESSION_DIALOG = 5;
const IMStatus = {
AVAILABLE: 0,
BUSY: 1,
HIDDEN: 2,
AWAY: 3,
IDLE: 4,
OFFLINE: 5,
LAST: 6
};
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
<property name="Id" type="s" access="read"/>
<property name="Remote" type="b" access="read"/>
<property name="Class" type="s" access="read"/>
<property name="Type" type="s" access="read"/>
<property name="State" type="s" access="read"/>
</interface>;
const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface);
const IMStatusItem = new Lang.Class({
Name: 'IMStatusItem',
Extends: PopupMenu.PopupBaseMenuItem,
_init: function(label, iconName) {
this.parent();
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.actor.label_actor = this.label;
this.addActor(this.label);
}
});
const IMUserNameItem = new Lang.Class({
Name: 'IMUserNameItem',
Extends: PopupMenu.PopupBaseMenuItem,
_init: function() {
this.parent({ reactive: false,
can_focus: 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.label.clutter_text.set_ellipsize(Pango.EllipsizeMode.NONE);
this._wrapper.add_actor(this.label);
},
_wrapperGetPreferredWidth: function(actor, forHeight, alloc) {
alloc.min_size = 1;
alloc.natural_size = 1;
},
_wrapperGetPreferredHeight: function(actor, forWidth, alloc) {
[alloc.min_size, alloc.natural_size] = this.label.get_preferred_height(forWidth);
},
_wrapperAllocate: function(actor, box, flags) {
this.label.allocate(box, flags);
}
});
const IMStatusChooserItem = new Lang.Class({
Name: 'IMStatusChooserItem',
Extends: PopupMenu.PopupBaseMenuItem,
_init: function() {
this.parent({ reactive: false,
can_focus: false,
style_class: 'status-chooser' });
this._userManager = AccountsService.UserManager.get_default();
this._user = this._userManager.get_user(GLib.get_user_name());
this._avatar = new UserWidget.Avatar(this._user, { reactive: true });
this._iconBin = new St.Button({ child: this._avatar.actor });
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-symbolic');
this._combo.addMenuItem(item, IMStatus.AVAILABLE);
item = new IMStatusItem(_("Busy"), 'user-busy-symbolic');
this._combo.addMenuItem(item, IMStatus.BUSY);
item = new IMStatusItem(_("Invisible"), 'user-invisible-symbolic');
this._combo.addMenuItem(item, IMStatus.HIDDEN);
item = new IMStatusItem(_("Away"), 'user-away-symbolic');
this._combo.addMenuItem(item, IMStatus.AWAY);
item = new IMStatusItem(_("Idle"), 'user-idle-symbolic');
this._combo.addMenuItem(item, IMStatus.IDLE);
item = new IMStatusItem(_("Offline"), 'user-offline-symbolic');
this._combo.addMenuItem(item, IMStatus.OFFLINE);
this._combo.connect('active-item-changed',
Lang.bind(this, this._changeIMStatus));
this._presence = new GnomeSession.Presence();
this._presence.connectSignal('StatusChanged', Lang.bind(this, function(proxy, senderName, [status]) {
this._sessionStatusChanged(status);
}));
this._sessionPresenceRestored = false;
this._imPresenceRestored = false;
this._currentPresence = undefined;
this._accountMgr = Tp.AccountManager.dup();
this._accountMgr.connect('most-available-presence-changed',
Lang.bind(this, this._IMStatusChanged));
this._accountMgr.connect('account-enabled',
Lang.bind(this, this._IMAccountsChanged));
this._accountMgr.connect('account-disabled',
Lang.bind(this, this._IMAccountsChanged));
this._accountMgr.connect('account-removed',
Lang.bind(this, this._IMAccountsChanged));
this._accountMgr.connect('account-validity-changed',
Lang.bind(this, this._IMAccountsChanged));
this._accountMgr.prepare_async(null, Lang.bind(this,
function(mgr) {
this._IMAccountsChanged(mgr);
if (this._networkMonitor.network_available)
this._restorePresence();
else
this._setComboboxPresence(Tp.ConnectionPresenceType.OFFLINE);
}));
this._networkMonitor = Gio.NetworkMonitor.get_default();
this._networkMonitor.connect('network-changed',
Lang.bind(this, function(monitor, available) {
this._IMAccountsChanged(this._accountMgr);
if (available && !this._imPresenceRestored)
this._restorePresence();
}));
this._userLoadedId = this._user.connect('notify::is-loaded',
Lang.bind(this,
this._updateUser));
this._userChangedId = this._user.connect('changed',
Lang.bind(this,
this._updateUser));
this.actor.connect('notify::mapped', Lang.bind(this, function() {
if (this.actor.mapped)
this._updateUser();
}));
this.connect('sensitive-changed', function(sensitive) {
this._avatar.setSensitive(sensitive);
});
},
_restorePresence: function() {
let [presence, status, msg] = this._accountMgr.get_most_available_presence();
let savedPresence = global.settings.get_int('saved-im-presence');
if (savedPresence == presence) {
this._IMStatusChanged(this._accountMgr, presence, status, msg);
} else {
this._setComboboxPresence(savedPresence);
status = this._statusForPresence(savedPresence);
msg = msg ? msg : '';
this._accountMgr.set_all_requested_presences(savedPresence, status, msg);
}
},
destroy: function() {
// clean up signal handlers
if (this._userLoadedId != 0) {
this._user.disconnect(this._userLoadedId);
this._userLoadedId = 0;
}
if (this._userChangedId != 0) {
this._user.disconnect(this._userChangedId);
this._userChangedId = 0;
}
this.parent();
},
// Override getColumnWidths()/setColumnWidths() to make the item
// independent from the overall column layout of the menu
getColumnWidths: function() {
return [];
},
setColumnWidths: function(widths) {
},
_updateUser: function() {
if (this._user.is_loaded)
this._name.label.set_text(this._user.get_real_name());
else
this._name.label.set_text("");
this._avatar.update();
},
_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';
}
},
_IMAccountsChanged: function(mgr) {
let accounts = mgr.get_valid_accounts().filter(function(account) {
return account.enabled;
});
let sensitive = accounts.length > 0 && this._networkMonitor.network_available;
this._combo.setSensitive(sensitive);
},
_IMStatusChanged: function(accountMgr, presence, status, message) {
if (!this._imPresenceRestored)
this._imPresenceRestored = true;
if (presence == this._currentPresence)
return;
this._currentPresence = presence;
this._setComboboxPresence(presence);
if (!this._sessionPresenceRestored) {
this._sessionStatusChanged(this._presence.status);
return;
}
if (presence == Tp.ConnectionPresenceType.AVAILABLE)
this._presence.status = GnomeSession.PresenceStatus.AVAILABLE;
// We ignore the actual value of _expectedPresence and never safe
// the first presence change after an "automatic" change, assuming
// that it is the response to our request; this is to account for
// mission control falling back to "similar" presences if an account
// type does not implement the requested presence.
if (!this._expectedPresence)
global.settings.set_int('saved-im-presence', presence);
else
this._expectedPresence = undefined;
},
_setComboboxPresence: function(presence) {
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);
},
getIMPresenceForSessionStatus: function(sessionStatus) {
// Restore the last user-set presence when coming back from
// BUSY/IDLE (otherwise the last user-set presence matches
// the current one)
if (sessionStatus == GnomeSession.PresenceStatus.AVAILABLE)
return global.settings.get_int('saved-im-presence');
if (sessionStatus == GnomeSession.PresenceStatus.BUSY) {
// Only change presence if the current one is "more present" than
// busy, or if coming back from idle
if (this._currentPresence == Tp.ConnectionPresenceType.AVAILABLE ||
this._currentPresence == Tp.ConnectionPresenceType.EXTENDED_AWAY)
return Tp.ConnectionPresenceType.BUSY;
}
if (sessionStatus == GnomeSession.PresenceStatus.IDLE) {
// Only change presence if the current one is "more present" than
// idle
if (this._currentPresence != Tp.ConnectionPresenceType.OFFLINE &&
this._currentPresence != Tp.ConnectionPresenceType.HIDDEN)
return Tp.ConnectionPresenceType.EXTENDED_AWAY;
}
return this._currentPresence;
},
_sessionStatusChanged: function(sessionStatus) {
if (!this._imPresenceRestored)
return;
let savedStatus = global.settings.get_int('saved-session-presence');
if (!this._sessionPresenceRestored) {
// We should never save/restore a status other than AVAILABLE
// or BUSY
if (savedStatus != GnomeSession.PresenceStatus.AVAILABLE &&
savedStatus != GnomeSession.PresenceStatus.BUSY)
savedStatus = GnomeSession.PresenceStatus.AVAILABLE;
if (sessionStatus != savedStatus) {
this._presence.status = savedStatus;
return;
}
this._sessionPresenceRestored = true;
}
if ((sessionStatus == GnomeSession.PresenceStatus.AVAILABLE ||
sessionStatus == GnomeSession.PresenceStatus.BUSY) &&
savedStatus != sessionStatus)
global.settings.set_int('saved-session-presence', sessionStatus);
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
let newPresence, status;
let newPresence = this.getIMPresenceForSessionStatus(sessionStatus);
if (!newPresence || newPresence == presence)
return;
status = this._statusForPresence(newPresence);
msg = msg ? msg : '';
this._expectedPresence = newPresence;
this._accountMgr.set_all_requested_presences(newPresence, status, msg);
}
});
const UserMenuButton = new Lang.Class({
Name: 'UserMenuButton',
Extends: PanelMenu.Button,
_init: function() {
this.parent(0.0);
this.actor.accessible_role = Atk.Role.MENU;
let box = new St.BoxLayout({ name: 'panelUserMenu' });
this.actor.add_actor(box);
this._screenSaverSettings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
this._privacySettings = new Gio.Settings({ schema: PRIVACY_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._haveSuspend = true;
this._accountMgr = Tp.AccountManager.dup();
this._loginManager = LoginManager.getLoginManager();
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-symbolic',
style_class: 'popup-menu-icon' });
this._availableIcon = new St.Icon({ icon_name: 'user-available-symbolic',
style_class: 'popup-menu-icon' });
this._busyIcon = new St.Icon({ icon_name: 'user-busy-symbolic',
style_class: 'popup-menu-icon' });
this._invisibleIcon = new St.Icon({ icon_name: 'user-invisible-symbolic',
style_class: 'popup-menu-icon' });
this._awayIcon = new St.Icon({ icon_name: 'user-away-symbolic',
style_class: 'popup-menu-icon' });
this._idleIcon = new St.Icon({ icon_name: 'user-idle-symbolic',
style_class: 'popup-menu-icon' });
this._pendingIcon = new St.Icon({ icon_name: 'user-status-pending-symbolic',
style_class: 'popup-menu-icon' });
this._lockedIcon = new St.Icon({ icon_name: 'changes-prevent-symbolic',
style_class: 'popup-menu-icon' });
this._accountMgr.connect('most-available-presence-changed',
Lang.bind(this, this._updatePresenceIcon));
this._accountMgr.connect('account-enabled',
Lang.bind(this, this._onAccountEnabled));
this._accountMgr.connect('account-removed',
Lang.bind(this, this._onAccountRemoved));
this._accountMgr.prepare_async(null, Lang.bind(this,
function(mgr) {
let [presence, s, msg] = mgr.get_most_available_presence();
this._updatePresenceIcon(mgr, presence, s, msg);
this._setupAccounts();
}));
this._name = new St.Label();
this.actor.label_actor = this._name;
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._updateUserName();
this._createSubMenu();
this._updateSwitch(this._presence.status);
this._presence.connectSignal('StatusChanged', Lang.bind(this, function (proxy, senderName, [status]) {
this._updateSwitch(status);
}));
this._userManager.connect('notify::is-loaded',
Lang.bind(this, this._updateMultiUser));
this._userManager.connect('notify::has-multiple-users',
Lang.bind(this, this._updateMultiUser));
this._userManager.connect('user-added',
Lang.bind(this, this._updateMultiUser));
this._userManager.connect('user-removed',
Lang.bind(this, this._updateMultiUser));
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));
global.settings.connect('changed::' + ALWAYS_SHOW_LOG_OUT_KEY,
Lang.bind(this, this._updateLogout));
this._screenSaverSettings.connect('changed::' + SHOW_FULL_NAME_IN_TOP_BAR_KEY,
Lang.bind(this, this._updateUserName));
this._privacySettings.connect('changed::' + SHOW_FULL_NAME_IN_TOP_BAR_KEY,
Lang.bind(this, this._updateUserName));
this._updateSwitchUser();
this._updateLogout();
this._updateLockScreen();
this._updatesFile = Gio.File.new_for_path('/var/lib/PackageKit/prepared-update');
this._updatesMonitor = this._updatesFile.monitor(Gio.FileMonitorFlags.NONE, null);
this._updatesMonitor.connect('changed', Lang.bind(this, this._updateInstallUpdates));
// 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)
return;
this._updateHaveShutdown();
this._updateHaveSuspend();
}));
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
Lang.bind(this, this._updateHaveShutdown));
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
if (Main.screenShield)
Main.screenShield.connect('locked-changed', Lang.bind(this, this._updatePresenceIcon));
this._sessionUpdated();
},
_sessionUpdated: function() {
this.actor.visible = !Main.sessionMode.isGreeter;
let allowSettings = Main.sessionMode.allowSettings;
this._statusChooser.setSensitive(allowSettings);
this.setSensitive(!Main.sessionMode.isLocked);
this._updatePresenceIcon();
this._updateUserName();
},
_onDestroy: function() {
this._user.disconnect(this._userLoadedId);
this._user.disconnect(this._userChangedId);
},
_updateUserName: function() {
let settings = this._privacySettings;
if (Main.sessionMode.isLocked)
settings = this._screenSaverSettings;
if (this._user.is_loaded && settings.get_boolean(SHOW_FULL_NAME_IN_TOP_BAR_KEY))
this._name.set_text(this._user.get_real_name());
else
this._name.set_text("");
},
_updateMultiUser: function() {
this._updateSwitchUser();
this._updateLogout();
},
_updateSwitchUser: function() {
let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY);
let multiUser = this._userManager.can_switch() && this._userManager.has_multiple_users;
this._loginScreenItem.actor.visible = allowSwitch && multiUser;
},
_updateLogout: function() {
let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY);
let alwaysShow = global.settings.get_boolean(ALWAYS_SHOW_LOG_OUT_KEY);
let systemAccount = this._user.system_account;
let localAccount = this._user.local_account;
let multiUser = this._userManager.has_multiple_users;
let multiSession = Gdm.get_session_ids().length > 1;
this._logoutItem.actor.visible = allowLogout && (alwaysShow || multiUser || multiSession || systemAccount || !localAccount);
},
_updateLockScreen: function() {
let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
this._lockScreenItem.actor.visible = allowLockScreen && LoginManager.canLock();
},
_updateInstallUpdates: function() {
let haveUpdates = this._updatesFile.query_exists(null);
this._installUpdatesItem.actor.visible = haveUpdates && this._haveShutdown;
},
_updateHaveShutdown: function() {
this._session.CanShutdownRemote(Lang.bind(this,
function(result, error) {
if (!error) {
this._haveShutdown = result[0];
this._updateInstallUpdates();
this._updateSuspendOrPowerOff();
}
}));
},
_updateHaveSuspend: function() {
this._loginManager.canSuspend(Lang.bind(this,
function(result) {
this._haveSuspend = result;
this._updateSuspendOrPowerOff();
}));
},
_updateSuspendOrPowerOff: function() {
if (!this._suspendOrPowerOffItem)
return;
this._suspendOrPowerOffItem.actor.visible = this._haveShutdown || this._haveSuspend;
// If we can't power off show Suspend instead
// and disable the alt key
if (!this._haveShutdown) {
this._suspendOrPowerOffItem.updateText(_("Suspend"), null);
} else if (!this._haveSuspend) {
this._suspendOrPowerOffItem.updateText(_("Power Off"), null);
} else {
this._suspendOrPowerOffItem.updateText(_("Power Off"), _("Suspend"));
}
},
_updateSwitch: function(status) {
let active = status == GnomeSession.PresenceStatus.AVAILABLE;
this._notificationsSwitch.setToggleState(active);
},
_updatePresenceIcon: function(accountMgr, presence, status, message) {
if (Main.sessionMode.isLocked)
this._iconBox.child = this._lockedIcon;
else 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;
if (Main.sessionMode.isLocked)
this._iconBox.visible = Main.screenShield.locked;
else
this._iconBox.visible = true;
},
_setupAccounts: function() {
let accounts = this._accountMgr.get_valid_accounts();
for (let i = 0; i < accounts.length; i++) {
accounts[i]._changingId = accounts[i].connect('notify::connection-status',
Lang.bind(this, this._updateChangingPresence));
}
this._updateChangingPresence();
},
_onAccountEnabled: function(accountMgr, account) {
if (!account._changingId)
account._changingId = account.connect('notify::connection-status',
Lang.bind(this, this._updateChangingPresence));
this._updateChangingPresence();
},
_onAccountRemoved: function(accountMgr, account) {
if (account._changingId) {
account.disconnect(account._changingId);
account._changingId = 0;
}
this._updateChangingPresence();
},
_updateChangingPresence: function() {
let accounts = this._accountMgr.get_valid_accounts();
let changing = false;
for (let i = 0; i < accounts.length; i++) {
if (accounts[i].connection_status == Tp.ConnectionStatus.CONNECTING) {
changing = true;
break;
}
}
if (changing) {
this._iconBox.child = this._pendingIcon;
} else {
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
this._updatePresenceIcon(this._accountMgr, presence, s, msg);
}
},
_createSubMenu: function() {
let item;
item = new IMStatusChooserItem();
item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
this.menu.addMenuItem(item);
this._statusChooser = item;
item = new PopupMenu.PopupSwitchMenuItem(_("Notifications"));
item.connect('toggled', Lang.bind(this, this._updatePresenceStatus));
this.menu.addMenuItem(item);
this._notificationsSwitch = item;
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
this.menu.addSettingsAction(_("Settings"), 'gnome-control-center.desktop');
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(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.PopupMenuItem(_("Lock"));
item.connect('activate', Lang.bind(this, this._onLockScreenActivate));
this.menu.addMenuItem(item);
this._lockScreenItem = item;
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
item = new PopupMenu.PopupAlternatingMenuItem(_("Power Off"),
_("Suspend"));
this.menu.addMenuItem(item);
item.connect('activate', Lang.bind(this, this._onSuspendOrPowerOffActivate));
this._suspendOrPowerOffItem = item;
this._updateSuspendOrPowerOff();
item = new PopupMenu.PopupMenuItem(_("Install Updates & Restart"));
item.connect('activate', Lang.bind(this, this._onInstallUpdatesActivate));
this.menu.addMenuItem(item);
this._installUpdatesItem = item;
},
_updatePresenceStatus: function(item, event) {
let status;
if (item.state) {
status = GnomeSession.PresenceStatus.AVAILABLE;
} else {
status = GnomeSession.PresenceStatus.BUSY;
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
let newPresence = this._statusChooser.getIMPresenceForSessionStatus(status);
if (newPresence != presence &&
newPresence == Tp.ConnectionPresenceType.BUSY)
Main.notify(_("Your chat status will be set to busy"),
_("Notifications are now disabled, including chat messages. Your online status has been adjusted to let others know that you might not see their messages."));
}
this._presence.status = status;
},
_onMyAccountActivate: function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().lookup_app('gnome-user-accounts-panel.desktop');
app.activate();
},
_onLockScreenActivate: function() {
this.menu.close(BoxPointer.PopupAnimation.NONE);
Main.overview.hide();
Main.screenShield.lock(true);
},
_onLoginScreenActivate: function() {
this.menu.close(BoxPointer.PopupAnimation.NONE);
Main.overview.hide();
if (Main.screenShield)
Main.screenShield.lock(false);
Gdm.goto_login_session_sync(null);
},
_onQuitSessionActivate: function() {
Main.overview.hide();
this._session.LogoutRemote(0);
},
_onInstallUpdatesActivate: function() {
Main.overview.hide();
Util.spawn(['pkexec', '/usr/libexec/pk-trigger-offline-update']);
this._session.RebootRemote();
},
_openSessionWarnDialog: function(sessions) {
let dialog = new ModalDialog.ModalDialog();
let subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject',
text: _("Other users are logged in.") });
dialog.contentLayout.add(subjectLabel, { y_fill: true,
y_align: St.Align.START });
let descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description'});
descriptionLabel.set_text(_("Shutting down might cause them to lose unsaved work."));
descriptionLabel.clutter_text.line_wrap = true;
dialog.contentLayout.add(descriptionLabel, { x_fill: true,
y_fill: true,
y_align: St.Align.START });
let scrollView = new St.ScrollView({ style_class: 'end-session-dialog-app-list' });
scrollView.add_style_class_name('vfade');
scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
dialog.contentLayout.add(scrollView, { x_fill: true, y_fill: true });
let userList = new St.BoxLayout({ vertical: true });
scrollView.add_actor(userList);
for (let i = 0; i < sessions.length; i++) {
let session = sessions[i];
let userEntry = new St.BoxLayout({ style_class: 'login-dialog-user-list-item',
vertical: false });
let avatar = new UserWidget.Avatar(session.user);
avatar.update();
userEntry.add(avatar.actor);
let userLabelText = "";;
let userName = session.user.get_real_name() ?
session.user.get_real_name() : session.username;
if (session.info.remote)
/* Translators: Remote here refers to a remote session, like a ssh login */
userLabelText = _("%s (remote)").format(userName);
else if (session.info.type == "tty")
/* Translators: Console here refers to a tty like a VT console */
userLabelText = _("%s (console)").format(userName);
else
userLabelText = userName;
let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box',
vertical: true });
textLayout.add(new St.Label({ text: userLabelText }),
{ y_fill: false,
y_align: St.Align.MIDDLE,
expand: true });
userEntry.add(textLayout, { expand: true });
userList.add(userEntry, { x_fill: true });
}
let cancelButton = { label: _("Cancel"),
action: function() { dialog.close(); },
key: Clutter.Escape };
let powerOffButton = { label: _("Power Off"), action: Lang.bind(this, function() {
dialog.close();
this._session.ShutdownRemote();
}), default: true };
dialog.setButtons([cancelButton, powerOffButton]);
dialog.open();
},
_onSuspendOrPowerOffActivate: function() {
Main.overview.hide();
if (this._haveShutdown &&
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
this._loginManager.listSessions(Lang.bind(this,
function(result) {
let sessions = [];
let n = 0;
for (let i = 0; i < result.length; i++) {
let[id, uid, userName, seat, sessionPath] = result[i];
let proxy = new SystemdLoginSession(Gio.DBus.system,
'org.freedesktop.login1',
sessionPath);
if (proxy.Class != 'user')
continue;
if (proxy.State == 'closing')
continue;
if (proxy.Id == GLib.getenv('XDG_SESSION_ID'))
continue;
sessions.push({ user: this._userManager.get_user(userName),
username: userName,
info: { type: proxy.Type,
remote: proxy.Remote }
});
// limit the number of entries
n++;
if (n == MAX_USERS_IN_SESSION_DIALOG)
break;
}
if (n != 0)
this._openSessionWarnDialog(sessions);
else
this._session.ShutdownRemote();
}));
} else {
this.menu.close(BoxPointer.PopupAnimation.NONE);
this._loginManager.suspend();
}
}
});

View File

@ -24,7 +24,7 @@ const Avatar = new Lang.Class({
this._user = user;
params = Params.parse(params, { reactive: false,
iconSize: AVATAR_ICON_SIZE,
styleClass: 'status-chooser-user-icon' });
styleClass: 'framed-user-icon' });
this._iconSize = params.iconSize;
this.actor = new St.Bin({ style_class: params.styleClass,

View File

@ -473,13 +473,17 @@ const WindowManager = new Lang.Class({
this._dimWindow(this._dimmedWindows[i]);
}));
this._workspaceTracker = new WorkspaceTracker(this);
if (Main.sessionMode.hasWorkspaces)
this._workspaceTracker = new WorkspaceTracker(this);
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
false, -1, 1);
},
keepWorkspaceAlive: function(workspace, duration) {
if (!this._workspaceTracker)
return;
this._workspaceTracker.keepWorkspaceAlive(workspace, duration);
},
@ -829,7 +833,7 @@ const WindowManager = new Lang.Class({
},
_switchWorkspace : function(shellwm, from, to, direction) {
if (!this._shouldAnimate()) {
if (!Main.sessionMode.hasWorkspaces || !this._shouldAnimate()) {
shellwm.completed_switch_workspace();
return;
}
@ -982,6 +986,9 @@ const WindowManager = new Lang.Class({
},
_showWorkspaceSwitcher : function(display, screen, window, binding) {
if (!Main.sessionMode.hasWorkspaces)
return;
if (screen.n_workspaces == 1)
return;
@ -1023,14 +1030,19 @@ const WindowManager = new Lang.Class({
},
actionMoveWorkspace: function(workspace) {
if (!Main.sessionMode.hasWorkspaces)
return;
let activeWorkspace = global.screen.get_active_workspace();
if (activeWorkspace != workspace)
workspace.activate(global.get_current_time());
},
actionMoveWindow: function(window, workspace) {
if (!Main.sessionMode.hasWorkspaces)
return;
let activeWorkspace = global.screen.get_active_workspace();
if (activeWorkspace != workspace) {
@ -1043,6 +1055,5 @@ const WindowManager = new Lang.Class({
global.display.clear_mouse_mode();
workspace.activate_with_focus (window, global.get_current_time());
}
},
});

View File

@ -7,6 +7,7 @@ data/gnome-shell.desktop.in.in
data/gnome-shell-extension-prefs.desktop.in.in
data/org.gnome.shell.gschema.xml.in.in
js/extensionPrefs/main.js
js/gdm/authPrompt.js
js/gdm/loginDialog.js
js/gdm/powerMenu.js
js/gdm/util.js
@ -48,9 +49,9 @@ js/ui/status/keyboard.js
js/ui/status/lockScreenMenu.js
js/ui/status/network.js
js/ui/status/power.js
js/ui/status/system.js
js/ui/status/volume.js
js/ui/unlockDialog.js
js/ui/userMenu.js
js/ui/viewSelector.js
js/ui/wanda.js
js/ui/windowAttentionHandler.js

450
po/cs.po
View File

@ -12,8 +12,8 @@ msgstr ""
"Project-Id-Version: gnome-shell\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&keywords=I18N+L10N&component=general\n"
"POT-Creation-Date: 2013-06-27 11:02+0000\n"
"PO-Revision-Date: 2013-06-28 18:32+0200\n"
"POT-Creation-Date: 2013-07-26 08:43+0000\n"
"PO-Revision-Date: 2013-07-28 13:09+0200\n"
"Last-Translator: Marek Černocký <marek@manet.cz>\n"
"Language-Team: Czech <gnome-cs-list@gnome.org>\n"
"Language: cs\n"
@ -32,7 +32,7 @@ msgstr "Snímky obrazovky"
msgid "Record a screencast"
msgstr "Nahrát dění na obrazovce"
#: ../data/50-gnome-shell-system.xml.in.h:1
#: ../data/50-gnome-shell-system.xml.in.h:1 ../js/ui/status/system.js:48
msgid "System"
msgstr "Systém"
@ -363,41 +363,45 @@ msgid "Select an extension to configure using the combobox above."
msgstr ""
"Pomocí rozbalovacího seznamu výše zvolte rozšíření, které chete nastavit."
#: ../js/gdm/loginDialog.js:308
#: ../js/gdm/authPrompt.js:132 ../js/ui/components/networkAgent.js:137
#: ../js/ui/components/polkitAgent.js:161 ../js/ui/endSessionDialog.js:376
#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
#: ../js/ui/status/bluetooth.js:218 ../js/ui/status/system.js:305
msgid "Cancel"
msgstr "Zrušit"
#: ../js/gdm/authPrompt.js:154 ../js/gdm/authPrompt.js:206
msgid "Next"
msgstr "Následující"
#: ../js/gdm/authPrompt.js:202 ../js/ui/shellMountOperation.js:403
#: ../js/ui/unlockDialog.js:55
msgid "Unlock"
msgstr "Odemknout"
#: ../js/gdm/authPrompt.js:204
msgctxt "button"
msgid "Sign In"
msgstr "Přihlásit se"
#: ../js/gdm/loginDialog.js:299
msgid "Choose Session"
msgstr "Vybrat sezení"
#: ../js/gdm/loginDialog.js:326
#: ../js/gdm/loginDialog.js:317
msgid "Session"
msgstr "Sezení"
#. translators: this message is shown below the user list on the
#. login screen. It can be activated to reveal an entry for
#. manually entering the username.
#: ../js/gdm/loginDialog.js:528
#: ../js/gdm/loginDialog.js:468
msgid "Not listed?"
msgstr "Nejste na seznamu?"
#: ../js/gdm/loginDialog.js:810 ../js/ui/components/networkAgent.js:137
#: ../js/ui/components/polkitAgent.js:161 ../js/ui/endSessionDialog.js:376
#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
#: ../js/ui/status/bluetooth.js:449 ../js/ui/unlockDialog.js:95
#: ../js/ui/userMenu.js:884
msgid "Cancel"
msgstr "Zrušit"
#: ../js/gdm/loginDialog.js:833
msgctxt "button"
msgid "Sign In"
msgstr "Přihlásit se"
#: ../js/gdm/loginDialog.js:833
msgid "Next"
msgstr "Následující"
#. Translators: this message is shown below the username entry field
#. to clue the user in on how to login to the local network realm
#: ../js/gdm/loginDialog.js:934
#: ../js/gdm/loginDialog.js:630
#, c-format
msgid "(e.g., user or %s)"
msgstr "(např. uživatel nebo %s)"
@ -405,12 +409,12 @@ msgstr "(např. uživatel nebo %s)"
#. 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)
#: ../js/gdm/loginDialog.js:938 ../js/ui/components/networkAgent.js:260
#: ../js/gdm/loginDialog.js:635 ../js/ui/components/networkAgent.js:260
#: ../js/ui/components/networkAgent.js:278
msgid "Username: "
msgstr "Uživatelské jméno: "
#: ../js/gdm/loginDialog.js:1205
#: ../js/gdm/loginDialog.js:886
msgid "Login Window"
msgstr "Přihlašovací okno"
@ -419,8 +423,7 @@ msgstr "Přihlašovací okno"
msgid "Power"
msgstr "Vypnout"
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:651 ../js/ui/userMenu.js:655
#: ../js/ui/userMenu.js:768
#: ../js/gdm/powerMenu.js:93
msgid "Suspend"
msgstr "Uspat do paměti"
@ -428,18 +431,18 @@ msgstr "Uspat do paměti"
msgid "Restart"
msgstr "Restartovat"
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:653
#: ../js/ui/userMenu.js:655 ../js/ui/userMenu.js:767 ../js/ui/userMenu.js:888
#: ../js/gdm/powerMenu.js:103 ../js/ui/status/system.js:215
#: ../js/ui/status/system.js:309
msgid "Power Off"
msgstr "Vypnout"
#: ../js/gdm/util.js:248
#: ../js/gdm/util.js:251
msgid "Authentication error"
msgstr "Chyba ověření"
#. Translators: this message is shown below the password entry field
#. to indicate the user can swipe their finger instead
#: ../js/gdm/util.js:365
#: ../js/gdm/util.js:369
msgid "(or swipe finger)"
msgstr "(nebo otiskněte prst)"
@ -458,23 +461,23 @@ msgstr "Nelze analyzovat příkaz:"
msgid "Execution of '%s' failed:"
msgstr "Vykonání „%s“ selhalo:"
#: ../js/ui/appDisplay.js:397
#: ../js/ui/appDisplay.js:386
msgid "Frequent"
msgstr "Časté"
#: ../js/ui/appDisplay.js:404
#: ../js/ui/appDisplay.js:393
msgid "All"
msgstr "Všechny"
#: ../js/ui/appDisplay.js:996
#: ../js/ui/appDisplay.js:986
msgid "New Window"
msgstr "Nové okno"
#: ../js/ui/appDisplay.js:999 ../js/ui/dash.js:284
#: ../js/ui/appDisplay.js:989 ../js/ui/dash.js:284
msgid "Remove from Favorites"
msgstr "Odstranit z oblíbených"
#: ../js/ui/appDisplay.js:1000
#: ../js/ui/appDisplay.js:990
msgid "Add to Favorites"
msgstr "Přidat mezi oblíbené"
@ -488,7 +491,7 @@ msgstr "%s byl přidán mezi oblíbené."
msgid "%s has been removed from your favorites."
msgstr "%s byl odstraněn z oblíbených."
#: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:744
#: ../js/ui/backgroundMenu.js:19 ../js/ui/status/system.js:207
msgid "Settings"
msgstr "Nastavení"
@ -1152,7 +1155,6 @@ msgid "Download and install '%s' from extensions.gnome.org?"
msgstr "Stáhnout a nainstalovat „%s“ z extensions.gnome.org?"
#: ../js/ui/keyboard.js:619 ../js/ui/status/keyboard.js:333
#: ../js/ui/status/power.js:211
msgid "Keyboard"
msgstr "Klávesnice"
@ -1204,35 +1206,35 @@ msgstr "Zobrazit zdroj"
msgid "Web Page"
msgstr "Webová stránka"
#: ../js/ui/messageTray.js:1182
#: ../js/ui/messageTray.js:1241
msgid "Open"
msgstr "Otevřít"
#: ../js/ui/messageTray.js:1189
#: ../js/ui/messageTray.js:1248
msgid "Remove"
msgstr "Odstranit"
#: ../js/ui/messageTray.js:1501
#: ../js/ui/messageTray.js:1564
msgid "Clear Messages"
msgstr "Vymazat zprávy"
#: ../js/ui/messageTray.js:1528
#: ../js/ui/messageTray.js:1591
msgid "Notification Settings"
msgstr "Nastavení upozornění"
#: ../js/ui/messageTray.js:1711
#: ../js/ui/messageTray.js:1774
msgid "No Messages"
msgstr "Žádné zprávy"
#: ../js/ui/messageTray.js:1784
#: ../js/ui/messageTray.js:1846
msgid "Message Tray"
msgstr "Lišta zpráv"
#: ../js/ui/messageTray.js:2811
#: ../js/ui/messageTray.js:2852
msgid "System Information"
msgstr "Informace o systému"
#: ../js/ui/notificationDaemon.js:629 ../src/shell-app.c:378
#: ../js/ui/notificationDaemon.js:629 ../src/shell-app.c:396
msgctxt "program"
msgid "Unknown"
msgstr "Neznámé"
@ -1261,17 +1263,17 @@ msgstr "Přehled"
msgid "Type to search…"
msgstr "Vyhledávejte psaním…"
#: ../js/ui/panel.js:567
#: ../js/ui/panel.js:568
msgid "Quit"
msgstr "Ukončit"
#. Translators: If there is no suitable word for "Activities"
#. in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:618
#: ../js/ui/panel.js:619
msgid "Activities"
msgstr "Činnosti"
#: ../js/ui/panel.js:914
#: ../js/ui/panel.js:915
msgid "Top Bar"
msgstr "Horní lišta"
@ -1280,7 +1282,7 @@ msgstr "Horní lišta"
#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
#. switches containing "◯" and "|"). Other values will
#. simply result in invisible toggle switches.
#: ../js/ui/popupMenu.js:549
#: ../js/ui/popupMenu.js:488
msgid "toggle-switch-us"
msgstr "toggle-switch-intl"
@ -1306,7 +1308,7 @@ msgstr[0] "%d nové upozornění"
msgstr[1] "%d nová upozornění"
msgstr[2] "%d nových upozornění"
#: ../js/ui/screenShield.js:449 ../js/ui/userMenu.js:759
#: ../js/ui/screenShield.js:449 ../js/ui/status/system.js:211
msgid "Lock"
msgstr "Uzamknout"
@ -1321,11 +1323,11 @@ msgstr "GNOME potřebuje uzamknout obrazovku"
#.
#. XXX: another option is to kick the user into the gdm login
#. screen, where we're not affected by grabs
#: ../js/ui/screenShield.js:775 ../js/ui/screenShield.js:1215
#: ../js/ui/screenShield.js:788 ../js/ui/screenShield.js:1216
msgid "Unable to lock"
msgstr "Nelze uzamknout obrazovku"
#: ../js/ui/screenShield.js:776 ../js/ui/screenShield.js:1216
#: ../js/ui/screenShield.js:789 ../js/ui/screenShield.js:1217
msgid "Lock was blocked by an application"
msgstr "Zamknutí bylo zablokováno některou z aplikací"
@ -1361,10 +1363,6 @@ msgstr "Heslo"
msgid "Remember Password"
msgstr "Pamatovat si heslo"
#: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:108
msgid "Unlock"
msgstr "Odemknout"
#: ../js/ui/status/accessibility.js:36
msgid "Accessibility"
msgstr "Zpřístupnění"
@ -1401,142 +1399,107 @@ msgstr "Vícenásobné stisky kláves"
msgid "Mouse Keys"
msgstr "Myš klávesnicí"
#: ../js/ui/status/accessibility.js:72
msgid "Universal Access Settings"
msgstr "Nastavení zpřístupnění"
#: ../js/ui/status/accessibility.js:129
#: ../js/ui/status/accessibility.js:126
msgid "High Contrast"
msgstr "Vysoký kontrast"
#: ../js/ui/status/accessibility.js:178
#: ../js/ui/status/accessibility.js:175
msgid "Large Text"
msgstr "Styl velkého textu"
#: ../js/ui/status/bluetooth.js:28 ../js/ui/status/bluetooth.js:32
#: ../js/ui/status/bluetooth.js:290 ../js/ui/status/bluetooth.js:327
#: ../js/ui/status/bluetooth.js:355 ../js/ui/status/bluetooth.js:391
#: ../js/ui/status/bluetooth.js:422 ../js/ui/status/network.js:713
#. The Bluetooth menu only appears when Bluetooth is in use,
#. so just statically build it with a "Turn Off" menu item.
#: ../js/ui/status/bluetooth.js:21 ../js/ui/status/bluetooth.js:25
#: ../js/ui/status/bluetooth.js:59 ../js/ui/status/bluetooth.js:96
#: ../js/ui/status/bluetooth.js:124 ../js/ui/status/bluetooth.js:160
#: ../js/ui/status/bluetooth.js:191 ../js/ui/status/network.js:713
msgid "Bluetooth"
msgstr "Bluetooth"
#: ../js/ui/status/bluetooth.js:45
msgid "Visibility"
msgstr "Viditelnost"
#: ../js/ui/status/bluetooth.js:27
msgid "Turn Off"
msgstr "Vypnout"
#: ../js/ui/status/bluetooth.js:59
msgid "Send Files to Device…"
msgstr "Odeslat soubory do zařízení…"
#: ../js/ui/status/bluetooth.js:60
msgid "Set Up a New Device…"
msgstr "Nastavit nové zařízení…"
#: ../js/ui/status/bluetooth.js:84
#: ../js/ui/status/bluetooth.js:30
#| msgid "Bluetooth"
msgid "Bluetooth Settings"
msgstr "Nastavit Bluetooth"
msgstr "Nastavení Bluetooth"
#. TRANSLATORS: this means that bluetooth was disabled by hardware rfkill
#: ../js/ui/status/bluetooth.js:105 ../js/ui/status/network.js:140
msgid "hardware disabled"
msgstr "zařízení zakázáno"
#: ../js/ui/status/bluetooth.js:54
#, c-format
msgid "%d Connected Device"
msgid_plural "%d Connected Devices"
msgstr[0] "%d připojené zařízení"
msgstr[1] "%d připojená zařízení"
msgstr[2] "%d připojených zařízení"
#: ../js/ui/status/bluetooth.js:198
msgid "Connection"
msgstr "Připojení"
#: ../js/ui/status/bluetooth.js:209 ../js/ui/status/network.js:399
msgid "disconnecting..."
msgstr "odpojování…"
#: ../js/ui/status/bluetooth.js:222 ../js/ui/status/network.js:405
#: ../js/ui/status/network.js:1298
msgid "connecting..."
msgstr "připojování…"
#: ../js/ui/status/bluetooth.js:240
msgid "Send Files…"
msgstr "Odeslat soubory…"
#: ../js/ui/status/bluetooth.js:247
msgid "Keyboard Settings"
msgstr "Nastavení klávesnice"
#: ../js/ui/status/bluetooth.js:250
msgid "Mouse Settings"
msgstr "Nastavení myši"
#: ../js/ui/status/bluetooth.js:255 ../js/ui/status/volume.js:313
msgid "Sound Settings"
msgstr "Nastavení zvuku"
#: ../js/ui/status/bluetooth.js:328 ../js/ui/status/bluetooth.js:356
#: ../js/ui/status/bluetooth.js:97 ../js/ui/status/bluetooth.js:125
#, c-format
msgid "Authorization request from %s"
msgstr "Požadavek na autorizaci od %s"
#: ../js/ui/status/bluetooth.js:334 ../js/ui/status/bluetooth.js:399
#: ../js/ui/status/bluetooth.js:430
#: ../js/ui/status/bluetooth.js:103 ../js/ui/status/bluetooth.js:168
#: ../js/ui/status/bluetooth.js:199
#, c-format
msgid "Device %s wants to pair with this computer"
msgstr "Zařízení %s se chce spárovat s tímto počítačem"
#: ../js/ui/status/bluetooth.js:336
#: ../js/ui/status/bluetooth.js:105
msgid "Allow"
msgstr "Povolit"
#: ../js/ui/status/bluetooth.js:337
#: ../js/ui/status/bluetooth.js:106
msgid "Deny"
msgstr "Zamítnout"
#: ../js/ui/status/bluetooth.js:362
#: ../js/ui/status/bluetooth.js:131
#, c-format
msgid "Device %s wants access to the service '%s'"
msgstr "Zařízení %s požaduje přístup ke službě „%s“"
#: ../js/ui/status/bluetooth.js:364
#: ../js/ui/status/bluetooth.js:133
msgid "Always grant access"
msgstr "Vždy udělovat přístup"
#: ../js/ui/status/bluetooth.js:365
#: ../js/ui/status/bluetooth.js:134
msgid "Grant this time only"
msgstr "Udělit pouze tentokrát"
#: ../js/ui/status/bluetooth.js:366
#: ../js/ui/status/bluetooth.js:135
msgid "Reject"
msgstr "Odmítnout"
#. Translators: argument is the device short name
#: ../js/ui/status/bluetooth.js:393
#: ../js/ui/status/bluetooth.js:162
#, c-format
msgid "Pairing confirmation for %s"
msgstr "Potvrzení spárování pro %s"
#: ../js/ui/status/bluetooth.js:400
#: ../js/ui/status/bluetooth.js:169
#, c-format
msgid ""
"Please confirm whether the Passkey '%06d' matches the one on the device."
msgstr "Ověřte prosím, zda klíč „%06d“ odpovídá tomu na zařízení."
#. Translators: this is the verb, not the noun
#: ../js/ui/status/bluetooth.js:403
#: ../js/ui/status/bluetooth.js:172
msgid "Matches"
msgstr "Souhlasí"
#: ../js/ui/status/bluetooth.js:404
#: ../js/ui/status/bluetooth.js:173
msgid "Does not match"
msgstr "Nesouhlasí"
#: ../js/ui/status/bluetooth.js:423
#: ../js/ui/status/bluetooth.js:192
#, c-format
msgid "Pairing request for %s"
msgstr "Požadavek na spárování pro %s"
#: ../js/ui/status/bluetooth.js:431
#: ../js/ui/status/bluetooth.js:200
msgid "Please enter the PIN mentioned on the device."
msgstr "Zadejte prosím PIN, který je uveden na zařízení."
#: ../js/ui/status/bluetooth.js:448
#: ../js/ui/status/bluetooth.js:217
msgid "OK"
msgstr "Budiž"
@ -1544,10 +1507,6 @@ msgstr "Budiž"
msgid "Show Keyboard Layout"
msgstr "Zobrazit rozložení klávesnice"
#: ../js/ui/status/keyboard.js:401
msgid "Region & Language Settings"
msgstr "Místní a jazyková nastavení"
#: ../js/ui/status/lockScreenMenu.js:43
msgid "Volume, network, battery"
msgstr "Hlasitost, síť, baterie"
@ -1560,6 +1519,10 @@ msgstr "<neznámé>"
msgid "Wi-Fi"
msgstr "Wi-Fi"
#: ../js/ui/status/network.js:140
msgid "hardware disabled"
msgstr "zařízení zakázáno"
#. Translators: this indicates that wireless or wwan is disabled by hardware killswitch
#: ../js/ui/status/network.js:162
msgid "disabled"
@ -1571,6 +1534,14 @@ msgstr "zakázáno"
msgid "unmanaged"
msgstr "nespravováno"
#: ../js/ui/status/network.js:399
msgid "disconnecting..."
msgstr "odpojování…"
#: ../js/ui/status/network.js:405 ../js/ui/status/network.js:1298
msgid "connecting..."
msgstr "připojování…"
#. Translators: this is for network connections that require some kind of key or password
#: ../js/ui/status/network.js:408 ../js/ui/status/network.js:1301
msgid "authentication required"
@ -1614,220 +1585,97 @@ msgstr "Mobilní širokopásmová"
msgid "Enable networking"
msgstr "Povolit síť"
#: ../js/ui/status/network.js:1522
msgid "Network Settings"
msgstr "Nastavení sítě"
#: ../js/ui/status/network.js:1539
#: ../js/ui/status/network.js:1537
msgid "Network Manager"
msgstr "Network Manager"
#: ../js/ui/status/network.js:1623
#: ../js/ui/status/network.js:1621
msgid "Connection failed"
msgstr "Připojení selhalo"
#: ../js/ui/status/network.js:1624
#: ../js/ui/status/network.js:1622
msgid "Activation of network connection failed"
msgstr "Aktivace síťového připojení selhala"
#: ../js/ui/status/network.js:1937
#: ../js/ui/status/network.js:1936
msgid "Networking is disabled"
msgstr "Síť je zakázána"
#: ../js/ui/status/power.js:55
#: ../js/ui/status/power.js:31 ../js/ui/status/power.js:44
msgid "Battery"
msgstr "Baterie"
#: ../js/ui/status/power.js:81
#: ../js/ui/status/power.js:45
msgid "Power Settings"
msgstr "Nastavení napájení"
#: ../js/ui/status/power.js:61
msgid "Fully Charged"
msgstr "Plně nabito"
#. 0 is reported when UPower does not have enough data
#. to estimate battery life
#: ../js/ui/status/power.js:99
#. state is one of PENDING_CHARGING, PENDING_DISCHARGING
#: ../js/ui/status/power.js:67 ../js/ui/status/power.js:84
msgid "Estimating…"
msgstr "Odhaduje se…"
#: ../js/ui/status/power.js:106
#. Translators: this is <hours>:<minutes> Remaining (<percentage>)
#: ../js/ui/status/power.js:75
#, c-format
msgid "%d hour remaining"
msgid_plural "%d hours remaining"
msgstr[0] "Zbývá %d hodina"
msgstr[1] "Zbývají %d hodiny"
msgstr[2] "Zbývá %d hodin"
msgid "%d\\u2236%d Remaining (%d%%)"
msgstr "Zbývá %d\\u2236%d (%d%%)"
#. TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining"
#: ../js/ui/status/power.js:109
#. Translators: this is <hours>:<minutes> Until Full (<percentage>)
#: ../js/ui/status/power.js:80
#, c-format
msgid "%d %s %d %s remaining"
msgstr "Zbývá %d %s %d %s"
msgid "%d\\u2236%d Until Full (%d%%)"
msgstr "%d\\u2236%d do nabití (%d%%)"
#: ../js/ui/status/power.js:111
msgid "hour"
msgid_plural "hours"
msgstr[0] "hodina"
msgstr[1] "hodiny"
msgstr[2] "hodin"
#: ../js/ui/status/power.js:111
msgid "minute"
msgid_plural "minutes"
msgstr[0] "minuta"
msgstr[1] "minuty"
msgstr[2] "minut"
#: ../js/ui/status/power.js:114
#, c-format
msgid "%d minute remaining"
msgid_plural "%d minutes remaining"
msgstr[0] "zbývá %d minuta"
msgstr[1] "zbývají %d minuty"
msgstr[2] "zbývá %d minut"
#: ../js/ui/status/power.js:117 ../js/ui/status/power.js:191
#, c-format
msgctxt "percent of battery remaining"
msgid "%d%%"
msgstr "%d%%"
#: ../js/ui/status/power.js:201
msgid "AC Adapter"
msgstr "Napájecí adaptér"
#: ../js/ui/status/power.js:203
msgid "Laptop Battery"
msgstr "Baterie notebooku"
#: ../js/ui/status/power.js:205
msgid "UPS"
msgstr "Záložní zdroj"
#: ../js/ui/status/power.js:207
msgid "Monitor"
msgstr "Monitor"
#: ../js/ui/status/power.js:209
msgid "Mouse"
msgstr "Myš"
#: ../js/ui/status/power.js:213
msgid "PDA"
msgstr "PDA"
#: ../js/ui/status/power.js:215
msgid "Cell Phone"
msgstr "Mobilní telefon"
#: ../js/ui/status/power.js:217
msgid "Media Player"
msgstr "Multimediální přehrávač"
#: ../js/ui/status/power.js:219
msgid "Tablet"
msgstr "Tablet"
#: ../js/ui/status/power.js:221
msgid "Computer"
msgstr "Počítač"
#: ../js/ui/status/power.js:223
msgctxt "device"
msgid "Unknown"
msgstr "Neznámé"
#: ../js/ui/status/volume.js:121
msgid "Volume changed"
msgstr "Hlasitost změněna"
#. Translators: This is the label for audio volume
#: ../js/ui/status/volume.js:246 ../js/ui/status/volume.js:294
msgid "Volume"
msgstr "Hlasitost"
#: ../js/ui/status/volume.js:255
msgid "Microphone"
msgstr "Mikrofon"
#: ../js/ui/unlockDialog.js:119
msgid "Log in as another user"
msgstr "Přihlásit se jako jiný uživatel"
#: ../js/ui/unlockDialog.js:140
msgid "Unlock Window"
msgstr "Odemykací okno"
#: ../js/ui/userMenu.js:149
msgid "Available"
msgstr "Přítomen"
#: ../js/ui/userMenu.js:152
msgid "Busy"
msgstr "Zaneprázdněn"
#: ../js/ui/userMenu.js:155
msgid "Invisible"
msgstr "Neviditelný"
#: ../js/ui/userMenu.js:158
msgid "Away"
msgstr "Nepřítomen"
#: ../js/ui/userMenu.js:161
msgid "Idle"
msgstr "Nečinný"
#: ../js/ui/userMenu.js:164
msgid "Offline"
msgstr "Odpojen"
#: ../js/ui/userMenu.js:736
msgid "Notifications"
msgstr "Upozornění"
#: ../js/ui/userMenu.js:749
#: ../js/ui/status/system.js:188
msgid "Switch User"
msgstr "Přepnout uživatele"
#: ../js/ui/userMenu.js:754
#: ../js/ui/status/system.js:193
msgid "Log Out"
msgstr "Odhlásit se"
#: ../js/ui/userMenu.js:774
msgid "Install Updates & Restart"
msgstr "Nainstalovat aktualizace a restartovat"
#: ../js/ui/userMenu.js:792
msgid "Your chat status will be set to busy"
msgstr "Váš stav v konverzacích byl nastaven na „Zaneprázdněn“"
#: ../js/ui/userMenu.js:793
msgid ""
"Notifications are now disabled, including chat messages. Your online status "
"has been adjusted to let others know that you might not see their messages."
msgstr ""
"Upozornění jsou nyní vypnuta, včetně zpráv v konverzacích. Váš stav on-line "
"byl změněn tak, aby ostatní věděli, že si jejich zprávy nemusíte přečíst."
#: ../js/ui/userMenu.js:834
#: ../js/ui/status/system.js:255
msgid "Other users are logged in."
msgstr "Jsou přihlášeni jiní uživatelé."
#: ../js/ui/userMenu.js:839
#: ../js/ui/status/system.js:260
msgid "Shutting down might cause them to lose unsaved work."
msgstr "Vypnutí by mohlo způsobit ztrátu jejich neuložené práce."
#. Translators: Remote here refers to a remote session, like a ssh login
#: ../js/ui/userMenu.js:867
#: ../js/ui/status/system.js:288
#, c-format
msgid "%s (remote)"
msgstr "%s (vzdálený)"
#. Translators: Console here refers to a tty like a VT console
#: ../js/ui/userMenu.js:870
#: ../js/ui/status/system.js:291
#, c-format
msgid "%s (console)"
msgstr "%s (konzole)"
#: ../js/ui/status/volume.js:126
msgid "Volume changed"
msgstr "Hlasitost změněna"
#: ../js/ui/status/volume.js:302
msgid "Volume"
msgstr "Hlasitost"
#: ../js/ui/unlockDialog.js:63
msgid "Log in as another user"
msgstr "Přihlásit se jako jiný uživatel"
#: ../js/ui/unlockDialog.js:80
msgid "Unlock Window"
msgstr "Odemykací okno"
#: ../js/ui/viewSelector.js:100
msgid "Applications"
msgstr "Aplikace"
@ -1899,7 +1747,7 @@ msgstr "Použít pro přihlašovací obrazovku určitý mód, např. „gdm“."
msgid "List possible modes"
msgstr "Vypsat možné režimy"
#: ../src/shell-app.c:626
#: ../src/shell-app.c:644
#, c-format
msgid "Failed to launch '%s'"
msgstr "Nelze spustit „%s“"
@ -1914,4 +1762,4 @@ msgstr "Heslo nemůže být prázdné."
#: ../src/shell-polkit-authentication-agent.c:343
msgid "Authentication dialog was dismissed by the user"
msgstr "Dialogové okno ověření bylo uživatelem zrušeno"
msgstr "Dialogové okno ověření bylo uživatelem zrušeno"

713
po/de.po

File diff suppressed because it is too large Load Diff

478
po/es.po
View File

@ -10,8 +10,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: 2013-06-26 18:42+0000\n"
"PO-Revision-Date: 2013-06-27 12:33+0200\n"
"POT-Creation-Date: 2013-07-19 18:59+0000\n"
"PO-Revision-Date: 2013-07-22 13:22+0200\n"
"Last-Translator: Daniel Mustieles <daniel.mustieles@gmail.com>\n"
"Language-Team: Español <gnome-es-list@gnome.org>\n"
"Language: \n"
@ -29,7 +29,7 @@ msgstr "Capturas de pantalla"
msgid "Record a screencast"
msgstr "Grabar una captura de pantalla"
#: ../data/50-gnome-shell-system.xml.in.h:1
#: ../data/50-gnome-shell-system.xml.in.h:1 ../js/ui/status/system.js:48
msgid "System"
msgstr "Sistema"
@ -369,43 +369,33 @@ msgid "Select an extension to configure using the combobox above."
msgstr ""
"Seleccione una extensión que configurar usando la caja combinada de arriba."
#: ../js/gdm/loginDialog.js:302
#| msgid "Switch Session"
#: ../js/gdm/loginDialog.js:298
msgid "Choose Session"
msgstr "Elegir sesión"
#: ../js/gdm/loginDialog.js:320
#| msgid "Session…"
#: ../js/gdm/loginDialog.js:316
msgid "Session"
msgstr "Sesión"
#. translators: this message is shown below the user list on the
#. login screen. It can be activated to reveal an entry for
#. manually entering the username.
#: ../js/gdm/loginDialog.js:522
#: ../js/gdm/loginDialog.js:478
msgid "Not listed?"
msgstr "¿No está en la lista?"
#: ../js/gdm/loginDialog.js:739 ../js/ui/components/networkAgent.js:137
#: ../js/ui/components/polkitAgent.js:161 ../js/ui/endSessionDialog.js:376
#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
#: ../js/ui/status/bluetooth.js:449 ../js/ui/unlockDialog.js:95
#: ../js/ui/userMenu.js:884
msgid "Cancel"
msgstr "Cancelar"
#: ../js/gdm/loginDialog.js:768
#: ../js/gdm/loginDialog.js:663
msgctxt "button"
msgid "Sign In"
msgstr "Iniciar sesión"
#: ../js/gdm/loginDialog.js:768
#: ../js/gdm/loginDialog.js:665 ../js/gdm/util.js:561
msgid "Next"
msgstr "Siguiente"
#. Translators: this message is shown below the username entry field
#. to clue the user in on how to login to the local network realm
#: ../js/gdm/loginDialog.js:869
#: ../js/gdm/loginDialog.js:716
#, c-format
msgid "(e.g., user or %s)"
msgstr "(ej., usuario o %s)"
@ -413,12 +403,12 @@ msgstr "(ej., usuario o %s)"
#. 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)
#: ../js/gdm/loginDialog.js:873 ../js/ui/components/networkAgent.js:260
#: ../js/gdm/loginDialog.js:721 ../js/ui/components/networkAgent.js:260
#: ../js/ui/components/networkAgent.js:278
msgid "Username: "
msgstr "Nombre de usuario:"
#: ../js/gdm/loginDialog.js:1140
#: ../js/gdm/loginDialog.js:987
msgid "Login Window"
msgstr "Ventana de inicio de sesión"
@ -427,8 +417,7 @@ msgstr "Ventana de inicio de sesión"
msgid "Power"
msgstr "Energía"
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:651 ../js/ui/userMenu.js:655
#: ../js/ui/userMenu.js:768
#: ../js/gdm/powerMenu.js:93
msgid "Suspend"
msgstr "Suspender"
@ -436,21 +425,28 @@ msgstr "Suspender"
msgid "Restart"
msgstr "Reiniciar"
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:653
#: ../js/ui/userMenu.js:655 ../js/ui/userMenu.js:767 ../js/ui/userMenu.js:888
#: ../js/gdm/powerMenu.js:103 ../js/ui/status/system.js:215
#: ../js/ui/status/system.js:309
msgid "Power Off"
msgstr "Apagar"
#: ../js/gdm/util.js:248
#: ../js/gdm/util.js:255
msgid "Authentication error"
msgstr "Error de autenticación"
#. Translators: this message is shown below the password entry field
#. to indicate the user can swipe their finger instead
#: ../js/gdm/util.js:365
#: ../js/gdm/util.js:372
msgid "(or swipe finger)"
msgstr "(o pase el dedo)"
#: ../js/gdm/util.js:539 ../js/ui/components/networkAgent.js:137
#: ../js/ui/components/polkitAgent.js:161 ../js/ui/endSessionDialog.js:376
#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
#: ../js/ui/status/bluetooth.js:431 ../js/ui/status/system.js:305
msgid "Cancel"
msgstr "Cancelar"
#: ../js/misc/util.js:97
msgid "Command not found"
msgstr "Comando no encontrado"
@ -496,7 +492,7 @@ msgstr "Se ha añadido %s a sus favoritos."
msgid "%s has been removed from your favorites."
msgstr "Se ha quitado %s de sus favoritos."
#: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:744
#: ../js/ui/backgroundMenu.js:19 ../js/ui/status/system.js:207
msgid "Settings"
msgstr "Configuración"
@ -1160,7 +1156,6 @@ msgid "Download and install '%s' from extensions.gnome.org?"
msgstr "¿Descargar e instalar «%s» desde extensions.gnome.org?"
#: ../js/ui/keyboard.js:619 ../js/ui/status/keyboard.js:333
#: ../js/ui/status/power.js:211
msgid "Keyboard"
msgstr "Teclado"
@ -1212,31 +1207,31 @@ msgstr "Ver fuente"
msgid "Web Page"
msgstr "Página web"
#: ../js/ui/messageTray.js:1182
#: ../js/ui/messageTray.js:1241
msgid "Open"
msgstr "Abrir"
#: ../js/ui/messageTray.js:1189
#: ../js/ui/messageTray.js:1248
msgid "Remove"
msgstr "Quitar"
#: ../js/ui/messageTray.js:1501
#: ../js/ui/messageTray.js:1560
msgid "Clear Messages"
msgstr "Limpiar mensajes"
#: ../js/ui/messageTray.js:1528
#: ../js/ui/messageTray.js:1587
msgid "Notification Settings"
msgstr "Configuración de las notificaciones"
#: ../js/ui/messageTray.js:1711
#: ../js/ui/messageTray.js:1770
msgid "No Messages"
msgstr "No hay mensajes"
#: ../js/ui/messageTray.js:1784
#: ../js/ui/messageTray.js:1842
msgid "Message Tray"
msgstr "Bandeja de mensajes"
#: ../js/ui/messageTray.js:2811
#: ../js/ui/messageTray.js:2854
msgid "System Information"
msgstr "Información del sistema"
@ -1287,7 +1282,7 @@ msgstr "Barra superior"
#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
#. switches containing "◯" and "|"). Other values will
#. simply result in invisible toggle switches.
#: ../js/ui/popupMenu.js:549
#: ../js/ui/popupMenu.js:488
msgid "toggle-switch-us"
msgstr "toggle-switch-intl"
@ -1312,7 +1307,7 @@ msgid_plural "%d new notifications"
msgstr[0] "%d notificación nueva"
msgstr[1] "%d notificaciones nuevas"
#: ../js/ui/screenShield.js:449 ../js/ui/userMenu.js:759
#: ../js/ui/screenShield.js:449 ../js/ui/status/system.js:211
msgid "Lock"
msgstr "Bloquear"
@ -1327,11 +1322,11 @@ msgstr "GNOME necesita bloquear la pantalla"
#.
#. XXX: another option is to kick the user into the gdm login
#. screen, where we're not affected by grabs
#: ../js/ui/screenShield.js:773 ../js/ui/screenShield.js:1213
#: ../js/ui/screenShield.js:788 ../js/ui/screenShield.js:1216
msgid "Unable to lock"
msgstr "No se pudo bloquear"
#: ../js/ui/screenShield.js:774 ../js/ui/screenShield.js:1214
#: ../js/ui/screenShield.js:789 ../js/ui/screenShield.js:1217
msgid "Lock was blocked by an application"
msgstr "Una aplicación impidió el bloqueo"
@ -1367,7 +1362,7 @@ msgstr "Contraseña"
msgid "Remember Password"
msgstr "Recordar contraseña"
#: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:108
#: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:68
msgid "Unlock"
msgstr "Desbloquear"
@ -1407,22 +1402,18 @@ msgstr "Rechazo de teclas"
msgid "Mouse Keys"
msgstr "Teclas del ratón"
#: ../js/ui/status/accessibility.js:72
msgid "Universal Access Settings"
msgstr "Configuración del acceso universal"
#: ../js/ui/status/accessibility.js:129
#: ../js/ui/status/accessibility.js:126
msgid "High Contrast"
msgstr "Contraste alto"
#: ../js/ui/status/accessibility.js:178
#: ../js/ui/status/accessibility.js:175
msgid "Large Text"
msgstr "Texto grande"
#: ../js/ui/status/bluetooth.js:28 ../js/ui/status/bluetooth.js:32
#: ../js/ui/status/bluetooth.js:290 ../js/ui/status/bluetooth.js:327
#: ../js/ui/status/bluetooth.js:355 ../js/ui/status/bluetooth.js:391
#: ../js/ui/status/bluetooth.js:422 ../js/ui/status/network.js:713
#: ../js/ui/status/bluetooth.js:272 ../js/ui/status/bluetooth.js:309
#: ../js/ui/status/bluetooth.js:337 ../js/ui/status/bluetooth.js:373
#: ../js/ui/status/bluetooth.js:404 ../js/ui/status/network.js:713
msgid "Bluetooth"
msgstr "Bluetooth"
@ -1438,87 +1429,71 @@ msgstr "Enviar archivos al dispositivo…"
msgid "Set Up a New Device…"
msgstr "Configurar un dispositivo nuevo…"
#: ../js/ui/status/bluetooth.js:84
msgid "Bluetooth Settings"
msgstr "Configuración de Bluetooth"
#. TRANSLATORS: this means that bluetooth was disabled by hardware rfkill
#: ../js/ui/status/bluetooth.js:105 ../js/ui/status/network.js:140
#: ../js/ui/status/bluetooth.js:103 ../js/ui/status/network.js:140
msgid "hardware disabled"
msgstr "hardware desactivado"
#: ../js/ui/status/bluetooth.js:198
#: ../js/ui/status/bluetooth.js:196
msgid "Connection"
msgstr "Conexión"
#: ../js/ui/status/bluetooth.js:209 ../js/ui/status/network.js:399
#: ../js/ui/status/bluetooth.js:207 ../js/ui/status/network.js:399
msgid "disconnecting..."
msgstr "deconectando…"
#: ../js/ui/status/bluetooth.js:222 ../js/ui/status/network.js:405
#: ../js/ui/status/bluetooth.js:220 ../js/ui/status/network.js:405
#: ../js/ui/status/network.js:1298
msgid "connecting..."
msgstr "conectando…"
#: ../js/ui/status/bluetooth.js:240
#: ../js/ui/status/bluetooth.js:238
msgid "Send Files…"
msgstr "Enviar archivos…"
#: ../js/ui/status/bluetooth.js:247
msgid "Keyboard Settings"
msgstr "Configuración del teclado"
#: ../js/ui/status/bluetooth.js:250
msgid "Mouse Settings"
msgstr "Configuración del ratón…"
#: ../js/ui/status/bluetooth.js:255 ../js/ui/status/volume.js:313
msgid "Sound Settings"
msgstr "Configuración del sonido"
#: ../js/ui/status/bluetooth.js:328 ../js/ui/status/bluetooth.js:356
#: ../js/ui/status/bluetooth.js:310 ../js/ui/status/bluetooth.js:338
#, c-format
msgid "Authorization request from %s"
msgstr "Solicitud de autorización de %s"
#: ../js/ui/status/bluetooth.js:334 ../js/ui/status/bluetooth.js:399
#: ../js/ui/status/bluetooth.js:430
#: ../js/ui/status/bluetooth.js:316 ../js/ui/status/bluetooth.js:381
#: ../js/ui/status/bluetooth.js:412
#, c-format
msgid "Device %s wants to pair with this computer"
msgstr "El dispositivo «%s» quiere emparejarse con este equipo"
#: ../js/ui/status/bluetooth.js:336
#: ../js/ui/status/bluetooth.js:318
msgid "Allow"
msgstr "Permitir"
#: ../js/ui/status/bluetooth.js:337
#: ../js/ui/status/bluetooth.js:319
msgid "Deny"
msgstr "Denegar"
#: ../js/ui/status/bluetooth.js:362
#: ../js/ui/status/bluetooth.js:344
#, c-format
msgid "Device %s wants access to the service '%s'"
msgstr "El dispositivo %s quiere acceder al servicio «%s»"
#: ../js/ui/status/bluetooth.js:364
#: ../js/ui/status/bluetooth.js:346
msgid "Always grant access"
msgstr "Conceder acceso siempre"
#: ../js/ui/status/bluetooth.js:365
#: ../js/ui/status/bluetooth.js:347
msgid "Grant this time only"
msgstr "Conceder sólo esta vez"
#: ../js/ui/status/bluetooth.js:366
#: ../js/ui/status/bluetooth.js:348
msgid "Reject"
msgstr "Rechazar"
#. Translators: argument is the device short name
#: ../js/ui/status/bluetooth.js:393
#: ../js/ui/status/bluetooth.js:375
#, c-format
msgid "Pairing confirmation for %s"
msgstr "Confirmación de emparejamiento para «%s»"
#: ../js/ui/status/bluetooth.js:400
#: ../js/ui/status/bluetooth.js:382
#, c-format
msgid ""
"Please confirm whether the Passkey '%06d' matches the one on the device."
@ -1526,24 +1501,24 @@ msgstr ""
"Confirme que la clave mostrada en «%06d» coincide con la del dispositivo."
#. Translators: this is the verb, not the noun
#: ../js/ui/status/bluetooth.js:403
#: ../js/ui/status/bluetooth.js:385
msgid "Matches"
msgstr "Coincide"
#: ../js/ui/status/bluetooth.js:404
#: ../js/ui/status/bluetooth.js:386
msgid "Does not match"
msgstr "No coincide"
#: ../js/ui/status/bluetooth.js:423
#: ../js/ui/status/bluetooth.js:405
#, c-format
msgid "Pairing request for %s"
msgstr "Solicitud de emparejamiento para «%s»"
#: ../js/ui/status/bluetooth.js:431
#: ../js/ui/status/bluetooth.js:413
msgid "Please enter the PIN mentioned on the device."
msgstr "Introduzca el PIN mencionado en el dispositivo."
#: ../js/ui/status/bluetooth.js:448
#: ../js/ui/status/bluetooth.js:430
msgid "OK"
msgstr "Aceptar"
@ -1551,10 +1526,6 @@ msgstr "Aceptar"
msgid "Show Keyboard Layout"
msgstr "Mostrar la distribución del teclado"
#: ../js/ui/status/keyboard.js:401
msgid "Region & Language Settings"
msgstr "Configuración de región e idioma"
#: ../js/ui/status/lockScreenMenu.js:43
msgid "Volume, network, battery"
msgstr "Volumen, red, batería"
@ -1621,217 +1592,97 @@ msgstr "Banda ancha móvil"
msgid "Enable networking"
msgstr "Activar red"
#: ../js/ui/status/network.js:1522
msgid "Network Settings"
msgstr "Configuración de la red"
#: ../js/ui/status/network.js:1539
#: ../js/ui/status/network.js:1537
msgid "Network Manager"
msgstr "Gestor de la red"
#: ../js/ui/status/network.js:1623
#: ../js/ui/status/network.js:1621
msgid "Connection failed"
msgstr "Falló la conexión"
#: ../js/ui/status/network.js:1624
#: ../js/ui/status/network.js:1622
msgid "Activation of network connection failed"
msgstr "Falló la activación de la conexión de red"
#: ../js/ui/status/network.js:1937
#: ../js/ui/status/network.js:1936
msgid "Networking is disabled"
msgstr "La red está desactivada"
#: ../js/ui/status/power.js:55
#: ../js/ui/status/power.js:31 ../js/ui/status/power.js:44
msgid "Battery"
msgstr "Batería"
#: ../js/ui/status/power.js:81
#: ../js/ui/status/power.js:45
msgid "Power Settings"
msgstr "Configuración de energía"
#: ../js/ui/status/power.js:61
msgid "Fully Charged"
msgstr "Cargada completamente"
#. 0 is reported when UPower does not have enough data
#. to estimate battery life
#: ../js/ui/status/power.js:99
#. state is one of PENDING_CHARGING, PENDING_DISCHARGING
#: ../js/ui/status/power.js:67 ../js/ui/status/power.js:84
msgid "Estimating…"
msgstr "Estimando…"
#: ../js/ui/status/power.js:106
#. Translators: this is <hours>:<minutes> Remaining (<percentage>)
#: ../js/ui/status/power.js:75
#, c-format
msgid "%d hour remaining"
msgid_plural "%d hours remaining"
msgstr[0] "Queda %d hora"
msgstr[1] "Queda %d horas"
msgid "%d\\u2236%d Remaining (%d%%)"
msgstr "Quedan %d\\u2236%d (%d%%)"
#. TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining"
#: ../js/ui/status/power.js:109
#. Translators: this is <hours>:<minutes> Until Full (<percentage>)
#: ../js/ui/status/power.js:80
#, c-format
msgid "%d %s %d %s remaining"
msgstr "Quedan %d %s %d %s"
msgid "%d\\u2236%d Until Full (%d%%)"
msgstr "%d\\u2236%d para la carga completa (%d%%)"
#: ../js/ui/status/power.js:111
msgid "hour"
msgid_plural "hours"
msgstr[0] "hora"
msgstr[1] "horas"
#: ../js/ui/status/power.js:111
msgid "minute"
msgid_plural "minutes"
msgstr[0] "minuto"
msgstr[1] "minutos"
#: ../js/ui/status/power.js:114
#, c-format
msgid "%d minute remaining"
msgid_plural "%d minutes remaining"
msgstr[0] "Queda %d minuto"
msgstr[1] "Quedan %d minutos"
#: ../js/ui/status/power.js:117 ../js/ui/status/power.js:191
#, c-format
msgctxt "percent of battery remaining"
msgid "%d%%"
msgstr "%d%%"
#: ../js/ui/status/power.js:201
msgid "AC Adapter"
msgstr "Adaptador de corriente"
#: ../js/ui/status/power.js:203
msgid "Laptop Battery"
msgstr "Batería del portátil"
#: ../js/ui/status/power.js:205
msgid "UPS"
msgstr "SAI"
#: ../js/ui/status/power.js:207
msgid "Monitor"
msgstr "Monitor"
#: ../js/ui/status/power.js:209
msgid "Mouse"
msgstr "Ratón"
#: ../js/ui/status/power.js:213
msgid "PDA"
msgstr "PDA"
#: ../js/ui/status/power.js:215
msgid "Cell Phone"
msgstr "Teléfono móvil"
#: ../js/ui/status/power.js:217
msgid "Media Player"
msgstr "Reproductor multimedia"
#: ../js/ui/status/power.js:219
msgid "Tablet"
msgstr "Tableta"
#: ../js/ui/status/power.js:221
msgid "Computer"
msgstr "Equipo"
#: ../js/ui/status/power.js:223
msgctxt "device"
msgid "Unknown"
msgstr "Desconocido"
#: ../js/ui/status/volume.js:121
msgid "Volume changed"
msgstr "Volumen modificado"
#. Translators: This is the label for audio volume
#: ../js/ui/status/volume.js:246 ../js/ui/status/volume.js:294
msgid "Volume"
msgstr "Volumen"
#: ../js/ui/status/volume.js:255
msgid "Microphone"
msgstr "Micrófono"
#: ../js/ui/unlockDialog.js:119
msgid "Log in as another user"
msgstr "Iniciar sesión como otro usuario"
#: ../js/ui/unlockDialog.js:140
msgid "Unlock Window"
msgstr "Desbloquear ventana"
#: ../js/ui/userMenu.js:149
msgid "Available"
msgstr "Disponible"
#: ../js/ui/userMenu.js:152
msgid "Busy"
msgstr "Ocupado"
#: ../js/ui/userMenu.js:155
msgid "Invisible"
msgstr "Invisible"
#: ../js/ui/userMenu.js:158
msgid "Away"
msgstr "Ausente"
#: ../js/ui/userMenu.js:161
msgid "Idle"
msgstr "Inactivo"
#: ../js/ui/userMenu.js:164
msgid "Offline"
msgstr "Desconectado"
#: ../js/ui/userMenu.js:736
msgid "Notifications"
msgstr "Notificaciones"
#: ../js/ui/userMenu.js:749
#: ../js/ui/status/system.js:188
msgid "Switch User"
msgstr "Cambiar de usuario"
#: ../js/ui/userMenu.js:754
#: ../js/ui/status/system.js:193
msgid "Log Out"
msgstr "Cerrar la sesión"
#: ../js/ui/userMenu.js:774
msgid "Install Updates & Restart"
msgstr "Instalar actualizaciones y reiniciar"
#: ../js/ui/userMenu.js:792
msgid "Your chat status will be set to busy"
msgstr "Su estado del chat se establecerá a «ocupado»"
#: ../js/ui/userMenu.js:793
msgid ""
"Notifications are now disabled, including chat messages. Your online status "
"has been adjusted to let others know that you might not see their messages."
msgstr ""
"Las notificaciones están ahora desactivadas, incluyendo los mensajes de "
"chat. Su estado en línea se ha ajustado para que otros sepan que puede no "
"leer sus mensajes."
#: ../js/ui/userMenu.js:834
#: ../js/ui/status/system.js:255
msgid "Other users are logged in."
msgstr "Hay otros usuarios con la sesión iniciada"
#: ../js/ui/userMenu.js:839
#: ../js/ui/status/system.js:260
msgid "Shutting down might cause them to lose unsaved work."
msgstr "Apagar puede hacer que pierdan el trabajo que no hayan guardado."
#. Translators: Remote here refers to a remote session, like a ssh login
#: ../js/ui/userMenu.js:867
#: ../js/ui/status/system.js:288
#, c-format
msgid "%s (remote)"
msgstr "%s (remoto)"
#. Translators: Console here refers to a tty like a VT console
#: ../js/ui/userMenu.js:870
#: ../js/ui/status/system.js:291
#, c-format
msgid "%s (console)"
msgstr "%s (consola)"
#: ../js/ui/status/volume.js:126
msgid "Volume changed"
msgstr "Volumen modificado"
#: ../js/ui/status/volume.js:302
msgid "Volume"
msgstr "Volumen"
#: ../js/ui/unlockDialog.js:78
msgid "Log in as another user"
msgstr "Iniciar sesión como otro usuario"
#: ../js/ui/unlockDialog.js:97
msgid "Unlock Window"
msgstr "Desbloquear ventana"
#: ../js/ui/viewSelector.js:100
msgid "Applications"
msgstr "Aplicaciones"
@ -1920,6 +1771,127 @@ msgstr "La contraseña no puede estar vacía"
msgid "Authentication dialog was dismissed by the user"
msgstr "El usuario rechazó el diálogo de autenticación"
#~ msgid "Universal Access Settings"
#~ msgstr "Configuración del acceso universal"
#~ msgid "Bluetooth Settings"
#~ msgstr "Configuración de Bluetooth"
#~ msgid "Keyboard Settings"
#~ msgstr "Configuración del teclado"
#~ msgid "Mouse Settings"
#~ msgstr "Configuración del ratón…"
#~ msgid "Sound Settings"
#~ msgstr "Configuración del sonido"
#~ msgid "Region & Language Settings"
#~ msgstr "Configuración de región e idioma"
#~ msgid "Network Settings"
#~ msgstr "Configuración de la red"
#~ msgid "%d hour remaining"
#~ msgid_plural "%d hours remaining"
#~ msgstr[0] "Queda %d hora"
#~ msgstr[1] "Queda %d horas"
#~ msgid "%d %s %d %s remaining"
#~ msgstr "Quedan %d %s %d %s"
#~ msgid "hour"
#~ msgid_plural "hours"
#~ msgstr[0] "hora"
#~ msgstr[1] "horas"
#~ msgid "minute"
#~ msgid_plural "minutes"
#~ msgstr[0] "minuto"
#~ msgstr[1] "minutos"
#~ msgid "%d minute remaining"
#~ msgid_plural "%d minutes remaining"
#~ msgstr[0] "Queda %d minuto"
#~ msgstr[1] "Quedan %d minutos"
#~ msgctxt "percent of battery remaining"
#~ msgid "%d%%"
#~ msgstr "%d%%"
#~ msgid "AC Adapter"
#~ msgstr "Adaptador de corriente"
#~ msgid "Laptop Battery"
#~ msgstr "Batería del portátil"
#~ msgid "UPS"
#~ msgstr "SAI"
#~ msgid "Monitor"
#~ msgstr "Monitor"
#~ msgid "Mouse"
#~ msgstr "Ratón"
#~ msgid "PDA"
#~ msgstr "PDA"
#~ msgid "Cell Phone"
#~ msgstr "Teléfono móvil"
#~ msgid "Media Player"
#~ msgstr "Reproductor multimedia"
#~ msgid "Tablet"
#~ msgstr "Tableta"
#~ msgid "Computer"
#~ msgstr "Equipo"
#~ msgctxt "device"
#~ msgid "Unknown"
#~ msgstr "Desconocido"
#~ msgid "Microphone"
#~ msgstr "Micrófono"
#~ msgid "Available"
#~ msgstr "Disponible"
#~ msgid "Busy"
#~ msgstr "Ocupado"
#~ msgid "Invisible"
#~ msgstr "Invisible"
#~ msgid "Away"
#~ msgstr "Ausente"
#~ msgid "Idle"
#~ msgstr "Inactivo"
#~ msgid "Offline"
#~ msgstr "Desconectado"
#~ msgid "Notifications"
#~ msgstr "Notificaciones"
#~ msgid "Install Updates & Restart"
#~ msgstr "Instalar actualizaciones y reiniciar"
#~ msgid "Your chat status will be set to busy"
#~ msgstr "Su estado del chat se establecerá a «ocupado»"
#~ msgid ""
#~ "Notifications are now disabled, including chat messages. Your online "
#~ "status has been adjusted to let others know that you might not see their "
#~ "messages."
#~ msgstr ""
#~ "Las notificaciones están ahora desactivadas, incluyendo los mensajes de "
#~ "chat. Su estado en línea se ha ajustado para que otros sepan que puede no "
#~ "leer sus mensajes."
#~ msgid "cable unplugged"
#~ msgstr "cable desconectado"

616
po/id.po

File diff suppressed because it is too large Load Diff

643
po/it.po

File diff suppressed because it is too large Load Diff

599
po/ja.po

File diff suppressed because it is too large Load Diff

142
po/nb.po
View File

@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gnome-shell 3.9.x\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-06-17 12:28+0200\n"
"PO-Revision-Date: 2013-06-17 12:29+0200\n"
"POT-Creation-Date: 2013-07-18 15:29+0200\n"
"PO-Revision-Date: 2013-07-04 11:09+0200\n"
"Last-Translator: Kjartan Maraas <kmaraas@gnome.org>\n"
"Language-Team: Norwegian bokmål <i18n-nb@lister.ping.uio.no>\n"
"Language: \n"
@ -331,18 +331,22 @@ msgstr "Utvidelse"
msgid "Select an extension to configure using the combobox above."
msgstr "Velg en utvidelse som skal konfigureres med komboboksen over."
#: ../js/gdm/loginDialog.js:370
msgid "Session"
msgstr "Økt …"
#: ../js/gdm/loginDialog.js:308
msgid "Choose Session"
msgstr "Velg økt"
#: ../js/gdm/loginDialog.js:326
msgid "Session"
msgstr "Økt"
#. translators: this message is shown below the user list on the
#. login screen. It can be activated to reveal an entry for
#. manually entering the username.
#: ../js/gdm/loginDialog.js:600
#: ../js/gdm/loginDialog.js:533
msgid "Not listed?"
msgstr "Ikke listet?"
#: ../js/gdm/loginDialog.js:775 ../js/ui/components/networkAgent.js:137
#: ../js/gdm/loginDialog.js:810 ../js/ui/components/networkAgent.js:137
#: ../js/ui/components/polkitAgent.js:161 ../js/ui/endSessionDialog.js:376
#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
#: ../js/ui/status/bluetooth.js:449 ../js/ui/unlockDialog.js:95
@ -350,18 +354,18 @@ msgstr "Ikke listet?"
msgid "Cancel"
msgstr "Avbryt"
#: ../js/gdm/loginDialog.js:790
#: ../js/gdm/loginDialog.js:833
msgctxt "button"
msgid "Sign In"
msgstr "Logg inn"
#: ../js/gdm/loginDialog.js:790
#: ../js/gdm/loginDialog.js:833
msgid "Next"
msgstr "Neste"
#. Translators: this message is shown below the username entry field
#. to clue the user in on how to login to the local network realm
#: ../js/gdm/loginDialog.js:887
#: ../js/gdm/loginDialog.js:934
#, c-format
msgid "(e.g., user or %s)"
msgstr "(f.eks. bruker eller %s)"
@ -369,12 +373,12 @@ msgstr "(f.eks. bruker eller %s)"
#. 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)
#: ../js/gdm/loginDialog.js:891 ../js/ui/components/networkAgent.js:260
#: ../js/gdm/loginDialog.js:938 ../js/ui/components/networkAgent.js:260
#: ../js/ui/components/networkAgent.js:278
msgid "Username: "
msgstr "Brukernavn: "
#: ../js/gdm/loginDialog.js:1157
#: ../js/gdm/loginDialog.js:1205
msgid "Login Window"
msgstr "Innloggingsvindu"
@ -794,14 +798,14 @@ msgstr "<b>%d</b> <b>%B</b> <b>%Y</b>, <b>%H.%M</b> "
#. Translators: this is the other person changing their old IM name to their new
#. IM name.
#: ../js/ui/components/telepathyClient.js:985
#: ../js/ui/components/telepathyClient.js:986
#, 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/components/telepathyClient.js:1088
#: ../js/ui/components/telepathyClient.js:1089
#, c-format
msgid "Invitation to %s"
msgstr "Invitasjon til %s"
@ -809,38 +813,38 @@ 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/components/telepathyClient.js:1096
#: ../js/ui/components/telepathyClient.js:1097
#, c-format
msgid "%s is inviting you to join %s"
msgstr "%s inviterer deg til å bli med i %s"
#: ../js/ui/components/telepathyClient.js:1098
#: ../js/ui/components/telepathyClient.js:1137
#: ../js/ui/components/telepathyClient.js:1177
#: ../js/ui/components/telepathyClient.js:1240
#: ../js/ui/components/telepathyClient.js:1099
#: ../js/ui/components/telepathyClient.js:1138
#: ../js/ui/components/telepathyClient.js:1178
#: ../js/ui/components/telepathyClient.js:1241
msgid "Decline"
msgstr "Avslå"
#: ../js/ui/components/telepathyClient.js:1099
#: ../js/ui/components/telepathyClient.js:1178
#: ../js/ui/components/telepathyClient.js:1241
#: ../js/ui/components/telepathyClient.js:1100
#: ../js/ui/components/telepathyClient.js:1179
#: ../js/ui/components/telepathyClient.js:1242
msgid "Accept"
msgstr "Godta"
#. translators: argument is a contact name like Alice for example.
#: ../js/ui/components/telepathyClient.js:1129
#: ../js/ui/components/telepathyClient.js:1130
#, c-format
msgid "Video call from %s"
msgstr "Videosamtale fra %s"
#. translators: argument is a contact name like Alice for example.
#: ../js/ui/components/telepathyClient.js:1132
#: ../js/ui/components/telepathyClient.js:1133
#, c-format
msgid "Call from %s"
msgstr "Samtale fra %s"
#. translators: this is a button label (verb), not a noun
#: ../js/ui/components/telepathyClient.js:1139
#: ../js/ui/components/telepathyClient.js:1140
msgid "Answer"
msgstr "Svar"
@ -849,110 +853,110 @@ msgstr "Svar"
#. * file name. The string will be something
#. * like: "Alice is sending you test.ogg"
#.
#: ../js/ui/components/telepathyClient.js:1171
#: ../js/ui/components/telepathyClient.js:1172
#, c-format
msgid "%s is sending you %s"
msgstr "%s sender deg %s"
#. To translators: The parameter is the contact's alias
#: ../js/ui/components/telepathyClient.js:1206
#: ../js/ui/components/telepathyClient.js:1207
#, c-format
msgid "%s would like permission to see when you are online"
msgstr "%s vil ha rettigheter til å se når du er tilkoblet"
#: ../js/ui/components/telepathyClient.js:1298
#: ../js/ui/components/telepathyClient.js:1299
msgid "Network error"
msgstr "Nettverksfeil"
#: ../js/ui/components/telepathyClient.js:1300
#: ../js/ui/components/telepathyClient.js:1301
msgid "Authentication failed"
msgstr "Autentisering feilet"
#: ../js/ui/components/telepathyClient.js:1302
#: ../js/ui/components/telepathyClient.js:1303
msgid "Encryption error"
msgstr "Feil ved kryptering"
#: ../js/ui/components/telepathyClient.js:1304
#: ../js/ui/components/telepathyClient.js:1305
msgid "Certificate not provided"
msgstr "Sertifikat ikke oppgitt"
#: ../js/ui/components/telepathyClient.js:1306
#: ../js/ui/components/telepathyClient.js:1307
msgid "Certificate untrusted"
msgstr "Stoler ikke på sertifikatet"
#: ../js/ui/components/telepathyClient.js:1308
#: ../js/ui/components/telepathyClient.js:1309
msgid "Certificate expired"
msgstr "Sertifikatet er utløpt"
#: ../js/ui/components/telepathyClient.js:1310
#: ../js/ui/components/telepathyClient.js:1311
msgid "Certificate not activated"
msgstr "Sertifikatet er ikke aktivert"
#: ../js/ui/components/telepathyClient.js:1312
#: ../js/ui/components/telepathyClient.js:1313
msgid "Certificate hostname mismatch"
msgstr "Feil vertsnavn for sertifikat"
#: ../js/ui/components/telepathyClient.js:1314
#: ../js/ui/components/telepathyClient.js:1315
msgid "Certificate fingerprint mismatch"
msgstr "Feil fingeravtrykk for sertifikat"
#: ../js/ui/components/telepathyClient.js:1316
#: ../js/ui/components/telepathyClient.js:1317
msgid "Certificate self-signed"
msgstr "Sertifikatet er selvsignert"
#: ../js/ui/components/telepathyClient.js:1318
#: ../js/ui/components/telepathyClient.js:1319
msgid "Status is set to offline"
msgstr "Status er satt til frakoblet"
#: ../js/ui/components/telepathyClient.js:1320
#: ../js/ui/components/telepathyClient.js:1321
msgid "Encryption is not available"
msgstr "Kryptering er ikke tilgjengelig"
#: ../js/ui/components/telepathyClient.js:1322
#: ../js/ui/components/telepathyClient.js:1323
msgid "Certificate is invalid"
msgstr "Sertifikatet er ugyldig"
#: ../js/ui/components/telepathyClient.js:1324
#: ../js/ui/components/telepathyClient.js:1325
msgid "Connection has been refused"
msgstr "Tilkobling ble nektet"
#: ../js/ui/components/telepathyClient.js:1326
#: ../js/ui/components/telepathyClient.js:1327
msgid "Connection can't be established"
msgstr "Tilkobling kan ikke etableres"
#: ../js/ui/components/telepathyClient.js:1328
#: ../js/ui/components/telepathyClient.js:1329
msgid "Connection has been lost"
msgstr "Tilkobling tapt"
#: ../js/ui/components/telepathyClient.js:1330
#: ../js/ui/components/telepathyClient.js:1331
msgid "This account is already connected to the server"
msgstr "Denne kontoen er allerede koblet til tjeneren"
#: ../js/ui/components/telepathyClient.js:1332
#: ../js/ui/components/telepathyClient.js:1333
msgid ""
"Connection has been replaced by a new connection using the same resource"
msgstr ""
"Tilkoblingen har blitt erstattet av en ny tilkobling som bruker samme ressurs"
#: ../js/ui/components/telepathyClient.js:1334
#: ../js/ui/components/telepathyClient.js:1335
msgid "The account already exists on the server"
msgstr "Kontoen eksisterer allerede på tjeneren"
#: ../js/ui/components/telepathyClient.js:1336
#: ../js/ui/components/telepathyClient.js:1337
msgid "Server is currently too busy to handle the connection"
msgstr "Tjener er for opptatt til å håndtere tilkoblingen"
#: ../js/ui/components/telepathyClient.js:1338
#: ../js/ui/components/telepathyClient.js:1339
msgid "Certificate has been revoked"
msgstr "Sertifikatet er tilbaketrukket"
#: ../js/ui/components/telepathyClient.js:1340
#: ../js/ui/components/telepathyClient.js:1341
msgid ""
"Certificate uses an insecure cipher algorithm or is cryptographically weak"
msgstr ""
"Sertifikatet bruker en usikker sifferalgoritme eller er krytografisk svakt"
#: ../js/ui/components/telepathyClient.js:1342
#: ../js/ui/components/telepathyClient.js:1343
msgid ""
"The length of the server certificate, or the depth of the server certificate "
"chain, exceed the limits imposed by the cryptography library"
@ -960,22 +964,22 @@ msgstr ""
"Lengden eller dybden på tjenersertifikatet oversteg grensen som er satt i "
"kryptografibiblioteket"
#: ../js/ui/components/telepathyClient.js:1344
#: ../js/ui/components/telepathyClient.js:1345
msgid "Internal error"
msgstr "Intern feil"
#. translators: argument is the account name, like
#. * name@jabber.org for example.
#: ../js/ui/components/telepathyClient.js:1354
#: ../js/ui/components/telepathyClient.js:1355
#, c-format
msgid "Unable to connect to %s"
msgstr "Kan ikke koble til %s"
#: ../js/ui/components/telepathyClient.js:1359
#: ../js/ui/components/telepathyClient.js:1360
msgid "View account"
msgstr "Vis konto"
#: ../js/ui/components/telepathyClient.js:1398
#: ../js/ui/components/telepathyClient.js:1399
msgid "Unknown reason"
msgstr "Ukjent årsak"
@ -1008,7 +1012,7 @@ msgstr "Innstillinger for dato og klokkeslett"
#. 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:202
#: ../js/ui/dateMenu.js:201
msgid "%A %B %e, %Y"
msgstr "%a %e %B, %Y"
@ -1166,31 +1170,31 @@ msgstr "Vis kildekode"
msgid "Web Page"
msgstr "Nettside"
#: ../js/ui/messageTray.js:1182
#: ../js/ui/messageTray.js:1241
msgid "Open"
msgstr "Åpne"
#: ../js/ui/messageTray.js:1189
#: ../js/ui/messageTray.js:1248
msgid "Remove"
msgstr "Fjern"
#: ../js/ui/messageTray.js:1501
#: ../js/ui/messageTray.js:1560
msgid "Clear Messages"
msgstr "Tøm meldinger"
#: ../js/ui/messageTray.js:1528
#: ../js/ui/messageTray.js:1587
msgid "Notification Settings"
msgstr "Innstillinger for varsling"
#: ../js/ui/messageTray.js:1711
#: ../js/ui/messageTray.js:1770
msgid "No Messages"
msgstr "Ingen meldinger"
#: ../js/ui/messageTray.js:1784
#: ../js/ui/messageTray.js:1842
msgid "Message Tray"
msgstr "Meldingstrau"
#: ../js/ui/messageTray.js:2811
#: ../js/ui/messageTray.js:2854
msgid "System Information"
msgstr "Systeminformasjon"
@ -1222,17 +1226,17 @@ msgstr "Oversikt"
msgid "Type to search…"
msgstr "Skriv for å søke …"
#: ../js/ui/panel.js:642
#: ../js/ui/panel.js:567
msgid "Quit"
msgstr "Avslutt"
#. Translators: If there is no suitable word for "Activities"
#. in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:693
#: ../js/ui/panel.js:618
msgid "Activities"
msgstr "Aktiviteter"
#: ../js/ui/panel.js:989
#: ../js/ui/panel.js:914
msgid "Top Bar"
msgstr "Topp-panel"
@ -1241,7 +1245,7 @@ msgstr "Topp-panel"
#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
#. switches containing "◯" and "|"). Other values will
#. simply result in invisible toggle switches.
#: ../js/ui/popupMenu.js:545
#: ../js/ui/popupMenu.js:517
msgid "toggle-switch-us"
msgstr "toggle-switch-intl"
@ -1281,11 +1285,11 @@ msgstr "GNOME må låse skjermen"
#.
#. XXX: another option is to kick the user into the gdm login
#. screen, where we're not affected by grabs
#: ../js/ui/screenShield.js:773 ../js/ui/screenShield.js:1213
#: ../js/ui/screenShield.js:788 ../js/ui/screenShield.js:1216
msgid "Unable to lock"
msgstr "Kan ikke låse"
#: ../js/ui/screenShield.js:774 ../js/ui/screenShield.js:1214
#: ../js/ui/screenShield.js:789 ../js/ui/screenShield.js:1217
msgid "Lock was blocked by an application"
msgstr "Låsing ble stoppet av et program"
@ -1590,7 +1594,7 @@ msgstr "Tilkobling feilet"
msgid "Activation of network connection failed"
msgstr "Aktivering av nettverkstilkobling feilet"
#: ../js/ui/status/network.js:1937
#: ../js/ui/status/network.js:1938
msgid "Networking is disabled"
msgstr "Nettverk er slått av"

395
po/sk.po
View File

@ -10,8 +10,8 @@ msgstr ""
"Project-Id-Version: gnome-shell\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&keywords=I18N+L10N&component=general\n"
"POT-Creation-Date: 2013-05-24 21:21+0000\n"
"PO-Revision-Date: 2013-05-24 23:24+0100\n"
"POT-Creation-Date: 2013-07-13 07:31+0000\n"
"PO-Revision-Date: 2013-07-13 20:32+0200\n"
"Last-Translator: Dušan Kazik <prescott66@gmail.com>\n"
"Language-Team: Slovak <gnome-sk-list@gnome.org>\n"
"Language: sk\n"
@ -384,38 +384,43 @@ msgstr "Rozšírenie"
msgid "Select an extension to configure using the combobox above."
msgstr "Použitím ponuky vyberte rozšírenie na nastavenie"
#: ../js/gdm/loginDialog.js:371
msgid "Session…"
msgstr "Relácia…"
# button
#: ../js/gdm/loginDialog.js:308
msgid "Choose Session"
msgstr "Vybrať reláciu"
#: ../js/gdm/loginDialog.js:326
msgid "Session"
msgstr "Relácia"
# https://bugzilla.gnome.org/show_bug.cgi?id=659972
#. translators: this message is shown below the user list on the
#. login screen. It can be activated to reveal an entry for
#. manually entering the username.
#: ../js/gdm/loginDialog.js:601
#: ../js/gdm/loginDialog.js:533
msgid "Not listed?"
msgstr "Nie ste v zozname?"
#: ../js/gdm/loginDialog.js:776 ../js/ui/components/networkAgent.js:137
#: ../js/gdm/loginDialog.js:810 ../js/ui/components/networkAgent.js:137
#: ../js/ui/components/polkitAgent.js:161 ../js/ui/endSessionDialog.js:376
#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
#: ../js/ui/status/bluetooth.js:415 ../js/ui/unlockDialog.js:96
#: ../js/ui/userMenu.js:938
#: ../js/ui/status/bluetooth.js:449 ../js/ui/unlockDialog.js:95
#: ../js/ui/userMenu.js:884
msgid "Cancel"
msgstr "Zrušiť"
#: ../js/gdm/loginDialog.js:791
#: ../js/gdm/loginDialog.js:833
msgctxt "button"
msgid "Sign In"
msgstr "Prihlásiť sa"
#: ../js/gdm/loginDialog.js:791
#: ../js/gdm/loginDialog.js:833
msgid "Next"
msgstr "Ďalej"
#. Translators: this message is shown below the username entry field
#. to clue the user in on how to login to the local network realm
#: ../js/gdm/loginDialog.js:888
#: ../js/gdm/loginDialog.js:934
#, c-format
msgid "(e.g., user or %s)"
msgstr "(napr., používateľ alebo %s)"
@ -423,12 +428,12 @@ msgstr "(napr., používateľ alebo %s)"
#. 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)
#: ../js/gdm/loginDialog.js:892 ../js/ui/components/networkAgent.js:260
#: ../js/gdm/loginDialog.js:938 ../js/ui/components/networkAgent.js:260
#: ../js/ui/components/networkAgent.js:278
msgid "Username: "
msgstr "Používateľské meno: "
#: ../js/gdm/loginDialog.js:1158
#: ../js/gdm/loginDialog.js:1205
msgid "Login Window"
msgstr "Prihlasovacie okno"
@ -437,8 +442,8 @@ msgstr "Prihlasovacie okno"
msgid "Power"
msgstr "Napájanie"
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:696 ../js/ui/userMenu.js:700
#: ../js/ui/userMenu.js:816
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:651 ../js/ui/userMenu.js:655
#: ../js/ui/userMenu.js:768
msgid "Suspend"
msgstr "Uspať"
@ -446,18 +451,18 @@ msgstr "Uspať"
msgid "Restart"
msgstr "Reštartovať"
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:698
#: ../js/ui/userMenu.js:700 ../js/ui/userMenu.js:815 ../js/ui/userMenu.js:942
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:653
#: ../js/ui/userMenu.js:655 ../js/ui/userMenu.js:767 ../js/ui/userMenu.js:888
msgid "Power Off"
msgstr "Vypnúť"
#: ../js/gdm/util.js:247
#: ../js/gdm/util.js:248
msgid "Authentication error"
msgstr "Chyba pri overovaní totožnosti"
#. Translators: this message is shown below the password entry field
#. to indicate the user can swipe their finger instead
#: ../js/gdm/util.js:364
#: ../js/gdm/util.js:365
msgid "(or swipe finger)"
msgstr "(alebo prejdite prstom)"
@ -476,23 +481,23 @@ msgstr "Nepodarilo sa analyzovať príkaz:"
msgid "Execution of '%s' failed:"
msgstr "Spustenie „%s“ zlyhalo:"
#: ../js/ui/appDisplay.js:361
#: ../js/ui/appDisplay.js:397
msgid "Frequent"
msgstr "Často používané"
#: ../js/ui/appDisplay.js:368
#: ../js/ui/appDisplay.js:404
msgid "All"
msgstr "Všetky"
#: ../js/ui/appDisplay.js:960
#: ../js/ui/appDisplay.js:996
msgid "New Window"
msgstr "Nové okno"
#: ../js/ui/appDisplay.js:963 ../js/ui/dash.js:284
#: ../js/ui/appDisplay.js:999 ../js/ui/dash.js:284
msgid "Remove from Favorites"
msgstr "Odstrániť z obľúbených"
#: ../js/ui/appDisplay.js:964
#: ../js/ui/appDisplay.js:1000
msgid "Add to Favorites"
msgstr "Pridať do obľúbených"
@ -506,7 +511,7 @@ msgstr "Program %s bol pridaný medzi obľúbené."
msgid "%s has been removed from your favorites."
msgstr "Program %s bol odstránený z obľúbených."
#: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:789
#: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:744
msgid "Settings"
msgstr "Nastavenia"
@ -631,35 +636,35 @@ msgid "S"
msgstr "So"
#. Translators: Text to show if there are no events
#: ../js/ui/calendar.js:735
#: ../js/ui/calendar.js:750
msgid "Nothing Scheduled"
msgstr "Žiadne naplánované udalosti"
#. Translators: Shown on calendar heading when selected day occurs on current year
#: ../js/ui/calendar.js:751
#: ../js/ui/calendar.js:768
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:754
#: ../js/ui/calendar.js:771
msgctxt "calendar heading"
msgid "%A, %B %d, %Y"
msgstr "%A, %e. %B %Y"
#: ../js/ui/calendar.js:764
#: ../js/ui/calendar.js:782
msgid "Today"
msgstr "Dnes"
#: ../js/ui/calendar.js:768
#: ../js/ui/calendar.js:786
msgid "Tomorrow"
msgstr "Zajtra"
#: ../js/ui/calendar.js:779
#: ../js/ui/calendar.js:797
msgid "This week"
msgstr "Tento týždeň"
#: ../js/ui/calendar.js:787
#: ../js/ui/calendar.js:805
msgid "Next week"
msgstr "Ďalší týždeň"
@ -851,14 +856,14 @@ msgstr "<b>%e.</b> <b>%B</b> <b>%Y</b> o <b>%H:%M</b>"
#. Translators: this is the other person changing their old IM name to their new
#. IM name.
#: ../js/ui/components/telepathyClient.js:985
#: ../js/ui/components/telepathyClient.js:986
#, c-format
msgid "%s is now known as %s"
msgstr "Kontakt %s odteraz vystupuje ako %s"
#. translators: argument is a room name like
#. * room@jabber.org for example.
#: ../js/ui/components/telepathyClient.js:1088
#: ../js/ui/components/telepathyClient.js:1089
#, c-format
msgid "Invitation to %s"
msgstr "Pozvánka do %s"
@ -866,38 +871,38 @@ msgstr "Pozvánka do %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/components/telepathyClient.js:1096
#: ../js/ui/components/telepathyClient.js:1097
#, c-format
msgid "%s is inviting you to join %s"
msgstr "Kontakt %s vás pozýva aby ste sa pridali do %s"
#: ../js/ui/components/telepathyClient.js:1098
#: ../js/ui/components/telepathyClient.js:1137
#: ../js/ui/components/telepathyClient.js:1177
#: ../js/ui/components/telepathyClient.js:1240
#: ../js/ui/components/telepathyClient.js:1099
#: ../js/ui/components/telepathyClient.js:1138
#: ../js/ui/components/telepathyClient.js:1178
#: ../js/ui/components/telepathyClient.js:1241
msgid "Decline"
msgstr "Odmietnuť"
#: ../js/ui/components/telepathyClient.js:1099
#: ../js/ui/components/telepathyClient.js:1178
#: ../js/ui/components/telepathyClient.js:1241
#: ../js/ui/components/telepathyClient.js:1100
#: ../js/ui/components/telepathyClient.js:1179
#: ../js/ui/components/telepathyClient.js:1242
msgid "Accept"
msgstr "Prijať"
#. translators: argument is a contact name like Alice for example.
#: ../js/ui/components/telepathyClient.js:1129
#: ../js/ui/components/telepathyClient.js:1130
#, c-format
msgid "Video call from %s"
msgstr "Videohovor od kontaktu %s"
#. translators: argument is a contact name like Alice for example.
#: ../js/ui/components/telepathyClient.js:1132
#: ../js/ui/components/telepathyClient.js:1133
#, c-format
msgid "Call from %s"
msgstr "Hovor od kontaktu %s"
#. translators: this is a button label (verb), not a noun
#: ../js/ui/components/telepathyClient.js:1139
#: ../js/ui/components/telepathyClient.js:1140
msgid "Answer"
msgstr "Prijať hovor"
@ -906,110 +911,110 @@ msgstr "Prijať hovor"
#. * file name. The string will be something
#. * like: "Alice is sending you test.ogg"
#.
#: ../js/ui/components/telepathyClient.js:1171
#: ../js/ui/components/telepathyClient.js:1172
#, c-format
msgid "%s is sending you %s"
msgstr "Kontakt %s vám posiela %s"
#. To translators: The parameter is the contact's alias
#: ../js/ui/components/telepathyClient.js:1206
#: ../js/ui/components/telepathyClient.js:1207
#, c-format
msgid "%s would like permission to see when you are online"
msgstr "Kontakt %s by chcel získať oprávnenie vidieť, kedy ste pripojený"
#: ../js/ui/components/telepathyClient.js:1298
#: ../js/ui/components/telepathyClient.js:1299
msgid "Network error"
msgstr "Chyba siete"
#: ../js/ui/components/telepathyClient.js:1300
#: ../js/ui/components/telepathyClient.js:1301
msgid "Authentication failed"
msgstr "Overenie totožnosti zlyhalo"
#: ../js/ui/components/telepathyClient.js:1302
#: ../js/ui/components/telepathyClient.js:1303
msgid "Encryption error"
msgstr "Chyba šifrovania"
#: ../js/ui/components/telepathyClient.js:1304
#: ../js/ui/components/telepathyClient.js:1305
msgid "Certificate not provided"
msgstr "Neposkytnutý certifikát"
#: ../js/ui/components/telepathyClient.js:1306
#: ../js/ui/components/telepathyClient.js:1307
msgid "Certificate untrusted"
msgstr "Nedôveryhodný certifikát"
#: ../js/ui/components/telepathyClient.js:1308
#: ../js/ui/components/telepathyClient.js:1309
msgid "Certificate expired"
msgstr "Certifikát s ukončenou platnosťou"
#: ../js/ui/components/telepathyClient.js:1310
#: ../js/ui/components/telepathyClient.js:1311
msgid "Certificate not activated"
msgstr "Neaktivovaný certifikát"
#: ../js/ui/components/telepathyClient.js:1312
#: ../js/ui/components/telepathyClient.js:1313
msgid "Certificate hostname mismatch"
msgstr "Certifikát s nesúhlasným názvom hostiteľa"
#: ../js/ui/components/telepathyClient.js:1314
#: ../js/ui/components/telepathyClient.js:1315
msgid "Certificate fingerprint mismatch"
msgstr "Certifikát s nesúhlasným odtlačkom"
#: ../js/ui/components/telepathyClient.js:1316
#: ../js/ui/components/telepathyClient.js:1317
msgid "Certificate self-signed"
msgstr "Sebou podpísaný certifikát"
#: ../js/ui/components/telepathyClient.js:1318
#: ../js/ui/components/telepathyClient.js:1319
msgid "Status is set to offline"
msgstr "Stav je nastavený na odhlásený"
#: ../js/ui/components/telepathyClient.js:1320
#: ../js/ui/components/telepathyClient.js:1321
msgid "Encryption is not available"
msgstr "Šifrovanie nie je dostupné"
#: ../js/ui/components/telepathyClient.js:1322
#: ../js/ui/components/telepathyClient.js:1323
msgid "Certificate is invalid"
msgstr "Certifikát je neplatný"
#: ../js/ui/components/telepathyClient.js:1324
#: ../js/ui/components/telepathyClient.js:1325
msgid "Connection has been refused"
msgstr "Pripojenie bolo odmietnuté"
#: ../js/ui/components/telepathyClient.js:1326
#: ../js/ui/components/telepathyClient.js:1327
msgid "Connection can't be established"
msgstr "Nedá sa nadviazať spojenie"
#: ../js/ui/components/telepathyClient.js:1328
#: ../js/ui/components/telepathyClient.js:1329
msgid "Connection has been lost"
msgstr "Spojenie sa stratilo"
#: ../js/ui/components/telepathyClient.js:1330
#: ../js/ui/components/telepathyClient.js:1331
msgid "This account is already connected to the server"
msgstr "Tento účet je už pripojený k serveru"
#: ../js/ui/components/telepathyClient.js:1332
#: ../js/ui/components/telepathyClient.js:1333
msgid ""
"Connection has been replaced by a new connection using the same resource"
msgstr "Pripojenie bolo nahradené novým, ktoré používa rovnaký zdroj"
#: ../js/ui/components/telepathyClient.js:1334
#: ../js/ui/components/telepathyClient.js:1335
msgid "The account already exists on the server"
msgstr "Účet na serveri už existuje"
#: ../js/ui/components/telepathyClient.js:1336
#: ../js/ui/components/telepathyClient.js:1337
msgid "Server is currently too busy to handle the connection"
msgstr "Server je momentálne príliš zaneprázdnený na zvládnutie pripojenia"
#: ../js/ui/components/telepathyClient.js:1338
#: ../js/ui/components/telepathyClient.js:1339
msgid "Certificate has been revoked"
msgstr "Certifikát bol zrušený"
#: ../js/ui/components/telepathyClient.js:1340
#: ../js/ui/components/telepathyClient.js:1341
msgid ""
"Certificate uses an insecure cipher algorithm or is cryptographically weak"
msgstr ""
"Šifrovací algoritmus používaný certifikátom nie je bezpečný alebo je "
"kryptograficky slabý"
#: ../js/ui/components/telepathyClient.js:1342
#: ../js/ui/components/telepathyClient.js:1343
msgid ""
"The length of the server certificate, or the depth of the server certificate "
"chain, exceed the limits imposed by the cryptography library"
@ -1017,22 +1022,22 @@ msgstr ""
"Dĺžka certifikátu servera, alebo hĺbka reťazca certifikátu servera presahuje "
"limit stanovený kryptografickou knižnicou."
#: ../js/ui/components/telepathyClient.js:1344
#: ../js/ui/components/telepathyClient.js:1345
msgid "Internal error"
msgstr "Vnútorná chyba"
#. translators: argument is the account name, like
#. * name@jabber.org for example.
#: ../js/ui/components/telepathyClient.js:1354
#: ../js/ui/components/telepathyClient.js:1355
#, c-format
msgid "Unable to connect to %s"
msgstr "Nepodarilo sa pripojiť účet %s"
#: ../js/ui/components/telepathyClient.js:1359
#: ../js/ui/components/telepathyClient.js:1360
msgid "View account"
msgstr "Zobraziť účet"
#: ../js/ui/components/telepathyClient.js:1398
#: ../js/ui/components/telepathyClient.js:1399
msgid "Unknown reason"
msgstr "Neznámy dôvod"
@ -1047,26 +1052,26 @@ msgstr "Zobrazí aplikácie"
#. Translators: this is the name of the dock/favorites area on
#. the left of the overview
#: ../js/ui/dash.js:435
#: ../js/ui/dash.js:439
msgid "Dash"
msgstr "Dok"
#: ../js/ui/dateMenu.js:86
#: ../js/ui/dateMenu.js:85
msgid "Open Calendar"
msgstr "Otvoriť kalendár"
#: ../js/ui/dateMenu.js:90
#: ../js/ui/dateMenu.js:89
msgid "Open Clocks"
msgstr "Otvoriť hodiny"
#: ../js/ui/dateMenu.js:97
#: ../js/ui/dateMenu.js:96
msgid "Date & Time Settings"
msgstr "Nastavenia dátumu a času"
#. 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:208
#: ../js/ui/dateMenu.js:201
msgid "%A %B %e, %Y"
msgstr "%A, %e. %B %Y"
@ -1175,7 +1180,7 @@ msgstr "Inštalovať"
msgid "Download and install '%s' from extensions.gnome.org?"
msgstr "Stiahnuť a nainštalovať „%s“ z extensions.gnome.org?"
#: ../js/ui/keyboard.js:619 ../js/ui/status/keyboard.js:314
#: ../js/ui/keyboard.js:619 ../js/ui/status/keyboard.js:333
#: ../js/ui/status/power.js:211
msgid "Keyboard"
msgstr "Klávesnica"
@ -1229,33 +1234,33 @@ msgstr "Zobraziť zdroj"
msgid "Web Page"
msgstr "Webová stránka"
#: ../js/ui/messageTray.js:1182
#: ../js/ui/messageTray.js:1241
msgid "Open"
msgstr "Otvoriť"
#: ../js/ui/messageTray.js:1189
#: ../js/ui/messageTray.js:1248
msgid "Remove"
msgstr "Odstrániť"
#: ../js/ui/messageTray.js:1501
#: ../js/ui/messageTray.js:1560
msgid "Clear Messages"
msgstr "Vymazať správy"
#: ../js/ui/messageTray.js:1528
#: ../js/ui/messageTray.js:1587
msgid "Notification Settings"
msgstr "Nastavenia oznámení"
#: ../js/ui/messageTray.js:1707
#: ../js/ui/messageTray.js:1770
msgid "No Messages"
msgstr "Žiadne správy"
# DK: zvazoval som pouzit "Panel správ"
# neviem co bude vhodnejsie ako preklad "tray"
#: ../js/ui/messageTray.js:1780
#: ../js/ui/messageTray.js:1842
msgid "Message Tray"
msgstr "Lišta správ"
#: ../js/ui/messageTray.js:2800
#: ../js/ui/messageTray.js:2854
msgid "System Information"
msgstr "Informácie o systéme"
@ -1264,7 +1269,7 @@ msgctxt "program"
msgid "Unknown"
msgstr "Neznámy"
#: ../js/ui/overviewControls.js:472 ../js/ui/screenShield.js:150
#: ../js/ui/overviewControls.js:474 ../js/ui/screenShield.js:150
#, c-format
msgid "%d new message"
msgid_plural "%d new messages"
@ -1289,17 +1294,17 @@ msgstr "Prehľad"
msgid "Type to search…"
msgstr "Zadajte text na vyhľadanie…"
#: ../js/ui/panel.js:642
#: ../js/ui/panel.js:567
msgid "Quit"
msgstr "Ukončiť"
#. Translators: If there is no suitable word for "Activities"
#. in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:693
#: ../js/ui/panel.js:618
msgid "Activities"
msgstr "Aktivity"
#: ../js/ui/panel.js:989
#: ../js/ui/panel.js:914
msgid "Top Bar"
msgstr "Horná lišta"
@ -1308,7 +1313,7 @@ msgstr "Horná lišta"
#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
#. switches containing "◯" and "|"). Other values will
#. simply result in invisible toggle switches.
#: ../js/ui/popupMenu.js:738
#: ../js/ui/popupMenu.js:549
msgid "toggle-switch-us"
msgstr "toggle-switch-intl"
@ -1337,7 +1342,7 @@ msgstr[0] "%d nových oznámení"
msgstr[1] "%d nové oznámenie"
msgstr[2] "%d nové oznámenia"
#: ../js/ui/screenShield.js:449 ../js/ui/userMenu.js:807
#: ../js/ui/screenShield.js:449 ../js/ui/userMenu.js:759
msgid "Lock"
msgstr "Uzamknúť"
@ -1352,19 +1357,19 @@ msgstr "Prostredie GNOME vyžaduje uzamknutie obrazovky"
#.
#. XXX: another option is to kick the user into the gdm login
#. screen, where we're not affected by grabs
#: ../js/ui/screenShield.js:773 ../js/ui/screenShield.js:1213
#: ../js/ui/screenShield.js:788 ../js/ui/screenShield.js:1215
msgid "Unable to lock"
msgstr "Nepodarilo sa uzamknúť obrazovku"
#: ../js/ui/screenShield.js:774 ../js/ui/screenShield.js:1214
#: ../js/ui/screenShield.js:789 ../js/ui/screenShield.js:1216
msgid "Lock was blocked by an application"
msgstr "Uzamknutie bolo zablokované aplikáciou"
#: ../js/ui/searchDisplay.js:453
#: ../js/ui/searchDisplay.js:445
msgid "Searching…"
msgstr "Hľadá sa…"
#: ../js/ui/searchDisplay.js:497
#: ../js/ui/searchDisplay.js:489
msgid "No results."
msgstr "Žiadne výsledky."
@ -1392,7 +1397,7 @@ msgstr "Heslo"
msgid "Remember Password"
msgstr "Zapamätať heslo"
#: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:109
#: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:108
msgid "Unlock"
msgstr "Odblokovať"
@ -1448,9 +1453,9 @@ msgid "Large Text"
msgstr "Veľký text"
#: ../js/ui/status/bluetooth.js:28 ../js/ui/status/bluetooth.js:32
#: ../js/ui/status/bluetooth.js:289 ../js/ui/status/bluetooth.js:321
#: ../js/ui/status/bluetooth.js:357 ../js/ui/status/bluetooth.js:388
#: ../js/ui/status/network.js:739
#: ../js/ui/status/bluetooth.js:290 ../js/ui/status/bluetooth.js:327
#: ../js/ui/status/bluetooth.js:355 ../js/ui/status/bluetooth.js:391
#: ../js/ui/status/bluetooth.js:422 ../js/ui/status/network.js:713
msgid "Bluetooth"
msgstr "Bluetooth"
@ -1471,106 +1476,117 @@ msgid "Bluetooth Settings"
msgstr "Nastavenia Bluetooth"
#. TRANSLATORS: this means that bluetooth was disabled by hardware rfkill
#: ../js/ui/status/bluetooth.js:104 ../js/ui/status/network.js:142
#: ../js/ui/status/bluetooth.js:105 ../js/ui/status/network.js:140
msgid "hardware disabled"
msgstr "hardvér zakázaný"
#: ../js/ui/status/bluetooth.js:197
#: ../js/ui/status/bluetooth.js:198
msgid "Connection"
msgstr "Pripojenie"
#: ../js/ui/status/bluetooth.js:208 ../js/ui/status/network.js:404
#: ../js/ui/status/bluetooth.js:209 ../js/ui/status/network.js:399
msgid "disconnecting..."
msgstr "odpája sa…"
#: ../js/ui/status/bluetooth.js:221 ../js/ui/status/network.js:410
#: ../js/ui/status/network.js:1343
#: ../js/ui/status/bluetooth.js:222 ../js/ui/status/network.js:405
#: ../js/ui/status/network.js:1298
msgid "connecting..."
msgstr "pripája sa…"
# menu item
#: ../js/ui/status/bluetooth.js:239
#: ../js/ui/status/bluetooth.js:240
msgid "Send Files…"
msgstr "Odoslať súbory…"
#: ../js/ui/status/bluetooth.js:246
#: ../js/ui/status/bluetooth.js:247
msgid "Keyboard Settings"
msgstr "Nastavenia klávesnice"
#: ../js/ui/status/bluetooth.js:249
#: ../js/ui/status/bluetooth.js:250
msgid "Mouse Settings"
msgstr "Nastavenia myši"
#: ../js/ui/status/bluetooth.js:254 ../js/ui/status/volume.js:316
#: ../js/ui/status/bluetooth.js:255 ../js/ui/status/volume.js:313
msgid "Sound Settings"
msgstr "Nastavenia zvuku"
#: ../js/ui/status/bluetooth.js:322
#: ../js/ui/status/bluetooth.js:328 ../js/ui/status/bluetooth.js:356
#, c-format
msgid "Authorization request from %s"
msgstr "Žiadosť o potvrdenie prístupu od %s"
#: ../js/ui/status/bluetooth.js:328
#, c-format
msgid "Device %s wants access to the service '%s'"
msgstr "Zariadenie %s chce pristupovať k službe „%s“"
#: ../js/ui/status/bluetooth.js:330
msgid "Always grant access"
msgstr "Vždy povoliť prístup"
#: ../js/ui/status/bluetooth.js:331
msgid "Grant this time only"
msgstr "Povoliť iba teraz"
#: ../js/ui/status/bluetooth.js:332
msgid "Reject"
msgstr "Odmietnuť"
#. Translators: argument is the device short name
#: ../js/ui/status/bluetooth.js:359
#, c-format
msgid "Pairing confirmation for %s"
msgstr "Potvrdenie spárovania pre %s"
#: ../js/ui/status/bluetooth.js:365 ../js/ui/status/bluetooth.js:396
#: ../js/ui/status/bluetooth.js:334 ../js/ui/status/bluetooth.js:399
#: ../js/ui/status/bluetooth.js:430
#, c-format
msgid "Device %s wants to pair with this computer"
msgstr "Zariadenie %s sa chce spárovať s týmto počítačom"
# button
#: ../js/ui/status/bluetooth.js:336
msgid "Allow"
msgstr "Povoliť"
# button
#: ../js/ui/status/bluetooth.js:337
msgid "Deny"
msgstr "Zakázať"
#: ../js/ui/status/bluetooth.js:362
#, c-format
msgid "Device %s wants access to the service '%s'"
msgstr "Zariadenie %s chce pristupovať k službe „%s“"
#: ../js/ui/status/bluetooth.js:364
msgid "Always grant access"
msgstr "Vždy povoliť prístup"
#: ../js/ui/status/bluetooth.js:365
msgid "Grant this time only"
msgstr "Povoliť iba teraz"
#: ../js/ui/status/bluetooth.js:366
msgid "Reject"
msgstr "Odmietnuť"
#. Translators: argument is the device short name
#: ../js/ui/status/bluetooth.js:393
#, c-format
msgid "Pairing confirmation for %s"
msgstr "Potvrdenie spárovania pre %s"
#: ../js/ui/status/bluetooth.js:400
#, c-format
msgid ""
"Please confirm whether the Passkey '%06d' matches the one on the device."
msgstr "Prosím, potvrďte, či sa heslo „%06d“ zhoduje s tým na zariadení."
#. Translators: this is the verb, not the noun
#: ../js/ui/status/bluetooth.js:369
#: ../js/ui/status/bluetooth.js:403
msgid "Matches"
msgstr "Zhoduje sa"
#: ../js/ui/status/bluetooth.js:370
#: ../js/ui/status/bluetooth.js:404
msgid "Does not match"
msgstr "Nezhoduje sa"
#: ../js/ui/status/bluetooth.js:389
#: ../js/ui/status/bluetooth.js:423
#, c-format
msgid "Pairing request for %s"
msgstr "Požiadavka na spárovanie pre %s"
#: ../js/ui/status/bluetooth.js:397
#: ../js/ui/status/bluetooth.js:431
msgid "Please enter the PIN mentioned on the device."
msgstr "Zadajte PIN, ktoré je uvedené na zariadení."
#: ../js/ui/status/bluetooth.js:414
#: ../js/ui/status/bluetooth.js:448
msgid "OK"
msgstr "Ok"
#: ../js/ui/status/keyboard.js:368
#: ../js/ui/status/keyboard.js:396
msgid "Show Keyboard Layout"
msgstr "Zobraziť rozloženie klávesnice"
#: ../js/ui/status/keyboard.js:373
#: ../js/ui/status/keyboard.js:401
msgid "Region & Language Settings"
msgstr "Miestne a jazykové nastavenia"
@ -1583,87 +1599,81 @@ msgstr "Hlasitosť, sieť, batéria"
msgid "<unknown>"
msgstr "<neznáme>"
#: ../js/ui/status/network.js:127
#: ../js/ui/status/network.js:125
msgid "Wi-Fi"
msgstr "Wi-Fi"
#. Translators: this indicates that wireless or wwan is disabled by hardware killswitch
#: ../js/ui/status/network.js:164
#: ../js/ui/status/network.js:162
msgid "disabled"
msgstr "zakázané"
#. 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:402
#: ../js/ui/status/network.js:397
msgid "unmanaged"
msgstr "nespravované"
#. Translators: this is for network connections that require some kind of key or password
#: ../js/ui/status/network.js:413 ../js/ui/status/network.js:1346
#: ../js/ui/status/network.js:408 ../js/ui/status/network.js:1301
msgid "authentication required"
msgstr "požaduje sa overenie totožnosti"
#. Translators: this is for devices that require some kind of firmware or kernel
#. module, which is missing
#: ../js/ui/status/network.js:423
#: ../js/ui/status/network.js:419
msgid "firmware missing"
msgstr "chýba firmvér"
#. Translators: this is for wired network devices that are physically disconnected
#: ../js/ui/status/network.js:430
msgid "cable unplugged"
msgstr "kábel odpojený"
#. 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:435
#: ../js/ui/status/network.js:423
msgid "unavailable"
msgstr "nedostupné"
#: ../js/ui/status/network.js:437 ../js/ui/status/network.js:1348
#: ../js/ui/status/network.js:425 ../js/ui/status/network.js:1303
msgid "connection failed"
msgstr "pripojenie zlyhalo"
#: ../js/ui/status/network.js:490 ../js/ui/status/network.js:1236
#: ../js/ui/status/network.js:1424
#: ../js/ui/status/network.js:478 ../js/ui/status/network.js:1190
msgid "More…"
msgstr "Viac…"
#. 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:518 ../js/ui/status/network.js:1191
#: ../js/ui/status/network.js:506 ../js/ui/status/network.js:1142
msgid "Connected (private)"
msgstr "Pripojené (súkromne)"
#: ../js/ui/status/network.js:597
#: ../js/ui/status/network.js:572
msgid "Wired"
msgstr "Drôtové pripojenie"
#: ../js/ui/status/network.js:611
#: ../js/ui/status/network.js:592
msgid "Mobile broadband"
msgstr "Širokopásmové pripojenie"
#: ../js/ui/status/network.js:1522
#: ../js/ui/status/network.js:1474
msgid "Enable networking"
msgstr "Povoliť sieť"
#: ../js/ui/status/network.js:1583
#: ../js/ui/status/network.js:1522
msgid "Network Settings"
msgstr "Nastavenia siete"
#: ../js/ui/status/network.js:1600
#: ../js/ui/status/network.js:1539
msgid "Network Manager"
msgstr "Správca siete"
#: ../js/ui/status/network.js:1690
#: ../js/ui/status/network.js:1623
msgid "Connection failed"
msgstr "Pripojenie zlyhalo"
#: ../js/ui/status/network.js:1691
#: ../js/ui/status/network.js:1624
msgid "Activation of network connection failed"
msgstr "Aktivácia pripojenia k sieti zlyhala"
#: ../js/ui/status/network.js:2047
#: ../js/ui/status/network.js:1938
msgid "Networking is disabled"
msgstr "Sieť je zakázaná"
@ -1768,72 +1778,72 @@ msgctxt "device"
msgid "Unknown"
msgstr "Neznáme"
#: ../js/ui/status/volume.js:124
#: ../js/ui/status/volume.js:121
msgid "Volume changed"
msgstr "Hlasitosť bola zmenená"
#. Translators: This is the label for audio volume
#: ../js/ui/status/volume.js:249 ../js/ui/status/volume.js:297
#: ../js/ui/status/volume.js:246 ../js/ui/status/volume.js:294
msgid "Volume"
msgstr "Hlasitosť"
#: ../js/ui/status/volume.js:258
#: ../js/ui/status/volume.js:255
msgid "Microphone"
msgstr "Mikrofón"
#: ../js/ui/unlockDialog.js:120
#: ../js/ui/unlockDialog.js:119
msgid "Log in as another user"
msgstr "Prihlásiť ako iný používateľ"
#: ../js/ui/unlockDialog.js:141
#: ../js/ui/unlockDialog.js:140
msgid "Unlock Window"
msgstr "Odomykacie okno"
#: ../js/ui/userMenu.js:193
#: ../js/ui/userMenu.js:149
msgid "Available"
msgstr "Prítomný"
#: ../js/ui/userMenu.js:196
#: ../js/ui/userMenu.js:152
msgid "Busy"
msgstr "Zaneprázdnený"
#: ../js/ui/userMenu.js:199
#: ../js/ui/userMenu.js:155
msgid "Invisible"
msgstr "Neviditeľný"
#: ../js/ui/userMenu.js:202
#: ../js/ui/userMenu.js:158
msgid "Away"
msgstr "Neprítomný"
#: ../js/ui/userMenu.js:205
#: ../js/ui/userMenu.js:161
msgid "Idle"
msgstr "Nečinný"
#: ../js/ui/userMenu.js:208
#: ../js/ui/userMenu.js:164
msgid "Offline"
msgstr "Odhlásený"
#: ../js/ui/userMenu.js:781
#: ../js/ui/userMenu.js:736
msgid "Notifications"
msgstr "Upozornenia"
#: ../js/ui/userMenu.js:797
#: ../js/ui/userMenu.js:749
msgid "Switch User"
msgstr "Prepnúť používateľa"
#: ../js/ui/userMenu.js:802
#: ../js/ui/userMenu.js:754
msgid "Log Out"
msgstr "Odhlásiť sa"
#: ../js/ui/userMenu.js:822
#: ../js/ui/userMenu.js:774
msgid "Install Updates & Restart"
msgstr "Nainštalovať aktualizácie a reštartovať"
#: ../js/ui/userMenu.js:840
#: ../js/ui/userMenu.js:792
msgid "Your chat status will be set to busy"
msgstr "Váš stav bude nastavený na zaneprázdnený"
#: ../js/ui/userMenu.js:841
#: ../js/ui/userMenu.js:793
msgid ""
"Notifications are now disabled, including chat messages. Your online status "
"has been adjusted to let others know that you might not see their messages."
@ -1841,22 +1851,22 @@ msgstr ""
"Oznámenia, vrátane správ rozhovoru, sú teraz zakázané. Váš stav online bol "
"nastavený tak, aby ostatní vedeli, že nemusíte vidieť ich správy."
#: ../js/ui/userMenu.js:888
#: ../js/ui/userMenu.js:834
msgid "Other users are logged in."
msgstr "Sú prihlásení iní používatelia."
#: ../js/ui/userMenu.js:893
#: ../js/ui/userMenu.js:839
msgid "Shutting down might cause them to lose unsaved work."
msgstr "Vypnutie môže spôsobiť stratu neuloženej práce."
#. Translators: Remote here refers to a remote session, like a ssh login
#: ../js/ui/userMenu.js:921
#: ../js/ui/userMenu.js:867
#, c-format
msgid "%s (remote)"
msgstr "%s (vzdialená relácia)"
#. Translators: Console here refers to a tty like a VT console
#: ../js/ui/userMenu.js:924
#: ../js/ui/userMenu.js:870
#, c-format
msgid "%s (console)"
msgstr "%s (konzola)"
@ -1918,19 +1928,19 @@ msgstr[2] "%u vstupy"
msgid "System Sounds"
msgstr "Systémové zvuky"
#: ../src/main.c:372
#: ../src/main.c:353
msgid "Print version"
msgstr "Verzia pre tlač"
#: ../src/main.c:378
#: ../src/main.c:359
msgid "Mode used by GDM for login screen"
msgstr "Režim používaný GDM pre prihlasovaciu obrazovku"
#: ../src/main.c:384
#: ../src/main.c:365
msgid "Use a specific mode, e.g. \"gdm\" for login screen"
msgstr "Použitie zvláštneho režimu, napr. „gdm“ pre prihlasovaciu obrazovku"
#: ../src/main.c:390
#: ../src/main.c:371
msgid "List possible modes"
msgstr "Zoznam možných režimov"
@ -1952,6 +1962,9 @@ msgstr "Heslo nemôže byť prázdne"
msgid "Authentication dialog was dismissed by the user"
msgstr "Dialógové okno overenia totožnosti bolo zatvorené používateľom"
#~ msgid "cable unplugged"
#~ msgstr "kábel odpojený"
#~ msgid "Whether to collect stats about applications usage"
#~ msgstr "Či sa majú zhromažďovať štatistické údaje o používaní aplikácií"

749
po/tg.po

File diff suppressed because it is too large Load Diff

720
po/vi.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -214,6 +214,7 @@ shell_app_create_icon_texture (ShellApp *app,
typedef struct {
ShellApp *app;
int size;
ClutterTextDirection direction;
} CreateFadedIconData;
static CoglHandle
@ -231,7 +232,7 @@ shell_app_create_faded_icon_cpu (StTextureCache *cache,
guint8 n_channels;
gboolean have_alpha;
gint fade_start;
gint fade_range;
gint fade_end;
guint i, j;
guint pixbuf_byte_size;
guint8 *orig_pixels;
@ -283,14 +284,26 @@ shell_app_create_faded_icon_cpu (StTextureCache *cache,
pixels = g_malloc0 (rowstride * height);
memcpy (pixels, orig_pixels, pixbuf_byte_size);
fade_start = width / 2;
fade_range = width - fade_start;
for (i = fade_start; i < width; i++)
/* fade on the right side for LTR, left side for RTL */
if (data->direction == CLUTTER_TEXT_DIRECTION_LTR)
{
fade_start = width / 2;
fade_end = width;
}
else
{
fade_start = 0;
fade_end = width / 2;
}
for (i = fade_start; i < fade_end; i++)
{
for (j = 0; j < height; j++)
{
guchar *pixel = &pixels[j * rowstride + i * n_channels];
float fade = 1.0 - ((float) i - fade_start) / fade_range;
float fade = ((float) i - fade_start) / (fade_end - fade_start);
if (data->direction == CLUTTER_TEXT_DIRECTION_LTR)
fade = 1.0 - fade;
pixel[0] = 0.5 + pixel[0] * fade;
pixel[1] = 0.5 + pixel[1] * fade;
pixel[2] = 0.5 + pixel[2] * fade;
@ -316,13 +329,14 @@ shell_app_create_faded_icon_cpu (StTextureCache *cache,
* shell_app_get_faded_icon:
* @app: A #ShellApp
* @size: Size in pixels
* @direction: Whether to fade on the left or right
*
* Return an actor with a horizontally faded look.
*
* Return value: (transfer none): A floating #ClutterActor, or %NULL if no icon
*/
ClutterActor *
shell_app_get_faded_icon (ShellApp *app, int size)
shell_app_get_faded_icon (ShellApp *app, int size, ClutterTextDirection direction)
{
CoglHandle texture;
ClutterActor *result;
@ -338,9 +352,13 @@ shell_app_get_faded_icon (ShellApp *app, int size)
/* Use icon: prefix so that we get evicted from the cache on
* icon theme changes. */
cache_key = g_strdup_printf ("icon:%s,size=%d,faded", shell_app_get_id (app), size);
cache_key = g_strdup_printf ("icon:%s,size=%d,faded-%s",
shell_app_get_id (app),
size,
direction == CLUTTER_TEXT_DIRECTION_RTL ? "rtl" : "ltr");
data.app = app;
data.size = size;
data.direction = direction;
texture = st_texture_cache_load (st_texture_cache_get_default (),
cache_key,
ST_TEXTURE_CACHE_POLICY_FOREVER,

View File

@ -43,7 +43,7 @@ GMenuTreeEntry *shell_app_get_tree_entry (ShellApp *app);
GDesktopAppInfo *shell_app_get_app_info (ShellApp *app);
ClutterActor *shell_app_create_icon_texture (ShellApp *app, int size);
ClutterActor *shell_app_get_faded_icon (ShellApp *app, int size);
ClutterActor *shell_app_get_faded_icon (ShellApp *app, int size, ClutterTextDirection direction);
const char *shell_app_get_name (ShellApp *app);
const char *shell_app_get_description (ShellApp *app);
gboolean shell_app_is_window_backed (ShellApp *app);

View File

@ -70,7 +70,6 @@ struct _ShellGlobal {
GtkWindow *grab_notifier;
gboolean gtk_grab_active;
ShellStageInputMode input_mode;
XserverRegion input_region;
GjsContext *js_context;
@ -94,6 +93,8 @@ struct _ShellGlobal {
guint32 xdnd_timestamp;
gint64 last_gc_end_time;
gboolean has_modal;
};
enum {
@ -106,7 +107,6 @@ enum {
PROP_SCREEN_WIDTH,
PROP_SCREEN_HEIGHT,
PROP_STAGE,
PROP_STAGE_INPUT_MODE,
PROP_WINDOW_GROUP,
PROP_TOP_WINDOW_GROUP,
PROP_WINDOW_MANAGER,
@ -141,9 +141,6 @@ shell_global_set_property(GObject *object,
switch (prop_id)
{
case PROP_STAGE_INPUT_MODE:
shell_global_set_stage_input_mode (global, g_value_get_enum (value));
break;
case PROP_SESSION_MODE:
g_clear_pointer (&global->session_mode, g_free);
global->session_mode = g_ascii_strdown (g_value_get_string (value), -1);
@ -195,9 +192,6 @@ shell_global_get_property(GObject *object,
case PROP_STAGE:
g_value_set_object (value, global->stage);
break;
case PROP_STAGE_INPUT_MODE:
g_value_set_enum (value, global->input_mode);
break;
case PROP_WINDOW_GROUP:
g_value_set_object (value, meta_get_window_group_for_screen (global->meta_screen));
break;
@ -278,8 +272,6 @@ shell_global_init (ShellGlobal *global)
g_signal_connect (global->grab_notifier, "grab-notify", G_CALLBACK (grab_notify), global);
global->gtk_grab_active = FALSE;
global->input_mode = SHELL_STAGE_INPUT_MODE_NORMAL;
global->sound_context = ca_gtk_context_get ();
ca_context_change_props (global->sound_context,
CA_PROP_APPLICATION_NAME, "GNOME Shell",
@ -417,14 +409,6 @@ shell_global_class_init (ShellGlobalClass *klass)
"Stage holding the desktop scene graph",
CLUTTER_TYPE_ACTOR,
G_PARAM_READABLE));
g_object_class_install_property (gobject_class,
PROP_STAGE_INPUT_MODE,
g_param_spec_enum ("stage-input-mode",
"Stage input mode",
"The stage input mode",
SHELL_TYPE_STAGE_INPUT_MODE,
SHELL_STAGE_INPUT_MODE_NORMAL,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class,
PROP_WINDOW_GROUP,
g_param_spec_object ("window-group",
@ -529,6 +513,18 @@ shell_global_get (void)
return the_object;
}
static guint32
get_current_time_maybe_roundtrip (ShellGlobal *global)
{
guint32 time;
time = shell_global_get_current_time (global);
if (time != CurrentTime)
return time;
return meta_display_get_current_time_roundtrip (global->meta_display);
}
static void
focus_window_changed (MetaDisplay *display,
GParamSpec *param,
@ -536,58 +532,71 @@ focus_window_changed (MetaDisplay *display,
{
ShellGlobal *global = user_data;
if (global->input_mode == SHELL_STAGE_INPUT_MODE_FOCUSED &&
meta_display_get_focus_window (display) != NULL)
shell_global_set_stage_input_mode (global, SHELL_STAGE_INPUT_MODE_NORMAL);
if (global->has_modal)
return;
/* If the stage window became unfocused, drop the key focus
* on Clutter's side. */
if (!meta_stage_is_focused (global->meta_screen))
clutter_stage_set_key_focus (global->stage, NULL);
}
/**
* shell_global_set_stage_input_mode:
* @global: the #ShellGlobal
* @mode: the stage input mode
*
* Sets the input mode of the stage; when @mode is
* %SHELL_STAGE_INPUT_MODE_NORMAL, then the stage accepts clicks in
* the region defined by shell_global_set_stage_input_region() but
* passes through clicks outside that region. When it is
* %SHELL_STAGE_INPUT_MODE_FULLSCREEN, the stage absorbs all input.
*
* When the input mode is %SHELL_STAGE_INPUT_MODE_FOCUSED, the pointer
* is handled as with %SHELL_STAGE_INPUT_MODE_NORMAL, but additionally
* the stage window has the keyboard focus. If the stage loses the
* focus (eg, because the user clicked into a window) the input mode
* will revert to %SHELL_STAGE_INPUT_MODE_NORMAL.
*
* Note that whenever a mutter-internal Gtk widget has a pointer grab,
* the shell goes unresponsive and passes things to the underlying GTK+
* widget to ensure that the widget gets any clicks it is expecting.
*/
void
shell_global_set_stage_input_mode (ShellGlobal *global,
ShellStageInputMode mode)
static ClutterActor *
get_key_focused_actor (ShellGlobal *global)
{
MetaScreen *screen;
ClutterActor *actor;
g_return_if_fail (SHELL_IS_GLOBAL (global));
actor = clutter_stage_get_key_focus (global->stage);
screen = meta_plugin_get_screen (global->plugin);
/* If there's no explicit key focus, clutter_stage_get_key_focus()
* returns the stage. This is a terrible API. */
if (actor == CLUTTER_ACTOR (global->stage))
actor = NULL;
return actor;
}
static void
sync_stage_window_focus (ShellGlobal *global)
{
ClutterActor *actor;
if (global->has_modal)
return;
actor = get_key_focused_actor (global);
/* An actor got key focus and the stage needs to be focused. */
if (actor != NULL && !meta_stage_is_focused (global->meta_screen))
meta_focus_stage_window (global->meta_screen,
get_current_time_maybe_roundtrip (global));
/* An actor dropped key focus. Focus the default window. */
else if (actor == NULL && meta_stage_is_focused (global->meta_screen))
meta_screen_focus_default_window (global->meta_screen,
get_current_time_maybe_roundtrip (global));
}
static void
focus_actor_changed (ClutterStage *stage,
GParamSpec *param,
gpointer user_data)
{
ShellGlobal *global = user_data;
sync_stage_window_focus (global);
}
static void
sync_input_region (ShellGlobal *global)
{
MetaScreen *screen = global->meta_screen;
if (global->gtk_grab_active)
meta_empty_stage_input_region (screen);
else if (mode == SHELL_STAGE_INPUT_MODE_FULLSCREEN || !global->input_region)
else if (global->has_modal)
meta_set_stage_input_region (screen, None);
else
meta_set_stage_input_region (screen, global->input_region);
if (mode == SHELL_STAGE_INPUT_MODE_FOCUSED)
meta_focus_stage_window (global->meta_screen,
shell_global_get_current_time (global));
if (mode != global->input_mode)
{
global->input_mode = mode;
g_object_notify (G_OBJECT (global), "stage-input-mode");
}
}
/**
@ -682,8 +691,7 @@ shell_global_unset_cursor (ShellGlobal *global)
* describing the input region.
*
* Sets the area of the stage that is responsive to mouse clicks when
* the stage mode is %SHELL_STAGE_INPUT_MODE_NORMAL (but does not change the
* current stage mode).
* we don't have a modal or grab.
*/
void
shell_global_set_stage_input_region (ShellGlobal *global,
@ -713,10 +721,7 @@ shell_global_set_stage_input_region (ShellGlobal *global,
global->input_region = XFixesCreateRegion (global->xdisplay, rects, nrects);
g_free (rects);
/* set_stage_input_mode() will figure out whether or not we
* should actually change the input region right now.
*/
shell_global_set_stage_input_mode (global, global->input_mode);
sync_input_region (global);
}
/**
@ -956,6 +961,8 @@ _shell_global_set_plugin (ShellGlobal *global,
"End of stage page repaint",
"");
g_signal_connect (global->stage, "notify::key-focus",
G_CALLBACK (focus_actor_changed), global);
g_signal_connect (global->meta_display, "notify::focus-window",
G_CALLBACK (focus_window_changed), global);
@ -991,7 +998,14 @@ shell_global_begin_modal (ShellGlobal *global,
guint32 timestamp,
MetaModalOptions options)
{
return meta_plugin_begin_modal (global->plugin, global->stage_xwindow, None, options, timestamp);
/* Make it an error to call begin_modal while we already
* have a modal active. */
if (global->has_modal)
return FALSE;
global->has_modal = meta_plugin_begin_modal (global->plugin, global->stage_xwindow, None, options, timestamp);
sync_input_region (global);
return global->has_modal;
}
/**
@ -1004,7 +1018,25 @@ void
shell_global_end_modal (ShellGlobal *global,
guint32 timestamp)
{
ClutterActor *actor;
if (!global->has_modal)
return;
meta_plugin_end_modal (global->plugin, timestamp);
global->has_modal = FALSE;
/* If the stage window is unfocused, ensure that there's no
* actor focused on Clutter's side. */
if (!meta_stage_is_focused (global->meta_screen))
clutter_stage_set_key_focus (global->stage, NULL);
/* An actor dropped key focus. Focus the default window. */
else if (get_key_focused_actor (global) && meta_stage_is_focused (global->meta_screen))
meta_screen_focus_default_window (global->meta_screen,
get_current_time_maybe_roundtrip (global));
sync_input_region (global);
}
void
@ -1222,7 +1254,7 @@ grab_notify (GtkWidget *widget, gboolean was_grabbed, gpointer user_data)
global->gtk_grab_active = !was_grabbed;
/* Update for the new setting of gtk_grab_active */
shell_global_set_stage_input_mode (global, global->input_mode);
sync_input_region (global);
}
/**
@ -1314,10 +1346,6 @@ shell_global_sync_pointer (ShellGlobal *global)
event.type = CLUTTER_MOTION;
event.time = shell_global_get_current_time (global);
event.flags = 0;
/* This is wrong: we should be setting event.stage to NULL if the
* pointer is not inside the bounds of the stage given the current
* stage_input_mode. For our current purposes however, this works.
*/
event.stage = global->stage;
event.x = x;
event.y = y;

View File

@ -47,14 +47,6 @@ void shell_global_end_modal (ShellGlobal *global,
void shell_global_freeze_keyboard (ShellGlobal *global,
guint32 timestamp);
typedef enum {
SHELL_STAGE_INPUT_MODE_NORMAL,
SHELL_STAGE_INPUT_MODE_FOCUSED,
SHELL_STAGE_INPUT_MODE_FULLSCREEN
} ShellStageInputMode;
void shell_global_set_stage_input_mode (ShellGlobal *global,
ShellStageInputMode mode);
void shell_global_set_stage_input_region (ShellGlobal *global,
GSList *rectangles);

View File

@ -104,8 +104,8 @@ shell_slicer_paint_child (ShellSlicer *self)
return;
st_bin_get_alignment (ST_BIN (self), &x_align, &y_align);
_st_get_align_factors (x_align, y_align,
&x_align_factor, &y_align_factor);
st_get_align_factors (x_align, y_align,
&x_align_factor, &y_align_factor);
clutter_actor_get_allocation_box (CLUTTER_ACTOR (self), &self_box);
clutter_actor_get_allocation_box (child, &child_box);

View File

@ -108,8 +108,8 @@ st_bin_allocate (ClutterActor *self,
gdouble x_align_f, y_align_f;
st_theme_node_get_content_box (theme_node, box, &childbox);
_st_get_align_factors (priv->x_align, priv->y_align,
&x_align_f, &y_align_f);
st_get_align_factors (priv->x_align, priv->y_align,
&x_align_f, &y_align_f);
clutter_actor_allocate_align_fill (priv->child, &childbox,
x_align_f, y_align_f,
priv->x_fill, priv->y_fill,

View File

@ -285,7 +285,6 @@ get_content_preferred_width (StBoxLayout *self,
{
StBoxLayoutPrivate *priv = self->priv;
gint n_children = 0;
gint n_fixed = 0;
gfloat min_width, natural_width;
ClutterActor *child;
@ -304,12 +303,6 @@ get_content_preferred_width (StBoxLayout *self,
n_children++;
if (clutter_actor_get_fixed_position_set (child))
{
n_fixed++;
continue;
}
if (priv->is_vertical)
{
_st_actor_get_preferred_width (child, -1, FALSE,
@ -329,10 +322,10 @@ get_content_preferred_width (StBoxLayout *self,
}
}
if (!priv->is_vertical && (n_children - n_fixed) > 1)
if (!priv->is_vertical && n_children > 1)
{
min_width += priv->spacing * (n_children - n_fixed - 1);
natural_width += priv->spacing * (n_children - n_fixed - 1);
min_width += priv->spacing * (n_children - 1);
natural_width += priv->spacing * (n_children - 1);
}
if (min_width_p)
@ -367,7 +360,6 @@ get_content_preferred_height (StBoxLayout *self,
{
StBoxLayoutPrivate *priv = self->priv;
gint n_children = 0;
gint n_fixed = 0;
gfloat min_height, natural_height;
ClutterActor *child;
@ -386,12 +378,6 @@ get_content_preferred_height (StBoxLayout *self,
n_children++;
if (clutter_actor_get_fixed_position_set (child))
{
n_fixed++;
continue;
}
if (priv->is_vertical)
{
clutter_container_child_get ((ClutterContainer*) self, child,
@ -416,10 +402,10 @@ get_content_preferred_height (StBoxLayout *self,
}
}
if (priv->is_vertical && (n_children - n_fixed) > 1)
if (priv->is_vertical && n_children > 1)
{
min_height += priv->spacing * (n_children - n_fixed - 1);
natural_height += priv->spacing * (n_children - n_fixed - 1);
min_height += priv->spacing * (n_children - 1);
natural_height += priv->spacing * (n_children - 1);
}
if (min_height_p)
@ -511,12 +497,9 @@ compute_shrinks (StBoxLayout *self,
{
gfloat child_min, child_nat;
gboolean child_fill;
gboolean fixed;
fixed = clutter_actor_get_fixed_position_set (child);
shrinks[i].child_index = i;
if (CLUTTER_ACTOR_IS_VISIBLE (child) && !fixed)
if (CLUTTER_ACTOR_IS_VISIBLE (child))
{
if (priv->is_vertical)
{
@ -738,20 +721,13 @@ st_box_layout_allocate (ClutterActor *actor,
{
ClutterActorBox child_box;
gfloat child_min, child_nat, child_allocated;
gboolean xfill, yfill, expand, fixed;
gboolean xfill, yfill, expand;
StAlign xalign, yalign;
gdouble xalign_f, yalign_f;
if (!CLUTTER_ACTOR_IS_VISIBLE (child))
goto next_child;
fixed = clutter_actor_get_fixed_position_set (child);
if (fixed)
{
clutter_actor_allocate_preferred_size (child, flags);
goto next_child;
}
clutter_container_child_get ((ClutterContainer*) actor, child,
"x-fill", &xfill,
"y-fill", &yfill,
@ -760,7 +736,7 @@ st_box_layout_allocate (ClutterActor *actor,
"expand", &expand,
NULL);
_st_get_align_factors (xalign, yalign, &xalign_f, &yalign_f);
st_get_align_factors (xalign, yalign, &xalign_f, &yalign_f);
if (priv->is_vertical)
{

View File

@ -98,66 +98,6 @@ _st_actor_get_preferred_height (ClutterActor *actor,
clutter_actor_get_preferred_height (actor, for_width, min_height_p, natural_height_p);
}
/**
* _st_get_align_factors:
* @x_align: an #StAlign
* @y_align: an #StAlign
* @x_align_out: (out) (allow-none): @x_align as a #gdouble
* @y_align_out: (out) (allow-none): @y_align as a #gdouble
*
* Converts @x_align and @y_align to #gdouble values.
*/
void
_st_get_align_factors (StAlign x_align,
StAlign y_align,
gdouble *x_align_out,
gdouble *y_align_out)
{
if (x_align_out)
{
switch (x_align)
{
case ST_ALIGN_START:
*x_align_out = 0.0;
break;
case ST_ALIGN_MIDDLE:
*x_align_out = 0.5;
break;
case ST_ALIGN_END:
*x_align_out = 1.0;
break;
default:
g_warn_if_reached ();
break;
}
}
if (y_align_out)
{
switch (y_align)
{
case ST_ALIGN_START:
*y_align_out = 0.0;
break;
case ST_ALIGN_MIDDLE:
*y_align_out = 0.5;
break;
case ST_ALIGN_END:
*y_align_out = 1.0;
break;
default:
g_warn_if_reached ();
break;
}
}
}
/**
* _st_set_text_from_style:
* @text: Target #ClutterText

View File

@ -45,11 +45,6 @@ G_END_DECLS
ClutterActor *_st_widget_get_dnd_clone (StWidget *widget);
void _st_get_align_factors (StAlign x_align,
StAlign y_align,
gdouble *x_align_out,
gdouble *y_align_out);
void _st_actor_get_preferred_width (ClutterActor *actor,
gfloat for_height,
gboolean y_fill,

View File

@ -598,7 +598,7 @@ st_scroll_view_allocate (ClutterActor *actor,
*/
/* Vertical scrollbar */
if (CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll))
if (vscrollbar_visible)
{
if (clutter_actor_get_text_direction (actor) == CLUTTER_TEXT_DIRECTION_RTL)
{
@ -617,7 +617,7 @@ st_scroll_view_allocate (ClutterActor *actor,
}
/* Horizontal scrollbar */
if (CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll))
if (hscrollbar_visible)
{
if (clutter_actor_get_text_direction (actor) == CLUTTER_TEXT_DIRECTION_RTL)
{

View File

@ -257,8 +257,8 @@ st_table_homogeneous_allocate (ClutterActor *self,
row_span = meta->row_span;
col_span = meta->col_span;
_st_get_align_factors (meta->x_align, meta->y_align,
&x_align_f, &y_align_f);
st_get_align_factors (meta->x_align, meta->y_align,
&x_align_f, &y_align_f);
if (ltr)
{
@ -609,8 +609,8 @@ st_table_preferred_allocate (ClutterActor *self,
row_span = meta->row_span;
col_span = meta->col_span;
_st_get_align_factors (meta->x_align, meta->y_align,
&x_align_f, &y_align_f);
st_get_align_factors (meta->x_align, meta->y_align,
&x_align_f, &y_align_f);
/* initialise the width and height */
col_width = col_widths[col];

View File

@ -1268,8 +1268,7 @@ st_theme_node_prerender_background (StThemeNode *node,
return texture;
}
static void st_theme_node_paint_borders (StThemeNode *node,
StThemeNodePaintState *state,
static void st_theme_node_paint_borders (StThemeNodePaintState *state,
const ClutterActorBox *box,
guint8 paint_opacity);
@ -1370,9 +1369,11 @@ st_theme_node_load_background_image (StThemeNode *node)
return node->background_texture != COGL_INVALID_HANDLE;
}
static void st_theme_node_prerender_shadow (StThemeNodePaintState *state);
static void
st_theme_node_render_resources (StThemeNode *node,
StThemeNodePaintState *state,
st_theme_node_render_resources (StThemeNodePaintState *state,
StThemeNode *node,
float width,
float height)
{
@ -1390,6 +1391,7 @@ st_theme_node_render_resources (StThemeNode *node,
*/
st_theme_node_paint_state_free (state);
st_theme_node_paint_state_set_node (state, node);
state->alloc_width = width;
state->alloc_height = height;
@ -1474,40 +1476,82 @@ st_theme_node_render_resources (StThemeNode *node,
state->box_shadow_material = _st_create_shadow_material (box_shadow_spec,
state->prerendered_texture);
else if (node->background_color.alpha > 0 || has_border)
st_theme_node_prerender_shadow (state);
}
/* If we don't have cached textures yet, check whether we can cache
them. */
if (!node->cached_textures)
{
if (state->prerendered_material == COGL_INVALID_HANDLE &&
width >= node->box_shadow_min_width &&
height >= node->box_shadow_min_height)
{
CoglHandle buffer, offscreen;
int texture_width = ceil (width);
int texture_height = ceil (height);
buffer = cogl_texture_new_with_size (texture_width,
texture_height,
COGL_TEXTURE_NO_SLICING,
COGL_PIXEL_FORMAT_ANY);
offscreen = cogl_offscreen_new_to_texture (buffer);
if (offscreen != COGL_INVALID_HANDLE)
{
ClutterActorBox box = { 0, 0, width, height };
CoglColor clear_color;
cogl_push_framebuffer (offscreen);
cogl_ortho (0, width, height, 0, 0, 1.0);
cogl_color_set_from_4ub (&clear_color, 0, 0, 0, 0);
cogl_clear (&clear_color, COGL_BUFFER_BIT_COLOR);
st_theme_node_paint_borders (node, state, &box, 0xFF);
cogl_pop_framebuffer ();
cogl_handle_unref (offscreen);
state->box_shadow_material = _st_create_shadow_material (box_shadow_spec,
buffer);
}
cogl_handle_unref (buffer);
st_theme_node_paint_state_copy (&node->cached_state, state);
node->cached_textures = TRUE;
}
}
}
static void
st_theme_node_update_resources (StThemeNodePaintState *state,
StThemeNode *node,
float width,
float height)
{
gboolean had_prerendered_texture = FALSE;
gboolean had_box_shadow = FALSE;
StShadow *box_shadow_spec;
g_return_if_fail (width > 0 && height > 0);
/* Free handles we can't reuse */
if (state->prerendered_texture != COGL_INVALID_HANDLE)
{
cogl_handle_unref (state->prerendered_texture);
state->prerendered_texture = COGL_INVALID_HANDLE;
had_prerendered_texture = TRUE;
}
if (state->prerendered_material != COGL_INVALID_HANDLE)
{
cogl_handle_unref (state->prerendered_material);
state->prerendered_material = COGL_INVALID_HANDLE;
if (node->border_slices_texture == COGL_INVALID_HANDLE &&
state->box_shadow_material != COGL_INVALID_HANDLE)
{
cogl_handle_unref (state->box_shadow_material);
state->box_shadow_material = COGL_INVALID_HANDLE;
had_box_shadow = TRUE;
}
}
st_theme_node_paint_state_set_node (state, node);
state->alloc_width = width;
state->alloc_height = height;
box_shadow_spec = st_theme_node_get_box_shadow (node);
if (had_prerendered_texture)
{
state->prerendered_texture = st_theme_node_prerender_background (node, width, height);
state->prerendered_material = _st_create_texture_material (state->prerendered_texture);
}
else
{
int corner_id;
for (corner_id = 0; corner_id < 4; corner_id++)
if (state->corner_material[corner_id] == COGL_INVALID_HANDLE)
state->corner_material[corner_id] =
st_theme_node_lookup_corner (node, width, height, corner_id);
}
if (had_box_shadow)
state->box_shadow_material = _st_create_shadow_material (box_shadow_spec,
state->prerendered_texture);
}
static void
paint_material_with_opacity (CoglHandle material,
ClutterActorBox *box,
@ -1527,11 +1571,11 @@ paint_material_with_opacity (CoglHandle material,
}
static void
st_theme_node_paint_borders (StThemeNode *node,
StThemeNodePaintState *state,
st_theme_node_paint_borders (StThemeNodePaintState *state,
const ClutterActorBox *box,
guint8 paint_opacity)
{
StThemeNode *node = state->node;
float width, height;
int border_width[4];
guint border_radius[4];
@ -1792,6 +1836,353 @@ st_theme_node_paint_borders (StThemeNode *node,
}
}
static void
st_theme_node_paint_sliced_shadow (StThemeNodePaintState *state,
const ClutterActorBox *box,
guint8 paint_opacity)
{
StThemeNode *node = state->node;
guint border_radius[4];
CoglColor color;
StShadow *box_shadow_spec;
gfloat xoffset, yoffset;
gfloat width, height;
gfloat shadow_width, shadow_height;
gfloat xend, yend, top, bottom, left, right;
gfloat s_top, s_bottom, s_left, s_right;
gfloat shadow_blur_radius, x_spread_factor, y_spread_factor;
float rectangles[8 * 9];
gint idx;
if (paint_opacity == 0)
return;
st_theme_node_reduce_border_radius (node, box->x2 - box->x1, box->y2 - box->y1, border_radius);
box_shadow_spec = st_theme_node_get_box_shadow (node);
/* Compute input & output areas :
*
* yoffset ----------------------------
* | | | |
* | | | |
* | | | |
* top ----------------------------
* | | | |
* | | | |
* | | | |
* bottom ----------------------------
* | | | |
* | | | |
* | | | |
* yend ----------------------------
* xoffset left right xend
*
* s_top = top in offscreen's coordinates (0.0 - 1.0)
* s_bottom = bottom in offscreen's coordinates (0.0 - 1.0)
* s_left = left in offscreen's coordinates (0.0 - 1.0)
* s_right = right in offscreen's coordinates (0.0 - 1.0)
*/
if (box_shadow_spec->blur == 0)
shadow_blur_radius = 0;
else
shadow_blur_radius = (5 * (box_shadow_spec->blur / 2.0)) / 2;
shadow_width = state->box_shadow_width + 2 * shadow_blur_radius;
shadow_height = state->box_shadow_height + 2 * shadow_blur_radius;
/* Compute input regions parameters */
s_top = shadow_blur_radius + box_shadow_spec->blur +
MAX (node->border_radius[ST_CORNER_TOPLEFT],
node->border_radius[ST_CORNER_TOPRIGHT]);
s_bottom = shadow_blur_radius + box_shadow_spec->blur +
MAX (node->border_radius[ST_CORNER_BOTTOMLEFT],
node->border_radius[ST_CORNER_BOTTOMRIGHT]);
s_left = shadow_blur_radius + box_shadow_spec->blur +
MAX (node->border_radius[ST_CORNER_TOPLEFT],
node->border_radius[ST_CORNER_BOTTOMLEFT]);
s_right = shadow_blur_radius + box_shadow_spec->blur +
MAX (node->border_radius[ST_CORNER_TOPRIGHT],
node->border_radius[ST_CORNER_BOTTOMRIGHT]);
/* Compute output regions parameters */
xoffset = box->x1 + box_shadow_spec->xoffset - shadow_blur_radius - box_shadow_spec->spread;
yoffset = box->y1 + box_shadow_spec->yoffset - shadow_blur_radius - box_shadow_spec->spread;
width = box->x2 - box->x1 + 2 * shadow_blur_radius;
height = box->y2 - box->y1 + 2 * shadow_blur_radius;
x_spread_factor = (width + 2 * box_shadow_spec->spread) / width;
y_spread_factor = (height + 2 * box_shadow_spec->spread) / height;
width += 2 * box_shadow_spec->spread;
height += 2 * box_shadow_spec->spread;
xend = xoffset + width;
yend = yoffset + height;
top = s_top * y_spread_factor;
bottom = s_bottom * y_spread_factor;
left = s_left * x_spread_factor;
right = s_right * x_spread_factor;
bottom = height - bottom;
right = width - right;
/* Final adjustments */
s_top /= shadow_height;
s_bottom /= shadow_height;
s_left /= shadow_width;
s_right /= shadow_width;
s_bottom = 1.0 - s_bottom;
s_right = 1.0 - s_right;
top += yoffset;
bottom += yoffset;
left += xoffset;
right += xoffset;
/* Setup pipeline */
cogl_color_set_from_4ub (&color,
box_shadow_spec->color.red * paint_opacity / 255,
box_shadow_spec->color.green * paint_opacity / 255,
box_shadow_spec->color.blue * paint_opacity / 255,
box_shadow_spec->color.alpha * paint_opacity / 255);
cogl_color_premultiply (&color);
cogl_material_set_layer_combine_constant (state->box_shadow_material, 0, &color);
cogl_set_source (state->box_shadow_material);
idx = 0;
if (top > 0)
{
if (left > 0)
{
/* Top left corner */
rectangles[idx++] = xoffset;
rectangles[idx++] = yoffset;
rectangles[idx++] = left;
rectangles[idx++] = top;
rectangles[idx++] = 0;
rectangles[idx++] = 0;
rectangles[idx++] = s_left;
rectangles[idx++] = s_top;
}
/* Top middle */
rectangles[idx++] = left;
rectangles[idx++] = yoffset;
rectangles[idx++] = right;
rectangles[idx++] = top;
rectangles[idx++] = s_left;
rectangles[idx++] = 0;
rectangles[idx++] = s_right;
rectangles[idx++] = s_top;
if (right > 0)
{
/* Top right corner */
rectangles[idx++] = right;
rectangles[idx++] = yoffset;
rectangles[idx++] = xend;
rectangles[idx++] = top;
rectangles[idx++] = s_right;
rectangles[idx++] = 0;
rectangles[idx++] = 1;
rectangles[idx++] = s_top;
}
}
if (left > 0)
{
/* Left middle */
rectangles[idx++] = xoffset;
rectangles[idx++] = top;
rectangles[idx++] = left;
rectangles[idx++] = bottom;
rectangles[idx++] = 0;
rectangles[idx++] = s_top;
rectangles[idx++] = s_left;
rectangles[idx++] = s_bottom;
}
/* Center middle */
rectangles[idx++] = left;
rectangles[idx++] = top;
rectangles[idx++] = right;
rectangles[idx++] = bottom;
rectangles[idx++] = s_left;
rectangles[idx++] = s_top;
rectangles[idx++] = s_right;
rectangles[idx++] = s_bottom;
if (right > 0)
{
/* Right middle */
rectangles[idx++] = right;
rectangles[idx++] = top;
rectangles[idx++] = xend;
rectangles[idx++] = bottom;
rectangles[idx++] = s_right;
rectangles[idx++] = s_top;
rectangles[idx++] = 1;
rectangles[idx++] = s_bottom;
}
if (bottom > 0)
{
if (left > 0)
{
/* Bottom left corner */
rectangles[idx++] = xoffset;
rectangles[idx++] = bottom;
rectangles[idx++] = left;
rectangles[idx++] = yend;
rectangles[idx++] = 0;
rectangles[idx++] = s_bottom;
rectangles[idx++] = s_left;
rectangles[idx++] = 1;
}
/* Bottom middle */
rectangles[idx++] = left;
rectangles[idx++] = bottom;
rectangles[idx++] = right;
rectangles[idx++] = yend;
rectangles[idx++] = s_left;
rectangles[idx++] = s_bottom;
rectangles[idx++] = s_right;
rectangles[idx++] = 1;
if (right > 0)
{
/* Bottom right corner */
rectangles[idx++] = right;
rectangles[idx++] = bottom;
rectangles[idx++] = xend;
rectangles[idx++] = yend;
rectangles[idx++] = s_right;
rectangles[idx++] = s_bottom;
rectangles[idx++] = 1;
rectangles[idx++] = 1;
}
}
cogl_rectangles_with_texture_coords (rectangles, idx / 8);
#if 0
/* Visual feedback on shadow's 9-slice and orignal offscreen buffer,
for debug purposes */
cogl_rectangle (xend, yoffset, xend + shadow_width, yoffset + shadow_height);
cogl_set_source_color4ub (0xff, 0x0, 0x0, 0xff);
cogl_rectangle (xoffset, top, xend, top + 1);
cogl_rectangle (xoffset, bottom, xend, bottom + 1);
cogl_rectangle (left, yoffset, left + 1, yend);
cogl_rectangle (right, yoffset, right + 1, yend);
cogl_rectangle (xend, yoffset, xend + shadow_width, yoffset + 1);
cogl_rectangle (xend, yoffset + shadow_height, xend + shadow_width, yoffset + shadow_height + 1);
cogl_rectangle (xend, yoffset, xend + 1, yoffset + shadow_height);
cogl_rectangle (xend + shadow_width, yoffset, xend + shadow_width + 1, yoffset + shadow_height);
s_top *= shadow_height;
s_bottom *= shadow_height;
s_left *= shadow_width;
s_right *= shadow_width;
cogl_rectangle (xend, yoffset + s_top, xend + shadow_width, yoffset + s_top + 1);
cogl_rectangle (xend, yoffset + s_bottom, xend + shadow_width, yoffset + s_bottom + 1);
cogl_rectangle (xend + s_left, yoffset, xend + s_left + 1, yoffset + shadow_height);
cogl_rectangle (xend + s_right, yoffset, xend + s_right + 1, yoffset + shadow_height);
#endif
}
static void
st_theme_node_prerender_shadow (StThemeNodePaintState *state)
{
StThemeNode *node = state->node;
guint border_radius[4];
int max_borders[4];
int center_radius, corner_id;
CoglHandle buffer, offscreen;
/* Get infos from the node */
if (state->alloc_width < node->box_shadow_min_width ||
state->alloc_height < node->box_shadow_min_height)
st_theme_node_reduce_border_radius (node, state->alloc_width, state->alloc_height, border_radius);
else
for (corner_id = 0; corner_id < 4; corner_id++)
border_radius[corner_id] = node->border_radius[corner_id];
/* Compute maximum borders sizes */
max_borders[ST_SIDE_TOP] = MAX (node->border_radius[ST_CORNER_TOPLEFT],
node->border_radius[ST_CORNER_TOPRIGHT]);
max_borders[ST_SIDE_BOTTOM] = MAX (node->border_radius[ST_CORNER_BOTTOMLEFT],
node->border_radius[ST_CORNER_BOTTOMRIGHT]);
max_borders[ST_SIDE_LEFT] = MAX (node->border_radius[ST_CORNER_TOPLEFT],
node->border_radius[ST_CORNER_BOTTOMLEFT]);
max_borders[ST_SIDE_RIGHT] = MAX (node->border_radius[ST_CORNER_TOPRIGHT],
node->border_radius[ST_CORNER_BOTTOMRIGHT]);
center_radius = (node->box_shadow->blur > 0) ? (2 * node->box_shadow->blur + 1) : 1;
node->box_shadow_min_width = max_borders[ST_SIDE_LEFT] + max_borders[ST_SIDE_RIGHT] + center_radius;
node->box_shadow_min_height = max_borders[ST_SIDE_TOP] + max_borders[ST_SIDE_BOTTOM] + center_radius;
if (state->alloc_width < node->box_shadow_min_width ||
state->alloc_height < node->box_shadow_min_height)
{
state->box_shadow_width = state->alloc_width;
state->box_shadow_height = state->alloc_height;
}
else
{
state->box_shadow_width = node->box_shadow_min_width;
state->box_shadow_height = node->box_shadow_min_height;
}
/* Render offscreen */
buffer = cogl_texture_new_with_size (state->box_shadow_width,
state->box_shadow_height,
COGL_TEXTURE_NO_SLICING,
COGL_PIXEL_FORMAT_ANY);
offscreen = cogl_offscreen_new_to_texture (buffer);
if (offscreen != COGL_INVALID_HANDLE)
{
ClutterActorBox box = { 0, 0, state->box_shadow_width, state->box_shadow_height};
CoglColor clear_color;
cogl_push_framebuffer (offscreen);
cogl_ortho (0, state->box_shadow_width, state->box_shadow_height, 0, 0, 1.0);
cogl_color_set_from_4ub (&clear_color, 0, 0, 0, 0);
cogl_clear (&clear_color, COGL_BUFFER_BIT_COLOR);
st_theme_node_paint_borders (state, &box, 0xFF);
cogl_pop_framebuffer ();
cogl_handle_unref (offscreen);
state->box_shadow_material = _st_create_shadow_material (st_theme_node_get_box_shadow (node),
buffer);
}
cogl_handle_unref (buffer);
}
static void
st_theme_node_paint_sliced_border_image (StThemeNode *node,
float width,
@ -1945,6 +2336,43 @@ st_theme_node_paint_outline (StThemeNode *node,
cogl_rectangles (rects, 4);
}
static gboolean
st_theme_node_needs_new_box_shadow_for_size (StThemeNodePaintState *state,
StThemeNode *node,
float width,
float height)
{
if (!node->rendered_once)
return TRUE;
/* The allocation hasn't changed, no need to recompute a new
box-shadow. */
if (state->alloc_width == width &&
state->alloc_height == height)
return FALSE;
/* If there is no shadow, no need to recompute a new box-shadow. */
if (node->box_shadow_min_width == 0 ||
node->box_shadow_min_height == 0)
return FALSE;
/* If the new size is inferior to the box-shadow minimum size (we
already know the size has changed), we need to recompute the
box-shadow. */
if (width < node->box_shadow_min_width ||
height < node->box_shadow_min_height)
return TRUE;
/* Now checking whether the size of the node has crossed the minimum
box-shadow size boundary, from below to above the minimum size .
If that's the case, we need to recompute the box-shadow */
if (state->alloc_width < node->box_shadow_min_width ||
state->alloc_height < node->box_shadow_min_height)
return TRUE;
return FALSE;
}
void
st_theme_node_paint (StThemeNode *node,
StThemeNodePaintState *state,
@ -1964,11 +2392,28 @@ st_theme_node_paint (StThemeNode *node,
if (width <= 0 || height <= 0)
return;
if (state->alloc_width != width || state->alloc_height != height)
/* Check whether we need to recreate the textures of the paint
* state, either because :
* 1) the theme node associated to the paint state has changed
* 2) the allocation size change requires recreating textures
*/
if (state->node != node ||
st_theme_node_needs_new_box_shadow_for_size (state, node, width, height))
{
state->node = node;
st_theme_node_render_resources (node, state, width, height);
/* If we had the ability to cache textures on the node, then we
can just copy them over to the paint state and avoid all
rendering. We end up sharing textures a cross different
widgets. */
if (node->rendered_once && node->cached_textures &&
width >= node->box_shadow_min_width && height >= node->box_shadow_min_height)
st_theme_node_paint_state_copy (state, &node->cached_state);
else
st_theme_node_render_resources (state, node, width, height);
node->rendered_once = TRUE;
}
else if (state->alloc_width != width || state->alloc_height != height)
st_theme_node_update_resources (state, node, width, height);
/* Rough notes about the relationship of borders and backgrounds in CSS3;
* see http://www.w3.org/TR/css3-background/ for more accurate details.
@ -1997,10 +2442,18 @@ st_theme_node_paint (StThemeNode *node,
*/
if (state->box_shadow_material)
_st_paint_shadow_with_opacity (node->box_shadow,
state->box_shadow_material,
&allocation,
paint_opacity);
{
if (state->alloc_width < node->box_shadow_min_width ||
state->alloc_height < node->box_shadow_min_height)
_st_paint_shadow_with_opacity (node->box_shadow,
state->box_shadow_material,
&allocation,
paint_opacity);
else
st_theme_node_paint_sliced_shadow (state,
&allocation,
paint_opacity);
}
if (state->prerendered_material != COGL_INVALID_HANDLE ||
st_theme_node_load_border_image (node))
@ -2024,7 +2477,7 @@ st_theme_node_paint (StThemeNode *node,
}
else
{
st_theme_node_paint_borders (node, state, box, paint_opacity);
st_theme_node_paint_borders (state, box, paint_opacity);
}
st_theme_node_paint_outline (node, box, paint_opacity);
@ -2076,8 +2529,9 @@ st_theme_node_paint (StThemeNode *node,
}
}
void
st_theme_node_paint_state_free (StThemeNodePaintState *state)
static void
st_theme_node_paint_state_node_free_internal (StThemeNodePaintState *state,
gboolean unref_node)
{
int corner_id;
@ -2092,14 +2546,46 @@ st_theme_node_paint_state_free (StThemeNodePaintState *state)
if (state->corner_material[corner_id] != COGL_INVALID_HANDLE)
cogl_handle_unref (state->corner_material[corner_id]);
if (unref_node)
st_theme_node_paint_state_set_node (state, NULL);
st_theme_node_paint_state_init (state);
}
static void
st_theme_node_paint_state_node_freed (StThemeNodePaintState *state)
{
st_theme_node_paint_state_node_free_internal (state, FALSE);
}
void
st_theme_node_paint_state_set_node (StThemeNodePaintState *state, StThemeNode *node)
{
if (state->node)
g_object_weak_unref (G_OBJECT (state->node),
(GWeakNotify) st_theme_node_paint_state_node_freed,
state);
state->node = node;
if (state->node)
g_object_weak_ref (G_OBJECT (state->node),
(GWeakNotify) st_theme_node_paint_state_node_freed,
state);
}
void
st_theme_node_paint_state_free (StThemeNodePaintState *state)
{
st_theme_node_paint_state_node_free_internal (state, TRUE);
}
void
st_theme_node_paint_state_init (StThemeNodePaintState *state)
{
int corner_id;
state->alloc_width = 0;
state->alloc_height = 0;
state->node = NULL;
state->box_shadow_material = COGL_INVALID_HANDLE;
state->prerendered_texture = COGL_INVALID_HANDLE;
@ -2120,10 +2606,12 @@ st_theme_node_paint_state_copy (StThemeNodePaintState *state,
st_theme_node_paint_state_free (state);
state->node = other->node;
st_theme_node_paint_state_set_node (state, other->node);
state->alloc_width = other->alloc_width;
state->alloc_height = other->alloc_height;
state->box_shadow_width = other->box_shadow_width;
state->box_shadow_height = other->box_shadow_height;
if (other->box_shadow_material)
state->box_shadow_material = cogl_handle_ref (other->box_shadow_material);

View File

@ -99,12 +99,19 @@ struct _StThemeNode {
guint background_image_shadow_computed : 1;
guint text_shadow_computed : 1;
guint link_type : 2;
guint rendered_once : 1;
guint cached_textures : 1;
int box_shadow_min_width;
int box_shadow_min_height;
CoglHandle border_slices_texture;
CoglHandle border_slices_material;
CoglHandle background_texture;
CoglHandle background_material;
CoglHandle background_shadow_material;
StThemeNodePaintState cached_state;
};
struct _StThemeNodeClass {

View File

@ -148,9 +148,13 @@ st_theme_node_transition_update (StThemeNodeTransition *transition,
if (st_theme_node_equal (new_node, old_node))
{
{
StThemeNodePaintState tmp = priv->old_paint_state;
priv->old_paint_state = priv->new_paint_state;
priv->new_paint_state = tmp;
StThemeNodePaintState tmp;
st_theme_node_paint_state_init (&tmp);
st_theme_node_paint_state_copy (&tmp, &priv->old_paint_state);
st_theme_node_paint_state_copy (&priv->old_paint_state, &priv->new_paint_state);
st_theme_node_paint_state_copy (&priv->new_paint_state, &tmp);
st_theme_node_paint_state_free (&tmp);
}
if (clutter_timeline_get_elapsed_time (priv->timeline) > 0)

View File

@ -54,6 +54,8 @@ st_theme_node_init (StThemeNode *node)
node->background_shadow_material = COGL_INVALID_HANDLE;
node->border_slices_texture = COGL_INVALID_HANDLE;
node->border_slices_material = COGL_INVALID_HANDLE;
st_theme_node_paint_state_init (&node->cached_state);
}
static void
@ -101,6 +103,8 @@ st_theme_node_dispose (GObject *gobject)
g_signal_handlers_disconnect_by_func (node->theme,
on_custom_stylesheets_changed, node);
st_theme_node_paint_state_free (&node->cached_state);
g_clear_object (&node->theme);
G_OBJECT_CLASS (st_theme_node_parent_class)->dispose (gobject);

View File

@ -102,6 +102,9 @@ struct _StThemeNodePaintState {
float alloc_width;
float alloc_height;
float box_shadow_width;
float box_shadow_height;
CoglHandle box_shadow_material;
CoglHandle prerendered_texture;
CoglHandle prerendered_material;
@ -278,6 +281,8 @@ void st_theme_node_paint_state_free (StThemeNodePaintState *state);
void st_theme_node_paint_state_copy (StThemeNodePaintState *state,
StThemeNodePaintState *other);
void st_theme_node_paint_state_invalidate (StThemeNodePaintState *state);
void st_theme_node_paint_state_set_node (StThemeNodePaintState *state,
StThemeNode *node);
G_END_DECLS

View File

@ -67,6 +67,8 @@ struct _StWidgetPrivate
gboolean hover : 1;
gboolean can_focus : 1;
gulong texture_file_changed_id;
AtkObject *accessible;
AtkRole accessible_role;
AtkStateSet *local_state_set;
@ -362,9 +364,11 @@ st_widget_dispose (GObject *gobject)
priv->label_actor = NULL;
}
g_signal_handlers_disconnect_by_func (st_texture_cache_get_default (),
st_widget_texture_cache_changed,
actor);
if (priv->texture_file_changed_id != 0)
{
g_signal_handler_disconnect (st_texture_cache_get_default (), priv->texture_file_changed_id);
priv->texture_file_changed_id = 0;
}
g_clear_object (&priv->prev_first_child);
g_clear_object (&priv->prev_last_child);
@ -1552,8 +1556,8 @@ st_widget_init (StWidget *actor)
g_signal_connect (actor, "notify::first-child", G_CALLBACK (st_widget_first_child_notify), NULL);
g_signal_connect (actor, "notify::last-child", G_CALLBACK (st_widget_last_child_notify), NULL);
g_signal_connect (st_texture_cache_get_default (), "texture-file-changed",
G_CALLBACK (st_widget_texture_cache_changed), actor);
priv->texture_file_changed_id = g_signal_connect (st_texture_cache_get_default (), "texture-file-changed",
G_CALLBACK (st_widget_texture_cache_changed), actor);
for (i = 0; i < G_N_ELEMENTS (priv->paint_states); i++)
st_theme_node_paint_state_init (&priv->paint_states[i]);
@ -2881,3 +2885,63 @@ st_widget_get_focus_chain (StWidget *widget)
{
return ST_WIDGET_GET_CLASS (widget)->get_focus_chain (widget);
}
/**
* st_get_align_factors:
* @x_align: an #StAlign
* @y_align: an #StAlign
* @x_align_out: (out) (allow-none): @x_align as a #gdouble
* @y_align_out: (out) (allow-none): @y_align as a #gdouble
*
* Converts @x_align and @y_align to #gdouble values.
*/
void
st_get_align_factors (StAlign x_align,
StAlign y_align,
gdouble *x_align_out,
gdouble *y_align_out)
{
if (x_align_out)
{
switch (x_align)
{
case ST_ALIGN_START:
*x_align_out = 0.0;
break;
case ST_ALIGN_MIDDLE:
*x_align_out = 0.5;
break;
case ST_ALIGN_END:
*x_align_out = 1.0;
break;
default:
g_warn_if_reached ();
break;
}
}
if (y_align_out)
{
switch (y_align)
{
case ST_ALIGN_START:
*y_align_out = 0.0;
break;
case ST_ALIGN_MIDDLE:
*y_align_out = 0.5;
break;
case ST_ALIGN_END:
*y_align_out = 1.0;
break;
default:
g_warn_if_reached ();
break;
}
}
}

View File

@ -151,7 +151,6 @@ StThemeNode * st_widget_peek_theme_node (StWidget *widg
GList * st_widget_get_focus_chain (StWidget *widget);
void st_widget_paint_background (StWidget *widget);
/* debug methods */
char *st_describe_actor (ClutterActor *actor);
void st_set_slow_down_factor (gfloat factor);
@ -169,6 +168,12 @@ void st_widget_set_accessible_name (StWidget *widget,
const gchar *name);
const gchar * st_widget_get_accessible_name (StWidget *widget);
/* utility methods */
void st_get_align_factors (StAlign x_align,
StAlign y_align,
gdouble *x_align_out,
gdouble *y_align_out);
G_END_DECLS
#endif /* __ST_WIDGET_H__ */

View File

@ -8,6 +8,7 @@ TEST_JS = \
interactive/border-radius.js \
interactive/border-width.js \
interactive/box-layout.js \
interactive/box-shadow-animated.js \
interactive/box-shadows.js \
interactive/calendar.js \
interactive/css-fonts.js \

View File

@ -45,12 +45,6 @@ function test() {
style: 'border: 1px solid #aaaaaa; '
+ 'background: #cceeff' }));
b2.add(new St.Label({ x: 50,
y: 50,
text: "Fixed",
style: 'border: 1px solid #aaaaaa;'
+ 'background: #ffffcc' }));
////////////////////////////////////////////////////////////////////////////////
function createCollapsableBox(width) {

View File

@ -0,0 +1,84 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const St = imports.gi.St;
const Mainloop = imports.mainloop;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const UI = imports.testcommon.ui;
const DELAY = 2000;
function resize_animated(label) {
if (label.width == 100) {
label.save_easing_state();
label.set_easing_mode(Clutter.AnimationMode.EASE_OUT_QUAD);
label.set_easing_duration(DELAY - 50);
label.set_size(500, 500);
label.restore_easing_state();
} else {
label.save_easing_state();
label.set_easing_mode(Clutter.AnimationMode.EASE_OUT_QUAD);
label.set_easing_duration(DELAY - 50);
label.set_size(100, 100);
label.restore_easing_state();
}
}
function get_css_style(shadow_style)
{
return 'border: 20px solid black;' +
'border-radius: 20px;' +
'background-color: white; ' +
'padding: 5px;' + shadow_style;
}
function test() {
let stage = new Clutter.Stage({ width: 1000, height: 600 });
UI.init(stage);
let iter = 0;
let shadowStyles = [ 'box-shadow: 3px 50px 0px 4px rgba(0,0,0,0.5);',
'box-shadow: 3px 4px 10px 4px rgba(0,0,0,0.5);',
'box-shadow: 0px 50px 0px 0px rgba(0,0,0,0.5);',
'box-shadow: 100px 100px 20px 4px rgba(0,0,0,0.5);'];
let label1 = new St.Label({ style: get_css_style(shadowStyles[iter]),
text: shadowStyles[iter],
x: 20,
y: 20,
width: 100,
height: 100
});
stage.add_actor(label1);
let label2 = new St.Label({ style: get_css_style(shadowStyles[iter]),
text: shadowStyles[iter],
x: 500,
y: 20,
width: 100,
height: 100
});
stage.add_actor(label2);
resize_animated(label1);
resize_animated(label2);
Mainloop.timeout_add(DELAY, Lang.bind(this, function() {
log(label1 + label1.get_size());
resize_animated(label1);
resize_animated(label2);
return true;
}));
Mainloop.timeout_add(2 * DELAY, Lang.bind(this, function() {
iter += 1;
iter %= shadowStyles.length;
label1.set_style(get_css_style(shadowStyles[iter]));
label1.set_text(shadowStyles[iter]);
label2.set_style(get_css_style(shadowStyles[iter]));
label2.set_text(shadowStyles[iter]);
return true;
}));
UI.main(stage);
}
test();