Compare commits

...

1429 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
317b9a9c87 dnd: Make the draggable much faster
It turns out that picking a 3200x1200 scene on notebook chipsets
every time the mouse is moved isn't exactly the fastest thing. Defer
picking to an idle to ensure that it won't get in the way of keeping
up with mouse events.

https://bugzilla.gnome.org/show_bug.cgi?id=703443
2013-07-02 14:17:38 -04:00
67f10ea7eb network-agent: Remove prototype of unused function
This causes a debug SpiderMonkey build to fail when it throws an
exception for the missing symbol, but doesn't properly return FALSE
when executing the script.

https://bugzilla.gnome.org/show_bug.cgi?id=703442
2013-07-02 14:17:38 -04:00
793edd3a2b Make autorun notifications work
The code that checks for various conditions is confusing and
undercommented. It appears one of the recent refactorings
inadvertedly inverted the sense of the 'hidden mountpoint'
check, and caused autorun to not work for anything that does
not have a 'native root' - which is pretty much all volumes
implemented by gvfs.

https://bugzilla.gnome.org/show_bug.cgi?id=703418
2013-07-01 16:54:01 -04:00
5dabaf2fe9 Fix launching apps / search results in new workspaces by dragging
When I landed the new workspace tracker move, I didn't realize there
was public API being used by clients here. Add that back.

https://bugzilla.gnome.org/show_bug.cgi?id=698593
2013-07-01 13:22:05 -04:00
1d9b25f771 Update gvc 2013-07-01 13:20:02 -04:00
74cd20116a boxpointer: fix left/right arrow side calculation
Commit d6cace32 introduced a typo in the left/right arrow side
calculation code that causes in most scenarios (where the monitor
width is greater then the height) to not flip the box when it doesn't
fit inside the monitor.

https://bugzilla.gnome.org/show_bug.cgi?id=703403
2013-07-01 18:42:00 +02:00
867695eb4f workspace: Remove unused zooming property
We no longer support zooming.

https://bugzilla.gnome.org/show_bug.cgi?id=703304
2013-06-29 11:56:37 +02:00
9e3592ebf3 Updated Czech translation 2013-06-28 18:33:47 +02:00
f8ec14d625 Updated Galician translations 2013-06-28 01:35:26 +02:00
1f21e50663 Updated Spanish translation 2013-06-27 13:02:31 +02:00
7403545a48 screenShield: fix empty screen shield
If the drag action ends after something else has put the screen shield
into a different state we can end up in an inconsistent screen shield
state where the whole thing is empty.

https://bugzilla.gnome.org/show_bug.cgi?id=703126
2013-06-27 11:14:09 +01:00
1d95b317ba st-widget: Fix paint state leak
https://bugzilla.gnome.org/show_bug.cgi?id=703160
2013-06-27 09:26:05 +01:00
f7568b69d4 loginDialog: make spinner and session menu button share position
They never need to be shown at the same time, and the design has
the UI fade between them.

This commit implements that.

https://bugzilla.gnome.org/show_bug.cgi?id=702818
2013-06-26 15:20:10 -04:00
8d7649eaec loginDialog: Move the session list to a PopupMenu
There are some issues with the existing session menu. First, it looks
kinda bad. It seems like it's hanging around there, but it doesn't really know
what to do with itself.

Second, when it expands down it requires that the buttons below move
down with it. This kind of movement is awkward and looks a bit weird.

Third, its current position makes the "dialog" tall and unwieldy when
you add things like messages for fingerprint readers or authentication errors.

This commit moves the session list to a menu behind a button to address
the above problems.

Based on a patch by Jasper St. Pierre.

https://bugzilla.gnome.org/show_bug.cgi?id=702818
2013-06-26 14:42:31 -04:00
10b1c7e8a3 Updated Czech translation 2013-06-26 19:13:33 +02:00
b1c936164c overview: Really fix zoom animation
Commit 16fa186b63 attempted to fix the zoom animation problem
by throwing changes on the floor while the overview is animating. This has
the side effect that we might end up missing some positioning changes causes
windows to overlap the workspace thumbnails.

So revert those changes and fix it by simply by passing
WindowPositionFlags.ANIMATE during the overview animation.
This way the animation works as expected and we don't miss any position changes.

https://bugzilla.gnome.org/show_bug.cgi?id=703105
2013-06-26 18:06:54 +02:00
74ad6abfc2 loginDialog: don't show Not Listed? button before user list
Right now, there's a weird flicker at start up where the
Not Listed? button shows up before the user list, which looks
pretty bad if you're watching for it.

This commit fixes that problem by hiding the Not Listed button
initially and showing it at the appropriate time.

https://bugzilla.gnome.org/show_bug.cgi?id=703132
2013-06-26 10:53:39 -04:00
9786b2d096 layout: Keep the top_window group above newly added chrome
The top_window_group was introduced for popup windows that should
appear above system chrome, but as the group itself is just a child
of Main.uiGroup, chrome that is added after top_window_group will
still be stacked on top.
At least correct the stacking for actors added via addChrome().

https://bugzilla.gnome.org/show_bug.cgi?id=702338
2013-06-26 13:37:58 +02:00
ea02380c15 loginDialog: drop use of modal dialog
The login screen is no longer even remotely dialog-like, so
using ModalDialog is pretty weird. It also makes it difficult
to put the session list in the same place as the spinner.

This commit moves loginDialog away from using modal dialog.

https://bugzilla.gnome.org/show_bug.cgi?id=702818
2013-06-25 16:39:12 -04:00
048d5dc914 loginDialog: clean up import lines
There are a few unused imports, and some import lines
out of order.

This commit tries to clean it all up.

https://bugzilla.gnome.org/show_bug.cgi?id=702818
2013-06-25 16:13:00 -04:00
aa6b63373e ui: move AnimatedIcon out of panel.js
The class is generally useful, so it only makes sense in panel.js
for historical reasons. Because other parts of the code are
using it, though, problems are cropping up that require a
workaround like:

placeSpinner: function(...) {
    /* This is here because of recursive imports */
    const Panel = imports.ui.panel;
    Panel.AnimatedIcon(spinnerIcon, WORK_SPINNER_ICON_SIZE);
    ...
}

This commit moves AnimatedIcon to its own file so we can drop that
workaround.

https://bugzilla.gnome.org/show_bug.cgi?id=702818
2013-06-25 15:16:42 -04:00
0b219bf8cb theme-node: fix support for box-shadow: none
We currently don't parse "box-shadow: none", despite being valid CSS.
Fix it so that it uses the default shadow values.

https://bugzilla.gnome.org/show_bug.cgi?id=702782
2013-06-25 10:11:31 -07:00
109b29aeb5 slider: Add rounded ends to sliders
https://bugzilla.gnome.org/show_bug.cgi?id=702825
2013-06-25 09:49:44 -04:00
bc069b99ec osdWindow: Make sure to clear the hide timeout
When the osd window is hidden based on the timeout, it accidentally
left the timeout ID in place. When a subsequent switcher popup came
up, it thought the OSD window was scheduled to be hidden and tried
to re-hide the actor. This caused the tween to be run along with
an extra call to enable_unredirect_for_screen.
2013-06-24 17:23:43 -04:00
16fa186b63 overview: Fix zoom animation
When the allocation of the workspacesView changes during the animation we override
the tween with one that does not animate causing the overview zoom animation
not to happen.

Fix that by ignoring the alloactionChanged notification during the overview
animation.
2013-06-24 22:56:17 +02:00
e70c0d3e2d st: Be more forgiving when calling get_theme_node() on unstaged widgets
While it is obviously still an error to call get_theme_node() on a
widget that hasn't been added to the stage hierarchy yet, asserting
on it hasn't proven too successful in avoiding those errors - it's
likely the most frequent reason for crash reports. Just accept that
there'll always be code paths where we can hit this case and make
it non-fatal.

https://bugzilla.gnome.org/show_bug.cgi?id=610279
2013-06-24 21:53:42 +02:00
8d47afb195 slider: Use clutter_cairo_set_source_color
This cuts down on the denseness of the code.

https://bugzilla.gnome.org/show_bug.cgi?id=702825
2013-06-24 15:14:03 -04:00
a01469fb08 slider: Extend the trough to the allocation extents
This aligns the edge of the slider with the allocation of items
under it, which is what we want to happen in the new aggregate
menu.

https://bugzilla.gnome.org/show_bug.cgi?id=702825
2013-06-24 15:14:03 -04:00
929636ebd0 dateMenu: Update visibility syncing for new dateMenu layout
The code here before was trying to play hierarchy tricks to
figure out how to show / hide the events list, which broke
when we rearranged how the date menu was laid out. Simplify
the code here to not be so tricky, and update the CSS to
match the new designs.

https://bugzilla.gnome.org/show_bug.cgi?id=702849
2013-06-24 15:14:03 -04:00
681ef1efec telepathyClient: Increase the timestamp timeout to 3 minutes
The timestamp timeout specifies how long we should wait before
adding a timestamp to the notification. A timeout of one minute
ended up showing a lot of timestamps, so increase it to 3 minutes.

https://bugzilla.gnome.org/show_bug.cgi?id=687809
2013-06-24 13:54:06 +02:00
4f14f122bd telepathyClient: Align timestamps to the right again
Commit 3de0ebf changed timestamps to be center-aligned. New
design updates change this, so revert it.

http://bugzilla.gnome.org/show_bug.cgi?id=687809
2013-06-24 13:54:06 +02:00
67e9ed5d60 gdm: Align the "Not Listed?" label
https://bugzilla.gnome.org/show_bug.cgi?id=702307
2013-06-24 12:01:08 +01:00
5c25497e16 PopupSliderMenuItem: Add scroll method
This fixes fallout from 847cb5b972

https://bugzilla.gnome.org/show_bug.cgi?id=702849
2013-06-24 00:00:40 +02:00
626cbea9cf Updated Kazakh translation 2013-06-22 15:09:01 +06:00
aa7ed319e9 messageTray: Ensure notifications are focused after they are expanded
If we focus notifications before they're expanded, the body and action
area won't be visible, and the can_focus members like the text entry
will not be able to be focused.

Ensure that all of the all actors that would be in an expanded notification
are visible before we attempt to focus them.

https://bugzilla.gnome.org/show_bug.cgi?id=698778
2013-06-21 15:32:14 -04:00
580bd67278 Fix syntax error 2013-06-18 22:59:48 +02:00
9e44978aed osdWindow: Don't call enable_unredirection on startup
This is wrong because it is already enabled. So move this
to out of reset, which gets called from _init().
2013-06-18 22:47:15 +02:00
64b5ec0b11 Bump version to 3.9.3
Update NEWS.
2013-06-18 17:47:30 +02:00
48f9ea3d9e gdm: clear the messages queue when the user answers a prompt
the messages were being shown even when the user entered the
right information they were asked for.

https://bugzilla.gnome.org/show_bug.cgi?id=702458
2013-06-18 11:58:20 -03:00
798a0ca240 Background: don't require a URI scheme for picture-uri
Migration from old settings can result in a path instead of URI
there. This is technically invalid, but can easily recognize it
and avoid the crash.

Minor changes by Ray Strode

https://bugzilla.gnome.org/show_bug.cgi?id=702121
2013-06-18 10:55:52 -04:00
5ee6cbd4c8 Updated Spanish translation 2013-06-17 17:54:56 +02:00
3b219a6a9a Updated Norwegian bokmål translation 2013-06-17 12:29:15 +02:00
536ff6f561 Tajik translation updated 2013-06-16 23:32:30 +05:00
013b6aa44a network: don't fail if we can't read a connection associated with an active
It can happen in legitimate cases, such as a version incompatibility
between NetworkManager and libnm-glib or a restricted permission.

https://bugzilla.gnome.org/show_bug.cgi?id=701849
2013-06-14 20:38:54 +02:00
0e7d3a7558 network: Remove support for virtual networking
This isn't in the new design, and no good can come from this.
Just allow people to use the control center to configure virtual
networking.

https://bugzilla.gnome.org/show_bug.cgi?id=701954
2013-06-14 14:15:17 -04:00
8bd4895538 network: Simplify connections to the firmware signal
The status item will go away soon, so make sure the one-time
fire is given its own function. At the same time, only connect
to the signal when the situation actually matters.

https://bugzilla.gnome.org/show_bug.cgi?id=701954
2013-06-14 14:15:17 -04:00
465c77ddcf network: Make the device field private
With getIndicatorIcon() replacing the main use of the .device
field, we can make this a private API.

https://bugzilla.gnome.org/show_bug.cgi?id=701954
2013-06-14 14:15:17 -04:00
6ef2d4a4cc network: Merge NMConnectionBased back into NMDevice
https://bugzilla.gnome.org/show_bug.cgi?id=701954
2013-06-14 14:15:17 -04:00
7ae7f046c2 network: Rewrite VPN section to be independent of NMConnectionBased
https://bugzilla.gnome.org/show_bug.cgi?id=701954
2013-06-14 14:15:17 -04:00
f4051e810e network: Remove overflow of VPN configurations
It's unlikely that somebody has more than five VPN connections
configured, and the code is

https://bugzilla.gnome.org/show_bug.cgi?id=701954
2013-06-14 14:15:16 -04:00
e6c239d0f3 network: Move indicator icon selection to individual devices
This cuts down on the number of cross-connected "public" API between
the devices, hopefully allowing us to reduce it further.

https://bugzilla.gnome.org/show_bug.cgi?id=701954
2013-06-14 14:15:16 -04:00
35a7a3c1ac network: Always show the VPN indicator when connected to VPN
The new designs ask for this.

https://bugzilla.gnome.org/show_bug.cgi?id=701954
2013-06-14 14:15:11 -04:00
a0991c8261 network: "Remove" support for dial-up modems
NetworkManager has never supported dial-up modems, which are the
only case we have a modem device without any of these capabilities.

https://bugzilla.gnome.org/show_bug.cgi?id=701954
2013-06-14 13:48:35 -04:00
9520880568 network: Remove dead code
No class in here has this.carrier as a property. Presumably, this was
meant to be this.device.carrier, but since this code is going to be
rewritten soon anyway, might as well just junk the never-working
code for now.

https://bugzilla.gnome.org/show_bug.cgi?id=701954
2013-06-14 13:48:35 -04:00
719d793e22 Calendar: refactor the layout of the event list
In order to have event descriptions on multiple lines, but still
maintain proper alignment with the day and time strings, refactor
the whole event list to be one big table. Headers are implemented
as spanning cells, and uneven spacing is a mix of row/column spacing
and cell padding.

https://bugzilla.gnome.org/show_bug.cgi?id=701231
2013-06-14 18:44:47 +02:00
c7fb65c78e configure: Bump gnome-bluetooth requirement
Needed for the changes in the agent.

https://bugzilla.gnome.org/show_bug.cgi?id=700891
2013-06-14 11:36:22 +02:00
dd74ea99a7 bluetooth: Port to BlueZ 5
In BlueZ 4, Authorize() was used to authorize both service
and JustWorks authorization requests. In BlueZ 5 these two
have been split into AuthorizeService() for services and
RequestAuthorization for JustWorks devices. Adapt the
Bluetooth code accordingly.

https://bugzilla.gnome.org/show_bug.cgi?id=700891
2013-06-14 11:36:10 +02:00
c6fe6eb7ab network: Fix a bad signal name
We try to disconnect from firmwareChangedId, not firmwareMissingId.

https://bugzilla.gnome.org/show_bug.cgi?id=701954
2013-06-12 16:58:34 -04:00
2cbee05c8a network: Remove setActiveConnection/clearActiveConnection
This can be more easily achieved by listening for changes to the
device's active-connection property. VPN will still need support to
track active connections, as it does not have an associated
device. But as VPN can track multiple active connections, the names
"set" and "clear" don't quite fit. Rename them to the more-standard
"add" and "remove".

https://bugzilla.gnome.org/show_bug.cgi?id=701954
2013-06-12 16:57:56 -04:00
308b1d6039 dateMenu: Remove bad setColumnWidths call
The correct API is to pass in an array, so I don't even know how
this managed to work before.

https://bugzilla.gnome.org/show_bug.cgi?id=702125
2013-06-12 16:13:47 -04:00
4cd832c05a network: Rename a variable for consistency
We tend to use the name "a" in this method. This matches consistency
with the rest of the code.

https://bugzilla.gnome.org/show_bug.cgi?id=701954
2013-06-12 16:11:37 -04:00
2af4925d95 logout dialog: Show the correct text right away
The end session dialog was waiting a second before updating
its text to display the timer. It is nicer to show the correct
message from the start.

https://bugzilla.gnome.org/show_bug.cgi?id=702056
2013-06-12 05:48:23 -04:00
5e52f0e2a8 Updated Bulgarian translation 2013-06-08 11:09:42 +03:00
a46a68d616 Updated Czech translation 2013-06-08 07:49:24 +02:00
203d7c4b43 window-switcher: Only show windows from current workspace by default
When adding the window switcher, we copied our default values from
the alternate-tab extension. Arguably it makes more sense to mimick
the old mutter/metacity behavior though, so change the default
accordingly.

https://bugzilla.gnome.org/show_bug.cgi?id=701214
2013-06-07 21:42:54 +02:00
24703ffa57 osdWindow: Disable unredirection while showing the OSD
This allows OSDs to be visible even when displayed on top
of unredirected windows.

https://bugzilla.gnome.org/show_bug.cgi?id=701224
2013-06-07 21:21:00 +02:00
5cd913a527 windowManager: Use the correct schema for 'dynamic-workspaces'
We currently monitor the shell's override schema for changes to
the 'dynamic-workspaces' key, which ends up being the wrong
schema in classic mode. With the new ability to use mode-specific
overides, we can finally fix this.

https://bugzilla.gnome.org/show_bug.cgi?id=701717
2013-06-07 19:52:10 +02:00
91844e48e9 main: Pick up overridesSchema from sessionMode
This will allow the use of mode-specific defaults. For classic mode
we currently implement this with mini-extensions, but this may result
in confusing behavior when settings change due to extensions being
disabled during screen locks (not to mention that those mini-extensions
are hardly an elegant approach).

https://bugzilla.gnome.org/show_bug.cgi?id=701717
2013-06-07 19:52:10 +02:00
6c2f3d1d17 main: Move pref overrides to JS
We will allow to use mode-specific overrides; in preparation for that,
move the code so that we only override preferences after initializing
the session mode.

https://bugzilla.gnome.org/show_bug.cgi?id=701717
2013-06-07 19:52:10 +02:00
9c222c7e5c network: Remove _createAPItem
The code flows better if this is inlined like this.

https://bugzilla.gnome.org/show_bug.cgi?id=698918
2013-06-06 17:16:44 -04:00
2249da7976 network: Remove some more dead code
Submenus gone means we can remove the title param and things
that use it.

https://bugzilla.gnome.org/show_bug.cgi?id=698918
2013-06-06 17:16:44 -04:00
a55288bda0 network: Don't create submenus for multiple-connection items
As multiple-connections for a Wi-Fi AP won't fit in the new design,
remove submenus right now. Simply make a simple item that connects
to the first known connection for the AP, which should be the common
case.

https://bugzilla.gnome.org/show_bug.cgi?id=698918
2013-06-06 17:16:07 -04:00
e645edbda7 popupMenu: Fix PopupSliderMenuItem
It seems a bad rebase took this away.
2013-06-06 15:49:20 -04:00
aee7cd73c4 network: Reformat connection code
This makes the code flow the same between removeConnection and checkConnection.

https://bugzilla.gnome.org/show_bug.cgi?id=701755
2013-06-06 15:32:06 -04:00
380a71dd21 network: Remove a dead signal
This signal is unused.

https://bugzilla.gnome.org/show_bug.cgi?id=701755
2013-06-06 14:00:38 -04:00
23dd5cc160 volume: Remove some unused variables
https://bugzilla.gnome.org/show_bug.cgi?id=701755
2013-06-06 14:00:38 -04:00
847cb5b972 slider: Separate PopupSliderMenuItem into its own widget
https://bugzilla.gnome.org/show_bug.cgi?id=701755
2013-06-06 14:00:38 -04:00
cc64091f9c popupMenu: Fix bubbling in the slider widget
https://bugzilla.gnome.org/show_bug.cgi?id=701755
2013-06-06 14:00:38 -04:00
b68eb44ca5 userMenu: Use addSettingsAction
https://bugzilla.gnome.org/show_bug.cgi?id=701755
2013-06-06 14:00:38 -04:00
403540e8a1 userMenu: Move UserAvatarWidget into UserWidget
https://bugzilla.gnome.org/show_bug.cgi?id=701755
2013-06-06 14:00:37 -04:00
c39497222f update Simplified Chinese (zh_CN) translation 2013-06-06 11:44:33 +08:00
9e56e668e0 dnd: Use pushModal() to grab the keyboard
Currently we "only" grab the keyboard when starting a drag operation,
which does not impede keybindings to be processed. This is at best
not harmful (like workspace switching), but may have unintended effects
otherwise - for instance, the hot corner is disabled, so having the
corresponding keyboard shortcut still active is fairly odd (not to
mention that it leaves the system in a confused state).
Fix this by switching to pushModal()/popModal(), which will push a
dedicated keybinding mode for us.

https://bugzilla.gnome.org/show_bug.cgi?id=700877
2013-06-05 17:32:49 +02:00
41ae93dba0 screenShield: Clear clipboard on lock
Currently the clipboard's contents may leak to unauthorized parties by
pasting into the unlock dialog's password entry and unmasking the entry.
Prevent this from happening by clearing the clipboard on lock.

https://bugzilla.gnome.org/show_bug.cgi?id=698922
2013-06-05 16:22:27 +02:00
6c527c1bb4 osdWindow: Make sure the OSD is always our topmost chrome element
In particular this fixes the OSD showing up behind a modal dialog's
lightbox.

https://bugzilla.gnome.org/show_bug.cgi?id=701269
2013-06-05 15:40:17 +02:00
90c7876341 dash: Grow the empty dash during drag operations
When the dash does not contain any applications (either favorites
or running), it is currently impossable to add a favorite via DND.
Grow the dash slightly in that case to provide a drop target.

https://bugzilla.gnome.org/show_bug.cgi?id=684618
2013-06-05 15:32:43 +02:00
10b77a8305 dash: Minor cleanup
https://bugzilla.gnome.org/show_bug.cgi?id=684618
2013-06-05 15:32:43 +02:00
1902f4773b dash: Use a single code path for clearing the drag placeholder
We currently only keep track of old placeholders when moving past
the dragged app's current favorite position, as this is the only
case where we need to worry about jitter. Still, moving it into
_clearDragPlaceholder() allows us to consolidate code paths, which
is a good thing ...

https://bugzilla.gnome.org/show_bug.cgi?id=684618
2013-06-05 15:32:43 +02:00
4b95be6a95 dash: Make sure _clearDragPlaceholder() resets _dragPlaceholderPos
The function currently only resets the placeholder position if
there is a placeholder; this is not necessarily true, as the
placeholder may be reset outside _clearDragPlaceholder().
If this happens, the placeholder will temporarily stop working
for the "old" position (and permanently if it's the only position).
Just reset the position unconditionally.

https://bugzilla.gnome.org/show_bug.cgi?id=684618
2013-06-05 15:32:43 +02:00
61323926e0 Updated Traditional Chinese translation(Hong Kong and Taiwan) 2013-06-04 21:49:58 +08:00
e30d18febe appDisplay: Give more horizontal space to control buttons
Before, the text of those buttons were truncated when the text exceeded
the fixed width we had in the CSS.
Now, we give more horizontal space to the control buttons to match
the maximum text length of all buttons.

https://bugzilla.gnome.org/show_bug.cgi?id=696307
2013-06-03 19:26:26 +02:00
9f2f80ae4f Updated German translation 2013-06-02 09:48:44 +02:00
bd6e0ceb81 dash: Simplify some code
https://bugzilla.gnome.org/show_bug.cgi?id=701386
2013-06-01 00:35:24 +02:00
673d7038d8 Restore gvc revision
Commit 9d54e46ce7 accidentally reverted to a previous revision.
2013-05-31 20:57:43 +02:00
3a6231dcc1 build: Bump gcr requirement
https://bugzilla.gnome.org/show_bug.cgi?id=700972
2013-05-31 20:43:24 +02:00
9d54e46ce7 Updated Galician translations 2013-05-31 01:04:06 +02:00
63e6d11892 Tajik updated 2013-05-30 17:29:52 +05:00
0509bb9bb4 shell-gtk-embed: Lower tray icon windows to the bottom of the stack
Otherwise they break the "top level window" detection used by the
unredirect code in mutter, causes game windows not to be unredirected
when tray icons are present.

https://bugzilla.gnome.org/show_bug.cgi?id=701224
2013-05-30 00:57:37 +02:00
5c78908a5f xdnd: Lower window activation timeout
The current one is perceived as too high by users, so try a shorter timeout.

https://bugzilla.gnome.org/show_bug.cgi?id=700150
2013-05-29 22:04:07 +02:00
5a2269c6c6 overviewControls: Don't push window thumbs over when workspaces are invisible
Make the sliding control invisible when the thumbnails box is as well.

https://bugzilla.gnome.org/show_bug.cgi?id=701167
2013-05-29 13:04:45 -04:00
a7e9655e32 searchDisplay: Remove the setResults/getResultsForDisplay dance
Now that we control our own destiny, I noticed that getResultsToDisplay
is the only user of this._notDisplayedResult, and it's called immediately
after setResults, which is the only thing that sets it. Just remove the
stateness entirely.

https://bugzilla.gnome.org/show_bug.cgi?id=693836
2013-05-29 11:32:50 -04:00
1ec82d2ddd searchDisplay: Remove getVisibleResultCount
This is only used in one place, so we can quickly substitute
where it's needed.

https://bugzilla.gnome.org/show_bug.cgi?id=693836
2013-05-29 11:32:50 -04:00
98eaef621a searchDisplay: Remove the "provider meta"
As everything is tracked on the SearchResults or subclasses of now,
just use that, which we call the "provider display".

https://bugzilla.gnome.org/show_bug.cgi?id=693836
2013-05-29 11:32:50 -04:00
74a6ca58ef searchDisplay: Move the provider actors to the results display
Since the provider icon only appears in the list results, it makes
sense for that to be stored with the results class, rather than outside,
triggered by which sort of display it is.

https://bugzilla.gnome.org/show_bug.cgi?id=693836
2013-05-29 11:32:49 -04:00
747faa43ae searchDisplay: Remove the providerIcon from the providerMeta
Since the resultsDisplay is tracking it, it's not needed.

https://bugzilla.gnome.org/show_bug.cgi?id=693836
2013-05-29 11:32:49 -04:00
32a53f7412 searchDisplay: Remove focus management code
It turns out that this focus code broke sometime in the 3.6 cycle --
when updating results, the focus is always on the text entry, so this
never gets called. We'll eventually replace it with something that
keeps track of the focused result meta, but for now, remove it.

https://bugzilla.gnome.org/show_bug.cgi?id=693836
2013-05-29 11:32:49 -04:00
9c94e9813c searchDisplay: Make renderResults private
There's no need to call this from outside now.

https://bugzilla.gnome.org/show_bug.cgi?id=693836
2013-05-29 11:32:49 -04:00
19749bb37f searchDisplay: Make the results display in charge of updating the actor
While this is a very simple translation right now, soon enough it will
be so that it will have a less crazy "public" API and can do things like
cache result metas.

https://bugzilla.gnome.org/show_bug.cgi?id=693836
2013-05-29 11:32:49 -04:00
5ab4c484a5 searchDisplay: Remove clearDisplayForProvider
Since the two paths that call this want to keep the actor in two different
states, it makes sense to just call the one function that's the same between
both individually.

https://bugzilla.gnome.org/show_bug.cgi?id=693836
2013-05-29 11:32:49 -04:00
e602199bfb searchDisplay: Add a base class for common parts of search results
Right now, this doesn't give us very much, since IconGrid and StBoxLayout
have different APIs. But since we want to introduce result caching, it
makes to reduce the duplication we already have so we don't need to add
the code to do so in both places.

https://bugzilla.gnome.org/show_bug.cgi?id=693836
2013-05-29 11:32:49 -04:00
62e1c08dd6 searchDisplay: Remove pendingClear
It turns out that every time we call getResultsForDisplay is
directly after a setResults, so pendingClear is always true.

https://bugzilla.gnome.org/show_bug.cgi?id=693836
2013-05-29 11:32:49 -04:00
37e2b60cd3 search: Rename pushResults to setResults
pushResults, and the original async search API, were originally intended
so search results that weren't immediate could be added as they come in.
Since then, we've decided that the design of search results is that they
should finish at once with all results. Thus, the code was modified so
that pushResults always overwrote the current result set. As such, it makes
sense to rename the method so that the name matches the behavior.

https://bugzilla.gnome.org/show_bug.cgi?id=693836
2013-05-29 11:32:49 -04:00
f299078585 Updated Czech translation 2013-05-29 12:26:52 +02:00
bf0a0d5bad Bump version to 3.9.2
Update NEWS.
2013-05-28 19:38:25 +02:00
365cda386c messageTray: Fix lightbox outside the overview
Commit d0310bd745 blindly replaced global.overlay_group with
Main.layout.overviewGroup, but unlike the former, the latter is
hidden while the overview is not active, which makes it unsuitable
for the message tray's light box. In fact, with the removal of
global.overlay_group, there is no longer a container which may
be used both inside and outside the overview, so we can either
recreate the lightbox each time we show/hide the overview, or
use different lightboxes altogether; this opts for the latter.

https://bugzilla.gnome.org/show_bug.cgi?id=701097
2013-05-28 16:33:47 +02:00
5de5b7a66a Updated Norwegian bokmål translation 2013-05-28 09:44:18 +02:00
d6de0a64ed Tajik Updated 2013-05-28 12:16:32 +05:00
26f8441f73 status/keyboard: Stop destroying the IBusBus object
This is a singleton object inside libibus which means that if we
destroy it (e.g. because ibus-daemon got restarted) then, other
library users, like the ibus gtk+ IM module that we also use
in-process, will break.

https://bugzilla.gnome.org/show_bug.cgi?id=699189
2013-05-27 14:07:56 +02:00
51bca08386 messageTray: Check if the clicked summary item has a right click menu
Commit e71129aa68 introduced the
possibility of having summary items without a right click menu so we
should check for one before trying to show it.

https://bugzilla.gnome.org/show_bug.cgi?id=700190
2013-05-27 14:05:54 +02:00
1579aad362 status/keyboard: Allow switching input source in the message tray
We still can't show a popup switcher in the message tray but we can at
least degrade gracefully and advance to the next input source.

https://bugzilla.gnome.org/show_bug.cgi?id=697009
2013-05-27 14:05:53 +02:00
a2a580954a status/keyboard: Switch input source on special modifiers accelerator
This simply mimics the X server's layout switching behavior by
advancing to the next input source and wrapping around.

https://bugzilla.gnome.org/show_bug.cgi?id=697008
2013-05-27 14:05:51 +02:00
944c28f3b3 status/keyboard: Synchronize input source switching with key events
Currently we simply set the gsettings key when activating an input
source. This obviously introduces a time window, between the event that
activates the switch and when the switch is complete, under which key
events are being delivered to applications and interpreted according
to the previous input source.

The patches in bug 696996 introduce a DBus API in g-s-d that allows us
to know when an input source if effectively active. Using that and
freezing keyboard events in the X server until we hear back from g-s-d
we can ensure that events won't be misinterpreted after an input
source switch.

https://bugzilla.gnome.org/show_bug.cgi?id=697007
2013-05-27 14:05:50 +02:00
c13a597fe0 Updated Spanish translation 2013-05-27 13:54:24 +02:00
86e0ae0b93 [l10n] Minor fix on Catalan translation 2013-05-26 08:21:57 +02:00
f71ffed3c7 Updated Slovenian translation 2013-05-25 21:39:26 +02:00
cae96d9023 Updated slovak translation 2013-05-25 21:12:50 +02:00
676b649731 Tajik updated 2013-05-25 13:17:52 +05:00
634ce34e00 global: Use the new mutter API for focusing the stage window
This way, we aren't "going behind mutter's back" about what the current
focused window is.

https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-05-24 17:44:17 -04:00
6c3096f71f Updated slovak translation 2013-05-24 23:21:21 +02:00
991ed2da72 messageTray: Fix a stuck grab related to bubble notifications
If the user middle clicks on a summary item in the tray, we'll
try to grab null instead of an actor, which is wrong.

https://bugzilla.gnome.org/show_bug.cgi?id=700854
2013-05-24 13:29:07 -04:00
907f02cd28 Tajik Updated 2013-05-24 20:00:58 +05:00
9ba78ce04a Bluetooth: Use "Passkey" instead of "PIN"
Passkey is the proper name to be used when we are pairing with a Bluetooth
2.1+ device. PIN are only used for older devices which is not the case
here, the message is only shown when dealing with 2.1+ devices.

https://bugzilla.gnome.org/show_bug.cgi?id=697661
2013-05-24 16:42:04 +02:00
14a3d9f7fb Depend on gcr-base instead of gcr pkg-config file
gnome-shell does not use the UI bits of the Gcr library

https://bugzilla.gnome.org/show_bug.cgi?id=700944
2013-05-24 11:59:00 +02:00
f73a01295c messageTray: Don't destroy a notification from within its destroy handler
Nothing but bad can come from this.

https://bugzilla.gnome.org/show_bug.cgi?id=700923
2013-05-23 18:25:39 -04:00
ccfa3d3be1 Re-lock the screen if we're restarted from a previously crashed shell
This way we "fail closed", which is better for security.

See https://bugs.launchpad.net/ubuntu/+source/gdm/+bug/1064584

https://bugzilla.gnome.org/show_bug.cgi?id=691987
2013-05-23 16:10:03 -04:00
727e4c0b37 workspace: don't use multiple later calls to propagate actualGeometry calls
Meta laters are invoked in reverse order of registration, so
having multiple laters propagating the geometry cause all but the
first one in the frame (which is usually wrong) to be ignored.
Instead, queue at most one later call, and use the last set geometry
in the callback.

https://bugzilla.gnome.org/show_bug.cgi?id=700853
2013-05-23 18:10:50 +02:00
6ce470b9b2 docs: Include ShellKeyBindingMode in docs
https://bugzilla.gnome.org/show_bug.cgi?id=700900
2013-05-23 17:07:50 +02:00
4b7e230531 layout: Properly order startup initialization
In order to make sure that the struts and regions are initialized,
we need to make sure that we do with the conditions that nothing
is transformed, like the uiGroup, and that everything is visible.

These invariants broke during a fix to hide the UI until the
system background texture could be loaded. Now, reorder things so
that the system background is loaded, and then the UI is properly
loaded, and so on.

https://bugzilla.gnome.org/show_bug.cgi?id=696159
2013-05-22 12:53:39 -04:00
aefe0d3ddd layout: Fix indentation
https://bugzilla.gnome.org/show_bug.cgi?id=696159
2013-05-22 12:53:39 -04:00
bbb23b515f popupMenu: Allow for an optional border for slider handle
While the default style works well will a solid handle, using both
border and fill color would be desirable in classic mode. Add the
necessary (optional) style properties to allow this.

https://bugzilla.gnome.org/show_bug.cgi?id=697917
2013-05-22 18:46:34 +02:00
976166a04a Finish removing the overlay_group concept
In preparation for removing from mutter too.

https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-05-22 18:34:05 +02:00
b20129c37e screenshot: Hide cursor while magnifier is active
As with the screen recorder, the magnifier already adds its own
copy of the system cursor, so we should not add it again. Just
as in the screen recorder case, we don't address the case where
the cursor should not be included in the screenshot, but the
magnifier adds it anyway.

https://bugzilla.gnome.org/show_bug.cgi?id=700488
2013-05-22 18:33:16 +02:00
4fe1360b2c recorder: Hide cursor while magnifier is active
The magnifier adds its own copy of the system cursor to apply the
expected transformations, so we don't need to add it again in the
recorder; this avoids two different cursors showing up in recordings,
but doesn't address the case where the cursor should not be recorded
at all, but the magnifier adds it anyway.

https://bugzilla.gnome.org/show_bug.cgi?id=700488
2013-05-22 18:33:16 +02:00
f0113c5ff5 Overview: use a normal chrome actor to handle events during XDND
Instead of using the input mode, when the overview is not modal
it should use a Chrome-tracked actor, that is added to the input
region. Because the overview always takes pointer input when
visible, the actor is added at startup, and it is shown and hidden
as needed.

https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-05-22 18:27:27 +02:00
a259016436 screenShield: Pop the modal immediately
Allow people to start typing immediately after unlocking their
session, rather than having to wait for a transition.

https://bugzilla.gnome.org/show_bug.cgi?id=700847
2013-05-22 12:11:19 -04:00
9a79c71e88 global: Remove support for the NONREACTIVE input mode
As it's unused, this is a quick cleanup before we can go onto
more important things.

https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-05-22 12:11:19 -04:00
e62d22a50e layout: Remove affectsInputRegion
This can easy be worked around by adding things to the uiGroup
instead, so there's really no reason to have it here still.

https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-05-22 12:11:19 -04:00
402f2d939c layout: Remove use of skip_paint
We no longer use this on the uiGroup.

https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-05-22 12:11:19 -04:00
374aee75d8 screencast: Fix return value in case of invalid file template
If we are passed an invalid file template, ShellRecorder.record()
will return a %NULL filename; as the Screencast DBus interface
expects a string return value, we cannot return the value unmodified
in that case.

https://bugzilla.gnome.org/show_bug.cgi?id=700842
2013-05-22 17:22:28 +02:00
88ce65266e Disable all extensions in reverse order
So we avoid having to disable and enable an extension that is to
be disabled later anyway.

https://bugzilla.gnome.org/show_bug.cgi?id=700784
2013-05-22 07:51:23 +02:00
e0a6a623d2 workspacesView: Adapt to changes in windowManager
The actionMoveWorkspace() function was changed in commit 8727661c1,
adapt its use in workspacesView as well.

https://bugzilla.gnome.org/show_bug.cgi?id=659288
2013-05-21 22:42:28 +02:00
d0310bd745 Overview: don't use the overlay_group
It's a deprecated concept, and we want to have our own actor
that we can add to the chrome to handle the input region.

https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-05-21 22:24:53 +02:00
35c665156b Fix bad rebase
A variable rename was missed.
2013-05-21 22:23:18 +02:00
3754a76f10 userWidget: Fix leaking instances
UserWidget instances should be destroyed when the actor is. That's
what consumer code expects since the destroy() method is never called.

https://bugzilla.gnome.org/show_bug.cgi?id=700807
2013-05-21 21:59:14 +02:00
db2e6e5b95 keyboard: don't raise to the top when showing
The stacking is already correct due to the grouping, and calling
raise_top() results in the keyboard above the OSDs.

https://bugzilla.gnome.org/show_bug.cgi?id=700620
2013-05-20 22:00:36 +02:00
d5f95db68d messageTray: Hide notifications when the tray is summoned
If the user summons the tray, it should take priority over a
notification showing.

https://bugzilla.gnome.org/show_bug.cgi?id=700639
2013-05-20 14:25:55 -04:00
d96726c392 messageTray: Add a synonym for hasNotifications
This prevents lines from getting too long.

https://bugzilla.gnome.org/show_bug.cgi?id=700639
2013-05-20 14:25:55 -04:00
d1c54f55e6 messageTray: Don't hide the tray for urgent notifications
The tray should never close on its own, without the user's input.

https://bugzilla.gnome.org/show_bug.cgi?id=700639
2013-05-20 14:25:55 -04:00
176a73f4e9 messageTray: Clean up variable names
Variable names like "sourceNotificationStackDoneShowing" are too
long, and too undescriptive: this one points to a source, not a
notification stack that has been done showing.
2013-05-20 13:38:23 -04:00
54a9592e19 main: Override the workspace layout in WindowManager
https://bugzilla.gnome.org/show_bug.cgi?id=691746
2013-05-20 13:20:21 -04:00
b2aa29e221 main: Move workspace tracking code to WindowManager
https://bugzilla.gnome.org/show_bug.cgi?id=691746
2013-05-20 13:20:21 -04:00
0eba0f8dd3 appDisplay: Also account for folder popup's close buttons
As the close button of folder popups overlaps at the top, it ends
up being cut off if the folder is located at the very top of the
view. Fix this glitch by taking the button's overlap into account
in that case.

https://bugzilla.gnome.org/show_bug.cgi?id=694371
2013-05-19 23:30:39 +02:00
d8d0afff0b appDisplay: Fix cut-off folders in All view
We already take care of growing the view if open folders overlap
at the bottom, however folder popups may still end up being cut
off when opening above the source icon - if the popup is high enough,
its y coordinate will be negative and therefore outside the parent's
allocation. To fix, we can either make sure that folders pop up below
their source icon in that case, or adjust the parent grid's position
as necessary while a folder is open. This implements the latter.

https://bugzilla.gnome.org/show_bug.cgi?id=694371
2013-05-19 23:30:39 +02:00
b799e8c0a6 appDisplay: Expand AllView
If the view doesn't fill the available space, content should still
start at the top rather than the center - not least the positioning
code for folder popups assumes that, so set the appropriate expand
flags.

https://bugzilla.gnome.org/show_bug.cgi?id=694371
2013-05-19 23:30:39 +02:00
583255e1a8 theme: polish the session chooser
The session chooser list has an embedded look which doesn't fit
well with the rest of the theme. Give it more of a flat appearance
and simplify the visuals.
https://bugzilla.gnome.org/show_bug.cgi?id=695742
2013-05-19 15:25:03 -04:00
c68ccbf263 screenshot: Check for NULL window in screenshot_window()
screenshot_window() currently assumes a focus window, which will
result in a crash if that's not the case.

https://bugzilla.gnome.org/show_bug.cgi?id=700625
2013-05-19 15:32:06 +02:00
d658ec8de2 overviewControls: Move reactive flag to main actor
The main overview actor was made reactive to catch scroll-events
and propagate them; after some code shuffling, the actor that
catches scroll events ended up not being the same actor that's
supposed to propagate this, which broke using the scroll wheel
to switch workspaces.

https://bugzilla.gnome.org/show_bug.cgi?id=700595
2013-05-18 21:07:41 +02:00
8727661c1c WindowManager: Show switcher popup for switch-to-workspace-n keybindings
Currently we show the workspace popup for relative targets ("up", "down"),
but not when targetting a specific workspace directly.
There is not really a good reason for that difference, and as we are about
to introduce a new shortcut to target the last workspace (which does vary
with dynamic workspaces), it makes sense to unify the behavior and always
show the switcher.

https://bugzilla.gnome.org/show_bug.cgi?id=659288
2013-05-18 20:12:49 +02:00
731e8bfe2b Replace 'enable-app-monitoring' setting
The org.gnome.desktop.privacy schema gained a 'remember-app-usage'
key, use that instead of our own preference.

https://bugzilla.gnome.org/show_bug.cgi?id=699714
2013-05-18 20:12:49 +02:00
f88d9c06f5 appDisplay: Hide frequent view when app monitoring is disabled
Currently we stop monitoring application usage when disabling the
'enable-app-monitoring' setting, but we still expose previously
gathered data in the app picker's frequent view. This is not what
users should expect, so hide the view in that case.

https://bugzilla.gnome.org/show_bug.cgi?id=699714
2013-05-18 20:12:49 +02:00
248a0c1b6c recorder: Use workarea to position the recording icon
It looks a bit unpolished to overlap our own chrome with the recording
icon, which may happen when an existing adds UI at the bottom edge.
Fix this by using the primary monitor's workarea for the position rather
than the entire monitor.

https://bugzilla.gnome.org/show_bug.cgi?id=700409
2013-05-18 14:29:26 +02:00
434f1edb25 overview: Add focusSearch() method and export it over DBus
Some keyboard spot a dedicated search key, which gnome-settings-daemon
currently handles by spawning gnome-search-tool. It makes a lot of
sense to promote the Shell's integrated search feature instead, so
expose an appropriate DBus method g-s-d can use.

https://bugzilla.gnome.org/show_bug.cgi?id=700536
2013-05-17 18:27:17 +02:00
2a550e6466 Document --clutter-display in the man page
This is the sole option that was missing from the
man page.

https://bugzilla.gnome.org/show_bug.cgi?id=700339
2013-05-16 18:43:52 -04:00
4f4132943d Updated Norwegian bokmål translation 2013-05-16 13:27:26 +02:00
62760d5b2d windowManager: Enable switch-to-workspace-n keybindings in overview
Those keybindings are unassigned by default, but that's not a valid
reason they shouldn't work like the related switch-up/down bindings.

https://bugzilla.gnome.org/show_bug.cgi?id=649977
2013-05-16 00:40:11 +02:00
34aa501637 network: Fix stupid typo
There is no ACTIVE state in NetworkManager.ActionConnectionState,
it's ACTIVATED.

https://bugzilla.gnome.org/show_bug.cgi?id=700394
2013-05-15 17:47:36 +02:00
6330379f77 network: Use accessPointAdded to add initial access points
This is technically a smidge slower due to the constant bisect insert,
but since this should only happen when we make a Wi-Fi dialog, it's
insignificant.

https://bugzilla.gnome.org/show_bug.cgi?id=700322
2013-05-15 11:13:11 -04:00
30c64baa7f network: Don't pass this._connections to the VPN section
It's always empty by this point.

https://bugzilla.gnome.org/show_bug.cgi?id=700322
2013-05-15 11:13:11 -04:00
cdbed0c615 network: Use checkConnection to set up the initial connections
https://bugzilla.gnome.org/show_bug.cgi?id=700322
2013-05-15 11:13:11 -04:00
f6e7034172 network: Remove an unused getter
https://bugzilla.gnome.org/show_bug.cgi?id=700322
2013-05-15 11:13:11 -04:00
cd9c5b9c5d network: Remove semi-complex primary device finding
According to dcbw (and some updated documentation), there will
always be a 1:1 correspondence between NMActiveConnection and
an NMDevice.

https://bugzilla.gnome.org/show_bug.cgi?id=700322
2013-05-15 11:13:11 -04:00
7833e21b01 altTab: Always activate MRU window when activating an app
Commit 2499f2ed80 went back to using shell_app_activate() for
selecting an app, which favors windows on the current workspace;
this is the behavior we want for instance when activating a
launcher, but it's wrong for the alt-tab list - explicitly
request the first (e.g. MRU) window in that case.

https://bugzilla.gnome.org/show_bug.cgi?id=700356
2013-05-15 12:12:35 +02:00
12ba2b222f Add a way to get backtraces from criticals and warnings
Attaching gdb and running with G_DEBUG=fatal-warnings is not a very
fast to debug a specific issue (especially if you have warnings at
startup, since then you need to run the shell from a terminal).
Instead, introduce a new SHELL_DEBUG environment variable that can
be set to backtrace-warning, causing a gjs_dumpstack() after every
warning or critical.

https://bugzilla.gnome.org/show_bug.cgi?id=700262
2013-05-14 23:25:11 +02:00
8583ca73e4 popupMenu: Fix regular submenus
This method was originally private, but it was renamed quickly
before landing, and I forgot to update it.
2013-05-14 13:58:10 -04:00
b588ae4e0e PopupMenuManager: Fix child menus
Since commit c84dc6254d, popup menus are closed automatically
when another menu opens (to catch the case where a menu is opened
by keyboard shortcut, which wasn't handled before). However in the
case of child menus, both child and parent are expected to be visible,
so handle this case explicitly.

https://bugzilla.gnome.org/show_bug.cgi?id=699678
2013-05-14 19:08:00 +02:00
28abc15c00 volume: Adjust to icon name changes
The headphone icon gained an audio- prefix, use that instead of
the old name.
2013-05-14 14:29:22 +02:00
4d663680d8 Fix build one more time
Third time's the charm.
2013-05-13 18:38:35 -04:00
6505f8e94a Fix build on certain systems
The SCANNERFLAGS were wrong here.
2013-05-13 18:36:36 -04:00
ff3c20dda2 remoteMenu: Minor code cleanups
https://bugzilla.gnome.org/show_bug.cgi?id=700257
2013-05-13 18:34:26 -04:00
c698dee071 remoteMenu: Add support for the submenu show requests
This lets clients defer submenus showing until their submenu model
is ready.

https://bugzilla.gnome.org/show_bug.cgi?id=700257
2013-05-13 18:34:26 -04:00
8891a41793 remoteMenu: Add support for submenu items
Wrap new GtkMenuTracker API that adds an easy way to bind to
tracker items, and use it to add back support for submenus.
This also adds support for a submenu feature that we didn't
have support for before, action namespaces.

https://bugzilla.gnome.org/show_bug.cgi?id=700257
2013-05-13 18:34:26 -04:00
026f61f5aa remoteMenu: Split the tracking code out of RemoteMenu
The tracking logic will be used to more easily implement submenus.

https://bugzilla.gnome.org/show_bug.cgi?id=700257
2013-05-13 18:34:26 -04:00
af063dc2f2 remoteMenu: Allow separator items to have labels
This fixes a regression with remote menus where sections have
labels, like gnome-documents.

https://bugzilla.gnome.org/show_bug.cgi?id=700257
2013-05-13 18:34:26 -04:00
3075f3cfe4 remoteMenu: Add support for extended accessibility roles
https://bugzilla.gnome.org/show_bug.cgi?id=700257
2013-05-13 18:34:26 -04:00
419f2dca15 remoteMenu: Port to GtkMenuTrackerItem
This pulls in new upstream API that Ryan will maintain, removing
code on our side.

Currently, our implementation of submenus will be gone, but this
will be fixed in a few commits.

https://bugzilla.gnome.org/show_bug.cgi?id=700257
2013-05-13 18:34:26 -04:00
5803f69605 popupMenu: Split the remote menu code into its own module
This is getting sufficiently complicated to deserve its own place.

https://bugzilla.gnome.org/show_bug.cgi?id=700257
2013-05-13 18:34:26 -04:00
bfd1cc99a0 Add a separate ShellMenu GIR file for introspecting the copy/paste code
We'll need some of these pieces to be introspectable when we port to
GtkMenuTrackerItem. Due to technical limitations in introspection, we
can't put Gtk-prefixed items in the shell namespace, so add them to
a new introspection library instead.

https://bugzilla.gnome.org/show_bug.cgi?id=700257
2013-05-13 18:34:26 -04:00
a6a2cea414 gtkactionmuxer: Reintroduce the passing of event timestamps
This is a hack we have in our local fork as compared to upstream;
work on a generic "hook" system in here is ongoing, but until then,
this is the easiest way to do it.

https://bugzilla.gnome.org/show_bug.cgi?id=700257
2013-05-13 18:34:26 -04:00
10e857cebe Update copy/paste code from upstream
This includes a rename from the G* namespace to the Gtk* one, which
will help us with introspecting this code. Note that this removes
some of the custom code we added to GActionMuxer to relay event times
to the remote action group. We'll add this back soon.

https://bugzilla.gnome.org/show_bug.cgi?id=700257
2013-05-13 18:34:26 -04:00
ed76b54511 MessageTray: fix typo 2013-05-13 23:41:16 +02:00
9659d056b7 polkitAgent: Allow retrying mistyped passwords
Don't hide the polkit agent window when someone mistypes their password.
Allow them to try again. The user can cancel at any point.

https://bugzilla.gnome.org/show_bug.cgi?id=684431
2013-05-13 21:10:40 +02:00
b79b0952b4 appDisplay: Implement folder keynav and shortcuts
When opening an application folder, it should take key focus to
allow for keynav; also, Escape closing both folder and app picker
is unexpected, it should only close the popup.

https://bugzilla.gnome.org/show_bug.cgi?id=695314
2013-05-13 19:41:23 +02:00
fa44dc7d16 MessageTray: don't list the sources all the time
The point of a hash table is that you don't need to list all the
elements. To avoid that, keep a "clearableCount" in MessageTray,
which can be used by the message tray menu to show and hide the
clear item, and that is updated in constant time when sources
are added or removed.

https://bugzilla.gnome.org/show_bug.cgi?id=700194
2013-05-12 21:06:34 +02:00
b0dc841e00 Hash: make .size() constant time
MessageTray calls .size() very often to update the no messages label,
so a linear time implementation is not good enough.

https://bugzilla.gnome.org/show_bug.cgi?id=700194
2013-05-12 21:06:34 +02:00
c72ae375c8 app-system: Remove last real use of shell_app_get_tree_entry
We want to replace our GMenuTreeEntries with GDesktopAppInfos

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-05-12 14:45:36 -04:00
0fd6ae5330 app-system: Use g_hash_table_iter_remove
glib now provides an iteration-safe way to remove items from
the hash table, so use it instead of building a separate list.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-05-12 14:44:42 -04:00
c8792ccfa6 app-system: Kill an unused warning
https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-05-12 14:44:42 -04:00
b689972e67 overview: Don't allow exiting the overview if we have a DND drag
Otherwise, people can drag app icons or window thumbnails out of
the overview, and it just looks weird.

https://bugzilla.gnome.org/show_bug.cgi?id=698484
2013-05-12 14:40:05 -04:00
ae6d7bbfa3 MessageTray: actually respect other components acking notifications
If a notification is marked acknowledged from outside, filter it
out from the queue.

https://bugzilla.gnome.org/show_bug.cgi?id=698812
2013-05-12 20:19:06 +02:00
2591bc90ac ScreenShield: mark music notifications as acknowledged immediately
After all, the user is seeing it (or will see them before unlocking),
so there is no point in queing them as banners.

https://bugzilla.gnome.org/show_bug.cgi?id=698812
2013-05-12 20:19:06 +02:00
61fe000daa ScreenShield: clear the lock screen early when deactivating
Upon popMode, MessageTray will try readding all notifications
to their rightful parent, so we must tell NotificationBox to
relinquish them before st_bin_set_child() fails (leaving a dangling
child pointer and crashing at the next allocation)

https://bugzilla.gnome.org/show_bug.cgi?id=698812
2013-05-12 20:19:06 +02:00
c864ebeabb data: Fix typo in Screencast docs 2013-05-10 23:41:36 +02:00
24c530611f Expose screencast functionality via DBus
Like screenshots, the screen recorder can be a useful tool in other cases
than being triggered by a keyboard shortcut. To account for that, export
a Screencast DBus API similar to the existing Screenshot interface.

https://bugzilla.gnome.org/show_bug.cgi?id=696247
2013-05-10 19:49:01 +02:00
d44d00f0f4 sessionMode: Add 'allowScreencast' property
Our built-in screen recorder is implemented as a component, so it will
just be disabled when the session mode doesn't allow screencasting.
However we will expose screencasting functionality on DBus as well, and
while it makes sense to restrict its availablity to the same modes as
the existing recorder, exporting/unexporting the service depending on
the session mode is not very consumer friendly.
For that reason, add an additional 'allowScreencast' property that for now
mirrors the availability of the 'recorder' component.

https://bugzilla.gnome.org/show_bug.cgi?id=696247
2013-05-10 19:49:01 +02:00
dc3082e66d shell-recorder: Make drawing the cursor optional
As with screenshots, showing the cursor in a recording may not be
desirable, so add a property to control whether the cursor is drawn
or not.

https://bugzilla.gnome.org/show_bug.cgi?id=696247
2013-05-10 19:49:01 +02:00
c61573a8b7 shell-recorder: Support specifying the recorded area
Currently we will always record the entire screen. It has been requested
to support recording a specified area analogous to the screenshot API as
well, so add a set_area() method which allows this.

https://bugzilla.gnome.org/show_bug.cgi?id=696247
2013-05-10 19:49:01 +02:00
3b95560d32 shell-recorder: Optionally return the filename of the recording
It is currently not always possible to predict the actual output filename
of a recording - the file-template does not necessarily use an absolute
path and may contain %d and %t escape sequences.
This is OK for fire-and-forget uses like the existing keyboard shortcut,
but we will soon expose the functionality on DBus and consumers of that
API might very well need to access the file after the recording. So do
the same as our screenshot API and add an optional (out) parameter to
record().

https://bugzilla.gnome.org/show_bug.cgi?id=696247
2013-05-10 19:49:01 +02:00
990f68375e main: Always pass a default stylesheet when constructing the theme
There is not always a clear distinction between code and style,
which is why the interface ends up being mostly unusable when we
end up without *any* style, for instance because the specified
application-stylesheet is corrupt.
Setting the default stylesheet in addition to the application-stylesheet
is no guarantee for non-default themes not messing up the interface, but
it should at least lower the risk ...

https://bugzilla.gnome.org/show_bug.cgi?id=700097
2013-05-10 19:41:50 +02:00
1290c98c9b shellEntry: Set the input-purpose property for password entries
This way input methods can disable themselves automatically for
entries holding passwords.

https://bugzilla.gnome.org/show_bug.cgi?id=700043
2013-05-10 12:42:18 +02:00
08599afdd4 st-entry: add input purpose and hints
Add input-purpose and input-hints properties to StEntry,
and pass these on to StIMText.

https://bugzilla.gnome.org/show_bug.cgi?id=691392
2013-05-10 13:28:23 +09:00
bb040918e3 st-im-text: add input purpose and hints
Add input-purpose and input-hints properties to StIMText,
and pass these on to GtkIMContext.

https://bugzilla.gnome.org/show_bug.cgi?id=691392
2013-05-10 13:25:06 +09:00
6fac33ea7d st-im-text: remove undefined st_im_text_set_autoshow_im decl
https://bugzilla.gnome.org/show_bug.cgi?id=691392
2013-05-10 13:23:51 +09:00
e16c16b3ef workspace: Only show the close buttons for windows that can close
https://bugzilla.gnome.org/show_bug.cgi?id=699269
2013-05-09 15:35:25 -04:00
1eeeead78f Don't put non-Shell windows in Ctrl+Alt+Tab if they wouldn't be visible
Metacity's Ctrl+Alt+Tab would include X11 windows
with hints like GDK_WINDOW_TYPE_HINT_DOCK and
GDK_WINDOW_TYPE_HINT_DESKTOP (there are more conditions, but that's a
good start). If we're in normal mode, those are visible and it's OK
to display those in the Ctrl+Alt+Tab order, but if we're in the lock
screen or the unlock dialog, they're not visible and it doesn't make
sense to focus them.

Bug: https://bugzilla.gnome.org/show_bug.cgi?id=699862
Signed-off-by: Simon McVittie <simon.mcvittie@collabora.co.uk>
Reviewed-by: Florian Müllner <fmuellner@gnome.org>
2013-05-09 19:31:28 +01:00
046a1a7af8 modalDialog: Show spinner when working
Use the same UI concept from the login screen to show spinners when
the polkit or keyring dialogs are working

https://bugzilla.gnome.org/show_bug.cgi?id=684438
2013-05-09 17:38:21 +02:00
db89648f62 Remove simple uses of ClutterRectangle
https://bugzilla.gnome.org/show_bug.cgi?id=699975
2013-05-09 09:49:12 -04:00
9c839cdc70 dnd: Clean up construction
https://bugzilla.gnome.org/show_bug.cgi?id=699975
2013-05-09 09:49:12 -04:00
938b1ff06a lookingGlass: Remove a useless container
https://bugzilla.gnome.org/show_bug.cgi?id=699975
2013-05-09 09:49:12 -04:00
ecd838bf01 lookingGlass: Remove an unused separator between results
At one time, this may have had styling, but it's not there now,
and I don't see a reason to add it back.

https://bugzilla.gnome.org/show_bug.cgi?id=699975
2013-05-09 09:49:12 -04:00
855a31ff25 components: Allow cancelling of dialog between prompts
Some callers of the keyring prompt keep the dialog up while
processing the prompt. Allow the user to cancel the prompt
while in this state.

This is propagated to the caller, who can cancel the operation
in question when this occurs.

https://bugzilla.gnome.org/show_bug.cgi?id=682830
2013-05-09 05:58:55 +02:00
7464add904 Update libgvc 2013-05-08 21:59:41 -04:00
7514607129 network: don't use active connections that are in invalid states
Only ACTIVE or ACTIVATING connections are important when deciding
what icon to show, don't fallback on any, possibly invalid or deactivating,
active connection object.

https://bugzilla.gnome.org/show_bug.cgi?id=676285
2013-05-08 23:58:53 +02:00
12a1d7b38d keyring: Fix button disabled when prompting for confirmation
https://bugzilla.gnome.org/show_bug.cgi?id=696304
2013-05-08 22:17:43 +02:00
d6fe008b2c st-shadow: Fix offset shadow offscreen rendering
The shadows are currently rendered by painting the actor we want to
apply shadow on, in an offscreen buffer. The problem is that when this
actor has an allocation padding (ie allocation that isn't at 0x0
relatively to its parent), this padding is added within the offscreen
buffer and as a result the shadow rendering is truncated because the
offscreen buffer size is the size of the allocation box, not the
allocation box + padding.

This patch reposition the actor at 0x0 with rendering it by changing
the initial transformation matrix when rendering the actor offscreen.

https://bugzilla.gnome.org/show_bug.cgi?id=698301
2013-05-08 16:17:02 +01:00
d920da6624 loginDialog: Adjust logoBin to modalDialog changes
With modalDialogs' backgroundStack using a BinLayout now, we need
to set approriate expand flags on the iconBin.

https://bugzilla.gnome.org/show_bug.cgi?id=699877
2013-05-08 00:07:04 +02:00
e2c86cef47 modalDialog: Replace Shell.Stack with layout manager
Commit e98eb57e3e added flags to expand the dialog's background
stack, which works fine with the current clutter-1.16 branch, but
breaks on clutter-1.14 (as shipped with GNOME 3.8).
Using an St.Widget with a Clutter.BinLayout fixes this, and is more
modern Clutter usage.

https://bugzilla.gnome.org/show_bug.cgi?id=699877
2013-05-08 00:07:03 +02:00
8e270dc246 osdWindow: Allow popup to grow if necessary
The popup currently has a fixed size based on monitor size. As a result,
the popup's content may overflow if its minimum size is larger than the
popup size. To prevent this, use min-width/min-height for the popup size
so that the popup can grow if necessary.

https://bugzilla.gnome.org/show_bug.cgi?id=696523
2013-05-07 21:34:26 +02:00
22b6a25408 loginDialog: Remove logo in upper left corner
With optional branding now being shown below the user list, we can
remove the unloved instance in the upper left corner ...

https://bugzilla.gnome.org/show_bug.cgi?id=694912
2013-05-07 20:52:58 +02:00
cde695d903 loginDialog: (Optionally) show logo below user list
The optional logo on the login screen is currently shown in the
top bar, which is not only a rather unprominent position, it also
gives the wrong suggestion of a clickable element.
Newer designs call for the logo to be shown horizontally centered
at the bottom of the screen, so implement that instead.

https://bugzilla.gnome.org/show_bug.cgi?id=694912
2013-05-07 20:52:58 +02:00
e98eb57e3e modalDialog: Always use a stack for the background
Currently a system modal dialog's actor hierarchy depends on whether
events should be blocked while the dialog is shown or not. Change
it to always contain a stack, to allow subclasses to add additional
background elements.

https://bugzilla.gnome.org/show_bug.cgi?id=694912
2013-05-07 20:52:58 +02:00
11d997c42b shell-js: Adapt to new gjs interface 2013-05-07 14:35:34 -04:00
eab1ab0fac sessionMode: Remove initial-setup mode
Now that we have external session modes, we can simply
make gnome-initial-setup bundle its own mode.
2013-05-07 14:27:10 -04:00
660b801775 st-bin: Delegate popup-menu signal to child if we have one
This makes it much easier to implement correct popup-menu behavior
in the case of nested bins.

This fixes the context menu key in application search results when a
result has focus.

https://bugzilla.gnome.org/show_bug.cgi?id=699800
2013-05-07 05:14:58 -04:00
eb80503bcc st-theme-node: Make sure that two NULL paint states are not equal
The comment clearly intended that for this to be the case, but a typo
prevented this from actually being done. This fixes the focused state
of the search field not working more than once.

https://bugzilla.gnome.org/show_bug.cgi?id=699799
2013-05-07 02:51:41 -04:00
14ceb10555 dateMenu: do not show "Open Calendar" button with no installed calendar application
https://bugzilla.gnome.org/show_bug.cgi?id=697725
2013-05-06 16:06:33 +01:00
d9a4688e98 panelMenu: Attempt to navigate menus only if we can't navigate inside the menu
This enables key navigation in the calendar.

https://bugzilla.gnome.org/show_bug.cgi?id=667434
2013-05-03 23:20:02 -04:00
092586c931 focus-manager: Add navigate_from_event method
This will be used to implement key navigation inside the calendar without
having to listen to key events manually.

https://bugzilla.gnome.org/show_bug.cgi?id=667434
2013-05-03 23:20:02 -04:00
31478e9fb4 calendar: Ensure clicked calendar item retains key focus
The date actors get destroyed and recreated on every date change which drops
key focus for the selected date. Restore key focus in such a case, but only
when the selected date was actually clicked. Whenever the next/prev month
buttons code is used (for scrolling, mouse click, or keyboard click), have
the corresponding button grab focus. Changing months currently causes the
calendar to update twice as the eventSource gets changed, so key focus gets
lost if it is on a date when the month changes.

https://bugzilla.gnome.org/show_bug.cgi?id=667434
2013-05-03 23:20:02 -04:00
cc7630c236 theme: Add focus styles for the calendar menu
https://bugzilla.gnome.org/show_bug.cgi?id=667434
2013-05-03 23:20:02 -04:00
c29810b2f6 dateMenu: Don't override accessible_role inherited from PanelMenu.Button
The menu is keyboard navigable now, so allow it to be advertised as such.

https://bugzilla.gnome.org/show_bug.cgi?id=667434
2013-05-03 23:20:02 -04:00
c1240d3f2c calendar, dateMenu: Allow focus on menu items
https://bugzilla.gnome.org/show_bug.cgi?id=667434
2013-05-03 23:20:01 -04:00
654f1dd055 gdm: Fix regression where domain login hint not shown
Also only keep around realmd while we're actually using it, allow it
to quit if no other clients are active.

https://bugzilla.gnome.org/show_bug.cgi?id=698200
2013-05-03 21:59:57 +02:00
53c609c278 st-theme-node: Add a to_string function for debugging purposes
https://bugzilla.gnome.org/show_bug.cgi?id=697274
2013-05-03 15:56:05 -04:00
a2fcbb7e65 st-widget: Cache two paint states
In most cases, we'll transition between two states on hover / focus.
Instead of recalculating and repainting our resources on state change,
simply cache the last state when we transition.

https://bugzilla.gnome.org/show_bug.cgi?id=697274
2013-05-03 15:56:05 -04:00
db14dc973a Revert "screenshield: Remove box-shadow"
This reverts commit 98e74dfd38.

https://bugzilla.gnome.org/show_bug.cgi?id=697274
2013-05-03 14:44:04 -04:00
c29aaa047d st-theme-node: Move some allocation-independent textures back to the theme node
The background image, background image shadow and border image are
allocation-indepedent, so we can keep these in the node. Given that these are
are likely cached in the StTextureCache, the slight increase in code complexity
may not be worth caching these textures and materials -- we might be better off
just computing when we need to paint.

https://bugzilla.gnome.org/show_bug.cgi?id=697274
2013-05-03 14:44:03 -04:00
49064ed56d st: Move the theme node drawing state to StWidget
This ensures that two widgets sharing the same theme node won't trample
on each other's prerendered materials if the actors are of different
sizes. This also tries to be very careful to share as much as possible
during a transition.

This has the side effect that if a widget changes state a bunch of times,
we won't cache every state. Since we expect that state changes are
infrequent and that most cases we'll be able to use the texture cache
to do most of the heavy lifting, this cost is much more insignificant
than rendering a number of different actors with the same theme node
and different sizes.

https://bugzilla.gnome.org/show_bug.cgi?id=697274
2013-05-03 10:50:30 -04:00
72bc46d339 st-theme-node-drawing: Move most of the cached paint state to another struct
Since we now share theme nodes between, we shouldn't cache the paint state
across all nodes. As a first step towards putting this in the actor, split
out the state into another structure. Keep it in the theme node for now
so that we don't make too many changes in one commit.

It's possible that some of these pieces of drawing state could be shared
between theme nodes. For the sake of simplicity, assume that none of them
are shared or should be shared. A future commit could identify those that
could be shared and move them back into the theme node.

https://bugzilla.gnome.org/show_bug.cgi?id=697274
2013-05-03 10:50:30 -04:00
40b895d16b st-theme-node-drawing: Depend less on the paint state in some helper functions
We want to put the paint state in the actor rather than in the theme
node, as having two actors with different sizes but the same theme node
is now much less efficient.

https://bugzilla.gnome.org/show_bug.cgi?id=697274
2013-05-03 10:50:30 -04:00
33cad9a824 ctrlAltTab: Use a symbolic icon for desktop windows
The nautilus icon sticks out pretty badly among the symbolic
icons we use for other desktop components. This commit finds
windows of type DESKTOP, and uses the video-display-symbolic
icon for them.
https://bugzilla.gnome.org/show_bug.cgi?id=697914
2013-05-03 10:26:00 -04:00
e96d9e0ea1 st-icon: check if gicon is null before unref.
Commit a6b49fe7d6
introduced a check for equality of priv->gicon vs gicon
and unref then return early if so. But if both are null,
we should not unref gicon. Only return.

https://bugzilla.gnome.org/show_bug.cgi?id=698863
2013-05-02 09:51:32 +02:00
ed63bda932 Updated Slovenian translation 2013-05-01 20:34:15 +02:00
18107d567d Bump version to 3.9.1
Update NEWS.
2013-04-30 23:58:35 +02:00
608818fa9f Only recognize common url schemes in notification messages
Before the fix, the message tray highlighted all urls containing "://", even
invalid ones. This change fixes this by have the message tray highlight only
the urls with http, https, ftp schemes.

Credit goes to: Phuong Vu, Liye Fu, Monica Chelliah, Owen Taylor

https://bugzilla.gnome.org/show_bug.cgi?id=661225
2013-04-29 11:22:44 -04:00
9ae2440ec1 Remove LayoutManager::fullscreen-changed
Since we now have global.screen::in-fullscreen-changed, remove the
duplicate signal. To prevent ordering problems in connecting to
this signal, make inFullscreen a property-function of a new Monitor
object rather than a data property we tack on to a Rectangle object.

https://bugzilla.gnome.org/show_bug.cgi?id=649748
2013-04-29 11:17:29 -04:00
ef9c50e63a StIcon: use g_signal_connect_object() for safety
Prevent a crash in case an icon is destroyed before the texture
finishes loading, and something else is keeping the texture alive
for any reason.

https://bugzilla.gnome.org/show_bug.cgi?id=696720
2013-04-29 00:19:59 +02:00
5d50b08351 Updated Lithuanian translation 2013-04-28 13:37:01 +03:00
123fc19c4e configure: Bump NetworkManager requirement
Since commit 871c28aeeb, NMGtk is no longer optional, so
bump the requirement accordingly.

https://bugzilla.gnome.org/show_bug.cgi?id=699075
2013-04-27 22:51:15 +02:00
a9058e471c WorkspacesView: set the actual geometry when creating workspaces
If we created a workspace after showing the view, we would never
set the geometry on it, which would cause an exception in the
window layout code and leave the DND state tracking in an undefined
state.

https://bugzilla.gnome.org/show_bug.cgi?id=699029
2013-04-27 15:09:19 +02:00
bd1f48d9fe Updated Czech translation 2013-04-27 01:55:09 +02:00
66ab4d217d Updated Czech translation 2013-04-26 19:19:39 +02:00
7a8b392607 network: Remove some genericism in the wireless code
This was presumably originally used in multiple places, but it's
not anymore, so un-generify the code here.

https://bugzilla.gnome.org/show_bug.cgi?id=698918
2013-04-26 12:24:03 -04:00
bfdf069d2d network: Rename "apObj" to "network"
We put these "access point objects" in "this._networks" and
"this._activeNetwork", so let's rename it. This also makes
the fact that each "access point object" can contain multiple
access points a tiny bit less confusing.

https://bugzilla.gnome.org/show_bug.cgi?id=698918
2013-04-26 12:18:39 -04:00
b4b00a48d9 network: Remove explicit autoconnection code
NM is now a lot smarter about dealing with automatic connections, so just
create an empty connection and pass it to it. The only places where NM
requires connection settings is where we require explicit setup: Bluetooth
DUN, WPA-Enterprise and WWAN/VPN. These cases are already handled by
gnome-control-center, where complex configuration is handled, so remove
the automatic connection management for now and just let NM handle it.

https://bugzilla.gnome.org/show_bug.cgi?id=698918
2013-04-26 12:16:06 -04:00
e5f226612e network: Use activeApChanged to get the initial active network
https://bugzilla.gnome.org/show_bug.cgi?id=698918
2013-04-26 12:16:06 -04:00
20619ad3c1 network: Fix some splice mishaps
Calling splice() without a second argument removes all of the elements
after the provided index, not just one.

https://bugzilla.gnome.org/show_bug.cgi?id=698918
2013-04-26 12:16:06 -04:00
37dce7d4c3 network: Deduplicate some similar code
https://bugzilla.gnome.org/show_bug.cgi?id=698918
2013-04-26 12:16:06 -04:00
a67b82e730 network: Remove some dead code
We cannot possibly reach createActiveConnectionItem unless we have
an active network, so the plain unreative menu item cannot ever
be created.

https://bugzilla.gnome.org/show_bug.cgi?id=698918
2013-04-26 12:16:05 -04:00
e0b8ad7911 network: Remove UNKNOWN security type
It's unused.

https://bugzilla.gnome.org/show_bug.cgi?id=698918
2013-04-26 12:16:05 -04:00
dbb39d366e network: Properly disconnect from the state-changed signal
The destroy signal never gets emitted, so we need to properly
disconnect manually when we destroy the wrapper.

https://bugzilla.gnome.org/show_bug.cgi?id=698918
2013-04-26 12:16:05 -04:00
e1de36398d network: Remove some unused helpers
https://bugzilla.gnome.org/show_bug.cgi?id=698918
2013-04-26 12:16:05 -04:00
871c28aeeb network: Require NMGtk
Enough time has passed that we can safely depend on NMGtk in the new
network implementation.

https://bugzilla.gnome.org/show_bug.cgi?id=698918
2013-04-26 12:16:05 -04:00
c84dc6254d PopupMenuManager: Close active menu when another one opens
We already do this when navigating between menus via mouse or
keynav, but miss cases where a menu is opened by other means,
for instance via a keyboard shortcut.

https://bugzilla.gnome.org/show_bug.cgi?id=686756
2013-04-26 17:50:06 +02:00
60cb1ad7c5 panel: Change openAppMenu() to a toggle action
It makes sense to allow closing the app menu with the same shortcut
that is used to open it, so make it a toggle action and allow it
TOPBAR_POPUP mode.

https://bugzilla.gnome.org/show_bug.cgi?id=686756
2013-04-26 17:32:54 +02:00
4a5ff5dcfb panel: Add keybinding mode for top bar popups and use it
Allow some keybindings to still work while a top bar menu is open
by assigning it a keybinding mode.

https://bugzilla.gnome.org/show_bug.cgi?id=698938
2013-04-26 17:32:53 +02:00
5c40307745 popupMenu: Allow setting grabHelper params for PopupMenuManager
Currently all keybindings are disabled while some popup menu is open.
However some keybindings may still be useful in some cases, so expose
GrabHelper's modal params parameter to allow specifying a keybinding
mode for particular menus.

https://bugzilla.gnome.org/show_bug.cgi?id=698938
2013-04-26 17:32:53 +02:00
39426f03e6 shellDBus: Fix Eval() return value when disabled
Eval() is expected to return a boolean success value and a string result.
However when the function is disabled (via the development-tools setting),
we return null for the latter which is not a valid string value.
Return an empty string instead.

https://bugzilla.gnome.org/show_bug.cgi?id=698959
2013-04-26 17:32:52 +02:00
92e5d2b8f5 backgroundMenu: Ignore releases when using long-press
Otherwise, a release from the long-press will be seen as a
button event that should close the menu.

https://bugzilla.gnome.org/show_bug.cgi?id=697203
2013-04-26 11:23:37 -04:00
09aa59f98b layout: Correct hot corner barriers in RTL layouts
https://bugzilla.gnome.org/show_bug.cgi?id=698884
2013-04-26 11:23:37 -04:00
c9c1c89a27 dateMenu: append .desktop to evolution strings
Since that is what the actual app_id's are.
2013-04-26 16:54:45 +10:00
96b76709e9 session: Remove the clock from initial setup mode
If you're in the initial setup mode you're not likely to have the
correct time displayed.
2013-04-25 11:52:02 +01:00
ca3107e21b Updated Greek translation 2013-04-25 08:12:32 +03:00
2e4f223207 Updated Malayalam Translation 2013-04-25 08:26:22 +05:30
17df668186 remoteSearch: support serialized GIcons
Since [1], GIO supports generic serialization and deserialization of a
GIcon into a GVariant. This is also implemented by GdkPixbuf and could be
used instead of our homegrown code for it.

This commit adds support to another 'icon' key in the metas dictionary
returned by applications for it. The previous 'gicon' and 'icon-data'
keys are still parsed and supported as before, but are now deprecated.

[1]
https://git.gnome.org/browse/glib/commit/?id=c16f914b40c749b938490a4e10a3c54ec1855c42

https://bugzilla.gnome.org/show_bug.cgi?id=698761
2013-04-24 15:54:31 -04:00
aef70152de Keyboard: clear currentSource after destruction
StLabel doesn't like that we set its properties after destructions,
and this would happen in currentInputSourceChanged() at the end,
when setting the ornament.
2013-04-24 21:47:03 +02:00
e0252f35be workspace: Sort windows when we calculate slots
This ensures that windows don't change positions when we lay
them out again when expanding the workspace switcher.

https://bugzilla.gnome.org/show_bug.cgi?id=698776
2013-04-24 14:10:30 -04:00
0f47534766 Forgot ia.po file 2013-04-24 15:55:12 +02:00
54feaa67e8 [l10n] Add Interlingua translation 2013-04-24 15:53:43 +02:00
64ecfa49eb layout: Ensure that the hotCorners array is always indexable by monitor number
messageTray relies on indexing the hot corners array by monitor number,
so we should make this a guarantee.

https://bugzilla.gnome.org/show_bug.cgi?id=698513
2013-04-23 19:14:01 -04:00
fdae613b14 messageTray: Take modal grabs for the context menu and notification boxpointer
As the context menu and notification boxpointer can only appear if we already
take a modal grab, grabFocus will have problematic results if the focus does
somehow change.

https://bugzilla.gnome.org/show_bug.cgi?id=698483
2013-04-23 16:33:13 -04:00
c57c08b2c6 popupMenu: Fix a bad rebase
The wrong patch got pushed accidentally here
2013-04-23 16:33:13 -04:00
d2103995cb popupMenu: Remove some now-unused code
We don't have any sections with separators, so don't bother
tracking them.

https://bugzilla.gnome.org/show_bug.cgi?id=698427
2013-04-23 15:50:36 -04:00
196fb0f16e popupMenu: Add and use GtkMenuTracker to build the remote menu
This simplifies the code required to build remote menus and
put all the items in the right place, and makes us share our
implementation with GTK+.

https://bugzilla.gnome.org/show_bug.cgi?id=698427
2013-04-23 15:50:36 -04:00
c0afe7260a popupMenu: Create and insert menu items that don't have actions yet
Instead of recreating the entire model, which can be expensive... but
keep them insensitive for now. This also matches what GTK+ does.

https://bugzilla.gnome.org/show_bug.cgi?id=698427
2013-04-23 15:50:36 -04:00
099c8703ae popupMenu: Always use a PopupMenuItem
By this point, we'll know we'll always have a PopupMenuItem.

https://bugzilla.gnome.org/show_bug.cgi?id=698427
2013-04-23 15:50:36 -04:00
b03e480dbf popupMenu: Use a checkmark for boolean items
This matches GTK+'s styling, and it makes most boolean switches
look more natural, as a lot of booleans are not meant for hardware
switches.

https://bugzilla.gnome.org/show_bug.cgi?id=698427
2013-04-23 15:50:36 -04:00
8430353389 popupMenu: Add a check ornament
This will be used to replace switches in the remote menu

https://bugzilla.gnome.org/show_bug.cgi?id=698427
2013-04-23 15:50:36 -04:00
a123ec94ef popupMenu: Use a unicode character for the ornament
This makes it easy to replace the dot with another label in the future.
Change the allocation logic, as text layout is more complicated than
simple icon logic.

https://bugzilla.gnome.org/show_bug.cgi?id=698427
2013-04-23 15:50:36 -04:00
4a2f54f6ff popupMenu: Define the dot next to the menu as an "ornament"
We want to remove switches in remote menus, so make way for
a checkmark ornament for the popup menu item.

https://bugzilla.gnome.org/show_bug.cgi?id=698427
2013-04-23 15:35:55 -04:00
b5c85eaeca messageTray: Fix a regression from 1b13509
It seems this rename somehow got lost in a rebase.
2013-04-22 19:22:33 -04:00
8b3b91d78d workspace: Invalidate the current layout when windows are added or removed
If windows are removed or added, we shouldn't keep the old layout, as it's
not valid anymore. If windows are removed, this is especially bad, as the
rows contain references to the removed window objects, causing crashes.

https://bugzilla.gnome.org/show_bug.cgi?id=698622
2013-04-22 18:33:34 -04:00
e1de3973fe workspace: Recalculate window positions when we have no layout
If for any reason the current layout is invalidated, queue a
reposition.

https://bugzilla.gnome.org/show_bug.cgi?id=698622
2013-04-22 18:33:34 -04:00
c1993a6ffc overview: Fix XDnD
I accidentally removed this import which some XDnD code depends upon.
2013-04-22 18:24:02 -04:00
ab26fc438a workspace: only scale a layout row when it doesn't fit
Instead of applying an additional scale factor to all the rows in the
layout, only do it for those rows that don't fit.
This avoids the visual distraction of resizing a row when there's no
need to.
2013-04-22 18:06:09 -04:00
c37259b01d workspace: Lay out windows based on the real allocation
Instead of doing an entire recalculation of window positions when
sliding the thumbnails box, simply recalculate the position and scale
with basic aspect ratio math. This also ensures that windows won't
miraculously swap positions, even if we reposition windows while the
thumbnails box is expanded.

https://bugzilla.gnome.org/show_bug.cgi?id=694469
2013-04-22 14:01:00 -04:00
bde8cc3285 workspace: Split out window repositioning logic and rename
Split out the part that moves the window clones around from
the part that calculates the window clone positions, and rename
both methods so that the overall meaning is more clear.

https://bugzilla.gnome.org/show_bug.cgi?id=694469
2013-04-22 14:01:00 -04:00
59ba5504d0 workspace: Calculate the window slots when we reposition
Repositioning will eventually be separated from recalculation
to accomodate two different geometries, so we'll need to do
the padding and area manipulation in two different areas.

https://bugzilla.gnome.org/show_bug.cgi?id=694469
2013-04-22 14:01:00 -04:00
65eb5a3d05 workspace: Separate out spacing/padding code
This will be used when we introduce the second geometry.

https://bugzilla.gnome.org/show_bug.cgi?id=694469
2013-04-22 14:01:00 -04:00
b925322e9e workspace: Make room for a second geometry to keep track of
As we want to eventually track two geometries, we need to rename
our very plain "_x, _y, _width, _height". While we could just prefix
them, I think that stuffing them in an object makes more sense.

At the same time, make the variable and method name more descriptive
by adding such a prefix, as well as a bit of documentation.

https://bugzilla.gnome.org/show_bug.cgi?id=694469
2013-04-22 14:01:00 -04:00
f0c2ad00f8 workspacesView: Calculate the workspaces geometry ourselves
To ensure that we don't recalculate window layouts when zooming
in or out, we need to always pass the full geometry. This will
break window repositioning when we zoom back in; for the purposes
of commit clarity, this breaks this feature for now. It will be
added back soon.

https://bugzilla.gnome.org/show_bug.cgi?id=694469
2013-04-22 14:00:59 -04:00
fc53a25a4c overviewControls: Add an accessor for the visible-width property
To add a geometry that's independent of the slide factor of the workspace,
we need to get this from outside the sliding control.

https://bugzilla.gnome.org/show_bug.cgi?id=694469
2013-04-22 14:00:59 -04:00
58872d162b AppDisplay/FrequentView: filter out hidden applications
Filter out all applications which have the NoDisplay, Hidden or
Not/OnlyShowIn bits, as those are not meant to be launched directly.
This also allows the user to filter apps from the frequent view
using alacarte.

https://bugzilla.gnome.org/show_bug.cgi?id=696949
2013-04-22 19:11:17 +02:00
30f1b8f02a Updated Spanish translation 2013-04-22 17:52:13 +02:00
ee4f199a9f AppMenuButton: Improve handling of signals
If for some reason an extension needs to destroy the AppMenu object,
currently it is not possible to do this cleanly due to these signals
remaining connected.

https://bugzilla.gnome.org/show_bug.cgi?id=698531
2013-04-22 10:14:48 +10:00
e3957f3bac workspacesView: Make setGeometry take a rectangle
This cleanup will be more important in the future,
but for now, we can simply pass a monitor.

https://bugzilla.gnome.org/show_bug.cgi?id=694469
2013-04-20 08:33:26 -04:00
2506673514 workspacesView: Minor cleanup
https://bugzilla.gnome.org/show_bug.cgi?id=694469
2013-04-20 08:33:26 -04:00
7cb12015fd workspacesView: Fix indentation
https://bugzilla.gnome.org/show_bug.cgi?id=694469
2013-04-20 08:33:26 -04:00
caaac9b9ec workspace: Do window slot computing in three steps
This ensures that we have the correct Y value when sorting
windows.

https://bugzilla.gnome.org/show_bug.cgi?id=694469
2013-04-20 08:33:26 -04:00
1dac4d00c4 workspace: Abort relayouting much earlier
This means that the code for computeAllWindowSlots is a bit
cleaner, which will help in the future.

https://bugzilla.gnome.org/show_bug.cgi?id=694469
2013-04-20 08:33:26 -04:00
b41902f4df workspace: Only create one strategy
Now that we don't have any other strategies but the unaligned
one, we only need to construct it once.

https://bugzilla.gnome.org/show_bug.cgi?id=694469
2013-04-20 08:33:26 -04:00
ada70dd683 workspace: Remove more dead code
By the time zoomToOverview is called, an animation will
always be in progress.

https://bugzilla.gnome.org/show_bug.cgi?id=694469
2013-04-20 08:33:26 -04:00
7e5d8a8d54 workspace: Make positionWindows private
It's not used outside, and it's going to be broken up soon.

https://bugzilla.gnome.org/show_bug.cgi?id=694469
2013-04-20 08:33:26 -04:00
e981cae27c workspace: Don't save the current layout
This was saved so that doing something which called relayout
but only changed the area rectangle would simply be needed to
recompute window scaling parameters. With the new overview
relayout, the flow control changed, it turns out that the
current layout is always cleared. Remove this for now, and we'll
put in a different strategy for this.

https://bugzilla.gnome.org/show_bug.cgi?id=694469
2013-04-20 08:33:26 -04:00
d7c377c229 workspace: Use Workspace.WindowPositionFlags.NONE in another case
https://bugzilla.gnome.org/show_bug.cgi?id=694469
2013-04-20 08:33:26 -04:00
8772edcd33 overview: Move the group construction to the controls manager
Instead of creating a bunch of random actors and then passing
them off to the controls manager, let the controls manager
construct them. This leaves the controls manager in charge
of the ordeal.

https://bugzilla.gnome.org/show_bug.cgi?id=694469
2013-04-20 08:33:25 -04:00
254740cf68 [l10n] Updated Turkish translation 2013-04-20 00:44:55 +03:00
c3775c0f56 st-theme-node: Disable transitions if animations-enabled is off
https://bugzilla.gnome.org/show_bug.cgi?id=698391
2013-04-19 14:34:06 -04:00
5ecf40e967 panel: show spinner animation for busy applications
When the active application signals its busy state, we now will show a
spinner on the panel, next to the application name.

https://bugzilla.gnome.org/show_bug.cgi?id=697207
2013-04-19 13:54:56 -04:00
45026df4bd shell-app: track the busy state of GApplications
Watch for property changes on the org.gtk.Application interface, and
transition the state to BUSY when the corresponding property is flipped
on.

https://bugzilla.gnome.org/show_bug.cgi?id=697207
2013-04-19 13:54:56 -04:00
b8830f4a09 shell-app: don't recreate a session proxy every time
Cache the connection inside the ShellAppRunningState structure instead.

https://bugzilla.gnome.org/show_bug.cgi?id=697207
2013-04-19 13:54:56 -04:00
811ee1d989 Revert "messageTray: Don't show the context-menu when the tray isn't open"
This reverts commit 1bce210c51.
2013-04-19 13:38:41 -04:00
8c32102e99 messageTray: Move the notification actor out of the tray
Putting the notification actor in the tray actor has caused a lot
of various bugs and glitches over the years related to syncing the
two, fizzling out events, and so on. It's a much simpler model if
we consider the notification actor and tray to be separate widgets.

As a side effect, this makes the context menu not pop up when we
right-click on notifications.

https://bugzilla.gnome.org/show_bug.cgi?id=695800
2013-04-19 13:38:41 -04:00
1b135095c7 messageTray: Move hover tracking to the notification widget
This does nothing while the tray is active, so it doesn't make sense
to track it on the tray. This also makes the code a lot easier to read,
with notification behavior being labeled "notification" rather than
"tray".

https://bugzilla.gnome.org/show_bug.cgi?id=695800
2013-04-19 13:38:41 -04:00
becf4396c9 Disable naked Super keybinding if sticky
With sticky keys, users should be able to press and release a
modifier and then press a key to activate a modifier-key combination.
Activating the overview on the Super key release keeps these
users from using keyboard shortcuts involving the Super modifier.

The solution implemented here is to simply disable the Super-release
binding if sticky keys are enabled. It is still possible to go
to the overview by using Super-S or Alt-F1.
https://bugzilla.gnome.org/show_bug.cgi?id=685974
2013-04-19 10:25:28 -04:00
929e066506 Add a toggle-overview keybinding
This is a new, regular keybinding for going to the overview.
The default binding is Super-S, which goes well with Super-A
for going to the application grid.
This is separate from the existing panel-main-menu keybinding,
so that we can keep Alt-F1 opening the main menu in classic mode.

https://bugzilla.gnome.org/show_bug.cgi?id=698251
2013-04-18 20:34:41 -04:00
355ad9ac2c Updated Galician translations 2013-04-18 03:23:16 +02:00
2cda0472bd Bump version to 3.8.1
Update NEWS.
2013-04-16 21:25:54 +02:00
4710753700 panel: Pick up spinner icon from CSS
In order to use a different spinner image in classic mode (or any
other mode specific style), get it from CSS rather than hardcoding
a particular image.

https://bugzilla.gnome.org/show_bug.cgi?id=693688
2013-04-16 19:10:40 +02:00
2b439ef209 theme-node: Add lookup_url/get_url() methods
Similar to the existing generic getter methods, add lookup functions
for URL properties like the standard background-image/border-image
properties.

https://bugzilla.gnome.org/show_bug.cgi?id=693688
2013-04-16 19:10:40 +02:00
2fc76e6d9e panel: Pass a full pathname to AnimatedIcon
For classic mode, we want to use a different styling for the spinner,
so we will pick up the image filename from CSS to make use of mode
specific styling. As the CSS will give us a full pathname, adapt the
API to take a full pathname instead of building it inside AnimatedIcon
from the passed basename.

https://bugzilla.gnome.org/show_bug.cgi?id=693688
2013-04-16 19:10:40 +02:00
ff544450a5 Revert "classic: Invert the spinner color"
This reverts commit f358bb1a96.
2013-04-15 18:40:00 -04:00
b4590da686 Revert "Add an OSD for sticky modifiers"
This reverts commit 96994721ef.
2013-04-15 18:39:52 -04:00
6c4be0311a Revert "Make it possible to always show the a11y status"
This reverts commit 6ce79c62eb.
2013-04-15 18:39:44 -04:00
4a2cdc20f0 Add networkAgent to initial-setup mode
We want to be able to set up e.g. a WEP connection on the
network page of gnome-initial-setup, so we need a network
agent in the session.
2013-04-15 18:05:57 -04:00
6ce79c62eb Make it possible to always show the a11y status
A switch for this is part of the redesign for the universal
access control-center panel.

https://bugzilla.gnome.org/show_bug.cgi?id=698001
2013-04-15 17:48:47 -04:00
96994721ef Add an OSD for sticky modifiers
This commit adds an OSD that displays which modifiers are
currently latched or locked. This is commonly used together
with sticky keys.
https://bugzilla.gnome.org/show_bug.cgi?id=647711
2013-04-15 17:48:47 -04:00
f358bb1a96 classic: Invert the spinner color
https://bugzilla.gnome.org/show_bug.cgi?id=693688
2013-04-15 17:48:47 -04:00
1bce210c51 messageTray: Don't show the context-menu when the tray isn't open
https://bugzilla.gnome.org/show_bug.cgi?id=697709
2013-04-15 12:49:30 -04:00
8a3c8d1b1c Add a note to POTFILES.in about the gvc submodule 2013-04-14 21:59:03 +02:00
08b224de1f Updated Vietnamese translation 2013-04-13 17:53:05 +10:00
86c92c37d2 st-im-text: Override ClutterText's cursor_event to set cursor position
This is both more efficient and accurate than doing it from the paint
vfunc.

https://bugzilla.gnome.org/show_bug.cgi?id=697722
2013-04-10 17:27:28 +02:00
07053c3df7 shellEntry: Remove close method
GrabHelper already takes care of putting the key focus back on the
widget that had it when the grab was established.

ShellEntry's close() has recently become harmful since it's now called
from the menu's destroy() method and that is called in the entry's
destroy handler which means that this._entry might no longer be valid
at close() time.

https://bugzilla.gnome.org/show_bug.cgi?id=697560
2013-04-08 21:02:03 +02:00
4fb33c9b09 dnd: Set the position and scale on the drag actor before it's reparented
We need to do this to ensure that it gets the correct scale, as otherwise
the transform that we retrieve will be incorrect.

https://bugzilla.gnome.org/show_bug.cgi?id=697504
2013-04-08 14:57:49 -04:00
5e6a25c3c2 dnd: Set the scale on the drag actor
The actor is the same as the drag actor in this case, but we tend to
set properties on the drag actor and get them from the actor elsewhere
in this codepath. Make this consistent.

https://bugzilla.gnome.org/show_bug.cgi?id=697504
2013-04-08 14:57:49 -04:00
e14ef4a294 theme: remove excessive padding from notification buttons
Set the button width manually and remove the huge horizontal
padding we had. This means that long button labels don't get cut
off.

https://bugzilla.gnome.org/show_bug.cgi?id=664411
2013-04-08 19:25:46 +02:00
8b659f0f4c St: support -st-natural-width/height CSS properties
It is sometimes desirable to specify the size of a box from CSS without
changing the minimum-width too.
This property implements that.

https://bugzilla.gnome.org/show_bug.cgi?id=664411
2013-04-08 19:25:45 +02:00
a6395c95d4 Small fix in Serbian translation 2013-04-08 15:29:57 +02:00
98e74dfd38 screenshield: Remove box-shadow
This causes very low performance in some situations (like multiscreen). Proper
fixes are too invasive at this point (3.8.1) so lets just remove the shadow
for now and add it back later once he have fixed it.

https://bugzilla.gnome.org/show_bug.cgi?id=697274
2013-04-07 22:06:46 +02:00
1d728186db recorder: Enable the keybinding for all modes
There's not really a good reason to restrict the screen recorder
to normal mode and overview, so allow it for all keybinding modes.

https://bugzilla.gnome.org/show_bug.cgi?id=696200
2013-04-07 21:13:30 +02:00
2499f2ed80 AppSwitcherPopup: Activate only the selected window if any
If there's an explicitly selected window thumbnail we should bring up
only that particular window instead of all the application's windows.

https://bugzilla.gnome.org/show_bug.cgi?id=697480
2013-04-07 21:05:38 +02:00
963905adcd runDialog: don't destroy on close
It's a singleton like the endSessionDialog

https://bugzilla.gnome.org/show_bug.cgi?id=697435
2013-04-06 11:10:30 -04:00
7c21ab0985 Revert "Revert "modalDialog: Destroy dialogs on close""
This reverts commit 007820b7c1.

(I only meant to revert that in my local branch)
2013-04-06 10:53:11 -04:00
77d3712261 popupMenu: close menu on destroy
This fixes a crash if a background menu is open
and the background changes.

https://bugzilla.gnome.org/show_bug.cgi?id=697432
2013-04-06 10:47:16 -04:00
0376f22d41 background: stop monitoring file after removing from cache
https://bugzilla.gnome.org/show_bug.cgi?id=697395
2013-04-06 10:45:53 -04:00
007820b7c1 Revert "modalDialog: Destroy dialogs on close"
This reverts commit 3dd6113a0a.
2013-04-06 10:28:47 -04:00
dc98711477 backgroundMenu: drop cursor object with rest of menu actors
https://bugzilla.gnome.org/show_bug.cgi?id=697395
2013-04-06 09:45:45 -04:00
e98c6ff31b background: when updating image remove old one from cache
We're removing the new content from the cache instead of the
old content.

This commit fixes that.

https://bugzilla.gnome.org/show_bug.cgi?id=697395
2013-04-06 09:44:51 -04:00
9eae74357a background: don't leak background objects during quick changes
We currently let some backgrounds "fall through the cracks" if
a bunch of change notifications come in at once.

This commit fixes that.

https://bugzilla.gnome.org/show_bug.cgi?id=697395
2013-04-06 09:44:50 -04:00
1ee88a2878 screenShield: Ensure we destroy background container widgets
We may accidentally leak a widget when monitors change while
locked otherwise. This is especially bad because we put a
box-shadow on this widget.

https://bugzilla.gnome.org/show_bug.cgi?id=697300
2013-04-04 19:16:49 -04:00
14189e6827 shellEntry: Destroy entry menus when the entry actors are destroyed
This ensures that a menu and manager aren't sticking around.

https://bugzilla.gnome.org/show_bug.cgi?id=697295
2013-04-04 15:04:14 -04:00
3dd6113a0a modalDialog: Destroy dialogs on close
It turns out that we never destroyed modal dialogs when closing
them, causing them to still linger in the scene graph even when
there were no references to them in the JS. The one case where
we don't want to destroy modal dialogs after being closed is
endSessionDialog, so provide a parameter that allows classes
to override this behavior.

https://bugzilla.gnome.org/show_bug.cgi?id=697295
2013-04-04 15:04:14 -04:00
ae5cdea5af Updated Norwegian bokmål translation 2013-04-03 13:31:44 +02:00
3c1f389e25 backgroundMenu: destroy menu when actor is destroyed
This fixes a leak.

https://bugzilla.gnome.org/show_bug.cgi?id=697119
2013-04-02 23:20:15 -04:00
284d821233 Updated Basque language 2013-04-02 21:59:27 +02:00
78272e5592 RemoteSearch: don't sort the last explicitly sorted provider last
It's a confusing semantic, and keeping it causes bugs in the control
center panels.

https://bugzilla.gnome.org/show_bug.cgi?id=694974
2013-04-02 21:49:57 +02:00
098bc41083 Updated Polish translation 2013-04-02 15:58:59 +02:00
c837090282 Update Simplified Chinese translation 2013-04-02 00:55:01 +08:00
cdc1678e6f Updated Belarusian translation. 2013-04-01 15:22:56 +03:00
2a3d76b0cc Update Arabic translation 2013-03-31 23:08:29 +02:00
29e89491de Updated British English translation 2013-03-29 16:07:45 +00:00
d63947aec4 Silly typo in calendar.js
https://bugzilla.gnome.org/show_bug.cgi?id=696436
2013-03-29 16:15:19 +01:00
512f0a67fb shellDBus: Fix SetActive(false) for ScreenSaver DBus API
Someone changed .unlock() to deactivate() for the obvious symmetry
with activate(), but forgot to update it here.

https://bugzilla.gnome.org/show_bug.cgi?id=696585
2013-03-29 10:13:24 -04:00
a7cd4657f5 loginManager.js: Check for logind, not for systemd
It is possible to build systemd without logind, in which case
/sys/fs/cgroup/systemd would still exist. Check for /run/systemd/seats instead,
as recommended by systemd upstream.

For details, see:
<https://mail.gnome.org/archives/desktop-devel-list/2013-March/msg00092.html>

https://bugzilla.gnome.org/show_bug.cgi?id=696252
2013-03-29 11:00:52 +01:00
bf02cde598 altTab: Move input focus to target window before dropping the grab
We chain up on _finish() to drop the grab and destroy the switcher
popup but we should activate the target window first because dropping
the grab results in the previously focused windows getting a focus in
event immediately followed by the focus out event from the target
window activation which we can easily avoid.

https://bugzilla.gnome.org/show_bug.cgi?id=696259
2013-03-28 16:17:07 +01:00
b7dbb35546 Updated Aragonese translation 2013-03-28 11:11:10 +01:00
6fd5f0e3de popupMenu: Fix another child popup regression
When switching menus, we need to make sure we close all existing
grabs, not just the first one.

https://bugzilla.gnome.org/show_bug.cgi?id=695859
2013-03-27 16:32:42 -04:00
e6469df065 popupMenu: Fix a missed rename
this._activeMenu was changed to this.activeMenu, but this wasn't
updated by accident.

https://bugzilla.gnome.org/show_bug.cgi?id=695859
2013-03-27 16:32:42 -04:00
d61fe357f6 layout: Clip the window group when doing the startup animation
This ensures that when we have windows that are already visible,
like desktop icons, they don't fly across the screen from what
seems to be hyperspace to get into view.

https://bugzilla.gnome.org/show_bug.cgi?id=696323
2013-03-27 16:30:00 -04:00
965dd2ab67 Bump version to 3.8.0.1
Update NEWS.
2013-03-27 12:28:56 -04:00
78e011d558 background: fix multimonitor background placement in overview
This is like the previous commit but for monitor index, instead
of effects.

https://bugzilla.gnome.org/show_bug.cgi?id=696712
2013-03-27 12:13:47 -04:00
76590d6c69 background: fix vignette in overview
We no longer apply background effects when loading backgrounds.

This commit fixes that.

https://bugzilla.gnome.org/show_bug.cgi?id=696712
2013-03-27 12:09:02 -04:00
16dc6ce41a Bump version to 3.8.0
Update NEWS.
2013-03-26 22:29:57 +01:00
1dadcee5c9 background: don't choke on huge slide show durations
if a slideshow file has a really large duration we'll currently
throw an exception.

This bug is aggravated by the fact that some versions of
gnome-desktop use UINT_MAX as a sentinel value to mean,
"don't ever update slide".

This commit treats durations that would overflow as infinitely
long.

https://bugzilla.gnome.org/show_bug.cgi?id=696157
2013-03-26 17:02:05 -04:00
bed3bb45f7 background: don't load the same image more than once concurrently
If a background gets requested from the cache while it's still
being loaded from an earlier call, then there will be two concurrent
loads of the same file.

That concurrency is mitigates the effectiveness of the cache and
also causes leaks.

This commit consolidates file loads so that concurrency doesn't
happen.

https://bugzilla.gnome.org/show_bug.cgi?id=696157
2013-03-26 17:02:05 -04:00
18d850d7d6 workspaceThumbnail: always destroy bgManager when destroyed
Right now we only destroy the bgManager object when the
workspaceThumbnail is explicitly destroy with its destroy()
method.

This commit makes sure bgManager gets destroyed when the
workspaceThumbnail actor is destroyed without calling
destroy().

https://bugzilla.gnome.org/show_bug.cgi?id=696157
2013-03-26 17:02:05 -04:00
f920fd8b6f background: properly disconnect background signals
BackgroundManager connects to the changed signal in
the backgrounds it manages.

The signal ids for the changed signal connectionss are stored
as state on the background manager object.

If the background being managed changes while the manager
is still loading the old background, then the signal id
variable can get out of sync with the background object being
managed.

This commit ties the signal id to the background objects themselves,
so there is no opportunity for them to desynchronize.

https://bugzilla.gnome.org/show_bug.cgi?id=696157
2013-03-26 17:02:05 -04:00
b9da6d9ef6 background: When updating actor content, evict old content from cache
Normally backgrounds get evicted from the cache when their actor is
destroyed.  If the actor changes content without destroying itself,
though, we should evict the old content from the cache, too.

https://bugzilla.gnome.org/show_bug.cgi?id=696157
2013-03-26 17:02:04 -04:00
3be489c69e background: always add content to cache
We currently only add the first instance of a background
to the cache.  This means if the actor associated with that
background is destroyed, the content will be evicted and
it will need to get reloaded, even if it's already loaded
on another actor.

This commit ensures every content gets added to the cache.

https://bugzilla.gnome.org/show_bug.cgi?id=696157
2013-03-26 17:02:04 -04:00
cab092aad7 Updated Telugu Translation 2013-03-26 18:53:24 +05:30
0451cba343 Updated LT translation 2013-03-26 12:40:32 +01:00
12334cd9ad Updated Odia Language along with FUEL implementation 2013-03-26 16:09:27 +05:30
9d18a2dff4 screenShield: Go back to a shorter animation when locking manually
Increasing it was mostly to paper over animation jerkiness which
shouldn't be a problem anymore since we removed the desaturate and
blur effects.

https://bugzilla.gnome.org/show_bug.cgi?id=696380
2013-03-26 09:27:24 +01:00
869e1dc241 loginManager: listen to the correct object path
Use GetSession() to get the valid object path for the current
XDG_SESSION_ID.

https://bugzilla.gnome.org/show_bug.cgi?id=696287
2013-03-25 20:53:13 -04:00
b878f3fc4a Restore visibility of windows in initial-setup mode
gnome-initial-setup runs a regular X window, so we need to show it.

https://bugzilla.gnome.org/show_bug.cgi?id=696235
2013-03-25 18:22:48 -04:00
2aeabcc38b Updated Telugu Translations 2013-03-25 21:53:19 +05:30
94778c0dac layout: Fix opening the overview for xdnd
In the (no barriers) fallback case handleDragOver has somehow
ended up being turned into a nop and thus breaking xdnd
overview opening.

Fix that by calling _toggleOverview() when a xdnd source triggers
it.

https://bugzilla.gnome.org/show_bug.cgi?id=696447
2013-03-25 14:43:22 +01:00
390491b37a dateMenu: Re-add special-casing of evolution-calendar
When commit 724a2bd7 changed the way to determine the default
calendar app, it dropped all special handling of evolution.
Unfortunately we still need it to not end up with the default
mail component, so add it back.

https://bugzilla.gnome.org/show_bug.cgi?id=696432
2013-03-25 10:43:30 +01:00
f8ea825577 grabHelper: Consider events that release the grab handled
Currently, if a button-press event results in releasing the last modal
grab (e.g. clicks outside the grabbed actors), we don't consider the
event handled and allow its emission to continue. If we consider
dismissing a grab as an action of its own, any additional action
triggered by the same event becomes an unexpected side effect.
Tweak the capture handler accordingly.

https://bugzilla.gnome.org/show_bug.cgi?id=696422
2013-03-25 10:43:29 +01:00
f77ad7d184 Completed for Malayalam 2013-03-25 14:58:55 +05:30
e467a53def Uploaded Ukranian 2013-03-25 08:00:49 +02:00
9cd2e12915 l10n: Update Japanese translation 2013-03-25 12:10:39 +09:00
627a6587b4 Update Danish translation 2013-03-24 14:57:24 +01:00
9b3b419f7a initial-setup: Add battery icon
There is no reason why there shouldn't be a battery icon in initial-setup,
knowing how much battery you have left is useful in during setup as well.

This also fixes an exception in the lock screen caused by the combined icon
not finding the battery one.

https://bugzilla.gnome.org/show_bug.cgi?id=696212
2013-03-24 09:39:34 +01:00
f37ed44837 Updated Dutch translation 2013-03-23 23:35:30 +01:00
dddc333407 Updated Belarusian translation. 2013-03-23 23:04:07 +03:00
75714fb784 Added slovak translation 2013-03-23 21:01:35 +01:00
5a6342a5fe Updated kn translations 2013-03-23 19:00:18 +05:30
e07379b406 Revert "Remove non-existing file to make intltool happy"
This reverts commit 54bfe34273.

see 8f1df14ae7
-> It very much exists. Do a 'git submodule init && git submodule update'.
2013-03-23 13:35:47 +01:00
54bfe34273 Remove non-existing file to make intltool happy 2013-03-23 12:49:36 +01:00
bef876e4b4 Updated kn translations 2013-03-23 14:44:25 +05:30
6ef9ab6a0b st-scroll-view-fade: Don't fade out gradient
The changes from commit b4f5f1e461 and b394d184cc increased the
instructions required for the fade fragment shader. This is over the limit
for some hardware (like intel gen3), which causes the driver to fallback
to software rendering for the shader. The result is that painting a scrollview
that has a fade effect takes around 30 (!!) seconds.

So lets go back to the old effect for 3.8 until we find a solution.

https://bugzilla.gnome.org/show_bug.cgi?id=696404
2013-03-22 19:56:13 +01:00
53ffdd8d68 Updated Russian translation 2013-03-22 21:03:27 +04:00
ba198034cb screenShield: Remove blur and desaturation from lock screen
There's large performance issues with both the blur and desaturation
that make the screen shield hard to use on slower computers, and this
has always been a temporary stopgap until the user can pick a different
image for the lock screen.

https://bugzilla.gnome.org/show_bug.cgi?id=696322
2013-03-22 12:36:43 -04:00
58e66b9e1b autorunManager: Mark the Removable Devices source as resident
This ensures that it can't be removed when we clear the message
tray.

https://bugzilla.gnome.org/show_bug.cgi?id=696199
2013-03-22 12:36:43 -04:00
ac7efcb43a hindi translation 2013-03-22 18:00:09 +05:30
b514b9ec7c Tamil Translations Updated 2013-03-22 17:23:06 +05:30
f69942eb35 Updated Odia Translation. 2013-03-21 18:18:27 +05:30
f28827f625 Updated Marathi Translations 2013-03-21 09:27:22 +05:30
f20a6b3635 Updated Hungarian translation 2013-03-20 16:40:57 +01:00
7f7ff3ce3e L10N: Updated Persian Translation 2013-03-20 09:22:41 +03:30
0abb0756ba [l10n] Added Tadjik translation 2013-03-19 20:43:49 +01:00
070ed9396c Update French translation 2013-03-19 11:43:32 +01:00
307004a455 Bump version to 3.7.92
Update NEWS.
2013-03-19 00:12:27 +01:00
d097327bd8 loginDialog,unlockDialog: Give user time to read messages
Right now, if multiple messages come in, they just sort of
clobber each other.

This commit sets up a message queue, and introduces pauses
long enough for the user to hopefully be able to read those
messages.

https://bugzilla.gnome.org/show_bug.cgi?id=694688
2013-03-18 19:01:30 -04:00
f72f501931 unlockDialog: ignore resets after success
GDM sends a reset signal after verification succeeds
so that a user-switched login screen can prepare for
the next time it's going to be used.

The unlock screen treats resets as failures, though.
This means, on success, we're emitting "failed" and
clearing any last second messages.

This commit changes the unlock code to ignore resets from
GDM after successful verification.

https://bugzilla.gnome.org/show_bug.cgi?id=694688
2013-03-18 19:01:30 -04:00
e9584cfcab gdm: don't clear user-verifier on reset automatically
Right, the common code between the login screen and
the unlock screen handles clearing the user verifier
when GDM sends a reset.

We don't actually always want to clear the messages on
reset in the unlock case, though, so doing it implicitly
is problematic.

This commit moves the clear() call from the common code
to the specific reset handlers.

https://bugzilla.gnome.org/show_bug.cgi?id=694688
2013-03-18 19:01:30 -04:00
aec0e75d73 grabHelper: Ensure we reset ignoreRelease when we drop our event handler
Since we drop our event handler, we won't see ever the release event
from the button press, so unset the flag so it doesn't muck with the
next time somebody takes a grab.

https://bugzilla.gnome.org/show_bug.cgi?id=696102
2013-03-18 16:49:12 -04:00
a6b4d49454 Use Mutter fullscreen window tracking for hiding chrome and auto-minimize
Getting fullscreen window tracking right in GNOME Shell turned out to
be very hard, because it depended on details both how Mutter handled
fullscreen windows and the exact timing of that. Fullscreen tracking
and auto-minimization of fullscreen windows that lose their fullscreen
status has thus been implemented in Mutter: use that.

https://bugzilla.gnome.org/show_bug.cgi?id=649748
2013-03-18 14:44:10 -04:00
1cc726593e runDialog: Ignore expected IO errors while enumerating $PATH
$PATH might contain non-existent or non-directory entries. Ignore
those error cases.

https://bugzilla.gnome.org/show_bug.cgi?id=696064
2013-03-18 18:00:12 +01:00
5001bd8810 Don't use ClutterX11TexturePixmap directly to embed docked windows
Previously when a client requests that a window should be docked the
shell would reparent the socket window onto the stage's window and
then use ClutterX11TexturePixmap to get a texture to represent the
window. This will not work if Clutter is no longer using the X11
winsys for example if it becomes its own display server. Instead this
patch leaves the socket window as a child of the root window and lets
mutter create a MetaWindow out of it. If Mutter is acting as a display
server then this mechanism will still work via the headless x server.

The ShellGtkEmbed instance now registers for notification of the
‘window-created’ signal of the display so that it can find the
MetaWindow that gets created to represent the socket window. When this
window is found it is prevented from being displayed on the screen by
setting the actor's opacity to 0. An input shape is then set on the
window to prevent it receiving any input.

Instead of being a subclass of ClutterX11TexturePixmap, ShellGtkEmbed
is now a subclass of ClutterClone. When the MetaWindow is found for
the socket window the clone's source is set to the invisible actor for
the window so it can be displayed in the panel as before.

The ShellEmbeddedWindow no longer needs to know what the stage is
because it no longer reparents the socket window. Therefore the
ShellTrayManager doesn't need to know the stage either so
shell_tray_manager_manage_stage has been replaced with just
shell_tray_manager_manage_screen.

https://bugzilla.gnome.org/show_bug.cgi?id=693438
2013-03-18 16:49:47 +00:00
46aa70aa09 searchDisplay: Add an accessible name to the provider icon
https://bugzilla.gnome.org/show_bug.cgi?id=696007
2013-03-18 12:12:40 -04:00
41cf447c45 searchDisplay: Label search result actors for accessibility
https://bugzilla.gnome.org/show_bug.cgi?id=696007
2013-03-18 12:12:40 -04:00
b328fd7aed layout: Move window_group visibility to the layout
This ensures that windows are hidden in the screen shield and in gdm.

https://bugzilla.gnome.org/show_bug.cgi?id=695747
2013-03-18 03:57:36 -04:00
917a24580b Updated gujarati file 2013-03-18 12:33:14 +05:30
af36182879 [l10n] Updated Catalan (Valencian) translation 2013-03-17 19:52:19 +01:00
160c721afc Revert "WorkspacesView: set the clip when setting the geometry"
This reverts commit d3ab367fcd.
Pushed by mistake.
2013-03-17 19:39:58 +01:00
ee0040ef1c WorkspaceThumbnails: animate the right workspace when creating by DND
To create a new workspace by dropping on the placeholder, we move all
the windows down one workspace and then wait for _checkWorkspaces() to
automatically create the empty one at the end.
This means that, from the implementation POV, the new workspace is not
the one created by DND but it's the last one, and this detail was exposed
in the UI because the animation was applied on that one.
Fix that by starting the animation manually from the DND code, and then
blocking the animation from happening when the new workspace is created
with a flag.

https://bugzilla.gnome.org/show_bug.cgi?id=685285
2013-03-17 19:35:15 +01:00
9e60a55dd7 WorkspacesView: don't fail when doing window DND from the last workspace
DND of windows has a lot of side effects, including the possibility of
current workspace disappering from under our feet. We need to account
for that when trying to activate it.

https://bugzilla.gnome.org/show_bug.cgi?id=685285
2013-03-17 19:35:14 +01:00
57f27572ae DND: don't cancel a drag if the actor is destroyed inside acceptDrop
This happens in the case of Workspace/WorkspaceThumbnail: they call
meta_window_change_workspace_by_index(), which fires window-removed
on the old workspace, thus destroying the window clone.

https://bugzilla.gnome.org/show_bug.cgi?id=685285
2013-03-17 19:26:13 +01:00
ceae032112 Workspace/WorkspaceThumbnail: fix Clutter warnings from stacking code
Windows can be restacked at any time, including when the stackAbove
property of the window clone is being dragged, and thus parented to
the uiGroup. To do stacking properly, we need to skip it for the duration
of the drag, and sync it again at the end (which is already done by
mutter because of the workspace change)

https://bugzilla.gnome.org/show_bug.cgi?id=685285
2013-03-17 19:26:13 +01:00
d3ab367fcd WorkspacesView: set the clip when setting the geometry
Fixes clipping the windows when you open the overview with the workspace
switcher expanded and then shrink it.

https://bugzilla.gnome.org/show_bug.cgi?id=694092
2013-03-17 19:26:12 +01:00
0ceefb48c8 Workspace: don't place new windows at random locations
Instead, have them grow from the center of their assigned slots.

https://bugzilla.gnome.org/show_bug.cgi?id=695582
2013-03-17 19:26:09 +01:00
a9815ae1e9 ScreenShield: don't animate arrows if the user is idle
If the user is idle, it is very likely the monitor is off too, so let's
save some power by avoiding animations noone is going to see.

https://bugzilla.gnome.org/show_bug.cgi?id=690857
2013-03-17 19:20:43 +01:00
79140fd132 Updated Belarusian translation. 2013-03-17 16:00:14 +03:00
6c0f48ce25 layout: Only emit fullscreen-changed if things actually changed
This prevents the message tray from doing a lot of work that
it doesn't need to.
2013-03-16 16:50:30 -04:00
da7ab7057d Updated Korean translation 2013-03-16 23:59:08 +09:00
499ae609dd appDisplay: Use a custom LayoutManager for the AllView stack
ClutterBinLayout uses the maximum width/height of all children in
size requests and positions children according to the expand/align
properties. This means that the vertical position of folder popups
is not considered in size requests, so if the main view is smaller
than a folder popup's height and offset, the popup will be truncated
and/or mispositioned. Fix those cases by using a custom LayoutManager
that behaves like ClutterFixedLayout for height requests and like
ClutterBinLayout otherwise.

https://bugzilla.gnome.org/show_bug.cgi?id=694371
2013-03-15 22:41:54 +01:00
e78b3644e4 login-dialog: Increase top/bottom padding
The login dialog has a maximum height set to prevent the user list
from growing indefinitively. However this approach fails if the
monitor height is smaller as said maximum, so add some additional
top/bottom padding to make sure there's some whitespace above/below
the user list in that case as well.

https://bugzilla.gnome.org/show_bug.cgi?id=685851
2013-03-15 19:35:18 +01:00
ce6b39bd1c Revert "doap: add myself to doap file"
I just added myself temporarily to work around
mango.

This reverts commit ec8dfb0cad.
2013-03-15 14:02:31 -04:00
603f528b47 theme: Adjust border-radius for focus indication to match hover
Commit 7b3a689aad changed the border-radius of the hover effect
to match the prelight effect, but not the focus indication, so
the focused item now changes corners on hover. Fix this by using
a consistent border-radius.

https://bugzilla.gnome.org/show_bug.cgi?id=691578
2013-03-15 00:04:03 +01:00
5fecd07045 background: Look at the duration per-step
We need to look at the duration of the current step of the slideshow
to determine when to next queue an event, rather than the full slideshow
duration.

https://bugzilla.gnome.org/show_bug.cgi?id=695882
2013-03-14 17:48:07 -04:00
8edd7ad32e background: Fix a bug with empty animations
Arrays always evaluate to true, even empty arrays,
so we need to check the length to make sure we have
no files.

https://bugzilla.gnome.org/show_bug.cgi?id=695882
2013-03-14 17:48:07 -04:00
3a27d0b849 background: Keep the state of key frame files on Animation
This gets us from a mix of pure getters and class state
to just class state.

https://bugzilla.gnome.org/show_bug.cgi?id=695882
2013-03-14 17:48:07 -04:00
b351536dee background: Rename animationUpdate to updateAnimation
This is more consistent.

https://bugzilla.gnome.org/show_bug.cgi?id=695882
2013-03-14 17:48:07 -04:00
eeea8559b6 background: Fix math to calculate the interval from the duration
The math here before was incorrect. This is still wrong, as we're
looking at the total duration of the animation rather than the next
step.

https://bugzilla.gnome.org/show_bug.cgi?id=695882
2013-03-14 17:48:07 -04:00
17591117c1 background: Fix updating existing images that need loading
Due to weird and strange JS scoping semantics, if we are in a
callback, "i" won't be captured and when the callback is called,
we'll have the wrong index, causing addImage to be called instead
of updateImage.

https://bugzilla.gnome.org/show_bug.cgi?id=695882
2013-03-14 17:48:07 -04:00
c4470fd1e6 background: Set the correct index on images
The pattern is underneath, so we need to add one. It turns out
that ClutterGroup doesn't care about that, but we need to do this
to remove deprecations in mutter.

https://bugzilla.gnome.org/show_bug.cgi?id=695882
2013-03-14 17:48:07 -04:00
45fc7ec01f st-scroll-view-fade: Don't use return in the shader
Returing from main() makes llvmpipe unhappy (produces black output color),
so rework the logic to avoid the return.

https://bugzilla.gnome.org/show_bug.cgi?id=695607
2013-03-14 22:43:14 +01:00
dc54472ca5 messageTray: Remove some dead positioning code
We don't modify the tray box's anchor position, so this shouldn't
ever get called.

https://bugzilla.gnome.org/show_bug.cgi?id=695800
2013-03-14 17:18:20 -04:00
4b3bf05aaf screenShield: Only release logind inhibitor on suspend
To make sure that the screen shield is shown before suspending, we
take a logind inhibitor and release it when the screen shield is
shown. As the screen shield is not only shown on suspend, we can end
up releasing the inhibitor independently from suspending (lock, idle),
in which case the screen might not be locked when we do suspend.
To fix, only release the inhibitor after showing the screen shield
when we are about to suspend.

https://bugzilla.gnome.org/show_bug.cgi?id=693708
2013-03-14 19:09:15 +01:00
ec014a7ecf main: Do not export DBus interfaces before initializing the UI
Since commit 7cdb75e7ce, initializing UI is deferred until the session
mode has been loaded. However DBus is still initialized immediately,
which means that for DBus methods that access properties in Main, there
is now a window between the method being exposed on the bus and the
method being ready to be called. At least g-s-d grabbing global keybindings
is likely to fall in this window on session startup, and almost guaranteed
when regrabbing bindings after a shell restart.
To fix, defer initializing DBus as well.

https://bugzilla.gnome.org/show_bug.cgi?id=694321
2013-03-14 17:28:31 +01:00
a8c87f3bab overview: Only keep ourselves in the overview on button events
This prevents using Control to quickly launch a new instance of
an app in search results or with keyboard navigation.

https://bugzilla.gnome.org/show_bug.cgi?id=695801
2013-03-14 12:05:42 -04:00
b39e76200a overview: Don't use a captured-event handler to detect the control key
captured-event handlers are easily messed up by an earlier event
handler capturing the event. Instead, use the current Clutter event
and check for the state of that.

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

https://bugzilla.gnome.org/show_bug.cgi?id=695801
2013-03-14 12:05:42 -04:00
01deb9ef7d status/keyboard: Check if this._currentSource is valid on activation
this._currentSource might be invalid so check for that to prevent an
exception and still switch to the newly activated source.

https://bugzilla.gnome.org/show_bug.cgi?id=695841
2013-03-14 14:56:28 +01:00
5c89568cca status/keyboard: Always hide the old source panel indicator label
When we switch into an invalid input source we hide the panel
indicator and return early but we were not hiding the previously
active source label and its menu item dot and thus when switching
again to a third input source we would end up showing 2 overlapping
labels in the panel and the menu would have 2 entries with a dot.

https://bugzilla.gnome.org/show_bug.cgi?id=695841
2013-03-14 14:56:27 +01:00
6119b44746 Improve tracking of fullscreen windows
It's possible in some corner cases for the status of the topwindow
to change and make it not fullscreen without ::restacked being
changed. One way that it could happen with the old code was if the
layer of the top window changed from NORMAL to FULLSCREEN.

Change the logic not to look at the layer, which is a function of
Mutter's *intended* stacking, rather than the *actual* stacking,
which is what ::restacked gives you. Instead, look at the top
portion of the stack, down to the first non-override-redirect
window, and see if their are any monitor-sized windows there.

Connect to changes on the top portion of the stack, so we know
if conditions change.

https://bugzilla.gnome.org/show_bug.cgi?id=649748
2013-03-14 08:06:08 -04:00
7af4a7bf88 Updated Czech translation 2013-03-14 12:58:19 +01:00
04e4d69662 IBus.Property's state needs to be kept in IBus.PropType.NORMAL
https://bugzilla.gnome.org/show_bug.cgi?id=695824
2013-03-14 09:41:58 +01:00
a87e0f028a screenShield: Also unlock on Return/Enter
Since that's what I always did before, having it ignore those is
driving me mad.

https://bugzilla.gnome.org/show_bug.cgi?id=686740
2013-03-13 13:27:34 -04:00
31774a7711 theme: Add :focus style for app-view-controls
https://bugzilla.gnome.org/show_bug.cgi?id=694951
2013-03-13 10:42:22 +01:00
a32f27a2aa ctrlAltTab: Fix external DOCK windows
We always add external DOCK windows to the ctrl-alt-tab switcher,
e.g. separate dock applications or nautilus' desktop windows.
Since commit 1f46a0dc26, all items in the switcher are expected
to set a proxy parameter, but the aforementioned code was not
updated accordingly.

https://bugzilla.gnome.org/show_bug.cgi?id=695395
2013-03-13 10:18:16 +01:00
805a409318 keyring: Don't replace an already running prompter
* Prompters have state, and cancelling an already prompter will
   cause prompts that are in progress to fail.
 * In addition allow replacement of our shell prompter for debugging
   purposes.

https://bugzilla.gnome.org/show_bug.cgi?id=695485
2013-03-13 07:57:16 +01:00
42d45bd14a searchDisplay: Scroll the search results when using keynav
This makes keynav for search results much more usable.

https://bugzilla.gnome.org/show_bug.cgi?id=689681
2013-03-12 19:29:48 -04:00
5870709fbc appDisplay: Move ensureIconVisible logic to util, make it more generic
In particular, make it work if we have multiple parents, like in the
search case.

https://bugzilla.gnome.org/show_bug.cgi?id=689681
2013-03-12 19:29:47 -04:00
0fdb7430ff Updated Portuguese translation 2013-03-12 22:38:14 +00:00
4771f80d6f messageTray: Remove the tray left timeout when showing a new notification
https://bugzilla.gnome.org/show_bug.cgi?id=695659
2013-03-12 11:58:53 -04:00
91405583fd messageTray: Fizzle out hover notifications where the values are the same
notify::* doesn't guarantee that the value has changed, only that it
may have been. We need to ensure that we track the old value to make sure
we don't do things like overwrite timeouts if they already exist.

https://bugzilla.gnome.org/show_bug.cgi?id=695659
2013-03-12 11:58:52 -04:00
f864303aac messageTray: Remove summary state management
Now that the tray is modal, the summary is tied to the tray,
and we don't need to have separate states for the tray and
summary. This also removes the nearly invisible opacity tween
on the summary items when opening the message tray.

https://bugzilla.gnome.org/show_bug.cgi?id=695659
2013-03-12 11:58:52 -04:00
e5ebf4a2b2 messageTray: Remove locking
The only way that locking happens is with when the summary box
pointer is active. As it can only happen if the summary state
is active, it's impossible for a notification to be expired,
or the summary to be hidden while it's showing.

https://bugzilla.gnome.org/show_bug.cgi?id=695659
2013-03-12 11:58:52 -04:00
725a36e37a messageTray: Use the bottom monitor's fullscreen state for rate limiting
It makes more sense to use the monitor the tray is on, rather than the
primary monitor. This also matches us with whether we can open the tray
from a barrier/dwell or not.

https://bugzilla.gnome.org/show_bug.cgi?id=695659
2013-03-12 11:58:51 -04:00
78dacfa865 messageTray: Fix idle tracking condition
The user is active if they have less than IDLETIME, not if
they have more than it.

This fixes an issue where notifications may never go away.

https://bugzilla.gnome.org/show_bug.cgi?id=695659
2013-03-12 11:58:51 -04:00
16547fb3c2 messageTray: Clean up code a tiny bit
Remove some duplication by reusing a method.

https://bugzilla.gnome.org/show_bug.cgi?id=695659
2013-03-12 11:58:51 -04:00
9eeabf79f9 messageTray: Fix style
https://bugzilla.gnome.org/show_bug.cgi?id=695659
2013-03-12 11:58:51 -04:00
81b71cc143 messageTray: Remove a few leftover variables
https://bugzilla.gnome.org/show_bug.cgi?id=695659
2013-03-12 11:58:50 -04:00
8301acd4d6 grabHelper: Use a round trip for focusing the default window
We may release the focus grab at any time, so it's not guaranteed
we'll be in event processing. In particular, hovering over and out
of a notification will cause this to happen, as the notification
is hidden on a timeout.

https://bugzilla.gnome.org/show_bug.cgi?id=695659
2013-03-12 11:58:50 -04:00
f80afb1755 [l10n] Updated Estonian translation 2013-03-12 17:53:56 +02:00
71c6b60054 [l10n] Updated Catalan translation 2013-03-11 23:02:08 +01:00
95a1b874d8 Update libgnome-volume-control to master
Pick a few fixes, including a memory leak fix.

https://bugzilla.gnome.org/show_bug.cgi?id=695656
2013-03-11 22:28:55 +01:00
07de96ede9 recorder: Disconnect stage signals when not recording
This causes unnecessary work, so only connect them when needed i.e
while recording.

https://bugzilla.gnome.org/show_bug.cgi?id=695650
2013-03-11 20:11:18 +01:00
209014b083 screenShield: Forward key presses to tne entry when raising the shield
https://bugzilla.gnome.org/show_bug.cgi?id=686740
2013-03-11 15:08:21 -04:00
127f10e7a8 screenShield: Don't wait until the dialog is loaded before opening it
If we wait asynchronously, key presses while the shield is opening
will be dropped in the void.

https://bugzilla.gnome.org/show_bug.cgi?id=686740
2013-03-11 15:08:21 -04:00
67615a0cbc screenShield: Remove bump on key press
Any key press of a character-emitting key will now raise the shell.
Note that the key press will not be forwarded to the entry yet.

https://bugzilla.gnome.org/show_bug.cgi?id=686740
2013-03-11 15:08:20 -04:00
dde20f0c76 screenShield: Move opening of screen shield to key press
This makes the screen shield much more responsive.

https://bugzilla.gnome.org/show_bug.cgi?id=686740
2013-03-11 15:08:20 -04:00
ddced79475 recorder: port to XInput2
Pointer tracking is broken when the pointer is over the stage input
area.  This is apparently fallout from mutter going to XInput2.

This commit changes the mouse event handling code to also use XInput2.

https://bugzilla.gnome.org/show_bug.cgi?id=695324
2013-03-11 13:11:08 -04:00
e80795b7f0 recorder: don't bother selecting for enter/leave/motion events
Mutter already handles that for us.

https://bugzilla.gnome.org/show_bug.cgi?id=695324
2013-03-11 12:35:38 -04:00
080337a4e8 Updated German translation 2013-03-11 14:25:21 +01:00
884af3c986 style: Prevent dash from overlapping content
In order to center the view selector, the dash has been moved to a
separate layer, which means that it will overlap the app picker and
search results if the available width is smaller than the maximum
width that the content will request. Fix this by adding enough
horizontal padding to account for the width the dash will have at
its largest icon size.

https://bugzilla.gnome.org/show_bug.cgi?id=695471
2013-03-11 13:33:05 +01:00
edc95b2cb7 Updated Indonesian translation 2013-03-11 16:47:37 +07:00
96239e95ec userMenu: Add translation comments
Seems like "console" vs. "remote" in the "other users" dialog  confuses some
translators so add a comments to clarify their meaning.

https://bugzilla.gnome.org/show_bug.cgi?id=695601
2013-03-11 10:35:42 +01:00
331ad76675 Updated German translation 2013-03-10 23:59:06 +01:00
29152d3022 layout: Disable the overview hotcorner when we have a full-screen window open
For the same reasons that we disable the tray barrier, we should
disable hot corners as well -- when users have a full-screen game
open, we shouldn't allow overview activation by the hotcorner.

https://bugzilla.gnome.org/show_bug.cgi?id=694997
2013-03-10 12:00:09 -04:00
73fa4b1cbd messageTray: Don't open by pressure when we have a full-screen window open
When we have a full-screen window open, we expect the app to get all of
the chrome, so we should disable the bottom barrier as well.

https://bugzilla.gnome.org/show_bug.cgi?id=694997
2013-03-10 12:00:09 -04:00
7766a91e8c Wanda: so long GNOME 2, and thanks for all the fish.
gnome-panel is going away in 3.8, so we can't rely on it to provide our
friendly and reliable companion. But no regret, because we can ship it
ourselves, and at the same time remove some unnecessary configuration.

https://bugzilla.gnome.org/show_bug.cgi?id=695526
2013-03-10 15:54:40 +01:00
b7bad765aa Updated Latvian translation 2013-03-10 14:56:32 +02:00
5af09a60e9 Main: close the looking glass when we don't have a run dialog
No run dialog means no looking glass either.

https://bugzilla.gnome.org/show_bug.cgi?id=695458
2013-03-08 23:18:31 +01:00
ef623cfbbd Updated Serbian translation 2013-03-08 20:28:31 +01:00
ce5faba185 osdWindow: bump down the OSD window size a bit
Matches what this commit did for g-s-d:
https://git.gnome.org/browse/gnome-settings-daemon/commit/plugins/media-keys?id=fbf3c5aa366ef7212f209e123d4aae315a1a2a8e

https://bugzilla.gnome.org/show_bug.cgi?id=695409
2013-03-07 17:02:41 -05:00
40d9ed535b runDialog: Remove the use of file monitors for file completion
Launching the run dialog to open the looking glass or something
like that shouldn't install a bunch of file monitors that monitor
every IO change to the home and system directories.

Instead, simply scan all the paths when trying to complete.

https://bugzilla.gnome.org/show_bug.cgi?id=695338
2013-03-07 16:35:37 -05:00
d5675765f3 runDialog: Remove tab-completion preloading
This is iffy anyway, since we don't wait for the correct signal.
Just make the user press tab again, like they would do anyway.

https://bugzilla.gnome.org/show_bug.cgi?id=695338
2013-03-07 16:35:37 -05:00
65067c24cc main: Show the greeter dialog when we're prepared
If we don't wait until the stage is mapped, pushing a modal
will fail with an X error.

https://bugzilla.gnome.org/show_bug.cgi?id=694321
2013-03-07 16:35:37 -05:00
e2463cb501 layout: Move showing the stage and the startup-prepared signal
Waiting until we're idle means nothing if we're constructing
complex actors.

https://bugzilla.gnome.org/show_bug.cgi?id=694321
2013-03-07 16:35:36 -05:00
de2f2d7ef1 main: Remove leftover hunk connection to sessionUpdated
We shouldn't connect to sessionUpdated twice.

https://bugzilla.gnome.org/show_bug.cgi?id=694321
2013-03-07 16:35:36 -05:00
7cdb75e7ce Defer initializing UI until after the global session has loaded
https://bugzilla.gnome.org/show_bug.cgi?id=694321
2013-03-07 16:35:36 -05:00
1502e308b8 Updated Slovenian translation 2013-03-07 20:51:04 +01:00
dd1fe68e5c Finnish translation update by Jiri Grönroos 2013-03-07 21:02:09 +02:00
85c1dc5a0c Updated Kazakh translation. 2013-03-07 20:19:30 +06:00
7b3a689aad theme: match the hover effect radius to the prelight effect
Application view: the radius of the corners on the hover effect
should match the radius of the prelight effect that is used for
running apps. Original fix from Bharath Thiruveedula.

https://bugzilla.gnome.org/show_bug.cgi?id=691578
2013-03-07 12:48:38 +00:00
2c28a12cca Updated Uyghur translation
Signed-off-by: Gheyret Kenji <gheyret@gmail.com>
2013-03-07 21:05:47 +09:00
324021fd64 Updated Russian translation 2013-03-07 09:36:24 +04:00
1dceae97c6 Revert "Defer initializing UI until after the global session has loaded"
This reverts commit 0bef925b51.

Just for now.  Jasper has plans to clean up start up eventually.

https://bugzilla.gnome.org/show_bug.cgi?id=694321
2013-03-06 17:26:15 -05:00
0bef925b51 Defer initializing UI until after the global session has loaded
https://bugzilla.gnome.org/show_bug.cgi?id=694321
2013-03-06 14:55:58 -05:00
0ba1f29e40 main: Merge two different session-mode-updated handlers
https://bugzilla.gnome.org/show_bug.cgi?id=694321
2013-03-06 14:34:59 -05:00
3b1e536822 screenShield: Drop fallback implementation
With fallback mode gone, we can no longer rely on gnome-screensaver
being installed. Rather than handling three different cases (GDM,
gnome-screensaver, no lock), disable the lock functionality when
not running under GDM.

https://bugzilla.gnome.org/show_bug.cgi?id=693403
2013-03-06 16:22:07 +01:00
9a83662a18 loginManager: Move UnlockDialog.isSupported() here
With fallback mode dropped, we can no longer rely on gnome-screensaver
to be installed, so we'll have cases where we are unable to lock the
screen. The user menu should not show the "Lock" item in this case,
but as UnlockDialog includes UserMenu, we cannot use the existing check
without creating a circular dependency; move the function to a more
generic place to fix.

https://bugzilla.gnome.org/show_bug.cgi?id=693403
2013-03-06 16:22:07 +01:00
5bcead5b5c build: Really remove build time dependency on tp-logger
Commit 00eb764880 removed the dependency from configure, but kept
the gir include. Remove that as well.

https://bugzilla.gnome.org/show_bug.cgi?id=695272
2013-03-06 16:22:06 +01:00
d8f9502ea2 Updated Traditional Chinese translation(Hong Kong and Taiwan) 2013-03-06 19:07:01 +08:00
020128b9ca background: don't use raise_top() since it's costly
We currently resync the stacking order of the two key frames
every iteration of the animation.  This is costly and unnecessary.

This commit ensures they're stacked properly up front and doesn't
touch them after that.

https://bugzilla.gnome.org/show_bug.cgi?id=694993
2013-03-05 16:03:12 -05:00
1566a4e607 unlockDialog, loginDialog: Connect to the activate signal on the entry
This is the recommended way to connect to an entry's activation binding --
event bubbling is not guaranteed by ClutterText.

https://bugzilla.gnome.org/show_bug.cgi?id=695154
2013-03-05 15:25:11 -05:00
fbd59631ee Assamese translation updated for gnome 3.8 2013-03-05 19:20:22 +05:30
6c36856499 layout: fix errors in fallback HotCorner function 2013-03-05 17:53:59 +11:00
f4cda49988 Bump version to 3.7.91
Update NEWS.
2013-03-05 00:42:25 +01:00
a6b49fe7d6 st-icon: Don't cause a redraw by replacing the icon with itself
Check if the two icons match before setting a new icon, if they
do don't do anything.

Based on patch from Owen Taylor <otaylor@fishsoup.net>

https://bugzilla.gnome.org/show_bug.cgi?id=694988
2013-03-05 00:03:29 +01:00
26966b2bf3 layout: Port the hot corner to pressure barriers
If the X server supports the new barrier features, we should
trigger the overview hot corner with a pressure barrier as well.

https://bugzilla.gnome.org/show_bug.cgi?id=663661
2013-03-04 17:47:48 -05:00
36a7429aa0 overview: Try to do the right thing related to XDnD
Rather than expose a dizzying array of methods related to managing
state that require infecting every user of the overview methods, try
to do the sensible and smart thing internally. Now, the overview
itself tracks when XDND drags start, and simply calling show, hide or
toggle while an XDnD drag is in effect will show the overview, and
will only take the grab until after the XDND drag ends.

https://bugzilla.gnome.org/show_bug.cgi?id=663661
2013-03-04 17:47:47 -05:00
7be1fe09f1 layout: Don't use the corner's position for positioning ripples
The corner may not be there in the future.

https://bugzilla.gnome.org/show_bug.cgi?id=663661
2013-03-04 17:03:24 -05:00
62bf08d323 layout: Put two barriers near every single hot corner
This will make the hot corner easier to hit on multi-monitor
scenarios, and also gives us a convenient set of barriers to
key pressure off of.

https://bugzilla.gnome.org/show_bug.cgi?id=663661
2013-03-04 17:03:24 -05:00
260c082c4e main: defer session update until startup animation
We currently call the session updated handler as soon as
the session modes are read.  This handler sets up keybindings
for leaving the overview (if a user session) and shows the
login dialog (if a gdm session).

We can't do the latter until the stage is mapped because it
takes a grab, and we don't need to do the former until the
user goes into the overview.

This commit defers processing session updates until the
the layout manager says start up is prepared.

It fixes a race condition at login screen startup now that
we don't show the stage right away.

https://bugzilla.gnome.org/show_bug.cgi?id=694321
2013-03-04 16:45:32 -05:00
5e0ff7fd56 main: don't explicitly hide the stage
It's hidden by default, so there's no point in hiding it explicitly.

https://bugzilla.gnome.org/show_bug.cgi?id=694321
2013-03-04 16:45:32 -05:00
aac312ca34 overviewControls: only show chat icon in messages indicator for chats
Only show the chat icon in the new messages indicator when at least one
among the outstanding notifications is a chat.
2013-03-04 16:25:50 -05:00
a90401454a layout: Pass the X/Y coordinates when constructing the corner
There's no guarantee that hot corners will have an actor in
the future -- they may be powered entirely by a barrier.

https://bugzilla.gnome.org/show_bug.cgi?id=663661
2013-03-04 15:50:45 -05:00
5679e6b3a9 layout: Construct the primary monitors's hot corner, too
This cleans up the code considerably, and makes it so that
one path creates all hot corners for all monitors. Why this
wasn't done originally, I have no clue...

The one complication is debouncing if the button and hot corner
are triggered in rapid succession, so we just move this tracking
to the overview.

https://bugzilla.gnome.org/show_bug.cgi?id=663661
2013-03-04 15:50:45 -05:00
194574bb0e layout: Allow multiple barriers to contribute to a PressureBarrier
For the HotCorner case, we want to have to barriers both contribute
to the hot corner pressure, so we can't simply wrap the pressure
barrier.
2013-03-04 15:50:44 -05:00
337d2da38a layout: Move tray-specific event filtration to the user of PressureBarrier
For the HotCorner, we want to have different logic for tossing out
specific events based on the grabbed state, etc. so make us have
to pass in an event filter callback.
2013-03-04 15:50:44 -05:00
df848aa084 layout: Move the keybinding mode to the user of PressureBarrier
For the hot corner case, we want to have the pressure apply both in
and outside of the overview, so we need to move this to the user. At
the same time, use keybinding mode math that's more like what's used
in filterKeybinding.

While it may seem like an abuse of the KeyBindingMode API, it may
become more reasonable if one thinks of the pressure barrier as a
binding of sorts, just applied to the mouse. If a ButtonBinding API
was added to mutter, I think we'd use the existing KeyBindingMode
infastructure there as well.
2013-03-04 15:50:44 -05:00
0cc1615252 layout: Debounce triggering barriers
Ensure that the pointer leaves the barrier before we trigger again.

For the message tray case, this doesn't matter much, as the trigger
won't have any effect after the grab is taken, but in the overview
HotCorner case, this ensures that we don't trigger the overview
transition many times simply by holding pressure against the hot
corner, which is easy to do accidentally.
2013-03-04 15:50:44 -05:00
d4259fa8aa layout: Prevent going into negatives with the pressure
We capped each event to 15px of travel, but we didn't do the same
cap when subtracting events that were too old.
2013-03-04 15:50:43 -05:00
157d7e2b59 Updated Lithuanian translation 2013-03-04 22:45:23 +02:00
ee50904147 Calendar: only show events when configured in Evolution
When no calendars are enabled, hide the events pane completely instead
of showing it empty.

https://bugzilla.gnome.org/show_bug.cgi?id=680083
2013-03-04 18:59:30 +01:00
ec39aa3890 Calendar: dim days from other months, even if they have events
Invert the order of the declarations, so those for calendar-other-month-day
take priority over those for calendar-day-with-events.

https://bugzilla.gnome.org/show_bug.cgi?id=641383
2013-03-04 18:59:30 +01:00
beb0fdf4b8 Calendar: clean up code by always having an event source
Instead of sometimes having an event source and sometimes not, use
the empty event source when the session mode says the calendar is
disabled. This way, the code can assume an event source object and
avoid checks.

https://bugzilla.gnome.org/show_bug.cgi?id=641383
2013-03-04 18:59:30 +01:00
443fe813c3 Calendar: freeze updates when the event source is loading
Check if the event source is currently doing an async call, and prevent
UI updates in that case. This avoids a flash of "No updates" when switching
months.

https://bugzilla.gnome.org/show_bug.cgi?id=641383
2013-03-04 18:59:30 +01:00
69cdc5a9b9 Calendar: force all months at the same height
Add one or two weeks from nearby months to avoid changing the menu height
when navigating it.

https://bugzilla.gnome.org/show_bug.cgi?id=641383
2013-03-04 18:59:30 +01:00
dac54a6019 Migrate from libgnome-keyring to libsecret
* See: https://live.gnome.org/GnomeGoals/LibsecretMigration

https://bugzilla.gnome.org/show_bug.cgi?id=679851
2013-03-04 18:47:52 +01:00
e5ca53e56c status/keyboard: Don't allow the switcher in the message tray
SwitcherPopup relies on being able to pushModal(), setting the stage
input mode to FULLSCREEN, and then doing regular event processing on
the actor it adds to uiGroup. But MessageTray uses GrabHelper which
sets up a 'captured-event' handler on the stage and thus gets all
events itself.

This, of course, breaks the switcher if it's brought up in the message
tray so, for now, we'll just prevent it from being used there.

https://bugzilla.gnome.org/show_bug.cgi?id=693907
2013-03-04 18:32:49 +01:00
7a79236bab theme: use standard styling for ibus candidate popups
This is more consistent and helps with legibility.

https://bugzilla.gnome.org/show_bug.cgi?id=694796
2013-03-04 17:03:31 +00:00
cd37e2908f main: don't hook up overview keybinding until after startup
Toggling the overview during the startup animation reportedly
causes stuck grab and other odd behavior.

There's no reason to handle toggling the overview during this
time anyway.

This commit defers that handling until after startup.

https://bugzilla.gnome.org/show_bug.cgi?id=694837
2013-03-04 11:58:39 -05:00
3368e49aa7 main: drop superfluous Lang.bind(this, ...)
Jasper noticed this and I forgot to clean it up before pushing,
then I just noticed it again when doing some other patches

https://bugzilla.gnome.org/show_bug.cgi?id=694326
2013-03-04 11:35:36 -05:00
9e31f05861 shellEntry: Disable copy action for password entries
Curently it is possible to copy the content of password entries,
and paste it elsewhere in clear text. This is undesirable, so
follow GTK+'s behavior and disable the copy action for password
entries.

https://bugzilla.gnome.org/show_bug.cgi?id=695104
2013-03-04 16:43:59 +01:00
b52f4ed25b st-entry: Disable cut/copy actions in password entries
Curently it is possible to copy the content of password entries,
and paste it elsewhere in clear text. This is undesirable, so
follow GTK+'s behavior and disable the cut/copy actions for
password entries.

https://bugzilla.gnome.org/show_bug.cgi?id=695104
2013-03-04 16:43:59 +01:00
00eb764880 configure.ac: remove telepathy-logger dep
We don't use it in C code any more.

https://bugzilla.gnome.org/show_bug.cgi?id=687748
2013-03-04 16:38:47 +01:00
6d011a3700 workspacesView: Always zoom out switcher when using static workspaces
We always leave the workspace switcher zoomed out when we can assume
that the user is actually making use of workspaces. For the default
dynamic workspace behavior, we make this assumption when more than
two workspaces are in use (e.g. at least two workspaces contain windows
plus an empty one at the end). However this test does not make sense
when using static workspaces - in that case, not using workspaces
would be indicated by a workspace number of 1 (in which case the
entire switcher is hidden completely), so add a check for dynamic
workspaces to the condition.

https://bugzilla.gnome.org/show_bug.cgi?id=695126
2013-03-04 15:51:25 +01:00
f9d9caf417 switcherPopup: Remove separator support
Since the alt-tab switcher stopped using separators, the functionality
is unused, remove it.

https://bugzilla.gnome.org/show_bug.cgi?id=661156
2013-03-04 15:28:38 +01:00
b50702dd52 altTab: Ignore workspaces in popups
Currently both the app switcher and the thumbnail list divide items
first into two groups (based on whether the item is located on the
current workspace or not), and then sort each group individually
by MRU.
The resulting behavior is often confusing, e.g. when using alt-tab
a second time does not switch back to the original window when the first
invocation involved a workspace switch and the workspace contains
windows of more than one application.
Instead, make the behavior more predictable by sorting both lists
strictly by MRU.

https://bugzilla.gnome.org/show_bug.cgi?id=661156
2013-03-04 15:28:37 +01:00
e74a82b9d6 Updated Spanish translation 2013-03-04 14:52:05 +01:00
12fa983242 Updated Norwegian bokmål translation 2013-03-04 14:15:59 +01:00
1607df835a Updated Galician translations 2013-03-04 12:41:17 +01:00
7b705dd670 modalDialog: Fix auto-completion of prompts immediately upon display
Shell modal dialogs can take their action on a certain key's
key-release-event. For example on <enter> the affirmative action is
usually run.

Make sure that the key was also pressed on the dialog and we're not
seeing a spurious key-release-event from a key that was pressed before
the dialog was displayed.

https://bugzilla.gnome.org/show_bug.cgi?id=692937
2013-03-04 09:17:41 +01:00
323e3028b6 Updated Brazilian Portuguese translation 2013-03-04 00:52:05 -03:00
7e1b4692e2 status/keyboard: Don't show the switcher with less than 2 items
https://bugzilla.gnome.org/show_bug.cgi?id=695000
2013-03-04 00:30:04 +01:00
c1dd971ce9 xdndHandler: Ensure that the XDnD clone is on top of the panel
The panel is added to the uiGroup, not the overlay group, so
to stack on top of it, we need to add ourselves to the uiGroup
as well.

https://bugzilla.gnome.org/show_bug.cgi?id=695073
2013-03-03 15:41:52 -05:00
39610e6933 viewSelector: Move the loupe icon to the primary slot
This matches the behavior in GTK+. See the rationale there.

https://bugzilla.gnome.org/show_bug.cgi?id=695069
2013-03-03 15:41:52 -05:00
fdb189102d Updated Russian translation 2013-03-03 23:22:11 +04:00
e54d095064 OtherUsersDialog: wrap the description label
In some languages (like Italian) it doesn't fit in one line.

https://bugzilla.gnome.org/show_bug.cgi?id=695002
2013-03-03 13:51:30 +01:00
e6634c56d3 OtherUsersDialog: don't show closing sessions
It is possible that a session survives after closing, if any processes
(usually PulseAudio and GConf) are still alive at the end. In that case,
ignore it, as the user is already logged out and there is nothing to lose.

https://bugzilla.gnome.org/show_bug.cgi?id=695002
2013-03-03 13:51:30 +01:00
deb77a4dde switcherPopup: Cancel the OSD popup before showing
It is irritating to show two different system popups at the same
time, so let switcher popups like alt-tab cancel out the OSD.

https://bugzilla.gnome.org/show_bug.cgi?id=613543
2013-03-03 12:59:09 +01:00
90ea27c423 shellDBus: Export ShowOSD() method on the bus
Export a simple method to trigger an OSD popup. gnome-settings-daemon
will use this to replace its own OSD UI.

https://bugzilla.gnome.org/show_bug.cgi?id=613543
2013-03-03 12:59:09 +01:00
19533f87e3 osdWindow: Add a simple OSD popup
Implement a basic OSD popup that shows an icon and optionally a label
and a fill level. It is based on the existing OSD implementation in
gnome-settings-daemon, which it will replace.

https://bugzilla.gnome.org/show_bug.cgi?id=613543
2013-03-03 12:59:08 +01:00
e6c5c84133 Updated Greek translation 2013-03-03 13:24:37 +02:00
404aaa5658 Updated Uyghur translation
Signed-off-by: Gheyret Kenji <gheyret@gmail.com>
2013-03-03 16:00:53 +09:00
2c70df25f5 Updated Vietnamese translation 2013-03-03 11:13:32 +07:00
db959ac2eb po/vi: import from Damned Lies 2013-03-03 11:11:26 +07:00
bbf0dce390 Punjabi: Translation updated (aalam) 2013-03-03 07:10:45 +05:30
1cbb8b9851 layout: tweak startup animation
Following designer feedback, this commit makes the startup
animation a little more subtle.

https://bugzilla.gnome.org/show_bug.cgi?id=694326
2013-03-02 17:44:26 -05:00
96387cce47 background: drop StillFrame class
We aren't using it anymore so nix it.

https://bugzilla.gnome.org/show_bug.cgi?id=694326
2013-03-02 17:44:26 -05:00
570fc68cb1 main: put noise texture below startup animation, not still frames
Right now we take a still frame of the desktop before showing the
start up animation.  This gives us an animation over what was
there before startup.

That's not actually desirable when restarting the shell.  We don't
want to animate over undecorated windows, we really want to animate
over the noise texture.

This commit drops the still frames in favor of the noise texture.

https://bugzilla.gnome.org/show_bug.cgi?id=694326
2013-03-02 17:44:26 -05:00
e6ce0057af screenShield: only pop lock-screen mode at the end of lift
This avoids the style of the top panel changing as soon as the lift key
is pressed weakening the curtain effect.

https://bugzilla.gnome.org/show_bug.cgi?id=695023
2013-03-02 17:26:28 -05:00
a3f625fe39 screenshot: Discard invalid sized areas
https://bugzilla.gnome.org/show_bug.cgi?id=694998
2013-03-02 22:26:27 +01:00
bdfe459d64 css: Make input sources switcher symbol font smaller
Particularly when using asian languages the symbol could become large
enough to not fit in the space we have and we'd end up with a totally
ellipsized item.

https://bugzilla.gnome.org/show_bug.cgi?id=695001
2013-03-02 18:42:59 +01:00
95ec8ef5e1 shell-global: Fix regression from 071a4e5f83
The clutter_threads_add_repaint_func_full need to return TRUE otherwise
they will be only called once.

https://bugzilla.gnome.org/show_bug.cgi?id=695006
2013-03-02 18:25:34 +01:00
b8198716d9 l10n: Updated Italian translation 2013-03-02 15:55:04 +01:00
071a4e5f83 shell-global: Don't connect to the stage's paint signal
Doing so causes useless full stage redraws and breaks culling
as clutter cannot know how the signal handler affects painting.

So use clutter_threads_add_repaint_func_full instead.

https://bugzilla.gnome.org/show_bug.cgi?id=694988
2013-03-02 14:43:11 +01:00
d99cd71408 Updated POTFILES.in 2013-03-02 01:31:00 +01:00
030e6aa507 Fix overview sizing with multimonitor
Previously, the overview BoxLayout was sized and positioned explicitly
using the primary monitor coordinates, as its parent was at 0,0 and
with a fixed layout manager. Now we use a bin layout, so we need to
position and size the stack actor, and set the overview boxlayout to
expand.
Take also the occasion to use a MonitorConstraint instead of handling
position and size manually.

https://bugzilla.gnome.org/show_bug.cgi?id=694969
2013-03-02 00:29:13 +01:00
f146b01e3e Fix dash sliding animation with multiple monitors
We used to clip the overview group to prevent the dash from sliding into
neighbor monitors, but now it moved to the groupStack, so we must move
the clip too.

https://bugzilla.gnome.org/show_bug.cgi?id=694970
2013-03-02 00:26:39 +01:00
c249ff9046 theme: tone down search results description font style
- don't use bold
- use regular font size
- make the text shadow less dramatic
2013-03-01 14:21:07 -05:00
8b48560c81 overviewControls: add a new messages indicator
The new messages indicator will be visible when there are non-resident
notifications in the tray.

https://bugzilla.gnome.org/show_bug.cgi?id=687787
2013-03-01 13:50:51 -05:00
bdbea2463b messageTray: add an indicatorCount property
This filters out resident and transient notifications in the normal
case, but just returns the number of unread messages for the Telepathy
implementation.

https://bugzilla.gnome.org/show_bug.cgi?id=687787
2013-03-01 13:17:51 -05:00
8fb2263471 messageTray: emit a signal when we're showing or hiding
This will be useful in a future commit.

https://bugzilla.gnome.org/show_bug.cgi?id=687787
2013-03-01 13:17:51 -05:00
ea55c36a3a st-widget: Don't add hidden actors to the paint volume
We are not painting them anyway.
2013-03-01 19:13:22 +01:00
37595ff3e7 shell-generic-container: Exclude skip_paint actors from the paint volume
We are not painting them so there is no reason to redraw a bigger
area because of them.
2013-03-01 19:13:10 +01:00
cca008b73c overview: Move dash to a separate layer
We generally want view content centered, in particular where the
view itself is symmetrical. So move the dash to a separate layer
and use a placeholder to account for its size when showing the
window picker, which is the only view where it doesn't make sense
to center the content.

https://bugzilla.gnome.org/show_bug.cgi?id=694261
2013-03-01 18:25:53 +01:00
b9dcbd9d33 overview: Use the new main actor in show/hide transitions
Commit 43ed66cf26 changed the toplevel overview actor, so it
makes sense to change the show/hide transitions to use that instead
of the existing group, to avoid elements layered on top of the
group being excluded from the transitions.
2013-03-01 18:25:53 +01:00
b6bf8d5b2d Layout: don't minimize fullscreen windows that are focused
Windows can sometimes be focused, or appear to be focused, without being
at the exact top of the stack, for example in the case of override-redirect
windows, or with attached modal dialogs. In that case, we should not
try to minimize them (as it creates a loop that makes it impossible to restore
the window)

https://bugzilla.gnome.org/show_bug.cgi?id=694905
2013-03-01 18:07:47 +01:00
43ed66cf26 overview: use a bin for the main overview actor
We'll pack the new messages indicator on top of the current overview
group, so we need to pack it into a ClutterBinLayout first.

https://bugzilla.gnome.org/show_bug.cgi?id=687787
2013-03-01 11:34:09 -05:00
57eae1be43 main: register with session manager explicitly
Mutter now makes session registration an explicit required
step.  This is so we can tell the session manager when
we're ready to move on to the next phase.

This commit calls the new Meta.register_with_session() api after we're
initialized.

https://bugzilla.gnome.org/show_bug.cgi?id=694876
2013-03-01 11:31:18 -05:00
b394d184cc scroll-view-fade: Tweak easing at edges
Since commit b4f5f1e461, the effect is eased in at the scroll
view's edges so that it does not appear out of nowhere. However,
the linear easing used is not the best option, as now the effect
appears so late that content near the edges ends up just being
cut off rather than faded out.
So adjust the easing function to have the effect appear faster.

https://bugzilla.gnome.org/show_bug.cgi?id=694327
2013-03-01 17:30:01 +01:00
81e01b6f88 shellDBus: Add methods to handle key grabs
Expose Mutter's external grab API on the bus, so gnome-settings-daemon
can refer keygrabbing to the shell.

https://bugzilla.gnome.org/show_bug.cgi?id=643111
2013-03-01 16:14:30 +01:00
52efe32e0f Updated Basque language 2013-03-01 09:27:53 +01:00
e7886734c4 search: fix description of results
"description" is documented as a valid field for search result metas,
and ListSearchResults implements it, so pass it down to be used.
Also, don't wrap the description in quotes, so that the search provider
can decide if it is an excerpt from the searched text or something else.
And to that extent, set use_markup to true, so that terms can be
highlighted.

https://bugzilla.gnome.org/show_bug.cgi?id=694906
2013-03-01 00:38:50 +01:00
083c37a7b2 workspace: Fix bad window positions with small windows
This can happen if you open two or three terminal windows, and then
open the overview -- they're not centered. The issue is that because
of the WINDOW_CLONE_MAXIMUM_SCALE clamping, the scale that is being
laid out is different from the scale that the layout was calculated
for.

Implement and document a hack-ish solution which simply keeps the
scale for the layout as originally calculated, but centers the
windows inside the cell.

https://bugzilla.gnome.org/show_bug.cgi?id=694902
2013-02-28 18:15:15 -05:00
b24a10aa00 workspace: Use the scaled width when calculating the total width
This is a small inconsistency I found in the code after review.

https://bugzilla.gnome.org/show_bug.cgi?id=694902
2013-02-28 18:13:50 -05:00
e1ef14d12b workspace: Write a giant wall of text describing the algorithm
I wrote this for me to find solutions to some problems we were having
with the current situation, so why not check it in?

https://bugzilla.gnome.org/show_bug.cgi?id=694902
2013-02-28 18:13:50 -05:00
1a33de91e2 workspace: Separate out per-window scaling as a separate variable
Multiplication is linear, so we can split this out as a separate
component. This will make it easier to think of it as an additional
per-window scaling factor, rather than tweaking the scale a bit,
which is more correct to the model.

https://bugzilla.gnome.org/show_bug.cgi?id=694902
2013-02-28 16:26:09 -05:00
6e15e2d72a workspace: Remove unused variable
This typo seems to have been here since the very beginning;
no "state" variable appears anywhere.

https://bugzilla.gnome.org/show_bug.cgi?id=694902
2013-02-28 16:26:08 -05:00
d58b715c52 workspace: Remove some additional features used for GridLayoutStrategy
While we won't tear down the entire strategy infrastructure, we want to
rework some layout code in the future, so just tear this piece out for
now.

https://bugzilla.gnome.org/show_bug.cgi?id=694902
2013-02-28 16:26:08 -05:00
804ff8b5a5 layout: Mark the correct monitor as fullscreen 2013-02-28 13:34:05 -05:00
6e89d2f46a extensionPrefs: Fix opening extension preferences on startup
gnome-shell-extension-prefs supports opening a specific extension's
preferences directly from the command line by passing the UUID.
However this broke when extension loading was changed to be processed
asynchronously, as no extension has been loaded when the command
line argument is processed. Fix by deferring opening the extension's
preferences until all extensions have been loaded.

https://bugzilla.gnome.org/show_bug.cgi?id=694858
2013-02-28 15:37:29 +01:00
e99d69b7d9 extensionUtil: Add ExtensionFinder::extensions-loaded signal
Consumers might want to defer work until the initial loading of
extensions has finished, so add an appropriate signal.

https://bugzilla.gnome.org/show_bug.cgi?id=694858
2013-02-28 15:37:28 +01:00
f25416c3f5 Updated Greek translation 2013-02-28 10:10:11 +02:00
a7bb6a2781 Show the session list when needed
Some of the conditions for showing the user list were not
properly inverted, causing the session list to be hidden
when it shouldn't be and shown when it shouldn't be.

https://bugzilla.gnome.org/show_bug.cgi?id=694784
2013-02-27 18:09:43 -05:00
41f14e0e89 layout: Trigger the tray if one event passes the threshold
As a special-case to the "cap event" rules, this allows a heavy swipe
from top to bottom to allow triggering the tray without having to push
into it.

https://bugzilla.gnome.org/show_bug.cgi?id=694467
2013-02-27 14:01:42 -05:00
01bd10f485 layout: Cap each event to 15px of motion
When pressing against the bottom of the screen, we shouldn't
really take more than 15px from each event, to prevent spruious
mouse movements from opening the barrier.

https://bugzilla.gnome.org/show_bug.cgi?id=694467
2013-02-27 14:01:42 -05:00
476eacd5ca layout: Adjust the pressure barrier threshold values
After a day of experimentation with Cosimo and Jon McCann, we
found that this was the best way to do this.

https://bugzilla.gnome.org/show_bug.cgi?id=694467
2013-02-27 14:01:42 -05:00
ca2fb74f41 Updated Traditional Chinese translation(Hong Kong and Taiwan) 2013-02-27 22:59:02 +08:00
8a2baf5b6e Updated Vietnamese translation 2013-02-26 20:47:16 +07:00
d44fc3aa1b po/vi: import from Damned Lies 2013-02-26 20:34:33 +07:00
7d78c42dfc workspaceThumbnails: remove custom scrolling code
Now that we listen to scroll event on the whole overview group, this
makes the thumbnail switcher scroll twice per-event.
2013-02-25 18:23:42 -05:00
a361180745 layout: Handle _NET_WM_FULLSCREEN_MONITORS
Mark all monitors that fullscreen when window is using
the _NET_WM_FULLSCREEN_MONITORS to be fullscreen on multiple monitors.

https://bugzilla.gnome.org/show_bug.cgi?id=646861
2013-02-25 22:06:45 +01:00
310dc10c4d Center the search results as well
Use overlay_scrollbars and the same padding.

https://bugzilla.gnome.org/show_bug.cgi?id=694701
2013-02-25 14:49:25 -05:00
96e02c4c2e tweener: Look at the XSetting for disabling animations
gnome-settings-daemon will be changed to override the XSetting in
the case where we're on a remote display rather than overwriting a
user setting, so we need to look at the XSetting here.

https://bugzilla.gnome.org/show_bug.cgi?id=694320
2013-02-25 12:59:35 -05:00
afb3b9f029 Updated Norwegian bokmål translation 2013-02-25 18:35:23 +01:00
d5e647a191 ScreenShield: fix positioning of background with multimonitor
Previously, we would create one StBin per monitor, but each was positioned
at 0,0 and sized as the screen, so they would overlap and draw the box shadows
on top of the other backgrounds.
Instead, we need to size appropriately the bin, and then we need to position
the actual MetaBacgroundActor at 0,0, so add a flag to BackgroundManager
for this.
Also, get rid of MetaBackgroundGroup, they do nothing because the screenshield
is not a descendant of the MetaWindowGroup and because the widget in between
blocks the propagation of the visible region. At the same time, use a
widget, not a bin, because StBin requires you to set .child, not call add_child().

https://bugzilla.gnome.org/show_bug.cgi?id=694394
2013-02-25 14:46:40 +01:00
a1d37617a8 Fixed typo in string. Fixes bug #687248 2013-02-25 14:27:31 +01:00
2541f85942 Updated gujarati file 2013-02-25 16:39:49 +05:30
07fd23dc5e Updated Slovenian translation 2013-02-24 21:34:43 +01:00
946311b2a3 Updated Polish translation 2013-02-24 01:55:12 +01:00
4868032215 Updated Czech translation 2013-02-24 01:18:25 +01:00
fb0cf64536 viewSelector: Don't focus search entry on whitespace
We recently started to trim leading and trailing whitespace from
the search string, and not to trigger a search when the resulting
string was empty. However we still allow whitespace to trigger
type-ahead-find, so that the key focus is moved briefly to the
search entry and back to the stage, resulting in a disruptive
flickering of the entry.
Fix this by excluding whitespace from triggering type-ahead-find.

https://bugzilla.gnome.org/show_bug.cgi?id=694475
2013-02-23 09:42:09 +01:00
b37afcdba1 xdnd: Only pick reactive actors
This is the only sane way to get select windows in the overview,
as workspace._dropRect blocks our view otherwise.
2013-02-22 20:02:22 +01:00
f644bee831 xdnd: Don't wait for the stage to be disabled when it is already visible
xdndHandler used to wait for the stage to show up before calling
global.init_xdnd() . Since commit 65303d027a xdndHandler is initalized
later so the stage might be already visible at this point causing the
show signal never to be emitted and thus global.init_xdnd will never
be called.

Fix that by checking by calling global.init_xdnd without waiting for
the stage's show signal to be emitted, as the stage is guaranted to be
visible at this point.
2013-02-22 19:37:40 +01:00
6fcc7e3e23 Background: don't check draw-background key
The key was removed from gsettings-desktop-schemas, GNOME should
always draw a background: the key existed to support xsetroot, but
we no longer read the _XROOTPMAP_ID.

https://bugzilla.gnome.org/show_bug.cgi?id=694463
2013-02-22 17:49:16 +01:00
95602eb85d AppDisplay: fix allocation loop warnings
Don't use a constraint to position the boxpointer horizontally. Instead,
use the right alignment flags and let the layout managers do their job.

https://bugzilla.gnome.org/show_bug.cgi?id=694284
2013-02-22 14:58:13 +01:00
9f3afdf928 windowManager: Don't use show_all
It is deprecated and does not make sense there anyway, so use show() instead.

https://bugzilla.gnome.org/show_bug.cgi?id=694441
2013-02-22 14:28:58 +01:00
627a2412d2 ui: Don't use Clutter.Group
It is deprecated use Clutter.Actor instead.

https://bugzilla.gnome.org/show_bug.cgi?id=694441
2013-02-22 14:28:58 +01:00
acffd1e792 main: Guard against NULL log_domain in default handler
g_str_has_prefix() will assert when passed NULL, so we need to make
sure that we are passed a non-NULL log_domain first.

Spotted by <goughost<at>yahoo.com.cn>

https://bugzilla.gnome.org/show_bug.cgi?id=663601
2013-02-22 11:02:00 +01:00
62ca4ba624 appDisplay: Apply the same padding to AllView and FrequentView
We add some horizontal padding to the AllView's content to make
sure content does not end up underneath the scrollbar; while this
is not required in case of the FrequentView which does not have
a scrollbar, applying the same padding ensures that both views
end up with the same spacing, which makes switching between them
less disruptive.

https://bugzilla.gnome.org/show_bug.cgi?id=694261
2013-02-22 10:57:50 +01:00
6ea8f35343 appDisplay: Center AllView content
If the AllView is scrolled, the vertical scrollbar will take away
some horizontal space on one side, resulting in the content ending
up slightly off-center.
Account for this by using overlay-scrollbars instead.

https://bugzilla.gnome.org/show_bug.cgi?id=694261
2013-02-22 10:57:50 +01:00
1bd8c67041 st-scroll-view: Add :overlay-scrollbars property
If enabled, scrollbars take away from the allocation given to the
view's content. This is usually preferrable to painting the bars on
top of the content, but there are exceptions, for instance when the
content needs to be centered with regard to the view as a whole.
Add a :overlay-scrollbars property to account for those cases.

https://bugzilla.gnome.org/show_bug.cgi?id=694261
2013-02-22 10:57:50 +01:00
7425b382d6 appDisplay: Remove scroll view hack in FrequentView
The frequent view should not be scrolled, but to work around the
icon grid overflowing, we used a (non-scrolling) scroll view anyway.
Now that IconGrid:fillParent allows us to avoid overflow, we can
remove this hack.

https://bugzilla.gnome.org/show_bug.cgi?id=694256
2013-02-22 09:50:57 +01:00
5f61b57d63 iconGrid: Add fillParent property
Setting this property will only display as many icons as fit the parent's
allocation, rather than overflowing.

https://bugzilla.gnome.org/show_bug.cgi?id=694256
2013-02-22 09:46:30 +01:00
9db73767d9 appDisplay: Remove unnecessary StBin from hierarchy
The bin is used to top-align views that don't fill the entire height,
but the same can be achieved by setting appropriate expand properties.
2013-02-22 00:07:54 +01:00
c562245c16 main: don't show stage until still frames are loaded
We sometimes map the stage before we've loaded a background on it
because of a race asynchronously loading the session mode.

This manifests as the startup animating starting over a white
background.

This commit defers showing the stage until after the still frames
are loaded.

https://bugzilla.gnome.org/show_bug.cgi?id=694321
2013-02-21 15:01:39 -05:00
9525216d78 messageTray: Fix warning when opening the tray
As the tray hover handler will call _cancelTrayDwell, we need to
ensure this variable is initialized even if tray dwelling isn't.

https://bugzilla.gnome.org/show_bug.cgi?id=694336
2013-02-21 11:56:33 -05:00
28a71a29e6 Revert "messageTray: Fix warning when opening the tray"
This reverts commit 7b06d34ba4.

https://bugzilla.gnome.org/show_bug.cgi?id=694336
2013-02-21 11:52:20 -05:00
7b06d34ba4 messageTray: Fix warning when opening the tray
As the tray hover handler will call _cancelTrayDwell, we need to
ensure this variable is initialized even if tray dwelling isn't.

https://bugzilla.gnome.org/show_bug.cgi?id=694336
2013-02-21 11:40:49 -05:00
656d24e477 overviewControls: don't set padding on the whole overview group
Set it on the control slider actor instead. At the same time, remove
bottom padding from search results actor.

https://bugzilla.gnome.org/show_bug.cgi?id=694287
2013-02-21 11:37:44 -05:00
df0f03d831 overview: Keep open when a Control key is held
It is useful at times to perform several actions that would usually
close the overview (for instance launching an application) at once.
Currently we allow this by dragging items to a workspace rather than
just clicking it, but it's an odd metaphor with its own set of
problems.
Introduce an alternative approach (inspired by file selection in
file managers) by keeping the overview open if a Control key is
held down.

https://bugzilla.gnome.org/show_bug.cgi?id=686984
2013-02-21 17:21:54 +01:00
1db6d15677 overviewControls: fade view selector and thumbnails slider on DnD
When an item is dragged from a page that is not the windows one, the
only possible target is the dash, so fade out the rest.

https://bugzilla.gnome.org/show_bug.cgi?id=686984
2013-02-21 11:16:43 -05:00
08a0479c9e WorkspaceThumbnail: fix mutter warning
Pass a proper timestamp to workspace activation functions

https://bugzilla.gnome.org/show_bug.cgi?id=694365
2013-02-21 16:10:09 +01:00
6682b7dfa5 Updated Russian translation 2013-02-21 18:22:27 +04:00
aad5d98b43 iconGrid: Take extra spacing into account for height requests
This fixes the clipped app picker in the overview.

https://bugzilla.gnome.org/show_bug.cgi?id=694234
2013-02-20 19:38:38 -05:00
a8a4a85dac lg: add a reference to lookingGlass in the Extensions tab.
When the user clicks on "View Source" or "Web Page" in the "Extensions" tab of
looking glass, the callback _onViewSource() or _onWebPage() is called and they
try to close looking glass: this._lookingGlass.close();

But it does not work and generate the exception "this._lookingGlass is
undefined". This patch fixes that.

https://bugzilla.gnome.org/show_bug.cgi?id=693814
2013-02-20 23:14:53 +00:00
ad71b969b2 Workspace: fix stacking of window clones
We must set the stack above property of window clones, or they will
not stack themselves properly when a drag is cancelled.

https://bugzilla.gnome.org/show_bug.cgi?id=694292
2013-02-20 20:56:58 +01:00
8bcb10391e dash: add a workaround for Clutter bug 692744
https://bugzilla.gnome.org/show_bug.cgi?id=690643
2013-02-20 20:51:11 +01:00
8e7d74bc3b dash: fix icon animation
animateIn and animateOut should not reset the scale, otherwise
extra animate calls (which are possible because the diff algorithm
in _redisplay is not optimal) cause unneeded movement.
Therefore, create new items hidden, and have the creator call
animateIn or set the scale/opacity properties manually.
adjustIconSize must be changed to always set the icon size temporarily,
otherwise the first time it computes the icon size with 0 scale.

https://bugzilla.gnome.org/show_bug.cgi?id=690643
2013-02-20 20:51:11 +01:00
629b6faa22 dash: fix allocation loop when increasing icon size
The DashActor will known to allocate the show apps button only if the
icon size is (temporarily) too big for the containing box, therefore
it should request just that as the minimum size.
This solves a glitch that happened when removing a favorite and at the
same time causing the dash to expand.

https://bugzilla.gnome.org/show_bug.cgi?id=690643
2013-02-20 20:51:11 +01:00
ef1e27966d dash: turn DashItemContainer into a proper St.Widget
This removes a number of unneeded ._delegate accesses and cleans up
the code.

https://bugzilla.gnome.org/show_bug.cgi?id=690643
2013-02-20 20:51:11 +01:00
ea0eb4ba09 Bump version to 3.7.90
Update NEWS.
2013-02-20 20:18:00 +01:00
f83ad77c08 workspacesView: Remove some unused variables
More left-overs from the custom swipe-scrolling code.

https://bugzilla.gnome.org/show_bug.cgi?id=686639
2013-02-20 20:17:20 +01:00
af219ce9e7 workspacesView: Allow workspace switching via scroll wheel
This is already possible over the workspace switcher, extend this
behavior to the entire window picker area.

https://bugzilla.gnome.org/show_bug.cgi?id=686639
2013-02-20 20:17:20 +01:00
2ac88a7fa6 overview: Remove some unused variables
Those are left-overs from the custom swipe-scroll implementation.

https://bugzilla.gnome.org/show_bug.cgi?id=686639
2013-02-20 20:17:19 +01:00
c495ff8ad8 overview: Expose scroll-events
Using the scroll wheel in the window picker should switch workspaces.
As the picker doesn't have a visible boundary though, it makes sense
to accept scroll-event for the entire overview area. Rather than
making the overview's main actor public, expose scroll-events via
a signal.

https://bugzilla.gnome.org/show_bug.cgi?id=686639
2013-02-20 20:17:19 +01:00
0e12215614 workspace: Remove zooming of window previews
Using the scroll wheel to switch workspaces makes sense, but it is
currently only supported on the workspace switcher, as it conflicts
with window zooming in the picker.
As changing workspaces is far more useful than the zoom feature,
remove the latter in order to "free" the scroll wheel for workspace
switching.

https://bugzilla.gnome.org/show_bug.cgi?id=686639
2013-02-20 20:17:19 +01:00
b4b13b0cb9 endSessionDialog: don't process response until closed
Right now if a user logs out, they are thrown to the login screen
imediately, without even seeing the dialog close.

This commit fades the dialog out before processing the logout.

https://bugzilla.gnome.org/show_bug.cgi?id=694296
2013-02-20 14:16:54 -05:00
677cdfd6c8 modalDialog: emit 'closed' signal when dialog is closed
Opening and closing a modal dialog is not instant. There
is a transition animation involved.  When the dialog is
finished opening, it currently emits the "opened" signal.

When it's finished closing, however, it doesn't emit a
"closed" signal.  This means, there's not a good way to
know when the dialog finishes closing.

This commit adds the "closed" signal.

https://bugzilla.gnome.org/show_bug.cgi?id=694296
2013-02-20 14:16:53 -05:00
e9f18c6d8d modalDialog: drop global.get_current_time() arguments to close
Modal dialogs take an optional timestamp in their close method.
If the timestamp is not passed in, then global.get_current_time()
is used.

Some callers of the close method pass in global.get_current_time()
unnecessarly (since it's the default).

This commit drops the argument for those cases.

https://bugzilla.gnome.org/show_bug.cgi?id=694296
2013-02-20 14:16:53 -05:00
8640e18d1f backgroundMenu: Rename "System Settings" to "Settings"
And reverse menu order in order to avoid it reading "Change Background… Settings"

"System Settings" shouldn't be used because it's not used to mean the control-center in any other place.

https://bugzilla.gnome.org/show_bug.cgi?id=694290
2013-02-20 20:07:08 +01:00
300c4d8432 gdm: make entry reactive when asking user question
We disable the entry after a user answers a question while we
process it, but we don't reactivate it later if asked another
one.

This commit makes sure the entry is always reactive when we
are waiting for an answer from the user.

https://bugzilla.gnome.org/show_bug.cgi?id=691806
2013-02-20 13:38:00 -05:00
92c877493a theme: Remove some unused css classes 2013-02-20 18:10:59 +01:00
ff4926be35 theme: remove unused references to dash-search-button
The search provider buttons have been removed, so we don't need
their styling in the css.

https://bugzilla.gnome.org/show_bug.cgi?id=694289
2013-02-20 16:55:32 +00:00
df3872f665 theme: use standard button styling for the app view switcher
The switcher widget for all / frequent applications should use the
standand button styling. This is more consistent.

https://bugzilla.gnome.org/show_bug.cgi?id=694265
2013-02-20 17:46:51 +01:00
fcded2ea2b appDisplay: Fix view-switcher buttons in RTL locales
https://bugzilla.gnome.org/show_bug.cgi?id=694265
2013-02-20 17:46:51 +01:00
420f0109ee workspacesView: Treat 'gesture-cancel' the same as 'gesture-end'
When a swipe scroll completes, we scroll the active workspace into
view. This works fine if the gesture is completed properly by
emitting 'gesture-end', but not when Clutter considers the gesture
cancelled - in that case, the view remains stuck in an intermediate
position. To fix, treat 'gesture-cancel' the same as 'gesture-end'.

https://bugzilla.gnome.org/show_bug.cgi?id=689394
2013-02-20 17:43:44 +01:00
b4678a1cee layout: Clean up consoleBackgroundGroup
We shouldn't leave a reference to a destroyed actor just hanging
around.

https://bugzilla.gnome.org/show_bug.cgi?id=694227
2013-02-20 11:36:19 -05:00
5d3c90197c layout: Rebuild struts and the input region when the Shell starts up
This means that windows will be positioned correctly with respect
to the panel when the shell starts up, and there won't be adjusting
after the session animation zooms in entirely.

https://bugzilla.gnome.org/show_bug.cgi?id=694227
2013-02-20 11:36:19 -05:00
d5d517748c layout: Remove freezeUpdateRegions/thawUpdateRegions
Due to a bad rebase causing freezeUpdateCount to never get initialized,
these functions effectively did nothing. Since we're going to go to a
different mechanism for freezing region updates, let's just tear these
out now instead of fixing them before tearing them out.

https://bugzilla.gnome.org/show_bug.cgi?id=694227
2013-02-20 11:36:19 -05:00
de3d3c15a5 layout: Don't bother updating the regions in the greeter
The greeter is always full-screen, so this should never matter.

https://bugzilla.gnome.org/show_bug.cgi?id=694227
2013-02-20 11:36:18 -05:00
3db253998b panel: Don't try to begin a grab op while something else has taken a grab
https://bugzilla.gnome.org/show_bug.cgi?id=694241
2013-02-20 11:36:06 -05:00
d3f978a19d AppDisplay: fix undefined variable reference
https://bugzilla.gnome.org/show_bug.cgi?id=694282
2013-02-20 17:04:01 +01:00
a50ddd6d8c AppDisplay: fake a release event to dnd when opening the menu
PopupMenuManager eats the next release event, which would otherwise close
the menu, so we need to tell dnd that we're handling it, and no drag should
be started, so it can ungrab the pointer and restore state.

https://bugzilla.gnome.org/show_bug.cgi?id=694276
2013-02-20 17:01:25 +01:00
37700b4b1a AppDisplay: fix folder popup arrow
The arrow origin must be in the coordinate space of the box pointer,
but it cannot be calculated when the box pointer is shown, because it's
not allocated yet, so pass the source to the boxpointer and adjust
the arrow at the next paint.

https://bugzilla.gnome.org/show_bug.cgi?id=694264
2013-02-20 16:04:54 +01:00
a8f9871725 keyboard: destroy actors when a group is removed
We can't reuse key actors since signals connected to them are
associated to the original group object.
Fix of commit 824fbe09c2.

https://bugzilla.gnome.org/show_bug.cgi?id=681735
2013-02-20 22:39:41 +09:00
a4a5492782 st-bin: fix a typo in the property setter
This got unnoticed for more than 3 years!

https://bugzilla.gnome.org/show_bug.cgi?id=694234
2013-02-20 00:06:52 -05:00
9ebb80502a background: don't call destroy() from the destroy handler
We're calling destroy() in a callback of the destroy handler, which
results in an X server crash at login.

https://bugzilla.gnome.org/show_bug.cgi?id=694240
2013-02-20 00:06:38 -05:00
dcacbb37c3 background: destroy actors don't remove them from their parent
This reads better, and it also fixes a warning when background
is changed while transitioning to the overview.

https://bugzilla.gnome.org/show_bug.cgi?id=694227
2013-02-19 21:54:01 -05:00
34443da7ab screenShield: don't use Bin for BackgroundManager container
St.Bin() really only expects one child at a time, and the
BackgroundManager will add two. This can cause assertion
failures when destroying one of the background actors.

https://bugzilla.gnome.org/show_bug.cgi?id=694227
2013-02-19 21:54:00 -05:00
09c14f1a10 background: s/NULL/null/
woops.
2013-02-19 21:08:27 -05:00
e801647083 layout: Fix some indentation 2013-02-19 21:00:46 -05:00
0122cee0ae Background: ignore cancellation errors
If the request cancellable is cancelled, the requesting background was
destroyed, so don't bother reporting the result.

https://bugzilla.gnome.org/show_bug.cgi?id=694227
2013-02-19 20:57:21 -05:00
1188b1be36 layout: Hide the message tray and OSK during the startup animation
https://bugzilla.gnome.org/show_bug.cgi?id=694227
2013-02-19 20:57:21 -05:00
905c4932d1 layout: invert isGreeter check
I somehow lost an exclamation point.

https://bugzilla.gnome.org/show_bug.cgi?id=694227
2013-02-19 20:57:21 -05:00
6484da26f0 layout: add back startingUp state variable
I dropped it on accident during last minute refactoring

https://bugzilla.gnome.org/show_bug.cgi?id=694227
2013-02-19 20:57:21 -05:00
079041cf1f BackgroundMenu: fix regression from new background handling
BackgroundMenu must ensure the actor it attaches to is reactive, and
the layout manager must create a background menu for the first background
too.

https://bugzilla.gnome.org/show_bug.cgi?id=694227
2013-02-19 20:57:21 -05:00
b8df7798bf Punjabi: Translation updated (aalam) 2013-02-20 01:37:24 +00:00
e4af954a95 LayoutManager: add missing constant
https://bugzilla.gnome.org/show_bug.cgi?id=694227
2013-02-20 02:25:36 +01:00
f3df62f27e WorkspaceThumbnail: fix typo in stacking code
Was causing thumbnails to be below the background.

https://bugzilla.gnome.org/show_bug.cgi?id=694227
2013-02-20 01:59:14 +01:00
4b3c9e826d workspace: Don't use grid layout
Right now we arrange the window thumbnails in a grid if there are more than two
rows of them. This was originally intended to reduce the amount of noise in the
thumbnail arrangement. It also made sense when the thumbnails were of a similar
size.

Nowadays we reflect the size of windows in the size of the thumbnail. This
leads to huge amounts of space between some windows when they are grid aligned.
Removing the grid alignment would also lead to larger thumbnails.

https://bugzilla.gnome.org/show_bug.cgi?id=694210
2013-02-20 00:44:37 +01:00
ba4780cbd3 layout: don't bother hiding window group
Right now we hide the window group if we're a greeter.
We did this to avoid showing the background. These days
we don't add the background in the first place, so we
can drop this code.

https://bugzilla.gnome.org/show_bug.cgi?id=682429
2013-02-19 18:39:54 -05:00
b1a6940188 background: add slide show support
gnome-desktop's background drawing code supports an
XML format for presenting backgrounds based on time of day,
monitor geometry, etc. Now that we don't use gnome-desktop for drawing the
background, we need to implement that support ourselves to maintain
feature parity.

This commit implements that.

https://bugzilla.gnome.org/show_bug.cgi?id=682429
2013-02-19 18:39:54 -05:00
3c8325f1f3 layout: rework background handling
This commit updates the code to use mutter's new background
api, and changes the shell's startup animation to be closer
to the mockups.

Based on initial work by Giovanni Campagna

https://bugzilla.gnome.org/show_bug.cgi?id=682429
2013-02-19 18:39:54 -05:00
5fa9581db3 loginDialog: add cross fade animation between states
This commit adds a crossfade between the user selection state
and the user verification state.

https://bugzilla.gnome.org/show_bug.cgi?id=694062
2013-02-19 18:39:54 -05:00
96001d86e1 loginDialog: put "Not Listed?" button and user list in separate container
The user list and the "Not Listed?" button get shown and hidden at the
same time, so we can simplify the code by putting them in a new
subcontainer.

This commit creates a userSelectionBox container that both actors get
put in, and changes all the code that shows and hides these actors to
show and hide userSelectionBox instead.

https://bugzilla.gnome.org/show_bug.cgi?id=694062
2013-02-19 18:39:54 -05:00
963e808d98 loginDialog: move prompt hiding code to hidePrompt
The sessionList and the prompt hint are all really
part of the prompt, so we should have the code that
hides those things in hidePrompt instead of in
showUserList.

This commit does that.

https://bugzilla.gnome.org/show_bug.cgi?id=694062
2013-02-19 18:39:54 -05:00
d124ca377f loginDialog: use user widget when doing verification
Right now, when a user item is clicked we remove all other users from
the list and position the item in the appropriate place on screen.

Ultimately, we're going to want to crossfade from the fully populated
list to the user prompt.  Since we're going to need to show the user
avatar in two different positions we can't simply move it.

This commit leaves the user item for the user list, and instead shows
a UserWidget actor during user verification, in the same way the
unlock dialog shows a UserWidget actor during reauthentication.

https://bugzilla.gnome.org/show_bug.cgi?id=694062
2013-02-19 18:39:53 -05:00
a3d3d81447 unlockDialog: move user widget into separate file
The user widget is the username and avatar shown on
the unlock dialog.

The login dialog has something very similar.

This commit separates the user widget out to its own
file, so we can use it from the login dialog in a
later commit.

https://bugzilla.gnome.org/show_bug.cgi?id=694062
2013-02-19 18:39:53 -05:00
244121d920 loginDialog: fade out before starting session
Right now we very abruptly kill the login screen
and start the users session without any transition
out.

This commit introduces a fade out of the dialog and
panels.

https://bugzilla.gnome.org/show_bug.cgi?id=694062
2013-02-19 18:39:53 -05:00
adf95fb90d loginDialog: drop animations
The latest mockups don't animate between states by
resizing actors. Instead, crossfades are employed.

This commit strips out many of the existing animations
as a first step toward implementing the new ones.

https://bugzilla.gnome.org/show_bug.cgi?id=694062
2013-02-19 18:39:53 -05:00
f8446f1bfc loginDialog: get rid of title label
the login screen currently says "Sign In" at the top of it.

The latest mock ups don't have that.

This commit drops it.

https://bugzilla.gnome.org/show_bug.cgi?id=694062
2013-02-19 18:39:53 -05:00
b922035d4f iconGrid: Dynamically adjust spacing based on columnLimit
Adjust the spacing based on the available width when a columnLimit
is set to evenly place the icons.

https://bugzilla.gnome.org/show_bug.cgi?id=694215
2013-02-20 00:39:18 +01:00
b75fc2bde3 appDisplay: Add additional view showing frequently used apps
According to the design mockups, the app picker should follow the
recent view pattern as used by applications, where the user is first
offered a subset of applications he/she is likely to start, and only
then allow switching to the full set of installed applications.
So implement the ability to manage several views in AppDisplay and add
FrequentView as additional view, which uses the existing ShellAppUsage
to display a list of frequently used applications.

https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:18:56 +01:00
10e16cca22 shell-app-usage: Remove unused parameter
https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:18:56 +01:00
6628214484 appDisplay: Make folder popups "view-modal"
While a group is open, we want to block events on items underneath,
but still allow interaction with components outside the app display
as well as scrolling of the view as a whole.

https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:18:56 +01:00
e3c9c46c1c appDisplay: Create AppFolderIcons for selected categories
App folders are intended for grouping some applications, not to
assign a category to every single application, so we will only
create folders for a selected subset of the existing categories.
Software/Alacarte will eventually allow to create/modify those
folders, so store the setting in GSettings so that it can be
shared with those applications.

https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:18:56 +01:00
6a1b341336 appDisplay: Add AllView.addFolder() method
Adjust AllView to be able to hold both apps and folders, and add an
addFolder() method to insert a folder.

https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:18:56 +01:00
65f96494f8 appDisplay: Separate app loading from filling the grid
At the moment when loading the applications, each app is inserted
at its correct (alphabetical) position. Avoid this overhead by
loading all apps first, then sort them once and fill the grid with
the sorted actors.

https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:18:56 +01:00
6e3e2d9f29 appDisplay: Add new FolderIcon class
FolderIcons will appear in the primary app view along AppIcons, but
represent a group of applications rather than a single application.

https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:18:56 +01:00
25d8debb11 appDisplay: Add intermediate stack widget to hold folder popups
AllView will be updated to allow app folders in addition to app
launchers. Folders will pop up in the existing view, so add an
additional stack widget into the hierarchy which popups will use
as parent.

https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:18:56 +01:00
426b227f03 appDisplay: Make AlphabeticalView an abstract base class of AllView
We are going to introduce app folders, which will also present an
alphabetical list of applications, but use a different UI. In
preparation for that, split out the item logic into an abstract
base class.

https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:18:56 +01:00
2fbd9b95a7 appDisplay: Set a maximum column number
In particular on widescreen monitors, the number of items in a row
can be overwhelming, so limit the number of columns and center the
view.

https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:08:11 +01:00
9758029c11 appDisplay: Make _loadCategory() a utility function
When we bring back categories as folders, this method will be
useful outside of AppDisplay.

https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:08:11 +01:00
f21b8206af messageTray: Move makeCloseButton() into Util
The function is useful for other modules as well, so move it to a
more generic place.

https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:08:11 +01:00
4f8f79a907 appDisplay: Remove unused variables
https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:08:11 +01:00
c9e2f16404 appDisplay: Merge AllAppDisplay and ViewByCategories
With categories removed, the separation between AllAppDisplay and
ViewByCategories no longer makes sense. Also use this opportunity
to rename the outdated AllAppDisplay to AppDisplay; it will
eventually be used to manage different views.

https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:08:11 +01:00
2658754295 appDisplay: Merge AppIcon and AppWellIcon
AppIcon is just a tiny wrapper around BaseIcon, which does not add
anything over using BaseIcon directly, so merge its code with
AppWellIcon. As the concept of the "app well" has not been used
since well before 3.0, use AppIcon as name for the merged class.

https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:08:11 +01:00
77d8ff3620 appDisplay: Remove categories
We will eventually display some categories as app folders, but the
existing code doesn't help with the implementation, so remove it.

https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:08:11 +01:00
c4621119b3 AccessibilityMenu: hide when not needed
Tie the menu visibility to having at least one a11y feature enabled.

https://bugzilla.gnome.org/show_bug.cgi?id=681528
2013-02-19 22:07:08 +01:00
a2a292d48e Updated POTFILES.in 2013-02-19 20:29:16 +01:00
8349b70ba8 theme: add some transparency to the menu backgrounds
They look too hard with a fully opaque black background. A small
bit of transparency will give a softer appearance without impairing
readability.

https://bugzilla.gnome.org/show_bug.cgi?id=694202
2013-02-19 18:55:09 +00:00
dd6fe58475 polKitAgent: Don't crash when we cannot enable/disable the auth agent
This makes it easier to run with jhbuild.

https://bugzilla.gnome.org/show_bug.cgi?id=687556
2013-02-19 17:25:53 +01:00
386929e84b l10n: Updated Italian translation 2013-02-19 11:07:46 +01:00
43a355e07a userMenu: Use single quotes for non translateable strings
Coding style cleanup ...
2013-02-19 08:44:01 +01:00
1a5d2ac459 build: Bump gnome-desktop requirement to 3.7.90 2013-02-19 07:54:52 +01:00
21403b19e0 overviewControls: use Params for SlidingControl constructor
https://bugzilla.gnome.org/show_bug.cgi?id=686984
2013-02-18 22:50:11 -05:00
cb7778d433 dash: don't react to window drags
Instead, make the actor half-opaque in that case.

https://bugzilla.gnome.org/show_bug.cgi?id=686984
2013-02-18 22:50:11 -05:00
07c0105c83 loginDialog: don't try to stop work spinner if it's already gone
This can happen if the dialog gets reset at the wrong moment.

https://bugzilla.gnome.org/show_bug.cgi?id=693757
2013-02-18 20:35:05 -05:00
7da186d4e9 loginDialog: fix cancel from "Not Listed?"
Since commit 1ae0fadbf4 we
no longer start a PAM conversation as soon as the user clicks
the "Not Listed?" button.  Instead we defer starting the PAM
conversation until the user types their username.

Unfortunately, the cancel button resets the dialog back to the
user list indrectly by cancelling the current PAM conversation.

This means if the user hasn't yet entered thier username then
the cancel button doesn't work.

This commit performs a direct dialog reset in the case the
PAM conversation hasn't been started yet.

https://bugzilla.gnome.org/show_bug.cgi?id=693756
2013-02-18 20:34:20 -05:00
75885c3095 appDisplay: Fix _ensureIconVisible()
The fade effect was renamed a while ago, but we missed to update
the use in _ensureIconVisible().

https://bugzilla.gnome.org/show_bug.cgi?id=694125
2013-02-18 22:44:23 +01:00
5d275389af scroll-view-fade: Fix [vh]fade_offset property getters
The two were mixed up all along, and nobody noticed ...

https://bugzilla.gnome.org/show_bug.cgi?id=694125
2013-02-18 22:44:23 +01:00
75589b4450 shellEntry: Don't navigate forward before popping up the menu
Doing it before the grab messes up the grab helper's tracking
of where the focus was before, leading to it not tracking the
saved focus correctly, meaning that when we pop the menu back
down with escape, it doesn't restore focus correctly.

https://bugzilla.gnome.org/show_bug.cgi?id=694123
2013-02-18 16:17:09 -05:00
a9b12d5d73 shellEntry: Add an animation when opening the menu 2013-02-18 16:02:44 -05:00
6a00a504d4 backgroundMenu: Don't add an animation to the background menu
We don't do this for shell entries nor for the message tray
context menu, so it doesn't make sense to do it here, either.
2013-02-18 16:01:02 -05:00
6b9cac928b shellEntry: Add a modeline 2013-02-18 16:01:02 -05:00
b292384380 messageTray: Animate the context menu
Use animation for opening and closing the menu.
2013-02-18 22:00:39 +01:00
01ac5aeb0d messageTray: Use 'Clear Message' instead of 'Clear for the context menu 2013-02-18 21:59:48 +01:00
1a41ee0c30 backgroundMenu: Fix a string
The proper terminology is "Background". And while we're at it,
use a real ellipsis.
2013-02-18 15:48:30 -05:00
1950a67e15 Run the startup animation when we don't have much going on
Starting the startup animation when we don't have that much IO
makes it a lot more visible.

Based on a patch by Giovanni Campagna <gcampagna@src.gnome.org>

https://bugzilla.gnome.org/show_bug.cgi?id=682429
2013-02-18 15:37:59 -05:00
65303d027a main: defer startup until we know our session mode
commit 92083eaf76 made
session mode loading an asynchronous operation.

Aspects of the session mode aren't known immediately at
start up. For instance, sessionMode.isGreeter returns
false for greeter sessions until the asynchronous
operation completes.

This commit defers start up processing until the session
mode is fully known.

https://bugzilla.gnome.org/show_bug.cgi?id=682429
2013-02-18 15:37:44 -05:00
fae838b054 backgroundMenu: Add a context menu on the background actor
Allow users to change their wallpaper and launch System Settings
from it.

https://bugzilla.gnome.org/show_bug.cgi?id=681540
2013-02-18 15:23:25 -05:00
eab4c7bce9 Don't call beginItemDrag() for XDND
The dragged actors are not overview items.

https://bugzilla.gnome.org/show_bug.cgi?id=686984
2013-02-18 14:54:53 -05:00
6bb38b873b appDisplay: remove unused parameter to AppWellIcon constructor
https://bugzilla.gnome.org/show_bug.cgi?id=686984
2013-02-18 14:54:53 -05:00
ba5860cdd6 xdndHandler: Add the dummy actor to the uiGroup
While this isn't too urgent, since this won't break if we don't have
a drag actor, I was wondering what the other child in my stage was.
2013-02-18 14:47:17 -05:00
ee62863759 messageTray: Don't allow opening while entering/leaving the overview
The complex overview transition code means that there's no easy
way to handle with this right now. Blocking the message tray
while the overview is animating seems like the correct thing to do.

https://bugzilla.gnome.org/show_bug.cgi?id=694038
2013-02-18 14:42:44 -05:00
4a3a61bea1 messageTray: Jiggle some code around
This is to put state management with state management

https://bugzilla.gnome.org/show_bug.cgi?id=694038
2013-02-18 14:42:44 -05:00
f3901eb668 messageTray: Drop more special overview handling
https://bugzilla.gnome.org/show_bug.cgi?id=694038
2013-02-18 14:42:44 -05:00
b53d1df4df messageTray: Re-add the hotcorner when the panel is updated
The activities button may come and go at any moment now that we
have a dynamic panel. We need to re-check the activities button
whenever the panel is updated.

https://bugzilla.gnome.org/show_bug.cgi?id=694038
2013-02-18 14:42:44 -05:00
df9b0c548e theme: Clean up and merge window-caption definitions 2013-02-18 14:24:40 -05:00
3056fc4710 overviewControls: don't perform translation when target is met already
Fixes a regression from ccb81919f7
2013-02-18 14:18:57 -05:00
e411a4af21 userMenu: Use the session id to detect the current session
This allows us to detect startx sessions.

https://bugzilla.gnome.org/show_bug.cgi?id=693896
2013-02-18 20:13:38 +01:00
3430012136 overview: Don't display minimized windows differently in the overview
Making them not fully opaque just makes them harder to see and there is no reason why the user should care whether the window is minimized or not when
switching to a window display them like any other windows.

https://bugzilla.gnome.org/show_bug.cgi?id=693991
2013-02-18 19:40:48 +01:00
809295c03d popupMenu: Close all child menus when the main menu is closed
Otherwise, if something managed to call close(), it wouldn't
close any child menus.

https://bugzilla.gnome.org/show_bug.cgi?id=693572
2013-02-18 13:31:15 -05:00
c9783b38c4 popupMenu: Move child menus to be a PopupMenu-only thing
It doesn't make sense for combo menus, sections, or submenus
to have child menus.

https://bugzilla.gnome.org/show_bug.cgi?id=693572
2013-02-18 13:31:15 -05:00
44e4cbf04a popupMenu: Remove support for nested combo boxes
I'm sure this was originally a testcase for nested child
menus, and we never actually inteded any support for this.

https://bugzilla.gnome.org/show_bug.cgi?id=693572
2013-02-18 13:31:15 -05:00
049695b914 Minimize fullscreen windows when they end up in the background
Alt-Tab away from a monitor sized on the primary monitor results into the top
panel being displayed on top of the window which looks very bad.

So just hide those windows by minimizing them.

The icon geometry animation does not really make sense for fullscreen windows
so just fade them out.

https://bugzilla.gnome.org/show_bug.cgi?id=693991
2013-02-18 19:30:56 +01:00
7b1aab3759 layout: Fix tray barrier never getting destroyed
Due to a typo, we accidentally set trayPressure to null,
making the next branch always fail.

https://bugzilla.gnome.org/show_bug.cgi?id=694070
2013-02-18 13:25:12 -05:00
5e87fea1ee StEntry: reset the cursor when unmapped
We cannot reset the cursor at the next leave event, as that might
happen on a NULL stage and cause a BadWindow error, so do it on
unmap (which is guaranteed to happen before the stage is cleared).

https://bugzilla.gnome.org/show_bug.cgi?id=694057
2013-02-18 18:08:57 +01:00
a187111a26 OverviewControl: simplify code to add bottom padding
All the complexity with a custom actor and a generic container was
just to add some padding below the overview controls. Remove that,
and use CSS instead.

https://bugzilla.gnome.org/show_bug.cgi?id=694100
2013-02-18 17:37:46 +01:00
3044a6b517 Remove builtin settings search provider
It is now provided as a remote provider by gnome-control-center.

https://bugzilla.gnome.org/show_bug.cgi?id=690824
2013-02-18 16:56:45 +01:00
824fbe09c2 keyboard: track XKB group configuration change
Since GNOME 3.6, switching XKB layouts changes the group
configuration.  This patch tries to track group configuration changes
and reconstruct UI as needed.  See also caribou bug#694011.

https://bugzilla.gnome.org/show_bug.cgi?id=681735
2013-02-19 00:32:26 +09:00
764eed7599 l10n: Updated Italian translation 2013-02-18 15:39:01 +01:00
ccb81919f7 overviewControls: update slide in translations from pageEmpty
Now that we unified animation times, move in controls together with the
new view.

https://bugzilla.gnome.org/show_bug.cgi?id=694064
2013-02-18 09:34:06 -05:00
4505c38b58 overviewControls: fix math for translations when sliding in
The math to compute translations was wrong when sliding in; also, don't
depend on the slide value for the translation anymore.

https://bugzilla.gnome.org/show_bug.cgi?id=694064
2013-02-18 09:34:06 -05:00
d9770dcffb layout: Fix regressions
Commit 6b4f524620 removed the layer checks
_updateFullscreen ... this causes corruption when alt-tabbing out
of a fullscreen window so restore the check.

The commit also removed the screen sized check so we are no longer
setting all monitors to fullscreen. Fix that as well by using
window.is_screen_sized() to perform the check.

https://bugzilla.gnome.org/show_bug.cgi?id=694079
2013-02-18 14:13:37 +01:00
9936f97fcb overview: Sort windows by travel distance within rows
Sort windows inside the rows by there travel distance.

https://bugzilla.gnome.org/show_bug.cgi?id=690313
2013-02-18 12:02:22 +01:00
4cf09f46e7 messageTray: Fix indentation 2013-02-18 04:37:21 -05:00
6ffe3a424c grabHelper: Fix indentation
https://bugzilla.gnome.org/show_bug.cgi?id=694038
2013-02-18 04:31:45 -05:00
641794d458 layout: Reword a comment
I don't even know what this was trying to say before...
2013-02-18 04:31:45 -05:00
73d9ac6c01 overviewControls: synchronize viewSelector fade and controls slide
If the animation time is not the same for these two, the translation
will be adjusted to the allocation during the tween, resulting in a jump
in the animation.

https://bugzilla.gnome.org/show_bug.cgi?id=694035
2013-02-18 00:30:04 -05:00
3205d1e16a windowManager: Fix fallback path for icon_geometry animation
It doesn't seem like this code was ever tested.

https://bugzilla.gnome.org/show_bug.cgi?id=694052
2013-02-17 18:57:42 -05:00
ec61004622 Overview: clip contents to the allocation
If they're not clipped, overview controls can leak to secondary monitors
while animating.
2013-02-17 22:38:40 +01:00
ae93d258a5 layout: Call meta_window_* on the meta_window rather then on the actor 2013-02-17 21:50:23 +01:00
12845f0eef overviewControls: reset any translation when sliding in for drag
Fixes the dash not sliding when dragging an app from the search results.
2013-02-17 15:48:25 -05:00
6b4f524620 layout: Use window.is_monitor_sized
Mutter now provides a method for this, so use it.
2013-02-17 21:43:11 +01:00
dec0baa147 Remove gap between windows when switching workspaces
Previously there was a distracting gap between workspaces with two full-screen
applications.

https://bugzilla.gnome.org/show_bug.cgi?id=685849
2013-02-17 21:05:55 +01:00
340609d149 Fix XDND regression
The top_window_group blocks the panel elements from being found by the XDND
pick so hide the whole group from picks as we never attempt to pick its contents
anyway.
2013-02-17 20:52:00 +01:00
9a30c3d722 messageTray: Allow dwell to work in the overview
Special case the overview in _trayDwellTimeout because the
tray is hidden by default in the overview now.
2013-02-17 20:16:52 +01:00
e19a927579 overviewControls: Fix reallocation cycles
Instead of setting a new height, let the Clutter relayout
machinery do the work for us.
2013-02-17 13:53:24 -05:00
53a595885a pressure-barrier: don't discard grabbed events when overview is grabbing
Since we still want to trigger the message tray in that case.

https://bugzilla.gnome.org/show_bug.cgi?id=693987
2013-02-17 13:12:55 -05:00
ebd7406386 main: remove unused function
https://bugzilla.gnome.org/show_bug.cgi?id=693987
2013-02-17 13:12:55 -05:00
66cdbc5cad overviewControls: account for the search entry in each control
Account for the search entry space at the bottom (the former message
tray clone) individually in each side control, instead of packing
another actor in the overview.
This allows us to extend the central view all the way to the bottom,
while still keeping controls centered vertically.

https://bugzilla.gnome.org/show_bug.cgi?id=693987
2013-02-17 13:12:55 -05:00
060c049056 messageTray: remove overview special handling
Don't show the message tray in the overview by default. From now on the
message tray in overview behaves as regularly, i.e. it will slide up the
overview on Super+M keypress.

https://bugzilla.gnome.org/show_bug.cgi?id=693987
2013-02-17 13:12:54 -05:00
31270c36d2 Updated Polish translation 2013-02-17 18:36:20 +01:00
0aa26e9134 Calendar: don't initialize the event source synchronously
We don't want to block the shell start to load evolution-data-server
and open all calendars (which can involve network connections)

https://bugzilla.gnome.org/show_bug.cgi?id=694030
2013-02-17 16:06:00 +01:00
b45a2d7335 ScreenShield: don't add the source actor directly to the boxlayout
StBoxLayout always fills on the orthogonal direction, so the icon
becomes distorted as the layout grows to accomodate more details.
Instead, use a bin that aligns at the start.

https://bugzilla.gnome.org/show_bug.cgi?id=693822
2013-02-17 14:53:41 +01:00
07676d483d MessageTray: don't clear the banner text when adding to the body
ScreenShield uses Notification.bannerBodyText to fill the body of detailed
notifications, so use a separate boolean property to indicate it was already
added to the body.

https://bugzilla.gnome.org/show_bug.cgi?id=693822
2013-02-17 14:53:41 +01:00
6436452bc9 Updated Galician translations 2013-02-17 13:51:33 +01:00
60ad374514 Updated Brazilian Portuguese translation 2013-02-16 18:10:09 -02:00
b611ddda5e messageTray: Close menu when clicking outside of the menu 2013-02-16 19:55:59 +01:00
186bd156dd messageTray: Allow opening the context menu on long press
Right click does not work for touch devices, so support opening the menu
using long press as well.

https://bugzilla.gnome.org/show_bug.cgi?id=693887
2013-02-16 19:37:26 +01:00
3628c81885 grabHelper: Ignore key focus changes when ungrabbing
Calling onUngrab() may change key focus, either directly or
indirectly (e.g. hiding the actor). Such key focus changes
would cause an extra actor to be ungrabbed, so make sure to
ignore such focus changes while we're ungrabbing.

https://bugzilla.gnome.org/show_bug.cgi?id=693975
2013-02-16 13:33:45 -05:00
180000a531 grabHelper: Track the grab before trying to set key focus
If we don't this for a nested grabFocus grab, the notify::key-focus
will be called, not think that the new key focus is part of the
grab, and cancel the full grab. This leaves the grab helper in an
inconsistent and confused state, as the grab is pushed onto the
grab stack after.

https://bugzilla.gnome.org/show_bug.cgi?id=693975
2013-02-16 13:33:45 -05:00
5f995c64d4 grabHelper: Correct typo preventing focus-window-changed disconnect
While debugging, I found that the signal to focus-window-changed
was never getting disconnected, making a call to ungrab every time
the focus window changed, even if there were no focus grabs anymore.

https://bugzilla.gnome.org/show_bug.cgi?id=693975
2013-02-16 13:33:45 -05:00
fe2c2014de grabHelper: Use a mode: js header
This brings us into consistency with the rest of the modelines

https://bugzilla.gnome.org/show_bug.cgi?id=693975
2013-02-16 13:33:45 -05:00
754df5ebe2 shell-app-system: Ensure that we correctly order subsearches
As we use g_slist_prepend for efficiency when building the list
of results before ordering it, we need to make sure we traverse
the list of previous results backwards so that it's built in
the same order.

https://bugzilla.gnome.org/show_bug.cgi?id=693936
2013-02-16 13:33:45 -05:00
d8b7ac9044 viewSelector: block show apps button checked signal when resetting state
When we reset the state of the checked button due to the overview
showing and hiding, block the normal checked callback and immediately
switch to the workspaces page, so that windows seamlessly fade in.

https://bugzilla.gnome.org/show_bug.cgi?id=693974
2013-02-16 13:26:38 -05:00
5f3b04ecbd viewSelector: make the old page fade-out optional
When leaving the overview, we don't want the old page to fade out, but
we want to fade in the windows directly.

https://bugzilla.gnome.org/show_bug.cgi?id=693974
2013-02-16 13:26:38 -05:00
1f1aba4a32 viewSelector: always reset search string when entering overview
Instead of relying on the apps button callback to do it for us.

https://bugzilla.gnome.org/show_bug.cgi?id=693974
2013-02-16 13:26:38 -05:00
1390cd4048 viewSelector: don't use resetShowAppsButton when pressing escape
Set the checked property explicitly. This is because
resetShowAppsButton() will only be used to flip the button state
blocking the page change in a future commit.

https://bugzilla.gnome.org/show_bug.cgi?id=693974
2013-02-16 13:26:38 -05:00
31cdac7bfb overview: change _hideInProgress to visibleTarget
And use it in overviewControls. When we moved this code from overview.js
to overviewControls.js we lost a condition so we now slide in controls
even when going back from the overview, which looks bad.

https://bugzilla.gnome.org/show_bug.cgi?id=693974
2013-02-16 13:26:38 -05:00
0705901bce overviewControls: reset translation as well when entering overview
https://bugzilla.gnome.org/show_bug.cgi?id=693974
2013-02-16 13:26:38 -05:00
87495575bc search: Don't run a full search for equal terms
Due to the timeout on the search entry, it's possible for
a very quick edit to the entry to launch a subsearch on the
same search string.

https://bugzilla.gnome.org/show_bug.cgi?id=693937
2013-02-16 13:24:21 -05:00
b292bba092 search: Simplify and correct behavior for substring searches
Early on, search was based on a list of terms, which was like a set
of tags, in that terms were OR'd, and that order didn't matter. As
such, modifying any one of the terms wouldn't produce new results.
Nowadays, providers take the order into account, so a substring
should only be the case if new terms are added to the end.

https://bugzilla.gnome.org/show_bug.cgi?id=693935
2013-02-16 13:24:21 -05:00
5c7f0a0ad8 WindowOverlay: remove animations when relayouting
If WindowOverlay.relayout() is called without animation, we must stop
any preexisting animation, otherwise it will continue to run with the
previous parameters and cause the overlay to end up in the wrong position.

https://bugzilla.gnome.org/show_bug.cgi?id=693970
2013-02-16 19:08:02 +01:00
2df86e1966 shell-keybinding-modes: Don't use expression references
This confuses the gobject-introspection scanner, silently
making SHELL_KEYBINDING_MODE_NONE into -1.

https://bugzilla.gnome.org/show_bug.cgi?id=693940
2013-02-16 11:44:47 -05:00
41406e3be2 overviewControls: use translations for explicit slide in/out
The slideX property controls the allocation of the view selector; since
we now know when there are no visible views from the page-empty signal,
we can use it to set the full slideX for the next page at that time,
allowing the new view to fade in with the right width.
This allows us to use simple x translations for the side components when
switching pages, keeping the noise due to resizes at the minimum.

The slideX resize for now is kept for DnD, and will always be needed for
the thumbnails box when showing the windows page.

https://bugzilla.gnome.org/show_bug.cgi?id=693924
2013-02-15 23:47:13 -05:00
7ed4bd67b8 overviewControls: split out an util function
Will be used later

https://bugzilla.gnome.org/show_bug.cgi?id=693924
2013-02-15 23:47:12 -05:00
8ce3531de8 overviewControls: make dash private
https://bugzilla.gnome.org/show_bug.cgi?id=693924
2013-02-15 23:47:12 -05:00
579e53f02c overview: move controls visibility handling to a separate object
This keeps the core code of the overview clean and will help
coordinate animations.

https://bugzilla.gnome.org/show_bug.cgi?id=693924
2013-02-15 23:47:12 -05:00
1379c6b404 overview: move ctrlAltTab handling to Dash
This is also what the panel does.

https://bugzilla.gnome.org/show_bug.cgi?id=693924
2013-02-15 23:47:12 -05:00
153d3045ed viewSelector: emit a signal when the view is empty
This is very a useful point in time when the size of the side controls
can change without affecting any visible view. Emit a signal at that
time.

https://bugzilla.gnome.org/show_bug.cgi?id=693924
2013-02-15 23:47:12 -05:00
278686dc48 viewSelector: don't crossfade between pages
Instead, wait until the first animation is completed before switching to
the second page.

https://bugzilla.gnome.org/show_bug.cgi?id=693924
2013-02-15 23:47:12 -05:00
9b9f33bc8b Use the right conversion from cairo to GdkPixbuf
GdkPixbuf is stored in memory as individual bytes, in strict red-green-blue-alpha
order, while cairo image surfaces are stored as 32bits units whose
order depends on the endianess.
It is probably possible to do something better, taking advantage
of cogl and the GL using the actual component order, but for now
it is easier to use the GDK utility to convert the cairo surface.

https://bugzilla.gnome.org/show_bug.cgi?id=693931
2013-02-16 04:09:21 +01:00
24984458de networkAgent: Don't register/unregister, just refuse to handle requests
https://bugzilla.gnome.org/show_bug.cgi?id=693746
2013-02-15 16:50:12 -05:00
e3eb4a20a5 layout: Remove _addBarrierEvent
Moving it inline to when we get the event means that we can
remove the duplicate call to _getDistanceAcrossBarrier.

https://bugzilla.gnome.org/show_bug.cgi?id=693854
2013-02-15 16:49:46 -05:00
001bbd36f5 layout: Fix old event removal
If we increment our index variable while looping, this means that
firstNewEvent will be one higher than it should. With a length 1
array, all events will be removed, so this has a cascading effect
that events will not be stored at all.

https://bugzilla.gnome.org/show_bug.cgi?id=693854
2013-02-15 16:49:46 -05:00
382cc52baa NetworkMenu: use async initialization for libnm-glib objects
NMClient recently got more heavyweight, with a property holding supported
connections. As fully initializing a NMObject is a recursive operation
and requires multiple DBus calls, switch to async initalization for NMClient
and NMRemoteSettings.

https://bugzilla.gnome.org/show_bug.cgi?id=683288
2013-02-15 21:37:04 +01:00
2af368ab82 Updated Lithuanian translation 2013-02-15 21:49:34 +02:00
f514160fae unlockDialog: fix another occurrence of add_watch => add_idle_watch
https://bugzilla.gnome.org/show_bug.cgi?id=693909
2013-02-15 12:41:17 -05:00
30e4dcef90 keyboard: Don't show the keyboard when enabled
https://bugzilla.gnome.org/show_bug.cgi?id=692773
2013-02-15 18:23:55 +01:00
4618e11406 screenShield: Fix crash with the idle monitor 2013-02-15 08:01:00 -05:00
f887b77e36 Updated Serbian translation 2013-02-15 12:25:30 +01:00
fb527139a0 build: Bump gtk+ requirement to 3.7.9
Needed by 7fc2b33a0a
2013-02-15 09:41:01 +01:00
261fbef516 Update to new GnomeIdleMonitor API
https://bugzilla.gnome.org/show_bug.cgi?id=688227
2013-02-15 09:13:02 +01:00
32e8a9b377 keyboard: respect "center" alignment of keys
https://bugzilla.gnome.org/show_bug.cgi?id=679925
2013-02-15 12:27:43 +09:00
5f95351074 tweener: follow org.gnome.desktop.interface enable-animations
Disable the animations by making the animation time a very small value.

https://bugzilla.gnome.org/show_bug.cgi?id=655746
2013-02-14 20:42:53 -05:00
1c57c0e651 endSessionDialog: don't use Tweener for the timer
We want to make Tweener short-circuit animations when resources are
constrained, so this is not going to work.
Instead, use a one-second timeout until the seconds left reach zero.

https://bugzilla.gnome.org/show_bug.cgi?id=655746
2013-02-14 20:42:53 -05:00
ec6ee27f29 [l10n] Updated Catalan (Valencian) translation 2013-02-15 01:13:55 +01:00
70234edd02 [l10n] Updated Catalan translation 2013-02-15 01:13:49 +01:00
7e24a696bd ibusCandidatePopup: Fix cursor positioning
After moving the dummy source actor, we still have to poke the
boxpointer so that it gets repositioned.

This has always been broken but went unnoticed until now since none of
the commonly used engines currently depend on this method. Thanks to
Mathieu Bridon for pointing it out.

https://bugzilla.gnome.org/show_bug.cgi?id=691902
2013-02-15 00:26:47 +01:00
c3ed936776 ibusCandidatePopup: Make candidates reactive to pointer clicks
Allow for candidates to be selected by clicking on them.

https://bugzilla.gnome.org/show_bug.cgi?id=691902
2013-02-15 00:26:47 +01:00
235ec7cb2e CandidateArea: make setOrientation() public
setCandidates() has too many arguments and setting the orientation
isn't particularly related with it. It might also be useful to switch
orientation without changing the candidates.

https://bugzilla.gnome.org/show_bug.cgi?id=691902
2013-02-15 00:26:47 +01:00
4f8586d81d ibusCandidatePopup: Add pagination buttons
Makes switching candidates pages with pointer clicks possible.

https://bugzilla.gnome.org/show_bug.cgi?id=691902
2013-02-15 00:26:47 +01:00
6cd1e38425 ibusCandidatePopup: Style updates according to design mockups
Make it look more like the mockups.

In order to do that we stop using PopupMenu and friends as it doesn't
really buy us anything and just makes it more cumbersome to add the
style classes we need.

https://bugzilla.gnome.org/show_bug.cgi?id=691902
2013-02-15 00:26:47 +01:00
f7512dc540 viewSelector: don't change page on drag begin
We now slide in controls as appropriate when a drag is started.

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2013-02-14 18:18:22 -05:00
45415a742f overview: hide side controls when searching
Hide the elements when the search is active. Show them if the search
is cancelled.

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2013-02-14 18:18:22 -05:00
2247065dc5 dash: add a SlidingControl for the dash actor
Will be needed to hide and show it when searching.

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2013-02-14 18:18:22 -05:00
c550e2ccf5 workspacesView: simplify code
We always pass the same coordinates to setGeometry and setClipRect in
WorkspacesView; remove the latter to simplify code.

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2013-02-14 18:18:22 -05:00
bc75e5ec44 workspace: include the close button overlap in the right side chrome
As we do for the top side; the border is overlaied in the top/right
corner.

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2013-02-14 18:18:22 -05:00
5b39496008 overview: set side controls visibility on view page change
For now use the same behavior as before - hide the workspace thumbnails
when showing the applications page.

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2013-02-14 18:18:21 -05:00
4016da6632 viewSelector: add a method to get the currently active page
https://bugzilla.gnome.org/show_bug.cgi?id=682050
2013-02-14 18:18:21 -05:00
f2edcb9bdf viewSelector: notify on active page changes
In order to do this, we also need to move the assignment of
this._activePage from the animation onComplete callback to the function
itself.

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2013-02-14 18:18:21 -05:00
3d8a87563d workspaceThumbnail: pack ThumbnailsBox in the overview directly
Use a SlidingControl subclass and pack that in the overview group
directly.

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2013-02-14 18:18:21 -05:00
93bde0cae2 Introduce a SlideLayout layout manager
SlideLayout is a fixed layout that takes care of requesting and
allocating the right sizes so its contents can slide horizontally as the
actor is resized.
Sliding is controlled with a slideX and slideDirection properties, which
do the right thing wrt. RTL automatically.

Also add a SlidingControl base class that will be used by the overview
to pack and slide the workspace thumbnail switcher and the dash.

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2013-02-14 18:18:21 -05:00
30179bb60d popupMenu: Treat a menu will all invisible menu items as empty
As an example, a menu that has only settings actions might
be "empty" if allowSettings is false.

https://bugzilla.gnome.org/show_bug.cgi?id=681540
2013-02-14 18:16:21 -05:00
498bc21133 theme: Fix style
https://bugzilla.gnome.org/show_bug.cgi?id=681540
2013-02-14 18:16:21 -05:00
af7a34521f ShellSearchProvider: Update documentation about 'name' and 'id'
Document and enforce that a meta ID corresponds to the result ID
that we've passed in. This does not break any existing search providers
as far as I'm aware.

https://bugzilla.gnome.org/show_bug.cgi?id=693836
2013-02-14 16:44:14 -05:00
7fc2b33a0a st-texture-cache: Use async variants of the icon loading API
While we were relying on gtk_icon_info_load_icon and friends being
thread-safe, there was no such guarantee, and recent caching that
was added to GTK+ made it non-threadsafe. To replace it, _async()
variants of the icon loading code were added that are thread-safe.
Use those instead of using our own worker threads.

https://bugzilla.gnome.org/show_bug.cgi?id=692845
2013-02-14 16:16:00 -05:00
a5d3f7785a st-texture-cache: Add one simple way to free texture load data
https://bugzilla.gnome.org/show_bug.cgi?id=692845
2013-02-14 16:09:04 -05:00
f1cdce38a6 st-texture-cache: Remove a no-op scale pixbuf
We already passed GtkIconTheme the size when it wanted to load it,
so it knows the desired size and will scale it to what we want.

https://bugzilla.gnome.org/show_bug.cgi?id=692845
2013-02-14 16:09:04 -05:00
65e4652142 viewSelector: Make sure not to start searching when we only have whitespace
As this is thrown out before we actually query providers, we end up with
a confusing screen that says "Searching..." that won't ever get results.

https://bugzilla.gnome.org/show_bug.cgi?id=693458
2013-02-14 14:13:50 -05:00
569797d7c5 searchDisplay: Remove doSearch
This is nothing but a middle man, as the view selector already owns
the search system. We want to start being a bit more tricky with what
we do with the search system so that we ignore whitespace, so let's
cut the middle-man out now.

https://bugzilla.gnome.org/show_bug.cgi?id=693458
2013-02-14 14:05:37 -05:00
35a7c94cd1 search: Fix style
https://bugzilla.gnome.org/show_bug.cgi?id=693458
2013-02-14 14:05:26 -05:00
c8365b7444 viewSelector: Don't strip whitespace from search strings
This is already done by the search system itself.

https://bugzilla.gnome.org/show_bug.cgi?id=693458
2013-02-14 14:04:00 -05:00
8c5a343729 accessibility: Remove some unused constants 2013-02-14 13:45:40 -05:00
22ddec46ab Place popup menus and other override-redirect windows on top of the panel
https://bugzilla.gnome.org/show_bug.cgi?id=633620
2013-02-14 13:45:39 -05:00
a4e70ba4ca layout: Restructure input region and struts code
Have two branches, one for input region and one for struts. This
makes it easier to skip one of the branches, like in the case where
we want to skip input regions if we have a popup menu visible.

https://bugzilla.gnome.org/show_bug.cgi?id=633620
2013-02-14 13:24:04 -05:00
978ac65cae layout: Don't use reparent
reparent() defines the new actor stacking order based on the
existing depth of the actor, which is flat out wrong. Simply
remove the actor from its old parent and add the new one in.

https://bugzilla.gnome.org/show_bug.cgi?id=633620
2013-02-14 13:24:04 -05:00
eb5a836822 screenshot: Write metadata to png file
Use GdkPixbuf rather then cairo_surface_write_to_png_stream when saving
screenshots because this allows us to embedded metadata into the file which
is used by the background control panel to filter out screenshots.

https://bugzilla.gnome.org/show_bug.cgi?id=693737
2013-02-14 19:21:15 +01:00
ad1b9b71ae grabHelper: Merge _navigateActor() with its only user
https://bugzilla.gnome.org/show_bug.cgi?id=693570
2013-02-14 19:17:32 +01:00
60257f422a grabHelper: Restore the actually saved focus on ungrab
GrabHelper saves the actor that had key focus when taking over the grab
(if any). On ungrab, the key focus is either restored or moved to some
child of the saved actor. The latter is unexpected and causes some odd
behavior, so don't be fancy and only restore the actual focus.

https://bugzilla.gnome.org/show_bug.cgi?id=693570
2013-02-14 19:17:32 +01:00
6576d4852c Updated Malayalam Localization 2013-02-14 22:17:03 +04:00
c30661c44c NotificationDaemon: support sound in notifications
The notifications spec has two hints for playing a sound, sound-file
and sound-name. We can support them using the existing code that
wraps libcanberra.

https://bugzilla.gnome.org/show_bug.cgi?id=642831
2013-02-14 18:54:08 +01:00
2d9cf195d7 layout: Remove obsolete message tray <=> OSK interaction code
Message tray and on-screen keyboard are now exclusive, so remove
all code that shuffles boxes around to make it possible to show
both at the same time.

https://bugzilla.gnome.org/show_bug.cgi?id=662687
2013-02-14 17:31:32 +01:00
a21e76caab keyboard: Remove 'tray' button
Now that message tray and keyboard are never shown together, the
odd 'tray' button is pointless, kill it.

https://bugzilla.gnome.org/show_bug.cgi?id=662687
2013-02-14 17:31:32 +01:00
a534a6bf09 messageTray: Always hide while the OSK is up
The message tray currently operates in three modes: in the overview,
normal, and while the on-screen keyboard is up. The last case is
particularly odd, and exclusively used for chat-notifications. As
users can still use the Chat application directly on touch-only
devices, the additional mode isn't really justified, so remove it.

https://bugzilla.gnome.org/show_bug.cgi?id=662687
2013-02-14 17:31:32 +01:00
2b0f219bfd Updated Spanish translation 2013-02-14 16:54:22 +01:00
33dde63256 Main: move the stage hierarchy initialization to LayoutManager
It is cleaner to concentrate all layout and chrome management
in one place, instead of having it scattered around the codebase.

https://bugzilla.gnome.org/show_bug.cgi?id=682429
2013-02-14 01:12:24 -05:00
4dc9540325 ScreenShield: try harder to become modal, and catch failures
The screenshield was not checking the return value of pushModal(), meaning
that it believed it was fully locked when it was not. Later, calling
popModal() would fail, causing an exception and blocking the unlock.
Now when we fail we include an explanatory message, pointing the user
to the actual cause of the issue.

https://bugzilla.gnome.org/show_bug.cgi?id=689106
2013-02-13 17:46:39 +01:00
2b1a661614 ScreenShield: use a longer animation when locking manually
The curtain animation looks jerky at its current speed, and more so if
we blank the screen immediately at the end. Make it a little slower and
it becomes more confortable.

https://bugzilla.gnome.org/show_bug.cgi?id=691964
2013-02-13 15:14:37 +01:00
2138fc2349 messageTray: Make clear item insensetive when there is nothing to clear
There is no point in having a menu entry that does nothing so set the item
to insensitive when there are no items to clear.
2013-02-13 13:12:03 +01:00
cdc8dd88e9 Updated Uyghur translation
Signed-off-by: Gheyret Kenji <gheyret@gmail.com>
2013-02-13 19:59:00 +09:00
2a5f8e84bb js: Use proper Unicode ellipsis (…) instead of three dots
https://bugzilla.gnome.org/show_bug.cgi?id=689542
2013-02-12 17:05:00 -05:00
6f8540f25a Use nice Unicode for date formatting
The ratio character is nicer aligned for this use than the
stock colon, and a thin space is all that is needed before am/pm.

https://bugzilla.gnome.org/show_bug.cgi?id=689251
2013-02-12 10:16:47 -05:00
151c699f5c EndSessionDialog: Add a Close method
When there are multiple sessions, we may get a polkit dialog in
response to clicking 'Reboot' in the end session dialog. If the
polkit dialog gets canceled or otherwise ends unsuccessfully,
we are left with the lightbox that the end session dialog puts
up when 'Reboot' is clicked. To remove the lightbox and make the
session fully functional again, gnome-session will call Close.

https://bugzilla.gnome.org/show_bug.cgi?id=688915
2013-02-12 10:15:51 -05:00
9133f6d352 messageTray: Add context menu
Add a context menu to the tray that allows cleaning up sources
and open the notification settings panel.

https://bugzilla.gnome.org/show_bug.cgi?id=691035
2013-02-12 08:39:21 +01:00
ab276e4e86 693611 fix 2013-02-12 01:00:11 +02:00
cdc4f85e47 shell-screenshot: Fix cursor fetching for non single window screenshots
We have to mark the cairo_surface dirty after modifying the data behind
cairo's back.

Also use CAIRO_FORMAT_ARGB32 rather then CAIRO_FORMAT_RGB24 for the
surface to be consistent with the rest of the code.

https://bugzilla.gnome.org/show_bug.cgi?id=691715
2013-02-11 19:53:54 +01:00
b2fb84e361 Don't ask for a password on shutdown
Show a warning and the list of active users instead when
multiple sessions are present.

https://bugzilla.gnome.org/show_bug.cgi?id=693385
2013-02-11 19:39:00 +01:00
4ab1e893f0 userMenu: Handle the case where the user has no icon file
Fix a crash in UserAvatarWidget.update that gets triggered when the user has
no icon file.

https://bugzilla.gnome.org/show_bug.cgi?id=693385
2013-02-11 19:39:00 +01:00
70bc3d178b Updated Spanish translation 2013-02-11 14:38:12 +01:00
777f145128 st-clipboard: Use GDK for the event filters instead of Clutter
Instead of using Clutter to add an event filter for X events it now
uses the GDK API. The Clutter API won't work if Clutter is not using
an X11-based backend such as if Mutter is directly running with the
KMS backend. This is a step towards making Mutter be its own display
server and a step towards being a Wayland compositor. In this case GDK
will still be using the X backend because it will connect to the
headless X server.

https://bugzilla.gnome.org/show_bug.cgi?id=693438
2013-02-11 09:48:45 +00:00
2f0181ac44 Punjabi: Translation updated (aalam) 2013-02-10 23:38:16 +00:00
83b4b698a0 Updated Malayalam Localization 2013-02-10 18:18:21 +04:00
a10a294a2e st-im-text: Remove unused variable
https://bugzilla.gnome.org/show_bug.cgi?id=693522
2013-02-10 13:39:30 +01:00
34036a3b51 Updated Lithuanian translation 2013-02-09 22:43:42 +02:00
911bc03381 Updated Bulgarian translation 2013-02-09 09:47:25 +02:00
0240aeac8d Updated Uyghur translation
Signed-off-by: Gheyret Kenji <gheyret@gmail.com>
2013-02-09 10:25:34 +09:00
a43be186d3 lookingGlass: Remove old hack for static functions on interfaces
This has been fixed with the addition of interfaces in Gjs.
2013-02-08 14:31:08 -05:00
fb63f48d0a layout: Trigger the message tray by downward pressure
A pressure barrier is a barrier that activates after the user pushes
against the bottom of the screen in a short time. Implement this using
the new XInput 2.3 features that provide extended information about
pointer barriers, and use it so that pushing against the bottom of
the screen edge brings up the message tray.

https://bugzilla.gnome.org/show_bug.cgi?id=677215
2013-02-08 14:28:11 -05:00
c0279df3c6 layout: Port to the new mutter-based barrier wrappers
As pressure barriers need a signalling mechanism to provide
information about when and where they are hit, an object which
provides a signal is a more appropriate abstraction for a pointer
barrier than a functional ID-based approach. Mutter has gained
pointer barrier wrappers, so use its objects instead of ours.

https://bugzilla.gnome.org/show_bug.cgi?id=677215
2013-02-08 14:27:42 -05:00
e614b993f2 messageTray: Remove tray dwelling if we have new barrier features
If we have the new barrier features, then we should not activate the
old tray dwelling mechanism.

https://bugzilla.gnome.org/show_bug.cgi?id=677215
2013-02-08 14:27:42 -05:00
8035f30305 autorunManager: Define this._session as an instance of GnomeSession
This is a fallout from 526a16298c.

https://bugzilla.gnome.org/show_bug.cgi?id=693161
2013-02-08 18:55:38 +01:00
af0b5e6176 searchDisplay: Unset default result when clearing search
If we fail to do so, a previously set result may be activated by
hitting enter, even if a search did not turn up any actual results.
2013-02-08 17:52:39 +01:00
1bf996c705 loginManager: Fix canSuspend in non-logind path
It is supposed to be a NOP, but commit b91d9c2867 got it wrong.

https://bugzilla.gnome.org/show_bug.cgi?id=693162
2013-02-08 17:29:33 +01:00
33f69481f7 Don't rely on implicit type conversion in the scroll view shader
Before version 1.2 of GLSL it would not implicitly convert from int to
float which meant that if you compare a float variable with an integer
constant it will generate a compile error. In particular this means
that on GLES2 (which uses GLSL 1.0) the scroll view shader will not
compile on pedantic compilers, which includes Mesa. This patch just
changes it to use floating point constants.

https://bugzilla.gnome.org/show_bug.cgi?id=693339
2013-02-08 12:03:38 +00:00
354fb6b8c6 Updated Slovenian translation 2013-02-07 20:07:06 +01:00
d30477b6f8 css: Keep the panel height the same in all modes
Since the panel affects struts, if we change its height on mode
transitions, windows will move around which is particularly noticable
with maximized windows when coming out of the screen shield.

https://bugzilla.gnome.org/show_bug.cgi?id=692966
2013-02-07 18:25:36 +01:00
496b5d62ae css: Remove style transitions for the panel
This, together with the panel's transparent background in the screen
shield, has the unfortunate side-effect of showing the desktop
background in a brief flash while coming out of the screen shield.

https://bugzilla.gnome.org/show_bug.cgi?id=692966
2013-02-07 18:25:35 +01:00
3652002a68 screenShield: Pop modes before starting unlock animation
This way the top panel remains visible while the shield hiding
animation is ongoing instead of suddenly appearing just when it ends.

https://bugzilla.gnome.org/show_bug.cgi?id=692966
2013-02-07 18:25:35 +01:00
c0e5719271 keyboard: Don't use set_skip_paint
https://bugzilla.gnome.org/show_bug.cgi?id=693303
2013-02-07 05:43:50 -05:00
997f851031 keyboard: Make sure to set currentSource if we only have one source
This way, popping up and re-closing the switcher won't emit an error
trying to check for this._currentSource.

https://bugzilla.gnome.org/show_bug.cgi?id=693303
2013-02-07 05:43:50 -05:00
eaf184b585 st-entry: Implement middle click paste
https://bugzilla.gnome.org/show_bug.cgi?id=645019
2013-02-07 04:51:30 -05:00
0616261a94 st-clipboard: Add the ability to choose the clipboard type
https://bugzilla.gnome.org/show_bug.cgi?id=645019
2013-02-07 04:51:30 -05:00
d2c45f428f workspace, workspaceThumbnail: Fix another bad rebase
It seems I pushed an old version of the patch by accident.
2013-02-07 02:31:52 -05:00
6054ab35cb workspaceThumbnail: Fix bad rebase 2013-02-07 02:27:40 -05:00
5ba0c6404b main: Move getWindowActorsForWorkspace to layout
It's only used here.

https://bugzilla.gnome.org/show_bug.cgi?id=691746
2013-02-07 01:36:31 -05:00
f738c2be9d main: Remove isWindowActorDisplayedOnWorkspace
We now have a convenience method in mutter.

https://bugzilla.gnome.org/show_bug.cgi?id=691746
2013-02-07 01:36:31 -05:00
12ac2e5534 Switch all external uses of Main.panel.actor.height to the work area
https://bugzilla.gnome.org/show_bug.cgi?id=692680
2013-02-06 22:35:49 -05:00
60985b396a layout: Use the mutter functions for doing monitor math
https://bugzilla.gnome.org/show_bug.cgi?id=692680
2013-02-06 22:35:10 -05:00
d9b33e01ee layout: Replace uses of find(Monitor|Index)ForWindow with window.get_monitor()
Mutter already does the math for us, so we shouldn't have to do it.
2013-02-06 22:35:10 -05:00
0335f2726a workspace: Use mutter's work area calcuating rather than doing it manually
https://bugzilla.gnome.org/show_bug.cgi?id=692680
2013-02-06 22:35:10 -05:00
23717cc4d7 shell: Remove old jsapi-compat file
As we now depend on mozjs185, we know the API we'll be porting to.

https://bugzilla.gnome.org/show_bug.cgi?id=693284
2013-02-06 22:32:03 -05:00
4422f301b4 Updated Dutch translation 2013-02-06 21:53:57 +01:00
1981da1b4e Updated slovak translation 2013-02-06 18:06:32 +01:00
a130f50107 Updated Galician translations 2013-02-06 17:57:56 +01:00
c40198046c Updated slovak translation 2013-02-06 17:39:36 +01:00
e5b44a3970 [l10n] Update Japanese translation 2013-02-06 23:15:57 +09:00
5e7443706b [l10n] remove obsolet message. 2013-02-06 23:08:22 +09:00
39e75e932a [l10n] remove location from ja.po 2013-02-06 23:03:22 +09:00
b8a8edc513 Don't try to use GLX if Cogl isn't using that Winsys
Instead of directly using symbols from GLX to check for the swap event
notification, the plugin now first verifies that the Cogl renderer is
actually using the GLX winsys and then indirectly fetches the pointers
for the GLX functions using cogl_get_proc_address. That way it will
continue to work if Cogl is using an EGL winsys.

Nothing in the Gnome Shell plugin now directly uses symbols from libGL
so we don't need to link to it. This helps to avoid problems linking
against two GL APIs when cogl is using a non-GL driver such as GLES2.

https://bugzilla.gnome.org/show_bug.cgi?id=693225
2013-02-06 00:35:54 +00:00
b1051365fa Bump version to 3.7.5
Update NEWS.
2013-02-06 01:12:32 +01:00
a935cd0c5d build: Update POTFILES.in 2013-02-06 01:06:16 +01:00
62ce90795f userMenu: Remove explicit screen lock on suspend
This is now handled by the screen shield itself.

https://bugzilla.gnome.org/show_bug.cgi?id=686482
2013-02-06 01:01:52 +01:00
6bcad45392 screenShield: Use inhibitors to lock screen on suspend
If the screen lock is enabled, lock the screen before suspension.
When using systemd, this will cover both explicitly suspending from
the user menu and suspension initiated by g-s-d (lid close, power
button).

https://bugzilla.gnome.org/show_bug.cgi?id=686482
2013-02-06 01:01:52 +01:00
8747c0c58d loginManager: Add inhibitor API
If screen locking is enabled, the screen shield should drop down
on suspend. Currently this is achieved by either explicitly locking
the screen (when selecting "Suspend" from the user menu) or by
relying on g-s-d delaying the suspension enough time for the shield
to get into place (lid close, power button).
Systemd inhibitors offer a safer way to ensure that the screen is
locked before going to sleep, so add a small abstraction for them
in the loginManager - with inhibitors being a systemd-only feature,
the ConsoleKit path only has a dummy implementation.

https://bugzilla.gnome.org/show_bug.cgi?id=686482
2013-02-06 01:01:52 +01:00
b91d9c2867 loginManager: Make suspend() a NOP in the ConsoleKit path
UPower will remove its suspend support eventually, and g-s-d already
depends on logind for power management.

https://bugzilla.gnome.org/show_bug.cgi?id=693162
2013-02-06 01:01:52 +01:00
140935bca3 shell-docs: remove leftover mobile-providers include 2013-02-06 01:33:51 +02:00
67c7abaac3 main: Fix typo
This fixes a regression from commit fcc32fca.
2013-02-05 20:42:27 +01:00
11bd7dc7de scroll-view-fade: Remove explicit GLSL version
Cogl sets this for us since commit 2701b93f159bf2d3387cedf2d06fe921ad5641f3.

Setting it twice is illegal and causes compile failures:
error C0204: version directive must be first statement and may not be repeated.
2013-02-05 20:32:08 +01:00
406f65cb23 [l10n] Updated German translation 2013-02-05 20:31:22 +01:00
2344706462 extensionSystem: Allow extensions to provide mode-specific styling
Allow extensions to integrate with modes that provide a distinct
style by providing a mode-specific stylesheet.

https://bugzilla.gnome.org/show_bug.cgi?id=693219
2013-02-05 19:54:17 +01:00
fcc32fca7e sessionMode: Add stylesheetName property
This will allow to visually differentiate modes by providing
separate styling.

https://bugzilla.gnome.org/show_bug.cgi?id=693219
2013-02-05 19:54:17 +01:00
7f587fd4da messageTray: Add 'source-removed' signal
While it is possible to keep track of removed sources by tracking
their summary items' actor:.destroy signal, a dedicated signal
mirroring the existing 'source-added' one is more convenient.

https://bugzilla.gnome.org/show_bug.cgi?id=693220
2013-02-05 19:54:17 +01:00
f47c0601ce panel: Add :overview pseudo class while in overview
The panel used to provide an .in-overview class which was removed after
the theme stopped using it. Classic mode should use a different top bar
style in the overview, so bring it back (but use a pseudo class this
time for consistency with MessageTray and ActivitiesButton).

https://bugzilla.gnome.org/show_bug.cgi?id=693218
2013-02-05 19:54:17 +01:00
3d638c692e bluetooth: use "Set Up" instead of "Set up"
Capitalize prepositions if they are part of a verb phrase

https://bugzilla.gnome.org/show_bug.cgi?id=689589
2013-02-05 13:21:13 -05:00
e6ef3ea24f power: consistently use Title Case
https://bugzilla.gnome.org/show_bug.cgi?id=689589
2013-02-05 13:20:55 -05:00
8dd880d0c8 mobile-providers: use libnm-gtk to work with mobile providers
This commit removes all the code in charge of playing with the database of
mobile providers, which was originally included in order to perform
MCCMNC->OperatorName and SID->OperatorName conversions.

This logic is now exposed by libnm-gtk.

https://bugzilla.gnome.org/show_bug.cgi?id=688943
2013-02-05 17:59:39 +01:00
393a3a079f Updated Spanish translation 2013-02-05 17:13:00 +01:00
ceb035e9b3 network: treat LTE-only modems as GSM ones
LTE-only modems need to be treated as GSM/HSPA modems, as they all are 3GPP
modems and they all need the same kind of configuration (APN, user, password,
PIN...).

https://bugzilla.gnome.org/show_bug.cgi?id=688144
2013-02-05 15:49:39 +01:00
e38570437e network: add support for the new 'ModemManager1' interface
ModemManager >= 0.7 comes with a new DBus interface. This patch makes the shell
work with the new interface if the modem is detected as being exposed by the new
ModemManager (based on the device.udi string reported by NM).

https://bugzilla.gnome.org/show_bug.cgi?id=687359.
2013-02-05 15:39:38 +01:00
f0203d1f19 general: Use & instead of 'and' for Settings panels
UI consistency follow up from bug 676562

https://bugzilla.gnome.org/show_bug.cgi?id=689590
2013-02-05 08:54:16 -05:00
e0c5a61be5 loginManager: Remove sessionActive property
It is unused after switching to GnomeSession:SessionIsActive, so
remove it together with shell_session_is_active_for_systemd().

https://bugzilla.gnome.org/show_bug.cgi?id=693161
2013-02-05 00:01:33 +01:00
526a16298c Replace LoginManager:sessionActive with GnomeSession:SessionIsActive
Gnome session started to track the session's active state a while
ago, so use that instead of our own ConsoleKit/logind abstraction
in LoginManager.

https://bugzilla.gnome.org/show_bug.cgi?id=693161
2013-02-05 00:01:33 +01:00
443f02cd92 [l10n] Updated Italian translation. 2013-02-04 18:14:28 +01:00
b682c8e052 main: Move KeybindingMode into Shell
Having the definition in C instead of Javascript allows sharing
the corresponding header with gnome-settings-daemon.

https://bugzilla.gnome.org/show_bug.cgi?id=643111
2013-02-04 16:48:40 +01:00
d07c8dcd9c volume: Suppress a critical warning
gvc_mixer_stream_get_port() emits a warning in the no-ports case,
which we can avoid by rearranging the check for that case.

https://bugzilla.gnome.org/show_bug.cgi?id=693049
2013-02-04 12:42:36 +01:00
9bcce4f271 Fix typo in gnome-shell-extension-prefs
Environment variables should be checked for zero length,
instead of non-zero. This prevented the script to run
correctly in a jhbuild shell for example.

https://bugzilla.gnome.org/show_bug.cgi?id=693031
2013-02-04 12:00:42 +01:00
eab497a814 layout: Call _startupAnimation() on init
Commit 8e231cb2ec inadvertently removed
it.

https://bugzilla.gnome.org/show_bug.cgi?id=693067
2013-02-04 11:23:38 +01:00
a8c8df6ee8 StImText: don't translate keyboard state again
Clutter translates keyboard state internally, and clears the lock bits
from modifier state, so translating again results in the wrong keysym.
Given that Clutter already gives us a fine keysym, we don't need this.

https://bugzilla.gnome.org/show_bug.cgi?id=692586
2013-02-03 16:45:28 +01:00
b3549f421d UserMenu: don't show a lock icon when we're not actually locked
The screen shield can now be active (forcing the lock-screen session mode)
without being locked.

https://bugzilla.gnome.org/show_bug.cgi?id=693007
2013-02-03 14:59:58 +01:00
ae0accb5a4 ScreenShield: separate notifications for active and locked
Now that we have an explicit active-but-not-locked state, we should
use different signals to notify changes. lock-status-changed is
renamed to active-changed, and a new locked-changed is introduced.

https://bugzilla.gnome.org/show_bug.cgi?id=693007
2013-02-03 14:59:58 +01:00
d367566db9 Updated Traditional Chinese translation(Hong Kong and Taiwan) 2013-02-02 18:39:58 +08:00
35812208e7 Updated Hebrew translation. 2013-02-02 12:06:39 +02:00
afaff724f6 Updated Uyghur translation
Signed-off-by: Gheyret Kenji <gheyret@gmail.com>
2013-02-02 13:19:42 +09:00
ec8dfb0cad doap: add myself to doap file
This is so hopefully darkxst's mango request will show
up for me to approve.
2013-02-01 17:30:35 -05:00
7798da8dff layout: Remove an unused variable
The tray barrier was removed when the hot corner to activate the message
tray was removed.

https://bugzilla.gnome.org/show_bug.cgi?id=677215
2013-02-01 12:55:01 -05:00
89a49ce72e windowManager: Respect icon geometry when minimizing
When using a dock or window-list with the shell, it makes sense for
us to minimize to the location requested rather than the activities
button.

https://bugzilla.gnome.org/show_bug.cgi?id=692997
2013-02-01 17:38:33 +01:00
655dce6a4b st-theme-node: Recalculate properties on stylesheet changes
As theme nodes keep a cache of matched properties, we need to make
sure to update it when the list of stylesheets changes. In particular
this fixes a regression from commit dc2ec0a8f9, which caused
extensions with stylesheets to crash the shell when re-enabled (for
instances when coming back from the lock screen).

https://bugzilla.gnome.org/show_bug.cgi?id=692994
2013-02-01 17:28:40 +01:00
2cf403a3e9 st-theme: Add "custom-stylesheets-changed" signal
StThemeNodes cache matched properties from stylesheets, so when the
list of custom stylesheets changes, the node may miss better matches
(when a stylesheet was added) or have pointers to invalid memory in
the list (when a stylesheet was removed).
In order to allow theme nodes to listen for stylesheet changes, add
an appropriate signal to StTheme.

https://bugzilla.gnome.org/show_bug.cgi?id=692994
2013-02-01 17:28:39 +01:00
4351ad2414 Updated Dutch translation 2013-02-01 17:00:13 +01:00
fd2944fa8d userMenu: Update logout item when always-show-log-out changes
https://bugzilla.gnome.org/show_bug.cgi?id=692996
2013-02-01 14:56:28 +01:00
f4626cf8ad workspace: Make previews of minimized windows translucent
Because the comment says so! Seriously, Jakub suggested this after
bug 682887 brought back a rebranded default shortcut for minimize.

https://bugzilla.gnome.org/show_bug.cgi?id=692999
2013-02-01 14:54:38 +01:00
65bf0d20e7 keyboard: Fix a warning
While we handle the case where ibus_bus_get_global_engine() returns
NULL, this case actually generates an exception we have to catch to
avoid some (harmless) console spam.

https://bugzilla.gnome.org/show_bug.cgi?id=692995
2013-02-01 13:06:00 +01:00
2f496de98a Updated Polish translation 2013-01-31 20:03:14 +01:00
644b8304ab ScreenShield: fix wrong signal value in ActiveChanged
ActiveChanged should be about active, not locked.

https://bugzilla.gnome.org/show_bug.cgi?id=691964
2013-01-31 18:25:31 +01:00
5c4570fcaa ScreenShield: fix regression in GDM greeter
The initial state of the lock screen is HIDDEN, and hideLockScreen
enforces that now, so reflect it in Clutter state too.

https://bugzilla.gnome.org/show_bug.cgi?id=692948
2013-01-31 16:57:38 +01:00
ac0bd3b116 Fix bad rebase and regression in 3f6f597 2013-01-31 16:07:16 +01:00
7ad881d0ec ScreenShield: emit lock-status-changed at the end of animation for manual locking too
gnome-settings-daemon uses lock-status-changed/ActiveChanged to drive
screen blanking, so must wait for the animation end before emitting it.

https://bugzilla.gnome.org/show_bug.cgi?id=691964
2013-01-31 14:10:21 +01:00
9a25224890 ScreenShield: lower the shield when the user is idle but before locking
In time span between idle and lock the shield should behave like autologin,
but should prevent accidental reactivation (for example when using a touch
screen) by showing the curtain.

https://bugzilla.gnome.org/show_bug.cgi?id=692560
2013-01-31 14:10:20 +01:00
3f6f597093 ScreenShield: only emit ActiveChanged at the end of the idle fading
gnome-settings-daemon wants to use ActiveChanged to drive screen
blanking policies.
I also added two big comments that should cover all cases, to clear
up what's happening when the idle timers fire.

https://bugzilla.gnome.org/show_bug.cgi?id=691964
2013-01-31 14:07:26 +01:00
9ab22fe551 ScreenShield: fix fallout from 8cb3884
We must remove music notifications before we're destroyed, otherwise
they get destroyed with us.
Also, integrate a review comment I previously forgot.

https://bugzilla.gnome.org/show_bug.cgi?id=685926
2013-01-31 13:45:57 +01:00
9794e71a86 Add policy for builtin message sources
Some notifications, despite being emitted by shell code, should appear
to be from application or "separable" system components. Do that by
associating them with a notification-daemon policy.

Note that for this to look really good, empathy should rename itself
to Chat.

https://bugzilla.gnome.org/show_bug.cgi?id=685926
2013-01-31 13:20:08 +01:00
8cb3884fae ScreenShield: decouple detailed notifications from resident notifications
The designs says that only music notifications should be shown in full
in the screenshield, the others should be either shown as a summary or
with very light details.

https://bugzilla.gnome.org/show_bug.cgi?id=685926
2013-01-31 13:20:08 +01:00
098bd4509b MessageTray: introduce configurable per-source notification policy
Allow message tray sources to provide a NotificationPolicy object,
that will configure how and if the source is displayed. For notification
daemon sources, this object is hooked to GSettings.

https://bugzilla.gnome.org/show_bug.cgi?id=685926
2013-01-31 13:20:08 +01:00
4dc5bac72f NotificationDaemon: improve associating sources with applications
Try harder to find a ShellApp for a notification source, by looking
at the PID, tray icon wm_class and desktop-entry libnotify hint.

https://bugzilla.gnome.org/show_bug.cgi?id=685926
2013-01-31 13:20:08 +01:00
b150869b51 MessageTray: clean up source tracking
Use the new Hash.Map class, and store signal connections along with
the source and summaryItem. This allows to remove sources without destroying
them.

https://bugzilla.gnome.org/show_bug.cgi?id=685926
2013-01-31 13:20:08 +01:00
daf8be9f78 Add a new Hash module
A simple implementation of the ES6 Map proposal, internally
done as a hash table, using System.addressOf() to support keys that
are arbitrary objects.
Should help replacing linear searches in various places around the shell.

https://bugzilla.gnome.org/show_bug.cgi?id=685926
2013-01-31 13:20:08 +01:00
d14cf1e80b Updated Basque language 2013-01-30 22:02:44 +01:00
605d34008e Updated Basque language 2013-01-30 22:00:50 +01:00
d3135414cc Updated Norwegian bokmål translation 2013-01-29 18:29:45 +01:00
b3f29b46cf NetworkMenu: defer calling syncSectionTitle until after pushing the device
syncSectionTitle looks at device list for the section, to understand if
the section should be visible or not, so obviously it needs to see the
new device.

I wonder when this broke.

https://bugzilla.gnome.org/show_bug.cgi?id=692749
2013-01-29 15:28:02 +01:00
4545719e18 MessageTray: disconnect signal to notification early
Similar to the previous patch, this adds robustness and prevents more
stacktraces and confusion in the state machine.

https://bugzilla.gnome.org/show_bug.cgi?id=692693
2013-01-28 21:02:46 +01:00
cc60afa31a Disconnect content-updated signal early
During the box pointer animation, other events can trigger an updateState,
losing the information that the summary is hiding and thus never disconnecting
the signals. Then, this stale connections can cause stacktraces, as they
fire when summaryBoxPointerItem is null.

https://bugzilla.gnome.org/show_bug.cgi?id=692693
2013-01-28 21:02:45 +01:00
85743ede7e MessageTray: fix reentrancy problem in hideNotificationCompleted
Hiding notificationWidget with a telepathy notification causes
unfocused to be emitted, which causes a reentrant updateState.
If another notification is queued, it is shown before the old
one is cleared.

https://bugzilla.gnome.org/show_bug.cgi?id=683986
2013-01-28 21:02:45 +01:00
8e231cb2ec layout: Merge Chrome and LayoutManager
The two classes have been gaining each other's functionality for a little
while, adding the new code wherever it was more convenient. Rather than
have a clear delineation between "This Manages Shell Chrome" and "This
Manages Shell Layout", I think it's better off if we just accept that
the responsibilities are pretty much the same.

https://bugzilla.gnome.org/show_bug.cgi?id=692677
2013-01-28 12:41:06 -05:00
72405cd43f layout: Shuffle code around
This will make the next diff much cleaner.

https://bugzilla.gnome.org/show_bug.cgi?id=692677
2013-01-28 12:41:06 -05:00
a9ad9d5e6d remoteSearch: fix fallout from RemoteSearchProvider refactor
RemoteSearchProvider.title was removed, but we were still using it in a
few places.

https://bugzilla.gnome.org/show_bug.cgi?id=692723
2013-01-28 12:34:19 -05:00
2d79c7333f main: Fix a bad rebase
This wasn't meant to be here...
2013-01-28 12:14:41 -05:00
7a79cfd76b keyboard: Do the initial redraw in _init
It doesn't seem like there's anything preventing us from doing this.

https://bugzilla.gnome.org/show_bug.cgi?id=692678
2013-01-28 12:10:32 -05:00
6600d6b6d9 status/power: Init the proxy asynchronously
Blocking the compositor is evil!

https://bugzilla.gnome.org/show_bug.cgi?id=692715
2013-01-28 17:35:02 +01:00
8ca25aed8b viewSelector: stop launching app from empty search
In the overview, when there is no text in the search entry, this._searchActive
will be set to false. Moving the Clutter.Return code block ensures that
pressing enter in the search field after deleting the characters of a search
will no longer launch the #1 application for the previous search.

https://bugzilla.gnome.org/show_bug.cgi?id=692391
2013-01-27 20:29:26 +01:00
1db353a0fc Updated Norwegian bokmål translation 2013-01-27 12:54:02 +01:00
2d384eb546 PA Update (alam): Punjabi Translation completed 2013-01-27 09:27:42 +00:00
5d1de33026 Use the standard apps tree for gnome-control-center panels desktop files
gnome-control-center is planning on removing its own tree in the
future. Since it already installs these applications into
/usr/share/applications, just use this for now.

https://bugzilla.gnome.org/show_bug.cgi?id=692483
2013-01-26 19:56:01 -05:00
f2ba49fa35 app-system: Remove unused get_settings_tree method
The last use for this is long-gone.

https://bugzilla.gnome.org/show_bug.cgi?id=692483
2013-01-26 19:56:00 -05:00
772ae1cae4 Updated Slovenian translation 2013-01-26 20:08:46 +01:00
1d267f2642 Added Friulian translation 2013-01-26 04:09:31 +01:00
cba299160a Added Friulian language 2013-01-26 04:09:22 +01:00
ea33193e73 Assamese translation updated 2013-01-25 17:43:05 +05:30
ef69c228fd workspace: Use a better algorithm for computing individual thumbnail scale
The one we had before could make unmaximized windows appear to be bigger
than maximized ones, for a few reasons. Ensure that this doesn't happen
again, and add some comments to explain the whys and needs for twiddling
the individual thumbnail size.

https://bugzilla.gnome.org/show_bug.cgi?id=686944
2013-01-24 15:14:03 -05:00
7ba0f07732 workspace: Clamp individual maximized scales to WINDOW_CLONE_MAXIMUM_SCALE
We clamp the overall layout's scale to WINDOW_CLONE_MAXIMUM_SCALE, but since
we do a bit of tweaking to try and make super small windows a tad larger, it's
theoretically possible that windows may become larger than the proper maximum
scale. Fix this issue.

https://bugzilla.gnome.org/show_bug.cgi?id=686944
2013-01-24 15:13:54 -05:00
07696086a2 appDisplay: set hscrollbar_policy = NEVER on the category view actor
https://bugzilla.gnome.org/show_bug.cgi?id=686881
2013-01-24 15:10:02 -05:00
426581eb1e scroll-view: reserve scrollbar allocation when policy is automatic
This is useful to avoid reflowing the layout when there's a
center-aligned block and a scrollbar appears.

https://bugzilla.gnome.org/show_bug.cgi?id=686881
2013-01-24 15:10:02 -05:00
fe88811a40 [l10n] Updated German translation 2013-01-24 20:09:42 +01:00
30aaa6e26c searchDisplay: set a max width on the search results scrolled child
On large displays, we don't want the search results list to expand
across the whole screen; set a maximum width of 1000px.
Unfortunately, since in St max-width only affects size requisition, we
need a little custom layout manager to have it applied to the allocation
too.

https://bugzilla.gnome.org/show_bug.cgi?id=692453
2013-01-24 12:06:46 -05:00
4bd071bf3c viewSelector: clean up active search state code
'active' isn't terribly clear about just what is active; also, make it
private, remove an useless extra object state we were saving, and
refactor some messy code.
Based on a patch by Tanner Doshier.

https://bugzilla.gnome.org/show_bug.cgi?id=692454
2013-01-24 12:04:20 -05:00
309ac65447 Updated Spanish translation 2013-01-24 16:42:18 +01:00
fe246470ce Make Show Applications button depress when held down
The way it currently exists is awkward and not how most virtual buttons
work. This patch causes the "clicked" look to occur when the button is held.

https://bugzilla.gnome.org/show_bug.cgi?id=692319
2013-01-24 12:41:15 +00:00
660cfe707c searchDisplay: Make list results span all available horizontal space
https://bugzilla.gnome.org/show_bug.cgi?id=691967
2013-01-22 23:44:21 -05:00
a0d7d7bc4b loginDialog: Allow right-clicking on button items
In a gdm session, we may not know what mouse orientation the user
may be in, so it makes sense to support both the left and right
mouse buttons to activate login or other items.

Additionally, add the behavior to all modal dialog items, even in
a user session, because it's unlikely that the user will right-click
on buttons, and it makes for an easier implementation.

https://bugzilla.gnome.org/show_bug.cgi?id=688748
2013-01-22 23:42:53 -05:00
758764ea75 Updated Serbian translation 2013-01-22 21:10:34 +01:00
6935c62a61 Updated POTFILES.in 2013-01-22 16:41:03 +01:00
a34d4d9124 network: more NM 0.9.6 compatibility
Virtual device support requires
NMGtk.utils_get_connection_device_name(), so don't try to support
virtual devices with older NM.
2013-01-21 16:01:08 -05:00
c49bb5aa03 network: add support for virtual device types (vlan, bond, bridge)
Virtual devices may not actually exist until their corresponding
connections are brought up. So we need new code to create fake device
wrapper objects for them based on the corresponding NMConnections.

https://bugzilla.gnome.org/show_bug.cgi?id=677144
https://bugzilla.gnome.org/show_bug.cgi?id=677146
https://bugzilla.gnome.org/show_bug.cgi?id=677148
2013-01-21 16:01:08 -05:00
427750d6af ShellGlobal: improve code to emit sound events
Use libcanberra-gtk3 and improve the set of context properties to correctly
associate the sounds with the shell.

https://bugzilla.gnome.org/show_bug.cgi?id=642831
2013-01-21 18:03:23 +01:00
b9ad5f8727 Assamese translation updated 2013-01-21 19:42:56 +05:30
c776826425 Updated Norwegian bokmål translation 2013-01-20 21:46:55 +01:00
a8ede18db5 Updated Spanish translation 2013-01-20 12:05:39 +01:00
9aa84ec54a MessageTray: restore symbolic icons to notification buttons
These were lost when we moved away with StIconType. The idea was that
apps needed to include -symbolic in their action IDs, but apps were
not updated, and it never makes sense to have non symbolic icons there,
so let's restore the previous behavior.

https://bugzilla.gnome.org/show_bug.cgi?id=692091
2013-01-19 19:41:24 +01:00
91a6520c3b MessageTray: don't show expanded content when the notification is collapsed
This fixes the image and scrollbars peeking through in banner mode,
because StTable wasn't able to allocate them at the restricted height
imposed by CSS.

https://bugzilla.gnome.org/show_bug.cgi?id=692091
2013-01-19 19:41:23 +01:00
45fde4888e Notification: fix regression in notification image handling
We must set the image after calling .update(), because we're passing
clear: true and thus we're removing the image too.
Also, we need to specify an explicit icon size, or St.Icon will use
the default (48px)

https://bugzilla.gnome.org/show_bug.cgi?id=692091
2013-01-19 19:41:23 +01:00
484ef5f2f6 MessageTray: simplify image handling
Remove duplicate checks before calling unsetImage, and set properties
directly in the constructor.

https://bugzilla.gnome.org/show_bug.cgi?id=692091
2013-01-19 19:41:23 +01:00
cdd354739a configure.ac: Remove deprecated macro
AM_PROG_CC_C_O has been deprecated upstream for some time now.

As a side effect, this makes "git grep taskpanel" turn up blank!

https://bugzilla.gnome.org/show_bug.cgi?id=692052
2013-01-19 13:38:04 -05:00
420f544bf3 configure.ac: Rearrange pkg-config checks
https://bugzilla.gnome.org/show_bug.cgi?id=692052
2013-01-19 13:38:03 -05:00
15b33189ae configure.ac: Remove old introspection infrastructure
This hasn't been needed in quite some time.

https://bugzilla.gnome.org/show_bug.cgi?id=692052
2013-01-19 13:37:18 -05:00
685a9f36f6 configure.ac: Remove checks for JS_NewGlobalObject
The need for this is long gone.

https://bugzilla.gnome.org/show_bug.cgi?id=692052
2013-01-19 13:37:18 -05:00
3b57812e29 configure.ac: Use GNOME_COMPILE_WARNINGS
This is now implemented as standard in gnome-common.

https://bugzilla.gnome.org/show_bug.cgi?id=692052
2013-01-19 13:37:18 -05:00
d583c48992 NetworkMenu: don't queue menu updates for destroyed devices
It will cause an exception as the work ID is now invalid, and may block
new devices from appearing for example when NM is restarted.

https://bugzilla.gnome.org/show_bug.cgi?id=684279
2013-01-19 03:07:03 +01:00
0e636ea67e NetworkMenu: don't clear the section when queuing an update
It's the very first thing that queueing does.

https://bugzilla.gnome.org/show_bug.cgi?id=684279
2013-01-19 03:07:03 +01:00
c9ce1bd30f Updated Portuguese translation and converted to New Spelling (Novo AO) 2013-01-19 00:37:51 +00:00
f2cbc3192c switcherPopup: Hide the top level actor on _init
destroy() checks if we are visible to decide whether to destroy the
actor immediately or after a fade out animation. Since actors are
visible by default it would always end up destroying only after the
animation time.

https://bugzilla.gnome.org/show_bug.cgi?id=691963
2013-01-19 00:27:36 +01:00
56d76791f1 PointerWatcher: keep a reference to the idle monitor
If nothing references the idle monitor, it can be finalized at GC,
causing it to have no effect. Worse, if the finalization happens at
wrong time, the pointer watcher can be permanently off, disabling the
message tray.

https://bugzilla.gnome.org/show_bug.cgi?id=691976
2013-01-18 21:14:09 +01:00
df2cab877f MessageTray: wait until the tray is hidden to switch styles
Switching style on Overview::hiding creates a weird effect, as the noise
texture is shown while the overview is still visibile. Instead, wait for
the tray to be fully hidden, then apply the new style.
As now the switch is invisible, there is no need for the transition
(which introduced the same problem on overview showing)

https://bugzilla.gnome.org/show_bug.cgi?id=689091
2013-01-18 21:07:12 +01:00
27ad8305e5 Overview: apply extra padding to windows in external monitor
Add an style class targetting workspaces located outside the overview,
and use it for extra padding around the window clones. Padding is passed
down and applied inside LayoutStrategy, consolidating code that previously
handled the bottom side only.

https://bugzilla.gnome.org/show_bug.cgi?id=690171
2013-01-18 20:58:07 +01:00
c97b4dd48e ViewSelector: use Params to pass optional parameters to addPage
This allows to introduce new parameters without making the signature
overly complex.

https://bugzilla.gnome.org/show_bug.cgi?id=690171
2013-01-18 20:53:20 +01:00
59ecd610b1 MessageTray: pass keyboard events to tray icons
Synthetize XKeyEvents for clicks emulated by StButton using Return or
Space.

https://bugzilla.gnome.org/show_bug.cgi?id=687425
2013-01-18 20:53:20 +01:00
42c1285ead messageTray: Remove code for ignoring num lock / scroll lock
Clutter now ignores these masks, so we can remove code that depends
on this.

https://bugzilla.gnome.org/show_bug.cgi?id=691731
2013-01-18 14:25:32 -05:00
622c1c9236 shell-global: Move shell_fonts_init() back to main
We do not need any stage-specific code, so let's move
the initialization back to where other components are
initialized.

https://bugzilla.gnome.org/show_bug.cgi?id=691745
2013-01-18 00:25:42 -05:00
2d88508f9d Updated Portuguese translation and converted to New Spelling (Novo AO) 2013-01-18 00:02:32 +00:00
86efba4ecc Bump version to 3.7.4.1
Update NEWS.
2013-01-17 13:56:38 +01:00
90fae00aa6 screenshot: Immediately show the flash spot
The "flash" effect looks awkward when it fades in and out. Real
camera have an immediate flash of light, which then seems to fade
out as our eyes readjust the rapid change in lighting.

https://bugzilla.gnome.org/show_bug.cgi?id=691875
2013-01-16 17:19:16 -05:00
d497ed4040 build: Drop obsololete variable from configure.ac
LIBEDATASERVERUI_MIN_VERSION is not used
2013-01-16 23:13:20 +01:00
c4a21ae5ad Updated Belarusian translation. 2013-01-16 16:09:54 +03:00
5e5798bee9 dateMenu: Add "Open Clocks" entry
This is similar to how the dateMenu already allows opening the calendar
application. However, the new entry only appears if GNOME Clocks is
installed, as it is not a core GNOME application.

https://bugzilla.gnome.org/show_bug.cgi?id=644390
2013-01-16 05:06:55 +02:00
d793077b91 dateMenu: Move the "Open Calendar" entry
The design calls for it to be in the left column rather than the right
one.

https://bugzilla.gnome.org/show_bug.cgi?id=644390
2013-01-16 05:06:52 +02:00
3e826f9249 build: Bump gsettings-desktop-schemas requirement
Needed for f7212cf80c
2013-01-15 12:18:53 -05:00
2adfb60dc4 data: Remove unused show-full-name in schema
It's now in gsettings-desktop-schemas.

https://bugzilla.gnome.org/show_bug.cgi?id=689561
2013-01-15 09:53:08 +01:00
f7212cf80c userMenu: Use show-full-name-in-top-bar setting
For the non-locked case, from gsettings-desktop-schemas' privacy
schema. For the locked case, from the privacy schema.

https://bugzilla.gnome.org/show_bug.cgi?id=689561
2013-01-15 09:53:08 +01:00
69556643bb Depend on gjs 1.35.4
Needed for 9548cd8341
2013-01-15 09:25:36 +01:00
f0ebc84840 Bump version to 3.7.4
Update NEWS.
2013-01-15 02:48:12 +01:00
490eb59ea3 build: Add missing file 2013-01-15 02:48:12 +01:00
16a9391726 remoteSearch: fix a typo in createIcon
4288761235 changed the method to always
create a GIcon, but didn't pass it down to the StIcon constructor.

https://bugzilla.gnome.org/show_bug.cgi?id=691750
2013-01-14 19:43:45 -05:00
adf8ba67d2 run-test: Properly set the path for the GVC library
We really should get a better testrunner rather than this hacked
together thing.

https://bugzilla.gnome.org/show_bug.cgi?id=691743
2013-01-14 17:53:23 -05:00
c37b222cbf network: support NM 0.9.6 again
NM 0.9.7 is still not released even as a tarball, so fix this to work
with 0.9.6 again for now (although it doesn't do any device name
disambiguation in this case now).

https://bugzilla.gnome.org/show_bug.cgi?id=691720
2013-01-14 11:25:40 -05:00
72f8f2beb1 Updated Norwegian bokmål translation 2013-01-14 11:21:19 +01:00
16bb9c17f5 status/keyboard: Add input source switching per window
If the setting is enabled, we record the last activated input source
for the currently focused window and switch to it when focusing back
that window. The Overview is considered a window for this purpose.

https://bugzilla.gnome.org/show_bug.cgi?id=691414
2013-01-14 11:11:00 +01:00
ca44977d92 status/keyboard: Only change the current source setting if it changed
This avoids all the work that goes on in various processes when
switching input sources if the activated source is the currently
configured one.

https://bugzilla.gnome.org/show_bug.cgi?id=691414
2013-01-14 11:11:00 +01:00
bcf294475f Updated Slovenian translation 2013-01-12 23:03:41 +01:00
3b31774dd3 Fix remaining uses of transition-duration without time units
Commit 8be3c5ed21 changed the CSS parser
to only accept values with explicit time units.
2013-01-12 17:39:08 +01:00
c106347c59 Updated Uyghur translation
Signed-off-by: Gheyret Kenji <gheyret@gmail.com>
2013-01-12 14:30:08 +09:00
2679be9d97 Telepathy: lookup PATH when launching empathy-accounts
It might be installed only in the jhbuild path.

https://bugzilla.gnome.org/show_bug.cgi?id=691553
2013-01-11 16:27:53 +01:00
4d59368c7d Updated Slovenian translation 2013-01-10 21:37:09 +01:00
3f29680fb6 workspace: Use MetaButtonLayout to get side of close button
The current code parses the button-layout setting because MetaButtonLayout
was not usable from introspection. With that fixed, we can switch to
using meta_prefs_get_button_layout().

https://bugzilla.gnome.org/show_bug.cgi?id=689263
2013-01-10 00:08:55 +01:00
8f1df14ae7 Revert "Remove non-existing file to make intltool happy"
This reverts commit 8410fc38c9.

It very much exists. Do a 'git submodule init && git submodule update'.
2013-01-09 21:02:06 +01:00
7159e360d9 Update Simplified Chinese translation 2013-01-09 06:40:20 +08:00
65723bcac5 PowerMenu: increase padding to the left of the percentage
This avoids having the percentage and device label in close succession.

https://bugzilla.gnome.org/show_bug.cgi?id=689297
2013-01-08 23:30:02 +01:00
73ae451cb4 Updated slovak translation 2013-01-08 21:41:08 +01:00
dc0ea3a248 main: Remove unused 'background' property 2013-01-08 13:24:09 -05:00
9548cd8341 js: Explicitly dispose all cairo contexts
Due to limitations and bugs in SpiderMonkey's GC, wrapper objects
for cairo contexts and similar may not get cleaned up immediately
after repainting, leading to leaking memory. Explicitly disposing
of such objects after they're not needed can clean up large portions
of memory for cairo surfaces.

https://bugzilla.gnome.org/show_bug.cgi?id=685513
2013-01-08 13:07:51 -05:00
8410fc38c9 Remove non-existing file to make intltool happy 2013-01-08 14:40:18 +01:00
0c9c2168b3 Updated minor fixes in Brazilian Portuguese translation contributed by F. Neves <florencioneves@gmail.com> 2013-01-08 10:32:34 -02:00
2690f54743 Updated Brazilian Portuguese translation 2013-01-08 10:17:45 -02:00
22783813c5 Update German translation 2013-01-07 15:38:44 +01:00
b5ed46a5af Updated Norwegian bokmål translation 2013-01-07 11:28:29 +01:00
1a3f17e296 Updated Kazakh translation 2013-01-06 20:22:44 +06:00
9281129f07 Really use the right getter
Doh, it is actually a uint, not an int.
2013-01-05 14:21:30 -05:00
0e9ddd3b99 Use the right getter
org.gnome.desktop.screensaver lock-delay is an integer, not
a boolean, so don't use get_bool() on it.

https://bugzilla.gnome.org/show_bug.cgi?id=691170
2013-01-04 22:01:25 -05:00
d87db04e55 panel: Update corners' nearest button on session mode change
With panel buttons changing dynamically on session mode changes we can
no longer rely on a corner's respective box style-changed signal to
find the nearest button.

Instead, make the panel take care of telling the corners to look for a
new button when buttons are changed.

https://bugzilla.gnome.org/show_bug.cgi?id=690180
2013-01-04 13:35:53 +00:00
906ec3c8a9 panel: Remove unused method
https://bugzilla.gnome.org/show_bug.cgi?id=690180
2013-01-04 13:35:47 +00:00
449575ceae keyboard: Don't hide or show the keyboard immediately
Acting on each Show/Hide DBus call immediately may cause a lot of
jittery movement when Alt+Tabbing or even just switching tabs in
e.g. gnome-terminal.

To make the OSK feel sturdier, we wait a bit before actually showing
or hiding it so that we can coalesce tight sequences of Show/Hide
calls. I.e. the last call wins which means that we might end up not
doing anything.

https://bugzilla.gnome.org/show_bug.cgi?id=688646
2013-01-04 13:13:15 +00:00
6255c77eba keyboard: Don't set keys as checked
There's no reason to do it and it actually breaks when showing subkeys
since then we won't get a 'key-released' signal for the main key.

https://bugzilla.gnome.org/show_bug.cgi?id=674955
2013-01-04 13:13:08 +00:00
c33622f2b3 keyboard: Fix subkeys handling
We can't pushModal() when showing the subkeys popup because that will
cause the application to lose focus and thus we get a Hide() call for
the whole OSK.

Instead, capture events on the main OSK actor while the subkeys popup
is shown so that we can both prevent events from reaching the main
keys but also cancel the subkeys if the user clicks away in the OSK.

https://bugzilla.gnome.org/show_bug.cgi?id=674955
2013-01-04 13:13:00 +00:00
e4860acb58 screenshot: use g_file_replace() when using an absolute path
Instead of g_file_create(), which always fails if the file exists
already.
2013-01-03 15:02:00 +01:00
e96867f757 screenshot: document the Screenshot interface
Use gdbus-codegen to generate a docbook for the screenshot interface,
and add it to our gtk-doc.

https://bugzilla.gnome.org/show_bug.cgi?id=688004
2013-01-03 12:56:46 +01:00
57bd43baf3 screenshot: move to a separate interface
Since we're breaking API already, take this as an occasion to use a
separate interface for all the screenshot-related methods. The interface
name is org.gnome.Shell.Screenshot.
Internally, move all the related code to screenshot.js.

https://bugzilla.gnome.org/show_bug.cgi?id=688004
2013-01-03 12:56:45 +01:00
3a4e595d32 screenshot: save to an unique path when using a basename
When a basename is passed, avoid filename conflicts. The unique path is
returned by the API.

https://bugzilla.gnome.org/show_bug.cgi?id=688004
2013-01-03 12:56:45 +01:00
dcad22bfa8 screenshot: change API to return the filename used for saving
Since we also support passing a basename now, clients might be
interested in knowing the path used to save the file.
Add an out argument to the interface for that.

https://bugzilla.gnome.org/show_bug.cgi?id=688004
2013-01-03 12:56:45 +01:00
acba0e47d8 screenshot: support non-absolute paths when saving screenshots
If a non-absolute path is passed to the screenshot methods, treat it as
a basename for the output image, and automatically try to save it in
$XDG_PICTURES_DIR, falling back to $HOME if it doesn't exist.

https://bugzilla.gnome.org/show_bug.cgi?id=688004
2013-01-03 12:56:45 +01:00
dd19459e18 Add a SelectArea() DBus method
This will be useful for e.g. selecting an area for a screenshot.

https://bugzilla.gnome.org/show_bug.cgi?id=687954
2013-01-03 12:56:45 +01:00
8be3c5ed21 St: fix parsing of transition-duration values
According to css3-transition, transition-duration is expressed
as a time, that is, in seconds or milliseconds. Fix that by
recognizing numbers with units and implicitly converting to
milliseconds after parsing.

https://bugzilla.gnome.org/show_bug.cgi?id=681376
2013-01-03 03:47:58 +01:00
51726d8de7 Fix regression from 4288761235 2013-01-03 02:48:22 +01:00
8f41c6bad8 UnlockDialog: honor org.gnome.desktop.screensaver.user-switch-enabled
The screensaver schema has a key that it is meant for locking down
the ability to switch user when the screen is locked, but support
for it was not implemented in the new screenshield.
Fix that by checking the key before creating the button.

https://bugzilla.gnome.org/show_bug.cgi?id=691042
2013-01-03 01:43:27 +01:00
e294abc567 keyboard: Remove leftover DBus import
This isn't there in newer versions of gjs.
2013-01-02 13:59:33 -05:00
9f9518c872 gnome-shell-plugin: Port event filter to XInput2
Otherwise, we'll see issues with tray icons.

https://bugzilla.gnome.org/show_bug.cgi?id=690590
2013-01-02 13:43:06 -05:00
5faf7cb59e gnome-shell-plugin: Extract crossing event ignores
This is a preliminary patch to make the diff in the next patch
cleaner. This just shuffles code around without changing anything.

https://bugzilla.gnome.org/show_bug.cgi?id=690590
2013-01-02 13:43:06 -05:00
4b095d532c st-texture-cache: Remove st_texture_cache_load_from_raw
This is now unused.

https://bugzilla.gnome.org/show_bug.cgi?id=691019
2013-01-02 12:32:29 -05:00
4288761235 remoteSearch: Use GIcon for loading icon data
This removes us from caching the pixbuf data in the icon cache,
and allows us to remove St.TextureCache.load_from_raw().

https://bugzilla.gnome.org/show_bug.cgi?id=691019
2013-01-02 12:32:29 -05:00
73388f30fd main: Add a better comment about shifting the modal stack
It took me a few minutes to realize why, so let's just add this
in for future reference.

https://bugzilla.gnome.org/show_bug.cgi?id=690965
2013-01-02 12:32:29 -05:00
2d9ddd4bc8 main: Rename curFocus to prevFocus
This better describes what we're tracking here: the previous
keyboard focus before pushing the modal.

https://bugzilla.gnome.org/show_bug.cgi?id=690965
2013-01-02 12:32:29 -05:00
55aa0cf303 main: Don't mess up the modal stack when the focus actor is destroyed
This seems to be an incorrect conversion when we moved from an array
to an object of keys in 3a6b4f3.

https://bugzilla.gnome.org/show_bug.cgi?id=690965
2013-01-02 12:32:29 -05:00
52ca15b514 grabHelper: Allow pressing escape on grab focus grabs
We didn't install the captured event handler on grab focus grabs,
leading to the case where we didn't ungrab correctly.

https://bugzilla.gnome.org/show_bug.cgi?id=690897
2013-01-02 12:32:29 -05:00
bd383888de notificationDaemon: Clean up icon/image handling
Make the logic for this clearer and easier to see.

https://bugzilla.gnome.org/show_bug.cgi?id=680414
2013-01-02 12:24:10 -05:00
1bd349485f notificationDaemon: Merge two pieces of similar code
Use the same code for parsing notification data to handle both
icons and images.

https://bugzilla.gnome.org/show_bug.cgi?id=680414
2012-12-30 14:09:39 -05:00
155f9dc1b1 messageTray: Support setImage(null) to mean unsetImage()
This is a quick API change that should clean up some conditionals.

https://bugzilla.gnome.org/show_bug.cgi?id=680414
2012-12-30 14:09:39 -05:00
fe7ee1edc3 notificationDaemon: Clean up code paths
While we really need to clean up the bad MessageTray API, this is
a quick step to allow for a cleaner codebase for the future.

https://bugzilla.gnome.org/show_bug.cgi?id=680414
2012-12-30 14:09:39 -05:00
c11cbff605 notificationDaemon: Prevent doing redundant work
We already calculated and created a gicon based on the icon and hints.

https://bugzilla.gnome.org/show_bug.cgi?id=680414
2012-12-30 14:09:39 -05:00
fbc629266f notificationDaemon: Fix style
https://bugzilla.gnome.org/show_bug.cgi?id=680414
2012-12-30 14:09:39 -05:00
75d44dca6b keyring: Actually ensure sensitivity when the keyring dialog opens
I misunderstood how the control flow for the keyring worked.

https://bugzilla.gnome.org/show_bug.cgi?id=690895
2012-12-30 14:09:39 -05:00
3e6b794a33 keyring: Fix copy/paste error
This was clearly meant to be continueButton.

https://bugzilla.gnome.org/show_bug.cgi?id=690895
2012-12-30 14:09:39 -05:00
a757ce48a1 ScreenShield: don't reset the lock screen animation if it's already happening
When you click Suspend from the user menu, the following things happen:
- we lock the screen internally by calling Main.screenShield.lock() and waiting
  for lock-screen-shown
- logind emits a Lock signal, which causes us to lock again
- gnome-settings-daemon notices PrepareForSleep, and calls org.gnome.ScreenSaver.Lock,
  just in case, so we lock once more
This means that, if you're lucky, you can see the curtain fall down multiple times,
as each .lock() call resets the animation.

https://bugzilla.gnome.org/show_bug.cgi?id=690858
2012-12-30 01:39:24 +01:00
507f29a7bd layout: Make OSK animation quicker, snappier
https://bugzilla.gnome.org/show_bug.cgi?id=688642
2012-12-30 00:12:27 +01:00
e5f4f77073 Updated Hungarian translation 2012-12-29 16:14:51 +01:00
f07fee538d keyboard: Show in an idle on clutter key focus changes
It's common to do actor.grab_key_focus() before the actor is mapped
which means that we can't reliably determine where the actor is at
notify::key-focus time and thus might end up showing the keyboard on
the wrong monitor.

This is happening, in particular, with the run dialog. Delaying until
we hit the main loop allows us to know where the actor finally is
before showing the OSK.

https://bugzilla.gnome.org/show_bug.cgi?id=685856
2012-12-28 13:00:15 -05:00
fa1420b384 lookingGlass: Move to an inspect() function
Rather than add invalid results to the place where you enter JavaScript
commands when you use the eyedropped, add an inspect() function and add
a fake call to it.

https://bugzilla.gnome.org/show_bug.cgi?id=690726
2012-12-28 03:20:37 -05:00
724a2bd72f Calendar: use text/calendar preferred app as the calendar app
That's what the info panel in g-c-c defines as the preferred calendar
application.

https://bugzilla.gnome.org/show_bug.cgi?id=690767
2012-12-27 19:05:52 +01:00
d525d02348 ScreenShield: honor lock-delay GSettings key
org.gnome.desktop.screensaver.lock-delay contains the grace period
of the screensaver: if deactivated within that many seconds from the
start of the idle period, the shell should not prompt for a password.
This setting correspond to the "Lock screen after" combo in screen
and privacy panels.

https://bugzilla.gnome.org/show_bug.cgi?id=690766
2012-12-27 19:05:44 +01:00
4920cf2b98 Updated Polish translation 2012-12-27 00:42:13 +01:00
f41d0938a4 shell-global: Drop unused variable
Leftover from 5566aa4588
2012-12-26 16:23:38 +01:00
924a405829 Updated Spanish translation 2012-12-26 11:59:17 +01:00
fc9225e24a keyring: Make sure the entries are insensitive after submission
https://bugzilla.gnome.org/show_bug.cgi?id=690594
2012-12-25 22:18:42 -05:00
ac202cbdd3 keyring: Don't use setButtons
https://bugzilla.gnome.org/show_bug.cgi?id=690594
2012-12-25 22:18:42 -05:00
cedd68c942 polkitAgent: Make sure the entries are insensitive after submission
It feels weird and strange to be able to type in the polkit agent
dialog after hitting enter.

https://bugzilla.gnome.org/show_bug.cgi?id=690594
2012-12-25 22:18:42 -05:00
4231c879ef polkitAgent: Don't use setButtons
https://bugzilla.gnome.org/show_bug.cgi?id=690594
2012-12-25 22:18:42 -05:00
42e58a4b72 modalDialog: Make the button layout visible always
With the last button-less modal dialog gone, there's no reason it
should ever be hidden.

https://bugzilla.gnome.org/show_bug.cgi?id=690594
2012-12-25 22:18:42 -05:00
ebd1bc83c9 Ping the active window when using the app menu
Use the new meta_window_check_alive() to verify if the application is
responding after the user activates an action from the app menu.
This in particular restores the ability to force quit applications
from the menu, even if the use a custom GMenu.

https://bugzilla.gnome.org/show_bug.cgi?id=684340
2012-12-25 18:41:22 +01:00
0bc9d7455f Updated Galician translations 2012-12-25 00:37:18 +01:00
5274166f8c layout: Fix strut heuristics for multi-monitor
Spotted while going through the code.

https://bugzilla.gnome.org/show_bug.cgi?id=690666
2012-12-23 21:17:40 -05:00
5566aa4588 shell-global: Remove hacks around clutter_event_get_current_time()
clutter_event_get_current_time() has been fixed upstream as part
of reentrant bug fixes, so there's no need for these shenanigans.

https://bugzilla.gnome.org/show_bug.cgi?id=690665
2012-12-23 21:17:40 -05:00
ab638a4f54 gnome-shell-jhbuild: Remove old envvar
This isn't used anymore.

https://bugzilla.gnome.org/show_bug.cgi?id=690667
2012-12-23 21:17:40 -05:00
86a8452ab7 run-test: Load shell files from the srcdir
https://bugzilla.gnome.org/show_bug.cgi?id=690667
2012-12-23 21:17:26 -05:00
8c96c16eac Update Arabic translation 2012-12-23 18:35:16 +02:00
45c2e6575d Bluetooth: remove dead code
Notification.grabFocus doesn't exist anymore.

https://bugzilla.gnome.org/show_bug.cgi?id=687081
2012-12-23 13:32:13 +01:00
a786a7dc55 Revert "Remove a non-existing file"
This reverts commit 685262fc5e.

Please don't remove this file from POTFILES.in.

You need to run 'git submodule init && git submodule update' in your
git checkout. Damned Lies currently lacks support for submodules,
thus this error.
2012-12-21 20:43:42 +01:00
6adf5cbee5 Reverting "volume: Put canberra sound events on output streams only"
This reverts commit 1964b54627.

This was committed accidentally.
2012-12-21 13:28:46 -05:00
1964b54627 volume: Put canberra sound events on output streams only
This seems to be an oversight in the original volume menu code
that slipped in by accident.
2012-12-21 13:24:02 -05:00
c9d0e82c52 volume: Clean up stream/slider handling code
Rather than using naming schemes and dynamic property lookups as
a kind of namespace, use what was designed to be used as a namespace:
a class.

https://bugzilla.gnome.org/show_bug.cgi?id=690539
2012-12-21 13:23:48 -05:00
1a4948f0f2 volume: Pick up hasHeadphones from the signal handler
A preliminary patch for big cleanups happening soon.

https://bugzilla.gnome.org/show_bug.cgi?id=690539
2012-12-21 12:30:16 -05:00
66da3f5668 volume: Don't have a separate syncVisibility method
With it doing less now, we can simply do it inline.

https://bugzilla.gnome.org/show_bug.cgi?id=690539
2012-12-21 12:30:03 -05:00
9ebeb64570 volume: Don't set the visibility of the main icon
As the main icon is inside the actor, this is needless calculation.

https://bugzilla.gnome.org/show_bug.cgi?id=690539
2012-12-21 12:30:03 -05:00
571aaece2e volume: Merge the two update handlers
With our mess of callbacks gone, we can update stream volume
in a single place only.

https://bugzilla.gnome.org/show_bug.cgi?id=690539
2012-12-21 12:30:03 -05:00
f60fb954a2 volume: Make icon calculation stable
Calculate an icon based on our current state, not a mess of
signal emissions and callbacks. This is a preliminary basic
cleanup patch in preparation for the next one.

https://bugzilla.gnome.org/show_bug.cgi?id=690539
2012-12-21 12:30:03 -05:00
1d136cacfb lockScreenMenu: Construct actual panel icons much more accurately
Instead of faking it by adding a bunch of main icons and secondary
icons to our own box, try and recreate the original button box
with the original icons.

https://bugzilla.gnome.org/show_bug.cgi?id=690589
2012-12-21 12:28:00 -05:00
994021d77f panelMenu: Remove inaccurate comment
This has been out of date ever since the -symbolic fallbacks
have gone away.

https://bugzilla.gnome.org/show_bug.cgi?id=690589
2012-12-21 12:26:32 -05:00
8899325a0e boxpointer: Fix a small typo
This was showing up in my auto-complete results.
2012-12-21 12:24:48 -05:00
43876a9357 boxpointer: Defer re-allocation after a flip
As we may be flipping the box pointer in response to re-allocation,
like the addition of a new actor to the boxpointer, we can't queue
a re-layout while in a re-layout, so defer.

https://bugzilla.gnome.org/show_bug.cgi?id=690608
2012-12-21 12:24:48 -05:00
d6cace32f5 boxpointer: Don't use the box allocation when calculating the arrow side
Depending on the current state of arrowSide, the box allocation may be
wrong; e.g. if the user requested a TOP, but we flipped to a BOTTOM, the
next request would look to the y2 value of the flipped BOTTOM, which is
wrong.

Instead, use the origin, plus the calculated preferred size of the box.

https://bugzilla.gnome.org/show_bug.cgi?id=690608
2012-12-21 12:24:48 -05:00
2388de455b boxpointer: Rework how flipping works
Make sure we re-allocate after we flip sides, to ensure that
padding around the child actor is updated correctly. Additionally,
ensure that we flip after we setPosition, as we won't get re-allocated
auotmatically by just changing the position.

https://bugzilla.gnome.org/show_bug.cgi?id=690608
2012-12-21 12:24:48 -05:00
b14b3ab276 boxpointer: Clean up reposition
Given that it's modifying state, there's no reason it can't be
using it instead. This makes it easier to use, so we aren't passing
in a bunch of instance variables every time.

https://bugzilla.gnome.org/show_bug.cgi?id=690608
2012-12-21 12:24:48 -05:00
32ccb779c6 theme: notification improvements
Tweak the layout to give more space to action buttons, associate
the heading with the body text, and give button labels a regular
font weight.

https://bugzilla.gnome.org/show_bug.cgi?id=688506
2012-12-21 11:37:48 +00:00
52536e94e5 st-scroll-view: Fix build with -Werror=maybe-uninitialized 2012-12-20 23:01:13 +01:00
5d6b3abd26 st-adjustment: Add missing math.h include 2012-12-20 23:01:13 +01:00
79682bbcb0 Updated Lithuanian translation 2012-12-20 23:55:18 +02:00
770ff19313 workspace: Add smooth scrolling support to zoom windows
https://bugzilla.gnome.org/show_bug.cgi?id=687573
2012-12-20 08:40:05 -05:00
8d4855f100 volume: Add smooth scrolling to adjust output volume
Allow users to smoothly scroll on the volume indicator icon
to adjust the volume. Do this by simply passing the scroll
event to the slider inside the menu.

https://bugzilla.gnome.org/show_bug.cgi?id=687573
2012-12-20 08:40:04 -05:00
7d4e14f384 popupMenu: Add smooth scrolling support for sliders
Allowing smooth scrolling on the Y axis to accurately adjust the value
of the slider.

https://bugzilla.gnome.org/show_bug.cgi?id=687573
2012-12-20 08:40:04 -05:00
f162dd7e87 scroll-bar: Add smooth scrolling support
Do the same for StScrollBar.

https://bugzilla.gnome.org/show_bug.cgi?id=687573
2012-12-20 08:40:04 -05:00
06dc12e217 scroll-view: Add proper smooth scrolling
The code here before was added as dummy code to satisfy an error
in the missing switch, and wasn't ever tested due to the lack of XI2
in mutter. Use the same math as GtkRange does to calculate scroll bar
positions from raw XI2 deltas to allow for proper smooth scrolling.

https://bugzilla.gnome.org/show_bug.cgi?id=687573
2012-12-20 08:40:04 -05:00
36edff9e15 Updated Hebrew translation. 2012-12-20 13:37:03 +02:00
34a0c079de power: Update for new D-Bus name
https://bugzilla.gnome.org/show_bug.cgi?id=690506
2012-12-19 17:18:32 +01:00
84b581ae13 Crisp menu separators
Instead of being fuzzy, the menu separators should be a clear
line with a horizontal gradient. This looks better and is
consistent with the mockups.

https://bugzilla.gnome.org/show_bug.cgi?id=641745
2012-12-19 15:45:18 +00:00
587c93eadf StDrawingArea: fix texture drawing
cogl_rectangle() wants the four rectangle corners, not the size.

https://bugzilla.gnome.org/show_bug.cgi?id=641745
2012-12-19 15:45:18 +00:00
b5aa549e0c [l10n] Updated Estonian translation 2012-12-19 17:14:03 +02:00
5fb6738612 Updated Norwegian bokmål translation 2012-12-19 15:45:24 +01:00
685262fc5e Remove a non-existing file 2012-12-19 15:45:21 +01:00
8802f6cf23 Updated Slovenian translation 2012-12-19 11:38:43 +01:00
2698d0db29 Bump version to 3.7.3
Update NEWS.
2012-12-18 18:28:42 +01:00
2fa7230133 gvc: Update git submodule
This fixes make distcheck.
2012-12-18 18:28:42 +01:00
658243eae4 build: Exclude .gitmodules from distribution 2012-12-18 18:28:42 +01:00
7282bc8859 build: Bump gsettings-desktop-schemas requirement 2012-12-18 18:28:42 +01:00
07511cb21a Handle 'switch-input-source' keybinding move
Moving this to gsettings-desktop-schemas instead so that
gnome-control-center doesn't have to grow a dependency on gnome-shell.

https://bugzilla.gnome.org/show_bug.cgi?id=690427
2012-12-18 16:13:13 +00:00
f71f767a3c [l10n] Updated Estonian translation 2012-12-18 17:34:09 +02:00
176daa1469 WorkspacesDisplay: clean up workspacesView lists
Rather than sometimes having a list and sometimes null, keep an array
always and check for its length.

https://bugzilla.gnome.org/show_bug.cgi?id=688234
2012-12-18 16:00:54 +01:00
5487f8cf93 WorkspacesView: don't use clutter_actor_reparent()
It's deprecated and in this case is not needed because at that point
the actor hasn't been parented yet.

https://bugzilla.gnome.org/show_bug.cgi?id=688234
2012-12-18 16:00:54 +01:00
944762ac83 WindowOverlay: fix title sizing
After the first time the title was placed, we were setting its width,
thus forcing get_preferred_width() to return that as the minimum and
natural width.
To workaround that, explicitly reset the width to -1, -1, causing
StLabel->get_preferred_width() to be called, which would give us a meaningful
value for minimum and natural width.

https://bugzilla.gnome.org/show_bug.cgi?id=688234
2012-12-18 16:00:54 +01:00
c66068c435 Workspace: fix window clone slot reporting
WindowOverlay was at times seeing bogus values reported as WindowClone
sizes. Fix that by storing and passing the value from the authoritative
source, that is, the LayoutStrategy.

https://bugzilla.gnome.org/show_bug.cgi?id=688234
2012-12-18 16:00:54 +01:00
d479c936b8 Workspace: miscellaneuous cleanups
Use a single GSettings object for all layout changes of window overlays,
and avoid quadratic behavior for window positioning.

https://bugzilla.gnome.org/show_bug.cgi?id=650843
2012-12-18 16:00:54 +01:00
c5a3f784dd theme: make the 'more results' emblem more legible on light icons 2012-12-18 15:40:23 +01:00
529d012865 LockScreenMenu: double the space between icons
Using the same spacing as secondary status icons causes the
three indicators to be too close to each other.

https://bugzilla.gnome.org/show_bug.cgi?id=687955
2012-12-17 17:25:29 +01:00
8a1b83417a network: add minimal support for InfiniBand
If present, InfiniBand devices show up with the ethernet devices (and
the presence of an InfiniBand connection will cause "Wired" to be
renamed to "Ethernet").

https://bugzilla.gnome.org/show_bug.cgi?id=677150
2012-12-17 08:11:36 -05:00
6c64c0873c workspaceThumbnail: unify restack handling
Both WorkspacesDisplay and ThumbnailsBox need to know when windows have been
restacked. Instead of each tracking changes on their own or trying to call
each other, have the overview keep track and do the calculations, emitting
a signal with the result.

https://bugzilla.gnome.org/show_bug.cgi?id=690175
2012-12-17 13:24:25 +01:00
d2c6149923 workspaceThumbnail: handle show/hide when entering/leaving the overview
Handle this internally, instead of having WorkspacesDisplay show/hide
us.

https://bugzilla.gnome.org/show_bug.cgi?id=690175
2012-12-17 13:24:25 +01:00
9f98f6f02d workspaceThumbnail: handle scroll-event internally
Instead of having WorkspacesDisplay calling us.

https://bugzilla.gnome.org/show_bug.cgi?id=690175
2012-12-17 13:24:25 +01:00
2f7c0b1985 workspaceThumbnail: make ThumbnailsBox track workspace changes itself
Instead of having WorkspacesDisplay calling us.

https://bugzilla.gnome.org/show_bug.cgi?id=690175
2012-12-17 13:24:25 +01:00
f61548234a status/keyboard: Add an Alt+Tab like input source switcher
https://bugzilla.gnome.org/show_bug.cgi?id=682315
2012-12-17 11:43:02 +00:00
6eef830ba4 status/keyboard: Keep a list of input sources in MRU order
This will allow us to provide an Alt+Tab like input source switcher.

https://bugzilla.gnome.org/show_bug.cgi?id=682315
2012-12-17 11:43:02 +00:00
88a9b7648c Add the new switch-input-source keybinding
https://bugzilla.gnome.org/show_bug.cgi?id=682315
2012-12-17 11:43:02 +00:00
b7678493f9 windowManager: Return the KeyBindingAction value from addKeybinding()
meta_display_add_keybinding() returns a keybinding action ID for
dynamically registered keybindings which can be used to match a
keycode/mask pair to the action it is bound to.

https://bugzilla.gnome.org/show_bug.cgi?id=682315
2012-12-17 11:43:01 +00:00
a9ec8a354a main: Initialize WindowManager earlier
This allows us to register keybindings from most other places that
might need to do so like the panel or any status indicator.

https://bugzilla.gnome.org/show_bug.cgi?id=682315
2012-12-17 11:43:01 +00:00
a42d35deab main: Add an ALL value to KeybindingMode
https://bugzilla.gnome.org/show_bug.cgi?id=682315
2012-12-17 11:43:01 +00:00
2008feb7da status/keyboard: Store IBus engine properties per InputSource object
Instead of storing them globally and having an hardcoded list of
engines which are allowed to change their indicator symbol when the
InputMode property changes.

https://bugzilla.gnome.org/show_bug.cgi?id=682318
2012-12-17 11:43:01 +00:00
c41424b57b status/keyboard: Make input sources be proper objects
Introduce an InputSource class which makes it easier to keep track of
all the data we have about them.

https://bugzilla.gnome.org/show_bug.cgi?id=682318
2012-12-17 11:43:01 +00:00
3bd5563a7e ctrlAltTab: Remove a "window" parameter leftover
Commit 1f46a0d removed support for the items "window" parameter but
there was an occurence left.

https://bugzilla.gnome.org/show_bug.cgi?id=690312
2012-12-16 23:03:34 +01:00
eff8ec00c4 Updated slovak translation 2012-12-16 22:50:06 +01:00
1256af7b9a WindowManager: scale windows to their monitor's corner on minimize
We previously scaled windows to (primary.x, 0), which is wrong if the
primary monitor is not at y == 0. At the same time, change the policy
to pick the hot corner on the monitor the window is on, because that's
closest to where it will appear in the overview.

https://bugzilla.gnome.org/show_bug.cgi?id=690241
2012-12-16 22:40:33 +01:00
b38ecaf925 LayoutManager: fix findMonitorForWindow to take a metaWindow
This makes the method usable in places where the associated window actor
might not have the right size (such as from window manager animations).
Also, make the method public from LayoutManager.

https://bugzilla.gnome.org/show_bug.cgi?id=690241
2012-12-16 22:40:33 +01:00
bd5d945fb2 Updated Spanish translation 2012-12-16 20:48:08 +01:00
15063ef3d5 WindowManager: clean up scale_y when overwrite the map animation
If we overwrite a map animation (for example because the actor is now destroyed),
we need to complete it first, otherwise it starts off from a random
middle point. This is the same treatment opacity gets for normal windows.

https://bugzilla.gnome.org/show_bug.cgi?id=690241
2012-12-16 20:45:43 +01:00
3b8a125732 WindowManager: clean up effects code
Use consistently shouldAnimateActor(), and try to have a uniform code flow
between the modal dialog and the normal case.

https://bugzilla.gnome.org/show_bug.cgi?id=690241
2012-12-16 20:45:43 +01:00
9860b1c677 St: fix regression from f7af96dbb2
A bad rebase caused the wrong patch to be pushed.

https://bugzilla.gnome.org/show_bug.cgi?id=687881
2012-12-16 20:45:43 +01:00
a0e340f06e workspaces-view: make sure to add spacing between view and controls
The thumbnail controls are not a separate actor in the overview group
yet, so we need to ensure a spacing between them and the workspaces
view.
Instead of exporting the overview spacing, just add a temporary style
class to the workspaces-view actor for it. It will be removed in the
future when we change the layout of overview elements.

https://bugzilla.gnome.org/show_bug.cgi?id=690174
2012-12-15 14:13:36 -05:00
57d3bec95c MessageTray: change notification and boxpointer background opacity to
make the text in it more clear.
2012-12-15 16:34:58 +01:00
be10f3c2b5 ViewSelector: remove unnecessary StBoxLayout
It was a remnant from the tabbed interface.

https://bugzilla.gnome.org/show_bug.cgi?id=690171
2012-12-15 15:23:21 +01:00
f7af96dbb2 St: avoid blocking IO to resolve relative urls
realpath() does a series of lstat() on each path component to resolve
symbolic links, but we just want to get an absolute path, and we don't
really care if it is physical or not. Going through a GFile does the
canonicalization we need, and is a lot faster.

https://bugzilla.gnome.org/show_bug.cgi?id=687881
2012-12-15 03:40:13 +01:00
468a855f04 make the window thumbnails bigger
Adjust the layout of the overview and window thumbnails to make them
bigger. Also, make the background shade darker to compensate for the
increased thumbnail density.

https://bugzilla.gnome.org/show_bug.cgi?id=689876
2012-12-14 12:58:43 +00:00
429f9e1d15 iconGrid: Only recreate icons when needed
Recreating icons on every style change -- like hover, can have
disasterous effects. Not only is the quick creation/destruction of
the actors bad, but adding/removing actors at runtime queues many
relayouts, which makes the whole system slower as a lot of unnecessary
reallocations are figured out.

While an optimization was here before, it was broken because it
broke high-contrast themes. Connect explicitly to the texture cache
to know when the icon theme has changed, instead of removing a valuable
optimization.

https://bugzilla.gnome.org/show_bug.cgi?id=672941
2012-12-13 21:35:05 -05:00
5de91197ae status/keyboard: remove the property whitelist
It was just a stopgap solution for 3.6, as the necessary UI bits
were not implemented.

https://bugzilla.gnome.org/show_bug.cgi?id=682318
2012-12-14 00:47:19 +01:00
bc6b4b01ea status/keyboard: implement remaining property types
Input methods other than Anthy need different property types, so we
should support all those exposed by the IBus protocol.

https://bugzilla.gnome.org/show_bug.cgi?id=682318
2012-12-14 00:47:19 +01:00
e89ce7358a overview: don't clone the whole message tray when layouting
The message tray actor also includes notifications themselves. We want
our ghost to be sized as the base part of the tray instead.
Just make sure to use the same style class as the base actor then, as
its height is specified by the CSS.

https://bugzilla.gnome.org/show_bug.cgi?id=690174
2012-12-13 17:56:23 -05:00
bdf0a6fd0b message-tray: use a style class for summary-mode actor
We want to use the same style class for the message tray ghost.

https://bugzilla.gnome.org/show_bug.cgi?id=690174
2012-12-13 17:56:22 -05:00
783abd4f5f Reverting "extensionUtils: Remove unused property"
This reverts commit 28aa9201f0.

This broke the extension-state-changed signal.

https://bugzilla.gnome.org/show_bug.cgi?id=690173
2012-12-13 14:16:44 -05:00
28aa9201f0 extensionUtils: Remove unused property
https://bugzilla.gnome.org/show_bug.cgi?id=690173
2012-12-13 19:55:34 +01:00
378df515d0 extensionSystem: Fix error reporting in lg / ListExtensionErrors()
Commit c99e8eb29 removed this line probably accidentally, as no
effort has been made to replace consumers in lookingGlass and
shellDBus.

https://bugzilla.gnome.org/show_bug.cgi?id=690173
2012-12-13 19:55:33 +01:00
04d68c6e36 overview: overview as boxlayouts
https://bugzilla.gnome.org/show_bug.cgi?id=682286
2012-12-13 09:38:45 -05:00
5308d12239 search: skip combining diacritical marks in search operations
https://bugzilla.gnome.org/show_bug.cgi?id=648587
2012-12-12 17:41:45 +01:00
15cac0157c networkAgent: Only unregister if we've registered
It's possible to turn auto_register back on, but not immediately be
registered. Don't cause a (harmless) critical in this case.

https://bugzilla.gnome.org/show_bug.cgi?id=689884
2012-12-12 06:35:46 -05:00
fc696bc054 userMenu: Update user name on session mode changes
The new privacy panel offers separate settings for the user name
visibility in lock screen and normal mode. The code to support
either setting is already in place, but we need to make sure to
update the user name on session mode changes to pick up the correct
one.

https://bugzilla.gnome.org/show_bug.cgi?id=690049
2012-12-11 20:31:46 +01:00
5a7b7b7389 bluetooth: Update settings desktop file
For the one now used in gnome-control-center

https://bugzilla.gnome.org/show_bug.cgi?id=690046
2012-12-11 17:12:00 +01:00
9c9cf6f5ab Depend on libpulse 2.0
Commit 3af9f636af introduced a dependency on
libpulse 2.0 so best to check for it.

https://bugzilla.gnome.org/show_bug.cgi?id=689965
2012-12-11 11:42:50 +01:00
0155739bef search: remove more dead code
https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-12-10 16:50:54 -05:00
d0902fa28b search: remove SearchProvider base class
This is causing more confusion than anything else these days; the DBus
API is properly documented now and that's what people are expected to
use, the rest are implementation details we're not interested in
exposing.

https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-12-10 16:50:54 -05:00
3aa0d455c9 search: remove SearchResultDisplay base class
It's unused, and the clear() method is just wrong. Remove it.

https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-12-10 16:50:54 -05:00
835d4e4b58 appDisplay: Don't use icons in results for settings
The search results are not necessarily improved by including icons, so don't.

https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-12-10 16:50:54 -05:00
6075332f2f remoteSearch: We do not need a fallback for createIcon
Remote providers no longer have access to a grid layout, where an icon is
a requirement. If they don't specify an icon, don't create one.

https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-12-10 16:50:53 -05:00
c5d8484e19 searchDisplay: Add ListSearchResult and ListSearchResults
These are for all search results except apps (and Wanda).
We also simplify a bit the packing of search results, which removes some
ugly code in navigateFocus() where we needed to call
st_widget_navigate_focus() twice, since the grid icon was composed by
two nested boxes, both focusable.

https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-12-10 16:50:53 -05:00
575ab0d609 searchDisplay: simplify drag actor source code
Make sure this._dragActorSource is always set from _init() to siplify
code.

https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-12-10 16:50:53 -05:00
b48a7d5cfd popupMenu: Break separator drawing code out of PopupSeparatorMenuItem
And into a separate HorizontalSeparatorClass.

https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-12-10 16:50:53 -05:00
9af107feff searchDisplay, and others: Switch from provider title to provider icon
Display a '+' icon on the provider icon if there are more results that are
hidden. If the provider icon is clicked, ask the provider to launch itself and
perform a search with the current terms.

https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-12-10 16:50:53 -05:00
70b5db16d3 search: make sure to pass a timestamp to LaunchSearch()
Do not repeat past mistakes while we're still in time.

https://bugzilla.gnome.org/show_bug.cgi?id=690009
2012-12-10 16:50:22 -05:00
5072ea7e47 Calendar: remove dead code
this._weekdateHeader is never assigned, so _setWeekdateHeaderWidth is
useless. Everything else falls from that.

Spotted by mathematical.coffee@gmail.com, our human coverity :)

https://bugzilla.gnome.org/show_bug.cgi?id=687250
2012-12-10 22:34:10 +01:00
267 changed files with 67575 additions and 44051 deletions

View File

@ -16,6 +16,7 @@ EXTRA_DIST = \
# These are files checked into Git that we don't want to distribute
DIST_EXCLUDE = \
.gitignore \
.gitmodules \
gnome-shell.doap \
HACKING \
MAINTAINERS \

475
NEWS
View File

@ -1,3 +1,478 @@
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]
* Tweak timeout for activating windows during XDND [Adel; #700150]
* Fix ellipsization in control buttons in app picker [Carlos; #696307]
* Fix DND to empty dash [Florian; #684618]
* Fix OSD window appearing below system modal dialogs [Rui; #701269]
* Clear clipboard on screen lock to prevent information leak [Florian; #698922]
* Allow session mode specific overrides schema [Florian; #701717]
* window-switcher: Only show windows from current workspace by default
[Florian; #701214]
* logout dialog: Show the correct text right away [Matthias; #702056]
* bluetooth: Port to bluez 5 [Emilio; #700891]
* dateMenu: Allow events to span multiple lines [Giovanni; #701231]
* gdm: Clear message queue when no more messages are pending [Jonh; #702458]
* Misc bug fixes and cleanups [Jasper, Florian, Adel, Giovanni; #693836,
#700972, #701386, #700877, #701755, #698918, #701224, #702125, #701954,
#701849, #702121]
Contributors:
Giovanni Campagna, Matthias Clasen, Fran Diéguez, Adel Gadllah, Rui Matos,
Florian Müllner, Emilio Pozuelo Monfort, Carlos Soriano, Jasper St. Pierre,
Jonh Wendell
Translations:
Marek Černocký [cs], Victor Ibragimov [tg], Fran Diéguez [gl],
Benjamin Steinwender [de], Cheng-Chia Tseng [zh_HK, zh_TW],
eternalhui [zh_CN], Ivaylo Valkov [bg], Kjartan Maraas [nb],
Daniel Mustieles [es]
3.9.2
=====
* Use a symbolic icon for DESKTOP windows [Matthias; #697914]
* Move paint state cache into StWidget [Jasper; #697274]
* gdm: Fix regression where domain login hint not shown [Stef; #698200]
* Make calendar keyboard navigable [Tanner; #667434]
* Hide "Open Calendar" item when no calendar app is installed [Lionel; #697725]
* Update how branding appears on login screen [Florian; #694912, #699877]
* Allow OSD popups to grow if necessary [Marta; #696523]
* Fix offset of shadow offscreen rendering [Lionel; #698301]
* Fix insensitive button preventing empty keyring password [Stef; #696304]
* Allow cancelling keyring dialog between prompts [Stef; #682830]
* modalDialog: Show spinner while working [Stef; #684438]
* overview: Only show close buttons for windows that may close [Jasper; #699269]
* Add input purpose and hints to StEntry and StIMText [Daiki; #691392]
* Set input-purpose property for password entries [Rui; #700043]
* Provide a DBus API for screencasting [Florian; #696247]
* overview: Disable hotcorner during DND [Jasper; #698484]
* polkitAgent: Allow retrying after mistyped passwords [Stef; #684431]
* Add a way to get backtraces from criticals and warnings [Giovanni; #700262]
* Allow switch-to-workspace-n keybindings in overview [Florian; #649977]
* Update man page [Matthias; #700339]
* Add FocusSearch DBus method [Florian; #700536]
* Hide frequent view when app monitoring is disabled [Florian; #699714]
* Show switcher popup for switch-to-workspace-n keybindings [Elad; #659288]
* gdm: Update the session chooser style [Allan; #695742]
* Fix some app folders getting truncated at the top [Florian; #694371]
* Don't block the message tray while a notification is showing [Jasper; #700639]
* popupMenu: Allow for an optional border for slider handle [Florian; #697917]
* Re-lock screen when restarted after a crash [Colin; #691987]
* Synchronize input source switching with key events [Rui; #697007]
* Switch input source on modifiers-only accelerator [Rui; #697008]
* Allow input source switching in message tray [Rui; #697009]
* Misc bug fixes and cleanups [Alban, Jasper, Giovanni, Florian, Rui, Tomeu,
Stef, Gustavo; #698863, #699799, #699800, #676285, #699975, #700097, #698812,
#698486, #700194, #695314, #700257, #699678, #700356, #700322, #700394,
#700409, #700595, #700625, #691746, #700620, #700807, #659288, #700784,
#700842, #700847, #700488, #700735, #696159, #700900, #700853, #700923,
#700944, #697661, #700854, #700190, #699189, #701097]
Contributors:
Elad Alfassa, Alban Browaeys, Giovanni Campagna, Matthias Clasen, Allan Day,
Tanner Doshier, Lionel Landwerlin, Rui Matos, Simon McVittie,
Marta Milakovic, Florian Müllner, Gustavo Padovan, Jasper St. Pierre,
Daiki Ueno, Tomeu Vizoso, Stef Walter, Colin Walters
Translations:
Matej Urbančič [sl], Kjartan Maraas [nb], Victor Ibragimov [tg],
Dušan Kazik [sk], Gil Forcada [ca], Daniel Mustieles [es]
3.9.1
=====
* Add additional toggle-overview keybinding [Matthias; #698251]
* Disable <super> shortcut when sticky keys are enabled [Matthias; #685974]
* Disable tray context menu while a notification displays [Jasper; #695800]
* Watch GApplication busy state [Cosimo; #697207]
* Disable style transitions if animations are disabled [Jasper; #698391]
* Filter out hidden applications from "Frequent" view [Giovanni; #696949]
* Fix window previews swapping place randomly [Jasper; #694469, #698776]
* Add support for serialized GIcons in remote search providers [Cosimo; #698761]
* Fix hotcorner regression in RTL locales [Jasper; #698884]
* Allow some keybindings to work while a top bar menu is open [Florian; #698938]
* Make open-app-menu keybinding a toggle action [Florian; #686756]
* Only recognize common URL schemes in notification messages [Monica; #661225]
* Misc fixes and cleanups [Tim, Jasper, Florian, Giovanni, Owen; #698531,
#698622, #698427, #698483, #698513, #697203, #698959, #698918, #699029,
#699075, #696720, #649748]
Contributors:
Giovanni Campagna, Cosimo Cecchi, Monica Chelliah, Matthias Clasen, Tim Lunn,
Florian Müllner, Jasper St. Pierre, Michael Wood, Owen W. Taylor
Translations:
Fran Diéguez [gl], Muhammet Kara [tr], Daniel Mustieles [es],
Gil Forcada [ia], Anish A [ml], Dimitris Spingos [el], Marek Černocký [cs],
Žygimantas Beručka [lt]
3.8.1
=====
* Clip window group during startup animation [Jasper; #696323]
* Check for logind rather than systemd [Martin; #696252]
* Don't special-case last remote search provider position [Giovanni; #694974]
* Fix memory leaks [Ray, Jasper; ##697119, #697295, #697300, #697395]
* AppSwitcherPopup: Activate only the selected window if any [Rui; #697480]
* Enable screen recorder keybinding in all modes [Florian; #696200]
* Remove box-shadow from screen shield for performance reasons [Adel; #697274]
* Add support for -st-natural-width/height CSS properties [Giovanni; #664411]
* Remove excessive padding from notification buttons [Allan; #664411]
* Fix thumbnail dragging in overview [Jasper; #697504]
* theme-node: Add get_url()/lookup_url() methods [Florian; #693688]
* Misc bug fixes and cleanups [Jasper, Rui, Colin, David, Ray, Matthias:
#695859, #696259, #696585, #696436, #697432, #697435, #697560, #697722,
#697709]
Contributors:
Giovanni Campagna, Matthias Clasen, Allan Day, Adel Gadllah, David Gumberg,
Rui Matos, Florian Müllner, Martin Pitt, Jasper St. Pierre, Ray Strode,
Colin Walters
Translations:
Daniel Martinez [an], Bruce Cowan [en_GB], Khaled Hosny [ar],
Ihar Hrachyshka [be], Aron Xu [zh_CN], Wojciech Szczęsny [pl],
Inaki Larranaga Murgoitio [eu], Kjartan Maraas [nb],
Милош Поповић [sr, sr@latin], Trần Ngọc Quân [vi]
3.8.0.1
=======
* Background bug fixes [Ray; #696712]
3.8.0
=====
* Remove blur and desaturation from lock screen [Jasper; #696322]
* Remove scroll view fade near edges [Adel; #696404]
* dateMenu: Open calendar component when using Evolution [Florian; #696432]
* Fix unlocking on fast user switch [Cosimo; #696287]
* Tweak screen shield animation [Rui; #696380]
* Fix major memory leak when changing backgrounds [Ray; #696157]
* Miscellaneous bug fixes [Jasper, Adel, Florian; #696199, #696212, #696422,
#696447, #696235]
Contributors:
Giovanni Campagna, Cosimo Cecchi, Adel Gadllah, Rui Matos, Florian Müllner,
Jasper St. Pierre, Ray Strode
Translations:
Alexandre Franke [fr], Victor Ibragimov [tg], Arash Mousavi [fa],
Gabor Kelemen [hu], Sandeep Sheshrao Shedmake [mr], ManojKumar Giri [or],
Shantha kumar [ta], Rajesh Ranjan [hi], Stas Solovey [ru],
Shankar Prasad [kn], Dušan Kazik [sk], Ihar Hrachyshka [be],
Wouter Bolsterlee [nl], Kris Thomsen [da], Jiro Matsuzawa [ja],
Daniel Korostil [uk], Ani Peter [ml], Krishnababu Krothapalli [te],
Mantas Kriaučiūnas [lt], Praveen Illa [te]
3.7.92
======
* Drop fallback lock implementation [Florian; #693403]
* Don't let the user trigger message-tray when in fullscreen [Jasper; #694997]
* Scroll search results when using keynav [Jasper; #689681]
* Allow raising the shield by starting to type the password [Jasper; #686740]
* Improve tracking of fullscreen windows [Owen; #649748]
* Improve animation of new windows in overview [Giovanni; #695582]
* workspace switcher: Animate new workspaces created by DND [Giovanni; #685285]
* Give user time to read messages on login screen [Ray; #694688]
* Misc bug fixes and cleanups [Jasper, Ray, Florian, Cosimo, Giovanni, Adel,
Stef, Takao, Rui, Neil; #695154, #694993, #695272, #691578, #694321, #695338,
#695409, #695458, #695526, #695601, #695471, #695324, #695650, #695656,
#695659, #695485, #695395, #694951, #695824, #695841, #695801, #694321,
#693708, #695800, #695607, #695882, #691578, #685851, #694371, #690857,
#694092, #695747, #696007, #693438, #696064, #696102
Contributors:
Giovanni Campagna, Cosimo Cecchi, Allan Day, Takao Fujiwara, Adel Gadllah,
Tim Lunn, Rui Matos, Florian Müllner, Neil Roberts, Jasper St. Pierre,
Ray Strode, Stef Walter, Colin Walters, Owen W. Taylor
Translations:
Nilamdyuti Goswami [as], Chao-Hsiung Liao [zh_HK, zh_TW],
Yuri Myasoedov [ru], Gheyret Kenji [ug], Baurzhan Muftakhidinov [kk],
Ville-Pekka Vainio [fi], Matej Urbančič [sl],
Мирослав Николић [sr, sr@latin], Rūdolfs Mazurs [lv], Christian Kirbach [de],
Andika Triwidada [id], Gil Forcada [ca], Mattias Põldaru [et],
Duarte Loreto [pt], Adam Matoušek [cs], Changwoo Ryu [ko],
Ihar Hrachyshka [be], Carles Ferrando [ca@valencia], Sweta Kothari [gu]
3.7.91
======
* overview: Fade out controls during DND that are not targets [Cosimo; #686984]
* overview: Keep open when a Control key is held [Florian; #686984]
* Improve login screen => session transition [Ray; #694321]
* Center application grid horizontally [Florian; #694261]
* Fix hiding panel when fullscreen windows span multiple monitors [Adel; 646861]
* Tweak thresholds of pressure barrier [Jasper; #694467]
* Tweak window picker layout [Jasper; #694902]
* Expose key grab DBus API to gnome-settings-daemon [Florian; #643111]
* Don't always show message tray in overview, add message indicator
[Cosimo; #687787]
* Tweak startup animation [Ray; #694326]
* Add OSD popups and expose them to gnome-settings-daemon [Florian; #613543]
* Move loupe icon to the start of the search entry [Jasper; #695069]
* Don't show the input switcher with less than 2 items [Rui; 695000]
* Fix auto-completion of system modals immediately upon display [Stef; #692937]
* Ignore workspaces in alt-tab [Florian; #661156]
* Disable copying text from password entries [Florian; #695104]
* Use standard styling for ibus candidate popups [Allan; #694796]
* Fix calendar changing height on month changes [Giovanni; #641383]
* Port the hot corner to use pressure barriers [Jasper; #663661]
* Misc bug fixes and cleanups: [Hashem, Giovanni, Alban, Jasper, Cosimo,
Florian, Adel, Daniel, Matthias, Ray, Rui, Guillaume, Stef; #685849, #690643,
#694292, #693814, #694234, #694365, #694287, #694336, #694256, #694261,
#663601, #694441, #694284, #694463, #694475, #687248, #694394, #694320,
#694701, #694784, #694858, #694906, #694327, #694876, #694905, #694969,
#694970, #694988, #695006, #695001, #694998, #695023, #695002, #695073,
#695126, #687748, #694837, #693907, #679851, #694988]
Contributors:
Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Alban Crequy, Allan Day,
Guillaume Desmottes, Adel Gadllah, Rui Matos, Daniel Mustieles,
Hashem Nasarat, Jasper St. Pierre, Ray Strode, Stef Walter
Translations:
Yuri Myasoedov [ru], Adam Matoušek [cs], Piotr Drąg [pl], Matej Urbančič [sl],
Sweta Kothari [gu], Kjartan Maraas [nb], Nguyễn Thái Ngọc Duy [vi],
Chao-Hsiung Liao [zh_HK, zh_TW], Dimitris Spingos [el],
Inaki Larranaga Murgoitio [eu], Luca Ferretti [it], A S Alam [pa],
Gheyret Kenji [ug], Stas Solovey [ru], Enrico Nicoletto [pt_BR],
Fran Diéguez [gl], Daniel Mustieles [es], Aurimas Černius [lt]
3.7.90
======
* Let GNOME Shell work on EGL and GLES2 [Neil; #693225, #693438, #693339]
* Implement middle-click paste [Jasper; #645019]
* Fix top bar transition between session modes [Rui; #692966]
* Trigger the message tray with downward pressure [Jasper; #677215]
* Don't ask for a password on shutdown [Adel; #693385]
* Add a context menu to the message tray [Adel; #691035, #693887]
* Use unicode formatting in the date menu [Matthias; #689251]
* Use proper ellipsis instead of three dots [Jeremy; #689542]
* Tweak screen shield animation [Giovanni; #691964]
* Always hide the OSK when showing the message tray [Florian; #662687]
* Support sound in notifications [Giovanni; #642831]
* Place application popup menus above chrome [Jasper; #633620]
* Hide overview elements while searching [Cosimo; #682050]
* Implement updated IBus candidate popup designs [Rui; #691902]
* Add support for enable-animations preference [Cosimo; #655746]
* Don't always show the message tray in the overview [Cosimo; #693987]
* Improve arrangement of window previews [Adel; #690313]
* Remove builtin settings provider [Giovanni; #690824]
* Minimize fullscreen windows when they end up in the background [Adel; #693991]
* Add context menu to the background actor [Jasper; #681540]
* Handle backgrounds in the shell, improve startup animation [Ray; #682429]
* Hide universal access menu when not needed [Giovanni; #681528]
* Implement updated app picker designs [Florian; #694192]
* Improve login manager -> session transition [Ray; #694062]
* Don't use a grid layout in window picker [Adel; #694210]
* Use scroll wheel for workspace switching rather than zoom [Florian; #686639]
* Misc bug fixes and cleanups: [Jasper, Florian, Debarshi, Adel, Matthias,
Giovanni, Daiki, Rico, Bastien, Cosimo, Ray, Allan, Antonio; #693284,
#692680, #691746, #693303, #693162, #693161, #693522, #693385, #691715,
#688915, #689106, #682429, #693570, #693737, #693458, #692845, 693836,
#681540, #679925, #688227, #692773, #693909, #683288, #693854, #693746,
#693931, #693924, #693940, #693970, #693935, #693937, #693974, #693936,
#693975, #693822, #694030, #685849, #694052, #694035, #694038, #694079,
#694064, #681735, #694100, #694057, #694070, #693572, #693896, #686984,
#694123, #694125, #693756, #693757, #687556, #694215, 694062, #694227,
#694240, #694234, #694264, 694276, 694282, #694241, #689394, #694202,
#694265, #694289, #691806, #694290, #694296]
Contributors:
Jeremy Bicha, Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Allan Day,
António Fernandes, Adel Gadllah, Rui Matos, Florian Müllner, Bastien Nocera,
Debarshi Ray, Neil Roberts, Jasper St. Pierre, Ray Strode, Rico Tzschichholz,
Daiki Ueno
Translations:
Yasumichi Akahoshi [ja], Yoji TOYODA [ja], Dušan Kazik [sk],
Wouter Bolsterlee [nl], Matej Urbančič [sl], Gheyret Kenji [ug],
Ivaylo Valkov [bg], Daniel Korostil [uk], Gheyret Kenji [ug],
Daniel Mustieles [es], Anish A [ml], Gil Forcada [ca],
Carles Ferrando [ca@valencia], Мирослав Николић [sr, sr@latin],
Aurimas Černius [lt], Rafael Ferreira [pt_BR], Fran Diéguez [gl],
Piotr Drąg [pl], Luca Ferretti [it], A S Alam [pa]
3.7.5
=====
* MessageTray: pass keyboard events to tray icons [Giovanni; #687425]
* network: add support for virtual devices (vlan, bond, bridge) [Dan; #677144]
* gdm: Allow right-clicking buttons for left-handed users [Jasper; #688748]
* Make list search results span all available horizontal space [Tanner; #691967]
* Make Show-Applications button depress when held down [Hashem; #692319]
* Set a max width on search results [Cosimo; #692453]
* Reserve scrollbar allocation for automatic policy [Cosimo; #686881]
* Improve scaling algorithm for window thumbnails [Jasper; #686944]
* Fix launching settings panels after g-c-c changes [Jasper; #692483]
* Stop launching applications from empty searches [Hashem; #692391]
* Implement per-source notification filtering [Giovanni; #685926]
* ScreenShield: Omit ActiveChanged() signal at end of fade [Giovanni; #691964]
* ScreenShield: Lower the shield on idle before locking [Giovanni; #692560]
* Make previews of minimized windows translucent in overview [Florian; #692999]
* windowManager: Respect icon geometry when minimizing [Florian; #692997]
* ScreenShield: Only show lock icon when actually locked [Giovanni; #693007]
* general: Use & instead of 'and' for Settings panels [Jeremy; #689590]
* network: Add support for new ModemManager1 interface [Aleksander; #687359]
* network: Handle LTE-only modems as GSM ones [Aleksander; #688144]
* mobile-providers: Port to libnm-gtk [Aleksander; #688943]
* general: Consistently use Title Case in top bar [Jeremy; #689589]
* panel: Add :overview pseudo class while in overview [Florian; #693218]
* sessionMode: Add support for mode-specific styling [Florian; #693219]
* loginManager: Make suspend a NOP in the ConsoleKit patch [Florian; #693162]
* screenShield: Inhibit suspend until the screen is locked [Florian; #686482]
* Misc bug fixes and cleanups [Jasper, Giovanni, Rui, Cosimo, Florian, Stefano,
Adel, Yanko; #691745, #691731, #690171, #689091, #691976, #691963, #684279,
#692052, #692091, #642831, #692454, #692715, #692678, #692723, #692677,
#683986, #692693, #692749, #692948, #692995, #692996, #692994, #677215,
#692586, #693067, #693031, #693049, #643111, #693161, #693220]
Contributors:
Jeremy Bicha, Giovanni Campagna, Cosimo Cecchi, Tanner Doshier,
Stefano Facchini, Adel Gadllah, Yanko Kaneti, Rui Matos, Aleksander Morgado,
Florian Müllner, Hashem Nasarat, Jasper St. Pierre, Dan Winship
Translations:
Duarte Loreto [pt], Daniel Mustieles [es], Kjartan Maraas [nb],
Nilamdyuti Goswami [as], Мирослав Николић [sr,sr@latin],
Tobias Endrigkeit [de], Fabio Tomat [fur], Matej Urbančič [sl], A S Alam [pa],
Inaki Larranaga Murgoitio [eu], Piotr Drąg [pl], Wouter Bolsterlee [nl],
Gheyret Kenji [ug], Yaron Shahrabani [he], Chao-Hsiung Liao [zh_HK,zh_TW],
Milo Casagrande [it], Benjamin Steinwender [de]
3.7.4.1
=======
* userMenu: Use show-full-name-in-top-bar setting [Bastien; #689561]
* dateMenu: Add "Open Clocks" entry [Mathieu; #644390]
* screenshot: Immediately show the flash spot [Jasper; #691875]
* Misc. bug fixes [Rico, Jeremy]
Contributors:
Jeremy Bicha, Mathieu Bridon, Bastien Nocera, Jasper St. Pierre,
Rico Tzschichholz
Translations:
Ihar Hrachyshka [be]
3.7.4
=====
* Make menu separators crisp [Giovanni, Allan; #641745]
* power: Update for new D-Bus name [Bastien; #690506]
* Add smooth scrolling support [Jasper; #687573]
* Tweak notification layout [Allan; #688506]
* Ping the active window when using the app menu [Giovanni; #684340]
* Make password entries insensitive after submission [Jasper; #690594, #690895]
* Honor lock-delay GSettings key [Giovanni, Matthias; #690766, #691170]
* Use text/calendar preferred app as the calendar app [Giovanni; #690767]
* lookingGlass: Move to an inspect() function [Jasper; #690726]
* Make OSK animation quicker, snappier [Rui; #688642]
* Allow to close chat notifications with Escape [Jasper; #690897]
* Honor org.gnome.desktop.screensaver.user-switch-enabled [Giovanni; #691042]
* Add a SelectArea() DBus method [Cosimo; #687954]
* Support non-absolute paths when saving screenshots [Cosimo; #688004]
* OSK: Fix extended keys popups [Rui; #674955]
* Don't hide or show the keyboard immediately [Rui; #688646]
* Improve padding in power menu [Giovanni; #689297]
* Add per-window input source switching [Rui; #691414]
* Misc bug fixes and cleanups [Rico, Jasper, Giovanni, Rui, Florian, Dan;
#690608, #690589, #690539, #687081, #690667, #690665, #690666, #685856,
#690858, #690895, #680414, #690965, #691019, #690590, #681376, #690180,
#685513, #689263, #691553, #691720, #691743, #691750]
Contributors:
Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Allan Day, Rui Matos,
Florian Müllner, Bastien Nocera, Jasper St. Pierre, Rico Tzschichholz,
Dan Winship
Translations:
Matej Urbančič [sl], Kjartan Maraas [nb], Mattias Põldaru [et],
Yaron Shahrabani [he], Aurimas Černius [lt], Khaled Hosny [ar],
Fran Diéguez [gl], Daniel Mustieles [es], Piotr Drąg [pl], Balázs Úr [hu],
Baurzhan Muftakhidinov [kk], Tobias Endrigkeit [de], Dušan Kazik [sk],
Aron Xu [zh_CN], Gheyret Kenji [ug]
3.7.3
=====
* Add 'No Messages' label when message tray is empty [Victoria; #686738]
* Use better icons in Ctrl-Alt-Tab popup [Stéphane; #641303]
* Show the OSK on the monitor where the focused window lives [Giovanni; #685856]
* Highlight window clone and caption when hovered [Giovanni, Marc; #665310]
* Improve login process indication [Stéphane; #687113]
* Omit empty categories in apps view [Stéphane; #687970]
* Style panel differently according to mode [Florian; #684573]
* Make it possible to hide the user name [Matthias; #688577]
* Consolidate and improve chat connection notifications [Giovanni; #687213]
* Improve notification scrollbar appearance [Carlos; #688393]
* Fade scroll view fade near scrolling edges [Jasper; #689249]
* Add a read-only org.gnome.Shell.Mode property [Debarshi; #689300]
* Don't close message tray after using context menus [Giovanni; #689296]
* Port swipe-scrolling to ClutterPanAction [Jasper, Florian; #689062, #689552]
* Remember state of 'Remember Password' checkbox [Ron; #688039]
* Improve timestamp format in chat notifications [Carlos; #680989]
* Improve style of missed-messages counter [Carlos; #686472]
* Omit connection failure notifications if cancelled by user [Giovanni; #684823]
* Add window-based Alt-Tab popup [Florian; #688913]
* Support external session mode definitions [Florian; #689304]
* Support session-mode-specific extensions [Florian; #689305]
* Support 'parentMode' property in session modes [Florian; #689308]
* Support a new org.gnome.ShellSearchProvider2 DBus interface
[Cosimo; #689735, #690009]
* Add "windows" to Ctrl-Alt-Tab popup [Jasper; #689653]
* Port PopupMenu to GrabHelper [Jasper; #689109, #689954]
* Show headphone icon when headphones are plugged in [Giovanni; #675902]
* Display (non-app) search results as list [Tanner, Cosimo; #681797]
* Skip diacritical marks in search terms [Aleksander; #648587]
* Expose all engine options in input sources [Giovanni, Rui; #682318]
* Add input source switcher popup [Rui; #682315]
* Add minimal support for InfiniBand in network menu [Dan; #677150]
* Misc bug fixes and cleanups [Sebastian, Aleksander, Giovanni, Tim, Cosimo,
Florian, Matthias, Rui, Lionel, Colin, Piotr, Guillaume, Bastien, Tanner,
Carlos, Stéphane, Jakub; #688422, #688379, #688750, #688771, #686800,
#688133, #688895, #688966, #683986, #688004, #689108, #689029, #683449,
#688196, #689304, #689243, #689295, #689325, #689400, #679168, #689568,
#689537, #689528, #689749, #689789, #689353, #689820, #689868, #689778,
#689959, #688589, #688589, #689955, #687250, #689965, #690046, #690049,
#689884, #682286, #690173, #690174, #672941, #689876, #687881, #690171,
#690241, #690312, #690175, #687955, #650843, #688234, #690427
Contributors:
Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Stéphane Démurget,
Guillaume Desmottes, Tanner Doshier, Piotr Drąg, Sebastian Keller,
Lionel Landwerlin, Tim Lunn, Victoria Martínez de la Cruz, Aleksander Morgado,
Florian Müllner, Bastien Nocera, Marc Plano-Lesay, Carlos Soriano Sánchez,
Jakub Steiner, Jasper St. Pierre, Colin Walters, Dan Winship, Ron Yorston
Translations:
Yuri Myasoedov [ru], Wouter Bolsterlee [nl], Yaron Shahrabani [he],
Nilamdyuti Goswami [as], Ani Peter [ml], Kjartan Maraas [nb],
Dr.T.Vasudevan [ta], A S Alam [pa], Shankar Prasad [kn], Khaled Hosny [ar],
Daniel Mustieles [es], Dušan Kazik [sk]
3.7.2
=====
* Enforce RTL in he for messages that might end up as LTR [Florian; #686630]

View File

@ -1,5 +1,5 @@
AC_PREREQ(2.63)
AC_INIT([gnome-shell],[3.7.2],[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])
@ -16,8 +16,6 @@ m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
# Checks for programs.
AC_PROG_CC
# Needed for per-target cflags, like in gnomeshell-taskpanel
AM_PROG_CC_C_O
# Initialize libtool
LT_PREREQ([2.2.6])
@ -55,30 +53,29 @@ if $PKG_CONFIG --exists gstreamer-1.0 '>=' $GSTREAMER_MIN_VERSION ; then
AC_MSG_RESULT(yes)
build_recorder=true
recorder_modules="gstreamer-1.0 gstreamer-base-1.0 x11 gtk+-3.0"
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes gl)
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes)
else
AC_MSG_RESULT(no)
fi
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
CLUTTER_MIN_VERSION=1.11.11
CLUTTER_MIN_VERSION=1.13.4
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
GJS_MIN_VERSION=1.33.2
MUTTER_MIN_VERSION=3.7.2
GTK_MIN_VERSION=3.3.9
GIO_MIN_VERSION=2.35.0
GJS_MIN_VERSION=1.35.4
MUTTER_MIN_VERSION=3.9.4
GTK_MIN_VERSION=3.7.9
GIO_MIN_VERSION=2.37.0
LIBECAL_MIN_VERSION=3.5.3
LIBEDATASERVER_MIN_VERSION=3.5.3
LIBEDATASERVERUI_MIN_VERSION=3.5.3
TELEPATHY_GLIB_MIN_VERSION=0.17.5
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
POLKIT_MIN_VERSION=0.100
STARTUP_NOTIFICATION_MIN_VERSION=0.11
GCR_MIN_VERSION=3.3.90
GNOME_DESKTOP_REQUIRED_VERSION=3.7.1
GCR_MIN_VERSION=3.7.5
GNOME_DESKTOP_REQUIRED_VERSION=3.7.90
GNOME_MENUS_REQUIRED_VERSION=3.5.3
NETWORKMANAGER_MIN_VERSION=0.9.7
NETWORKMANAGER_MIN_VERSION=0.9.8
PULSE_MIN_VERS=2.0
# Collect more than 20 libraries for a prize!
PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
@ -90,46 +87,29 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION
$recorder_modules
gdk-x11-3.0 libsoup-2.4
gl
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION
gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
libcanberra
libcanberra libcanberra-gtk3
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION
libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
gnome-keyring-1 gcr-3 >= $GCR_MIN_VERSION)
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
GNOME_KEYBINDINGS_KEYSDIR=`$PKG_CONFIG --variable keysdir gnome-keybindings`
AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
saved_CFLAGS=$CFLAGS
saved_LIBS=$LIBS
CFLAGS=$GNOME_SHELL_CFLAGS
LIBS=$GNOME_SHELL_LIBS
AC_CHECK_FUNCS(JS_NewGlobalObject XFixesCreatePointerBarrier)
CFLAGS=$saved_CFLAGS
LIBS=$saved_LIBS
libsecret-unstable gcr-base-3 >= $GCR_MIN_VERSION)
PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11)
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
PKG_CHECK_MODULES(TRAY, gtk+-3.0)
PKG_CHECK_MODULES(GVC, libpulse libpulse-mainloop-glib gobject-2.0)
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.7.2)
PKG_CHECK_MODULES(GVC, libpulse >= $PULSE_MIN_VERS libpulse-mainloop-glib gobject-2.0)
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.7.4)
PKG_CHECK_MODULES(CARIBOU, caribou-1.0 >= 0.4.8)
AC_MSG_CHECKING([for bluetooth support])
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.9.0],
[BLUETOOTH_DIR=`$PKG_CONFIG --variable=applet_libdir gnome-bluetooth-1.0`
BLUETOOTH_LIBS=`$PKG_CONFIG --variable=applet_libs gnome-bluetooth-1.0`
AC_SUBST([BLUETOOTH_LIBS],["$BLUETOOTH_LIBS"])
@ -146,32 +126,10 @@ PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedatas
AC_SUBST(CALENDAR_SERVER_CFLAGS)
AC_SUBST(CALENDAR_SERVER_LIBS)
AC_ARG_WITH(systemd,
AS_HELP_STRING([--with-systemd],
[Add systemd support]),
[with_systemd=$withval], [with_systemd=auto])
GNOME_KEYBINDINGS_KEYSDIR=`$PKG_CONFIG --variable keysdir gnome-keybindings`
AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
PKG_CHECK_MODULES(SYSTEMD,
[libsystemd-login libsystemd-daemon],
[have_systemd=yes], [have_systemd=no])
if test "x$with_systemd" = "xauto" ; then
if test x$have_systemd = xno ; then
use_systemd=no
else
use_systemd=yes
fi
else
use_systemd=$with_systemd
fi
if test "x$use_systemd" = "xyes"; then
if test "x$have_systemd" = "xno"; then
AC_MSG_ERROR([Systemd support explicitly required, but systemd not found])
fi
AC_DEFINE(WITH_SYSTEMD, 1, [systemd support])
fi
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
MUTTER_GIR_DIR=`$PKG_CONFIG --variable=girdir libmutter`
MUTTER_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter`
@ -198,16 +156,6 @@ fi
# Sets GLIB_GENMARSHAL and GLIB_MKENUMS
AM_PATH_GLIB_2_0()
G_IR_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0`
AC_SUBST(G_IR_SCANNER)
G_IR_COMPILER=`$PKG_CONFIG --variable=g_ir_compiler gobject-introspection-1.0`
AC_SUBST(G_IR_COMPILER)
G_IR_GENERATE=`$PKG_CONFIG --variable=g_ir_generate gobject-introspection-1.0`
AC_SUBST(G_IR_GENERATE)
GIRDIR=`$PKG_CONFIG --variable=girdir gobject-introspection-1.0`
AC_SUBST(GIRDIR)
TYPELIBDIR="$($PKG_CONFIG --variable=typelibdir gobject-introspection-1.0)"
AC_SUBST(TYPELIBDIR)
GTK_DOC_CHECK([1.15], [--flavour no-tmpl])
@ -223,32 +171,7 @@ if test "$enable_man" != no; then
fi
AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no)
# Stay command-line compatible with the gnome-common configure option. Here
# minimum/yes/maximum are the same, however.
AC_ARG_ENABLE(compile_warnings,
AS_HELP_STRING([--enable-compile-warnings=@<:@no/minimum/yes/maximum/error@:>@],[Turn on compiler warnings]),,
enable_compile_warnings=error)
changequote(,)dnl
if test "$enable_compile_warnings" != no ; then
if test "x$GCC" = "xyes"; then
case " $CFLAGS " in
*[\ \ ]-Wall[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Wall" ;;
esac
case " $CFLAGS " in
*[\ \ ]-Wmissing-prototypes[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Wmissing-prototypes" ;;
esac
if test "$enable_compile_warnings" = error ; then
case " $CFLAGS " in
*[\ \ ]-Werror[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Werror -Wno-error=deprecated-declarations" ;;
esac
fi
fi
fi
changequote([,])dnl
GNOME_COMPILE_WARNINGS([error])
AC_ARG_ENABLE(jhbuild-wrapper-script,
AS_HELP_STRING([--enable-jhbuild-wrapper-script],[Make "gnome-shell" script work for jhbuild]),,enable_jhbuild_wrapper_script=no)

View File

@ -11,6 +11,9 @@
<KeyListEntry name="focus-active-notification"
_description="Focus the active notification"/>
<KeyListEntry name="toggle-overview"
_description="Show the overview"/>
<KeyListEntry name="toggle-application-view"
_description="Show all applications"/>

View File

@ -1,3 +1,6 @@
wandadir = $(pkgdatadir)
dist_wanda_DATA = wanda.png
desktopdir=$(datadir)/applications
desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
@ -12,6 +15,8 @@ desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
introspectiondir = $(datadir)/dbus-1/interfaces
introspection_DATA = \
org.gnome.Shell.Screencast.xml \
org.gnome.Shell.Screenshot.xml \
org.gnome.ShellSearchProvider.xml \
org.gnome.ShellSearchProvider2.xml
@ -34,6 +39,7 @@ dist_theme_DATA = \
theme/gnome-shell.css \
theme/logged-in-indicator.svg \
theme/message-tray-background.png \
theme/more-results.svg \
theme/noise-texture.png \
theme/panel-button-border.svg \
theme/panel-button-highlight-narrow.svg \

View File

@ -0,0 +1,96 @@
<!DOCTYPE node PUBLIC
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
<node>
<!--
org.gnome.Shell.Screencast:
@short_description: Screencast interface
The interface used to record screen contents.
-->
<interface name="org.gnome.Shell.Screencast">
<!--
Screencast:
@file_template: the template for the filename to use
@options: a dictionary of optional parameters
@success: whether the screencast was started successfully
@filename_used: the file where the screencast is being saved
Records a screencast of the whole screen and saves it
(by default) as webm video under a filename derived from
@file_template. The template is either a relative or absolute
filename which may contain some escape sequences - %d and %t
will be replaced by the start date and time of the recording.
If a relative name is used, the screencast will be saved in the
$XDG_VIDEOS_DIR if it exists, or the home directory otherwise.
The actual filename of the saved video is returned in @filename_used.
The set of optional parameters in @options currently consists of:
'draw-cursor'(b): whether the cursor should be included in the
recording (true)
'framerate'(i): the number of frames per second that should be
recorded if possible (30)
'pipeline'(s): the GStreamer pipeline used to encode recordings
in gst-launch format; if not specified, the
recorder will produce vp8 (webm) video (unset)
-->
<method name="Screencast">
<arg type="s" direction="in" name="file_template"/>
<arg type="a{sv}" direction="in" name="options"/>
<arg type="b" direction="in" name="flash"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<!--
ScreencastArea:
@x: the X coordinate of the area to capture
@y: the Y coordinate of the area to capture
@width: the width of the area to capture
@height: the height of the area to capture
@file_template: the template for the filename to use
@options: a dictionary of optional parameters
@success: whether the screencast was started successfully
@filename_used: the file where the screencast is being saved
Records a screencast of the passed in area and saves it
(by default) as webm video under a filename derived from
@file_template. The template is either a relative or absolute
filename which may contain some escape sequences - %d and %t
will be replaced by the start date and time of the recording.
If a relative name is used, the screencast will be saved in the
$XDG_VIDEOS_DIR if it exists, or the home directory otherwise.
The actual filename of the saved video is returned in @filename_used.
The set of optional parameters in @options currently consists of:
'draw-cursor'(b): whether the cursor should be included in the
recording (true)
'framerate'(i): the number of frames per second that should be
recorded if possible (30)
'pipeline'(s): the GStreamer pipeline used to encode recordings
in gst-launch format; if not specified, the
recorder will produce vp8 (webm) video (unset)
-->
<method name="ScreencastArea">
<arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="y"/>
<arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/>
<arg type="s" direction="in" name="file_template"/>
<arg type="a{sv}" direction="in" name="options"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<!--
StopScreencast:
@success: whether stopping the recording was successful
Stop the recording started by either Screencast or ScreencastArea.
-->
<method name="StopScreencast">
<arg type="b" direction="out" name="success"/>
</method>
</interface>
</node>

View File

@ -0,0 +1,128 @@
<!DOCTYPE node PUBLIC
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
<node>
<!--
org.gnome.Shell.Screenshot:
@short_description: Screenshot interface
The interface used to capture pictures of the screen contents.
-->
<interface name="org.gnome.Shell.Screenshot">
<!--
Screenshot:
@filename: The filename for the screenshot
@include_cursor: Whether to include the cursor image or not
@flash: Whether to flash the screen or not
@success: whether the screenshot was captured
@filename_used: the file where the screenshot was saved
Takes a screenshot of the whole screen and saves it
in @filename as png image, it returns a boolean
indicating whether the operation was successful or not.
@filename can either be an absolute path or a basename, in
which case the screenshot will be saved in the $XDG_PICTURES_DIR
or the home directory if it doesn't exist. The filename used
to save the screenshot will be returned in @filename_used.
-->
<method name="Screenshot">
<arg type="b" direction="in" name="include_cursor"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<!--
ScreenshotWindow:
@include_frame: Whether to include the frame or not
@include_cursor: Whether to include the cursor image or not
@flash: Whether to flash the window area or not
@filename: The filename for the screenshot
@success: whether the screenshot was captured
@filename_used: the file where the screenshot was saved
Takes a screenshot of the focused window (optionally omitting the frame)
and saves it in @filename as png image, it returns a boolean
indicating whether the operation was successful or not.
@filename can either be an absolute path or a basename, in
which case the screenshot will be saved in the $XDG_PICTURES_DIR
or the home directory if it doesn't exist. The filename used
to save the screenshot will be returned in @filename_used.
-->
<method name="ScreenshotWindow">
<arg type="b" direction="in" name="include_frame"/>
<arg type="b" direction="in" name="include_cursor"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<!--
ScreenshotArea:
@x: the X coordinate of the area to capture
@y: the Y coordinate of the area to capture
@width: the width of the area to capture
@height: the height of the area to capture
@flash: whether to flash the area or not
@filename: the filename for the screenshot
@success: whether the screenshot was captured
@filename_used: the file where the screenshot was saved
Takes a screenshot of the passed in area and saves it
in @filename as png image, it returns a boolean
indicating whether the operation was successful or not.
@filename can either be an absolute path or a basename, in
which case the screenshot will be saved in the $XDG_PICTURES_DIR
or the home directory if it doesn't exist. The filename used
to save the screenshot will be returned in @filename_used.
-->
<method name="ScreenshotArea">
<arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="y"/>
<arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<!--
FlashArea:
@x: the X coordinate of the area to flash
@y: the Y coordinate of the area to flash
@width: the width of the area to flash
@height: the height of the area to flash
Renders a flash spot effect in the specified rectangle of the screen.
-->
<method name="FlashArea">
<arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="y"/>
<arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/>
</method>
<!--
SelectArea:
@x: the X coordinate of the selected area
@y: the Y coordinate of the selected area
@width: the width of the selected area
@height: the height of the selected area
Interactively allows the user to select a rectangular area of
the screen, and returns its coordinates.
-->
<method name="SelectArea">
<arg type="i" direction="out" name="x"/>
<arg type="i" direction="out" name="y"/>
<arg type="i" direction="out" name="width"/>
<arg type="i" direction="out" name="height"/>
</method>
</interface>
</node>

View File

@ -46,7 +46,7 @@
<!--
GetResultMetas:
@identifiers: An array of result identifiers as returned by GetInitialResultSet() or GetSubsearchResultSet()
@metas: A dictionary describing the given search result, containing 'id' and 'name' (both strings). Optionally, either 'gicon' (a serialized GIcon) or 'icon-data' (raw image data as (iiibiiay) - width, height, rowstride, has-alpha, bits per sample, channels, data) can be specified if the result can be better served with a thumbnail of the content (such as with images).
@metas: A dictionary describing the given search result, containing a human-readable 'name' (string), along with the result identifier this meta is for, 'id' (string). Optionally, either 'gicon' (a serialized GIcon) or 'icon-data' (raw image data as (iiibiiay) - width, height, rowstride, has-alpha, bits per sample, channels, data) can be specified if the result can be better served with a thumbnail of the content (such as with images). A 'description' field (string) may also be specified if more context would help the user find the desired result.
Return an array of meta data used to display each given result
-->

View File

@ -46,7 +46,7 @@
<!--
GetResultMetas:
@identifiers: An array of result identifiers as returned by GetInitialResultSet() or GetSubsearchResultSet()
@metas: A dictionary describing the given search result, containing 'id' and 'name' (both strings). Optionally, either 'gicon' (a serialized GIcon) or 'icon-data' (raw image data as (iiibiiay) - width, height, rowstride, has-alpha, bits per sample, channels, data) can be specified if the result can be better served with a thumbnail of the content (such as with images). A 'description' field (string) may also be specified if more context would help the user find the desired result.
@metas: A dictionary describing the given search result, containing a human-readable 'name' (string), along with the result identifier this meta is for, 'id' (string). Optionally, 'icon' (a serialized GIcon as obtained by g_icon_serialize) can be specified if the result can be better served with a thumbnail of the content (such as with images). 'gicon' (a serialized GIcon as obtained by g_icon_to_string) or 'icon-data' (raw image data as (iiibiiay) - width, height, rowstride, has-alpha, bits per sample, channels, data) are deprecated values that can also be used for that purpose. A 'description' field (string) may also be specified if more context would help the user find the desired result.
Return an array of meta data used to display each given result
-->
@ -75,11 +75,13 @@
<!--
LaunchSearch:
@terms: Array of search terms, which the provider should treat as logical AND.
@timestamp: A timestamp of the user interaction that triggered this call
Asks the search provider to launch a full search in the application for the provided terms.
-->
<method name="LaunchSearch">
<arg type="as" name="terms" direction="in" />
<arg type="u" name="timestamp" direction="in" />
</method>
</interface>
</node>

View File

@ -21,16 +21,6 @@
EnableExtension and DisableExtension DBus methods on org.gnome.Shell.
</_description>
</key>
<key name="enable-app-monitoring" type="b">
<default>true</default>
<_summary>Whether to collect stats about applications usage</_summary>
<_description>
The shell normally monitors active applications in order to present
the most used ones (e.g. in launchers). While this data will be
kept private, you may want to disable this for privacy reasons.
Please note that doing so won't remove already saved data.
</_description>
</key>
<key name="favorite-apps" type="as">
<default>[ 'epiphany.desktop', 'evolution.desktop', 'empathy.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'libreoffice-writer.desktop', 'nautilus.desktop', 'gnome-documents.desktop' ]</default>
<_summary>List of desktop file IDs for favorite applications</_summary>
@ -39,6 +29,14 @@
will be displayed in the favorites area.
</_description>
</key>
<key name="app-folder-categories" type="as">
<default>[ 'Utilities', 'Sundry' ]</default>
<_summary>List of categories that should be displayed as folders</_summary>
<_description>
Each category name in this list will be represented as folder in the
application view, rather than being displayed inline in the main view.
</_description>
</key>
<key name="command-history" type="as">
<default>[]</default>
<_summary>History for command (Alt-F2) dialog</_summary>
@ -65,11 +63,6 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
menuitem in single-user, single-session situations.
</_description>
</key>
<key name="show-full-name" type="b">
<default>true</default>
<_summary>Show full name in the user menu</_summary>
<_description>Whether the users full name is shown in the user menu or not.</_description>
</key>
<key name="remember-mount-password" type="b">
<default>false</default>
<_summary>Whether to remember password for mounting encrypted or remote filesystems</_summary>
@ -114,6 +107,13 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
Overview.
</_description>
</key>
<key name="toggle-overview" type="as">
<default>["&lt;Super&gt;s"]</default>
<_summary>Keybinding to open the overview</_summary>
<_description>
Keybinding to open the Activities Overview.
</_description>
</key>
<key name="toggle-message-tray" type="as">
<default>["&lt;Super&gt;m"]</default>
<_summary>Keybinding to toggle the visibility of the message tray</_summary>
@ -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"/>
@ -204,7 +217,7 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
</_description>
</key>
<key type="b" name="current-workspace-only">
<default>false</default>
<default>true</default>
<summary>Limit switcher to current workspace.</summary>
<description>
If true, only windows from the current workspace are shown in the switcher.

File diff suppressed because it is too large Load Diff

109
data/theme/more-results.svg Normal file
View File

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
id="svg12430"
version="1.1"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="more-results.svg">
<defs
id="defs12432" />
<sodipodi:namedview
id="base"
pagecolor="#7a7a7a"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="8.3155237"
inkscape:cy="0.89548874"
inkscape:document-units="px"
inkscape:current-layer="g14642-3-0"
showgrid="false"
borderlayer="true"
inkscape:showpageshadow="false"
inkscape:window-width="2560"
inkscape:window-height="1376"
inkscape:window-x="1200"
inkscape:window-y="187"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid13002" />
</sodipodi:namedview>
<metadata
id="metadata12435">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-1036.3622)">
<g
style="display:inline"
transform="translate(-141.99984,638.37113)"
inkscape:label="zoom-in"
id="g14642-3-0">
<path
sodipodi:type="inkscape:offset"
inkscape:radius="0"
inkscape:original="M 145.1875 400 C 144.5248 400 144 400.54899 144 401.21875 L 144 410.78125 C 144 411.45101 144.5248 412 145.1875 412 L 154.8125 412 C 155.4752 412 156 411.45101 156 410.78125 L 156 401.21875 C 156 400.54899 155.4752 400 154.8125 400 L 145.1875 400 z M 149 403 L 151 403 L 151 405 L 153 405 L 153 407 L 151 407 L 151 409 L 149 409 L 149 407 L 147 407 L 147 405 L 149 405 L 149 403 z "
xlink:href="#rect11749-5-0-1-8"
style="color:#bebebe;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;opacity:0.8"
id="path13004"
inkscape:href="#rect11749-5-0-1-8"
d="M 145.1875,400 C 144.5248,400 144,400.54899 144,401.21875 l 0,9.5625 c 0,0.66976 0.5248,1.21875 1.1875,1.21875 l 9.625,0 c 0.6627,0 1.1875,-0.54899 1.1875,-1.21875 l 0,-9.5625 C 156,400.54899 155.4752,400 154.8125,400 L 145.1875,400 z m 3.8125,3 2,0 0,2 2,0 0,2 -2,0 0,2 -2,0 0,-2 -2,0 0,-2 2,0 L 149,403 Z"
transform="translate(0,1)" />
<use
x="0"
y="0"
xlink:href="#path13004"
id="use11960"
transform="translate(1,-1)"
width="16"
height="16" />
<use
x="0"
y="0"
xlink:href="#use11960"
id="use11962"
transform="translate(-2,0)"
width="16"
height="16" />
<path
inkscape:connector-curvature="0"
style="color:#bebebe;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible"
d="M 145.1875,400 C 144.5248,400 144,400.54899 144,401.21875 l 0,9.5625 c 0,0.66976 0.5248,1.21875 1.1875,1.21875 l 9.625,0 c 0.6627,0 1.1875,-0.54899 1.1875,-1.21875 l 0,-9.5625 C 156,400.54899 155.4752,400 154.8125,400 L 145.1875,400 z m 3.8125,3 2,0 0,2 2,0 0,2 -2,0 0,2 -2,0 0,-2 -2,0 0,-2 2,0 L 149,403 Z"
id="rect11749-5-0-1-8" />
<rect
style="fill:none;stroke:none"
id="rect3620-5-4"
width="15.981825"
height="16"
x="142"
y="398"
rx="0"
ry="0" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
data/wanda.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -88,11 +88,18 @@ doc-gen-org.gnome.Shell.SearchProvider2.xml: $(top_srcdir)/data/org.gnome.ShellS
--generate-docbook doc-gen \
$(top_srcdir)/data/org.gnome.ShellSearchProvider2.xml
doc-gen-org.gnome.Shell.Screenshot.xml: $(top_srcdir)/data/org.gnome.Shell.Screenshot.xml
gdbus-codegen \
--interface-prefix org.gnome.Shell.Screenshot. \
--generate-docbook doc-gen \
$(top_srcdir)/data/org.gnome.Shell.Screenshot.xml
# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
content_files= \
doc-gen-org.gnome.Shell.SearchProvider.xml \
doc-gen-org.gnome.Shell.SearchProvider2.xml
doc-gen-org.gnome.Shell.SearchProvider2.xml \
doc-gen-org.gnome.Shell.Screenshot.xml
# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
# These files must be listed here *and* in content_files

View File

@ -46,11 +46,11 @@
<xi:include href="doc-gen-org.gnome.Shell.SearchProvider.xml"/>
<xi:include href="doc-gen-org.gnome.Shell.SearchProvider2.xml"/>
<xi:include href="xml/shell-global.xml"/>
<xi:include href="xml/shell-keybinding-modes.xml"/>
<xi:include href="xml/shell-wm.xml"/>
<xi:include href="xml/shell-xfixes-cursor.xml"/>
<xi:include href="xml/shell-util.xml"/>
<xi:include href="xml/shell-mount-operation.xml"/>
<xi:include href="xml/shell-mobile-providers.xml"/>
<xi:include href="xml/shell-network-agent.xml"/>
<xi:include href="xml/shell-polkit-authentication-agent.xml"/>
<xi:include href="xml/shell-tp-client.xml"/>

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 \
@ -28,16 +29,22 @@ nobase_dist_js_DATA = \
misc/extensionUtils.js \
misc/fileUtils.js \
misc/gnomeSession.js \
misc/hash.js \
misc/history.js \
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 \
ui/animation.js \
ui/appDisplay.js \
ui/appFavorites.js \
ui/backgroundMenu.js \
ui/background.js \
ui/boxpointer.js \
ui/calendar.js \
ui/checkBox.js \
@ -49,7 +56,6 @@ nobase_dist_js_DATA = \
ui/extensionSystem.js \
ui/extensionDownloader.js \
ui/environment.js \
ui/flashspot.js \
ui/ibusCandidatePopup.js\
ui/grabHelper.js \
ui/iconGrid.js \
@ -62,17 +68,24 @@ nobase_dist_js_DATA = \
ui/main.js \
ui/messageTray.js \
ui/modalDialog.js \
ui/separator.js \
ui/sessionMode.js \
ui/shellEntry.js \
ui/shellMountOperation.js \
ui/slider.js \
ui/notificationDaemon.js \
ui/osdWindow.js \
ui/overview.js \
ui/overviewControls.js \
ui/panel.js \
ui/panelMenu.js \
ui/pointerWatcher.js \
ui/popupMenu.js \
ui/remoteSearch.js \
ui/remoteMenu.js \
ui/runDialog.js \
ui/screencast.js \
ui/screenshot.js \
ui/screenShield.js \
ui/scripting.js \
ui/search.js \
@ -85,10 +98,11 @@ 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 \
ui/windowAttentionHandler.js \

View File

@ -45,6 +45,7 @@ const Application = new Lang.Class({
this._extensionPrefsModules = {};
this._extensionIters = {};
this._startupUuid = null;
},
_buildModel: function() {
@ -203,6 +204,7 @@ const Application = new Lang.Class({
_scanExtensions: function() {
let finder = new ExtensionUtils.ExtensionFinder();
finder.connect('extension-found', Lang.bind(this, this._extensionFound));
finder.connect('extensions-loaded', Lang.bind(this, this._extensionsLoaded));
finder.scanExtensions();
},
@ -212,6 +214,11 @@ const Application = new Lang.Class({
this._extensionIters[extension.uuid] = iter;
},
_extensionsLoaded: function() {
if (this._startupUuid && this._extensionAvailable(this._startupUuid))
this._selectExtension(this._startupUuid);
this._startupUuid = null;
},
_onActivate: function() {
this._window.present();
@ -232,10 +239,10 @@ const Application = new Lang.Class({
// Strip off "extension:///" prefix which fakes a URI, if it exists
uuid = stripPrefix(uuid, "extension:///");
if (!this._extensionAvailable(uuid))
return 1;
this._selectExtension(uuid);
if (this._extensionAvailable(uuid))
this._selectExtension(uuid);
else
this._startupUuid = uuid;
}
return 0;
}

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);

File diff suppressed because it is too large Load Diff

View File

@ -63,7 +63,7 @@ const Manager = new Lang.Class({
Lang.bind(this, this._reloadRealms))
this._realms = {};
this._aggregateProvider.connect('g-properties-changed',
this._signalId = this._aggregateProvider.connect('g-properties-changed',
Lang.bind(this, function(proxy, properties) {
if ('Realms' in properties.deep_unpack())
this._reloadRealms();
@ -106,7 +106,7 @@ const Manager = new Lang.Class({
realm.connect('g-properties-changed',
Lang.bind(this, function(proxy, properties) {
if ('Configured' in properties.deep_unpack())
this._reloadRealm();
this._reloadRealm(realm);
}));
},
@ -134,6 +134,18 @@ const Manager = new Lang.Class({
this._updateLoginFormat();
return this._loginFormat;
},
release: function() {
Service(Gio.DBus.system,
'org.freedesktop.realmd',
'/org/freedesktop/realmd',
function(service) {
service.ReleaseRemote();
});
this._aggregateProvider.disconnect(this._signalId);
this._realms = { };
this._updateLoginFormat();
}
});
Signals.addSignalMethods(Manager.prototype)

View File

@ -1,23 +1,31 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
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 Realmd = imports.gdm.realmd;
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';
@ -25,6 +33,9 @@ const ALLOWED_FAILURES_KEY = 'allowed-failures';
const LOGO_KEY = 'logo';
const DISABLE_USER_LIST_KEY = 'disable-user-list';
// Give user 16ms to read each character of a PAM message
const USER_READ_TIME = 16
function fadeInActor(actor) {
if (actor.opacity == 255 && actor.visible)
return null;
@ -71,6 +82,34 @@ function fadeOutActor(actor) {
return hold;
}
function cloneAndFadeOutActor(actor) {
// Immediately hide actor so its sibling can have its space
// and position, but leave a non-reactive clone on-screen,
// so from the user's point of view it smoothly fades away
// and reveals its sibling.
actor.hide();
let clone = new Clutter.Clone({ source: actor,
reactive: false });
Main.uiGroup.add_child(clone);
let [x, y] = actor.get_transformed_position();
clone.set_position(x, y);
let hold = new Batch.Hold();
Tweener.addTween(clone,
{ opacity: 0,
time: CLONE_FADE_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: function() {
clone.destroy();
hold.release();
}
});
return hold;
}
const ShellUserVerifier = new Lang.Class({
Name: 'ShellUserVerifier',
@ -81,9 +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._realmManager = new Realmd.Manager();
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;
},
@ -92,6 +156,7 @@ const ShellUserVerifier = new Lang.Class({
this._cancellable = new Gio.Cancellable();
this._hold = hold;
this._userName = userName;
this.reauthenticating = false;
this._checkForFingerprintReader();
@ -109,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() {
@ -123,13 +190,73 @@ const ShellUserVerifier = new Lang.Class({
this._userVerifier.run_dispose();
this._userVerifier = null;
}
this._clearMessageQueue();
},
answerQuery: function(serviceName, answer) {
// Clear any previous message
this.emit('show-message', null, null);
if (!this.hasPendingMessages) {
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
} else {
let signalId = this.connect('no-more-messages',
Lang.bind(this, function() {
this.disconnect(signalId);
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
}));
}
},
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
_getIntervalForMessage: function(message) {
// We probably could be smarter here
return message.length * USER_READ_TIME;
},
finishMessageQueue: function() {
if (!this.hasPendingMessages)
return;
this._messageQueue = [];
this.hasPendingMessages = false;
this.emit('no-more-messages');
},
_queueMessageTimeout: function() {
if (this._messageQueue.length == 0) {
this.finishMessageQueue();
return;
}
if (this._messageQueueTimeoutId != 0)
return;
let message = this._messageQueue.shift();
this.emit('show-message', message.text, message.iconName);
this._messageQueueTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
message.interval,
Lang.bind(this, function() {
this._messageQueueTimeoutId = 0;
this._queueMessageTimeout();
}));
},
_queueMessage: function(message, iconName) {
let interval = this._getIntervalForMessage(message);
this.hasPendingMessages = true;
this._messageQueue.push({ text: message, interval: interval, iconName: iconName });
this._queueMessageTimeout();
},
_clearMessageQueue: function() {
this.finishMessageQueue();
if (this._messageQueueTimeoutId != 0) {
GLib.source_remove(this._messageQueueTimeoutId);
this._messageQueueTimeoutId = 0;
}
this.emit('show-message', null, null);
},
_checkForFingerprintReader: function() {
@ -145,11 +272,33 @@ 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();
this.emit('show-message', _("Authentication error"), 'login-dialog-message-warning');
this._queueMessage(_("Authentication error"), 'login-dialog-message-warning');
this._verificationFailed(false);
},
@ -170,6 +319,7 @@ const ShellUserVerifier = new Lang.Class({
return;
}
this.reauthenticating = true;
this._connectSignals();
this._beginVerification();
this._hold.release();
@ -200,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) {
@ -220,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,
@ -240,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 {
@ -258,68 +432,45 @@ 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.emit('show-message', 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.emit('show-message', problem, 'login-dialog-message-warning');
},
_showRealmLoginHint: function() {
if (this._realmManager.loginFormat) {
let hint = this._realmManager.loginFormat;
hint = hint.replace(/%U/g, 'user');
hint = hint.replace(/%D/g, 'DOMAIN');
hint = hint.replace(/%[^UD]/g, '');
// 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.emit('show-login-hint',
_("(e.g., user or %s)").format(hint));
}
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._showRealmLoginHint();
this._realmLoginHintSignalId = this._realmManager.connect('login-format-changed',
Lang.bind(this, this._showRealmLoginHint));
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');
},
_onReset: function() {
this.clear();
// Clear previous attempts to authenticate
this._failCounter = 0;
this._updateDefaultService();
this.emit('reset');
},
@ -328,6 +479,15 @@ const ShellUserVerifier = new Lang.Class({
this.emit('verification-complete');
},
_cancelAndReset: function() {
this.cancel();
this._onReset();
},
_retry: function() {
this.begin(this._userName, new Batch.Hold());
},
_verificationFailed: function(retry) {
// For Not Listed / enterprise logins, immediately reset
// the dialog
@ -339,15 +499,25 @@ const ShellUserVerifier = new Lang.Class({
this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY);
if (canRetry) {
this.clear();
this.begin(this._userName, new Batch.Hold());
if (!this.hasPendingMessages) {
this._retry();
} else {
let signalId = this.connect('no-more-messages',
Lang.bind(this, function() {
this.disconnect(signalId);
this._retry();
}));
}
} else {
// Allow some time to see the message, then reset everything
Mainloop.timeout_add(3000, Lang.bind(this, function() {
this.cancel();
this._onReset();
}));
if (!this.hasPendingMessages) {
this._cancelAndReset();
} else {
let signalId = this.connect('no-more-messages',
Lang.bind(this, function() {
this.disconnect(signalId);
this._cancelAndReset();
}));
}
}
this.emit('verification-failed');
@ -357,16 +527,11 @@ 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);
}
this.emit('hide-login-hint');
if (this._realmLoginHintSignalId) {
this._realmManager.disconnect(this._realmLoginHintSignalId);
this._realmLoginHintSignalId = 0;
}
},
});
Signals.addSignalMethods(ShellUserVerifier.prototype);

View File

@ -174,10 +174,15 @@ const ExtensionFinder = new Lang.Class({
this.emit('extension-found', extension);
},
_extensionsLoaded: function() {
this.emit('extensions-loaded');
},
scanExtensions: function() {
let perUserDir = Gio.File.new_for_path(global.userdatadir);
FileUtils.collectFromDatadirsAsync('extensions',
{ processFile: Lang.bind(this, this._loadExtension),
loadedCallback: Lang.bind(this, this._extensionsLoaded),
includeUserDir: true,
data: perUserDir });
}

View File

@ -58,6 +58,7 @@ const SessionManagerIface = <interface name="org.gnome.SessionManager">
<arg type="u" direction="in" />
<arg type="b" direction="out" />
</method>
<property name="SessionIsActive" type="b" access="read"/>
<signal name="InhibitorAdded">
<arg type="o" direction="out"/>
</signal>

144
js/misc/hash.js Normal file
View File

@ -0,0 +1,144 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const System = imports.system;
const Params = imports.misc.params;
// This is an implementation of EcmaScript SameValue algorithm,
// which returns true if two values are not observably distinguishable.
// It was taken from http://wiki.ecmascript.org/doku.php?id=harmony:egal
//
// In the future, we may want to use the 'is' operator instead.
function _sameValue(x, y) {
if (x === y) {
// 0 === -0, but they are not identical
return x !== 0 || 1 / x === 1 / y;
}
// NaN !== NaN, but they are identical.
// NaNs are the only non-reflexive value, i.e., if x !== x,
// then x is a NaN.
// isNaN is broken: it converts its argument to number, so
// isNaN("foo") => true
return x !== x && y !== y;
}
const _hashers = {
object: function(o) { return o ? System.addressOf(o) : 'null'; },
function: function(f) { return System.addressOf(f); },
string: function(s) { return s; },
number: function(n) { return String(n); },
undefined: function() { return 'undefined'; },
};
/* Map is meant to be similar in usage to ES6 Map, which is
described at http://wiki.ecmascript.org/doku.php?id=harmony:simple_maps_and_sets,
without requiring more than ES5 + Gjs extensions.
Known differences from other implementations:
Polyfills around the web usually implement HashMaps for
primitive values and reversed WeakMaps for object keys,
but we want real maps with real O(1) semantics in all cases,
and the easiest way is to have different hashers for different
types.
Known differences from the ES6 specification:
- Map is a Lang.Class, not a ES6 class, so inheritance,
prototype, sealing, etc. work differently.
- items(), keys() and values() don't return iterators,
they return actual arrays, so they incur a full copy everytime
they're called, and they don't see changes if you mutate
the table while iterating
(admittedly, the ES6 spec is a bit unclear on this, and
the reference code would just blow up)
*/
const Map = new Lang.Class({
Name: 'Map',
_init: function(iterable) {
this._pool = { };
this._size = 0;
if (iterable) {
for (let i = 0; i < iterable.length; i++) {
let [key, value] = iterable[i];
this.set(key, value);
}
}
},
_hashKey: function(key) {
let type = typeof(key);
return type + ':' + _hashers[type](key);
},
_internalGet: function(key) {
let hash = this._hashKey(key);
let node = this._pool[hash];
if (node && _sameValue(node.key, key))
return [true, node.value];
else
return [false, null];
},
get: function(key) {
return this._internalGet(key)[1];
},
has: function(key) {
return this._internalGet(key)[0];
},
set: function(key, value) {
let hash = this._hashKey(key);
let node = this._pool[hash];
if (node) {
node.key = key;
node.value = value;
} else {
this._pool[hash] = { key: key, value: value };
this._size++;
}
},
delete: function(key) {
let hash = this._hashKey(key);
let node = this._pool[hash];
if (node && _sameValue(node.key, key)) {
delete this._pool[hash];
this._size--;
return [node.key, node.value];
} else {
return [null, null];
}
},
keys: function() {
let pool = this._pool;
return Object.getOwnPropertyNames(pool).map(function(hash) {
return pool[hash].key;
});
},
values: function() {
let pool = this._pool;
return Object.getOwnPropertyNames(pool).map(function(hash) {
return pool[hash].value;
});
},
items: function() {
let pool = this._pool;
return Object.getOwnPropertyNames(pool).map(function(hash) {
return [pool[hash].key, pool[hash].value];
});
},
size: function() {
return this._size;
},
});

View File

@ -5,7 +5,7 @@ const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
const UPowerGlib = imports.gi.UPowerGlib;
const Signals = imports.signals;
const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager'>
<method name='PowerOff'>
@ -26,6 +26,23 @@ const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager
<method name='CanSuspend'>
<arg type='s' direction='out'/>
</method>
<method name='Inhibit'>
<arg type='s' direction='in'/>
<arg type='s' direction='in'/>
<arg type='s' direction='in'/>
<arg type='s' direction='in'/>
<arg type='h' direction='out'/>
</method>
<method name='GetSession'>
<arg type='s' direction='in'/>
<arg type='o' direction='out'/>
</method>
<method name='ListSessions'>
<arg name='sessions' type='a(susso)' direction='out'/>
</method>
<signal name='PrepareForSleep'>
<arg type='b' direction='out'/>
</signal>
</interface>;
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
@ -51,12 +68,6 @@ const ConsoleKitManagerIface = <interface name='org.freedesktop.ConsoleKit.Manag
</interface>;
const ConsoleKitSessionIface = <interface name='org.freedesktop.ConsoleKit.Session'>
<method name='IsActive'>
<arg type='b' direction='out' />
</method>
<signal name='ActiveChanged'>
<arg type='b' direction='out' />
</signal>
<signal name='Lock' />
<signal name='Unlock' />
</interface>;
@ -65,7 +76,36 @@ const ConsoleKitSession = Gio.DBusProxy.makeProxyWrapper(ConsoleKitSessionIface)
const ConsoleKitManager = Gio.DBusProxy.makeProxyWrapper(ConsoleKitManagerIface);
function haveSystemd() {
return GLib.access("/sys/fs/cgroup/systemd", 0) >= 0;
return GLib.access("/run/systemd/seats", 0) >= 0;
}
function versionCompare(required, reference) {
required = required.split('.');
reference = reference.split('.');
for (let i = 0; i < required.length; i++) {
if (required[i] != reference[i])
return required[i] < reference[i];
}
return true;
}
function canLock() {
try {
let params = GLib.Variant.new('(ss)', ['org.gnome.DisplayManager.Manager', 'Version']);
let result = Gio.DBus.system.call_sync('org.gnome.DisplayManager',
'/org/gnome/DisplayManager/Manager',
'org.freedesktop.DBus.Properties',
'Get', params, null,
Gio.DBusCallFlags.NONE,
-1, null);
let version = result.deep_unpack()[0].deep_unpack();
return versionCompare('3.5.91', version);
} catch(e) {
return false;
}
}
let _loginManager = null;
@ -93,24 +133,30 @@ const LoginManagerSystemd = new Lang.Class({
this._proxy = new SystemdLoginManager(Gio.DBus.system,
'org.freedesktop.login1',
'/org/freedesktop/login1');
this._proxy.connectSignal('PrepareForSleep',
Lang.bind(this, this._prepareForSleep));
},
// Having this function is a bit of a hack since the Systemd and ConsoleKit
// session objects have different interfaces - but in both cases there are
// Lock/Unlock signals, and that's all we count upon at the moment.
getCurrentSessionProxy: function() {
if (!this._currentSession) {
this._currentSession = new SystemdLoginSession(Gio.DBus.system,
'org.freedesktop.login1',
'/org/freedesktop/login1/session/' +
GLib.getenv('XDG_SESSION_ID'));
getCurrentSessionProxy: function(callback) {
if (this._currentSession) {
callback (this._currentSession);
return;
}
return this._currentSession;
},
get sessionActive() {
return Shell.session_is_active_for_systemd();
this._proxy.GetSessionRemote(GLib.getenv('XDG_SESSION_ID'), Lang.bind(this,
function(result, error) {
if (error) {
logError(error, 'Could not get a proxy for the current session');
} else {
this._currentSession = new SystemdLoginSession(Gio.DBus.system,
'org.freedesktop.login1',
result[0]);
callback(this._currentSession);
}
}));
},
canPowerOff: function(asyncCallback) {
@ -140,6 +186,15 @@ const LoginManagerSystemd = new Lang.Class({
});
},
listSessions: function(asyncCallback) {
this._proxy.ListSessionsRemote(function(result, error) {
if (error)
asyncCallback([]);
else
asyncCallback(result[0]);
});
},
powerOff: function() {
this._proxy.PowerOffRemote(true);
},
@ -150,8 +205,33 @@ const LoginManagerSystemd = new Lang.Class({
suspend: function() {
this._proxy.SuspendRemote(true);
},
inhibit: function(reason, callback) {
let inVariant = GLib.Variant.new('(ssss)',
['sleep',
'GNOME Shell',
reason,
'delay']);
this._proxy.call_with_unix_fd_list('Inhibit', inVariant, 0, -1, null, null,
Lang.bind(this, function(proxy, result) {
let fd = -1;
try {
let [outVariant, fdList] = proxy.call_with_unix_fd_list_finish(result);
fd = fdList.steal_fds(outVariant.deep_unpack())[0];
callback(new Gio.UnixInputStream({ fd: fd }));
} catch(e) {
logError(e, "Error getting systemd inhibitor");
callback(null);
}
}));
},
_prepareForSleep: function(proxy, sender, [aboutToSuspend]) {
this.emit('prepare-for-sleep', aboutToSuspend);
}
});
Signals.addSignalMethods(LoginManagerSystemd.prototype);
const LoginManagerConsoleKit = new Lang.Class({
Name: 'LoginManagerConsoleKit',
@ -160,34 +240,28 @@ const LoginManagerConsoleKit = new Lang.Class({
this._proxy = new ConsoleKitManager(Gio.DBus.system,
'org.freedesktop.ConsoleKit',
'/org/freedesktop/ConsoleKit/Manager');
this._upClient = new UPowerGlib.Client();
},
// Having this function is a bit of a hack since the Systemd and ConsoleKit
// session objects have different interfaces - but in both cases there are
// Lock/Unlock signals, and that's all we count upon at the moment.
getCurrentSessionProxy: function() {
if (!this._currentSession) {
let [currentSessionId] = this._proxy.GetCurrentSessionSync();
this._currentSession = new ConsoleKitSession(Gio.DBus.system,
'org.freedesktop.ConsoleKit',
currentSessionId);
getCurrentSessionProxy: function(callback) {
if (this._currentSession) {
callback (this._currentSession);
return;
}
return this._currentSession;
},
get sessionActive() {
if (this._sessionActive !== undefined)
return this._sessionActive;
let session = this.getCurrentSessionProxy();
session.connectSignal('ActiveChanged', Lang.bind(this, function(object, senderName, [isActive]) {
this._sessionActive = isActive;
}));
[this._sessionActive] = session.IsActiveSync();
return this._sessionActive;
this._proxy.GetCurrentSessionRemote(Lang.bind(this,
function(result, error) {
if (error) {
logError(error, 'Could not get a proxy for the current session');
} else {
this._currentSession = new ConsoleKitSession(Gio.DBus.system,
'org.freedesktop.ConsoleKit',
result[0]);
callback(this._currentSession);
}
}));
},
canPowerOff: function(asyncCallback) {
@ -209,10 +283,11 @@ const LoginManagerConsoleKit = new Lang.Class({
},
canSuspend: function(asyncCallback) {
Mainloop.idle_add(Lang.bind(this, function() {
asyncCallback(this._upClient.get_can_suspend());
return false;
}));
asyncCallback(false);
},
listSessions: function(asyncCallback) {
asyncCallback([]);
},
powerOff: function() {
@ -224,6 +299,12 @@ const LoginManagerConsoleKit = new Lang.Class({
},
suspend: function() {
this._upClient.suspend_sync(null);
this.emit('prepare-for-sleep', true);
this.emit('prepare-for-sleep', false);
},
inhibit: function(reason, callback) {
callback(null);
}
});
Signals.addSignalMethods(LoginManagerConsoleKit.prototype);

View File

@ -2,9 +2,93 @@
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const NMGtk = imports.gi.NMGtk;
const Signals = imports.signals;
// _getMobileProvidersDatabase:
//
// Gets the database of mobile providers, with references between MCCMNC/SID and
// operator name
//
let _mpd;
function _getMobileProvidersDatabase() {
if (_mpd == null) {
try {
_mpd = new NMGtk.MobileProvidersDatabase();
_mpd.init(null);
} catch (e) {
log(e.message);
_mpd = null;
}
}
return _mpd;
}
// _findProviderForMccMnc:
// @operator_name: operator name
// @operator_code: operator code
//
// Given an operator name string (which may not be a real operator name) and an
// operator code string, tries to find a proper operator name to display.
//
function _findProviderForMccMnc(operator_name, operator_code) {
if (operator_name) {
if (operator_name.length != 0 &&
(operator_name.length > 6 || operator_name.length < 5)) {
// this looks like a valid name, i.e. not an MCCMNC (that some
// devices return when not yet connected
return operator_name;
}
if (isNaN(parseInt(operator_name))) {
// name is definitely not a MCCMNC, so it may be a name
// after all; return that
return operator_name;
}
}
let needle;
if ((!operator_name || operator_name.length == 0) && operator_code)
needle = operator_code;
else if (operator_name && (operator_name.length == 6 || operator_name.length == 5))
needle = operator_name;
else // nothing to search
return null;
let mpd = _getMobileProvidersDatabase();
if (mpd) {
let provider = mpd.lookup_3gpp_mcc_mnc(needle);
if (provider)
return provider.get_name();
}
return null;
}
// _findProviderForSid:
// @sid: System Identifier of the serving CDMA network
//
// Tries to find the operator name corresponding to the given SID
//
function _findProviderForSid(sid) {
if (sid == 0)
return null;
let mpd = _getMobileProvidersDatabase();
if (mpd) {
let provider = mpd.lookup_cdma_sid(sid);
if (provider)
return provider.get_name();
}
return null;
}
//------------------------------------------------------------------------------
// Support for the old ModemManager interface (MM < 0.7)
//------------------------------------------------------------------------------
// The following are not the complete interfaces, just the methods we need
// (or may need in the future)
@ -42,76 +126,6 @@ const ModemCdmaInterface = <interface name="org.freedesktop.ModemManager.Modem.C
const ModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(ModemCdmaInterface);
let _providersTable;
function _getProvidersTable() {
if (_providersTable)
return _providersTable;
return _providersTable = Shell.mobile_providers_parse(null,null);
}
function findProviderForMCCMNC(table, needle) {
let needlemcc = needle.substring(0, 3);
let needlemnc = needle.substring(3, needle.length);
let name2, name3;
for (let iter in table) {
let country = table[iter];
let providers = country.get_providers();
// Search through each country's providers
for (let i = 0; i < providers.length; i++) {
let provider = providers[i];
// Search through MCC/MNC list
let list = provider.get_gsm_mcc_mnc();
for (let j = 0; j < list.length; j++) {
let mccmnc = list[j];
// Match both 2-digit and 3-digit MNC; prefer a
// 3-digit match if found, otherwise a 2-digit one.
if (mccmnc.mcc != needlemcc)
continue; // MCC was wrong
if (!name3 && needle.length == 6 && needlemnc == mccmnc.mnc)
name3 = provider.name;
if (!name2 && needlemnc.substring(0, 2) == mccmnc.mnc.substring(0, 2))
name2 = provider.name;
if (name2 && name3)
break;
}
}
}
return name3 || name2 || null;
}
function findProviderForSid(table, sid) {
if (sid == 0)
return null;
// Search through each country
for (let iter in table) {
let country = table[iter];
let providers = country.get_providers();
// Search through each country's providers
for (let i = 0; i < providers.length; i++) {
let provider = providers[i];
let cdma_sid = provider.get_cdma_sid();
// Search through CDMA SID list
for (let j = 0; j < cdma_sid.length; j++) {
if (cdma_sid[j] == sid)
return provider.name;
}
}
}
return null;
}
const ModemGsm = new Lang.Class({
Name: 'ModemGsm',
@ -127,7 +141,7 @@ const ModemGsm = new Lang.Class({
this.emit('notify::signal-quality');
}));
this._proxy.connectSignal('RegistrationInfo', Lang.bind(this, function(proxy, sender, [status, code, name]) {
this.operator_name = this._findOperatorName(name, code);
this.operator_name = _findProviderForMccMnc(name, code);
this.emit('notify::operator-name');
}));
this._proxy.GetRegistrationInfoRemote(Lang.bind(this, function([result], err) {
@ -137,7 +151,7 @@ const ModemGsm = new Lang.Class({
}
let [status, code, name] = result;
this.operator_name = this._findOperatorName(name, code);
this.operator_name = _findProviderForMccMnc(name, code);
this.emit('notify::operator-name');
}));
this._proxy.GetSignalQualityRemote(Lang.bind(this, function(result, err) {
@ -150,32 +164,6 @@ const ModemGsm = new Lang.Class({
}
this.emit('notify::signal-quality');
}));
},
_findOperatorName: function(name, opCode) {
if (name) {
if (name && name.length != 0 && (name.length > 6 || name.length < 5)) {
// this looks like a valid name, i.e. not an MCCMNC (that some
// devices return when not yet connected
return name;
}
if (isNaN(parseInt(name))) {
// name is definitely not a MCCMNC, so it may be a name
// after all; return that
return name;
}
}
let needle;
if ((name == null || name.length == 0) && opCode)
needle = opCode;
else if (name.length == 6 || name.length == 5)
needle = name;
else // nothing to search
return null;
let table = _getProvidersTable();
return findProviderForMCCMNC(table, needle);
}
});
Signals.addSignalMethods(ModemGsm.prototype);
@ -215,15 +203,99 @@ const ModemCdma = new Lang.Class({
// it will return an error if the device is not connected
this.operator_name = null;
} else {
let [bandClass, band, id] = result;
if (name.length > 0) {
let table = _getProvidersTable();
this.operator_name = findProviderForSid(table, id);
} else
this.operator_name = null;
let [bandClass, band, sid] = result;
this.operator_name = _findProviderForSid(sid)
}
this.emit('notify::operator-name');
}));
}
});
Signals.addSignalMethods(ModemCdma.prototype);
//------------------------------------------------------------------------------
// Support for the new ModemManager1 interface (MM >= 0.7)
//------------------------------------------------------------------------------
const BroadbandModemInterface = <interface name="org.freedesktop.ModemManager1.Modem">
<property name="SignalQuality" type="(ub)" access="read" />
</interface>;
const BroadbandModemProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemInterface);
const BroadbandModem3gppInterface = <interface name="org.freedesktop.ModemManager1.Modem.Modem3gpp">
<property name="OperatorCode" type="s" access="read" />
<property name="OperatorName" type="s" access="read" />
</interface>;
const BroadbandModem3gppProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModem3gppInterface);
const BroadbandModemCdmaInterface = <interface name="org.freedesktop.ModemManager1.Modem.ModemCdma">
<property name="Sid" type="u" access="read" />
</interface>;
const BroadbandModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemCdmaInterface);
const BroadbandModem = new Lang.Class({
Name: 'BroadbandModem',
_init: function(path, capabilities) {
this._proxy = new BroadbandModemProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
this._proxy_3gpp = new BroadbandModem3gppProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
this._proxy_cdma = new BroadbandModemCdmaProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
this._capabilities = capabilities;
this._proxy.connect('g-properties-changed', Lang.bind(this, function(proxy, properties) {
if ('SignalQuality' in properties.deep_unpack())
this._reloadSignalQuality();
}));
this._reloadSignalQuality();
this._proxy_3gpp.connect('g-properties-changed', Lang.bind(this, function(proxy, properties) {
let unpacked = properties.deep_unpack();
if ('OperatorName' in unpacked || 'OperatorCode' in unpacked)
this._reload3gppOperatorName();
}));
this._reload3gppOperatorName();
this._proxy_cdma.connect('g-properties-changed', Lang.bind(this, function(proxy, properties) {
let unpacked = properties.deep_unpack();
if ('Nid' in unpacked || 'Sid' in unpacked)
this._reloadCdmaOperatorName();
}));
this._reloadCdmaOperatorName();
},
_reloadSignalQuality: function() {
let [quality, recent] = this._proxy.SignalQuality;
this.signal_quality = quality;
this.emit('notify::signal-quality');
},
_reloadOperatorName: function() {
let new_name = "";
if (this.operator_name_3gpp && this.operator_name_3gpp.length > 0)
new_name += this.operator_name_3gpp;
if (this.operator_name_cdma && this.operator_name_cdma.length > 0) {
if (new_name != "")
new_name += ", ";
new_name += this.operator_name_cdma;
}
this.operator_name = new_name;
this.emit('notify::operator-name');
},
_reload3gppOperatorName: function() {
let name = this._proxy_3gpp.OperatorName;
let code = this._proxy_3gpp.OperatorCode;
this.operator_name_3gpp = _findProviderForMccMnc(name, code);
this._reloadOperatorName();
},
_reloadCdmaOperatorName: function() {
let sid = this._proxy_cdma.Sid;
this.operator_name_cdma = _findProviderForSid(sid);
this._reloadOperatorName();
}
});
Signals.addSignalMethods(BroadbandModem.prototype);

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

@ -1,8 +1,13 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const St = imports.gi.St;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const SCROLL_TIME = 0.1;
// http://daringfireball.net/2010/07/improved_regex_for_matching_urls
const _balancedParens = '\\((?:[^\\s()<>]+|(?:\\(?:[^\\s()<>]+\\)))*\\)';
@ -13,7 +18,7 @@ const _urlRegexp = new RegExp(
'(^|' + _leadingJunk + ')' +
'(' +
'(?:' +
'[a-z][\\w-]+://' + // scheme://
'(?:http|https|ftp)://' + // scheme://
'|' +
'www\\d{0,3}[.]' + // www.
'|' +
@ -208,3 +213,63 @@ function insertSorted(array, val, cmp) {
return pos;
}
function makeCloseButton() {
let closeButton = new St.Button({ style_class: 'notification-close'});
// This is a bit tricky. St.Bin has its own x-align/y-align properties
// that compete with Clutter's properties. This should be fixed for
// Clutter 2.0. Since St.Bin doesn't define its own setters, the
// setters are a workaround to get Clutter's version.
closeButton.set_x_align(Clutter.ActorAlign.END);
closeButton.set_y_align(Clutter.ActorAlign.START);
// XXX Clutter 2.0 workaround: ClutterBinLayout needs expand
// to respect the alignments.
closeButton.set_x_expand(true);
closeButton.set_y_expand(true);
closeButton.connect('style-changed', function() {
let themeNode = closeButton.get_theme_node();
closeButton.translation_x = themeNode.get_length('-shell-close-overlap-x');
closeButton.translation_y = themeNode.get_length('-shell-close-overlap-y');
});
return closeButton;
}
function ensureActorVisibleInScrollView(scrollView, actor) {
let adjustment = scrollView.vscroll.adjustment;
let [value, lower, upper, stepIncrement, pageIncrement, pageSize] = adjustment.get_values();
let offset = 0;
let vfade = scrollView.get_effect("fade");
if (vfade)
offset = vfade.vfade_offset;
let box = actor.get_allocation_box();
let y1 = box.y1, y2 = box.y2;
let parent = actor.get_parent();
while (parent != scrollView) {
if (!parent)
throw new Error("actor not in scroll view");
let box = parent.get_allocation_box();
y1 += box.y1;
y2 += box.y1;
parent = parent.get_parent();
}
if (y1 < value + offset)
value = Math.max(0, y1 - offset);
else if (y2 > value + pageSize - offset)
value = Math.min(upper, y2 + offset - pageSize);
else
return;
Tweener.addTween(adjustment,
{ value: value,
time: SCROLL_TIME,
transition: 'easeOutQuad' });
}

View File

@ -98,43 +98,13 @@ const AppSwitcherPopup = new Lang.Class({
}
},
_getAppLists: function() {
let tracker = Shell.WindowTracker.get_default();
let appSys = Shell.AppSystem.get_default();
let allApps = appSys.get_running ();
let screen = global.screen;
let display = screen.get_display();
let windows = display.get_tab_list(Meta.TabList.NORMAL_ALL, screen,
screen.get_active_workspace());
// windows is only the windows on the current workspace. For
// each one, if it corresponds to an app we know, move that
// app from allApps to apps.
let apps = [];
for (let i = 0; i < windows.length && allApps.length != 0; i++) {
let app = tracker.get_window_app(windows[i]);
let index = allApps.indexOf(app);
if (index != -1) {
apps.push(app);
allApps.splice(index, 1);
}
}
// Now @apps is a list of apps on the current workspace, in
// standard Alt+Tab order (MRU except for minimized windows),
// and allApps is a list of apps that only appear on other
// workspaces, sorted by user_time, which is good enough.
return [apps, allApps];
},
_createSwitcher: function() {
let [localApps, otherApps] = this._getAppLists();
let apps = Shell.AppSystem.get_default().get_running ();
if (localApps.length == 0 && otherApps.length == 0)
if (apps.length == 0)
return false;
this._switcherList = new AppSwitcher(localApps, otherApps, this);
this._switcherList = new AppSwitcher(apps, this);
this._items = this._switcherList.icons;
return true;
@ -262,15 +232,13 @@ const AppSwitcherPopup = new Lang.Class({
},
_finish : function(timestamp) {
this.parent();
let appIcon = this._items[this._selectedIndex];
let window;
if (this._currentWindow >= 0)
window = appIcon.cachedWindows[this._currentWindow];
if (this._currentWindow < 0)
appIcon.app.activate_window(appIcon.cachedWindows[0], timestamp);
else
window = null;
appIcon.app.activate_window(window, timestamp);
Main.activateWindow(appIcon.cachedWindows[this._currentWindow], timestamp);
this.parent();
},
_onDestroy : function() {
@ -429,9 +397,9 @@ const WindowSwitcherPopup = new Lang.Class({
},
_finish: function() {
this.parent();
Main.activateWindow(this._items[this._selectedIndex].window);
this.parent();
}
});
@ -461,34 +429,31 @@ const AppSwitcher = new Lang.Class({
Name: 'AppSwitcher',
Extends: SwitcherPopup.SwitcherList,
_init : function(localApps, otherApps, altTabPopup) {
_init : function(apps, altTabPopup) {
this.parent(true);
// Construct the AppIcons, add to the popup
let activeWorkspace = global.screen.get_active_workspace();
let workspaceIcons = [];
let otherIcons = [];
for (let i = 0; i < localApps.length; i++) {
let appIcon = new AppIcon(localApps[i]);
// Cache the window list now; we don't handle dynamic changes here,
// and we don't want to be continually retrieving it
appIcon.cachedWindows = appIcon.app.get_windows();
workspaceIcons.push(appIcon);
}
for (let i = 0; i < otherApps.length; i++) {
let appIcon = new AppIcon(otherApps[i]);
appIcon.cachedWindows = appIcon.app.get_windows();
otherIcons.push(appIcon);
}
this.icons = [];
this._arrows = [];
for (let i = 0; i < workspaceIcons.length; i++)
this._addIcon(workspaceIcons[i]);
if (workspaceIcons.length > 0 && otherIcons.length > 0)
this.addSeparator();
for (let i = 0; i < otherIcons.length; i++)
this._addIcon(otherIcons[i]);
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, workspace);
// Construct the AppIcons, add to the popup
for (let i = 0; i < apps.length; i++) {
let appIcon = new AppIcon(apps[i]);
// Cache the window list now; we don't handle dynamic changes here,
// and we don't want to be continually retrieving it
appIcon.cachedWindows = allWindows.filter(function(w) {
return windowTracker.get_window_app (w) == appIcon.app;
});
if (workspace == null || appIcon.cachedWindows.length > 0) {
this._addIcon(appIcon);
}
}
this._curApp = -1;
this._iconSize = 0;
@ -514,8 +479,6 @@ const AppSwitcher = new Lang.Class({
let [iconMinHeight, iconNaturalHeight] = this.icons[j].label.get_preferred_height(-1);
let iconSpacing = iconNaturalHeight + iconPadding + iconBorder;
let totalSpacing = this._list.spacing * (this._items.length - 1);
if (this._separator)
totalSpacing += this._separator.width + this._list.spacing;
// We just assume the whole screen here due to weirdness happing with the passed width
let primary = Main.layoutManager.primaryMonitor;
@ -638,24 +601,12 @@ const ThumbnailList = new Lang.Class({
_init : function(windows) {
this.parent(false);
let activeWorkspace = global.screen.get_active_workspace();
// We fake the value of 'separatorAdded' when the app has no window
// on the current workspace, to avoid displaying a useless separator in
// that case.
let separatorAdded = windows.length == 0 || windows[0].get_workspace() != activeWorkspace;
this._labels = new Array();
this._thumbnailBins = new Array();
this._clones = new Array();
this._windows = windows;
for (let i = 0; i < windows.length; i++) {
if (!separatorAdded && windows[i].get_workspace() != activeWorkspace) {
this.addSeparator();
separatorAdded = true;
}
let box = new St.BoxLayout({ style_class: 'thumbnail-box',
vertical: true });

84
js/ui/animation.js Normal file
View File

@ -0,0 +1,84 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const St = imports.gi.St;
const Signals = imports.signals;
const Atk = imports.gi.Atk;
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
const Animation = new Lang.Class({
Name: 'Animation',
_init: function(filename, width, height, speed) {
this.actor = new St.Bin();
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._speed = speed;
this._isLoaded = false;
this._isPlaying = false;
this._timeoutId = 0;
this._frame = 0;
this._animations = St.TextureCache.get_default().load_sliced_image (filename, width, height,
Lang.bind(this, this._animationsLoaded));
this.actor.set_child(this._animations);
},
play: function() {
if (this._isLoaded && this._timeoutId == 0) {
if (this._frame == 0)
this._showFrame(0);
this._timeoutId = Mainloop.timeout_add(this._speed, Lang.bind(this, this._update));
}
this._isPlaying = true;
},
stop: function() {
if (this._timeoutId > 0) {
Mainloop.source_remove(this._timeoutId);
this._timeoutId = 0;
}
this._isPlaying = false;
},
_showFrame: function(frame) {
let oldFrameActor = this._animations.get_child_at_index(this._frame);
if (oldFrameActor)
oldFrameActor.hide();
this._frame = (frame % this._animations.get_n_children());
let newFrameActor = this._animations.get_child_at_index(this._frame);
if (newFrameActor)
newFrameActor.show();
},
_update: function() {
this._showFrame(this._frame + 1);
return true;
},
_animationsLoaded: function() {
this._isLoaded = true;
if (this._isPlaying)
this.play();
},
_onDestroy: function() {
this.stop();
}
});
const AnimatedIcon = new Lang.Class({
Name: 'AnimatedIcon',
Extends: Animation,
_init: function(filename, size) {
this.parent(filename, size, size, ANIMATED_ICON_UPDATE_TIMEOUT);
}
});

File diff suppressed because it is too large Load Diff

826
js/ui/background.js Normal file
View File

@ -0,0 +1,826 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GDesktopEnums = imports.gi.GDesktopEnums;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const GnomeDesktop = imports.gi.GnomeDesktop;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Signals = imports.signals;
const Main = imports.ui.main;
const Params = imports.misc.params;
const Tweener = imports.ui.tweener;
const BACKGROUND_SCHEMA = 'org.gnome.desktop.background';
const PRIMARY_COLOR_KEY = 'primary-color';
const SECONDARY_COLOR_KEY = 'secondary-color';
const COLOR_SHADING_TYPE_KEY = 'color-shading-type';
const BACKGROUND_STYLE_KEY = 'picture-options';
const PICTURE_OPACITY_KEY = 'picture-opacity';
const PICTURE_URI_KEY = 'picture-uri';
const FADE_ANIMATION_TIME = 1.0;
// These parameters affect how often we redraw.
// The first is how different (percent crossfaded) the slide show
// has to look before redrawing and the second is the minimum
// frequency (in seconds) we're willing to wake up
const ANIMATION_OPACITY_STEP_INCREMENT = 4.0;
const ANIMATION_MIN_WAKEUP_INTERVAL = 1.0;
let _backgroundCache = null;
const BackgroundCache = new Lang.Class({
Name: 'BackgroundCache',
_init: function() {
this._patterns = [];
this._images = [];
this._pendingFileLoads = [];
this._fileMonitors = {};
},
getPatternContent: function(params) {
params = Params.parse(params, { monitorIndex: 0,
color: null,
secondColor: null,
shadingType: null,
effects: Meta.BackgroundEffects.NONE });
let content = null;
let candidateContent = null;
for (let i = 0; i < this._patterns.length; i++) {
if (!this._patterns[i])
continue;
if (this._patterns[i].get_shading() != params.shadingType)
continue;
if (!params.color.equal(this._patterns[i].get_color()))
continue;
if (params.shadingType != GDesktopEnums.BackgroundShading.SOLID &&
!params.secondColor.equal(this._patterns[i].get_second_color()))
continue;
candidateContent = this._patterns[i];
if (params.effects != this._patterns[i].effects)
continue;
break;
}
if (candidateContent) {
content = candidateContent.copy(params.monitorIndex, params.effects);
} else {
content = new Meta.Background({ meta_screen: global.screen,
monitor: params.monitorIndex,
effects: params.effects });
if (params.shadingType == GDesktopEnums.BackgroundShading.SOLID) {
content.load_color(params.color);
} else {
content.load_gradient(params.shadingType, params.color, params.secondColor);
}
}
this._patterns.push(content);
return content;
},
_monitorFile: function(filename) {
if (this._fileMonitors[filename])
return;
let file = Gio.File.new_for_path(filename);
let monitor = file.monitor(Gio.FileMonitorFlags.NONE, null);
let signalId = monitor.connect('changed',
Lang.bind(this, function() {
for (let i = 0; i < this._images.length; i++) {
if (this._images[i].get_filename() == filename)
this._images.splice(i, 1);
}
monitor.disconnect(signalId);
this.emit('file-changed', filename);
}));
this._fileMonitors[filename] = monitor;
},
_removeContent: function(contentList, content) {
let index = contentList.indexOf(content);
if (index >= 0)
contentList.splice(index, 1);
},
removePatternContent: function(content) {
this._removeContent(this._patterns, content);
},
removeImageContent: function(content) {
let filename = content.get_filename();
if (filename && this._fileMonitors[filename])
delete this._fileMonitors[filename];
this._removeContent(this._images, content);
},
_loadImageContent: function(params) {
params = Params.parse(params, { monitorIndex: 0,
style: null,
filename: null,
effects: Meta.BackgroundEffects.NONE,
cancellable: null,
onFinished: null });
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);
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,
effects: params.effects });
content.load_file_async(params.filename,
params.style,
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);
this._monitorFile(params.filename);
this._images.push(content);
} catch(e) {
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;
if (pendingLoad.cancellable.is_cancelled())
continue;
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);
}
}));
},
_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,
filename: null,
effects: Meta.BackgroundEffects.NONE,
cancellable: null,
onFinished: null });
let content = null;
let candidateContent = null;
for (let i = 0; i < this._images.length; i++) {
if (!this._images[i])
continue;
if (this._images[i].get_style() != params.style)
continue;
if (this._images[i].get_filename() != params.filename)
continue;
if (params.style == GDesktopEnums.BackgroundStyle.SPANNED &&
this._images[i].monitor_index != this._monitorIndex)
continue;
candidateContent = this._images[i];
if (params.effects != this._images[i].effects)
continue;
break;
}
if (candidateContent) {
content = candidateContent.copy(params.monitorIndex, params.effects);
if (params.cancellable && params.cancellable.is_cancelled())
content = null;
else
this._images.push(content);
if (params.onFinished)
params.onFinished(content);
} else {
this._loadImageContent({ filename: params.filename,
style: params.style,
effects: params.effects,
monitorIndex: params.monitorIndex,
cancellable: params.cancellable,
onFinished: params.onFinished });
}
},
getAnimation: function(params) {
params = Params.parse(params, { filename: null,
onLoaded: null });
if (this._animationFilename == params.filename) {
if (params.onLoaded) {
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
params.onLoaded(this._animation);
}));
}
}
let animation = new Animation({ filename: params.filename });
animation.load(Lang.bind(this, function() {
this._monitorFile(params.filename);
this._animationFilename = params.filename;
this._animation = animation;
if (params.onLoaded) {
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
params.onLoaded(this._animation);
}));
}
}));
}
});
Signals.addSignalMethods(BackgroundCache.prototype);
function getBackgroundCache() {
if (!_backgroundCache)
_backgroundCache = new BackgroundCache();
return _backgroundCache;
}
const Background = new Lang.Class({
Name: 'Background',
_init: function(params) {
params = Params.parse(params, { monitorIndex: 0,
layoutManager: Main.layoutManager,
effects: Meta.BackgroundEffects.NONE });
this.actor = new Meta.BackgroundGroup();
this.actor._delegate = this;
this._destroySignalId = this.actor.connect('destroy',
Lang.bind(this, this._destroy));
this._settings = new Gio.Settings({ schema: BACKGROUND_SCHEMA });
this._monitorIndex = params.monitorIndex;
this._layoutManager = params.layoutManager;
this._effects = params.effects;
this._fileWatches = {};
this._pattern = null;
// contains a single image for static backgrounds and
// two images (from and to) for slide shows
this._images = {};
this._brightness = 1.0;
this._vignetteSharpness = 0.2;
this._saturation = 1.0;
this._cancellable = new Gio.Cancellable();
this.isLoaded = false;
this._settings.connect('changed', Lang.bind(this, function() {
this.emit('changed');
}));
this._load();
},
_destroy: function() {
this._cancellable.cancel();
if (this._updateAnimationTimeoutId) {
GLib.source_remove (this._updateAnimationTimeoutId);
this._updateAnimationTimeoutId = 0;
}
let i;
let keys = Object.keys(this._fileWatches);
for (i = 0; i < keys.length; i++) {
this._cache.disconnect(this._fileWatches[keys[i]]);
}
this._fileWatches = null;
if (this._pattern) {
if (this._pattern.content)
this._cache.removePatternContent(this._pattern.content);
this._pattern.destroy();
this._pattern = null;
}
keys = Object.keys(this._images);
for (i = 0; i < keys.length; i++) {
let actor = this._images[keys[i]];
if (actor.content)
this._cache.removeImageContent(actor.content);
actor.destroy();
this._images[keys[i]] = null;
}
this.actor.disconnect(this._destroySignalId);
this._destroySignalId = 0;
},
_setLoaded: function() {
if (this.isLoaded)
return;
this.isLoaded = true;
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
this.emit('loaded');
return false;
}));
},
_loadPattern: function() {
let colorString, res, color, secondColor;
colorString = this._settings.get_string(PRIMARY_COLOR_KEY);
[res, color] = Clutter.Color.from_string(colorString);
colorString = this._settings.get_string(SECONDARY_COLOR_KEY);
[res, secondColor] = Clutter.Color.from_string(colorString);
let shadingType = this._settings.get_enum(COLOR_SHADING_TYPE_KEY);
let content = this._cache.getPatternContent({ monitorIndex: this._monitorIndex,
effects: this._effects,
color: color,
secondColor: secondColor,
shadingType: shadingType });
this._pattern = new Meta.BackgroundActor();
this.actor.add_child(this._pattern);
this._pattern.content = content;
},
_watchCacheFile: function(filename) {
if (this._fileWatches[filename])
return;
let signalId = this._cache.connect('file-changed',
Lang.bind(this, function(cache, changedFile) {
if (changedFile == filename) {
this.emit('changed');
}
}));
this._fileWatches[filename] = signalId;
},
_addImage: function(content, index, filename) {
content.saturation = this._saturation;
content.brightness = this._brightness;
content.vignette_sharpness = this._vignetteSharpness;
let actor = new Meta.BackgroundActor();
actor.content = content;
// The background pattern is the first actor in
// the group, and all images should be above that.
this.actor.insert_child_at_index(actor, index + 1);
this._images[index] = actor;
this._watchCacheFile(filename);
},
_updateImage: function(content, index, filename) {
content.saturation = this._saturation;
content.brightness = this._brightness;
content.vignette_sharpness = this._vignetteSharpness;
this._cache.removeImageContent(this._images[index].content);
this._images[index].content = content;
this._watchCacheFile(filename);
},
_updateAnimationProgress: function() {
if (this._images[1])
this._images[1].opacity = this._animation.transitionProgress * 255;
this._queueUpdateAnimation();
},
_updateAnimation: function() {
this._updateAnimationTimeoutId = 0;
this._animation.update(this._layoutManager.monitors[this._monitorIndex]);
let files = this._animation.keyFrameFiles;
if (files.length == 0) {
this._setLoaded();
this._queueUpdateAnimation();
return;
}
let numPendingImages = files.length;
for (let i = 0; i < files.length; i++) {
if (this._images[i] && this._images[i].content &&
this._images[i].content.get_filename() == files[i]) {
numPendingImages--;
if (numPendingImages == 0)
this._updateAnimationProgress();
continue;
}
this._cache.getImageContent({ monitorIndex: this._monitorIndex,
effects: this._effects,
style: this._style,
filename: files[i],
cancellable: this._cancellable,
onFinished: Lang.bind(this, function(content, i) {
numPendingImages--;
if (!content) {
this._setLoaded();
if (numPendingImages == 0)
this._updateAnimationProgress();
return;
}
if (!this._images[i]) {
this._addImage(content, i, files[i]);
} else {
this._updateImage(content, i, files[i]);
}
if (numPendingImages == 0) {
this._setLoaded();
this._updateAnimationProgress();
}
}, i)
});
}
},
_queueUpdateAnimation: function() {
if (this._updateAnimationTimeoutId != 0)
return;
if (!this._cancellable || this._cancellable.is_cancelled())
return;
if (!this._animation.transitionDuration)
return;
let nSteps = 255 / ANIMATION_OPACITY_STEP_INCREMENT;
let timePerStep = (this._animation.transitionDuration * 1000) / nSteps;
let interval = Math.max(ANIMATION_MIN_WAKEUP_INTERVAL * 1000,
timePerStep);
if (interval > GLib.MAXUINT32)
return;
this._updateAnimationTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
interval,
Lang.bind(this, function() {
this._updateAnimationTimeoutId = 0;
this._updateAnimation();
return false;
}));
},
_loadAnimation: function(filename) {
this._cache.getAnimation({ filename: filename,
onLoaded: Lang.bind(this, function(animation) {
this._animation = animation;
if (!this._animation || this._cancellable.is_cancelled()) {
this._setLoaded();
return;
}
this._updateAnimation();
this._watchCacheFile(filename);
})
});
},
_loadFile: function(filename) {
this._cache.getImageContent({ monitorIndex: this._monitorIndex,
effects: this._effects,
style: this._style,
filename: filename,
cancellable: this._cancellable,
onFinished: Lang.bind(this, function(content) {
if (!content) {
if (!this._cancellable.is_cancelled())
this._loadAnimation(filename);
return;
}
this._addImage(content, 0, filename);
this._setLoaded();
})
});
},
_load: function () {
this._cache = getBackgroundCache();
this._loadPattern(this._cache);
this._style = this._settings.get_enum(BACKGROUND_STYLE_KEY);
if (this._style == GDesktopEnums.BackgroundStyle.NONE) {
this._setLoaded();
return;
}
let uri = this._settings.get_string(PICTURE_URI_KEY);
let filename;
if (GLib.uri_parse_scheme(uri) != null)
filename = Gio.File.new_for_uri(uri).get_path();
else
filename = uri;
if (!filename) {
this._setLoaded();
return;
}
this._loadFile(filename);
},
get saturation() {
return this._saturation;
},
set saturation(saturation) {
this._saturation = saturation;
if (this._pattern && this._pattern.content)
this._pattern.content.saturation = saturation;
let keys = Object.keys(this._images);
for (let i = 0; i < keys.length; i++) {
let image = this._images[keys[i]];
if (image && image.content)
image.content.saturation = saturation;
}
},
get brightness() {
return this._brightness;
},
set brightness(factor) {
this._brightness = factor;
if (this._pattern && this._pattern.content)
this._pattern.content.brightness = factor;
let keys = Object.keys(this._images);
for (let i = 0; i < keys.length; i++) {
let image = this._images[keys[i]];
if (image && image.content)
image.content.brightness = factor;
}
},
get vignetteSharpness() {
return this._vignetteSharpness;
},
set vignetteSharpness(sharpness) {
this._vignetteSharpness = sharpness;
if (this._pattern && this._pattern.content)
this._pattern.content.vignette_sharpness = sharpness;
let keys = Object.keys(this._images);
for (let i = 0; i < keys.length; i++) {
let image = this._images[keys[i]];
if (image && image.content)
image.content.vignette_sharpness = sharpness;
}
}
});
Signals.addSignalMethods(Background.prototype);
const SystemBackground = new Lang.Class({
Name: 'SystemBackground',
_init: function() {
this._cache = getBackgroundCache();
this.actor = new Meta.BackgroundActor();
this._cache.getImageContent({ style: GDesktopEnums.BackgroundStyle.WALLPAPER,
filename: global.datadir + '/theme/noise-texture.png',
effects: Meta.BackgroundEffects.NONE,
onFinished: Lang.bind(this, function(content) {
this.actor.content = content;
this.emit('loaded');
})
});
}
});
Signals.addSignalMethods(SystemBackground.prototype);
const Animation = new Lang.Class({
Name: 'Animation',
_init: function(params) {
params = Params.parse(params, { filename: null });
this.filename = params.filename;
this.keyFrameFiles = [];
this.transitionProgress = 0.0;
this.transitionDuration = 0.0;
this.loaded = false;
},
load: function(callback) {
let file = Gio.File.new_for_path(this.filename);
this._show = new GnomeDesktop.BGSlideShow({ filename: this.filename });
this._show.load_async(null,
Lang.bind(this,
function(object, result) {
this.loaded = true;
if (callback)
callback();
}));
},
update: function(monitor) {
this.keyFrameFiles = [];
if (!this._show)
return;
if (this._show.get_num_slides() < 1)
return;
let [progress, duration, isFixed, file1, file2] = this._show.get_current_slide(monitor.width, monitor.height);
this.transitionDuration = duration;
this.transitionProgress = progress;
if (file1)
this.keyFrameFiles.push(file1);
if (file2)
this.keyFrameFiles.push(file2);
},
});
Signals.addSignalMethods(Animation.prototype);
const BackgroundManager = new Lang.Class({
Name: 'BackgroundManager',
_init: function(params) {
params = Params.parse(params, { container: null,
layoutManager: Main.layoutManager,
monitorIndex: null,
effects: Meta.BackgroundEffects.NONE,
controlPosition: true });
this._container = params.container;
this._layoutManager = params.layoutManager;
this._effects = params.effects;
this._monitorIndex = params.monitorIndex;
this._controlPosition = params.controlPosition;
this.background = this._createBackground();
this._newBackground = null;
},
destroy: function() {
if (this._newBackground) {
this._newBackground.actor.destroy();
this._newBackground = null;
}
if (this.background) {
this.background.actor.destroy();
this.background = null;
}
},
_updateBackground: function(background, monitorIndex) {
let newBackground = this._createBackground(monitorIndex);
newBackground.vignetteSharpness = background.vignetteSharpness;
newBackground.brightness = background.brightness;
newBackground.saturation = background.saturation;
newBackground.visible = background.visible;
newBackground.loadedSignalId = newBackground.connect('loaded',
Lang.bind(this, function() {
newBackground.disconnect(newBackground.loadedSignalId);
newBackground.loadedSignalId = 0;
Tweener.addTween(background.actor,
{ opacity: 0,
time: FADE_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
if (this._newBackground == newBackground) {
this.background = newBackground;
this._newBackground = null;
} else {
newBackground.actor.destroy();
}
background.actor.destroy();
this.emit('changed');
})
});
}));
this._newBackground = newBackground;
},
_createBackground: function() {
let background = new Background({ monitorIndex: this._monitorIndex,
layoutManager: this._layoutManager,
effects: this._effects });
this._container.add_child(background.actor);
let monitor = this._layoutManager.monitors[this._monitorIndex];
background.actor.set_size(monitor.width, monitor.height);
if (this._controlPosition) {
background.actor.set_position(monitor.x, monitor.y);
background.actor.lower_bottom();
}
background.changeSignalId = background.connect('changed', Lang.bind(this, function() {
background.disconnect(background.changeSignalId);
background.changeSignalId = 0;
this._updateBackground(background, this._monitorIndex);
}));
background.actor.connect('destroy', Lang.bind(this, function() {
if (background.changeSignalId)
background.disconnect(background.changeSignalId);
if (background.loadedSignalId)
background.disconnect(background.loadedSignalId);
}));
return background;
},
});
Signals.addSignalMethods(BackgroundManager.prototype);

68
js/ui/backgroundMenu.js Normal file
View File

@ -0,0 +1,68 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const BoxPointer = imports.ui.boxpointer;
const Main = imports.ui.main;
const PopupMenu = imports.ui.popupMenu;
const BackgroundMenu = new Lang.Class({
Name: 'BackgroundMenu',
Extends: PopupMenu.PopupMenu,
_init: function(source) {
this.parent(source, 0, St.Side.TOP);
this.addSettingsAction(_("Settings"), 'gnome-control-center.desktop');
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.addSettingsAction(_("Change Background…"), 'gnome-background-panel.desktop');
this.actor.add_style_class_name('background-menu');
Main.uiGroup.add_actor(this.actor);
this.actor.hide();
}
});
function addBackgroundMenu(actor) {
let cursor = new St.Bin({ opacity: 0 });
Main.uiGroup.add_actor(cursor);
actor.reactive = true;
actor._backgroundMenu = new BackgroundMenu(cursor);
actor._backgroundManager = new PopupMenu.PopupMenuManager({ actor: actor });
actor._backgroundManager.addMenu(actor._backgroundMenu);
function openMenu() {
let [x, y] = global.get_pointer();
cursor.set_position(x, y);
actor._backgroundMenu.open(BoxPointer.PopupAnimation.NONE);
}
let clickAction = new Clutter.ClickAction();
clickAction.connect('long-press', function(action, actor, state) {
if (state == Clutter.LongPressState.QUERY)
return action.get_button() == 1 && !actor._backgroundMenu.isOpen;
if (state == Clutter.LongPressState.ACTIVATE) {
openMenu();
actor._backgroundManager.ignoreRelease();
}
return true;
});
clickAction.connect('clicked', function(action) {
if (action.get_button() == 3)
openMenu();
});
actor.add_action(clickAction);
actor.connect('destroy', function() {
actor._backgroundMenu.destroy();
actor._backgroundMenu = null;
actor._backgroundManager = null;
cursor.destroy();
});
}

View File

@ -38,6 +38,7 @@ const BoxPointer = new Lang.Class({
this._arrowSide = arrowSide;
this._userArrowSide = arrowSide;
this._arrowOrigin = 0;
this._arrowActor = null;
this.actor = new St.Bin({ x_fill: true,
y_fill: true });
this._container = new Shell.GenericContainer();
@ -220,31 +221,27 @@ const BoxPointer = new Lang.Class({
this.bin.allocate(childBox, flags);
if (this._sourceActor && this._sourceActor.mapped) {
this._reposition(this._sourceActor, this._arrowAlignment);
if (this._shouldFlip()) {
switch (this._arrowSide) {
case St.Side.TOP:
this._arrowSide = St.Side.BOTTOM;
break;
case St.Side.BOTTOM:
this._arrowSide = St.Side.TOP;
break;
case St.Side.LEFT:
this._arrowSide = St.Side.RIGHT;
break;
case St.Side.RIGHT:
this._arrowSide = St.Side.LEFT;
break;
}
this._reposition(this._sourceActor, this._arrowAlignment);
}
this._reposition();
this._updateFlip();
}
},
_drawBorder: function(area) {
let themeNode = this.actor.get_theme_node();
if (this._arrowActor) {
let [sourceX, sourceY] = this._arrowActor.get_transformed_position();
let [sourceWidth, sourceHeight] = this._arrowActor.get_transformed_size();
let [absX, absY] = this.actor.get_transformed_position();
if (this._arrowSide == St.Side.TOP ||
this._arrowSide == St.Side.BOTTOM) {
this._arrowOrigin = sourceX - absX + sourceWidth / 2;
} else {
this._arrowOrigin = sourceY - absY + sourceHeight / 2;
}
}
let borderWidth = themeNode.get_length('-arrow-border-width');
let base = themeNode.get_length('-arrow-base');
let rise = themeNode.get_length('-arrow-rise');
@ -405,11 +402,11 @@ const BoxPointer = new Lang.Class({
cr.setLineWidth(borderWidth);
cr.stroke();
}
cr.$dispose();
},
setPosition: function(sourceActor, alignment) {
this._arrowSide = this._userArrowSide;
// We need to show it now to force an allocation,
// so that we can query the correct size.
this.actor.show();
@ -417,7 +414,8 @@ const BoxPointer = new Lang.Class({
this._sourceActor = sourceActor;
this._arrowAlignment = alignment;
this._reposition(sourceActor, alignment);
this._reposition();
this._updateFlip();
},
setSourceAlignment: function(alignment) {
@ -429,7 +427,10 @@ const BoxPointer = new Lang.Class({
this.setPosition(this._sourceActor, this._arrowAlignment);
},
_reposition: function(sourceActor, alignment) {
_reposition: function() {
let sourceActor = this._sourceActor;
let alignment = this._arrowAlignment;
// Position correctly relative to the sourceActor
let sourceNode = sourceActor.get_theme_node();
let sourceContentBox = sourceNode.get_content_box(sourceActor.get_allocation_box());
@ -550,10 +551,20 @@ const BoxPointer = new Lang.Class({
}
},
// @actor: an actor relative to which the arrow is positioned.
// Differently from setPosition, this will not move the boxpointer itself,
// on the arrow
setArrowActor: function(actor) {
if (this._arrowActor != actor) {
this._arrowActor = actor;
this._border.queue_repaint();
}
},
_shiftActor : function() {
// Since the position of the BoxPointer depends on the allocated size
// of the BoxPointer and the position of the source actor, trying
// to position the BoxPoiner via the x/y properties will result in
// to position the BoxPointer via the x/y properties will result in
// allocation loops and warnings. Instead we do the positioning via
// the anchor point, which is independent of allocation, and leave
// x == y == 0.
@ -561,37 +572,47 @@ const BoxPointer = new Lang.Class({
-(this._yPosition + this._yOffset));
},
_shouldFlip: function() {
_calculateArrowSide: function(arrowSide) {
let sourceAllocation = Shell.util_get_transformed_allocation(this._sourceActor);
let boxAllocation = Shell.util_get_transformed_allocation(this.actor);
let boxWidth = boxAllocation.x2 - boxAllocation.x1;
let boxHeight = boxAllocation.y2 - boxAllocation.y1;
let [minWidth, minHeight, boxWidth, boxHeight] = this._container.get_preferred_size();
let monitor = Main.layoutManager.findMonitorForActor(this.actor);
switch (this._arrowSide) {
switch (arrowSide) {
case St.Side.TOP:
if (boxAllocation.y2 > monitor.y + monitor.height &&
if (sourceAllocation.y2 + boxHeight > monitor.y + monitor.height &&
boxHeight < sourceAllocation.y1 - monitor.y)
return true;
return St.Side.BOTTOM;
break;
case St.Side.BOTTOM:
if (boxAllocation.y1 < monitor.y &&
if (sourceAllocation.y1 - boxHeight < monitor.y &&
boxHeight < monitor.y + monitor.height - sourceAllocation.y2)
return true;
return St.Side.TOP;
break;
case St.Side.LEFT:
if (boxAllocation.x2 > monitor.x + monitor.width &&
if (sourceAllocation.x2 + boxWidth > monitor.x + monitor.width &&
boxWidth < sourceAllocation.x1 - monitor.x)
return true;
return St.Side.RIGHT;
break;
case St.Side.RIGHT:
if (boxAllocation.x1 < monitor.x &&
if (sourceAllocation.x1 - boxWidth < monitor.x &&
boxWidth < monitor.x + monitor.width - sourceAllocation.x2)
return true;
return St.Side.LEFT;
break;
}
return false;
return arrowSide;
},
_updateFlip: function() {
let arrowSide = this._calculateArrowSide(this._userArrowSide);
if (this._arrowSide != arrowSide) {
this._arrowSide = arrowSide;
this._reposition();
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
this._container.queue_relayout();
return false;
}));
}
},
set xOffset(offset) {

View File

@ -2,6 +2,7 @@
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const St = imports.gi.St;
const Signals = imports.signals;
@ -11,7 +12,6 @@ const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
const MSECS_IN_DAY = 24 * 60 * 60 * 1000;
const WEEKDATE_HEADER_WIDTH_DIGITS = 3;
const SHOW_WEEKDATE_KEY = 'show-weekdate';
// in org.gnome.desktop.interface
@ -63,15 +63,18 @@ function _formatEventTime(event, clockFormat) {
} else {
switch (clockFormat) {
case '24h':
/* Translators: Shown in calendar event list, if 24h format */
ret = event.date.toLocaleFormat(C_("event list time", "%H:%M"));
/* Translators: Shown in calendar event list, if 24h format,
\u2236 is a ratio character, similar to : */
ret = event.date.toLocaleFormat(C_("event list time", "%H\u2236%M"));
break;
default:
/* explicit fall-through */
case '12h':
/* Transators: Shown in calendar event list, if 12h format */
ret = event.date.toLocaleFormat(C_("event list time", "%l:%M %p"));
/* Translators: Shown in calendar event list, if 12h format,
\u2236 is a ratio character, similar to : and \u2009 is
a thin space */
ret = event.date.toLocaleFormat(C_("event list time", "%l\u2236%M\u2009%p"));
break;
}
}
@ -95,15 +98,6 @@ function _getCalendarWeekForDate(date) {
return weekNumber;
}
function _getDigitWidth(actor){
let context = actor.get_pango_context();
let themeNode = actor.get_theme_node();
let font = themeNode.get_font();
let metrics = context.get_metrics(font, context.get_language());
let width = metrics.get_approximate_digit_width();
return width;
}
function _getCalendarDayAbbreviation(dayNumber) {
let abbreviations = [
/* Translators: Calendar grid abbreviation for Sunday.
@ -174,6 +168,12 @@ const EmptyEventSource = new Lang.Class({
Name: 'EmptyEventSource',
_init: function() {
this.isLoading = false;
this.isDummy = true;
this.hasCalendars = false;
},
destroy: function() {
},
requestRange: function(begin, end) {
@ -197,21 +197,18 @@ const CalendarServerIface = <interface name="org.gnome.Shell.CalendarServer">
<arg type="b" direction="in" />
<arg type="a(sssbxxa{sv})" direction="out" />
</method>
<property name="HasCalendars" type="b" access="read" />
<signal name="Changed" />
</interface>;
const CalendarServerInfo = Gio.DBusInterfaceInfo.new_for_xml(CalendarServerIface);
function CalendarServer() {
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.session,
g_interface_name: CalendarServerInfo.name,
g_interface_info: CalendarServerInfo,
g_name: 'org.gnome.Shell.CalendarServer',
g_object_path: '/org/gnome/Shell/CalendarServer',
g_flags: Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES });
self.init(null);
return self;
return new Gio.DBusProxy({ g_connection: Gio.DBus.session,
g_interface_name: CalendarServerInfo.name,
g_interface_info: CalendarServerInfo,
g_name: 'org.gnome.Shell.CalendarServer',
g_object_path: '/org/gnome/Shell/CalendarServer' });
}
function _datesEqual(a, b) {
@ -238,18 +235,49 @@ const DBusEventSource = new Lang.Class({
_init: function() {
this._resetCache();
this.isLoading = false;
this.isDummy = false;
this._initialized = false;
this._dbusProxy = new CalendarServer();
this._dbusProxy.connectSignal('Changed', Lang.bind(this, this._onChanged));
this._dbusProxy.init_async(GLib.PRIORITY_DEFAULT, null, Lang.bind(this, function(object, result) {
try {
this._dbusProxy.init_finish(result);
} catch(e) {
log('Error loading calendars: ' + e.message);
return;
}
this._dbusProxy.connect('notify::g-name-owner', Lang.bind(this, function() {
if (this._dbusProxy.g_name_owner)
this._onNameAppeared();
else
this._onNameVanished();
this._dbusProxy.connectSignal('Changed', Lang.bind(this, this._onChanged));
this._dbusProxy.connect('notify::g-name-owner', Lang.bind(this, function() {
if (this._dbusProxy.g_name_owner)
this._onNameAppeared();
else
this._onNameVanished();
}));
this._dbusProxy.connect('g-properties-changed', Lang.bind(this, function() {
this.emit('notify::has-calendars');
}));
this._initialized = true;
this.emit('notify::has-calendars');
this._onNameAppeared();
}));
},
destroy: function() {
this._dbusProxy.run_dispose();
},
get hasCalendars() {
if (this._initialized)
return this._dbusProxy.HasCalendars;
else
return false;
},
_resetCache: function() {
this._events = [];
this._lastRequestBegin = null;
@ -289,10 +317,15 @@ const DBusEventSource = new Lang.Class({
}
this._events = newEvents;
this.isLoading = false;
this.emit('changed');
},
_loadEvents: function(forceReload) {
// Ignore while loading
if (!this._initialized)
return;
if (this._curRequestBegin && this._curRequestEnd){
let callFlags = Gio.DBusCallFlags.NO_AUTO_START;
if (forceReload)
@ -307,6 +340,7 @@ const DBusEventSource = new Lang.Class({
requestRange: function(begin, end, forceReload) {
if (forceReload || !(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
this.isLoading = true;
this._lastRequestBegin = begin;
this._lastRequestEnd = end;
this._curRequestBegin = begin;
@ -345,8 +379,6 @@ const Calendar = new Lang.Class({
_init: function() {
this._weekStart = Shell.util_get_week_start();
this._weekdate = NaN;
this._digitWidth = NaN;
this._settings = new Gio.Settings({ schema: 'org.gnome.shell.calendar' });
this._settings.connect('changed::' + SHOW_WEEKDATE_KEY, Lang.bind(this, this._onSettingsChange));
@ -383,19 +415,11 @@ const Calendar = new Lang.Class({
// @eventSource: is an object implementing the EventSource API, e.g. the
// requestRange(), getEvents(), hasEvents() methods and the ::changed signal.
setEventSource: function(eventSource) {
if (this._eventSource) {
this._eventSource.disconnect(this._eventSourceChangedId);
this._eventSource = null;
}
this._eventSource = eventSource;
if (this._eventSource) {
this._eventSourceChangedId = this._eventSource.connect('changed', Lang.bind(this, function() {
this._update(false);
}));
this._update(true);
}
this._eventSource.connect('changed', Lang.bind(this, function() {
this._update(false);
}));
this._update(true);
},
// Sets the calendar to show a specific date
@ -419,18 +443,18 @@ const Calendar = new Lang.Class({
this.actor.add(this._topBox,
{ row: 0, col: 0, col_span: offsetCols + 7 });
this.actor.connect('style-changed', Lang.bind(this, this._onStyleChange));
let back = new St.Button({ style_class: 'calendar-change-month-back' });
this._topBox.add(back);
back.connect('clicked', Lang.bind(this, this._onPrevMonthButtonClicked));
this._backButton = new St.Button({ style_class: 'calendar-change-month-back',
can_focus: true });
this._topBox.add(this._backButton);
this._backButton.connect('clicked', Lang.bind(this, this._onPrevMonthButtonClicked));
this._monthLabel = new St.Label({style_class: 'calendar-month-label'});
this._topBox.add(this._monthLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
let forward = new St.Button({ style_class: 'calendar-change-month-forward' });
this._topBox.add(forward);
forward.connect('clicked', Lang.bind(this, this._onNextMonthButtonClicked));
this._forwardButton = new St.Button({ style_class: 'calendar-change-month-forward',
can_focus: true });
this._topBox.add(this._forwardButton);
this._forwardButton.connect('clicked', Lang.bind(this, this._onNextMonthButtonClicked));
// Add weekday labels...
//
@ -457,18 +481,6 @@ const Calendar = new Lang.Class({
this._firstDayIndex = this.actor.get_n_children();
},
_onStyleChange: function(actor, event) {
// width of a digit in pango units
this._digitWidth = _getDigitWidth(this.actor) / Pango.SCALE;
this._setWeekdateHeaderWidth();
},
_setWeekdateHeaderWidth: function() {
if (this.digitWidth != NaN && this._useWeekdate && this._weekdateHeader) {
this._weekdateHeader.set_width (this._digitWidth * WEEKDATE_HEADER_WIDTH_DIGITS);
}
},
_onScroll : function(actor, event) {
switch (event.get_scroll_direction()) {
case Clutter.ScrollDirection.UP:
@ -501,10 +513,12 @@ const Calendar = new Lang.Class({
}
}
this.setDate(newDate, false);
},
this._backButton.grab_key_focus();
_onNextMonthButtonClicked: function() {
this.setDate(newDate, false);
},
_onNextMonthButtonClicked: function() {
let newDate = new Date(this._selectedDate);
let oldMonth = newDate.getMonth();
if (oldMonth == 11) {
@ -523,7 +537,9 @@ const Calendar = new Lang.Class({
}
}
this.setDate(newDate, false);
this._forwardButton.grab_key_focus();
this.setDate(newDate, false);
},
_onSettingsChange: function() {
@ -546,30 +562,60 @@ const Calendar = new Lang.Class({
children[i].destroy();
// Start at the beginning of the week before the start of the month
//
// We want to show always 6 weeks (to keep the calendar menu at the same
// height if there are no events), so we pad it according to the following
// policy:
//
// 1 - If a month has 6 weeks, we place no padding (example: Dec 2012)
// 2 - If a month has 5 weeks and it starts on week start, we pad one week
// before it (example: Apr 2012)
// 3 - If a month has 5 weeks and it starts on any other day, we pad one week
// after it (example: Nov 2012)
// 4 - If a month has 4 weeks, we pad one week before and one after it
// (example: Feb 2010)
//
// Actually computing the number of weeks is complex, but we know that the
// problematic categories (2 and 4) always start on week start, and that
// all months at the end have 6 weeks.
let beginDate = new Date(this._selectedDate);
beginDate.setDate(1);
beginDate.setSeconds(0);
beginDate.setHours(12);
let year = beginDate.getYear();
let daysToWeekStart = (7 + beginDate.getDay() - this._weekStart) % 7;
beginDate.setTime(beginDate.getTime() - daysToWeekStart * MSECS_IN_DAY);
let startsOnWeekStart = daysToWeekStart == 0;
let weekPadding = startsOnWeekStart ? 7 : 0;
beginDate.setTime(beginDate.getTime() - (weekPadding + daysToWeekStart) * MSECS_IN_DAY);
let iter = new Date(beginDate);
let row = 2;
while (true) {
let button = new St.Button({ label: iter.getDate().toString() });
// nRows here means 6 weeks + one header + one navbar
let nRows = 8;
while (row < 8) {
let button = new St.Button({ label: iter.getDate().toString(),
can_focus: true });
let rtl = button.get_text_direction() == Clutter.TextDirection.RTL;
if (!this._eventSource)
if (this._eventSource.isDummy)
button.reactive = false;
let iterStr = iter.toUTCString();
button.connect('clicked', Lang.bind(this, function() {
this._shouldDateGrabFocus = true;
let newlySelectedDate = new Date(iterStr);
this.setDate(newlySelectedDate, false);
this._shouldDateGrabFocus = false;
}));
let hasEvents = this._eventSource && this._eventSource.hasEvents(iter);
let hasEvents = this._eventSource.hasEvents(iter);
let styleClass = 'calendar-day-base calendar-day';
if (_isWorkDay(iter))
styleClass += ' calendar-work-day'
else
@ -589,9 +635,6 @@ const Calendar = new Lang.Class({
else if (iter.getMonth() != this._selectedDate.getMonth())
styleClass += ' calendar-other-month-day';
if (_sameDay(this._selectedDate, iter))
button.add_style_pseudo_class('active');
if (hasEvents)
styleClass += ' calendar-day-with-events'
@ -601,6 +644,13 @@ const Calendar = new Lang.Class({
this.actor.add(button,
{ row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7 });
if (_sameDay(this._selectedDate, iter)) {
button.add_style_pseudo_class('active');
if (this._shouldDateGrabFocus)
button.grab_key_focus();
}
if (this._useWeekdate && iter.getDay() == 4) {
let label = new St.Label({ text: _getCalendarWeekForDate(iter).toString(),
style_class: 'calendar-day-base calendar-week-number'});
@ -609,17 +659,13 @@ const Calendar = new Lang.Class({
}
iter.setTime(iter.getTime() + MSECS_IN_DAY);
if (iter.getDay() == this._weekStart) {
// We stop on the first "first day of the week" after the month we are displaying
if (iter.getMonth() > this._selectedDate.getMonth() || iter.getYear() > this._selectedDate.getYear())
break;
if (iter.getDay() == this._weekStart)
row++;
}
}
// Signal to the event source that we are interested in events
// only from this date range
if (this._eventSource)
this._eventSource.requestRange(beginDate, iter, forceReload);
this._eventSource.requestRange(beginDate, iter, forceReload);
}
});
@ -629,7 +675,7 @@ const EventsList = new Lang.Class({
Name: 'EventsList',
_init: function() {
this.actor = new St.BoxLayout({ vertical: true, style_class: 'events-header-vbox'});
this.actor = new St.Table({ style_class: 'events-table' });
this._date = new Date();
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
this._desktopSettings.connect('changed', Lang.bind(this, this._update));
@ -637,70 +683,76 @@ const EventsList = new Lang.Class({
},
setEventSource: function(eventSource) {
if (this._eventSource) {
this._eventSource.disconnect(this._eventSourceChangedId);
this._eventSource = null;
}
this._eventSource = eventSource;
if (this._eventSource) {
this._eventSourceChangedId = this._eventSource.connect('changed', Lang.bind(this, this._update));
}
this._eventSource.connect('changed', Lang.bind(this, this._update));
},
_addEvent: function(dayNameBox, timeBox, eventTitleBox, includeDayName, day, time, desc) {
if (includeDayName) {
dayNameBox.add(new St.Label( { style_class: 'events-day-dayname',
text: day } ),
{ x_fill: true } );
}
timeBox.add(new St.Label( { style_class: 'events-day-time',
text: time} ),
{ x_fill: true } );
eventTitleBox.add(new St.Label( { style_class: 'events-day-task',
text: desc} ));
_addEvent: function(event, index, includeDayName) {
let dayString;
if (includeDayName)
dayString = _getEventDayAbbreviation(event.date.getDay());
else
dayString = '';
let dayLabel = new St.Label({ style_class: 'events-day-dayname',
text: dayString });
dayLabel.clutter_text.line_wrap = false;
dayLabel.clutter_text.ellipsize = false;
this.actor.add(dayLabel, { row: index, col: 0,
x_expand: false, x_align: St.Align.END,
y_fill: false, y_align: St.Align.START });
let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);
let timeString = _formatEventTime(event, clockFormat);
let timeLabel = new St.Label({ style_class: 'events-day-time',
text: timeString });
timeLabel.clutter_text.line_wrap = false;
timeLabel.clutter_text.ellipsize = false;
this.actor.add(timeLabel, { row: index, col: 1,
x_expand: false, x_align: St.Align.MIDDLE,
y_fill: false, y_align: St.Align.START });
let titleLabel = new St.Label({ style_class: 'events-day-task',
text: event.summary });
titleLabel.clutter_text.line_wrap = true;
titleLabel.clutter_text.ellipsize = false;
this.actor.add(titleLabel, { row: index, col: 2,
x_expand: true, x_align: St.Align.START,
y_fill: false, y_align: St.Align.START });
},
_addPeriod: function(header, begin, end, includeDayName, showNothingScheduled) {
if (!this._eventSource)
return;
_addPeriod: function(header, index, begin, end, includeDayName, showNothingScheduled) {
let events = this._eventSource.getEvents(begin, end);
let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);;
if (events.length == 0 && !showNothingScheduled)
return;
return index;
let vbox = new St.BoxLayout( {vertical: true} );
this.actor.add(vbox);
vbox.add(new St.Label({ style_class: 'events-day-header', text: header }));
let box = new St.BoxLayout({style_class: 'events-header-hbox'});
let dayNameBox = new St.BoxLayout({ vertical: true, style_class: 'events-day-name-box' });
let timeBox = new St.BoxLayout({ vertical: true, style_class: 'events-time-box' });
let eventTitleBox = new St.BoxLayout({ vertical: true, style_class: 'events-event-box' });
box.add(dayNameBox, {x_fill: false});
box.add(timeBox, {x_fill: false});
box.add(eventTitleBox, {expand: true});
vbox.add(box);
this.actor.add(new St.Label({ style_class: 'events-day-header', text: header }),
{ row: index, col: 0, col_span: 3,
// In theory, x_expand should be true here, but x_expand
// is a property of the column for StTable, ie all day cells
// get it too
x_expand: false, x_align: St.Align.START,
y_fill: false, y_align: St.Align.START });
index++;
for (let n = 0; n < events.length; n++) {
let event = events[n];
let dayString = _getEventDayAbbreviation(event.date.getDay());
let timeString = _formatEventTime(event, clockFormat);
let summaryString = event.summary;
this._addEvent(dayNameBox, timeBox, eventTitleBox, includeDayName, dayString, timeString, summaryString);
this._addEvent(events[n], index, includeDayName);
index++;
}
if (events.length == 0 && showNothingScheduled) {
let now = new Date();
/* Translators: Text to show if there are no events */
let nothingEvent = new CalendarEvent(now, now, _("Nothing Scheduled"), true);
let timeString = _formatEventTime(nothingEvent, clockFormat);
this._addEvent(dayNameBox, timeBox, eventTitleBox, false, "", timeString, nothingEvent.summary);
this._addEvent(nothingEvent, index, false);
index++;
}
return index;
},
_showOtherDay: function(day) {
@ -717,20 +769,21 @@ const EventsList = new Lang.Class({
else
/* Translators: Shown on calendar heading when selected day occurs on different year */
dayString = day.toLocaleFormat(C_("calendar heading", "%A, %B %d, %Y"));
this._addPeriod(dayString, dayBegin, dayEnd, false, true);
this._addPeriod(dayString, 0, dayBegin, dayEnd, false, true);
},
_showToday: function() {
this.actor.destroy_all_children();
let index = 0;
let now = new Date();
let dayBegin = _getBeginningOfDay(now);
let dayEnd = _getEndOfDay(now);
this._addPeriod(_("Today"), dayBegin, dayEnd, false, true);
index = this._addPeriod(_("Today"), index, dayBegin, dayEnd, false, true);
let tomorrowBegin = new Date(dayBegin.getTime() + 86400 * 1000);
let tomorrowEnd = new Date(dayEnd.getTime() + 86400 * 1000);
this._addPeriod(_("Tomorrow"), tomorrowBegin, tomorrowEnd, false, true);
index = this._addPeriod(_("Tomorrow"), index, tomorrowBegin, tomorrowEnd, false, true);
let dayInWeek = (dayEnd.getDay() - this._weekStart + 7) % 7;
@ -741,7 +794,7 @@ const EventsList = new Lang.Class({
*/
let thisWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
let thisWeekEnd = new Date(dayEnd.getTime() + (6 - dayInWeek) * 86400 * 1000);
this._addPeriod(_("This week"), thisWeekBegin, thisWeekEnd, true, false);
index = this._addPeriod(_("This week"), index, thisWeekBegin, thisWeekEnd, true, false);
} else {
/* otherwise it's one of the two last days of the week ... show
* "Next week" and include events up until and including *next*
@ -749,7 +802,7 @@ const EventsList = new Lang.Class({
*/
let nextWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
let nextWeekEnd = new Date(dayEnd.getTime() + (13 - dayInWeek) * 86400 * 1000);
this._addPeriod(_("Next week"), nextWeekBegin, nextWeekEnd, true, false);
index = this._addPeriod(_("Next week"), index, nextWeekBegin, nextWeekEnd, true, false);
}
},
@ -762,6 +815,9 @@ const EventsList = new Lang.Class({
},
_update: function() {
if (this._eventSource.isLoading)
return;
let today = new Date();
if (_sameDay (this._date, today)) {
this._showToday();

View File

@ -8,7 +8,6 @@ const Params = imports.misc.params;
const Shell = imports.gi.Shell;
const GnomeSession = imports.misc.gnomeSession;
const LoginManager = imports.misc.loginManager;
const Main = imports.ui.main;
const ShellMountOperation = imports.ui.shellMountOperation;
@ -33,7 +32,6 @@ const AutomountManager = new Lang.Class({
Lang.bind(this, this._InhibitorsChanged));
this._inhibited = false;
this._loginManager = LoginManager.getLoginManager();
this._volumeMonitor = Gio.VolumeMonitor.get();
},
@ -85,25 +83,29 @@ const AutomountManager = new Lang.Class({
_onDriveConnected: function() {
// if we're not in the current ConsoleKit session,
// or screensaver is active, don't play sounds
if (!this._loginManager.sessionActive)
if (!this._session.SessionIsActive)
return;
global.play_theme_sound(0, 'device-added-media');
global.play_theme_sound(0, 'device-added-media',
_("External drive connected"),
null);
},
_onDriveDisconnected: function() {
// if we're not in the current ConsoleKit session,
// or screensaver is active, don't play sounds
if (!this._loginManager.sessionActive)
if (!this._session.SessionIsActive)
return;
global.play_theme_sound(0, 'device-removed-media');
global.play_theme_sound(0, 'device-removed-media',
_("External drive disconnected"),
null);
},
_onDriveEjectButton: function(monitor, drive) {
// TODO: this code path is not tested, as the GVfs volume monitor
// doesn't emit this signal just yet.
if (!this._loginManager.sessionActive)
if (!this._session.SessionIsActive)
return;
// we force stop/eject in this case, so we don't have to pass a
@ -143,7 +145,7 @@ const AutomountManager = new Lang.Class({
if (params.checkSession) {
// if we're not in the current ConsoleKit session,
// don't attempt automount
if (!this._loginManager.sessionActive)
if (!this._session.SessionIsActive)
return;
}

View File

@ -4,7 +4,7 @@ const Lang = imports.lang;
const Gio = imports.gi.Gio;
const St = imports.gi.St;
const LoginManager = imports.misc.loginManager;
const GnomeSession = imports.misc.gnomeSession;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const ShellMountOperation = imports.ui.shellMountOperation;
@ -31,7 +31,7 @@ function shouldAutorunMount(mount, forTransient) {
if (!volume || (!volume.allowAutorun && forTransient))
return false;
if (!root.is_native() || isMountRootHidden(root))
if (root.is_native() && isMountRootHidden(root))
return false;
return true;
@ -162,8 +162,7 @@ const AutorunManager = new Lang.Class({
Name: 'AutorunManager',
_init: function() {
this._loginManager = LoginManager.getLoginManager();
this._session = new GnomeSession.SessionManager();
this._volumeMonitor = Gio.VolumeMonitor.get();
this._transDispatcher = new AutorunTransientDispatcher(this);
@ -215,7 +214,7 @@ const AutorunManager = new Lang.Class({
_onMountAdded: function(monitor, mount) {
// don't do anything if our session is not the currently
// active one
if (!this._loginManager.sessionActive)
if (!this._session.SessionIsActive)
return;
this._processMount(mount, true);
@ -293,7 +292,7 @@ const AutorunResidentSource = new Lang.Class({
_init: function(manager) {
this.parent(_("Removable Devices"), 'media-removable');
this.showInLockScreen = false;
this.resident = true;
this._mounts = [];
@ -301,6 +300,10 @@ const AutorunResidentSource = new Lang.Class({
this._notification = new AutorunResidentNotification(this._manager, this);
},
_createPolicy: function() {
return new MessageTray.NotificationPolicy({ showInLockScreen: false });
},
buildRightClickMenu: function() {
return null;
},

View File

@ -25,7 +25,7 @@ const KeyringDialog = new Lang.Class({
this.prompt = new Shell.KeyringPrompt();
this.prompt.connect('show-password', Lang.bind(this, this._onShowPassword));
this.prompt.connect('show-confirm', Lang.bind(this, this._onShowConfirm));
this.prompt.connect('hide-prompt', Lang.bind(this, this._onHidePrompt));
this.prompt.connect('prompt-close', Lang.bind(this, this._onHidePrompt));
let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
vertical: false });
@ -60,18 +60,20 @@ const KeyringDialog = new Lang.Class({
this._controlTable = null;
let buttons = [{ label: '',
action: Lang.bind(this, this._onCancelButton),
key: Clutter.Escape
},
{ label: '',
action: Lang.bind(this, this._onContinueButton),
default: true
}]
this.setButtons(buttons);
this._cancelButton = buttons[0].button;
this._continueButton = buttons[1].button;
this._cancelButton = this.addButton({ label: '',
action: Lang.bind(this, this._onCancelButton),
key: Clutter.Escape },
{ expand: true, x_fill: false, x_align: St.Align.START });
this.placeSpinner({ expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
this._continueButton = this.addButton({ label: '',
action: Lang.bind(this, this._onContinueButton),
default: true },
{ expand: false, x_fill: false, x_align: St.Align.END });
this.prompt.bind_property('cancel-label', this._cancelButton, 'label', GObject.BindingFlags.SYNC_CREATE);
this.prompt.bind_property('continue-label', this._continueButton, 'label', GObject.BindingFlags.SYNC_CREATE);
@ -146,6 +148,22 @@ const KeyringDialog = new Lang.Class({
this._messageBox.add(table, { x_fill: true, y_fill: true });
},
_updateSensitivity: function(sensitive) {
if (this._passwordEntry) {
this._passwordEntry.reactive = sensitive;
this._passwordEntry.clutter_text.editable = sensitive;
}
if (this._confirmEntry) {
this._confirmEntry.reactive = sensitive;
this._confirmEntry.clutter_text.editable = sensitive;
}
this._continueButton.can_focus = sensitive;
this._continueButton.reactive = sensitive;
this.setWorking(!sensitive);
},
_ensureOpen: function() {
// NOTE: ModalDialog.open() is safe to call if the dialog is
// already open - it just returns true without side-effects
@ -167,12 +185,14 @@ const KeyringDialog = new Lang.Class({
_onShowPassword: function(prompt) {
this._buildControlTable();
this._ensureOpen();
this._updateSensitivity(true);
this._passwordEntry.grab_key_focus();
},
_onShowConfirm: function(prompt) {
this._buildControlTable();
this._ensureOpen();
this._updateSensitivity(true);
this._continueButton.grab_key_focus();
},
@ -192,6 +212,7 @@ const KeyringDialog = new Lang.Class({
},
_onContinueButton: function() {
this._updateSensitivity(false);
this.prompt.complete();
},
@ -215,7 +236,7 @@ const KeyringPrompter = new Lang.Class({
enable: function() {
this._prompter.register(Gio.DBus.session);
this._dbusId = Gio.DBus.session.own_name('org.gnome.keyring.SystemPrompter',
Gio.BusNameOwnerFlags.REPLACE, null, null);
Gio.BusNameOwnerFlags.ALLOW_REPLACEMENT, null, null);
},
disable: function() {

View File

@ -587,18 +587,19 @@ const NetworkAgent = new Lang.Class({
Name: 'NetworkAgent',
_init: function() {
this._native = new Shell.NetworkAgent({ auto_register: false,
identifier: 'org.gnome.Shell.NetworkAgent' });
this._native = new Shell.NetworkAgent({ identifier: 'org.gnome.Shell.NetworkAgent' });
this._dialogs = { };
this._vpnRequests = { };
this._native.connect('new-request', Lang.bind(this, this._newRequest));
this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest));
this._enabled = false;
},
enable: function() {
this._native.auto_register = true;
this._enabled = true;
},
disable: function() {
@ -612,11 +613,15 @@ const NetworkAgent = new Lang.Class({
this._vpnRequests[requestId].cancel(true);
this._vpnRequests = { };
this._native.auto_register = false;
this._native.unregister();
this._enabled = false;
},
_newRequest: function(agent, requestId, connection, settingName, hints, flags) {
if (!this._enabled) {
agent.respond(requestId, Shell.NetworkAgentResponse.USER_CANCELED);
return;
}
if (settingName == 'vpn') {
this._vpnRequest(requestId, connection, hints, flags);
return;

View File

@ -16,7 +16,7 @@ const PolkitAgent = imports.gi.PolkitAgent;
const Components = imports.ui.components;
const ModalDialog = imports.ui.modalDialog;
const ShellEntry = imports.ui.shellEntry;
const UserMenu = imports.ui.userMenu;
const UserWidget = imports.ui.userWidget;
const DIALOG_ICON_SIZE = 48;
@ -31,7 +31,6 @@ const AuthenticationDialog = new Lang.Class({
this.message = message;
this.userNames = userNames;
this._wasDismissed = false;
this._completed = false;
let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
vertical: false });
@ -101,9 +100,9 @@ const AuthenticationDialog = new Lang.Class({
let userBox = new St.BoxLayout({ style_class: 'polkit-dialog-user-layout',
vertical: false });
messageBox.add(userBox);
this._userAvatar = new UserMenu.UserAvatarWidget(this._user,
{ iconSize: DIALOG_ICON_SIZE,
styleClass: 'polkit-dialog-user-icon' });
this._userAvatar = new UserWidget.Avatar(this._user,
{ iconSize: DIALOG_ICON_SIZE,
styleClass: 'polkit-dialog-user-icon' });
this._userAvatar.actor.hide();
userBox.add(this._userAvatar.actor,
{ x_fill: true,
@ -159,29 +158,34 @@ const AuthenticationDialog = new Lang.Class({
messageBox.add(this._nullMessageLabel);
this._nullMessageLabel.show();
this.setButtons([{ label: _("Cancel"),
action: Lang.bind(this, this.cancel),
key: Clutter.Escape
},
{ label: _("Authenticate"),
action: Lang.bind(this, this._onAuthenticateButtonPressed),
default: true
}]);
this._cancelButton = this.addButton({ label: _("Cancel"),
action: Lang.bind(this, this.cancel),
key: Clutter.Escape },
{ expand: true, x_fill: false, x_align: St.Align.START });
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: _("Authenticate"),
action: Lang.bind(this, this._onAuthenticateButtonPressed),
default: true },
{ expand: false, x_fill: false, x_align: St.Align.END });
this._doneEmitted = false;
this._identityToAuth = Polkit.UnixUser.new_for_name(userName);
this._cookie = cookie;
},
performAuthentication: function() {
this.destroySession();
this._session = new PolkitAgent.Session({ identity: this._identityToAuth,
cookie: this._cookie });
this._session.connect('completed', Lang.bind(this, this._onSessionCompleted));
this._session.connect('request', Lang.bind(this, this._onSessionRequest));
this._session.connect('show-error', Lang.bind(this, this._onSessionShowError));
this._session.connect('show-info', Lang.bind(this, this._onSessionShowInfo));
},
startAuthentication: function() {
this._session.initiate();
},
@ -203,19 +207,29 @@ const AuthenticationDialog = new Lang.Class({
log('polkitAuthenticationAgent: Failed to show modal dialog.' +
' Dismissing authentication request for action-id ' + this.actionId +
' cookie ' + this._cookie);
this._emitDone(false, true);
this._emitDone(true);
}
},
_emitDone: function(keepVisible, dismissed) {
_emitDone: function(dismissed) {
if (!this._doneEmitted) {
this._doneEmitted = true;
this.emit('done', keepVisible, dismissed);
this.emit('done', dismissed);
}
},
_updateSensitivity: function(sensitive) {
this._passwordEntry.reactive = sensitive;
this._passwordEntry.clutter_text.editable = sensitive;
this._okButton.can_focus = sensitive;
this._okButton.reactive = sensitive;
this.setWorking(!sensitive);
},
_onEntryActivate: function() {
let response = this._passwordEntry.get_text();
this._updateSensitivity(false);
this._session.response(response);
// When the user responds, dismiss already shown info and
// error texts (if any)
@ -229,12 +243,16 @@ const AuthenticationDialog = new Lang.Class({
},
_onSessionCompleted: function(session, gainedAuthorization) {
if (this._completed)
if (this._completed || this._doneEmitted)
return;
this._completed = true;
if (!gainedAuthorization) {
/* Yay, all done */
if (gainedAuthorization) {
this._emitDone(false);
} else {
/* Unless we are showing an existing error message from the PAM
* module (the PAM module could be reporting the authentication
* error providing authentication-method specific information),
@ -250,8 +268,10 @@ const AuthenticationDialog = new Lang.Class({
this._infoMessageLabel.hide();
this._nullMessageLabel.hide();
}
/* Try and authenticate again */
this.performAuthentication();
}
this._emitDone(!gainedAuthorization, false);
},
_onSessionRequest: function(session, request, echo_on) {
@ -269,6 +289,7 @@ const AuthenticationDialog = new Lang.Class({
this._passwordBox.show();
this._passwordEntry.set_text('');
this._passwordEntry.grab_key_focus();
this._updateSensitivity(true);
this._ensureOpen();
},
@ -294,6 +315,7 @@ const AuthenticationDialog = new Lang.Class({
if (this._session) {
if (!this._completed)
this._session.cancel();
this._completed = false;
this._session = null;
}
},
@ -308,7 +330,7 @@ const AuthenticationDialog = new Lang.Class({
cancel: function() {
this._wasDismissed = true;
this.close(global.get_current_time());
this._emitDone(false, true);
this._emitDone(true);
},
});
Signals.addSignalMethods(AuthenticationDialog.prototype);
@ -318,7 +340,6 @@ const AuthenticationAgent = new Lang.Class({
_init: function() {
this._currentDialog = null;
this._isCompleting = false;
this._handle = null;
this._native = new Shell.PolkitAuthenticationAgent();
this._native.connect('initiate', Lang.bind(this, this._onInitiate));
@ -326,11 +347,19 @@ const AuthenticationAgent = new Lang.Class({
},
enable: function() {
this._native.register();
try {
this._native.register();
} catch(e) {
log('Failed to register AuthenticationAgent');
}
},
disable: function() {
this._native.unregister();
try {
this._native.unregister();
} catch(e) {
log('Failed to unregister AuthenticationAgent');
}
},
_onInitiate: function(nativeAgent, actionId, message, iconName, cookie, userNames) {
@ -347,45 +376,24 @@ const AuthenticationAgent = new Lang.Class({
// discussion.
this._currentDialog.connect('done', Lang.bind(this, this._onDialogDone));
this._currentDialog.startAuthentication();
this._currentDialog.performAuthentication();
},
_onCancel: function(nativeAgent) {
this._completeRequest(false, false);
this._completeRequest(false);
},
_onDialogDone: function(dialog, keepVisible, dismissed) {
this._completeRequest(keepVisible, dismissed);
_onDialogDone: function(dialog, dismissed) {
this._completeRequest(dismissed);
},
_reallyCompleteRequest: function(dismissed) {
_completeRequest: function(dismissed) {
this._currentDialog.close();
this._currentDialog.destroySession();
this._currentDialog = null;
this._isCompleting = false;
this._native.complete(dismissed)
this._native.complete(dismissed);
},
_completeRequest: function(keepVisible, wasDismissed) {
if (this._isCompleting)
return;
this._isCompleting = true;
if (keepVisible) {
// Give the user 2 seconds to read 'Authentication Failure' before
// dismissing the dialog
Mainloop.timeout_add(2000,
Lang.bind(this,
function() {
this._reallyCompleteRequest(wasDismissed);
return false;
}));
} else {
this._reallyCompleteRequest(wasDismissed);
}
}
});
const Component = AuthenticationAgent;

View File

@ -20,8 +20,7 @@ const Recorder = new Lang.Class({
Main.wm.addKeybinding('toggle-recording',
this._bindingSettings,
Meta.KeyBindingFlags.NONE,
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW,
Shell.KeyBindingMode.ALL,
Lang.bind(this, this._toggleRecorder));
},

View File

@ -13,11 +13,12 @@ const Tp = imports.gi.TelepathyGLib;
const History = imports.misc.history;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const NotificationDaemon = imports.ui.notificationDaemon;
const Params = imports.misc.params;
const PopupMenu = imports.ui.popupMenu;
// See Notification.appendMessage
const SCROLLBACK_IMMEDIATE_TIME = 60; // 1 minute
const SCROLLBACK_IMMEDIATE_TIME = 3 * 60; // 3 minutes
const SCROLLBACK_RECENT_TIME = 15 * 60; // 15 minutes
const SCROLLBACK_RECENT_LENGTH = 20;
const SCROLLBACK_IDLE_LENGTH = 5;
@ -415,6 +416,8 @@ const TelepathyClient = new Lang.Class({
_ensureAppSource: function() {
if (this._appSource == null) {
this._appSource = new MessageTray.Source(_("Chat"), 'empathy');
this._appSource.policy = new NotificationDaemon.NotificationApplicationPolicy('empathy');
Main.messageTray.add(this._appSource);
this._appSource.connect('destroy', Lang.bind(this, function () {
this._appSource = null;
@ -484,6 +487,10 @@ const ChatSource = new Lang.Class({
return rightClickMenu;
},
_createPolicy: function() {
return new NotificationDaemon.NotificationApplicationPolicy('empathy');
},
_updateAlias: function() {
let oldAlias = this.title;
let newAlias = this._contact.get_alias();
@ -633,6 +640,10 @@ const ChatSource = new Lang.Class({
return this._pendingMessages.length;
},
get indicatorCount() {
return this.count;
},
get unseenCount() {
return this.count;
},
@ -956,7 +967,8 @@ const ChatNotification = new Lang.Class({
let timeLabel = this._append({ body: this._formatTimestamp(lastMessageDate),
group: 'meta',
styles: ['chat-meta-message'],
childProps: { expand: true, x_fill: false },
childProps: { expand: true, x_fill: false,
x_align: St.Align.END },
noTimestamp: true,
timestamp: lastMessageTime });
@ -1048,6 +1060,10 @@ const ApproverSource = new Lang.Class({
}));
},
_createPolicy: function() {
return new NotificationDaemon.NotificationApplicationPolicy('empathy');
},
destroy: function() {
if (this._invalidId != 0) {
this._dispatchOp.disconnect(this._invalidId);
@ -1346,9 +1362,8 @@ const AccountNotification = new Lang.Class({
this.connect('action-invoked', Lang.bind(this, function(self, action) {
switch (action) {
case 'view':
let cmd = '/usr/bin/empathy-accounts'
+ ' --select-account=%s'
.format(account.get_path_suffix());
let cmd = 'empathy-accounts --select-account=' +
account.get_path_suffix();
let app_info = Gio.app_info_create_from_commandline(cmd, null, 0);
app_info.launch([], global.create_app_launch_context());
break;

View File

@ -58,15 +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.NONREACTIVE ||
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,
@ -89,22 +84,33 @@ const CtrlAltTabManager = new Lang.Class({
let items = this._items.filter(function (item) { return item.proxy.mapped; });
// And add the windows metacity would show in its Ctrl-Alt-Tab list
if (!Main.overview.visible) {
if (Main.sessionMode.hasWindows && !Main.overview.visible) {
let screen = global.screen;
let display = screen.get_display();
let windows = display.get_tab_list(Meta.TabList.DOCKS, screen, screen.get_active_workspace ());
let windowTracker = Shell.WindowTracker.get_default();
let textureCache = St.TextureCache.get_default();
for (let i = 0; i < windows.length; i++) {
let icon;
let app = windowTracker.get_window_app(windows[i]);
if (app)
icon = app.create_icon_texture(POPUP_APPICON_SIZE);
else
icon = textureCache.bind_pixbuf_property(windows[i], 'icon');
items.push({ window: windows[i],
name: windows[i].title,
let icon = null;
let iconName = null;
if (windows[i].get_window_type () == Meta.WindowType.DESKTOP) {
iconName = 'video-display-symbolic';
} else {
let app = windowTracker.get_window_app(windows[i]);
if (app)
icon = app.create_icon_texture(POPUP_APPICON_SIZE);
else
icon = textureCache.bind_pixbuf_property(windows[i], 'icon');
}
items.push({ name: windows[i].title,
proxy: windows[i].get_compositor_private(),
focusCallback: Lang.bind(windows[i],
function(timestamp) {
Main.activateWindow(this, timestamp);
}),
iconActor: icon,
iconName: iconName,
sortGroup: SortGroup.MIDDLE });
}
}
@ -126,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

@ -22,11 +22,8 @@ const DASH_ITEM_LABEL_HIDE_TIME = 0.1;
const DASH_ITEM_HOVER_TIMEOUT = 300;
function getAppFromSource(source) {
if (source instanceof AppDisplay.AppWellIcon) {
if (source instanceof AppDisplay.AppIcon) {
return source.app;
} else if (source.metaWindow) {
let tracker = Shell.WindowTracker.get_default();
return tracker.get_window_app(source.metaWindow);
} else {
return null;
}
@ -36,30 +33,26 @@ function getAppFromSource(source) {
// when requesting a size
const DashItemContainer = new Lang.Class({
Name: 'DashItemContainer',
Extends: St.Widget,
_init: function() {
this.actor = new Shell.GenericContainer({ style_class: 'dash-item-container' });
this.actor.connect('get-preferred-width',
Lang.bind(this, this._getPreferredWidth));
this.actor.connect('get-preferred-height',
Lang.bind(this, this._getPreferredHeight));
this.actor.connect('allocate',
Lang.bind(this, this._allocate));
this.actor._delegate = this;
this.parent({ style_class: 'dash-item-container' });
this._labelText = "";
this.label = new St.Label({ style_class: 'dash-label'});
this.label.hide();
Main.layoutManager.addChrome(this.label);
this.actor.label_actor = this.label;
this.label_actor = this.label;
this.child = null;
this._childScale = 1;
this._childOpacity = 255;
this._childScale = 0;
this._childOpacity = 0;
this.animatingOut = false;
},
_allocate: function(actor, box, flags) {
vfunc_allocate: function(box, flags) {
this.set_allocation(box, flags);
if (this.child == null)
return;
@ -81,28 +74,28 @@ const DashItemContainer = new Lang.Class({
this.child.allocate(childBox, flags);
},
_getPreferredHeight: function(actor, forWidth, alloc) {
alloc.min_size = 0;
alloc.natural_size = 0;
vfunc_get_preferred_height: function(forWidth) {
let themeNode = this.get_theme_node();
if (this.child == null)
return;
return [0, 0];
forWidth = themeNode.adjust_for_width(forWidth);
let [minHeight, natHeight] = this.child.get_preferred_height(forWidth);
alloc.min_size += minHeight * this.child.scale_y;
alloc.natural_size += natHeight * this.child.scale_y;
return themeNode.adjust_preferred_height(minHeight * this.child.scale_y,
natHeight * this.child.scale_y);
},
_getPreferredWidth: function(actor, forHeight, alloc) {
alloc.min_size = 0;
alloc.natural_size = 0;
vfunc_get_preferred_width: function(forHeight) {
let themeNode = this.get_theme_node();
if (this.child == null)
return;
return [0, 0];
forHeight = themeNode.adjust_for_height(forHeight);
let [minWidth, natWidth] = this.child.get_preferred_width(forHeight);
alloc.min_size = minWidth * this.child.scale_y;
alloc.natural_size = natWidth * this.child.scale_y;
return themeNode.adjust_preferred_width(minWidth * this.child.scale_y,
natWidth * this.child.scale_y);
},
showLabel: function() {
@ -113,9 +106,9 @@ const DashItemContainer = new Lang.Class({
this.label.opacity = 0;
this.label.show();
let [stageX, stageY] = this.actor.get_transformed_position();
let [stageX, stageY] = this.get_transformed_position();
let itemHeight = this.actor.allocation.y2 - this.actor.allocation.y1;
let itemHeight = this.allocation.y2 - this.allocation.y1;
let labelHeight = this.label.get_height();
let yOffset = Math.floor((itemHeight - labelHeight) / 2)
@ -129,7 +122,7 @@ const DashItemContainer = new Lang.Class({
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
x = stageX - this.label.get_width() - xOffset;
else
x = stageX + this.actor.get_width() + xOffset;
x = stageX + this.get_width() + xOffset;
this.label.set_position(x, y);
Tweener.addTween(this.label,
@ -159,22 +152,25 @@ const DashItemContainer = new Lang.Class({
if (this.child == actor)
return;
this.actor.destroy_all_children();
this.destroy_all_children();
this.child = actor;
this.actor.add_actor(this.child);
this.add_actor(this.child);
this.child.set_scale_with_gravity(this._childScale, this._childScale,
Clutter.Gravity.CENTER);
this.child.set_opacity(this._childOpacity);
},
animateIn: function() {
show: function(animate) {
if (this.child == null)
return;
this.childScale = 0;
this.childOpacity = 0;
let time = animate ? DASH_ANIMATION_TIME : 0;
Tweener.addTween(this,
{ childScale: 1.0,
childOpacity: 255,
time: DASH_ANIMATION_TIME,
time: time,
transition: 'easeOutQuad'
});
},
@ -183,7 +179,7 @@ const DashItemContainer = new Lang.Class({
if (this.label)
this.label.destroy();
this.actor.destroy();
this.parent();
},
animateOutAndDestroy: function() {
@ -191,19 +187,18 @@ const DashItemContainer = new Lang.Class({
this.label.destroy();
if (this.child == null) {
this.actor.destroy();
this.destroy();
return;
}
this.animatingOut = true;
this.childScale = 1.0;
Tweener.addTween(this,
{ childScale: 0.0,
childOpacity: 0,
time: DASH_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this.actor.destroy();
this.destroy();
})
});
},
@ -216,7 +211,7 @@ const DashItemContainer = new Lang.Class({
this.child.set_scale_with_gravity(scale, scale,
Clutter.Gravity.CENTER);
this.actor.queue_relayout();
this.queue_relayout();
},
get childScale() {
@ -230,7 +225,7 @@ const DashItemContainer = new Lang.Class({
return;
this.child.set_opacity(opacity);
this.actor.queue_redraw();
this.queue_redraw();
},
get childOpacity() {
@ -292,13 +287,7 @@ const ShowAppsIcon = new Lang.Class({
},
handleDragOver: function(source, actor, x, y, time) {
let app = getAppFromSource(source);
if (app == null)
return DND.DragMotionResult.NO_DROP;
let id = app.get_id();
let isFavorite = AppFavorites.getAppFavorites().isFavorite(id);
if (!isFavorite)
if (!this._canRemoveApp(getAppFromSource(source)))
return DND.DragMotionResult.NO_DROP;
return DND.DragMotionResult.MOVE_DROP;
@ -306,7 +295,7 @@ const ShowAppsIcon = new Lang.Class({
acceptDrop: function(source, actor, x, y, time) {
let app = getAppFromSource(source);
if (app == null)
if (!this._canRemoveApp(app))
return false;
let id = app.get_id();
@ -331,6 +320,16 @@ const DragPlaceholderItem = new Lang.Class({
}
});
const EmptyDropTargetItem = new Lang.Class({
Name: 'EmptyDropTargetItem',
Extends: DashItemContainer,
_init: function() {
this.parent();
this.setChild(new St.Bin({ style_class: 'empty-dash-drop-target' }));
}
});
const DashActor = new Lang.Class({
Name: 'DashActor',
Extends: St.Widget,
@ -361,6 +360,23 @@ const DashActor = new Lang.Class({
childBox.y1 = contentBox.y2 - showAppsNatHeight;
childBox.y2 = contentBox.y2;
showAppsButton.allocate(childBox, flags);
},
vfunc_get_preferred_height: function(forWidth) {
// We want to request the natural height of all our children
// as our natural height, so we chain up to StWidget (which
// then calls BoxLayout), but we only request the showApps
// button as the minimum size
let [, natHeight] = this.parent(forWidth);
let themeNode = this.get_theme_node();
let adjustedForWidth = themeNode.adjust_for_width(forWidth);
let [, showAppsButton] = this.get_children();
let [minHeight, ] = showAppsButton.get_preferred_height(adjustedForWidth);
[minHeight, ] = themeNode.adjust_preferred_height(minHeight, natHeight);
return [minHeight, natHeight];
}
});
@ -386,12 +402,14 @@ const Dash = new Lang.Class({
this._container.add_actor(this._box);
this._showAppsIcon = new ShowAppsIcon();
this._showAppsIcon.childScale = 1;
this._showAppsIcon.childOpacity = 255;
this._showAppsIcon.icon.setIconSize(this.iconSize);
this._hookUpLabel(this._showAppsIcon);
this.showAppsButton = this._showAppsIcon.toggleButton;
this._container.add_actor(this._showAppsIcon.actor);
this._container.add_actor(this._showAppsIcon);
this.actor = new St.Bin({ child: this._container });
this.actor.connect('notify::height', Lang.bind(this,
@ -415,12 +433,10 @@ const Dash = new Lang.Class({
Lang.bind(this, this._onDragEnd));
Main.overview.connect('item-drag-cancelled',
Lang.bind(this, this._onDragCancelled));
Main.overview.connect('window-drag-begin',
Lang.bind(this, this._onDragBegin));
Main.overview.connect('window-drag-cancelled',
Lang.bind(this, this._onDragCancelled));
Main.overview.connect('window-drag-end',
Lang.bind(this, this._onDragEnd));
// Translators: this is the name of the dock/favorites area on
// the left of the overview
Main.ctrlAltTabManager.addGroup(this.actor, _("Dash"), 'user-bookmarks-symbolic');
},
_onDragBegin: function() {
@ -429,6 +445,12 @@ const Dash = new Lang.Class({
dragMotion: Lang.bind(this, this._onDragMotion)
};
DND.addDragMonitor(this._dragMonitor);
if (this._box.get_n_children() == 0) {
this._emptyDropTarget = new EmptyDropTargetItem();
this._box.insert_child_at_index(this._emptyDropTarget, 0);
this._emptyDropTarget.show(true);
}
},
_onDragCancelled: function() {
@ -445,6 +467,7 @@ const Dash = new Lang.Class({
_endDrag: function() {
this._clearDragPlaceholder();
this._clearEmptyDropTarget();
this._showAppsIcon.setDragApp(null);
DND.removeDragMonitor(this._dragMonitor);
},
@ -455,7 +478,7 @@ const Dash = new Lang.Class({
return DND.DragMotionResult.CONTINUE;
let showAppsHovered =
this._showAppsIcon.actor.contains(dragEvent.targetActor);
this._showAppsIcon.contains(dragEvent.targetActor);
if (!this._box.contains(dragEvent.targetActor) || showAppsHovered)
this._clearDragPlaceholder();
@ -491,9 +514,9 @@ const Dash = new Lang.Class({
},
_createAppItem: function(app) {
let appIcon = new AppDisplay.AppWellIcon(app,
{ setSizeManually: true,
showLabel: false });
let appIcon = new AppDisplay.AppIcon(app,
{ setSizeManually: true,
showLabel: false });
appIcon._draggable.connect('drag-begin',
Lang.bind(this, function() {
appIcon.actor.opacity = 50;
@ -510,7 +533,7 @@ const Dash = new Lang.Class({
let item = new DashItemContainer();
item.setChild(appIcon.actor);
// Override default AppWellIcon label_actor, now the
// Override default AppIcon label_actor, now the
// accessible_name is set at DashItemContainer.setLabelText
appIcon.actor.label_actor = null;
item.setLabelText(app.get_name());
@ -570,13 +593,13 @@ const Dash = new Lang.Class({
// animating out (which means they will be destroyed at the end of
// the animation)
let iconChildren = this._box.get_children().filter(function(actor) {
return actor._delegate.child &&
actor._delegate.child._delegate &&
actor._delegate.child._delegate.icon &&
!actor._delegate.animatingOut;
return actor.child &&
actor.child._delegate &&
actor.child._delegate.icon &&
!actor.animatingOut;
});
iconChildren.push(this._showAppsIcon.actor);
iconChildren.push(this._showAppsIcon);
if (this._maxHeight == -1)
return;
@ -589,23 +612,18 @@ const Dash = new Lang.Class({
let availHeight = maxContent.y2 - maxContent.y1;
let spacing = themeNode.get_length('spacing');
let firstIcon = iconChildren[0]._delegate.child._delegate.icon;
let firstButton = iconChildren[0].child;
let firstIcon = firstButton._delegate.icon;
let minHeight, natHeight;
// Enforce the current icon size during the size request if
// the icon is animating
if (firstIcon._animating) {
let [currentWidth, currentHeight] = firstIcon.icon.get_size();
// Enforce the current icon size during the size request
let [currentWidth, currentHeight] = firstIcon.icon.get_size();
firstIcon.icon.set_size(this.iconSize, this.iconSize);
[minHeight, natHeight] = iconChildren[0].get_preferred_height(-1);
firstIcon.icon.set_size(this.iconSize, this.iconSize);
[minHeight, natHeight] = firstButton.get_preferred_height(-1);
firstIcon.icon.set_size(currentWidth, currentHeight);
} else {
[minHeight, natHeight] = iconChildren[0].get_preferred_height(-1);
}
firstIcon.icon.set_size(currentWidth, currentHeight);
// Subtract icon padding and box spacing from the available height
availHeight -= iconChildren.length * (natHeight - this.iconSize) +
@ -630,7 +648,7 @@ const Dash = new Lang.Class({
let scale = oldIconSize / newIconSize;
for (let i = 0; i < iconChildren.length; i++) {
let icon = iconChildren[i]._delegate.child._delegate.icon;
let icon = iconChildren[i].child._delegate.icon;
// Set the new size immediately, to keep the icons' sizes
// in sync with this.iconSize
@ -650,15 +668,11 @@ const Dash = new Lang.Class({
icon.icon.set_size(icon.icon.width * scale,
icon.icon.height * scale);
icon._animating = true;
Tweener.addTween(icon.icon,
{ width: targetWidth,
height: targetHeight,
time: DASH_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: function() {
icon._animating = false;
}
});
}
},
@ -669,13 +683,13 @@ const Dash = new Lang.Class({
let running = this._appSystem.get_running();
let children = this._box.get_children().filter(function(actor) {
return actor._delegate.child &&
actor._delegate.child._delegate &&
actor._delegate.child._delegate.app;
return actor.child &&
actor.child._delegate &&
actor.child._delegate.app;
});
// Apps currently in the dash
let oldApps = children.map(function(actor) {
return actor._delegate.child._delegate.app;
return actor.child._delegate.app;
});
// Apps supposed to be in the dash
let newApps = [];
@ -741,7 +755,7 @@ const Dash = new Lang.Class({
let insertHere = newApps[newIndex + 1] &&
newApps[newIndex + 1] == oldApps[oldIndex];
let alreadyRemoved = removedActors.reduce(function(result, actor) {
let removedApp = actor._delegate.child._delegate.app;
let removedApp = actor.child._delegate.app;
return result || removedApp == newApps[newIndex];
}, false);
@ -758,11 +772,11 @@ const Dash = new Lang.Class({
}
for (let i = 0; i < addedItems.length; i++)
this._box.insert_child_at_index(addedItems[i].item.actor,
this._box.insert_child_at_index(addedItems[i].item,
addedItems[i].pos);
for (let i = 0; i < removedActors.length; i++) {
let item = removedActors[i]._delegate;
let item = removedActors[i];
// Don't animate item removal when the overview is transitioning
// or hidden
@ -776,25 +790,39 @@ const Dash = new Lang.Class({
// Skip animations on first run when adding the initial set
// of items, to avoid all items zooming in at once
if (!this._shownInitially) {
let animate = this._shownInitially && Main.overview.visible &&
!Main.overview.animationInProgress;
if (!this._shownInitially)
this._shownInitially = true;
return;
for (let i = 0; i < addedItems.length; i++) {
addedItems[i].item.show(animate);
}
// Don't animate item addition when the overview is transitioning
// or hidden
if (!Main.overview.visible || Main.overview.animationInProgress)
return;
for (let i = 0; i < addedItems.length; i++)
addedItems[i].item.animateIn();
// Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744
// Without it, StBoxLayout may use a stale size cache
this._box.queue_relayout();
},
_clearDragPlaceholder: function() {
if (this._dragPlaceholder) {
this._animatingPlaceholdersCount++;
this._dragPlaceholder.animateOutAndDestroy();
this._dragPlaceholder.connect('destroy',
Lang.bind(this, function() {
this._animatingPlaceholdersCount--;
}));
this._dragPlaceholder = null;
this._dragPlaceholderPos = -1;
}
this._dragPlaceholderPos = -1;
},
_clearEmptyDropTarget: function() {
if (this._emptyDropTarget) {
this._emptyDropTarget.animateOutAndDestroy();
this._emptyDropTarget = null;
}
},
@ -818,27 +846,22 @@ const Dash = new Lang.Class({
// the remove target has the same size as "normal" items, we don't
// need to do the same adjustment there.
if (this._dragPlaceholder) {
boxHeight -= this._dragPlaceholder.actor.height;
boxHeight -= this._dragPlaceholder.height;
numChildren--;
}
let pos = Math.floor(y * numChildren / boxHeight);
let pos;
if (!this._emptyDropTarget)
pos = Math.floor(y * numChildren / boxHeight);
else
pos = 0; // always insert at the top when dash is empty
if (pos != this._dragPlaceholderPos && pos <= numFavorites && this._animatingPlaceholdersCount == 0) {
this._dragPlaceholderPos = pos;
// Don't allow positioning before or after self
if (favPos != -1 && (pos == favPos || pos == favPos + 1)) {
if (this._dragPlaceholder) {
this._dragPlaceholder.animateOutAndDestroy();
this._animatingPlaceholdersCount++;
this._dragPlaceholder.actor.connect('destroy',
Lang.bind(this, function() {
this._animatingPlaceholdersCount--;
}));
}
this._dragPlaceholder = null;
this._clearDragPlaceholder();
return DND.DragMotionResult.CONTINUE;
}
@ -847,7 +870,7 @@ const Dash = new Lang.Class({
// an animation
let fadeIn;
if (this._dragPlaceholder) {
this._dragPlaceholder.actor.destroy();
this._dragPlaceholder.destroy();
fadeIn = false;
} else {
fadeIn = true;
@ -856,17 +879,16 @@ const Dash = new Lang.Class({
this._dragPlaceholder = new DragPlaceholderItem();
this._dragPlaceholder.child.set_width (this.iconSize);
this._dragPlaceholder.child.set_height (this.iconSize / 2);
this._box.insert_child_at_index(this._dragPlaceholder.actor,
this._box.insert_child_at_index(this._dragPlaceholder,
this._dragPlaceholderPos);
if (fadeIn)
this._dragPlaceholder.animateIn();
this._dragPlaceholder.show(fadeIn);
}
// Remove the drag placeholder if we are not in the
// "favorites zone"
if (pos > numFavorites && this._dragPlaceholder) {
if (pos > numFavorites)
this._clearDragPlaceholder();
}
if (!this._dragPlaceholder)
return DND.DragMotionResult.NO_DROP;
@ -897,10 +919,10 @@ const Dash = new Lang.Class({
let children = this._box.get_children();
for (let i = 0; i < this._dragPlaceholderPos; i++) {
if (this._dragPlaceholder &&
children[i] == this._dragPlaceholder.actor)
children[i] == this._dragPlaceholder)
continue;
let childId = children[i]._delegate.child._delegate.app.get_id();
let childId = children[i].child._delegate.app.get_id();
if (childId == id)
continue;
if (childId in favorites)

View File

@ -32,6 +32,7 @@ function _onVertSepRepaint (area)
cr.setDash([1, 3], 1); // Hard-code for now
cr.setLineWidth(stippleWidth);
cr.stroke();
cr.$dispose();
};
const DateMenuButton = new Lang.Class({
@ -48,11 +49,6 @@ const DateMenuButton = new Lang.Class({
menuAlignment = 1.0 - menuAlignment;
this.parent(menuAlignment);
// At this moment calendar menu is not keyboard navigable at
// all (so not accessible), so it doesn't make sense to set as
// role ATK_ROLE_MENU like other elements of the panel.
this.actor.accessible_role = Atk.Role.LABEL;
this._clockDisplay = new St.Label();
this.actor.add_actor(this._clockDisplay);
@ -83,14 +79,23 @@ const DateMenuButton = new Lang.Class({
}));
vbox.add(this._calendar.actor);
item = this.menu.addSettingsAction(_("Date and Time Settings"), 'gnome-datetime-panel.desktop');
if (item) {
let separator = new PopupMenu.PopupSeparatorMenuItem();
separator.setColumnWidths(1);
vbox.add(separator.actor, {y_align: St.Align.END, expand: true, y_fill: false});
let separator = new PopupMenu.PopupSeparatorMenuItem();
vbox.add(separator.actor, { y_align: St.Align.END, expand: true, y_fill: false });
this._openCalendarItem = new PopupMenu.PopupMenuItem(_("Open Calendar"));
this._openCalendarItem.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
vbox.add(this._openCalendarItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
this._openClocksItem = new PopupMenu.PopupMenuItem(_("Open Clocks"));
this._openClocksItem.connect('activate', Lang.bind(this, this._onOpenClocksActivate));
vbox.add(this._openClocksItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
Shell.AppSystem.get_default().connect('installed-changed',
Lang.bind(this, this._appInstalledChanged));
item = this.menu.addSettingsAction(_("Date & Time Settings"), 'gnome-datetime-panel.desktop');
if (item) {
item.actor.show_on_set_parent = false;
item.actor.can_focus = false;
item.actor.reparent(vbox);
this._dateAndTimeSeparator = separator;
}
@ -101,22 +106,7 @@ const DateMenuButton = new Lang.Class({
hbox.add(this._separator);
// Fill up the second column
vbox = new St.BoxLayout({ name: 'calendarEventsArea',
vertical: true });
hbox.add(vbox, { expand: true });
// Event list
vbox.add(this._eventList.actor, { expand: true });
this._openCalendarItem = new PopupMenu.PopupMenuItem(_("Open Calendar"));
this._openCalendarItem.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
this._openCalendarItem.actor.can_focus = false;
vbox.add(this._openCalendarItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
this._calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' });
this._calendarSettings.connect('changed::exec',
Lang.bind(this, this._calendarSettingsChanged));
this._calendarSettingsChanged();
hbox.add(this._eventList.actor, { expand: true, y_fill: false, y_align: St.Align.START });
// Whenever the menu is opened, select today
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
@ -151,30 +141,40 @@ const DateMenuButton = new Lang.Class({
this._sessionUpdated();
},
_calendarSettingsChanged: function() {
let exec = this._calendarSettings.get_string('exec');
let fullExec = GLib.find_program_in_path(exec);
this._openCalendarItem.actor.visible = fullExec != null;
_appInstalledChanged: function() {
this._calendarApp = undefined;
this._updateEventsVisibility();
},
_setEventsVisibility: function(visible) {
this._openCalendarItem.actor.visible = visible;
_updateEventsVisibility: function() {
let visible = this._eventSource.hasCalendars;
this._openCalendarItem.actor.visible = visible &&
(this._getCalendarApp() != null);
this._openClocksItem.actor.visible = visible &&
(this._getClockApp() != null);
this._separator.visible = visible;
this._eventList.actor.visible = visible;
if (visible) {
let alignment = 0.25;
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
alignment = 1.0 - alignment;
this.menu._arrowAlignment = alignment;
this._eventList.actor.get_parent().show();
let alignment = 0.25;
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
alignment = 1.0 - alignment;
this.menu._arrowAlignment = alignment;
} else {
this.menu._arrowAlignment = 0.5;
this._eventList.actor.get_parent().hide();
this.menu._arrowAlignment = 0.5;
}
},
_setEventSource: function(eventSource) {
if (this._eventSource)
this._eventSource.destroy();
this._calendar.setEventSource(eventSource);
this._eventList.setEventSource(eventSource);
this._eventSource = eventSource;
this._eventSource.connect('notify::has-calendars', Lang.bind(this, function() {
this._updateEventsVisibility();
}));
},
_sessionUpdated: function() {
@ -183,10 +183,10 @@ const DateMenuButton = new Lang.Class({
if (showEvents) {
eventSource = new Calendar.DBusEventSource();
} else {
eventSource = null;
eventSource = new Calendar.EmptyEventSource();
}
this._setEventSource(eventSource);
this._setEventsVisibility(showEvents);
this._updateEventsVisibility();
// This needs to be handled manually, as the code to
// autohide separators doesn't work across the vbox
@ -203,26 +203,34 @@ const DateMenuButton = new Lang.Class({
this._date.set_text(displayDate.toLocaleFormat(dateFormat));
},
_getCalendarApp: function() {
if (this._calendarApp !== undefined)
return this._calendarApp;
let apps = Gio.AppInfo.get_recommended_for_type('text/calendar');
if (apps && (apps.length > 0))
this._calendarApp = apps[0];
else
this._calendarApp = null;
return this._calendarApp;
},
_getClockApp: function() {
return Shell.AppSystem.get_default().lookup_app('gnome-clocks.desktop');
},
_onOpenCalendarActivate: function() {
this.menu.close();
let tool = this._calendarSettings.get_string('exec');
if (tool.length == 0 || tool.substr(0, 9) == 'evolution') {
// TODO: pass the selected day
let app = Shell.AppSystem.get_default().lookup_app('evolution-calendar.desktop');
app.activate();
} else {
let needTerm = this._calendarSettings.get_boolean('needs-term');
if (needTerm) {
let terminalSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.terminal' });
let term = terminalSettings.get_string('exec');
let arg = terminalSettings.get_string('exec-arg');
if (arg != '')
Util.spawn([term, arg, tool]);
else
Util.spawn([term, tool]);
} else {
Util.spawnCommandLine(tool)
}
}
let app = this._getCalendarApp();
if (app.get_id() == 'evolution.desktop')
app = Gio.DesktopAppInfo.new('evolution-calendar.desktop');
app.launch([], global.create_app_launch_context());
},
_onOpenClocksActivate: function() {
this.menu.close();
let app = this._getClockApp();
app.activate();
}
});

View File

@ -1,6 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const St = imports.gi.St;
const Lang = imports.lang;
@ -43,9 +44,7 @@ let dragMonitors = [];
function _getEventHandlerActor() {
if (!eventHandlerActor) {
eventHandlerActor = new Clutter.Rectangle();
eventHandlerActor.width = 0;
eventHandlerActor.height = 0;
eventHandlerActor = new Clutter.Actor({ width: 0, height: 0 });
Main.uiGroup.add_actor(eventHandlerActor);
// We connect to 'event' rather than 'captured-event' because the capturing phase doesn't happen
// when you've grabbed the pointer.
@ -85,11 +84,13 @@ const _Draggable = new Lang.Class({
this.actor.connect('destroy', Lang.bind(this, function() {
this._actorDestroyed = true;
// If the drag actor is destroyed and we were going to fix
// up its hover state, fix up the parent hover state instead
if (this.actor == this._firstLeaveActor)
this._firstLeaveActor = this._dragOrigParent;
if (this._dragInProgress)
if (this._dragInProgress && this._dragCancellable)
this._cancelDrag(global.get_current_time());
this.disconnectAll();
}));
@ -102,6 +103,7 @@ const _Draggable = new Lang.Class({
this._buttonDown = false; // The mouse button has been pressed and has not yet been released.
this._dragInProgress = false; // The drag has been started, and has not been dropped or cancelled yet.
this._animationInProgress = false; // The drag is over and the item is in the process of animating to its original position (snapping back or reverting).
this._dragCancellable = true;
// During the drag, we eat enter/leave events so that actors don't prelight.
// But we remember the actors that we first left/last entered so we can
@ -136,25 +138,26 @@ const _Draggable = new Lang.Class({
},
_ungrabActor: function() {
Clutter.ungrab_pointer();
if (!this._onEventId)
return;
Clutter.ungrab_pointer();
this.actor.disconnect(this._onEventId);
this._onEventId = null;
},
_grabEvents: function() {
if (!this._eventsGrabbed) {
Clutter.grab_pointer(_getEventHandlerActor());
Clutter.grab_keyboard(_getEventHandlerActor());
this._eventsGrabbed = true;
this._eventsGrabbed = Main.pushModal(_getEventHandlerActor());
if (this._eventsGrabbed)
Clutter.grab_pointer(_getEventHandlerActor());
}
},
_ungrabEvents: function() {
if (this._eventsGrabbed) {
Clutter.ungrab_pointer();
Clutter.ungrab_keyboard();
Main.popModal(_getEventHandlerActor());
this._eventsGrabbed = false;
}
},
@ -203,6 +206,19 @@ const _Draggable = new Lang.Class({
return false;
},
/**
* fakeRelease:
*
* Fake a release event.
* Must be called if you want to intercept release events on draggable
* actors for other purposes (for example if you're using
* PopupMenu.ignoreRelease())
*/
fakeRelease: function() {
this._buttonDown = false;
this._ungrabActor();
},
/**
* startDrag:
* @stageX: X coordinate of event
@ -234,7 +250,7 @@ const _Draggable = new Lang.Class({
this._dragY = this._dragStartY = stageY;
if (this.actor._delegate && this.actor._delegate.getDragActor) {
this._dragActor = this.actor._delegate.getDragActor(this._dragStartX, this._dragStartY);
this._dragActor = this.actor._delegate.getDragActor();
this._dragActor.reparent(Main.uiGroup);
this._dragActor.raise_top();
Shell.util_set_hidden_from_pick(this._dragActor, true);
@ -274,19 +290,19 @@ const _Draggable = new Lang.Class({
this._dragOrigY = this._dragActor.y;
this._dragOrigScale = this._dragActor.scale_x;
this._dragActor.reparent(Main.uiGroup);
this._dragActor.raise_top();
Shell.util_set_hidden_from_pick(this._dragActor, true);
// Set the actor's scale such that it will keep the same
// transformed size when it's reparented to the uiGroup
let [scaledWidth, scaledHeight] = this.actor.get_transformed_size();
this._dragActor.set_scale(scaledWidth / this.actor.width,
scaledHeight / this.actor.height);
let [actorStageX, actorStageY] = this.actor.get_transformed_position();
this._dragOffsetX = actorStageX - this._dragStartX;
this._dragOffsetY = actorStageY - this._dragStartY;
// Set the actor's scale such that it will keep the same
// transformed size when it's reparented to the uiGroup
let [scaledWidth, scaledHeight] = this.actor.get_transformed_size();
this.actor.set_scale(scaledWidth / this.actor.width,
scaledHeight / this.actor.height);
this._dragActor.reparent(Main.uiGroup);
this._dragActor.raise_top();
Shell.util_set_hidden_from_pick(this._dragActor, true);
}
this._dragOrigOpacity = this._dragActor.opacity;
@ -343,60 +359,65 @@ const _Draggable = new Lang.Class({
return true;
},
_updateDragHover : function () {
let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
this._dragX, this._dragY);
let dragEvent = {
x: this._dragX,
y: this._dragY,
dragActor: this._dragActor,
source: this.actor._delegate,
targetActor: target
};
for (let i = 0; i < dragMonitors.length; i++) {
let motionFunc = dragMonitors[i].dragMotion;
if (motionFunc) {
let result = motionFunc(dragEvent);
if (result != DragMotionResult.CONTINUE) {
global.set_cursor(DRAG_CURSOR_MAP[result]);
return false;
}
}
}
while (target) {
if (target._delegate && target._delegate.handleDragOver) {
let [r, targX, targY] = target.transform_stage_point(this._dragX, this._dragY);
// We currently loop through all parents on drag-over even if one of the children has handled it.
// We can check the return value of the function and break the loop if it's true if we don't want
// to continue checking the parents.
let result = target._delegate.handleDragOver(this.actor._delegate,
this._dragActor,
targX,
targY,
0);
if (result != DragMotionResult.CONTINUE) {
global.set_cursor(DRAG_CURSOR_MAP[result]);
return false;
}
}
target = target.get_parent();
}
global.set_cursor(Shell.Cursor.DND_IN_DRAG);
return false;
},
_queueUpdateDragHover: function() {
if (this._updateHoverId)
GLib.source_remove(this._updateHoverId);
this._updateHoverId = GLib.idle_add(GLib.PRIORITY_DEFAULT,
Lang.bind(this, this._updateDragHover));
},
_updateDragPosition : function (event) {
let [stageX, stageY] = event.get_coords();
this._dragX = stageX;
this._dragY = stageY;
this._dragActor.set_position(stageX + this._dragOffsetX,
stageY + this._dragOffsetY);
// If we are dragging, update the position
if (this._dragActor) {
this._dragActor.set_position(stageX + this._dragOffsetX,
stageY + this._dragOffsetY);
let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
stageX, stageY);
// We call observers only once per motion with the innermost
// target actor. If necessary, the observer can walk the
// parent itself.
let dragEvent = {
x: stageX,
y: stageY,
dragActor: this._dragActor,
source: this.actor._delegate,
targetActor: target
};
for (let i = 0; i < dragMonitors.length; i++) {
let motionFunc = dragMonitors[i].dragMotion;
if (motionFunc) {
let result = motionFunc(dragEvent);
if (result != DragMotionResult.CONTINUE) {
global.set_cursor(DRAG_CURSOR_MAP[result]);
return true;
}
}
}
while (target) {
if (target._delegate && target._delegate.handleDragOver) {
let [r, targX, targY] = target.transform_stage_point(stageX, stageY);
// We currently loop through all parents on drag-over even if one of the children has handled it.
// We can check the return value of the function and break the loop if it's true if we don't want
// to continue checking the parents.
let result = target._delegate.handleDragOver(this.actor._delegate,
this._dragActor,
targX,
targY,
event.get_time());
if (result != DragMotionResult.CONTINUE) {
global.set_cursor(DRAG_CURSOR_MAP[result]);
return true;
}
}
target = target.get_parent();
}
global.set_cursor(Shell.Cursor.DND_IN_DRAG);
}
this._queueUpdateDragHover();
return true;
},
@ -425,6 +446,11 @@ const _Draggable = new Lang.Class({
}
}
// At this point it is too late to cancel a drag by destroying
// the actor, the fate of which is decided by acceptDrop and its
// side-effects
this._dragCancellable = false;
while (target) {
if (target._delegate && target._delegate.acceptDrop) {
let [r, targX, targY] = target.transform_stage_point(dropX, dropY);
@ -433,8 +459,6 @@ const _Draggable = new Lang.Class({
targX,
targY,
event.get_time())) {
if (this._actorDestroyed)
return true;
// If it accepted the drop without taking the actor,
// handle it ourselves.
if (this._dragActor.get_parent() == Main.uiGroup) {
@ -493,6 +517,11 @@ const _Draggable = new Lang.Class({
},
_cancelDrag: function(eventTime) {
if (this._updateHoverId) {
GLib.source_remove(this._updateHoverId);
this._updateHoverId = 0;
}
this.emit('drag-cancelled', eventTime);
this._dragInProgress = false;
let [snapBackX, snapBackY, snapBackScale] = this._getRestoreLocation();

View File

@ -19,6 +19,7 @@
*/
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Signals = imports.signals;
const AccountsService = imports.gi.AccountsService;
@ -34,7 +35,7 @@ const GnomeSession = imports.misc.gnomeSession;
const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog;
const Tweener = imports.ui.tweener;
const UserMenu = imports.ui.userMenu;
const UserWidget = imports.ui.userWidget;
let _endSessionDialog = null;
@ -50,6 +51,7 @@ const EndSessionDialogIface = <interface name="org.gnome.SessionManager.EndSessi
<arg type="u" direction="in" />
<arg type="ao" direction="in" />
</method>
<method name="Close" />
<signal name="ConfirmedLogout" />
<signal name="ConfirmedReboot" />
<signal name="ConfirmedShutdown" />
@ -223,7 +225,8 @@ const EndSessionDialog = new Lang.Class({
Extends: ModalDialog.ModalDialog,
_init: function() {
this.parent({ styleClass: 'end-session-dialog' });
this.parent({ styleClass: 'end-session-dialog',
destroyOnClose: false });
this._user = AccountsService.UserManager.get_default().get_user(GLib.get_user_name());
@ -357,9 +360,9 @@ const EndSessionDialog = new Lang.Class({
icon_size: _DIALOG_ICON_SIZE,
style_class: dialogContent.iconStyleClass });
} else {
let avatarWidget = new UserMenu.UserAvatarWidget(this._user,
{ iconSize: _DIALOG_ICON_SIZE,
styleClass: dialogContent.iconStyleClass });
let avatarWidget = new UserWidget.Avatar(this._user,
{ iconSize: _DIALOG_ICON_SIZE,
styleClass: dialogContent.iconStyleClass });
this._iconBin.child = avatarWidget.actor;
avatarWidget.update();
}
@ -377,7 +380,12 @@ const EndSessionDialog = new Lang.Class({
let signal = dialogContent.confirmButtons[i].signal;
let label = dialogContent.confirmButtons[i].label;
buttons.push({ action: Lang.bind(this, function() {
this._confirm(signal);
this.close(true);
let signalId = this.connect('closed',
Lang.bind(this, function() {
this.disconnect(signalId);
this._confirm(signal);
}));
}),
label: label });
}
@ -385,15 +393,17 @@ const EndSessionDialog = new Lang.Class({
this.setButtons(buttons);
},
close: function() {
close: function(skipSignal) {
this.parent();
this._dbusImpl.emit_signal('Closed', null);
if (!skipSignal)
this._dbusImpl.emit_signal('Closed', null);
},
cancel: function() {
this._stopTimer();
this._dbusImpl.emit_signal('Canceled', null);
this.close(global.get_current_time());
this.close();
},
_confirm: function(signal) {
@ -408,22 +418,35 @@ const EndSessionDialog = new Lang.Class({
},
_startTimer: function() {
let startTime = GLib.get_monotonic_time();
this._secondsLeft = this._totalSecondsToStayOpen;
Tweener.addTween(this,
{ _secondsLeft: 0,
time: this._secondsLeft,
transition: 'linear',
onUpdate: Lang.bind(this, this._updateDescription),
onComplete: Lang.bind(this, function() {
let dialogContent = DialogContent[this._type];
let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
this._confirm(button.signal);
}),
});
this._updateDescription();
this._timerId = Mainloop.timeout_add_seconds(1, Lang.bind(this,
function() {
let currentTime = GLib.get_monotonic_time();
let secondsElapsed = ((currentTime - startTime) / 1000000);
this._secondsLeft = this._totalSecondsToStayOpen - secondsElapsed;
if (this._secondsLeft > 0) {
this._updateDescription();
return true;
}
let dialogContent = DialogContent[this._type];
let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
this._confirm(button.signal);
return false;
}));
},
_stopTimer: function() {
Tweener.removeTweens(this);
if (this._timerId != 0) {
Mainloop.source_remove(this._timerId);
this._timerId = 0;
}
this._secondsLeft = 0;
},
@ -440,7 +463,7 @@ const EndSessionDialog = new Lang.Class({
let item = new ListItem(app, reason);
item.connect('activate',
Lang.bind(this, function() {
this.close(global.get_current_time());
this.close();
}));
this._applicationList.add(item.actor, { x_fill: true });
this._stopTimer();
@ -488,5 +511,9 @@ const EndSessionDialog = new Lang.Class({
invocation.return_value(null);
this.disconnect(signalId);
}));
},
Close: function(parameters, invocation) {
this.close();
}
});

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

@ -215,7 +215,7 @@ const InstallExtensionDialog = new Lang.Class({
},
_onCancelButtonPressed: function(button, event) {
this.close(global.get_current_time());
this.close();
this._invocation.return_value(GLib.Variant.new('(s)', ['cancelled']));
},
@ -257,7 +257,7 @@ const InstallExtensionDialog = new Lang.Class({
gotExtensionZipFile(session, message, uuid, dir, callback, errback);
}));
this.close(global.get_current_time());
this.close();
}
});

View File

@ -106,11 +106,15 @@ function enableExtension(uuid) {
extensionOrder.push(uuid);
let stylesheetFile = extension.dir.get_child('stylesheet.css');
if (stylesheetFile.query_exists(null)) {
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
theme.load_stylesheet(stylesheetFile.get_path());
extension.stylesheet = stylesheetFile;
let stylesheetNames = [global.session_mode + '.css', 'stylesheet.css'];
for (let i = 0; i < stylesheetNames.length; i++) {
let stylesheetFile = extension.dir.get_child(stylesheetNames[i]);
if (stylesheetFile.query_exists(null)) {
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
theme.load_stylesheet(stylesheetFile.get_path());
extension.stylesheet = stylesheetFile;
break;
}
}
extension.stateObj.enable();
@ -129,6 +133,7 @@ function logExtensionError(uuid, error) {
extension.state = ExtensionState.ERROR;
if (!extension.errors)
extension.errors = [];
extension.errors.push(message);
log('Extension "%s" had error: %s'.format(uuid, message));
_signals.emit('extension-state-changed', { uuid: uuid,
@ -287,7 +292,7 @@ function disableAllExtensions() {
return;
if (initted) {
enabledExtensions.forEach(function(uuid) {
extensionOrder.slice().reverse().forEach(function(uuid) {
disableExtension(uuid);
});
}

View File

@ -1,45 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const FLASHSPOT_ANIMATION_TIME = 0.25; // seconds
const Flashspot = new Lang.Class({
Name: 'Flashspot',
Extends: Lightbox.Lightbox,
_init: function(area) {
this.parent(Main.uiGroup, { inhibitEvents: true,
width: area.width,
height: area.height });
this.actor.style_class = 'flashspot';
this.actor.set_position(area.x, area.y);
},
fire: function() {
this.actor.opacity = 0;
Tweener.addTween(this.actor,
{ opacity: 255,
time: FLASHSPOT_ANIMATION_TIME,
transition: 'linear',
onComplete: Lang.bind(this, this._onFireShowComplete)
});
this.actor.show();
},
_onFireShowComplete: function() {
Tweener.addTween(this.actor,
{ opacity: 0,
time: FLASHSPOT_ANIMATION_TIME,
transition: 'linear',
onComplete: Lang.bind(this, function() {
this.destroy();
})
});
}
});

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gtk = imports.gi.Gtk;
@ -10,17 +10,6 @@ const St = imports.gi.St;
const Main = imports.ui.main;
const Params = imports.misc.params;
function _navigateActor(actor) {
if (!actor)
return;
let needsGrab = true;
if (actor instanceof St.Widget)
needsGrab = !actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
if (needsGrab)
actor.grab_key_focus();
}
// GrabHelper:
// @owner: the actor that owns the GrabHelper
// @params: optional parameters to pass to Main.pushModal()
@ -43,12 +32,9 @@ const GrabHelper = new Lang.Class({
this._actors = [];
this._capturedEventId = 0;
this._keyFocusNotifyId = 0;
this._focusWindowChangedId = 0;
this._ignoreRelease = false;
this._modalCount = 0;
this._grabFocusCount = 0;
},
// addActor:
@ -77,7 +63,7 @@ const GrabHelper = new Lang.Class({
},
_isWithinGrabbedActor: function(actor) {
let currentActor = this.currentGrab.actor;
let currentActor = this.currentGrab.actor;
while (actor) {
if (this._actors.indexOf(actor) != -1)
return true;
@ -128,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 });
@ -172,18 +156,18 @@ const GrabHelper = new Lang.Class({
params.savedFocus = focus;
if (params.modal && !this._takeModalGrab())
if (!this._takeModalGrab())
return false;
if (params.grabFocus && !this._takeFocusGrab(hadFocus))
return false;
if (params.focus)
params.focus.grab_key_focus();
else if (hadFocus || params.grabFocus)
_navigateActor(newFocus);
this._grabStack.push(params);
if (params.focus) {
params.focus.grab_key_focus();
} else if (newFocus && hadFocus) {
if (!newFocus.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false))
newFocus.grab_key_focus();
}
return true;
},
@ -205,63 +189,15 @@ const GrabHelper = new Lang.Class({
if (this._modalCount > 0)
return;
if (this._capturedEventId > 0) {
global.stage.disconnect(this._capturedEventId);
this._capturedEventId = 0;
}
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.NONREACTIVE ||
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._focusWindowChanged > 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.get_current_time());
},
// ignoreRelease:
//
// Make sure that the next button release event evaluated by the
@ -275,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 });
@ -301,21 +241,25 @@ const GrabHelper = new Lang.Class({
if (poppedGrab.onUngrab)
poppedGrab.onUngrab(params.isUser);
if (poppedGrab.modal)
this._releaseModalGrab();
if (poppedGrab.grabFocus)
this._releaseFocusGrab();
this._releaseModalGrab();
}
if (hadFocus) {
let poppedGrab = poppedGrabs[0];
_navigateActor(poppedGrab.savedFocus);
if (poppedGrab.savedFocus)
poppedGrab.savedFocus.grab_key_focus();
}
},
_onCapturedEvent: function(actor, event) {
let type = event.type();
if (type == Clutter.EventType.KEY_PRESS &&
event.get_key_symbol() == Clutter.KEY_Escape) {
this.ungrab({ isUser: true });
return true;
}
let press = type == Clutter.EventType.BUTTON_PRESS;
let release = type == Clutter.EventType.BUTTON_RELEASE;
let button = press || release;
@ -325,15 +269,6 @@ const GrabHelper = new Lang.Class({
return true;
}
if (!button && this._modalCount == 0)
return false;
if (type == Clutter.EventType.KEY_PRESS &&
event.get_key_symbol() == Clutter.KEY_Escape) {
this.ungrab({ isUser: true });
return true;
}
if (this._isWithinGrabbedActor(event.get_source()))
return false;
@ -347,20 +282,9 @@ const GrabHelper = new Lang.Class({
this._ignoreRelease = true;
let i = this._actorInGrabStack(event.get_source()) + 1;
this.ungrab({ actor: this._grabStack[i].actor, isUser: true });
return true;
}
return this._modalCount > 0;
return true;
},
_onKeyFocusChanged: function() {
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

@ -3,107 +3,149 @@
const Clutter = imports.gi.Clutter;
const IBus = imports.gi.IBus;
const Lang = imports.lang;
const Signals = imports.signals;
const St = imports.gi.St;
const BoxPointer = imports.ui.boxpointer;
const Main = imports.ui.main;
const PopupMenu = imports.ui.popupMenu;
const MAX_CANDIDATES_PER_PAGE = 16;
const CandidateArea = new Lang.Class({
Name: 'CandidateArea',
Extends: PopupMenu.PopupBaseMenuItem,
_init: function() {
this.parent({ reactive: false });
// St.Table exhibits some sizing problems so let's go with a
// clutter layout manager for now.
this._table = new Clutter.Actor();
this.addActor(this._table);
this._tableLayout = new Clutter.TableLayout();
this._table.set_layout_manager(this._tableLayout);
this._indexLabels = [];
this._candidateLabels = [];
this.actor = new St.BoxLayout({ vertical: true,
visible: false });
this._candidateBoxes = [];
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
this._indexLabels.push(new St.Label({ style_class: 'candidate-index' }));
this._candidateLabels.push(new St.Label({ style_class: 'candidate-label' }));
let box = new St.BoxLayout({ style_class: 'candidate-box',
reactive: true,
track_hover: true });
box._indexLabel = new St.Label({ style_class: 'candidate-index' });
box._candidateLabel = new St.Label({ style_class: 'candidate-label' });
box.add(box._indexLabel, { y_fill: false });
box.add(box._candidateLabel, { y_fill: false });
this._candidateBoxes.push(box);
this.actor.add(box);
let j = i;
box.connect('button-release-event', Lang.bind(this, function(actor, event) {
this.emit('candidate-clicked', j, event.get_button(), event.get_state());
}));
}
this._buttonBox = new St.BoxLayout({ style_class: 'candidate-page-button-box' });
this._previousButton = new St.Button({ style_class: 'candidate-page-button candidate-page-button-previous' });
this._previousButton.child = new St.Icon({ style_class: 'candidate-page-button-icon' });
this._buttonBox.add(this._previousButton, { expand: true });
this._nextButton = new St.Button({ style_class: 'candidate-page-button candidate-page-button-next' });
this._nextButton.child = new St.Icon({ style_class: 'candidate-page-button-icon' });
this._buttonBox.add(this._nextButton, { expand: true });
this.actor.add(this._buttonBox);
this._previousButton.connect('clicked', Lang.bind(this, function() {
this.emit('previous-page');
}));
this._nextButton.connect('clicked', Lang.bind(this, function() {
this.emit('next-page');
}));
this._orientation = -1;
this._cursorPosition = 0;
},
_setOrientation: function(orientation) {
setOrientation: function(orientation) {
if (this._orientation == orientation)
return;
this._orientation = orientation;
this._table.remove_all_children();
if (this._orientation == IBus.Orientation.HORIZONTAL)
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
this._tableLayout.pack(this._indexLabels[i], i*2, 0);
this._tableLayout.pack(this._candidateLabels[i], i*2 + 1, 0);
}
else // VERTICAL || SYSTEM
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
this._tableLayout.pack(this._indexLabels[i], 0, i);
this._tableLayout.pack(this._candidateLabels[i], 1, i);
}
if (this._orientation == IBus.Orientation.HORIZONTAL) {
this.actor.vertical = false;
this.actor.remove_style_class_name('vertical');
this.actor.add_style_class_name('horizontal');
this._previousButton.child.icon_name = 'go-previous-symbolic';
this._nextButton.child.icon_name = 'go-next-symbolic';
} else { // VERTICAL || SYSTEM
this.actor.vertical = true;
this.actor.add_style_class_name('vertical');
this.actor.remove_style_class_name('horizontal');
this._previousButton.child.icon_name = 'go-up-symbolic';
this._nextButton.child.icon_name = 'go-down-symbolic';
}
},
setCandidates: function(indexes, candidates, orientation, cursorPosition, cursorVisible) {
this._setOrientation(orientation);
setCandidates: function(indexes, candidates, cursorPosition, cursorVisible) {
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
let visible = i < candidates.length;
this._indexLabels[i].visible = visible;
this._candidateLabels[i].visible = visible;
let box = this._candidateBoxes[i];
box.visible = visible;
if (!visible)
continue;
this._indexLabels[i].text = ((indexes && indexes[i]) ? indexes[i] : '%x.'.format(i + 1));
this._candidateLabels[i].text = candidates[i];
box._indexLabel.text = ((indexes && indexes[i]) ? indexes[i] : '%x'.format(i + 1));
box._candidateLabel.text = candidates[i];
}
this._candidateLabels[this._cursorPosition].remove_style_pseudo_class('selected');
this._candidateBoxes[this._cursorPosition].remove_style_pseudo_class('selected');
this._cursorPosition = cursorPosition;
if (cursorVisible)
this._candidateLabels[cursorPosition].add_style_pseudo_class('selected');
this._candidateBoxes[cursorPosition].add_style_pseudo_class('selected');
},
updateButtons: function(wrapsAround, page, nPages) {
if (nPages < 2) {
this._buttonBox.hide();
return;
}
this._buttonBox.show();
this._previousButton.reactive = wrapsAround || page > 0;
this._nextButton.reactive = wrapsAround || page < nPages - 1;
},
});
Signals.addSignalMethods(CandidateArea.prototype);
const CandidatePopup = new Lang.Class({
Name: 'CandidatePopup',
Extends: PopupMenu.PopupMenu,
_init: function() {
this._cursor = new St.Bin({ opacity: 0 });
Main.uiGroup.add_actor(this._cursor);
this.parent(this._cursor, 0, St.Side.TOP);
this.actor.hide();
Main.uiGroup.add_actor(this.actor);
this._boxPointer = new BoxPointer.BoxPointer(St.Side.TOP);
this._boxPointer.actor.visible = false;
this._boxPointer.actor.style_class = 'candidate-popup-boxpointer';
Main.layoutManager.addChrome(this._boxPointer.actor);
this._preeditTextItem = new PopupMenu.PopupMenuItem('', { reactive: false });
this._preeditTextItem.actor.hide();
this.addMenuItem(this._preeditTextItem);
let box = new St.BoxLayout({ style_class: 'candidate-popup-content',
vertical: true });
this._boxPointer.bin.set_child(box);
this._auxTextItem = new PopupMenu.PopupMenuItem('', { reactive: false });
this._auxTextItem.actor.hide();
this.addMenuItem(this._auxTextItem);
this._preeditText = new St.Label({ style_class: 'candidate-popup-text',
visible: false });
box.add(this._preeditText);
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._auxText = new St.Label({ style_class: 'candidate-popup-text',
visible: false });
box.add(this._auxText);
this._lookupTableItem = new CandidateArea();
this._lookupTableItem.actor.hide();
this.addMenuItem(this._lookupTableItem);
this._candidateArea = new CandidateArea();
box.add(this._candidateArea.actor);
this._candidateArea.connect('previous-page', Lang.bind(this, function() {
this._panelService.page_up();
}));
this._candidateArea.connect('next-page', Lang.bind(this, function() {
this._panelService.page_down();
}));
this._candidateArea.connect('candidate-clicked', Lang.bind(this, function(ca, index, button, state) {
this._panelService.candidate_clicked(index, button, state);
}));
this._panelService = null;
},
@ -117,66 +159,61 @@ const CandidatePopup = new Lang.Class({
Lang.bind(this, function(ps, x, y, w, h) {
this._cursor.set_position(x, y);
this._cursor.set_size(w, h);
if (this._boxPointer.actor.visible)
this._boxPointer.setPosition(this._cursor, 0);
}));
panelService.connect('update-preedit-text',
Lang.bind(this, function(ps, text, cursorPosition, visible) {
if (visible)
this._preeditTextItem.actor.show();
else
this._preeditTextItem.actor.hide();
this._preeditText.visible = visible;
this._updateVisibility();
this._preeditTextItem.actor.label_actor.text = text.get_text();
this._preeditText.text = text.get_text();
let attrs = text.get_attributes();
if (attrs)
this._setTextAttributes(this._preeditTextItem.actor.label_actor.clutter_text,
this._setTextAttributes(this._preeditText.clutter_text,
attrs);
}));
panelService.connect('show-preedit-text',
Lang.bind(this, function(ps) {
this._preeditTextItem.actor.show();
this._preeditText.show();
this._updateVisibility();
}));
panelService.connect('hide-preedit-text',
Lang.bind(this, function(ps) {
this._preeditTextItem.actor.hide();
this._preeditText.hide();
this._updateVisibility();
}));
panelService.connect('update-auxiliary-text',
Lang.bind(this, function(ps, text, visible) {
if (visible)
this._auxTextItem.actor.show();
else
this._auxTextItem.actor.hide();
this._auxText.visible = visible;
this._updateVisibility();
this._auxTextItem.actor.label_actor.text = text.get_text();
this._auxText.text = text.get_text();
}));
panelService.connect('show-auxiliary-text',
Lang.bind(this, function(ps) {
this._auxTextItem.actor.show();
this._auxText.show();
this._updateVisibility();
}));
panelService.connect('hide-auxiliary-text',
Lang.bind(this, function(ps) {
this._auxTextItem.actor.hide();
this._auxText.hide();
this._updateVisibility();
}));
panelService.connect('update-lookup-table',
Lang.bind(this, function(ps, lookupTable, visible) {
if (visible)
this._lookupTableItem.actor.show();
else
this._lookupTableItem.actor.hide();
this._candidateArea.actor.visible = visible;
this._updateVisibility();
let nCandidates = lookupTable.get_number_of_candidates();
let cursorPos = lookupTable.get_cursor_pos();
let pageSize = lookupTable.get_page_size();
let nPages = Math.ceil(nCandidates / pageSize);
let page = ((cursorPos == 0) ? 0 : Math.floor(cursorPos / pageSize));
let startIndex = page * pageSize;
let endIndex = Math.min((page + 1) * pageSize,
lookupTable.get_number_of_candidates());
let endIndex = Math.min((page + 1) * pageSize, nCandidates);
let indexes = [];
let indexLabel;
for (let i = 0; indexLabel = lookupTable.get_label(i); ++i)
@ -186,37 +223,41 @@ const CandidatePopup = new Lang.Class({
for (let i = startIndex; i < endIndex; ++i)
candidates.push(lookupTable.get_candidate(i).get_text());
this._lookupTableItem.setCandidates(indexes,
candidates,
lookupTable.get_orientation(),
cursorPos % pageSize,
lookupTable.is_cursor_visible());
this._candidateArea.setCandidates(indexes,
candidates,
cursorPos % pageSize,
lookupTable.is_cursor_visible());
this._candidateArea.setOrientation(lookupTable.get_orientation());
this._candidateArea.updateButtons(lookupTable.is_round(), page, nPages);
}));
panelService.connect('show-lookup-table',
Lang.bind(this, function(ps) {
this._lookupTableItem.actor.show();
this._candidateArea.actor.show();
this._updateVisibility();
}));
panelService.connect('hide-lookup-table',
Lang.bind(this, function(ps) {
this._lookupTableItem.actor.hide();
this._candidateArea.actor.hide();
this._updateVisibility();
}));
panelService.connect('focus-out',
Lang.bind(this, function(ps) {
this.close(BoxPointer.PopupAnimation.NONE);
this._boxPointer.hide(BoxPointer.PopupAnimation.NONE);
}));
},
_updateVisibility: function() {
let isVisible = (this._preeditTextItem.actor.visible ||
this._auxTextItem.actor.visible ||
this._lookupTableItem.actor.visible);
let isVisible = (this._preeditText.visible ||
this._auxText.visible ||
this._candidateArea.actor.visible);
if (isVisible)
this.open(BoxPointer.PopupAnimation.NONE);
else
this.close(BoxPointer.PopupAnimation.NONE);
if (isVisible) {
this._boxPointer.setPosition(this._cursor, 0);
this._boxPointer.show(BoxPointer.PopupAnimation.NONE);
this._boxPointer.actor.raise_top();
} else {
this._boxPointer.hide(BoxPointer.PopupAnimation.NONE);
}
},
_setTextAttributes: function(clutterText, ibusAttrList) {

View File

@ -23,6 +23,8 @@ const BaseIcon = new Lang.Class({
this.actor._delegate = this;
this.actor.connect('style-changed',
Lang.bind(this, this._onStyleChanged));
this.actor.connect('destroy',
Lang.bind(this, this._onDestroy));
this._spacing = 0;
@ -52,6 +54,9 @@ const BaseIcon = new Lang.Class({
this._setSizeManually = params.setSizeManually;
this.icon = null;
let cache = St.TextureCache.get_default();
this._iconThemeChangedId = cache.connect('icon-theme-changed', Lang.bind(this, this._onIconThemeChanged));
},
_allocate: function(actor, box, flags) {
@ -146,7 +151,22 @@ const BaseIcon = new Lang.Class({
size = found ? len : ICON_SIZE;
}
if (this.iconSize == size && this._iconBin.child)
return;
this._createIconTexture(size);
},
_onDestroy: function() {
if (this._iconThemeChangedId > 0) {
let cache = St.TextureCache.get_default();
cache.disconnect(this._iconThemeChangedId);
this._iconThemeChangedId = 0;
}
},
_onIconThemeChanged: function() {
this._createIconTexture(this.iconSize);
}
});
@ -156,13 +176,16 @@ const IconGrid = new Lang.Class({
_init: function(params) {
params = Params.parse(params, { rowLimit: null,
columnLimit: null,
fillParent: false,
xAlign: St.Align.MIDDLE });
this._rowLimit = params.rowLimit;
this._colLimit = params.columnLimit;
this._xAlign = params.xAlign;
this._fillParent = params.fillParent;
this.actor = new St.BoxLayout({ style_class: 'icon-grid',
vertical: true });
// Pulled from CSS, but hardcode some defaults here
this._spacing = 0;
this._hItemSize = this._vItemSize = ICON_SIZE;
@ -176,6 +199,11 @@ const IconGrid = new Lang.Class({
},
_getPreferredWidth: function (grid, forHeight, alloc) {
if (this._fillParent)
// Ignore all size requests of children and request a size of 0;
// later we'll allocate as many children as fit the parent
return;
let children = this._grid.get_children();
let nColumns = this._colLimit ? Math.min(this._colLimit,
children.length)
@ -197,12 +225,20 @@ const IconGrid = new Lang.Class({
},
_getPreferredHeight: function (grid, forWidth, alloc) {
if (this._fillParent)
// Ignore all size requests of children and request a size of 0;
// later we'll allocate as many children as fit the parent
return;
let children = this._getVisibleChildren();
let nColumns;
if (forWidth < 0)
let nColumns, spacing;
if (forWidth < 0) {
nColumns = children.length;
else
nColumns = this._computeLayout(forWidth)[0];
spacing = this._spacing;
} else {
[nColumns, , spacing] = this._computeLayout(forWidth);
}
let nRows;
if (nColumns > 0)
nRows = Math.ceil(children.length / nColumns);
@ -210,18 +246,25 @@ const IconGrid = new Lang.Class({
nRows = 0;
if (this._rowLimit)
nRows = Math.min(nRows, this._rowLimit);
let totalSpacing = Math.max(0, nRows - 1) * this._spacing;
let totalSpacing = Math.max(0, nRows - 1) * spacing;
let height = nRows * this._vItemSize + totalSpacing;
alloc.min_size = height;
alloc.natural_size = height;
},
_allocate: function (grid, box, flags) {
if (this._fillParent) {
// Reset the passed in box to fill the parent
let parentBox = this.actor.get_parent().allocation;
let gridBox = this.actor.get_theme_node().get_content_box(parentBox);
box = this._grid.get_theme_node().get_content_box(gridBox);
}
let children = this._getVisibleChildren();
let availWidth = box.x2 - box.x1;
let availHeight = box.y2 - box.y1;
let [nColumns, usedWidth] = this._computeLayout(availWidth);
let [nColumns, usedWidth, spacing] = this._computeLayout(availWidth);
let leftPadding;
switch(this._xAlign) {
@ -260,7 +303,8 @@ const IconGrid = new Lang.Class({
childBox.x2 = childBox.x1 + width;
childBox.y2 = childBox.y1 + height;
if (this._rowLimit && rowIndex >= this._rowLimit) {
if (this._rowLimit && rowIndex >= this._rowLimit ||
this._fillParent && childBox.y2 > availHeight) {
this._grid.set_skip_paint(children[i], true);
} else {
children[i].allocate(childBox, flags);
@ -274,10 +318,10 @@ const IconGrid = new Lang.Class({
}
if (columnIndex == 0) {
y += this._vItemSize + this._spacing;
y += this._vItemSize + spacing;
x = box.x1 + leftPadding;
} else {
x += this._hItemSize + this._spacing;
x += this._hItemSize + spacing;
}
}
},
@ -293,16 +337,25 @@ const IconGrid = new Lang.Class({
_computeLayout: function (forWidth) {
let nColumns = 0;
let usedWidth = 0;
let spacing = this._spacing;
if (this._colLimit) {
let itemWidth = this._hItemSize * this._colLimit;
let emptyArea = forWidth - itemWidth;
spacing = Math.max(this._spacing, emptyArea / (2 * this._colLimit));
spacing = Math.round(spacing);
}
while ((this._colLimit == null || nColumns < this._colLimit) &&
(usedWidth + this._hItemSize <= forWidth)) {
usedWidth += this._hItemSize + this._spacing;
usedWidth += this._hItemSize + spacing;
nColumns += 1;
}
if (nColumns > 0)
usedWidth -= this._spacing;
usedWidth -= spacing;
return [nColumns, usedWidth];
return [nColumns, usedWidth, spacing];
},
_onStyleChanged: function() {

View File

@ -2,18 +2,21 @@
const Caribou = imports.gi.Caribou;
const Clutter = imports.gi.Clutter;
const DBus = imports.dbus;
const Gdk = imports.gi.Gdk;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
const BoxPointer = imports.ui.boxpointer;
const Layout = imports.ui.layout;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const KEYBOARD_REST_TIME = Layout.KEYBOARD_ANIMATION_TIME * 2 * 1000;
const KEYBOARD_SCHEMA = 'org.gnome.shell.keyboard';
const KEYBOARD_TYPE = 'keyboard-type';
@ -56,14 +59,7 @@ const Key = new Lang.Class({
if (this._key.name == 'Control_L' || this._key.name == 'Alt_L')
this._key.latch = true;
this._key.connect('key-pressed', Lang.bind(this, function ()
{ this.actor.checked = true }));
this._key.connect('key-released', Lang.bind(this, function ()
{ this.actor.checked = false; }));
if (this._extended_keys.length > 0) {
this._grabbed = false;
this._eventCaptureId = 0;
this._key.connect('notify::show-subkeys', Lang.bind(this, this._onShowSubkeysChanged));
this._boxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
{ x_fill: true,
@ -115,52 +111,23 @@ const Key = new Lang.Class({
this._boxPointer.bin.add_actor(this._extended_keyboard);
},
_onEventCapture: function (actor, event) {
let source = event.get_source();
let type = event.type();
if ((type == Clutter.EventType.BUTTON_PRESS ||
type == Clutter.EventType.BUTTON_RELEASE) &&
this._extended_keyboard.contains(source)) {
source.extended_key.press();
source.extended_key.release();
return false;
}
if (type == Clutter.EventType.BUTTON_PRESS) {
this._boxPointer.actor.hide();
this._ungrab();
return true;
}
return false;
},
_ungrab: function () {
global.stage.disconnect(this._eventCaptureId);
this._eventCaptureId = 0;
this._grabbed = false;
Main.popModal(this.actor);
get subkeys() {
return this._boxPointer;
},
_onShowSubkeysChanged: function () {
if (this._key.show_subkeys) {
this.actor.fake_release();
this._boxPointer.actor.raise_top();
this._boxPointer.setPosition(this.actor, 0.5);
this._boxPointer.show(BoxPointer.PopupAnimation.FULL);
this.emit('show-subkeys');
this.actor.fake_release();
this.actor.set_hover(false);
if (!this._grabbed) {
Main.pushModal(this.actor);
this._eventCaptureId = global.stage.connect('captured-event', Lang.bind(this, this._onEventCapture));
this._grabbed = true;
}
this._key.release();
} else {
if (this._grabbed)
this._ungrab();
this._boxPointer.hide(BoxPointer.PopupAnimation.FULL);
this.emit('hide-subkeys');
}
}
});
Signals.addSignalMethods(Key.prototype);
const Keyboard = new Lang.Class({
// HACK: we can't set Name, because it collides with Name dbus property
@ -175,16 +142,26 @@ const Keyboard = new Lang.Class({
this._focusInExtendedKeys = false;
this._timestamp = global.display.get_current_time_roundtrip();
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
this._keyboardSettings = new Gio.Settings({ schema: KEYBOARD_SCHEMA });
this._keyboardSettings.connect('changed', Lang.bind(this, this._settingsChanged));
this._a11yApplicationsSettings = new Gio.Settings({ schema: A11Y_APPLICATIONS_SCHEMA });
this._a11yApplicationsSettings.connect('changed', Lang.bind(this, this._settingsChanged));
this._settingsChanged();
},
init: function () {
this._showIdleId = 0;
this._subkeysBoxPointer = null;
this._capturedEventId = 0;
this._capturedPress = false;
this._keyboardVisible = false;
Main.layoutManager.connect('keyboard-visible-changed', Lang.bind(this, function(o, visible) {
this._keyboardVisible = visible;
}));
this._keyboardRequested = false;
this._keyboardRestingId = 0;
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
this._redraw();
},
@ -199,25 +176,19 @@ const Keyboard = new Lang.Class({
if (this._keyboard)
this._destroyKeyboard();
if (this._enableKeyboard) {
// If we've been called because the setting actually just
// changed to true (as opposed to being called from
// this._init()), then we want to pop up the keyboard.
let showKeyboard = (settings != null);
// However, caribou-gtk-module or this._onKeyFocusChanged
// will probably immediately tell us to hide it, so we
// have to fake things out so we'll ignore that request.
if (showKeyboard)
this._timestamp = global.display.get_current_time_roundtrip() + 1;
this._setupKeyboard(showKeyboard);
} else
if (this._enableKeyboard)
this._setupKeyboard();
else
Main.layoutManager.hideKeyboard(true);
},
_destroyKeyboard: function() {
if (this._keyboardNotifyId)
this._keyboard.disconnect(this._keyboardNotifyId);
if (this._keyboardGroupAddedId)
this._keyboard.disconnect(this._keyboardGroupAddedId);
if (this._keyboardGroupRemovedId)
this._keyboard.disconnect(this._keyboardGroupRemovedId);
if (this._focusNotifyId)
global.stage.disconnect(this._focusNotifyId);
this._keyboard = null;
@ -227,7 +198,7 @@ const Keyboard = new Lang.Class({
this._destroySource();
},
_setupKeyboard: function(show) {
_setupKeyboard: function() {
this.actor = new St.BoxLayout({ name: 'keyboard', vertical: true, reactive: true });
Main.layoutManager.keyboardBox.add_actor(this.actor);
Main.layoutManager.trackChrome(this.actor);
@ -248,12 +219,11 @@ const Keyboard = new Lang.Class({
this.actor.text_direction = Clutter.TextDirection.LTR;
this._keyboardNotifyId = this._keyboard.connect('notify::active-group', Lang.bind(this, this._onGroupChanged));
this._keyboardGroupAddedId = this._keyboard.connect('group-added', Lang.bind(this, this._onGroupAdded));
this._keyboardGroupRemovedId = this._keyboard.connect('group-removed', Lang.bind(this, this._onGroupRemoved));
this._focusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
if (show)
this.show(Main.layoutManager.focusIndex);
else
this._createSource();
this._createSource();
},
_onKeyFocusChanged: function () {
@ -273,88 +243,102 @@ const Keyboard = new Lang.Class({
return;
let time = global.get_current_time();
if (focus instanceof Clutter.Text)
this.Show(time);
else
if (!(focus instanceof Clutter.Text)) {
this.Hide(time);
return;
}
if (!this._showIdleId)
this._showIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE,
Lang.bind(this, function() { this.Show(time); }));
},
_createLayersForGroup: function (gname) {
let group = this._keyboard.get_group(gname);
group.connect('notify::active-level', Lang.bind(this, this._onLevelChanged));
let layers = {};
let levels = group.get_levels();
for (let j = 0; j < levels.length; ++j) {
let lname = levels[j];
let level = group.get_level(lname);
let layout = new St.BoxLayout({ style_class: 'keyboard-layout',
vertical: true });
this._loadRows(level, layout);
layers[lname] = layout;
this.actor.add(layout, { x_fill: false });
layout.hide();
}
return layers;
},
_addKeys: function () {
let groups = this._keyboard.get_groups();
for (let i = 0; i < groups.length; ++i) {
let gname = groups[i];
let group = this._keyboard.get_group(gname);
group.connect('notify::active-level', Lang.bind(this, this._onLevelChanged));
let layers = {};
let levels = group.get_levels();
for (let j = 0; j < levels.length; ++j) {
let lname = levels[j];
let level = group.get_level(lname);
let layout = new St.BoxLayout({ style_class: 'keyboard-layout',
vertical: true });
this._loadRows(level, layout);
layers[lname] = layout;
this.actor.add(layout, { x_fill: false });
layout.hide();
}
this._groups[gname] = layers;
this._groups[gname] = this._createLayersForGroup(gname);
}
this._setActiveLayer();
},
_getTrayIcon: function () {
let trayButton = new St.Button ({ label: _("tray"),
style_class: 'keyboard-key' });
trayButton.key_width = 1;
trayButton.connect('button-press-event', Lang.bind(this, function () {
Main.messageTray.toggle();
}));
_onCapturedEvent: function(actor, event) {
let type = event.type();
let press = type == Clutter.EventType.BUTTON_PRESS;
let release = type == Clutter.EventType.BUTTON_RELEASE;
Main.overview.connect('showing', Lang.bind(this, function () {
trayButton.reactive = false;
trayButton.add_style_pseudo_class('grayed');
}));
Main.overview.connect('hiding', Lang.bind(this, function () {
trayButton.reactive = true;
trayButton.remove_style_pseudo_class('grayed');
}));
Main.sessionMode.connect('updated', Lang.bind(this, function() {
trayButton.reactive = !Main.sessionMode.isLocked;
if (Main.sessionMode.isLocked)
trayButton.add_style_pseudo_class('grayed');
else
trayButton.remove_style_pseudo_class('grayed');
}));
if (press)
this._capturedPress = true;
else if (release && this._capturedPress)
this._hideSubkeys();
return trayButton;
return true;
},
_addRows : function (keys, layout) {
let keyboard_row = new St.BoxLayout();
for (let i = 0; i < keys.length; ++i) {
let children = keys[i].get_children();
let right_box = new St.BoxLayout({ style_class: 'keyboard-row' });
let left_box = new St.BoxLayout({ style_class: 'keyboard-row' });
let center_box = new St.BoxLayout({ style_class: 'keyboard-row' });
let right_box = new St.BoxLayout({ style_class: 'keyboard-row' });
for (let j = 0; j < children.length; ++j) {
if (this._numOfHorizKeys == 0)
this._numOfHorizKeys = children.length;
let key = children[j];
let button = new Key(key);
if (key.align == 'right')
switch (key.align) {
case 'right':
right_box.add(button.actor);
else
break;
case 'center':
center_box.add(button.actor);
break;
case 'left':
default:
left_box.add(button.actor);
break;
}
if (key.name == 'Caribou_Prefs') {
key.connect('key-released', Lang.bind(this, this.hide));
// Add new key for hiding message tray
right_box.add(this._getTrayIcon());
}
button.connect('show-subkeys', Lang.bind(this, function() {
if (this._subkeysBoxPointer)
this._subkeysBoxPointer.hide(BoxPointer.PopupAnimation.FULL);
this._subkeysBoxPointer = button.subkeys;
this._subkeysBoxPointer.show(BoxPointer.PopupAnimation.FULL);
if (!this._capturedEventId)
this._capturedEventId = this.actor.connect('captured-event',
Lang.bind(this, this._onCapturedEvent));
}));
button.connect('hide-subkeys', Lang.bind(this, function() {
this._hideSubkeys();
}));
}
keyboard_row.add(left_box, { expand: true, x_fill: false, x_align: St.Align.START });
keyboard_row.add(center_box, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
keyboard_row.add(right_box, { expand: true, x_fill: false, x_align: St.Align.END });
}
layout.add(keyboard_row);
@ -427,6 +411,14 @@ const Keyboard = new Lang.Class({
this._redraw();
},
_onGroupAdded: function (keyboard, gname) {
this._groups[gname] = this._createLayersForGroup(gname);
},
_onGroupRemoved: function (keyboard, gname) {
delete this._groups[gname];
},
_setActiveLayer: function () {
let active_group_name = this._keyboard.active_group;
let active_group = this._keyboard.get_group(active_group_name);
@ -462,7 +454,37 @@ const Keyboard = new Lang.Class({
actor._extended_keys || actor.extended_key;
},
_clearKeyboardRestTimer: function() {
if (!this._keyboardRestingId)
return;
GLib.source_remove(this._keyboardRestingId);
this._keyboardRestingId = 0;
},
show: function (monitor) {
this._keyboardRequested = true;
if (this._keyboardVisible) {
if (monitor != Main.layoutManager.keyboardIndex) {
Main.layoutManager.keyboardIndex = monitor;
this._redraw();
}
return;
}
this._clearKeyboardRestTimer();
this._keyboardRestingId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
KEYBOARD_REST_TIME,
Lang.bind(this, function() {
this._clearKeyboardRestTimer();
this._show(monitor);
}));
},
_show: function(monitor) {
if (!this._keyboardRequested)
return;
Main.layoutManager.keyboardIndex = monitor;
this._redraw();
Main.layoutManager.showKeyboard();
@ -470,10 +492,41 @@ const Keyboard = new Lang.Class({
},
hide: function () {
this._keyboardRequested = false;
if (!this._keyboardVisible)
return;
this._clearKeyboardRestTimer();
this._keyboardRestingId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
KEYBOARD_REST_TIME,
Lang.bind(this, function() {
this._clearKeyboardRestTimer();
this._hide();
}));
},
_hide: function() {
if (this._keyboardRequested)
return;
this._hideSubkeys();
Main.layoutManager.hideKeyboard();
this._createSource();
},
_hideSubkeys: function() {
if (this._subkeysBoxPointer) {
this._subkeysBoxPointer.hide(BoxPointer.PopupAnimation.FULL);
this._subkeysBoxPointer = null;
}
if (this._capturedEventId) {
this.actor.disconnect(this._capturedEventId);
this._capturedEventId = 0;
}
this._capturedPress = false;
},
_moveTemporarily: function () {
let currentWindow = global.screen.get_display().focus_window;
let rect = currentWindow.get_outer_rect();
@ -502,6 +555,13 @@ const Keyboard = new Lang.Class({
return one - two;
},
_clearShowIdle: function() {
if (!this._showIdleId)
return;
GLib.source_remove(this._showIdleId);
this._showIdleId = 0;
},
// D-Bus methods
Show: function(timestamp) {
if (!this._enableKeyboard)
@ -510,6 +570,8 @@ const Keyboard = new Lang.Class({
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
return;
this._clearShowIdle();
if (timestamp != Clutter.CURRENT_TIME)
this._timestamp = timestamp;
this.show(Main.layoutManager.focusIndex);
@ -522,6 +584,8 @@ const Keyboard = new Lang.Class({
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
return;
this._clearShowIdle();
if (timestamp != Clutter.CURRENT_TIME)
this._timestamp = timestamp;
this.hide();
@ -556,11 +620,7 @@ const KeyboardSource = new Lang.Class({
this.keepTrayOnSummaryClick = true;
},
handleSummaryClick: function() {
let event = Clutter.get_current_event();
if (event.type() != Clutter.EventType.BUTTON_RELEASE)
return false;
handleSummaryClick: function(button) {
this.open();
return true;
},

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,7 @@
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Signals = imports.signals;
const St = imports.gi.St;
const Params = imports.misc.params;
@ -101,6 +102,7 @@ const Lightbox = new Lang.Class({
},
show: function() {
Tweener.removeTweens(this.actor);
if (this._fadeInTime) {
this.shown = false;
this.actor.opacity = 0;
@ -110,17 +112,20 @@ const Lightbox = new Lang.Class({
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this.shown = true;
this.emit('shown');
})
});
} else {
this.actor.opacity = 255 * this._fadeFactor;
this.shown = true;
this.emit('shown');
}
this.actor.show();
},
hide: function() {
this.shown = false;
Tweener.removeTweens(this.actor);
if (this._fadeOutTime) {
Tweener.addTween(this.actor,
{ opacity: 0,
@ -197,3 +202,4 @@ const Lightbox = new Lang.Class({
this.highlight(null);
}
});
Signals.addSignalMethods(Lightbox.prototype);

View File

@ -39,6 +39,7 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
* in the shell core code too. */
'const stage = global.stage; ' +
/* Special lookingGlass functions */
'const inspect = Lang.bind(Main.lookingGlass, Main.lookingGlass.inspect); ' +
'const it = Main.lookingGlass.getIt(); ' +
'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ';
@ -307,10 +308,6 @@ const Result = new Lang.Class({
box.add(resultTxt);
let objLink = new ObjLink(this._lookingGlass, o);
box.add(objLink.actor);
let line = new Clutter.Rectangle({ name: 'Separator' });
let padBin = new St.Bin({ name: 'Separator', x_fill: true, y_fill: true });
padBin.add_actor(line);
this.actor.add(padBin);
}
});
@ -684,7 +681,8 @@ const Memory = new Lang.Class({
const Extensions = new Lang.Class({
Name: 'Extensions',
_init: function() {
_init: function(lookingGlass) {
this._lookingGlass = lookingGlass;
this.actor = new St.BoxLayout({ vertical: true,
name: 'lookingGlassExtensions' });
this._noExtensions = new St.Label({ style_class: 'lg-extensions-none',
@ -851,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',
@ -871,8 +870,7 @@ const LookingGlass = new Lang.Class({
inspectIcon.connect('button-press-event', Lang.bind(this, function () {
let inspector = new Inspector(this);
inspector.connect('target', Lang.bind(this, function(i, target, stageX, stageY) {
this._pushResult('<inspect x:' + stageX + ' y:' + stageY + '>',
target);
this._pushResult('inspect(' + Math.round(stageX) + ', ' + Math.round(stageY) + ')', target);
}));
inspector.connect('closed', Lang.bind(this, function() {
this.actor.show();
@ -912,7 +910,7 @@ const LookingGlass = new Lang.Class({
this._memory = new Memory();
notebook.appendPage('Memory', this._memory.actor);
this._extensions = new Extensions();
this._extensions = new Extensions(this);
notebook.appendPage('Extensions', this._extensions.actor);
this._entry.clutter_text.connect('activate', Lang.bind(this, function (o, e) {
@ -950,9 +948,7 @@ const LookingGlass = new Lang.Class({
_updateFont: function() {
let fontName = this._interfaceSettings.get_string('monospace-font-name');
// This is mishandled by the scanner - should by Pango.FontDescription_from_string(fontName);
// https://bugzilla.gnome.org/show_bug.cgi?id=595889
let fontDesc = Pango.font_description_from_string(fontName);
let fontDesc = Pango.FontDescription.from_string(fontName);
// We ignore everything but size and style; you'd be crazy to set your system-wide
// monospace font to be bold/oblique/etc. Could easily be added here.
this.actor.style =
@ -990,28 +986,18 @@ const LookingGlass = new Lang.Class({
_showCompletions: function(completions) {
if (!this._completionActor) {
let actor = new St.BoxLayout({ vertical: true });
this._completionText = new St.Label({ name: 'LookingGlassAutoCompletionText', style_class: 'lg-completions-text' });
this._completionText.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._completionText.clutter_text.line_wrap = true;
actor.add(this._completionText);
let line = new Clutter.Rectangle();
let padBin = new St.Bin({ x_fill: true, y_fill: true });
padBin.add_actor(line);
actor.add(padBin);
this._completionActor = actor;
this._completionActor = new St.Label({ name: 'LookingGlassAutoCompletionText', style_class: 'lg-completions-text' });
this._completionActor.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._completionActor.clutter_text.line_wrap = true;
this._evalBox.insert_child_below(this._completionActor, this._entryArea);
}
this._completionText.set_text(completions.join(', '));
this._completionActor.set_text(completions.join(', '));
// Setting the height to -1 allows us to get its actual preferred height rather than
// whatever was last given in set_height by Tweener.
this._completionActor.set_height(-1);
let [minHeight, naturalHeight] = this._completionText.get_preferred_height(this._resultsArea.get_width());
let [minHeight, naturalHeight] = this._completionActor.get_preferred_height(this._resultsArea.get_width());
// Don't reanimate if we are already visible
if (this._completionActor.visible) {
@ -1057,6 +1043,10 @@ const LookingGlass = new Lang.Class({
this._entry.text = '';
},
inspect: function(x, y) {
return global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
},
getIt: function () {
return this._it;
},
@ -1083,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;
@ -1129,7 +1119,7 @@ const LookingGlass = new Lang.Class({
if (this._open)
return;
if (!Main.pushModal(this._entry, { keybindingMode: Main.KeybindingMode.LOOKING_GLASS }))
if (!Main.pushModal(this._entry, { keybindingMode: Shell.KeyBindingMode.LOOKING_GLASS }))
return;
this._notebook.selectIndex(0);

View File

@ -18,11 +18,13 @@ const ExtensionSystem = imports.ui.extensionSystem;
const ExtensionDownloader = imports.ui.extensionDownloader;
const Keyboard = imports.ui.keyboard;
const MessageTray = imports.ui.messageTray;
const OsdWindow = imports.ui.osdWindow;
const Overview = imports.ui.overview;
const Panel = imports.ui.panel;
const Params = imports.misc.params;
const RunDialog = imports.ui.runDialog;
const Layout = imports.ui.layout;
const LoginManager = imports.misc.loginManager;
const LookingGlass = imports.ui.lookingGlass;
const NotificationDaemon = imports.ui.notificationDaemon;
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
@ -31,26 +33,15 @@ const Scripting = imports.ui.scripting;
const SessionMode = imports.ui.sessionMode;
const ShellDBus = imports.ui.shellDBus;
const ShellMountOperation = imports.ui.shellMountOperation;
const UnlockDialog = imports.ui.unlockDialog;
const WindowManager = imports.ui.windowManager;
const Magnifier = imports.ui.magnifier;
const XdndHandler = imports.ui.xdndHandler;
const Util = imports.misc.util;
const OVERRIDES_SCHEMA = 'org.gnome.shell.overrides';
const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
const KeybindingMode = {
NONE: 0, // block all keybindings
NORMAL: 1 << 0, // window mode
OVERVIEW: 1 << 1,
LOCK_SCREEN: 1 << 2,
UNLOCK_SCREEN: 1 << 3,
LOGIN_SCREEN: 1 << 4,
MESSAGE_TRAY: 1 << 5,
SYSTEM_MODAL: 1 << 6,
LOOKING_GLASS: 1 << 7
};
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
const STICKY_KEYS_ENABLE = 'stickykeys-enable';
let componentManager = null;
let panel = null;
@ -63,12 +54,13 @@ let screenShield = null;
let notificationDaemon = null;
let windowAttentionHandler = null;
let ctrlAltTabManager = null;
let osdWindow = null;
let sessionMode = null;
let shellDBusService = null;
let shellMountOpDBusService = null;
let screenSaverDBus = null;
let modalCount = 0;
let keybindingMode = KeybindingMode.NORMAL;
let keybindingMode = Shell.KeyBindingMode.NONE;
let modalActorFocusStack = [];
let uiGroup = null;
let magnifier = null;
@ -78,24 +70,26 @@ let layoutManager = null;
let _startDate;
let _defaultCssStylesheet = null;
let _cssStylesheet = null;
let _overridesSettings = null;
let background = null;
let _a11ySettings = null;
let dynamicWorkspacesSchema = null;
function _sessionUpdated() {
_loadDefaultStylesheet();
wm.setCustomKeybindingHandler('panel-main-menu',
KeybindingMode.NORMAL |
KeybindingMode.OVERVIEW,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
sessionMode.hasOverview ? Lang.bind(overview, overview.toggle) : null);
wm.allowKeybinding('overlay-key', KeybindingMode.NORMAL |
KeybindingMode.OVERVIEW);
wm.allowKeybinding('overlay-key', Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW);
wm.setCustomKeybindingHandler('panel-run-dialog',
KeybindingMode.NORMAL |
KeybindingMode.OVERVIEW,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
sessionMode.hasRunDialog ? openRunDialog : null);
if (sessionMode.isGreeter)
screenShield.showDialog();
if (!sessionMode.hasRunDialog && lookingGlass)
lookingGlass.close();
}
function start() {
@ -109,9 +103,33 @@ function start() {
Gio.DesktopAppInfo.set_desktop_env('GNOME');
sessionMode = new SessionMode.SessionMode();
sessionMode.connect('sessions-loaded', _sessionsLoaded);
sessionMode.init();
}
function _sessionsLoaded() {
sessionMode.connect('updated', _sessionUpdated);
_initializePrefs();
_initializeUI();
shellDBusService = new ShellDBus.GnomeShell();
shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
_sessionUpdated();
}
function _initializePrefs() {
let keys = new Gio.Settings({ schema: sessionMode.overridesSchema }).list_keys();
for (let i = 0; i < keys.length; i++)
Meta.prefs_override_preference_schema(keys[i], sessionMode.overridesSchema);
if (keys.indexOf('dynamic-workspaces') > -1)
dynamicWorkspacesSchema = sessionMode.overridesSchema;
else
dynamicWorkspacesSchema = 'org.gnome.mutter';
}
function _initializeUI() {
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
// also initialize ShellAppSystem first. ShellAppSystem
// needs to load all the .desktop files, and ShellWindowTracker
@ -120,53 +138,29 @@ function start() {
// and recalculate application associations, so to avoid
// races for now we initialize it here. It's better to
// be predictable anyways.
let tracker = Shell.WindowTracker.get_default();
Shell.WindowTracker.get_default();
Shell.AppUsage.get_default();
tracker.connect('startup-sequence-changed', _queueCheckWorkspaces);
// The stage is always covered so Clutter doesn't need to clear it; however
// the color is used as the default contents for the Mutter root background
// actor so set it anyways.
global.stage.color = DEFAULT_BACKGROUND_COLOR;
global.stage.no_clear_hint = true;
_defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
loadTheme();
// Set up stage hierarchy to group all UI actors under one container.
uiGroup = new Shell.GenericContainer({ name: 'uiGroup' });
uiGroup.connect('allocate',
function (actor, box, flags) {
let children = uiGroup.get_children();
for (let i = 0; i < children.length; i++)
children[i].allocate_preferred_size(flags);
});
uiGroup.connect('get-preferred-width',
function(actor, forHeight, alloc) {
let width = global.stage.width;
[alloc.min_size, alloc.natural_size] = [width, width];
});
uiGroup.connect('get-preferred-height',
function(actor, forWidth, alloc) {
let height = global.stage.height;
[alloc.min_size, alloc.natural_size] = [height, height];
});
global.window_group.reparent(uiGroup);
global.overlay_group.reparent(uiGroup);
global.stage.add_actor(uiGroup);
_loadDefaultStylesheet();
// Setup the stage hierarchy early
layoutManager = new Layout.LayoutManager();
// Various parts of the codebase still refers to Main.uiGroup
// instead using the layoutManager. This keeps that code
// working until it's updated.
uiGroup = layoutManager.uiGroup;
xdndHandler = new XdndHandler.XdndHandler();
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
osdWindow = new OsdWindow.OsdWindow();
overview = new Overview.Overview();
magnifier = new Magnifier.Magnifier();
if (UnlockDialog.isSupported())
screenShield = new ScreenShield.ScreenShield();
else
screenShield = new ScreenShield.ScreenShieldFallback();
panel = new Panel.Panel();
wm = new WindowManager.WindowManager();
magnifier = new Magnifier.Magnifier();
if (LoginManager.canLock())
screenShield = new ScreenShield.ScreenShield();
panel = new Panel.Panel();
messageTray = new MessageTray.MessageTray();
keyboard = new Keyboard.Keyboard();
notificationDaemon = new NotificationDaemon.NotificationDaemon();
@ -174,19 +168,22 @@ function start() {
componentManager = new Components.ComponentManager();
layoutManager.init();
keyboard.init();
overview.init();
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
false, -1, 1);
global.display.connect('overlay-key', Lang.bind(overview, overview.toggle));
sessionMode.connect('updated', _sessionUpdated);
_sessionUpdated();
_a11ySettings = new Gio.Settings({ schema: A11Y_SCHEMA });
global.display.connect('overlay-key', Lang.bind(overview, function () {
if (!_a11ySettings.get_boolean (STICKY_KEYS_ENABLE))
overview.toggle();
}));
// Provide the bus object for gnome-session to
// initiate logouts.
EndSessionDialog.init();
// We're ready for the session manager to move to the next phase
Meta.register_with_session();
_startDate = new Date();
log('GNOME Shell started at ' + _startDate);
@ -198,200 +195,35 @@ function start() {
Scripting.runPerfScript(module, perfOutput);
}
_overridesSettings = new Gio.Settings({ schema: OVERRIDES_SCHEMA });
_overridesSettings.connect('changed::dynamic-workspaces', _queueCheckWorkspaces);
global.screen.connect('notify::n-workspaces', _nWorkspacesChanged);
global.screen.connect('window-entered-monitor', _windowEnteredMonitor);
global.screen.connect('window-left-monitor', _windowLeftMonitor);
global.screen.connect('restacked', _windowsRestacked);
_nWorkspacesChanged();
ExtensionDownloader.init();
ExtensionSystem.init();
if (sessionMode.isGreeter && screenShield) {
layoutManager.connect('startup-prepared', function() {
screenShield.showDialog();
});
}
layoutManager.connect('startup-complete', function() {
if (keybindingMode == Shell.KeyBindingMode.NONE) {
keybindingMode = Shell.KeyBindingMode.NORMAL;
}
if (screenShield) {
screenShield.lockIfWasLocked();
}
});
}
let _workspaces = [];
let _checkWorkspacesId = 0;
function _loadDefaultStylesheet() {
if (!sessionMode.isPrimary)
return;
/*
* When the last window closed on a workspace is a dialog or splash
* screen, we assume that it might be an initial window shown before
* the main window of an application, and give the app a grace period
* where it can map another window before we remove the workspace.
*/
const LAST_WINDOW_GRACE_TIME = 1000;
let stylesheet = global.datadir + '/theme/' + sessionMode.stylesheetName;
if (_defaultCssStylesheet == stylesheet)
return;
function _checkWorkspaces() {
let i;
let emptyWorkspaces = [];
if (!Meta.prefs_get_dynamic_workspaces()) {
_checkWorkspacesId = 0;
return false;
}
for (i = 0; i < _workspaces.length; i++) {
let lastRemoved = _workspaces[i]._lastRemovedWindow;
if ((lastRemoved &&
(lastRemoved.get_window_type() == Meta.WindowType.SPLASHSCREEN ||
lastRemoved.get_window_type() == Meta.WindowType.DIALOG ||
lastRemoved.get_window_type() == Meta.WindowType.MODAL_DIALOG)) ||
_workspaces[i]._keepAliveId)
emptyWorkspaces[i] = false;
else
emptyWorkspaces[i] = true;
}
let sequences = Shell.WindowTracker.get_default().get_startup_sequences();
for (i = 0; i < sequences.length; i++) {
let index = sequences[i].get_workspace();
if (index >= 0 && index <= global.screen.n_workspaces)
emptyWorkspaces[index] = false;
}
let windows = global.get_window_actors();
for (i = 0; i < windows.length; i++) {
let win = windows[i];
if (win.get_meta_window().is_on_all_workspaces())
continue;
let workspaceIndex = win.get_workspace();
emptyWorkspaces[workspaceIndex] = false;
}
// If we don't have an empty workspace at the end, add one
if (!emptyWorkspaces[emptyWorkspaces.length -1]) {
global.screen.append_new_workspace(false, global.get_current_time());
emptyWorkspaces.push(false);
}
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
let removingCurrentWorkspace = (emptyWorkspaces[activeWorkspaceIndex] &&
activeWorkspaceIndex < emptyWorkspaces.length - 1);
// Don't enter the overview when removing multiple empty workspaces at startup
let showOverview = (removingCurrentWorkspace &&
!emptyWorkspaces.every(function(x) { return x; }));
if (removingCurrentWorkspace) {
// "Merge" the empty workspace we are removing with the one at the end
wm.blockAnimations();
}
// Delete other empty workspaces; do it from the end to avoid index changes
for (i = emptyWorkspaces.length - 2; i >= 0; i--) {
if (emptyWorkspaces[i])
global.screen.remove_workspace(_workspaces[i], global.get_current_time());
}
if (removingCurrentWorkspace) {
global.screen.get_workspace_by_index(global.screen.n_workspaces - 1).activate(global.get_current_time());
wm.unblockAnimations();
if (!overview.visible && showOverview)
overview.show();
}
_checkWorkspacesId = 0;
return false;
}
function keepWorkspaceAlive(workspace, duration) {
if (workspace._keepAliveId)
Mainloop.source_remove(workspace._keepAliveId);
workspace._keepAliveId = Mainloop.timeout_add(duration, function() {
workspace._keepAliveId = 0;
_queueCheckWorkspaces();
return false;
});
}
function _windowRemoved(workspace, window) {
workspace._lastRemovedWindow = window;
_queueCheckWorkspaces();
Mainloop.timeout_add(LAST_WINDOW_GRACE_TIME, function() {
if (workspace._lastRemovedWindow == window) {
workspace._lastRemovedWindow = null;
_queueCheckWorkspaces();
}
return false;
});
}
function _windowLeftMonitor(metaScreen, monitorIndex, metaWin) {
// If the window left the primary monitor, that
// might make that workspace empty
if (monitorIndex == layoutManager.primaryIndex)
_queueCheckWorkspaces();
}
function _windowEnteredMonitor(metaScreen, monitorIndex, metaWin) {
// If the window entered the primary monitor, that
// might make that workspace non-empty
if (monitorIndex == layoutManager.primaryIndex)
_queueCheckWorkspaces();
}
function _windowsRestacked() {
// Figure out where the pointer is in case we lost track of
// it during a grab. (In particular, if a trayicon popup menu
// is dismissed, see if we need to close the message tray.)
global.sync_pointer();
}
function _queueCheckWorkspaces() {
if (_checkWorkspacesId == 0)
_checkWorkspacesId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, _checkWorkspaces);
}
function _nWorkspacesChanged() {
let oldNumWorkspaces = _workspaces.length;
let newNumWorkspaces = global.screen.n_workspaces;
if (oldNumWorkspaces == newNumWorkspaces)
return false;
let lostWorkspaces = [];
if (newNumWorkspaces > oldNumWorkspaces) {
let w;
// Assume workspaces are only added at the end
for (w = oldNumWorkspaces; w < newNumWorkspaces; w++)
_workspaces[w] = global.screen.get_workspace_by_index(w);
for (w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
let workspace = _workspaces[w];
workspace._windowAddedId = workspace.connect('window-added', _queueCheckWorkspaces);
workspace._windowRemovedId = workspace.connect('window-removed', _windowRemoved);
}
} else {
// Assume workspaces are only removed sequentially
// (e.g. 2,3,4 - not 2,4,7)
let removedIndex;
let removedNum = oldNumWorkspaces - newNumWorkspaces;
for (let w = 0; w < oldNumWorkspaces; w++) {
let workspace = global.screen.get_workspace_by_index(w);
if (_workspaces[w] != workspace) {
removedIndex = w;
break;
}
}
let lostWorkspaces = _workspaces.splice(removedIndex, removedNum);
lostWorkspaces.forEach(function(workspace) {
workspace.disconnect(workspace._windowAddedId);
workspace.disconnect(workspace._windowRemovedId);
});
}
_queueCheckWorkspaces();
return false;
_defaultCssStylesheet = stylesheet;
loadTheme();
}
/**
@ -432,7 +264,8 @@ function loadTheme() {
if (_cssStylesheet != null)
cssStylesheet = _cssStylesheet;
let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
let theme = new St.Theme ({ application_stylesheet: cssStylesheet,
default_stylesheet: _defaultCssStylesheet });
if (previousTheme) {
let customStylesheets = previousTheme.get_custom_stylesheets();
@ -474,17 +307,6 @@ function notifyError(msg, details) {
notify(msg, details);
}
function isWindowActorDisplayedOnWorkspace(win, workspaceIndex) {
return win.get_workspace() == workspaceIndex ||
(win.get_meta_window() && win.get_meta_window().is_on_all_workspaces());
}
function getWindowActorsForWorkspace(workspaceIndex) {
return global.get_window_actors().filter(function (win) {
return isWindowActorDisplayedOnWorkspace(win, workspaceIndex);
});
}
function _findModal(actor) {
for (let i = 0; i < modalActorFocusStack.length; i++) {
if (modalActorFocusStack[i].actor == actor)
@ -493,10 +315,6 @@ function _findModal(actor) {
return -1;
}
function isInModalStack(actor) {
return _findModal(actor) != -1;
}
/**
* pushModal:
* @actor: #ClutterActor which will be given keyboard focus
@ -519,7 +337,7 @@ function isInModalStack(actor) {
* - options: Meta.ModalOptions flags to indicate that the pointer is
* already grabbed
*
* - keybindingMode: used to set the current Main.KeybindingMode to filter
* - keybindingMode: used to set the current Shell.KeyBindingMode to filter
* global keybindings; the default of NONE will filter
* out all keybindings
*
@ -528,7 +346,7 @@ function isInModalStack(actor) {
function pushModal(actor, params) {
params = Params.parse(params, { timestamp: global.get_current_time(),
options: 0,
keybindingMode: KeybindingMode.NONE });
keybindingMode: Shell.KeyBindingMode.NONE });
if (modalCount == 0) {
if (!global.begin_modal(params.timestamp, params.options)) {
@ -538,27 +356,26 @@ 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);
if (index >= 0)
popModal(actor);
});
let curFocus = global.stage.get_key_focus();
let curFocusDestroyId;
if (curFocus != null) {
curFocusDestroyId = curFocus.connect('destroy', function() {
let prevFocus = global.stage.get_key_focus();
let prevFocusDestroyId;
if (prevFocus != null) {
prevFocusDestroyId = prevFocus.connect('destroy', function() {
let index = _findModal(actor);
if (index >= 0)
modalActorFocusStack[index].actor = null;
modalActorFocusStack[index].prevFocus = null;
});
}
modalActorFocusStack.push({ actor: actor,
focus: curFocus,
destroyId: actorDestroyId,
focusDestroyId: curFocusDestroyId,
prevFocus: prevFocus,
prevFocusDestroyId: prevFocusDestroyId,
keybindingMode: keybindingMode });
keybindingMode = params.keybindingMode;
@ -587,8 +404,7 @@ 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 = KeybindingMode.NORMAL;
keybindingMode = Shell.KeyBindingMode.NORMAL;
throw new Error('incorrect pop');
}
@ -599,18 +415,33 @@ function popModal(actor, timestamp) {
record.actor.disconnect(record.destroyId);
if (focusIndex == modalActorFocusStack.length - 1) {
if (record.focus)
record.focus.disconnect(record.focusDestroyId);
if (record.prevFocus)
record.prevFocus.disconnect(record.prevFocusDestroyId);
keybindingMode = record.keybindingMode;
global.stage.set_key_focus(record.focus);
global.stage.set_key_focus(record.prevFocus);
} else {
// If we have:
// global.stage.set_focus(a);
// Main.pushModal(b);
// Main.pushModal(c);
// Main.pushModal(d);
//
// then we have the stack:
// [{ prevFocus: a, actor: b },
// { prevFocus: b, actor: c },
// { prevFocus: c, actor: d }]
//
// When actor c is destroyed/popped, if we only simply remove the
// record, then the focus stack will be [a, c], rather than the correct
// [a, b]. Shift the focus stack up before removing the record to ensure
// that we get the correct result.
let t = modalActorFocusStack[modalActorFocusStack.length - 1];
if (t.focus)
t.focus.disconnect(t.focusDestroyId);
if (t.prevFocus)
t.prevFocus.disconnect(t.prevFocusDestroyId);
// Remove from the middle, shift the focus chain up
for (let i = modalActorFocusStack.length - 1; i > focusIndex; i--) {
modalActorFocusStack[i].focus = modalActorFocusStack[i - 1].focus;
modalActorFocusStack[i].focusDestroyId = modalActorFocusStack[i - 1].focusDestroyId;
modalActorFocusStack[i].prevFocus = modalActorFocusStack[i - 1].prevFocus;
modalActorFocusStack[i].prevFocusDestroyId = modalActorFocusStack[i - 1].prevFocusDestroyId;
modalActorFocusStack[i].keybindingMode = modalActorFocusStack[i - 1].keybindingMode;
}
}
@ -620,9 +451,8 @@ 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 = KeybindingMode.NORMAL;
keybindingMode = Shell.KeyBindingMode.NORMAL;
}
function createLookingGlass() {

File diff suppressed because it is too large Load Diff

View File

@ -14,6 +14,7 @@ const Atk = imports.gi.Atk;
const Params = imports.misc.params;
const Animation = imports.ui.animation;
const Layout = imports.ui.layout;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
@ -22,6 +23,10 @@ const Tweener = imports.ui.tweener;
const OPEN_AND_CLOSE_TIME = 0.1;
const FADE_OUT_DIALOG_TIME = 1.0;
const WORK_SPINNER_ICON_SIZE = 24;
const WORK_SPINNER_ANIMATION_DELAY = 1.0;
const WORK_SPINNER_ANIMATION_TIME = 0.3;
const State = {
OPENED: 0,
CLOSED: 1,
@ -37,14 +42,16 @@ const ModalDialog = new Lang.Class({
params = Params.parse(params, { shellReactive: false,
styleClass: null,
parentActor: Main.uiGroup,
keybindingMode: Main.KeybindingMode.SYSTEM_MODAL,
shouldFadeIn: true });
keybindingMode: Shell.KeyBindingMode.SYSTEM_MODAL,
shouldFadeIn: true,
destroyOnClose: true });
this.state = State.CLOSED;
this._hasModal = false;
this._keybindingMode = params.keybindingMode;
this._shellReactive = params.shellReactive;
this._shouldFadeIn = params.shouldFadeIn;
this._destroyOnClose = params.destroyOnClose;
this._group = new St.Widget({ visible: false,
x: 0,
@ -58,34 +65,32 @@ const ModalDialog = new Lang.Class({
this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy));
this._pressedKey = null;
this._buttonKeys = {};
this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
this._group.connect('key-release-event', Lang.bind(this, this._onKeyReleaseEvent));
this._backgroundBin = new St.Bin();
this.backgroundStack = new St.Widget({ layout_manager: new Clutter.BinLayout() });
this._backgroundBin = new St.Bin({ child: this.backgroundStack,
x_fill: true, y_fill: true });
this._monitorConstraint = new Layout.MonitorConstraint();
this._backgroundBin.add_constraint(this._monitorConstraint);
this._group.add_actor(this._backgroundBin);
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,
{ inhibitEvents: true });
this._lightbox.highlight(this._backgroundBin);
let stack = new Shell.Stack();
this._backgroundBin.child = stack;
this._eventBlocker = new Clutter.Group({ reactive: true });
stack.add_actor(this._eventBlocker);
stack.add_actor(this.dialogLayout);
} else {
this._backgroundBin.child = this.dialogLayout;
this._eventBlocker = new Clutter.Actor({ reactive: true });
this.backgroundStack.add_actor(this._eventBlocker);
}
this.backgroundStack.add_actor(this.dialogLayout);
this.contentLayout = new St.BoxLayout({ vertical: true });
@ -96,8 +101,7 @@ const ModalDialog = new Lang.Class({
y_align: St.Align.START });
this.buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
visible: false,
vertical: false });
vertical: false });
this.dialogLayout.add(this.buttonLayout,
{ expand: true,
x_align: St.Align.MIDDLE,
@ -107,6 +111,8 @@ const ModalDialog = new Lang.Class({
this._initialKeyFocus = this.dialogLayout;
this._initialKeyFocusDestroyId = 0;
this._savedKeyFocus = null;
this._workSpinner = null;
},
destroy: function() {
@ -120,7 +126,6 @@ const ModalDialog = new Lang.Class({
setButtons: function(buttons) {
this.clearButtons();
this.buttonLayout.visible = (buttons.length > 0);
for (let i = 0; i < buttons.length; i++) {
let buttonInfo = buttons[i];
@ -159,6 +164,7 @@ const ModalDialog = new Lang.Class({
keys = [];
let button = new St.Button({ style_class: 'modal-dialog-button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
reactive: true,
can_focus: true,
label: label });
@ -180,10 +186,55 @@ const ModalDialog = new Lang.Class({
return button;
},
_onKeyReleaseEvent: function(object, event) {
let symbol = event.get_key_symbol();
let buttonInfo = this._buttonKeys[symbol];
placeSpinner: function(layoutInfo) {
let spinnerIcon = global.datadir + '/theme/process-working.svg';
this._workSpinner = new Animation.AnimatedIcon(spinnerIcon, WORK_SPINNER_ICON_SIZE);
this._workSpinner.actor.opacity = 0;
this._workSpinner.actor.show();
this.buttonLayout.add(this._workSpinner.actor, layoutInfo);
},
setWorking: function(working) {
if (!this._workSpinner)
return;
Tweener.removeTweens(this._workSpinner.actor);
if (working) {
this._workSpinner.play();
Tweener.addTween(this._workSpinner.actor,
{ opacity: 255,
delay: WORK_SPINNER_ANIMATION_DELAY,
time: WORK_SPINNER_ANIMATION_TIME,
transition: 'linear'
});
} else {
Tweener.addTween(this._workSpinner.actor,
{ opacity: 0,
time: WORK_SPINNER_ANIMATION_TIME,
transition: 'linear',
onCompleteScope: this,
onComplete: function() {
if (this._workSpinner)
this._workSpinner.stop();
}
});
}
},
_onKeyPressEvent: function(object, event) {
this._pressedKey = event.get_key_symbol();
},
_onKeyReleaseEvent: function(object, event) {
let pressedKey = this._pressedKey;
this._pressedKey = null;
let symbol = event.get_key_symbol();
if (symbol != pressedKey)
return false;
let buttonInfo = this._buttonKeys[symbol];
if (!buttonInfo)
return false;
@ -266,6 +317,10 @@ const ModalDialog = new Lang.Class({
function() {
this.state = State.CLOSED;
this._group.hide();
this.emit('closed');
if (this._destroyOnClose)
this.destroy();
})
});
},
@ -301,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

@ -103,6 +103,126 @@ const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
'ibus-ui-gtk': 'keyboard'
};
const NotificationGenericPolicy = new Lang.Class({
Name: 'NotificationGenericPolicy',
Extends: MessageTray.NotificationPolicy,
_init: function() {
// Don't chain to parent, it would try setting
// our properties to the defaults
this.id = 'generic';
this._masterSettings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications' });
this._masterSettings.connect('changed', Lang.bind(this, this._changed));
},
store: function() { },
destroy: function() {
this._masterSettings.run_dispose();
},
_changed: function(settings, key) {
this.emit('policy-changed', key);
},
get enable() {
return true;
},
get enableSound() {
return true;
},
get showBanners() {
return this._masterSettings.get_boolean('show-banners');
},
get forceExpanded() {
return false;
},
get showInLockScreen() {
return this._masterSettings.get_boolean('show-in-lock-screen');
},
get detailsInLockScreen() {
return false;
}
});
const NotificationApplicationPolicy = new Lang.Class({
Name: 'NotificationApplicationPolicy',
Extends: MessageTray.NotificationPolicy,
_init: function(id) {
// Don't chain to parent, it would try setting
// our properties to the defaults
this.id = id;
this._canonicalId = this._canonicalizeId(id)
this._masterSettings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications' });
this._settings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications.application',
path: '/org/gnome/desktop/notifications/application/' + this._canonicalId + '/' });
this._masterSettings.connect('changed', Lang.bind(this, this._changed));
this._settings.connect('changed', Lang.bind(this, this._changed));
},
store: function() {
this._settings.set_string('application-id', this.id + '.desktop');
let apps = this._masterSettings.get_strv('application-children');
if (apps.indexOf(this._canonicalId) < 0) {
apps.push(this._canonicalId);
this._masterSettings.set_strv('application-children', apps);
}
},
destroy: function() {
this._masterSettings.run_dispose();
this._settings.run_dispose();
},
_changed: function(settings, key) {
this.emit('policy-changed', key);
},
_canonicalizeId: function(id) {
// Keys are restricted to lowercase alphanumeric characters and dash,
// and two dashes cannot be in succession
return id.toLowerCase().replace(/[^a-z0-9\-]/g, '-').replace(/--+/g, '-');
},
get enable() {
return this._settings.get_boolean('enable');
},
get enableSound() {
return this._settings.get_boolean('enable-sound-alerts');
},
get showBanners() {
return this._masterSettings.get_boolean('show-banners') &&
this._settings.get_boolean('show-banners');
},
get forceExpanded() {
return this._settings.get_boolean('force-expanded');
},
get showInLockScreen() {
return this._masterSettings.get_boolean('show-in-lock-screen') &&
this._settings.get_boolean('show-in-lock-screen');
},
get detailsInLockScreen() {
return this._settings.get_boolean('details-in-lock-screen');
}
});
const NotificationDaemon = new Lang.Class({
Name: 'NotificationDaemon',
@ -124,45 +244,45 @@ const NotificationDaemon = new Lang.Class({
Main.overview.connect('hidden',
Lang.bind(this, this._onFocusAppChanged));
this._trayManager.manage_stage(global.stage, Main.messageTray.actor);
this._trayManager.manage_screen(global.screen, Main.messageTray.actor);
},
_iconForNotificationData: function(icon, hints) {
// If an icon is not specified, we use 'image-data' or 'image-path' hint for an icon
// and don't show a large image. There are currently many applications that use
// notify_notification_set_icon_from_pixbuf() from libnotify, which in turn sets
// the 'image-data' hint. These applications don't typically pass in 'app_icon'
// argument to Notify() and actually expect the pixbuf to be shown as an icon.
// So the logic here does the right thing for this case. If both an icon and either
// one of 'image-data' or 'image-path' are specified, we show both an icon and
// a large image.
if (icon) {
if (icon.substr(0, 7) == 'file://')
return new Gio.FileIcon({ file: Gio.File.new_for_uri(icon) });
else if (icon[0] == '/') {
return new Gio.FileIcon({ file: Gio.File.new_for_path(icon) });
} else
return new Gio.ThemedIcon({ name: icon });
} else if (hints['image-data']) {
_imageForNotificationData: function(hints) {
if (hints['image-data']) {
let [width, height, rowStride, hasAlpha,
bitsPerSample, nChannels, data] = hints['image-data'];
return Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha,
bitsPerSample, width, height, rowStride);
} else if (hints['image-path']) {
return new Gio.FileIcon({ file: Gio.File.new_for_path(hints['image-path']) });
} else {
let stockIcon;
switch (hints.urgency) {
case Urgency.LOW:
case Urgency.NORMAL:
stockIcon = 'gtk-dialog-info';
break;
case Urgency.CRITICAL:
stockIcon = 'gtk-dialog-error';
break;
}
return new Gio.ThemedIcon({ name: stockIcon });
}
return null;
},
_fallbackIconForNotificationData: function(hints) {
let stockIcon;
switch (hints.urgency) {
case Urgency.LOW:
case Urgency.NORMAL:
stockIcon = 'gtk-dialog-info';
break;
case Urgency.CRITICAL:
stockIcon = 'gtk-dialog-error';
break;
}
return new Gio.ThemedIcon({ name: stockIcon });
},
_iconForNotificationData: function(icon) {
if (icon) {
if (icon.substr(0, 7) == 'file://')
return new Gio.FileIcon({ file: Gio.File.new_for_uri(icon) });
else if (icon[0] == '/')
return new Gio.FileIcon({ file: Gio.File.new_for_path(icon) });
else
return new Gio.ThemedIcon({ name: icon });
}
return null;
},
_lookupSource: function(title, pid, trayIcon) {
@ -213,7 +333,7 @@ const NotificationDaemon = new Lang.Class({
}
}
let source = new Source(title, pid, sender, trayIcon);
let source = new Source(title, pid, sender, trayIcon, ndata ? ndata.hints['desktop-entry'] : null);
source.setTransient(isForTransientNotification);
if (!isForTransientNotification) {
@ -355,12 +475,8 @@ const NotificationDaemon = new Lang.Class({
[ndata.id, ndata.icon, ndata.summary, ndata.body,
ndata.actions, ndata.hints, ndata.notification];
let gicon = this._iconForNotificationData(icon, hints);
if (notification == null) {
notification = new MessageTray.Notification(source, summary, body,
{ gicon: gicon,
bannerMarkup: true });
notification = new MessageTray.Notification(source);
ndata.notification = notification;
notification.connect('destroy', Lang.bind(this,
function(n, reason) {
@ -383,29 +499,38 @@ const NotificationDaemon = new Lang.Class({
function(n, actionId) {
this._emitActionInvoked(ndata.id, actionId);
}));
} else {
notification.update(summary, body, { gicon: gicon,
bannerMarkup: true,
clear: true });
}
// We only display a large image if an icon is also specified.
if (icon && (hints['image-data'] || hints['image-path'])) {
let image = null;
if (hints['image-data']) {
let [width, height, rowStride, hasAlpha,
bitsPerSample, nChannels, data] = hints['image-data'];
image = St.TextureCache.get_default().load_from_raw(data, hasAlpha,
width, height, rowStride, notification.IMAGE_SIZE);
} else if (hints['image-path']) {
image = St.TextureCache.get_default().load_uri_async(GLib.filename_to_uri(hints['image-path'], null),
notification.IMAGE_SIZE,
notification.IMAGE_SIZE);
}
notification.setImage(image);
} else {
notification.unsetImage();
}
// Mark music notifications so they can be shown in the screen shield
notification.isMusic = (ndata.hints['category'] == 'x-gnome.music');
let gicon = this._iconForNotificationData(icon, hints);
let gimage = this._imageForNotificationData(hints);
let image = null;
// If an icon is not specified, we use 'image-data' or 'image-path' hint for an icon
// and don't show a large image. There are currently many applications that use
// notify_notification_set_icon_from_pixbuf() from libnotify, which in turn sets
// the 'image-data' hint. These applications don't typically pass in 'app_icon'
// argument to Notify() and actually expect the pixbuf to be shown as an icon.
// So the logic here does the right thing for this case. If both an icon and either
// one of 'image-data' or 'image-path' are specified, we show both an icon and
// a large image.
if (gicon && gimage)
image = new St.Icon({ gicon: gimage,
icon_size: notification.IMAGE_SIZE });
else if (!gicon && gimage)
gicon = gimage;
else if (!gicon)
gicon = this._fallbackIconForNotificationData(hints);
notification.update(summary, body, { gicon: gicon,
bannerMarkup: true,
clear: true,
soundFile: hints['sound-file'],
soundName: hints['sound-name'] });
notification.setImage(image);
if (actions.length) {
notification.setUseActionIcons(hints['action-icons'] == true);
@ -435,7 +560,7 @@ const NotificationDaemon = new Lang.Class({
// of the 'transient' hint with hints['transient'] rather than hints.transient
notification.setTransient(hints['transient'] == true);
let sourceGIcon = source.useNotificationIcon ? this._iconForNotificationData(icon, hints) : null;
let sourceGIcon = source.useNotificationIcon ? gicon : null;
source.processNotification(notification, sourceGIcon);
},
@ -459,7 +584,7 @@ const NotificationDaemon = new Lang.Class({
// 'icon-multi',
'icon-static',
'persistence',
// 'sound',
'sound',
];
},
@ -515,12 +640,22 @@ const Source = new Lang.Class({
Name: 'NotificationDaemonSource',
Extends: MessageTray.Source,
_init: function(title, pid, sender, trayIcon) {
_init: function(title, pid, sender, trayIcon, appId) {
// Need to set the app before chaining up, so
// methods called from the parent constructor can find it
this.trayIcon = trayIcon;
this.pid = pid;
this.app = this._getApp(appId);
this.parent(title);
this.initialTitle = title;
this.pid = pid;
if (this.app)
this.title = this.app.get_name();
else
this.useNotificationIcon = true;
if (sender)
this._nameWatcherId = Gio.DBus.session.watch_name(sender,
Gio.BusNameWatcherFlags.NONE,
@ -529,16 +664,19 @@ const Source = new Lang.Class({
else
this._nameWatcherId = 0;
this._setApp();
if (this.app)
this.title = this.app.get_name();
else
this.useNotificationIcon = true;
this.trayIcon = trayIcon;
if (this.trayIcon) {
this._setSummaryIcon(this.trayIcon);
this.useNotificationIcon = false;
// Try again finding the app, using the WM_CLASS from the tray icon
this._setSummaryIcon(this.trayIcon);
this.useNotificationIcon = false;
}
},
_createPolicy: function() {
if (this.app) {
let id = this.app.get_id().replace(/\.desktop$/,'');
return new NotificationApplicationPolicy(id);
} else {
return new NotificationGenericPolicy();
}
},
@ -565,24 +703,22 @@ const Source = new Lang.Class({
this.notify(notification);
},
handleSummaryClick: function() {
handleSummaryClick: function(button) {
if (!this.trayIcon)
return false;
let event = Clutter.get_current_event();
if (event.type() != Clutter.EventType.BUTTON_RELEASE)
return false;
// Left clicks are passed through only where there aren't unacknowledged
// notifications, so it possible to open them in summary mode; right
// clicks are always forwarded, as the right click menu is not useful for
// tray icons
if (event.get_button() == 1 &&
if (button == 1 &&
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);
}));
@ -590,7 +726,7 @@ const Source = new Lang.Class({
return true;
},
_getApp: function() {
_getApp: function(appId) {
let app;
app = Shell.WindowTracker.get_default().get_app_from_pid(this.pid);
@ -598,7 +734,13 @@ const Source = new Lang.Class({
return app;
if (this.trayIcon) {
app = Shell.AppSystem.get_default().lookup_wmclass(this.trayIcon.wmclass);
app = Shell.AppSystem.get_default().lookup_wmclass(this.trayIcon.wm_class);
if (app != null)
return app;
}
if (appId) {
app = Shell.AppSystem.get_default().lookup_app(appId + '.desktop');
if (app != null)
return app;
}
@ -606,11 +748,11 @@ const Source = new Lang.Class({
return null;
},
_setApp: function() {
_setApp: function(appId) {
if (this.app)
return;
this.app = this._getApp();
this.app = this._getApp(appId);
if (!this.app)
return;

210
js/ui/osdWindow.js Normal file
View File

@ -0,0 +1,210 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const St = imports.gi.St;
const Lang = imports.lang;
const Layout = imports.ui.layout;
const Main = imports.ui.main;
const Mainloop = imports.mainloop;
const Tweener = imports.ui.tweener;
const Meta = imports.gi.Meta;
const HIDE_TIMEOUT = 1500;
const FADE_TIME = 0.1;
const LEVEL_ANIMATION_TIME = 0.1;
const LevelBar = new Lang.Class({
Name: 'LevelBar',
_init: function() {
this._level = 0;
this.actor = new St.Bin({ style_class: 'level',
x_fill: true, y_fill: true });
this._bar = new St.DrawingArea();
this._bar.connect('repaint', Lang.bind(this, this._repaint));
this.actor.set_child(this._bar);
},
get level() {
return this._level;
},
set level(value) {
let newValue = Math.max(0, Math.min(value, 100));
if (newValue == this._level)
return;
this._level = newValue;
this._bar.queue_repaint();
},
_repaint: function() {
let cr = this._bar.get_context();
let node = this.actor.get_theme_node();
let radius = node.get_border_radius(0); // assume same radius for all corners
Clutter.cairo_set_source_color(cr, node.get_foreground_color());
let [w, h] = this._bar.get_surface_size();
w *= (this._level / 100.);
if (w == 0)
return;
cr.moveTo(radius, 0);
if (w >= radius)
cr.arc(w - radius, radius, radius, 1.5 * Math.PI, 2. * Math.PI);
else
cr.lineTo(w, 0);
if (w >= radius)
cr.arc(w - radius, h - radius, radius, 0, 0.5 * Math.PI);
else
cr.lineTo(w, h);
cr.arc(radius, h - radius, radius, 0.5 * Math.PI, Math.PI);
cr.arc(radius, radius, radius, Math.PI, 1.5 * Math.PI);
cr.fill();
}
});
const OsdWindow = new Lang.Class({
Name: 'OsdWindow',
_init: function() {
this._popupSize = 0;
this.actor = new St.Widget({ x_expand: true,
y_expand: true,
x_align: Clutter.ActorAlign.CENTER,
y_align: Clutter.ActorAlign.CENTER });
this.actor.add_constraint(new Layout.MonitorConstraint({ primary: true }));
this._box = new St.BoxLayout({ style_class: 'osd-window',
vertical: true });
this.actor.add_actor(this._box);
this._box.connect('style-changed', Lang.bind(this, this._onStyleChanged));
this._box.connect('notify::height', Lang.bind(this,
function() {
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this,
function() {
this._box.width = this._box.height;
}));
}));
this._icon = new St.Icon();
this._box.add(this._icon, { expand: true });
this._label = new St.Label();
this._box.add(this._label);
this._level = new LevelBar();
this._box.add(this._level.actor);
this._hideTimeoutId = 0;
this._reset();
Main.layoutManager.connect('monitors-changed',
Lang.bind(this, this._monitorsChanged));
this._monitorsChanged();
Main.uiGroup.add_child(this.actor);
},
setIcon: function(icon) {
this._icon.gicon = icon;
},
setLabel: function(label) {
this._label.visible = (label != undefined);
if (label)
this._label.text = label;
},
setLevel: function(level) {
this._level.actor.visible = (level != undefined);
if (this.actor.visible)
Tweener.addTween(this._level,
{ level: level,
time: LEVEL_ANIMATION_TIME,
transition: 'easeOutQuad' });
else
this._level.level = level;
},
show: function() {
if (!this._icon.gicon)
return;
if (!this.actor.visible) {
Meta.disable_unredirect_for_screen(global.screen);
this.actor.show();
this.actor.opacity = 0;
this.actor.get_parent().set_child_above_sibling(this.actor, null);
Tweener.addTween(this.actor,
{ opacity: 255,
time: FADE_TIME,
transition: 'easeOutQuad' });
}
if (this._hideTimeoutId)
Mainloop.source_remove(this._hideTimeoutId);
this._hideTimeoutId = Mainloop.timeout_add(HIDE_TIMEOUT,
Lang.bind(this, this._hide));
},
cancel: function() {
if (!this._hideTimeoutId)
return;
Mainloop.source_remove(this._hideTimeoutId);
this._hide();
},
_hide: function() {
this._hideTimeoutId = 0;
Tweener.addTween(this.actor,
{ opacity: 0,
time: FADE_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this._reset();
Meta.enable_unredirect_for_screen(global.screen);
})
});
},
_reset: function() {
this.actor.hide();
this.setLabel(null);
this.setLevel(null);
},
_monitorsChanged: function() {
/* assume 110x110 on a 640x480 display and scale from there */
let monitor = Main.layoutManager.primaryMonitor;
let scalew = monitor.width / 640.0;
let scaleh = monitor.height / 480.0;
let scale = Math.min(scalew, scaleh);
this._popupSize = 110 * Math.max(1, scale);
this._box.translation_y = monitor.height / 4;
this._icon.icon_size = this._popupSize / 2;
this._box.style_changed();
},
_onStyleChanged: function() {
let themeNode = this._box.get_theme_node();
let horizontalPadding = themeNode.get_horizontal_padding();
let verticalPadding = themeNode.get_vertical_padding();
let topBorder = themeNode.get_border_width(St.Side.TOP);
let bottomBorder = themeNode.get_border_width(St.Side.BOTTOM);
let leftBorder = themeNode.get_border_width(St.Side.LEFT);
let rightBorder = themeNode.get_border_width(St.Side.RIGHT);
let minWidth = this._popupSize - verticalPadding - leftBorder - rightBorder;
let minHeight = this._popupSize - horizontalPadding - topBorder - bottomBorder;
this._box.style = 'min-height: %dpx;'.format(Math.max(minWidth, minHeight));
}
});

View File

@ -10,41 +10,28 @@ const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Gdk = imports.gi.Gdk;
const Dash = imports.ui.dash;
const Background = imports.ui.background;
const DND = imports.ui.dnd;
const LayoutManager = imports.ui.layout;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const OverviewControls = imports.ui.overviewControls;
const Panel = imports.ui.panel;
const Params = imports.misc.params;
const Tweener = imports.ui.tweener;
const ViewSelector = imports.ui.viewSelector;
const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
// Time for initial animation going into Overview mode
const ANIMATION_TIME = 0.25;
// XXX -- grab this automatically. Hard to do.
const DASH_MAX_WIDTH = 96;
// Must be less than ANIMATION_TIME, since we switch to
// or from the overview completely after ANIMATION_TIME,
// and don't want the shading animation to get cut off
const SHADE_ANIMATION_TIME = .20;
const DND_WINDOW_SWITCH_TIMEOUT = 1250;
const DND_WINDOW_SWITCH_TIMEOUT = 750;
const GLSL_DIM_EFFECT_DECLARATIONS = '';
const GLSL_DIM_EFFECT_CODE = '\
vec2 dist = cogl_tex_coord_in[0].xy - vec2(0.5, 0.5); \
float elipse_radius = 0.5; \
/* from https://bugzilla.gnome.org/show_bug.cgi?id=669798: \
the alpha on the gradient goes from 165 at its darkest to 98 at its most transparent. */ \
float y = 165.0 / 255.0; \
float x = 98.0 / 255.0; \
/* interpolate darkening value, based on distance from screen center */ \
float val = min(length(dist), elipse_radius); \
float a = mix(x, y, val / elipse_radius); \
/* dim_factor varies from [1.0 -> 0.5] when overview is showing \
We use it to smooth value, then we clamp it to valid color interval */ \
a = clamp(a - cogl_color_in.r + 0.5, 0.0, 1.0); \
/* We\'re blending between: color and black color (obviously omitted in the equation) */ \
cogl_color_out.xyz = cogl_color_out.xyz * (1.0 - a); \
cogl_color_out.a = 1.0;';
const OVERVIEW_ACTIVATION_TIMEOUT = 0.5;
const ShellInfo = new Lang.Class({
Name: 'ShellInfo',
@ -120,61 +107,52 @@ const Overview = new Lang.Class({
this._overviewCreated = true;
// The main BackgroundActor is inside global.window_group which is
// The main Background actors are inside global.window_group which are
// hidden when displaying the overview, so we create a new
// one. Instances of this class share a single CoglTexture behind the
// scenes which allows us to show the background with different
// rendering options without duplicating the texture data.
this._background = Meta.BackgroundActor.new_for_screen(global.screen);
this._background.add_glsl_snippet(Meta.SnippetHook.FRAGMENT,
GLSL_DIM_EFFECT_DECLARATIONS,
GLSL_DIM_EFFECT_CODE,
false);
this._background.hide();
global.overlay_group.add_actor(this._background);
let monitor = Main.layoutManager.primaryMonitor;
this._desktopFade = new St.Bin();
global.overlay_group.add_actor(this._desktopFade);
Main.layoutManager.overviewGroup.add_child(this._desktopFade);
this._spacing = 0;
let layout = new Clutter.BinLayout();
this._stack = new Clutter.Actor({ layout_manager: layout });
this._stack.add_constraint(new LayoutManager.MonitorConstraint({ primary: true }));
/* Translators: This is the main view to select
activities. See also note for "Activities" string. */
this._group = new St.Widget({ name: 'overview',
accessible_name: _("Overview"),
reactive: true });
this._group._delegate = this;
this._group.connect('style-changed',
Lang.bind(this, function() {
let node = this._group.get_theme_node();
let spacing = node.get_length('spacing');
if (spacing != this._spacing) {
this._spacing = spacing;
this._relayout();
}
}));
this._overview = new St.BoxLayout({ name: 'overview',
accessible_name: _("Overview"),
reactive: true,
vertical: true,
x_expand: true,
y_expand: true });
this._overview._delegate = this;
this._capturedEventId = 0;
this._buttonPressId = 0;
this._backgroundGroup = new Meta.BackgroundGroup();
Main.layoutManager.overviewGroup.add_child(this._backgroundGroup);
this._bgManagers = [];
this._activationTime = 0;
this.visible = false; // animating to overview, in overview, animating out
this._shown = false; // show() and not hide()
this._shownTemporarily = false; // showTemporarily() and not hideTemporarily()
this._modal = false; // have a modal grab
this.animationInProgress = false;
this._hideInProgress = false;
this.visibleTarget = false;
// During transitions, we raise this to the top to avoid having the overview
// area be reactive; it causes too many issues such as double clicks on
// Dash elements, or mouseover handlers in the workspaces.
this._coverPane = new Clutter.Rectangle({ opacity: 0,
reactive: true });
this._group.add_actor(this._coverPane);
this._coverPane = new Clutter.Actor({ opacity: 0,
reactive: true });
this._stack.add_actor(this._coverPane);
this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; }));
this._group.hide();
global.overlay_group.add_actor(this._group);
this._stack.add_actor(this._overview);
Main.layoutManager.overviewGroup.add_child(this._stack);
this._coverPane.hide();
@ -186,6 +164,8 @@ const Overview = new Lang.Class({
Main.xdndHandler.connect('drag-begin', Lang.bind(this, this._onDragBegin));
Main.xdndHandler.connect('drag-end', Lang.bind(this, this._onDragEnd));
global.screen.connect('restacked', Lang.bind(this, this._onRestacked));
this._windowSwitchTimeoutId = 0;
this._windowSwitchTimestamp = 0;
this._lastActiveWorkspaceIndex = -1;
@ -196,6 +176,56 @@ const Overview = new Lang.Class({
this.init();
},
_updateBackgrounds: function() {
for (let i = 0; i < this._bgManagers.length; i++)
this._bgManagers[i].destroy();
this._bgManagers = [];
for (let i = 0; i < Main.layoutManager.monitors.length; i++) {
let bgManager = new Background.BackgroundManager({ container: this._backgroundGroup,
monitorIndex: i,
effects: Meta.BackgroundEffects.VIGNETTE });
this._bgManagers.push(bgManager);
}
},
_unshadeBackgrounds: function() {
let backgrounds = this._backgroundGroup.get_children();
for (let i = 0; i < backgrounds.length; i++) {
let background = backgrounds[i]._delegate;
Tweener.addTween(background,
{ brightness: 1.0,
time: SHADE_ANIMATION_TIME,
transition: 'easeOutQuad'
});
Tweener.addTween(background,
{ vignetteSharpness: 0.0,
time: SHADE_ANIMATION_TIME,
transition: 'easeOutQuad'
});
}
},
_shadeBackgrounds: function() {
let backgrounds = this._backgroundGroup.get_children();
for (let i = 0; i < backgrounds.length; i++) {
let background = backgrounds[i]._delegate;
Tweener.addTween(background,
{ brightness: 0.8,
time: SHADE_ANIMATION_TIME,
transition: 'easeOutQuad'
});
Tweener.addTween(background,
{ vignetteSharpness: 0.7,
time: SHADE_ANIMATION_TIME,
transition: 'easeOutQuad'
});
}
},
_sessionUpdated: function() {
this.isDummy = !Main.sessionMode.hasOverview;
this._createOverview();
@ -213,34 +243,43 @@ const Overview = new Lang.Class({
this._shellInfo = new ShellInfo();
// Add a clone of the panel to the overview so spacing and such is
// automatic
this._panelGhost = new St.Bin({ child: new Clutter.Clone({ source: Main.panel.actor }),
reactive: false,
opacity: 0 });
this._overview.add_actor(this._panelGhost);
this._searchEntry = new St.Entry({ name: 'searchEntry',
/* Translators: this is the text displayed
in the search entry when no search is
active; it should not exceed ~30
characters. */
hint_text: _("Type to search..."),
hint_text: _("Type to search"),
track_hover: true,
can_focus: true });
this._group.add_actor(this._searchEntry);
this._searchEntryBin = new St.Bin({ child: this._searchEntry,
x_align: St.Align.MIDDLE });
this._overview.add_actor(this._searchEntryBin);
this._dash = new Dash.Dash();
this._viewSelector = new ViewSelector.ViewSelector(this._searchEntry,
this._dash.showAppsButton);
this._group.add_actor(this._viewSelector.actor);
this._group.add_actor(this._dash.actor);
// Create controls
this._controls = new OverviewControls.ControlsManager(this._searchEntry);
this._dash = this._controls.dash;
this._viewSelector = this._controls.viewSelector;
// Add our same-line elements after the search entry
this._overview.add(this._controls.actor, { y_fill: true, expand: true });
this._controls.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
this._stack.add_actor(this._controls.indicatorActor);
// TODO - recalculate everything when desktop size changes
this._dash.actor.add_constraint(this._viewSelector.constrainHeight);
this.dashIconSize = this._dash.iconSize;
this._dash.connect('icon-size-changed',
Lang.bind(this, function() {
this.dashIconSize = this._dash.iconSize;
}));
// Translators: this is the name of the dock/favorites area on
// the left of the overview
Main.ctrlAltTabManager.addGroup(this._dash.actor, _("Dash"), 'user-bookmarks-symbolic');
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
this._relayout();
},
@ -266,18 +305,22 @@ const Overview = new Lang.Class({
},
_onDragBegin: function() {
this._inXdndDrag = true;
DND.addDragMonitor(this._dragMonitor);
// Remember the workspace we started from
this._lastActiveWorkspaceIndex = global.screen.get_active_workspace_index();
},
_onDragEnd: function(time) {
this._inXdndDrag = false;
// In case the drag was canceled while in the overview
// we have to go back to where we started and hide
// the overview
if (this._shownTemporarily) {
if (this._shown) {
global.screen.get_workspace_by_index(this._lastActiveWorkspaceIndex).activate(time);
this.hideTemporarily();
this.hide();
}
this._resetWindowSwitchTimeout();
this._lastHoveredWindow = null;
@ -325,7 +368,7 @@ const Overview = new Lang.Class({
this._needsFakePointerEvent = true;
Main.activateWindow(dragEvent.targetActor._delegate.metaWindow,
this._windowSwitchTimestamp);
this.hideTemporarily();
this.hide();
this._lastHoveredWindow = null;
}));
}
@ -333,11 +376,15 @@ const Overview = new Lang.Class({
return DND.DragMotionResult.CONTINUE;
},
_onScrollEvent: function(actor, event) {
this.emit('scroll-event', event);
},
addAction: function(action) {
if (this.isDummy)
return;
this._group.add_action(action);
this._overview.add_action(action);
},
_getDesktopClone: function() {
@ -362,48 +409,31 @@ const Overview = new Lang.Class({
// when it is next shown.
this.hide();
let primary = Main.layoutManager.primaryMonitor;
let rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL);
let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
let contentY = Main.panel.actor.height;
let contentHeight = primary.height - contentY - Main.messageTray.actor.height;
this._coverPane.set_position(0, workArea.y);
this._coverPane.set_size(workArea.width, workArea.height);
this._group.set_position(primary.x, primary.y);
this._group.set_size(primary.width, primary.height);
this._updateBackgrounds();
},
this._coverPane.set_position(0, contentY);
this._coverPane.set_size(primary.width, contentHeight);
_onRestacked: function() {
let stack = global.get_window_actors();
let stackIndices = {};
let searchWidth = this._searchEntry.get_width();
let searchHeight = this._searchEntry.get_height();
let searchX = (primary.width - searchWidth) / 2;
let searchY = contentY + this._spacing;
let dashWidth = DASH_MAX_WIDTH;
let dashY = searchY + searchHeight + this._spacing;
let dashX;
if (rtl) {
this._dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
dashX = primary.width;
} else {
dashX = 0;
for (let i = 0; i < stack.length; i++) {
// Use the stable sequence for an integer to use as a hash key
stackIndices[stack[i].get_meta_window().get_stable_sequence()] = i;
}
let viewX = rtl ? 0 : dashWidth + this._spacing;
let viewY = searchY + searchHeight + this._spacing;
let viewWidth = primary.width - dashWidth - this._spacing;
let viewHeight = contentHeight - this._spacing - viewY;
this._searchEntry.set_position(searchX, searchY);
this._dash.actor.set_position(dashX, dashY);
this._viewSelector.actor.set_position(viewX, viewY);
this._viewSelector.actor.set_size(viewWidth, viewHeight);
this.emit('windows-restacked', stackIndices);
},
//// Public methods ////
beginItemDrag: function(source) {
this.emit('item-drag-begin');
this._inDrag = true;
},
cancelledItemDrag: function(source) {
@ -412,10 +442,12 @@ const Overview = new Lang.Class({
endItemDrag: function(source) {
this.emit('item-drag-end');
this._inDrag = false;
},
beginWindowDrag: function(source) {
this.emit('window-drag-begin');
this._inDrag = true;
},
cancelledWindowDrag: function(source) {
@ -424,23 +456,31 @@ const Overview = new Lang.Class({
endWindowDrag: function(source) {
this.emit('window-drag-end');
this._inDrag = false;
},
// show:
//
// Animates the overview visible and grabs mouse and keyboard input
show : function() {
show: function() {
if (this.isDummy)
return;
if (this._shown)
return;
this._shown = true;
this._syncInputMode();
if (!this._modal)
if (!this._syncGrab())
return;
Main.layoutManager.showOverview();
this._animateVisible();
},
focusSearch: function() {
this.show();
this._searchEntry.grab_key_focus();
},
fadeInDesktop: function() {
this._desktopFade.opacity = 0;
this._desktopFade.show();
@ -469,6 +509,8 @@ const Overview = new Lang.Class({
this.visible = true;
this.animationInProgress = true;
this.visibleTarget = true;
this._activationTime = Date.now() / 1000;
// All the the actors in the window group are completely obscured,
// hiding the group holding them while the Overview is displayed greatly
@ -480,49 +522,23 @@ const Overview = new Lang.Class({
//
// Disable unredirection while in the overview
Meta.disable_unredirect_for_screen(global.screen);
global.window_group.hide();
this._group.show();
this._background.show();
this._viewSelector.show();
this._group.opacity = 0;
Tweener.addTween(this._group,
this._stack.opacity = 0;
Tweener.addTween(this._stack,
{ opacity: 255,
transition: 'easeOutQuad',
time: ANIMATION_TIME,
onComplete: this._showDone,
onCompleteScope: this
});
Tweener.addTween(this._background,
{ dim_factor: 0.8,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
this._shadeBackgrounds();
this._coverPane.raise_top();
this._coverPane.show();
this.emit('showing');
},
// showTemporarily:
//
// Animates the overview visible without grabbing mouse and keyboard input;
// if show() has already been called, this has no immediate effect, but
// will result in the overview not being hidden until hideTemporarily() is
// called.
showTemporarily: function() {
if (this.isDummy)
return;
if (this._shownTemporarily)
return;
this._syncInputMode();
this._animateVisible();
this._shownTemporarily = true;
},
// hide:
//
// Reverses the effect of show()
@ -533,28 +549,20 @@ const Overview = new Lang.Class({
if (!this._shown)
return;
if (!this._shownTemporarily)
this._animateNotVisible();
let event = Clutter.get_current_event();
if (event) {
let type = event.type();
let button = (type == Clutter.EventType.BUTTON_PRESS ||
type == Clutter.EventType.BUTTON_RELEASE);
let ctrl = (event.get_state() & Clutter.ModifierType.CONTROL_MASK) != 0;
if (button && ctrl)
return;
}
this._animateNotVisible();
this._shown = false;
this._syncInputMode();
},
// hideTemporarily:
//
// Reverses the effect of showTemporarily()
hideTemporarily: function() {
if (this.isDummy)
return;
if (!this._shownTemporarily)
return;
if (!this._shown)
this._animateNotVisible();
this._shownTemporarily = false;
this._syncInputMode();
this._syncGrab();
},
toggle: function() {
@ -567,37 +575,51 @@ const Overview = new Lang.Class({
this.show();
},
// Checks if the Activities button is currently sensitive to
// clicks. The first call to this function within the
// OVERVIEW_ACTIVATION_TIMEOUT time of the hot corner being
// triggered will return false. This avoids opening and closing
// the overview if the user both triggered the hot corner and
// clicked the Activities button.
shouldToggleByCornerOrButton: function() {
if (this.animationInProgress)
return false;
if (this._inDrag)
return false;
if (this._activationTime == 0 || Date.now() / 1000 - this._activationTime > OVERVIEW_ACTIVATION_TIMEOUT)
return true;
return false;
},
//// Private methods ////
_syncInputMode: function() {
// We delay input mode changes during animation so that when removing the
_syncGrab: function() {
// We delay grab changes during animation so that when removing the
// overview we don't have a problem with the release of a press/release
// going to an application.
if (this.animationInProgress)
return;
return true;
if (this._shown) {
if (!this._modal) {
if (Main.pushModal(this._group,
{ keybindingMode: Main.KeybindingMode.OVERVIEW }))
this._modal = true;
else
this.hide();
let shouldBeModal = !this._inXdndDrag;
if (shouldBeModal) {
if (!this._modal) {
if (Main.pushModal(this._overview,
{ keybindingMode: Shell.KeyBindingMode.OVERVIEW })) {
this._modal = true;
} else {
this.hide();
return false;
}
}
}
} else if (this._shownTemporarily) {
if (this._modal) {
Main.popModal(this._group);
this._modal = false;
}
global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
} else {
if (this._modal) {
Main.popModal(this._group);
Main.popModal(this._overview);
this._modal = false;
}
else if (global.stage_input_mode == Shell.StageInputMode.FULLSCREEN)
global.stage_input_mode = Shell.StageInputMode.NORMAL;
}
return true;
},
_animateNotVisible: function() {
@ -605,24 +627,19 @@ const Overview = new Lang.Class({
return;
this.animationInProgress = true;
this._hideInProgress = true;
this.visibleTarget = false;
this._viewSelector.zoomFromOverview();
// Make other elements fade out.
Tweener.addTween(this._group,
Tweener.addTween(this._stack,
{ opacity: 0,
transition: 'easeOutQuad',
time: ANIMATION_TIME,
onComplete: this._hideDone,
onCompleteScope: this
});
Tweener.addTween(this._background,
{ dim_factor: 1.0,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
this._unshadeBackgrounds();
this._coverPane.raise_top();
this._coverPane.show();
@ -636,10 +653,10 @@ const Overview = new Lang.Class({
this.emit('shown');
// Handle any calls to hide* while we were showing
if (!this._shown && !this._shownTemporarily)
if (!this._shown)
this._animateNotVisible();
this._syncInputMode();
this._syncGrab();
global.sync_pointer();
},
@ -647,25 +664,21 @@ const Overview = new Lang.Class({
// Re-enable unredirection
Meta.enable_unredirect_for_screen(global.screen);
global.window_group.show();
this._viewSelector.hide();
this._desktopFade.hide();
this._background.hide();
this._group.hide();
this._coverPane.hide();
this.visible = false;
this.animationInProgress = false;
this._hideInProgress = false;
this._coverPane.hide();
this.emit('hidden');
// Handle any calls to show* while we were hiding
if (this._shown || this._shownTemporarily)
if (this._shown)
this._animateVisible();
else
Main.layoutManager.hideOverview();
this._syncInputMode();
this._syncGrab();
// Fake a pointer event if requested
if (this._needsFakePointerEvent) {

603
js/ui/overviewControls.js Normal file
View File

@ -0,0 +1,603 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const GObject = imports.gi.GObject;
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Dash = imports.ui.dash;
const Main = imports.ui.main;
const Params = imports.misc.params;
const Tweener = imports.ui.tweener;
const ViewSelector = imports.ui.viewSelector;
const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
const SIDE_CONTROLS_ANIMATION_TIME = 0.16;
function getRtlSlideDirection(direction, actor) {
let rtl = (actor.text_direction == Clutter.TextDirection.RTL);
if (rtl)
direction = (direction == SlideDirection.LEFT) ?
SlideDirection.RIGHT : SlideDirection.LEFT;
return direction;
};
const SlideDirection = {
LEFT: 0,
RIGHT: 1
};
const SlideLayout = new Lang.Class({
Name: 'SlideLayout',
Extends: Clutter.FixedLayout,
_init: function(params) {
this._slideX = 1;
this._direction = SlideDirection.LEFT;
this.parent(params);
},
vfunc_get_preferred_width: function(container, forHeight) {
let child = container.get_first_child();
let [minWidth, natWidth] = child.get_preferred_width(forHeight);
minWidth *= this._slideX;
natWidth *= this._slideX;
return [minWidth, natWidth];
},
vfunc_allocate: function(container, box, flags) {
let child = container.get_first_child();
let [, , natWidth, natHeight] = child.get_preferred_size();
let availWidth = Math.round(box.x2 - box.x1);
let availHeight = Math.round(box.y2 - box.y1);
let realDirection = getRtlSlideDirection(this._direction, child);
let translationX = (realDirection == SlideDirection.LEFT) ?
(availWidth - natWidth) : (natWidth - availWidth);
let actorBox = new Clutter.ActorBox({ x1: translationX,
y1: 0,
x2: child.x_expand ? availWidth : natWidth,
y2: child.y_expand ? availHeight : natHeight });
child.allocate(actorBox, flags);
},
set slideX(value) {
this._slideX = value;
this.layout_changed();
},
get slideX() {
return this._slideX;
},
set slideDirection(direction) {
this._direction = direction;
this.layout_changed();
},
get slideDirection() {
return this._direction;
}
});
const SlidingControl = new Lang.Class({
Name: 'SlidingControl',
_init: function(params) {
params = Params.parse(params, { slideDirection: SlideDirection.LEFT });
this.visible = true;
this.inDrag = false;
this.layout = new SlideLayout();
this.layout.slideDirection = params.slideDirection;
this.actor = new St.Widget({ layout_manager: this.layout,
style_class: 'overview-controls',
clip_to_allocation: true });
Main.overview.connect('showing', Lang.bind(this, this._onOverviewShowing));
Main.overview.connect('item-drag-begin', Lang.bind(this, this._onDragBegin));
Main.overview.connect('item-drag-end', Lang.bind(this, this._onDragEnd));
Main.overview.connect('item-drag-cancelled', Lang.bind(this, this._onDragEnd));
Main.overview.connect('window-drag-begin', Lang.bind(this, this._onWindowDragBegin));
Main.overview.connect('window-drag-cancelled', Lang.bind(this, this._onWindowDragEnd));
Main.overview.connect('window-drag-end', Lang.bind(this, this._onWindowDragEnd));
},
getSlide: function() {
throw new Error('getSlide() must be overridden');
},
updateSlide: function() {
Tweener.addTween(this.layout, { slideX: this.getSlide(),
time: SIDE_CONTROLS_ANIMATION_TIME,
transition: 'easeOutQuad' });
},
getVisibleWidth: function() {
let child = this.actor.get_first_child();
let [, , natWidth, ] = child.get_preferred_size();
return natWidth;
},
_getTranslation: function() {
let child = this.actor.get_first_child();
let direction = getRtlSlideDirection(this.layout.slideDirection, child);
let visibleWidth = this.getVisibleWidth();
if (direction == SlideDirection.LEFT)
return - visibleWidth;
else
return visibleWidth;
},
_updateTranslation: function() {
let translationStart = 0;
let translationEnd = 0;
let translation = this._getTranslation();
if (this.visible) {
translationStart = translation;
} else {
translationEnd = translation;
}
if (this.actor.translation_x == translationEnd)
return;
this.actor.translation_x = translationStart;
Tweener.addTween(this.actor, { translation_x: translationEnd,
time: SIDE_CONTROLS_ANIMATION_TIME,
transition: 'easeOutQuad'
});
},
_onOverviewShowing: function() {
// reset any translation and make sure the actor is visible when
// entering the overview
this.visible = true;
this.layout.slideX = this.getSlide();
this.actor.translation_x = 0;
},
_onWindowDragBegin: function() {
this._onDragBegin();
},
_onWindowDragEnd: function() {
this._onDragEnd();
},
_onDragBegin: function() {
this.inDrag = true;
this.actor.translation_x = 0;
this.updateSlide();
},
_onDragEnd: function() {
this.inDrag = false;
this.updateSlide();
},
fadeIn: function() {
Tweener.addTween(this.actor, { opacity: 255,
time: SIDE_CONTROLS_ANIMATION_TIME / 2,
transition: 'easeInQuad'
});
},
fadeHalf: function() {
Tweener.addTween(this.actor, { opacity: 128,
time: SIDE_CONTROLS_ANIMATION_TIME / 2,
transition: 'easeOutQuad'
});
},
slideIn: function() {
this.visible = true;
// we will update slideX and the translation from pageEmpty
},
slideOut: function() {
this.visible = false;
this._updateTranslation();
// we will update slideX from pageEmpty
},
pageEmpty: function() {
// When pageEmpty is received, there's no visible view in the
// selector; this means we can now safely set the full slide for
// the next page, since slideIn or slideOut might have been called,
// changing the visiblity
this.layout.slideX = this.getSlide();
this._updateTranslation();
}
});
const ThumbnailsSlider = new Lang.Class({
Name: 'ThumbnailsSlider',
Extends: SlidingControl,
_init: function(thumbnailsBox) {
this.parent({ slideDirection: SlideDirection.RIGHT });
this._thumbnailsBox = thumbnailsBox;
// SlideLayout reads the actor's expand flags to decide
// whether to allocate the natural size to its child, or the whole
// available allocation
this._thumbnailsBox.actor.y_expand = true;
this.actor.request_mode = Clutter.RequestMode.WIDTH_FOR_HEIGHT;
this.actor.reactive = true;
this.actor.track_hover = true;
this.actor.add_actor(this._thumbnailsBox.actor);
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this.updateSlide));
this.actor.connect('notify::hover', Lang.bind(this, this.updateSlide));
this._thumbnailsBox.actor.bind_property('visible', this.actor, 'visible', GObject.BindingFlags.SYNC_CREATE);
},
_getAlwaysZoomOut: function() {
// Always show the pager when hover, during a drag, or if workspaces are
// actually used, e.g. there are windows on more than one
let alwaysZoomOut = this.actor.hover || this.inDrag || !Meta.prefs_get_dynamic_workspaces() || global.screen.n_workspaces > 2;
if (!alwaysZoomOut) {
let monitors = Main.layoutManager.monitors;
let primary = Main.layoutManager.primaryMonitor;
/* Look for any monitor to the right of the primary, if there is
* one, we always keep zoom out, otherwise its hard to reach
* the thumbnail area without passing into the next monitor. */
for (let i = 0; i < monitors.length; i++) {
if (monitors[i].x >= primary.x + primary.width) {
alwaysZoomOut = true;
break;
}
}
}
return alwaysZoomOut;
},
getNonExpandedWidth: function() {
let child = this.actor.get_first_child();
return child.get_theme_node().get_length('visible-width');
},
getSlide: function() {
if (!this.visible)
return 0;
let alwaysZoomOut = this._getAlwaysZoomOut();
if (alwaysZoomOut)
return 1;
let child = this.actor.get_first_child();
let preferredHeight = child.get_preferred_height(-1)[1];
let expandedWidth = child.get_preferred_width(preferredHeight)[1];
return this.getNonExpandedWidth() / expandedWidth;
},
getVisibleWidth: function() {
let alwaysZoomOut = this._getAlwaysZoomOut();
if (alwaysZoomOut)
return this.parent();
else
return this.getNonExpandedWidth();
}
});
const DashSlider = new Lang.Class({
Name: 'DashSlider',
Extends: SlidingControl,
_init: function(dash) {
this.parent({ slideDirection: SlideDirection.LEFT });
this._dash = dash;
// SlideLayout reads the actor's expand flags to decide
// whether to allocate the natural size to its child, or the whole
// available allocation
this._dash.actor.x_expand = true;
this._dash.actor.y_expand = true;
this.actor.x_align = Clutter.ActorAlign.START;
this.actor.y_expand = true;
this.actor.add_actor(this._dash.actor);
this._dash.connect('icon-size-changed', Lang.bind(this, this.updateSlide));
},
getSlide: function() {
if (this.visible || this.inDrag)
return 1;
else
return 0;
},
_onWindowDragBegin: function() {
this.fadeHalf();
},
_onWindowDragEnd: function() {
this.fadeIn();
}
});
const DashSpacer = new Lang.Class({
Name: 'DashSpacer',
Extends: St.Widget,
_init: function(params) {
this.parent(params);
this._bindConstraint = null;
},
setDashActor: function(dashActor) {
if (this._bindConstraint) {
this.remove_constraint(this._bindConstraint);
this._bindConstraint = null;
}
if (dashActor) {
this._bindConstraint = new Clutter.BindConstraint({ source: dashActor,
coordinate: Clutter.BindCoordinate.SIZE });
this.add_constraint(this._bindConstraint);
}
},
vfunc_get_preferred_width: function(forHeight) {
let box = this.get_allocation_box();
let minWidth = this.parent(forHeight)[0];
let natWidth = box.x2 - box.x1;
return [minWidth, natWidth];
},
vfunc_get_preferred_height: function(forWidth) {
let box = this.get_allocation_box();
let minHeight = this.parent(forWidth)[0];
let natHeight = box.y2 - box.y1;
return [minHeight, natHeight];
}
});
const MessagesIndicator = new Lang.Class({
Name: 'MessagesIndicator',
_init: function(viewSelector) {
this._count = 0;
this._sources = [];
this._viewSelector = viewSelector;
this._container = new St.BoxLayout({ style_class: 'messages-indicator-contents',
reactive: true,
track_hover: true,
x_expand: true,
y_expand: true,
x_align: Clutter.ActorAlign.CENTER });
this._icon = new St.Icon({ icon_name: 'user-idle-symbolic',
icon_size: 16 });
this._container.add_actor(this._icon);
this._label = new St.Label();
this._container.add_actor(this._label);
this._highlight = new St.Widget({ style_class: 'messages-indicator-highlight',
x_expand: true,
y_expand: true,
y_align: Clutter.ActorAlign.END,
visible: false });
this._container.connect('notify::hover', Lang.bind(this,
function() {
this._highlight.visible = this._container.hover;
}));
let clickAction = new Clutter.ClickAction();
this._container.add_action(clickAction);
clickAction.connect('clicked', Lang.bind(this,
function() {
Main.messageTray.openTray();
}));
Main.messageTray.connect('showing', Lang.bind(this,
function() {
this._highlight.visible = false;
this._container.hover = false;
}));
let layout = new Clutter.BinLayout();
this.actor = new St.Widget({ layout_manager: layout,
style_class: 'messages-indicator',
y_expand: true,
y_align: Clutter.ActorAlign.END,
visible: false });
this.actor.add_actor(this._container);
this.actor.add_actor(this._highlight);
Main.messageTray.connect('source-added', Lang.bind(this, this._onSourceAdded));
Main.messageTray.connect('source-removed', Lang.bind(this, this._onSourceRemoved));
let sources = Main.messageTray.getSources();
sources.forEach(Lang.bind(this, function(source) { this._onSourceAdded(null, source); }));
this._viewSelector.connect('page-changed', Lang.bind(this, this._updateVisibility));
Main.overview.connect('showing', Lang.bind(this, this._updateVisibility));
},
_onSourceAdded: function(tray, source) {
if (source.trayIcon)
return;
if (source.isTransient)
return;
source.connect('count-updated', Lang.bind(this, this._updateCount));
this._sources.push(source);
this._updateCount();
},
_onSourceRemoved: function(tray, source) {
this._sources.splice(this._sources.indexOf(source), 1);
this._updateCount();
},
_updateCount: function() {
let count = 0;
let hasChats = false;
this._sources.forEach(Lang.bind(this,
function(source) {
count += source.indicatorCount;
hasChats |= source.isChat;
}));
this._count = count;
this._label.text = ngettext("%d new message",
"%d new messages",
count).format(count);
this._icon.visible = hasChats;
this._updateVisibility();
},
_updateVisibility: function() {
let activePage = this._viewSelector.getActivePage();
let visible = ((this._count > 0) && (activePage == ViewSelector.ViewPage.WINDOWS));
this.actor.visible = visible;
}
});
const ControlsManager = new Lang.Class({
Name: 'ControlsManager',
_init: function(searchEntry) {
this.dash = new Dash.Dash();
this._dashSlider = new DashSlider(this.dash);
this._dashSpacer = new DashSpacer();
this._dashSpacer.setDashActor(this._dashSlider.actor);
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
this._thumbnailsSlider = new ThumbnailsSlider(this._thumbnailsBox);
this.viewSelector = new ViewSelector.ViewSelector(searchEntry,
this.dash.showAppsButton);
this.viewSelector.connect('page-changed', Lang.bind(this, this._setVisibility));
this.viewSelector.connect('page-empty', Lang.bind(this, this._onPageEmpty));
this._indicator = new MessagesIndicator(this.viewSelector);
this.indicatorActor = this._indicator.actor;
this.actor = new St.Widget({ layout_manager: new Clutter.BinLayout(),
reactive: true,
x_expand: true, y_expand: true,
clip_to_allocation: true });
this._group = new St.BoxLayout({ name: 'overview-group',
x_expand: true, y_expand: true });
this.actor.add_actor(this._group);
this.actor.add_actor(this._dashSlider.actor);
this._group.add_actor(this._dashSpacer);
this._group.add(this.viewSelector.actor, { x_fill: true,
expand: true });
this._group.add_actor(this._thumbnailsSlider.actor);
this._group.connect('notify::allocation', Lang.bind(this, this._updateWorkspacesGeometry));
Main.overview.connect('showing', Lang.bind(this, this._updateSpacerVisibility));
Main.overview.connect('item-drag-begin', Lang.bind(this,
function() {
let activePage = this.viewSelector.getActivePage();
if (activePage != ViewSelector.ViewPage.WINDOWS)
this.viewSelector.fadeHalf();
}));
Main.overview.connect('item-drag-end', Lang.bind(this,
function() {
this.viewSelector.fadeIn();
}));
Main.overview.connect('item-drag-cancelled', Lang.bind(this,
function() {
this.viewSelector.fadeIn();
}));
},
_updateWorkspacesGeometry: function() {
let [x, y] = this.actor.get_transformed_position();
let [width, height] = this.actor.get_transformed_size();
let geometry = { x: x, y: y, width: width, height: height };
let spacing = this.actor.get_theme_node().get_length('spacing');
let dashWidth = this._dashSlider.getVisibleWidth() + spacing;
let thumbnailsWidth = this._thumbnailsSlider.getNonExpandedWidth() + spacing;
geometry.width -= dashWidth;
geometry.width -= thumbnailsWidth;
if (this.actor.get_text_direction() == Clutter.TextDirection.LTR)
geometry.x += dashWidth;
else
geometry.x += thumbnailsWidth;
this.viewSelector.setWorkspacesFullGeometry(geometry);
},
_setVisibility: function() {
// Ignore the case when we're leaving the overview, since
// actors will be made visible again when entering the overview
// next time, and animating them while doing so is just
// unnecessary noise
if (!Main.overview.visible ||
(Main.overview.animationInProgress && !Main.overview.visibleTarget))
return;
let activePage = this.viewSelector.getActivePage();
let dashVisible = (activePage == ViewSelector.ViewPage.WINDOWS ||
activePage == ViewSelector.ViewPage.APPS);
let thumbnailsVisible = (activePage == ViewSelector.ViewPage.WINDOWS);
if (dashVisible)
this._dashSlider.slideIn();
else
this._dashSlider.slideOut();
if (thumbnailsVisible)
this._thumbnailsSlider.slideIn();
else
this._thumbnailsSlider.slideOut();
},
_updateSpacerVisibility: function() {
if (Main.overview.animationInProgress && !Main.overview.visibleTarget)
return;
let activePage = this.viewSelector.getActivePage();
this._dashSpacer.visible = (activePage == ViewSelector.ViewPage.WINDOWS);
},
_onPageEmpty: function() {
this._dashSlider.pageEmpty();
this._thumbnailsSlider.pageEmpty();
this._updateSpacerVisibility();
}
});

View File

@ -15,13 +15,14 @@ const Signals = imports.signals;
const Atk = imports.gi.Atk;
const Animation = imports.ui.animation;
const Config = imports.misc.config;
const CtrlAltTab = imports.ui.ctrlAltTab;
const DND = imports.ui.dnd;
const Layout = imports.ui.layout;
const Overview = imports.ui.overview;
const PopupMenu = imports.ui.popupMenu;
const PanelMenu = imports.ui.panelMenu;
const RemoteMenu = imports.ui.remoteMenu;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
@ -29,7 +30,6 @@ const PANEL_ICON_SIZE = 24;
const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
const SPINNER_ANIMATION_TIME = 0.2;
// To make sure the panel corners blend nicely with the panel,
@ -75,81 +75,6 @@ function _unpremultiply(color) {
blue: blue, alpha: color.alpha });
};
const Animation = new Lang.Class({
Name: 'Animation',
_init: function(filename, width, height, speed) {
this.actor = new St.Bin();
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._speed = speed;
this._isLoaded = false;
this._isPlaying = false;
this._timeoutId = 0;
this._frame = 0;
this._animations = St.TextureCache.get_default().load_sliced_image (filename, width, height,
Lang.bind(this, this._animationsLoaded));
this.actor.set_child(this._animations);
},
play: function() {
if (this._isLoaded && this._timeoutId == 0) {
if (this._frame == 0)
this._showFrame(0);
this._timeoutId = Mainloop.timeout_add(this._speed, Lang.bind(this, this._update));
}
this._isPlaying = true;
},
stop: function() {
if (this._timeoutId > 0) {
Mainloop.source_remove(this._timeoutId);
this._timeoutId = 0;
}
this._isPlaying = false;
},
_showFrame: function(frame) {
let oldFrameActor = this._animations.get_child_at_index(this._frame);
if (oldFrameActor)
oldFrameActor.hide();
this._frame = (frame % this._animations.get_n_children());
let newFrameActor = this._animations.get_child_at_index(this._frame);
if (newFrameActor)
newFrameActor.show();
},
_update: function() {
this._showFrame(this._frame + 1);
return true;
},
_animationsLoaded: function() {
this._isLoaded = true;
if (this._isPlaying)
this.play();
},
_onDestroy: function() {
this.stop();
}
});
const AnimatedIcon = new Lang.Class({
Name: 'AnimatedIcon',
Extends: Animation,
_init: function(name, size) {
this.parent(global.datadir + '/theme/' + name, size, size, ANIMATED_ICON_UPDATE_TIMEOUT);
}
});
const TextShadower = new Lang.Class({
Name: 'TextShadower',
@ -203,7 +128,7 @@ const TextShadower = new Lang.Class({
let child = children[i];
let childBox = new Clutter.ActorBox();
// The order of the labels here is arbitrary, except
// we know the "real" label is at the end because Clutter.Group
// we know the "real" label is at the end because Clutter.Actor
// sorts by Z order
switch (i) {
case 0: // top
@ -259,6 +184,7 @@ const AppMenuButton = new Lang.Class({
this._actionGroupNotifyId = 0;
let bin = new St.Bin({ name: 'appMenu' });
bin.connect('style-changed', Lang.bind(this, this._onStyleChanged));
this.actor.add_actor(bin);
this.actor.bind_property("reactive", this.actor, "can-focus", 0);
@ -289,33 +215,31 @@ const AppMenuButton = new Lang.Class({
this._visible = !Main.overview.visible;
if (!this._visible)
this.actor.hide();
Main.overview.connect('hiding', Lang.bind(this, function () {
this._overviewHidingId = Main.overview.connect('hiding', Lang.bind(this, function () {
this.show();
}));
Main.overview.connect('showing', Lang.bind(this, function () {
this._overviewShowingId = Main.overview.connect('showing', Lang.bind(this, function () {
this.hide();
}));
this._stop = true;
this._spinner = new AnimatedIcon('process-working.svg',
PANEL_ICON_SIZE);
this._container.add_actor(this._spinner.actor);
this._spinner.actor.hide();
this._spinner.actor.lower_bottom();
this._spinner = null;
let tracker = Shell.WindowTracker.get_default();
let appSys = Shell.AppSystem.get_default();
tracker.connect('notify::focus-app', Lang.bind(this, this._focusAppChanged));
appSys.connect('app-state-changed', Lang.bind(this, this._onAppStateChanged));
global.window_manager.connect('switch-workspace', Lang.bind(this, this._sync));
this._focusAppNotifyId =
tracker.connect('notify::focus-app', Lang.bind(this, this._focusAppChanged));
this._appStateChangedSignalId =
appSys.connect('app-state-changed', Lang.bind(this, this._onAppStateChanged));
this._switchWorkspaceNotifyId =
global.window_manager.connect('switch-workspace', Lang.bind(this, this._sync));
this._sync();
},
show: function() {
if (this._visible || Main.screenShield.locked)
if (this._visible)
return;
this._visible = true;
@ -355,19 +279,34 @@ const AppMenuButton = new Lang.Class({
onCompleteScope: this });
},
_onStyleChanged: function(actor) {
let node = actor.get_theme_node();
let [success, icon] = node.lookup_url('spinner-image', false);
if (!success || this._spinnerIcon == icon)
return;
this._spinnerIcon = icon;
this._spinner = new Animation.AnimatedIcon(this._spinnerIcon, PANEL_ICON_SIZE);
this._container.add_actor(this._spinner.actor);
this._spinner.actor.hide();
this._spinner.actor.lower_bottom();
},
_onIconBoxStyleChanged: function() {
let node = this._iconBox.get_theme_node();
this._iconBottomClip = node.get_length('app-icon-bottom-clip');
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() {
@ -386,6 +325,10 @@ const AppMenuButton = new Lang.Class({
this._stop = true;
this.actor.reactive = true;
if (this._spinner == null)
return;
Tweener.addTween(this._spinner.actor,
{ opacity: 0,
time: SPINNER_ANIMATION_TIME,
@ -402,6 +345,10 @@ const AppMenuButton = new Lang.Class({
startAnimation: function() {
this._stop = false;
this.actor.reactive = false;
if (this._spinner == null)
return;
this._spinner.play();
this._spinner.actor.show();
},
@ -464,6 +411,9 @@ const AppMenuButton = new Lang.Class({
}
this._label.actor.allocate(childBox, flags);
if (this._spinner == null)
return;
if (direction == Clutter.TextDirection.LTR) {
childBox.x1 = Math.floor(iconWidth / 2) + this._label.actor.width;
childBox.x2 = childBox.x1 + this._spinner.actor.width;
@ -502,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();
@ -547,14 +497,20 @@ const AppMenuButton = new Lang.Class({
}
if (targetApp == this._targetApp) {
if (targetApp && targetApp.get_state() != Shell.AppState.STARTING) {
if (targetApp &&
targetApp.get_state() != Shell.AppState.STARTING &&
targetApp.get_state() != Shell.AppState.BUSY) {
this.stopAnimation();
this._maybeSetMenu();
} else if (targetApp &&
targetApp.get_state() == Shell.AppState.BUSY) {
this.startAnimation();
}
return;
}
this._spinner.actor.hide();
if (this._spinner)
this._spinner.actor.hide();
if (this._iconBox.child != null)
this._iconBox.child.destroy();
this._iconBox.hide();
@ -573,15 +529,14 @@ 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)
if (targetApp.get_state() == Shell.AppState.STARTING ||
targetApp.get_state() == Shell.AppState.BUSY)
this.startAnimation();
else
this._maybeSetMenu();
@ -593,11 +548,16 @@ const AppMenuButton = new Lang.Class({
let menu;
if (this._targetApp.action_group && this._targetApp.menu) {
if (this.menu instanceof PopupMenu.RemoteMenu &&
if (this.menu instanceof RemoteMenu.RemoteMenu &&
this.menu.actionGroup == this._targetApp.action_group)
return;
menu = new PopupMenu.RemoteMenu(this.actor, this._targetApp.menu, this._targetApp.action_group);
menu = new RemoteMenu.RemoteMenu(this.actor, this._targetApp.menu, this._targetApp.action_group);
menu.connect('activate', Lang.bind(this, function() {
let win = this._targetApp.get_windows()[0];
win.check_alive(global.get_current_time());
}));
} else {
if (this.menu.isDummyQuitMenu)
return;
@ -612,6 +572,33 @@ const AppMenuButton = new Lang.Class({
this.setMenu(menu);
this._menuManager.addMenu(menu);
},
destroy: function() {
if (this._appStateChangedSignalId > 0) {
let appSys = Shell.AppSystem.get_default();
appSys.disconnect(this._appStateChangedSignalId);
this._appStateChangedSignalId = 0;
}
if (this._focusAppNotifyId > 0) {
let tracker = Shell.WindowTracker.get_default();
tracker.disconnect(this._focusAppNotifyId);
this._focusAppNotifyId = 0;
}
if (this._overviewHidingId > 0) {
Main.overview.disconnect(this._overviewHidingId);
this._overviewHidingId = 0;
}
if (this._overviewShowingId > 0) {
Main.overview.disconnect(this._overviewShowingId);
this._overviewShowingId = 0;
}
if (this._switchWorkspaceNotifyId > 0) {
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
this._switchWorkspaceNotifyId = 0;
}
this.parent();
}
});
@ -625,23 +612,15 @@ const ActivitiesButton = new Lang.Class({
this.parent(0.0, null, true);
this.actor.accessible_role = Atk.Role.TOGGLE_BUTTON;
let container = new Shell.GenericContainer();
container.connect('get-preferred-width', Lang.bind(this, this._containerGetPreferredWidth));
container.connect('get-preferred-height', Lang.bind(this, this._containerGetPreferredHeight));
container.connect('allocate', Lang.bind(this, this._containerAllocate));
this.actor.add_actor(container);
this.actor.name = 'panelActivities';
/* Translators: If there is no suitable word for "Activities"
in your language, you can use the word for "Overview". */
this._label = new St.Label({ text: _("Activities") });
container.add_actor(this._label);
this.actor.add_actor(this._label);
this.actor.label_actor = this._label;
this.hotCorner = new Layout.HotCorner();
container.add_actor(this.hotCorner.actor);
this.actor.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
this.actor.connect_after('button-release-event', Lang.bind(this, this._onButtonRelease));
this.actor.connect_after('key-release-event', Lang.bind(this, this._onKeyRelease));
@ -656,44 +635,6 @@ const ActivitiesButton = new Lang.Class({
}));
this._xdndTimeOut = 0;
// Since the hot corner uses stage coordinates, Clutter won't
// queue relayouts for us when the panel moves. Queue a relayout
// when that happens.
Main.layoutManager.connect('panel-box-changed', Lang.bind(this, function() {
container.queue_relayout();
}));
},
_containerGetPreferredWidth: function(actor, forHeight, alloc) {
[alloc.min_size, alloc.natural_size] = this._label.get_preferred_width(forHeight);
},
_containerGetPreferredHeight: function(actor, forWidth, alloc) {
[alloc.min_size, alloc.natural_size] = this._label.get_preferred_height(forWidth);
},
_containerAllocate: function(actor, box, flags) {
this._label.allocate(box, flags);
// The hot corner needs to be outside any padding/alignment
// that has been imposed on us
let primary = Main.layoutManager.primaryMonitor;
let hotBox = new Clutter.ActorBox();
let ok, x, y;
if (actor.get_text_direction() == Clutter.TextDirection.LTR) {
[ok, x, y] = actor.transform_stage_point(primary.x, primary.y)
} else {
[ok, x, y] = actor.transform_stage_point(primary.x + primary.width, primary.y);
// hotCorner.actor has northeast gravity, so we don't need
// to adjust x for its width
}
hotBox.x1 = Math.round(x);
hotBox.x2 = hotBox.x1 + this.hotCorner.actor.width;
hotBox.y1 = Math.round(y);
hotBox.y2 = hotBox.y1 + this.hotCorner.actor.height;
this.hotCorner.actor.allocate(hotBox, flags);
},
handleDragOver: function(source, actor, x, y, time) {
@ -703,14 +644,14 @@ const ActivitiesButton = new Lang.Class({
if (this._xdndTimeOut != 0)
Mainloop.source_remove(this._xdndTimeOut);
this._xdndTimeOut = Mainloop.timeout_add(BUTTON_DND_ACTIVATION_TIMEOUT,
Lang.bind(this, this._xdndShowOverview, actor));
Lang.bind(this, this._xdndToggleOverview, actor));
return DND.DragMotionResult.CONTINUE;
},
_onCapturedEvent: function(actor, event) {
if (event.type() == Clutter.EventType.BUTTON_PRESS) {
if (!this.hotCorner.shouldToggleOverviewOnClick())
if (!Main.overview.shouldToggleByCornerOrButton())
return true;
}
return false;
@ -727,16 +668,12 @@ const ActivitiesButton = new Lang.Class({
}
},
_xdndShowOverview: function(actor) {
_xdndToggleOverview: function(actor) {
let [x, y, mask] = global.get_pointer();
let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
if (pickedActor == this.actor) {
if (!Main.overview.visible && !Main.overview.animationInProgress) {
Main.overview.showTemporarily();
Main.overview.beginItemDrag(actor);
}
}
if (pickedActor == this.actor && Main.overview.shouldToggleByCornerOrButton())
Main.overview.toggle();
Mainloop.source_remove(this._xdndTimeOut);
this._xdndTimeOut = 0;
@ -746,12 +683,9 @@ const ActivitiesButton = new Lang.Class({
const PanelCorner = new Lang.Class({
Name: 'PanelCorner',
_init: function(box, side) {
_init: function(side) {
this._side = side;
this._box = box;
this._box.connect('style-changed', Lang.bind(this, this._boxStyleChanged));
this.actor = new St.DrawingArea({ style_class: 'panel-corner' });
this.actor.connect('style-changed', Lang.bind(this, this._styleChanged));
this.actor.connect('repaint', Lang.bind(this, this._repaint));
@ -807,12 +741,12 @@ const PanelCorner = new Lang.Class({
return children[index];
},
_boxStyleChanged: function() {
setStyleParent: function(box) {
let side = this._side;
let rtlAwareContainer = this._box instanceof St.BoxLayout;
let rtlAwareContainer = box instanceof St.BoxLayout;
if (rtlAwareContainer &&
this._box.get_text_direction() == Clutter.TextDirection.RTL) {
box.get_text_direction() == Clutter.TextDirection.RTL) {
if (this._side == St.Side.LEFT)
side = St.Side.RIGHT;
else if (this._side == St.Side.RIGHT)
@ -821,9 +755,9 @@ const PanelCorner = new Lang.Class({
let button;
if (side == St.Side.LEFT)
button = this._findLeftmostButton(this._box);
button = this._findLeftmostButton(box);
else if (side == St.Side.RIGHT)
button = this._findRightmostButton(this._box);
button = this._findRightmostButton(box);
if (button) {
if (this._button && this._buttonStyleChangedSignalId) {
@ -850,7 +784,7 @@ const PanelCorner = new Lang.Class({
// The corner doesn't support theme transitions, so override
// the .panel-button default
button.style = 'transition-duration: 0';
button.style = 'transition-duration: 0ms';
}
},
@ -863,8 +797,8 @@ const PanelCorner = new Lang.Class({
let backgroundColor = node.get_color('-panel-corner-background-color');
let borderColor = node.get_color('-panel-corner-border-color');
let noOverlap = borderColor.alpha == 0;
let offsetY = noOverlap ? borderWidth : 0;
let overlap = borderColor.alpha != 0;
let offsetY = overlap ? 0 : borderWidth;
let cr = this.actor.get_context();
cr.setOperator(Cairo.Operator.SOURCE);
@ -888,17 +822,18 @@ const PanelCorner = new Lang.Class({
Clutter.cairo_set_source_color(cr, over);
cr.fill();
if (noOverlap)
return;
if (overlap) {
let offset = borderWidth;
Clutter.cairo_set_source_color(cr, backgroundColor);
let offset = borderWidth;
Clutter.cairo_set_source_color(cr, backgroundColor);
cr.save();
cr.translate(xOffsetDirection * offset, - offset);
cr.appendPath(savedPath);
cr.fill();
cr.restore();
}
cr.save();
cr.translate(xOffsetDirection * offset, - offset);
cr.appendPath(savedPath);
cr.fill();
cr.restore();
cr.$dispose();
},
_styleChanged: function() {
@ -917,13 +852,13 @@ const PANEL_ITEM_IMPLEMENTATIONS = {
'appMenu': AppMenuButton,
'dateMenu': imports.ui.dateMenu.DateMenuButton,
'a11y': imports.ui.status.accessibility.ATIndicator,
'a11yGreeter': imports.ui.status.accessibility.ATGreeterIndicator,
'volume': imports.ui.status.volume.Indicator,
'battery': imports.ui.status.power.Indicator,
'lockScreen': imports.ui.status.lockScreenMenu.Indicator,
'logo': imports.gdm.loginDialog.LogoMenuButton,
'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)
@ -949,7 +884,7 @@ const Panel = new Lang.Class({
this.statusArea = {};
this.menuManager = new PopupMenu.PopupMenuManager(this);
this.menuManager = new PopupMenu.PopupMenuManager(this, { keybindingMode: Shell.KeyBindingMode.TOPBAR_POPUP });
this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
this.actor.add_actor(this._leftBox);
@ -958,17 +893,10 @@ const Panel = new Lang.Class({
this._rightBox = new St.BoxLayout({ name: 'panelRight' });
this.actor.add_actor(this._rightBox);
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL)
this._leftCorner = new PanelCorner(this._rightBox, St.Side.LEFT);
else
this._leftCorner = new PanelCorner(this._leftBox, St.Side.LEFT);
this._leftCorner = new PanelCorner(St.Side.LEFT);
this.actor.add_actor(this._leftCorner.actor);
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL)
this._rightCorner = new PanelCorner(this._leftBox, St.Side.RIGHT);
else
this._rightCorner = new PanelCorner(this._rightBox, St.Side.RIGHT);
this._rightCorner = new PanelCorner(St.Side.RIGHT);
this.actor.add_actor(this._rightCorner.actor);
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
@ -976,6 +904,13 @@ const Panel = new Lang.Class({
this.actor.connect('allocate', Lang.bind(this, this._allocate));
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
Main.overview.connect('showing', Lang.bind(this, function () {
this.actor.add_style_pseudo_class('overview');
}));
Main.overview.connect('hiding', Lang.bind(this, function () {
this.actor.remove_style_pseudo_class('overview');
}));
Main.layoutManager.panelBox.add(this.actor);
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'emblem-system-symbolic',
{ sortGroup: CtrlAltTab.SortGroup.TOP });
@ -1062,6 +997,9 @@ const Panel = new Lang.Class({
},
_onButtonPress: function(actor, event) {
if (Main.modalCount > 0)
return false;
if (event.get_source() != actor)
return false;
@ -1100,17 +1038,18 @@ const Panel = new Lang.Class({
return true;
},
openAppMenu: function() {
toggleAppMenu: function() {
let indicator = this.statusArea.appMenu;
if (!indicator) // appMenu not supported by current session mode
return;
let menu = indicator.menu;
if (!indicator.actor.reactive || menu.isOpen)
if (!indicator.actor.reactive)
return;
menu.open();
menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
menu.toggle();
if (menu.isOpen)
menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
},
set boxOpacity(value) {
@ -1141,17 +1080,13 @@ const Panel = new Lang.Class({
this._sessionStyle = Main.sessionMode.panelStyle;
if (this._sessionStyle)
this._addStyleClassName(this._sessionStyle);
},
_initBox: function(elements, box) {
for (let i = 0; i < elements.length; i++) {
let role = elements[i];
let constructor = PANEL_ITEM_IMPLEMENTATIONS[role];
if (!constructor) {
// panel icon is not supported (can happen for
// bluetooth or network)
continue;
}
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL) {
this._leftCorner.setStyleParent(this._rightBox);
this._rightCorner.setStyleParent(this._leftBox);
} else {
this._leftCorner.setStyleParent(this._leftBox);
this._rightCorner.setStyleParent(this._rightBox);
}
},

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,7 +184,18 @@ 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;
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
let group = global.focus_manager.get_group(this.actor);
@ -205,10 +217,8 @@ const Button = new Lang.Class({
// Setting the max-height won't do any good if the minimum height of the
// menu is higher then the screen; it's useful if part of the menu is
// scrollable so the minimum height is smaller than the natural height
let monitor = Main.layoutManager.primaryMonitor;
this.menu.actor.style = ('max-height: ' +
Math.round(monitor.height - Main.panel.actor.height) +
'px;');
let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
this.menu.actor.style = ('max-height: ' + Math.round(workArea.height) + 'px;');
},
destroy: function() {
@ -244,18 +254,21 @@ const SystemStatusButton = new Lang.Class({
this.setIcon(iconName);
},
get icons() {
return this._box.get_children();
},
addIcon: function(gicon) {
let icon = new St.Icon({ gicon: gicon,
style_class: 'system-status-icon' });
this._box.add_actor(icon);
this.emit('icons-changed');
return icon;
},
setIcon: function(iconName) {
// Need to first add a NULL GIcon and then set icon_name, to ensure
// compatibility with -symbolic fallbacks
if (!this.mainIcon)
this.mainIcon = this.addIcon(null);
this.mainIcon.icon_name = iconName;

View File

@ -41,10 +41,9 @@ const PointerWatcher = new Lang.Class({
Name: 'PointerWatcher',
_init: function() {
let idleMonitor = new GnomeDesktop.IdleMonitor();
idleMonitor.connect('became-active', Lang.bind(this, this._onIdleMonitorBecameActive));
idleMonitor.add_watch(IDLE_TIME, Lang.bind(this, this._onIdleMonitorBecameIdle));
this._idle = idleMonitor.get_idletime() > IDLE_TIME;
this._idleMonitor = new GnomeDesktop.IdleMonitor();
this._idleMonitor.add_idle_watch(IDLE_TIME, Lang.bind(this, this._onIdleMonitorBecameIdle));
this._idle = this._idleMonitor.get_idletime() > IDLE_TIME;
this._watches = [];
this.pointerX = null;
this.pointerY = null;
@ -87,6 +86,7 @@ const PointerWatcher = new Lang.Class({
_onIdleMonitorBecameIdle: function(monitor) {
this._idle = true;
this._idleMonitor.add_user_active_watch(Lang.bind(this, this._onIdleMonitorBecameActive));
this._updateTimeout();
},

File diff suppressed because it is too large Load Diff

199
js/ui/remoteMenu.js Normal file
View File

@ -0,0 +1,199 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Atk = imports.gi.Atk;
const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const ShellMenu = imports.gi.ShellMenu;
const St = imports.gi.St;
const PopupMenu = imports.ui.popupMenu;
function stripMnemonics(label) {
if (!label)
return '';
// remove all underscores that are not followed by another underscore
return label.replace(/_([^_])/, '$1');
}
function _insertItem(menu, trackerItem, position) {
let mapper;
if (trackerItem.get_is_separator())
mapper = new RemoteMenuSeparatorItemMapper(trackerItem);
else if (trackerItem.get_has_submenu())
mapper = new RemoteMenuSubmenuItemMapper(trackerItem);
else
mapper = new RemoteMenuItemMapper(trackerItem);
let item = mapper.menuItem;
menu.addMenuItem(item, position);
}
function _removeItem(menu, position) {
let items = menu._getMenuItems();
items[position].destroy();
}
const RemoteMenuSeparatorItemMapper = new Lang.Class({
Name: 'RemoteMenuSeparatorItemMapper',
_init: function(trackerItem) {
this._trackerItem = trackerItem;
this.menuItem = new PopupMenu.PopupSeparatorMenuItem();
this._trackerItem.connect('notify::label', Lang.bind(this, this._updateLabel));
this._updateLabel();
this.menuItem.connect('destroy', function() {
trackerItem.run_dispose();
});
},
_updateLabel: function() {
this.menuItem.label.text = stripMnemonics(this._trackerItem.label);
},
});
const RequestSubMenu = new Lang.Class({
Name: 'RequestSubMenu',
Extends: PopupMenu.PopupSubMenuMenuItem,
_init: function() {
this.parent('');
this._requestOpen = false;
},
_setOpenState: function(open) {
this.emit('request-open', open);
this._requestOpen = open;
},
_getOpenState: function() {
return this._requestOpen;
},
});
const RemoteMenuSubmenuItemMapper = new Lang.Class({
Name: 'RemoteMenuSubmenuItemMapper',
_init: function(trackerItem) {
this._trackerItem = trackerItem;
this.menuItem = new RequestSubMenu();
this._trackerItem.connect('notify::label', Lang.bind(this, this._updateLabel));
this._updateLabel();
this._tracker = Shell.MenuTracker.new_for_item_submenu(this._trackerItem,
_insertItem.bind(null, this.menuItem.menu),
_removeItem.bind(null, this.menuItem.menu));
this.menuItem.connect('request-open', Lang.bind(this, function(menu, open) {
this._trackerItem.request_submenu_shown(open);
}));
this._trackerItem.connect('notify::submenu-shown', Lang.bind(this, function() {
this.menuItem.setSubmenuShown(this._trackerItem.get_submenu_shown());
}));
this.menuItem.connect('destroy', function() {
trackerItem.run_dispose();
});
},
destroy: function() {
this._tracker.destroy();
this.parent();
},
_updateLabel: function() {
this.menuItem.label.text = stripMnemonics(this._trackerItem.label);
},
});
const RemoteMenuItemMapper = new Lang.Class({
Name: 'RemoteMenuItemMapper',
_init: function(trackerItem) {
this._trackerItem = trackerItem;
this.menuItem = new PopupMenu.PopupBaseMenuItem();
this._label = new St.Label();
this.menuItem.addActor(this._label);
this.menuItem.actor.label_actor = this._label;
this.menuItem.connect('activate', Lang.bind(this, function() {
this._trackerItem.activated();
}));
this._trackerItem.bind_property('visible', this.menuItem.actor, 'visible', GObject.BindingFlags.SYNC_CREATE);
this._trackerItem.connect('notify::label', Lang.bind(this, this._updateLabel));
this._trackerItem.connect('notify::sensitive', Lang.bind(this, this._updateSensitivity));
this._trackerItem.connect('notify::role', Lang.bind(this, this._updateRole));
this._trackerItem.connect('notify::toggled', Lang.bind(this, this._updateDecoration));
this._updateLabel();
this._updateSensitivity();
this._updateRole();
this.menuItem.connect('destroy', function() {
trackerItem.run_dispose();
});
},
_updateLabel: function() {
this._label.text = stripMnemonics(this._trackerItem.label);
},
_updateSensitivity: function() {
this.menuItem.setSensitive(this._trackerItem.sensitive);
},
_updateDecoration: function() {
let ornamentForRole = {};
ornamentForRole[ShellMenu.MenuTrackerItemRole.RADIO] = PopupMenu.Ornament.DOT;
ornamentForRole[ShellMenu.MenuTrackerItemRole.CHECK] = PopupMenu.Ornament.CHECK;
let ornament = PopupMenu.Ornament.NONE;
if (this._trackerItem.toggled)
ornament = ornamentForRole[this._trackerItem.role];
this.menuItem.setOrnament(ornament);
},
_updateRole: function() {
let a11yRoles = {};
a11yRoles[ShellMenu.MenuTrackerItemRole.NORMAL] = Atk.Role.MENU_ITEM;
a11yRoles[ShellMenu.MenuTrackerItemRole.RADIO] = Atk.Role.RADIO_MENU_ITEM;
a11yRoles[ShellMenu.MenuTrackerItemRole.CHECK] = Atk.Role.CHECK_MENU_ITEM;
let a11yRole = a11yRoles[this._trackerItem.role];
this.menuItem.actor.accessible_role = a11yRole;
this._updateDecoration();
},
});
const RemoteMenu = new Lang.Class({
Name: 'RemoteMenu',
Extends: PopupMenu.PopupMenu,
_init: function(sourceActor, model, actionGroup) {
this.parent(sourceActor, 0.0, St.Side.TOP);
this._model = model;
this._actionGroup = actionGroup;
this._tracker = Shell.MenuTracker.new(this._actionGroup,
this._model,
null, /* action namespace */
_insertItem.bind(null, this),
_removeItem.bind(null, this));
},
destroy: function() {
this._tracker.destroy();
this.parent();
},
});

View File

@ -1,11 +1,12 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const GdkPixbuf = imports.gi.GdkPixbuf;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
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';
@ -50,6 +51,7 @@ const SearchProvider2Iface = <interface name="org.gnome.Shell.SearchProvider2">
</method>
<method name="LaunchSearch">
<arg type="as" direction="in" />
<arg type="u" direction="in" />
</method>
</interface>;
@ -57,116 +59,118 @@ 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 (version >= 2)
remoteProvider = new RemoteSearchProvider2(appInfo, busName, objectPath);
else
remoteProvider = new RemoteSearchProvider(appInfo, busName, objectPath);
if (objectPaths[objectPath])
return;
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 searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
let sortOrder = searchSettings.get_strv('sort-order');
let numSorted = sortOrder.length;
loadState.loadedProviders.sort(
function(providerA, providerB) {
let idxA, idxB;
let appIdA, appIdB;
appIdA = providerA.appInfo.get_id();
appIdB = providerB.appInfo.get_id();
idxA = sortOrder.indexOf(appIdA);
idxB = sortOrder.indexOf(appIdB);
// if no provider is found in the order, use alphabetical order
if ((idxA == -1) && (idxB == -1))
return GLib.utf8_collate(providerA.title, providerB.title);
if (numSorted > 1) {
// if providerA is the last, it goes after everything
if ((idxA + 1) == numSorted)
return 1;
// if providerB is the last, it goes after everything
else if ((idxB + 1) == numSorted)
return -1;
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;
}
// if providerA isn't found, it's sorted after providerB
if (idxA == -1)
return 1;
let version = '1';
try {
version = keyfile.get_string(group, 'Version');
} catch (e) {
// ignore error
}
// if providerB isn't found, it's sorted after providerA
if (idxB == -1)
return -1;
if (version >= 2)
remoteProvider = new RemoteSearchProvider2(appInfo, busName, objectPath);
else
remoteProvider = new RemoteSearchProvider(appInfo, busName, objectPath);
// finally, if both providers are found, return their order in the list
return (idxA - idxB);
});
objectPaths[objectPath] = remoteProvider;
loadedProviders.push(remoteProvider);
} catch(e) {
log('Failed to add search provider %s: %s'.format(path, e.toString()));
}
}
loadState.loadedProviders.forEach(
function(provider) {
loadState.addProviderCallback(provider);
});
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');
loadedProviders.sort(function(providerA, providerB) {
let idxA, idxB;
let appIdA, appIdB;
appIdA = providerA.appInfo.get_id();
appIdB = providerB.appInfo.get_id();
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();
return GLib.utf8_collate(nameA, nameB);
}
// 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;
// finally, if both providers are found, return their order in the list
return (idxA - idxB);
});
loadedProviders.forEach(addProviderCallback);
}
const RemoteSearchProvider = new Lang.Class({
Name: 'RemoteSearchProvider',
Extends: Search.SearchProvider,
_init: function(appInfo, dbusName, dbusPath, proxyType) {
if (!proxyType)
@ -175,7 +179,10 @@ const RemoteSearchProvider = new Lang.Class({
this.proxy = new proxyType(Gio.DBus.session,
dbusName, dbusPath, Lang.bind(this, this._onProxyConstructed));
this.parent(appInfo.get_name().toUpperCase(), appInfo, true);
this.appInfo = appInfo;
this.id = appInfo.get_id();
this.isRemoteProvider = true;
this._cancellable = new Gio.Cancellable();
},
@ -184,26 +191,26 @@ const RemoteSearchProvider = new Lang.Class({
},
createIcon: function(size, meta) {
if (meta['gicon']) {
return new St.Icon({ gicon: Gio.icon_new_for_string(meta['gicon']),
icon_size: size });
let gicon;
if (meta['icon']) {
gicon = Gio.icon_deserialize(meta['icon']);
} else if (meta['gicon']) {
gicon = Gio.icon_new_for_string(meta['gicon']);
} else if (meta['icon-data']) {
let [width, height, rowStride, hasAlpha,
bitsPerSample, nChannels, data] = meta['icon-data'];
let textureCache = St.TextureCache.get_default();
return textureCache.load_from_raw(data, hasAlpha,
width, height, rowStride, size);
gicon = Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha,
bitsPerSample, width, height, rowStride);
}
// Ugh, but we want to fall back to something ...
return new St.Icon({ icon_name: 'text-x-generic',
return new St.Icon({ gicon: gicon,
icon_size: size });
},
_getResultsFinished: function(results, error) {
if (error)
return;
this.searchSystem.pushResults(this, results[0]);
this.searchSystem.setResults(this, results[0]);
},
getInitialResultSet: function(terms) {
@ -214,8 +221,8 @@ const RemoteSearchProvider = new Lang.Class({
Lang.bind(this, this._getResultsFinished),
this._cancellable);
} catch(e) {
log('Error calling GetInitialResultSet for provider %s: %s'.format( this.title, e.toString()));
this.searchSystem.pushResults(this, []);
log('Error calling GetInitialResultSet for provider %s: %s'.format(this.id, e.toString()));
this.searchSystem.setResults(this, []);
}
},
@ -227,8 +234,8 @@ const RemoteSearchProvider = new Lang.Class({
Lang.bind(this, this._getResultsFinished),
this._cancellable);
} catch(e) {
log('Error calling GetSubsearchResultSet for provider %s: %s'.format(this.title, e.toString()));
this.searchSystem.pushResults(this, []);
log('Error calling GetSubsearchResultSet for provider %s: %s'.format(this.id, e.toString()));
this.searchSystem.setResults(this, []);
}
},
@ -240,10 +247,15 @@ const RemoteSearchProvider = new Lang.Class({
let metas = results[0];
let resultMetas = [];
for (let i = 0; i < metas.length; i++) {
for (let prop in metas[i])
metas[i][prop] = metas[i][prop].deep_unpack();
for (let prop in metas[i]) {
// we can use the serialized icon variant directly
if (prop != 'icon')
metas[i][prop] = metas[i][prop].deep_unpack();
}
resultMetas.push({ id: metas[i]['id'],
name: metas[i]['name'],
description: metas[i]['description'],
createIcon: Lang.bind(this,
this.createIcon, metas[i]) });
}
@ -258,7 +270,7 @@ const RemoteSearchProvider = new Lang.Class({
Lang.bind(this, this._getResultMetasFinished, callback),
this._cancellable);
} catch(e) {
log('Error calling GetResultMetas for provider %s: %s'.format(this.title, e.toString()));
log('Error calling GetResultMetas for provider %s: %s'.format(this.id, e.toString()));
callback([]);
}
},
@ -290,6 +302,6 @@ const RemoteSearchProvider2 = new Lang.Class({
},
launchSearch: function(terms) {
this.proxy.LaunchSearchRemote(terms);
this.proxy.LaunchSearchRemote(terms, global.get_current_time());
}
});

View File

@ -30,144 +30,13 @@ const EXEC_ARG_KEY = 'exec-arg';
const DIALOG_GROW_TIME = 0.1;
const CommandCompleter = new Lang.Class({
Name: 'CommandCompleter',
_init : function() {
this._changedCount = 0;
this._paths = GLib.getenv('PATH').split(':');
this._paths.push(GLib.get_home_dir());
this._valid = false;
this._updateInProgress = false;
this._childs = new Array(this._paths.length);
this._monitors = new Array(this._paths.length);
for (let i = 0; i < this._paths.length; i++) {
this._childs[i] = [];
let file = Gio.file_new_for_path(this._paths[i]);
let info;
try {
info = file.query_info(Gio.FILE_ATTRIBUTE_STANDARD_TYPE, Gio.FileQueryInfoFlags.NONE, null);
} catch (e) {
// FIXME catchall
this._paths[i] = null;
continue;
}
if (info.get_attribute_uint32(Gio.FILE_ATTRIBUTE_STANDARD_TYPE) != Gio.FileType.DIRECTORY)
continue;
this._paths[i] = file.get_path();
this._monitors[i] = file.monitor_directory(Gio.FileMonitorFlags.NONE, null);
if (this._monitors[i] != null) {
this._monitors[i].connect('changed', Lang.bind(this, this._onChanged));
}
}
this._paths = this._paths.filter(function(a) {
return a != null;
});
this._update(0);
},
update : function() {
if (this._valid)
return;
this._update(0);
},
_update : function(i) {
if (i == 0 && this._updateInProgress)
return;
this._updateInProgress = true;
this._changedCount = 0;
this._i = i;
if (i >= this._paths.length) {
this._valid = true;
this._updateInProgress = false;
return;
}
let file = Gio.file_new_for_path(this._paths[i]);
this._childs[this._i] = [];
FileUtils.listDirAsync(file, Lang.bind(this, function (files) {
for (let i = 0; i < files.length; i++) {
this._childs[this._i].push(files[i].get_name());
}
this._update(this._i + 1);
}));
},
_onChanged : function(m, f, of, type) {
if (!this._valid)
return;
let path = f.get_parent().get_path();
let k = undefined;
for (let i = 0; i < this._paths.length; i++) {
if (this._paths[i] == path)
k = i;
}
if (k === undefined) {
return;
}
if (type == Gio.FileMonitorEvent.CREATED) {
this._childs[k].push(f.get_basename());
}
if (type == Gio.FileMonitorEvent.DELETED) {
this._changedCount++;
if (this._changedCount > MAX_FILE_DELETED_BEFORE_INVALID) {
this._valid = false;
}
let name = f.get_basename();
this._childs[k] = this._childs[k].filter(function(e) {
return e != name;
});
}
if (type == Gio.FileMonitorEvent.UNMOUNTED) {
this._childs[k] = [];
}
},
getCompletion: function(text) {
let common = '';
let notInit = true;
if (!this._valid) {
this._update(0);
return common;
}
function _getCommon(s1, s2) {
let k = 0;
for (; k < s1.length && k < s2.length; k++) {
if (s1[k] != s2[k])
break;
}
if (k == 0)
return '';
return s1.substr(0, k);
}
function _hasPrefix(s1, prefix) {
return s1.indexOf(prefix) == 0;
}
for (let i = 0; i < this._childs.length; i++) {
for (let k = 0; k < this._childs[i].length; k++) {
if (!_hasPrefix(this._childs[i][k], text))
continue;
if (notInit) {
common = this._childs[i][k];
notInit = false;
}
common = _getCommon(common, this._childs[i][k]);
}
}
if (common.length)
return common.substr(text.length);
return common;
}
});
const RunDialog = new Lang.Class({
Name: 'RunDialog',
Extends: ModalDialog.ModalDialog,
_init : function() {
this.parent({ styleClass: 'run-dialog' });
this.parent({ styleClass: 'run-dialog',
destroyOnClose: false });
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
this._terminalSettings = new Gio.Settings({ schema: TERMINAL_SCHEMA });
@ -242,8 +111,6 @@ const RunDialog = new Lang.Class({
key: Clutter.Escape }]);
this._pathCompleter = new Gio.FilenameCompleter();
this._commandCompleter = new CommandCompleter();
this._group.connect('notify::visible', Lang.bind(this._commandCompleter, this._commandCompleter.update));
this._history = new History.HistoryManager({ gsettingsKey: HISTORY_KEY,
entry: this._entryText });
@ -259,18 +126,6 @@ const RunDialog = new Lang.Class({
return true;
}
if (symbol == Clutter.slash) {
// Need preload data before get completion. GFilenameCompleter load content of parent directory.
// Parent directory for /usr/include/ is /usr/. So need to add fake name('a').
let text = o.get_text().concat('/a');
let prefix;
if (text.lastIndexOf(' ') == -1)
prefix = text;
else
prefix = text.substr(text.lastIndexOf(' ') + 1);
this._getCompletion(prefix);
return false;
}
if (symbol == Clutter.Tab) {
let text = o.get_text();
let prefix;
@ -282,8 +137,6 @@ const RunDialog = new Lang.Class({
if (postfix != null && postfix.length > 0) {
o.insert_text(postfix, -1);
o.set_cursor_position(text.length + postfix.length);
if (postfix[postfix.length - 1] == '/')
this._getCompletion(text + postfix + 'a');
}
return true;
}
@ -291,11 +144,53 @@ const RunDialog = new Lang.Class({
}));
},
_getCommandCompletion: function(text) {
function _getCommon(s1, s2) {
if (s1 == null)
return s2;
let k = 0;
for (; k < s1.length && k < s2.length; k++) {
if (s1[k] != s2[k])
break;
}
if (k == 0)
return '';
return s1.substr(0, k);
}
let paths = GLib.getenv('PATH').split(':');
paths.push(GLib.get_home_dir());
let someResults = paths.map(function(path) {
let results = [];
try {
let file = Gio.File.new_for_path(path);
let fileEnum = file.enumerate_children('standard::name', Gio.FileQueryInfoFlags.NONE, null);
let info;
while ((info = fileEnum.next_file(null))) {
let name = info.get_name();
if (name.slice(0, text.length) == text)
results.push(name);
}
} catch (e if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND) &&
!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_DIRECTORY))) {
log(e);
} finally {
return results;
}
});
let results = someResults.reduce(function(a, b) {
return a.concat(b);
}, []);
let common = results.reduce(_getCommon, null);
return common.substr(text.length);
},
_getCompletion : function(text) {
if (text.indexOf('/') != -1) {
return this._pathCompleter.get_completion_suffix(text);
} else {
return this._commandCompleter.getCompletion(text);
return this._getCommandCompletion(text);
}
},

File diff suppressed because it is too large Load Diff

140
js/ui/screencast.js Normal file
View File

@ -0,0 +1,140 @@
// -*- 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 Shell = imports.gi.Shell;
const Hash = imports.misc.hash;
const Main = imports.ui.main;
const ScreencastIface = <interface name="org.gnome.Shell.Screencast">
<method name="Screencast">
<arg type="s" direction="in" name="file_template"/>
<arg type="a{sv}" direction="in" name="options"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<method name="ScreencastArea">
<arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="y"/>
<arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/>
<arg type="s" direction="in" name="file_template"/>
<arg type="a{sv}" direction="in" name="options"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<method name="StopScreencast">
<arg type="b" direction="out" name="success"/>
</method>
</interface>;
const ScreencastService = new Lang.Class({
Name: 'ScreencastService',
_init: function() {
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreencastIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/Screencast');
Gio.DBus.session.own_name('org.gnome.Shell.Screencast', Gio.BusNameOwnerFlags.REPLACE, null, null);
this._recorders = new Hash.Map();
Main.sessionMode.connect('updated',
Lang.bind(this, this._sessionModeChanged));
},
_ensureRecorderForSender: function(sender) {
let recorder = this._recorders.get(sender);
if (!recorder) {
recorder = new Shell.Recorder({ stage: global.stage });
recorder._watchNameId =
Gio.bus_watch_name(Gio.BusType.SESSION, sender, 0, null,
Lang.bind(this, this._onNameVanished));
this._recorders.set(sender, recorder);
}
return recorder;
},
_sessionModeChanged: function() {
if (Main.sessionMode.allowScreencast)
return;
for (let sender in this._recorders.keys())
this._recorders.delete(sender);
},
_onNameVanished: function(connection, name) {
this._stopRecordingForSender(name);
},
_stopRecordingForSender: function(sender) {
let recorder = this._recorders.get(sender);
if (!recorder)
return false;
Gio.bus_unwatch_name(recorder._watchNameId);
recorder.close();
this._recorders.delete(sender);
return true;
},
_applyOptionalParameters: function(recorder, options) {
for (let option in options)
options[option] = options[option].deep_unpack();
if (options['pipeline'])
recorder.set_pipeline(options['pipeline']);
if (options['framerate'])
recorder.set_framerate(options['framerate']);
if (options['draw-cursor'])
recorder.set_draw_cursor(options['draw-cursor']);
},
ScreencastAsync: function(params, invocation) {
let returnValue = [false, ''];
if (!Main.sessionMode.allowScreencast)
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
let sender = invocation.get_sender();
let recorder = this._ensureRecorderForSender(sender);
if (!recorder.is_recording()) {
let [fileTemplate, options] = params;
recorder.set_file_template(fileTemplate);
this._applyOptionalParameters(recorder, options);
let [success, fileName] = recorder.record();
returnValue = [success, fileName ? fileName : ''];
}
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
},
ScreencastAreaAsync: function(params, invocation) {
let returnValue = [false, ''];
if (!Main.sessionMode.allowScreencast)
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
let sender = invocation.get_sender();
let recorder = this._ensureRecorderForSender(sender);
if (!recorder.is_recording()) {
let [x, y, width, height, fileTemplate, options] = params;
recorder.set_file_template(fileTemplate);
recorder.set_area(x, y, width, height);
this._applyOptionalParameters(recorder, options);
let [success, fileName] = recorder.record();
returnValue = [success, fileName ? fileName : ''];
}
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
},
StopScreencastAsync: function(params, invocation) {
let success = this._stopRecordingForSender(invocation.get_sender());
invocation.return_value(GLib.Variant.new('(b)', [success]));
}
});

277
js/ui/screenshot.js Normal file
View File

@ -0,0 +1,277 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gdk = imports.gi.Gdk;
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 Signals = imports.signals;
const St = imports.gi.St;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const ScreenshotIface = <interface name="org.gnome.Shell.Screenshot">
<method name="ScreenshotArea">
<arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="y"/>
<arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<method name="ScreenshotWindow">
<arg type="b" direction="in" name="include_frame"/>
<arg type="b" direction="in" name="include_cursor"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<method name="Screenshot">
<arg type="b" direction="in" name="include_cursor"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<method name="SelectArea">
<arg type="i" direction="out" name="x"/>
<arg type="i" direction="out" name="y"/>
<arg type="i" direction="out" name="width"/>
<arg type="i" direction="out" name="height"/>
</method>
<method name="FlashArea">
<arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="y"/>
<arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/>
</method>
</interface>;
const ScreenshotService = new Lang.Class({
Name: 'ScreenshotService',
_init: function() {
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenshotIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/Screenshot');
Gio.DBus.session.own_name('org.gnome.Shell.Screenshot', Gio.BusNameOwnerFlags.REPLACE, null, null);
},
_onScreenshotComplete: function(obj, result, area, filenameUsed, flash, invocation) {
if (flash && result) {
let flashspot = new Flashspot(area);
flashspot.fire();
}
let retval = GLib.Variant.new('(bs)', [result, filenameUsed]);
invocation.return_value(retval);
},
ScreenshotAreaAsync : function (params, invocation) {
let [x, y, width, height, flash, filename, callback] = params;
if (height <= 0 || width <= 0) {
invocation.return_error_literal(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
"Invalid params");
return;
}
let screenshot = new Shell.Screenshot();
screenshot.screenshot_area (x, y, width, height, filename,
Lang.bind(this, this._onScreenshotComplete,
flash, invocation));
},
ScreenshotWindowAsync : function (params, invocation) {
let [include_frame, include_cursor, flash, filename] = params;
let screenshot = new Shell.Screenshot();
screenshot.screenshot_window (include_frame, include_cursor, filename,
Lang.bind(this, this._onScreenshotComplete,
flash, invocation));
},
ScreenshotAsync : function (params, invocation) {
let [include_cursor, flash, filename] = params;
let screenshot = new Shell.Screenshot();
screenshot.screenshot(include_cursor, filename,
Lang.bind(this, this._onScreenshotComplete,
flash, invocation));
},
SelectAreaAsync: function (params, invocation) {
let selectArea = new SelectArea();
selectArea.show();
selectArea.connect('finished', Lang.bind(this,
function(selectArea, areaRectangle) {
if (areaRectangle) {
let retval = GLib.Variant.new('(iiii)',
[areaRectangle.x, areaRectangle.y,
areaRectangle.width, areaRectangle.height]);
invocation.return_value(retval);
} else {
invocation.return_error_literal(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
"Operation was cancelled");
}
}));
},
FlashArea: function(x, y, width, height) {
let flashspot = new Flashspot({ x : x, y : y, width: width, height: height});
flashspot.fire();
}
});
const SelectArea = new Lang.Class({
Name: 'SelectArea',
_init: function() {
this._startX = -1;
this._startY = -1;
this._lastX = 0;
this._lastY = 0;
this._initRubberbandColors();
this._group = new St.Widget({ visible: false,
reactive: true,
x: 0,
y: 0 });
Main.uiGroup.add_actor(this._group);
this._group.connect('button-press-event',
Lang.bind(this, this._onButtonPress));
this._group.connect('button-release-event',
Lang.bind(this, this._onButtonRelease));
this._group.connect('key-press-event',
Lang.bind(this, this._onKeyPress));
this._group.connect('motion-event',
Lang.bind(this, this._onMotionEvent));
let constraint = new Clutter.BindConstraint({ source: global.stage,
coordinate: Clutter.BindCoordinate.ALL });
this._group.add_constraint(constraint);
this._rubberband = new Clutter.Rectangle({ color: this._background,
has_border: true,
border_width: 1,
border_color: this._border });
this._group.add_actor(this._rubberband);
},
show: function() {
if (!Main.pushModal(this._group) || this._group.visible)
return;
global.set_cursor(Shell.Cursor.CROSSHAIR);
this._group.visible = true;
},
_initRubberbandColors: function() {
function colorFromRGBA(rgba) {
return new Clutter.Color({ red: rgba.red * 255,
green: rgba.green * 255,
blue: rgba.blue * 255,
alpha: rgba.alpha * 255 });
}
let path = new Gtk.WidgetPath();
path.append_type(Gtk.IconView);
let context = new Gtk.StyleContext();
context.set_path(path);
context.add_class('rubberband');
this._background = colorFromRGBA(context.get_background_color(Gtk.StateFlags.NORMAL));
this._border = colorFromRGBA(context.get_border_color(Gtk.StateFlags.NORMAL));
},
_getGeometry: function() {
return { x: Math.min(this._startX, this._lastX),
y: Math.min(this._startY, this._lastY),
width: Math.abs(this._startX - this._lastX),
height: Math.abs(this._startY - this._lastY) };
},
_onKeyPress: function(actor, event) {
if (event.get_key_symbol() == Clutter.Escape)
this._destroy(null, false);
return;
},
_onMotionEvent: function(actor, event) {
if (this._startX == -1 || this._startY == -1)
return false;
[this._lastX, this._lastY] = event.get_coords();
let geometry = this._getGeometry();
this._rubberband.set_position(geometry.x, geometry.y);
this._rubberband.set_size(geometry.width, geometry.height);
return false;
},
_onButtonPress: function(actor, event) {
[this._startX, this._startY] = event.get_coords();
this._rubberband.set_position(this._startX, this._startY);
return false;
},
_onButtonRelease: function(actor, event) {
this._destroy(this._getGeometry(), true);
return false;
},
_destroy: function(geometry, fade) {
Tweener.addTween(this._group,
{ opacity: 0,
time: fade ? 0.2 : 0,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
Main.popModal(this._group);
this._group.destroy();
global.unset_cursor();
this.emit('finished', geometry);
})
});
}
});
Signals.addSignalMethods(SelectArea.prototype);
const FLASHSPOT_ANIMATION_OUT_TIME = 0.5; // seconds
const Flashspot = new Lang.Class({
Name: 'Flashspot',
Extends: Lightbox.Lightbox,
_init: function(area) {
this.parent(Main.uiGroup, { inhibitEvents: true,
width: area.width,
height: area.height });
this.actor.style_class = 'flashspot';
this.actor.set_position(area.x, area.y);
},
fire: function() {
this.actor.show();
this.actor.opacity = 255;
Tweener.addTween(this.actor,
{ opacity: 0,
time: FLASHSPOT_ANIMATION_OUT_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this.destroy();
})
});
}
});

View File

@ -1,187 +1,10 @@
// -*- 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 Signals = imports.signals;
const Shell = imports.gi.Shell;
const Util = imports.misc.util;
const FileUtils = imports.misc.fileUtils;
const Main = imports.ui.main;
const SEARCH_PROVIDERS_SCHEMA = 'org.gnome.desktop.search-providers';
// Not currently referenced by the search API, but
// this enumeration can be useful for provider
// implementations.
const MatchType = {
NONE: 0,
SUBSTRING: 1,
PREFIX: 2
};
const SearchResultDisplay = new Lang.Class({
Name: 'SearchResultDisplay',
_init: function(provider) {
this.provider = provider;
this.actor = null;
},
/**
* renderResults:
* @results: List of identifier strings
* @terms: List of search term strings
*
* Display the given search matches which resulted
* from the given terms. It's expected that not
* all results will fit in the space for the container
* actor; in this case, show as many as makes sense
* for your result type.
*
* The terms are useful for search match highlighting.
*/
renderResults: function(results, terms) {
throw new Error('Not implemented');
},
/**
* clear:
* Remove all results from this display.
*/
clear: function() {
this.actor.destroy_all_children();
},
/**
* getVisibleResultCount:
*
* Returns: The number of actors visible.
*/
getVisibleResultCount: function() {
throw new Error('Not implemented');
},
});
/**
* SearchProvider:
*
* Subclass this object to add a new result type
* to the search system, then call registerProvider()
* in SearchSystem with an instance.
* Search is asynchronous and uses the
* getInitialResultSet()/getSubsearchResultSet() methods.
*/
const SearchProvider = new Lang.Class({
Name: 'SearchProvider',
_init: function(title, appInfo, isRemoteProvider) {
this.title = title;
this.appInfo = appInfo;
this.searchSystem = null;
this.isRemoteProvider = !!isRemoteProvider;
this.canLaunchSearch = false;
},
/**
* getInitialResultSet:
* @terms: Array of search terms, treated as logical AND
*
* Called when the user first begins a search (most likely
* therefore a single term of length one or two), or when
* a new term is added.
*
* Should "return" an array of result identifier strings representing
* items which match the given search terms. This
* is expected to be a substring match on the metadata for a given
* item. Ordering of returned results is up to the discretion of the provider,
* but you should follow these heruistics:
*
* * Put items where the term matches multiple criteria (e.g. name and
* description) before single matches
* * Put items which match on a prefix before non-prefix substring matches
*
* We say "return" above, but in order to make the query asynchronous, use
* this.searchSystem.pushResults();. The return value should be ignored.
*
* This function should be fast; do not perform unindexed full-text searches
* or network queries.
*/
getInitialResultSet: function(terms) {
throw new Error('Not implemented');
},
/**
* getSubsearchResultSet:
* @previousResults: Array of item identifiers
* @newTerms: Updated search terms
*
* Called when a search is performed which is a "subsearch" of
* the previous search; i.e. when every search term has exactly
* one corresponding term in the previous search which is a prefix
* of the new term.
*
* This allows search providers to only search through the previous
* result set, rather than possibly performing a full re-query.
*
* Similar to getInitialResultSet, the return value for this will
* be ignored; use this.searchSystem.pushResults();.
*/
getSubsearchResultSet: function(previousResults, newTerms) {
throw new Error('Not implemented');
},
/**
* getResultMetas:
* @ids: Result identifier strings
*
* Call callback with array of objects with 'id', 'name', (both strings) and
* 'createIcon' (function(size) returning a Clutter.Texture) properties
* with the same number of members as @ids
*/
getResultMetas: function(ids, callback) {
throw new Error('Not implemented');
},
/**
* createResultActor:
* @resultMeta: Object with result metadata
* @terms: Array of search terms, should be used for highlighting
*
* Search providers may optionally override this to render a
* particular serch result in a custom fashion. The default
* implementation will show the icon next to the name.
*
* The actor should be an instance of St.Widget, with the style class
* 'search-result-content'.
*/
createResultActor: function(resultMeta, terms) {
return null;
},
/**
* activateResult:
* @id: Result identifier string
*
* Called when the user chooses a given result.
*/
activateResult: function(id) {
throw new Error('Not implemented');
},
/**
* launchSearch:
* @terms: Current search terms
*
* Called when the user clicks the provider icon.
*/
launchSearch: function(terms) {
throw new Error('Not implemented');
}
});
Signals.addSignalMethods(SearchProvider.prototype);
const SearchSystem = new Lang.Class({
Name: 'SearchSystem',
@ -208,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() {
@ -228,7 +51,7 @@ const SearchSystem = new Lang.Class({
this._previousResults = [];
},
pushResults: function(provider, results) {
setResults: function(provider, results) {
let i = this._providers.indexOf(provider);
if (i == -1)
return;
@ -237,28 +60,18 @@ const SearchSystem = new Lang.Class({
this.emit('search-updated', this._previousResults[i]);
},
updateSearch: function(searchString) {
searchString = searchString.replace(/^\s+/g, '').replace(/\s+$/g, '');
if (searchString == '')
return;
let terms = searchString.split(/\s+/);
this.updateSearchResults(terms);
},
updateSearchResults: function(terms) {
if (!terms)
return;
let isSubSearch = terms.length == this._previousTerms.length;
if (isSubSearch) {
for (let i = 0; i < terms.length; i++) {
if (terms[i].indexOf(this._previousTerms[i]) != 0) {
isSubSearch = false;
break;
}
}
}
let searchString = terms.join(' ');
let previousSearchString = this._previousTerms.join(' ');
if (searchString == previousSearchString)
return;
let isSubSearch = false;
if (this._previousTerms.length > 0)
isSubSearch = searchString.indexOf(previousSearchString) == 0;
let previousResultsArr = this._previousResults;
@ -273,7 +86,7 @@ const SearchSystem = new Lang.Class({
results.push([provider, []]);
provider.getSubsearchResultSet(previousResults, terms);
} catch (error) {
log('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
log('A ' + error.name + ' has occured in ' + provider.id + ': ' + error.message);
}
}
} else {
@ -283,10 +96,10 @@ const SearchSystem = new Lang.Class({
results.push([provider, []]);
provider.getInitialResultSet(terms);
} catch (error) {
log('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
log('A ' + error.name + ' has occured in ' + provider.id + ': ' + error.message);
}
}
}
},
}
});
Signals.addSignalMethods(SearchSystem.prototype);

View File

@ -4,6 +4,7 @@ const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Gtk = imports.gi.Gtk;
const Meta = imports.gi.Meta;
const Signals = imports.signals;
const St = imports.gi.St;
const Atk = imports.gi.Atk;
@ -11,10 +12,32 @@ const DND = imports.ui.dnd;
const IconGrid = imports.ui.iconGrid;
const Main = imports.ui.main;
const Overview = imports.ui.overview;
const Separator = imports.ui.separator;
const Search = imports.ui.search;
const Util = imports.misc.util;
const MAX_SEARCH_RESULTS_ROWS = 1;
const MAX_LIST_SEARCH_RESULTS_ROWS = 3;
const MAX_GRID_SEARCH_RESULTS_ROWS = 1;
const MaxWidthBin = new Lang.Class({
Name: 'MaxWidthBin',
Extends: St.Bin,
vfunc_allocate: function(box, flags) {
let themeNode = this.get_theme_node();
let maxWidth = themeNode.get_max_width();
let availWidth = box.x2 - box.x1;
let adjustedBox = box;
if (availWidth > maxWidth) {
let excessWidth = availWidth - maxWidth;
adjustedBox.x1 += Math.floor(excessWidth / 2);
adjustedBox.x2 -= Math.floor(excessWidth / 2);
}
this.parent(adjustedBox, flags);
}
});
const SearchResult = new Lang.Class({
Name: 'SearchResult',
@ -23,33 +46,103 @@ const SearchResult = new Lang.Class({
this.provider = provider;
this.metaInfo = metaInfo;
this.terms = terms;
this.actor = new St.Button({ style_class: 'search-result',
reactive: true,
this.actor = new St.Button({ reactive: true,
can_focus: true,
track_hover: true,
x_align: St.Align.START,
y_fill: true });
this.actor._delegate = this;
this._dragActorSource = null;
this.actor.connect('clicked', Lang.bind(this, this.activate));
},
activate: function() {
this.provider.activateResult(this.metaInfo.id, this.terms);
Main.overview.toggle();
},
setSelected: function(selected) {
if (selected)
this.actor.add_style_pseudo_class('selected');
else
this.actor.remove_style_pseudo_class('selected');
}
});
const ListSearchResult = new Lang.Class({
Name: 'ListSearchResult',
Extends: SearchResult,
ICON_SIZE: 64,
_init: function(provider, metaInfo, terms) {
this.parent(provider, metaInfo, terms);
this.actor.style_class = 'list-search-result';
this.actor.x_fill = true;
let content = new St.BoxLayout({ style_class: 'list-search-result-content',
vertical: false });
this.actor.set_child(content);
// An icon for, or thumbnail of, content
let icon = this.metaInfo['createIcon'](this.ICON_SIZE);
if (icon) {
content.add(icon);
}
let details = new St.BoxLayout({ vertical: true });
content.add(details, { x_fill: true,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
let title = new St.Label({ style_class: 'list-search-result-title',
text: this.metaInfo['name'] })
details.add(title, { x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
this.actor.label_actor = title;
if (this.metaInfo['description']) {
let description = new St.Label({ style_class: 'list-search-result-description' });
description.clutter_text.set_markup(this.metaInfo['description']);
details.add(description, { x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.END });
}
}
});
const GridSearchResult = new Lang.Class({
Name: 'GridSearchResult',
Extends: SearchResult,
_init: function(provider, metaInfo, terms) {
this.parent(provider, metaInfo, terms);
this.actor.style_class = 'grid-search-result';
let content = provider.createResultActor(metaInfo, terms);
let dragSource = null;
if (content == null) {
content = new St.Bin({ style_class: 'search-result-content',
reactive: true,
can_focus: true,
track_hover: true,
accessible_role: Atk.Role.PUSH_BUTTON });
content = new St.Bin();
let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
{ createIcon: this.metaInfo['createIcon'] });
content.set_child(icon.actor);
this._dragActorSource = icon.icon;
content.label_actor = icon.label;
dragSource = icon.icon;
} else {
if (content._delegate && content._delegate.getDragActorSource)
this._dragActorSource = content._delegate.getDragActorSource();
dragSource = content._delegate.getDragActorSource();
}
this._content = content;
this.actor.set_child(content);
this.actor.connect('clicked', Lang.bind(this, this._onResultClicked));
this.actor.set_child(content);
this.actor.label_actor = content.label_actor;
let draggable = DND.makeDraggable(this.actor);
draggable.connect('drag-begin',
@ -64,32 +157,18 @@ const SearchResult = new Lang.Class({
Lang.bind(this, function() {
Main.overview.endItemDrag(this);
}));
},
setSelected: function(selected) {
if (selected)
this._content.add_style_pseudo_class('selected');
else
this._content.remove_style_pseudo_class('selected');
},
activate: function() {
this.provider.activateResult(this.metaInfo.id, this.terms);
Main.overview.toggle();
},
_onResultClicked: function(actor) {
this.activate();
if (!dragSource)
// not exactly right, but alignment problems are hard to notice
dragSource = content;
this._dragActorSource = dragSource;
},
getDragActorSource: function() {
if (this._dragActorSource)
return this._dragActorSource;
// not exactly right, but alignment problems are hard to notice
return this._content;
return this._dragActorSource;
},
getDragActor: function(stageX, stageY) {
getDragActor: function() {
return this.metaInfo['createIcon'](Main.overview.dashIconSize);
},
@ -101,75 +180,169 @@ const SearchResult = new Lang.Class({
}
});
const SearchResultsBase = new Lang.Class({
Name: 'SearchResultsBase',
_init: function(provider) {
this.provider = provider;
this._terms = [];
this.actor = new St.BoxLayout({ style_class: 'search-section',
vertical: true });
this._resultDisplayBin = new St.Bin({ x_fill: true,
y_fill: true });
this.actor.add(this._resultDisplayBin, { expand: true });
let separator = new Separator.HorizontalSeparator({ style_class: 'search-section-separator' });
this.actor.add(separator.actor);
},
destroy: function() {
this.actor.destroy();
this._terms = [];
},
_clearResultDisplay: function() {
},
clear: function() {
this._clearResultDisplay();
this.actor.hide();
},
_keyFocusIn: function(icon) {
this.emit('key-focus-in', icon);
},
_setMoreIconVisible: function(visible) {
},
updateSearch: function(providerResults, terms, callback) {
this._terms = terms;
if (providerResults.length == 0) {
this._clearResultDisplay();
this.actor.hide();
callback();
} else {
let maxResults = this._getMaxDisplayedResults();
let results = providerResults.slice(0, maxResults);
let hasMoreResults = results.length < providerResults.length;
this.provider.getResultMetas(results, Lang.bind(this, function(metas) {
this.clear();
// To avoid CSS transitions causing flickering when
// the first search result stays the same, we hide the
// content while filling in the results.
this.actor.hide();
this._clearResultDisplay();
this._renderResults(metas);
this._setMoreIconVisible(hasMoreResults && this.provider.canLaunchSearch);
this.actor.show();
callback();
}));
}
}
});
const ListSearchResults = new Lang.Class({
Name: 'ListSearchResults',
Extends: SearchResultsBase,
_init: function(provider) {
this.parent(provider);
this._container = new St.BoxLayout({ style_class: 'search-section-content' });
this.providerIcon = new ProviderIcon(provider);
this.providerIcon.connect('clicked', Lang.bind(this,
function() {
provider.launchSearch(this._terms);
Main.overview.toggle();
}));
this._container.add(this.providerIcon, { x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
this._content = new St.BoxLayout({ style_class: 'list-search-results',
vertical: true });
this._container.add(this._content, { expand: true });
this._resultDisplayBin.set_child(this._container);
},
_setMoreIconVisible: function(visible) {
this.providerIcon.moreIcon.visible = true;
},
_getMaxDisplayedResults: function() {
return MAX_LIST_SEARCH_RESULTS_ROWS;
},
_renderResults: function(metas) {
for (let i = 0; i < metas.length; i++) {
let display = new ListSearchResult(this.provider, metas[i], this._terms);
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
this._content.add_actor(display.actor);
}
},
_clearResultDisplay: function () {
this._content.destroy_all_children();
},
getFirstResult: function() {
if (this._content.get_n_children() > 0)
return this._content.get_child_at_index(0)._delegate;
else
return null;
}
});
Signals.addSignalMethods(ListSearchResults.prototype);
const GridSearchResults = new Lang.Class({
Name: 'GridSearchResults',
Extends: Search.SearchResultDisplay,
Extends: SearchResultsBase,
_init: function(provider, grid) {
_init: function(provider) {
this.parent(provider);
this._grid = grid || new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
xAlign: St.Align.START });
this.actor = new St.Bin({ x_align: St.Align.START });
this._grid = new IconGrid.IconGrid({ rowLimit: MAX_GRID_SEARCH_RESULTS_ROWS,
xAlign: St.Align.START });
this._bin = new St.Bin({ x_align: St.Align.MIDDLE });
this._bin.set_child(this._grid.actor);
this.actor.set_child(this._grid.actor);
this._width = 0;
this.actor.connect('notify::width', Lang.bind(this, function() {
this._width = this.actor.width;
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
let results = this.getResultsForDisplay();
if (results.length == 0)
return;
provider.getResultMetas(results, Lang.bind(this, this.renderResults));
}));
}));
this._notDisplayedResult = [];
this._terms = [];
this._pendingClear = false;
this._resultDisplayBin.set_child(this._bin);
},
getResultsForDisplay: function() {
let alreadyVisible = this._pendingClear ? 0 : this._grid.visibleItemsCount();
let canDisplay = this._grid.childrenInRow(this._width) * this._grid.getRowLimit()
- alreadyVisible;
let numResults = Math.min(this._notDisplayedResult.length, canDisplay);
return this._notDisplayedResult.splice(0, numResults);
_getMaxDisplayedResults: function() {
return this._grid.childrenInRow(this._bin.width) * this._grid.getRowLimit();
},
getVisibleResultCount: function() {
return this._grid.visibleItemsCount();
},
setResults: function(results, terms) {
// copy the lists
this._notDisplayedResult = results.slice(0);
this._terms = terms.slice(0);
this._pendingClear = true;
},
renderResults: function(metas) {
_renderResults: function(metas) {
for (let i = 0; i < metas.length; i++) {
let display = new SearchResult(this.provider, metas[i], this._terms);
let display = new GridSearchResult(this.provider, metas[i], this._terms);
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
this._grid.addItem(display.actor);
}
},
clear: function () {
_clearResultDisplay: function () {
this._grid.removeAll();
this._pendingClear = false;
},
getFirstResult: function() {
if (this.getVisibleResultCount() > 0)
if (this._grid.visibleItemsCount() > 0)
return this._grid.getItemAtIndex(0)._delegate;
else
return null;
}
});
Signals.addSignalMethods(GridSearchResults.prototype);
const SearchResults = new Lang.Class({
Name: 'SearchResults',
@ -183,12 +356,20 @@ const SearchResults = new Lang.Class({
this._content = new St.BoxLayout({ name: 'searchResultsContent',
vertical: true });
this._contentBin = new MaxWidthBin({ name: 'searchResultsBin',
x_fill: true,
y_fill: true,
child: this._content });
let scrollChild = new St.BoxLayout();
scrollChild.add(this._contentBin, { expand: true });
this._scrollView = new St.ScrollView({ x_fill: true,
y_fill: false,
style_class: 'vfade' });
overlay_scrollbars: true,
style_class: 'search-display vfade' });
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
this._scrollView.add_actor(this._content);
this._scrollView.add_actor(scrollChild);
let action = new Clutter.PanAction({ interpolate: true });
action.connect('pan', Lang.bind(this, this._onPan));
this._scrollView.add_action(action);
@ -205,9 +386,9 @@ const SearchResults = new Lang.Class({
this._content.add(this._statusBin, { expand: true });
this._statusBin.add_actor(this._statusText);
this._providers = this._searchSystem.getProviders();
this._providerMeta = [];
this._providerDisplays = {};
for (let i = 0; i < this._providers.length; i++) {
this.createProviderMeta(this._providers[i]);
this.createProviderDisplay(this._providers[i]);
}
this._highlightDefault = false;
@ -221,81 +402,61 @@ const SearchResults = new Lang.Class({
return false;
},
createProviderMeta: function(provider) {
let providerBox = new St.BoxLayout({ style_class: 'search-section',
vertical: true });
let title = new St.Label({ style_class: 'search-section-header',
text: provider.title });
providerBox.add(title, { x_fill: false, x_align: St.Align.START });
let resultDisplayBin = new St.Bin({ style_class: 'search-section-results',
x_fill: true,
y_fill: true });
providerBox.add(resultDisplayBin, { expand: true });
let resultDisplay = new GridSearchResults(provider);
resultDisplayBin.set_child(resultDisplay.actor);
this._providerMeta.push({ provider: provider,
actor: providerBox,
resultDisplay: resultDisplay });
this._content.add(providerBox);
_keyFocusIn: function(provider, icon) {
Util.ensureActorVisibleInScrollView(this._scrollView, icon);
},
destroyProviderMeta: function(provider) {
for (let i=0; i < this._providerMeta.length; i++) {
let meta = this._providerMeta[i];
if (meta.provider == provider) {
meta.actor.destroy();
this._providerMeta.splice(i, 1);
break;
}
createProviderDisplay: function(provider) {
let providerDisplay = null;
if (provider.appInfo) {
providerDisplay = new ListSearchResults(provider);
} else {
providerDisplay = new GridSearchResults(provider);
}
providerDisplay.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
this._providerDisplays[provider.id] = providerDisplay;
this._content.add(providerDisplay.actor);
},
destroyProviderDisplay: function(provider) {
this._providerDisplays[provider.id].destroy();
delete this._providerDisplays[provider.id];
},
_clearDisplay: function() {
for (let i = 0; i < this._providerMeta.length; i++) {
let meta = this._providerMeta[i];
meta.resultDisplay.clear();
meta.actor.hide();
for (let i = 0; i < this._providers.length; i++) {
let provider = this._providers[i];
let providerDisplay = this._providerDisplays[provider.id];
providerDisplay.clear();
}
},
_clearDisplayForProvider: function(provider) {
let meta = this._metaForProvider(provider);
meta.resultDisplay.clear();
meta.actor.hide();
},
reset: function() {
this._searchSystem.reset();
this._statusBin.hide();
this._clearDisplay();
this._defaultResult = null;
},
startingSearch: function() {
this.reset();
this._statusText.set_text(_("Searching..."));
this._statusText.set_text(_("Searching"));
this._statusBin.show();
},
doSearch: function (searchString) {
this._searchSystem.updateSearch(searchString);
},
_metaForProvider: function(provider) {
return this._providerMeta[this._providers.indexOf(provider)];
},
_maybeSetInitialSelection: function() {
let newDefaultResult = null;
for (let i = 0; i < this._providerMeta.length; i++) {
let meta = this._providerMeta[i];
for (let i = 0; i < this._providers.length; i++) {
let provider = this._providers[i];
let display = this._providerDisplays[provider.id];
if (!meta.actor.visible)
if (!display.actor.visible)
continue;
let firstResult = meta.resultDisplay.getFirstResult();
let firstResult = display.getFirstResult();
if (firstResult) {
newDefaultResult = firstResult;
break; // select this one!
@ -315,11 +476,14 @@ const SearchResults = new Lang.Class({
_updateStatusText: function () {
let haveResults = false;
for (let i = 0; i < this._providerMeta.length; ++i)
if (this._providerMeta[i].resultDisplay.getFirstResult()) {
for (let i = 0; i < this._providers.length; i++) {
let provider = this._providers[i];
let display = this._providerDisplays[provider.id];
if (display.getFirstResult()) {
haveResults = true;
break;
}
}
if (!haveResults) {
this._statusText.set_text(_("No results."));
@ -332,37 +496,12 @@ const SearchResults = new Lang.Class({
_updateResults: function(searchSystem, results) {
let terms = searchSystem.getTerms();
let [provider, providerResults] = results;
let meta = this._metaForProvider(provider);
let display = this._providerDisplays[provider.id];
if (providerResults.length == 0) {
this._clearDisplayForProvider(provider);
meta.resultDisplay.setResults([], []);
display.updateSearch(providerResults, terms, Lang.bind(this, function() {
this._maybeSetInitialSelection();
this._updateStatusText();
} else {
meta.resultDisplay.setResults(providerResults, terms);
let results = meta.resultDisplay.getResultsForDisplay();
provider.getResultMetas(results, Lang.bind(this, function(metas) {
this._clearDisplayForProvider(provider);
meta.actor.show();
// Hiding drops the key focus if we have it
let focus = global.stage.get_key_focus();
// To avoid CSS transitions causing flickering when
// the first search result stays the same, we hide the
// content while filling in the results.
this._content.hide();
meta.resultDisplay.renderResults(metas);
this._maybeSetInitialSelection();
this._updateStatusText();
this._content.show();
if (this._content.contains(focus))
global.stage.set_key_focus(focus);
}));
}
}));
},
activateDefault: function() {
@ -388,10 +527,38 @@ const SearchResults = new Lang.Class({
let from = this._defaultResult ? this._defaultResult.actor : null;
this.actor.navigate_focus(from, direction, false);
if (this._defaultResult) {
// The default result appears focused, so navigate directly to the
// next result.
this.actor.navigate_focus(global.stage.key_focus, direction, false);
}
}
});
const ProviderIcon = new Lang.Class({
Name: 'ProviderIcon',
Extends: St.Button,
PROVIDER_ICON_SIZE: 48,
_init: function(provider) {
this.provider = provider;
this.parent({ style_class: 'search-provider-icon',
reactive: true,
can_focus: true,
accessible_name: provider.appInfo.get_name(),
track_hover: true });
this._content = new St.Widget({ layout_manager: new Clutter.BinLayout() });
this.set_child(this._content);
let rtl = (this.get_text_direction() == Clutter.TextDirection.RTL);
this.moreIcon = new St.Widget({ style_class: 'search-provider-icon-more',
visible: false,
x_align: rtl ? Clutter.ActorAlign.START : Clutter.ActorAlign.END,
y_align: Clutter.ActorAlign.END,
x_expand: true,
y_expand: true });
let icon = new St.Icon({ icon_size: this.PROVIDER_ICON_SIZE,
gicon: provider.appInfo.get_icon() });
this._content.add_actor(icon);
this._content.add_actor(this.moreIcon);
}
});

35
js/ui/separator.js Normal file
View File

@ -0,0 +1,35 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Cairo = imports.cairo;
const Lang = imports.lang;
const St = imports.gi.St;
const HorizontalSeparator = new Lang.Class({
Name: 'HorizontalSeparator',
_init: function (params) {
this.actor = new St.DrawingArea(params);
this.actor.connect('repaint', Lang.bind(this, this._onRepaint));
},
_onRepaint: function(area) {
let cr = area.get_context();
let themeNode = area.get_theme_node();
let [width, height] = area.get_surface_size();
let margin = themeNode.get_length('-margin-horizontal');
let gradientHeight = themeNode.get_length('-gradient-height');
let startColor = themeNode.get_color('-gradient-start');
let endColor = themeNode.get_color('-gradient-end');
let gradientWidth = (width - margin * 2);
let gradientOffset = (height - gradientHeight) / 2;
let pattern = new Cairo.LinearGradient(margin, gradientOffset, width - margin, gradientOffset + gradientHeight);
pattern.addColorStopRGBA(0, startColor.red / 255, startColor.green / 255, startColor.blue / 255, startColor.alpha / 255);
pattern.addColorStopRGBA(0.5, endColor.red / 255, endColor.green / 255, endColor.blue / 255, endColor.alpha / 255);
pattern.addColorStopRGBA(1, startColor.red / 255, startColor.green / 255, startColor.blue / 255, startColor.alpha / 255);
cr.setSource(pattern);
cr.rectangle(margin, gradientOffset, gradientWidth, gradientHeight);
cr.fill();
cr.$dispose();
}
});

View File

@ -15,6 +15,8 @@ const DEFAULT_MODE = 'restrictive';
const _modes = {
'restrictive': {
parentMode: null,
stylesheetName: 'gnome-shell.css',
overridesSchema: 'org.gnome.shell.overrides',
hasOverview: false,
showCalendarEvents: false,
allowSettings: false,
@ -44,9 +46,9 @@ const _modes = {
unlockDialog: imports.gdm.loginDialog.LoginDialog,
components: ['polkitAgent'],
panel: {
left: ['logo'],
left: [],
center: ['dateMenu'],
right: ['a11y', 'display', 'keyboard',
right: ['a11yGreeter', 'display', 'keyboard',
'volume', 'battery', 'powerMenu']
},
panelStyle: 'login-screen'
@ -58,7 +60,7 @@ const _modes = {
unlockDialog: undefined,
components: ['polkitAgent', 'telepathyClient'],
panel: {
left: ['userMenu'],
left: [],
center: [],
right: ['lockScreen']
},
@ -70,23 +72,13 @@ const _modes = {
unlockDialog: undefined,
components: ['polkitAgent', 'telepathyClient'],
panel: {
left: ['userMenu'],
left: [],
center: [],
right: ['a11y', 'keyboard', 'lockScreen']
},
panelStyle: 'unlock-screen'
},
'initial-setup': {
isPrimary: true,
components: ['keyring'],
panel: {
left: [],
center: ['dateMenu'],
right: ['a11y', 'keyboard', 'volume']
}
},
'user': {
hasOverview: true,
showCalendarEvents: true,
@ -105,7 +97,7 @@ const _modes = {
left: ['activities', 'appMenu'],
center: ['dateMenu'],
right: ['a11y', 'keyboard', 'volume', 'bluetooth',
'network', 'battery', 'userMenu']
'network', 'battery', 'system']
}
}
};
@ -157,12 +149,7 @@ function listModes() {
const SessionMode = new Lang.Class({
Name: 'SessionMode',
_init: function() {
global.connect('notify::session-mode', Lang.bind(this, this._sync));
this._modes = _modes;
this._modeStack = [DEFAULT_MODE];
this._sync();
init: function() {
_getModes(Lang.bind(this, function(modes) {
this._modes = modes;
let primary = modes[global.session_mode] &&
@ -170,6 +157,8 @@ const SessionMode = new Lang.Class({
let mode = primary ? global.session_mode : 'user';
this._modeStack = [mode];
this._sync();
this.emit('sessions-loaded');
}));
},
@ -196,6 +185,10 @@ const SessionMode = new Lang.Class({
return this._modeStack[this._modeStack.length - 1];
},
get allowScreencast() {
return this.components.indexOf('recorder') != -1;
},
_sync: function() {
let params = this._modes[this.currentMode];
let defaults;

View File

@ -1,16 +1,19 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Config = imports.misc.config;
const ExtensionSystem = imports.ui.extensionSystem;
const ExtensionDownloader = imports.ui.extensionDownloader;
const ExtensionUtils = imports.misc.extensionUtils;
const Flashspot = imports.ui.flashspot;
const Hash = imports.misc.hash;
const Main = imports.ui.main;
const Screencast = imports.ui.screencast;
const Screenshot = imports.ui.screenshot;
const GnomeShellIface = <interface name="org.gnome.Shell">
<method name="Eval">
@ -18,34 +21,28 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
<arg type="b" direction="out" name="success" />
<arg type="s" direction="out" name="result" />
</method>
<method name="ScreenshotArea">
<arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="y"/>
<arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<method name="FocusSearch"/>
<method name="ShowOSD">
<arg type="a{sv}" direction="in" name="params"/>
</method>
<method name="GrabAccelerator">
<arg type="s" direction="in" name="accelerator"/>
<arg type="u" direction="in" name="flags"/>
<arg type="u" direction="out" name="action"/>
</method>
<method name="GrabAccelerators">
<arg type="a(su)" direction="in" name="accelerators"/>
<arg type="au" direction="out" name="actions"/>
</method>
<method name="UngrabAccelerator">
<arg type="u" direction="in" name="action"/>
<arg type="b" direction="out" name="success"/>
</method>
<method name="ScreenshotWindow">
<arg type="b" direction="in" name="include_frame"/>
<arg type="b" direction="in" name="include_cursor"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
</method>
<method name="Screenshot">
<arg type="b" direction="in" name="include_cursor"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
</method>
<method name="FlashArea">
<arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="y"/>
<arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/>
</method>
<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" />
<property name="ShellVersion" type="s" access="read" />
@ -75,7 +72,17 @@ const GnomeShell = new Lang.Class({
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
this._extensionsSerivce = new GnomeShellExtensions();
this._extensionsService = new GnomeShellExtensions();
this._screencastService = new Screencast.ScreencastService();
this._screenshotService = new Screenshot.ScreenshotService();
this._grabbedAccelerators = new Hash.Map();
this._grabbers = new Hash.Map();
global.display.connect('accelerator-activated', Lang.bind(this,
function(display, action, deviceid, timestamp) {
this._emitAcceleratorActivated(action, deviceid, timestamp);
}));
},
/**
@ -94,7 +101,7 @@ const GnomeShell = new Lang.Class({
*/
Eval: function(code) {
if (!global.settings.get_boolean('development-tools'))
return [false, null];
return [false, ''];
let returnValue;
let success;
@ -111,81 +118,105 @@ const GnomeShell = new Lang.Class({
return [success, returnValue];
},
_onScreenshotComplete: function(obj, result, area, flash, invocation) {
if (flash && result) {
let flashspot = new Flashspot.Flashspot(area);
flashspot.fire();
FocusSearch: function() {
Main.overview.focusSearch();
},
ShowOSD: function(params) {
for (let param in params)
params[param] = params[param].deep_unpack();
let icon = null;
if (params['icon'])
icon = Gio.Icon.new_for_string(params['icon']);
Main.osdWindow.setIcon(icon);
Main.osdWindow.setLabel(params['label']);
Main.osdWindow.setLevel(params['level']);
Main.osdWindow.show();
},
GrabAcceleratorAsync: function(params, invocation) {
let [accel, flags] = params;
let sender = invocation.get_sender();
let bindingAction = this._grabAcceleratorForSender(accel, flags, sender);
return invocation.return_value(GLib.Variant.new('(u)', [bindingAction]));
},
GrabAcceleratorsAsync: function(params, invocation) {
let [accels] = params;
let sender = invocation.get_sender();
let bindingActions = [];
for (let i = 0; i < accels.length; i++) {
let [accel, flags] = accels[i];
bindingActions.push(this._grabAcceleratorForSender(accel, flags, sender));
}
return invocation.return_value(GLib.Variant.new('(au)', [bindingActions]));
},
UngrabAcceleratorAsync: function(params, invocation) {
let [action] = params;
let grabbedBy = this._grabbedAccelerators.get(action);
if (invocation.get_sender() != grabbedBy)
return invocation.return_value(GLib.Variant.new('(b)', [false]));
let ungrabSucceeded = global.display.ungrab_accelerator(action);
if (ungrabSucceeded)
this._grabbedAccelerators.delete(action);
return invocation.return_value(GLib.Variant.new('(b)', [ungrabSucceeded]));
},
_emitAcceleratorActivated: function(action, deviceid, timestamp) {
let destination = this._grabbedAccelerators.get(action);
if (!destination)
return;
let connection = this._dbusImpl.get_connection();
let info = this._dbusImpl.get_info();
connection.emit_signal(destination,
this._dbusImpl.get_object_path(),
info ? info.name : null,
'AcceleratorActivated',
GLib.Variant.new('(uuu)', [action, deviceid, timestamp]));
},
_grabAcceleratorForSender: function(accelerator, flags, sender) {
let bindingAction = global.display.grab_accelerator(accelerator);
if (bindingAction == Meta.KeyBindingAction.NONE)
return Meta.KeyBindingAction.NONE;
let bindingName = Meta.external_binding_name_for_action(bindingAction);
Main.wm.allowKeybinding(bindingName, flags);
this._grabbedAccelerators.set(bindingAction, sender);
if (!this._grabbers.has(sender)) {
let id = Gio.bus_watch_name(Gio.BusType.SESSION, sender, 0, null,
Lang.bind(this, this._onGrabberBusNameVanished));
this._grabbers.set(sender, id);
}
let retval = GLib.Variant.new('(b)', [result]);
invocation.return_value(retval);
return bindingAction;
},
/**
* ScreenshotArea:
* @x: The X coordinate of the area
* @y: The Y coordinate of the area
* @width: The width of the area
* @height: The height of the area
* @flash: Whether to flash the area or not
* @filename: The filename for the screenshot
*
* Takes a screenshot of the passed in area and saves it
* in @filename as png image, it returns a boolean
* indicating whether the operation was successful or not.
*
*/
ScreenshotAreaAsync : function (params, invocation) {
let [x, y, width, height, flash, filename, callback] = params;
let screenshot = new Shell.Screenshot();
screenshot.screenshot_area (x, y, width, height, filename,
Lang.bind(this, this._onScreenshotComplete,
flash, invocation));
_ungrabAccelerator: function(action) {
let ungrabSucceeded = global.display.ungrab_accelerator(action);
if (ungrabSucceeded)
this._grabbedAccelerators.delete(action);
},
/**
* ScreenshotWindow:
* @include_frame: Whether to include the frame or not
* @include_cursor: Whether to include the cursor image or not
* @flash: Whether to flash the window area or not
* @filename: The filename for the screenshot
*
* Takes a screenshot of the focused window (optionally omitting the frame)
* and saves it in @filename as png image, it returns a boolean
* indicating whether the operation was successful or not.
*
*/
ScreenshotWindowAsync : function (params, invocation) {
let [include_frame, include_cursor, flash, filename] = params;
let screenshot = new Shell.Screenshot();
screenshot.screenshot_window (include_frame, include_cursor, filename,
Lang.bind(this, this._onScreenshotComplete,
flash, invocation));
_onGrabberBusNameVanished: function(connection, name) {
let grabs = this._grabbedAccelerators.items();
for (let i = 0; i < grabs.length; i++) {
let [action, sender] = grabs[i];
if (sender == name)
this._ungrabAccelerator(action);
}
Gio.bus_unwatch_name(this._grabbers.get(name));
this._grabbers.delete(name);
},
/**
* Screenshot:
* @filename: The filename for the screenshot
* @include_cursor: Whether to include the cursor image or not
* @flash: Whether to flash the screen or not
*
* Takes a screenshot of the whole screen and saves it
* in @filename as png image, it returns a boolean
* indicating whether the operation was successful or not.
*
*/
ScreenshotAsync : function (params, invocation) {
let [include_cursor, flash, filename] = params;
let screenshot = new Shell.Screenshot();
screenshot.screenshot(include_cursor, filename,
Lang.bind(this, this._onScreenshotComplete,
flash, invocation));
},
FlashArea: function(x, y, width, height) {
let flashspot = new Flashspot.Flashspot({ x : x, y : y, width: width, height: height});
flashspot.fire();
},
Mode: global.session_mode,
@ -350,8 +381,8 @@ const ScreenSaverDBus = new Lang.Class({
this.parent();
this._screenShield = screenShield;
screenShield.connect('lock-status-changed', Lang.bind(this, function(shield) {
this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [shield.locked]));
screenShield.connect('active-changed', Lang.bind(this, function(shield) {
this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [shield.active]));
}));
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenSaverIface, this);
@ -372,13 +403,13 @@ const ScreenSaverDBus = new Lang.Class({
SetActive: function(active) {
if (active)
this._screenShield.lock(true);
this._screenShield.activate(true);
else
this._screenShield.unlock();
this._screenShield.deactivate(false);
},
GetActive: function() {
return this._screenShield.locked;
return this._screenShield.active;
},
GetActiveTime: function() {

View File

@ -1,8 +1,11 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const St = imports.gi.St;
const BoxPointer = imports.ui.boxpointer;
const Main = imports.ui.main;
const Params = imports.misc.params;
const PopupMenu = imports.ui.popupMenu;
@ -11,9 +14,7 @@ const EntryMenu = new Lang.Class({
Name: 'ShellEntryMenu',
Extends: PopupMenu.PopupMenu,
_init: function(entry, params) {
params = Params.parse (params, { isPassword: false });
_init: function(entry) {
this.parent(entry, 0, St.Side.TOP);
this.actor.add_style_class_name('entry-context-menu');
@ -34,8 +35,6 @@ const EntryMenu = new Lang.Class({
this._pasteItem = item;
this._passwordItem = null;
if (params.isPassword)
this._makePasswordItem();
Main.uiGroup.add_actor(this.actor);
this.actor.hide();
@ -50,47 +49,45 @@ const EntryMenu = new Lang.Class({
},
get isPassword() {
return this._passwordItem != null;
return this._passwordItem != null;
},
set isPassword(v) {
if (v == this.isPassword)
return;
if (v == this.isPassword)
return;
if (v)
this._makePasswordItem();
else {
this._passwordItem.destroy();
this._passwordItem = null;
}
if (v) {
this._makePasswordItem();
this._entry.input_purpose = Gtk.InputPurpose.PASSWORD;
} else {
this._passwordItem.destroy();
this._passwordItem = null;
this._entry.input_purpose = Gtk.InputPurpose.FREE_FORM;
}
},
open: function() {
open: function(animate) {
this._updatePasteItem();
this._updateCopyItem();
if (this._passwordItem)
this._updatePasswordItem();
this.parent(animate);
this._entry.add_style_pseudo_class('focus');
let direction = Gtk.DirectionType.TAB_FORWARD;
if (!this.actor.navigate_focus(null, direction, false))
this.actor.grab_key_focus();
this.parent();
this._entry.add_style_pseudo_class('focus');
},
close: function() {
this._entry.grab_key_focus();
this.parent();
},
_updateCopyItem: function() {
let selection = this._entry.clutter_text.get_selection();
this._copyItem.setSensitive(selection && selection != '');
this._copyItem.setSensitive(!this._entry.clutter_text.password_char &&
selection && selection != '');
},
_updatePasteItem: function() {
this._clipboard.get_text(Lang.bind(this,
this._clipboard.get_text(St.ClipboardType.CLIPBOARD, Lang.bind(this,
function(clipboard, text) {
this._pasteItem.setSensitive(text && text != '');
}));
@ -106,11 +103,11 @@ const EntryMenu = new Lang.Class({
_onCopyActivated: function() {
let selection = this._entry.clutter_text.get_selection();
this._clipboard.set_text(selection);
this._clipboard.set_text(St.ClipboardType.CLIPBOARD, selection);
},
_onPasteActivated: function() {
this._clipboard.get_text(Lang.bind(this,
this._clipboard.get_text(St.ClipboardType.CLIPBOARD, Lang.bind(this,
function(clipboard, text) {
if (!text)
return;
@ -134,12 +131,12 @@ function _setMenuAlignment(entry, stageX) {
function _onButtonPressEvent(actor, event, entry) {
if (entry.menu.isOpen) {
entry.menu.close();
entry.menu.close(BoxPointer.PopupAnimation.FULL);
return true;
} else if (event.get_button() == 3) {
let [stageX, stageY] = event.get_coords();
_setMenuAlignment(entry, stageX);
entry.menu.open();
entry.menu.open(BoxPointer.PopupAnimation.FULL);
return true;
}
return false;
@ -149,14 +146,17 @@ function _onPopup(actor, entry) {
let [success, textX, textY, lineHeight] = entry.clutter_text.position_to_coords(-1);
if (success)
entry.menu.setSourceAlignment(textX / entry.width);
entry.menu.open();
entry.menu.open(BoxPointer.PopupAnimation.FULL);
};
function addContextMenu(entry, params) {
if (entry.menu)
return;
entry.menu = new EntryMenu(entry, params);
params = Params.parse (params, { isPassword: false });
entry.menu = new EntryMenu(entry);
entry.menu.isPassword = params.isPassword;
entry._menuManager = new PopupMenu.PopupMenuManager({ actor: entry });
entry._menuManager.addMenu(entry.menu);
@ -167,4 +167,10 @@ function addContextMenu(entry, params) {
entry.connect('button-press-event', Lang.bind(null, _onButtonPressEvent, entry));
entry.connect('popup-menu', Lang.bind(null, _onPopup, entry));
entry.connect('destroy', function() {
entry.menu.destroy();
entry.menu = null;
entry._menuManager = null;
});
}

210
js/ui/slider.js Normal file
View File

@ -0,0 +1,210 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Cairo = imports.cairo;
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const St = imports.gi.St;
const Signals = imports.signals;
const SLIDER_SCROLL_STEP = 0.05; /* Slider scrolling step in % */
const Slider = new Lang.Class({
Name: "Slider",
_init: function(value) {
if (isNaN(value))
// Avoid spreading NaNs around
throw TypeError('The slider value must be a number');
this._value = Math.max(Math.min(value, 1), 0);
this.actor = new St.DrawingArea({ style_class: 'slider',
can_focus: true,
reactive: true });
this.actor.connect('repaint', Lang.bind(this, this._sliderRepaint));
this.actor.connect('button-press-event', Lang.bind(this, this._startDragging));
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
this._releaseId = this._motionId = 0;
this._dragging = false;
},
setValue: function(value) {
if (isNaN(value))
throw TypeError('The slider value must be a number');
this._value = Math.max(Math.min(value, 1), 0);
this.actor.queue_repaint();
},
_sliderRepaint: function(area) {
let cr = area.get_context();
let themeNode = area.get_theme_node();
let [width, height] = area.get_surface_size();
let handleRadius = themeNode.get_length('-slider-handle-radius');
let handleBorderWidth = themeNode.get_length('-slider-handle-border-width');
let [hasHandleColor, handleBorderColor] =
themeNode.lookup_color('-slider-handle-border-color', false);
let sliderHeight = themeNode.get_length('-slider-height');
let sliderBorderWidth = themeNode.get_length('-slider-border-width');
let sliderBorderRadius = Math.min(width, sliderHeight) / 2;
let sliderBorderColor = themeNode.get_color('-slider-border-color');
let sliderColor = themeNode.get_color('-slider-background-color');
let sliderActiveBorderColor = themeNode.get_color('-slider-active-border-color');
let sliderActiveColor = themeNode.get_color('-slider-active-background-color');
const TAU = Math.PI * 2;
let handleX = handleRadius + (width - 2 * handleRadius) * this._value;
cr.arc(sliderBorderRadius + sliderBorderWidth, height / 2, sliderBorderRadius, TAU * 1/4, TAU * 3/4);
cr.lineTo(handleX, (height - sliderHeight) / 2);
cr.lineTo(handleX, (height + sliderHeight) / 2);
cr.lineTo(sliderBorderRadius + sliderBorderWidth, (height + sliderHeight) / 2);
Clutter.cairo_set_source_color(cr, sliderActiveColor);
cr.fillPreserve();
Clutter.cairo_set_source_color(cr, sliderActiveBorderColor);
cr.setLineWidth(sliderBorderWidth);
cr.stroke();
cr.arc(width - sliderBorderRadius - sliderBorderWidth, height / 2, sliderBorderRadius, TAU * 3/4, TAU * 1/4);
cr.lineTo(handleX, (height + sliderHeight) / 2);
cr.lineTo(handleX, (height - sliderHeight) / 2);
cr.lineTo(width - sliderBorderRadius - sliderBorderWidth, (height - sliderHeight) / 2);
Clutter.cairo_set_source_color(cr, sliderColor);
cr.fillPreserve();
Clutter.cairo_set_source_color(cr, sliderBorderColor);
cr.setLineWidth(sliderBorderWidth);
cr.stroke();
let handleY = height / 2;
let color = themeNode.get_foreground_color();
Clutter.cairo_set_source_color(cr, color);
cr.arc(handleX, handleY, handleRadius, 0, 2 * Math.PI);
cr.fillPreserve();
if (hasHandleColor && handleBorderWidth) {
Clutter.cairo_set_source_color(cr, handleBorderColor);
cr.setLineWidth(handleBorderWidth);
cr.stroke();
}
cr.$dispose();
},
_startDragging: function(actor, event) {
this.startDragging(event);
},
startDragging: function(event) {
if (this._dragging)
return false;
this._dragging = true;
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;
},
_endDragging: function() {
if (this._dragging) {
this.actor.disconnect(this._releaseId);
this.actor.disconnect(this._motionId);
this._grabbedDevice.ungrab();
this._grabbedDevice = null;
this._dragging = false;
this.emit('drag-end');
}
return true;
},
scroll: function(event) {
let direction = event.get_scroll_direction();
let delta;
if (event.is_pointer_emulated())
return;
if (direction == Clutter.ScrollDirection.DOWN) {
delta = -SLIDER_SCROLL_STEP;
} else if (direction == Clutter.ScrollDirection.UP) {
delta = +SLIDER_SCROLL_STEP;
} else if (direction == Clutter.ScrollDirection.SMOOTH) {
let [dx, dy] = event.get_scroll_delta();
// Even though the slider is horizontal, use dy to match
// the UP/DOWN above.
delta = -dy / 10;
}
this._value = Math.min(Math.max(0, this._value + delta), 1);
this.actor.queue_repaint();
this.emit('value-changed', this._value);
},
_onScrollEvent: function(actor, event) {
this.scroll(event);
},
_motionEvent: function(actor, event) {
let absX, absY;
[absX, absY] = event.get_coords();
this._moveHandle(absX, absY);
return true;
},
_onKeyPressEvent: function (actor, event) {
let key = event.get_key_symbol();
if (key == Clutter.KEY_Right || key == Clutter.KEY_Left) {
let delta = key == Clutter.KEY_Right ? 0.1 : -0.1;
this._value = Math.max(0, Math.min(this._value + delta, 1));
this._slider.queue_repaint();
this.emit('value-changed', this._value);
this.emit('drag-end');
return true;
}
return false;
},
_moveHandle: function(absX, absY) {
let relX, relY, sliderX, sliderY;
[sliderX, sliderY] = this.actor.get_transformed_position();
relX = absX - sliderX;
relY = absY - sliderY;
let width = this.actor.width;
let handleRadius = this.actor.get_theme_node().get_length('-slider-handle-radius');
let newvalue;
if (relX < handleRadius)
newvalue = 0;
else if (relX > width - handleRadius)
newvalue = 1;
else
newvalue = (relX - handleRadius) / (width - 2 * handleRadius);
this._value = newvalue;
this.actor.queue_repaint();
this.emit('value-changed', this._value);
},
get value() {
return this._value;
}
});
Signals.addSignalMethods(Slider.prototype);

View File

@ -2,6 +2,7 @@
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
@ -14,12 +15,7 @@ const KEY_MOUSE_KEYS_ENABLED = 'mousekeys-enable';
const APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
const DPI_LOW_REASONABLE_VALUE = 50;
const DPI_HIGH_REASONABLE_VALUE = 500;
const DPI_FACTOR_LARGE = 1.25;
const DPI_FACTOR_LARGER = 1.5;
const DPI_FACTOR_LARGEST = 2.0;
const WM_SCHEMA = 'org.gnome.desktop.wm.preferences';
const KEY_VISUAL_BELL = 'visual-bell';
@ -72,8 +68,24 @@ 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();
},
_syncMenuVisibility: function() {
this._syncMenuVisibilityIdle = 0;
let items = this.menu._getMenuItems();
this.actor.visible = items.some(function(f) { return !!f.state; });
return false;
},
_queueSyncMenuVisibility: function() {
if (this._syncMenuVisibilityIdle)
return;
this._syncMenuVisbilityIdle = Mainloop.idle_add(Lang.bind(this, this._syncMenuVisibility));
},
_buildItemExtended: function(string, initial_value, writable, on_set) {
@ -95,9 +107,11 @@ const ATIndicator = new Lang.Class({
function(enabled) {
return settings.set_boolean(key, enabled);
});
settings.connect('changed::'+key, function() {
settings.connect('changed::'+key, Lang.bind(this, function() {
widget.setToggleState(settings.get_boolean(key));
});
this._queueSyncMenuVisibility();
}));
return widget;
},
@ -129,7 +143,7 @@ const ATIndicator = new Lang.Class({
wmSettings.reset(KEY_WM_THEME);
}
});
interfaceSettings.connect('changed::' + KEY_GTK_THEME, function() {
interfaceSettings.connect('changed::' + KEY_GTK_THEME, Lang.bind(this, function() {
let value = interfaceSettings.get_string(KEY_GTK_THEME);
if (value == HIGH_CONTRAST_THEME) {
highContrast.setToggleState(true);
@ -137,7 +151,9 @@ const ATIndicator = new Lang.Class({
highContrast.setToggleState(false);
gtkTheme = value;
}
});
this._queueSyncMenuVisibility();
}));
interfaceSettings.connect('changed::' + KEY_ICON_THEME, function() {
let value = interfaceSettings.get_string(KEY_ICON_THEME);
if (value != HIGH_CONTRAST_THEME)
@ -166,11 +182,22 @@ const ATIndicator = new Lang.Class({
else
settings.reset(KEY_TEXT_SCALING_FACTOR);
});
settings.connect('changed::' + KEY_TEXT_SCALING_FACTOR, function() {
settings.connect('changed::' + KEY_TEXT_SCALING_FACTOR, Lang.bind(this, function() {
let factor = settings.get_double(KEY_TEXT_SCALING_FACTOR);
let active = (factor > 1.0);
widget.setToggleState(active);
});
this._queueSyncMenuVisibility();
}));
return widget;
}
});
const ATGreeterIndicator = new Lang.Class({
Name: 'ATGreeterIndicator',
Extends: ATIndicator,
// Override visibility handling to be always visible
_syncMenuVisibility: function() { },
_queueSyncMenuVisibility: function() { }
});

View File

@ -9,16 +9,10 @@ const St = imports.gi.St;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
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,
@ -26,273 +20,56 @@ 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"), 'bluetooth-properties.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));
this._applet.connect('auth-request', Lang.bind(this, this._authRequest));
this._applet.connect('auth-service-request', Lang.bind(this, this._authServiceRequest));
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() {
if (!this._source) {
this._source = new MessageTray.Source(_("Bluetooth"), 'bluetooth-active');
this._source.policy = new NotificationDaemon.NotificationApplicationPolicy('gnome-bluetooth-panel');
Main.messageTray.add(this._source);
}
},
_authRequest: function(applet, device_path, name, long_name, uuid) {
_authRequest: function(applet, device_path, name, long_name) {
this._ensureSource();
this._source.notify(new AuthNotification(this._source, this._applet, device_path, name, long_name, uuid));
this._source.notify(new AuthNotification(this._source, this._applet, device_path, name, long_name));
},
_authServiceRequest: function(applet, device_path, name, long_name, uuid) {
this._ensureSource();
this._source.notify(new AuthServiceNotification(this._source, this._applet, device_path, name, long_name, uuid));
},
_confirmRequest: function(applet, device_path, name, long_name, pin) {
@ -314,6 +91,34 @@ const AuthNotification = new Lang.Class({
Name: 'AuthNotification',
Extends: MessageTray.Notification,
_init: function(source, applet, device_path, name, long_name) {
this.parent(source,
_("Bluetooth"),
_("Authorization request from %s").format(name),
{ customContent: true });
this.setResident(true);
this._applet = applet;
this._devicePath = device_path;
this.addBody(_("Device %s wants to pair with this computer").format(long_name));
this.addButton('allow', _("Allow"));
this.addButton('deny', _("Deny"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
if (action == 'allow')
this._applet.agent_reply_confirm(this._devicePath, true);
else
this._applet.agent_reply_confirm(this._devicePath, false);
this.destroy();
}));
}
});
const AuthServiceNotification = new Lang.Class({
Name: 'AuthServiceNotification',
Extends: MessageTray.Notification,
_init: function(source, applet, device_path, name, long_name, uuid) {
this.parent(source,
_("Bluetooth"),
@ -332,14 +137,14 @@ const AuthNotification = new Lang.Class({
this.connect('action-invoked', Lang.bind(this, function(self, action) {
switch (action) {
case 'always-grant':
this._applet.agent_reply_auth(this._devicePath, true, true);
this._applet.agent_reply_auth_service(this._devicePath, true, true);
break;
case 'grant':
this._applet.agent_reply_auth(this._devicePath, true, false);
this._applet.agent_reply_auth_service(this._devicePath, true, false);
break;
case 'reject':
default:
this._applet.agent_reply_auth(this._devicePath, false, false);
this._applet.agent_reply_auth_service(this._devicePath, false, false);
}
this.destroy();
}));
@ -361,7 +166,7 @@ const ConfirmNotification = new Lang.Class({
this._applet = applet;
this._devicePath = device_path;
this.addBody(_("Device %s wants to pair with this computer").format(long_name));
this.addBody(_("Please confirm whether the PIN '%06d' matches the one on the device.").format(pin));
this.addBody(_("Please confirm whether the Passkey '%06d' matches the one on the device.").format(pin));
/* Translators: this is the verb, not the noun */
this.addButton('matches', _("Matches"));
@ -446,10 +251,5 @@ const PinNotification = new Lang.Class({
return this._entry.clutter_text.text.length == 6;
else
return true;
},
grabFocus: function(lockTray) {
this.parent(lockTray);
global.stage.set_key_focus(this._entry);
}
});

View File

@ -1,9 +1,11 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const GnomeDesktop = imports.gi.GnomeDesktop;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
@ -21,6 +23,7 @@ try {
const Main = imports.ui.main;
const PopupMenu = imports.ui.popupMenu;
const PanelMenu = imports.ui.panelMenu;
const SwitcherPopup = imports.ui.switcherPopup;
const Util = imports.misc.util;
const DESKTOP_INPUT_SOURCES_SCHEMA = 'org.gnome.desktop.input-sources';
@ -30,6 +33,33 @@ const KEY_INPUT_SOURCES = 'sources';
const INPUT_SOURCE_TYPE_XKB = 'xkb';
const INPUT_SOURCE_TYPE_IBUS = 'ibus';
// This is the longest we'll keep the keyboard frozen until an input
// source is active.
const MAX_INPUT_SOURCE_ACTIVATION_TIME = 4000; // ms
const BUS_NAME = 'org.gnome.SettingsDaemon.Keyboard';
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Keyboard';
const KeyboardManagerInterface =
<interface name="org.gnome.SettingsDaemon.Keyboard">
<method name="SetInputSource">
<arg type="u" direction="in" />
</method>
</interface>;
const KeyboardManagerProxy = Gio.DBusProxy.makeProxyWrapper(KeyboardManagerInterface);
function releaseKeyboard() {
if (Main.modalCount > 0)
global.display.unfreeze_keyboard(global.get_current_time());
else
global.display.ungrab_keyboard(global.get_current_time());
}
function holdKeyboard() {
global.freeze_keyboard(global.get_current_time());
}
const IBusManager = new Lang.Class({
Name: 'IBusManager',
@ -42,35 +72,33 @@ const IBusManager = new Lang.Class({
this._readyCallback = readyCallback;
this._candidatePopup = new IBusCandidatePopup.CandidatePopup();
this._ibus = null;
this._panelService = null;
this._engines = {};
this._ready = false;
this._registerPropertiesId = 0;
this._currentEngineName = null;
this._nameWatcherId = Gio.DBus.session.watch_name(IBus.SERVICE_IBUS,
Gio.BusNameWatcherFlags.NONE,
Lang.bind(this, this._onNameAppeared),
Lang.bind(this, this._clear));
this._ibus = IBus.Bus.new_async();
this._ibus.connect('connected', Lang.bind(this, this._onConnected));
this._ibus.connect('disconnected', Lang.bind(this, this._clear));
// Need to set this to get 'global-engine-changed' emitions
this._ibus.set_watch_ibus_signal(true);
this._ibus.connect('global-engine-changed', Lang.bind(this, this._engineChanged));
},
_clear: function() {
if (this._panelService)
this._panelService.destroy();
if (this._ibus)
this._ibus.destroy();
this._ibus = null;
this._panelService = null;
this._candidatePopup.setPanelService(null);
this._engines = {};
this._ready = false;
this._registerPropertiesId = 0;
},
this._currentEngineName = null;
_onNameAppeared: function() {
this._ibus = IBus.Bus.new_async();
this._ibus.connect('connected', Lang.bind(this, this._onConnected));
if (this._readyCallback)
this._readyCallback(false);
},
_onConnected: function() {
@ -79,7 +107,6 @@ const IBusManager = new Lang.Class({
IBus.BusNameFlag.REPLACE_EXISTING,
-1, null,
Lang.bind(this, this._initPanelService));
this._ibus.connect('disconnected', Lang.bind(this, this._clear));
},
_initEngines: function(ibus, result) {
@ -89,12 +116,10 @@ const IBusManager = new Lang.Class({
let name = enginesList[i].get_name();
this._engines[name] = enginesList[i];
}
this._updateReadiness();
} else {
this._clear();
return;
}
this._updateReadiness();
},
_initPanelService: function(ibus, result) {
@ -103,29 +128,38 @@ const IBusManager = new Lang.Class({
this._panelService = new IBus.PanelService({ connection: this._ibus.get_connection(),
object_path: IBus.PATH_PANEL });
this._candidatePopup.setPanelService(this._panelService);
// Need to set this to get 'global-engine-changed' emitions
this._ibus.set_watch_ibus_signal(true);
this._ibus.connect('global-engine-changed', Lang.bind(this, this._resetProperties));
this._panelService.connect('update-property', Lang.bind(this, this._updateProperty));
this._resetProperties();
// If an engine is already active we need to get its properties
this._ibus.get_global_engine_async(-1, null, Lang.bind(this, function(i, result) {
let engine;
try {
engine = this._ibus.get_global_engine_async_finish(result);
if (!engine)
return;
} catch(e) {
return;
}
this._engineChanged(this._ibus, engine.get_name());
}));
this._updateReadiness();
} else {
this._clear();
return;
}
this._updateReadiness();
},
_updateReadiness: function() {
this._ready = (Object.keys(this._engines).length > 0 &&
this._panelService != null);
if (this._ready && this._readyCallback)
this._readyCallback();
if (this._readyCallback)
this._readyCallback(this._ready);
},
_resetProperties: function() {
this.emit('properties-registered', null);
_engineChanged: function(bus, engineName) {
if (!this._ready)
return;
this._currentEngineName = engineName;
if (this._registerPropertiesId != 0)
return;
@ -138,25 +172,18 @@ const IBusManager = new Lang.Class({
this._panelService.disconnect(this._registerPropertiesId);
this._registerPropertiesId = 0;
this.emit('properties-registered', props);
this.emit('properties-registered', this._currentEngineName, props);
}));
},
_updateProperty: function(panel, prop) {
this.emit('property-updated', prop);
this.emit('property-updated', this._currentEngineName, prop);
},
activateProperty: function(key, state) {
this._panelService.property_activate(key, state);
},
hasProperties: function(id) {
if (id == 'anthy')
return true;
return false;
},
getEngineDesc: function(id) {
if (!IBus || !this._ready)
return null;
@ -181,16 +208,127 @@ const LayoutMenuItem = new Lang.Class({
}
});
const InputSource = new Lang.Class({
Name: 'InputSource',
_init: function(type, id, displayName, shortName, index) {
this.type = type;
this.id = id;
this.displayName = displayName;
this._shortName = shortName;
this.index = index;
this._menuItem = new LayoutMenuItem(this.displayName, this._shortName);
this._menuItem.connect('activate', Lang.bind(this, this.activate));
this._indicatorLabel = new St.Label({ text: this._shortName });
this.properties = null;
},
destroy: function() {
this._menuItem.destroy();
this._indicatorLabel.destroy();
},
get shortName() {
return this._shortName;
},
set shortName(v) {
this._shortName = v;
this._menuItem.indicator.set_text(v);
this._indicatorLabel.set_text(v);
},
get menuItem() {
return this._menuItem;
},
get indicatorLabel() {
return this._indicatorLabel;
},
activate: function() {
this.emit('activate');
},
});
Signals.addSignalMethods(InputSource.prototype);
const InputSourcePopup = new Lang.Class({
Name: 'InputSourcePopup',
Extends: SwitcherPopup.SwitcherPopup,
_init: function(items, action, actionBackward) {
this.parent(items);
this._action = action;
this._actionBackward = actionBackward;
},
_createSwitcher: function() {
this._switcherList = new InputSourceSwitcher(this._items);
return true;
},
_initialSelection: function(backward, binding) {
if (binding == 'switch-input-source') {
if (backward)
this._selectedIndex = this._items.length - 1;
} else if (binding == 'switch-input-source-backward') {
if (!backward)
this._selectedIndex = this._items.length - 1;
}
this._select(this._selectedIndex);
},
_keyPressHandler: function(keysym, backwards, action) {
if (action == this._action)
this._select(backwards ? this._previous() : this._next());
else if (action == this._actionBackward)
this._select(backwards ? this._next() : this._previous());
else if (keysym == Clutter.Left)
this._select(this._previous());
else if (keysym == Clutter.Right)
this._select(this._next());
},
_finish : function() {
this.parent();
this._items[this._selectedIndex].activate();
},
});
const InputSourceSwitcher = new Lang.Class({
Name: 'InputSourceSwitcher',
Extends: SwitcherPopup.SwitcherList,
_init: function(items) {
this.parent(true);
for (let i = 0; i < items.length; i++)
this._addIcon(items[i]);
},
_addIcon: function(item) {
let box = new St.BoxLayout({ vertical: true });
let bin = new St.Bin({ style_class: 'input-source-switcher-symbol' });
let symbol = new St.Label({ text: item.shortName });
bin.set_child(symbol);
box.add(bin, { x_fill: false, y_fill: false } );
let text = new St.Label({ text: item.displayName });
box.add(text, { x_fill: false });
this.addItem(box, text);
}
});
const InputSourceIndicator = new Lang.Class({
Name: 'InputSourceIndicator',
Extends: PanelMenu.Button,
_propertiesWhitelist: [
'InputMode',
'TypingMode',
'DictMode'
],
_init: function() {
this.parent(0.0, _("Keyboard"));
@ -201,14 +339,36 @@ const InputSourceIndicator = new Lang.Class({
this.actor.add_actor(this._container);
this.actor.add_style_class_name('panel-status-button');
this._labelActors = {};
this._layoutItems = {};
// All valid input sources currently in the gsettings
// KEY_INPUT_SOURCES list indexed by their index there
this._inputSources = {};
// All valid input sources currently in the gsettings
// KEY_INPUT_SOURCES list of type INPUT_SOURCE_TYPE_IBUS
// indexed by the IBus ID
this._ibusSources = {};
this._currentSource = null;
// All valid input sources currently in the gsettings
// KEY_INPUT_SOURCES list ordered by most recently used
this._mruSources = [];
this._keybindingAction =
Main.wm.addKeybinding('switch-input-source',
new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }),
Meta.KeyBindingFlags.REVERSES,
Shell.KeyBindingMode.ALL,
Lang.bind(this, this._switchInputSource));
this._keybindingActionBackward =
Main.wm.addKeybinding('switch-input-source-backward',
new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }),
Meta.KeyBindingFlags.REVERSES |
Meta.KeyBindingFlags.REVERSED,
Shell.KeyBindingMode.ALL,
Lang.bind(this, this._switchInputSource));
this._settings = new Gio.Settings({ schema: DESKTOP_INPUT_SOURCES_SCHEMA });
this._settings.connect('changed::' + KEY_CURRENT_INPUT_SOURCE, Lang.bind(this, this._currentInputSourceChanged));
this._settings.connect('changed::' + KEY_INPUT_SOURCES, Lang.bind(this, this._inputSourcesChanged));
this._currentSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
this._xkbInfo = new GnomeDesktop.XkbInfo();
this._propSeparator = new PopupMenu.PopupSeparatorMenuItem();
@ -217,20 +377,33 @@ const InputSourceIndicator = new Lang.Class({
this.menu.addMenuItem(this._propSection);
this._propSection.actor.hide();
this._properties = null;
this._ibusManager = new IBusManager(Lang.bind(this, this._inputSourcesChanged));
this._ibusReady = false;
this._ibusManager = new IBusManager(Lang.bind(this, this._ibusReadyCallback));
this._ibusManager.connect('properties-registered', Lang.bind(this, this._ibusPropertiesRegistered));
this._ibusManager.connect('property-updated', Lang.bind(this, this._ibusPropertyUpdated));
this._inputSourcesChanged();
this._keyboardManager = new KeyboardManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
function(proxy, error) {
if (error)
log(error.message);
});
this._keyboardManager.g_default_timeout = MAX_INPUT_SOURCE_ACTIVATION_TIME;
global.display.connect('modifiers-accelerator-activated', Lang.bind(this, this._modifiersSwitcher));
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._showLayoutItem = this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, this._showLayout));
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._sessionUpdated();
this.menu.addSettingsAction(_("Region and Language Settings"), 'gnome-region-panel.desktop');
this._sourcesPerWindow = false;
this._focusWindowNotifyId = 0;
this._overviewShowingId = 0;
this._overviewHiddenId = 0;
this._settings.connect('changed::per-window', Lang.bind(this, this._sourcesPerWindowChanged));
this._sourcesPerWindowChanged();
},
_sessionUpdated: function() {
@ -241,18 +414,73 @@ const InputSourceIndicator = new Lang.Class({
this._showLayoutItem.actor.visible = Main.sessionMode.allowSettings;
},
_ibusReadyCallback: function(ready) {
if (this._ibusReady == ready)
return;
this._ibusReady = ready;
this._mruSources = [];
this._inputSourcesChanged();
},
_modifiersSwitcher: function() {
let sourceIndexes = Object.keys(this._inputSources);
if (sourceIndexes.length == 0) {
releaseKeyboard();
return true;
}
let is = this._currentSource;
if (!is)
is = this._inputSources[sourceIndexes[0]];
let nextIndex = is.index + 1;
if (nextIndex > sourceIndexes[sourceIndexes.length - 1])
nextIndex = 0;
while (!(is = this._inputSources[nextIndex]))
nextIndex += 1;
is.activate();
return true;
},
_switchInputSource: function(display, screen, window, binding) {
if (this._mruSources.length < 2)
return;
// HACK: Fall back on simple input source switching since we
// can't show a popup switcher while a GrabHelper grab is in
// effect without considerable work to consolidate the usage
// of pushModal/popModal and grabHelper. See
// https://bugzilla.gnome.org/show_bug.cgi?id=695143 .
if (Main.keybindingMode == Shell.KeyBindingMode.MESSAGE_TRAY ||
Main.keybindingMode == Shell.KeyBindingMode.TOPBAR_POPUP) {
this._modifiersSwitcher();
return;
}
let popup = new InputSourcePopup(this._mruSources, this._keybindingAction, this._keybindingActionBackward);
let modifiers = binding.get_modifiers();
let backwards = modifiers & Meta.VirtualModifier.SHIFT_MASK;
if (!popup.show(backwards, binding.get_name(), binding.get_mask()))
popup.destroy();
},
_currentInputSourceChanged: function() {
let nVisibleSources = Object.keys(this._layoutItems).length;
let newCurrentSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
let newLayoutItem = this._layoutItems[newCurrentSourceIndex];
let hasProperties;
let nVisibleSources = Object.keys(this._inputSources).length;
let newSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
let newSource = this._inputSources[newSourceIndex];
if (newLayoutItem)
hasProperties = this._ibusManager.hasProperties(newLayoutItem.ibusEngineId);
else
hasProperties = false;
let oldSource;
[oldSource, this._currentSource] = [this._currentSource, newSource];
if (!newLayoutItem || (nVisibleSources < 2 && !hasProperties)) {
if (oldSource) {
oldSource.menuItem.setOrnament(PopupMenu.Ornament.NONE);
oldSource.indicatorLabel.hide();
}
if (!newSource || (nVisibleSources < 2 && !newSource.properties)) {
// This source index might be invalid if we weren't able
// to build a menu item for it, so we hide ourselves since
// we can't fix it here. *shrug*
@ -266,106 +494,116 @@ const InputSourceIndicator = new Lang.Class({
this.actor.show();
if (this._layoutItems[this._currentSourceIndex]) {
this._layoutItems[this._currentSourceIndex].setShowDot(false);
this._container.set_skip_paint(this._labelActors[this._currentSourceIndex], true);
}
newSource.menuItem.setOrnament(PopupMenu.Ornament.DOT);
newSource.indicatorLabel.show();
newLayoutItem.setShowDot(true);
this._buildPropSection(newSource.properties);
let newLabelActor = this._labelActors[newCurrentSourceIndex];
this._container.set_skip_paint(newLabelActor, false);
for (let i = 1; i < this._mruSources.length; ++i)
if (this._mruSources[i] == newSource) {
let currentSource = this._mruSources.splice(i, 1);
this._mruSources = currentSource.concat(this._mruSources);
break;
}
if (hasProperties)
newLabelActor.set_text(newLayoutItem.indicator.get_text());
this._currentSourceIndex = newCurrentSourceIndex;
this._changePerWindowSource();
},
_inputSourcesChanged: function() {
let sources = this._settings.get_value(KEY_INPUT_SOURCES);
let nSources = sources.n_children();
for (let i in this._layoutItems)
this._layoutItems[i].destroy();
for (let i in this._inputSources)
this._inputSources[i].destroy();
for (let i in this._labelActors)
this._labelActors[i].destroy();
this._inputSources = {};
this._ibusSources = {};
this._currentSource = null;
this._layoutItems = {};
this._labelActors = {};
let infos = [];
let infosByShortName = {};
let inputSourcesByShortName = {};
for (let i = 0; i < nSources; i++) {
let info = { exists: false };
let displayName;
let shortName;
let [type, id] = sources.get_child_value(i).deep_unpack();
let exists = false;
if (type == INPUT_SOURCE_TYPE_XKB) {
[info.exists, info.displayName, info.shortName, , ] =
[exists, displayName, shortName, , ] =
this._xkbInfo.get_layout_info(id);
} else if (type == INPUT_SOURCE_TYPE_IBUS) {
let engineDesc = this._ibusManager.getEngineDesc(id);
if (engineDesc) {
let language = IBus.get_language_name(engineDesc.get_language());
info.exists = true;
info.displayName = language + ' (' + engineDesc.get_longname() + ')';
info.shortName = this._makeEngineShortName(engineDesc);
info.ibusEngineId = id;
exists = true;
displayName = language + ' (' + engineDesc.get_longname() + ')';
shortName = this._makeEngineShortName(engineDesc);
}
}
if (!info.exists)
if (!exists)
continue;
info.sourceIndex = i;
let is = new InputSource(type, id, displayName, shortName, i);
if (!(info.shortName in infosByShortName))
infosByShortName[info.shortName] = [];
infosByShortName[info.shortName].push(info);
infos.push(info);
}
for (let i = 0; i < infos.length; i++) {
let info = infos[i];
if (infosByShortName[info.shortName].length > 1) {
let sub = infosByShortName[info.shortName].indexOf(info) + 1;
info.shortName += String.fromCharCode(0x2080 + sub);
}
let item = new LayoutMenuItem(info.displayName, info.shortName);
item.ibusEngineId = info.ibusEngineId;
this._layoutItems[info.sourceIndex] = item;
this.menu.addMenuItem(item, i);
item.connect('activate', Lang.bind(this, function() {
this._settings.set_value(KEY_CURRENT_INPUT_SOURCE,
GLib.Variant.new_uint32(info.sourceIndex));
is.connect('activate', Lang.bind(this, function() {
holdKeyboard();
this._keyboardManager.SetInputSourceRemote(is.index, releaseKeyboard);
}));
let shortLabel = new St.Label({ text: info.shortName });
this._labelActors[info.sourceIndex] = shortLabel;
this._container.add_actor(shortLabel);
this._container.set_skip_paint(shortLabel, true);
if (!(is.shortName in inputSourcesByShortName))
inputSourcesByShortName[is.shortName] = [];
inputSourcesByShortName[is.shortName].push(is);
this._inputSources[is.index] = is;
if (is.type == INPUT_SOURCE_TYPE_IBUS)
this._ibusSources[is.id] = is;
}
let menuIndex = 0;
for (let i in this._inputSources) {
let is = this._inputSources[i];
if (inputSourcesByShortName[is.shortName].length > 1) {
let sub = inputSourcesByShortName[is.shortName].indexOf(is) + 1;
is.shortName += String.fromCharCode(0x2080 + sub);
}
this.menu.addMenuItem(is.menuItem, menuIndex++);
is.indicatorLabel.hide();
this._container.add_actor(is.indicatorLabel);
}
let sourcesList = [];
for (let i in this._inputSources)
sourcesList.push(this._inputSources[i]);
let mruSources = [];
for (let i = 0; i < this._mruSources.length; i++) {
for (let j = 0; j < sourcesList.length; j++)
if (this._mruSources[i].type == sourcesList[j].type &&
this._mruSources[i].id == sourcesList[j].id) {
mruSources = mruSources.concat(sourcesList.splice(j, 1));
break;
}
}
this._mruSources = mruSources.concat(sourcesList);
this._currentInputSourceChanged();
},
_showLayout: function() {
Main.overview.hide();
let sources = this._settings.get_value(KEY_INPUT_SOURCES);
let current = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
let [type, id] = sources.get_child_value(current).deep_unpack();
let source = this._currentSource;
let xkbLayout = '';
let xkbVariant = '';
if (type == INPUT_SOURCE_TYPE_XKB) {
[, , , xkbLayout, xkbVariant] = this._xkbInfo.get_layout_info(id);
} else if (type == INPUT_SOURCE_TYPE_IBUS) {
let engineDesc = this._ibusManager.getEngineDesc(id);
if (source.type == INPUT_SOURCE_TYPE_XKB) {
[, , , xkbLayout, xkbVariant] = this._xkbInfo.get_layout_info(source.id);
} else if (source.type == INPUT_SOURCE_TYPE_IBUS) {
let engineDesc = this._ibusManager.getEngineDesc(source.id);
if (engineDesc) {
xkbLayout = engineDesc.get_layout();
xkbVariant = '';
@ -394,26 +632,25 @@ const InputSourceIndicator = new Lang.Class({
return String.fromCharCode(0x2328); // keyboard glyph
},
_propertyWhitelisted: function(prop) {
for (let i = 0; i < this._propertiesWhitelist.length; ++i) {
let key = prop.get_key();
if (key.substr(0, this._propertiesWhitelist[i].length) == this._propertiesWhitelist[i])
return true;
}
return false;
},
_ibusPropertiesRegistered: function(im, props) {
this._properties = props;
this._buildPropSection();
},
_ibusPropertyUpdated: function(im, prop) {
if (!this._propertyWhitelisted(prop))
_ibusPropertiesRegistered: function(im, engineName, props) {
let source = this._ibusSources[engineName];
if (!source)
return;
if (this._updateSubProperty(this._properties, prop))
this._buildPropSection();
source.properties = props;
if (source == this._currentSource)
this._currentInputSourceChanged();
},
_ibusPropertyUpdated: function(im, engineName, prop) {
let source = this._ibusSources[engineName];
if (!source)
return;
if (this._updateSubProperty(source.properties, prop) &&
source == this._currentSource)
this._currentInputSourceChanged();
},
_updateSubProperty: function(props, prop) {
@ -433,25 +670,12 @@ const InputSourceIndicator = new Lang.Class({
return false;
},
_updateIndicatorLabel: function(text) {
let layoutItem = this._layoutItems[this._currentSourceIndex];
let hasProperties;
if (layoutItem)
hasProperties = this._ibusManager.hasProperties(layoutItem.ibusEngineId);
else
hasProperties = false;
if (hasProperties)
this._labelActors[this._currentSourceIndex].set_text(text);
},
_buildPropSection: function() {
_buildPropSection: function(properties) {
this._propSeparator.actor.hide();
this._propSection.actor.hide();
this._propSection.removeAll();
this._buildPropSubMenu(this._propSection, this._properties);
this._buildPropSubMenu(this._propSection, properties);
if (!this._propSection.isEmpty()) {
this._propSection.actor.show();
@ -468,8 +692,7 @@ const InputSourceIndicator = new Lang.Class({
for (let i = 0; (p = props.get(i)) != null; ++i) {
let prop = p;
if (!this._propertyWhitelisted(prop) ||
!prop.get_visible())
if (!prop.get_visible())
continue;
if (prop.get_key() == 'InputMode') {
@ -480,20 +703,23 @@ const InputSourceIndicator = new Lang.Class({
text = prop.get_label().get_text();
if (text && text.length > 0 && text.length < 3)
this._updateIndicatorLabel(text);
this._currentSource.indicatorLabel.set_text(text);
}
let item;
let type = prop.get_prop_type();
if (type == IBus.PropType.MENU) {
switch (prop.get_prop_type()) {
case IBus.PropType.MENU:
item = new PopupMenu.PopupSubMenuMenuItem(prop.get_label().get_text());
this._buildPropSubMenu(item.menu, prop.get_sub_props());
} else if (type == IBus.PropType.RADIO) {
break;
case IBus.PropType.RADIO:
item = new PopupMenu.PopupMenuItem(prop.get_label().get_text());
item.prop = prop;
radioGroup.push(item);
item.radioGroup = radioGroup;
item.setShowDot(prop.get_state() == IBus.PropState.CHECKED);
item.setOrnament(prop.get_state() == IBus.PropState.CHECKED ?
PopupMenu.Ornament.DOT : PopupMenu.Ornament.NONE);
item.connect('activate', Lang.bind(this, function() {
if (item.prop.get_state() == IBus.PropState.CHECKED)
return;
@ -501,19 +727,51 @@ const InputSourceIndicator = new Lang.Class({
let group = item.radioGroup;
for (let i = 0; i < group.length; ++i) {
if (group[i] == item) {
item.setShowDot(true);
item.setOrnament(PopupMenu.Ornament.DOT);
item.prop.set_state(IBus.PropState.CHECKED);
this._ibusManager.activateProperty(item.prop.get_key(),
IBus.PropState.CHECKED);
} else {
group[i].setShowDot(false);
group[i].setOrnament(PopupMenu.Ornament.NONE);
group[i].prop.set_state(IBus.PropState.UNCHECKED);
this._ibusManager.activateProperty(group[i].prop.get_key(),
IBus.PropState.UNCHECKED);
}
}
}));
} else {
break;
case IBus.PropType.TOGGLE:
item = new PopupMenu.PopupSwitchMenuItem(prop.get_label().get_text(), prop.get_state() == IBus.PropState.CHECKED);
item.prop = prop;
item.connect('toggled', Lang.bind(this, function() {
if (item.state) {
item.prop.set_state(IBus.PropState.CHECKED);
this._ibusManager.activateProperty(item.prop.get_key(),
IBus.PropState.CHECKED);
} else {
item.prop.set_state(IBus.PropState.UNCHECKED);
this._ibusManager.activateProperty(item.prop.get_key(),
IBus.PropState.UNCHECKED);
}
}));
break;
case IBus.PropType.NORMAL:
item = new PopupMenu.PopupMenuItem(prop.get_label().get_text());
item.prop = prop;
item.connect('activate', Lang.bind(this, function() {
this._ibusManager.activateProperty(item.prop.get_key(),
item.prop.get_state());
}));
break;
case IBus.PropType.SEPARATOR:
item = new PopupMenu.PopupSeparatorMenuItem();
break;
default:
log ('IBus property %s has invalid type %d'.format(prop.get_key(), type));
continue;
}
@ -522,14 +780,91 @@ const InputSourceIndicator = new Lang.Class({
}
},
_getNewInputSource: function(current) {
for (let i in this._inputSources) {
let is = this._inputSources[i];
if (is.type == current.type &&
is.id == current.id)
return is;
}
return this._currentSource;
},
_getCurrentWindow: function() {
if (Main.overview.visible)
return Main.overview;
else
return global.display.focus_window;
},
_setPerWindowInputSource: function() {
let window = this._getCurrentWindow();
if (!window)
return;
if (!window._inputSources) {
window._inputSources = this._inputSources;
window._currentSource = this._currentSource;
} else if (window._inputSources == this._inputSources) {
window._currentSource.activate();
} else {
window._inputSources = this._inputSources;
window._currentSource = this._getNewInputSource(window._currentSource);
window._currentSource.activate();
}
},
_sourcesPerWindowChanged: function() {
this._sourcesPerWindow = this._settings.get_boolean('per-window');
if (this._sourcesPerWindow && this._focusWindowNotifyId == 0) {
this._focusWindowNotifyId = global.display.connect('notify::focus-window',
Lang.bind(this, this._setPerWindowInputSource));
this._overviewShowingId = Main.overview.connect('showing',
Lang.bind(this, this._setPerWindowInputSource));
this._overviewHiddenId = Main.overview.connect('hidden',
Lang.bind(this, this._setPerWindowInputSource));
} else if (!this._sourcesPerWindow && this._focusWindowNotifyId != 0) {
global.display.disconnect(this._focusWindowNotifyId);
this._focusWindowNotifyId = 0;
Main.overview.disconnect(this._overviewShowingId);
this._overviewShowingId = 0;
Main.overview.disconnect(this._overviewHiddenId);
this._overviewHiddenId = 0;
let windows = global.get_window_actors().map(function(w) {
return w.meta_window;
});
for (let i = 0; i < windows.length; ++i) {
delete windows[i]._inputSources;
delete windows[i]._currentSource;
}
delete Main.overview._inputSources;
delete Main.overview._currentSource;
}
},
_changePerWindowSource: function() {
if (!this._sourcesPerWindow)
return;
let window = this._getCurrentWindow();
if (!window)
return;
window._inputSources = this._inputSources;
window._currentSource = this._currentSource;
},
_containerGetPreferredWidth: function(container, for_height, alloc) {
// Here, and in _containerGetPreferredHeight, we need to query
// for the height of all children, but we ignore the results
// for those we don't actually display.
let max_min_width = 0, max_natural_width = 0;
for (let i in this._labelActors) {
let [min_width, natural_width] = this._labelActors[i].get_preferred_width(for_height);
for (let i in this._inputSources) {
let is = this._inputSources[i];
let [min_width, natural_width] = is.indicatorLabel.get_preferred_width(for_height);
max_min_width = Math.max(max_min_width, min_width);
max_natural_width = Math.max(max_natural_width, natural_width);
}
@ -541,8 +876,9 @@ const InputSourceIndicator = new Lang.Class({
_containerGetPreferredHeight: function(container, for_width, alloc) {
let max_min_height = 0, max_natural_height = 0;
for (let i in this._labelActors) {
let [min_height, natural_height] = this._labelActors[i].get_preferred_height(for_width);
for (let i in this._inputSources) {
let is = this._inputSources[i];
let [min_height, natural_height] = is.indicatorLabel.get_preferred_height(for_width);
max_min_height = Math.max(max_min_height, min_height);
max_natural_height = Math.max(max_natural_height, natural_height);
}
@ -558,7 +894,9 @@ const InputSourceIndicator = new Lang.Class({
box.y2 -= box.y1;
box.y1 = 0;
for (let i in this._labelActors)
this._labelActors[i].allocate_align_fill(box, 0.5, 0, false, false, flags);
for (let i in this._inputSources) {
let is = this._inputSources[i];
is.indicatorLabel.allocate_align_fill(box, 0.5, 0, false, false, flags);
}
}
});

View File

@ -10,48 +10,53 @@ const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const VolumeMenu = imports.ui.status.volume;
const FakeStatusIcon = new Lang.Class({
Name: 'FakeStatusIcon',
_init: function(button) {
this.actor = new St.BoxLayout({ style_class: 'panel-status-button-box' });
this._button = button;
this._button.connect('icons-updated', Lang.bind(this, this._reconstructIcons));
this._button.actor.bind_property('visible', this.actor, 'visible',
GObject.BindingFlags.SYNC_CREATE);
this._reconstructIcons();
},
_reconstructIcons: function() {
this.actor.destroy_all_children();
this._button.icons.forEach(Lang.bind(this, function(icon) {
let newIcon = new St.Icon({ style_class: 'system-status-icon' });
icon.bind_property('gicon', newIcon, 'gicon',
GObject.BindingFlags.SYNC_CREATE);
icon.bind_property('visible', newIcon, 'visible',
GObject.BindingFlags.SYNC_CREATE);
this.actor.add_actor(newIcon);
}));
}
});
const Indicator = new Lang.Class({
Name: 'LockScreenMenuIndicator',
Extends: PanelMenu.SystemStatusButton,
_init: function() {
this.parent(null, _("Volume, network, battery"));
this._box.style_class = 'lock-screen-status-button-box';
this._volume = Main.panel.statusArea.volume;
if (this._volume) {
this._volumeIcon = this.addIcon(null);
this._volume.mainIcon.bind_property('gicon', this._volumeIcon, 'gicon',
GObject.BindingFlags.SYNC_CREATE);
this._volume.mainIcon.bind_property('visible', this._volumeIcon, 'visible',
GObject.BindingFlags.SYNC_CREATE);
this._volumeControl = VolumeMenu.getMixerControl();
this._volumeMenu = new VolumeMenu.VolumeMenu(this._volumeControl);
this.menu.addMenuItem(this._volumeMenu);
this._volumeControl = VolumeMenu.getMixerControl();
this._volumeMenu = new VolumeMenu.VolumeMenu(this._volumeControl);
this.menu.addMenuItem(this._volumeMenu);
this._volume = new FakeStatusIcon(Main.panel.statusArea.volume);
this._box.add_child(this._volume.actor);
// Network may not exist if the user doesn't have NetworkManager
if (Main.panel.statusArea.network) {
this._network = new FakeStatusIcon(Main.panel.statusArea.network);
this._box.add_child(this._network.actor);
}
this._network = Main.panel.statusArea.network;
if (this._network) {
this._networkIcon = this.addIcon(null);
this._network.mainIcon.bind_property('gicon', this._networkIcon, 'gicon',
GObject.BindingFlags.SYNC_CREATE);
this._network.mainIcon.bind_property('visible', this._networkIcon, 'visible',
GObject.BindingFlags.SYNC_CREATE);
this._networkSecondaryIcon = this.addIcon(null);
this._network.secondaryIcon.bind_property('gicon', this._networkSecondaryIcon, 'gicon',
GObject.BindingFlags.SYNC_CREATE);
this._network.secondaryIcon.bind_property('visible', this._networkSecondaryIcon, 'visible',
GObject.BindingFlags.SYNC_CREATE);
}
this._battery = Main.panel.statusArea.battery;
if (this._battery) {
this._batteryIcon = this.addIcon(null);
this._battery.mainIcon.bind_property('gicon', this._batteryIcon, 'gicon',
GObject.BindingFlags.SYNC_CREATE);
this._battery.mainIcon.bind_property('visible', this._batteryIcon, 'visible',
GObject.BindingFlags.SYNC_CREATE);
}
this._battery = new FakeStatusIcon(Main.panel.statusArea.battery);
this._box.add_child(this._battery.actor);
}
});

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';
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" />
@ -54,92 +30,74 @@ const Indicator = new Lang.Class({
_init: function() {
this.parent('battery-missing-symbolic', _("Battery"));
this._proxy = new PowerManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH);
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._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();
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');
this._proxy.connect('g-properties-changed',
Lang.bind(this, this._devicesChanged));
this._devicesChanged();
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();
}
}));
},
@ -151,70 +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)) });
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

@ -5,11 +5,11 @@ const Lang = imports.lang;
const Gio = imports.gi.Gio;
const Gvc = imports.gi.Gvc;
const St = imports.gi.St;
const Signals = imports.signals;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const VOLUME_ADJUSTMENT_STEP = 0.05; /* Volume adjustment step in % */
const Slider = imports.ui.slider;
const VOLUME_NOTIFY_ID = 1;
@ -26,6 +26,220 @@ function getMixerControl() {
return _mixerControl;
}
const StreamSlider = new Lang.Class({
Name: 'StreamSlider',
_init: function(control) {
this._control = control;
this.item = new PopupMenu.PopupBaseMenuItem({ activate: false });
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._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;
},
get stream() {
return this._stream;
},
set stream(stream) {
if (this._stream) {
this._disconnectStream(this._stream);
}
this._stream = stream;
if (this._stream) {
this._connectStream(this._stream);
this._updateVolume();
} else {
this.emit('stream-updated');
}
this._updateVisibility();
},
_disconnectStream: function(stream) {
stream.disconnect(this._mutedChangedId);
this._mutedChangedId = 0;
stream.disconnect(this._volumeChangedId);
this._volumeChangedId = 0;
},
_connectStream: function(stream) {
this._mutedChangedId = stream.connect('notify::is-muted', Lang.bind(this, this._updateVolume));
this._volumeChangedId = stream.connect('notify::volume', Lang.bind(this, this._updateVolume));
},
_shouldBeVisible: function() {
return this._stream != null;
},
_updateVisibility: function() {
let visible = this._shouldBeVisible();
this.item.actor.visible = visible;
},
scroll: function(event) {
this._slider.scroll(event);
},
setValue: function(value) {
// piggy-back off of sliderChanged
this._slider.setValue(value);
},
_sliderChanged: function(slider, value, property) {
if (!this._stream)
return;
let volume = value * this._control.get_vol_max_norm();
let prevMuted = this._stream.is_muted;
if (volume < 1) {
this._stream.volume = 0;
if (!prevMuted)
this._stream.change_is_muted(true);
} else {
this._stream.volume = volume;
if (prevMuted)
this._stream.change_is_muted(false);
}
this._stream.push_volume();
},
_notifyVolumeChange: function() {
global.cancel_theme_sound(VOLUME_NOTIFY_ID);
global.play_theme_sound(VOLUME_NOTIFY_ID,
'audio-volume-change',
_("Volume changed"),
Clutter.get_current_event ());
},
_updateVolume: function() {
let muted = this._stream.is_muted;
this._slider.setValue(muted ? 0 : (this._stream.volume / this._control.get_vol_max_norm()));
this.emit('stream-updated');
},
getIcon: function() {
if (!this._stream)
return null;
let volume = this._stream.volume;
if (this._stream.is_muted || volume <= 0) {
return 'audio-volume-muted-symbolic';
} else {
let n = Math.floor(3 * volume / this._control.get_vol_max_norm()) + 1;
if (n < 2)
return 'audio-volume-low-symbolic';
if (n >= 3)
return 'audio-volume-high-symbolic';
return 'audio-volume-medium-symbolic';
}
}
});
Signals.addSignalMethods(StreamSlider.prototype);
const OutputStreamSlider = new Lang.Class({
Name: 'OutputStreamSlider',
Extends: StreamSlider,
_connectStream: function(stream) {
this.parent(stream);
this._portChangedId = stream.connect('notify::port', Lang.bind(this, this._portChanged));
this._portChanged();
},
_findHeadphones: function(sink) {
// This only works for external headphones (e.g. bluetooth)
if (sink.get_form_factor() == 'headset' ||
sink.get_form_factor() == 'headphone')
return true;
// a bit hackish, but ALSA/PulseAudio have a number
// of different identifiers for headphones, and I could
// not find the complete list
if (sink.get_ports().length > 0)
return sink.get_port().port.indexOf('headphone') >= 0;
return false;
},
_disconnectStream: function(stream) {
this.parent(stream);
stream.disconnect(this._portChangedId);
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._updateSliderIcon();
}
}
});
const InputStreamSlider = new Lang.Class({
Name: 'InputStreamSlider',
Extends: StreamSlider,
_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) {
this.parent(stream);
this._maybeShowInput();
},
_maybeShowInput: function() {
// only show input widgets if any application is recording audio
let showInput = false;
let recordingApps = this._control.get_source_outputs();
if (this._stream && recordingApps) {
for (let i = 0; i < recordingApps.length; i++) {
let outputStream = recordingApps[i];
let id = outputStream.get_application_id();
// but skip gnome-volume-control and pavucontrol
// (that appear as recording because they show the input level)
if (!id || (id != 'org.gnome.VolumeControl' && id != 'org.PulseAudio.pavucontrol')) {
showInput = true;
break;
}
}
}
this._showInput = showInput;
this._updateVisibility();
},
_shouldBeVisible: function() {
return this.parent() && this._showInput;
}
});
const VolumeMenu = new Lang.Class({
Name: 'VolumeMenu',
Extends: PopupMenu.PopupMenuSection,
@ -39,207 +253,44 @@ const VolumeMenu = new Lang.Class({
this._control.connect('state-changed', Lang.bind(this, this._onControlStateChanged));
this._control.connect('default-sink-changed', Lang.bind(this, this._readOutput));
this._control.connect('default-source-changed', Lang.bind(this, this._readInput));
this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput));
this._control.connect('stream-removed', Lang.bind(this, this._maybeShowInput));
this._volumeMax = this._control.get_vol_max_norm();
this._output = null;
this._outputVolumeId = 0;
this._outputMutedId = 0;
/* Translators: This is the label for audio volume */
this._outputTitle = new PopupMenu.PopupMenuItem(_("Volume"), { reactive: false });
this._outputSlider = new PopupMenu.PopupSliderMenuItem(0);
this._outputSlider.connect('value-changed', Lang.bind(this, this._sliderChanged, '_output'));
this._outputSlider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
this.addMenuItem(this._outputTitle);
this.addMenuItem(this._outputSlider);
this._output = new OutputStreamSlider(this._control);
this._output.connect('stream-updated', Lang.bind(this, function() {
this.emit('icon-changed');
}));
this.addMenuItem(this._output.item);
this._input = new InputStreamSlider(this._control);
this.addMenuItem(this._input.item);
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._input = null;
this._inputVolumeId = 0;
this._inputMutedId = 0;
this._inputTitle = new PopupMenu.PopupMenuItem(_("Microphone"), { reactive: false });
this._inputSlider = new PopupMenu.PopupSliderMenuItem(0);
this._inputSlider.connect('value-changed', Lang.bind(this, this._sliderChanged, '_input'));
this._inputSlider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
this.addMenuItem(this._inputTitle);
this.addMenuItem(this._inputSlider);
this._onControlStateChanged();
},
scroll: function(direction) {
let currentVolume = this._output.volume;
if (direction == Clutter.ScrollDirection.DOWN) {
let prev_muted = this._output.is_muted;
this._output.volume = Math.max(0, currentVolume - this._volumeMax * VOLUME_ADJUSTMENT_STEP);
if (this._output.volume < 1) {
this._output.volume = 0;
if (!prev_muted)
this._output.change_is_muted(true);
}
this._output.push_volume();
}
else if (direction == Clutter.ScrollDirection.UP) {
this._output.volume = Math.min(this._volumeMax, currentVolume + this._volumeMax * VOLUME_ADJUSTMENT_STEP);
this._output.change_is_muted(false);
this._output.push_volume();
}
this._notifyVolumeChange();
scroll: function(event) {
this._output.scroll(event);
},
_onControlStateChanged: function() {
if (this._control.get_state() == Gvc.MixerControlState.READY) {
this._readOutput();
this._readInput();
this._maybeShowInput();
this._readOutput();
} else {
this.emit('icon-changed', null);
this.emit('icon-changed');
}
},
_findHeadphones: function(sink) {
// This only works for external headphones (e.g. bluetooth)
if (sink.get_form_factor() == 'headset' ||
sink.get_form_factor() == 'headphone')
return true;
// a bit hackish, but ALSA/PulseAudio have a number
// of different identifiers for headphones, and I could
// not find the complete list
let port = sink.get_port();
if (port)
return port.port.indexOf('headphone') >= 0;
return false;
},
_portChanged: function() {
this.hasHeadphones = this._findHeadphones(this._output);
this.emit('headphones-changed');
},
_readOutput: function() {
if (this._outputVolumeId) {
this._output.disconnect(this._outputVolumeId);
this._output.disconnect(this._outputMutedId);
this._output.disconnect(this._outputPortId);
this._outputVolumeId = 0;
this._outputMutedId = 0;
this._outputPortId = 0;
}
this._output = this._control.get_default_sink();
if (this._output) {
this._outputMutedId = this._output.connect('notify::is-muted', Lang.bind(this, this._mutedChanged, '_output'));
this._outputVolumeId = this._output.connect('notify::volume', Lang.bind(this, this._volumeChanged, '_output'));
this._outputPortId = this._output.connect('notify::port', Lang.bind(this, this._portChanged));
this._mutedChanged(null, null, '_output');
this._volumeChanged(null, null, '_output');
this._portChanged();
} else {
this.hasHeadphones = false;
this._outputSlider.setValue(0);
this.emit('icon-changed', 'audio-volume-muted-symbolic');
}
this._output.stream = this._control.get_default_sink();
},
_readInput: function() {
if (this._inputVolumeId) {
this._input.disconnect(this._inputVolumeId);
this._input.disconnect(this._inputMutedId);
this._inputVolumeId = 0;
this._inputMutedId = 0;
}
this._input = this._control.get_default_source();
if (this._input) {
this._inputMutedId = this._input.connect('notify::is-muted', Lang.bind(this, this._mutedChanged, '_input'));
this._inputVolumeId = this._input.connect('notify::volume', Lang.bind(this, this._volumeChanged, '_input'));
this._mutedChanged (null, null, '_input');
this._volumeChanged (null, null, '_input');
} else {
this._inputTitle.actor.hide();
this._inputSlider.actor.hide();
}
this._input.stream = this._control.get_default_source();
},
_maybeShowInput: function() {
// only show input widgets if any application is recording audio
let showInput = false;
let recordingApps = this._control.get_source_outputs();
if (this._input && recordingApps) {
for (let i = 0; i < recordingApps.length; i++) {
let outputStream = recordingApps[i];
let id = outputStream.get_application_id();
// but skip gnome-volume-control and pavucontrol
// (that appear as recording because they show the input level)
if (!id || (id != 'org.gnome.VolumeControl' && id != 'org.PulseAudio.pavucontrol')) {
showInput = true;
break;
}
}
}
this._inputTitle.actor.visible = showInput;
this._inputSlider.actor.visible = showInput;
},
_volumeToIcon: function(volume) {
if (volume <= 0) {
return 'audio-volume-muted-symbolic';
} else {
let n = Math.floor(3 * volume / this._volumeMax) + 1;
if (n < 2)
return 'audio-volume-low-symbolic';
if (n >= 3)
return 'audio-volume-high-symbolic';
return 'audio-volume-medium-symbolic';
}
},
_sliderChanged: function(slider, value, property) {
if (this[property] == null) {
log ('Volume slider changed for %s, but %s does not exist'.format(property, property));
return;
}
let volume = value * this._volumeMax;
let prev_muted = this[property].is_muted;
if (volume < 1) {
this[property].volume = 0;
if (!prev_muted)
this[property].change_is_muted(true);
} else {
this[property].volume = volume;
if (prev_muted)
this[property].change_is_muted(false);
}
this[property].push_volume();
},
_notifyVolumeChange: function() {
global.cancel_theme_sound(VOLUME_NOTIFY_ID);
global.play_theme_sound(VOLUME_NOTIFY_ID, 'audio-volume-change');
},
_mutedChanged: function(object, param_spec, property) {
let muted = this[property].is_muted;
let slider = this[property+'Slider'];
slider.setValue(muted ? 0 : (this[property].volume / this._volumeMax));
if (property == '_output') {
if (muted)
this.emit('icon-changed', 'audio-volume-muted-symbolic');
else
this.emit('icon-changed', this._volumeToIcon(this._output.volume));
}
},
_volumeChanged: function(object, param_spec, property) {
this[property+'Slider'].setValue(this[property].volume / this._volumeMax);
if (property == '_output' && !this._output.is_muted)
this.emit('icon-changed', this._volumeToIcon(this._output.volume));
getIcon: function() {
return this._output.getIcon();
}
});
@ -252,33 +303,18 @@ const Indicator = new Lang.Class({
this._control = getMixerControl();
this._volumeMenu = new VolumeMenu(this._control);
this._volumeMenu.connect('icon-changed', Lang.bind(this, function(menu, icon) {
this._hasPulseAudio = (icon != null);
this._volumeMenu.connect('icon-changed', Lang.bind(this, function(menu) {
let icon = this._volumeMenu.getIcon();
this.actor.visible = (icon != null);
this.setIcon(icon);
this._syncVisibility();
}));
this._volumeMenu.connect('headphones-changed', Lang.bind(this, function() {
this._syncVisibility();
}));
this._headphoneIcon = this.addIcon(new Gio.ThemedIcon({ name: '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));
},
_syncVisibility: function() {
this.actor.visible = this._hasPulseAudio;
this.mainIcon.visible = this._hasPulseAudio;
this._headphoneIcon.visible = this._hasPulseAudio && this._volumeMenu.hasHeadphones;
},
_onScrollEvent: function(actor, event) {
this._volumeMenu.scroll(event.get_scroll_direction());
this._volumeMenu.scroll(event);
}
});

View File

@ -46,7 +46,8 @@ const SwitcherPopup = new Lang.Class({
this._selectedIndex = 0;
this.actor = new Shell.GenericContainer({ style_class: 'switcher-popup',
reactive: true });
reactive: true,
visible: false });
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this.actor.connect('allocate', Lang.bind(this, this._allocate));
@ -159,6 +160,7 @@ const SwitcherPopup = new Lang.Class({
// disturbed by the popup briefly flashing.
this._initialDelayTimeoutId = Mainloop.timeout_add(POPUP_DELAY_TIMEOUT,
Lang.bind(this, function () {
Main.osdWindow.cancel();
this.actor.opacity = 255;
this._initialDelayTimeoutId = 0;
}));
@ -303,7 +305,7 @@ const SwitcherList = new Lang.Class({
this.actor.connect('allocate', Lang.bind(this, this._allocateTop));
// Here we use a GenericContainer so that we can force all the
// children except the separator to have the same width.
// children to have the same width.
this._list = new Shell.GenericContainer({ style_class: 'switcher-list-item-container' });
this._list.spacing = 0;
this._list.connect('style-changed', Lang.bind(this, function() {
@ -338,7 +340,6 @@ const SwitcherList = new Lang.Class({
this._items = [];
this._highlighted = -1;
this._separator = null;
this._squareItems = squareItems;
this._minSize = 0;
this._scrollableRight = true;
@ -401,12 +402,6 @@ const SwitcherList = new Lang.Class({
this._itemEntered(index);
},
addSeparator: function () {
let box = new St.Bin({ style_class: 'separator' });
this._separator = box;
this._list.add_actor(box);
},
highlight: function(index, justOutline) {
if (this._highlighted != -1) {
this._items[this._highlighted].remove_style_pseudo_class('outlined');
@ -514,14 +509,8 @@ const SwitcherList = new Lang.Class({
_getPreferredWidth: function (actor, forHeight, alloc) {
let [maxChildMin, maxChildNat] = this._maxChildWidth(forHeight);
let separatorWidth = 0;
if (this._separator) {
let [sepMin, sepNat] = this._separator.get_preferred_width(forHeight);
separatorWidth = sepNat + this._list.spacing;
}
let totalSpacing = this._list.spacing * (this._items.length - 1);
alloc.min_size = this._items.length * maxChildMin + separatorWidth + totalSpacing;
alloc.min_size = this._items.length * maxChildMin + totalSpacing;
alloc.natural_size = alloc.min_size;
this._minSize = alloc.min_size;
},
@ -552,14 +541,7 @@ const SwitcherList = new Lang.Class({
let [maxChildMin, maxChildNat] = this._maxChildWidth(childHeight);
let totalSpacing = this._list.spacing * (this._items.length - 1);
let separatorWidth = 0;
if (this._separator) {
let [sepMin, sepNat] = this._separator.get_preferred_width(childHeight);
separatorWidth = sepNat;
totalSpacing += this._list.spacing;
}
let childWidth = Math.floor(Math.max(0, box.x2 - box.x1 - totalSpacing - separatorWidth) / this._items.length);
let childWidth = Math.floor(Math.max(0, box.x2 - box.x1 - totalSpacing) / this._items.length);
let x = 0;
let children = this._list.get_children();
@ -579,14 +561,6 @@ const SwitcherList = new Lang.Class({
children[i].allocate(childBox, flags);
x += this._list.spacing + childWidth;
} else if (children[i] == this._separator) {
// We want the separator to be more compact than the rest.
childBox.x1 = x;
childBox.y1 = 0;
childBox.x2 = x + separatorWidth;
childBox.y2 = childHeight;
children[i].allocate(childBox, flags);
x += this._list.spacing + separatorWidth;
} else {
// Something else, eg, AppSwitcher's arrows;
// we don't allocate it.
@ -637,5 +611,6 @@ function drawArrow(area, side) {
Clutter.cairo_set_source_color(cr, bodyColor);
cr.fill();
cr.$dispose();
}

View File

@ -2,6 +2,7 @@
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
@ -73,6 +74,9 @@ function _wrapTweening(target, tweeningParameters) {
}
}
if (!Gtk.Settings.get_default().gtk_enable_animations)
tweeningParameters['time'] = 0.000001;
_addHandler(target, tweeningParameters, 'onStart', _tweenStarted);
_addHandler(target, tweeningParameters, 'onComplete', _tweenCompleted);
}

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 UserMenu = imports.ui.userMenu;
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;
@ -26,356 +27,143 @@ const LoginDialog = imports.gdm.loginDialog;
// The timeout before going back automatically to the lock screen (in seconds)
const IDLE_TIMEOUT = 2 * 60;
function versionCompare(required, reference) {
required = required.split('.');
reference = reference.split('.');
for (let i = 0; i < required.length; i++) {
if (required[i] != reference[i])
return required[i] < reference[i];
}
return true;
}
function isSupported() {
try {
let params = GLib.Variant.new('(ss)', ['org.gnome.DisplayManager.Manager', 'Version']);
let result = Gio.DBus.system.call_sync('org.gnome.DisplayManager',
'/org/gnome/DisplayManager/Manager',
'org.freedesktop.DBus.Properties',
'Get', params, null,
Gio.DBusCallFlags.NONE,
-1, null);
let version = result.deep_unpack()[0].deep_unpack();
return versionCompare('3.5.91', version);
} catch(e) {
return false;
}
}
// A widget showing the user avatar and name
const UserWidget = new Lang.Class({
Name: 'UserWidget',
_init: function(user) {
this._user = user;
this.actor = new St.BoxLayout({ style_class: 'unlock-dialog-user-name-container',
vertical: false });
this._avatar = new UserMenu.UserAvatarWidget(user);
this.actor.add(this._avatar.actor,
{ x_fill: true, y_fill: true });
this._label = new St.Label({ style_class: 'login-dialog-username' });
this.actor.add(this._label,
{ expand: true,
x_fill: true,
y_fill: false,
y_align: St.Align.MIDDLE });
this._userLoadedId = this._user.connect('notify::is-loaded',
Lang.bind(this, this._updateUser));
this._userChangedId = this._user.connect('changed',
Lang.bind(this, this._updateUser));
if (this._user.is_loaded)
this._updateUser();
},
destroy: function() {
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.actor.destroy();
},
_updateUser: function() {
if (this._user.is_loaded)
this._label.text = this._user.get_real_name();
else
this._label.text = '';
this._avatar.update();
}
});
const UnlockDialog = new Lang.Class({
Name: 'UnlockDialog',
Extends: ModalDialog.ModalDialog,
_init: function(parentActor) {
this.parent({ shellReactive: true,
styleClass: 'login-dialog',
keybindingMode: Main.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._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(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.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._workSpinner = new Panel.AnimatedIcon('process-working.svg', LoginDialog.WORK_SPINNER_ICON_SIZE);
this._workSpinner.actor.opacity = 0;
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.buttonLayout.add(this._workSpinner.actor,
{ 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 otherUserLabel = new St.Label({ text: _("Log in as another user"),
style_class: 'login-dialog-not-listed-label' });
this._otherUserButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
can_focus: true,
child: otherUserLabel,
reactive: true,
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 });
let screenSaverSettings = new Gio.Settings({ schema: 'org.gnome.desktop.screensaver' });
if (screenSaverSettings.get_boolean('user-switch-enabled')) {
let otherUserLabel = new St.Label({ text: _("Log in as another user"),
style_class: 'login-dialog-not-listed-label' });
this._otherUserButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
can_focus: true,
child: otherUserLabel,
reactive: true,
x_align: St.Align.START,
x_fill: true });
this._otherUserButton.connect('clicked', Lang.bind(this, this._otherUserClicked));
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);
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
this.emit('loaded');
return false;
}));
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_watch(IDLE_TIMEOUT * 1000, Lang.bind(this, this._escape));
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._otherUserButton.reactive = sensitive;
this._otherUserButton.can_focus = sensitive;
},
this._authPrompt.updateSensitivity(sensitive);
_updateOkButtonSensitivity: function(sensitive) {
this._okButton.reactive = sensitive;
this._okButton.can_focus = sensitive;
},
_setWorking: function(working) {
if (working) {
this._workSpinner.play();
Tweener.addTween(this._workSpinner.actor,
{ opacity: 255,
delay: LoginDialog.WORK_SPINNER_ANIMATION_DELAY,
time: LoginDialog.WORK_SPINNER_ANIMATION_TIME,
transition: 'linear'
});
} else {
Tweener.addTween(this._workSpinner.actor,
{ opacity: 0,
time: LoginDialog.WORK_SPINNER_ANIMATION_TIME,
transition: 'linear',
onCompleteScope: this,
onComplete: function() {
this._workSpinner.stop();
}
});
if (this._otherUserButton) {
this._otherUserButton.reactive = sensitive;
this._otherUserButton.can_focus = sensitive;
}
},
_showMessage: function(userVerifier, message, styleClass) {
if (message) {
this._promptMessage.text = message;
this._promptMessage.styleClass = styleClass;
GdmUtil.fadeInActor(this._promptMessage);
} 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;
}
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);
},
_onVerificationComplete: function() {
this._userVerifier.clear();
this.emit('unlocked');
},
_onReset: function() {
_fail: function() {
this.emit('failed');
},
_onVerificationFailed: function() {
this._currentQuery = null;
this._firstQuestion = true;
_onReset: function(authPrompt, beginRequest) {
let userName;
if (beginRequest == AuthPrompt.BeginRequestType.PROVIDE_USERNAME) {
this._authPrompt.setUser(this._user);
userName = this._userName;
} else {
userName = null;
}
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._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,878 +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 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 BoxPointer = imports.ui.boxpointer;
const GnomeSession = imports.misc.gnomeSession;
const LoginManager = imports.misc.loginManager;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Params = imports.misc.params;
const Util = imports.misc.util;
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
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 LOCK_ENABLED_KEY = 'lock-enabled';
const ALWAYS_SHOW_LOG_OUT_KEY = 'always-show-log-out';
const SHOW_FULL_NAME_KEY = 'show-full-name';
const DIALOG_ICON_SIZE = 64;
const IMStatus = {
AVAILABLE: 0,
BUSY: 1,
HIDDEN: 2,
AWAY: 3,
IDLE: 4,
OFFLINE: 5,
LAST: 6
};
// Adapted from gdm/gui/user-switch-applet/applet.c
//
// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
// Copyright (C) 2008,2009 Red Hat, Inc.
const UserAvatarWidget = new Lang.Class({
Name: 'UserAvatarWidget',
_init: function(user, params) {
this._user = user;
params = Params.parse(params, { reactive: false,
iconSize: DIALOG_ICON_SIZE,
styleClass: 'status-chooser-user-icon' });
this._iconSize = params.iconSize;
this.actor = new St.Bin({ style_class: params.styleClass,
track_hover: params.reactive,
reactive: params.reactive });
},
setSensitive: function(sensitive) {
this.actor.can_focus = sensitive;
this.actor.reactive = sensitive;
},
update: function() {
let iconFile = this._user.get_icon_file();
if (!GLib.file_test(iconFile, GLib.FileTest.EXISTS))
iconFile = null;
if (iconFile) {
let file = Gio.File.new_for_path(iconFile);
this.actor.child = null;
this.actor.style = 'background-image: url("%s");'.format(iconFile);
} else {
this.actor.style = null;
this.actor.child = new St.Icon({ icon_name: 'avatar-default-symbolic',
icon_size: this._iconSize });
}
}
});
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 UserAvatarWidget(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._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));
this._screenSaverSettings.connect('changed::' + SHOW_FULL_NAME_KEY,
Lang.bind(this, this._updateUserName));
global.settings.connect('changed::' + SHOW_FULL_NAME_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));
this._sessionUpdated();
},
_sessionUpdated: function() {
this.actor.visible = !Main.sessionMode.isGreeter;
let allowSettings = Main.sessionMode.allowSettings;
this._statusChooser.setSensitive(allowSettings);
this._systemSettings.visible = allowSettings;
this.setSensitive(!Main.sessionMode.isLocked);
this._updatePresenceIcon();
},
_onDestroy: function() {
this._user.disconnect(this._userLoadedId);
this._user.disconnect(this._userChangedId);
},
_updateUserName: function() {
let settings = global.settings;
if (Main.sessionMode.isLocked)
settings = this._screenSaverSettings;
if (this._user.is_loaded && settings.get_boolean(SHOW_FULL_NAME_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;
},
_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;
},
_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);
item = new PopupMenu.PopupMenuItem(_("Settings"));
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
this.menu.addMenuItem(item);
this._systemSettings = item;
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_setting('gnome-user-accounts-panel.desktop');
app.activate();
},
_onPreferencesActivate: function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().lookup_app('gnome-control-center.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();
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();
},
_onSuspendOrPowerOffActivate: function() {
Main.overview.hide();
if (this._haveShutdown &&
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
this._session.ShutdownRemote();
} else {
if (this._screenSaverSettings.get_boolean(LOCK_ENABLED_KEY)) {
let tmpId = Main.screenShield.connect('lock-screen-shown', Lang.bind(this, function() {
Main.screenShield.disconnect(tmpId);
this._loginManager.suspend();
}));
this.menu.close(BoxPointer.PopupAnimation.NONE);
Main.screenShield.lock(true);
} else {
this._loginManager.suspend();
}
}
}
});

106
js/ui/userWidget.js Normal file
View File

@ -0,0 +1,106 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
//
// A widget showing the user avatar and name
const AccountsService = imports.gi.AccountsService;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const St = imports.gi.St;
const Params = imports.misc.params;
const AVATAR_ICON_SIZE = 64;
// Adapted from gdm/gui/user-switch-applet/applet.c
//
// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
// Copyright (C) 2008,2009 Red Hat, Inc.
const Avatar = new Lang.Class({
Name: 'Avatar',
_init: function(user, params) {
this._user = user;
params = Params.parse(params, { reactive: false,
iconSize: AVATAR_ICON_SIZE,
styleClass: 'framed-user-icon' });
this._iconSize = params.iconSize;
this.actor = new St.Bin({ style_class: params.styleClass,
track_hover: params.reactive,
reactive: params.reactive });
},
setSensitive: function(sensitive) {
this.actor.can_focus = sensitive;
this.actor.reactive = sensitive;
},
update: function() {
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);
this.actor.child = null;
this.actor.style = 'background-image: url("%s");'.format(iconFile);
} else {
this.actor.style = null;
this.actor.child = new St.Icon({ icon_name: 'avatar-default-symbolic',
icon_size: this._iconSize });
}
}
});
const UserWidget = new Lang.Class({
Name: 'UserWidget',
_init: function(user) {
this._user = user;
this.actor = new St.BoxLayout({ style_class: 'user-widget',
vertical: false });
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._avatar = new Avatar(user);
this.actor.add(this._avatar.actor,
{ x_fill: true, y_fill: true });
this._label = new St.Label({ style_class: 'user-widget-label' });
this.actor.add(this._label,
{ expand: true,
x_fill: true,
y_fill: false,
y_align: St.Align.MIDDLE });
this._userLoadedId = this._user.connect('notify::is-loaded',
Lang.bind(this, this._updateUser));
this._userChangedId = this._user.connect('changed',
Lang.bind(this, this._updateUser));
if (this._user.is_loaded)
this._updateUser();
},
_onDestroy: function() {
if (this._userLoadedId != 0) {
this._user.disconnect(this._userLoadedId);
this._userLoadedId = 0;
}
if (this._userChangedId != 0) {
this._user.disconnect(this._userChangedId);
this._userChangedId = 0;
}
},
_updateUser: function() {
if (this._user.is_loaded)
this._label.text = this._user.get_real_name();
else
this._label.text = '';
this._avatar.update();
}
});

View File

@ -12,6 +12,8 @@ const St = imports.gi.St;
const AppDisplay = imports.ui.appDisplay;
const Main = imports.ui.main;
const OverviewControls = imports.ui.overviewControls;
const Params = imports.misc.params;
const RemoteSearch = imports.ui.remoteSearch;
const Search = imports.ui.search;
const SearchDisplay = imports.ui.searchDisplay;
@ -22,6 +24,12 @@ const WorkspacesView = imports.ui.workspacesView;
const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings';
const ViewPage = {
WINDOWS: 1,
APPS: 2,
SEARCH: 3
};
const FocusTrap = new Lang.Class({
Name: 'FocusTrap',
Extends: St.Widget,
@ -34,26 +42,28 @@ const FocusTrap = new Lang.Class({
}
});
function getTermsForSearchString(searchString) {
searchString = searchString.replace(/^\s+/g, '').replace(/\s+$/g, '');
if (searchString == '')
return [];
let terms = searchString.split(/\s+/);
return terms;
}
const ViewSelector = new Lang.Class({
Name: 'ViewSelector',
_init : function(searchEntry, showAppsButton) {
this.actor = new St.BoxLayout({ name: 'viewSelector',
vertical: true });
this.actor = new Shell.Stack({ name: 'viewSelector' });
this._showAppsBlocked = false;
this._showAppsButton = showAppsButton;
this._showAppsButton.connect('notify::checked', Lang.bind(this, this._onShowAppsButtonToggled));
this._pageArea = new Shell.Stack();
this.actor.add(this._pageArea, { x_fill: true,
y_fill: true,
expand: true });
this._activePage = null;
this.active = false;
this._searchPending = false;
this._searchActive = false;
this._searchTimeoutId = 0;
this._searchSystem = new Search.SearchSystem();
@ -73,26 +83,26 @@ const ViewSelector = new Lang.Class({
this._entry.connect('notify::mapped', Lang.bind(this, this._onMapped));
global.stage.connect('notify::key-focus', Lang.bind(this, this._onStageKeyFocusChanged));
this._inactiveIcon = new St.Icon({ style_class: 'search-entry-icon',
icon_name: 'edit-find-symbolic' });
this._activeIcon = new St.Icon({ style_class: 'search-entry-icon',
icon_name: 'edit-clear-symbolic' });
this._entry.set_secondary_icon(this._inactiveIcon);
this._entry.set_primary_icon(new St.Icon({ style_class: 'search-entry-icon',
icon_name: 'edit-find-symbolic' }));
this._clearIcon = new St.Icon({ style_class: 'search-entry-icon',
icon_name: 'edit-clear-symbolic' });
this._iconClickedId = 0;
this._capturedEventId = 0;
this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay();
this._workspacesPage = this._addPage(this._workspacesDisplay.actor, null,
this._workspacesPage = this._addPage(this._workspacesDisplay.actor,
_("Windows"), 'emblem-documents-symbolic');
this._appDisplay = new AppDisplay.AllAppDisplay();
this._appsPage = this._addPage(this._appDisplay.actor, null,
this._appDisplay = new AppDisplay.AppDisplay();
this._appsPage = this._addPage(this._appDisplay.actor,
_("Applications"), 'view-grid-symbolic');
this._searchResults = new SearchDisplay.SearchResults(this._searchSystem);
this._searchPage = this._addPage(this._searchResults.actor, this._entry,
_("Search"), 'edit-find-symbolic');
this._searchPage = this._addPage(this._searchResults.actor,
_("Search"), 'edit-find-symbolic',
{ a11yFocus: this._entry });
this._searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
this._searchSettings.connect('changed::disabled', Lang.bind(this, this._reloadRemoteProviders));
@ -103,7 +113,6 @@ const ViewSelector = new Lang.Class({
// Wanda comes obviously first
this.addSearchProvider(new Wanda.WandaSearchProvider());
this.addSearchProvider(new AppDisplay.AppSearchProvider());
this.addSearchProvider(new AppDisplay.SettingsSearchProvider());
// Load remote search providers provided by applications
RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider));
@ -119,9 +128,6 @@ const ViewSelector = new Lang.Class({
global.focus_manager.add_group(this._searchResults.actor);
Main.overview.connect('item-drag-begin',
Lang.bind(this, this._resetShowAppsButton));
this._stageKeyPressId = 0;
Main.overview.connect('showing', Lang.bind(this,
function () {
@ -138,23 +144,20 @@ const ViewSelector = new Lang.Class({
}
}));
// Public constraints which may be used to tie actors' height or
// vertical position to the current tab's content; as the content's
// height and position depend on the view selector's style properties
// (e.g. font size, padding, spacing, ...) it would be extremely hard
// and ugly to get these from the outside. While it would be possible
// to use position and height properties directly, outside code would
// need to ensure that the content is properly allocated before
// accessing the properties.
this.constrainHeight = new Clutter.BindConstraint({ source: this._pageArea,
coordinate: Clutter.BindCoordinate.HEIGHT });
Main.wm.addKeybinding('toggle-application-view',
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
Meta.KeyBindingFlags.NONE,
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._toggleAppsPage));
Main.wm.addKeybinding('toggle-overview',
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
Meta.KeyBindingFlags.NONE,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(Main.overview, Main.overview.toggle));
},
_toggleAppsPage: function() {
@ -165,6 +168,7 @@ const ViewSelector = new Lang.Class({
show: function() {
this._activePage = this._workspacesPage;
this.reset();
this._appsPage.hide();
this._searchPage.hide();
this._workspacesDisplay.show();
@ -172,7 +176,7 @@ const ViewSelector = new Lang.Class({
if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows())
Main.overview.fadeOutDesktop();
this._showPage(this._workspacesPage);
this._showPage(this._workspacesPage, true);
},
zoomFromOverview: function() {
@ -182,18 +186,24 @@ const ViewSelector = new Lang.Class({
Main.overview.fadeInDesktop();
},
setWorkspacesFullGeometry: function(geom) {
this._workspacesDisplay.setWorkspacesFullGeometry(geom);
},
hide: function() {
this._workspacesDisplay.hide();
},
_addPage: function(actor, a11yFocus, name, a11yIcon) {
_addPage: function(actor, name, a11yIcon, params) {
params = Params.parse(params, { a11yFocus: null });
let page = new St.Bin({ child: actor,
x_align: St.Align.START,
y_align: St.Align.START,
x_fill: true,
y_fill: true });
if (a11yFocus)
Main.ctrlAltTabManager.addGroup(a11yFocus, name, a11yIcon);
if (params.a11yFocus)
Main.ctrlAltTabManager.addGroup(params.a11yFocus, name, a11yIcon);
else
Main.ctrlAltTabManager.addGroup(actor, name, a11yIcon,
{ proxy: this.actor,
@ -202,33 +212,44 @@ const ViewSelector = new Lang.Class({
this._a11yFocusPage(page);
})
});;
this._pageArea.add_actor(page);
this.actor.add_actor(page);
return page;
},
_showPage: function(page) {
if(page == this._activePage)
_fadePageIn: function(oldPage) {
if (oldPage)
oldPage.hide();
this.emit('page-empty');
this._activePage.show();
Tweener.addTween(this._activePage,
{ opacity: 255,
time: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME,
transition: 'easeOutQuad'
});
},
_showPage: function(page, noFade) {
if (page == this._activePage)
return;
if(this._activePage) {
Tweener.addTween(this._activePage,
let oldPage = this._activePage;
this._activePage = page;
this.emit('page-changed');
if (oldPage && !noFade)
Tweener.addTween(oldPage,
{ opacity: 0,
time: 0.1,
time: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
this._activePage.hide();
this._activePage = page;
this._fadePageIn(oldPage);
})
});
}
page.show();
Tweener.addTween(page,
{ opacity: 255,
time: 0.1,
transition: 'easeOutQuad'
});
else
this._fadePageIn(oldPage);
},
_a11yFocusPage: function(page) {
@ -237,15 +258,19 @@ const ViewSelector = new Lang.Class({
},
_onShowAppsButtonToggled: function() {
if (this.active)
this.reset();
else
this._showPage(this._showAppsButton.checked ? this._appsPage
: this._workspacesPage);
if (this._showAppsBlocked)
return;
this._showPage(this._showAppsButton.checked ?
this._appsPage : this._workspacesPage);
},
_resetShowAppsButton: function() {
this._showAppsBlocked = true;
this._showAppsButton.checked = false;
this._showAppsBlocked = false;
this._showPage(this._workspacesPage, true);
},
_onStageKeyPress: function(actor, event) {
@ -258,17 +283,16 @@ const ViewSelector = new Lang.Class({
let symbol = event.get_key_symbol();
if (symbol == Clutter.Escape) {
if (this.active)
if (this._searchActive)
this.reset();
else if (this._showAppsButton.checked)
this._resetShowAppsButton();
this._showAppsButton.checked = false;
else
Main.overview.hide();
return true;
} else if (Clutter.keysym_to_unicode(symbol) ||
(symbol == Clutter.BackSpace && this.active)) {
} else if (this._shouldTriggerSearch(symbol)) {
this.startSearch(event);
} else if (!this.active) {
} else if (!this._searchActive) {
if (symbol == Clutter.Tab || symbol == Clutter.Down) {
this._activePage.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
return true;
@ -331,6 +355,17 @@ const ViewSelector = new Lang.Class({
}
},
_shouldTriggerSearch: function(symbol) {
let unicode = Clutter.keysym_to_unicode(symbol);
if (unicode == 0)
return false;
if (getTermsForSearchString(String.fromCharCode(unicode)).length > 0)
return true;
return symbol == Clutter.BackSpace && this._searchActive;
},
startSearch: function(event) {
global.stage.set_key_focus(this._text);
this._text.event(event, true);
@ -342,39 +377,39 @@ const ViewSelector = new Lang.Class({
},
_onTextChanged: function (se, prop) {
let searchPreviouslyActive = this.active;
this.active = this._entry.get_text() != '';
this._searchPending = this.active && !searchPreviouslyActive;
if (this._searchPending) {
let terms = getTermsForSearchString(this._entry.get_text());
let searchPreviouslyActive = this._searchActive;
this._searchActive = (terms.length > 0);
let startSearch = this._searchActive && !searchPreviouslyActive;
if (startSearch)
this._searchResults.startingSearch();
}
if (this.active) {
this._entry.set_secondary_icon(this._activeIcon);
if (this._iconClickedId == 0) {
if (this._searchActive) {
this._entry.set_secondary_icon(this._clearIcon);
if (this._iconClickedId == 0)
this._iconClickedId = this._entry.connect('secondary-icon-clicked',
Lang.bind(this, function() {
this.reset();
}));
}
} else {
if (this._iconClickedId > 0)
this._entry.disconnect(this._iconClickedId);
this._iconClickedId = 0;
Lang.bind(this, this.reset));
if (this._searchTimeoutId == 0)
this._searchTimeoutId = Mainloop.timeout_add(150,
Lang.bind(this, this._doSearch));
} else {
if (this._iconClickedId > 0) {
this._entry.disconnect(this._iconClickedId);
this._iconClickedId = 0;
}
this._entry.set_secondary_icon(this._inactiveIcon);
this._searchCancelled();
}
if (!this.active) {
if (this._searchTimeoutId > 0) {
Mainloop.source_remove(this._searchTimeoutId);
this._searchTimeoutId = 0;
}
return;
this._entry.set_secondary_icon(null);
this._searchCancelled();
}
if (this._searchTimeoutId > 0)
return;
this._searchTimeoutId = Mainloop.timeout_add(150, Lang.bind(this, this._doSearch));
},
_onKeyPress: function(entry, event) {
@ -384,16 +419,7 @@ const ViewSelector = new Lang.Class({
this.reset();
return true;
}
} else if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) {
// We can't connect to 'activate' here because search providers
// might want to do something with the modifiers in activateDefault.
if (this._searchTimeoutId > 0) {
Mainloop.source_remove(this._searchTimeoutId);
this._doSearch();
}
this._searchResults.activateDefault();
return true;
} else if (this.active) {
} else if (this._searchActive) {
let arrowNext, nextDirection;
if (entry.get_text_direction() == Clutter.TextDirection.RTL) {
arrowNext = Clutter.Left;
@ -417,6 +443,15 @@ const ViewSelector = new Lang.Class({
} else if (symbol == arrowNext && this._text.position == -1) {
this._searchResults.navigateFocus(nextDirection);
return true;
} else if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) {
// We can't connect to 'activate' here because search providers
// might want to do something with the modifiers in activateDefault.
if (this._searchTimeoutId > 0) {
Mainloop.source_remove(this._searchTimeoutId);
this._doSearch();
}
this._searchResults.activateDefault();
return true;
}
}
return false;
@ -439,9 +474,10 @@ const ViewSelector = new Lang.Class({
_doSearch: function () {
this._searchTimeoutId = 0;
let text = this._text.get_text().replace(/^\s+/g, '').replace(/\s+$/g, '');
this._searchResults.doSearch(text);
let terms = getTermsForSearchString(this._entry.get_text());
this._searchSystem.updateSearchResults(terms);
this._showPage(this._searchPage);
},
@ -472,12 +508,37 @@ const ViewSelector = new Lang.Class({
return;
this._searchSystem.registerProvider(provider);
this._searchResults.createProviderMeta(provider);
this._searchResults.createProviderDisplay(provider);
},
removeSearchProvider: function(provider) {
this._searchSystem.unregisterProvider(provider);
this._searchResults.destroyProviderMeta(provider);
this._searchResults.destroyProviderDisplay(provider);
},
getActivePage: function() {
if (this._activePage == this._workspacesPage)
return ViewPage.WINDOWS;
else if (this._activePage == this._appsPage)
return ViewPage.APPS;
else
return ViewPage.SEARCH;
},
fadeIn: function() {
let actor = this._activePage;
Tweener.addTween(actor, { opacity: 255,
time: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME / 2,
transition: 'easeInQuad'
});
},
fadeHalf: function() {
let actor = this._activePage;
Tweener.addTween(actor, { opacity: 128,
time: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME / 2,
transition: 'easeOutQuad'
});
}
});
Signals.addSignalMethods(ViewSelector.prototype);

View File

@ -10,14 +10,15 @@ const IconGrid = imports.ui.iconGrid;
const Layout = imports.ui.layout;
const Main = imports.ui.main;
const Panel = imports.ui.panel;
const Search = imports.ui.search;
// we could make these gsettings
const FISH_NAME = 'wanda';
const FISH_FILENAME = 'wanda.png';
const FISH_SPEED = 300;
const FISH_COMMAND = 'fortune';
// The size of an individual frame in the animation
const FISH_HEIGHT = 22;
const FISH_WIDTH = 36;
const GNOME_PANEL_PIXMAPDIR = '../gnome-panel/fish';
const FISH_GROUP = 'Fish Animation';
const MAGIC_FISH_KEY = 'free the fish';
@ -27,33 +28,16 @@ const WandaIcon = new Lang.Class({
Extends: IconGrid.BaseIcon,
_init : function(fish, label, params) {
this._fish = fish;
let file = GLib.build_filenamev([global.datadir, GNOME_PANEL_PIXMAPDIR, fish + '.fish']);
if (GLib.file_test(file, GLib.FileTest.EXISTS)) {
this._keyfile = new GLib.KeyFile();
this._keyfile.load_from_file(file, GLib.KeyFileFlags.NONE);
this._imageFile = GLib.build_filenamev([global.datadir, GNOME_PANEL_PIXMAPDIR,
this._keyfile.get_string(FISH_GROUP, 'image')]);
let tmpPixbuf = GdkPixbuf.Pixbuf.new_from_file(this._imageFile);
this._imgHeight = tmpPixbuf.height;
this._imgWidth = tmpPixbuf.width / this._keyfile.get_integer(FISH_GROUP, 'frames');
} else {
this._imageFile = null;
}
this.parent(label, params);
this._fish = fish;
this._imageFile = GLib.build_filenamev([global.datadir, fish + '.png']);
this._imgHeight = FISH_HEIGHT;
this._imgWidth = FISH_WIDTH;
},
createIcon: function(iconSize) {
if (!this._imageFile) {
return new St.Icon({ icon_name: 'face-smile',
icon_size: iconSize });
}
this._animations = new Panel.Animation(this._imageFile, this._imgWidth, this._imgHeight, FISH_SPEED);
this._animations.play();
return this._animations.actor;
@ -71,8 +55,7 @@ const WandaIconBin = new Lang.Class({
Name: 'WandaIconBin',
_init: function(fish, label, params) {
this.actor = new St.Bin({ style_class: 'search-result-content',
reactive: true,
this.actor = new St.Bin({ reactive: true,
track_hover: true });
this.icon = new WandaIcon(fish, label, params);
@ -133,10 +116,9 @@ function capitalize(str) {
const WandaSearchProvider = new Lang.Class({
Name: 'WandaSearchProvider',
Extends: Search.SearchProvider,
_init: function() {
this.parent(_("Your favorite Easter Egg"));
this.id = 'wanda';
},
getResultMetas: function(fish, callback) {
@ -152,9 +134,9 @@ const WandaSearchProvider = new Lang.Class({
getInitialResultSet: function(terms) {
if (terms.join(' ') == MAGIC_FISH_KEY) {
this.searchSystem.pushResults(this, [ FISH_NAME ]);
this.searchSystem.setResults(this, [ FISH_NAME ]);
} else {
this.searchSystem.pushResults(this, []);
this.searchSystem.setResults(this, []);
}
},

View File

@ -4,6 +4,7 @@ const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
@ -66,6 +67,209 @@ function getWindowDimmer(actor) {
}
}
/*
* When the last window closed on a workspace is a dialog or splash
* screen, we assume that it might be an initial window shown before
* the main window of an application, and give the app a grace period
* where it can map another window before we remove the workspace.
*/
const LAST_WINDOW_GRACE_TIME = 1000;
const WorkspaceTracker = new Lang.Class({
Name: 'WorkspaceTracker',
_init: function(wm) {
this._wm = wm;
this._workspaces = [];
this._checkWorkspacesId = 0;
let tracker = Shell.WindowTracker.get_default();
tracker.connect('startup-sequence-changed', Lang.bind(this, this._queueCheckWorkspaces));
global.screen.connect('notify::n-workspaces', Lang.bind(this, this._nWorkspacesChanged));
global.screen.connect('window-entered-monitor', Lang.bind(this, this._windowEnteredMonitor));
global.screen.connect('window-left-monitor', Lang.bind(this, this._windowLeftMonitor));
global.screen.connect('restacked', Lang.bind(this, this._windowsRestacked));
this._workspaceSettings = new Gio.Settings({ schema: Main.dynamicWorkspacesSchema });
this._workspaceSettings.connect('changed::dynamic-workspaces', Lang.bind(this, this._queueCheckWorkspaces));
this._nWorkspacesChanged();
},
_checkWorkspaces: function() {
let i;
let emptyWorkspaces = [];
if (!Meta.prefs_get_dynamic_workspaces()) {
this._checkWorkspacesId = 0;
return false;
}
for (i = 0; i < this._workspaces.length; i++) {
let lastRemoved = this._workspaces[i]._lastRemovedWindow;
if ((lastRemoved &&
(lastRemoved.get_window_type() == Meta.WindowType.SPLASHSCREEN ||
lastRemoved.get_window_type() == Meta.WindowType.DIALOG ||
lastRemoved.get_window_type() == Meta.WindowType.MODAL_DIALOG)) ||
this._workspaces[i]._keepAliveId)
emptyWorkspaces[i] = false;
else
emptyWorkspaces[i] = true;
}
let sequences = Shell.WindowTracker.get_default().get_startup_sequences();
for (i = 0; i < sequences.length; i++) {
let index = sequences[i].get_workspace();
if (index >= 0 && index <= global.screen.n_workspaces)
emptyWorkspaces[index] = false;
}
let windows = global.get_window_actors();
for (i = 0; i < windows.length; i++) {
let win = windows[i];
if (win.get_meta_window().is_on_all_workspaces())
continue;
let workspaceIndex = win.get_workspace();
emptyWorkspaces[workspaceIndex] = false;
}
// If we don't have an empty workspace at the end, add one
if (!emptyWorkspaces[emptyWorkspaces.length -1]) {
global.screen.append_new_workspace(false, global.get_current_time());
emptyWorkspaces.push(false);
}
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
let removingCurrentWorkspace = (emptyWorkspaces[activeWorkspaceIndex] &&
activeWorkspaceIndex < emptyWorkspaces.length - 1);
// Don't enter the overview when removing multiple empty workspaces at startup
let showOverview = (removingCurrentWorkspace &&
!emptyWorkspaces.every(function(x) { return x; }));
if (removingCurrentWorkspace) {
// "Merge" the empty workspace we are removing with the one at the end
this._wm.blockAnimations();
}
// Delete other empty workspaces; do it from the end to avoid index changes
for (i = emptyWorkspaces.length - 2; i >= 0; i--) {
if (emptyWorkspaces[i])
global.screen.remove_workspace(this._workspaces[i], global.get_current_time());
}
if (removingCurrentWorkspace) {
global.screen.get_workspace_by_index(global.screen.n_workspaces - 1).activate(global.get_current_time());
this._wm.unblockAnimations();
if (!Main.overview.visible && showOverview)
Main.overview.show();
}
this._checkWorkspacesId = 0;
return false;
},
keepWorkspaceAlive: function(workspace, duration) {
if (workspace._keepAliveId)
Mainloop.source_remove(workspace._keepAliveId);
workspace._keepAliveId = Mainloop.timeout_add(duration, Lang.bind(this, function() {
workspace._keepAliveId = 0;
this._queueCheckWorkspaces();
return false;
}));
},
_windowRemoved: function(workspace, window) {
workspace._lastRemovedWindow = window;
this._queueCheckWorkspaces();
Mainloop.timeout_add(LAST_WINDOW_GRACE_TIME, Lang.bind(this, function() {
if (workspace._lastRemovedWindow == window) {
workspace._lastRemovedWindow = null;
this._queueCheckWorkspaces();
}
return false;
}));
},
_windowLeftMonitor: function(metaScreen, monitorIndex, metaWin) {
// If the window left the primary monitor, that
// might make that workspace empty
if (monitorIndex == Main.layoutManager.primaryIndex)
this._queueCheckWorkspaces();
},
_windowEnteredMonitor: function(metaScreen, monitorIndex, metaWin) {
// If the window entered the primary monitor, that
// might make that workspace non-empty
if (monitorIndex == Main.layoutManager.primaryIndex)
this._queueCheckWorkspaces();
},
_windowsRestacked: function() {
// Figure out where the pointer is in case we lost track of
// it during a grab. (In particular, if a trayicon popup menu
// is dismissed, see if we need to close the message tray.)
global.sync_pointer();
},
_queueCheckWorkspaces: function() {
if (this._checkWorkspacesId == 0)
this._checkWorkspacesId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, this._checkWorkspaces));
},
_nWorkspacesChanged: function() {
let oldNumWorkspaces = this._workspaces.length;
let newNumWorkspaces = global.screen.n_workspaces;
if (oldNumWorkspaces == newNumWorkspaces)
return false;
let lostWorkspaces = [];
if (newNumWorkspaces > oldNumWorkspaces) {
let w;
// Assume workspaces are only added at the end
for (w = oldNumWorkspaces; w < newNumWorkspaces; w++)
this._workspaces[w] = global.screen.get_workspace_by_index(w);
for (w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
let workspace = this._workspaces[w];
workspace._windowAddedId = workspace.connect('window-added', Lang.bind(this, this._queueCheckWorkspaces));
workspace._windowRemovedId = workspace.connect('window-removed', Lang.bind(this, this._windowRemoved));
}
} else {
// Assume workspaces are only removed sequentially
// (e.g. 2,3,4 - not 2,4,7)
let removedIndex;
let removedNum = oldNumWorkspaces - newNumWorkspaces;
for (let w = 0; w < oldNumWorkspaces; w++) {
let workspace = global.screen.get_workspace_by_index(w);
if (this._workspaces[w] != workspace) {
removedIndex = w;
break;
}
}
let lostWorkspaces = this._workspaces.splice(removedIndex, removedNum);
lostWorkspaces.forEach(function(workspace) {
workspace.disconnect(workspace._windowAddedId);
workspace.disconnect(workspace._windowRemovedId);
});
}
this._queueCheckWorkspaces();
return false;
}
});
const WindowManager = new Lang.Class({
Name: 'WindowManager',
@ -105,75 +309,160 @@ const WindowManager = new Lang.Class({
this._workspaceSwitcherPopup = null;
this.setCustomKeybindingHandler('switch-to-workspace-left',
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-right',
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-up',
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-down',
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-left',
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-right',
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-up',
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-down',
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-1',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-2',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-3',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-4',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-5',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-6',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-7',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-8',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-9',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-10',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-11',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-12',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-1',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-2',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-3',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-4',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-5',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-6',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-7',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-8',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-9',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-10',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-11',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-12',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-applications',
Main.KeybindingMode.NORMAL,
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._startAppSwitcher));
this.setCustomKeybindingHandler('switch-group',
Main.KeybindingMode.NORMAL,
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._startAppSwitcher));
this.setCustomKeybindingHandler('switch-applications-backward',
Main.KeybindingMode.NORMAL,
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._startAppSwitcher));
this.setCustomKeybindingHandler('switch-group-backward',
Main.KeybindingMode.NORMAL,
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._startAppSwitcher));
this.setCustomKeybindingHandler('switch-windows',
Main.KeybindingMode.NORMAL,
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._startWindowSwitcher));
this.setCustomKeybindingHandler('switch-windows-backward',
Main.KeybindingMode.NORMAL,
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._startWindowSwitcher));
this.setCustomKeybindingHandler('switch-panels',
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW |
Main.KeybindingMode.LOCK_SCREEN |
Main.KeybindingMode.UNLOCK_SCREEN |
Main.KeybindingMode.LOGIN_SCREEN,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW |
Shell.KeyBindingMode.LOCK_SCREEN |
Shell.KeyBindingMode.UNLOCK_SCREEN |
Shell.KeyBindingMode.LOGIN_SCREEN,
Lang.bind(this, this._startA11ySwitcher));
this.setCustomKeybindingHandler('switch-panels-backward',
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW |
Main.KeybindingMode.LOCK_SCREEN |
Main.KeybindingMode.UNLOCK_SCREEN |
Main.KeybindingMode.LOGIN_SCREEN,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW |
Shell.KeyBindingMode.LOCK_SCREEN |
Shell.KeyBindingMode.UNLOCK_SCREEN |
Shell.KeyBindingMode.LOGIN_SCREEN,
Lang.bind(this, this._startA11ySwitcher));
this.addKeybinding('open-application-menu',
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
Meta.KeyBindingFlags.NONE,
Main.KeybindingMode.NORMAL,
Lang.bind(this, this._openAppMenu));
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.TOPBAR_POPUP,
Lang.bind(this, this._toggleAppMenu));
Main.overview.connect('showing', Lang.bind(this, function() {
for (let i = 0; i < this._dimmedWindows.length; i++)
@ -183,6 +472,19 @@ const WindowManager = new Lang.Class({
for (let i = 0; i < this._dimmedWindows.length; i++)
this._dimWindow(this._dimmedWindows[i]);
}));
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);
},
setCustomKeybindingHandler: function(name, modes, handler) {
@ -191,13 +493,15 @@ const WindowManager = new Lang.Class({
},
addKeybinding: function(name, settings, flags, modes, handler) {
if (global.display.add_keybinding(name, settings, flags, handler))
let action = global.display.add_keybinding(name, settings, flags, handler);
if (action != Meta.KeyBindingAction.NONE)
this.allowKeybinding(name, modes);
return action;
},
removeKeybinding: function(name) {
if (global.display.remove_keybinding(name))
this.allowKeybinding(name, Main.KeybindingMode.NONE);
this.allowKeybinding(name, Shell.KeyBindingMode.NONE);
},
allowKeybinding: function(name, modes) {
@ -219,7 +523,9 @@ const WindowManager = new Lang.Class({
_shouldAnimateActor: function(actor) {
if (!this._shouldAnimate())
return false;
return actor.meta_window.get_window_type() == Meta.WindowType.NORMAL;
let windowType = actor.meta_window.get_window_type();
return windowType == Meta.WindowType.NORMAL ||
windowType == Meta.WindowType.MODAL_DIALOG;
},
_removeEffect : function(list, actor) {
@ -238,23 +544,12 @@ const WindowManager = new Lang.Class({
}
actor.set_scale(1.0, 1.0);
actor.move_anchor_point_from_gravity(Clutter.Gravity.CENTER);
/* scale window down to 0x0.
* maybe TODO: get icon geometry passed through and move the window towards it?
*/
this._minimizing.push(actor);
let primary = Main.layoutManager.primaryMonitor;
let xDest = primary.x;
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
xDest += primary.width;
Tweener.addTween(actor,
{ scale_x: 0.0,
scale_y: 0.0,
x: xDest,
y: 0,
if (actor.meta_window.is_monitor_sized()) {
Tweener.addTween(actor,
{ opacity: 0,
time: WINDOW_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: this._minimizeWindowDone,
@ -264,12 +559,46 @@ const WindowManager = new Lang.Class({
onOverwriteScope: this,
onOverwriteParams: [shellwm, actor]
});
} else {
let xDest, yDest, xScale, yScale;
let [success, geom] = actor.meta_window.get_icon_geometry();
if (success) {
xDest = geom.x;
yDest = geom.y;
xScale = geom.width / actor.width;
yScale = geom.height / actor.height;
} else {
let monitor = Main.layoutManager.monitors[actor.meta_window.get_monitor()];
xDest = monitor.x;
yDest = monitor.y;
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
xDest += monitor.width;
xScale = 0;
yScale = 0;
}
Tweener.addTween(actor,
{ scale_x: xScale,
scale_y: yScale,
x: xDest,
y: yDest,
time: WINDOW_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: this._minimizeWindowDone,
onCompleteScope: this,
onCompleteParams: [shellwm, actor],
onOverwrite: this._minimizeWindowOverwritten,
onOverwriteScope: this,
onOverwriteParams: [shellwm, actor]
});
}
},
_minimizeWindowDone : function(shellwm, actor) {
if (this._removeEffect(this._minimizing, actor)) {
Tweener.removeTweens(actor);
actor.set_scale(1.0, 1.0);
actor.set_opacity(255);
actor.move_anchor_point_from_gravity(Clutter.Gravity.NORTH_WEST);
shellwm.completed_minimize(actor);
@ -369,57 +698,56 @@ const WindowManager = new Lang.Class({
actor._windowType = type;
}));
if (actor.meta_window.is_attached_dialog()) {
this._checkDimming(actor.get_meta_window().get_transient_for());
if (this._shouldAnimate()) {
actor.set_scale(1.0, 0.0);
actor.scale_gravity = Clutter.Gravity.CENTER;
actor.show();
this._mapping.push(actor);
Tweener.addTween(actor,
{ scale_y: 1,
time: WINDOW_ANIMATION_TIME,
transition: "easeOutQuad",
onComplete: this._mapWindowDone,
onCompleteScope: this,
onCompleteParams: [shellwm, actor],
onOverwrite: this._mapWindowOverwrite,
onOverwriteScope: this,
onOverwriteParams: [shellwm, actor]
});
return;
}
shellwm.completed_map(actor);
return;
}
if (!this._shouldAnimateActor(actor)) {
shellwm.completed_map(actor);
return;
}
actor.opacity = 0;
actor.show();
if (actor.meta_window.is_attached_dialog()) {
/* Scale the window from the center of the parent */
this._checkDimming(actor.get_meta_window().get_transient_for());
actor.set_scale(1.0, 0.0);
actor.scale_gravity = Clutter.Gravity.CENTER;
actor.show();
this._mapping.push(actor);
/* Fade window in */
this._mapping.push(actor);
Tweener.addTween(actor,
{ opacity: 255,
time: WINDOW_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: this._mapWindowDone,
onCompleteScope: this,
onCompleteParams: [shellwm, actor],
onOverwrite: this._mapWindowOverwrite,
onOverwriteScope: this,
onOverwriteParams: [shellwm, actor]
});
Tweener.addTween(actor,
{ scale_y: 1,
time: WINDOW_ANIMATION_TIME,
transition: "easeOutQuad",
onComplete: this._mapWindowDone,
onCompleteScope: this,
onCompleteParams: [shellwm, actor],
onOverwrite: this._mapWindowOverwrite,
onOverwriteScope: this,
onOverwriteParams: [shellwm, actor]
});
} else {
/* Fade window in */
actor.opacity = 0;
actor.show();
this._mapping.push(actor);
Tweener.addTween(actor,
{ opacity: 255,
time: WINDOW_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: this._mapWindowDone,
onCompleteScope: this,
onCompleteParams: [shellwm, actor],
onOverwrite: this._mapWindowOverwrite,
onOverwriteScope: this,
onOverwriteParams: [shellwm, actor]
});
}
},
_mapWindowDone : function(shellwm, actor) {
if (this._removeEffect(this._mapping, actor)) {
Tweener.removeTweens(actor);
actor.opacity = 255;
actor.scale_y = 1;
shellwm.completed_map(actor);
}
},
@ -441,18 +769,21 @@ const WindowManager = new Lang.Class({
return win != window;
});
}
if (!this._shouldAnimateActor(actor)) {
shellwm.completed_destroy(actor);
return;
}
this._destroying.push(actor);
if (window.is_attached_dialog()) {
let parent = window.get_transient_for();
this._checkDimming(parent, window);
if (!this._shouldAnimate()) {
shellwm.completed_destroy(actor);
return;
}
actor.set_scale(1.0, 1.0);
actor.scale_gravity = Clutter.Gravity.CENTER;
actor.show();
this._destroying.push(actor);
actor._parentDestroyId = parent.connect('unmanaged', Lang.bind(this, function () {
Tweener.removeTweens(actor);
@ -487,14 +818,14 @@ const WindowManager = new Lang.Class({
},
_filterKeybinding: function(shellwm, binding) {
if (Main.keybindingMode == Main.KeybindingMode.NONE)
if (Main.keybindingMode == Shell.KeyBindingMode.NONE)
return true;
// There's little sense in implementing a keybinding in mutter and
// not having it work in NORMAL mode; handle this case generically
// so we don't have to explicitly allow all builtin keybindings in
// NORMAL mode.
if (Main.keybindingMode == Main.KeybindingMode.NORMAL &&
if (Main.keybindingMode == Shell.KeyBindingMode.NORMAL &&
binding.is_builtin())
return false;
@ -502,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;
}
@ -518,11 +849,11 @@ const WindowManager = new Lang.Class({
if (direction == Meta.MotionDirection.UP ||
direction == Meta.MotionDirection.UP_LEFT ||
direction == Meta.MotionDirection.UP_RIGHT)
yDest = global.screen_height;
yDest = global.screen_height - Main.panel.actor.height;
else if (direction == Meta.MotionDirection.DOWN ||
direction == Meta.MotionDirection.DOWN_LEFT ||
direction == Meta.MotionDirection.DOWN_RIGHT)
yDest = -global.screen_height;
yDest = -global.screen_height + Main.panel.actor.height;
if (direction == Meta.MotionDirection.LEFT ||
direction == Meta.MotionDirection.UP_LEFT ||
@ -535,9 +866,9 @@ const WindowManager = new Lang.Class({
let switchData = {};
this._switchData = switchData;
switchData.inGroup = new Clutter.Group();
switchData.outGroup = new Clutter.Group();
switchData.movingWindowBin = new Clutter.Group();
switchData.inGroup = new Clutter.Actor();
switchData.outGroup = new Clutter.Actor();
switchData.movingWindowBin = new Clutter.Actor();
switchData.windows = [];
let wgroup = global.window_group;
@ -564,7 +895,7 @@ const WindowManager = new Lang.Class({
switchData.windows.push({ window: window,
parent: window.get_parent() });
window.reparent(switchData.inGroup);
window.show_all();
window.show();
}
}
@ -650,27 +981,42 @@ const WindowManager = new Lang.Class({
Main.ctrlAltTabManager.popup(backwards, binding.get_name(), binding.get_mask());
},
_openAppMenu : function(display, screen, window, event, binding) {
Main.panel.openAppMenu();
_toggleAppMenu : function(display, screen, window, event, binding) {
Main.panel.toggleAppMenu();
},
_showWorkspaceSwitcher : function(display, screen, window, binding) {
if (!Main.sessionMode.hasWorkspaces)
return;
if (screen.n_workspaces == 1)
return;
let [action,,,direction] = binding.get_name().split('-');
let direction = Meta.MotionDirection[direction.toUpperCase()];
let [action,,,target] = binding.get_name().split('-');
let newWs;
let direction;
if (isNaN(target)) {
direction = Meta.MotionDirection[target.toUpperCase()];
newWs = screen.get_active_workspace().get_neighbor(direction);
} else if (target > 0) {
target--;
newWs = screen.get_workspace_by_index(target);
if (screen.get_active_workspace().index() > target)
direction = Meta.MotionDirection.UP;
else
direction = Meta.MotionDirection.DOWN;
}
if (direction != Meta.MotionDirection.UP &&
direction != Meta.MotionDirection.DOWN)
return;
if (action == 'switch')
newWs = this.actionMoveWorkspace(direction);
this.actionMoveWorkspace(newWs);
else
newWs = this.actionMoveWindow(window, direction);
this.actionMoveWindow(window, newWs);
if (!Main.overview.visible) {
if (this._workspaceSwitcherPopup == null) {
@ -683,31 +1029,31 @@ const WindowManager = new Lang.Class({
}
},
actionMoveWorkspace: function(direction) {
actionMoveWorkspace: function(workspace) {
if (!Main.sessionMode.hasWorkspaces)
return;
let activeWorkspace = global.screen.get_active_workspace();
let toActivate = activeWorkspace.get_neighbor(direction);
if (activeWorkspace != toActivate)
toActivate.activate(global.get_current_time());
return toActivate;
if (activeWorkspace != workspace)
workspace.activate(global.get_current_time());
},
actionMoveWindow: function(window, direction) {
let activeWorkspace = global.screen.get_active_workspace();
let toActivate = activeWorkspace.get_neighbor(direction);
actionMoveWindow: function(window, workspace) {
if (!Main.sessionMode.hasWorkspaces)
return;
if (activeWorkspace != toActivate) {
let activeWorkspace = global.screen.get_active_workspace();
if (activeWorkspace != workspace) {
// This won't have any effect for "always sticky" windows
// (like desktop windows or docks)
this._movingWindow = window;
window.change_workspace(toActivate);
window.change_workspace(workspace);
global.display.clear_mouse_mode();
toActivate.activate_with_focus (window, global.get_current_time());
workspace.activate_with_focus (window, global.get_current_time());
}
return toActivate;
},
});

File diff suppressed because it is too large Load Diff

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