Compare commits

...

488 Commits

Author SHA1 Message Date
1afb04d1e4 overview: new transition 2014-01-31 17:57:57 -05:00
51e63233ce viewSelector: Move to a sync() model for deciding which page to show
https://bugzilla.gnome.org/show_bug.cgi?id=722196
2014-01-31 17:57:57 -05:00
a7d7f94892 screenShield: Don't hide windows until we've fully locked the shield
Revert the commit that ties hasWindows to the session mode, as we
need to do the "transition" manually. Instead, show/hide the
sessionGroup ourselves.

For optimization purposes, we also need to hide the window groups
when in the overview. Ideally, these would be culled out by Clutter,
but they are not from experimentation.
2014-01-31 17:57:57 -05:00
427f516d45 layout: Replace SystemBackground with CSS on the systemGroup
This merges the implementation of the noise texture on the lockScreenDialog
and the startup animation -- they now just use the systemGroup.
2014-01-31 17:57:57 -05:00
cbb88ffdbb layout: Add sessionGroup / systemGroup to better-define layers
In order to build a better transition animation from the lock screen, we
need to split the world into layers, as per this reference:

https://raw.github.com/gnome-design-team/gnome-mockups/master/system-lock-login-boot/system-layers2.png

Everything that pertains to the user's session is in the "session group",
which includes the window group, overview, message tray (for now),
keyboard, OSDs, menus, etc.

For implementation sake, we did not match this mockup exactly. The new layers
look like this, from top to bottom:

 * Stage
   * Magnifier (clones the uiGroup)
   * uiGroup
     * overlayGroup
       * menuGroup
     * panelGroup
     * screenShieldGroup
     * sessionGroup
       * top_window_group
       * other boxes (trayBox, keyboardBox, etc.)
       * other groups (osdGroup, switcherPopupGroup, etc.)
       * overviewGroup
       * window_group
     * systemGroup

The "session startup" animation now only zooms in the sessionGroup.

The panel is now outside the session, as it needs to sit above the screen
shield. This also means that it's not zoomed in as part of startup. I think
this is OK.

This also means that the lightboxes that the screen shield uses to fade out
the screen have to go in a new group, above the panel. This is known as the
overlayGroup, which has no relation to the old mutter group of the same name.

We also change the screen shield to put the lockDialogGroup in the system
group, and put the lockScreenGroup in the screenShieldGroup, which means
that the layer stacking is correct. Note that we don't hide the session
group in the lock screen yet, which is something I want to do.

Since not a lot of items need to be in the uiGroup anymore, we've removed
the Main.uiGroup fallback; others should use sessionGroup instead, when
appropriate.
2014-01-31 17:57:57 -05:00
71670bad3b layout: Add different groups in the LayoutManager for discrete UI components
This helps take cruft out of the uiGroup, and ensures that components remain
stacked properly on top of each other. In the future, we'll use this group
to ensure that grabs are ordered properly, as well.
2014-01-31 17:57:57 -05:00
a2e4153fa0 background: Fix cancellable issues
If we have the following sequence:

    cache.getImageContent({ filename: "foo", cancellable: cancellable1 });
    cache.getImageContent({ filename: "foo", cancellable: cancellable2 });
    cancellable1.cancel();

Then the second load will complete with "null" as its content, even though
it was never cancelled, and we'll see a blank image. Meanwhile, since the
second load simply appends to the list of callers for the second load,
cancellable2 does absolutely nothing: cancelling it won't stop the load,
and it will still receive onFinished handling.

To prevent this from happening, give the actual load operation its own
Gio.Cancellable, which is "ref-counted" -- only cancel it when all the other
possible callers cancel.

Additionally, clean up the large nested loops by splitting out duplicated
code and other stuff.

https://bugzilla.gnome.org/show_bug.cgi?id=722149
2014-01-31 17:57:57 -05:00
2fba8e29e0 shell-app: Rename internal function as suggested in review
Ooops, this was meant to be squashed with commit d44f40d105.
2014-01-31 13:59:30 +01:00
de1bb4e203 window-tracker: Remove is_window_interesting()
It is now unused, so kill it.

https://bugzilla.gnome.org/show_bug.cgi?id=723308
2014-01-31 13:49:07 +01:00
b4680a5c25 Use meta_window_is_skip_taskbar() directly
Rather than going through ShellWindowTracker, we can just clean up
the code to use the underlying MetaWindow property directly.

https://bugzilla.gnome.org/show_bug.cgi?id=723308
2014-01-31 13:49:07 +01:00
d44f40d105 shell-app: Track changes to MetaWindow:skip-taskbar
So far we have assumed that whether or not a window is interesting
is static. In general this is the case, but as it is legal for the
underlying properties to change at any time, there are of course
offenders that actually do this (flash I'm looking at ya).
While we used the property to determine whether a window should be
tracked or not, the worst case was showing windows that should be
hidden or missing windows that should be shown.
However as we nowadays base an app's running state on the number of
interesting windows, we need to be more careful in order to avoid
ending up with running apps with no windows.

https://bugzilla.gnome.org/show_bug.cgi?id=723308
2014-01-31 13:49:07 +01:00
8d5771e302 window-tracker: Use MetaWindow:skip-taskbar
The code from shell_window_tracker_is_window_interesting() is equivalent
of MetaWindow's skip-taskbar property, so use it to avoid code duplication.

https://bugzilla.gnome.org/show_bug.cgi?id=723308
2014-01-31 13:49:06 +01:00
fb31f99aed Updated Aragonese translation 2014-01-31 12:45:29 +01:00
b97f3a9ecf overview: Fix DESKTOP clone position
The DESKTOP window might not be located at (0,0), in particular
on multi-monitor setups. While we already consider this by setting
the clone's position, we then stuff the clone into a container which
ignores it - meh ...

https://bugzilla.gnome.org/show_bug.cgi?id=723306
2014-01-31 00:00:07 +01:00
ac22172a6e window-tracker: Fix memory leak
shell_window_tracker_get_window_app() returns a new reference
which we need to drop once we're done.

https://bugzilla.gnome.org/show_bug.cgi?id=722927
2014-01-30 23:55:13 +01:00
57367380f5 Updated Hebrew translation 2014-01-29 22:39:47 +02:00
7101cc3170 osdWindow: Don't tween to undefined
Check that we got a valid level before setting the level bar to it, to
prevent a Tweener warning.
2014-01-29 15:08:03 -05:00
7051411be7 network: Add a Wired device
This isn't quite like the design, as we don't show icons for other
devices when wired is in an error state.

https://bugzilla.gnome.org/show_bug.cgi?id=708966
2014-01-29 15:02:52 -05:00
bb8397b9b1 appDisplay: Support the 'excluded-apps' key for app folders
https://bugzilla.gnome.org/show_bug.cgi?id=723179
2014-01-29 14:02:27 -05:00
bb8fa61cb4 appDisplay: Support the 'categories' key for app folders
https://bugzilla.gnome.org/show_bug.cgi?id=723179
2014-01-29 14:02:27 -05:00
10147ee331 appDisplay: Make refiltering folders more efficient
Rather than queueing a full redisplay, simply filter the apps inside
folders by toggling the icon's visibility.

https://bugzilla.gnome.org/show_bug.cgi?id=723179
2014-01-29 14:02:27 -05:00
3779ac2c8a appDisplay: Make AllView handle the display of the all apps view
Rather than having the central component handle it. Just a code tidying-up.

https://bugzilla.gnome.org/show_bug.cgi?id=723179
2014-01-29 14:02:27 -05:00
bdad4db9ec appDisplay: Only reload frequently used data when mapping the frequent view
If the user mostly uses the All Apps view and uses it as his default view,
we shouldn't reload frequent data after a timeout. Simply do it when the
view is mapped.

https://bugzilla.gnome.org/show_bug.cgi?id=723179
2014-01-29 14:02:26 -05:00
36c69124f7 shell-app: Don't crash when trying to dispose
If we dispose a ShellApp that has windows left, then we'll crash
when we remove the last window, as that frees and NULLs out
app->running_state.

https://bugzilla.gnome.org/show_bug.cgi?id=723197
2014-01-29 14:02:26 -05:00
7c8c811134 Dispose cairo contexts in osdWindow and screenShield
Need to manually dispose of cairo contexts used in gjs with $dispose(),
or the context object will leak. These classes used cairo for drawing but
were missing the dispose call.

https://bugzilla.gnome.org/show_bug.cgi?id=722812
2014-01-28 16:46:10 -08:00
ccfc9f3ab0 TelepathyClient: disconnect signals from destroyed ChatSources
Otherwise they keep firing if the source is destroyed by the user
and they trigger Clutter warnings from touching invalid actors.

https://bugzilla.gnome.org/show_bug.cgi?id=722660
2014-01-28 21:50:07 +01:00
d163b92e0b Add indicator for location service being used
If an application is accessing location through geoclue, show an
indicator in the panel for that so that user knows.

https://bugzilla.gnome.org/show_bug.cgi?id=709372
2014-01-28 18:39:12 +00:00
887a21afb9 appDisplay: Properly check for TerminalEmulator in the Categories key
get_categories() returns an unparsed string, not a list of categories.
We need to parse the list by splitting on ';' to deterine whether the
actual 'TerminalEmulator' category is in the list, rather than something
like 'X-GNOME-TerminalEmulator'. It's an edge case, but I need to split
the list properly for the new folders, so I might as well fix this.

https://bugzilla.gnome.org/show_bug.cgi?id=723179
2014-01-28 13:32:36 -05:00
634adc9f71 Fixed Russian translation 2014-01-28 22:18:22 +04:00
974f01cef7 appDisplay: Remove unused method 2014-01-28 11:17:34 -05:00
ef9ade3548 shell-global: Remove unused declaration 2014-01-28 11:17:34 -05:00
24fdb73b44 Updated Lithuanian translation 2014-01-25 22:17:09 +02:00
ba06a87ba8 window-tracker: Fix typo 2014-01-24 12:38:27 -05:00
e6be483755 network: Fix copy-paste typo 2014-01-23 16:11:11 -05:00
a36bfced47 network: Use NMDevice.get_available_connections(); for NMWirelessDialog
It's a lot simpler and doesn't require us routing the NMRemoteSettings
all the way through. It's still a bit complicated to do this for the
usual connections, so let's drop it for now.
2014-01-23 16:02:19 -05:00
4faf421d5a network: Let NetworkManager figure out the best AP for the connection
According to dcbw, this can be handled by NetworkManager now.
2014-01-23 14:38:40 -05:00
a739455414 gnome-shell-extension-prefs: Fix warnings 2014-01-23 14:38:31 -05:00
73f6e75d8d shell-app: Unref running state when window count drops to zero
With the lastest ShellApp changes, an app is considered stopped
when the last "interesting" window is closed. However the app
may still track non-interesting windows, so if we unref the
running state on the state transition, we hit an assertion later-on
when trying to remove the non-interesting window.
Fix this by keeping the running state around until the last window
is closed.

https://bugzilla.gnome.org/show_bug.cgi?id=722840
2014-01-23 12:45:23 -05:00
17e7f8057a Assamese translation updated 2014-01-23 19:06:34 +05:30
b62c157680 shell-app: Base running state on "interesting" windows
An app should be considered running if it has at least one "interesting"
window, however the code considers an app running if it has at least
one tracked window. This was fine while we were only tracking interesting
windows, but since commit d21aa0d85f this is no longer the case.
So keep track of the number of interesting windows as well and use that
to determine the running state.

https://bugzilla.gnome.org/show_bug.cgi?id=722690
2014-01-22 22:16:03 +01:00
816f5162f9 BackgroundManager: don't destroy the newly created actor
Because we were setting this.background before calling .destroy(),
the call that was meant to destroy the old background was actually
destroying the new one!

https://bugzilla.gnome.org/show_bug.cgi?id=722787
2014-01-22 20:35:02 +01:00
9d8f8277aa altTab: Always filter out items with no windows
When restricting the switcher popup to the current workspace, we
filter out running apps with an empty window list (namely: no open
windows on the current workspace). However we may end up with an
empty window list even when not restricting items to the current
workspace when all windows of a running app are associated with a
different application via the transient_for hint.
To fix this, just filter out items with an empty window list
unconditionally.

https://bugzilla.gnome.org/show_bug.cgi?id=722434
2014-01-22 13:52:49 -05:00
cca14053a4 window-tracker: Always enable transient_for redirection
It is possible to associate an application's window with a different
application using the transient_for hint. However we currently only
consider the hint in get_window_app() and not when making the original
association, which opens the door to some confusing inconsistencies;
for instance, get_window_app() will not necessarily return the same
value for all windows retrieved via shell_app_get_windows().
Fix this by looking at the transient_for hint when making the original
association, not just in get_window_app().

https://bugzilla.gnome.org/show_bug.cgi?id=722434
2014-01-22 13:52:49 -05:00
427790f005 switcherPopup: Fix spacing calculation for empty lists
Without special-casing, our current spacing calculation results in
negative size requests for empty lists, which will trigger a Clutter
assert later.
While the list is never supposed to be empty, bugs happen; crashing
users' systems is the least graceful way of handling this, so don't
do it.

https://bugzilla.gnome.org/show_bug.cgi?id=722434
2014-01-22 13:52:49 -05:00
17845bf71e Updated Norwegian bokmål translation 2014-01-21 20:03:49 +01:00
452f5ab3ba Updated Galician translations 2014-01-20 23:10:20 +01:00
335744e78a build: bump clutter requirement to 1.15.90
PopupMenus don't render properly with clutter older than this.

https://bugzilla.gnome.org/show_bug.cgi?id=722593
2014-01-21 08:03:29 +11:00
c9e24439b2 Updated Spanish translation 2014-01-20 16:39:10 +01:00
61c697b6db Updated Brazilian Portuguese translation 2014-01-19 21:12:15 -02:00
3227d4f3ed ShellApp+ShellGlobal: unify app launch context code
Extend shell_global_create_app_launch_context() with the required
parameters and use that for shell_app_launch() too.

https://bugzilla.gnome.org/show_bug.cgi?id=669603
2014-01-19 18:51:48 +01:00
7e27afb645 Introduce support for desktop actions in the dash
Using the new list_actions() API in Gio, add entries for static
actions specified in .desktop files in the right-click app menus,
in the dash, app well and search.

https://bugzilla.gnome.org/show_bug.cgi?id=669603
2014-01-19 18:47:29 +01:00
9ba4790b4d AppDisplay: clean up handling of right click popup menu
Instead of connecting a global activate handler on the menu,
have one on each menu item, individually doing the right thing.

https://bugzilla.gnome.org/show_bug.cgi?id=669603
2014-01-19 18:46:31 +01:00
3a26f7f4d5 WindowManager: WORKAROUND: disable dimming in the overview
Clones and effects don't mix, due to a bug in Clutter we don't
know how to fix, and sometimes the clone is clipped completely.
As a workaround, undim windows with dialogs in the overview.

Clutter bug: https://bugzilla.gnome.org/show_bug.cgi?id=659523

https://bugzilla.gnome.org/show_bug.cgi?id=650843
2014-01-19 16:38:19 +01:00
8b99617513 WorkspaceThumbnails: show attached modal dialogs togheter with their parents
The window clones in the central part of the overview are showing modal
dialogs now, and this creates an inconsistency if the thumbnail doesn't too.
Code is intentionally similar in the two places.

https://bugzilla.gnome.org/show_bug.cgi?id=650843
2014-01-19 16:37:17 +01:00
587655f063 Workspace: show attached modal dialog
Windows in the overview should be like they appear in the workspace,
including modal dialogs that are attached above them.
In addition, hiding the dialogs in the overview causes a flash as
dialog appears at the end of the transition.

Based on a patch by Maxim Ermilov <zaspire@rambler.ru>

https://bugzilla.gnome.org/show_bug.cgi?id=650843
2014-01-19 16:37:17 +01:00
7e9ecf4eb2 Add a radial background shade for modal dialogs
Use a new ShellGLSLQuad actor class to build a RadialEffect that can be
enabled on Lightboxes to achieve a radial effect similar to the overview
one. Then enable it for modal dialogs.

https://bugzilla.gnome.org/show_bug.cgi?id=669798
2014-01-19 16:02:46 +01:00
5413010c60 Notification: don't expand the content on a destroyed notification
If the notification is destroyed between an allocate and the redraw,
the meta_later is invoked on a destroyed object, and fails because
the clutter calls are invalid at that point.

https://bugzilla.gnome.org/show_bug.cgi?id=722547
2014-01-19 15:59:18 +01:00
7d13cf1587 ShellTrayIcon: forward key presses too
StWidget::popup-menu is emitted when Menu/<Shift>F10 is pressed,
not released (for consistency with Gtk+), so we need to forward
that. Note that for key press we don't emit the matching key
release, because the app will take a grab and get the event directly
from X when the key is physicall released.

https://bugzilla.gnome.org/show_bug.cgi?id=721267
2014-01-19 15:42:54 +01:00
24cd13935a build: Don't dist generated gresources files
In addition to 2dd7db4808
2014-01-19 10:15:15 +01:00
1ae7dbec67 build: Fix linker failure for gnome-shell-extension-prefs
In addition to 2dd7db4808
2014-01-19 10:08:42 +01:00
2b0a2ab3bc build tools: Update to 3.12 2014-01-18 19:33:49 +01:00
4ed0f3e5f0 AppDisplay: fix isTerminal() check for apps without .desktop files
If the app has no .desktop file get_app_info() returns null.
This breaks window switching using the dash for those apps.

https://bugzilla.gnome.org/show_bug.cgi?id=722494
2014-01-18 11:36:10 -05:00
9cacc703dd Tajik translation updated 2014-01-18 16:48:41 +05:00
9ae70c6519 Updated Czech translation 2014-01-18 12:23:10 +01:00
02b38fed49 Update Chinese simplified translation 2014-01-18 16:48:13 +08:00
b2a65f809f Use recommended quotes
See https://wiki.gnome.org/Design/OS/Typography
2014-01-17 16:34:44 -05:00
2931869522 [l10n] Updated Estonian translation 2014-01-17 17:44:49 +02:00
11d8640ba6 workspacesView: Fix activating empty workspaces
By default, gesture actions no longer wait for the dnd threshold to
be reached before triggering, which breaks our mixing of click- and
pan actions. Fix this by only panning after reaching the threshold
and letting the click action go through if the pan is cancelled.

https://bugzilla.gnome.org/show_bug.cgi?id=722417
2014-01-17 09:30:59 -05:00
65ff947b5e Updated Czech translation 2014-01-17 14:21:41 +01:00
2dd7db4808 Make gnome-shell-extension-prefs a binary executable
Since commit 1ebb162a00 moved JS sources into resources,
the extension-prefs tool was broken. To fix it, we would either
need to generate an external GResource in addition to the generated
C code and teach gjs-console about loading it before evaluating
the script, or turn gnome-shell-extension-prefs into a binary with
the JS resources compiled in.

https://bugzilla.gnome.org/show_bug.cgi?id=722334
2014-01-16 09:33:01 -05:00
1d7354696e Bump version to 3.11.4
Update NEWS.
2014-01-16 01:52:54 +01:00
cbceac4c8a boxpointer: Add condition checks for -arrow-rise:0px
This is done for properly drawing popup menu when arrow rise is 0 (in
case of background menu).

Normally, the menu with arrow rise set to 0 is drawn properly having
all four corners rounded. But when the source(click/arrowOrigin) is
near screen's edges, one of the corners (depending on source's position
and arrow alignment) is drawn right angled.

This happens because the rounded corners are skipped and right angled
arrow is drawn when arrow origin is close to the edges.(That's why when
arrow-rise is 0, it forms right angled corner).

So, a few condition checks are made to ensure that right angled corner
is not drawn.

https://bugzilla.gnome.org/show_bug.cgi?id=699608
2014-01-15 18:28:31 +01:00
297877fbe2 theme: Set -arrow-rise property of backgroundMenu to 0
Background menu shown on right clicking desktop background has an arrow
pointer which points to nothing. This patch sets its height (rise) to 0
so that no arrow is formed.

https://bugzilla.gnome.org/show_bug.cgi?id=699608
2014-01-15 18:28:31 +01:00
0d92451c49 shell-global: Remove unused gc-notifications support
This is going to be removed upstream.
2014-01-15 09:18:53 -05:00
c8a58dcb69 layout: Add a standard dummy cursor
Right now we have three "dummy cursor" widgets between the background
menu, the message tray menu, and the IBus candidate popup. Consolidate
these into one "dummy cursor" widget which is tracked in the layout
manager.
2014-01-14 18:56:45 -05:00
a4dea25d76 layout: Don't require fullscreen-tracked actors to be toplevel
I don't know why this was ever here; We only have one
fullscreen-tracked actor.
2014-01-14 18:56:45 -05:00
bfb0235fc6 Remove our custom hashmap implementation
gjs uses Spidermonkey 24, which implements Map from the ES6
specification, so we can use that instead.

https://bugzilla.gnome.org/show_bug.cgi?id=722210
2014-01-15 00:55:00 +01:00
6dcc3d637f windowManager: Fix the name of the keybinding 2014-01-14 17:58:51 -05:00
9bb4d17e31 Add a debug keybinding to pause/resume all tweens
So we can inspect a mid-tween scene easier.
2014-01-14 17:25:14 -05:00
9df09db5fe appDisplay: Use the new org.gnome.desktop.app-folders schema for grouping
Rather than GMenu / app-folder-categories. This removes our last use of
gnome-menus in the stock gnome-shell, which is exciting, but also means
that app folders in Software start working.

Ideally, we'd have a button to launch our Software app as well from the
overview.

https://bugzilla.gnome.org/show_bug.cgi?id=722117
2014-01-14 11:25:08 -05:00
d8e28ec274 viewSelector: Remove unused setActivePage 2014-01-14 09:10:12 -05:00
d3905734c1 background: Remove unused argument 2014-01-13 20:17:57 -05:00
8fe7f923ec appDisplay: Move from instanceof-testing to polymorphic duck typing
Instead of having _compareItems, _getItemId, etc. on the view to
pull out info about items, use the AppIcon / FolderIcon items we
create as a place to track this additional info. We now require
that these items have a '.id' property for deduplication, and a
'.name' property to sort by.

https://bugzilla.gnome.org/show_bug.cgi?id=722117
2014-01-13 17:28:32 -05:00
933f38390b background: Don't require passing in a background to _updateBackground()
To make debugging background issues easier.
2014-01-13 17:14:58 -05:00
a4e019442f AppDisplay: fix isTerminal() check for apps without categories
If there is no Categories line in the .desktop file, get_categories()
returns null, not an empty array.
2014-01-13 23:13:28 +01:00
d1c4e60636 switcherPopup: Always show the arrows if the popup is scrollable
Currently we only show/hide the left and right arrows when
we reach the initial/end position. This patch changes this
behaviour by showing the arrows whenever is a scroll is possible.

https://bugzilla.gnome.org/show_bug.cgi?id=711467
2014-01-13 16:02:35 -02:00
765d0228c0 loginDialog: move user list loading after actors are constructed
Right now we queue populating the user list in the middle of setting
up the dialog actors. Of course, the actual population happens some time
later after going back to the main loop.

It's more logical to structure the code so the the actors are
instantiated first in one block and then other things after that.

This commit moves the user list population enqueuing operation to the
bottom of the constuctor.

https://bugzilla.gnome.org/show_bug.cgi?id=721868
2014-01-13 12:42:39 -05:00
2d2020a20d loginDialog: defer loading user list until idle
In some cases we load the user list after going back
to main loop and in other cases we load the user list
right away (depending on if accounts service is ready).

In the case we load the user list right away we cause a
traceback because loading the user list forces a reset,
which then tries to reset actors which aren't instantiated
yet.

This commit ensures the user list is loaded after the constructor
finishes and the event loop runs irregardless of the accountsservice
state.

https://bugzilla.gnome.org/show_bug.cgi?id=721868
2014-01-13 12:42:33 -05:00
03ab282f67 appDisplay: Fix style 2014-01-13 11:39:30 -05:00
7f94cb1cad Updated Galician translations 2014-01-13 01:46:07 +01:00
a2a303bd72 Updated Greek translation 2014-01-12 19:53:24 +02:00
d34bf9a14d app-system: Remove unnecessary debugging statement
It's just spam.
2014-01-10 15:19:07 -05:00
68faba6bde appDisplay: Special case terminal launching
One of the most frequent complaints about our launching behaviour is
how we handle terminals. Among all MDI applications, the terminal is
the one that is most likely to have lots of semi-independent windows
opened at the same time, and spawning new windows is much more common.
More so, if it does not support tabs.

Therefore, we special case terminal launchers to always create a new
window. It is an application that most non-technical users will not
use, so chances of them being confused by any special behaviour is
expected to be low.

https://bugzilla.gnome.org/show_bug.cgi?id=695010
2014-01-10 12:08:14 +01:00
5c5b9cfd96 Also update gtkaction*
Forgot about this.
2014-01-09 14:59:31 -05:00
9d683f4767 gtkmenutracker: Update from GTK+ 2014-01-09 14:47:11 -05:00
f2912bad95 fileUtils: Remove listDirAsync()
It's unused since commit da4238ec68, just kill it.

https://bugzilla.gnome.org/show_bug.cgi?id=721629
2014-01-09 13:23:26 -05:00
c8adfe0131 Updated Hebrew translation 2014-01-08 17:32:29 +02:00
8b7e637e74 Updated Spanish translation 2014-01-08 12:43:32 +01:00
43cffd7c4a main: allow session mode to be specified in the environment
Specifying the session mode on the command-line doesn't play
well with session management (since the saved session desktop
file well either drop the specified session mode, or force it
always, even if the user picked a different mode at the login
screen)

This commit adds support for specifying the session mode via an
enviroment variable. For now, keep the old command line interface
for backward compatibility

https://bugzilla.gnome.org/show_bug.cgi?id=720894
2014-01-07 16:36:26 -05:00
f3dad3765e Changed obsolete FSF postal address.
https://bugzilla.gnome.org/show_bug.cgi?id=721507
2014-01-08 04:35:14 +07:00
70c25141fc Tajik translation updated 2014-01-07 23:32:30 +05:00
b1b81a2672 Improve some strings in gschema file 2014-01-07 18:55:25 +01:00
46197bf262 Tajik translation updated 2014-01-07 13:49:47 +05:00
58ec409e7f [l10n] Updated Italian translation. 2014-01-06 09:42:06 +01:00
c2d68599de Update Kazakh translation 2014-01-05 20:31:42 +06:00
65f00f3af2 ShellWindowTracker: remove gtk-doc marks from private functions
static internal functions should be documented with /*, not /**

https://bugzilla.gnome.org/show_bug.cgi?id=721439
2014-01-04 17:34:29 +01:00
6544326ffd ShellWindowTracker: fix reference counting of ShellApp
All get_app_from_*() helpers are transfer full, but
get_app_from_gapplication_id() was directly returning the result
of lookup_app(), which is transfer none.

https://bugzilla.gnome.org/show_bug.cgi?id=721439
2014-01-04 17:34:10 +01:00
a23c206ccb Updated Indonesian translation 2014-01-04 10:25:11 +07:00
1b152e6bd0 WorkspacesView: fix removal of workspaces that are not at the end
Workspaces can removed from any index, and in particular they
will be removed by the WorkspaceTracker if they stop containing
windows at some point. Make sure WorkspacesView is not confused
and destroyes the right Workspace objects.

https://bugzilla.gnome.org/show_bug.cgi?id=721417
2014-01-03 22:46:45 +01:00
d9624d9882 Updated Lithuanian translation 2014-01-03 23:13:04 +02:00
178b8471cc shell-global: Remove an explicit js-version set
gjs's default js-version is already 1.8, and we're planning on removing
this API in the future, in accordance with upstream.
2014-01-02 13:41:14 -05:00
719d2092a7 Updated German translation 2014-01-02 14:43:18 +01:00
2f3a4675da Updated Brazilian Portuguese translation 2014-01-02 04:16:35 -02:00
9513be664b Updated Slovenian translation 2014-01-01 20:06:54 +01:00
64d8b7853a Update Chinese simplified translation 2014-01-01 17:02:30 +08:00
4174e57c13 Updated Spanish translation 2013-12-30 11:55:56 +01:00
88b395599a Updated Czech translation 2013-12-30 00:10:17 +01:00
b6d682c92c Updated Czech translation 2013-12-29 23:55:23 +01:00
3b02894341 Updated Brazilian Portuguese translation 2013-12-26 23:45:28 -02:00
f3feb13dfe ShellAppSystem: own the memory for the startup wm class and app id
The hash table must keep a copy of the IDs, because the GAppInfos
are unreferenced (and thus freed) at the end of the function.
This was possibly not a problem if the GAppInfos were referencing
the memory-mapped cache, but it becomes one for regularly parsed
desktop files in ~/.local.

https://bugzilla.gnome.org/show_bug.cgi?id=721039
2013-12-26 23:44:54 +01:00
114d8d0aba [l10n] Updated Italian translation. 2013-12-24 10:27:16 +01:00
503a843bb3 Updated Norwegian bokmål translation 2013-12-23 10:25:28 +01:00
c4d91aff40 Updated Aragonese translation 2013-12-22 21:16:13 +01:00
2ffe5faf6e Updated Slovenian translation 2013-12-22 19:24:13 +01:00
5d2a03aa82 Updated German translation 2013-12-22 18:59:27 +01:00
f4c83d1221 Update Chinese simplified translation 2013-12-22 22:54:09 +08:00
100e91714b Updated slovak translation 2013-12-22 13:14:30 +01:00
dafb7b5259 Bump version to 3.11.3
Update NEWS.
2013-12-19 21:54:37 +01:00
92906e217c Updated Spanish translation 2013-12-19 14:36:46 +01:00
5c7b721879 Tajik translation updated 2013-12-19 14:13:33 +05:00
c27dcb0414 Updated Galician translations 2013-12-19 01:53:09 +01:00
7fcae1e974 Remove use of superfluous MetaWindowActor APIs 2013-12-16 12:48:53 -05:00
cc4659f5c6 calendar: Don't rebuild the entire calendar widget when choosing a date
It's inefficient and wasteful. Combined with the JS GC not being great,
it leaks around 100 actors waiting to be GC'd. Yikes.

https://bugzilla.gnome.org/show_bug.cgi?id=720298
2013-12-16 12:44:23 -05:00
9fce12d6b4 calendar: Don't ever force reload
https://bugzilla.gnome.org/show_bug.cgi?id=720298
2013-12-16 12:44:22 -05:00
deb2f30b37 js: Use EVENT_PROPAGATE/EVENT_STOP constants in event handlers
Just as SOURCE_CONTINUE/SOURCE_REMOVE in source functions, these
constants increase code clarity over plain true/false.

https://bugzilla.gnome.org/show_bug.cgi?id=719567
2013-12-16 18:27:19 +01:00
751a3f0e94 js: Use SOURCE_CONTINUE/SOURCE_REMOVE constants in source functions
With support for boolean constants in g-i, we can finally use the
more readable constants instead of true/false.

https://bugzilla.gnome.org/show_bug.cgi?id=719567
2013-12-16 18:27:19 +01:00
fee2a07e08 runDialog: Explicitly set horizontal alignment
When set to fill, the label will always end up left-aligned, which
is only correct in LTR locales. Set the alignment explicitly to
work in both RTL and LTR locales.

https://bugzilla.gnome.org/show_bug.cgi?id=712579
2013-12-16 18:27:19 +01:00
17726abb0a NetworkAgent: handle empty hints and VPN secrets correctly
get_secrets_keyring_cb() contained an optimization (copied over from
nm-applet) that avoided a D-Bus round-trip when NetworkManager sent
secrets hints that were not satisified by the user.  This code did
not properly handle empty hints though, and proceeded to always
request new secrets whenever empty hints were sent.  Remove this
code entirely since the complexity is not worth it (per Jasper).

Second, get_secrets_keyring_cb() was mishandling VPN secrets which
were marked as "always ask".  Because the VPN secrets are not GObject
properties because they cannot be pre-defined, they are passed in
a hash table that is a GObject property marked 'secret'.  Unfortunately,
that means that the shell agent cannot determine their secret flags.
But since the VPN plugin auth dialogs have much better information
about what's required than the shell agent does, always ask the VPN
auth dialogs to handle the secrets requests after grabbing any that
already exist from the keyring.  This is also what nm-applet does.

https://bugzilla.gnome.org/show_bug.cgi?id=719815
2013-12-13 16:23:24 -06:00
01f740ce69 background: Don't prematurely remove file monitors
We need to only remove file monitors when there's no other users
of the content...
2013-12-13 11:50:17 -05:00
b168ccb605 st-scroll-view: Fix style 2013-12-11 20:36:44 -05:00
04a31a52ae calendar: Fix style 2013-12-11 20:36:44 -05:00
6a236fb91e keyring: Align more a srtings to the right side in RTL
It is missed in the previous patch.

Signed-off-by: Yosef Or Boczko <yoseforb@gmail.com>
2013-12-11 23:23:23 +02:00
619fa1bff8 polkitAgent: Align more a string to the right side in RTL
Signed-off-by: Yosef Or Boczko <yoseforb@gmail.com>
2013-12-11 23:14:08 +02:00
53b37e8d0c endSessionDialog: Align some strings to the right in RTL
https://bugzilla.gnome.org/show_bug.cgi?id=712600
2013-12-11 22:42:01 +02:00
f8eb8adfbe keyring: Align some srtings to right in RTL
https://bugzilla.gnome.org/show_bug.cgi?id=712594
2013-12-11 22:40:18 +02:00
3c2aecb81f polkitAgent: Explicitly set horizontal alignment
When set to fill, the label will always end up left-aligned, which
is only correct in LTR locales. Set the alignment explicitly to
work in both RTL and LTR locales.

https://bugzilla.gnome.org/show_bug.cgi?id=712596
2013-12-11 22:38:58 +02:00
efca9e11d6 unlockDialog: Explicitly set horizontal alignment
When set to fill, the label will always end up left-aligned, which
is only correct in LTR locales. Set the alignment explicitly to
work in both RTL and LTR locales.

https://bugzilla.gnome.org/show_bug.cgi?id=712638
2013-12-11 22:36:49 +02:00
49189e0e43 authPrompt: Explicitly set horizontal alignment
When set to fill, the label will always end up left-aligned, which
is only correct in LTR locales. Set the alignment explicitly to
work in both RTL and LTR locales.

https://bugzilla.gnome.org/show_bug.cgi?id=712638
2013-12-11 22:36:15 +02:00
ea86c9bafb userWidget: Fix the padding in RTL
https://bugzilla.gnome.org/show_bug.cgi?id=712638
2013-12-11 22:35:44 +02:00
c361b6a85c Update Arabic translation 2013-12-11 06:53:36 +02:00
c7ff45045c remoteSearch: Let remote search providers not provide an icon
The documentation indicates that they are optional, so let us make the
code behave accordingly.

https://bugzilla.gnome.org/show_bug.cgi?id=719965
2013-12-10 17:01:42 +01:00
090af35ea1 Finnish translation update 2013-12-09 08:08:10 +02:00
8e668ca633 sessionMode: Add back external session modes
Since commit da4238ec68, external session modes are no longer
loaded during start-up; bring them back.

https://bugzilla.gnome.org/show_bug.cgi?id=720017
2013-12-07 10:10:06 +01:00
4d9a16f33b sessionMode: Fix listModes()
Commit da4238ec68 just broke that function completely by
calling array methods on objects and other funny stuff. Fix it.

https://bugzilla.gnome.org/show_bug.cgi?id=720017
2013-12-07 10:10:06 +01:00
c9f2a0f694 sessionMode: Rename _getModes()
Since commit da4238ec68, the function doesn't return anything,
so rename it to _loadModes() to reflect this.

https://bugzilla.gnome.org/show_bug.cgi?id=720017
2013-12-07 10:10:06 +01:00
46bac3069e sessionMode: Fix left-over variable in _loadMode()
Commit da4238ec68 dropped a variable, but didn't replace all
consumers.

https://bugzilla.gnome.org/show_bug.cgi?id=720017
2013-12-07 10:10:06 +01:00
d21aa0d85f shell-app: Track all application windows
Filtering out "non-interesting" windows beforehand as we currently do
means that we may get properties that should be based on all windows,
like the last time the application was used, wrong.
Just track all windows and filter out non-interesting windows manually
in the one place we actually care about the difference.

https://bugzilla.gnome.org/show_bug.cgi?id=719824
2013-12-07 10:10:06 +01:00
3e87d699eb [l10n]Updated Turkish translation 2013-12-07 08:20:27 +02:00
23aa216908 [l10n] Updated Estonian translation 2013-12-05 12:45:57 +02:00
e34e681157 messageTray: Fix style 2013-12-05 01:32:28 -05:00
acd543fe4b messageTray: Fix style
We're missing a semi here
2013-12-04 20:51:10 -05:00
c2df5939d6 Sort js-resources.gresource.xml 2013-12-04 20:44:22 -05:00
b52e74b615 messageTray: Remove transient sources
As far as I can tell, the only behavior change of a transient source
is that they auto-destroy after viewing their summary box pointer.
Since all transient sources are only associated with transient
notifications, it seems that we can never get to their summary box
pointer in the first place! Remove support for this.

https://bugzilla.gnome.org/show_bug.cgi?id=710115
2013-12-04 20:25:28 -05:00
ec2bb039ae viewSelector: Give the active page key focus when it is shown
Rather than implement special focus policies like only allowing keynav
when pressing down, simply give the active page key focus when entering
the overview.

This may break stuff, as it's somewhat of a tricky patch to get right.
Testing this one would be super appreciated.

https://bugzilla.gnome.org/show_bug.cgi?id=644306
2013-12-04 11:22:55 -05:00
aeb9f5775f workspace: Grab the key focus when hovering over a window
This is simply an experiment. I'm not sure I like the result, but it was
worth trying out regardless.

https://bugzilla.gnome.org/show_bug.cgi?id=644306
2013-12-04 11:22:55 -05:00
47a20756b9 workspace: Implement key navigation on the workspaces page
Simply use St's existing key navigation system by making all the window
clones StWidgets, and making the WorkspacesView a focus group.

Since the workspace view is effectively "fake", we need to add a focus
delegator so that when key focus is assigned to the fake workspaces page,
we can keynav inside it properly.

https://bugzilla.gnome.org/show_bug.cgi?id=644306
2013-12-04 11:22:55 -05:00
a7aba1d585 st-widget: Sort actors by distance from the focused actor
Sorting actors by the distance in the axis of movement first and against
the axis otherwise means that if we have a situation like:

  A      F
   B

where "F" is the focused actor, and it slightly overlaps with B vertically,
then we'll choose "B" to go left, rather than "A", which is most likely
what the user intended.

This is especially apparent in the overview where slight window size
differences mean we might not get an exact grid shape.

https://bugzilla.gnome.org/show_bug.cgi?id=644306
2013-12-04 11:22:55 -05:00
c89af0cea4 messageTray: Only attempt to grab the summary box pointer after showing it
For mysterious reasons I'm not sure of myself, navigate_focus will only focus
mapped actors. So, make sure the widget is showing before navigating to it.

https://bugzilla.gnome.org/show_bug.cgi?id=709853
2013-12-04 11:22:55 -05:00
9d3a109946 messageTray: Reword a boolean condition
Just so it's a bit easier to understand...
2013-12-04 11:22:55 -05:00
079f1e6fff messageTray: Fix style 2013-12-04 11:22:55 -05:00
7249b11899 background: Don't silently fizzle out when removing bad content from the cache
If the background is already removed, or we're trying to remove bad content,
this is probably a bug in content accounting, so let us crash so we can fix
the bugs.

https://bugzilla.gnome.org/show_bug.cgi?id=719803
2013-12-03 19:13:12 -05:00
4cfb000812 background: Add copied content from pending image loads to the cache
https://bugzilla.gnome.org/show_bug.cgi?id=719803
2013-12-03 19:13:12 -05:00
5262a41619 background: Clarify the intent of the code
Stomping on local variables and trying to keep loop state isn't
too fun. Just use a new variable here so we aren't too confused
with what we're doing.

https://bugzilla.gnome.org/show_bug.cgi?id=719803
2013-12-03 19:13:12 -05:00
887590730d background: Simplify animation code
https://bugzilla.gnome.org/show_bug.cgi?id=719803
2013-12-03 19:13:12 -05:00
adb49bdf0b background: Remove unused variable
https://bugzilla.gnome.org/show_bug.cgi?id=719803
2013-12-03 19:13:12 -05:00
7f3aadc157 background: Remove the system noise content when not in use
https://bugzilla.gnome.org/show_bug.cgi?id=719803
2013-12-03 19:13:12 -05:00
eb1c85f3f5 background: Don't wait for gdk-pixbuf to fail before loading animations
We don't have any better way of determining whether something is a slideshow
animation, so discriminate on the .xml filename instead of waiting for
gdk-pixbuf to determine whether it can load a file or not.

https://bugzilla.gnome.org/show_bug.cgi?id=719803
2013-12-03 19:13:12 -05:00
dbdc884c96 background: Remove more bogus checks
The content in these arrays can never be null...

https://bugzilla.gnome.org/show_bug.cgi?id=719803
2013-12-03 19:13:12 -05:00
5166354e34 networkAgent: add support for EAP-FAST password requests
EAP-FAST is a top-level EAP method like TTLS, PEAP, etc.

https://bugzilla.gnome.org/show_bug.cgi?id=719813
2013-12-03 18:03:52 -06:00
04ea95049a background: Fix the check for spanning backgrounds
this._monitorIndex does not exist, and neither does
MetaBackground.monitor_index...

https://bugzilla.gnome.org/show_bug.cgi?id=719803
2013-12-03 18:36:46 -05:00
ec62e49001 bluetooth: Remove unused import 2013-12-03 18:36:46 -05:00
6fb21850d8 Updated Brazilian Portuguese translation 2013-12-02 23:17:13 -02:00
98b50fd942 UserWidget: replace vfunc_destroy override with a signal connection
The destroy vfunc might be called during object finalization, and
we can't call any JS from a GC finalizer, so we use a signal
connection instead, as that is removed by GObject the first time
the object is disposed.

https://bugzilla.gnome.org/show_bug.cgi?id=719730
2013-12-02 23:59:50 +01:00
729c962b7c loginDialog: Implement cancel()
The screen shield expects a cancel() method on the unlockDialog
implementation, but LoginDialog does not provide it currently.

https://bugzilla.gnome.org/show_bug.cgi?id=719378
2013-11-27 14:30:24 +01:00
ebc15e60a8 bluetooth: Remove GnomeBluetoothApplet hacks
https://bugzilla.gnome.org/show_bug.cgi?id=719341
2013-11-26 18:53:18 +01:00
85f2d94253 bluetooth: Use BluetoothClient to detect connected devices
Instead of the GnomeBluetoothApplet helper.

https://bugzilla.gnome.org/show_bug.cgi?id=719341
2013-11-26 18:53:18 +01:00
981a536cb5 bluetooth: Use g-s-d to turn off Bluetooth
https://bugzilla.gnome.org/show_bug.cgi?id=719341
2013-11-26 18:53:18 +01:00
abf7c333b1 bluetooth: Remove pairing agent
We'll only have it in the Bluetooth settings panel.

https://bugzilla.gnome.org/show_bug.cgi?id=719341
2013-11-26 18:53:18 +01:00
b2f547e934 authPrompt: propagate gdm "reset" signal after user switching
After a user types in their password at the login screen, one
of two things can happen

1) a new session is started
2) an existing session is switched to

In the latter case, GDM sends a reset signal to the login screen,
so it knows to go back to the user list and wait to be summoned
again.

Unfortunately, all reset signals are ignored after verification
success.  The reason is because the reset handler was copied from
the unlock dialog as part of a deduplication effort in commit
7e7295f259 and the unlock dialog
handler at the time also emitted a "failed" signal on reset
(which wouldn't make sense to emit after success).

These days "failed" is handled in a different way.

This commit changes the code to let reset signals through after
successful verification.

https://bugzilla.gnome.org/show_bug.cgi?id=710456
2013-11-25 22:38:44 -05:00
0870a25e2f Typo fixed in Dutch translation 2013-11-22 22:44:13 +01:00
1091e577a5 Updated Dutch translation by Erwin Poeze 2013-11-22 22:41:18 +01:00
151ad16fe6 Updated Norwegian bokmål translation 2013-11-21 21:24:50 +01:00
e325258091 build: require gjs 1.39.0
This is required for the gresources loader
2013-11-22 06:58:33 +11:00
1139a02b40 viewSelector: Don't show pages until they need to be visible
AppDisplay queues a deferred work to load frequently used apps when the
apps page is loaded. Unfortunately, when the overview is first opened,
all the pages start out visible and then immediately get hidden, so the
deferred work runs immediately after the first overview opening, whether
the user was going to view their frequent apps or not.

Start all pages off as hidden, and rearrange the code so that pages are
only shown when they really need to be.

https://bugzilla.gnome.org/show_bug.cgi?id=712753
2013-11-21 12:50:03 -05:00
4b90953226 osdWindow: add setMonitor() to allow changing the monitor
This is also exposed in the ShowOSD DBus method, the "monitor"
parameter may contain an integer to indicate the monitor number.
If the value is not provided or <0 is used, the monitor is shown
on the primary monitor as usually.

This way, the OSD can be used to notify upon events that solely
apply to one monitor, like tablet mapping as discussed in
https://bugzilla.gnome.org/show_bug.cgi?id=710373.

https://bugzilla.gnome.org/show_bug.cgi?id=712664
2013-11-20 18:03:32 +01:00
d77fc01580 boxpointer: Don't hide when we're already hidden
You would think we would already do something like this, but apparently
lots of code was calling hide() without checking if the box pointer was
already visible, causing it to queue a full tween. The biggest win was
with ibusCandidatePopup.js, which called hide() on every DBus message.

This increases the performance for me to enter the overview by a tiny
bit. The remaining time is spent updating the frequent apps / all apps
display.

https://bugzilla.gnome.org/show_bug.cgi?id=712727
2013-11-19 23:23:25 -05:00
216d84faeb layout: Adjust the opening animation to be less intense
https://bugzilla.gnome.org/show_bug.cgi?id=712362
2013-11-19 22:09:34 -05:00
0c9d95f183 userWidget: Chain up in destroy() 2013-11-19 18:11:43 -05:00
913739d732 shell-app: Remove unused method 2013-11-19 18:11:43 -05:00
7ecb5af587 appDisplay: Remove unused signal
The signal was last used in the pre-3.0 days, so we can stop dragging
it along and just remove it.
2013-11-18 16:24:13 +01:00
87f0e79749 messageTray: Prevent reentrancy issues in _updateState
The methods we call in _updateState may not be reentrant, so make
sure that we never get into a situation where _updateState, through
some crazy chain of events, calls itself.

https://bugzilla.gnome.org/show_bug.cgi?id=711694
2013-11-17 12:06:38 -05:00
c85145d73c entry: Make sure we chain up in enter/leave handlers
To ensure that the focus tracking executes correctly.

https://bugzilla.gnome.org/show_bug.cgi?id=706749
2013-11-15 12:51:19 -05:00
eea689841b highlight session menu button on key focus
https://bugzilla.gnome.org/show_bug.cgi?id=710539
2013-11-15 11:29:21 -05:00
e50a59361d entry: Remove old documentation about the hover style class 2013-11-15 11:14:36 -05:00
9862185bda theme: Clean up
Remove some unused CSS
2013-11-15 11:14:35 -05:00
fe05d35bbb messageTray: Fix style 2013-11-15 10:39:30 -05:00
ba602c17d4 appDisplay: Use the desktop file index for app searching
Rather than scanning all apps for searching, use Ryan's new desktop
file index and the glib support APIs for app searching instead of our
own system.

https://bugzilla.gnome.org/show_bug.cgi?id=711631
2013-11-14 14:28:52 -05:00
831bd07b0d layout: Fix several issues with the background management code
If monitor-changed fires at startup, it will destroy all of the
backgrounds, but since this._isStartup is true, won't recreate any
of them. Additionally, since _bgManagers is indexed by monitor index,
if the primary index is not 0, it could become a sparse array (e.g.
[undefined, undefined, primaryBackground]), and our for loop will
crash trying to access properties of undefined.

Fix both of these issues by always creating background managers for
every monitor, hiding them on startup but only showing them after
the startup animation is complete.

One thing we need to watch out for is that while LayoutManager is
constructing, Main.uiGroup / Main.layoutManager will be undefined,
so addBackgroundMenu will fail. Fix this by passing down the uiGroup
to the background menu code.

https://bugzilla.gnome.org/show_bug.cgi?id=709313
2013-11-14 14:28:51 -05:00
175c5d9fc3 overview: Fix stacking of background and desktop icons
If desktop icons are enabled and not covered by maximized windows,
we will fade them in/out during overview transitions. However when
moving background handling into mutter/gnome-shell, we ended up with
the overview background on top of the DESKTOP window clone, hiding
the fade transition.
Fix the stack order to bring the effect back.

https://bugzilla.gnome.org/show_bug.cgi?id=707671
2013-11-14 15:49:28 +00:00
2639e30d9c Bump version to 3.11.2
Update NEWS.
2013-11-13 21:24:30 +01:00
78a0218a91 build: Add built js-resources to CLEANFILES 2013-11-13 21:24:30 +01:00
e12bf8daed shellDBus: Fix error message returned from Eval
Annoyingly, `message` on errors are non-enumerable, which mean that
JSON.stringify on the error will produce `{}`. Just cast it to a string
for now.
2013-11-13 15:15:03 -05:00
04e2072e2c Updated Tamil Translations 2013-11-11 14:45:06 +05:30
7bafe20a34 Update Chinese simplified translation 2013-11-10 14:19:51 +08:00
554d5aeb7c More invalid source fixes
https://bugzilla.gnome.org/show_bug.cgi?id=711732
2013-11-09 17:58:59 +01:00
3991d2729d dash: Make sure we clear the timeout IDs for the label tooltip
https://bugzilla.gnome.org/show_bug.cgi?id=711732
2013-11-09 11:44:44 -05:00
5bc8a0860a tweener: Remove the onAnimationStart/onAnimationComplete callbacks
Our gnome-shell tweener integration has had hooks to determine when
the tweens have started and completed... except that they had a bug
in them. When a tween completed, it queued an idle handler to run
the callback in. If no tweens were running when the idle was removing,
it reset the tween state that contained the idle handler ID. It also
returned false, meaning that the source would always get removed.

If the actor had a tween in-flight when the idle was fired, it wouldn't
clean up after itself. While this is also a simple bug fix, remove the
callback so we don't queue unnecessary, unused idles.

https://bugzilla.gnome.org/show_bug.cgi?id=711732
2013-11-09 11:44:44 -05:00
ad03fb0815 app-system: Add back StartupWMClass matching
While unfortunate that we still have to scan all apps with get_all(),
support for this feature will be short-lived, so hopefully we can drop
it in the future as new apps adapt to the desktop file / app ID
recommendations.

For now, simply scan all desktop IDs.

https://bugzilla.gnome.org/show_bug.cgi?id=711631
2013-11-07 16:35:03 -05:00
e10d2a68f3 app-system: Put back support for the installed-changed signal
Use the new GAppInfoMonitor that Ryan added to glib to know when the
set of apps has changed.

https://bugzilla.gnome.org/show_bug.cgi?id=711631
2013-11-07 16:35:03 -05:00
213ee8d381 ShellApp: Connect applications to systemd journal (if available)
Systemd-for-the-user-session would also do this, but that's a deeply
invasive change that I may not actually get to this cycle.  This
change is tiny and non-invasive, but provides an important benefit:
You can actually reliably tell *which* applications are logging which
messages (assuming they're launched by the shell).

This actually complements a recent change in DBus:
See https://bugs.freedesktop.org/show_bug.cgi?id=68559
which does a similar thing for bus activated apps.

https://bugzilla.gnome.org/show_bug.cgi?id=711626
2013-11-07 13:44:03 -05:00
52b1a1b835 popupMenu: Fix removing the active menu from PopupMenuManager
Commit b42af9aa99 changed the parameter list of _closeMenu()
to account for changes in the GrabHelper ungrab mechanism, but
didn't update other callers.

https://bugzilla.gnome.org/show_bug.cgi?id=709806
2013-11-07 00:09:50 +01:00
fce2930b85 dnd: Don't queue an idle handler if we already have one
Removing an existing source before scheduling a new one is not wrong,
but slightly less effective than doing nothing and relying on the
previously created source to do the job.

https://bugzilla.gnome.org/show_bug.cgi?id=711555
2013-11-06 18:36:42 +01:00
735f589b1c dnd: Don't try to remove an invalid idle source
As the handler returns false, the corresponding source is removed
automatically and its id invalidated. Reset the id to 0 to reflect
this, otherwise newer versions of GLib will print a warning when
we later try to remove it explicitly.

https://bugzilla.gnome.org/show_bug.cgi?id=711555
2013-11-06 16:40:50 +01:00
69f17da5ca trivial: Fix the signedness of boolean fields
The standard old kludge with gboolean being signed, not unsigned.
Encountered while printing the values for debugging.

https://bugzilla.gnome.org/show_bug.cgi?id=644306
2013-11-05 09:11:49 -05:00
5c0ee02251 trivial: st-widget: Remove super old 'stylable' property
I don't think we ever used this, even way back in 3.0...

https://bugzilla.gnome.org/show_bug.cgi?id=644306
2013-11-04 21:11:00 -05:00
0cb4c7e437 Updated German translation 2013-11-04 21:05:26 +01:00
842c792868 search: Only do a subsearch if the previous results have returned from DBus
There's a potential race condition in the search code: if we have an
outstanding search call to a provider for search "A", and if before it comes
back we do a subsearch for "AB", we won't have any results to pass along.

Previously, we used an empty list when storing the provider results, so we
effectively told the remote search app to filter through this empty list for
any search results that meet the new query, meaning we showed the user 0
results for the provider in this case.

Now that we don't store an empty list, but instead store `undefined`, this race
raises a warning. Solve it by doing an initial search query in this case
instead.

The search code isn't too smart about chained subsearches: now, if we hit this
race while already on a subsearch, we'll do an initial search for the subsearch
query instead, but that is much better than showing the user nothing. This
could be fixed in the future for a performance improvement.

Reviewed-by: Florian Müllner <fmuellner@gnome.org>
2013-11-04 14:50:45 -05:00
4ba8518462 messageTray: Use a regular tween when expanding the notification
When a notification becomes expanded, it's either already shown,
or in the process of being shown. Don't set the state to SHOWING
again, which confuses our state machine.
2013-11-04 14:25:29 -05:00
143dfb6246 messageTray: Simplify code
If notificationRemoved, then mustClose is true, so we don't need to
double-check for this.
2013-11-04 14:16:28 -05:00
da4238ec68 Synchronize shell startup
The asynchronous nature of extension loading, session loading, and more,
makes the code racy as to what is initialized first, and hard to debug.
Additionally, since gjs is single-threaded, the only code we're running
in a thread anyway is readdir, which is going to be I/O bound, so the
code here is actually likely to be faster.

Drop this in favor of some good old fashioned synchronous loading.
2013-11-04 11:50:20 -05:00
5f9e3edbe1 notificationDaemon: Only store policies for "real" apps
Fake, window-backed apps should not have a policy for them.
2013-11-04 11:47:43 -05:00
1c68aee577 screenShield: Fix details in notifications
bannerBodyMarkup is a boolean flag to indicate that bannerBodyText
contains markup, not the markup text itself.

https://bugzilla.gnome.org/show_bug.cgi?id=711416
2013-11-04 16:46:25 +01:00
e8d9a4bd49 screencast: Validate parameters of ScreencastArea
... just as we do for screenshots.

https://bugzilla.gnome.org/show_bug.cgi?id=699752
2013-11-04 16:21:45 +01:00
9520e87a38 screencast: Fix disabling screencasts via session mode
If screencasts are disabled, we return a DBus error, but still start
the recording happily - add early returns in that case.

https://bugzilla.gnome.org/show_bug.cgi?id=699752
2013-11-04 16:21:45 +01:00
f9f5004909 screenshot: Extend ScreenshotArea parameter validation
We currently only ensure that width and height are positive, so it
is still possible to pass in values that don't make any sense at all
(which may even result in a crash when exceeding limits imposed by
X11).
There is nothing to screenshot outside the actual screen area, so
restrict the parameters to that.

https://bugzilla.gnome.org/show_bug.cgi?id=699752
2013-11-04 16:21:45 +01:00
4f7014b2d5 networkAgent: Make sure to update the OK button on dialog pop up
For consistency.
2013-11-04 08:55:59 -05:00
01dbfddb64 workspace: Remove unused includes and constants 2013-11-04 10:32:17 +01:00
6266a22d86 Updated Greek translation 2013-11-04 10:53:08 +02:00
09fe31179a app-system: Fix memory leak
The shell app takes a ref on the info.
2013-11-03 16:20:00 -05:00
78343f4837 app-system: Fix bad code
I had this locally, but forgot to amend before pushing apparently
2013-11-03 16:19:53 -05:00
a5619bc0a3 app-system: Fix const warning 2013-11-03 12:50:21 -05:00
dcb28aad2a app-system: Fix the keys in the id_to_app table
The ID that's passed to us isn't usable as a key to store, as it's
probably junk after the method returns. Use the app's storage for
the ID instead.
2013-11-03 12:10:00 -05:00
754cf172f5 Updated Norwegian bokmål translation 2013-11-03 16:03:44 +01:00
7890af1659 app-system: Lazily create ShellApps for apps we care about
Rather than create all ShellApps up-front, create them lazily. We really
had no reason to do this before as we were scanning GMenu to get all the
apps, but doing this can remove a need for get_all, which is slow and
memory-hungry.
2013-11-02 20:50:35 -04:00
d27d9fe694 app-system: Remove use of gnome-menus internally
We want to transition to a system in the future where we have a desktop
file cache. As we no longer differentiate categories or similar, it no
longer makes sense to have app visibility based on categories. Thus,
we no longer need to use gnome-menus to list all apps. The potential
issue here is reloading all desktop files when new files are created,
but this can be dealt with individually.

The "All Applications" view still uses gnome-menus.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-11-02 20:12:37 -04:00
634a599db6 appDisplay: Ignore the NoDisplay flag for directories
This makes us match the native app search.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-11-02 20:12:37 -04:00
5d0d859a1f app-system: Remove visible_id_to_app
Since appDisplay.js makes its own GMenu tree, it's not necessary
anymore. This does mean that searches will show apps in NoDisplay
categories, but that's an obscure enough edge case not to matter.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-11-02 20:12:37 -04:00
40c966fcd6 app-system: Remove lookup_app_for_path
It's absurdly silly. Just modify the one place that uses it
to be better.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-11-02 20:12:37 -04:00
d9245598a4 app-system: Remove known_vendor_prefixes
This does remove support for legacy prefixed app infos with
subdirs, but since we want to remove support for the menu spec,
let's not even bother.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-11-02 20:12:37 -04:00
03b0f4b16b app-system: Remove get_tree
Make clients construct their own gmenu tree if they need it.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-11-02 20:12:37 -04:00
2daa0d057b app-system: Map wmclass to ID rather than apps
This makes the refcounting and memory management easier to understand.
2013-11-02 20:12:36 -04:00
76eca409a3 app-system: Don't use gmenu_tree_entry_get_desktop_app_info
It's a broken method when it comes to giving us a useful GDesktopAppInfo,
and it's hard to fix libgmenu properly, so simply recreate the app info
using the desktop file ID that libgmenu has.
2013-11-02 20:12:36 -04:00
d84b018ba7 app: Port to be based on GDesktopAppInfo
We weren't using the GMenuTreeEntry for anything special anymore,
so remove it.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-11-02 20:12:36 -04:00
027c3d1661 app-system: Remove lookup_app_by_tree_entry
We want to move away from gnome-menus eventually, so the simple
utility method isn't really worth keeping around. Reimplement it
in the one place that uses it.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-11-02 20:12:36 -04:00
4965b1ca7b search: Fix previous commit
It was pushed by accident before it was tested...
2013-11-02 20:11:13 -04:00
9cd7ea9371 search: Make the internal search interface callback-based
Long ago, the search system worked in a synchronous manner: providers
were given a query, and results were collected in a single array of
[provider, results] pairs, and then the search display was updated
from that.

We introduced an asynchronous search system when we wanted to potentially
add a Zeitgeist search provider to the Shell in 3.2. For a while, search
providers were either async or sync, which worked by storing a dummy array
in the results, and adding a method for search providers to add results
later.

Later, we removed the search system entirely and ported the remaining
search providers to simply use the API to modify the empty array, but the
remains of the synchronous search system with its silly array still
lingered.

Finally, it's time to modernize. Promises^WCallbacks are the future.

Port the one remaining in-shell search engine (app search) to the new
callback based system, and simplify the remote search system in the
process.
2013-11-02 20:07:06 -04:00
dc2468b27b remoteSearch: Do filtering here of providers here
We already do ordering at load time, so why not filtering?
2013-11-02 20:07:06 -04:00
ea2451d882 overviewControls: Fix bad expression causing incorrect thumbnails allocation
`a + b ? c : d` is parsed as `(a + b) ? c : d`, not the more intuitive
`a + (b ? c : d)`.

This was causing a bad slide animation and Clutter warnings when coming
out of the overview.
2013-11-02 20:07:06 -04:00
252617bd70 search: Remove unnecessary import and full reference
I'm surprised this worked, actually...
2013-11-02 18:01:26 -04:00
8bd7003ea7 bluetooth: Parse pins starting with 0 correctly
If we have a numeric PIN that starts with 0, it will be treated
as an octal number rather than a number that just starts with 0.
2013-11-02 17:55:54 -04:00
280203158c Updated Russian translation 2013-11-01 23:32:39 +04:00
d456c3f62e system: Restore support for 'disable-restart-buttons'
The org.gnome.login-screen schema contains a key to disable the
power/restart buttons; our support for this fell victim to the
new combined status menu, add it back.

https://bugzilla.gnome.org/show_bug.cgi?id=711244
2013-11-01 13:08:02 +01:00
f64d17963b Support disabling browser plugin
Some downstreams may not want it for security reasons.

https://bugzilla.gnome.org/show_bug.cgi?id=711218
2013-10-31 12:44:51 -04:00
f12378cf7b search: Hide overview when activating result
This was (accidentally?) dropped by commit 3749b09366.

https://bugzilla.gnome.org/show_bug.cgi?id=711205
2013-10-31 16:19:25 +00:00
15ff426be8 shell_global_reexec_self: add support for OpenBSD
https://bugzilla.gnome.org/show_bug.cgi?id=709571
2013-10-31 11:46:59 +01:00
c4a6837d56 build: Make sure built-sources are introspected as needed 2013-10-31 00:01:38 +01:00
e6c28cf509 Revert "overviewControls: Always allocate the actor its full size"
This reverts commit f5a9dbb348.

This broke the alignment of the workspace thumbnail widget. I don't
know why, but let's not break things if we can...
2013-10-30 18:33:13 -04:00
a347a75617 st-theme: Use constructed instead of constructor 2013-10-30 18:33:13 -04:00
7fc2183826 build: Do not ship generated source-files in tarball 2013-10-30 23:29:45 +01:00
b6c3c9891c workspacesView: Make sure to update workspace actors when entering the overview
Otherwise, they will be in the wrong positions.
2013-10-30 18:20:54 -04:00
d401b493a4 search: Fix Return for searching in the overview 2013-10-30 17:43:12 -04:00
da1a8308b6 build: Also look for generated js-resources.* files in builddir 2013-10-30 21:41:52 +01:00
106d827a21 build: Also look for resources in $(builddir)
Since misc/config.js is built, we need to tell the resource compiler
to find it too.

Fixes the build in Continuous where srcdir != builddir.
2013-10-30 14:00:58 -04:00
1ebb162a00 Load JS from GResource
Since gjs can now load JS from a GResource, compile it in the
gnome-shell binary. This should be a bit faster, and make life easier
with JHBuild.
2013-10-30 13:27:16 -04:00
9d2791d9f8 dnd: Don't use reparent for adding actors to the uiGroup
It's deprecated, terrible, and causes get_theme_node crashes.
2013-10-30 13:20:02 -04:00
04a00f6564 loginDialog: Use UserWidget
https://bugzilla.gnome.org/show_bug.cgi?id=706851
2013-10-30 13:19:02 -04:00
a5dd44c77f userWidget: Use the user name if the real name doesn't fit
This meets the new designs.

https://bugzilla.gnome.org/show_bug.cgi?id=706851
2013-10-30 13:19:02 -04:00
8f86fd6bae workspacesView: Simplify the workspacesOnlyOnPrimary implementation
Before, workspacesOnlyOnPrimary was implemented in quite a crazy manner:

 * If workspacesOnlyOnPrimary was false, we'd create one WorkspacesView per
   monitor, with the primary one being a bit special.

 * If workspacesOnlyOnPrimary was true, we'd create one WorkspacesView, and
   additional montiors would be handled inside that WorkspacesView as
   "extra workspaces".

This caused numerous bugs as the two modes weren't consistently
implemented, and a lot of code was duplicated between all the modes.
Fix this by always creating WorkspaceViews, even if it only handles
one interface. We do this by having two different WorkspacesView-ish
classes: WorkspacesView handles the traditional combination of lots
of workspces, and a new ExtraWorkspaceView is in control of only one
workspace.
2013-10-30 13:17:39 -04:00
d5cd534320 workspacesView: Make each WorkspacesView own its set of workspaces
Right now, the workspace update code is complex and spread across parts:
WorkspacesView takes a set of workspaces and looks like it owns them, but
WorkspacesDisplay is actually in charge of setting them up and creating
new ones for each WorkspacesView.

Change initialization and handling to move all of the creation/destruction
responsibilities to WorkspacesView.

We pass in monitorIndex into each WorkspacesView, which is a lie in the
workspacesOnlyOnPrimary case, as the primary WorkspacesView currently has
the responsibility of handling the extra workspaces on all the other
monitors. The commit will clean this up and punt the responsibility back
to WorkspacesDisplay.
2013-10-30 13:17:39 -04:00
a5a6fd3bc2 workspacesView: Don't tween the upper of our scroll adjustment
This really doesn't make sense. The new upper should be the new workspace
index as soon as it's available.
2013-10-30 13:17:39 -04:00
287ddda5df workspacesView: Don't zoom into the overview based on a signal
Instead, simply have somebody else call us, like we do for hiding
the overview as well.
2013-10-30 13:17:39 -04:00
7747f1c31d workspacesView: Don't use a drag monitor to get the clone
Just use the new variable passed to the signal.
2013-10-30 13:17:39 -04:00
8097cbbbe3 workspacesView: Don't do any special handling for item-dragging
The code here really only cares about new windows.
2013-10-30 13:17:39 -04:00
e4c07875a3 overview: Send the clone with the window-drag events
This allows clients that care about the actual item we're dragging to
make smarter decisions without adding a drag monitor themselves.
2013-10-30 13:17:38 -04:00
026fd4cf35 workspace: Punt the geometry-fizzling-out logic here 2013-10-30 13:17:38 -04:00
87016f9620 workspacesView: Remove spacing
It's not used in the theme and it complicates the layout code a bit
too much; in fact, if set, things start breaking.
2013-10-30 13:17:38 -04:00
88393f0f65 overviewControls: Move translation-x to SlideLayout
Not because ClutterActor is bad or wrong, but because I always get
confused on the difference, and having them both in SlideLayout
makes the code a bit easier to read and understand.
2013-10-30 13:16:45 -04:00
f5a9dbb348 overviewControls: Always allocate the actor its full size
This is the new Clutter way -- if the actor wants to adjust its
alignment, it will do it itself with its alignment flags.
2013-10-30 13:16:45 -04:00
dbf3bb112c overviewControls: Make getSlide/updateSlide private 2013-10-30 13:16:45 -04:00
f3186bd501 overviewControls: Make visible and inDrag private
These don't need to be accessed from outside SlidingControl,
so don't let them be.
2013-10-30 13:16:44 -04:00
3f1a252b91 overviewControls: Move slideOut on overview hide to the parent as well 2013-10-30 13:16:44 -04:00
1240d6be76 overviewControls: Remove dead code
The parent SlidingControl had an onOverviewShowing, but we had
overridden it with the same code in both subclasses. Just move it
back to SlidingControl.
2013-10-30 13:16:44 -04:00
faf7b62f5c overviewControls: Mark the DashSlider as x_expand
Actors need to expand in order for them to their x_aligns to be taken
into account.
2013-10-30 13:16:44 -04:00
445011b1e5 overviewControls: Add the parent's box into the allocation
Currently, this is always 0, but this will change when we introduce
a custom layout manager into the story.
2013-10-30 13:16:44 -04:00
e630fec63a search: Fix adding items to icon grids 2013-10-30 13:06:56 -04:00
17421e8a63 iconGrid: Actually throw programmer errors
This way we get a backtrace.
2013-10-30 13:05:20 -04:00
af06b78605 searchDisplay: Cache result display actors
When we create a result actor, cache it, so it can be used for
subsearches of the same initial. For now, to keep memory usage
and the stage graph relatively clean, don't persist the actors
across searches, but maybe we should do this in the future.

This also means that we don't query getResultMetas for items
that we've seen in the same initial search.

https://bugzilla.gnome.org/show_bug.cgi?id=704912
2013-10-30 13:01:20 -04:00
3749b09366 searchDisplay: Make the search result actors stateless, by removing terms
We want to cache result actors between searches, so we shouldn't
instantiate them with search-specific info.

https://bugzilla.gnome.org/show_bug.cgi?id=704912
2013-10-30 13:01:20 -04:00
27cac10d0c appDisplay: Use a proper string key for the app search provider
Since we're going to be caching results based on the result ID,
we need to return a string-based result ID to cache on.

https://bugzilla.gnome.org/show_bug.cgi?id=704912
2013-10-30 13:00:47 -04:00
0590962d36 viewSelector: Move all of the provider-loading logic to SearchSystem
The existing provider system is split between a confusing mess of
RemoteSearch, SearchSystem, SearchDisplay, and ViewSelector, partly
because of the vestigal in-shell search system. Move most of the
logic to search.js so it's easier to read.
2013-10-30 12:59:41 -04:00
c0c20d49a5 search: Always fetch the list of search providers
We fetch and store the list of providers from the search system when we
construct SearchResults, but we never update this list when providers are
changed at runtime, causing various bugs making the search not seem as
snappy as it should be. Make sure to always fetch the list of providers
from the search system.
2013-10-30 12:59:31 -04:00
cf7cf45003 Bump version to 3.11.1
Update NEWS.
2013-10-30 17:04:40 +01:00
633dd0d9de notificationDaemon: Save notifications on source destruction
While the existing comment is correct in that a source's notifications
will be destroyed first, the code takes a shortcut which prevents the
Source::count-updated signal from being emitted. Given that the purpose
of the signal is to keep notification counters up-to-date which is
pointless when the source is about to be destroyed, the shortcut makes
sense; just save notifications explicitly in that case.

https://bugzilla.gnome.org/show_bug.cgi?id=710596
2013-10-30 16:40:33 +01:00
b929320d4c lookingGlass: Remove Memory tab and add a gc Icon to toolbar
With js24 it won't be possible to access gjs memory stats from C code.

https://bugzilla.gnome.org/show_bug.cgi?id=711052
2013-10-30 07:44:34 +11:00
7296bedd8e remove direct access to jsapi
with js24 its not possible to access jsapi directly from C code

https://bugzilla.gnome.org/show_bug.cgi?id=711052
2013-10-30 07:44:34 +11:00
e9fbbf4000 port shell-js to c++
https://bugzilla.gnome.org/show_bug.cgi?id=711052
2013-10-30 07:44:34 +11:00
dd44219aa5 Merge searchDisplay.js and search.js
search.js used to do a lot more, but now that most of the
functionality has been moved to the remote search system,
it doesn't do a lot. Merge searchDisplay.js into it.
2013-10-29 16:02:32 -04:00
8cc1fe007d search: Actually crash when seeing errors from a native search provider
We don't implement many of these, and not catching the error lets us
see stack traces and other such information a lot faster.

https://bugzilla.gnome.org/show_bug.cgi?id=704912
2013-10-29 16:02:32 -04:00
c0b45c9fc4 Remove wanda
It's been broken for quite a bit since we removed Panel.Animation,
and hasn't really ever worked with our new search results. It's also
the only non-remote provider left.

Maybe we'll add it back as a remote provider later, but for now, just
ditch it.
2013-10-29 15:46:49 -04:00
41315f45a9 notificationDaemon: Fix custom icons
The 'icon' property contains a serialized GIcon, so we need to
deserialize it when setting the icon.

https://bugzilla.gnome.org/show_bug.cgi?id=710596
2013-10-28 12:28:37 +01:00
04d28a0eea notificationDaemon: Fix urgency hint
We currently mark notifications as urgent which merely contain the
'urgent' property, even when set to false. Look at the actual value
instead.

https://bugzilla.gnome.org/show_bug.cgi?id=710596
2013-10-28 12:28:37 +01:00
1e9cd3f785 Updated Brazilian Portuguese translation
Fixes "Off" translation
2013-10-27 15:46:38 -02:00
f462dd6a4c Updated Hebrew translation
Maybe DL not updated?
2013-10-27 16:40:28 +02:00
34e75fc595 notificationDaemon: Fix button parameter name
Gio ended up using 'target' rather than 'action-target'.

https://bugzilla.gnome.org/show_bug.cgi?id=710596
2013-10-27 11:38:36 +01:00
dac513e046 notificationDaemon: Unpack button label
deep_unpack() doesn't unpack as deeply as one might hope ...

https://bugzilla.gnome.org/show_bug.cgi?id=710596
2013-10-27 11:38:36 +01:00
2c538d247b catch more errors on extensions enable() and disable()
https://bugzilla.gnome.org/show_bug.cgi?id=688331
2013-10-26 16:35:47 +02:00
f3b7f61e54 AltTabSwitcherPopup: check the number of items after creating the popup child
The popup can be empty if the alt-tab switcher is configured
in workspace-only mode, even if there are applications running.

https://bugzilla.gnome.org/show_bug.cgi?id=710745
2013-10-26 16:29:27 +02:00
d47ecf19f5 need space between item in endsession dialog session-list and app-list
https://bugzilla.gnome.org/show_bug.cgi?id=710543
2013-10-26 16:24:41 +02:00
da19b344b5 RemoteSearch: don't autostart dbus search providers at login
Use the new glib flag that allows us to create the proxy immediately
but only activate the service when making the first call.

https://bugzilla.gnome.org/show_bug.cgi?id=708830
2013-10-26 15:20:52 +02:00
eb66407926 NotificationDaemon: fix more fallout
Missing Gtk import for action-icon buttons.

https://bugzilla.gnome.org/show_bug.cgi?id=710596
2013-10-26 14:56:50 +02:00
61c5b8e7d2 notificationDaemon: Pass the correct id to makeButton()
The function expects the action's ID, not the notification's one.

https://bugzilla.gnome.org/show_bug.cgi?id=710596
2013-10-25 14:15:10 +01:00
4b09d57ec2 Fix fallout from notification changes
Commit 5f081b8f8d moved code without moving a helper function
used.

https://bugzilla.gnome.org/show_bug.cgi?id=710596
2013-10-25 14:15:10 +01:00
a16f699dfc theme: Add some vertical padding on login screen
This was apparently lost during some rewrite this cycle ...

https://bugzilla.gnome.org/show_bug.cgi?id=710555
2013-10-25 14:15:10 +01:00
b908a3d70a Stringify the xml definitions for E4X removal
https://bugzilla.gnome.org/show_bug.cgi?id=691409
2013-10-25 08:57:27 +11:00
d519c7263e Background: Drop "saturation" related source
"saturation" was removed from MetaBackground in mutter with
https://git.gnome.org/browse/mutter/commit/?id=0e589061
2013-10-23 21:37:27 +02:00
5dedc5d8ba shell-app: Remove old unused functionality 2013-10-21 17:49:46 -04:00
3ca1784ff4 notificationDaemon: Fix activating with a target
Targets should not be unpacked, and action IDs should.
2013-10-21 16:50:18 -04:00
b54d512f3f notificationDaemon: Fix loading/saving of notifications 2013-10-21 16:14:14 -04:00
9d8fb19f55 notificationDaemon: Write notifications out to disk
This allows notifications to persist even after reboots and
gnome-shell restarts.

https://bugzilla.gnome.org/show_bug.cgi?id=710137
2013-10-21 14:13:21 -04:00
1ac4ab7edc shell-global: Add new APIs for saving/loading persistent state
https://bugzilla.gnome.org/show_bug.cgi?id=710137
2013-10-21 14:13:21 -04:00
c9b73ac731 shell-global: Handle empty variants better
In cases where we have an array of 0 elements or similar, the
data returned may be NULL. Since g_file_replace_contents will
assert in this case, simply check for this and delete the file
instead.

https://bugzilla.gnome.org/show_bug.cgi?id=710137
2013-10-21 14:13:21 -04:00
e0b87f1e14 notificationDaemon: Implement the new GTK+ notifications API
The new API is designed to support features like persistence and uses
the new org.freedesktop.Application specification for activating
actions on notifications. While we won't add support for persistence
yet, implement the new notification spec with parity of the old one.

https://bugzilla.gnome.org/show_bug.cgi?id=710137
2013-10-21 14:12:47 -04:00
394743efc8 notificationDaemon: Rename the existing implementation to the FdoNotificationSource
We'll add a new, simpler private implementation that's used by the new
GNotification API in gio.

https://bugzilla.gnome.org/show_bug.cgi?id=710137
2013-10-21 12:30:25 -04:00
27a86a4756 notificationDaemon: Move nextNotificationId inside the daemon class
This won't be used by the new notification daemon.

https://bugzilla.gnome.org/show_bug.cgi?id=710137
2013-10-21 12:30:25 -04:00
8ee0ef2cde messageTray: Don't always open the source when clicking on the notification
Some consumers may not always want to open the app, so make clients that
want to do this explicitly connect to the 'clicked' signal.

https://bugzilla.gnome.org/show_bug.cgi?id=710137
2013-10-21 12:30:25 -04:00
43f4682ec4 messageTray: Make addButton/addAction take a callback
This is a much simpler API for consumers to manage.

https://bugzilla.gnome.org/show_bug.cgi?id=710137
2013-10-21 12:30:25 -04:00
5f081b8f8d messageTray: Remove useActionIcons feature
This can be put in the legacy notification daemon with the new
addButtonFull API, to create icon names for actions.

https://bugzilla.gnome.org/show_bug.cgi?id=710137
2013-10-21 12:30:25 -04:00
5023542882 messageTray: Split out addButton to allow consumers to pass a pre-made button
Some consumers may want to construct their buttons specially, so allow them
to do that by adding a new API that takes a button instead of a label.

https://bugzilla.gnome.org/show_bug.cgi?id=710137
2013-10-21 12:30:25 -04:00
88d0731d80 messageTray: Replace setButtonSensitive by simply returning the button
We want to remove 'id's from buttons, and simply returning the button actor
is more powerful anyway.

https://bugzilla.gnome.org/show_bug.cgi?id=710137
2013-10-21 12:30:25 -04:00
06cb8c52d7 Updated Hebrew translation 2013-10-20 19:52:43 +03:00
4d1358b7ed st-widget: Use g_clear_pointer
https://bugzilla.gnome.org/show_bug.cgi?id=710541
2013-10-20 14:25:16 +02:00
0cfa7c1c56 power: Fix undefined variables
A couple of variables that should have been gathered from the proxy
and weren't.
2013-10-19 18:00:08 +02:00
76928390a3 Updated Russian translation 2013-10-18 18:30:12 +04:00
af1f9cd76d network: Don't use StButtons for items in selector
Their use blocks activation of the default button by keyboard, which
is important for accessibility. Use a Clutter.ClickAction instead,
which doesn't have this problem as it only considers mouse events.

https://bugzilla.gnome.org/show_bug.cgi?id=710144
2013-10-17 19:46:30 +02:00
2f39f3d146 Revert "network: being able to use keyboard to connect to a Wireless"
This reverts commit d175a588f7.

https://bugzilla.gnome.org/show_bug.cgi?id=710144
2013-10-17 19:46:29 +02:00
f72f39bc26 messageTray: Only try to focus the summary when summoning the tray by <Super>M
Otherwise, when closing the tray, we'll try to focus an actor, which will
focus the stage window, which will drop the focus from whatever window we
already had focused.

https://bugzilla.gnome.org/show_bug.cgi?id=710347
2013-10-17 13:07:42 -04:00
2659ba6bb4 power: Fix typo
JS ERROR: ReferenceError: this_proxy is not defined
Indicator<._getStatus@/usr/share/gnome-shell/js/ui/status/power.js:66
2013-10-17 18:12:02 +02:00
0b8c0c202e power: Use UPower directly instead of gnome-settings-daemon
UPower master exports a display device that can be used to
compute whether to show a status icon, and what we should show.

https://bugzilla.gnome.org/show_bug.cgi?id=710273
2013-10-17 17:36:27 +02:00
37c8132632 workspace: Adapt to mutter API changes 2013-10-15 15:46:36 -04:00
02f2f694e4 Bump version to 3.10.1
Update NEWS.
2013-10-15 20:27:52 +02:00
d175a588f7 network: being able to use keyboard to connect to a Wireless
https://bugzilla.gnome.org/show_bug.cgi?id=710144
2013-10-15 20:27:05 +02:00
4bb41f2f66 Revert "network: being able to use keyboard to connect to a Wireless"
This reverts commit d581d29198.
2013-10-15 20:14:02 +02:00
088c46c7be Revert "Bump version to 3.10.1"
This reverts commit 4228c40b3d.
2013-10-15 20:14:00 +02:00
4228c40b3d Bump version to 3.10.1
Update NEWS.
2013-10-15 20:06:25 +02:00
d581d29198 network: being able to use keyboard to connect to a Wireless
https://bugzilla.gnome.org/show_bug.cgi?id=710144
2013-10-15 19:10:38 +02:00
5a7e854f9e appDisplay: Remember selected view across sessions
The application picker will always open with the view that was last
selected during the session, but the selection is reset on each
restart. This results in some annoyance for users that use the
ALL view exclusively, as they have to toggle views once each
session - the same would apply to exclusive FREQUENT view users
were the defaults to be changed, so the best solution is to simply
make the selected view persistent by storing it in GSettings.

https://bugzilla.gnome.org/show_bug.cgi?id=710042
2013-10-15 19:06:53 +02:00
6e13823ccc shell-js: fix incorrect G_BEGIN_DECLS in header 2013-10-15 09:21:59 +11:00
5c5f2fdf8f a11y: setting ATK_ROLE_ARROW to object unicodeArrow
https://bugzilla.gnome.org/show_bug.cgi?id=710120
2013-10-14 22:29:09 +02:00
96aa33f4ef messageTray: Don't remove and re-add the focus group on button changes
https://bugzilla.gnome.org/show_bug.cgi?id=710115
2013-10-14 15:24:53 -04:00
25fd23e703 messageTray: Split out the notification's destroy handler
This is complex enough to split out.

https://bugzilla.gnome.org/show_bug.cgi?id=710115
2013-10-14 15:24:53 -04:00
99cf4e5787 messageTray: Only connect to a notification's open/destroy once
If we pushNotification the same notification multiple times, we
won't append it to the array again, but we will attach multiple
handlers needlessly.

https://bugzilla.gnome.org/show_bug.cgi?id=710115
2013-10-14 15:24:53 -04:00
66a4cb5875 messageTray: Move the notification policy classes here
The NotificationDaemon really should be for the hookup of remote
notifications, rather than policies.

https://bugzilla.gnome.org/show_bug.cgi?id=710115
2013-10-14 15:24:53 -04:00
da14e2c349 messageTray: Clean up code that determines if something is clearable
https://bugzilla.gnome.org/show_bug.cgi?id=710115
2013-10-14 15:24:53 -04:00
4cda61a16a gdm: support pre-authenticated logins from oVirt
oVirt is software for managing medium-to-large scale deployments of
virtual machine guests across multiple hosts. It supports a feature
where users can authenticate with a central server and get
transparently connected to a guest system and then automatically get logged
into that guest to an associated user session.

Guests using old versions of GDM support this single-sign-on capability
by means of a greeter plugin, using the old greeter's extension
API.

This commit adds similar support to the gnome-shell based login screen.

How it works:

* The OVirtCredentialsManager singleton listens for

  'org.ovirt.vdsm.Credentials.UserAuthenticated'

  D-Bus signal on the system bus from the

  'org.ovirt.vdsm.Credentials'

  bus name. The service that provides that bus name is called
  the oVirt guest agent. It is also responsible for interacting
  with the the central server to get user credentials.

* This UserAuthenticated signal passes, as a parameter, the a token
  which needs to be passed through to the PAM service that is specifically
  set up to integrate with the oVirt authentication architecture.
  The singleton object keeps the token internally so it can be queried
  later on.

* The OVirtCredentialsManager emits a signal 'user-authenticated' on
  it's object once the dbus signal is triggered

* When the 'user-authenticated' signal is emitted, the login screen
  tells GDM to start user verification using the PAM service. The
  authentication stack of the service includes a PAM module
  provided by oVirt that securely retrieves user credentials
  from the oVirt guest agent. The PAM module then forwards those
  credentials on to other modules in the stack so, e.g.,
  the user's gnome keyring can be automatically unlocked.

* In case of the screen shield being visible, it also will react on that
  'user-authenticated' signal and lift the shield.
  In that case the login screen will check on construction time if
  the signal has already been triggered, and a token is available.
  If a token is available it will immediately trigger the functionality
  as described above.

Signed-off-by: Vinzenz Feenstra <evilissimo@redhat.com>

https://bugzilla.gnome.org/show_bug.cgi?id=702162
2013-10-14 13:54:30 -04:00
002afda503 SearchDisplay: handle certain result IDs specially
Allow the prefix 'special:' applied to result IDs to mark results
that should be always shown, even when they would overflow the
maximum results cap. This will be used by epiphany for the special
"Search the Web" result.

https://bugzilla.gnome.org/show_bug.cgi?id=707055
2013-10-14 18:47:20 +02:00
cb7a2e8c6a shell-global: Use G_VARIANT_TYPE macro for checking 2013-10-14 10:52:45 -04:00
d21ae1dad1 shell-global: Fix an invalid memory botch-up
The value we return is a floating ref, which gjs shouldn't
try to free.

https://bugzilla.gnome.org/show_bug.cgi?id=710104
2013-10-14 09:49:51 -04:00
4548859509 shell-global: Fix a few memory leaks
https://bugzilla.gnome.org/show_bug.cgi?id=710104
2013-10-14 09:49:51 -04:00
09c6e6427a Updated Serbian translation 2013-10-14 10:29:57 +02:00
8c34398a15 Updated Slovenian translation 2013-10-13 23:43:34 +02:00
a65b705080 [l10n] Updated Catalan (Valencian) translation 2013-10-13 22:43:34 +02:00
7e28c71074 [l10n] Update Catalan translation 2013-10-13 22:43:31 +02:00
726f4a6715 Updated Lithuanian translation 2013-10-13 20:06:00 +03:00
a444b43548 Updated Portuguese translation 2013-10-13 12:22:54 +01:00
473bb139d1 Updated Latvian translation 2013-10-13 13:35:18 +03:00
d084770cea NetworkMenu: fix regression for VPN connections
Fix regression from e898e29910,
the code was moved from a method to a global function, but one
call site was not updated.

https://bugzilla.gnome.org/show_bug.cgi?id=710019
2013-10-13 02:11:14 +02:00
619389ed20 NotificationDaemon: fix icons for notifications without an app
NotificationDaemon doesn't pass a gicon to the Notification constructor,
because it calls .update() immediately after, so messageTray.js
calls into Source.createIcon(), which returns null and crashes.
Instead, shortcut the Notification constructor by skipping
.update() completely.

https://bugzilla.gnome.org/show_bug.cgi?id=709998
2013-10-13 01:23:18 +02:00
ad043e009e workspaceThumbnail: Drop the _background hack
The _background hack was added because the old way the zooming animation
worked, it set the allocation of the workspaces view and thumbnails box
to the final position and used animations to smoothly animate.

During the 3.6 cycle when we added the new search view, Cosimo changed the
way the zoom animation works so that rather than set the final allocation
and animate, we actually do adjust the allocation of the workspaces view
and thumbnails box.

So, as the hack is no longer necessary, we can drop it.

https://bugzilla.gnome.org/show_bug.cgi?id=694881
2013-10-12 14:38:13 -04:00
89b9d079b1 overviewControls: Don't use the child's preferred size to slide from
In order for the workspace thumbnails box to have the correct size,
we need to constrain the width of the thumbnails box to the height we're
given, instead of assuming an unlimited height.

https://bugzilla.gnome.org/show_bug.cgi?id=694881
2013-10-12 14:38:13 -04:00
58a8845047 overviewControls: Don't try to align something sliding to the right
0 is already the correct value.

https://bugzilla.gnome.org/show_bug.cgi?id=694881
2013-10-12 14:38:13 -04:00
3e6c8e68b4 overviewControls: Clarify some code with a comment
translationX is sort of a bad name, since it confuses with the
actor's translation, which is used for sliding without allocation.

https://bugzilla.gnome.org/show_bug.cgi?id=694881
2013-10-12 14:38:13 -04:00
40cd92f701 overviewControls: Correct the use of x2 in SlidingControl
The x2 here needs to be more than just the width; it needs to
be added onto the x1.

https://bugzilla.gnome.org/show_bug.cgi?id=694881
2013-10-12 14:38:13 -04:00
e216addf7c Updated Dutch translation 2013-10-12 19:59:34 +02:00
9291594330 app-system: remove outdated comment
This parameter was removed before 3.2 but the comment stuck around.
2013-10-12 10:09:36 -05:00
f6010864ea layout: Flush region update in showOverview
We cannot wait for the queued update region to fire when
xdnd is being used because a wrong input shape can result
into a xdnd leave event when the user moves the pointer fast.

https://bugzilla.gnome.org/show_bug.cgi?id=708887
2013-10-11 20:16:44 +02:00
13e6a6def5 Revert "overview: Ignore dragEnd while the animation is still in progress"
This reverts commit 80a3bb85aa.

https://bugzilla.gnome.org/show_bug.cgi?id=708887
2013-10-11 20:16:44 +02:00
575b373cd5 power: Use the icon from the primary device for status
https://bugzilla.gnome.org/show_bug.cgi?id=709925
2013-10-11 13:37:10 -04:00
0892065649 notificationDaemon: Raise the app when clicking on a notification, not the MRU window
https://bugzilla.gnome.org/show_bug.cgi?id=709866
2013-10-11 13:37:10 -04:00
766ef367fb notificationDaemon: Fix the fallback for image-data
The indentation here is wrong, the else actually belongs to the outer
if statement, not the inner if statement.
2013-10-11 13:37:10 -04:00
0c7d9958f5 notificationDaemon: Remove dead code 2013-10-11 13:37:09 -04:00
425b8f6073 Updated Czech translation 2013-10-11 18:28:52 +02:00
63593e45a6 Make dropdown arrows consistent size
Since the agregate menu does 120% of font-size, make this
for all dropdown arrows in gnome-shell and rename the css
class to make clear that it is used in overall gnome-shell

https://bugzilla.gnome.org/show_bug.cgi?id=709564
2013-10-11 17:32:55 +02:00
a6ee9806de Uploaded Ukranian 2013-10-11 15:02:28 +03:00
a6579f4ceb Updated Brazilian Portuguese translation 2013-10-11 07:24:18 -03:00
639622a4fe [l10n] Updated Italian translation. 2013-10-11 09:41:24 +02:00
e28ec2f5ab Updated Indonesian translation 2013-10-11 10:15:14 +07:00
873753c735 Updated Hebrew translation 2013-10-11 01:14:45 +03:00
ff14951be4 Updated Hebrew translation 2013-10-11 01:11:25 +03:00
b47b445558 Updated Polish translation 2013-10-10 23:43:39 +02:00
6e9a2fea89 messageTray: Add 'Notifications' switch to tray menu
According to the designs, the notifications switch was supposed
to move from the user menu to the new message tray menu. However
so far the new system status implementation only removed the old
switch, so add it back in its new place now.

https://bugzilla.gnome.org/show_bug.cgi?id=707073
2013-10-10 23:13:47 +02:00
64d2679b3c Updated Czech translation 2013-10-10 21:09:42 +02:00
3c3d4dfccb Updated Hungarian translation 2013-10-10 13:43:23 +02:00
0ccffed517 Updated Spanish translation 2013-10-10 10:49:53 +02:00
20c18c1fc0 Suppress animation for titles in overview
Because of the animation and collision with relayout, the title of windows in overview may not appear, mainly
the first time we enter in overview

With an animation delay of 0.1s, you'll not see the difference

https://bugzilla.gnome.org/show_bug.cgi?id=709392
2013-10-10 08:51:13 +02:00
a21c0097c2 Tajik translation updated 2013-10-10 11:48:03 +05:00
404d9ef2af [l10n] Updated Italian translation. 2013-10-10 08:37:21 +02:00
503fa1cbce Updated Indonesian translation 2013-10-10 12:59:23 +07:00
63f7991d0f Updated Brazilian Portuguese translation 2013-10-09 23:26:40 -03:00
ae301c1f39 Updated Galician translations 2013-10-09 23:09:57 +02:00
8e911fb719 Updated Galician translations 2013-10-09 23:04:21 +02:00
a0d84e44c5 L10N: Updated Persian translations 2013-10-10 00:28:17 +03:30
81fb7ebb31 Fix some broken GTK-Doc comment blocks 2013-10-09 20:14:57 +02:00
3751211590 shell-global: Fix introspection issues
Remove stray bullet points, and don't actually mark for introspection
since it's private anyway (it starts with a _)
2013-10-09 13:31:00 -04:00
6b4cba09be Updated Polish translation 2013-10-09 19:04:21 +02:00
6a7d184b7b NetworkMenu: make the settings launcher point to the right devices
Tell gnome-control-center to focus on the associated device when
launching it from one of the submenus.

https://bugzilla.gnome.org/show_bug.cgi?id=709246
2013-10-09 18:39:44 +02:00
bde5cfc8bb NetworkMenu/NMDeviceModem: use the operator name when we have it
Instead of the connection name, as the operator name is usually
shorter and more useful.

https://bugzilla.gnome.org/show_bug.cgi?id=709043
2013-10-09 18:39:44 +02:00
777c7a952b NetworkMenu/NMDeviceModem: fix status texts for airplane mode
Show "Hardware Disabled" when disabled by HW switch, and
generically "Disabled" when airplane mode is active, as
indicated by v4 mockups.
Note that bluetooth is not affected by NM handling of airplane
mode (and generally the firmware makes the USB bluetooth
adapter disappear when rfkilled), so this is in NMDeviceModem
instead of NMConnectionDevice.

https://bugzilla.gnome.org/show_bug.cgi?id=709043
2013-10-09 18:39:44 +02:00
c79bdd9029 NetworkMenu/NMConnectionDevice: fix status text
Be consistent with v4 mockups

https://bugzilla.gnome.org/show_bug.cgi?id=709043
2013-10-09 18:39:44 +02:00
6a154efe65 Network: show Not Connected instead of Off when wifi radio is on
When wifi is powered but not connected, show Not Connected instead
of off. This avoids a Off status next to a Turn Off menu item.

https://bugzilla.gnome.org/show_bug.cgi?id=709043
2013-10-09 18:39:44 +02:00
627f3ef36b keyring: Cancel active prompts on disable()
Since commit 1242a16265, we will use a fake prompt which
cancels alls requests without dialog when the keyring component
is disabled. However this does only apply to new requests, dialogs
that are already active when the session mode changes are kept
open. This is not quite as expected, so cancel the prompt in that
case.

https://bugzilla.gnome.org/show_bug.cgi?id=708910
2013-10-09 17:58:37 +02:00
3d28836f2c keyring: Remove unused global
https://bugzilla.gnome.org/show_bug.cgi?id=708910
2013-10-09 17:58:37 +02:00
c28bd04958 Finnish translation update 2013-10-09 14:23:48 +03:00
a6fb3acb42 NetworkMenu: hide the "Turn On" item when the rfkill is hardware blocked
If wifi is disabled in hardware, there is nothing we can do at
the sw level, so hide the menu item.

https://bugzilla.gnome.org/show_bug.cgi?id=709635
2013-10-08 22:20:27 +02:00
8a8b3bf96e NetworkMenu: fix wireless-enabled property notifications
The property is on the NMClient, not NMDevice. Also, make sure
we disconnect the signal when the item is destroyed.
Also, connect to wireless-hardware-enabled, which we'll use soon.

https://bugzilla.gnome.org/show_bug.cgi?id=709635
2013-10-08 22:20:27 +02:00
f7624e5f05 network: make the icon visible again when going from ethernet to nothing
https://bugzilla.gnome.org/show_bug.cgi?id=709638
2013-10-08 14:26:38 -04:00
55edfd2e4a notificationDaemon: Focus the new window before destroying the notification
Destroying the notification will make the key focus be reset to NULL, which
means that gnome-shell will try to focus the MRU window, thinking the user is
done interacting and wants to go back to whatever they were doing.

Unfortunately, since we focus two windows at the same time, they will have
the same timestamp, meaning that the window that actually gets focused will
be a race as to whoever responds to their WM_TAKE_FOCUS event last.

If we explicitly set the focus beforehand, then gnome-shell will believe it
got key focus taken away from it, and won't try to focus the MRU when the
key focus drops to NULL.

https://bugzilla.gnome.org/show_bug.cgi?id=703265
2013-10-08 13:05:34 -04:00
91878dd52c Updated Indonesian translation 2013-10-08 17:07:23 +07:00
c132358ec9 Updated Latvian translation 2013-10-07 21:54:39 +03:00
e426f8ac47 BoxPointer: account for border width when requesting size
We must reduce the forWidth in the call to get_preferred_height()
with the border width, otherwise we might request a smaller height
that we actually need and overflow.

https://bugzilla.gnome.org/show_bug.cgi?id=696564
2013-10-07 20:18:48 +02:00
740dca8afc build: Bump gjs requirement
https://bugzilla.gnome.org/show_bug.cgi?id=709543
2013-10-07 09:37:59 +02:00
6ae06d319c [l10n] Updated Catalan (Valencian) translation 2013-10-06 21:58:43 +02:00
30bc2b2f9c [l10n] Update Catalan translation 2013-10-06 21:06:39 +02:00
7dc8e9d657 Updated Russian translation 2013-10-06 12:31:47 +04:00
1ab2fa5bf0 [l10n]Updated Turkish translation 2013-10-06 09:19:37 +03:00
779a1077bf Updated Serbian translation 2013-10-05 20:59:51 +02:00
815bf7a53a L10N: Updated Persian translations 2013-10-05 18:06:59 +03:30
91a382dfb5 Fix Lithuanian calendar translation 2013-10-05 16:51:19 +03:00
2d8d4cd57d Updated Serbian translation 2013-10-05 06:49:07 +02:00
0c5fe2b3bf Updated Serbian translation 2013-10-05 06:08:07 +02:00
56c487347f Updated Belarusian translation. 2013-10-03 23:02:05 +03:00
0a572fce1b Updated Norwegian bokmål translation 2013-10-03 20:38:08 +02:00
e898e29910 NetworkMenu/NMConnectionDevice: grab the connection before using it
If the active connection for the device is not the primary or
activating globally, it won't have the _connection and _primaryDevice
expando properties, so grab them from the settings object.

https://bugzilla.gnome.org/show_bug.cgi?id=709043
2013-10-03 16:03:41 +02:00
843f076225 NetworkMenu/NMDeviceWireless: use the device state instead of active connection state
Since we connect to signals for that, we need to sync on that, or
we can miss a notification.

https://bugzilla.gnome.org/show_bug.cgi?id=709043
2013-10-03 16:01:27 +02:00
fdb732c8c2 Bluetooth: fix ngettext call
https://bugzilla.gnome.org/show_bug.cgi?id=709043
2013-10-03 16:01:27 +02:00
33896a4e8f network: fix NMConnectionSection destruction
The patch fixes the following warning, and along with it, the proper
destruction of the NMConnectionSection is performed so that items get
correctly removed from the menu.

(gnome-shell:24528): Gjs-WARNING **: JS ERROR: TypeError:
this.statusItem is undefined
NMConnectionSection<.destroy@/home/aleksander/gnome/install/share/gnome-shell/js/ui/status/network.js:173
wrapper@/home/aleksander/gnome/install/share/gjs-1.0/lang.js:213
_parent@/home/aleksander/gnome/install/share/gjs-1.0/lang.js:175
NMConnectionDevice<.destroy@/home/aleksander/gnome/install/share/gnome-shell/js/ui/status/network.js:292
wrapper@/home/aleksander/gnome/install/share/gjs-1.0/lang.js:213
_parent@/home/aleksander/gnome/install/share/gjs-1.0/lang.js:175
NMDeviceModem<.destroy@/home/aleksander/gnome/install/share/gnome-shell/js/ui/status/network.js:448
wrapper@/home/aleksander/gnome/install/share/gjs-1.0/lang.js:213
NMApplet<._removeDeviceWrapper@/home/aleksander/gnome/install/share/gnome-shell/js/ui/status/network.js:1421
wrapper@/home/aleksander/gnome/install/share/gjs-1.0/lang.js:213
NMApplet<._deviceRemoved@/home/aleksander/gnome/install/share/gnome-shell/js/ui/status/network.js:1416
wrapper@/home/aleksander/gnome/install/share/gjs-1.0/lang.js:213

https://bugzilla.gnome.org/show_bug.cgi?id=709248
2013-10-03 00:50:02 +02:00
51e016a0d6 theme: modify the avatar and user name
Change the avatar and user name border radius, padding
and text size to be consistent with mockups.

https://bugzilla.gnome.org/show_bug.cgi?id=702309
2013-10-02 22:26:30 +02:00
8737b06559 loginDialog: MessageType is now in GdmUtil
https://bugzilla.gnome.org/show_bug.cgi?id=709286
2013-10-02 13:58:09 -04:00
15ab285174 layout: Use monitor index when adding bg managers
Don't assume that this._bgManagers.push() (i.e adding to the end) is always
correct.

On startup we call _createPrimaryBackground which passes in the primary index
which may not be 0.
2013-10-02 15:58:22 +02:00
3a4782cc64 app-system: Fix some enum warnings
https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-10-02 09:49:03 -04:00
0256a6d47b app: Use g_variant_lookup instead of dict iteration 2013-10-02 09:49:02 -04:00
8b0e846e0e background: Disconnect settings signal handler on destroy
We connect to the changed signal in _init() but never actually disconnect from
it. The callback has a reference to "this" which results into the background
object not getting garbage collected.

Fix that leaks by disconnecting in _destroy()

https://bugzilla.gnome.org/show_bug.cgi?id=709263
2013-10-02 15:43:30 +02:00
41acb5d3cc Updated Czech translation 2013-10-02 09:56:45 +02:00
a2f9b8ea9b workspacesView: Remove dead code 2013-10-01 18:41:26 -04:00
6237a1c505 workspace: Remove unused leftover constant
from the window zooming days
2013-10-01 18:41:26 -04:00
7c08db0b0f [l10n] Updated Italian translation. 2013-10-01 19:22:11 +02:00
df1270ac49 Updated Danish translation 2013-10-01 19:10:05 +02:00
46edc053d4 overview: Add cover pane directly to overviewGroup
The cover pane is used to block events during transitions, but as
workspaces don't share the same container as other overview elements,
they are currently excempt from the event blocking.
Move the cover pane to the top-level overview container instead.

https://bugzilla.gnome.org/show_bug.cgi?id=709034
2013-10-01 17:16:51 +02:00
908046c31a [l10n] Updated Estonian translation 2013-10-01 17:14:01 +03:00
8380c79875 network: Update the network indicator when the VPN state changes 2013-10-01 10:25:46 -04:00
8a4879a96a Updated Traditional Chinese translation(Hong Kong and Taiwan) 2013-09-30 19:40:48 +08:00
cdf1a77f08 Updated Spanish translation 2013-09-30 12:00:58 +02:00
3f9857ccbd Assamese translation updated 2013-09-30 13:32:07 +05:30
1d65a31420 Updated Lithuanian translation 2013-09-29 20:33:17 +03:00
dafdf0838a Updated Slovak translation 2013-09-29 17:41:47 +01:00
f9cf135f68 update Punjabi Translaiton 2013-09-29 05:26:34 -05:00
a1878e54c9 Updated Norwegian bokmål translation 2013-09-29 11:29:08 +02:00
95e5d899a9 Updated Basque language 2013-09-29 10:53:34 +02:00
ee8321df67 Updated Slovenian translation 2013-09-28 21:49:04 +02:00
4918213e68 Tajik translation updated 2013-09-28 19:39:12 +05:00
ed7f349fc6 Updated Galician translations 2013-09-28 16:25:15 +02:00
2888f22a24 Updated Norwegian bokmål translation 2013-09-28 14:53:19 +02:00
fcb217f681 Updated Brazilian Portuguese translation 2013-09-28 05:16:53 -03:00
9ffa9fe1a8 Updated Galician translations 2013-09-27 21:38:08 +02:00
905020c507 Updated Slovak translation 2013-09-27 17:40:35 +01:00
02f5500641 theme: Make indicators checked&active like checked 2013-09-27 17:06:26 +02:00
465af55d6e theme: Add hover/active states to indicators
Until now we had the same svg for hover, active and checked
states in the pagination indicators. Just differentiate between
them using differents svg.

svg files provided by Jakub Steiner

https://bugzilla.gnome.org/show_bug.cgi?id=708852
2013-09-27 15:50:28 +02:00
80a3bb85aa overview: Ignore dragEnd while the animation is still in progress
Moving the mouse fast enough during xdnd will trigger a xdnd-leave event
because the input shape is not updated until after the animation is done.

So simply ignore the leave events while the animation is in progress.

https://bugzilla.gnome.org/show_bug.cgi?id=708887
2013-09-27 12:54:22 +02:00
ea26bd3003 Updated Latvian translation 2013-09-26 23:30:59 +03:00
508a511d2a Revert "overviewControls: Don't allow appearing controls to "pop in""
This reverts commit e31693bbee.

This doesn't properly adjust the allocation, leading to an unbalanced
overview where things aren't centered properly. Just revert for now,
and we'll rethink this next cycle.
2013-09-26 16:20:06 -04:00
2d80cb71db searchDisplay: Scroll results when the user focuses a provider icon
Otherwise, it seems odd to highlight results the user can't see
simply by pressing down a lot.

https://bugzilla.gnome.org/show_bug.cgi?id=708868
2013-09-26 15:17:29 -04:00
e31693bbee overviewControls: Don't allow appearing controls to "pop in"
When coming back from search or apps, the workspace thumbnails and dash
don't slide in but "pop in". This is because of bad timing: when slideIn
is called, we immediately start the translation animation, and it
completes before by the time we fade the new page in.

Fix this by calling slideIn and slideOut at two different times: we now
slide out when the old page with our controls is fading out, and slide in
when the new page with our controls is fading in.

https://bugzilla.gnome.org/show_bug.cgi?id=708340
2013-09-26 13:39:20 -04:00
fb561f10a7 network: Make sure to set the signal icon at dialog item construction time
We forgot to set it here; it would be updated if there was changes in the
signal, but not when it was created.

https://bugzilla.gnome.org/show_bug.cgi?id=708442
2013-09-26 11:06:42 -04:00
0c57d53e03 Update Esperanto translation 2013-09-26 09:09:37 -04:00
3b1b9f589b SystemMenu: wait for a completed paint before switching VT
Activating the GDM login screen switches VT and causes X to freeze
event processing (because it lost the drm master), so must make
sure to have painted the lock screen at least once before proceeding,
or the user can go back and see the unlocked desktop.

https://bugzilla.gnome.org/show_bug.cgi?id=708051
2013-09-26 10:46:42 +02:00
ac8d39acf4 Updated Lithuanian translation 2013-09-25 23:22:27 +03:00
664e795217 [l10n]Updated Turkish translation 2013-09-25 21:16:28 +03:00
82bf323f63 Updated German translation 2013-09-25 15:25:31 +02:00
547ac85113 Updated Basque language 2013-09-25 11:17:01 +02:00
212 changed files with 23268 additions and 27902 deletions

2
.gitignore vendored
View File

@ -43,6 +43,8 @@ docs/reference/*/xml/
docs/reference/shell/doc-gen-* docs/reference/shell/doc-gen-*
gtk-doc.make gtk-doc.make
js/misc/config.js js/misc/config.js
js/js-resources.c
js/js-resources.h
intltool-extract.in intltool-extract.in
intltool-merge.in intltool-merge.in
intltool-update.in intltool-update.in

41
COPYING
View File

@ -1,12 +1,12 @@
GNU GENERAL PUBLIC LICENSE GNU GENERAL PUBLIC LICENSE
Version 2, June 1991 Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc. Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed. of this license document, but changing it is not allowed.
Preamble Preamble
The licenses for most software are designed to take away your The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public freedom to share and change it. By contrast, the GNU General Public
@ -15,7 +15,7 @@ software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to the GNU Lesser General Public License instead.) You can apply it to
your programs, too. your programs, too.
When we speak of free software, we are referring to freedom, not When we speak of free software, we are referring to freedom, not
@ -55,8 +55,8 @@ patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and The precise terms and conditions for copying, distribution and
modification follow. modification follow.
GNU GENERAL PUBLIC LICENSE GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains 0. This License applies to any program or other work which contains
@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions:
License. (Exception: if the Program itself is interactive but License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on does not normally print such an announcement, your work based on
the Program is not required to print an announcement.) the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program, identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in and can be reasonably considered independent and separate works in
@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not distribution of the source code, even though third parties are not
compelled to copy the source along with the object code. compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program 4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is otherwise to copy, modify, sublicense or distribute the Program is
@ -225,7 +225,7 @@ impose that choice.
This section is intended to make thoroughly clear what is believed to This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License. be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in 8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License original copyright holder who places the Program under this License
@ -255,7 +255,7 @@ make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally. of promoting the sharing and reuse of software generally.
NO WARRANTY NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES. POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it possible use to the public, the best way to achieve this is to make it
@ -303,17 +303,16 @@ the "copyright" line and a pointer to where the full notice is found.
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License along
along with this program; if not, write to the Free Software with this program; if not, write to the Free Software Foundation, Inc.,
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail. Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this If the program is interactive, make it output a short notice like this
when it starts in an interactive mode: when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details. under certain conditions; type `show c' for details.
@ -336,5 +335,5 @@ necessary. Here is a sample; alter the names:
This General Public License does not permit incorporating your program into This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. Public License instead of this License.

View File

@ -1,7 +1,11 @@
# Point to our macro directory and pick up user flags from the environment # Point to our macro directory and pick up user flags from the environment
ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
SUBDIRS = data js src browser-plugin tests po docs SUBDIRS = data js src tests po docs
if BUILD_BROWSER_PLUGIN
SUBDIRS += browser-plugin
endif
if ENABLE_MAN if ENABLE_MAN
SUBDIRS += man SUBDIRS += man

143
NEWS
View File

@ -1,3 +1,146 @@
3.11.4
======
* Fix removal of workspacaes that are not at the end [Giovanni; #721417]
* Allow session mode to be specified in the environment [Ray; #720894]
* Special-case launching of terminals [Debarshi; #695010]
* Always show arrow if app switcher is scrollable [Jonh; #711467]
* Implement new app folders system [Jasper; #722117]
* Remove arrow from background menu [Tarun; #699608]
* Misc bug fixes and cleanups [Giovanni, Andika, Florian, Ray; #721039,
#721439, #721507, #721629, #721868, #722210]
Contributors:
Giovanni Campagna, Piotr Drąg, Tarun Kumar Joshi, Florian Müllner,
Debarshi Ray, Jasper St. Pierre, Ray Strode, Andika Triwidada, Jonh Wendell
Translations:
Dušan Kazik [sk], Tong Hui [zh_CN], Benjamin Steinwender [de],
Matej Urbančič [sl], Jorge Pérez Pérez [an], Kjartan Maraas [nb],
Milo Casagrande [it], Rafael Ferreira [pt_BR], Marek Černocký [cs],
Daniel Mustieles [es], Adorilson Bezerra [pt_BR], Christian Kirbach [de],
Aurimas Černius [lt], Andika Triwidada [id], Baurzhan Muftakhidinov [kk],
Victor Ibragimov [tg], Yosef Or Boczko [he], Dimitris Spingos [el],
Fran Diéguez [gl]
3.11.3
======
* Fix fade effect of desktop icons [Florian; #707671]
* Fix issues with background management code [Jasper; #709313]
* Use new Glib facilities for application search [Jasper; #711631]
* Add focus indication to session menu button [Sebastien; #710539]
* Fix hover tracking for StEntries [Jasper; #706749]
* Fix reentrancy issue in message tray [Jasper; #711694]
* Tone down zoom animation on login/unlock [Jasper; #712362]
* Allow specifying monitor for OSD [Carlos; #712664]
* Fix resetting prompt on user switch [Ray; #710456]
* Stop using gnome-bluetooth-applet [Bastien; #719341]
* Add support for EAP-FAST password requests [Dan; #719813]
* Fix entry focus of chat notifications [Jasper; #709853]
* Make window previews keyboard navigatable [Jasper; #644306]
* Fix app switcher order with dialog windows [Florian; #719824]
* Allow remote search providers without icons [Debarshi; #719965]
* Fix various alignment issues in RTL locales [Yosef; #712638, #712596,
#712594, #712600, #712579]
* Misc. bug fixes and cleanups [Jasper, Florian, Giovanni, Dan; #712727,
#712753, #719378, #719730, #719803, #710115, #720017, #719815, #719567,
#720298]
Contributors:
Giovanni Campagna, Carlos Garnacho, Sebastien Lafargue, Tim Lunn,
Florian Müllner, Bastien Nocera, Yosef Or Boczko, Debarshi Ray,
Jasper St. Pierre, Ray Strode, Dan Williams
Translations:
Kjartan Maraas [nb], Reinout van Schouwen [nl], Rafael Ferreira [pt_BR],
Mattias Põldaru [et], Emin Tufan Çetin [tr], Jiri Grönroos [fi],
Khaled Hosny [ar], Fran Diéguez [gl], Victor Ibragimov [tg],
Daniel Mustieles [es]
3.11.2
======
* Cache search result display actors [Jasper; #704912]
* Use username in userWidget if real name doesn't fit [Jasper; #706851]
* Support shell_global_reexec_self() on OpenBSD [Antoine; #709571]
* Support disabling browser plugin [Colin; #711218]
* Restore support for 'disable-restart-buttons' [Florian; #711244]
* Validate parameters of exposed DBus methods [Florian; #699752]
* Connect applications to systemd journal if available [Colin; #711626]
* Misc bug fixes and cleanups [Florian, Jasper; #711205, #698486, #711416,
#644306, #711555, #709806, #711631, #711732]
Contributors:
Cosimo Cecchi, Antoine Jacoutot, Florian Müllner, Jasper St. Pierre,
Rico Tzschichholz, Colin Walters
Translations:
Yuri Myasoedov [ru], Kjartan Maraas [nb], Efstathios Iosifidis [el],
Benjamin Steinwender [de], eternalhui [zh_CN], Shantha kumar [ta]
3.11.1
======
* power: Use UPower directly instead of gnome-settings-daemon [Bastien; #710273]
* Implement support for new GTK+ notification API [Jasper, Giovanni, Florian;
#710137, #710596]
* gdm: Don't allow user-list to fill up the entire screen [Florian; #710555]
* Don't autostart remote search providers at login [Giovanni; #708830]
* Fix spacing in end-session dialog [Sebastien; #710543]
* Prepare for js24 [Tim; #711052]
* Misc bug fixes and cleanups [Jasper, Florian, Adel, Tim, Sebastien; #710347,
#710144, #710541, #691409, #710745, #688331, #704912]
Contributors:
Giovanni Campagna, Adel Gadllah, Sebastien Lafargue, Tim Lunn,
Florian Müllner, Bastien Nocera, Jasper St. Pierre, Rico Tzschichholz
Translations:
Stas Solovey [ru], Yosef Or Boczko [he], Rafael Ferreira [pt_BR]
3.10.1
======
* Make sure lock screen is drawn once before switching user [Giovanni; #708051]
* Fix signal strength indicators in network selector [Jasper; #708442]
* Scroll search results when focusing provider icons [Jasper; #708868]
* Add separate hover/active states to page indicators [Carlos; #708852]
* Tweak appearance of user name and avatar [Yash; #702309]
* Hide "Turn On" in network menu when disabled by hardware [Giovanni; #709635]
* Cancel open keyring prompts when the screen is locked [Florian; #708910]
* Differentiate "Not Connected" and "Off" in network menu [Giovanni; #709043]
* Make network settings items point to the right device [Giovanni; #709246]
* Remove animation of window preview titles [Sebastien; #709392]
* Add 'Notifications' switch to tray menu [Florian; #707073]
* Make dropdown arrows consistent [Carlos; #709564]
* power: Use icon from primary device for status [Jasper; #709925]
* Fix XDND drags to overview [Adel; #708887]
* Fix workspace switcher disappearing with too many workspaces [Jasper; #694881]
* Handle search results with 'special:' prefix specially [Giovanni; #707055]
* gdm: Support pre-authenticated logins from oVirt [Vinzenz; #702162]
* Use ARROW role for labels representing arrows [Alejandro; #710120]
* Make selected view in app picker persistent [Florian; #710042]
* Make network selector navigable by keyboard [Alejandro; #710144]
* Misc bug fixes [Florian, Adel, Jasper, Aleksander, Giovanni, Dan, Michael,
Tim; #709034, #709263, #698486, #709286, #709248, #709543, #696564, #703265,
#709638, #709866, #709998, #710019, #710104, #710115]
Contributors:
Giovanni Campagna, Michael Catanzaro, Vinzenz Feenstra, Adel Gadllah,
Yash Girdhar, Sebastien Lafargue, Tim Lunn, Aleksander Morgado,
Florian Müllner, Alejandro Piñeiro, Carlos Soriano, Jasper St. Pierre,
Dieter Verfaillie, Dan Winship
Translations:
Inaki Larranaga Murgoitio [eu], Christian Kirbach [de], Muhammet Kara [tr],
Aurimas Černius [lt], Ryan Lortie [eo], Rūdolfs Mazurs [lv],
Dušan Kazik [sk], Fran Diéguez [gl], Enrico Nicoletto [pt_BR],
Kjartan Maraas [nb], Victor Ibragimov [tg], Matej Urbančič [sl],
A S Alam [pa], Nilamdyuti Goswami [as], Daniel Mustieles [es],
Cheng-Chia Tseng [zh_HK, zh_TW], Mattias Põldaru [et], Kenneth Nielsen [da],
Milo Casagrande [it], Marek Černocký [cs], Ihar Hrachyshka [be],
Мирослав Николић [sr, sr@latin], Arash Mousavi [fa], Yuri Myasoedov [ru],
Gil Forcada [ca], Carles Ferrando [ca@valencia], Andika Triwidada [id],
Timo Jyrinki [fi], Piotr Drąg [pl], Rafael Ferreira [pt_BR],
Gabor Kelemen [hu], Yosef Or Boczko [he], Daniel Korostil [uk],
Wouter Bolsterlee [nl], António Lima [pt]
3.10.0.1 3.10.0.1
========= =========
* Fix login screen [Ray; #708691] * Fix login screen [Ray; #708691]

View File

@ -13,9 +13,7 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, see <http://www.gnu.org/licenses/>.
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
* *
* Authors: * Authors:
* Jasper St. Pierre <jstpierre@mecheye.net> * Jasper St. Pierre <jstpierre@mecheye.net>

View File

@ -1,5 +1,5 @@
AC_PREREQ(2.63) AC_PREREQ(2.63)
AC_INIT([gnome-shell],[3.10.0.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell]) AC_INIT([gnome-shell],[3.11.4],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_CONFIG_HEADERS([config.h]) AC_CONFIG_HEADERS([config.h])
AC_CONFIG_SRCDIR([src/shell-global.c]) AC_CONFIG_SRCDIR([src/shell-global.c])
@ -16,6 +16,7 @@ m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
# Checks for programs. # Checks for programs.
AC_PROG_CC AC_PROG_CC
AC_PROG_CXX
# Initialize libtool # Initialize libtool
LT_PREREQ([2.2.6]) LT_PREREQ([2.2.6])
@ -57,10 +58,25 @@ fi
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder) AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
CLUTTER_MIN_VERSION=1.13.4 AC_ARG_ENABLE([systemd],
AS_HELP_STRING([--enable-systemd], [Use systemd]),
[enable_systemd=$enableval],
[enable_systemd=auto])
AS_IF([test x$enable_systemd != xno], [
AC_MSG_CHECKING([for libsystemd-journal])
PKG_CHECK_EXISTS([libsystemd-journal],
[have_systemd=yes
AC_DEFINE([HAVE_SYSTEMD], [1], [Define if we have systemd])],
[have_systemd=no])
AC_MSG_RESULT($have_systemd)
])
AC_MSG_RESULT($enable_systemd)
CLUTTER_MIN_VERSION=1.15.90
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1 GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
GJS_MIN_VERSION=1.35.4 GJS_MIN_VERSION=1.39.0
MUTTER_MIN_VERSION=3.10.0 MUTTER_MIN_VERSION=3.11.1
GTK_MIN_VERSION=3.7.9 GTK_MIN_VERSION=3.7.9
GIO_MIN_VERSION=2.37.0 GIO_MIN_VERSION=2.37.0
LIBECAL_MIN_VERSION=3.5.3 LIBECAL_MIN_VERSION=3.5.3
@ -70,7 +86,6 @@ POLKIT_MIN_VERSION=0.100
STARTUP_NOTIFICATION_MIN_VERSION=0.11 STARTUP_NOTIFICATION_MIN_VERSION=0.11
GCR_MIN_VERSION=3.7.5 GCR_MIN_VERSION=3.7.5
GNOME_DESKTOP_REQUIRED_VERSION=3.7.90 GNOME_DESKTOP_REQUIRED_VERSION=3.7.90
GNOME_MENUS_REQUIRED_VERSION=3.5.3
NETWORKMANAGER_MIN_VERSION=0.9.8 NETWORKMANAGER_MIN_VERSION=0.9.8
PULSE_MIN_VERS=2.0 PULSE_MIN_VERS=2.0
@ -80,7 +95,6 @@ SHARED_PCS="gio-unix-2.0 >= $GIO_MIN_VERSION
gtk+-3.0 >= $GTK_MIN_VERSION gtk+-3.0 >= $GTK_MIN_VERSION
atk-bridge-2.0 atk-bridge-2.0
gjs-internals-1.0 >= $GJS_MIN_VERSION gjs-internals-1.0 >= $GJS_MIN_VERSION
libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION
$recorder_modules $recorder_modules
gdk-x11-3.0 libsoup-2.4 gdk-x11-3.0 libsoup-2.4
xtst xtst
@ -94,6 +108,9 @@ SHARED_PCS="gio-unix-2.0 >= $GIO_MIN_VERSION
libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION
libnm-gtk >= $NETWORKMANAGER_MIN_VERSION libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
libsecret-unstable gcr-base-3 >= $GCR_MIN_VERSION" libsecret-unstable gcr-base-3 >= $GCR_MIN_VERSION"
if test x$have_systemd = xyes; then
SHARED_PCS="${SHARED_PCS} libsystemd-journal"
fi
PKG_CHECK_MODULES(GNOME_SHELL, $SHARED_PCS) PKG_CHECK_MODULES(GNOME_SHELL, $SHARED_PCS)
PKG_CHECK_MODULES(MUTTER, libmutter >= $MUTTER_MIN_VERSION) PKG_CHECK_MODULES(MUTTER, libmutter >= $MUTTER_MIN_VERSION)
@ -109,25 +126,25 @@ 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(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_PERF_HELPER, gtk+-3.0 gio-2.0)
PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-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(TRAY, gtk+-3.0)
PKG_CHECK_MODULES(GVC, libpulse >= $PULSE_MIN_VERS libpulse-mainloop-glib gobject-2.0) 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(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.7.4)
PKG_CHECK_MODULES(CARIBOU, caribou-1.0 >= 0.4.8) PKG_CHECK_MODULES(CARIBOU, caribou-1.0 >= 0.4.8)
AC_MSG_CHECKING([for bluetooth support]) AC_ARG_ENABLE(browser-plugin,
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.9.0], [AS_HELP_STRING([--enable-browser-plugin],
[BLUETOOTH_DIR=`$PKG_CONFIG --variable=applet_libdir gnome-bluetooth-1.0` [Enable browser plugin [default=yes]])],,
BLUETOOTH_LIBS=`$PKG_CONFIG --variable=applet_libs gnome-bluetooth-1.0` enable_browser_plugin=yes)
AC_SUBST([BLUETOOTH_LIBS],["$BLUETOOTH_LIBS"]) AS_IF([test x$enable_browser_plugin = xyes], [
AC_SUBST([BLUETOOTH_DIR],["$BLUETOOTH_DIR"]) PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
AC_DEFINE_UNQUOTED([BLUETOOTH_DIR],["$BLUETOOTH_DIR"],[Path to installed GnomeBluetooth typelib and library]) ])
AC_DEFINE([HAVE_BLUETOOTH],[1],[Define if you have libgnome-bluetooth-applet]) AM_CONDITIONAL(BUILD_BROWSER_PLUGIN, test x$enable_browser_plugin = xyes)
AC_SUBST([HAVE_BLUETOOTH],[1])
AC_MSG_RESULT([yes])], PKG_CHECK_MODULES(BLUETOOTH, gnome-bluetooth-1.0 >= 3.9.0,
[AC_DEFINE([HAVE_BLUETOOTH],[1],[Define if you have libgnome-bluetooth-applet])
AC_SUBST([HAVE_BLUETOOTH],[1])],
[AC_DEFINE([HAVE_BLUETOOTH],[0]) [AC_DEFINE([HAVE_BLUETOOTH],[0])
AC_SUBST([HAVE_BLUETOOTH],[0]) AC_SUBST([HAVE_BLUETOOTH],[0])])
AC_MSG_RESULT([no])])
PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION gio-2.0) PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION gio-2.0)
AC_SUBST(CALENDAR_SERVER_CFLAGS) AC_SUBST(CALENDAR_SERVER_CFLAGS)
@ -147,6 +164,9 @@ AC_SUBST(MUTTER_TYPELIB_DIR)
GJS_CONSOLE=`$PKG_CONFIG --variable=gjs_console gjs-1.0` GJS_CONSOLE=`$PKG_CONFIG --variable=gjs_console gjs-1.0`
AC_SUBST(GJS_CONSOLE) AC_SUBST(GJS_CONSOLE)
GLIB_COMPILE_RESOURCES=`$PKG_CONFIG --variable glib_compile_resources gio-2.0`
AC_SUBST(GLIB_COMPILE_RESOURCES)
AC_CHECK_FUNCS(fdwalk) AC_CHECK_FUNCS(fdwalk)
AC_CHECK_FUNCS(mallinfo) AC_CHECK_FUNCS(mallinfo)
AC_CHECK_HEADERS([sys/resource.h]) AC_CHECK_HEADERS([sys/resource.h])

View File

@ -1,6 +1,3 @@
wandadir = $(pkgdatadir)
dist_wanda_DATA = wanda.png
desktopdir=$(datadir)/applications desktopdir=$(datadir)/applications
desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
if HAVE_MUTTER_WAYLAND if HAVE_MUTTER_WAYLAND
@ -45,8 +42,10 @@ dist_theme_DATA = \
theme/message-tray-background.png \ theme/message-tray-background.png \
theme/more-results.svg \ theme/more-results.svg \
theme/noise-texture.png \ theme/noise-texture.png \
theme/page-indicator-active.svg \ theme/page-indicator-active.svg \
theme/page-indicator-inactive.svg \ theme/page-indicator-inactive.svg \
theme/page-indicator-checked.svg \
theme/page-indicator-hover.svg \
theme/panel-button-border.svg \ theme/panel-button-border.svg \
theme/panel-button-highlight-narrow.svg \ theme/panel-button-highlight-narrow.svg \
theme/panel-button-highlight-wide.svg \ theme/panel-button-highlight-wide.svg \

View File

@ -13,12 +13,12 @@
</key> </key>
<key name="enabled-extensions" type="as"> <key name="enabled-extensions" type="as">
<default>[]</default> <default>[]</default>
<_summary>Uuids of extensions to enable</_summary> <_summary>UUIDs of extensions to enable</_summary>
<_description> <_description>
GNOME Shell extensions have a uuid property; this key lists extensions GNOME Shell extensions have a UUID property; this key lists extensions
which should be loaded. Any extension that wants to be loaded needs which should be loaded. Any extension that wants to be loaded needs
to be in this list. You can also manipulate this list with the to be in this list. You can also manipulate this list with the
EnableExtension and DisableExtension DBus methods on org.gnome.Shell. EnableExtension and DisableExtension D-Bus methods on org.gnome.Shell.
</_description> </_description>
</key> </key>
<key name="favorite-apps" type="as"> <key name="favorite-apps" type="as">
@ -29,13 +29,12 @@
will be displayed in the favorites area. will be displayed in the favorites area.
</_description> </_description>
</key> </key>
<key name="app-folder-categories" type="as"> <key name="app-picker-view" type="u">
<default>[ 'Utilities', 'Sundry' ]</default> <default>0</default>
<_summary>List of categories that should be displayed as folders</_summary> <summary>App Picker View</summary>
<_description> <description>
Each category name in this list will be represented as folder in the Index of the currently selected view in the application picker.
application view, rather than being displayed inline in the main view. </description>
</_description>
</key> </key>
<key name="command-history" type="as"> <key name="command-history" type="as">
<default>[]</default> <default>[]</default>
@ -47,10 +46,10 @@
</key> </key>
<key name="always-show-log-out" type="b"> <key name="always-show-log-out" type="b">
<default>false</default> <default>false</default>
<_summary>Always show the 'Log out' menuitem in the user menu.</_summary> <_summary>Always show the 'Log out' menu item in the user menu.</_summary>
<_description> <_description>
This key overrides the automatic hiding of the 'Log out' This key overrides the automatic hiding of the 'Log out'
menuitem in single-user, single-session situations. menu item in single-user, single-session situations.
</_description> </_description>
</key> </key>
<key name="remember-mount-password" type="b"> <key name="remember-mount-password" type="b">
@ -117,6 +116,11 @@
Keybinding to focus the active notification. Keybinding to focus the active notification.
</_description> </_description>
</key> </key>
<key name="pause-resume-tweens" type="as">
<default>[]</default>
<summary>Keybinding that pauses and resumes all running tweens, for debugging purposes</summary>
<description></description>
</key>
</schema> </schema>
<schema id="org.gnome.shell.keyboard" path="/org/gnome/shell/keyboard/" <schema id="org.gnome.shell.keyboard" path="/org/gnome/shell/keyboard/"

View File

@ -157,7 +157,7 @@ StScrollBar StButton#vhandle:active {
min-width: 200px; min-width: 200px;
} }
.popup-submenu-menu-item-triangle { .unicode-arrow {
font-size: 120%; font-size: 120%;
} }
@ -306,16 +306,13 @@ StScrollBar StButton#vhandle:active {
font-size: 12pt; font-size: 12pt;
border-bottom: 1px solid #666; border-bottom: 1px solid #666;
padding: 12px; padding: 12px;
}
.nm-dialog-item:checked {
background-color: #333;
}
.nm-dialog-item-box {
spacing: 20px; spacing: 20px;
} }
.nm-dialog-item:selected {
background-color: #333;
}
.nm-dialog-icons { .nm-dialog-icons {
spacing: .5em; spacing: .5em;
} }
@ -693,7 +690,9 @@ StScrollBar StButton#vhandle:active {
padding-bottom: 32px; padding-bottom: 32px;
} }
.workspace-thumbnails-background { .workspace-thumbnails {
spacing: 11px;
visible-width: 32px; /* Amount visible before hovering */
border: 1px solid rgba(128, 128, 128, 0.4); border: 1px solid rgba(128, 128, 128, 0.4);
border-right: 0px; border-right: 0px;
border-radius: 9px 0px 0px 9px; border-radius: 9px 0px 0px 9px;
@ -701,18 +700,13 @@ StScrollBar StButton#vhandle:active {
padding: 11px 7px 11px 11px; padding: 11px 7px 11px 11px;
} }
.workspace-thumbnails-background:rtl { .workspace-thumbnails:rtl {
border-right: 1px; border-right: 1px;
border-left: 0px; border-left: 0px;
border-radius: 0px 9px 9px 0px; border-radius: 0px 9px 9px 0px;
padding: 11px 11px 11px 7px; padding: 11px 11px 11px 7px;
} }
.workspace-thumbnails {
spacing: 11px;
visible-width: 32px; /* Amount visible before hovering */
}
.workspace-thumbnail-indicator { .workspace-thumbnail-indicator {
border: 4px solid rgba(255,255,255,0.7); border: 4px solid rgba(255,255,255,0.7);
border-radius: 4px; border-radius: 4px;
@ -950,11 +944,19 @@ StScrollBar StButton#vhandle:active {
background-image: url(page-indicator-inactive.svg); background-image: url(page-indicator-inactive.svg);
} }
.page-indicator:hover .page-indicator-icon, .page-indicator:hover .page-indicator-icon {
.page-indicator:checked .page-indicator-icon { background-image: url(page-indicator-hover.svg);
}
.page-indicator:active .page-indicator-icon {
background-image: url(page-indicator-active.svg); background-image: url(page-indicator-active.svg);
} }
.page-indicator:checked .page-indicator-icon,
.page-indicator:checked:active .page-indicator-icon {
background-image: url(page-indicator-checked.svg);
}
.no-frequent-applications-label { .no-frequent-applications-label {
font-size: 18pt; font-size: 18pt;
color: #999999; color: #999999;
@ -1942,6 +1944,7 @@ StScrollBar StButton#vhandle:active {
.end-session-dialog-description:rtl { .end-session-dialog-description:rtl {
padding-right: 17px; padding-right: 17px;
text-align: right;
} }
.end-session-dialog-logout-icon { .end-session-dialog-logout-icon {
@ -1965,10 +1968,19 @@ StScrollBar StButton#vhandle:active {
padding-left: 50px; padding-left: 50px;
} }
.end-session-dialog-session-list,
.end-session-dialog-app-list {
spacing: 1em;
}
.end-session-dialog-list-header { .end-session-dialog-list-header {
font-weight: bold; font-weight: bold;
} }
.end-session-dialog-list-header:rtl {
text-align: right;
}
.end-session-dialog-app-list-item, .end-session-dialog-app-list-item,
.end-session-dialog-session-list-item { .end-session-dialog-session-list-item {
spacing: 1em; spacing: 1em;
@ -2088,6 +2100,10 @@ StScrollBar StButton#vhandle:active {
color: #666666; color: #666666;
} }
.prompt-dialog-description:rtl {
text-align: right;
}
.prompt-dialog-password-box { .prompt-dialog-password-box {
spacing: 1em; spacing: 1em;
padding-bottom: 1em; padding-bottom: 1em;
@ -2268,7 +2284,7 @@ StScrollBar StButton#vhandle:active {
.framed-user-icon { .framed-user-icon {
border: 2px solid #8b8b8b; border: 2px solid #8b8b8b;
border-radius: 5px; border-radius: 3px;
background-size: contain; background-size: contain;
} }
@ -2312,8 +2328,10 @@ StScrollBar StButton#vhandle:active {
} }
.login-dialog-user-list-item { .login-dialog-user-list-item {
border-radius: 10px; border-radius: 5px;
padding: .2em; padding: .2em;
color: #bfbfbf;
text-shadow: black 0px 2px 2px;
} }
.login-dialog-user-list-item:ltr { .login-dialog-user-list-item:ltr {
@ -2324,23 +2342,6 @@ StScrollBar StButton#vhandle:active {
padding-left: 1em; padding-left: 1em;
} }
.login-dialog-user-list-item .login-dialog-user-list-item-name {
font-size: 20pt;
padding-left: 9px;
}
.login-dialog-user-list:expanded .login-dialog-user-list-item {
color: #666666;
}
.login-dialog-user-list-item,
.login-dialog-user-list-item:hover .login-dialog-user-list-item-name,
.login-dialog-user-list:expanded .login-dialog-user-list-item:focus .login-dialog-user-list-item-name,
.login-dialog-user-list:expanded .login-dialog-user-list-item:logged-in {
color: white;
text-shadow: black 0px 2px 2px;
}
.login-dialog-user-list-item:hover { .login-dialog-user-list-item:hover {
background-color: rgba(255,255,255,0.1); background-color: rgba(255,255,255,0.1);
} }
@ -2367,13 +2368,6 @@ StScrollBar StButton#vhandle:active {
background-color: #8b8b8b; background-color: #8b8b8b;
} }
.login-dialog-user-list-item-icon {
border: 2px solid #8b8b8b;
border-radius: 8px;
width: 64px;
height: 64px;
}
.login-dialog-not-listed-label { .login-dialog-not-listed-label {
font-size: 10.5pt; font-size: 10.5pt;
font-weight: bold; font-weight: bold;
@ -2381,6 +2375,10 @@ StScrollBar StButton#vhandle:active {
padding-top: 1em; padding-top: 1em;
} }
.login-dialog-user-selection-box {
padding: 100px 0;
}
.login-dialog-user-selection-box .login-dialog-not-listed-label { .login-dialog-user-selection-box .login-dialog-not-listed-label {
padding-left: 2px; padding-left: 2px;
} }
@ -2420,6 +2418,7 @@ StScrollBar StButton#vhandle:active {
} }
.login-dialog-session-list-button:hover, .login-dialog-session-list-button:hover,
.login-dialog-session-list-button:focus,
.login-dialog-session-list-button:active { .login-dialog-session-list-button:active {
color: white; color: white;
} }
@ -2486,13 +2485,21 @@ StScrollBar StButton#vhandle:active {
} }
.user-widget-label { .user-widget-label {
font-size: 16pt; font-size: 20px;
font-weight: bold; font-weight: bold;
text-align: left; text-align: left;
padding-left: 15px; color:white;
text-shadow: black 0px 4px 3px 0px; text-shadow: black 0px 4px 3px 0px;
} }
.user-widget-label:ltr {
padding-left: 18px;
}
.user-widget-label:rtl {
padding-right: 18px;
}
/* Screen shield */ /* Screen shield */
#panel.lock-screen, #panel.lock-screen,
@ -2505,7 +2512,7 @@ StScrollBar StButton#vhandle:active {
box-shadow: 0px 4px 8px rgba(0,0,0,0.9); box-shadow: 0px 4px 8px rgba(0,0,0,0.9);
} }
#lockDialogGroup { #systemGroup {
background: #2e3436 url(noise-texture.png); background: #2e3436 url(noise-texture.png);
background-repeat: repeat; background-repeat: repeat;
} }
@ -2617,4 +2624,5 @@ StScrollBar StButton#vhandle:active {
.background-menu { .background-menu {
-boxpointer-gap: 4px; -boxpointer-gap: 4px;
-arrow-rise: 0px;
} }

View File

@ -14,7 +14,7 @@
id="svg4703" id="svg4703"
version="1.1" version="1.1"
inkscape:version="0.48.4 r9939" inkscape:version="0.48.4 r9939"
sodipodi:docname="page-indicator-active.svg"> sodipodi:docname="page-indicator-pushed.svg">
<defs <defs
id="defs4705" /> id="defs4705" />
<sodipodi:namedview <sodipodi:namedview
@ -24,18 +24,22 @@
borderopacity="1.0" borderopacity="1.0"
inkscape:pageopacity="0.0" inkscape:pageopacity="0.0"
inkscape:pageshadow="2" inkscape:pageshadow="2"
inkscape:zoom="22.197802" inkscape:zoom="31.392433"
inkscape:cx="2.1522887" inkscape:cx="1.0245308"
inkscape:cy="16.782904" inkscape:cy="13.3715"
inkscape:current-layer="layer1" inkscape:current-layer="layer1"
showgrid="true" showgrid="true"
inkscape:grid-bbox="true" inkscape:grid-bbox="true"
inkscape:document-units="px" inkscape:document-units="px"
inkscape:window-width="1920" inkscape:window-width="2560"
inkscape:window-height="1021" inkscape:window-height="1374"
inkscape:window-x="0" inkscape:window-x="0"
inkscape:window-y="27" inkscape:window-y="27"
inkscape:window-maximized="1" /> inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid6140" />
</sodipodi:namedview>
<metadata <metadata
id="metadata4708"> id="metadata4708">
<rdf:RDF> <rdf:RDF>
@ -44,7 +48,7 @@
<dc:format>image/svg+xml</dc:format> <dc:format>image/svg+xml</dc:format>
<dc:type <dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title /> <dc:title></dc:title>
</cc:Work> </cc:Work>
</rdf:RDF> </rdf:RDF>
</metadata> </metadata>
@ -54,14 +58,14 @@
inkscape:groupmode="layer" inkscape:groupmode="layer"
transform="translate(0,2)"> transform="translate(0,2)">
<path <path
transform="matrix(0.72823872,0,0,0.8336417,-1512.2872,-525.55618)" transform="matrix(0.54617904,0,0,0.62523128,-1131.9904,-392.39214)"
d="m 2099.9808,638.83099 c 0,5.29998 -4.9184,9.59645 -10.9854,9.59645 -6.0671,0 -10.9854,-4.29647 -10.9854,-9.59645 0,-5.29997 4.9183,-9.59645 10.9854,-9.59645 6.067,0 10.9854,4.29648 10.9854,9.59645 z" d="m 2099.9808,638.83099 a 10.985409,9.5964489 0 1 1 -21.9708,0 10.985409,9.5964489 0 1 1 21.9708,0 z"
sodipodi:ry="9.5964489" sodipodi:ry="9.5964489"
sodipodi:rx="10.985409" sodipodi:rx="10.985409"
sodipodi:cy="638.83099" sodipodi:cy="638.83099"
sodipodi:cx="2088.9954" sodipodi:cx="2088.9954"
id="path4711" id="path4711"
style="fill:#fdffff;fill-opacity:0.94117647;stroke:none" style="fill:#fdffff;fill-opacity:1;stroke:none"
sodipodi:type="arc" /> sodipodi:type="arc" />
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="18"
height="18"
id="svg4703"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="page-indicator-active.svg">
<defs
id="defs4705" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="22.197802"
inkscape:cx="2.1522887"
inkscape:cy="16.782904"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="1920"
inkscape:window-height="1021"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1" />
<metadata
id="metadata4708">
<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
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer"
transform="translate(0,2)">
<path
transform="matrix(0.72823872,0,0,0.8336417,-1512.2872,-525.55618)"
d="m 2099.9808,638.83099 c 0,5.29998 -4.9184,9.59645 -10.9854,9.59645 -6.0671,0 -10.9854,-4.29647 -10.9854,-9.59645 0,-5.29997 4.9183,-9.59645 10.9854,-9.59645 6.067,0 10.9854,4.29648 10.9854,9.59645 z"
sodipodi:ry="9.5964489"
sodipodi:rx="10.985409"
sodipodi:cy="638.83099"
sodipodi:cx="2088.9954"
id="path4711"
style="fill:#fdffff;fill-opacity:0.94117647;stroke:none"
sodipodi:type="arc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="18"
height="18"
id="svg5266"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="page-indicator-inactive.svg">
<defs
id="defs5268" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="11.313709"
inkscape:cx="-2.307566"
inkscape:cy="17.859535"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="2560"
inkscape:window-height="1374"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1" />
<metadata
id="metadata5271">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer"
transform="translate(0,2)">
<path
sodipodi:type="arc"
style="fill:none;fill-opacity:0;stroke:#ffffff;stroke-width:2.93356276000000005;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path5274"
sodipodi:cx="2088.9954"
sodipodi:cy="638.83099"
sodipodi:rx="10.985409"
sodipodi:ry="9.5964489"
d="m 2099.9808,638.83099 c 0,5.29998 -4.9184,9.59645 -10.9854,9.59645 -6.0671,0 -10.9854,-4.29647 -10.9854,-9.59645 0,-5.29997 4.9183,-9.59645 10.9854,-9.59645 6.067,0 10.9854,4.29648 10.9854,9.59645 z"
transform="matrix(0.63720887,0,0,0.72943648,-1322.1264,-458.98661)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -25,14 +25,14 @@
inkscape:pageopacity="0" inkscape:pageopacity="0"
inkscape:pageshadow="2" inkscape:pageshadow="2"
inkscape:zoom="11.313709" inkscape:zoom="11.313709"
inkscape:cx="13.381365" inkscape:cx="-2.307566"
inkscape:cy="17.859535" inkscape:cy="17.859535"
inkscape:current-layer="layer1" inkscape:current-layer="layer1"
showgrid="true" showgrid="true"
inkscape:grid-bbox="true" inkscape:grid-bbox="true"
inkscape:document-units="px" inkscape:document-units="px"
inkscape:window-width="1920" inkscape:window-width="2560"
inkscape:window-height="1021" inkscape:window-height="1374"
inkscape:window-x="0" inkscape:window-x="0"
inkscape:window-y="27" inkscape:window-y="27"
inkscape:window-maximized="1" /> inkscape:window-maximized="1" />
@ -55,7 +55,7 @@
transform="translate(0,2)"> transform="translate(0,2)">
<path <path
sodipodi:type="arc" sodipodi:type="arc"
style="fill:#ffffff;fill-opacity:0;stroke:#ffffff;stroke-width:2.93356276;stroke-miterlimit:4;stroke-opacity:0.39215686;stroke-dasharray:none" style="fill:none;fill-opacity:0;stroke:#ffffff;stroke-width:2.93356276000000005;stroke-miterlimit:4;stroke-opacity:0.39215686000000000;stroke-dasharray:none"
id="path5274" id="path5274"
sodipodi:cx="2088.9954" sodipodi:cx="2088.9954"
sodipodi:cy="638.83099" sodipodi:cy="638.83099"

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@ -112,7 +112,7 @@ expand_content_files=
# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) # e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) # e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
GTKDOC_CFLAGS=$(GNOME_SHELL_CFLAGS) GTKDOC_CFLAGS=$(GNOME_SHELL_CFLAGS)
GTKDOC_LIBS=$(GNOME_SHELL_LIBS) $(BLUETOOTH_LIBS) $(top_builddir)/src/libgnome-shell-menu.la $(top_builddir)/src/libgnome-shell-base.la $(top_builddir)/src/libgnome-shell.la GTKDOC_LIBS=$(GNOME_SHELL_LIBS) $(top_builddir)/src/libgnome-shell-menu.la $(top_builddir)/src/libgnome-shell-base.la $(top_builddir)/src/libgnome-shell.la
# This includes the standard gtk-doc make rules, copied by gtkdocize. # This includes the standard gtk-doc make rules, copied by gtkdocize.
include $(top_srcdir)/gtk-doc.make include $(top_srcdir)/gtk-doc.make

View File

@ -1,7 +1,5 @@
NULL = NULL =
BUILT_SOURCES =
EXTRA_DIST = misc/config.js.in
CLEANFILES = misc/config.js
misc/config.js: misc/config.js.in Makefile misc/config.js: misc/config.js.in Makefile
[ -d $(@D) ] || $(mkdir_p) $(@D) ; \ [ -d $(@D) ] || $(mkdir_p) $(@D) ; \
@ -14,111 +12,26 @@ misc/config.js: misc/config.js.in Makefile
-e "s|[@]sysconfdir@|$(sysconfdir)|g" \ -e "s|[@]sysconfdir@|$(sysconfdir)|g" \
$< > $@ $< > $@
jsdir = $(pkgdatadir)/js js_resource_files = $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(srcdir) --generate-dependencies $(srcdir)/js-resources.gresource.xml)
js-resources.h: js-resources.gresource.xml $(js_resource_files) misc/config.js
$(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) --sourcedir=$(builddir) --generate --c-name shell_js_resources $<
js-resources.c: js-resources.gresource.xml $(js_resource_files) misc/config.js
$(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) --sourcedir=$(builddir) --generate --c-name shell_js_resources $<
nobase_dist_js_DATA = \ js_built_sources = js-resources.c js-resources.h
gdm/authPrompt.js \
gdm/batch.js \ BUILT_SOURCES += $(js_built_sources)
gdm/fingerprint.js \
gdm/loginDialog.js \ all-local: $(js_built_sources)
gdm/realmd.js \
gdm/util.js \ js_resource_dist_files = $(filter-out misc/config.js, $(js_resource_files))
extensionPrefs/main.js \
misc/config.js \ EXTRA_DIST = \
misc/extensionUtils.js \ $(js_resource_dist_files) \
misc/fileUtils.js \ js-resources.gresource.xml \
misc/gnomeSession.js \ misc/config.js.in \
misc/hash.js \ $(NULL)
misc/history.js \
misc/jsParse.js \ CLEANFILES = \
misc/loginManager.js \ $(js_built_sources) \
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 \
ui/ctrlAltTab.js \
ui/dash.js \
ui/dateMenu.js \
ui/dnd.js \
ui/endSessionDialog.js \
ui/extensionSystem.js \
ui/extensionDownloader.js \
ui/environment.js \
ui/focusCaretTracker.js\
ui/ibusCandidatePopup.js\
ui/grabHelper.js \
ui/iconGrid.js \
ui/keyboard.js \
ui/layout.js \
ui/lightbox.js \
ui/lookingGlass.js \
ui/magnifier.js \
ui/magnifierDBus.js \
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 \
ui/searchDisplay.js \
ui/shellDBus.js \
ui/status/accessibility.js \
ui/status/brightness.js \
ui/status/keyboard.js \
ui/status/network.js \
ui/status/power.js \
ui/status/rfkill.js \
ui/status/volume.js \
ui/status/bluetooth.js \
ui/status/screencast.js \
ui/status/system.js \
ui/switcherPopup.js \
ui/tweener.js \
ui/unlockDialog.js \
ui/userWidget.js \
ui/viewSelector.js \
ui/wanda.js \
ui/windowAttentionHandler.js \
ui/windowManager.js \
ui/workspace.js \
ui/workspaceThumbnail.js \
ui/workspacesView.js \
ui/workspaceSwitcherPopup.js \
ui/xdndHandler.js \
ui/components/__init__.js \
ui/components/autorunManager.js \
ui/components/automountManager.js \
ui/components/networkAgent.js \
ui/components/polkitAgent.js \
ui/components/telepathyClient.js \
ui/components/keyring.js \
$(NULL) $(NULL)

View File

@ -13,13 +13,15 @@ const _ = Gettext.gettext;
const Config = imports.misc.config; const Config = imports.misc.config;
const ExtensionUtils = imports.misc.extensionUtils; const ExtensionUtils = imports.misc.extensionUtils;
const GnomeShellIface = <interface name="org.gnome.Shell.Extensions"> const GnomeShellIface = '<node> \
<signal name="ExtensionStatusChanged"> <interface name="org.gnome.Shell.Extensions"> \
<arg type="s" name="uuid"/> <signal name="ExtensionStatusChanged"> \
<arg type="i" name="state"/> <arg type="s" name="uuid"/> \
<arg type="s" name="error"/> <arg type="i" name="state"/> \
</signal> <arg type="s" name="error"/> \
</interface>; </signal> \
</interface> \
</node>';
const GnomeShellProxy = Gio.DBusProxy.makeProxyWrapper(GnomeShellIface); const GnomeShellProxy = Gio.DBusProxy.makeProxyWrapper(GnomeShellIface);
@ -204,11 +206,11 @@ const Application = new Lang.Class({
_scanExtensions: function() { _scanExtensions: function() {
let finder = new ExtensionUtils.ExtensionFinder(); let finder = new ExtensionUtils.ExtensionFinder();
finder.connect('extension-found', Lang.bind(this, this._extensionFound)); finder.connect('extension-found', Lang.bind(this, this._extensionFound));
finder.connect('extensions-loaded', Lang.bind(this, this._extensionsLoaded));
finder.scanExtensions(); finder.scanExtensions();
this._extensionsLoaded();
}, },
_extensionFound: function(signals, extension) { _extensionFound: function(finder, extension) {
let iter = this._model.append(); let iter = this._model.append();
this._model.set(iter, [0, 1], [extension.uuid, extension.metadata.name]); this._model.set(iter, [0, 1], [extension.uuid, extension.metadata.name]);
this._extensionIters[extension.uuid] = iter; this._extensionIters[extension.uuid] = iter;

View File

@ -59,6 +59,7 @@ const AuthPrompt = new Lang.Class({
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete)); this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
this._userVerifier.connect('reset', Lang.bind(this, this._onReset)); this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
this._userVerifier.connect('smartcard-status-changed', Lang.bind(this, this._onSmartcardStatusChanged)); this._userVerifier.connect('smartcard-status-changed', Lang.bind(this, this._onSmartcardStatusChanged));
this._userVerifier.connect('ovirt-user-authenticated', Lang.bind(this, this._onOVirtUserAuthenticated));
this.smartcardDetected = this._userVerifier.smartcardDetected; this.smartcardDetected = this._userVerifier.smartcardDetected;
this.connect('next', Lang.bind(this, function() { this.connect('next', Lang.bind(this, function() {
@ -79,6 +80,7 @@ const AuthPrompt = new Lang.Class({
if (event.get_key_symbol() == Clutter.KEY_Escape) { if (event.get_key_symbol() == Clutter.KEY_Escape) {
this.cancel(); this.cancel();
} }
return Clutter.EVENT_PROPAGATE;
})); }));
this._userWell = new St.Bin({ x_fill: true, this._userWell = new St.Bin({ x_fill: true,
@ -92,7 +94,7 @@ const AuthPrompt = new Lang.Class({
this.actor.add(this._label, this.actor.add(this._label,
{ expand: true, { expand: true,
x_fill: true, x_fill: false,
y_fill: true, y_fill: true,
x_align: St.Align.START }); x_align: St.Align.START });
this._entry = new St.Entry({ style_class: 'login-dialog-prompt-entry', this._entry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
@ -110,7 +112,7 @@ const AuthPrompt = new Lang.Class({
this._message = new St.Label({ opacity: 0, this._message = new St.Label({ opacity: 0,
styleClass: 'login-dialog-message' }); styleClass: 'login-dialog-message' });
this._message.clutter_text.line_wrap = true; this._message.clutter_text.line_wrap = true;
this.actor.add(this._message, { x_fill: true, y_align: St.Align.START }); this.actor.add(this._message, { x_fill: false, x_align: St.Align.START, y_align: St.Align.START });
this._buttonBox = new St.BoxLayout({ style_class: 'login-dialog-button-box', this._buttonBox = new St.BoxLayout({ style_class: 'login-dialog-button-box',
vertical: false }); vertical: false });
@ -219,6 +221,11 @@ const AuthPrompt = new Lang.Class({
this.emit('prompted'); this.emit('prompted');
}, },
_onOVirtUserAuthenticated: function() {
if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED)
this.reset();
},
_onSmartcardStatusChanged: function() { _onSmartcardStatusChanged: function() {
this.smartcardDetected = this._userVerifier.smartcardDetected; this.smartcardDetected = this._userVerifier.smartcardDetected;
@ -257,10 +264,8 @@ const AuthPrompt = new Lang.Class({
}, },
_onReset: function() { _onReset: function() {
if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED) { this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING; this.reset();
this.reset();
}
}, },
addActorToDefaultButtonWell: function(actor) { addActorToDefaultButtonWell: function(actor) {
@ -444,10 +449,11 @@ const AuthPrompt = new Lang.Class({
// The user is constant at the unlock screen, so it will immediately // The user is constant at the unlock screen, so it will immediately
// respond to the request with the username // respond to the request with the username
beginRequestType = BeginRequestType.PROVIDE_USERNAME; beginRequestType = BeginRequestType.PROVIDE_USERNAME;
} else if (this.smartcardDetected && } else if (this._userVerifier.serviceIsForeground(GdmUtil.OVIRT_SERVICE_NAME) ||
this._userVerifier.serviceIsForeground(GdmUtil.SMARTCARD_SERVICE_NAME)) { (this.smartcardDetected &&
this._userVerifier.serviceIsForeground(GdmUtil.SMARTCARD_SERVICE_NAME))) {
// We don't need to know the username if the user preempted the login screen // We don't need to know the username if the user preempted the login screen
// with a smartcard. // with a smartcard or with preauthenticated oVirt credentials
beginRequestType = BeginRequestType.DONT_PROVIDE_USERNAME; beginRequestType = BeginRequestType.DONT_PROVIDE_USERNAME;
} else { } else {
// In all other cases, we should get the username up front. // In all other cases, we should get the username up front.

View File

@ -13,9 +13,7 @@
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, see <http://www.gnu.org/licenses/>.
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/ */
const Lang = imports.lang; const Lang = imports.lang;

View File

@ -5,11 +5,13 @@ const Lang = imports.lang;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Signals = imports.signals; const Signals = imports.signals;
const FprintManagerIface = <interface name='net.reactivated.Fprint.Manager'> const FprintManagerIface = '<node> \
<method name='GetDefaultDevice'> <interface name="net.reactivated.Fprint.Manager"> \
<arg type='o' direction='out' /> <method name="GetDefaultDevice"> \
</method> <arg type="o" direction="out" /> \
</interface>; </method> \
</interface> \
</node>';
const FprintManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(FprintManagerIface); const FprintManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(FprintManagerIface);

View File

@ -13,9 +13,7 @@
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, see <http://www.gnu.org/licenses/>.
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/ */
const AccountsService = imports.gi.AccountsService; const AccountsService = imports.gi.AccountsService;
@ -59,7 +57,7 @@ const UserListItem = new Lang.Class({
this._userChangedId = this.user.connect('changed', this._userChangedId = this.user.connect('changed',
Lang.bind(this, this._onUserChanged)); Lang.bind(this, this._onUserChanged));
let layout = new St.BoxLayout({ vertical: false }); let layout = new St.BoxLayout({ vertical: true });
this.actor = new St.Button({ style_class: 'login-dialog-user-list-item', this.actor = new St.Button({ style_class: 'login-dialog-user-list-item',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE, button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
can_focus: true, can_focus: true,
@ -68,39 +66,18 @@ const UserListItem = new Lang.Class({
x_align: St.Align.START, x_align: St.Align.START,
x_fill: true }); x_fill: true });
this._userAvatar = new UserWidget.Avatar(this.user, this._userWidget = new UserWidget.UserWidget(this.user);
{ styleClass: 'login-dialog-user-list-item-icon' }); layout.add(this._userWidget.actor);
layout.add(this._userAvatar.actor);
let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box',
vertical: true });
layout.add(textLayout, { expand: true });
this._nameLabel = new St.Label({ style_class: 'login-dialog-user-list-item-name' });
this.actor.label_actor = this._nameLabel;
textLayout.add(this._nameLabel,
{ y_fill: false,
y_align: St.Align.MIDDLE,
expand: true });
this._timedLoginIndicator = new St.Bin({ style_class: 'login-dialog-timed-login-indicator', this._timedLoginIndicator = new St.Bin({ style_class: 'login-dialog-timed-login-indicator',
scale_x: 0 }); scale_x: 0 });
textLayout.add(this._timedLoginIndicator, layout.add(this._timedLoginIndicator);
{ x_fill: true,
x_align: St.Align.MIDDLE,
y_fill: false,
y_align: St.Align.END });
this.actor.connect('clicked', Lang.bind(this, this._onClicked)); this.actor.connect('clicked', Lang.bind(this, this._onClicked));
this._onUserChanged(); this._onUserChanged();
}, },
_onUserChanged: function() { _onUserChanged: function() {
this._nameLabel.set_text(this.user.get_real_name());
this._userAvatar.update();
this._updateLoggedIn();
},
syncStyleClasses: function() {
this._updateLoggedIn(); this._updateLoggedIn();
}, },
@ -189,7 +166,6 @@ const UserList = new Lang.Class({
for (let userName in this._items) { for (let userName in this._items) {
let item = this._items[userName]; let item = this._items[userName];
item.actor.sync_hover(); item.actor.sync_hover();
item.syncStyleClasses();
} }
}, },
@ -298,7 +274,7 @@ const SessionMenuButton = new Lang.Class({
this.actor = new St.Bin({ child: this._button }); this.actor = new St.Bin({ child: this._button });
this._menu = new PopupMenu.PopupMenu(this._button, 0, St.Side.TOP); this._menu = new PopupMenu.PopupMenu(this._button, 0, St.Side.TOP);
Main.uiGroup.add_actor(this._menu.actor); Main.layoutManager.menuGroup.add_actor(this._menu.actor);
this._menu.actor.hide(); this._menu.actor.hide();
this._menu.connect('open-state-changed', this._menu.connect('open-state-changed',
@ -478,18 +454,6 @@ const LoginDialog = new Lang.Class({
this.actor.add_child(this._logoBin); this.actor.add_child(this._logoBin);
this._updateLogo(); this._updateLogo();
if (!this._userManager.is_loaded)
this._userManagerLoadedId = this._userManager.connect('notify::is-loaded',
Lang.bind(this, function() {
if (this._userManager.is_loaded) {
this._loadUserList();
this._userManager.disconnect(this._userManagerLoadedId);
this._userManagerLoadedId = 0;
}
}));
else
this._loadUserList();
this._userList.connect('activate', this._userList.connect('activate',
Lang.bind(this, function(userList, item) { Lang.bind(this, function(userList, item) {
this._onUserListActivated(item); this._onUserListActivated(item);
@ -505,6 +469,18 @@ const LoginDialog = new Lang.Class({
this._sessionMenuButton.actor.show(); this._sessionMenuButton.actor.show();
this._authPrompt.addActorToDefaultButtonWell(this._sessionMenuButton.actor); this._authPrompt.addActorToDefaultButtonWell(this._sessionMenuButton.actor);
if (!this._userManager.is_loaded)
this._userManagerLoadedId = this._userManager.connect('notify::is-loaded',
Lang.bind(this, function() {
if (this._userManager.is_loaded) {
this._loadUserList();
this._userManager.disconnect(this._userManagerLoadedId);
this._userManagerLoadedId = 0;
}
}));
else
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, this._loadUserList));
}, },
_updateDisableUserList: function() { _updateDisableUserList: function() {
@ -619,7 +595,7 @@ const LoginDialog = new Lang.Class({
// Translators: this message is shown below the username entry field // Translators: this message is shown below the username entry field
// to clue the user in on how to login to the local network realm // to clue the user in on how to login to the local network realm
this._authPrompt.setMessage(_("(e.g., user or %s)").format(hint), AuthPrompt.MessageType.HINT); this._authPrompt.setMessage(_("(e.g., user or %s)").format(hint), GdmUtil.MessageType.HINT);
}, },
_askForUsernameAndBeginVerification: function() { _askForUsernameAndBeginVerification: function() {
@ -666,7 +642,7 @@ const LoginDialog = new Lang.Class({
onComplete: function() { onComplete: function() {
Mainloop.idle_add(Lang.bind(this, function() { Mainloop.idle_add(Lang.bind(this, function() {
this._greeter.call_start_session_when_ready_sync(serviceName, true, null); this._greeter.call_start_session_when_ready_sync(serviceName, true, null);
return false; return GLib.SOURCE_REMOVE;
})); }));
}, },
onCompleteScope: this }); onCompleteScope: this });
@ -721,6 +697,7 @@ const LoginDialog = new Lang.Class({
function() { function() {
this._timedLoginAnimationTime -= _TIMED_LOGIN_IDLE_THRESHOLD; this._timedLoginAnimationTime -= _TIMED_LOGIN_IDLE_THRESHOLD;
hold.release(); hold.release();
return GLib.SOURCE_REMOVE;
}); });
return hold; return hold;
}, },
@ -785,7 +762,7 @@ const LoginDialog = new Lang.Class({
global.stage.connect('captured-event', global.stage.connect('captured-event',
Lang.bind(this, function(actor, event) { Lang.bind(this, function(actor, event) {
if (this._timedLoginDelay == undefined) if (this._timedLoginDelay == undefined)
return false; return Clutter.EVENT_PROPAGATE;
if (event.type() == Clutter.EventType.KEY_PRESS || if (event.type() == Clutter.EventType.KEY_PRESS ||
event.type() == Clutter.EventType.BUTTON_PRESS) { event.type() == Clutter.EventType.BUTTON_PRESS) {
@ -798,7 +775,7 @@ const LoginDialog = new Lang.Class({
this._resetTimedLogin(); this._resetTimedLogin();
} }
return false; return Clutter.EVENT_PROPAGATE;
})); }));
}, },
@ -907,6 +884,10 @@ const LoginDialog = new Lang.Class({
Main.ctrlAltTabManager.removeGroup(this.dialogLayout); Main.ctrlAltTabManager.removeGroup(this.dialogLayout);
}, },
cancel: function() {
this._authPrompt.cancel();
},
addCharacter: function(unichar) { addCharacter: function(unichar) {
this._authPrompt.addCharacter(unichar); this._authPrompt.addCharacter(unichar);
}, },

64
js/gdm/oVirt.js Normal file
View File

@ -0,0 +1,64 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Signals = imports.signals;
const OVirtCredentialsIface = '<node> \
<interface name="org.ovirt.vdsm.Credentials"> \
<signal name="UserAuthenticated"> \
<arg type="s" name="token"/> \
</signal> \
</interface> \
</node>';
const OVirtCredentialsInfo = Gio.DBusInterfaceInfo.new_for_xml(OVirtCredentialsIface);
let _oVirtCredentialsManager = null;
function OVirtCredentials() {
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.system,
g_interface_name: OVirtCredentialsInfo.name,
g_interface_info: OVirtCredentialsInfo,
g_name: 'org.ovirt.vdsm.Credentials',
g_object_path: '/org/ovirt/vdsm/Credentials',
g_flags: (Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
self.init(null);
return self;
}
const OVirtCredentialsManager = new Lang.Class({
Name: 'OVirtCredentialsManager',
_init: function() {
this._token = null;
this._credentials = new OVirtCredentials();
this._credentials.connectSignal('UserAuthenticated',
Lang.bind(this, this._onUserAuthenticated));
},
_onUserAuthenticated: function(proxy, sender, [token]) {
this._token = token;
this.emit('user-authenticated', token);
},
hasToken: function() {
return this._token != null;
},
getToken: function() {
return this._token;
},
resetToken: function() {
this._token = null;
}
});
Signals.addSignalMethods(OVirtCredentialsManager.prototype);
function getOVirtCredentialsManager() {
if (!_oVirtCredentialsManager)
_oVirtCredentialsManager = new OVirtCredentialsManager();
return _oVirtCredentialsManager;
}

View File

@ -5,52 +5,58 @@ const Lang = imports.lang;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Signals = imports.signals; const Signals = imports.signals;
const ProviderIface = <interface name='org.freedesktop.realmd.Provider'> const ProviderIface = '<node> \
<property name="Name" type="s" access="read"/> <interface name="org.freedesktop.realmd.Provider"> \
<property name="Version" type="s" access="read"/> <property name="Name" type="s" access="read"/> \
<property name="Realms" type="ao" access="read"/> <property name="Version" type="s" access="read"/> \
<method name="Discover"> <property name="Realms" type="ao" access="read"/> \
<arg name="string" type="s" direction="in"/> <method name="Discover"> \
<arg name="options" type="a{sv}" direction="in"/> <arg name="string" type="s" direction="in"/> \
<arg name="relevance" type="i" direction="out"/> <arg name="options" type="a{sv}" direction="in"/> \
<arg name="realm" type="ao" direction="out"/> <arg name="relevance" type="i" direction="out"/> \
</method> <arg name="realm" type="ao" direction="out"/> \
</interface>; </method> \
</interface> \
</node>';
const Provider = Gio.DBusProxy.makeProxyWrapper(ProviderIface); const Provider = Gio.DBusProxy.makeProxyWrapper(ProviderIface);
const ServiceIface = <interface name="org.freedesktop.realmd.Service"> const ServiceIface = '<node> \
<method name="Cancel"> <interface name="org.freedesktop.realmd.Service"> \
<arg name="operation" type="s" direction="in"/> <method name="Cancel"> \
</method> <arg name="operation" type="s" direction="in"/> \
<method name="Release" /> </method> \
<method name="SetLocale"> <method name="Release" /> \
<arg name="locale" type="s" direction="in"/> <method name="SetLocale"> \
</method> <arg name="locale" type="s" direction="in"/> \
<signal name="Diagnostics"> </method> \
<arg name="data" type="s"/> <signal name="Diagnostics"> \
<arg name="operation" type="s"/> <arg name="data" type="s"/> \
</signal> <arg name="operation" type="s"/> \
</interface>; </signal> \
</interface> \
</node>';
const Service = Gio.DBusProxy.makeProxyWrapper(ServiceIface); const Service = Gio.DBusProxy.makeProxyWrapper(ServiceIface);
const RealmIface = <interface name="org.freedesktop.realmd.Realm"> const RealmIface = '<node> \
<property name="Name" type="s" access="read"/> <interface name="org.freedesktop.realmd.Realm"> \
<property name="Configured" type="s" access="read"/> <property name="Name" type="s" access="read"/> \
<property name="Details" type="a(ss)" access="read"/> <property name="Configured" type="s" access="read"/> \
<property name="LoginFormats" type="as" access="read"/> <property name="Details" type="a(ss)" access="read"/> \
<property name="LoginPolicy" type="s" access="read"/> <property name="LoginFormats" type="as" access="read"/> \
<property name="PermittedLogins" type="as" access="read"/> <property name="LoginPolicy" type="s" access="read"/> \
<property name="SupportedInterfaces" type="as" access="read"/> <property name="PermittedLogins" type="as" access="read"/> \
<method name="ChangeLoginPolicy"> <property name="SupportedInterfaces" type="as" access="read"/> \
<arg name="login_policy" type="s" direction="in"/> <method name="ChangeLoginPolicy"> \
<arg name="permitted_add" type="as" direction="in"/> <arg name="login_policy" type="s" direction="in"/> \
<arg name="permitted_remove" type="as" direction="in"/> <arg name="permitted_add" type="as" direction="in"/> \
<arg name="options" type="a{sv}" direction="in"/> <arg name="permitted_remove" type="as" direction="in"/> \
</method> <arg name="options" type="a{sv}" direction="in"/> \
<method name="Deconfigure"> </method> \
<arg name="options" type="a{sv}" direction="in"/> <method name="Deconfigure"> \
</method> <arg name="options" type="a{sv}" direction="in"/> \
</interface>; </method> \
</interface> \
</node>';
const Realm = Gio.DBusProxy.makeProxyWrapper(RealmIface); const Realm = Gio.DBusProxy.makeProxyWrapper(RealmIface);
const Manager = new Lang.Class({ const Manager = new Lang.Class({

View File

@ -10,6 +10,7 @@ const St = imports.gi.St;
const Batch = imports.gdm.batch; const Batch = imports.gdm.batch;
const Fprint = imports.gdm.fingerprint; const Fprint = imports.gdm.fingerprint;
const OVirt = imports.gdm.oVirt;
const Main = imports.ui.main; const Main = imports.ui.main;
const Params = imports.misc.params; const Params = imports.misc.params;
const ShellEntry = imports.ui.shellEntry; const ShellEntry = imports.ui.shellEntry;
@ -19,6 +20,7 @@ const Tweener = imports.ui.tweener;
const PASSWORD_SERVICE_NAME = 'gdm-password'; const PASSWORD_SERVICE_NAME = 'gdm-password';
const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint'; const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
const SMARTCARD_SERVICE_NAME = 'gdm-smartcard'; const SMARTCARD_SERVICE_NAME = 'gdm-smartcard';
const OVIRT_SERVICE_NAME = 'gdm-ovirtcred';
const FADE_ANIMATION_TIME = 0.16; const FADE_ANIMATION_TIME = 0.16;
const CLONE_FADE_ANIMATION_TIME = 0.25; const CLONE_FADE_ANIMATION_TIME = 0.25;
@ -151,6 +153,14 @@ const ShellUserVerifier = new Lang.Class({
this.reauthenticating = false; this.reauthenticating = false;
this._failCounter = 0; this._failCounter = 0;
this._oVirtCredentialsManager = OVirt.getOVirtCredentialsManager();
if (this._oVirtCredentialsManager.hasToken())
this._oVirtUserAuthenticated(this._oVirtCredentialsManager.getToken());
this._oVirtCredentialsManager.connect('user-authenticated',
Lang.bind(this, this._oVirtUserAuthenticated));
}, },
begin: function(userName, hold) { begin: function(userName, hold) {
@ -240,6 +250,7 @@ const ShellUserVerifier = new Lang.Class({
Lang.bind(this, function() { Lang.bind(this, function() {
this._messageQueueTimeoutId = 0; this._messageQueueTimeoutId = 0;
this._queueMessageTimeout(); this._queueMessageTimeout();
return GLib.SOURCE_REMOVE;
})); }));
}, },
@ -277,6 +288,11 @@ const ShellUserVerifier = new Lang.Class({
})); }));
}, },
_oVirtUserAuthenticated: function(token) {
this._preemptingService = OVIRT_SERVICE_NAME;
this.emit('ovirt-user-authenticated');
},
_checkForSmartcard: function() { _checkForSmartcard: function() {
let smartcardDetected; let smartcardDetected;
@ -455,6 +471,12 @@ const ShellUserVerifier = new Lang.Class({
if (!this.serviceIsForeground(serviceName)) if (!this.serviceIsForeground(serviceName))
return; return;
if (serviceName == OVIRT_SERVICE_NAME) {
// The only question asked by this service is "Token?"
this.answerQuery(serviceName, this._oVirtCredentialsManager.getToken());
return;
}
this.emit('ask-question', serviceName, secretQuestion, '\u25cf'); this.emit('ask-question', serviceName, secretQuestion, '\u25cf');
}, },
@ -515,6 +537,16 @@ const ShellUserVerifier = new Lang.Class({
}, },
_onConversationStopped: function(client, serviceName) { _onConversationStopped: function(client, serviceName) {
// If the login failed with the preauthenticated oVirt credentials
// then discard the credentials and revert to default authentication
// mechanism.
if (this.serviceIsForeground(OVIRT_SERVICE_NAME)) {
this._oVirtCredentialsManager.resetToken();
this._preemptingService = null;
this._verificationFailed(false);
return;
}
// if the password service fails, then cancel everything. // if the password service fails, then cancel everything.
// But if, e.g., fingerprint fails, still give // But if, e.g., fingerprint fails, still give
// password authentication a chance to succeed // password authentication a chance to succeed

View File

@ -0,0 +1,114 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/org/gnome/shell">
<file>gdm/authPrompt.js</file>
<file>gdm/batch.js</file>
<file>gdm/fingerprint.js</file>
<file>gdm/loginDialog.js</file>
<file>gdm/oVirt.js</file>
<file>gdm/realmd.js</file>
<file>gdm/util.js</file>
<file>extensionPrefs/main.js</file>
<file>misc/config.js</file>
<file>misc/extensionUtils.js</file>
<file>misc/fileUtils.js</file>
<file>misc/gnomeSession.js</file>
<file>misc/history.js</file>
<file>misc/jsParse.js</file>
<file>misc/loginManager.js</file>
<file>misc/modemManager.js</file>
<file>misc/objectManager.js</file>
<file>misc/params.js</file>
<file>misc/smartcardManager.js</file>
<file>misc/util.js</file>
<file>perf/core.js</file>
<file>ui/altTab.js</file>
<file>ui/animation.js</file>
<file>ui/appDisplay.js</file>
<file>ui/appFavorites.js</file>
<file>ui/backgroundMenu.js</file>
<file>ui/background.js</file>
<file>ui/boxpointer.js</file>
<file>ui/calendar.js</file>
<file>ui/checkBox.js</file>
<file>ui/ctrlAltTab.js</file>
<file>ui/dash.js</file>
<file>ui/dateMenu.js</file>
<file>ui/dnd.js</file>
<file>ui/endSessionDialog.js</file>
<file>ui/environment.js</file>
<file>ui/extensionDownloader.js</file>
<file>ui/extensionSystem.js</file>
<file>ui/focusCaretTracker.js</file>
<file>ui/grabHelper.js</file>
<file>ui/ibusCandidatePopup.js</file>
<file>ui/iconGrid.js</file>
<file>ui/keyboard.js</file>
<file>ui/layout.js</file>
<file>ui/lightbox.js</file>
<file>ui/lookingGlass.js</file>
<file>ui/magnifier.js</file>
<file>ui/magnifierDBus.js</file>
<file>ui/main.js</file>
<file>ui/messageTray.js</file>
<file>ui/modalDialog.js</file>
<file>ui/notificationDaemon.js</file>
<file>ui/osdWindow.js</file>
<file>ui/overview.js</file>
<file>ui/overviewControls.js</file>
<file>ui/panel.js</file>
<file>ui/panelMenu.js</file>
<file>ui/pointerWatcher.js</file>
<file>ui/popupMenu.js</file>
<file>ui/remoteMenu.js</file>
<file>ui/remoteSearch.js</file>
<file>ui/runDialog.js</file>
<file>ui/screenShield.js</file>
<file>ui/screencast.js</file>
<file>ui/screenshot.js</file>
<file>ui/scripting.js</file>
<file>ui/search.js</file>
<file>ui/separator.js</file>
<file>ui/sessionMode.js</file>
<file>ui/shellDBus.js</file>
<file>ui/shellEntry.js</file>
<file>ui/shellMountOperation.js</file>
<file>ui/slider.js</file>
<file>ui/switcherPopup.js</file>
<file>ui/tweener.js</file>
<file>ui/unlockDialog.js</file>
<file>ui/userWidget.js</file>
<file>ui/viewSelector.js</file>
<file>ui/windowAttentionHandler.js</file>
<file>ui/windowManager.js</file>
<file>ui/workspace.js</file>
<file>ui/workspaceSwitcherPopup.js</file>
<file>ui/workspaceThumbnail.js</file>
<file>ui/workspacesView.js</file>
<file>ui/xdndHandler.js</file>
<file>ui/components/__init__.js</file>
<file>ui/components/autorunManager.js</file>
<file>ui/components/automountManager.js</file>
<file>ui/components/networkAgent.js</file>
<file>ui/components/polkitAgent.js</file>
<file>ui/components/telepathyClient.js</file>
<file>ui/components/keyring.js</file>
<file>ui/status/accessibility.js</file>
<file>ui/status/brightness.js</file>
<file>ui/status/location.js</file>
<file>ui/status/keyboard.js</file>
<file>ui/status/network.js</file>
<file>ui/status/power.js</file>
<file>ui/status/rfkill.js</file>
<file>ui/status/volume.js</file>
<file>ui/status/bluetooth.js</file>
<file>ui/status/screencast.js</file>
<file>ui/status/system.js</file>
</gresource>
</gresources>

View File

@ -174,17 +174,9 @@ const ExtensionFinder = new Lang.Class({
this.emit('extension-found', extension); this.emit('extension-found', extension);
}, },
_extensionsLoaded: function() {
this.emit('extensions-loaded');
},
scanExtensions: function() { scanExtensions: function() {
let perUserDir = Gio.File.new_for_path(global.userdatadir); let perUserDir = Gio.File.new_for_path(global.userdatadir);
FileUtils.collectFromDatadirsAsync('extensions', FileUtils.collectFromDatadirs('extensions', true, Lang.bind(this, this._loadExtension, perUserDir));
{ processFile: Lang.bind(this, this._loadExtension),
loadedCallback: Lang.bind(this, this._extensionsLoaded),
includeUserDir: true,
data: perUserDir });
} }
}); });
Signals.addSignalMethods(ExtensionFinder.prototype); Signals.addSignalMethods(ExtensionFinder.prototype);

View File

@ -5,80 +5,27 @@ const GLib = imports.gi.GLib;
const Lang = imports.lang; const Lang = imports.lang;
const Params = imports.misc.params; const Params = imports.misc.params;
function listDirAsync(file, callback) { function collectFromDatadirs(subdir, includeUserDir, processFile) {
let allFiles = [];
file.enumerate_children_async('standard::name,standard::type',
Gio.FileQueryInfoFlags.NONE,
GLib.PRIORITY_LOW, null, function (obj, res) {
let enumerator = obj.enumerate_children_finish(res);
function onNextFileComplete(obj, res) {
let files = obj.next_files_finish(res);
if (files.length) {
allFiles = allFiles.concat(files);
enumerator.next_files_async(100, GLib.PRIORITY_LOW, null, onNextFileComplete);
} else {
enumerator.close(null);
callback(allFiles);
}
}
enumerator.next_files_async(100, GLib.PRIORITY_LOW, null, onNextFileComplete);
});
}
function _collectFromDirectoryAsync(dir, loadState) {
function done() {
loadState.numLoading--;
if (loadState.loadedCallback &&
loadState.numLoading == 0)
loadState.loadedCallback(loadState.data);
}
dir.query_info_async('standard::type', Gio.FileQueryInfoFlags.NONE,
GLib.PRIORITY_DEFAULT, null, function(object, res) {
try {
object.query_info_finish(res);
} catch (e) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
log(e.message);
done();
return;
}
listDirAsync(dir, Lang.bind(this, function(infos) {
for (let i = 0; i < infos.length; i++)
loadState.processFile(dir.get_child(infos[i].get_name()),
infos[i], loadState.data);
done();
}));
});
}
function collectFromDatadirsAsync(subdir, params) {
params = Params.parse(params, { includeUserDir: false,
processFile: null,
loadedCallback: null,
data: null });
let loadState = { data: params.data,
numLoading: 0,
loadedCallback: params.loadedCallback,
processFile: params.processFile };
if (params.processFile == null) {
if (params.loadedCallback)
params.loadedCallback(params.data);
return;
}
let dataDirs = GLib.get_system_data_dirs(); let dataDirs = GLib.get_system_data_dirs();
if (params.includeUserDir) if (includeUserDir)
dataDirs.unshift(GLib.get_user_data_dir()); dataDirs.unshift(GLib.get_user_data_dir());
loadState.numLoading = dataDirs.length;
for (let i = 0; i < dataDirs.length; i++) { for (let i = 0; i < dataDirs.length; i++) {
let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', subdir]); let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', subdir]);
let dir = Gio.File.new_for_path(path); let dir = Gio.File.new_for_path(path);
_collectFromDirectoryAsync(dir, loadState); 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)))
processFile(fileEnum.get_child(info), info);
}
} }
} }

View File

@ -4,15 +4,17 @@ const Gio = imports.gi.Gio;
const Lang = imports.lang; const Lang = imports.lang;
const Signals = imports.signals; const Signals = imports.signals;
const PresenceIface = <interface name="org.gnome.SessionManager.Presence"> const PresenceIface = '<node> \
<method name="SetStatus"> <interface name="org.gnome.SessionManager.Presence"> \
<arg type="u" direction="in"/> <method name="SetStatus"> \
</method> <arg type="u" direction="in"/> \
<property name="status" type="u" access="readwrite"/> </method> \
<signal name="StatusChanged"> <property name="status" type="u" access="readwrite"/> \
<arg type="u" direction="out"/> <signal name="StatusChanged"> \
</signal> <arg type="u" direction="out"/> \
</interface>; </signal> \
</interface> \
</node>';
const PresenceStatus = { const PresenceStatus = {
AVAILABLE: 0, AVAILABLE: 0,
@ -30,14 +32,16 @@ function Presence(initCallback, cancellable) {
// Note inhibitors are immutable objects, so they don't // Note inhibitors are immutable objects, so they don't
// change at runtime (changes always come in the form // change at runtime (changes always come in the form
// of new inhibitors) // of new inhibitors)
const InhibitorIface = <interface name="org.gnome.SessionManager.Inhibitor"> const InhibitorIface = '<node> \
<method name="GetAppId"> <interface name="org.gnome.SessionManager.Inhibitor"> \
<arg type="s" direction="out" /> <method name="GetAppId"> \
</method> <arg type="s" direction="out" /> \
<method name="GetReason"> </method> \
<arg type="s" direction="out" /> <method name="GetReason"> \
</method> <arg type="s" direction="out" /> \
</interface>; </method> \
</interface> \
</node>';
var InhibitorProxy = Gio.DBusProxy.makeProxyWrapper(InhibitorIface); var InhibitorProxy = Gio.DBusProxy.makeProxyWrapper(InhibitorIface);
function Inhibitor(objectPath, initCallback, cancellable) { function Inhibitor(objectPath, initCallback, cancellable) {
@ -45,27 +49,29 @@ function Inhibitor(objectPath, initCallback, cancellable) {
} }
// Not the full interface, only the methods we use // Not the full interface, only the methods we use
const SessionManagerIface = <interface name="org.gnome.SessionManager"> const SessionManagerIface = '<node> \
<method name="Logout"> <interface name="org.gnome.SessionManager"> \
<arg type="u" direction="in" /> <method name="Logout"> \
</method> <arg type="u" direction="in" /> \
<method name="Shutdown" /> </method> \
<method name="Reboot" /> <method name="Shutdown" /> \
<method name="CanShutdown"> <method name="Reboot" /> \
<arg type="b" direction="out" /> <method name="CanShutdown"> \
</method> <arg type="b" direction="out" /> \
<method name="IsInhibited"> </method> \
<arg type="u" direction="in" /> <method name="IsInhibited"> \
<arg type="b" direction="out" /> <arg type="u" direction="in" /> \
</method> <arg type="b" direction="out" /> \
<property name="SessionIsActive" type="b" access="read"/> </method> \
<signal name="InhibitorAdded"> <property name="SessionIsActive" type="b" access="read"/> \
<arg type="o" direction="out"/> <signal name="InhibitorAdded"> \
</signal> <arg type="o" direction="out"/> \
<signal name="InhibitorRemoved"> </signal> \
<arg type="o" direction="out"/> <signal name="InhibitorRemoved"> \
</signal> <arg type="o" direction="out"/> \
</interface>; </signal> \
</interface> \
</node>';
var SessionManagerProxy = Gio.DBusProxy.makeProxyWrapper(SessionManagerIface); var SessionManagerProxy = Gio.DBusProxy.makeProxyWrapper(SessionManagerIface);
function SessionManager(initCallback, cancellable) { function SessionManager(initCallback, cancellable) {

View File

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

@ -89,7 +89,7 @@ const HistoryManager = new Lang.Class({
} else if (symbol == Clutter.KEY_Down) { } else if (symbol == Clutter.KEY_Down) {
return this._setNextItem(entry.get_text()); return this._setNextItem(entry.get_text());
} }
return false; return Clutter.EVENT_PROPAGATE;
}, },
_indexChanged: function() { _indexChanged: function() {

View File

@ -7,58 +7,66 @@ const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Signals = imports.signals; const Signals = imports.signals;
const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager'> const SystemdLoginManagerIface = '<node> \
<method name='Suspend'> <interface name="org.freedesktop.login1.Manager"> \
<arg type='b' direction='in'/> <method name="Suspend"> \
</method> <arg type="b" direction="in"/> \
<method name='CanSuspend'> </method> \
<arg type='s' direction='out'/> <method name="CanSuspend"> \
</method> <arg type="s" direction="out"/> \
<method name='Inhibit'> </method> \
<arg type='s' direction='in'/> <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='s' direction='in'/> <arg type="s" direction="in"/> \
<arg type='h' direction='out'/> <arg type="s" direction="in"/> \
</method> <arg type="h" direction="out"/> \
<method name='GetSession'> </method> \
<arg type='s' direction='in'/> <method name="GetSession"> \
<arg type='o' direction='out'/> <arg type="s" direction="in"/> \
</method> <arg type="o" direction="out"/> \
<method name='ListSessions'> </method> \
<arg name='sessions' type='a(susso)' direction='out'/> <method name="ListSessions"> \
</method> <arg name="sessions" type="a(susso)" direction="out"/> \
<signal name='PrepareForSleep'> </method> \
<arg type='b' direction='out'/> <signal name="PrepareForSleep"> \
</signal> <arg type="b" direction="out"/> \
</interface>; </signal> \
</interface> \
</node>';
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'> const SystemdLoginSessionIface = '<node> \
<signal name='Lock' /> <interface name="org.freedesktop.login1.Session"> \
<signal name='Unlock' /> <signal name="Lock" /> \
</interface>; <signal name="Unlock" /> \
</interface> \
</node>';
const SystemdLoginManager = Gio.DBusProxy.makeProxyWrapper(SystemdLoginManagerIface); const SystemdLoginManager = Gio.DBusProxy.makeProxyWrapper(SystemdLoginManagerIface);
const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface); const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface);
const ConsoleKitManagerIface = <interface name='org.freedesktop.ConsoleKit.Manager'> const ConsoleKitManagerIface = '<node> \
<method name='CanRestart'> <interface name="org.freedesktop.ConsoleKit.Manager"> \
<arg type='b' direction='out'/> <method name="CanRestart"> \
</method> <arg type="b" direction="out"/> \
<method name='CanStop'> </method> \
<arg type='b' direction='out'/> <method name="CanStop"> \
</method> <arg type="b" direction="out"/> \
<method name='Restart' /> </method> \
<method name='Stop' /> <method name="Restart" /> \
<method name='GetCurrentSession'> <method name="Stop" /> \
<arg type='o' direction='out' /> <method name="GetCurrentSession"> \
</method> <arg type="o" direction="out" /> \
</interface>; </method> \
</interface> \
</node>';
const ConsoleKitSessionIface = <interface name='org.freedesktop.ConsoleKit.Session'> const ConsoleKitSessionIface = '<node> \
<signal name='Lock' /> <interface name="org.freedesktop.ConsoleKit.Session"> \
<signal name='Unlock' /> <signal name="Lock" /> \
</interface>; <signal name="Unlock" /> \
</interface> \
</node>';
const ConsoleKitSession = Gio.DBusProxy.makeProxyWrapper(ConsoleKitSessionIface); const ConsoleKitSession = Gio.DBusProxy.makeProxyWrapper(ConsoleKitSessionIface);
const ConsoleKitManager = Gio.DBusProxy.makeProxyWrapper(ConsoleKitManagerIface); const ConsoleKitManager = Gio.DBusProxy.makeProxyWrapper(ConsoleKitManagerIface);

View File

@ -92,37 +92,41 @@ function _findProviderForSid(sid) {
// The following are not the complete interfaces, just the methods we need // The following are not the complete interfaces, just the methods we need
// (or may need in the future) // (or may need in the future)
const ModemGsmNetworkInterface = <interface name="org.freedesktop.ModemManager.Modem.Gsm.Network"> const ModemGsmNetworkInterface = '<node> \
<method name="GetRegistrationInfo"> <interface name="org.freedesktop.ModemManager.Modem.Gsm.Network"> \
<arg type="(uss)" direction="out" /> <method name="GetRegistrationInfo"> \
</method> <arg type="(uss)" direction="out" /> \
<method name="GetSignalQuality"> </method> \
<arg type="u" direction="out" /> <method name="GetSignalQuality"> \
</method> <arg type="u" direction="out" /> \
<property name="AccessTechnology" type="u" access="read" /> </method> \
<signal name="SignalQuality"> <property name="AccessTechnology" type="u" access="read" /> \
<arg type="u" direction="out" /> <signal name="SignalQuality"> \
</signal> <arg type="u" direction="out" /> \
<signal name="RegistrationInfo"> </signal> \
<arg type="u" direction="out" /> <signal name="RegistrationInfo"> \
<arg type="s" direction="out" /> <arg type="u" direction="out" /> \
<arg type="s" direction="out" /> <arg type="s" direction="out" /> \
</signal> <arg type="s" direction="out" /> \
</interface>; </signal> \
</interface> \
</node>';
const ModemGsmNetworkProxy = Gio.DBusProxy.makeProxyWrapper(ModemGsmNetworkInterface); const ModemGsmNetworkProxy = Gio.DBusProxy.makeProxyWrapper(ModemGsmNetworkInterface);
const ModemCdmaInterface = <interface name="org.freedesktop.ModemManager.Modem.Cdma"> const ModemCdmaInterface = '<node> \
<method name="GetSignalQuality"> <interface name="org.freedesktop.ModemManager.Modem.Cdma"> \
<arg type="u" direction="out" /> <method name="GetSignalQuality"> \
</method> <arg type="u" direction="out" /> \
<method name="GetServingSystem"> </method> \
<arg type="(usu)" direction="out" /> <method name="GetServingSystem"> \
</method> <arg type="(usu)" direction="out" /> \
<signal name="SignalQuality"> </method> \
<arg type="u" direction="out" /> <signal name="SignalQuality"> \
</signal> <arg type="u" direction="out" /> \
</interface>; </signal> \
</interface> \
</node>';
const ModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(ModemCdmaInterface); const ModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(ModemCdmaInterface);
@ -218,20 +222,26 @@ Signals.addSignalMethods(ModemCdma.prototype);
// Support for the new ModemManager1 interface (MM >= 0.7) // Support for the new ModemManager1 interface (MM >= 0.7)
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
const BroadbandModemInterface = <interface name="org.freedesktop.ModemManager1.Modem"> const BroadbandModemInterface = '<node> \
<property name="SignalQuality" type="(ub)" access="read" /> <interface name="org.freedesktop.ModemManager1.Modem"> \
</interface>; <property name="SignalQuality" type="(ub)" access="read" /> \
</interface> \
</node>';
const BroadbandModemProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemInterface); const BroadbandModemProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemInterface);
const BroadbandModem3gppInterface = <interface name="org.freedesktop.ModemManager1.Modem.Modem3gpp"> const BroadbandModem3gppInterface = '<node> \
<property name="OperatorCode" type="s" access="read" /> <interface name="org.freedesktop.ModemManager1.Modem.Modem3gpp"> \
<property name="OperatorName" type="s" access="read" /> <property name="OperatorCode" type="s" access="read" /> \
</interface>; <property name="OperatorName" type="s" access="read" /> \
</interface> \
</node>';
const BroadbandModem3gppProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModem3gppInterface); const BroadbandModem3gppProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModem3gppInterface);
const BroadbandModemCdmaInterface = <interface name="org.freedesktop.ModemManager1.Modem.ModemCdma"> const BroadbandModemCdmaInterface = '<node> \
<property name="Sid" type="u" access="read" /> <interface name="org.freedesktop.ModemManager1.Modem.ModemCdma"> \
</interface>; <property name="Sid" type="u" access="read" /> \
</interface> \
</node>';
const BroadbandModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemCdmaInterface); const BroadbandModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemCdmaInterface);
const BroadbandModem = new Lang.Class({ const BroadbandModem = new Lang.Class({

View File

@ -8,19 +8,21 @@ const Signals = imports.signals;
// Specified in the D-Bus specification here: // Specified in the D-Bus specification here:
// http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager // http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager
const ObjectManagerIface = <interface name="org.freedesktop.DBus.ObjectManager"> const ObjectManagerIface = '<node> \
<method name="GetManagedObjects"> <interface name="org.freedesktop.DBus.ObjectManager"> \
<arg name="objects" type="a{oa{sa{sv}}}" direction="out"/> <method name="GetManagedObjects"> \
</method> <arg name="objects" type="a{oa{sa{sv}}}" direction="out"/> \
<signal name="InterfacesAdded"> </method> \
<arg name="objectPath" type="o"/> <signal name="InterfacesAdded"> \
<arg name="interfaces" type="a{sa{sv}}" /> <arg name="objectPath" type="o"/> \
</signal> <arg name="interfaces" type="a{sa{sv}}" /> \
<signal name="InterfacesRemoved"> </signal> \
<arg name="objectPath" type="o"/> <signal name="InterfacesRemoved"> \
<arg name="interfaces" type="as" /> <arg name="objectPath" type="o"/> \
</signal> <arg name="interfaces" type="as" /> \
</interface>; </signal> \
</interface> \
</node>';
const ObjectManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(ObjectManagerIface); const ObjectManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(ObjectManagerIface);

View File

@ -7,12 +7,14 @@ const Signals = imports.signals;
const ObjectManager = imports.misc.objectManager; const ObjectManager = imports.misc.objectManager;
const SmartcardTokenIface = <interface name="org.gnome.SettingsDaemon.Smartcard.Token"> const SmartcardTokenIface = '<node> \
<property name="Name" type="s" access="read"/> <interface name="org.gnome.SettingsDaemon.Smartcard.Token"> \
<property name="Driver" type="o" access="read"/> <property name="Name" type="s" access="read"/> \
<property name="IsInserted" type="b" access="read"/> <property name="Driver" type="o" access="read"/> \
<property name="UsedToLogin" type="b" access="read"/> <property name="IsInserted" type="b" access="read"/> \
</interface>; <property name="UsedToLogin" type="b" access="read"/> \
</interface> \
</node>';
let _smartcardManager = null; let _smartcardManager = null;

View File

@ -1,6 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const Lang = imports.lang; const Lang = imports.lang;
const St = imports.gi.St; const St = imports.gi.St;
@ -79,6 +80,22 @@ function spawnCommandLine(command_line) {
} }
} }
// spawnApp:
// @argv: an argv array
//
// Runs @argv as if it was an application, handling startup notification
function spawnApp(argv) {
try {
let app = Gio.AppInfo.create_from_commandline(argv.join(' '), null,
Gio.AppInfoCreateFlags.SUPPORTS_STARTUP_NOTIFICATION);
let context = global.create_app_launch_context(0, -1);
app.launch([], context);
} catch(err) {
_handleSpawnError(argv[0], err);
}
}
// trySpawn: // trySpawn:
// @argv: an argv array // @argv: an argv array
// //
@ -136,7 +153,7 @@ function trySpawnCommandLine(command_line) {
} }
function _handleSpawnError(command, err) { function _handleSpawnError(command, err) {
let title = _("Execution of '%s' failed:").format(command); let title = _("Execution of %s failed:").format(command);
Main.notifyError(title, err.message); Main.notifyError(title, err.message);
} }

View File

@ -2,6 +2,7 @@
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta; const Meta = imports.gi.Meta;
@ -106,6 +107,8 @@ const AppSwitcherPopup = new Lang.Class({
this._switcherList = new AppSwitcher(apps, this); this._switcherList = new AppSwitcher(apps, this);
this._items = this._switcherList.icons; this._items = this._switcherList.icons;
if (this._items.length == 0)
return false;
return true; return true;
}, },
@ -310,7 +313,7 @@ const AppSwitcherPopup = new Lang.Class({
this._createThumbnails(); this._createThumbnails();
this._thumbnailTimeoutId = 0; this._thumbnailTimeoutId = 0;
this._thumbnailsFocused = false; this._thumbnailsFocused = false;
return false; return GLib.SOURCE_REMOVE;
}, },
_destroyThumbnails : function() { _destroyThumbnails : function() {
@ -375,6 +378,9 @@ const WindowSwitcherPopup = new Lang.Class({
this._switcherList = new WindowList(windows, mode); this._switcherList = new WindowList(windows, mode);
this._items = this._switcherList.icons; this._items = this._switcherList.icons;
if (this._items.length == 0)
return false;
return true; return true;
}, },
@ -454,9 +460,10 @@ const AppSwitcher = new Lang.Class({
appIcon.cachedWindows = allWindows.filter(function(w) { appIcon.cachedWindows = allWindows.filter(function(w) {
return windowTracker.get_window_app (w) == appIcon.app; return windowTracker.get_window_app (w) == appIcon.app;
}); });
if (workspace == null || appIcon.cachedWindows.length > 0) { if (appIcon.cachedWindows.length > 0)
this._addIcon(appIcon); this._addIcon(appIcon);
} else if (workspace == null)
throw new Error('%s appears to be running, but doesn\'t have any windows'.format(appIcon.app.get_name()));
} }
this._curApp = -1; this._curApp = -1;
@ -542,7 +549,7 @@ const AppSwitcher = new Lang.Class({
Lang.bind(this, function () { Lang.bind(this, function () {
this._enterItem(index); this._enterItem(index);
this._mouseTimeOutId = 0; this._mouseTimeOutId = 0;
return false; return GLib.SOURCE_REMOVE;
})); }));
} else } else
this._itemEntered(index); this._itemEntered(index);

View File

@ -1,5 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const GLib = imports.gi.GLib;
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
const St = imports.gi.St; const St = imports.gi.St;
@ -59,7 +60,7 @@ const Animation = new Lang.Class({
_update: function() { _update: function() {
this._showFrame(this._frame + 1); this._showFrame(this._frame + 1);
return true; return GLib.SOURCE_CONTINUE;
}, },
_animationsLoaded: function() { _animationsLoaded: function() {

View File

@ -5,7 +5,6 @@ const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject; const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk; const Gtk = imports.gi.Gtk;
const GMenu = imports.gi.GMenu;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Lang = imports.lang; const Lang = imports.lang;
const Signals = imports.signals; const Signals = imports.signals;
@ -47,24 +46,45 @@ const INDICATORS_ANIMATION_MAX_TIME = 0.75;
const PAGE_SWITCH_TRESHOLD = 0.2; const PAGE_SWITCH_TRESHOLD = 0.2;
const PAGE_SWITCH_TIME = 0.3; const PAGE_SWITCH_TIME = 0.3;
// Recursively load a GMenuTreeDirectory; we could put this in ShellAppSystem too function _getCategories(info) {
function _loadCategory(dir, view) { let categoriesStr = info.get_categories();
let iter = dir.iter(); if (!categoriesStr)
let appSystem = Shell.AppSystem.get_default(); return [];
let nextType; return categoriesStr.split(';');
while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) { }
if (nextType == GMenu.TreeItemType.ENTRY) {
let entry = iter.get_entry(); function _isTerminal(app) {
let app = appSystem.lookup_app_by_tree_entry(entry); let info = app.get_app_info();
if (!entry.get_app_info().get_nodisplay()) if (!info)
view.addApp(app); return false;
} else if (nextType == GMenu.TreeItemType.DIRECTORY) { let categories = _getCategories(info);
let itemDir = iter.get_directory(); return categories.indexOf('TerminalEmulator') > -1;
if (!itemDir.get_is_nodisplay()) }
_loadCategory(itemDir, view);
function _listsIntersect(a, b) {
for (let itemA of a)
if (b.indexOf(itemA) >= 0)
return true;
return false;
}
function _getFolderName(folder) {
let name = folder.get_string('name');
if (folder.get_boolean('translate')) {
let keyfile = new GLib.KeyFile();
let path = 'desktop-directories/' + name;
try {
keyfile.load_from_data_dirs(path, GLib.KeyFileFlags.NONE);
name = keyfile.get_locale_string('Desktop Entry', 'Name', null);
} catch(e) {
return name;
} }
} }
};
return name;
}
const BaseAppView = new Lang.Class({ const BaseAppView = new Lang.Class({
Name: 'BaseAppView', Name: 'BaseAppView',
@ -92,45 +112,38 @@ const BaseAppView = new Lang.Class({
}, },
removeAll: function() { removeAll: function() {
this._grid.removeAll(); this._grid.destroyAll();
this._items = {}; this._items = {};
this._allItems = []; this._allItems = [];
}, },
_getItemId: function(item) { _redisplay: function() {
throw new Error('Not implemented'); this.removeAll();
this._loadApps();
}, },
_createItemIcon: function(item) { getAllItems: function() {
throw new Error('Not implemented'); return this._allItems;
},
addItem: function(icon) {
let id = icon.id;
if (this._items[id] !== undefined)
return;
this._allItems.push(icon);
this._items[id] = icon;
}, },
_compareItems: function(a, b) { _compareItems: function(a, b) {
throw new Error('Not implemented'); return a.name.localeCompare(b.name);
},
_addItem: function(item) {
let id = this._getItemId(item);
if (this._items[id] !== undefined)
return null;
let itemIcon = this._createItemIcon(item);
this._allItems.push(item);
this._items[id] = itemIcon;
return itemIcon;
}, },
loadGrid: function() { loadGrid: function() {
this._allItems.sort(Lang.bind(this, this._compareItems)); this._allItems.sort(this._compareItems);
this._allItems.forEach(Lang.bind(this, function(item) {
for (let i = 0; i < this._allItems.length; i++) { this._grid.addItem(item);
let id = this._getItemId(this._allItems[i]); }));
if (!id)
continue;
this._grid.addItem(this._items[id]);
}
this.emit('view-loaded'); this.emit('view-loaded');
}, },
@ -281,7 +294,7 @@ const AllView = new Lang.Class({
this._pageIndicators.actor.connect('scroll-event', Lang.bind(this, this._onScroll)); this._pageIndicators.actor.connect('scroll-event', Lang.bind(this, this._onScroll));
this.actor.add_actor(this._pageIndicators.actor); this.actor.add_actor(this._pageIndicators.actor);
this._folderIcons = []; this.folderIcons = [];
this._stack = new St.Widget({ layout_manager: new Clutter.BinLayout() }); this._stack = new St.Widget({ layout_manager: new Clutter.BinLayout() });
let box = new St.BoxLayout({ vertical: true }); let box = new St.BoxLayout({ vertical: true });
@ -346,6 +359,76 @@ const AllView = new Lang.Class({
this._keyPressEventId = 0; this._keyPressEventId = 0;
} }
})); }));
this._redisplayWorkId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));
Shell.AppSystem.get_default().connect('installed-changed', Lang.bind(this, function() {
Main.queueDeferredWork(this._redisplayWorkId);
}));
this._folderSettings = new Gio.Settings({ schema: 'org.gnome.desktop.app-folders' });
this._folderSettings.connect('changed::folder-children', Lang.bind(this, function() {
Main.queueDeferredWork(this._redisplayWorkId);
}));
},
removeAll: function() {
this.folderIcons = [];
this.parent();
},
_itemNameChanged: function(item) {
// If an item's name changed, we can pluck it out of where it's
// supposed to be and reinsert it where it's sorted.
let oldIdx = this._allItems.indexOf(item);
this._allItems.splice(oldIdx, 1);
let newIdx = Util.insertSorted(this._allItems, item, this._compareItems);
this._grid.removeItem(item);
this._grid.addItem(item, newIdx);
},
_refilterApps: function() {
this._allItems.forEach(function(icon) {
if (icon instanceof AppIcon)
icon.actor.visible = true;
});
this.folderIcons.forEach(Lang.bind(this, function(folder) {
let folderApps = folder.getAppIds();
folderApps.forEach(Lang.bind(this, function(appId) {
let appIcon = this._items[appId];
appIcon.actor.visible = false;
}));
}));
},
_loadApps: function() {
let apps = Gio.AppInfo.get_all().filter(function(appInfo) {
return appInfo.should_show();
}).map(function(app) {
return app.get_id();
});
let appSys = Shell.AppSystem.get_default();
let folders = this._folderSettings.get_strv('folder-children');
folders.forEach(Lang.bind(this, function(id) {
let path = this._folderSettings.path + 'folders/' + id + '/';
let icon = new FolderIcon(id, path, this);
icon.connect('name-changed', Lang.bind(this, this._itemNameChanged));
icon.connect('apps-changed', Lang.bind(this, this._refilterApps));
this.addItem(icon);
this.folderIcons.push(icon);
}));
apps.forEach(Lang.bind(this, function(appId) {
let app = appSys.lookup_app(appId);
let icon = new AppIcon(app);
this.addItem(icon);
}));
this.loadGrid();
this._refilterApps();
}, },
getCurrentPageY: function() { getCurrentPageY: function() {
@ -415,7 +498,7 @@ const AllView = new Lang.Class({
_onScroll: function(actor, event) { _onScroll: function(actor, event) {
if (this._displayingPopup) if (this._displayingPopup)
return true; return Clutter.EVENT_STOP;
let direction = event.get_scroll_direction(); let direction = event.get_scroll_direction();
if (direction == Clutter.ScrollDirection.UP) if (direction == Clutter.ScrollDirection.UP)
@ -423,7 +506,7 @@ const AllView = new Lang.Class({
else if (direction == Clutter.ScrollDirection.DOWN) else if (direction == Clutter.ScrollDirection.DOWN)
this.goToPage(this._currentPage + 1); this.goToPage(this._currentPage + 1);
return true; return Clutter.EVENT_STOP;
}, },
_onPan: function(action) { _onPan: function(action) {
@ -454,63 +537,17 @@ const AllView = new Lang.Class({
_onKeyPressEvent: function(actor, event) { _onKeyPressEvent: function(actor, event) {
if (this._displayingPopup) if (this._displayingPopup)
return true; return Clutter.EVENT_STOP;
if (event.get_key_symbol() == Clutter.Page_Up) { if (event.get_key_symbol() == Clutter.Page_Up) {
this.goToPage(this._currentPage - 1); this.goToPage(this._currentPage - 1);
return true; return Clutter.EVENT_STOP;
} else if (event.get_key_symbol() == Clutter.Page_Down) { } else if (event.get_key_symbol() == Clutter.Page_Down) {
this.goToPage(this._currentPage + 1); this.goToPage(this._currentPage + 1);
return true; return Clutter.EVENT_STOP;
} }
return false; return Clutter.EVENT_PROPAGATE;
},
_getItemId: function(item) {
if (item instanceof Shell.App)
return item.get_id();
else if (item instanceof GMenu.TreeDirectory)
return item.get_menu_id();
else
return null;
},
_createItemIcon: function(item) {
if (item instanceof Shell.App)
return new AppIcon(item);
else if (item instanceof GMenu.TreeDirectory)
return new FolderIcon(item, this);
else
return null;
},
_compareItems: function(itemA, itemB) {
// bit of a hack: rely on both ShellApp and GMenuTreeDirectory
// having a get_name() method
let nameA = GLib.utf8_collate_key(itemA.get_name(), -1);
let nameB = GLib.utf8_collate_key(itemB.get_name(), -1);
return (nameA > nameB) ? 1 : (nameA < nameB ? -1 : 0);
},
removeAll: function() {
this._folderIcons = [];
this.parent();
},
addApp: function(app) {
let appIcon = this._addItem(app);
if (appIcon)
appIcon.actor.connect('key-focus-in',
Lang.bind(this, this._ensureIconVisible));
},
addFolder: function(dir) {
let folderIcon = this._addItem(dir);
this._folderIcons.push(folderIcon);
if (folderIcon)
folderIcon.actor.connect('key-focus-in',
Lang.bind(this, this._ensureIconVisible));
}, },
addFolderPopup: function(popup) { addFolderPopup: function(popup) {
@ -577,8 +614,8 @@ const AllView = new Lang.Class({
this._availWidth = availWidth; this._availWidth = availWidth;
this._availHeight = availHeight; this._availHeight = availHeight;
// Update folder views // Update folder views
for (let i = 0; i < this._folderIcons.length; i++) for (let i = 0; i < this.folderIcons.length; i++)
this._folderIcons[i].adaptToSize(availWidth, availHeight); this.folderIcons[i].adaptToSize(availWidth, availHeight);
} }
}); });
Signals.addSignalMethods(AllView.prototype); Signals.addSignalMethods(AllView.prototype);
@ -607,13 +644,18 @@ const FrequentView = new Lang.Class({
this._noFrequentAppsLabel.hide(); this._noFrequentAppsLabel.hide();
this._usage = Shell.AppUsage.get_default(); this._usage = Shell.AppUsage.get_default();
this.actor.connect('notify::mapped', Lang.bind(this, function() {
if (this.actor.mapped)
this._redisplay();
}));
}, },
hasUsefulData: function() { hasUsefulData: function() {
return this._usage.get_most_used("").length >= MIN_FREQUENT_APPS_COUNT; return this._usage.get_most_used("").length >= MIN_FREQUENT_APPS_COUNT;
}, },
loadApps: function() { _loadApps: function() {
let mostUsed = this._usage.get_most_used (""); let mostUsed = this._usage.get_most_used ("");
let hasUsefulData = this.hasUsefulData(); let hasUsefulData = this.hasUsefulData();
this._noFrequentAppsLabel.visible = !hasUsefulData; this._noFrequentAppsLabel.visible = !hasUsefulData;
@ -691,16 +733,6 @@ const AppDisplay = new Lang.Class({
Name: 'AppDisplay', Name: 'AppDisplay',
_init: function() { _init: function() {
this._appSystem = Shell.AppSystem.get_default();
this._appSystem.connect('installed-changed', Lang.bind(this, function() {
Main.queueDeferredWork(this._allAppsWorkId);
}));
Main.overview.connect('showing', Lang.bind(this, function() {
Main.queueDeferredWork(this._frequentAppsWorkId);
}));
global.settings.connect('changed::app-folder-categories', Lang.bind(this, function() {
Main.queueDeferredWork(this._allAppsWorkId);
}));
this._privacySettings = new Gio.Settings({ schema: 'org.gnome.desktop.privacy' }); this._privacySettings = new Gio.Settings({ schema: 'org.gnome.desktop.privacy' });
this._privacySettings.connect('changed::remember-app-usage', this._privacySettings.connect('changed::remember-app-usage',
Lang.bind(this, this._updateFrequentVisibility)); Lang.bind(this, this._updateFrequentVisibility));
@ -744,20 +776,16 @@ const AppDisplay = new Lang.Class({
this._views[i].control.connect('clicked', Lang.bind(this, this._views[i].control.connect('clicked', Lang.bind(this,
function(actor) { function(actor) {
this._showView(viewIndex); this._showView(viewIndex);
global.settings.set_uint('app-picker-view', viewIndex);
})); }));
} }
let initialView = Math.min(global.settings.get_uint('app-picker-view'),
this._views.length - 1);
let frequentUseful = this._views[Views.FREQUENT].view.hasUsefulData(); let frequentUseful = this._views[Views.FREQUENT].view.hasUsefulData();
this._showView(frequentUseful ? Views.FREQUENT : Views.ALL); if (initialView == Views.FREQUENT && !frequentUseful)
initialView = Views.ALL;
this._showView(initialView);
this._updateFrequentVisibility(); this._updateFrequentVisibility();
// We need a dummy actor to catch the keyboard focus if the
// user Ctrl-Alt-Tabs here before the deferred work creates
// our real contents
this._focusDummy = new St.Bin({ can_focus: true });
this._viewStack.add_actor(this._focusDummy);
this._allAppsWorkId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplayAllApps));
this._frequentAppsWorkId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplayFrequentApps));
}, },
_showView: function(activeIndex) { _showView: function(activeIndex) {
@ -791,52 +819,6 @@ const AppDisplay = new Lang.Class({
this._showView(Views.ALL); this._showView(Views.ALL);
}, },
_redisplay: function() {
this._redisplayFrequentApps();
this._redisplayAllApps();
},
_redisplayFrequentApps: function() {
let view = this._views[Views.FREQUENT].view;
view.removeAll();
view.loadApps();
},
_redisplayAllApps: function() {
let view = this._views[Views.ALL].view;
view.removeAll();
let tree = this._appSystem.get_tree();
let root = tree.get_root_directory();
let iter = root.iter();
let nextType;
let folderCategories = global.settings.get_strv('app-folder-categories');
while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
if (nextType == GMenu.TreeItemType.DIRECTORY) {
let dir = iter.get_directory();
if (dir.get_is_nodisplay())
continue;
if (folderCategories.indexOf(dir.get_menu_id()) != -1)
view.addFolder(dir);
else
_loadCategory(dir, view);
}
}
view.loadGrid();
if (this._focusDummy) {
let focused = this._focusDummy.has_key_focus();
this._focusDummy.destroy();
this._focusDummy = null;
if (focused)
this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
}
},
selectApp: function(id) { selectApp: function(id) {
this._showView(Views.ALL); this._showView(Views.ALL);
this._views[Views.ALL].view.selectApp(id); this._views[Views.ALL].view.selectApp(id);
@ -866,8 +848,8 @@ const AppSearchProvider = new Lang.Class({
getResultMetas: function(apps, callback) { getResultMetas: function(apps, callback) {
let metas = []; let metas = [];
for (let i = 0; i < apps.length; i++) { for (let i = 0; i < apps.length; i++) {
let app = apps[i]; let app = this._appSys.lookup_app(apps[i]);
metas.push({ 'id': app, metas.push({ 'id': app.get_id(),
'name': app.get_name(), 'name': app.get_name(),
'createIcon': function(size) { 'createIcon': function(size) {
return app.create_icon_texture(size); return app.create_icon_texture(size);
@ -877,18 +859,36 @@ const AppSearchProvider = new Lang.Class({
callback(metas); callback(metas);
}, },
getInitialResultSet: function(terms) { filterResults: function(results, maxNumber) {
this.searchSystem.setResults(this, this._appSys.initial_search(terms)); return results.slice(0, maxNumber);
}, },
getSubsearchResultSet: function(previousResults, terms) { getInitialResultSet: function(terms, callback, cancellable) {
this.searchSystem.setResults(this, this._appSys.subsearch(previousResults, terms)); let query = terms.join(' ');
let groups = Gio.DesktopAppInfo.search(query);
let usage = Shell.AppUsage.get_default();
let results = [];
groups.forEach(function(group) {
group = group.filter(function(appID) {
let app = Gio.DesktopAppInfo.new(appID);
return app && app.should_show();
});
results = results.concat(group.sort(function(a, b) {
return usage.compare('', a, b);
}));
});
callback(results);
}, },
activateResult: function(app) { getSubsearchResultSet: function(previousResults, terms, callback, cancellable) {
this.getInitialResultSet(terms, callback, cancellable);
},
activateResult: function(result) {
let app = this._appSys.lookup_app(result);
let event = Clutter.get_current_event(); let event = Clutter.get_current_event();
let modifiers = event ? event.get_state() : 0; let modifiers = event ? event.get_state() : 0;
let openNewWindow = modifiers & Clutter.ModifierType.CONTROL_MASK; let openNewWindow = (modifiers & Clutter.ModifierType.CONTROL_MASK) || _isTerminal(app);
if (openNewWindow) if (openNewWindow)
app.open_new_window(-1); app.open_new_window(-1);
@ -904,8 +904,8 @@ const AppSearchProvider = new Lang.Class({
app.open_new_window(workspace); app.open_new_window(workspace);
}, },
createResultObject: function (resultMeta, terms) { createResultObject: function (resultMeta) {
let app = resultMeta['id']; let app = this._appSys.lookup_app(resultMeta['id']);
return new AppIcon(app); return new AppIcon(app);
} }
}); });
@ -931,22 +931,6 @@ const FolderView = new Lang.Class({
this.actor.add_action(action); this.actor.add_action(action);
}, },
_getItemId: function(item) {
return item.get_id();
},
_createItemIcon: function(item) {
return new AppIcon(item);
},
_compareItems: function(a, b) {
return a.compare_by_name(b);
},
addApp: function(app) {
this._addItem(app);
},
createFolderIcon: function(size) { createFolderIcon: function(size) {
let icon = new St.Widget({ layout_manager: new Clutter.BinLayout(), let icon = new St.Widget({ layout_manager: new Clutter.BinLayout(),
style_class: 'app-folder-icon', style_class: 'app-folder-icon',
@ -955,7 +939,7 @@ const FolderView = new Lang.Class({
let aligns = [ Clutter.ActorAlign.START, Clutter.ActorAlign.END ]; let aligns = [ Clutter.ActorAlign.START, Clutter.ActorAlign.END ];
for (let i = 0; i < Math.min(this._allItems.length, 4); i++) { for (let i = 0; i < Math.min(this._allItems.length, 4); i++) {
let texture = this._allItems[i].create_icon_texture(subSize); let texture = this._allItems[i].app.create_icon_texture(subSize);
let bin = new St.Bin({ child: texture, let bin = new St.Bin({ child: texture,
x_expand: true, y_expand: true }); x_expand: true, y_expand: true });
bin.set_x_align(aligns[i % 2]); bin.set_x_align(aligns[i % 2]);
@ -1032,10 +1016,12 @@ const FolderView = new Lang.Class({
const FolderIcon = new Lang.Class({ const FolderIcon = new Lang.Class({
Name: 'FolderIcon', Name: 'FolderIcon',
_init: function(dir, parentView) { _init: function(id, path, parentView) {
this._dir = dir; this.id = id;
this._parentView = parentView; this._parentView = parentView;
this._folder = new Gio.Settings({ schema_id: 'org.gnome.desktop.app-folders.folder',
path: path });
this.actor = new St.Button({ style_class: 'app-well-app app-folder', this.actor = new St.Button({ style_class: 'app-well-app app-folder',
button_mask: St.ButtonMask.ONE, button_mask: St.ButtonMask.ONE,
toggle_mode: true, toggle_mode: true,
@ -1046,15 +1032,11 @@ const FolderIcon = new Lang.Class({
// whether we need to update arrow side, position etc. // whether we need to update arrow side, position etc.
this._popupInvalidated = false; this._popupInvalidated = false;
let label = this._dir.get_name(); this.icon = new IconGrid.BaseIcon('', { createIcon: Lang.bind(this, this._createIcon), setSizeManually: true });
this.icon = new IconGrid.BaseIcon(label,
{ createIcon: Lang.bind(this, this._createIcon), setSizeManually: true });
this.actor.set_child(this.icon.actor); this.actor.set_child(this.icon.actor);
this.actor.label_actor = this.icon.label; this.actor.label_actor = this.icon.label;
this.view = new FolderView(); this.view = new FolderView();
_loadCategory(dir, this.view);
this.view.loadGrid();
this.actor.connect('clicked', Lang.bind(this, this.actor.connect('clicked', Lang.bind(this,
function() { function() {
@ -1067,6 +1049,63 @@ const FolderIcon = new Lang.Class({
if (!this.actor.mapped && this._popup) if (!this.actor.mapped && this._popup)
this._popup.popdown(); this._popup.popdown();
})); }));
this._folder.connect('changed', Lang.bind(this, this._redisplay));
this._redisplay();
},
getAppIds: function() {
return this.view.getAllItems().map(function(item) {
return item.id;
});
},
_updateName: function() {
let name = _getFolderName(this._folder);
if (this.name == name)
return;
this.name = name;
this.icon.label.text = this.name;
this.emit('name-changed');
},
_redisplay: function() {
this._updateName();
this.view.removeAll();
let excludedApps = this._folder.get_strv('excluded-apps');
let appSys = Shell.AppSystem.get_default();
let addAppId = (function addAppId(appId) {
if (excludedApps.indexOf(appId) >= 0)
return;
let app = appSys.lookup_app(appId);
if (!app)
return;
if (!app.get_app_info().should_show())
return;
let icon = new AppIcon(app);
this.view.addItem(icon);
}).bind(this);
let folderApps = this._folder.get_strv('apps');
folderApps.forEach(addAppId);
let folderCategories = this._folder.get_strv('categories');
Gio.AppInfo.get_all().forEach(function(appInfo) {
let appCategories = _getCategories(appInfo);
if (!_listsIntersect(folderCategories, appCategories))
return;
addAppId(appInfo.get_id());
});
this.view.loadGrid();
this.emit('apps-changed');
}, },
_createIcon: function(iconSize) { _createIcon: function(iconSize) {
@ -1145,6 +1184,7 @@ const FolderIcon = new Lang.Class({
this._popupInvalidated = true; this._popupInvalidated = true;
}, },
}); });
Signals.addSignalMethods(FolderIcon.prototype);
const AppFolderPopup = new Lang.Class({ const AppFolderPopup = new Lang.Class({
Name: 'AppFolderPopup', Name: 'AppFolderPopup',
@ -1198,13 +1238,13 @@ const AppFolderPopup = new Lang.Class({
_onKeyPress: function(actor, event) { _onKeyPress: function(actor, event) {
if (!this._isOpen) if (!this._isOpen)
return false; return Clutter.EVENT_PROPAGATE;
if (event.get_key_symbol() != Clutter.KEY_Escape) if (event.get_key_symbol() != Clutter.KEY_Escape)
return false; return Clutter.EVENT_PROPAGATE;
this.popdown(); this.popdown();
return true; return Clutter.EVENT_STOP;
}, },
toggle: function() { toggle: function() {
@ -1263,6 +1303,9 @@ const AppIcon = new Lang.Class({
_init : function(app, iconParams) { _init : function(app, iconParams) {
this.app = app; this.app = app;
this.id = app.get_id();
this.name = app.get_name();
this.actor = new St.Button({ style_class: 'app-well-app', this.actor = new St.Button({ style_class: 'app-well-app',
reactive: true, reactive: true,
button_mask: St.ButtonMask.ONE | St.ButtonMask.TWO, button_mask: St.ButtonMask.ONE | St.ButtonMask.TWO,
@ -1343,13 +1386,15 @@ const AppIcon = new Lang.Class({
this._removeMenuTimeout(); this._removeMenuTimeout();
this._menuTimeoutId = Mainloop.timeout_add(MENU_POPUP_TIMEOUT, this._menuTimeoutId = Mainloop.timeout_add(MENU_POPUP_TIMEOUT,
Lang.bind(this, function() { Lang.bind(this, function() {
this._menuTimeoutId = 0;
this.popupMenu(); this.popupMenu();
return GLib.SOURCE_REMOVE;
})); }));
} else if (button == 3) { } else if (button == 3) {
this.popupMenu(); this.popupMenu();
return true; return Clutter.EVENT_STOP;
} }
return false; return Clutter.EVENT_PROPAGATE;
}, },
_onClicked: function(actor, button) { _onClicked: function(actor, button) {
@ -1361,7 +1406,6 @@ const AppIcon = new Lang.Class({
// Last workspace is always empty // Last workspace is always empty
let launchWorkspace = global.screen.get_workspace_by_index(global.screen.n_workspaces - 1); let launchWorkspace = global.screen.get_workspace_by_index(global.screen.n_workspaces - 1);
launchWorkspace.activate(global.get_current_time()); launchWorkspace.activate(global.get_current_time());
this.emit('launching');
this.app.open_new_window(-1); this.app.open_new_window(-1);
Main.overview.hide(); Main.overview.hide();
} }
@ -1420,11 +1464,11 @@ const AppIcon = new Lang.Class({
}, },
_onActivate: function (event) { _onActivate: function (event) {
this.emit('launching');
let modifiers = event.get_state(); let modifiers = event.get_state();
if (modifiers & Clutter.ModifierType.CONTROL_MASK if ((modifiers & Clutter.ModifierType.CONTROL_MASK
&& this.app.state == Shell.AppState.RUNNING) { && this.app.state == Shell.AppState.RUNNING)
|| _isTerminal(this.app)) {
this.app.open_new_window(-1); this.app.open_new_window(-1);
} else { } else {
this.app.activate(); this.app.activate();
@ -1472,8 +1516,6 @@ const AppIconMenu = new Lang.Class({
this._source = source; this._source = source;
this.connect('activate', Lang.bind(this, this._onActivate));
this.actor.add_style_class_name('app-well-menu'); this.actor.add_style_class_name('app-well-menu');
// Chain our visibility and lifecycle to that of the source // Chain our visibility and lifecycle to that of the source
@ -1483,13 +1525,15 @@ const AppIconMenu = new Lang.Class({
})); }));
source.actor.connect('destroy', Lang.bind(this, function () { this.actor.destroy(); })); source.actor.connect('destroy', Lang.bind(this, function () { this.actor.destroy(); }));
Main.uiGroup.add_actor(this.actor); Main.layoutManager.menuGroup.add_actor(this.actor);
}, },
_redisplay: function() { _redisplay: function() {
this.removeAll(); this.removeAll();
let windows = this._source.app.get_windows(); let windows = this._source.app.get_windows().filter(function(w) {
return !w.skip_taskbar;
});
// Display the app windows menu items and the separator between windows // Display the app windows menu items and the separator between windows
// of the current desktop and other windows. // of the current desktop and other windows.
@ -1497,25 +1541,54 @@ const AppIconMenu = new Lang.Class({
let separatorShown = windows.length > 0 && windows[0].get_workspace() != activeWorkspace; let separatorShown = windows.length > 0 && windows[0].get_workspace() != activeWorkspace;
for (let i = 0; i < windows.length; i++) { for (let i = 0; i < windows.length; i++) {
if (!separatorShown && windows[i].get_workspace() != activeWorkspace) { let window = windows[i];
if (!separatorShown && window.get_workspace() != activeWorkspace) {
this._appendSeparator(); this._appendSeparator();
separatorShown = true; separatorShown = true;
} }
let item = this._appendMenuItem(windows[i].title); let item = this._appendMenuItem(window.title);
item._window = windows[i]; item.connect('activate', Lang.bind(this, function() {
this.emit('activate-window', window);
}));
} }
if (!this._source.app.is_window_backed()) { if (!this._source.app.is_window_backed()) {
if (windows.length > 0) this._appendSeparator();
this._appendSeparator();
this._newWindowMenuItem = this._appendMenuItem(_("New Window"));
this._newWindowMenuItem.connect('activate', Lang.bind(this, function() {
this._source.app.open_new_window(-1);
this.emit('activate-window', null);
}));
this._appendSeparator();
let appInfo = this._source.app.get_app_info();
let actions = appInfo.list_actions();
for (let i = 0; i < actions.length; i++) {
let action = actions[i];
let item = this._appendMenuItem(appInfo.get_action_name(action));
item.connect('activate', Lang.bind(this, function(emitter, event) {
this._source.app.launch_action(action, event.get_time(), -1);
this.emit('activate-window', null);
}));
}
this._appendSeparator();
let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id()); let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
this._newWindowMenuItem = this._appendMenuItem(_("New Window")); if (isFavorite) {
this._appendSeparator(); let item = this._appendMenuItem(_("Remove from Favorites"));
item.connect('activate', Lang.bind(this, function() {
this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ? _("Remove from Favorites") let favs = AppFavorites.getAppFavorites();
: _("Add to Favorites")); favs.removeFavorite(this._source.app.get_id());
}));
} else {
let item = this._appendMenuItem(_("Add to Favorites"));
item.connect('activate', Lang.bind(this, function() {
let favs = AppFavorites.getAppFavorites();
favs.addFavorite(this._source.app.get_id());
}));
}
} }
}, },
@ -1534,24 +1607,6 @@ const AppIconMenu = new Lang.Class({
popup: function(activatingButton) { popup: function(activatingButton) {
this._redisplay(); this._redisplay();
this.open(); this.open();
},
_onActivate: function (actor, child) {
if (child._window) {
let metaWindow = child._window;
this.emit('activate-window', metaWindow);
} else if (child == this._newWindowMenuItem) {
this._source.app.open_new_window(-1);
this.emit('activate-window', null);
} else if (child == this._toggleFavoriteMenuItem) {
let favs = AppFavorites.getAppFavorites();
let isFavorite = favs.isFavorite(this._source.app.get_id());
if (isFavorite)
favs.removeFavorite(this._source.app.get_id());
else
favs.addFavorite(this._source.app.get_id());
}
this.close();
} }
}); });
Signals.addSignalMethods(AppIconMenu.prototype); Signals.addSignalMethods(AppIconMenu.prototype);

View File

@ -50,11 +50,9 @@ const BackgroundCache = new Lang.Class({
effects: Meta.BackgroundEffects.NONE }); effects: Meta.BackgroundEffects.NONE });
let content = null; let content = null;
let candidateContent = null; let candidateContent = null;
for (let i = 0; i < this._patterns.length; i++) { for (let i = 0; i < this._patterns.length; i++) {
if (!this._patterns[i])
continue;
if (this._patterns[i].get_shading() != params.shadingType) if (this._patterns[i].get_shading() != params.shadingType)
continue; continue;
@ -88,7 +86,6 @@ const BackgroundCache = new Lang.Class({
} }
this._patterns.push(content); this._patterns.push(content);
return content; return content;
}, },
@ -116,9 +113,9 @@ const BackgroundCache = new Lang.Class({
_removeContent: function(contentList, content) { _removeContent: function(contentList, content) {
let index = contentList.indexOf(content); let index = contentList.indexOf(content);
if (index < 0)
if (index >= 0) throw new Error("Trying to remove invalid content: " + content);
contentList.splice(index, 1); contentList.splice(index, 1);
}, },
removePatternContent: function(content) { removePatternContent: function(content) {
@ -128,12 +125,53 @@ const BackgroundCache = new Lang.Class({
removeImageContent: function(content) { removeImageContent: function(content) {
let filename = content.get_filename(); let filename = content.get_filename();
if (filename && this._fileMonitors[filename]) let hasOtherUsers = this._images.some(function(content) { return filename == content.get_filename(); });
if (!hasOtherUsers)
delete this._fileMonitors[filename]; delete this._fileMonitors[filename];
this._removeContent(this._images, content); this._removeContent(this._images, content);
}, },
_loadImageContentInternal: function(filename, style) {
let cancellable = new Gio.Cancellable();
let content = new Meta.Background({ meta_screen: global.screen });
let info = { filename: filename,
style: style,
cancellable: cancellable,
callers: [] };
content.load_file_async(filename, style, cancellable, Lang.bind(this, function(object, result) {
if (cancellable.is_cancelled())
return;
try {
content.load_file_finish(result);
} catch(e) {
content = null;
}
if (content) {
this._monitorFile(filename);
info.callers.forEach(Lang.bind(this, function(caller) {
let newContent = content.copy(caller.monitorIndex, caller.effects);
this._images.push(newContent);
caller.onFinished(newContent);
}));
} else {
info.callers.forEach(Lang.bind(this, function(caller) {
caller.onFinished(null);
}));
}
let idx = this._pendingFileLoads.indexOf(info);
this._pendingFileLoads.splice(idx, 1);
}));
this._pendingFileLoads.push(info);
return info;
},
_loadImageContent: function(params) { _loadImageContent: function(params) {
params = Params.parse(params, { monitorIndex: 0, params = Params.parse(params, { monitorIndex: 0,
style: null, style: null,
@ -142,63 +180,38 @@ const BackgroundCache = new Lang.Class({
cancellable: null, cancellable: null,
onFinished: null }); onFinished: null });
let caller = { monitorIndex: params.monitorIndex,
effects: params.effects,
cancellable: params.cancellable,
onFinished: params.onFinished };
let info = null;
for (let i = 0; i < this._pendingFileLoads.length; i++) { for (let i = 0; i < this._pendingFileLoads.length; i++) {
if (this._pendingFileLoads[i].filename == params.filename && let pendingLoad = this._pendingFileLoads[i];
this._pendingFileLoads[i].style == params.style) { if (pendingLoad.filename == params.filename && pendingLoad.style == params.style) {
this._pendingFileLoads[i].callers.push({ shouldCopy: true, info = pendingLoad;
monitorIndex: params.monitorIndex, break;
effects: params.effects,
onFinished: params.onFinished });
return;
} }
} }
this._pendingFileLoads.push({ filename: params.filename, if (!info)
style: params.style, info = this._loadImageContentInternal(params.filename, params.style);
callers: [{ shouldCopy: false,
monitorIndex: params.monitorIndex,
effects: params.effects,
onFinished: params.onFinished }] });
let content = new Meta.Background({ meta_screen: global.screen, info.callers.push(caller);
monitor: params.monitorIndex,
effects: params.effects });
content.load_file_async(params.filename, if (caller.cancellable) {
params.style, caller.cancellable.connect(Lang.bind(this, function() {
params.cancellable, let idx = info.callers.indexOf(caller);
Lang.bind(this, info.callers.splice(idx, 1);
function(object, result) {
try {
content.load_file_finish(result);
this._monitorFile(params.filename); if (info.callers.length == 0) {
this._images.push(content); info.cancellable.cancel();
} catch(e) {
content = null;
}
for (let i = 0; i < this._pendingFileLoads.length; i++) { let idx = this._pendingFileLoads.indexOf(info);
let pendingLoad = this._pendingFileLoads[i]; this._pendingFileLoads.splice(idx, 1);
if (pendingLoad.filename != params.filename || }
pendingLoad.style != params.style) }));
continue; }
for (let j = 0; j < pendingLoad.callers.length; j++) {
if (pendingLoad.callers[j].onFinished) {
if (content && pendingLoad.callers[j].shouldCopy) {
content = object.copy(pendingLoad.callers[j].monitorIndex,
pendingLoad.callers[j].effects);
}
pendingLoad.callers[j].onFinished(content);
}
}
this._pendingFileLoads.splice(i, 1);
}
}));
}, },
getImageContent: function(params) { getImageContent: function(params) {
@ -210,11 +223,9 @@ const BackgroundCache = new Lang.Class({
onFinished: null }); onFinished: null });
let content = null; let content = null;
let candidateContent = null; let candidateContent = null;
for (let i = 0; i < this._images.length; i++) { for (let i = 0; i < this._images.length; i++) {
if (!this._images[i])
continue;
if (this._images[i].get_style() != params.style) if (this._images[i].get_style() != params.style)
continue; continue;
@ -222,7 +233,7 @@ const BackgroundCache = new Lang.Class({
continue; continue;
if (params.style == GDesktopEnums.BackgroundStyle.SPANNED && if (params.style == GDesktopEnums.BackgroundStyle.SPANNED &&
this._images[i].monitor_index != this._monitorIndex) this._images[i].monitor != params.monitorIndex)
continue; continue;
candidateContent = this._images[i]; candidateContent = this._images[i];
@ -250,7 +261,6 @@ const BackgroundCache = new Lang.Class({
monitorIndex: params.monitorIndex, monitorIndex: params.monitorIndex,
cancellable: params.cancellable, cancellable: params.cancellable,
onFinished: params.onFinished }); onFinished: params.onFinished });
} }
}, },
@ -262,6 +272,7 @@ const BackgroundCache = new Lang.Class({
if (params.onLoaded) { if (params.onLoaded) {
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() { GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
params.onLoaded(this._animation); params.onLoaded(this._animation);
return GLib.SOURCE_REMOVE;
})); }));
} }
} }
@ -276,6 +287,7 @@ const BackgroundCache = new Lang.Class({
if (params.onLoaded) { if (params.onLoaded) {
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() { GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
params.onLoaded(this._animation); params.onLoaded(this._animation);
return GLib.SOURCE_REMOVE;
})); }));
} }
})); }));
@ -315,13 +327,12 @@ const Background = new Lang.Class({
this._brightness = 1.0; this._brightness = 1.0;
this._vignetteSharpness = 0.2; this._vignetteSharpness = 0.2;
this._saturation = 1.0;
this._cancellable = new Gio.Cancellable(); this._cancellable = new Gio.Cancellable();
this.isLoaded = false; this.isLoaded = false;
this._settings.connect('changed', Lang.bind(this, function() { this._settingsChangedSignalId = this._settings.connect('changed', Lang.bind(this, function() {
this.emit('changed'); this.emit('changed');
})); }));
this._load(); this._load();
}, },
@ -362,6 +373,10 @@ const Background = new Lang.Class({
this.actor.disconnect(this._destroySignalId); this.actor.disconnect(this._destroySignalId);
this._destroySignalId = 0; this._destroySignalId = 0;
if (this._settingsChangedSignalId != 0)
this._settings.disconnect(this._settingsChangedSignalId);
this._settingsChangedSignalId = 0;
}, },
_setLoaded: function() { _setLoaded: function() {
@ -372,7 +387,7 @@ const Background = new Lang.Class({
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() { GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
this.emit('loaded'); this.emit('loaded');
return false; return GLib.SOURCE_REMOVE;
})); }));
}, },
@ -411,29 +426,26 @@ const Background = new Lang.Class({
this._fileWatches[filename] = signalId; this._fileWatches[filename] = signalId;
}, },
_addImage: function(content, index, filename) { _ensureImage: function(index) {
content.saturation = this._saturation; if (this._images[index])
content.brightness = this._brightness; return;
content.vignette_sharpness = this._vignetteSharpness;
let actor = new Meta.BackgroundActor(); let actor = new Meta.BackgroundActor();
actor.content = content;
// The background pattern is the first actor in // The background pattern is the first actor in
// the group, and all images should be above that. // the group, and all images should be above that.
this.actor.insert_child_at_index(actor, index + 1); this.actor.insert_child_at_index(actor, index + 1);
this._images[index] = actor; this._images[index] = actor;
this._watchCacheFile(filename);
}, },
_updateImage: function(content, index, filename) { _updateImage: function(index, content, filename) {
content.saturation = this._saturation;
content.brightness = this._brightness; content.brightness = this._brightness;
content.vignette_sharpness = this._vignetteSharpness; content.vignette_sharpness = this._vignetteSharpness;
this._cache.removeImageContent(this._images[index].content); let image = this._images[index];
this._images[index].content = content; if (image.content)
this._cache.removeImageContent(content);
image.content = content;
this._watchCacheFile(filename); this._watchCacheFile(filename);
}, },
@ -481,11 +493,8 @@ const Background = new Lang.Class({
return; return;
} }
if (!this._images[i]) { this._ensureImage(i);
this._addImage(content, i, files[i]); this._updateImage(i, content, files[i]);
} else {
this._updateImage(content, i, files[i]);
}
if (numPendingImages == 0) { if (numPendingImages == 0) {
this._setLoaded(); this._setLoaded();
@ -520,7 +529,7 @@ const Background = new Lang.Class({
Lang.bind(this, function() { Lang.bind(this, function() {
this._updateAnimationTimeoutId = 0; this._updateAnimationTimeoutId = 0;
this._updateAnimation(); this._updateAnimation();
return false; return GLib.SOURCE_REMOVE;
})); }));
}, },
@ -540,30 +549,33 @@ const Background = new Lang.Class({
}); });
}, },
_loadFile: function(filename) { _loadImage: function(filename) {
this._cache.getImageContent({ monitorIndex: this._monitorIndex, this._cache.getImageContent({ monitorIndex: this._monitorIndex,
effects: this._effects, effects: this._effects,
style: this._style, style: this._style,
filename: filename, filename: filename,
cancellable: this._cancellable, cancellable: this._cancellable,
onFinished: Lang.bind(this, function(content) { onFinished: Lang.bind(this, function(content) {
if (!content) { if (content) {
if (!this._cancellable.is_cancelled()) this._ensureImage(0);
this._loadAnimation(filename); this._updateImage(0, content, filename);
return;
} }
this._addImage(content, 0, filename);
this._setLoaded(); this._setLoaded();
}) })
}); });
},
_loadFile: function(filename) {
if (filename.endsWith('.xml'))
this._loadAnimation(filename);
else
this._loadImage(filename);
}, },
_load: function () { _load: function () {
this._cache = getBackgroundCache(); this._cache = getBackgroundCache();
this._loadPattern(this._cache); this._loadPattern();
this._style = this._settings.get_enum(BACKGROUND_STYLE_KEY); this._style = this._settings.get_enum(BACKGROUND_STYLE_KEY);
if (this._style == GDesktopEnums.BackgroundStyle.NONE) { if (this._style == GDesktopEnums.BackgroundStyle.NONE) {
@ -586,24 +598,6 @@ const Background = new Lang.Class({
this._loadFile(filename); 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() { get brightness() {
return this._brightness; return this._brightness;
}, },
@ -640,25 +634,6 @@ const Background = new Lang.Class({
}); });
Signals.addSignalMethods(Background.prototype); 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({ const Animation = new Lang.Class({
Name: 'Animation', Name: 'Animation',
@ -743,30 +718,30 @@ const BackgroundManager = new Lang.Class({
} }
}, },
_updateBackground: function(background, monitorIndex) { _updateBackground: function() {
let newBackground = this._createBackground(monitorIndex); let newBackground = this._createBackground();
newBackground.vignetteSharpness = background.vignetteSharpness; newBackground.vignetteSharpness = this.background.vignetteSharpness;
newBackground.brightness = background.brightness; newBackground.brightness = this.background.brightness;
newBackground.saturation = background.saturation; newBackground.visible = this.background.visible;
newBackground.visible = background.visible;
newBackground.loadedSignalId = newBackground.connect('loaded', newBackground.loadedSignalId = newBackground.connect('loaded',
Lang.bind(this, function() { Lang.bind(this, function() {
newBackground.disconnect(newBackground.loadedSignalId); newBackground.disconnect(newBackground.loadedSignalId);
newBackground.loadedSignalId = 0; newBackground.loadedSignalId = 0;
Tweener.addTween(background.actor, Tweener.addTween(this.background.actor,
{ opacity: 0, { opacity: 0,
time: FADE_ANIMATION_TIME, time: FADE_ANIMATION_TIME,
transition: 'easeOutQuad', transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() { onComplete: Lang.bind(this, function() {
if (this._newBackground == newBackground) { if (this._newBackground != newBackground) {
this.background = newBackground; /* Not interesting, we queued another load */
this._newBackground = null;
} else {
newBackground.actor.destroy(); newBackground.actor.destroy();
return;
} }
background.actor.destroy(); this.background.actor.destroy();
this.background = newBackground;
this._newBackground = null;
this.emit('changed'); this.emit('changed');
}) })
@ -794,7 +769,7 @@ const BackgroundManager = new Lang.Class({
background.changeSignalId = background.connect('changed', Lang.bind(this, function() { background.changeSignalId = background.connect('changed', Lang.bind(this, function() {
background.disconnect(background.changeSignalId); background.disconnect(background.changeSignalId);
background.changeSignalId = 0; background.changeSignalId = 0;
this._updateBackground(background, this._monitorIndex); this._updateBackground();
})); }));
background.actor.connect('destroy', Lang.bind(this, function() { background.actor.connect('destroy', Lang.bind(this, function() {

View File

@ -13,8 +13,8 @@ const BackgroundMenu = new Lang.Class({
Name: 'BackgroundMenu', Name: 'BackgroundMenu',
Extends: PopupMenu.PopupMenu, Extends: PopupMenu.PopupMenu,
_init: function(source) { _init: function(layoutManager) {
this.parent(source, 0, St.Side.TOP); this.parent(layoutManager.dummyCursor, 0, St.Side.TOP);
this.addSettingsAction(_("Settings"), 'gnome-control-center.desktop'); this.addSettingsAction(_("Settings"), 'gnome-control-center.desktop');
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
@ -22,23 +22,20 @@ const BackgroundMenu = new Lang.Class({
this.actor.add_style_class_name('background-menu'); this.actor.add_style_class_name('background-menu');
Main.uiGroup.add_actor(this.actor); layoutManager.menuGroup.add_actor(this.actor);
this.actor.hide(); this.actor.hide();
} }
}); });
function addBackgroundMenu(actor) { function addBackgroundMenu(actor, layoutManager) {
let cursor = new St.Bin({ opacity: 0 });
Main.uiGroup.add_actor(cursor);
actor.reactive = true; actor.reactive = true;
actor._backgroundMenu = new BackgroundMenu(cursor); actor._backgroundMenu = new BackgroundMenu(layoutManager);
actor._backgroundManager = new PopupMenu.PopupMenuManager({ actor: actor }); actor._backgroundManager = new PopupMenu.PopupMenuManager({ actor: actor });
actor._backgroundManager.addMenu(actor._backgroundMenu); actor._backgroundManager.addMenu(actor._backgroundMenu);
function openMenu() { function openMenu() {
let [x, y] = global.get_pointer(); let [x, y] = global.get_pointer();
cursor.set_position(x, y); Main.layoutManager.setDummyCursorPosition(x, y);
actor._backgroundMenu.open(BoxPointer.PopupAnimation.NONE); actor._backgroundMenu.open(BoxPointer.PopupAnimation.NONE);
} }
@ -59,10 +56,8 @@ function addBackgroundMenu(actor) {
actor.add_action(clickAction); actor.add_action(clickAction);
actor.connect('destroy', function() { actor.connect('destroy', function() {
actor._backgroundMenu.destroy(); actor._backgroundMenu.destroy();
actor._backgroundMenu = null; actor._backgroundMenu = null;
actor._backgroundManager = null; actor._backgroundManager = null;
});
cursor.destroy();
});
} }

View File

@ -69,7 +69,7 @@ const BoxPointer = new Lang.Class({
_muteInput: function() { _muteInput: function() {
if (this._capturedEventId == 0) if (this._capturedEventId == 0)
this._capturedEventId = this.actor.connect('captured-event', this._capturedEventId = this.actor.connect('captured-event',
function() { return true; }); function() { return Clutter.EVENT_STOP; });
}, },
_unmuteInput: function() { _unmuteInput: function() {
@ -121,6 +121,9 @@ const BoxPointer = new Lang.Class({
}, },
hide: function(animate, onComplete) { hide: function(animate, onComplete) {
if (!this.actor.visible)
return;
let xOffset = 0; let xOffset = 0;
let yOffset = 0; let yOffset = 0;
let themeNode = this.actor.get_theme_node(); let themeNode = this.actor.get_theme_node();
@ -185,7 +188,9 @@ const BoxPointer = new Lang.Class({
}, },
_getPreferredHeight: function(actor, forWidth, alloc) { _getPreferredHeight: function(actor, forWidth, alloc) {
let [minSize, naturalSize] = this.bin.get_preferred_height(forWidth); let themeNode = this.actor.get_theme_node();
let borderWidth = themeNode.get_length('-arrow-border-width');
let [minSize, naturalSize] = this.bin.get_preferred_height(forWidth - 2 * borderWidth);
alloc.min_size = minSize; alloc.min_size = minSize;
alloc.natural_size = naturalSize; alloc.natural_size = naturalSize;
this._adjustAllocationForArrow(false, alloc); this._adjustAllocationForArrow(false, alloc);
@ -282,38 +287,40 @@ const BoxPointer = new Lang.Class({
let skipBottomLeft = false; let skipBottomLeft = false;
let skipBottomRight = false; let skipBottomRight = false;
switch (this._arrowSide) { if (rise) {
case St.Side.TOP: switch (this._arrowSide) {
if (this._arrowOrigin == x1) case St.Side.TOP:
skipTopLeft = true; if (this._arrowOrigin == x1)
else if (this._arrowOrigin == x2) skipTopLeft = true;
skipTopRight = true; else if (this._arrowOrigin == x2)
break; skipTopRight = true;
break;
case St.Side.RIGHT: case St.Side.RIGHT:
if (this._arrowOrigin == y1) if (this._arrowOrigin == y1)
skipTopRight = true; skipTopRight = true;
else if (this._arrowOrigin == y2) else if (this._arrowOrigin == y2)
skipBottomRight = true; skipBottomRight = true;
break; break;
case St.Side.BOTTOM: case St.Side.BOTTOM:
if (this._arrowOrigin == x1) if (this._arrowOrigin == x1)
skipBottomLeft = true; skipBottomLeft = true;
else if (this._arrowOrigin == x2) else if (this._arrowOrigin == x2)
skipBottomRight = true; skipBottomRight = true;
break; break;
case St.Side.LEFT: case St.Side.LEFT:
if (this._arrowOrigin == y1) if (this._arrowOrigin == y1)
skipTopLeft = true; skipTopLeft = true;
else if (this._arrowOrigin == y2) else if (this._arrowOrigin == y2)
skipBottomLeft = true; skipBottomLeft = true;
break; break;
}
} }
cr.moveTo(x1 + borderRadius, y1); cr.moveTo(x1 + borderRadius, y1);
if (this._arrowSide == St.Side.TOP) { if (this._arrowSide == St.Side.TOP && rise) {
if (skipTopLeft) { if (skipTopLeft) {
cr.moveTo(x1, y2 - borderRadius); cr.moveTo(x1, y2 - borderRadius);
cr.lineTo(x1, y1 - rise); cr.lineTo(x1, y1 - rise);
@ -335,7 +342,7 @@ const BoxPointer = new Lang.Class({
3*Math.PI/2, Math.PI*2); 3*Math.PI/2, Math.PI*2);
} }
if (this._arrowSide == St.Side.RIGHT) { if (this._arrowSide == St.Side.RIGHT && rise) {
if (skipTopRight) { if (skipTopRight) {
cr.lineTo(x2 + rise, y1); cr.lineTo(x2 + rise, y1);
cr.lineTo(x2 + rise, y1 + halfBase); cr.lineTo(x2 + rise, y1 + halfBase);
@ -356,7 +363,7 @@ const BoxPointer = new Lang.Class({
0, Math.PI/2); 0, Math.PI/2);
} }
if (this._arrowSide == St.Side.BOTTOM) { if (this._arrowSide == St.Side.BOTTOM && rise) {
if (skipBottomLeft) { if (skipBottomLeft) {
cr.lineTo(x1 + halfBase, y2); cr.lineTo(x1 + halfBase, y2);
cr.lineTo(x1, y2 + rise); cr.lineTo(x1, y2 + rise);
@ -377,7 +384,7 @@ const BoxPointer = new Lang.Class({
Math.PI/2, Math.PI); Math.PI/2, Math.PI);
} }
if (this._arrowSide == St.Side.LEFT) { if (this._arrowSide == St.Side.LEFT && rise) {
if (skipTopLeft) { if (skipTopLeft) {
cr.lineTo(x1, y1 + halfBase); cr.lineTo(x1, y1 + halfBase);
cr.lineTo(x1 - rise, y1); cr.lineTo(x1 - rise, y1);

View File

@ -17,16 +17,18 @@ const SHOW_WEEKDATE_KEY = 'show-weekdate';
// in org.gnome.desktop.interface // in org.gnome.desktop.interface
const CLOCK_FORMAT_KEY = 'clock-format'; const CLOCK_FORMAT_KEY = 'clock-format';
function _sameDay(dateA, dateB) {
return (dateA.getDate() == dateB.getDate() &&
dateA.getMonth() == dateB.getMonth() &&
dateA.getYear() == dateB.getYear());
}
function _sameYear(dateA, dateB) { function _sameYear(dateA, dateB) {
return (dateA.getYear() == dateB.getYear()); return (dateA.getYear() == dateB.getYear());
} }
function _sameMonth(dateA, dateB) {
return _sameYear(dateA, dateB) && (dateA.getMonth() == dateB.getMonth());
}
function _sameDay(dateA, dateB) {
return _sameMonth(dateA, dateB) && (dateA.getDate() == dateB.getDate());
}
/* TODO: maybe needs config - right now we assume that Saturday and /* TODO: maybe needs config - right now we assume that Saturday and
* Sunday are non-work days (not true in e.g. Israel, it's Sunday and * Sunday are non-work days (not true in e.g. Israel, it's Sunday and
* Monday there) * Monday there)
@ -190,16 +192,18 @@ const EmptyEventSource = new Lang.Class({
}); });
Signals.addSignalMethods(EmptyEventSource.prototype); Signals.addSignalMethods(EmptyEventSource.prototype);
const CalendarServerIface = <interface name="org.gnome.Shell.CalendarServer"> const CalendarServerIface = '<node> \
<method name="GetEvents"> <interface name="org.gnome.Shell.CalendarServer"> \
<arg type="x" direction="in" /> <method name="GetEvents"> \
<arg type="x" direction="in" /> <arg type="x" direction="in" /> \
<arg type="b" direction="in" /> <arg type="x" direction="in" /> \
<arg type="a(sssbxxa{sv})" direction="out" /> <arg type="b" direction="in" /> \
</method> <arg type="a(sssbxxa{sv})" direction="out" /> \
<property name="HasCalendars" type="b" access="read" /> </method> \
<signal name="Changed" /> <property name="HasCalendars" type="b" access="read" /> \
</interface>; <signal name="Changed" /> \
</interface> \
</node>';
const CalendarServerInfo = Gio.DBusInterfaceInfo.new_for_xml(CalendarServerIface); const CalendarServerInfo = Gio.DBusInterfaceInfo.new_for_xml(CalendarServerIface);
@ -327,25 +331,22 @@ const DBusEventSource = new Lang.Class({
return; return;
if (this._curRequestBegin && this._curRequestEnd){ if (this._curRequestBegin && this._curRequestEnd){
let callFlags = Gio.DBusCallFlags.NO_AUTO_START;
if (forceReload)
callFlags = Gio.DBusCallFlags.NONE;
this._dbusProxy.GetEventsRemote(this._curRequestBegin.getTime() / 1000, this._dbusProxy.GetEventsRemote(this._curRequestBegin.getTime() / 1000,
this._curRequestEnd.getTime() / 1000, this._curRequestEnd.getTime() / 1000,
forceReload, forceReload,
Lang.bind(this, this._onEventsReceived), Lang.bind(this, this._onEventsReceived),
callFlags); Gio.DBusCallFlags.NONE);
} }
}, },
requestRange: function(begin, end, forceReload) { requestRange: function(begin, end) {
if (forceReload || !(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) { if (!(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
this.isLoading = true; this.isLoading = true;
this._lastRequestBegin = begin; this._lastRequestBegin = begin;
this._lastRequestEnd = end; this._lastRequestEnd = end;
this._curRequestBegin = begin; this._curRequestBegin = begin;
this._curRequestEnd = end; this._curRequestEnd = end;
this._loadEvents(forceReload); this._loadEvents(false);
} }
}, },
@ -417,21 +418,19 @@ const Calendar = new Lang.Class({
setEventSource: function(eventSource) { setEventSource: function(eventSource) {
this._eventSource = eventSource; this._eventSource = eventSource;
this._eventSource.connect('changed', Lang.bind(this, function() { this._eventSource.connect('changed', Lang.bind(this, function() {
this._update(false); this._update();
})); }));
this._update(true); this._update();
}, },
// Sets the calendar to show a specific date // Sets the calendar to show a specific date
setDate: function(date, forceReload) { setDate: function(date) {
if (!_sameDay(date, this._selectedDate)) { if (_sameDay(date, this._selectedDate))
this._selectedDate = date; return;
this._update(forceReload);
this.emit('selected-date-changed', new Date(this._selectedDate)); this._selectedDate = date;
} else { this._update();
if (forceReload) this.emit('selected-date-changed', new Date(this._selectedDate));
this._update(forceReload);
}
}, },
_buildHeader: function() { _buildHeader: function() {
@ -495,6 +494,7 @@ const Calendar = new Lang.Class({
this._onNextMonthButtonClicked(); this._onNextMonthButtonClicked();
break; break;
} }
return Clutter.EVENT_PROPAGATE;
}, },
_onPrevMonthButtonClicked: function() { _onPrevMonthButtonClicked: function() {
@ -518,7 +518,7 @@ const Calendar = new Lang.Class({
this._backButton.grab_key_focus(); this._backButton.grab_key_focus();
this.setDate(newDate, false); this.setDate(newDate);
}, },
_onNextMonthButtonClicked: function() { _onNextMonthButtonClicked: function() {
@ -542,28 +542,25 @@ const Calendar = new Lang.Class({
this._forwardButton.grab_key_focus(); this._forwardButton.grab_key_focus();
this.setDate(newDate, false); this.setDate(newDate);
}, },
_onSettingsChange: function() { _onSettingsChange: function() {
this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY); this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY);
this._buildHeader(); this._buildHeader();
this._update(false); this._update();
}, },
_update: function(forceReload) { _rebuildCalendar: function() {
let now = new Date(); let now = new Date();
if (_sameYear(this._selectedDate, now))
this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormatWithoutYear);
else
this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormat);
// Remove everything but the topBox and the weekday labels // Remove everything but the topBox and the weekday labels
let children = this.actor.get_children(); let children = this.actor.get_children();
for (let i = this._firstDayIndex; i < children.length; i++) for (let i = this._firstDayIndex; i < children.length; i++)
children[i].destroy(); children[i].destroy();
this._buttons = [];
// Start at the beginning of the week before the start of the month // 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 // We want to show always 6 weeks (to keep the calendar menu at the same
@ -581,11 +578,13 @@ const Calendar = new Lang.Class({
// Actually computing the number of weeks is complex, but we know that the // 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 // problematic categories (2 and 4) always start on week start, and that
// all months at the end have 6 weeks. // all months at the end have 6 weeks.
let beginDate = new Date(this._selectedDate); let beginDate = new Date(this._selectedDate);
beginDate.setDate(1); beginDate.setDate(1);
beginDate.setSeconds(0); beginDate.setSeconds(0);
beginDate.setHours(12); beginDate.setHours(12);
this._calendarBegin = new Date(beginDate);
let year = beginDate.getYear(); let year = beginDate.getYear();
let daysToWeekStart = (7 + beginDate.getDay() - this._weekStart) % 7; let daysToWeekStart = (7 + beginDate.getDay() - this._weekStart) % 7;
@ -606,23 +605,18 @@ const Calendar = new Lang.Class({
if (this._eventSource.isDummy) if (this._eventSource.isDummy)
button.reactive = false; button.reactive = false;
let iterStr = iter.toUTCString(); button._date = new Date(iter);
button.connect('clicked', Lang.bind(this, function() { button.connect('clicked', Lang.bind(this, function() {
this._shouldDateGrabFocus = true; this.setDate(button._date);
let newlySelectedDate = new Date(iterStr);
this.setDate(newlySelectedDate, false);
this._shouldDateGrabFocus = false;
})); }));
let hasEvents = this._eventSource.hasEvents(iter); let hasEvents = this._eventSource.hasEvents(iter);
let styleClass = 'calendar-day-base calendar-day'; let styleClass = 'calendar-day-base calendar-day';
if (_isWorkDay(iter)) if (_isWorkDay(iter))
styleClass += ' calendar-work-day' styleClass += ' calendar-work-day';
else else
styleClass += ' calendar-nonwork-day' styleClass += ' calendar-nonwork-day';
// Hack used in lieu of border-collapse - see gnome-shell.css // Hack used in lieu of border-collapse - see gnome-shell.css
if (row == 2) if (row == 2)
@ -639,7 +633,7 @@ const Calendar = new Lang.Class({
styleClass += ' calendar-other-month-day'; styleClass += ' calendar-other-month-day';
if (hasEvents) if (hasEvents)
styleClass += ' calendar-day-with-events' styleClass += ' calendar-day-with-events';
button.style_class = styleClass; button.style_class = styleClass;
@ -647,12 +641,7 @@ const Calendar = new Lang.Class({
this.actor.add(button, this.actor.add(button,
{ row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7 }); { row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7 });
if (_sameDay(this._selectedDate, iter)) { this._buttons.push(button);
button.add_style_pseudo_class('active');
if (this._shouldDateGrabFocus)
button.grab_key_focus();
}
if (this._useWeekdate && iter.getDay() == 4) { if (this._useWeekdate && iter.getDay() == 4) {
let label = new St.Label({ text: _getCalendarWeekForDate(iter).toString(), let label = new St.Label({ text: _getCalendarWeekForDate(iter).toString(),
@ -666,9 +655,29 @@ const Calendar = new Lang.Class({
if (iter.getDay() == this._weekStart) if (iter.getDay() == this._weekStart)
row++; row++;
} }
// Signal to the event source that we are interested in events // Signal to the event source that we are interested in events
// only from this date range // only from this date range
this._eventSource.requestRange(beginDate, iter, forceReload); this._eventSource.requestRange(beginDate, iter);
},
_update: function() {
let now = new Date();
if (_sameYear(this._selectedDate, now))
this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormatWithoutYear);
else
this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormat);
if (!this._calendarBegin || !_sameMonth(this._selectedDate, this._calendarBegin))
this._rebuildCalendar();
this._buttons.forEach(Lang.bind(this, function(button) {
if (_sameDay(button._date, this._selectedDate))
button.add_style_pseudo_class('active');
else
button.remove_style_pseudo_class('active');
}));
} }
}); });

View File

@ -77,7 +77,7 @@ const AutomountManager = new Lang.Class({
})); }));
this._mountAllId = 0; this._mountAllId = 0;
return false; return GLib.SOURCE_REMOVE;
}, },
_onDriveConnected: function() { _onDriveConnected: function() {
@ -236,7 +236,7 @@ const AutomountManager = new Lang.Class({
_allowAutorunExpire: function(volume) { _allowAutorunExpire: function(volume) {
Mainloop.timeout_add_seconds(AUTORUN_EXPIRE_TIMEOUT_SECS, function() { Mainloop.timeout_add_seconds(AUTORUN_EXPIRE_TIMEOUT_SECS, function() {
volume.allowAutorun = false; volume.allowAutorun = false;
return false; return GLib.SOURCE_REMOVE;
}); });
} }
}); });

View File

@ -64,7 +64,7 @@ function startAppForMount(app, mount) {
try { try {
retval = app.launch(files, retval = app.launch(files,
global.create_app_launch_context()) global.create_app_launch_context(0, -1))
} catch (e) { } catch (e) {
log('Unable to launch the application ' + app.get_name() log('Unable to launch the application ' + app.get_name()
+ ': ' + e.toString()); + ': ' + e.toString());
@ -75,12 +75,14 @@ function startAppForMount(app, mount) {
/******************************************/ /******************************************/
const HotplugSnifferIface = <interface name="org.gnome.Shell.HotplugSniffer"> const HotplugSnifferIface = '<node> \
<method name="SniffURI"> <interface name="org.gnome.Shell.HotplugSniffer"> \
<arg type="s" direction="in" /> <method name="SniffURI"> \
<arg type="as" direction="out" /> <arg type="s" direction="in" /> \
</method> <arg type="as" direction="out" /> \
</interface>; </method> \
</interface> \
</node>';
const HotplugSnifferProxy = Gio.DBusProxy.makeProxyWrapper(HotplugSnifferIface); const HotplugSnifferProxy = Gio.DBusProxy.makeProxyWrapper(HotplugSnifferIface);
function HotplugSniffer() { function HotplugSniffer() {

View File

@ -13,8 +13,6 @@ const ModalDialog = imports.ui.modalDialog;
const ShellEntry = imports.ui.shellEntry; const ShellEntry = imports.ui.shellEntry;
const CheckBox = imports.ui.checkBox; const CheckBox = imports.ui.checkBox;
let prompter = null;
const KeyringDialog = new Lang.Class({ const KeyringDialog = new Lang.Class({
Name: 'KeyringDialog', Name: 'KeyringDialog',
Extends: ModalDialog.ModalDialog, Extends: ModalDialog.ModalDialog,
@ -47,7 +45,9 @@ const KeyringDialog = new Lang.Class({
this.prompt.bind_property('message', subject, 'text', GObject.BindingFlags.SYNC_CREATE); this.prompt.bind_property('message', subject, 'text', GObject.BindingFlags.SYNC_CREATE);
this._messageBox.add(subject, this._messageBox.add(subject,
{ y_fill: false, { x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START }); y_align: St.Align.START });
let description = new St.Label({ style_class: 'prompt-dialog-description' }); let description = new St.Label({ style_class: 'prompt-dialog-description' });
@ -138,6 +138,7 @@ const KeyringDialog = new Lang.Class({
warning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; warning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
warning.clutter_text.line_wrap = true; warning.clutter_text.line_wrap = true;
layout.pack(warning, 1, row); layout.pack(warning, 1, row);
layout.child_set(warning, { x_fill: false, x_align: Clutter.TableAlignment.START });
this.prompt.bind_property('warning-visible', warning, 'visible', GObject.BindingFlags.SYNC_CREATE); this.prompt.bind_property('warning-visible', warning, 'visible', GObject.BindingFlags.SYNC_CREATE);
this.prompt.bind_property('warning', warning, 'text', GObject.BindingFlags.SYNC_CREATE); this.prompt.bind_property('warning', warning, 'text', GObject.BindingFlags.SYNC_CREATE);
@ -248,11 +249,13 @@ const KeyringPrompter = new Lang.Class({
function() { function() {
let dialog = this._enabled ? new KeyringDialog() let dialog = this._enabled ? new KeyringDialog()
: new KeyringDummyDialog(); : new KeyringDummyDialog();
return dialog.prompt; this._currentPrompt = dialog.prompt;
return this._currentPrompt;
})); }));
this._dbusId = null; this._dbusId = null;
this._registered = false; this._registered = false;
this._enabled = false; this._enabled = false;
this._currentPrompt = null;
}, },
enable: function() { enable: function() {
@ -267,6 +270,10 @@ const KeyringPrompter = new Lang.Class({
disable: function() { disable: function() {
this._enabled = false; this._enabled = false;
if (this._prompter.prompting)
this._currentPrompt.cancel();
this._currentPrompt = null;
} }
}); });

View File

@ -138,6 +138,8 @@ const NetworkSecretDialog = new Lang.Class({
key: Clutter.KEY_Escape, key: Clutter.KEY_Escape,
}, },
this._okButton]); this._okButton]);
this._updateOkButton();
}, },
_updateOkButton: function() { _updateOkButton: function() {
@ -253,6 +255,7 @@ const NetworkSecretDialog = new Lang.Class({
case 'leap': case 'leap':
case 'ttls': case 'ttls':
case 'peap': case 'peap':
case 'fast':
// TTLS and PEAP are actually much more complicated, but this complication // TTLS and PEAP are actually much more complicated, but this complication
// is not visible here since we only care about phase2 authentication // is not visible here since we only care about phase2 authentication
// (and don't even care of which one) // (and don't even care of which one)
@ -306,7 +309,7 @@ const NetworkSecretDialog = new Lang.Class({
wirelessSetting = this._connection.get_setting_wireless(); wirelessSetting = this._connection.get_setting_wireless();
ssid = NetworkManager.utils_ssid_to_utf8(wirelessSetting.get_ssid()); ssid = NetworkManager.utils_ssid_to_utf8(wirelessSetting.get_ssid());
content.title = _("Authentication required by wireless network"); content.title = _("Authentication required by wireless network");
content.message = _("Passwords or encryption keys are required to access the wireless network '%s'.").format(ssid); content.message = _("Passwords or encryption keys are required to access the wireless network %s.").format(ssid);
this._getWirelessSecrets(content.secrets, wirelessSetting); this._getWirelessSecrets(content.secrets, wirelessSetting);
break; break;
case '802-3-ethernet': case '802-3-ethernet':
@ -333,7 +336,7 @@ const NetworkSecretDialog = new Lang.Class({
case 'cdma': case 'cdma':
case 'bluetooth': case 'bluetooth':
content.title = _("Mobile broadband network password"); content.title = _("Mobile broadband network password");
content.message = _("A password is required to connect to '%s'.").format(connectionSetting.get_id()); content.message = _("A password is required to connect to %s.").format(connectionSetting.get_id());
this._getMobileSecrets(content.secrets, connectionType); this._getMobileSecrets(content.secrets, connectionType);
break; break;
default: default:
@ -432,6 +435,7 @@ const VPNRequestHandler = new Lang.Class({
}, },
_vpnChildFinished: function(pid, status, requestObj) { _vpnChildFinished: function(pid, status, requestObj) {
this._childWatch = 0;
if (this._newStylePlugin) { if (this._newStylePlugin) {
// For new style plugin, all work is done in the async reading functions // For new style plugin, all work is done in the async reading functions
// Just reap the process here // Just reap the process here

View File

@ -54,7 +54,9 @@ const AuthenticationDialog = new Lang.Class({
text: _("Authentication Required") }); text: _("Authentication Required") });
messageBox.add(this._subjectLabel, messageBox.add(this._subjectLabel,
{ y_fill: false, { x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START }); y_align: St.Align.START });
this._descriptionLabel = new St.Label({ style_class: 'prompt-dialog-description', this._descriptionLabel = new St.Label({ style_class: 'prompt-dialog-description',
@ -63,7 +65,9 @@ const AuthenticationDialog = new Lang.Class({
this._descriptionLabel.clutter_text.line_wrap = true; this._descriptionLabel.clutter_text.line_wrap = true;
messageBox.add(this._descriptionLabel, messageBox.add(this._descriptionLabel,
{ y_fill: true, { x_fill: false,
y_fill: true,
x_align: St.Align.START,
y_align: St.Align.START }); y_align: St.Align.START });
if (userNames.length > 1) { if (userNames.length > 1) {
@ -95,7 +99,8 @@ const AuthenticationDialog = new Lang.Class({
if (userIsRoot) { if (userIsRoot) {
let userLabel = new St.Label(({ style_class: 'polkit-dialog-user-root-label', let userLabel = new St.Label(({ style_class: 'polkit-dialog-user-root-label',
text: userRealName })); text: userRealName }));
messageBox.add(userLabel); messageBox.add(userLabel, { x_fill: false,
x_align: St.Align.START });
} else { } else {
let userBox = new St.BoxLayout({ style_class: 'polkit-dialog-user-layout', let userBox = new St.BoxLayout({ style_class: 'polkit-dialog-user-layout',
vertical: false }); vertical: false });
@ -137,7 +142,7 @@ const AuthenticationDialog = new Lang.Class({
this._errorMessageLabel = new St.Label({ style_class: 'prompt-dialog-error-label' }); this._errorMessageLabel = new St.Label({ style_class: 'prompt-dialog-error-label' });
this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._errorMessageLabel.clutter_text.line_wrap = true; this._errorMessageLabel.clutter_text.line_wrap = true;
messageBox.add(this._errorMessageLabel); messageBox.add(this._errorMessageLabel, { x_fill: false, x_align: St.Align.START });
this._errorMessageLabel.hide(); this._errorMessageLabel.hide();
this._infoMessageLabel = new St.Label({ style_class: 'prompt-dialog-info-label' }); this._infoMessageLabel = new St.Label({ style_class: 'prompt-dialog-info-label' });

View File

@ -13,7 +13,6 @@ const Tp = imports.gi.TelepathyGLib;
const History = imports.misc.history; const History = imports.misc.history;
const Main = imports.ui.main; const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray; const MessageTray = imports.ui.messageTray;
const NotificationDaemon = imports.ui.notificationDaemon;
const Params = imports.misc.params; const Params = imports.misc.params;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
@ -416,7 +415,7 @@ const TelepathyClient = new Lang.Class({
_ensureAppSource: function() { _ensureAppSource: function() {
if (this._appSource == null) { if (this._appSource == null) {
this._appSource = new MessageTray.Source(_("Chat"), 'empathy'); this._appSource = new MessageTray.Source(_("Chat"), 'empathy');
this._appSource.policy = new NotificationDaemon.NotificationApplicationPolicy('empathy'); this._appSource.policy = new MessageTray.NotificationApplicationPolicy('empathy');
Main.messageTray.add(this._appSource); Main.messageTray.add(this._appSource);
this._appSource.connect('destroy', Lang.bind(this, function () { this._appSource.connect('destroy', Lang.bind(this, function () {
@ -447,6 +446,7 @@ const ChatSource = new Lang.Class({
this._closedId = this._channel.connect('invalidated', Lang.bind(this, this._channelClosed)); this._closedId = this._channel.connect('invalidated', Lang.bind(this, this._channelClosed));
this._notification = new ChatNotification(this); this._notification = new ChatNotification(this);
this._notification.connect('clicked', Lang.bind(this, this.open));
this._notification.setUrgency(MessageTray.Urgency.HIGH); this._notification.setUrgency(MessageTray.Urgency.HIGH);
this._notifyTimeoutId = 0; this._notifyTimeoutId = 0;
@ -488,7 +488,7 @@ const ChatSource = new Lang.Class({
}, },
_createPolicy: function() { _createPolicy: function() {
return new NotificationDaemon.NotificationApplicationPolicy('empathy'); return new MessageTray.NotificationApplicationPolicy('empathy');
}, },
_updateAlias: function() { _updateAlias: function() {
@ -545,20 +545,19 @@ const ChatSource = new Lang.Class({
this._notification.update(this._notification.title, null, { customContent: true }); this._notification.update(this._notification.title, null, { customContent: true });
}, },
open: function(notification) { open: function() {
if (this._client.is_handling_channel(this._channel)) { if (this._client.is_handling_channel(this._channel)) {
// We are handling the channel, try to pass it to Empathy // We are handling the channel, try to pass it to Empathy
this._client.delegate_channels_async([this._channel], this._client.delegate_channels_async([this._channel],
global.get_current_time(), global.get_current_time(),
'org.freedesktop.Telepathy.Client.Empathy.Chat', null); 'org.freedesktop.Telepathy.Client.Empathy.Chat', null);
} } else {
else { // We are not the handler, just ask to present the channel
// We are not the handler, just ask to present the channel let dbus = Tp.DBusDaemon.dup();
let dbus = Tp.DBusDaemon.dup(); let cd = Tp.ChannelDispatcher.new(dbus);
let cd = Tp.ChannelDispatcher.new(dbus);
cd.present_channel_async(this._channel, global.get_current_time(), null); cd.present_channel_async(this._channel, global.get_current_time(), null);
} }
}, },
_getLogMessages: function() { _getLogMessages: function() {
@ -622,7 +621,11 @@ const ChatSource = new Lang.Class({
this.notify(); this.notify();
}, },
_channelClosed: function() { destroy: function(reason) {
if (this._destroyed)
return;
this._destroyed = true;
this._channel.disconnect(this._closedId); this._channel.disconnect(this._closedId);
this._channel.disconnect(this._receivedId); this._channel.disconnect(this._receivedId);
this._channel.disconnect(this._pendingId); this._channel.disconnect(this._pendingId);
@ -632,7 +635,14 @@ const ChatSource = new Lang.Class({
this._contact.disconnect(this._notifyAvatarId); this._contact.disconnect(this._notifyAvatarId);
this._contact.disconnect(this._presenceChangedId); this._contact.disconnect(this._presenceChangedId);
this.destroy(); if (this._timestampTimeoutId)
Mainloop.source_remove(this._timestampTimeoutId);
this.parent(reason);
},
_channelClosed: function() {
this.destroy(MessageTray.NotificationDestroyedReason.SOURCE_CLOSED);
}, },
/* All messages are new messages for Telepathy sources */ /* All messages are new messages for Telepathy sources */
@ -676,7 +686,7 @@ const ChatSource = new Lang.Class({
this._notifyTimeoutId = 0; this._notifyTimeoutId = 0;
return false; return GLib.SOURCE_REMOVE;
}, },
// This is called for both messages we send from // This is called for both messages we send from
@ -961,6 +971,8 @@ const ChatNotification = new Lang.Class({
}, },
appendTimestamp: function() { appendTimestamp: function() {
this._timestampTimeoutId = 0;
let lastMessageTime = this._history[0].time; let lastMessageTime = this._history[0].time;
let lastMessageDate = new Date(lastMessageTime * 1000); let lastMessageDate = new Date(lastMessageTime * 1000);
@ -974,7 +986,7 @@ const ChatNotification = new Lang.Class({
this._filterMessages(); this._filterMessages();
return false; return GLib.SOURCE_REMOVE;
}, },
appendAliasChange: function(oldAlias, newAlias) { appendAliasChange: function(oldAlias, newAlias) {
@ -1012,7 +1024,7 @@ const ChatNotification = new Lang.Class({
this.source.setChatState(Tp.ChannelChatState.PAUSED); this.source.setChatState(Tp.ChannelChatState.PAUSED);
return false; return GLib.SOURCE_REMOVE;
}, },
_onEntryChanged: function() { _onEntryChanged: function() {
@ -1061,7 +1073,7 @@ const ApproverSource = new Lang.Class({
}, },
_createPolicy: function() { _createPolicy: function() {
return new NotificationDaemon.NotificationApplicationPolicy('empathy'); return new MessageTray.NotificationApplicationPolicy('empathy');
}, },
destroy: function() { destroy: function() {
@ -1096,22 +1108,16 @@ const RoomInviteNotification = new Lang.Class({
* for example. */ * for example. */
this.addBody(_("%s is inviting you to join %s").format(inviter.get_alias(), channel.get_identifier())); this.addBody(_("%s is inviting you to join %s").format(inviter.get_alias(), channel.get_identifier()));
this.addButton('decline', _("Decline")); this.addAction(_("Decline"), Lang.bind(this, function() {
this.addButton('accept', _("Accept")); dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE, '', function(src, result) {
src.leave_channels_finish(result);
this.connect('action-invoked', Lang.bind(this, function(self, action) { });
switch (action) { this.destroy();
case 'decline': }));
dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE, this.addAction(_("Accept"), Lang.bind(this, function() {
'', function(src, result) { dispatchOp.handle_with_time_async('', global.get_current_time(), function(src, result) {
src.leave_channels_finish(result)}); src.handle_with_time_finish(result);
break; });
case 'accept':
dispatchOp.handle_with_time_async('', global.get_current_time(),
function(src, result) {
src.handle_with_time_finish(result)});
break;
}
this.destroy(); this.destroy();
})); }));
} }
@ -1137,23 +1143,17 @@ const AudioVideoNotification = new Lang.Class({
this.setUrgency(MessageTray.Urgency.CRITICAL); this.setUrgency(MessageTray.Urgency.CRITICAL);
this.addButton('reject', _("Decline")); this.addAction(_("Decline"), Lang.bind(this, function() {
dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE, '', function(src, result) {
src.leave_channels_finish(result);
});
this.destroy();
}));
/* translators: this is a button label (verb), not a noun */ /* translators: this is a button label (verb), not a noun */
this.addButton('answer', _("Answer")); this.addAction(_("Answer"), Lang.bind(this, function() {
dispatchOp.handle_with_time_async('', global.get_current_time(), function(src, result) {
this.connect('action-invoked', Lang.bind(this, function(self, action) { src.handle_with_time_finish(result);
switch (action) { });
case 'reject':
dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE,
'', function(src, result) {
src.leave_channels_finish(result)});
break;
case 'answer':
dispatchOp.handle_with_time_async('', global.get_current_time(),
function(src, result) {
src.handle_with_time_finish(result)});
break;
}
this.destroy(); this.destroy();
})); }));
} }
@ -1177,22 +1177,16 @@ const FileTransferNotification = new Lang.Class({
{ customContent: true }); { customContent: true });
this.setResident(true); this.setResident(true);
this.addButton('decline', _("Decline")); this.addAction(_("Decline"), Lang.bind(this, function() {
this.addButton('accept', _("Accept")); dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE, '', function(src, result) {
src.leave_channels_finish(result);
this.connect('action-invoked', Lang.bind(this, function(self, action) { });
switch (action) { this.destroy();
case 'decline': }));
dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE, this.addAction(_("Accept"), Lang.bind(this, function() {
'', function(src, result) { dispatchOp.handle_with_time_async('', global.get_current_time(), function(src, result) {
src.leave_channels_finish(result)}); src.handle_with_time_finish(result);
break; });
case 'accept':
dispatchOp.handle_with_time_async('', global.get_current_time(),
function(src, result) {
src.handle_with_time_finish(result)});
break;
}
this.destroy(); this.destroy();
})); }));
} }
@ -1240,27 +1234,20 @@ const SubscriptionRequestNotification = new Lang.Class({
this.addActor(layout); this.addActor(layout);
this.addButton('decline', _("Decline")); this.addAction(_("Decline"), Lang.bind(this, function() {
this.addButton('accept', _("Accept")); contact.remove_async(function(src, result) {
src.remove_finish(result);
});
}));
this.addAction(_("Accept"), Lang.bind(this, function() {
// Authorize the contact and request to see his status as well
contact.authorize_publication_async(function(src, result) {
src.authorize_publication_finish(result);
});
this.connect('action-invoked', Lang.bind(this, function(self, action) { contact.request_subscription_async('', function(src, result) {
switch (action) { src.request_subscription_finish(result);
case 'decline': });
contact.remove_async(function(src, result) {
src.remove_finish(result)});
break;
case 'accept':
// Authorize the contact and request to see his status as well
contact.authorize_publication_async(function(src, result) {
src.authorize_publication_finish(result)});
contact.request_subscription_async('', function(src, result) {
src.request_subscription_finish(result)});
break;
}
// rely on _subscriptionStatesChangedCb to destroy the
// notification
})); }));
this._changedId = contact.connect('subscription-states-changed', this._changedId = contact.connect('subscription-states-changed',
@ -1359,18 +1346,11 @@ const AccountNotification = new Lang.Class({
this._account = account; this._account = account;
this.addButton('view', _("View account")); this.addAction(_("View account"), Lang.bind(this, function() {
let cmd = 'empathy-accounts --select-account=' +
this.connect('action-invoked', Lang.bind(this, function(self, action) { account.get_path_suffix();
switch (action) { let app_info = Gio.app_info_create_from_commandline(cmd, null, 0);
case 'view': app_info.launch([], global.create_app_launch_context(0, -1));
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;
}
this.destroy();
})); }));
this._enabledId = account.connect('notify::enabled', this._enabledId = account.connect('notify::enabled',

View File

@ -1,6 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Signals = imports.signals; const Signals = imports.signals;
const Lang = imports.lang; const Lang = imports.lang;
const Meta = imports.gi.Meta; const Meta = imports.gi.Meta;
@ -576,7 +577,8 @@ const Dash = new Lang.Class({
Lang.bind(this, function() { Lang.bind(this, function() {
this._labelShowing = true; this._labelShowing = true;
item.showLabel(); item.showLabel();
return false; this._showLabelTimeoutId = 0;
return GLib.SOURCE_REMOVE;
})); }));
if (this._resetHoverTimeoutId > 0) { if (this._resetHoverTimeoutId > 0) {
Mainloop.source_remove(this._resetHoverTimeoutId); Mainloop.source_remove(this._resetHoverTimeoutId);
@ -592,7 +594,8 @@ const Dash = new Lang.Class({
this._resetHoverTimeoutId = Mainloop.timeout_add(DASH_ITEM_HOVER_TIMEOUT, this._resetHoverTimeoutId = Mainloop.timeout_add(DASH_ITEM_HOVER_TIMEOUT,
Lang.bind(this, function() { Lang.bind(this, function() {
this._labelShowing = false; this._labelShowing = false;
return false; this._resetHoverTimeoutId = 0;
return GLib.SOURCE_REMOVE;
})); }));
} }
} }

View File

@ -113,22 +113,7 @@ const DateMenuButton = new Lang.Class({
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) { this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
if (isOpen) { if (isOpen) {
let now = new Date(); let now = new Date();
/* Passing true to setDate() forces events to be reloaded. We this._calendar.setDate(now);
* want this behavior, because
*
* o It will cause activation of the calendar server which is
* useful if it has crashed
*
* o It will cause the calendar server to reload events which
* is useful if dynamic updates are not supported or not
* properly working
*
* Since this only happens when the menu is opened, the cost
* isn't very big.
*/
this._calendar.setDate(now, true);
// No need to update this._eventList as ::selected-date-changed
// signal will fire
} }
})); }));
@ -226,7 +211,7 @@ const DateMenuButton = new Lang.Class({
let app = this._getCalendarApp(); let app = this._getCalendarApp();
if (app.get_id() == 'evolution.desktop') if (app.get_id() == 'evolution.desktop')
app = Gio.DesktopAppInfo.new('evolution-calendar.desktop'); app = Gio.DesktopAppInfo.new('evolution-calendar.desktop');
app.launch([], global.create_app_launch_context()); app.launch([], global.create_app_launch_context(0, -1));
}, },
_onOpenClocksActivate: function() { _onOpenClocksActivate: function() {

View File

@ -46,7 +46,7 @@ let dragMonitors = [];
function _getEventHandlerActor() { function _getEventHandlerActor() {
if (!eventHandlerActor) { if (!eventHandlerActor) {
eventHandlerActor = new Clutter.Actor({ width: 0, height: 0 }); eventHandlerActor = new Clutter.Actor({ width: 0, height: 0 });
Main.uiGroup.add_actor(eventHandlerActor); Main.layoutManager.sessionGroup.add_actor(eventHandlerActor);
// We connect to 'event' rather than 'captured-event' because the capturing phase doesn't happen // We connect to 'event' rather than 'captured-event' because the capturing phase doesn't happen
// when you've grabbed the pointer. // when you've grabbed the pointer.
eventHandlerActor.connect('event', eventHandlerActor.connect('event',
@ -106,10 +106,10 @@ const _Draggable = new Lang.Class({
_onButtonPress : function (actor, event) { _onButtonPress : function (actor, event) {
if (event.get_button() != 1) if (event.get_button() != 1)
return false; return Clutter.EVENT_PROPAGATE;
if (Tweener.getTweenCount(actor)) if (Tweener.getTweenCount(actor))
return false; return Clutter.EVENT_PROPAGATE;
this._buttonDown = true; this._buttonDown = true;
this._grabActor(); this._grabActor();
@ -118,7 +118,7 @@ const _Draggable = new Lang.Class({
this._dragStartX = stageX; this._dragStartX = stageX;
this._dragStartY = stageY; this._dragStartY = stageY;
return false; return Clutter.EVENT_PROPAGATE;
}, },
_grabActor: function() { _grabActor: function() {
@ -164,11 +164,11 @@ const _Draggable = new Lang.Class({
} else if (this._dragActor != null && !this._animationInProgress) { } else if (this._dragActor != null && !this._animationInProgress) {
// Drag must have been cancelled with Esc. // Drag must have been cancelled with Esc.
this._dragComplete(); this._dragComplete();
return true; return Clutter.EVENT_STOP;
} else { } else {
// Drag has never started. // Drag has never started.
this._ungrabActor(); this._ungrabActor();
return false; return Clutter.EVENT_PROPAGATE;
} }
// We intercept MOTION event to figure out if the drag has started and to draw // We intercept MOTION event to figure out if the drag has started and to draw
// this._dragActor under the pointer when dragging is in progress // this._dragActor under the pointer when dragging is in progress
@ -184,11 +184,11 @@ const _Draggable = new Lang.Class({
let symbol = event.get_key_symbol(); let symbol = event.get_key_symbol();
if (symbol == Clutter.Escape) { if (symbol == Clutter.Escape) {
this._cancelDrag(event.get_time()); this._cancelDrag(event.get_time());
return true; return Clutter.EVENT_STOP;
} }
} }
return false; return Clutter.EVENT_PROPAGATE;
}, },
/** /**
@ -236,7 +236,7 @@ const _Draggable = new Lang.Class({
if (this.actor._delegate && this.actor._delegate.getDragActor) { if (this.actor._delegate && this.actor._delegate.getDragActor) {
this._dragActor = this.actor._delegate.getDragActor(); this._dragActor = this.actor._delegate.getDragActor();
this._dragActor.reparent(Main.uiGroup); Main.layoutManager.sessionGroup.add_child(this._dragActor);
this._dragActor.raise_top(); this._dragActor.raise_top();
Shell.util_set_hidden_from_pick(this._dragActor, true); Shell.util_set_hidden_from_pick(this._dragActor, true);
@ -276,7 +276,7 @@ const _Draggable = new Lang.Class({
this._dragOrigScale = this._dragActor.scale_x; this._dragOrigScale = this._dragActor.scale_x;
// Set the actor's scale such that it will keep the same // Set the actor's scale such that it will keep the same
// transformed size when it's reparented to the uiGroup // transformed size when it's reparented to the sessionGroup
let [scaledWidth, scaledHeight] = this.actor.get_transformed_size(); let [scaledWidth, scaledHeight] = this.actor.get_transformed_size();
this._dragActor.set_scale(scaledWidth / this.actor.width, this._dragActor.set_scale(scaledWidth / this.actor.width,
scaledHeight / this.actor.height); scaledHeight / this.actor.height);
@ -285,7 +285,8 @@ const _Draggable = new Lang.Class({
this._dragOffsetX = actorStageX - this._dragStartX; this._dragOffsetX = actorStageX - this._dragStartX;
this._dragOffsetY = actorStageY - this._dragStartY; this._dragOffsetY = actorStageY - this._dragStartY;
this._dragActor.reparent(Main.uiGroup); this._dragOrigParent.remove_actor(this._dragActor);
Main.layoutManager.sessionGroup.add_child(this._dragActor);
this._dragActor.raise_top(); this._dragActor.raise_top();
Shell.util_set_hidden_from_pick(this._dragActor, true); Shell.util_set_hidden_from_pick(this._dragActor, true);
} }
@ -345,6 +346,7 @@ const _Draggable = new Lang.Class({
}, },
_updateDragHover : function () { _updateDragHover : function () {
this._updateHoverId = 0;
let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL, let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
this._dragX, this._dragY); this._dragX, this._dragY);
let dragEvent = { let dragEvent = {
@ -360,7 +362,7 @@ const _Draggable = new Lang.Class({
let result = motionFunc(dragEvent); let result = motionFunc(dragEvent);
if (result != DragMotionResult.CONTINUE) { if (result != DragMotionResult.CONTINUE) {
global.screen.set_cursor(DRAG_CURSOR_MAP[result]); global.screen.set_cursor(DRAG_CURSOR_MAP[result]);
return false; return GLib.SOURCE_REMOVE;
} }
} }
} }
@ -378,18 +380,18 @@ const _Draggable = new Lang.Class({
0); 0);
if (result != DragMotionResult.CONTINUE) { if (result != DragMotionResult.CONTINUE) {
global.screen.set_cursor(DRAG_CURSOR_MAP[result]); global.screen.set_cursor(DRAG_CURSOR_MAP[result]);
return false; return GLib.SOURCE_REMOVE;
} }
} }
target = target.get_parent(); target = target.get_parent();
} }
global.screen.set_cursor(Meta.Cursor.DND_IN_DRAG); global.screen.set_cursor(Meta.Cursor.DND_IN_DRAG);
return false; return GLib.SOURCE_REMOVE;
}, },
_queueUpdateDragHover: function() { _queueUpdateDragHover: function() {
if (this._updateHoverId) if (this._updateHoverId)
GLib.source_remove(this._updateHoverId); return;
this._updateHoverId = GLib.idle_add(GLib.PRIORITY_DEFAULT, this._updateHoverId = GLib.idle_add(GLib.PRIORITY_DEFAULT,
Lang.bind(this, this._updateDragHover)); Lang.bind(this, this._updateDragHover));
@ -446,7 +448,7 @@ const _Draggable = new Lang.Class({
event.get_time())) { event.get_time())) {
// If it accepted the drop without taking the actor, // If it accepted the drop without taking the actor,
// handle it ourselves. // handle it ourselves.
if (this._dragActor.get_parent() == Main.uiGroup) { if (this._dragActor.get_parent() == Main.layoutManager.sessionGroup) {
if (this._restoreOnSuccess) { if (this._restoreOnSuccess) {
this._restoreDragActor(event.get_time()); this._restoreDragActor(event.get_time());
return true; return true;
@ -555,7 +557,8 @@ const _Draggable = new Lang.Class({
_onAnimationComplete : function (dragActor, eventTime) { _onAnimationComplete : function (dragActor, eventTime) {
if (this._dragOrigParent) { if (this._dragOrigParent) {
dragActor.reparent(this._dragOrigParent); Main.layoutManager.sessionGroup.remove_child(this._dragActor);
this._dragOrigParent.add_actor(this._dragActor);
dragActor.set_scale(this._dragOrigScale, this._dragOrigScale); dragActor.set_scale(this._dragOrigScale, this._dragOrigScale);
dragActor.set_position(this._dragOrigX, this._dragOrigY); dragActor.set_position(this._dragOrigX, this._dragOrigY);
} else { } else {

View File

@ -13,9 +13,7 @@
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, see <http://www.gnu.org/licenses/>.
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/ */
const Lang = imports.lang; const Lang = imports.lang;
@ -43,20 +41,22 @@ const _DIALOG_ICON_SIZE = 32;
const GSM_SESSION_MANAGER_LOGOUT_FORCE = 2; const GSM_SESSION_MANAGER_LOGOUT_FORCE = 2;
const EndSessionDialogIface = <interface name="org.gnome.SessionManager.EndSessionDialog"> const EndSessionDialogIface = '<node> \
<method name="Open"> <interface name="org.gnome.SessionManager.EndSessionDialog"> \
<arg type="u" direction="in" /> <method name="Open"> \
<arg type="u" direction="in" /> <arg type="u" direction="in" /> \
<arg type="u" direction="in" /> <arg type="u" direction="in" /> \
<arg type="ao" direction="in" /> <arg type="u" direction="in" /> \
</method> <arg type="ao" direction="in" /> \
<method name="Close" /> </method> \
<signal name="ConfirmedLogout" /> <method name="Close" /> \
<signal name="ConfirmedReboot" /> <signal name="ConfirmedLogout" /> \
<signal name="ConfirmedShutdown" /> <signal name="ConfirmedReboot" /> \
<signal name="Canceled" /> <signal name="ConfirmedShutdown" /> \
<signal name="Closed" /> <signal name="Canceled" /> \
</interface>; <signal name="Closed" /> \
</interface> \
</node>';
const logoutDialogContent = { const logoutDialogContent = {
subjectWithUser: C_("title", "Log Out %s"), subjectWithUser: C_("title", "Log Out %s"),
@ -131,13 +131,15 @@ const DialogContent = {
const MAX_USERS_IN_SESSION_DIALOG = 5; const MAX_USERS_IN_SESSION_DIALOG = 5;
const LogindSessionIface = <interface name='org.freedesktop.login1.Session'> const LogindSessionIface = '<node> \
<property name="Id" type="s" access="read"/> <interface name="org.freedesktop.login1.Session"> \
<property name="Remote" type="b" access="read"/> <property name="Id" type="s" access="read"/> \
<property name="Class" type="s" access="read"/> <property name="Remote" type="b" access="read"/> \
<property name="Type" type="s" access="read"/> <property name="Class" type="s" access="read"/> \
<property name="State" type="s" access="read"/> <property name="Type" type="s" access="read"/> \
</interface>; <property name="State" type="s" access="read"/> \
</interface> \
</node>';
const LogindSession = Gio.DBusProxy.makeProxyWrapper(LogindSessionIface); const LogindSession = Gio.DBusProxy.makeProxyWrapper(LogindSessionIface);
@ -245,7 +247,9 @@ const EndSessionDialog = new Lang.Class({
this._subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject' }); this._subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject' });
messageLayout.add(this._subjectLabel, messageLayout.add(this._subjectLabel,
{ y_fill: false, { x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START }); y_align: St.Align.START });
this._descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description' }); this._descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description' });
@ -269,13 +273,15 @@ const EndSessionDialog = new Lang.Class({
this._applicationHeader = new St.Label({ style_class: 'end-session-dialog-list-header', this._applicationHeader = new St.Label({ style_class: 'end-session-dialog-list-header',
text: _("Some applications are busy or have unsaved work.") }); text: _("Some applications are busy or have unsaved work.") });
this._applicationList = new St.BoxLayout({ vertical: true }); this._applicationList = new St.BoxLayout({ style_class: 'end-session-dialog-app-list',
vertical: true });
this._inhibitorSection.add_actor(this._applicationHeader); this._inhibitorSection.add_actor(this._applicationHeader);
this._inhibitorSection.add_actor(this._applicationList); this._inhibitorSection.add_actor(this._applicationList);
this._sessionHeader = new St.Label({ style_class: 'end-session-dialog-list-header', this._sessionHeader = new St.Label({ style_class: 'end-session-dialog-list-header',
text: _("Other users are logged in.") }); text: _("Other users are logged in.") });
this._sessionList = new St.BoxLayout({ vertical: true }); this._sessionList = new St.BoxLayout({ style_class: 'end-session-dialog-session-list',
vertical: true });
this._inhibitorSection.add_actor(this._sessionHeader); this._inhibitorSection.add_actor(this._sessionHeader);
this._inhibitorSection.add_actor(this._sessionList); this._inhibitorSection.add_actor(this._sessionList);
@ -403,14 +409,15 @@ const EndSessionDialog = new Lang.Class({
this._secondsLeft = this._totalSecondsToStayOpen - secondsElapsed; this._secondsLeft = this._totalSecondsToStayOpen - secondsElapsed;
if (this._secondsLeft > 0) { if (this._secondsLeft > 0) {
this._sync(); this._sync();
return true; return GLib.SOURCE_CONTINUE;
} }
let dialogContent = DialogContent[this._type]; let dialogContent = DialogContent[this._type];
let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1]; let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
this._confirm(button.signal); this._confirm(button.signal);
this._timerId = 0;
return false; return GLib.SOURCE_REMOVE;
})); }));
}, },

View File

@ -201,7 +201,7 @@ const InstallExtensionDialog = new Lang.Class({
default: true default: true
}]); }]);
let message = _("Download and install '%s' from extensions.gnome.org?").format(info.name); let message = _("Download and install %s from extensions.gnome.org?").format(info.name);
let box = new St.BoxLayout(); let box = new St.BoxLayout();
this.contentLayout.add(box); this.contentLayout.add(box);

View File

@ -76,7 +76,11 @@ function disableExtension(uuid) {
theme.unload_stylesheet(extension.stylesheet.get_path()); theme.unload_stylesheet(extension.stylesheet.get_path());
} }
extension.stateObj.disable(); try {
extension.stateObj.disable();
} catch(e) {
logExtensionError(uuid, e);
}
for (let i = 0; i < order.length; i++) { for (let i = 0; i < order.length; i++) {
let uuid = order[i]; let uuid = order[i];
@ -89,8 +93,10 @@ function disableExtension(uuid) {
extensionOrder.splice(orderIdx, 1); extensionOrder.splice(orderIdx, 1);
extension.state = ExtensionState.DISABLED; if ( extension.state != ExtensionState.ERROR ) {
_signals.emit('extension-state-changed', extension); extension.state = ExtensionState.DISABLED;
_signals.emit('extension-state-changed', extension);
}
} }
function enableExtension(uuid) { function enableExtension(uuid) {
@ -117,10 +123,15 @@ function enableExtension(uuid) {
} }
} }
extension.stateObj.enable(); try {
extension.stateObj.enable();
extension.state = ExtensionState.ENABLED; extension.state = ExtensionState.ENABLED;
_signals.emit('extension-state-changed', extension); _signals.emit('extension-state-changed', extension);
return;
} catch(e) {
logExtensionError(uuid, e);
return;
}
} }
function logExtensionError(uuid, error) { function logExtensionError(uuid, error) {
@ -150,7 +161,8 @@ function loadExtension(extension) {
} else { } else {
let enabled = enabledExtensions.indexOf(extension.uuid) != -1; let enabled = enabledExtensions.indexOf(extension.uuid) != -1;
if (enabled) { if (enabled) {
initExtension(extension.uuid); if (!initExtension(extension.uuid))
return;
if (extension.state == ExtensionState.DISABLED) if (extension.state == ExtensionState.DISABLED)
enableExtension(extension.uuid); enableExtension(extension.uuid);
} else { } else {
@ -205,7 +217,12 @@ function initExtension(uuid) {
extensionModule = extension.imports.extension; extensionModule = extension.imports.extension;
if (extensionModule.init) { if (extensionModule.init) {
extensionState = extensionModule.init(extension); try {
extensionState = extensionModule.init(extension);
} catch(e) {
logExtensionError(uuid, e);
return false;
}
} }
if (!extensionState) if (!extensionState)
@ -214,6 +231,7 @@ function initExtension(uuid) {
extension.state = ExtensionState.DISABLED; extension.state = ExtensionState.DISABLED;
_signals.emit('extension-loaded', uuid); _signals.emit('extension-loaded', uuid);
return true;
} }
function getEnabledExtensions() { function getEnabledExtensions() {
@ -235,11 +253,7 @@ function onEnabledExtensionsChanged() {
newEnabledExtensions.filter(function(uuid) { newEnabledExtensions.filter(function(uuid) {
return enabledExtensions.indexOf(uuid) == -1; return enabledExtensions.indexOf(uuid) == -1;
}).forEach(function(uuid) { }).forEach(function(uuid) {
try { enableExtension(uuid);
enableExtension(uuid);
} catch(e) {
logExtensionError(uuid, e);
}
}); });
// Find and disable all the newly disabled extensions: UUIDs found in the // Find and disable all the newly disabled extensions: UUIDs found in the
@ -247,11 +261,7 @@ function onEnabledExtensionsChanged() {
enabledExtensions.filter(function(item) { enabledExtensions.filter(function(item) {
return newEnabledExtensions.indexOf(item) == -1; return newEnabledExtensions.indexOf(item) == -1;
}).forEach(function(uuid) { }).forEach(function(uuid) {
try { disableExtension(uuid);
disableExtension(uuid);
} catch(e) {
logExtensionError(uuid, e);
}
}); });
enabledExtensions = newEnabledExtensions; enabledExtensions = newEnabledExtensions;
@ -262,12 +272,8 @@ function _loadExtensions() {
enabledExtensions = getEnabledExtensions(); enabledExtensions = getEnabledExtensions();
let finder = new ExtensionUtils.ExtensionFinder(); let finder = new ExtensionUtils.ExtensionFinder();
finder.connect('extension-found', function(signals, extension) { finder.connect('extension-found', function(finder, extension) {
try { loadExtension(extension);
loadExtension(extension);
} catch(e) {
logExtensionError(extension.uuid, e);
}
}); });
finder.scanExtensions(); finder.scanExtensions();
} }

View File

@ -280,7 +280,7 @@ const GrabHelper = new Lang.Class({
if (type == Clutter.EventType.KEY_PRESS && if (type == Clutter.EventType.KEY_PRESS &&
event.get_key_symbol() == Clutter.KEY_Escape) { event.get_key_symbol() == Clutter.KEY_Escape) {
this.ungrab({ isUser: true }); this.ungrab({ isUser: true });
return true; return Clutter.EVENT_STOP;
} }
let press = type == Clutter.EventType.BUTTON_PRESS; let press = type == Clutter.EventType.BUTTON_PRESS;
@ -289,14 +289,14 @@ const GrabHelper = new Lang.Class({
if (release && this._ignoreRelease) { if (release && this._ignoreRelease) {
this._ignoreRelease = false; this._ignoreRelease = false;
return true; return Clutter.EVENT_STOP;
} }
if (this._isWithinGrabbedActor(event.get_source())) if (this._isWithinGrabbedActor(event.get_source()))
return false; return Clutter.EVENT_PROPAGATE;
if (Main.keyboard.shouldTakeEvent(event)) if (Main.keyboard.shouldTakeEvent(event))
return false; return Clutter.EVENT_PROPAGATE;
if (button) { if (button) {
// If we have a press event, ignore the next event, // If we have a press event, ignore the next event,
@ -305,9 +305,9 @@ const GrabHelper = new Lang.Class({
this._ignoreRelease = true; this._ignoreRelease = true;
let i = this._actorInGrabStack(event.get_source()) + 1; let i = this._actorInGrabStack(event.get_source()) + 1;
this.ungrab({ actor: this._grabStack[i].actor, isUser: true }); this.ungrab({ actor: this._grabStack[i].actor, isUser: true });
return true; return Clutter.EVENT_STOP;
} }
return true; return Clutter.EVENT_STOP;
}, },
}); });

View File

@ -32,6 +32,7 @@ const CandidateArea = new Lang.Class({
let j = i; let j = i;
box.connect('button-release-event', Lang.bind(this, function(actor, event) { box.connect('button-release-event', Lang.bind(this, function(actor, event) {
this.emit('candidate-clicked', j, event.get_button(), event.get_state()); this.emit('candidate-clicked', j, event.get_button(), event.get_state());
return Clutter.EVENT_PROPAGATE;
})); }));
} }
@ -114,9 +115,6 @@ const CandidatePopup = new Lang.Class({
Name: 'CandidatePopup', Name: 'CandidatePopup',
_init: function() { _init: function() {
this._cursor = new St.Bin({ opacity: 0 });
Main.uiGroup.add_actor(this._cursor);
this._boxPointer = new BoxPointer.BoxPointer(St.Side.TOP); this._boxPointer = new BoxPointer.BoxPointer(St.Side.TOP);
this._boxPointer.actor.visible = false; this._boxPointer.actor.visible = false;
this._boxPointer.actor.style_class = 'candidate-popup-boxpointer'; this._boxPointer.actor.style_class = 'candidate-popup-boxpointer';
@ -157,10 +155,9 @@ const CandidatePopup = new Lang.Class({
panelService.connect('set-cursor-location', panelService.connect('set-cursor-location',
Lang.bind(this, function(ps, x, y, w, h) { Lang.bind(this, function(ps, x, y, w, h) {
this._cursor.set_position(x, y); Main.layoutManager.setDummyCursorPosition(x, y);
this._cursor.set_size(w, h);
if (this._boxPointer.actor.visible) if (this._boxPointer.actor.visible)
this._boxPointer.setPosition(this._cursor, 0); this._boxPointer.setPosition(Main.layoutManager.dummyCursor, 0);
})); }));
panelService.connect('update-preedit-text', panelService.connect('update-preedit-text',
Lang.bind(this, function(ps, text, cursorPosition, visible) { Lang.bind(this, function(ps, text, cursorPosition, visible) {
@ -252,7 +249,7 @@ const CandidatePopup = new Lang.Class({
this._candidateArea.actor.visible); this._candidateArea.actor.visible);
if (isVisible) { if (isVisible) {
this._boxPointer.setPosition(this._cursor, 0); this._boxPointer.setPosition(Main.layoutManager.dummyCursor, 0);
this._boxPointer.show(BoxPointer.PopupAnimation.NONE); this._boxPointer.show(BoxPointer.PopupAnimation.NONE);
this._boxPointer.actor.raise_top(); this._boxPointer.actor.raise_top();
} else { } else {

View File

@ -413,15 +413,18 @@ const IconGrid = new Lang.Class({
}, },
removeAll: function() { removeAll: function() {
this._items = [];
this._grid.remove_all_children();
},
destroyAll: function() {
this._items = []; this._items = [];
this._grid.destroy_all_children(); this._grid.destroy_all_children();
}, },
addItem: function(item, index) { addItem: function(item, index) {
if (!item.icon || !item.icon instanceof BaseIcon) { if (!item.icon instanceof BaseIcon)
log('Only items with a BaseIcon icon property can be added to IconGrid'); throw new Error('Only items with a BaseIcon icon property can be added to IconGrid');
return;
}
this._items.push(item); this._items.push(item);
if (index !== undefined) if (index !== undefined)
@ -430,6 +433,10 @@ const IconGrid = new Lang.Class({
this._grid.add_actor(item.actor); this._grid.add_actor(item.actor);
}, },
removeItem: function(item) {
this._grid.remove_child(item.actor);
},
getItemAtIndex: function(index) { getItemAtIndex: function(index) {
return this._grid.get_child_at_index(index); return this._grid.get_child_at_index(index);
}, },

View File

@ -23,27 +23,29 @@ const KEYBOARD_TYPE = 'keyboard-type';
const A11Y_APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications'; const A11Y_APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
const SHOW_KEYBOARD = 'screen-keyboard-enabled'; const SHOW_KEYBOARD = 'screen-keyboard-enabled';
const CaribouKeyboardIface = <interface name='org.gnome.Caribou.Keyboard'> const CaribouKeyboardIface = '<node> \
<method name='Show'> <interface name="org.gnome.Caribou.Keyboard"> \
<arg type='u' direction='in' /> <method name="Show"> \
</method> <arg type="u" direction="in" /> \
<method name='Hide'> </method> \
<arg type='u' direction='in' /> <method name="Hide"> \
</method> <arg type="u" direction="in" /> \
<method name='SetCursorLocation'> </method> \
<arg type='i' direction='in' /> <method name="SetCursorLocation"> \
<arg type='i' direction='in' /> <arg type="i" direction="in" /> \
<arg type='i' direction='in' /> <arg type="i" direction="in" /> \
<arg type='i' direction='in' /> <arg type="i" direction="in" /> \
</method> <arg type="i" direction="in" /> \
<method name='SetEntryLocation'> </method> \
<arg type='i' direction='in' /> <method name="SetEntryLocation"> \
<arg type='i' direction='in' /> <arg type="i" direction="in" /> \
<arg type='i' direction='in' /> <arg type="i" direction="in" /> \
<arg type='i' direction='in' /> <arg type="i" direction="in" /> \
</method> <arg type="i" direction="in" /> \
<property name='Name' access='read' type='s' /> </method> \
</interface>; <property name="Name" access="read" type="s" /> \
</interface> \
</node>';
const Key = new Lang.Class({ const Key = new Lang.Class({
Name: 'Key', Name: 'Key',
@ -80,8 +82,16 @@ const Key = new Lang.Class({
style_class: 'keyboard-key' }); style_class: 'keyboard-key' });
button.key_width = this._key.width; button.key_width = this._key.width;
button.connect('button-press-event', Lang.bind(this, function () { this._key.press(); })); button.connect('button-press-event', Lang.bind(this,
button.connect('button-release-event', Lang.bind(this, function () { this._key.release(); })); function () {
this._key.press();
return Clutter.EVENT_PROPAGATE;
}));
button.connect('button-release-event', Lang.bind(this,
function () {
this._key.release();
return Clutter.EVENT_PROPAGATE;
}));
return button; return button;
}, },
@ -104,8 +114,16 @@ const Key = new Lang.Class({
let label = this._getUnichar(extended_key); let label = this._getUnichar(extended_key);
let key = new St.Button({ label: label, style_class: 'keyboard-key' }); let key = new St.Button({ label: label, style_class: 'keyboard-key' });
key.extended_key = extended_key; key.extended_key = extended_key;
key.connect('button-press-event', Lang.bind(this, function () { extended_key.press(); })); key.connect('button-press-event', Lang.bind(this,
key.connect('button-release-event', Lang.bind(this, function () { extended_key.release(); })); function () {
extended_key.press();
return Clutter.EVENT_PROPAGATE;
}));
key.connect('button-release-event', Lang.bind(this,
function () {
extended_key.release();
return Clutter.EVENT_PROPAGATE;
}));
this._extended_keyboard.add(key); this._extended_keyboard.add(key);
} }
this._boxPointer.bin.add_actor(this._extended_keyboard); this._boxPointer.bin.add_actor(this._extended_keyboard);
@ -250,7 +268,10 @@ const Keyboard = new Lang.Class({
if (!this._showIdleId) if (!this._showIdleId)
this._showIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, this._showIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE,
Lang.bind(this, function() { this.Show(time); })); Lang.bind(this, function() {
this.Show(time);
return GLib.SOURCE_REMOVE;
}));
}, },
_createLayersForGroup: function (gname) { _createLayersForGroup: function (gname) {
@ -292,7 +313,7 @@ const Keyboard = new Lang.Class({
else if (release && this._capturedPress) else if (release && this._capturedPress)
this._hideSubkeys(); this._hideSubkeys();
return true; return Clutter.EVENT_STOP;
}, },
_addRows : function (keys, layout) { _addRows : function (keys, layout) {
@ -436,7 +457,6 @@ const Keyboard = new Lang.Class({
_createSource: function () { _createSource: function () {
if (this._source == null) { if (this._source == null) {
this._source = new KeyboardSource(this); this._source = new KeyboardSource(this);
this._source.setTransient(true);
Main.messageTray.add(this._source); Main.messageTray.add(this._source);
} }
}, },
@ -478,6 +498,7 @@ const Keyboard = new Lang.Class({
Lang.bind(this, function() { Lang.bind(this, function() {
this._clearKeyboardRestTimer(); this._clearKeyboardRestTimer();
this._show(monitor); this._show(monitor);
return GLib.SOURCE_REMOVE;
})); }));
}, },
@ -503,6 +524,7 @@ const Keyboard = new Lang.Class({
Lang.bind(this, function() { Lang.bind(this, function() {
this._clearKeyboardRestTimer(); this._clearKeyboardRestTimer();
this._hide(); this._hide();
return GLib.SOURCE_REMOVE;
})); }));
}, },

View File

@ -162,9 +162,9 @@ const LayoutManager = new Lang.Class({
this._startingUp = true; this._startingUp = true;
// Normally, the stage is always covered so Clutter doesn't need to clear // Normally, the stage is always covered so Clutter doesn't need to clear
// it; however it becomes visible during the startup animation // it; however it becomes visible when using the magnifier.
// See the comment below for a longer explanation
global.stage.color = DEFAULT_BACKGROUND_COLOR; global.stage.color = DEFAULT_BACKGROUND_COLOR;
global.stage.no_clear_hint = true;
// Set up stage hierarchy to group all UI actors under one container. // Set up stage hierarchy to group all UI actors under one container.
this.uiGroup = new Shell.GenericContainer({ name: 'uiGroup' }); this.uiGroup = new Shell.GenericContainer({ name: 'uiGroup' });
@ -184,29 +184,27 @@ const LayoutManager = new Lang.Class({
let height = global.stage.height; let height = global.stage.height;
[alloc.min_size, alloc.natural_size] = [height, height]; [alloc.min_size, alloc.natural_size] = [height, height];
}); });
global.stage.add_child(this.uiGroup);
this.systemGroup = new St.Widget({ name: 'systemGroup',
layout_manager: new Clutter.BinLayout() });
this.uiGroup.add_actor(this.systemGroup);
this.sessionGroup = new St.Widget({ name: 'sessionGroup' });
this.uiGroup.add_child(this.sessionGroup);
global.stage.remove_actor(global.window_group); global.stage.remove_actor(global.window_group);
this.uiGroup.add_actor(global.window_group); this.sessionGroup.add_actor(global.window_group);
global.stage.add_child(this.uiGroup);
this.overviewGroup = new St.Widget({ name: 'overviewGroup', this.overviewGroup = new St.Widget({ name: 'overviewGroup',
visible: false }); visible: false });
this.addChrome(this.overviewGroup); this.addChrome(this.overviewGroup);
this.screenShieldGroup = new St.Widget({ name: 'screenShieldGroup', this.screenShieldGroup = new St.Widget({ name: 'screenShieldGroup',
visible: false,
clip_to_allocation: true, clip_to_allocation: true,
layout_manager: new Clutter.BinLayout(), layout_manager: new Clutter.BinLayout(),
}); });
this.addChrome(this.screenShieldGroup); this.uiGroup.add_child(this.screenShieldGroup);
this.panelBox = new St.BoxLayout({ name: 'panelBox',
vertical: true });
this.addChrome(this.panelBox, { affectsStruts: true,
trackFullscreen: true });
this.panelBox.connect('allocation-changed',
Lang.bind(this, this._panelBoxChanged));
this.trayBox = new St.Widget({ name: 'trayBox', this.trayBox = new St.Widget({ name: 'trayBox',
layout_manager: new Clutter.BinLayout() }); layout_manager: new Clutter.BinLayout() });
@ -219,8 +217,39 @@ const LayoutManager = new Lang.Class({
this.addChrome(this.keyboardBox); this.addChrome(this.keyboardBox);
this._keyboardHeightNotifyId = 0; this._keyboardHeightNotifyId = 0;
global.stage.remove_actor(global.top_window_group); this.osdGroup = new St.Widget();
this.uiGroup.add_actor(global.top_window_group); this.sessionGroup.add_child(this.osdGroup);
this.switcherPopupGroup = new St.Widget();
this.sessionGroup.add_child(this.switcherPopupGroup);
this.dialogGroup = new St.Widget();
this.sessionGroup.add_child(this.dialogGroup);
// A dummy actor that tracks the mouse or text cursor, based on the
// position set in setDummyCursorPosition.
this.dummyCursor = new St.Widget({ width: 0, height: 0 });
this.uiGroup.add_child(this.dummyCursor);
// The panel group isn't in the session, as it needs to go above the screen
// shield, and it isn't animated in the login animation.
this.panelGroup = new St.Widget({ name: 'panelGroup' });
this.uiGroup.add_child(this.panelGroup);
this._trackActor(this.panelGroup, { affectsStruts: true,
trackFullscreen: true });
this.panelGroup.connect('allocation-changed',
Lang.bind(this, this._panelGroupChanged));
this.overlayGroup = new St.Widget({ name: 'overlayGroup' });
this.uiGroup.add_child(this.overlayGroup);
this.menuGroup = new St.Widget();
this.overlayGroup.add_child(this.menuGroup);
this._topSessionGroup = new St.Widget();
this.overlayGroup.add_child(this._topSessionGroup);
global.stage.remove_child(global.top_window_group);
this._topSessionGroup.add_child(global.top_window_group);
this._backgroundGroup = new Meta.BackgroundGroup(); this._backgroundGroup = new Meta.BackgroundGroup();
global.window_group.add_child(this._backgroundGroup); global.window_group.add_child(this._backgroundGroup);
@ -242,8 +271,7 @@ const LayoutManager = new Lang.Class({
// This is called by Main after everything else is constructed // This is called by Main after everything else is constructed
init: function() { init: function() {
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated)); Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._prepareStartupAnimation();
this._loadBackground();
}, },
showOverview: function() { showOverview: function() {
@ -251,7 +279,10 @@ const LayoutManager = new Lang.Class({
this._inOverview = true; this._inOverview = true;
this._updateVisibility(); this._updateVisibility();
this._queueUpdateRegions(); this._updateRegions();
global.window_group.hide();
global.top_window_group.hide();
}, },
hideOverview: function() { hideOverview: function() {
@ -260,6 +291,9 @@ const LayoutManager = new Lang.Class({
this._inOverview = false; this._inOverview = false;
this._updateVisibility(); this._updateVisibility();
this._queueUpdateRegions(); this._queueUpdateRegions();
global.window_group.show();
global.top_window_group.show();
}, },
_sessionUpdated: function() { _sessionUpdated: function() {
@ -301,7 +335,7 @@ const LayoutManager = new Lang.Class({
}); });
this.hotCorners = []; this.hotCorners = [];
let size = this.panelBox.height; let size = this.panelGroup.height;
// build new hot corners // build new hot corners
for (let i = 0; i < this.monitors.length; i++) { for (let i = 0; i < this.monitors.length; i++) {
@ -352,26 +386,26 @@ const LayoutManager = new Lang.Class({
this.emit('hot-corners-changed'); this.emit('hot-corners-changed');
}, },
_createBackground: function(monitorIndex) { _addBackgroundMenu: function(bgManager) {
BackgroundMenu.addBackgroundMenu(bgManager.background.actor, this);
},
_createBackgroundManager: function(monitorIndex) {
let bgManager = new Background.BackgroundManager({ container: this._backgroundGroup, let bgManager = new Background.BackgroundManager({ container: this._backgroundGroup,
layoutManager: this, layoutManager: this,
monitorIndex: monitorIndex }); monitorIndex: monitorIndex });
BackgroundMenu.addBackgroundMenu(bgManager.background.actor);
bgManager.connect('changed', Lang.bind(this, function() { bgManager.connect('changed', Lang.bind(this, this._addBackgroundMenu));
BackgroundMenu.addBackgroundMenu(bgManager.background.actor); this._addBackgroundMenu(bgManager);
}));
this._bgManagers.push(bgManager); return bgManager;
return bgManager.background;
}, },
_createSecondaryBackgrounds: function() { _showSecondaryBackgrounds: function() {
for (let i = 0; i < this.monitors.length; i++) { for (let i = 0; i < this.monitors.length; i++) {
if (i != this.primaryIndex) { if (i != this.primaryIndex) {
let background = this._createBackground(i); let background = this._bgManagers[i].background;
background.actor.show();
background.actor.opacity = 0; background.actor.opacity = 0;
Tweener.addTween(background.actor, Tweener.addTween(background.actor,
{ opacity: 255, { opacity: 255,
@ -381,10 +415,6 @@ const LayoutManager = new Lang.Class({
} }
}, },
_createPrimaryBackground: function() {
this._createBackground(this.primaryIndex);
},
_updateBackgrounds: function() { _updateBackgrounds: function() {
let i; let i;
for (i = 0; i < this._bgManagers.length; i++) for (i = 0; i < this._bgManagers.length; i++)
@ -395,20 +425,21 @@ const LayoutManager = new Lang.Class({
if (Main.sessionMode.isGreeter) if (Main.sessionMode.isGreeter)
return; return;
if (this._startingUp)
return;
for (let i = 0; i < this.monitors.length; i++) { for (let i = 0; i < this.monitors.length; i++) {
this._createBackground(i); let bgManager = this._createBackgroundManager(i);
this._bgManagers.push(bgManager);
if (i != this.primaryIndex && this._startingUp)
bgManager.background.actor.hide();
} }
}, },
_updateBoxes: function() { _updateBoxes: function() {
this.screenShieldGroup.set_position(0, 0); this.systemGroup.set_position(0, 0);
this.screenShieldGroup.set_size(global.screen_width, global.screen_height); this.systemGroup.set_size(global.screen_width, global.screen_height);
this.panelBox.set_position(this.primaryMonitor.x, this.primaryMonitor.y); this.panelGroup.set_position(this.primaryMonitor.x, this.primaryMonitor.y);
this.panelBox.set_size(this.primaryMonitor.width, -1); this.panelGroup.set_size(this.primaryMonitor.width, -1);
if (this.keyboardIndex < 0) if (this.keyboardIndex < 0)
this.keyboardIndex = this.primaryIndex; this.keyboardIndex = this.primaryIndex;
@ -418,10 +449,10 @@ const LayoutManager = new Lang.Class({
this.trayBox.set_size(this.bottomMonitor.width, -1); this.trayBox.set_size(this.bottomMonitor.width, -1);
}, },
_panelBoxChanged: function() { _panelGroupChanged: function() {
this._updatePanelBarrier(); this._updatePanelBarrier();
let size = this.panelBox.height; let size = this.panelGroup.height;
this.hotCorners.forEach(function(corner) { this.hotCorners.forEach(function(corner) {
if (corner) if (corner)
corner.setBarrierSize(size); corner.setBarrierSize(size);
@ -434,12 +465,12 @@ const LayoutManager = new Lang.Class({
this._rightPanelBarrier = null; this._rightPanelBarrier = null;
} }
if (this.panelBox.height) { if (this.panelGroup.height) {
let primary = this.primaryMonitor; let primary = this.primaryMonitor;
this._rightPanelBarrier = new Meta.Barrier({ display: global.display, this._rightPanelBarrier = new Meta.Barrier({ display: global.display,
x1: primary.x + primary.width, y1: primary.y, x1: primary.x + primary.width, y1: primary.y,
x2: primary.x + primary.width, y2: primary.y + this.panelBox.height, x2: primary.x + primary.width, y2: primary.y + this.panelGroup.height,
directions: Meta.BarrierDirection.NEGATIVE_X }); directions: Meta.BarrierDirection.NEGATIVE_X });
} }
}, },
@ -545,25 +576,6 @@ const LayoutManager = new Lang.Class({
return this._keyboardIndex; return this._keyboardIndex;
}, },
_loadBackground: function() {
this._systemBackground = new Background.SystemBackground();
this._systemBackground.actor.hide();
global.stage.insert_child_below(this._systemBackground.actor, null);
let constraint = new Clutter.BindConstraint({ source: global.stage,
coordinate: Clutter.BindCoordinate.ALL });
this._systemBackground.actor.add_constraint(constraint);
let signalId = this._systemBackground.connect('loaded', Lang.bind(this, function() {
this._systemBackground.disconnect(signalId);
this._systemBackground.actor.show();
global.stage.show();
this._prepareStartupAnimation();
}));
},
// Startup Animations // Startup Animations
// //
// We have two different animations, depending on whether we're a greeter // We have two different animations, depending on whether we're a greeter
@ -584,6 +596,8 @@ const LayoutManager = new Lang.Class({
// screen. So, we set no_clear_hint at the end of the animation. // screen. So, we set no_clear_hint at the end of the animation.
_prepareStartupAnimation: function() { _prepareStartupAnimation: function() {
global.stage.show();
// During the initial transition, add a simple actor to block all events, // During the initial transition, add a simple actor to block all events,
// so they don't get delivered to X11 windows that have been transformed. // so they don't get delivered to X11 windows that have been transformed.
this._coverPane = new Clutter.Actor({ opacity: 0, this._coverPane = new Clutter.Actor({ opacity: 0,
@ -593,9 +607,9 @@ const LayoutManager = new Lang.Class({
this.addChrome(this._coverPane); this.addChrome(this._coverPane);
if (Main.sessionMode.isGreeter) { if (Main.sessionMode.isGreeter) {
this.panelBox.translation_y = -this.panelBox.height; this.panelGroup.translation_y = -this.panelGroup.height;
} else { } else {
this._createPrimaryBackground(); this._updateBackgrounds();
// We need to force an update of the regions now before we scale // We need to force an update of the regions now before we scale
// the UI group to get the coorect allocation for the struts. // the UI group to get the coorect allocation for the struts.
@ -608,10 +622,10 @@ const LayoutManager = new Lang.Class({
let x = monitor.x + monitor.width / 2.0; let x = monitor.x + monitor.width / 2.0;
let y = monitor.y + monitor.height / 2.0; let y = monitor.y + monitor.height / 2.0;
this.uiGroup.set_pivot_point(x / global.screen_width, this.sessionGroup.set_pivot_point(x / global.screen_width,
y / global.screen_height); y / global.screen_height);
this.uiGroup.scale_x = this.uiGroup.scale_y = 0.5; this.sessionGroup.scale_x = this.sessionGroup.scale_y = 0.75;
this.uiGroup.opacity = 0; this.sessionGroup.opacity = 0;
global.window_group.set_clip(monitor.x, monitor.y, monitor.width, monitor.height); global.window_group.set_clip(monitor.x, monitor.y, monitor.width, monitor.height);
} }
@ -625,7 +639,7 @@ const LayoutManager = new Lang.Class({
// when the system is bogged down // when the system is bogged down
GLib.idle_add(GLib.PRIORITY_LOW, Lang.bind(this, function() { GLib.idle_add(GLib.PRIORITY_LOW, Lang.bind(this, function() {
this._startupAnimation(); this._startupAnimation();
return false; return GLib.SOURCE_REMOVE;
})); }));
}, },
@ -637,7 +651,7 @@ const LayoutManager = new Lang.Class({
}, },
_startupAnimationGreeter: function() { _startupAnimationGreeter: function() {
Tweener.addTween(this.panelBox, Tweener.addTween(this.panelGroup,
{ translation_y: 0, { translation_y: 0,
time: STARTUP_ANIMATION_TIME, time: STARTUP_ANIMATION_TIME,
transition: 'easeOutQuad', transition: 'easeOutQuad',
@ -646,7 +660,7 @@ const LayoutManager = new Lang.Class({
}, },
_startupAnimationSession: function() { _startupAnimationSession: function() {
Tweener.addTween(this.uiGroup, Tweener.addTween(this.sessionGroup,
{ scale_x: 1, { scale_x: 1,
scale_y: 1, scale_y: 1,
opacity: 255, opacity: 255,
@ -657,23 +671,16 @@ const LayoutManager = new Lang.Class({
}, },
_startupAnimationComplete: function() { _startupAnimationComplete: function() {
// At this point, the UI group is covering everything, so
// we no longer need to clear the stage
global.stage.no_clear_hint = true;
this._coverPane.destroy(); this._coverPane.destroy();
this._coverPane = null; this._coverPane = null;
this._systemBackground.actor.destroy();
this._systemBackground = null;
this._startingUp = false; this._startingUp = false;
this.trayBox.show(); this.trayBox.show();
this.keyboardBox.show(); this.keyboardBox.show();
if (!Main.sessionMode.isGreeter) { if (!Main.sessionMode.isGreeter) {
this._createSecondaryBackgrounds(); this._showSecondaryBackgrounds();
global.window_group.remove_clip(); global.window_group.remove_clip();
} }
@ -723,6 +730,20 @@ const LayoutManager = new Lang.Class({
this._updateRegions(); this._updateRegions();
}, },
// setDummyCursorPosition:
//
// The cursor dummy is a standard widget commonly used for popup
// menus and box pointers to track, as the box pointer API only
// tracks actors. If you want to pop up a menu based on where the
// user clicked, or where the text cursor is, the cursor dummy
// is what you should use. Given that the menu should not track
// the actual mouse pointer as it moves, you need to call this
// function before you show the menu to ensure it is at the right
// position.
setDummyCursorPosition: function(x, y) {
this.dummyCursor.set_position(Math.round(x), Math.round(y));
},
// addChrome: // addChrome:
// @actor: an actor to add to the chrome // @actor: an actor to add to the chrome
// @params: (optional) additional params // @params: (optional) additional params
@ -742,9 +763,9 @@ const LayoutManager = new Lang.Class({
// monitor (it will be hidden whenever a fullscreen window is visible, // monitor (it will be hidden whenever a fullscreen window is visible,
// and shown otherwise) // and shown otherwise)
addChrome: function(actor, params) { addChrome: function(actor, params) {
this.uiGroup.add_actor(actor); this.sessionGroup.add_actor(actor);
if (this.uiGroup.contains(global.top_window_group)) if (this.sessionGroup.contains(global.top_window_group))
this.uiGroup.set_child_below_sibling(actor, global.top_window_group); this.sessionGroup.set_child_below_sibling(actor, global.top_window_group);
this._trackActor(actor, params); this._trackActor(actor, params);
}, },
@ -795,7 +816,7 @@ const LayoutManager = new Lang.Class({
// //
// Removes @actor from the chrome // Removes @actor from the chrome
removeChrome: function(actor) { removeChrome: function(actor) {
this.uiGroup.remove_actor(actor); this.sessionGroup.remove_actor(actor);
this._untrackActor(actor); this._untrackActor(actor);
}, },
@ -814,13 +835,10 @@ const LayoutManager = new Lang.Class({
let actorData = Params.parse(params, defaultParams); let actorData = Params.parse(params, defaultParams);
actorData.actor = actor; actorData.actor = actor;
actorData.isToplevel = actor.get_parent() == this.uiGroup;
actorData.visibleId = actor.connect('notify::visible', actorData.visibleId = actor.connect('notify::visible',
Lang.bind(this, this._queueUpdateRegions)); Lang.bind(this, this._queueUpdateRegions));
actorData.allocationId = actor.connect('notify::allocation', actorData.allocationId = actor.connect('notify::allocation',
Lang.bind(this, this._queueUpdateRegions)); Lang.bind(this, this._queueUpdateRegions));
actorData.parentSetId = actor.connect('parent-set',
Lang.bind(this, this._actorReparented));
// Note that destroying actor will unset its parent, so we don't // Note that destroying actor will unset its parent, so we don't
// need to connect to 'destroy' too. // need to connect to 'destroy' too.
@ -843,29 +861,13 @@ const LayoutManager = new Lang.Class({
this._queueUpdateRegions(); this._queueUpdateRegions();
}, },
_actorReparented: function(actor, oldParent) {
let newParent = actor.get_parent();
if (!newParent) {
this._untrackActor(actor);
} else {
let i = this._findActor(actor);
let actorData = this._trackedActors[i];
actorData.isToplevel = (newParent == this.uiGroup);
}
},
_updateVisibility: function() { _updateVisibility: function() {
let windowsVisible = Main.sessionMode.hasWindows && !this._inOverview; let windowsVisible = Main.sessionMode.hasWindows && !this._inOverview;
global.window_group.visible = windowsVisible;
global.top_window_group.visible = windowsVisible;
for (let i = 0; i < this._trackedActors.length; i++) { for (let i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i], visible; let actorData = this._trackedActors[i], visible;
if (!actorData.trackFullscreen) if (!actorData.trackFullscreen)
continue; continue;
if (!actorData.isToplevel)
continue;
if (!windowsVisible) if (!windowsVisible)
visible = true; visible = true;
@ -1042,7 +1044,7 @@ const LayoutManager = new Lang.Class({
workspace.set_builtin_struts(struts); workspace.set_builtin_struts(struts);
} }
return false; return GLib.SOURCE_REMOVE;
} }
}); });
Signals.addSignalMethods(LayoutManager.prototype); Signals.addSignalMethods(LayoutManager.prototype);
@ -1080,9 +1082,9 @@ const HotCorner = new Lang.Class({
this._ripple2 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0, visible: false }); this._ripple2 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0, visible: false });
this._ripple3 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0, visible: false }); this._ripple3 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0, visible: false });
layoutManager.uiGroup.add_actor(this._ripple1); layoutManager.sessionGroup.add_actor(this._ripple1);
layoutManager.uiGroup.add_actor(this._ripple2); layoutManager.sessionGroup.add_actor(this._ripple2);
layoutManager.uiGroup.add_actor(this._ripple3); layoutManager.sessionGroup.add_actor(this._ripple3);
}, },
setBarrierSize: function(size) { setBarrierSize: function(size) {
@ -1229,20 +1231,20 @@ const HotCorner = new Lang.Class({
this._entered = true; this._entered = true;
this._toggleOverview(); this._toggleOverview();
} }
return false; return Clutter.EVENT_PROPAGATE;
}, },
_onCornerLeft : function(actor, event) { _onCornerLeft : function(actor, event) {
if (event.get_related() != this.actor) if (event.get_related() != this.actor)
this._entered = false; this._entered = false;
// Consume event, otherwise this will confuse onEnvironsLeft // Consume event, otherwise this will confuse onEnvironsLeft
return true; return Clutter.EVENT_STOP;
}, },
_onEnvironsLeft : function(actor, event) { _onEnvironsLeft : function(actor, event) {
if (event.get_related() != this._corner) if (event.get_related() != this._corner)
this._entered = false; this._entered = false;
return false; return Clutter.EVENT_PROPAGATE;
} }
}); });

View File

@ -5,12 +5,38 @@ const Lang = imports.lang;
const Meta = imports.gi.Meta; const Meta = imports.gi.Meta;
const Signals = imports.signals; const Signals = imports.signals;
const St = imports.gi.St; const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Params = imports.misc.params; const Params = imports.misc.params;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const DEFAULT_FADE_FACTOR = 0.4; const DEFAULT_FADE_FACTOR = 0.4;
const GLSL_DIM_EFFECT_DECLARATIONS = '\
float compute_dim_factor (const vec2 coords) {\
vec2 dist = coords - vec2(0.5, 0.5); \
float elipse_radius = 0.5; \
/* interpolate darkening value, based on distance from screen center */ \
float val = min(length(dist), elipse_radius); \
return mix(0.3, 1.0, val / elipse_radius) * 0.4; \
}';
const GLSL_DIM_EFFECT_CODE = '\
float a = compute_dim_factor (cogl_tex_coord0_in.xy);\
cogl_color_out = vec4(0, 0, 0, cogl_color_in.a * a);'
;
const RadialShaderQuad = new Lang.Class({
Name: 'RadialShaderQuad',
Extends: Shell.GLSLQuad,
vfunc_build_pipeline: function() {
this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT,
GLSL_DIM_EFFECT_DECLARATIONS,
GLSL_DIM_EFFECT_CODE,
true);
},
});
/** /**
* Lightbox: * Lightbox:
* @container: parent Clutter.Container * @container: parent Clutter.Container
@ -43,15 +69,21 @@ const Lightbox = new Lang.Class({
width: null, width: null,
height: null, height: null,
fadeFactor: DEFAULT_FADE_FACTOR, fadeFactor: DEFAULT_FADE_FACTOR,
radialEffect: false,
}); });
this._container = container; this._container = container;
this._children = container.get_children(); this._children = container.get_children();
this._fadeFactor = params.fadeFactor; this._fadeFactor = params.fadeFactor;
this.actor = new St.Bin({ x: 0, if (params.radialEffect)
y: 0, this.actor = new RadialShaderQuad({ x: 0,
style_class: 'lightbox', y: 0,
reactive: params.inhibitEvents }); reactive: params.inhibitEvents });
else
this.actor = new St.Bin({ x: 0,
y: 0,
style_class: 'lightbox',
reactive: params.inhibitEvents });
container.add_actor(this.actor); container.add_actor(this.actor);
this.actor.raise_top(); this.actor.raise_top();

View File

@ -109,6 +109,7 @@ const AutoComplete = new Lang.Class({
} }
this._lastTabTime = currTime; this._lastTabTime = currTime;
} }
return Clutter.EVENT_PROPAGATE;
}, },
// Insert characters of text not already included in head at cursor position. i.e., if text="abc" and head="a", // Insert characters of text not already included in head at cursor position. i.e., if text="abc" and head="a",
@ -502,7 +503,7 @@ const Inspector = new Lang.Class({
let container = new Shell.GenericContainer({ width: 0, let container = new Shell.GenericContainer({ width: 0,
height: 0 }); height: 0 });
container.connect('allocate', Lang.bind(this, this._allocate)); container.connect('allocate', Lang.bind(this, this._allocate));
Main.uiGroup.add_actor(container); Main.layoutManager.sessionGroup.add_actor(container);
let eventHandler = new St.BoxLayout({ name: 'LookingGlassDialog', let eventHandler = new St.BoxLayout({ name: 'LookingGlassDialog',
vertical: false, vertical: false,
@ -558,7 +559,7 @@ const Inspector = new Lang.Class({
_onKeyPressEvent: function (actor, event) { _onKeyPressEvent: function (actor, event) {
if (event.get_key_symbol() == Clutter.Escape) if (event.get_key_symbol() == Clutter.Escape)
this._close(); this._close();
return true; return Clutter.EVENT_STOP;
}, },
_onButtonPressEvent: function (actor, event) { _onButtonPressEvent: function (actor, event) {
@ -567,7 +568,7 @@ const Inspector = new Lang.Class({
this.emit('target', this._target, stageX, stageY); this.emit('target', this._target, stageX, stageY);
} }
this._close(); this._close();
return true; return Clutter.EVENT_STOP;
}, },
_onScrollEvent: function (actor, event) { _onScrollEvent: function (actor, event) {
@ -601,12 +602,12 @@ const Inspector = new Lang.Class({
default: default:
break; break;
} }
return true; return Clutter.EVENT_STOP;
}, },
_onMotionEvent: function (actor, event) { _onMotionEvent: function (actor, event) {
this._update(event); this._update(event);
return true; return Clutter.EVENT_STOP;
}, },
_update: function(event) { _update: function(event) {
@ -629,55 +630,6 @@ const Inspector = new Lang.Class({
Signals.addSignalMethods(Inspector.prototype); Signals.addSignalMethods(Inspector.prototype);
const Memory = new Lang.Class({
Name: 'Memory',
_init: function() {
this.actor = new St.BoxLayout({ vertical: true });
this._glibc_uordblks = new St.Label();
this.actor.add(this._glibc_uordblks);
this._js_bytes = new St.Label();
this.actor.add(this._js_bytes);
this._gjs_boxed = new St.Label();
this.actor.add(this._gjs_boxed);
this._gjs_gobject = new St.Label();
this.actor.add(this._gjs_gobject);
this._gjs_function = new St.Label();
this.actor.add(this._gjs_function);
this._gjs_closure = new St.Label();
this.actor.add(this._gjs_closure);
this._last_gc_seconds_ago = new St.Label();
this.actor.add(this._last_gc_seconds_ago);
this._gcbutton = new St.Button({ label: 'Full GC',
style_class: 'lg-obj-inspector-button' });
this._gcbutton.connect('clicked', Lang.bind(this, function () { System.gc(); this._renderText(); }));
this.actor.add(this._gcbutton, { x_align: St.Align.START,
x_fill: false });
this.actor.connect('notify::mapped', Lang.bind(this, this._renderText));
},
_renderText: function() {
if (!this.actor.mapped)
return;
let memInfo = global.get_memory_info();
this._glibc_uordblks.text = 'glibc_uordblks: ' + memInfo.glibc_uordblks;
this._js_bytes.text = 'js bytes: ' + memInfo.js_bytes;
this._gjs_boxed.text = 'gjs_boxed: ' + memInfo.gjs_boxed;
this._gjs_gobject.text = 'gjs_gobject: ' + memInfo.gjs_gobject;
this._gjs_function.text = 'gjs_function: ' + memInfo.gjs_function;
this._gjs_closure.text = 'gjs_closure: ' + memInfo.gjs_closure;
this._last_gc_seconds_ago.text = 'last_gc_seconds_ago: ' + memInfo.last_gc_seconds_ago;
}
});
const Extensions = new Lang.Class({ const Extensions = new Lang.Class({
Name: 'Extensions', Name: 'Extensions',
@ -718,13 +670,13 @@ const Extensions = new Lang.Class({
_onViewSource: function (actor) { _onViewSource: function (actor) {
let extension = actor._extension; let extension = actor._extension;
let uri = extension.dir.get_uri(); let uri = extension.dir.get_uri();
Gio.app_info_launch_default_for_uri(uri, global.create_app_launch_context()); Gio.app_info_launch_default_for_uri(uri, global.create_app_launch_context(0, -1));
this._lookingGlass.close(); this._lookingGlass.close();
}, },
_onWebPage: function (actor) { _onWebPage: function (actor) {
let extension = actor._extension; let extension = actor._extension;
Gio.app_info_launch_default_for_uri(extension.metadata.url, global.create_app_launch_context()); Gio.app_info_launch_default_for_uri(extension.metadata.url, global.create_app_launch_context(0, -1));
this._lookingGlass.close(); this._lookingGlass.close();
}, },
@ -849,16 +801,14 @@ const LookingGlass = new Lang.Class({
this._updateFont(); this._updateFont();
// We want it to appear to slide out from underneath the panel // We want it to appear to slide out from underneath the panel
Main.uiGroup.add_actor(this.actor); Main.layoutManager.sessionGroup.add_actor(this.actor);
Main.uiGroup.set_child_below_sibling(this.actor, Main.layoutManager.panelGroup.connect('allocation-changed',
Main.layoutManager.panelBox); Lang.bind(this, this._queueResize));
Main.layoutManager.panelBox.connect('allocation-changed',
Lang.bind(this, this._queueResize));
Main.layoutManager.keyboardBox.connect('allocation-changed', Main.layoutManager.keyboardBox.connect('allocation-changed',
Lang.bind(this, this._queueResize)); Lang.bind(this, this._queueResize));
this._objInspector = new ObjInspector(this); this._objInspector = new ObjInspector(this);
Main.uiGroup.add_actor(this._objInspector.actor); Main.layoutManager.sessionGroup.add_actor(this._objInspector.actor);
this._objInspector.actor.hide(); this._objInspector.actor.hide();
let toolbar = new St.BoxLayout({ name: 'Toolbar' }); let toolbar = new St.BoxLayout({ name: 'Toolbar' });
@ -877,7 +827,22 @@ const LookingGlass = new Lang.Class({
global.stage.set_key_focus(this._entry); global.stage.set_key_focus(this._entry);
})); }));
this.actor.hide(); this.actor.hide();
return true; return Clutter.EVENT_STOP;
}));
let gcIcon = new St.Icon({ icon_name: 'gnome-fs-trash-full',
icon_size: 24 });
toolbar.add_actor(gcIcon);
gcIcon.reactive = true;
gcIcon.connect('button-press-event', Lang.bind(this, function () {
gcIcon.icon_name = 'gnome-fs-trash-empty';
System.gc();
this._timeoutId = Mainloop.timeout_add(500, Lang.bind(this, function () {
gcIcon.icon_name = 'gnome-fs-trash-full';
Mainloop.source_remove(this._timeoutId);
return GLib.SOURCE_REMOVE;
}));
return Clutter.EVENT_PROPAGATE;
})); }));
let notebook = new Notebook(); let notebook = new Notebook();
@ -907,9 +872,6 @@ const LookingGlass = new Lang.Class({
this._windowList = new WindowList(this); this._windowList = new WindowList(this);
notebook.appendPage('Windows', this._windowList.actor); notebook.appendPage('Windows', this._windowList.actor);
this._memory = new Memory();
notebook.appendPage('Memory', this._memory.actor);
this._extensions = new Extensions(this); this._extensions = new Extensions(this);
notebook.appendPage('Extensions', this._extensions.actor); notebook.appendPage('Extensions', this._extensions.actor);
@ -1073,7 +1035,7 @@ const LookingGlass = new Lang.Class({
let availableHeight = primary.height - Main.layoutManager.keyboardBox.height; let availableHeight = primary.height - Main.layoutManager.keyboardBox.height;
let myHeight = Math.min(primary.height * 0.7, availableHeight * 0.9); let myHeight = Math.min(primary.height * 0.7, availableHeight * 0.9);
this.actor.x = primary.x + (primary.width - myWidth) / 2; this.actor.x = primary.x + (primary.width - myWidth) / 2;
this._hiddenY = primary.y + Main.layoutManager.panelBox.height - myHeight - 4; // -4 to hide the top corners this._hiddenY = primary.y + Main.layoutManager.panelGroup.height - myHeight - 4; // -4 to hide the top corners
this._targetY = this._hiddenY + myHeight; this._targetY = this._hiddenY + myHeight;
this.actor.y = this._hiddenY; this.actor.y = this._hiddenY;
this.actor.width = myWidth; this.actor.width = myWidth;
@ -1102,7 +1064,7 @@ const LookingGlass = new Lang.Class({
} else { } else {
this.close(); this.close();
} }
return true; return Clutter.EVENT_STOP;
} }
// Ctrl+PgUp and Ctrl+PgDown switches tabs in the notebook view // Ctrl+PgUp and Ctrl+PgDown switches tabs in the notebook view
if (modifierState & Clutter.ModifierType.CONTROL_MASK) { if (modifierState & Clutter.ModifierType.CONTROL_MASK) {
@ -1112,7 +1074,7 @@ const LookingGlass = new Lang.Class({
this._notebook.nextTab(); this._notebook.nextTab();
} }
} }
return false; return Clutter.EVENT_PROPAGATE;
}, },
open : function() { open : function() {

View File

@ -1184,7 +1184,7 @@ const ZoomRegion = new Lang.Class({
// Clone the group that contains all of UI on the screen. This is the // Clone the group that contains all of UI on the screen. This is the
// chrome, the windows, etc. // chrome, the windows, etc.
this._uiGroupClone = new Clutter.Clone({ source: Main.uiGroup }); this._uiGroupClone = new Clutter.Clone({ source: Main.layoutManager.uiGroup });
mainGroup.add_actor(this._uiGroupClone); mainGroup.add_actor(this._uiGroupClone);
// Add either the given mouseSourceActor to the ZoomRegion, or a clone of // Add either the given mouseSourceActor to the ZoomRegion, or a clone of

View File

@ -9,85 +9,89 @@ const ZOOM_SERVICE_PATH = '/org/gnome/Magnifier/ZoomRegion';
// Subset of gnome-mag's Magnifier dbus interface -- to be expanded. See: // Subset of gnome-mag's Magnifier dbus interface -- to be expanded. See:
// http://git.gnome.org/browse/gnome-mag/tree/xml/...Magnifier.xml // http://git.gnome.org/browse/gnome-mag/tree/xml/...Magnifier.xml
const MagnifierIface = <interface name="org.gnome.Magnifier"> const MagnifierIface = '<node> \
<method name="setActive"> <interface name="org.gnome.Magnifier"> \
<arg type="b" direction="in" /> <method name="setActive"> \
</method> <arg type="b" direction="in" /> \
<method name="isActive"> </method> \
<arg type="b" direction="out" /> <method name="isActive"> \
</method> <arg type="b" direction="out" /> \
<method name="showCursor" /> </method> \
<method name="hideCursor" /> <method name="showCursor" /> \
<method name="createZoomRegion"> <method name="hideCursor" /> \
<arg type="d" direction="in" /> <method name="createZoomRegion"> \
<arg type="d" direction="in" /> <arg type="d" direction="in" /> \
<arg type="ai" direction="in" /> <arg type="d" direction="in" /> \
<arg type="ai" direction="in" /> <arg type="ai" direction="in" /> \
<arg type="o" direction="out" /> <arg type="ai" direction="in" /> \
</method> <arg type="o" direction="out" /> \
<method name="addZoomRegion"> </method> \
<arg type="o" direction="in" /> <method name="addZoomRegion"> \
<arg type="b" direction="out" /> <arg type="o" direction="in" /> \
</method> <arg type="b" direction="out" /> \
<method name="getZoomRegions"> </method> \
<arg type="ao" direction="out" /> <method name="getZoomRegions"> \
</method> <arg type="ao" direction="out" /> \
<method name="clearAllZoomRegions" /> </method> \
<method name="fullScreenCapable"> <method name="clearAllZoomRegions" /> \
<arg type="b" direction="out" /> <method name="fullScreenCapable"> \
</method> <arg type="b" direction="out" /> \
<method name="setCrosswireSize"> </method> \
<arg type="i" direction="in" /> <method name="setCrosswireSize"> \
</method> <arg type="i" direction="in" /> \
<method name="getCrosswireSize"> </method> \
<arg type="i" direction="out" /> <method name="getCrosswireSize"> \
</method> <arg type="i" direction="out" /> \
<method name="setCrosswireLength"> </method> \
<arg type="i" direction="in" /> <method name="setCrosswireLength"> \
</method> <arg type="i" direction="in" /> \
<method name="getCrosswireLength"> </method> \
<arg type="i" direction="out" /> <method name="getCrosswireLength"> \
</method> <arg type="i" direction="out" /> \
<method name="setCrosswireClip"> </method> \
<arg type="b" direction="in" /> <method name="setCrosswireClip"> \
</method> <arg type="b" direction="in" /> \
<method name="getCrosswireClip"> </method> \
<arg type="b" direction="out" /> <method name="getCrosswireClip"> \
</method> <arg type="b" direction="out" /> \
<method name="setCrosswireColor"> </method> \
<arg type="u" direction="in" /> <method name="setCrosswireColor"> \
</method> <arg type="u" direction="in" /> \
<method name="getCrosswireColor"> </method> \
<arg type="u" direction="out" /> <method name="getCrosswireColor"> \
</method> <arg type="u" direction="out" /> \
</interface>; </method> \
</interface> \
</node>';
// Subset of gnome-mag's ZoomRegion dbus interface -- to be expanded. See: // Subset of gnome-mag's ZoomRegion dbus interface -- to be expanded. See:
// http://git.gnome.org/browse/gnome-mag/tree/xml/...ZoomRegion.xml // http://git.gnome.org/browse/gnome-mag/tree/xml/...ZoomRegion.xml
const ZoomRegionIface = <interface name="org.gnome.Magnifier.ZoomRegion"> const ZoomRegionIface = '<node> \
<method name="setMagFactor"> <interface name="org.gnome.Magnifier.ZoomRegion"> \
<arg type="d" direction="in" /> <method name="setMagFactor"> \
<arg type="d" direction="in" /> <arg type="d" direction="in" /> \
</method> <arg type="d" direction="in" /> \
<method name="getMagFactor"> </method> \
<arg type="d" direction="out" /> <method name="getMagFactor"> \
<arg type="d" direction="out" /> <arg type="d" direction="out" /> \
</method> <arg type="d" direction="out" /> \
<method name="setRoi"> </method> \
<arg type="ai" direction="in" /> <method name="setRoi"> \
</method> <arg type="ai" direction="in" /> \
<method name="getRoi"> </method> \
<arg type="ai" direction="out" /> <method name="getRoi"> \
</method> <arg type="ai" direction="out" /> \
<method name="shiftContentsTo"> </method> \
<arg type="i" direction="in" /> <method name="shiftContentsTo"> \
<arg type="i" direction="in" /> <arg type="i" direction="in" /> \
<arg type="b" direction="out" /> <arg type="i" direction="in" /> \
</method> <arg type="b" direction="out" /> \
<method name="moveResize"> </method> \
<arg type="ai" direction="in" /> <method name="moveResize"> \
</method> <arg type="ai" direction="in" /> \
</interface>; </method> \
</interface> \
</node>';
// For making unique ZoomRegion DBus proxy object paths of the form: // For making unique ZoomRegion DBus proxy object paths of the form:
// '/org/gnome/Magnifier/ZoomRegion/zoomer0', // '/org/gnome/Magnifier/ZoomRegion/zoomer0',

View File

@ -64,7 +64,6 @@ let screencastService = null;
let modalCount = 0; let modalCount = 0;
let keybindingMode = Shell.KeyBindingMode.NONE; let keybindingMode = Shell.KeyBindingMode.NONE;
let modalActorFocusStack = []; let modalActorFocusStack = [];
let uiGroup = null;
let magnifier = null; let magnifier = null;
let xdndHandler = null; let xdndHandler = null;
let keyboard = null; let keyboard = null;
@ -112,11 +111,6 @@ function start() {
Gio.DesktopAppInfo.set_desktop_env('GNOME'); Gio.DesktopAppInfo.set_desktop_env('GNOME');
sessionMode = new SessionMode.SessionMode(); sessionMode = new SessionMode.SessionMode();
sessionMode.connect('sessions-loaded', _sessionsLoaded);
sessionMode.init();
}
function _sessionsLoaded() {
sessionMode.connect('updated', _sessionUpdated); sessionMode.connect('updated', _sessionUpdated);
_initializePrefs(); _initializePrefs();
_initializeUI(); _initializeUI();
@ -155,11 +149,6 @@ function _initializeUI() {
// Setup the stage hierarchy early // Setup the stage hierarchy early
layoutManager = new Layout.LayoutManager(); 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;
screencastService = new Screencast.ScreencastService(); screencastService = new Screencast.ScreencastService();
xdndHandler = new XdndHandler.XdndHandler(); xdndHandler = new XdndHandler.XdndHandler();
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager(); ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
@ -177,6 +166,19 @@ function _initializeUI() {
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler(); windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
componentManager = new Components.ComponentManager(); componentManager = new Components.ComponentManager();
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();
});
layoutManager.init(); layoutManager.init();
overview.init(); overview.init();
@ -207,21 +209,6 @@ function _initializeUI() {
ExtensionDownloader.init(); ExtensionDownloader.init();
ExtensionSystem.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();
}
});
} }
function _loadDefaultStylesheet() { function _loadDefaultStylesheet() {
@ -614,7 +601,7 @@ function queueDeferredWork(workId) {
_deferredTimeoutId = Mainloop.timeout_add_seconds(DEFERRED_TIMEOUT_SECONDS, function () { _deferredTimeoutId = Mainloop.timeout_add_seconds(DEFERRED_TIMEOUT_SECONDS, function () {
_runAllDeferredWork(); _runAllDeferredWork();
_deferredTimeoutId = 0; _deferredTimeoutId = 0;
return false; return GLib.SOURCE_REMOVE;
}); });
} }
} }

View File

@ -19,7 +19,6 @@ const BoxPointer = imports.ui.boxpointer;
const CtrlAltTab = imports.ui.ctrlAltTab; const CtrlAltTab = imports.ui.ctrlAltTab;
const GnomeSession = imports.misc.gnomeSession; const GnomeSession = imports.misc.gnomeSession;
const GrabHelper = imports.ui.grabHelper; const GrabHelper = imports.ui.grabHelper;
const Hash = imports.misc.hash;
const Lightbox = imports.ui.lightbox; const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main; const Main = imports.ui.main;
const PointerWatcher = imports.ui.pointerWatcher; const PointerWatcher = imports.ui.pointerWatcher;
@ -76,7 +75,7 @@ const Urgency = {
NORMAL: 1, NORMAL: 1,
HIGH: 2, HIGH: 2,
CRITICAL: 3 CRITICAL: 3
} };
function _fixMarkup(text, allowMarkup) { function _fixMarkup(text, allowMarkup) {
if (allowMarkup) { if (allowMarkup) {
@ -187,7 +186,7 @@ const URLHighlighter = new Lang.Class({
// The MessageTray doesn't actually hide us, so // The MessageTray doesn't actually hide us, so
// we need to check for paint opacities as well. // we need to check for paint opacities as well.
if (!actor.visible || actor.get_paint_opacity() == 0) if (!actor.visible || actor.get_paint_opacity() == 0)
return false; return Clutter.EVENT_PROPAGATE;
// Keep Notification.actor from seeing this and taking // Keep Notification.actor from seeing this and taking
// a pointer grab, which would block our button-release-event // a pointer grab, which would block our button-release-event
@ -196,7 +195,7 @@ const URLHighlighter = new Lang.Class({
})); }));
this.actor.connect('button-release-event', Lang.bind(this, function (actor, event) { this.actor.connect('button-release-event', Lang.bind(this, function (actor, event) {
if (!actor.visible || actor.get_paint_opacity() == 0) if (!actor.visible || actor.get_paint_opacity() == 0)
return false; return Clutter.EVENT_PROPAGATE;
let urlId = this._findUrlAtPos(event); let urlId = this._findUrlAtPos(event);
if (urlId != -1) { if (urlId != -1) {
@ -204,14 +203,14 @@ const URLHighlighter = new Lang.Class({
if (url.indexOf(':') == -1) if (url.indexOf(':') == -1)
url = 'http://' + url; url = 'http://' + url;
Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context()); Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context(0, -1));
return true; return Clutter.EVENT_STOP;
} }
return false; return Clutter.EVENT_PROPAGATE;
})); }));
this.actor.connect('motion-event', Lang.bind(this, function(actor, event) { this.actor.connect('motion-event', Lang.bind(this, function(actor, event) {
if (!actor.visible || actor.get_paint_opacity() == 0) if (!actor.visible || actor.get_paint_opacity() == 0)
return false; return Clutter.EVENT_PROPAGATE;
let urlId = this._findUrlAtPos(event); let urlId = this._findUrlAtPos(event);
if (urlId != -1 && !this._cursorChanged) { if (urlId != -1 && !this._cursorChanged) {
@ -221,16 +220,17 @@ const URLHighlighter = new Lang.Class({
global.screen.set_cursor(Meta.Cursor.DEFAULT); global.screen.set_cursor(Meta.Cursor.DEFAULT);
this._cursorChanged = false; this._cursorChanged = false;
} }
return false; return Clutter.EVENT_PROPAGATE;
})); }));
this.actor.connect('leave-event', Lang.bind(this, function() { this.actor.connect('leave-event', Lang.bind(this, function() {
if (!this.actor.visible || this.actor.get_paint_opacity() == 0) if (!this.actor.visible || this.actor.get_paint_opacity() == 0)
return; return Clutter.EVENT_PROPAGATE;
if (this._cursorChanged) { if (this._cursorChanged) {
this._cursorChanged = false; this._cursorChanged = false;
global.screen.set_cursor(Meta.Cursor.DEFAULT); global.screen.set_cursor(Meta.Cursor.DEFAULT);
} }
return Clutter.EVENT_PROPAGATE;
})); }));
}, },
@ -280,10 +280,6 @@ const URLHighlighter = new Lang.Class({
} }
}); });
function strHasSuffix(string, suffix) {
return string.substr(-suffix.length) == suffix;
}
// NotificationPolicy: // NotificationPolicy:
// An object that holds all bits of configurable policy related to a notification // An object that holds all bits of configurable policy related to a notification
// source, such as whether to play sound or honour the critical bit. // source, such as whether to play sound or honour the critical bit.
@ -310,6 +306,126 @@ const NotificationPolicy = new Lang.Class({
}); });
Signals.addSignalMethods(NotificationPolicy.prototype); Signals.addSignalMethods(NotificationPolicy.prototype);
const NotificationGenericPolicy = new Lang.Class({
Name: 'NotificationGenericPolicy',
Extends: 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: 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');
}
});
// Notification: // Notification:
// @source: the notification's Source // @source: the notification's Source
// @title: the title // @title: the title
@ -383,7 +499,6 @@ const Notification = new Lang.Class({
this.focused = false; this.focused = false;
this.acknowledged = false; this.acknowledged = false;
this._destroyed = false; this._destroyed = false;
this._useActionIcons = false;
this._customContent = false; this._customContent = false;
this.bannerBodyText = null; this.bannerBodyText = null;
this.bannerBodyMarkup = false; this.bannerBodyMarkup = false;
@ -438,7 +553,12 @@ const Notification = new Lang.Class({
this._bannerLabel = this._bannerUrlHighlighter.actor; this._bannerLabel = this._bannerUrlHighlighter.actor;
this._bannerBox.add_actor(this._bannerLabel); this._bannerBox.add_actor(this._bannerLabel);
this.update(title, banner, params); // If called with only one argument we assume the caller
// will call .update() later on. This is the case of
// NotificationDaemon, which wants to use the same code
// for new and updated notifications
if (arguments.length != 1)
this.update(title, banner, params);
}, },
// update: // update:
@ -711,19 +831,8 @@ const Notification = new Lang.Class({
} }
}, },
// addButton: addButton: function(button, callback) {
// @id: the action ID
// @label: the label for the action's button
//
// Adds a button with the given @label to the notification. All
// action buttons will appear in a single row at the bottom of
// the notification.
//
// If the button is clicked, the notification will emit the
// %action-invoked signal with @id as a parameter
addButton: function(id, label) {
if (!this._buttonBox) { if (!this._buttonBox) {
let box = new St.BoxLayout({ style_class: 'notification-actions' }); let box = new St.BoxLayout({ style_class: 'notification-actions' });
this.setActionArea(box, { x_expand: false, this.setActionArea(box, { x_expand: false,
y_expand: false, y_expand: false,
@ -731,49 +840,40 @@ const Notification = new Lang.Class({
y_fill: false, y_fill: false,
x_align: St.Align.END }); x_align: St.Align.END });
this._buttonBox = box; this._buttonBox = box;
global.focus_manager.add_group(this._buttonBox);
} }
let button = new St.Button({ can_focus: true });
button._actionId = id;
let iconName = strHasSuffix(id, '-symbolic') ? id : id + '-symbolic';
if (this._useActionIcons && Gtk.IconTheme.get_default().has_icon(iconName)) {
button.add_style_class_name('notification-icon-button');
button.child = new St.Icon({ icon_name: iconName });
} else {
button.add_style_class_name('notification-button');
button.label = label;
}
if (this._buttonBox.get_n_children() > 0)
global.focus_manager.remove_group(this._buttonBox);
this._buttonBox.add(button); this._buttonBox.add(button);
global.focus_manager.add_group(this._buttonBox); button.connect('clicked', Lang.bind(this, function() {
button.connect('clicked', Lang.bind(this, this._onActionInvoked, id)); callback();
if (!this.resident) {
// We don't hide a resident notification when the user invokes one of its actions,
// because it is common for such notifications to update themselves with new
// information based on the action. We'd like to display the updated information
// in place, rather than pop-up a new notification.
this.emit('done-displaying');
this.destroy();
}
}));
this.updated(); this.updated();
return button;
}, },
// setButtonSensitive: // addAction:
// @id: the action ID // @label: the label for the action's button
// @sensitive: whether the button should be sensitive // @callback: the callback for the action
// //
// If the notification contains a button with action ID @id, // Adds a button with the given @label to the notification. All
// its sensitivity will be set to @sensitive. Insensitive // action buttons will appear in a single row at the bottom of
// buttons cannot be clicked. // the notification.
setButtonSensitive: function(id, sensitive) { addAction: function(label, callback) {
if (!this._buttonBox) let button = new St.Button({ style_class: 'notification-button',
return; label: label,
can_focus: true });
let button = this._buttonBox.get_children().filter(function(b) { return this.addButton(button, callback);
return b._actionId == id;
})[0];
if (!button || button.reactive == sensitive)
return;
button.reactive = sensitive;
}, },
setUrgency: function(urgency) { setUrgency: function(urgency) {
@ -792,10 +892,6 @@ const Notification = new Lang.Class({
this.forFeedback = forFeedback; this.forFeedback = forFeedback;
}, },
setUseActionIcons: function(useIcons) {
this._useActionIcons = useIcons;
},
_styleChanged: function() { _styleChanged: function() {
this._spacing = this._table.get_theme_node().get_length('spacing-columns'); this._spacing = this._table.get_theme_node().get_length('spacing-columns');
}, },
@ -905,6 +1001,9 @@ const Notification = new Lang.Class({
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
Lang.bind(this, Lang.bind(this,
function() { function() {
if (this._destroyed)
return false;
if (this._canExpandContent()) { if (this._canExpandContent()) {
this._addBannerBody(); this._addBannerBody();
this._table.add_style_class_name('multi-line-notification'); this._table.add_style_class_name('multi-line-notification');
@ -1020,18 +1119,6 @@ const Notification = new Lang.Class({
this.actor.add_style_class_name('notification-unexpanded'); this.actor.add_style_class_name('notification-unexpanded');
}, },
_onActionInvoked: function(actor, mouseButtonClicked, id) {
this.emit('action-invoked', id);
if (!this.resident) {
// We don't hide a resident notification when the user invokes one of its actions,
// because it is common for such notifications to update themselves with new
// information based on the action. We'd like to display the updated information
// in place, rather than pop-up a new notification.
this.emit('done-displaying');
this.destroy();
}
},
_onClicked: function() { _onClicked: function() {
this.emit('clicked'); this.emit('clicked');
// We hide all types of notifications once the user clicks on them because the common // We hide all types of notifications once the user clicks on them because the common
@ -1075,7 +1162,7 @@ const SourceActor = new Lang.Class({
})); }));
this._actorDestroyed = false; this._actorDestroyed = false;
this._counterLabel = new St.Label( {x_align: Clutter.ActorAlign.CENTER, this._counterLabel = new St.Label({ x_align: Clutter.ActorAlign.CENTER,
x_expand: true, x_expand: true,
y_align: Clutter.ActorAlign.CENTER, y_align: Clutter.ActorAlign.CENTER,
y_expand: true }); y_expand: true });
@ -1179,7 +1266,6 @@ const Source = new Lang.Class({
this.title = title; this.title = title;
this.iconName = iconName; this.iconName = iconName;
this.isTransient = false;
this.isChat = false; this.isChat = false;
this.isMuted = false; this.isMuted = false;
this.keepTrayOnSummaryClick = false; this.keepTrayOnSummaryClick = false;
@ -1206,6 +1292,10 @@ const Source = new Lang.Class({
return this.count > 1; return this.count > 1;
}, },
get isClearable() {
return !this.trayIcon && !this.isChat && !this.resident;
},
countUpdated: function() { countUpdated: function() {
this.emit('count-updated'); this.emit('count-updated');
}, },
@ -1235,10 +1325,6 @@ const Source = new Lang.Class({
return rightClickMenu; return rightClickMenu;
}, },
setTransient: function(isTransient) {
this.isTransient = isTransient;
},
setTitle: function(newTitle) { setTitle: function(newTitle) {
this.title = newTitle; this.title = newTitle;
this.emit('title-changed'); this.emit('title-changed');
@ -1277,25 +1363,25 @@ const Source = new Lang.Class({
return this._mainIcon.actor; return this._mainIcon.actor;
}, },
_onNotificationDestroy: function(notification) {
let index = this.notifications.indexOf(notification);
if (index < 0)
return;
this.notifications.splice(index, 1);
if (this.notifications.length == 0)
this._lastNotificationRemoved();
this.countUpdated();
},
pushNotification: function(notification) { pushNotification: function(notification) {
if (this.notifications.indexOf(notification) < 0) { if (this.notifications.indexOf(notification) >= 0)
this.notifications.push(notification); return;
this.emit('notification-added', notification);
}
notification.connect('clicked', Lang.bind(this, this.open)); notification.connect('destroy', Lang.bind(this, this._onNotificationDestroy));
notification.connect('destroy', Lang.bind(this, this.notifications.push(notification);
function () { this.emit('notification-added', notification);
let index = this.notifications.indexOf(notification);
if (index < 0)
return;
this.notifications.splice(index, 1);
if (this.notifications.length == 0)
this._lastNotificationRemoved();
this.countUpdated();
}));
this.countUpdated(); this.countUpdated();
}, },
@ -1440,9 +1526,9 @@ const SummaryItem = new Lang.Class({
_onKeyPress: function(actor, event) { _onKeyPress: function(actor, event) {
if (event.get_key_symbol() == Clutter.KEY_Up) { if (event.get_key_symbol() == Clutter.KEY_Up) {
actor.emit('clicked', 1); actor.emit('clicked', 1);
return true; return Clutter.EVENT_STOP;
} }
return false; return Clutter.EVENT_PROPAGATE;
}, },
prepareNotificationStackForShowing: function() { prepareNotificationStackForShowing: function() {
@ -1507,26 +1593,42 @@ const MessageTrayMenu = new Lang.Class({
this._tray = tray; this._tray = tray;
this._presence = new GnomeSession.Presence(Lang.bind(this, function(proxy, error) {
if (error) {
logError(error, 'Error while reading gnome-session presence');
return;
}
this._onStatusChanged(proxy.status);
}));
this._presence.connectSignal('StatusChanged', Lang.bind(this, function(proxy, senderName, [status]) {
this._onStatusChanged(status);
}));
this._accountManager = Tp.AccountManager.dup();
this._accountManager.connect('most-available-presence-changed',
Lang.bind(this, this._onIMPresenceChanged));
this._accountManager.prepare_async(null, Lang.bind(this, this._onIMPresenceChanged));
this.actor.hide(); this.actor.hide();
Main.layoutManager.addChrome(this.actor); Main.layoutManager.menuGroup.add_child(this.actor);
this._busyItem = new PopupMenu.PopupSwitchMenuItem(_("Notifications"));
this._busyItem.connect('toggled', Lang.bind(this, this._updatePresence));
this.addMenuItem(this._busyItem);
let separator = new PopupMenu.PopupSeparatorMenuItem();
this.addMenuItem(separator);
this._clearItem = this.addAction(_("Clear Messages"), function() { this._clearItem = this.addAction(_("Clear Messages"), function() {
let toDestroy = []; let toDestroy = tray.getSources().filter(function(source) {
let sources = tray.getSources(); return source.isClearable;
for (let i = 0; i < sources.length; i++) { });
// We exclude trayIcons, chat and resident sources
if (sources[i].trayIcon ||
sources[i].isChat ||
sources[i].resident)
continue;
toDestroy.push(sources[i]);
}
for (let i = 0; i < toDestroy.length; i++) { toDestroy.forEach(function(source) {
toDestroy[i].destroy(); source.destroy();
} });
toDestroy = null;
tray.close(); tray.close();
}); });
@ -1541,9 +1643,43 @@ const MessageTrayMenu = new Lang.Class({
settingsItem.connect('activate', function() { tray.close(); }); settingsItem.connect('activate', function() { tray.close(); });
}, },
_onStatusChanged: function(status) {
this._sessionStatus = status;
this._busyItem.setToggleState(status != GnomeSession.PresenceStatus.BUSY);
},
_onIMPresenceChanged: function(am, type) {
if (type == Tp.ConnectionPresenceType.AVAILABLE &&
this._sessionStatus == GnomeSession.PresenceStatus.BUSY)
this._presence.SetStatusRemote(GnomeSession.PresenceStatus.AVAILABLE);
},
_updateClearSensitivity: function() { _updateClearSensitivity: function() {
this._clearItem.setSensitive(this._tray.clearableCount > 0); this._clearItem.setSensitive(this._tray.clearableCount > 0);
}, },
_updatePresence: function(item, state) {
let status = state ? GnomeSession.PresenceStatus.AVAILABLE
: GnomeSession.PresenceStatus.BUSY;
this._presence.SetStatusRemote(status);
let [type, s ,msg] = this._accountManager.get_most_available_presence();
let newType = 0;
let newStatus;
if (status == GnomeSession.PresenceStatus.BUSY &&
type == Tp.ConnectionPresenceType.AVAILABLE) {
newType = Tp.ConnectionPresenceType.BUSY;
newStatus = 'busy';
} else if (status == GnomeSession.PresenceStatus.AVAILABLE &&
type == Tp.ConnectionPresenceType.BUSY) {
newType = Tp.ConnectionPresenceType.AVAILABLE;
newStatus = 'available';
}
if (newType > 0)
this._accountManager.set_all_requested_presences(newType,
newStatus, msg);
}
}); });
const MessageTrayMenuButton = new Lang.Class({ const MessageTrayMenuButton = new Lang.Class({
@ -1650,6 +1786,7 @@ const MessageTray = new Lang.Class({
this._setClickedSummaryItem(null); this._setClickedSummaryItem(null);
this._updateState(); this._updateState();
actor.grab_key_focus(); actor.grab_key_focus();
return Clutter.EVENT_PROPAGATE;
})); }));
global.focus_manager.add_group(this.actor); global.focus_manager.add_group(this.actor);
this._summary = new St.BoxLayout({ style_class: 'message-tray-summary', this._summary = new St.BoxLayout({ style_class: 'message-tray-summary',
@ -1764,7 +1901,7 @@ const MessageTray = new Lang.Class({
Shell.KeyBindingMode.OVERVIEW, Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._expandActiveNotification)); Lang.bind(this, this._expandActiveNotification));
this._sources = new Hash.Map(); this._sources = new Map();
this._chatSummaryItemsCount = 0; this._chatSummaryItemsCount = 0;
this._trayDwellTimeoutId = 0; this._trayDwellTimeoutId = 0;
@ -1801,7 +1938,7 @@ const MessageTray = new Lang.Class({
}, },
_updateNoMessagesLabel: function() { _updateNoMessagesLabel: function() {
this._noMessages.visible = this._sources.size() == 0; this._noMessages.visible = this._sources.size == 0;
}, },
_sessionUpdated: function() { _sessionUpdated: function() {
@ -1855,32 +1992,32 @@ const MessageTray = new Lang.Class({
this._trayDwellTimeoutId = 0; this._trayDwellTimeoutId = 0;
if (Main.layoutManager.bottomMonitor.inFullscreen) if (Main.layoutManager.bottomMonitor.inFullscreen)
return false; return GLib.SOURCE_REMOVE;
// We don't want to open the tray when a modal dialog // We don't want to open the tray when a modal dialog
// is up, so we check the modal count for that. When we are in the // is up, so we check the modal count for that. When we are in the
// overview we have to take the overview's modal push into account // overview we have to take the overview's modal push into account
if (Main.modalCount > (Main.overview.visible ? 1 : 0)) if (Main.modalCount > (Main.overview.visible ? 1 : 0))
return false; return GLib.SOURCE_REMOVE;
// If the user interacted with the focus window since we started the tray // If the user interacted with the focus window since we started the tray
// dwell (by clicking or typing), don't activate the message tray // dwell (by clicking or typing), don't activate the message tray
let focusWindow = global.display.focus_window; let focusWindow = global.display.focus_window;
let currentUserTime = focusWindow ? focusWindow.user_time : 0; let currentUserTime = focusWindow ? focusWindow.user_time : 0;
if (currentUserTime != this._trayDwellUserTime) if (currentUserTime != this._trayDwellUserTime)
return false; return GLib.SOURCE_REMOVE;
this.openTray(); this.openTray();
return false; return GLib.SOURCE_REMOVE;
}, },
_onNotificationKeyRelease: function(actor, event) { _onNotificationKeyRelease: function(actor, event) {
if (event.get_key_symbol() == Clutter.KEY_Escape && event.get_state() == 0) { if (event.get_key_symbol() == Clutter.KEY_Escape && event.get_state() == 0) {
this._closeNotification(); this._closeNotification();
return true; return Clutter.EVENT_STOP;
} }
return false; return Clutter.EVENT_PROPAGATE;
}, },
_closeNotification: function() { _closeNotification: function() {
@ -1926,7 +2063,7 @@ const MessageTray = new Lang.Class({
this._summary.insert_child_at_index(summaryItem.actor, this._chatSummaryItemsCount); this._summary.insert_child_at_index(summaryItem.actor, this._chatSummaryItemsCount);
} }
if (!source.trayIcon && !source.isChat && !source.resident) if (source.isClearable)
this.clearableCount++; this.clearableCount++;
this._sources.set(source, obj); this._sources.set(source, obj);
@ -1964,13 +2101,14 @@ const MessageTray = new Lang.Class({
}, },
_removeSource: function(source) { _removeSource: function(source) {
let [, obj] = this._sources.delete(source); let obj = this._sources.get(source);
this._sources.delete(source);
let summaryItem = obj.summaryItem; let summaryItem = obj.summaryItem;
if (source.isChat) if (source.isChat)
this._chatSummaryItemsCount--; this._chatSummaryItemsCount--;
if (!source.trayIcon && !source.isChat && !source.resident) if (source.isClearable)
this.clearableCount--; this.clearableCount--;
source.disconnect(obj.notifyId); source.disconnect(obj.notifyId);
@ -1985,7 +2123,7 @@ const MessageTray = new Lang.Class({
}, },
getSources: function() { getSources: function() {
return this._sources.keys(); return [k for (k of this._sources.keys())];
}, },
_onSourceEnableChanged: function(policy, source) { _onSourceEnableChanged: function(policy, source) {
@ -2035,7 +2173,10 @@ const MessageTray = new Lang.Class({
}, },
toggleAndNavigate: function() { toggleAndNavigate: function() {
if (this.toggle()) if (!this.toggle())
return;
if (this._traySummoned)
this._summary.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false); this._summary.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
}, },
@ -2198,7 +2339,7 @@ const MessageTray = new Lang.Class({
this._updateNotificationTimeout(0); this._updateNotificationTimeout(0);
this._updateState(); this._updateState();
} }
return false; return GLib.SOURCE_REMOVE;
}, },
_escapeTray: function() { _escapeTray: function() {
@ -2215,6 +2356,13 @@ const MessageTray = new Lang.Class({
// _updateState() figures out what (if anything) needs to be done // _updateState() figures out what (if anything) needs to be done
// at the present time. // at the present time.
_updateState: function() { _updateState: function() {
// If our state changes caused _updateState to be called,
// just exit now to prevent reentrancy issues.
if (this._updatingState)
return;
this._updatingState = true;
// Filter out acknowledged notifications. // Filter out acknowledged notifications.
this._notificationQueue = this._notificationQueue.filter(function(n) { this._notificationQueue = this._notificationQueue.filter(function(n) {
return !n.acknowledged; return !n.acknowledged;
@ -2232,10 +2380,9 @@ const MessageTray = new Lang.Class({
this._showNotification(); this._showNotification();
} }
} else if (this._notificationState == State.SHOWN) { } else if (this._notificationState == State.SHOWN) {
let pinned = this._pointerInNotification && !this._notificationRemoved;
let expired = (this._userActiveWhileNotificationShown && let expired = (this._userActiveWhileNotificationShown &&
this._notificationTimeoutId == 0 && this._notificationTimeoutId == 0 &&
!(this._notification.urgency == Urgency.CRITICAL) && this._notification.urgency != Urgency.CRITICAL &&
!this._notification.focused && !this._notification.focused &&
!this._pointerInNotification); !this._pointerInNotification);
let mustClose = (this._notificationRemoved || !hasNotifications || expired || this._traySummoned); let mustClose = (this._notificationRemoved || !hasNotifications || expired || this._traySummoned);
@ -2243,9 +2390,9 @@ const MessageTray = new Lang.Class({
if (mustClose) { if (mustClose) {
let animate = hasNotifications && !this._notificationRemoved; let animate = hasNotifications && !this._notificationRemoved;
this._hideNotification(animate); this._hideNotification(animate);
} else if (pinned && !this._notification.expanded) { } else if (this._pointerInNotification && !this._notification.expanded) {
this._expandNotification(false); this._expandNotification(false);
} else if (pinned) { } else if (this._pointerInNotification) {
this._ensureNotificationFocused(); this._ensureNotificationFocused();
} }
} }
@ -2292,11 +2439,12 @@ const MessageTray = new Lang.Class({
this._desktopCloneState == State.SHOWN); this._desktopCloneState == State.SHOWN);
let desktopCloneShouldBeVisible = (trayShouldBeVisible); let desktopCloneShouldBeVisible = (trayShouldBeVisible);
if (!desktopCloneIsVisible && desktopCloneShouldBeVisible) { if (!desktopCloneIsVisible && desktopCloneShouldBeVisible)
this._showDesktopClone(); this._showDesktopClone();
} else if (desktopCloneIsVisible && !desktopCloneShouldBeVisible) { else if (desktopCloneIsVisible && !desktopCloneShouldBeVisible)
this._hideDesktopClone(); this._hideDesktopClone();
}
this._updatingState = false;
}, },
_tween: function(actor, statevar, value, params) { _tween: function(actor, statevar, value, params) {
@ -2364,7 +2512,7 @@ const MessageTray = new Lang.Class({
let cloneSource = Main.overview.visible ? Main.layoutManager.overviewGroup : global.window_group; let cloneSource = Main.overview.visible ? Main.layoutManager.overviewGroup : global.window_group;
this._desktopClone = new Clutter.Clone({ source: cloneSource, this._desktopClone = new Clutter.Clone({ source: cloneSource,
clip: new Clutter.Geometry(this._bottomMonitorGeometry) }); clip: new Clutter.Geometry(this._bottomMonitorGeometry) });
Main.uiGroup.insert_child_above(this._desktopClone, cloneSource); Main.layoutManager.sessionGroup.insert_child_above(this._desktopClone, cloneSource);
this._desktopClone.x = 0; this._desktopClone.x = 0;
this._desktopClone.y = 0; this._desktopClone.y = 0;
this._desktopClone.show(); this._desktopClone.show();
@ -2524,7 +2672,7 @@ const MessageTray = new Lang.Class({
this._lastSeenMouseX = x; this._lastSeenMouseX = x;
this._lastSeenMouseY = y; this._lastSeenMouseY = y;
return false; return GLib.SOURCE_REMOVE;
}, },
_hideNotification: function(animate) { _hideNotification: function(animate) {
@ -2615,12 +2763,12 @@ const MessageTray = new Lang.Class({
} else if (this._notification.y != expandedY) { } else if (this._notification.y != expandedY) {
// Tween also opacity here, to override a possible tween that's // Tween also opacity here, to override a possible tween that's
// currently hiding the notification. // currently hiding the notification.
this._tween(this._notificationWidget, '_notificationState', State.SHOWN, Tweener.addTween(this._notificationWidget,
{ y: expandedY, { y: expandedY,
opacity: 255, opacity: 255,
time: ANIMATION_TIME, time: ANIMATION_TIME,
transition: 'easeOutQuad' transition: 'easeOutQuad'
}); });
} }
}, },
@ -2661,13 +2809,13 @@ const MessageTray = new Lang.Class({
Lang.bind(this, this._onSourceDoneDisplayingContent)); Lang.bind(this, this._onSourceDoneDisplayingContent));
this._summaryBoxPointer.bin.child = child; this._summaryBoxPointer.bin.child = child;
this._grabHelper.grab({ actor: this._summaryBoxPointer.bin.child,
onUngrab: Lang.bind(this, this._onSummaryBoxPointerUngrabbed) });
this._summaryBoxPointer.actor.opacity = 0; this._summaryBoxPointer.actor.opacity = 0;
this._summaryBoxPointer.actor.show(); this._summaryBoxPointer.actor.show();
this._adjustSummaryBoxPointerPosition(); this._adjustSummaryBoxPointerPosition();
this._grabHelper.grab({ actor: this._summaryBoxPointer.bin.child,
onUngrab: Lang.bind(this, this._onSummaryBoxPointerUngrabbed) });
this._summaryBoxPointerState = State.SHOWING; this._summaryBoxPointerState = State.SHOWING;
this._summaryBoxPointer.show(BoxPointer.PopupAnimation.FULL, Lang.bind(this, function() { this._summaryBoxPointer.show(BoxPointer.PopupAnimation.FULL, Lang.bind(this, function() {
this._summaryBoxPointerState = State.SHOWN; this._summaryBoxPointerState = State.SHOWN;
@ -2722,13 +2870,13 @@ const MessageTray = new Lang.Class({
case Clutter.KEY_Escape: case Clutter.KEY_Escape:
this._setClickedSummaryItem(null); this._setClickedSummaryItem(null);
this._updateState(); this._updateState();
return true; return Clutter.EVENT_STOP;
case Clutter.KEY_Delete: case Clutter.KEY_Delete:
this._clickedSummaryItem.source.destroy(); this._clickedSummaryItem.source.destroy();
this._escapeTray(); this._escapeTray();
return true; return Clutter.EVENT_STOP;
} }
return false; return Clutter.EVENT_PROPAGATE;
}, },
_onSummaryBoxPointerUngrabbed: function() { _onSummaryBoxPointerUngrabbed: function() {
@ -2766,8 +2914,6 @@ const MessageTray = new Lang.Class({
this._summaryBoxPointerItem.doneShowingNotificationStack(); this._summaryBoxPointerItem.doneShowingNotificationStack();
this._summaryBoxPointerItem = null; this._summaryBoxPointerItem = null;
if (source.isTransient && !this._reNotifyAfterHideNotification)
source.destroy(NotificationDestroyedReason.EXPIRED);
if (this._reNotifyAfterHideNotification) { if (this._reNotifyAfterHideNotification) {
this._onNotify(this._reNotifyAfterHideNotification.source, this._reNotifyAfterHideNotification); this._onNotify(this._reNotifyAfterHideNotification.source, this._reNotifyAfterHideNotification);
this._reNotifyAfterHideNotification = null; this._reNotifyAfterHideNotification = null;
@ -2786,7 +2932,6 @@ const SystemNotificationSource = new Lang.Class({
_init: function() { _init: function() {
this.parent(_("System Information"), 'dialog-information-symbolic'); this.parent(_("System Information"), 'dialog-information-symbolic');
this.setTransient(true);
}, },
open: function() { open: function() {

View File

@ -41,7 +41,7 @@ const ModalDialog = new Lang.Class({
_init: function(params) { _init: function(params) {
params = Params.parse(params, { shellReactive: false, params = Params.parse(params, { shellReactive: false,
styleClass: null, styleClass: null,
parentActor: Main.uiGroup, parentActor: Main.layoutManager.dialogGroup,
keybindingMode: Shell.KeyBindingMode.SYSTEM_MODAL, keybindingMode: Shell.KeyBindingMode.SYSTEM_MODAL,
shouldFadeIn: true, shouldFadeIn: true,
destroyOnClose: true }); destroyOnClose: true });
@ -89,7 +89,8 @@ const ModalDialog = new Lang.Class({
if (!this._shellReactive) { if (!this._shellReactive) {
this._lightbox = new Lightbox.Lightbox(this._group, this._lightbox = new Lightbox.Lightbox(this._group,
{ inhibitEvents: true }); { inhibitEvents: true,
radialEffect: true });
this._lightbox.highlight(this._backgroundBin); this._lightbox.highlight(this._backgroundBin);
this._eventBlocker = new Clutter.Actor({ reactive: true }); this._eventBlocker = new Clutter.Actor({ reactive: true });
@ -229,6 +230,7 @@ const ModalDialog = new Lang.Class({
_onKeyPressEvent: function(object, event) { _onKeyPressEvent: function(object, event) {
this._pressedKey = event.get_key_symbol(); this._pressedKey = event.get_key_symbol();
return Clutter.EVENT_PROPAGATE;
}, },
_onKeyReleaseEvent: function(object, event) { _onKeyReleaseEvent: function(object, event) {
@ -237,21 +239,21 @@ const ModalDialog = new Lang.Class({
let symbol = event.get_key_symbol(); let symbol = event.get_key_symbol();
if (symbol != pressedKey) if (symbol != pressedKey)
return false; return Clutter.EVENT_PROPAGATE;
let buttonInfo = this._buttonKeys[symbol]; let buttonInfo = this._buttonKeys[symbol];
if (!buttonInfo) if (!buttonInfo)
return false; return Clutter.EVENT_PROPAGATE;
let button = buttonInfo['button']; let button = buttonInfo['button'];
let action = buttonInfo['action']; let action = buttonInfo['action'];
if (action && button.reactive) { if (action && button.reactive) {
action(); action();
return true; return Clutter.EVENT_STOP;
} }
return false; return Clutter.EVENT_PROPAGATE;
}, },
_onGroupDestroy: function() { _onGroupDestroy: function() {

View File

@ -4,6 +4,7 @@ const Clutter = imports.gi.Clutter;
const GdkPixbuf = imports.gi.GdkPixbuf; const GdkPixbuf = imports.gi.GdkPixbuf;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang; const Lang = imports.lang;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
@ -15,54 +16,56 @@ const MessageTray = imports.ui.messageTray;
const Params = imports.misc.params; const Params = imports.misc.params;
const Util = imports.misc.util; const Util = imports.misc.util;
let nextNotificationId = 1;
// Should really be defined in Gio.js // Should really be defined in Gio.js
const BusIface = <interface name="org.freedesktop.DBus"> const BusIface = '<node> \
<method name="GetConnectionUnixProcessID"> <interface name="org.freedesktop.DBus"> \
<arg type="s" direction="in" /> <method name="GetConnectionUnixProcessID"> \
<arg type="u" direction="out" /> <arg type="s" direction="in" /> \
</method> <arg type="u" direction="out" /> \
</interface>; </method> \
</interface> \
</node>';
var BusProxy = Gio.DBusProxy.makeProxyWrapper(BusIface); var BusProxy = Gio.DBusProxy.makeProxyWrapper(BusIface);
function Bus() { function Bus() {
return new BusProxy(Gio.DBus.session, 'org.freedesktop.DBus', '/org/freedesktop/DBus'); return new BusProxy(Gio.DBus.session, 'org.freedesktop.DBus', '/org/freedesktop/DBus');
} }
const NotificationDaemonIface = <interface name="org.freedesktop.Notifications"> const FdoNotificationsIface = '<node> \
<method name="Notify"> <interface name="org.freedesktop.Notifications"> \
<arg type="s" direction="in"/> <method name="Notify"> \
<arg type="u" direction="in"/> <arg type="s" direction="in"/> \
<arg type="s" direction="in"/> <arg type="u" direction="in"/> \
<arg type="s" direction="in"/> <arg type="s" direction="in"/> \
<arg type="s" direction="in"/> <arg type="s" direction="in"/> \
<arg type="as" direction="in"/> <arg type="s" direction="in"/> \
<arg type="a{sv}" direction="in"/> <arg type="as" direction="in"/> \
<arg type="i" direction="in"/> <arg type="a{sv}" direction="in"/> \
<arg type="u" direction="out"/> <arg type="i" direction="in"/> \
</method> <arg type="u" direction="out"/> \
<method name="CloseNotification"> </method> \
<arg type="u" direction="in"/> <method name="CloseNotification"> \
</method> <arg type="u" direction="in"/> \
<method name="GetCapabilities"> </method> \
<arg type="as" direction="out"/> <method name="GetCapabilities"> \
</method> <arg type="as" direction="out"/> \
<method name="GetServerInformation"> </method> \
<arg type="s" direction="out"/> <method name="GetServerInformation"> \
<arg type="s" direction="out"/> <arg type="s" direction="out"/> \
<arg type="s" direction="out"/> <arg type="s" direction="out"/> \
<arg type="s" direction="out"/> <arg type="s" direction="out"/> \
</method> <arg type="s" direction="out"/> \
<signal name="NotificationClosed"> </method> \
<arg type="u"/> <signal name="NotificationClosed"> \
<arg type="u"/> <arg type="u"/> \
</signal> <arg type="u"/> \
<signal name="ActionInvoked"> </signal> \
<arg type="u"/> <signal name="ActionInvoked"> \
<arg type="s"/> <arg type="u"/> \
</signal> <arg type="s"/> \
</interface>; </signal> \
</interface> \
</node>';
const NotificationClosedReason = { const NotificationClosedReason = {
EXPIRED: 1, EXPIRED: 1,
@ -103,131 +106,11 @@ const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
'ibus-ui-gtk': 'keyboard' 'ibus-ui-gtk': 'keyboard'
}; };
const NotificationGenericPolicy = new Lang.Class({ const FdoNotificationDaemon = new Lang.Class({
Name: 'NotificationGenericPolicy', Name: 'FdoNotificationDaemon',
Extends: MessageTray.NotificationPolicy,
_init: function() { _init: function() {
// Don't chain to parent, it would try setting this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(FdoNotificationsIface, this);
// 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',
_init: function() {
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(NotificationDaemonIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/freedesktop/Notifications'); this._dbusImpl.export(Gio.DBus.session, '/org/freedesktop/Notifications');
this._sources = []; this._sources = [];
@ -235,6 +118,8 @@ const NotificationDaemon = new Lang.Class({
this._notifications = {}; this._notifications = {};
this._busProxy = new Bus(); this._busProxy = new Bus();
this._nextNotificationId = 1;
this._trayManager = new Shell.TrayManager(); this._trayManager = new Shell.TrayManager();
this._trayIconAddedId = this._trayManager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded)); this._trayIconAddedId = this._trayManager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded));
this._trayIconRemovedId = this._trayManager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved)); this._trayIconRemovedId = this._trayManager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
@ -296,12 +181,10 @@ const NotificationDaemon = new Lang.Class({
}, },
// Returns the source associated with ndata.notification if it is set. // Returns the source associated with ndata.notification if it is set.
// Otherwise, returns the source associated with the title and pid if // If the existing or requested source is associated with a tray icon
// such source is stored in this._sources and the notification is not // and passed in pid matches a pid of an existing source, the title
// transient. If the existing or requested source is associated with // match is ignored to enable representing a tray icon and notifications
// a tray icon and passed in pid matches a pid of an existing source, // from the same application with a single source.
// the title match is ignored to enable representing a tray icon and
// notifications from the same application with a single source.
// //
// If no existing source is found, a new source is created as long as // If no existing source is found, a new source is created as long as
// pid is provided. // pid is provided.
@ -319,32 +202,20 @@ const NotificationDaemon = new Lang.Class({
if (ndata && ndata.notification) if (ndata && ndata.notification)
return ndata.notification.source; return ndata.notification.source;
let isForTransientNotification = (ndata && ndata.hints['transient'] == true); let source = this._lookupSource(title, pid, trayIcon);
if (source) {
// We don't want to override a persistent notification source.setTitle(title);
// with a transient one from the same sender, so we return source;
// always create a new source object for new transient notifications
// and never add it to this._sources .
if (!isForTransientNotification) {
let source = this._lookupSource(title, pid, trayIcon);
if (source) {
source.setTitle(title);
return source;
}
} }
let source = new Source(title, pid, sender, trayIcon, ndata ? ndata.hints['desktop-entry'] : null); let source = new FdoNotificationDaemonSource(title, pid, sender, trayIcon, ndata ? ndata.hints['desktop-entry'] : null);
source.setTransient(isForTransientNotification);
if (!isForTransientNotification) { this._sources.push(source);
this._sources.push(source); source.connect('destroy', Lang.bind(this, function() {
source.connect('destroy', Lang.bind(this, let index = this._sources.indexOf(source);
function() { if (index >= 0)
let index = this._sources.indexOf(source); this._sources.splice(index, 1);
if (index >= 0) }));
this._sources.splice(index, 1);
}));
}
Main.messageTray.add(source); Main.messageTray.add(source);
return source; return source;
@ -372,11 +243,11 @@ const NotificationDaemon = new Lang.Class({
hints['category'] == 'presence.offline')) { hints['category'] == 'presence.offline')) {
// Ignore replacesId since we already sent back a // Ignore replacesId since we already sent back a
// NotificationClosed for that id. // NotificationClosed for that id.
id = nextNotificationId++; id = this._nextNotificationId++;
Mainloop.idle_add(Lang.bind(this, Mainloop.idle_add(Lang.bind(this,
function () { function () {
this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED); this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
return false; return GLib.SOURCE_REMOVE;
})); }));
return invocation.return_value(GLib.Variant.new('(u)', [id])); return invocation.return_value(GLib.Variant.new('(u)', [id]));
} }
@ -396,12 +267,13 @@ const NotificationDaemon = new Lang.Class({
if (!hints['image-path'] && hints['image_path']) if (!hints['image-path'] && hints['image_path'])
hints['image-path'] = hints['image_path']; // version 1.1 of the spec hints['image-path'] = hints['image_path']; // version 1.1 of the spec
if (!hints['image-data']) if (!hints['image-data']) {
if (hints['image_data']) if (hints['image_data'])
hints['image-data'] = hints['image_data']; // version 1.1 of the spec hints['image-data'] = hints['image_data']; // version 1.1 of the spec
else if (hints['icon_data'] && !hints['image-path']) else if (hints['icon_data'] && !hints['image-path'])
// early versions of the spec; 'icon_data' should only be used if 'image-path' is not available // early versions of the spec; 'icon_data' should only be used if 'image-path' is not available
hints['image-data'] = hints['icon_data']; hints['image-data'] = hints['icon_data'];
}
let ndata = { appName: appName, let ndata = { appName: appName,
icon: icon, icon: icon,
@ -415,7 +287,7 @@ const NotificationDaemon = new Lang.Class({
ndata.notification = this._notifications[replacesId].notification; ndata.notification = this._notifications[replacesId].notification;
} else { } else {
replacesId = 0; replacesId = 0;
ndata.id = id = nextNotificationId++; ndata.id = id = this._nextNotificationId++;
} }
this._notifications[id] = ndata; this._notifications[id] = ndata;
@ -450,26 +322,29 @@ const NotificationDaemon = new Lang.Class({
let [pid] = result; let [pid] = result;
source = this._getSource(appName, pid, ndata, sender, null); source = this._getSource(appName, pid, ndata, sender, null);
// We only store sender-pid entries for persistent sources. this._senderToPid[sender] = pid;
// Removing the entries once the source is destroyed source.connect('destroy', Lang.bind(this, function() {
// would result in the entries associated with transient delete this._senderToPid[sender];
// sources removed once the notification is shown anyway. }));
// However, keeping these pairs would mean that we would
// possibly remove an entry associated with a persistent
// source when a transient source for the same sender is
// distroyed.
if (!source.isTransient) {
this._senderToPid[sender] = pid;
source.connect('destroy', Lang.bind(this, function() {
delete this._senderToPid[sender];
}));
}
this._notifyForSource(source, ndata); this._notifyForSource(source, ndata);
})); }));
return invocation.return_value(GLib.Variant.new('(u)', [id])); return invocation.return_value(GLib.Variant.new('(u)', [id]));
}, },
_makeButton: function(id, label, useActionIcons) {
let button = new St.Button({ can_focus: true });
let iconName = id.endsWith('-symbolic') ? id : id + '-symbolic';
if (useActionIcons && Gtk.IconTheme.get_default().has_icon(iconName)) {
button.add_style_class_name('notification-icon-button');
button.child = new St.Icon({ icon_name: iconName });
} else {
button.add_style_class_name('notification-button');
button.label = label;
}
return button;
},
_notifyForSource: function(source, ndata) { _notifyForSource: function(source, ndata) {
let [id, icon, summary, body, actions, hints, notification] = let [id, icon, summary, body, actions, hints, notification] =
[ndata.id, ndata.icon, ndata.summary, ndata.body, [ndata.id, ndata.icon, ndata.summary, ndata.body,
@ -495,10 +370,6 @@ const NotificationDaemon = new Lang.Class({
} }
this._emitNotificationClosed(ndata.id, notificationClosedReason); this._emitNotificationClosed(ndata.id, notificationClosedReason);
})); }));
notification.connect('action-invoked', Lang.bind(this,
function(n, actionId) {
this._emitActionInvoked(ndata.id, actionId);
}));
} }
// Mark music notifications so they can be shown in the screen shield // Mark music notifications so they can be shown in the screen shield
@ -532,18 +403,33 @@ const NotificationDaemon = new Lang.Class({
soundName: hints['sound-name'] }); soundName: hints['sound-name'] });
notification.setImage(image); notification.setImage(image);
let hasDefaultAction = false;
if (actions.length) { if (actions.length) {
notification.setUseActionIcons(hints['action-icons'] == true); let useActionIcons = (hints['action-icons'] == true);
for (let i = 0; i < actions.length - 1; i += 2) { for (let i = 0; i < actions.length - 1; i += 2) {
if (actions[i] == 'default') let [actionId, label] = [actions[i], actions[i+1]];
notification.connect('clicked', Lang.bind(this, if (actionId == 'default') {
function() { hasDefaultAction = true;
this._emitActionInvoked(ndata.id, "default"); } else {
})); notification.addButton(this._makeButton(actionId, label, useActionIcons), Lang.bind(this, function() {
else this._emitActionInvoked(ndata.id, actionId);
notification.addButton(actions[i], actions[i + 1]); }));
}
} }
} }
if (hasDefaultAction) {
notification.connect('clicked', Lang.bind(this, function() {
this._emitActionInvoked(ndata.id, 'default');
}));
} else {
notification.connect('clicked', Lang.bind(this, function() {
source.open();
}));
}
switch (hints.urgency) { switch (hints.urgency) {
case Urgency.LOW: case Urgency.LOW:
notification.setUrgency(MessageTray.Urgency.LOW); notification.setUrgency(MessageTray.Urgency.LOW);
@ -636,8 +522,8 @@ const NotificationDaemon = new Lang.Class({
} }
}); });
const Source = new Lang.Class({ const FdoNotificationDaemonSource = new Lang.Class({
Name: 'NotificationDaemonSource', Name: 'FdoNotificationDaemonSource',
Extends: MessageTray.Source, Extends: MessageTray.Source,
_init: function(title, pid, sender, trayIcon, appId) { _init: function(title, pid, sender, trayIcon, appId) {
@ -672,11 +558,11 @@ const Source = new Lang.Class({
}, },
_createPolicy: function() { _createPolicy: function() {
if (this.app) { if (this.app && this.app.get_app_info()) {
let id = this.app.get_id().replace(/\.desktop$/,''); let id = this.app.get_id().replace(/\.desktop$/,'');
return new NotificationApplicationPolicy(id); return new MessageTray.NotificationApplicationPolicy(id);
} else { } else {
return new NotificationGenericPolicy(); return new MessageTray.NotificationGenericPolicy();
} }
}, },
@ -752,22 +638,6 @@ const Source = new Lang.Class({
return null; return null;
}, },
_setApp: function(appId) {
if (this.app)
return;
this.app = this._getApp(appId);
if (!this.app)
return;
// Only override the icon if we were previously using
// notification-based icons (ie, not a trayicon) or if it was unset before
if (!this.trayIcon) {
this.useNotificationIcon = false;
this.iconUpdated();
}
},
setTitle: function(title) { setTitle: function(title) {
// Do nothing if .app is set, we don't want to override the // Do nothing if .app is set, we don't want to override the
// app name with whatever is provided through libnotify (usually // app name with whatever is provided through libnotify (usually
@ -778,9 +648,9 @@ const Source = new Lang.Class({
this.parent(title); this.parent(title);
}, },
open: function(notification) { open: function() {
this.destroyNonResidentNotifications();
this.openApp(); this.openApp();
this.destroyNonResidentNotifications();
}, },
_lastNotificationRemoved: function() { _lastNotificationRemoved: function() {
@ -792,11 +662,8 @@ const Source = new Lang.Class({
if (this.app == null) if (this.app == null)
return; return;
let windows = this.app.get_windows(); this.app.activate();
if (windows.length > 0) { Main.overview.hide();
let mostRecentWindow = windows[0];
Main.activateWindow(mostRecentWindow);
}
}, },
destroy: function() { destroy: function() {
@ -823,3 +690,276 @@ const Source = new Lang.Class({
} }
} }
}); });
const GtkNotificationDaemonNotification = new Lang.Class({
Name: 'GtkNotificationDaemonNotification',
Extends: MessageTray.Notification,
_init: function(source, notification) {
this.parent(source);
this._serialized = GLib.Variant.new('a{sv}', notification);
let { "title": title,
"body": body,
"icon": gicon,
"urgent": urgent,
"buttons": buttons,
"default-action": defaultAction,
"default-action-target": defaultActionTarget } = notification;
this.setUrgency(urgent.unpack() ? MessageTray.Urgency.CRITICAL
: MessageTray.Urgency.NORMAL);
if (buttons) {
buttons.deep_unpack().forEach(Lang.bind(this, function(button) {
this.addAction(button.label.unpack(),
Lang.bind(this, this._onButtonClicked, button));
}));
}
this._defaultAction = defaultAction ? defaultAction.unpack() : null;
this._defaultActionTarget = defaultActionTarget;
this.update(title.unpack(), body ? body.unpack() : null,
{ gicon: gicon ? Gio.icon_deserialize(gicon) : null });
},
_activateAction: function(namespacedActionId, target) {
if (namespacedActionId) {
if (namespacedActionId.startsWith('app.')) {
let actionId = namespacedActionId.slice('app.'.length);
this.source.activateAction(actionId, target);
}
} else {
this.source.open();
}
},
_onButtonClicked: function(button) {
let { 'action': action, 'target': actionTarget } = button;
this._activateAction(action.unpack(), actionTarget);
},
_onClicked: function() {
this._activateAction(this._defaultAction, this._defaultActionTarget);
this.parent();
},
serialize: function() {
return this._serialized;
},
});
const FdoApplicationIface = '<node> \
<interface name="org.freedesktop.Application"> \
<method name="ActivateAction"> \
<arg type="s" direction="in" /> \
<arg type="av" direction="in" /> \
<arg type="a{sv}" direction="in" /> \
</method> \
<method name="Activate"> \
<arg type="a{sv}" direction="in" /> \
</method> \
</interface> \
</node>';
const FdoApplicationProxy = Gio.DBusProxy.makeProxyWrapper(FdoApplicationIface);
function objectPathFromAppId(appId) {
return '/' + appId.replace(/\./g, '/');
}
function getPlatformData() {
let startupId = GLib.Variant.new('s', '_TIME' + global.get_current_time());
return { "desktop-startup-id": startupId };
}
function InvalidAppError() {}
const GtkNotificationDaemonAppSource = new Lang.Class({
Name: 'GtkNotificationDaemonAppSource',
Extends: MessageTray.Source,
_init: function(appId) {
this._appId = appId;
this._objectPath = objectPathFromAppId(appId);
this._app = Shell.AppSystem.get_default().lookup_app(appId + '.desktop');
if (!this._app)
throw new InvalidAppError();
this._notifications = {};
this.parent(this._app.get_name());
},
createIcon: function(size) {
return this._app.create_icon_texture(size);
},
_createPolicy: function() {
return new MessageTray.NotificationApplicationPolicy(this._appId);
},
_createApp: function() {
return new FdoApplicationProxy(Gio.DBus.session, this._appId, this._objectPath);
},
activateAction: function(actionId, target) {
let app = this._createApp();
app.ActivateActionRemote(actionId, target ? [target] : [], getPlatformData());
},
open: function() {
let app = this._createApp();
app.ActivateRemote(getPlatformData());
},
addNotification: function(notificationId, notificationParams, showBanner) {
if (this._notifications[notificationId])
this._notifications[notificationId].destroy();
let notification = new GtkNotificationDaemonNotification(this, notificationParams);
notification.connect('destroy', Lang.bind(this, function() {
delete this._notifications[notificationId];
}));
this._notifications[notificationId] = notification;
if (showBanner)
this.notify(notification);
else
this.pushNotification(notification);
},
removeNotification: function(notificationId) {
if (this._notifications[notificationId])
this._notifications[notificationId].destroy(MessageTray.NotificationDestroyedReason.SOURCE_CLOSED);
},
serialize: function() {
let notifications = [];
for (let notificationId in this._notifications) {
let notification = this._notifications[notificationId];
notifications.push([notificationId, notification.serialize()]);
}
return [this._appId, notifications];
},
});
const GtkNotificationsIface = '<node> \
<interface name="org.gtk.Notifications"> \
<method name="AddNotification"> \
<arg type="s" direction="in" /> \
<arg type="s" direction="in" /> \
<arg type="a{sv}" direction="in" /> \
</method> \
<method name="RemoveNotification"> \
<arg type="s" direction="in" /> \
<arg type="s" direction="in" /> \
</method> \
</interface> \
</node>';
const GtkNotificationDaemon = new Lang.Class({
Name: 'GtkNotificationDaemon',
_init: function() {
this._sources = {};
this._loadNotifications();
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GtkNotificationsIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gtk/Notifications');
Gio.DBus.session.own_name('org.gtk.Notifications', Gio.BusNameOwnerFlags.REPLACE, null, null);
},
_ensureAppSource: function(appId) {
if (this._sources[appId])
return this._sources[appId];
let source = new GtkNotificationDaemonAppSource(appId);
source.connect('destroy', Lang.bind(this, function() {
delete this._sources[appId];
this._saveNotifications();
}));
source.connect('count-updated', Lang.bind(this, this._saveNotifications));
Main.messageTray.add(source);
this._sources[appId] = source;
return source;
},
_loadNotifications: function() {
this._isLoading = true;
let value = global.get_persistent_state('a(sa(sv))', 'notifications');
if (value) {
let sources = value.deep_unpack();
sources.forEach(Lang.bind(this, function([appId, notifications]) {
if (notifications.length == 0)
return;
let source;
try {
source = this._ensureAppSource(appId);
} catch(e if e instanceof InvalidAppError) {
return;
}
notifications.forEach(function([notificationId, notification]) {
source.addNotification(notificationId, notification.deep_unpack(), false);
});
}));
}
this._isLoading = false;
},
_saveNotifications: function() {
if (this._isLoading)
return;
let sources = [];
for (let appId in this._sources) {
let source = this._sources[appId];
sources.push(source.serialize());
}
global.set_persistent_state('notifications', new GLib.Variant('a(sa(sv))', sources));
},
AddNotificationAsync: function(params, invocation) {
let [appId, notificationId, notification] = params;
let source;
try {
source = this._ensureAppSource(appId);
} catch(e if e instanceof InvalidAppError) {
invocation.return_dbus_error('org.gtk.Notifications.InvalidApp', 'The app by ID "%s" could not be found'.format(appId));
return;
}
source.addNotification(notificationId, notification, true);
invocation.return_value(null);
},
RemoveNotificationAsync: function(params, invocation) {
let [appId, notificationId] = params;
let source = this._sources[appId];
if (source)
source.removeNotification(notificationId);
invocation.return_value(null);
},
});
const NotificationDaemon = new Lang.Class({
Name: 'NotificationDaemon',
_init: function() {
this._fdoNotificationDaemon = new FdoNotificationDaemon();
this._gtkNotificationDaemon = new GtkNotificationDaemon();
},
});

View File

@ -1,6 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const St = imports.gi.St; const St = imports.gi.St;
const Lang = imports.lang; const Lang = imports.lang;
@ -65,6 +66,7 @@ const LevelBar = new Lang.Class({
cr.arc(radius, h - radius, radius, 0.5 * Math.PI, Math.PI); cr.arc(radius, h - radius, radius, 0.5 * Math.PI, Math.PI);
cr.arc(radius, radius, radius, Math.PI, 1.5 * Math.PI); cr.arc(radius, radius, radius, Math.PI, 1.5 * Math.PI);
cr.fill(); cr.fill();
cr.$dispose();
} }
}); });
@ -77,7 +79,8 @@ const OsdWindow = new Lang.Class({
y_expand: true, y_expand: true,
x_align: Clutter.ActorAlign.CENTER, x_align: Clutter.ActorAlign.CENTER,
y_align: Clutter.ActorAlign.CENTER }); y_align: Clutter.ActorAlign.CENTER });
this.actor.add_constraint(new Layout.MonitorConstraint({ primary: true })); this._currentMonitor = undefined;
this.setMonitor (-1);
this._box = new St.BoxLayout({ style_class: 'osd-window', this._box = new St.BoxLayout({ style_class: 'osd-window',
vertical: true }); vertical: true });
this.actor.add_actor(this._box); this.actor.add_actor(this._box);
@ -107,7 +110,7 @@ const OsdWindow = new Lang.Class({
Lang.bind(this, this._monitorsChanged)); Lang.bind(this, this._monitorsChanged));
this._monitorsChanged(); this._monitorsChanged();
Main.uiGroup.add_child(this.actor); Main.layoutManager.osdGroup.add_child(this.actor);
}, },
setIcon: function(icon) { setIcon: function(icon) {
@ -122,13 +125,15 @@ const OsdWindow = new Lang.Class({
setLevel: function(level) { setLevel: function(level) {
this._level.actor.visible = (level != undefined); this._level.actor.visible = (level != undefined);
if (this.actor.visible) if (level) {
Tweener.addTween(this._level, if (this.actor.visible)
{ level: level, Tweener.addTween(this._level,
time: LEVEL_ANIMATION_TIME, { level: level,
transition: 'easeOutQuad' }); time: LEVEL_ANIMATION_TIME,
else transition: 'easeOutQuad' });
this._level.level = level; else
this._level.level = level;
}
}, },
show: function() { show: function() {
@ -172,6 +177,7 @@ const OsdWindow = new Lang.Class({
Meta.enable_unredirect_for_screen(global.screen); Meta.enable_unredirect_for_screen(global.screen);
}) })
}); });
return GLib.SOURCE_REMOVE;
}, },
_reset: function() { _reset: function() {
@ -182,7 +188,13 @@ const OsdWindow = new Lang.Class({
_monitorsChanged: function() { _monitorsChanged: function() {
/* assume 110x110 on a 640x480 display and scale from there */ /* assume 110x110 on a 640x480 display and scale from there */
let monitor = Main.layoutManager.primaryMonitor; let monitor;
if (this._currentMonitor >= 0)
monitor = Main.layoutManager.monitors[this._currentMonitor];
else
monitor = Main.layoutManager.primaryMonitor;
let scalew = monitor.width / 640.0; let scalew = monitor.width / 640.0;
let scaleh = monitor.height / 480.0; let scaleh = monitor.height / 480.0;
let scale = Math.min(scalew, scaleh); let scale = Math.min(scalew, scaleh);
@ -206,5 +218,23 @@ const OsdWindow = new Lang.Class({
let minHeight = this._popupSize - horizontalPadding - topBorder - bottomBorder; let minHeight = this._popupSize - horizontalPadding - topBorder - bottomBorder;
this._box.style = 'min-height: %dpx;'.format(Math.max(minWidth, minHeight)); this._box.style = 'min-height: %dpx;'.format(Math.max(minWidth, minHeight));
},
setMonitor: function(index) {
let constraint;
if (index < 0)
index = -1;
if (this._currentMonitor == index)
return;
if (index < 0)
constraint = new Layout.MonitorConstraint({ primary: true });
else
constraint = new Layout.MonitorConstraint({ index: index });
this.actor.clear_constraints();
this.actor.add_constraint(constraint);
this._currentMonitor = index;
} }
}); });

View File

@ -1,6 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk; const Gtk = imports.gi.Gtk;
const Meta = imports.gi.Meta; const Meta = imports.gi.Meta;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
@ -78,10 +79,8 @@ const ShellInfo = new Lang.Class({
} }
this._undoCallback = undoCallback; this._undoCallback = undoCallback;
if (undoCallback) { if (undoCallback)
notification.addButton('system-undo', _("Undo")); notification.addAction(_("Undo"), Lang.bind(this, this._onUndoClicked));
notification.connect('action-invoked', Lang.bind(this, this._onUndoClicked));
}
this._source.notify(notification); this._source.notify(notification);
} }
@ -114,9 +113,6 @@ const Overview = new Lang.Class({
// rendering options without duplicating the texture data. // rendering options without duplicating the texture data.
let monitor = Main.layoutManager.primaryMonitor; let monitor = Main.layoutManager.primaryMonitor;
this._desktopFade = new St.Bin();
Main.layoutManager.overviewGroup.add_child(this._desktopFade);
let layout = new Clutter.BinLayout(); let layout = new Clutter.BinLayout();
this._stack = new Clutter.Actor({ layout_manager: layout }); this._stack = new Clutter.Actor({ layout_manager: layout });
this._stack.add_constraint(new LayoutManager.MonitorConstraint({ primary: true })); this._stack.add_constraint(new LayoutManager.MonitorConstraint({ primary: true }));
@ -135,6 +131,9 @@ const Overview = new Lang.Class({
Main.layoutManager.overviewGroup.add_child(this._backgroundGroup); Main.layoutManager.overviewGroup.add_child(this._backgroundGroup);
this._bgManagers = []; this._bgManagers = [];
this._desktopFade = new St.Widget();
Main.layoutManager.overviewGroup.add_child(this._desktopFade);
this._activationTime = 0; this._activationTime = 0;
this.visible = false; // animating to overview, in overview, animating out this.visible = false; // animating to overview, in overview, animating out
@ -148,8 +147,8 @@ const Overview = new Lang.Class({
// Dash elements, or mouseover handlers in the workspaces. // Dash elements, or mouseover handlers in the workspaces.
this._coverPane = new Clutter.Actor({ opacity: 0, this._coverPane = new Clutter.Actor({ opacity: 0,
reactive: true }); reactive: true });
this._stack.add_actor(this._coverPane); Main.layoutManager.overviewGroup.add_child(this._coverPane);
this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; })); this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return Clutter.EVENT_STOP; }));
this._stack.add_actor(this._overview); this._stack.add_actor(this._overview);
Main.layoutManager.overviewGroup.add_child(this._stack); Main.layoutManager.overviewGroup.add_child(this._stack);
@ -365,11 +364,13 @@ const Overview = new Lang.Class({
this._lastHoveredWindow = dragEvent.targetActor._delegate.metaWindow; this._lastHoveredWindow = dragEvent.targetActor._delegate.metaWindow;
this._windowSwitchTimeoutId = Mainloop.timeout_add(DND_WINDOW_SWITCH_TIMEOUT, this._windowSwitchTimeoutId = Mainloop.timeout_add(DND_WINDOW_SWITCH_TIMEOUT,
Lang.bind(this, function() { Lang.bind(this, function() {
this._windowSwitchTimeoutId = 0;
this._needsFakePointerEvent = true; this._needsFakePointerEvent = true;
Main.activateWindow(dragEvent.targetActor._delegate.metaWindow, Main.activateWindow(dragEvent.targetActor._delegate.metaWindow,
this._windowSwitchTimestamp); this._windowSwitchTimestamp);
this.hide(); this.hide();
this._lastHoveredWindow = null; this._lastHoveredWindow = null;
return GLib.SOURCE_REMOVE;
})); }));
} }
@ -378,6 +379,7 @@ const Overview = new Lang.Class({
_onScrollEvent: function(actor, event) { _onScrollEvent: function(actor, event) {
this.emit('scroll-event', event); this.emit('scroll-event', event);
return Clutter.EVENT_PROPAGATE;
}, },
addAction: function(action) { addAction: function(action) {
@ -445,17 +447,17 @@ const Overview = new Lang.Class({
this._inDrag = false; this._inDrag = false;
}, },
beginWindowDrag: function(source) { beginWindowDrag: function(clone) {
this.emit('window-drag-begin'); this.emit('window-drag-begin', clone);
this._inDrag = true; this._inDrag = true;
}, },
cancelledWindowDrag: function(source) { cancelledWindowDrag: function(clone) {
this.emit('window-drag-cancelled'); this.emit('window-drag-cancelled', clone);
}, },
endWindowDrag: function(source) { endWindowDrag: function(clone) {
this.emit('window-drag-end'); this.emit('window-drag-end', clone);
this._inDrag = false; this._inDrag = false;
}, },
@ -491,8 +493,8 @@ const Overview = new Lang.Class({
}, },
fadeOutDesktop: function() { fadeOutDesktop: function() {
if (!this._desktopFade.child) if (!this._desktopFade.get_n_children())
this._desktopFade.child = this._getDesktopClone(); this._desktopFade.add_child(this._getDesktopClone());
this._desktopFade.opacity = 255; this._desktopFade.opacity = 255;
this._desktopFade.show(); this._desktopFade.show();
@ -620,7 +622,7 @@ const Overview = new Lang.Class({
this.animationInProgress = true; this.animationInProgress = true;
this.visibleTarget = false; this.visibleTarget = false;
this.viewSelector.zoomFromOverview(); this.viewSelector.leaveOverview();
// Make other elements fade out. // Make other elements fade out.
Tweener.addTween(this._stack, Tweener.addTween(this._stack,

View File

@ -36,6 +36,7 @@ const SlideLayout = new Lang.Class({
_init: function(params) { _init: function(params) {
this._slideX = 1; this._slideX = 1;
this._translationX = 0;
this._direction = SlideDirection.LEFT; this._direction = SlideDirection.LEFT;
this.parent(params); this.parent(params);
@ -55,18 +56,21 @@ const SlideLayout = new Lang.Class({
vfunc_allocate: function(container, box, flags) { vfunc_allocate: function(container, box, flags) {
let child = container.get_first_child(); let child = container.get_first_child();
let [, , natWidth, natHeight] = child.get_preferred_size();
let availWidth = Math.round(box.x2 - box.x1); let availWidth = Math.round(box.x2 - box.x1);
let availHeight = Math.round(box.y2 - box.y1); let availHeight = Math.round(box.y2 - box.y1);
let [, natWidth] = child.get_preferred_width(availHeight);
// Align the actor inside the clipped box, as the actor's alignment
// flags only determine what to do if the allocated box is bigger
// than the actor's box.
let realDirection = getRtlSlideDirection(this._direction, child); let realDirection = getRtlSlideDirection(this._direction, child);
let translationX = (realDirection == SlideDirection.LEFT) ? let alignX = (realDirection == SlideDirection.LEFT) ? (availWidth - natWidth) : 0;
(availWidth - natWidth) : (natWidth - availWidth);
let actorBox = new Clutter.ActorBox({ x1: translationX, let actorBox = new Clutter.ActorBox();
y1: 0, actorBox.x1 = box.x1 + alignX + this._translationX;
x2: child.x_expand ? availWidth : natWidth, actorBox.x2 = actorBox.x1 + (child.x_expand ? availWidth : natWidth);
y2: child.y_expand ? availHeight : natHeight }); actorBox.y1 = box.y1;
actorBox.y2 = actorBox.y1 + availHeight;
child.allocate(actorBox, flags); child.allocate(actorBox, flags);
}, },
@ -87,7 +91,16 @@ const SlideLayout = new Lang.Class({
get slideDirection() { get slideDirection() {
return this._direction; return this._direction;
} },
set translationX(value) {
this._translationX = value;
this.layout_changed();
},
get translationX() {
return this._translationX;
},
}); });
const SlidingControl = new Lang.Class({ const SlidingControl = new Lang.Class({
@ -96,8 +109,8 @@ const SlidingControl = new Lang.Class({
_init: function(params) { _init: function(params) {
params = Params.parse(params, { slideDirection: SlideDirection.LEFT }); params = Params.parse(params, { slideDirection: SlideDirection.LEFT });
this.visible = true; this._visible = true;
this.inDrag = false; this._inDrag = false;
this.layout = new SlideLayout(); this.layout = new SlideLayout();
this.layout.slideDirection = params.slideDirection; this.layout.slideDirection = params.slideDirection;
@ -106,6 +119,7 @@ const SlidingControl = new Lang.Class({
clip_to_allocation: true }); clip_to_allocation: true });
Main.overview.connect('showing', Lang.bind(this, this._onOverviewShowing)); Main.overview.connect('showing', Lang.bind(this, this._onOverviewShowing));
Main.overview.connect('hiding', Lang.bind(this, this._onOverviewHiding));
Main.overview.connect('item-drag-begin', Lang.bind(this, this._onDragBegin)); 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-end', Lang.bind(this, this._onDragEnd));
@ -116,12 +130,12 @@ const SlidingControl = new Lang.Class({
Main.overview.connect('window-drag-end', Lang.bind(this, this._onWindowDragEnd)); Main.overview.connect('window-drag-end', Lang.bind(this, this._onWindowDragEnd));
}, },
getSlide: function() { _getSlide: function() {
throw new Error('getSlide() must be overridden'); throw new Error('getSlide() must be overridden');
}, },
updateSlide: function() { _updateSlide: function() {
Tweener.addTween(this.layout, { slideX: this.getSlide(), Tweener.addTween(this.layout, { slideX: this._getSlide(),
time: SIDE_CONTROLS_ANIMATION_TIME, time: SIDE_CONTROLS_ANIMATION_TIME,
transition: 'easeOutQuad' }); transition: 'easeOutQuad' });
}, },
@ -148,28 +162,30 @@ const SlidingControl = new Lang.Class({
let translationEnd = 0; let translationEnd = 0;
let translation = this._getTranslation(); let translation = this._getTranslation();
if (this.visible) { if (this._visible) {
translationStart = translation; translationStart = translation;
} else { } else {
translationEnd = translation; translationEnd = translation;
} }
if (this.actor.translation_x == translationEnd) if (this.layout.translationX == translationEnd)
return; return;
this.actor.translation_x = translationStart; this.layout.translationX = translationStart;
Tweener.addTween(this.actor, { translation_x: translationEnd, Tweener.addTween(this.layout, { translationX: translationEnd,
time: SIDE_CONTROLS_ANIMATION_TIME, time: SIDE_CONTROLS_ANIMATION_TIME,
transition: 'easeOutQuad' transition: 'easeOutQuad' });
});
}, },
_onOverviewShowing: function() { _onOverviewShowing: function() {
// reset any translation and make sure the actor is visible when this._visible = true;
// entering the overview this.layout.slideX = this._getSlide();
this.visible = true; this.layout.translationX = this._getTranslation();
this.layout.slideX = this.getSlide(); this.slideIn();
this.actor.translation_x = 0; },
_onOverviewHiding: function() {
this.slideOut();
}, },
_onWindowDragBegin: function() { _onWindowDragBegin: function() {
@ -181,14 +197,14 @@ const SlidingControl = new Lang.Class({
}, },
_onDragBegin: function() { _onDragBegin: function() {
this.inDrag = true; this._inDrag = true;
this.actor.translation_x = 0; this.layout.translationX = 0;
this.updateSlide(); this._updateSlide();
}, },
_onDragEnd: function() { _onDragEnd: function() {
this.inDrag = false; this._inDrag = false;
this.updateSlide(); this._updateSlide();
}, },
fadeIn: function() { fadeIn: function() {
@ -206,13 +222,13 @@ const SlidingControl = new Lang.Class({
}, },
slideIn: function() { slideIn: function() {
this.visible = true; this._visible = true;
this._updateTranslation(); this._updateTranslation();
// we will update slideX and the translation from pageEmpty // we will update slideX and the translation from pageEmpty
}, },
slideOut: function() { slideOut: function() {
this.visible = false; this._visible = false;
this._updateTranslation(); this._updateTranslation();
// we will update slideX from pageEmpty // we will update slideX from pageEmpty
}, },
@ -222,7 +238,7 @@ const SlidingControl = new Lang.Class({
// selector; this means we can now safely set the full slide for // selector; this means we can now safely set the full slide for
// the next page, since slideIn or slideOut might have been called, // the next page, since slideIn or slideOut might have been called,
// changing the visiblity // changing the visiblity
this.layout.slideX = this.getSlide(); this.layout.slideX = this._getSlide();
this._updateTranslation(); this._updateTranslation();
} }
}); });
@ -236,26 +252,20 @@ const ThumbnailsSlider = new Lang.Class({
this._thumbnailsBox = thumbnailsBox; 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.request_mode = Clutter.RequestMode.WIDTH_FOR_HEIGHT;
this.actor.reactive = true; this.actor.reactive = true;
this.actor.track_hover = true; this.actor.track_hover = true;
this.actor.add_actor(this._thumbnailsBox.actor); this.actor.add_actor(this._thumbnailsBox.actor);
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this.updateSlide)); Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._updateSlide));
Main.overview.connect('hiding', Lang.bind(this, this.slideOut)); this.actor.connect('notify::hover', 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); this._thumbnailsBox.actor.bind_property('visible', this.actor, 'visible', GObject.BindingFlags.SYNC_CREATE);
}, },
_getAlwaysZoomOut: function() { _getAlwaysZoomOut: function() {
// Always show the pager when hover, during a drag, or if workspaces are // Always show the pager when hover, during a drag, or if workspaces are
// actually used, e.g. there are windows on more than one // 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; let alwaysZoomOut = this.actor.hover || this._inDrag || !Meta.prefs_get_dynamic_workspaces() || global.screen.n_workspaces > 2;
if (!alwaysZoomOut) { if (!alwaysZoomOut) {
let monitors = Main.layoutManager.monitors; let monitors = Main.layoutManager.monitors;
@ -275,20 +285,13 @@ const ThumbnailsSlider = new Lang.Class({
return alwaysZoomOut; return alwaysZoomOut;
}, },
_onOverviewShowing: function() {
this.visible = true;
this.layout.slideX = this.getSlide();
this.actor.translation_x = this._getTranslation();
this.slideIn();
},
getNonExpandedWidth: function() { getNonExpandedWidth: function() {
let child = this.actor.get_first_child(); let child = this.actor.get_first_child();
return child.get_theme_node().get_length('visible-width'); return child.get_theme_node().get_length('visible-width');
}, },
getSlide: function() { _getSlide: function() {
if (!this.visible) if (!this._visible)
return 0; return 0;
let alwaysZoomOut = this._getAlwaysZoomOut(); let alwaysZoomOut = this._getAlwaysZoomOut();
@ -324,31 +327,23 @@ const DashSlider = new Lang.Class({
// whether to allocate the natural size to its child, or the whole // whether to allocate the natural size to its child, or the whole
// available allocation // available allocation
this._dash.actor.x_expand = true; this._dash.actor.x_expand = true;
this._dash.actor.y_expand = true;
this.actor.x_expand = true;
this.actor.x_align = Clutter.ActorAlign.START; this.actor.x_align = Clutter.ActorAlign.START;
this.actor.y_expand = true; this.actor.y_expand = true;
this.actor.add_actor(this._dash.actor); this.actor.add_actor(this._dash.actor);
this._dash.connect('icon-size-changed', Lang.bind(this, this.updateSlide)); this._dash.connect('icon-size-changed', Lang.bind(this, this._updateSlide));
Main.overview.connect('hiding', Lang.bind(this, this.slideOut));
}, },
getSlide: function() { _getSlide: function() {
if (this.visible || this.inDrag) if (this._visible || this._inDrag)
return 1; return 1;
else else
return 0; return 0;
}, },
_onOverviewShowing: function() {
this.visible = true;
this.layout.slideX = this.getSlide();
this.actor.translation_x = this._getTranslation();
this.slideIn();
},
_onWindowDragBegin: function() { _onWindowDragBegin: function() {
this.fadeHalf(); this.fadeHalf();
}, },
@ -465,9 +460,6 @@ const MessagesIndicator = new Lang.Class({
if (source.trayIcon) if (source.trayIcon)
return; return;
if (source.isTransient)
return;
source.connect('count-updated', Lang.bind(this, this._updateCount)); source.connect('count-updated', Lang.bind(this, this._updateCount));
this._sources.push(source); this._sources.push(source);
this._updateCount(); this._updateCount();

View File

@ -213,9 +213,7 @@ const AppMenuButton = new Lang.Class({
this._label = new TextShadower(); this._label = new TextShadower();
this._label.actor.y_align = Clutter.ActorAlign.CENTER; this._label.actor.y_align = Clutter.ActorAlign.CENTER;
this._hbox.add_actor(this._label.actor); this._hbox.add_actor(this._label.actor);
this._arrow = new St.Label({ text: '\u25BE', this._arrow = PopupMenu.unicodeArrow(St.Side.BOTTOM);
y_expand: true,
y_align: Clutter.ActorAlign.CENTER });
this._hbox.add_actor(this._arrow); this._hbox.add_actor(this._arrow);
this._iconBottomClip = 0; this._iconBottomClip = 0;
@ -604,14 +602,15 @@ const ActivitiesButton = new Lang.Class({
_onCapturedEvent: function(actor, event) { _onCapturedEvent: function(actor, event) {
if (event.type() == Clutter.EventType.BUTTON_PRESS) { if (event.type() == Clutter.EventType.BUTTON_PRESS) {
if (!Main.overview.shouldToggleByCornerOrButton()) if (!Main.overview.shouldToggleByCornerOrButton())
return true; return Clutter.EVENT_STOP;
} }
return false; return Clutter.EVENT_PROPAGATE;
}, },
_onButtonRelease: function() { _onButtonRelease: function() {
Main.overview.toggle(); Main.overview.toggle();
this.menu.close(); this.menu.close();
return Clutter.EVENT_PROPAGATE;
}, },
_onKeyRelease: function(actor, event) { _onKeyRelease: function(actor, event) {
@ -619,6 +618,7 @@ const ActivitiesButton = new Lang.Class({
if (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_space) { if (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_space) {
Main.overview.toggle(); Main.overview.toggle();
} }
return Clutter.EVENT_PROPAGATE;
}, },
_xdndToggleOverview: function(actor) { _xdndToggleOverview: function(actor) {
@ -630,6 +630,7 @@ const ActivitiesButton = new Lang.Class({
Mainloop.source_remove(this._xdndTimeOut); Mainloop.source_remove(this._xdndTimeOut);
this._xdndTimeOut = 0; this._xdndTimeOut = 0;
return GLib.SOURCE_REMOVE;
} }
}); });
@ -824,8 +825,10 @@ const AggregateMenu = new Lang.Class({
this._brightness = new imports.ui.status.brightness.Indicator(); this._brightness = new imports.ui.status.brightness.Indicator();
this._system = new imports.ui.status.system.Indicator(); this._system = new imports.ui.status.system.Indicator();
this._screencast = new imports.ui.status.screencast.Indicator(); this._screencast = new imports.ui.status.screencast.Indicator();
this._location = new imports.ui.status.location.Indicator();
this._indicators.add_child(this._screencast.indicators); this._indicators.add_child(this._screencast.indicators);
this._indicators.add_child(this._location.indicators);
this._indicators.add_child(this._network.indicators); this._indicators.add_child(this._network.indicators);
if (this._bluetooth) { if (this._bluetooth) {
this._indicators.add_child(this._bluetooth.indicators); this._indicators.add_child(this._bluetooth.indicators);
@ -833,9 +836,7 @@ const AggregateMenu = new Lang.Class({
this._indicators.add_child(this._rfkill.indicators); this._indicators.add_child(this._rfkill.indicators);
this._indicators.add_child(this._volume.indicators); this._indicators.add_child(this._volume.indicators);
this._indicators.add_child(this._power.indicators); this._indicators.add_child(this._power.indicators);
this._indicators.add_child(new St.Label({ text: '\u25BE', this._indicators.add_child(PopupMenu.unicodeArrow(St.Side.BOTTOM));
y_expand: true,
y_align: Clutter.ActorAlign.CENTER }));
this.menu.addMenuItem(this._volume.menu); this.menu.addMenuItem(this._volume.menu);
this.menu.addMenuItem(this._brightness.menu); this.menu.addMenuItem(this._brightness.menu);
@ -900,7 +901,7 @@ const Panel = new Lang.Class({
this.actor.remove_style_pseudo_class('overview'); this.actor.remove_style_pseudo_class('overview');
})); }));
Main.layoutManager.panelBox.add(this.actor); Main.layoutManager.panelGroup.add_child(this.actor);
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'emblem-system-symbolic', Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'emblem-system-symbolic',
{ sortGroup: CtrlAltTab.SortGroup.TOP }); { sortGroup: CtrlAltTab.SortGroup.TOP });
@ -987,23 +988,23 @@ const Panel = new Lang.Class({
_onButtonPress: function(actor, event) { _onButtonPress: function(actor, event) {
if (Main.modalCount > 0) if (Main.modalCount > 0)
return false; return Clutter.EVENT_PROPAGATE;
if (event.get_source() != actor) if (event.get_source() != actor)
return false; return Clutter.EVENT_PROPAGATE;
let button = event.get_button(); let button = event.get_button();
if (button != 1) if (button != 1)
return false; return Clutter.EVENT_PROPAGATE;
let focusWindow = global.display.focus_window; let focusWindow = global.display.focus_window;
if (!focusWindow) if (!focusWindow)
return false; return Clutter.EVENT_PROPAGATE;
let dragWindow = focusWindow.is_attached_dialog() ? focusWindow.get_transient_for() let dragWindow = focusWindow.is_attached_dialog() ? focusWindow.get_transient_for()
: focusWindow; : focusWindow;
if (!dragWindow) if (!dragWindow)
return false; return Clutter.EVENT_PROPAGATE;
let rect = dragWindow.get_outer_rect(); let rect = dragWindow.get_outer_rect();
let [stageX, stageY] = event.get_coords(); let [stageX, stageY] = event.get_coords();
@ -1012,7 +1013,7 @@ const Panel = new Lang.Class({
stageX > rect.x && stageX < rect.x + rect.width; stageX > rect.x && stageX < rect.x + rect.width;
if (!allowDrag) if (!allowDrag)
return false; return Clutter.EVENT_PROPAGATE;
global.display.begin_grab_op(global.screen, global.display.begin_grab_op(global.screen,
dragWindow, dragWindow,
@ -1024,7 +1025,7 @@ const Panel = new Lang.Class({
event.get_time(), event.get_time(),
stageX, stageY); stageX, stageY);
return true; return Clutter.EVENT_STOP;
}, },
toggleAppMenu: function() { toggleAppMenu: function() {

View File

@ -130,36 +130,37 @@ const Button = new Lang.Class({
this.menu.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged)); this.menu.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged));
this.menu.actor.connect('key-press-event', Lang.bind(this, this._onMenuKeyPress)); this.menu.actor.connect('key-press-event', Lang.bind(this, this._onMenuKeyPress));
Main.uiGroup.add_actor(this.menu.actor); Main.layoutManager.menuGroup.add_child(this.menu.actor);
this.menu.actor.hide(); this.menu.actor.hide();
} }
}, },
_onButtonPress: function(actor, event) { _onButtonPress: function(actor, event) {
if (!this.menu) if (!this.menu)
return; return Clutter.EVENT_PROPAGATE;
this.menu.toggle(); this.menu.toggle();
return Clutter.EVENT_PROPAGATE;
}, },
_onSourceKeyPress: function(actor, event) { _onSourceKeyPress: function(actor, event) {
if (!this.menu) if (!this.menu)
return false; return Clutter.EVENT_PROPAGATE;
let symbol = event.get_key_symbol(); let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) { if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
this.menu.toggle(); this.menu.toggle();
return true; return Clutter.EVENT_STOP;
} else if (symbol == Clutter.KEY_Escape && this.menu.isOpen) { } else if (symbol == Clutter.KEY_Escape && this.menu.isOpen) {
this.menu.close(); this.menu.close();
return true; return Clutter.EVENT_STOP;
} else if (symbol == Clutter.KEY_Down) { } else if (symbol == Clutter.KEY_Down) {
if (!this.menu.isOpen) if (!this.menu.isOpen)
this.menu.toggle(); this.menu.toggle();
this.menu.actor.navigate_focus(this.actor, Gtk.DirectionType.DOWN, false); this.menu.actor.navigate_focus(this.actor, Gtk.DirectionType.DOWN, false);
return true; return Clutter.EVENT_STOP;
} else } else
return false; return Clutter.EVENT_PROPAGATE;
}, },
_onVisibilityChanged: function() { _onVisibilityChanged: function() {
@ -172,7 +173,7 @@ const Button = new Lang.Class({
_onMenuKeyPress: function(actor, event) { _onMenuKeyPress: function(actor, event) {
if (global.focus_manager.navigate_from_event(event)) if (global.focus_manager.navigate_from_event(event))
return true; return Clutter.EVENT_STOP;
let symbol = event.get_key_symbol(); let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) { if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
@ -180,10 +181,10 @@ const Button = new Lang.Class({
if (group) { if (group) {
let direction = (symbol == Clutter.KEY_Left) ? Gtk.DirectionType.LEFT : Gtk.DirectionType.RIGHT; let direction = (symbol == Clutter.KEY_Left) ? Gtk.DirectionType.LEFT : Gtk.DirectionType.RIGHT;
group.navigate_focus(this.actor, direction, false); group.navigate_focus(this.actor, direction, false);
return true; return Clutter.EVENT_STOP;
} }
} }
return false; return Clutter.EVENT_PROPAGATE;
}, },
_onOpenStateChanged: function(menu, open) { _onOpenStateChanged: function(menu, open) {

View File

@ -1,5 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const GLib = imports.gi.GLib;
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta; const Meta = imports.gi.Meta;
@ -110,7 +111,7 @@ const PointerWatcher = new Lang.Class({
_onTimeout: function() { _onTimeout: function() {
this._updatePointer(); this._updatePointer();
return true; return GLib.SOURCE_CONTINUE;
}, },
_updatePointer: function() { _updatePointer: function() {

View File

@ -42,6 +42,33 @@ function isPopupMenuItemVisible(child) {
return child.visible; return child.visible;
} }
/**
* @side Side to which the arrow points.
*/
function unicodeArrow(side) {
let arrowChar;
switch (side) {
case St.Side.TOP:
arrowChar = '\u25B4';
break;
case St.Side.RIGHT:
arrowChar = '\u25B8';
break;
case St.Side.BOTTOM:
arrowChar = '\u25BE';
break;
case St.Side.LEFT:
arrowChar = '\u25C2';
break;
}
return new St.Label({ text: arrowChar,
style_class: 'unicode-arrow',
accessible_role: Atk.Role.ARROW,
y_expand: true,
y_align: Clutter.ActorAlign.CENTER });
}
const PopupBaseMenuItem = new Lang.Class({ const PopupBaseMenuItem = new Lang.Class({
Name: 'PopupBaseMenuItem', Name: 'PopupBaseMenuItem',
@ -99,7 +126,7 @@ const PopupBaseMenuItem = new Lang.Class({
_onButtonReleaseEvent: function (actor, event) { _onButtonReleaseEvent: function (actor, event) {
this.activate(event); this.activate(event);
return true; return Clutter.EVENT_STOP;
}, },
_onKeyPressEvent: function (actor, event) { _onKeyPressEvent: function (actor, event) {
@ -107,9 +134,9 @@ const PopupBaseMenuItem = new Lang.Class({
if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) { if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
this.activate(event); this.activate(event);
return true; return Clutter.EVENT_STOP;
} }
return false; return Clutter.EVENT_PROPAGATE;
}, },
_onKeyFocusIn: function (actor) { _onKeyFocusIn: function (actor) {
@ -901,10 +928,10 @@ const PopupSubMenu = new Lang.Class({
if (this.isOpen && event.get_key_symbol() == Clutter.KEY_Left) { if (this.isOpen && event.get_key_symbol() == Clutter.KEY_Left) {
this.close(BoxPointer.PopupAnimation.FULL); this.close(BoxPointer.PopupAnimation.FULL);
this.sourceActor._delegate.setActive(true); this.sourceActor._delegate.setActive(true);
return true; return Clutter.EVENT_STOP;
} }
return false; return Clutter.EVENT_PROPAGATE;
} }
}); });
@ -962,8 +989,7 @@ const PopupSubMenuMenuItem = new Lang.Class({
y_align: Clutter.ActorAlign.CENTER }); y_align: Clutter.ActorAlign.CENTER });
this.actor.add_child(this.status); this.actor.add_child(this.status);
this._triangle = new St.Label({ text: '\u25B8', this._triangle = unicodeArrow(St.Side.RIGHT);
style_class: 'popup-submenu-menu-item-triangle' });
this._triangle.pivot_point = new Clutter.Point({ x: 0.5, y: 0.6 }); this._triangle.pivot_point = new Clutter.Point({ x: 0.5, y: 0.6 });
this._triangleBin = new St.Widget({ y_expand: true, this._triangleBin = new St.Widget({ y_expand: true,
@ -1030,10 +1056,10 @@ const PopupSubMenuMenuItem = new Lang.Class({
if (symbol == Clutter.KEY_Right) { if (symbol == Clutter.KEY_Right) {
this._setOpenState(true); this._setOpenState(true);
this.menu.actor.navigate_focus(null, Gtk.DirectionType.DOWN, false); this.menu.actor.navigate_focus(null, Gtk.DirectionType.DOWN, false);
return true; return Clutter.EVENT_STOP;
} else if (symbol == Clutter.KEY_Left && this._getOpenState()) { } else if (symbol == Clutter.KEY_Left && this._getOpenState()) {
this._setOpenState(false); this._setOpenState(false);
return true; return Clutter.EVENT_STOP;
} }
return this.parent(actor, event); return this.parent(actor, event);
@ -1045,6 +1071,7 @@ const PopupSubMenuMenuItem = new Lang.Class({
_onButtonReleaseEvent: function(actor) { _onButtonReleaseEvent: function(actor) {
this._setOpenState(!this._getOpenState()); this._setOpenState(!this._getOpenState());
return Clutter.EVENT_PROPAGATE;
} }
}); });
@ -1076,7 +1103,7 @@ const PopupMenuManager = new Lang.Class({
if (source) { if (source) {
if (!menu.blockSourceEvents) if (!menu.blockSourceEvents)
this._grabHelper.addActor(source); this._grabHelper.addActor(source);
menudata.enterId = source.connect('enter-event', Lang.bind(this, function() { this._onMenuSourceEnter(menu); })); menudata.enterId = source.connect('enter-event', Lang.bind(this, function() { return this._onMenuSourceEnter(menu); }));
menudata.focusInId = source.connect('key-focus-in', Lang.bind(this, function() { this._onMenuSourceEnter(menu); })); menudata.focusInId = source.connect('key-focus-in', Lang.bind(this, function() { this._onMenuSourceEnter(menu); }));
} }
@ -1088,7 +1115,7 @@ const PopupMenuManager = new Lang.Class({
removeMenu: function(menu) { removeMenu: function(menu) {
if (menu == this.activeMenu) if (menu == this.activeMenu)
this._closeMenu(menu); this._closeMenu(false, menu);
let position = this._findMenu(menu); let position = this._findMenu(menu);
if (position == -1) // not a menu we manage if (position == -1) // not a menu we manage
@ -1138,13 +1165,13 @@ const PopupMenuManager = new Lang.Class({
_onMenuSourceEnter: function(menu) { _onMenuSourceEnter: function(menu) {
if (!this._grabHelper.grabbed) if (!this._grabHelper.grabbed)
return false; return Clutter.EVENT_PROPAGATE;
if (this._grabHelper.isActorGrabbed(menu.actor)) if (this._grabHelper.isActorGrabbed(menu.actor))
return false; return Clutter.EVENT_PROPAGATE;
this._changeMenu(menu); this._changeMenu(menu);
return false; return Clutter.EVENT_PROPAGATE;
}, },
_onMenuDestroy: function(menu) { _onMenuDestroy: function(menu) {

View File

@ -7,58 +7,63 @@ const Lang = imports.lang;
const St = imports.gi.St; const St = imports.gi.St;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const FileUtils = imports.misc.fileUtils;
const Search = imports.ui.search; const Search = imports.ui.search;
const KEY_FILE_GROUP = 'Shell Search Provider'; const KEY_FILE_GROUP = 'Shell Search Provider';
const SearchProviderIface = <interface name="org.gnome.Shell.SearchProvider"> const SearchProviderIface = '<node> \
<method name="GetInitialResultSet"> <interface name="org.gnome.Shell.SearchProvider"> \
<arg type="as" direction="in" /> <method name="GetInitialResultSet"> \
<arg type="as" direction="out" /> <arg type="as" direction="in" /> \
</method> <arg type="as" direction="out" /> \
<method name="GetSubsearchResultSet"> </method> \
<arg type="as" direction="in" /> <method name="GetSubsearchResultSet"> \
<arg type="as" direction="in" /> <arg type="as" direction="in" /> \
<arg type="as" direction="out" /> <arg type="as" direction="in" /> \
</method> <arg type="as" direction="out" /> \
<method name="GetResultMetas"> </method> \
<arg type="as" direction="in" /> <method name="GetResultMetas"> \
<arg type="aa{sv}" direction="out" /> <arg type="as" direction="in" /> \
</method> <arg type="aa{sv}" direction="out" /> \
<method name="ActivateResult"> </method> \
<arg type="s" direction="in" /> <method name="ActivateResult"> \
</method> <arg type="s" direction="in" /> \
</interface>; </method> \
</interface> \
</node>';
const SearchProvider2Iface = <interface name="org.gnome.Shell.SearchProvider2"> const SearchProvider2Iface = '<node> \
<method name="GetInitialResultSet"> <interface name="org.gnome.Shell.SearchProvider2"> \
<arg type="as" direction="in" /> <method name="GetInitialResultSet"> \
<arg type="as" direction="out" /> <arg type="as" direction="in" /> \
</method> <arg type="as" direction="out" /> \
<method name="GetSubsearchResultSet"> </method> \
<arg type="as" direction="in" /> <method name="GetSubsearchResultSet"> \
<arg type="as" direction="in" /> <arg type="as" direction="in" /> \
<arg type="as" direction="out" /> <arg type="as" direction="in" /> \
</method> <arg type="as" direction="out" /> \
<method name="GetResultMetas"> </method> \
<arg type="as" direction="in" /> <method name="GetResultMetas"> \
<arg type="aa{sv}" direction="out" /> <arg type="as" direction="in" /> \
</method> <arg type="aa{sv}" direction="out" /> \
<method name="ActivateResult"> </method> \
<arg type="s" direction="in" /> <method name="ActivateResult"> \
<arg type="as" direction="in" /> <arg type="s" direction="in" /> \
<arg type="u" direction="in" /> <arg type="as" direction="in" /> \
</method> <arg type="u" direction="in" /> \
<method name="LaunchSearch"> </method> \
<arg type="as" direction="in" /> <method name="LaunchSearch"> \
<arg type="u" direction="in" /> <arg type="as" direction="in" /> \
</method> <arg type="u" direction="in" /> \
</interface>; </method> \
</interface> \
</node>';
var SearchProviderProxy = Gio.DBusProxy.makeProxyWrapper(SearchProviderIface); var SearchProviderProxyInfo = Gio.DBusInterfaceInfo.new_for_xml(SearchProviderIface);
var SearchProvider2Proxy = Gio.DBusProxy.makeProxyWrapper(SearchProvider2Iface); var SearchProvider2ProxyInfo = Gio.DBusInterfaceInfo.new_for_xml(SearchProvider2Iface);
function loadRemoteSearchProviders(addProviderCallback) { function loadRemoteSearchProviders(callback) {
let objectPaths = {}; let objectPaths = {};
let loadedProviders = []; let loadedProviders = [];
@ -112,30 +117,25 @@ function loadRemoteSearchProviders(addProviderCallback) {
} }
} }
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 searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
if (searchSettings.get_boolean('disable-external')) {
callback([]);
return;
}
FileUtils.collectFromDatadirs('search-providers', false, loadRemoteSearchProvider);
let sortOrder = searchSettings.get_strv('sort-order'); let sortOrder = searchSettings.get_strv('sort-order');
// Special case gnome-control-center to be always active and always first // Special case gnome-control-center to be always active and always first
sortOrder.unshift('gnome-control-center.desktop'); sortOrder.unshift('gnome-control-center.desktop');
loadedProviders = loadedProviders.filter(function(provider) {
let appId = provider.appInfo.get_id();
let disabled = searchSettings.get_strv('disabled');
return disabled.indexOf(appId) == -1;
});
loadedProviders.sort(function(providerA, providerB) { loadedProviders.sort(function(providerA, providerB) {
let idxA, idxB; let idxA, idxB;
let appIdA, appIdB; let appIdA, appIdB;
@ -166,32 +166,34 @@ function loadRemoteSearchProviders(addProviderCallback) {
return (idxA - idxB); return (idxA - idxB);
}); });
loadedProviders.forEach(addProviderCallback); callback(loadedProviders);
} }
const RemoteSearchProvider = new Lang.Class({ const RemoteSearchProvider = new Lang.Class({
Name: 'RemoteSearchProvider', Name: 'RemoteSearchProvider',
_init: function(appInfo, dbusName, dbusPath, proxyType) { _init: function(appInfo, dbusName, dbusPath, proxyInfo) {
if (!proxyType) if (!proxyInfo)
proxyType = SearchProviderProxy; proxyInfo = SearchProviderProxyInfo;
this.proxy = new proxyType(Gio.DBus.session, this.proxy = new Gio.DBusProxy({ g_bus_type: Gio.BusType.SESSION,
dbusName, dbusPath, Lang.bind(this, this._onProxyConstructed)); g_name: dbusName,
g_object_path: dbusPath,
g_interface_info: proxyInfo,
g_interface_name: proxyInfo.name,
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START_AT_CONSTRUCTION |
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
this.proxy.init_async(GLib.PRIORITY_DEFAULT, null, null);
this.appInfo = appInfo; this.appInfo = appInfo;
this.id = appInfo.get_id(); this.id = appInfo.get_id();
this.isRemoteProvider = true; this.isRemoteProvider = true;
this._cancellable = new Gio.Cancellable();
},
_onProxyConstructed: function(proxy) {
// Do nothing
}, },
createIcon: function(size, meta) { createIcon: function(size, meta) {
let gicon; let gicon = null;
let icon = null;
if (meta['icon']) { if (meta['icon']) {
gicon = Gio.icon_deserialize(meta['icon']); gicon = Gio.icon_deserialize(meta['icon']);
} else if (meta['gicon']) { } else if (meta['gicon']) {
@ -203,44 +205,49 @@ const RemoteSearchProvider = new Lang.Class({
bitsPerSample, width, height, rowStride); bitsPerSample, width, height, rowStride);
} }
return new St.Icon({ gicon: gicon, if (gicon)
icon_size: size }); icon = new St.Icon({ gicon: gicon,
icon_size: size });
return icon;
}, },
_getResultsFinished: function(results, error) { filterResults: function(results, maxNumber) {
if (error) if (results.length <= maxNumber)
return results;
let regularResults = results.filter(function(r) { return !r.startsWith('special:'); });
let specialResults = results.filter(function(r) { return r.startsWith('special:'); });
return regularResults.slice(0, maxNumber).concat(specialResults.slice(0, maxNumber));
},
_getResultsFinished: function(results, error, callback) {
if (error) {
if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
log('Received error from DBus search provider %s: %s'.format(this.id, String(error)));
callback([]);
return; return;
this.searchSystem.setResults(this, results[0]); }
callback(results[0]);
}, },
getInitialResultSet: function(terms) { getInitialResultSet: function(terms, callback, cancellable) {
this._cancellable.cancel(); this.proxy.GetInitialResultSetRemote(terms,
this._cancellable.reset(); Lang.bind(this, this._getResultsFinished, callback),
try { cancellable);
this.proxy.GetInitialResultSetRemote(terms,
Lang.bind(this, this._getResultsFinished),
this._cancellable);
} catch(e) {
log('Error calling GetInitialResultSet for provider %s: %s'.format(this.id, e.toString()));
this.searchSystem.setResults(this, []);
}
}, },
getSubsearchResultSet: function(previousResults, newTerms) { getSubsearchResultSet: function(previousResults, newTerms, callback, cancellable) {
this._cancellable.cancel(); this.proxy.GetSubsearchResultSetRemote(previousResults, newTerms,
this._cancellable.reset(); Lang.bind(this, this._getResultsFinished, callback),
try { cancellable);
this.proxy.GetSubsearchResultSetRemote(previousResults, newTerms,
Lang.bind(this, this._getResultsFinished),
this._cancellable);
} catch(e) {
log('Error calling GetSubsearchResultSet for provider %s: %s'.format(this.id, e.toString()));
this.searchSystem.setResults(this, []);
}
}, },
_getResultMetasFinished: function(results, error, callback) { _getResultMetasFinished: function(results, error, callback) {
if (error) { if (error) {
if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
log('Received error from DBus search provider %s during GetResultMetas: %s'.format(this.id, String(error)));
callback([]); callback([]);
return; return;
} }
@ -262,17 +269,10 @@ const RemoteSearchProvider = new Lang.Class({
callback(resultMetas); callback(resultMetas);
}, },
getResultMetas: function(ids, callback) { getResultMetas: function(ids, callback, cancellable) {
this._cancellable.cancel(); this.proxy.GetResultMetasRemote(ids,
this._cancellable.reset(); Lang.bind(this, this._getResultMetasFinished, callback),
try { cancellable);
this.proxy.GetResultMetasRemote(ids,
Lang.bind(this, this._getResultMetasFinished, callback),
this._cancellable);
} catch(e) {
log('Error calling GetResultMetas for provider %s: %s'.format(this.id, e.toString()));
callback([]);
}
}, },
activateResult: function(id) { activateResult: function(id) {
@ -283,7 +283,7 @@ const RemoteSearchProvider = new Lang.Class({
// the provider is not compatible with the new version of the interface, launch // the provider is not compatible with the new version of the interface, launch
// the app itself but warn so we can catch the error in logs // the app itself but warn so we can catch the error in logs
log('Search provider ' + this.appInfo.get_id() + ' does not implement LaunchSearch'); log('Search provider ' + this.appInfo.get_id() + ' does not implement LaunchSearch');
this.appInfo.launch([], global.create_app_launch_context()); this.appInfo.launch([], global.create_app_launch_context(0, -1));
} }
}); });
@ -292,7 +292,7 @@ const RemoteSearchProvider2 = new Lang.Class({
Extends: RemoteSearchProvider, Extends: RemoteSearchProvider,
_init: function(appInfo, dbusName, dbusPath) { _init: function(appInfo, dbusName, dbusPath) {
this.parent(appInfo, dbusName, dbusPath, SearchProvider2Proxy); this.parent(appInfo, dbusName, dbusPath, SearchProvider2ProxyInfo);
this.canLaunchSearch = true; this.canLaunchSearch = true;
}, },

View File

@ -73,7 +73,9 @@ const RunDialog = new Lang.Class({
let label = new St.Label({ style_class: 'run-dialog-label', let label = new St.Label({ style_class: 'run-dialog-label',
text: _("Enter a Command") }); text: _("Enter a Command") });
this.contentLayout.add(label, { y_align: St.Align.START }); this.contentLayout.add(label, { x_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
let entry = new St.Entry({ style_class: 'run-dialog-entry', let entry = new St.Entry({ style_class: 'run-dialog-entry',
can_focus: true }); can_focus: true });
@ -101,6 +103,8 @@ const RunDialog = new Lang.Class({
this._errorMessage.clutter_text.line_wrap = true; this._errorMessage.clutter_text.line_wrap = true;
this._errorBox.add(this._errorMessage, { expand: true, this._errorBox.add(this._errorMessage, { expand: true,
x_align: St.Align.START,
x_fill: false,
y_align: St.Align.MIDDLE, y_align: St.Align.MIDDLE,
y_fill: false }); y_fill: false });
@ -124,7 +128,7 @@ const RunDialog = new Lang.Class({
!this.pushModal()) !this.pushModal())
this.close(); this.close();
return true; return Clutter.EVENT_STOP;
} }
if (symbol == Clutter.Tab) { if (symbol == Clutter.Tab) {
let text = o.get_text(); let text = o.get_text();
@ -138,9 +142,9 @@ const RunDialog = new Lang.Class({
o.insert_text(postfix, -1); o.insert_text(postfix, -1);
o.set_cursor_position(text.length + postfix.length); o.set_cursor_position(text.length + postfix.length);
} }
return true; return Clutter.EVENT_STOP;
} }
return false; return Clutter.EVENT_PROPAGATE;
})); }));
}, },
@ -229,7 +233,7 @@ const RunDialog = new Lang.Class({
let file = Gio.file_new_for_path(path); let file = Gio.file_new_for_path(path);
try { try {
Gio.app_info_launch_default_for_uri(file.get_uri(), Gio.app_info_launch_default_for_uri(file.get_uri(),
global.create_app_launch_context()); global.create_app_launch_context(0, -1));
} catch (e) { } catch (e) {
// The exception from gjs contains an error string like: // The exception from gjs contains an error string like:
// Error invoking Gio.app_info_launch_default_for_uri: No application // Error invoking Gio.app_info_launch_default_for_uri: No application

View File

@ -17,8 +17,8 @@ const TweenerEquations = imports.tweener.equations;
const Background = imports.ui.background; const Background = imports.ui.background;
const GnomeSession = imports.misc.gnomeSession; const GnomeSession = imports.misc.gnomeSession;
const Hash = imports.misc.hash;
const Layout = imports.ui.layout; const Layout = imports.ui.layout;
const OVirt = imports.gdm.oVirt;
const LoginManager = imports.misc.loginManager; const LoginManager = imports.misc.loginManager;
const Lightbox = imports.ui.lightbox; const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main; const Main = imports.ui.main;
@ -114,7 +114,7 @@ const NotificationsBox = new Lang.Class({
this.actor.add(this._musicBin); this.actor.add(this._musicBin);
this.actor.add(this._scrollView, { x_fill: true, x_align: St.Align.START }); this.actor.add(this._scrollView, { x_fill: true, x_align: St.Align.START });
this._sources = new Hash.Map(); this._sources = new Map();
Main.messageTray.getSources().forEach(Lang.bind(this, function(source) { Main.messageTray.getSources().forEach(Lang.bind(this, function(source) {
this._sourceAdded(Main.messageTray, source, true); this._sourceAdded(Main.messageTray, source, true);
})); }));
@ -129,9 +129,8 @@ const NotificationsBox = new Lang.Class({
this._sourceAddedId = 0; this._sourceAddedId = 0;
} }
let items = this._sources.items(); let items = this._sources.entries();
for (let i = 0; i < items.length; i++) { for (let [source, obj] of items) {
let [source, obj] = items[i];
this._removeSource(source, obj); this._removeSource(source, obj);
} }
@ -197,8 +196,8 @@ const NotificationsBox = new Lang.Class({
let body = ''; let body = '';
if (n.bannerBodyText) { if (n.bannerBodyText) {
body = n.bannerBodyMarkup ? n.bannerBodyText : body = n.bannerBodyMarkup ? n.bannerBodyText
GLib.markup_escape_text(n.bannerBodyMarkup, -1); : GLib.markup_escape_text(n.bannerBodyText, -1);
} }
let label = new St.Label({ style_class: 'screen-shield-notification-count-text' }); let label = new St.Label({ style_class: 'screen-shield-notification-count-text' });
@ -239,10 +238,6 @@ const NotificationsBox = new Lang.Class({
}, },
_sourceAdded: function(tray, source, initial) { _sourceAdded: function(tray, source, initial) {
// Ignore transient sources
if (source.isTransient)
return;
let obj = { let obj = {
visible: source.policy.showInLockScreen, visible: source.policy.showInLockScreen,
detailed: source.policy.detailsInLockScreen, detailed: source.policy.detailsInLockScreen,
@ -418,6 +413,7 @@ const Arrow = new Lang.Class({
cr.lineTo(w/2, thickness); cr.lineTo(w/2, thickness);
cr.lineTo(w - thickness / 2, h - thickness / 2); cr.lineTo(w - thickness / 2, h - thickness / 2);
cr.stroke(); cr.stroke();
cr.$dispose();
}, },
vfunc_style_changed: function() { vfunc_style_changed: function() {
@ -460,8 +456,6 @@ const ScreenShield = new Lang.Class({
Name: 'ScreenShield', Name: 'ScreenShield',
_init: function() { _init: function() {
this.actor = Main.layoutManager.screenShieldGroup;
this._lockScreenState = MessageTray.State.HIDDEN; this._lockScreenState = MessageTray.State.HIDDEN;
this._lockScreenGroup = new St.Widget({ x_expand: true, this._lockScreenGroup = new St.Widget({ x_expand: true,
y_expand: true, y_expand: true,
@ -470,6 +464,7 @@ const ScreenShield = new Lang.Class({
name: 'lockScreenGroup', name: 'lockScreenGroup',
visible: false, visible: false,
}); });
this._lockScreenGroup.connect('key-press-event', this._lockScreenGroup.connect('key-press-event',
Lang.bind(this, this._onLockScreenKeyPress)); Lang.bind(this, this._onLockScreenKeyPress));
this._lockScreenGroup.connect('scroll-event', this._lockScreenGroup.connect('scroll-event',
@ -520,10 +515,9 @@ const ScreenShield = new Lang.Class({
reactive: true, reactive: true,
pivot_point: new Clutter.Point({ x: 0.5, y: 0.5 }), pivot_point: new Clutter.Point({ x: 0.5, y: 0.5 }),
name: 'lockDialogGroup' }); name: 'lockDialogGroup' });
Main.layoutManager.systemGroup.add_child(this._lockDialogGroup);
this.actor.add_actor(this._lockDialogGroup); Main.layoutManager.screenShieldGroup.add_child(this._lockScreenGroup);
this.actor.add_actor(this._lockScreenGroup);
this._presence = new GnomeSession.Presence(Lang.bind(this, function(proxy, error) { this._presence = new GnomeSession.Presence(Lang.bind(this, function(proxy, error) {
if (error) { if (error) {
logError(error, 'Error while reading gnome-session presence'); logError(error, 'Error while reading gnome-session presence');
@ -545,6 +539,13 @@ const ScreenShield = new Lang.Class({
this._liftShield(true, 0); this._liftShield(true, 0);
})); }));
this._oVirtCredentialsManager = OVirt.getOVirtCredentialsManager();
this._oVirtCredentialsManager.connect('user-authenticated',
Lang.bind(this, function() {
if (this._isLocked)
this._liftShield(true, 0);
}));
this._inhibitor = null; this._inhibitor = null;
this._aboutToSuspend = false; this._aboutToSuspend = false;
this._loginManager = LoginManager.getLoginManager(); this._loginManager = LoginManager.getLoginManager();
@ -574,19 +575,27 @@ const ScreenShield = new Lang.Class({
// The "long" lightbox is used for the longer (20 seconds) fade from session // The "long" lightbox is used for the longer (20 seconds) fade from session
// to idle status, the "short" is used for quickly fading to black when locking // to idle status, the "short" is used for quickly fading to black when locking
// manually // manually
this._longLightbox = new Lightbox.Lightbox(Main.uiGroup, this._longLightbox = new Lightbox.Lightbox(Main.layoutManager.overlayGroup,
{ inhibitEvents: true, { inhibitEvents: true,
fadeFactor: 1 }); fadeFactor: 1 });
this._longLightbox.connect('shown', Lang.bind(this, this._onLongLightboxShown)); this._longLightbox.connect('shown', Lang.bind(this, this._onLongLightboxShown));
this._shortLightbox = new Lightbox.Lightbox(Main.uiGroup, this._shortLightbox = new Lightbox.Lightbox(Main.layoutManager.overlayGroup,
{ inhibitEvents: true, { inhibitEvents: true,
fadeFactor: 1 }); fadeFactor: 1 });
this._shortLightbox.connect('shown', Lang.bind(this, this._onShortLightboxShown)); this._shortLightbox.connect('shown', Lang.bind(this, this._onShortLightboxShown));
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._monitorsChanged));
this._monitorsChanged();
this.idleMonitor = Meta.IdleMonitor.get_core(); this.idleMonitor = Meta.IdleMonitor.get_core();
this._cursorTracker = Meta.CursorTracker.get_for_screen(global.screen); this._cursorTracker = Meta.CursorTracker.get_for_screen(global.screen);
}, },
_monitorsChanged: function() {
this._longLightbox.actor.set_size(global.screen_width, global.screen_height);
this._shortLightbox.actor.set_size(global.screen_width, global.screen_height);
},
_createBackground: function(monitorIndex) { _createBackground: function(monitorIndex) {
let monitor = Main.layoutManager.monitors[monitorIndex]; let monitor = Main.layoutManager.monitors[monitorIndex];
let widget = new St.Widget({ style_class: 'screen-shield-background', let widget = new St.Widget({ style_class: 'screen-shield-background',
@ -644,14 +653,14 @@ const ScreenShield = new Lang.Class({
if (this._isModal) if (this._isModal)
return true; return true;
this._isModal = Main.pushModal(this.actor, { keybindingMode: Shell.KeyBindingMode.LOCK_SCREEN }); this._isModal = Main.pushModal(this._lockDialogGroup, { keybindingMode: Shell.KeyBindingMode.LOCK_SCREEN });
if (this._isModal) if (this._isModal)
return true; return true;
// We failed to get a pointer grab, it means that // We failed to get a pointer grab, it means that
// something else has it. Try with a keyboard grab only // something else has it. Try with a keyboard grab only
this._isModal = Main.pushModal(this.actor, { options: Meta.ModalOptions.POINTER_ALREADY_GRABBED, this._isModal = Main.pushModal(this._lockDialogGroup, { options: Meta.ModalOptions.POINTER_ALREADY_GRABBED,
keybindingMode: Shell.KeyBindingMode.LOCK_SCREEN }); keybindingMode: Shell.KeyBindingMode.LOCK_SCREEN });
return this._isModal; return this._isModal;
}, },
@ -665,11 +674,11 @@ const ScreenShield = new Lang.Class({
// down after cancel. // down after cancel.
if (this._lockScreenState != MessageTray.State.SHOWN) if (this._lockScreenState != MessageTray.State.SHOWN)
return false; return Clutter.EVENT_PROPAGATE;
let isEnter = (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_KP_Enter); let isEnter = (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_KP_Enter);
if (!isEnter && !(GLib.unichar_isprint(unichar) || symbol == Clutter.KEY_Escape)) if (!isEnter && !(GLib.unichar_isprint(unichar) || symbol == Clutter.KEY_Escape))
return false; return Clutter.EVENT_PROPAGATE;
if (this._isLocked && if (this._isLocked &&
this._ensureUnlockDialog(true, true) && this._ensureUnlockDialog(true, true) &&
@ -677,12 +686,12 @@ const ScreenShield = new Lang.Class({
this._dialog.addCharacter(unichar); this._dialog.addCharacter(unichar);
this._liftShield(true, 0); this._liftShield(true, 0);
return true; return Clutter.EVENT_STOP;
}, },
_onLockScreenScroll: function(actor, event) { _onLockScreenScroll: function(actor, event) {
if (this._lockScreenState != MessageTray.State.SHOWN) if (this._lockScreenState != MessageTray.State.SHOWN)
return false; return Clutter.EVENT_PROPAGATE;
let delta = 0; let delta = 0;
if (event.get_scroll_direction() == Clutter.ScrollDirection.UP) if (event.get_scroll_direction() == Clutter.ScrollDirection.UP)
@ -697,7 +706,7 @@ const ScreenShield = new Lang.Class({
this._liftShield(true, 0); this._liftShield(true, 0);
} }
return true; return Clutter.EVENT_STOP;
}, },
_inhibitSuspend: function() { _inhibitSuspend: function() {
@ -748,7 +757,7 @@ const ScreenShield = new Lang.Class({
}); });
} }
return true; return GLib.SOURCE_CONTINUE;
}, },
_onDragBegin: function() { _onDragBegin: function() {
@ -844,7 +853,7 @@ const ScreenShield = new Lang.Class({
Lang.bind(this, function() { Lang.bind(this, function() {
this._lockTimeoutId = 0; this._lockTimeoutId = 0;
this.lock(false); this.lock(false);
return false; return GLib.SOURCE_REMOVE;
})); }));
} }
@ -909,7 +918,7 @@ const ScreenShield = new Lang.Class({
return false; return false;
})); }));
this.actor.show(); this._lockDialogGroup.show();
this._isGreeter = Main.sessionMode.isGreeter; this._isGreeter = Main.sessionMode.isGreeter;
this._isLocked = true; this._isLocked = true;
if (this._ensureUnlockDialog(true, true)) if (this._ensureUnlockDialog(true, true))
@ -999,6 +1008,7 @@ const ScreenShield = new Lang.Class({
return; return;
this._ensureLockScreen(); this._ensureLockScreen();
this._lockDialogGroup.hide();
this._lockDialogGroup.scale_x = 1; this._lockDialogGroup.scale_x = 1;
this._lockDialogGroup.scale_y = 1; this._lockDialogGroup.scale_y = 1;
@ -1093,12 +1103,14 @@ const ScreenShield = new Lang.Class({
global.stage.disconnect(motionId); global.stage.disconnect(motionId);
} }
return false; return Clutter.EVENT_PROPAGATE;
})); }));
this._cursorTracker.set_pointer_visible(false); this._cursorTracker.set_pointer_visible(false);
this._lockScreenState = MessageTray.State.SHOWN; this._lockDialogGroup.show();
this._lockScreenGroup.fixed_position_set = false; this._lockScreenGroup.fixed_position_set = false;
this._lockScreenState = MessageTray.State.SHOWN;
Main.layoutManager.sessionGroup.hide();
this._lockScreenScrollCounter = 0; this._lockScreenScrollCounter = 0;
if (params.fadeToBlack && params.animateFade) { if (params.fadeToBlack && params.animateFade) {
@ -1106,6 +1118,7 @@ const ScreenShield = new Lang.Class({
Mainloop.timeout_add(1000 * MANUAL_FADE_TIME, Lang.bind(this, function() { Mainloop.timeout_add(1000 * MANUAL_FADE_TIME, Lang.bind(this, function() {
this._activateFade(this._shortLightbox, MANUAL_FADE_TIME); this._activateFade(this._shortLightbox, MANUAL_FADE_TIME);
return GLib.SOURCE_REMOVE;
})); }));
} else { } else {
if (params.fadeToBlack) if (params.fadeToBlack)
@ -1220,10 +1233,11 @@ const ScreenShield = new Lang.Class({
this._dialog.popModal(); this._dialog.popModal();
if (this._isModal) { if (this._isModal) {
Main.popModal(this.actor); Main.popModal(this._lockDialogGroup);
this._isModal = false; this._isModal = false;
} }
Main.layoutManager.sessionGroup.show();
Tweener.addTween(this._lockDialogGroup, { Tweener.addTween(this._lockDialogGroup, {
scale_x: 0, scale_x: 0,
scale_y: 0, scale_y: 0,
@ -1242,7 +1256,7 @@ const ScreenShield = new Lang.Class({
this._longLightbox.hide(); this._longLightbox.hide();
this._shortLightbox.hide(); this._shortLightbox.hide();
this.actor.hide(); this._lockScreenGroup.hide();
if (this._becameActiveId != 0) { if (this._becameActiveId != 0) {
this.idleMonitor.remove_watch(this._becameActiveId); this.idleMonitor.remove_watch(this._becameActiveId);
@ -1266,7 +1280,7 @@ const ScreenShield = new Lang.Class({
if (this._activationTime == 0) if (this._activationTime == 0)
this._activationTime = GLib.get_monotonic_time(); this._activationTime = GLib.get_monotonic_time();
this.actor.show(); this._lockScreenGroup.show();
if (Main.sessionMode.currentMode != 'unlock-dialog' && if (Main.sessionMode.currentMode != 'unlock-dialog' &&
Main.sessionMode.currentMode != 'lock-screen') { Main.sessionMode.currentMode != 'lock-screen') {

View File

@ -6,30 +6,31 @@ const Lang = imports.lang;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Signals = imports.signals; const Signals = imports.signals;
const Hash = imports.misc.hash;
const Main = imports.ui.main; const Main = imports.ui.main;
const ScreencastIface = <interface name="org.gnome.Shell.Screencast"> const ScreencastIface = '<node> \
<method name="Screencast"> <interface name="org.gnome.Shell.Screencast"> \
<arg type="s" direction="in" name="file_template"/> <method name="Screencast"> \
<arg type="a{sv}" direction="in" name="options"/> <arg type="s" direction="in" name="file_template"/> \
<arg type="b" direction="out" name="success"/> <arg type="a{sv}" direction="in" name="options"/> \
<arg type="s" direction="out" name="filename_used"/> <arg type="b" direction="out" name="success"/> \
</method> <arg type="s" direction="out" name="filename_used"/> \
<method name="ScreencastArea"> </method> \
<arg type="i" direction="in" name="x"/> <method name="ScreencastArea"> \
<arg type="i" direction="in" name="y"/> <arg type="i" direction="in" name="x"/> \
<arg type="i" direction="in" name="width"/> <arg type="i" direction="in" name="y"/> \
<arg type="i" direction="in" name="height"/> <arg type="i" direction="in" name="width"/> \
<arg type="s" direction="in" name="file_template"/> <arg type="i" direction="in" name="height"/> \
<arg type="a{sv}" direction="in" name="options"/> <arg type="s" direction="in" name="file_template"/> \
<arg type="b" direction="out" name="success"/> <arg type="a{sv}" direction="in" name="options"/> \
<arg type="s" direction="out" name="filename_used"/> <arg type="b" direction="out" name="success"/> \
</method> <arg type="s" direction="out" name="filename_used"/> \
<method name="StopScreencast"> </method> \
<arg type="b" direction="out" name="success"/> <method name="StopScreencast"> \
</method> <arg type="b" direction="out" name="success"/> \
</interface>; </method> \
</interface> \
</node>';
const ScreencastService = new Lang.Class({ const ScreencastService = new Lang.Class({
Name: 'ScreencastService', Name: 'ScreencastService',
@ -40,13 +41,13 @@ const ScreencastService = new Lang.Class({
Gio.DBus.session.own_name('org.gnome.Shell.Screencast', Gio.BusNameOwnerFlags.REPLACE, null, null); Gio.DBus.session.own_name('org.gnome.Shell.Screencast', Gio.BusNameOwnerFlags.REPLACE, null, null);
this._recorders = new Hash.Map(); this._recorders = new Map();
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated)); Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
}, },
get isRecording() { get isRecording() {
return this._recorders.size() > 0; return this._recorders.size > 0;
}, },
_ensureRecorderForSender: function(sender) { _ensureRecorderForSender: function(sender) {
@ -67,8 +68,7 @@ const ScreencastService = new Lang.Class({
if (Main.sessionMode.allowScreencast) if (Main.sessionMode.allowScreencast)
return; return;
for (let sender in this._recorders.keys()) this._recorders.clear();
this._recorders.delete(sender);
this.emit('updated'); this.emit('updated');
}, },
@ -103,8 +103,10 @@ const ScreencastService = new Lang.Class({
ScreencastAsync: function(params, invocation) { ScreencastAsync: function(params, invocation) {
let returnValue = [false, '']; let returnValue = [false, ''];
if (!Main.sessionMode.allowScreencast) if (!Main.sessionMode.allowScreencast) {
invocation.return_value(GLib.Variant.new('(bs)', returnValue)); invocation.return_value(GLib.Variant.new('(bs)', returnValue));
return;
}
let sender = invocation.get_sender(); let sender = invocation.get_sender();
let recorder = this._ensureRecorderForSender(sender); let recorder = this._ensureRecorderForSender(sender);
@ -122,8 +124,10 @@ const ScreencastService = new Lang.Class({
ScreencastAreaAsync: function(params, invocation) { ScreencastAreaAsync: function(params, invocation) {
let returnValue = [false, '']; let returnValue = [false, ''];
if (!Main.sessionMode.allowScreencast) if (!Main.sessionMode.allowScreencast) {
invocation.return_value(GLib.Variant.new('(bs)', returnValue)); invocation.return_value(GLib.Variant.new('(bs)', returnValue));
return;
}
let sender = invocation.get_sender(); let sender = invocation.get_sender();
let recorder = this._ensureRecorderForSender(sender); let recorder = this._ensureRecorderForSender(sender);
@ -131,6 +135,16 @@ const ScreencastService = new Lang.Class({
if (!recorder.is_recording()) { if (!recorder.is_recording()) {
let [x, y, width, height, fileTemplate, options] = params; let [x, y, width, height, fileTemplate, options] = params;
if (x < 0 || y < 0 ||
width <= 0 || height <= 0 ||
x + width > global.screen_width ||
y + height > global.screen_height) {
invocation.return_error_literal(Gio.IOErrorEnum,
Gio.IOErrorEnum.CANCELLED,
"Invalid params");
return;
}
recorder.set_file_template(fileTemplate); recorder.set_file_template(fileTemplate);
recorder.set_area(x, y, width, height); recorder.set_area(x, y, width, height);
this._applyOptionalParameters(recorder, options); this._applyOptionalParameters(recorder, options);

View File

@ -15,45 +15,47 @@ const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main; const Main = imports.ui.main;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const ScreenshotIface = <interface name="org.gnome.Shell.Screenshot"> const ScreenshotIface = '<node> \
<method name="ScreenshotArea"> <interface name="org.gnome.Shell.Screenshot"> \
<arg type="i" direction="in" name="x"/> <method name="ScreenshotArea"> \
<arg type="i" direction="in" name="y"/> <arg type="i" direction="in" name="x"/> \
<arg type="i" direction="in" name="width"/> <arg type="i" direction="in" name="y"/> \
<arg type="i" direction="in" name="height"/> <arg type="i" direction="in" name="width"/> \
<arg type="b" direction="in" name="flash"/> <arg type="i" direction="in" name="height"/> \
<arg type="s" direction="in" name="filename"/> <arg type="b" direction="in" name="flash"/> \
<arg type="b" direction="out" name="success"/> <arg type="s" direction="in" name="filename"/> \
<arg type="s" direction="out" name="filename_used"/> <arg type="b" direction="out" name="success"/> \
</method> <arg type="s" direction="out" name="filename_used"/> \
<method name="ScreenshotWindow"> </method> \
<arg type="b" direction="in" name="include_frame"/> <method name="ScreenshotWindow"> \
<arg type="b" direction="in" name="include_cursor"/> <arg type="b" direction="in" name="include_frame"/> \
<arg type="b" direction="in" name="flash"/> <arg type="b" direction="in" name="include_cursor"/> \
<arg type="s" direction="in" name="filename"/> <arg type="b" direction="in" name="flash"/> \
<arg type="b" direction="out" name="success"/> <arg type="s" direction="in" name="filename"/> \
<arg type="s" direction="out" name="filename_used"/> <arg type="b" direction="out" name="success"/> \
</method> <arg type="s" direction="out" name="filename_used"/> \
<method name="Screenshot"> </method> \
<arg type="b" direction="in" name="include_cursor"/> <method name="Screenshot"> \
<arg type="b" direction="in" name="flash"/> <arg type="b" direction="in" name="include_cursor"/> \
<arg type="s" direction="in" name="filename"/> <arg type="b" direction="in" name="flash"/> \
<arg type="b" direction="out" name="success"/> <arg type="s" direction="in" name="filename"/> \
<arg type="s" direction="out" name="filename_used"/> <arg type="b" direction="out" name="success"/> \
</method> <arg type="s" direction="out" name="filename_used"/> \
<method name="SelectArea"> </method> \
<arg type="i" direction="out" name="x"/> <method name="SelectArea"> \
<arg type="i" direction="out" name="y"/> <arg type="i" direction="out" name="x"/> \
<arg type="i" direction="out" name="width"/> <arg type="i" direction="out" name="y"/> \
<arg type="i" direction="out" name="height"/> <arg type="i" direction="out" name="width"/> \
</method> <arg type="i" direction="out" name="height"/> \
<method name="FlashArea"> </method> \
<arg type="i" direction="in" name="x"/> <method name="FlashArea"> \
<arg type="i" direction="in" name="y"/> <arg type="i" direction="in" name="x"/> \
<arg type="i" direction="in" name="width"/> <arg type="i" direction="in" name="y"/> \
<arg type="i" direction="in" name="height"/> <arg type="i" direction="in" name="width"/> \
</method> <arg type="i" direction="in" name="height"/> \
</interface>; </method> \
</interface> \
</node>';
const ScreenshotService = new Lang.Class({ const ScreenshotService = new Lang.Class({
Name: 'ScreenshotService', Name: 'ScreenshotService',
@ -77,7 +79,9 @@ const ScreenshotService = new Lang.Class({
ScreenshotAreaAsync : function (params, invocation) { ScreenshotAreaAsync : function (params, invocation) {
let [x, y, width, height, flash, filename, callback] = params; let [x, y, width, height, flash, filename, callback] = params;
if (height <= 0 || width <= 0) { if (x < 0 || y < 0 ||
width <= 0 || height <= 0 ||
x + width > global.screen_width || y + height > global.screen_height) {
invocation.return_error_literal(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED, invocation.return_error_literal(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
"Invalid params"); "Invalid params");
return; return;
@ -142,7 +146,7 @@ const SelectArea = new Lang.Class({
reactive: true, reactive: true,
x: 0, x: 0,
y: 0 }); y: 0 });
Main.uiGroup.add_actor(this._group); Main.layoutManager.osdGroup.add_actor(this._group);
this._group.connect('button-press-event', this._group.connect('button-press-event',
Lang.bind(this, this._onButtonPress)); Lang.bind(this, this._onButtonPress));
@ -202,12 +206,12 @@ const SelectArea = new Lang.Class({
if (event.get_key_symbol() == Clutter.Escape) if (event.get_key_symbol() == Clutter.Escape)
this._destroy(null, false); this._destroy(null, false);
return; return Clutter.EVENT_PROPAGATE;
}, },
_onMotionEvent: function(actor, event) { _onMotionEvent: function(actor, event) {
if (this._startX == -1 || this._startY == -1) if (this._startX == -1 || this._startY == -1)
return false; return Clutter.EVENT_PROPAGATE;
[this._lastX, this._lastY] = event.get_coords(); [this._lastX, this._lastY] = event.get_coords();
let geometry = this._getGeometry(); let geometry = this._getGeometry();
@ -215,19 +219,19 @@ const SelectArea = new Lang.Class({
this._rubberband.set_position(geometry.x, geometry.y); this._rubberband.set_position(geometry.x, geometry.y);
this._rubberband.set_size(geometry.width, geometry.height); this._rubberband.set_size(geometry.width, geometry.height);
return false; return Clutter.EVENT_PROPAGATE;
}, },
_onButtonPress: function(actor, event) { _onButtonPress: function(actor, event) {
[this._startX, this._startY] = event.get_coords(); [this._startX, this._startY] = event.get_coords();
this._rubberband.set_position(this._startX, this._startY); this._rubberband.set_position(this._startX, this._startY);
return false; return Clutter.EVENT_PROPAGATE;
}, },
_onButtonRelease: function(actor, event) { _onButtonRelease: function(actor, event) {
this._destroy(this._getGeometry(), true); this._destroy(this._getGeometry(), true);
return false; return Clutter.EVENT_PROPAGATE;
}, },
_destroy: function(geometry, fade) { _destroy: function(geometry, fade) {
@ -255,9 +259,9 @@ const Flashspot = new Lang.Class({
Extends: Lightbox.Lightbox, Extends: Lightbox.Lightbox,
_init: function(area) { _init: function(area) {
this.parent(Main.uiGroup, { inhibitEvents: true, this.parent(Main.layoutManager.osdGroup, { inhibitEvents: true,
width: area.width, width: area.width,
height: area.height }); height: area.height });
this.actor.style_class = 'flashspot'; this.actor.style_class = 'flashspot';
this.actor.set_position(area.x, area.y); this.actor.set_position(area.x, area.y);

View File

@ -1,6 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta; const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
@ -41,7 +42,7 @@ function sleep(milliseconds) {
Mainloop.timeout_add(milliseconds, function() { Mainloop.timeout_add(milliseconds, function() {
if (cb) if (cb)
cb(); cb();
return false; return GLib.SOURCE_REMOVE;
}); });
return function(callback) { return function(callback) {
@ -69,16 +70,18 @@ function waitLeisure() {
}; };
} }
const PerfHelperIface = <interface name="org.gnome.Shell.PerfHelper"> const PerfHelperIface = '<node> \
<method name="CreateWindow"> <interface name="org.gnome.Shell.PerfHelper"> \
<arg type="i" direction="in" /> <method name="CreateWindow"> \
<arg type="i" direction="in" /> <arg type="i" direction="in" /> \
<arg type="b" direction="in" /> <arg type="i" direction="in" /> \
<arg type="b" direction="in" /> <arg type="b" direction="in" /> \
</method> <arg type="b" direction="in" /> \
<method name="WaitWindows" /> </method> \
<method name="DestroyWindows" /> <method name="WaitWindows" /> \
</interface>; <method name="DestroyWindows" /> \
</interface> \
</node>';
var PerfHelperProxy = Gio.DBusProxy.makeProxyWrapper(PerfHelperIface); var PerfHelperProxy = Gio.DBusProxy.makeProxyWrapper(PerfHelperIface);
function PerfHelper() { function PerfHelper() {

View File

@ -1,105 +1,706 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Lang = imports.lang; const Lang = imports.lang;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Meta = imports.gi.Meta;
const Signals = imports.signals; const Signals = imports.signals;
const St = imports.gi.St;
const Atk = imports.gi.Atk;
const AppDisplay = imports.ui.appDisplay;
const DND = imports.ui.dnd;
const IconGrid = imports.ui.iconGrid;
const Main = imports.ui.main;
const Overview = imports.ui.overview;
const RemoteSearch = imports.ui.remoteSearch;
const Separator = imports.ui.separator;
const Util = imports.misc.util;
const SEARCH_PROVIDERS_SCHEMA = 'org.gnome.desktop.search-providers'; const SEARCH_PROVIDERS_SCHEMA = 'org.gnome.desktop.search-providers';
const MAX_LIST_SEARCH_RESULTS_ROWS = 3;
const MAX_GRID_SEARCH_RESULTS_ROWS = 1;
const SearchSystem = new Lang.Class({ const SearchSystem = new Lang.Class({
Name: 'SearchSystem', Name: 'SearchSystem',
_init: function() { _init: function() {
this._providers = []; this._providers = [];
this._remoteProviders = [];
this.reset(); this._registerProvider(new AppDisplay.AppSearchProvider());
this._searchSettings = new Gio.Settings({ schema: SEARCH_PROVIDERS_SCHEMA });
this._searchSettings.connect('changed::disabled', Lang.bind(this, this._reloadRemoteProviders));
this._searchSettings.connect('changed::disable-external', Lang.bind(this, this._reloadRemoteProviders));
this._searchSettings.connect('changed::sort-order', Lang.bind(this, this._reloadRemoteProviders));
this._reloadRemoteProviders();
this._cancellable = new Gio.Cancellable();
}, },
registerProvider: function (provider) { addProvider: function(provider) {
provider.searchSystem = this;
this._providers.push(provider); this._providers.push(provider);
this.emit('providers-changed');
if (provider.isRemoteProvider)
this._remoteProviders.push(provider);
}, },
unregisterProvider: function (provider) { _reloadRemoteProviders: function() {
let index = this._providers.indexOf(provider); let remoteProviders = this._providers.filter(function(provider) {
if (index == -1) return provider.isRemoteProvider;
return; });
provider.searchSystem = null; remoteProviders.forEach(Lang.bind(this, function(provider) {
this._providers.splice(index, 1); this._unregisterProvider(provider);
}));
let remoteIndex = this._remoteProviders.indexOf(provider); RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, function(providers) {
if (remoteIndex != -1) providers.forEach(Lang.bind(this, this._registerProvider));
this._remoteProviders.splice(remoteIndex, 1); }));
this.emit('providers-changed');
},
_registerProvider: function (provider) {
this._providers.push(provider);
},
_unregisterProvider: function (provider) {
let index = this._providers.indexOf(provider);
this._providers.splice(index, 1);
}, },
getProviders: function() { getProviders: function() {
return this._providers; return this._providers;
}, },
getRemoteProviders: function() {
return this._remoteProviders;
},
getTerms: function() { getTerms: function() {
return this._previousTerms; return this._terms;
}, },
reset: function() { reset: function() {
this._previousTerms = []; this._terms = [];
this._previousResults = []; this._results = {};
}, },
setResults: function(provider, results) { _gotResults: function(results, provider) {
let i = this._providers.indexOf(provider); this._results[provider.id] = results;
if (i == -1) this.emit('search-updated', provider, results);
return;
this._previousResults[i] = [provider, results];
this.emit('search-updated', this._previousResults[i]);
}, },
updateSearchResults: function(terms) { setTerms: function(terms) {
this._cancellable.cancel();
this._cancellable.reset();
let previousResults = this._results;
let previousTerms = this._terms;
this.reset();
if (!terms) if (!terms)
return; return;
let searchString = terms.join(' '); let searchString = terms.join(' ');
let previousSearchString = this._previousTerms.join(' '); let previousSearchString = previousTerms.join(' ');
if (searchString == previousSearchString) if (searchString == previousSearchString)
return; return;
let isSubSearch = false; let isSubSearch = false;
if (this._previousTerms.length > 0) if (previousTerms.length > 0)
isSubSearch = searchString.indexOf(previousSearchString) == 0; isSubSearch = searchString.indexOf(previousSearchString) == 0;
let previousResultsArr = this._previousResults; this._terms = terms;
let results = []; this._providers.forEach(Lang.bind(this, function(provider) {
this._previousTerms = terms; let previousProviderResults = previousResults[provider.id];
this._previousResults = results; if (isSubSearch && previousProviderResults)
provider.getSubsearchResultSet(previousProviderResults, terms, Lang.bind(this, this._gotResults, provider), this._cancellable);
if (isSubSearch) { else
for (let i = 0; i < this._providers.length; i++) { provider.getInitialResultSet(terms, Lang.bind(this, this._gotResults, provider), this._cancellable);
let [provider, previousResults] = previousResultsArr[i]; }));
try {
results.push([provider, []]);
provider.getSubsearchResultSet(previousResults, terms);
} catch (error) {
log('A ' + error.name + ' has occured in ' + provider.id + ': ' + error.message);
}
}
} else {
for (let i = 0; i < this._providers.length; i++) {
let provider = this._providers[i];
try {
results.push([provider, []]);
provider.getInitialResultSet(terms);
} catch (error) {
log('A ' + error.name + ' has occured in ' + provider.id + ': ' + error.message);
}
}
}
} }
}); });
Signals.addSignalMethods(SearchSystem.prototype); Signals.addSignalMethods(SearchSystem.prototype);
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',
_init: function(provider, metaInfo) {
this.provider = provider;
this.metaInfo = metaInfo;
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.actor.connect('clicked', Lang.bind(this, this.activate));
},
activate: function() {
this.emit('activate', this.metaInfo.id);
},
setSelected: function(selected) {
if (selected)
this.actor.add_style_pseudo_class('selected');
else
this.actor.remove_style_pseudo_class('selected');
}
});
Signals.addSignalMethods(SearchResult.prototype);
const ListSearchResult = new Lang.Class({
Name: 'ListSearchResult',
Extends: SearchResult,
ICON_SIZE: 64,
_init: function(provider, metaInfo) {
this.parent(provider, metaInfo);
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) {
this.parent(provider, metaInfo);
this.actor.style_class = 'grid-search-result';
let content = provider.createResultObject(metaInfo);
let dragSource = null;
if (content == null) {
let actor = new St.Bin();
let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
{ createIcon: this.metaInfo['createIcon'] });
actor.set_child(icon.actor);
actor.label_actor = icon.label;
dragSource = icon.icon;
content = { actor: actor, icon: icon };
} else {
if (content._delegate && content._delegate.getDragActorSource)
dragSource = content._delegate.getDragActorSource();
}
this.actor.set_child(content.actor);
this.actor.label_actor = content.actor.label_actor;
this.icon = content.icon;
let draggable = DND.makeDraggable(this.actor);
draggable.connect('drag-begin',
Lang.bind(this, function() {
Main.overview.beginItemDrag(this);
}));
draggable.connect('drag-cancelled',
Lang.bind(this, function() {
Main.overview.cancelledItemDrag(this);
}));
draggable.connect('drag-end',
Lang.bind(this, function() {
Main.overview.endItemDrag(this);
}));
if (!dragSource)
// not exactly right, but alignment problems are hard to notice
dragSource = content;
this._dragActorSource = dragSource;
},
getDragActorSource: function() {
return this._dragActorSource;
},
getDragActor: function() {
return this.metaInfo['createIcon'](Main.overview.dashIconSize);
},
shellWorkspaceLaunch: function(params) {
if (this.provider.dragActivateResult)
this.provider.dragActivateResult(this.metaInfo.id, params);
else
this.provider.activateResult(this.metaInfo.id, this.terms);
}
});
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);
this._resultDisplays = {};
this._cancellable = new Gio.Cancellable();
},
destroy: function() {
this.actor.destroy();
this._terms = [];
},
_clearResultDisplay: function() {
},
clear: function() {
this._resultDisplays = {};
this._clearResultDisplay();
this.actor.hide();
},
_keyFocusIn: function(actor) {
this.emit('key-focus-in', actor);
},
_activateResult: function(result, id) {
this.provider.activateResult(id, this._terms);
Main.overview.toggle();
},
_setMoreIconVisible: function(visible) {
},
_ensureResultActors: function(results, callback) {
let metasNeeded = results.filter(Lang.bind(this, function(resultId) {
return this._resultDisplays[resultId] === undefined;
}));
if (metasNeeded.length === 0) {
callback();
} else {
this._cancellable.cancel();
this._cancellable.reset();
this.provider.getResultMetas(metasNeeded, Lang.bind(this, function(metas) {
metasNeeded.forEach(Lang.bind(this, function(resultId, i) {
let meta = metas[i];
let display = this._createResultDisplay(meta);
display.connect('activate', Lang.bind(this, this._activateResult));
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
this._resultDisplays[resultId] = display;
}));
callback();
}), this._cancellable);
}
},
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 = this.provider.filterResults(providerResults, maxResults);
let hasMoreResults = results.length < providerResults.length;
this._ensureResultActors(results, Lang.bind(this, function() {
this._clearResultDisplay();
// 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();
results.forEach(Lang.bind(this, function(resultId) {
this._addItem(this._resultDisplays[resultId]);
}));
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('key-focus-in', Lang.bind(this, this._keyFocusIn));
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;
},
_clearResultDisplay: function () {
this._content.remove_all_children();
},
_createResultDisplay: function(meta) {
return new ListSearchResult(this.provider, meta);
},
_addItem: function(display) {
this._content.add_actor(display.actor);
},
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: SearchResultsBase,
_init: function(provider) {
this.parent(provider);
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._resultDisplayBin.set_child(this._bin);
},
_getMaxDisplayedResults: function() {
return this._grid.columnsForWidth(this._bin.width) * this._grid.getRowLimit();
},
_renderResults: function(metas) {
for (let i = 0; i < metas.length; i++) {
let display = new GridSearchResult(this.provider, metas[i]);
display.connect('activate', Lang.bind(this, this._activateResult));
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
this._grid.addItem(display);
}
},
_clearResultDisplay: function () {
this._grid.removeAll();
},
_createResultDisplay: function(meta) {
return new GridSearchResult(this.provider, meta);
},
_addItem: function(display) {
this._grid.addItem(display);
},
getFirstResult: function() {
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',
_init: function() {
this.actor = new St.BoxLayout({ name: 'searchResults',
vertical: true });
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,
overlay_scrollbars: true,
style_class: 'search-display vfade' });
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
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);
this.actor.add(this._scrollView, { x_fill: true,
y_fill: true,
expand: true,
x_align: St.Align.START,
y_align: St.Align.START });
this._statusText = new St.Label({ style_class: 'search-statustext' });
this._statusBin = new St.Bin({ x_align: St.Align.MIDDLE,
y_align: St.Align.MIDDLE });
this._content.add(this._statusBin, { expand: true });
this._statusBin.add_actor(this._statusText);
this._highlightDefault = false;
this._defaultResult = null;
this._searchSystem = new SearchSystem();
this._searchSystem.connect('search-updated', Lang.bind(this, this._updateResults));
this._searchSystem.connect('providers-changed', Lang.bind(this, this._updateProviderDisplays));
this._updateProviderDisplays();
},
_onPan: function(action) {
let [dist, dx, dy] = action.get_motion_delta(0);
let adjustment = this._scrollView.vscroll.adjustment;
adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
return false;
},
_keyFocusIn: function(provider, actor) {
Util.ensureActorVisibleInScrollView(this._scrollView, actor);
},
_ensureProviderDisplay: function(provider) {
if (provider.display)
return;
let providerDisplay;
if (provider.appInfo)
providerDisplay = new ListSearchResults(provider);
else
providerDisplay = new GridSearchResults(provider);
providerDisplay.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
this._content.add(providerDisplay.actor);
provider.display = providerDisplay;
},
_updateProviderDisplays: function() {
this._searchSystem.getProviders().forEach(Lang.bind(this, this._ensureProviderDisplay));
},
_clearDisplay: function() {
this._searchSystem.getProviders().forEach(function(provider) {
provider.display.clear();
});
},
reset: function() {
this._searchSystem.reset();
this._statusBin.hide();
this._clearDisplay();
this._defaultResult = null;
},
startingSearch: function() {
this.reset();
this._statusText.set_text(_("Searching…"));
this._statusBin.show();
},
setTerms: function(terms) {
this._searchSystem.setTerms(terms);
},
_maybeSetInitialSelection: function() {
let newDefaultResult = null;
let providers = this._searchSystem.getProviders();
for (let i = 0; i < providers.length; i++) {
let provider = providers[i];
let display = provider.display;
if (!display.actor.visible)
continue;
let firstResult = display.getFirstResult();
if (firstResult) {
newDefaultResult = firstResult;
break; // select this one!
}
}
if (newDefaultResult != this._defaultResult) {
if (this._defaultResult)
this._defaultResult.setSelected(false);
if (newDefaultResult)
newDefaultResult.setSelected(this._highlightDefault);
this._defaultResult = newDefaultResult;
}
},
_updateStatusText: function () {
let haveResults = this._searchSystem.getProviders().some(function(provider) {
let display = provider.display;
return (display.getFirstResult() != null);
});
if (!haveResults) {
this._statusText.set_text(_("No results."));
this._statusBin.show();
} else {
this._statusBin.hide();
}
},
_updateResults: function(searchSystem, provider, results) {
let terms = searchSystem.getTerms();
let display = provider.display;
display.updateSearch(results, terms, Lang.bind(this, function() {
this._maybeSetInitialSelection();
this._updateStatusText();
}));
},
activateDefault: function() {
if (this._defaultResult)
this._defaultResult.activate();
},
highlightDefault: function(highlight) {
this._highlightDefault = highlight;
if (this._defaultResult)
this._defaultResult.setSelected(highlight);
},
navigateFocus: function(direction) {
let rtl = this.actor.get_text_direction() == Clutter.TextDirection.RTL;
if (direction == Gtk.DirectionType.TAB_BACKWARD ||
direction == (rtl ? Gtk.DirectionType.RIGHT
: Gtk.DirectionType.LEFT) ||
direction == Gtk.DirectionType.UP) {
this.actor.navigate_focus(null, direction, false);
return;
}
let from = this._defaultResult ? this._defaultResult.actor : null;
this.actor.navigate_focus(from, 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);
}
});

View File

@ -1,566 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
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;
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_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',
_init: function(provider, metaInfo, terms) {
this.provider = provider;
this.metaInfo = metaInfo;
this.terms = terms;
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.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.createResultObject(metaInfo, terms);
let dragSource = null;
if (content == null) {
let actor = new St.Bin();
let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
{ createIcon: this.metaInfo['createIcon'] });
actor.set_child(icon.actor);
actor.label_actor = icon.label;
dragSource = icon.icon;
content = { actor: actor, icon: icon };
} else {
if (content._delegate && content._delegate.getDragActorSource)
dragSource = content._delegate.getDragActorSource();
}
this.actor.set_child(content.actor);
this.actor.label_actor = content.actor.label_actor;
this.icon = content.icon;
let draggable = DND.makeDraggable(this.actor);
draggable.connect('drag-begin',
Lang.bind(this, function() {
Main.overview.beginItemDrag(this);
}));
draggable.connect('drag-cancelled',
Lang.bind(this, function() {
Main.overview.cancelledItemDrag(this);
}));
draggable.connect('drag-end',
Lang.bind(this, function() {
Main.overview.endItemDrag(this);
}));
if (!dragSource)
// not exactly right, but alignment problems are hard to notice
dragSource = content;
this._dragActorSource = dragSource;
},
getDragActorSource: function() {
return this._dragActorSource;
},
getDragActor: function() {
return this.metaInfo['createIcon'](Main.overview.dashIconSize);
},
shellWorkspaceLaunch: function(params) {
if (this.provider.dragActivateResult)
this.provider.dragActivateResult(this.metaInfo.id, params);
else
this.provider.activateResult(this.metaInfo.id, this.terms);
}
});
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: SearchResultsBase,
_init: function(provider) {
this.parent(provider);
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._resultDisplayBin.set_child(this._bin);
},
_getMaxDisplayedResults: function() {
return this._grid.columnsForWidth(this._bin.width) * this._grid.getRowLimit();
},
_renderResults: function(metas) {
for (let i = 0; i < metas.length; i++) {
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);
}
},
_clearResultDisplay: function () {
this._grid.removeAll();
},
getFirstResult: function() {
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',
_init: function(searchSystem) {
this._searchSystem = searchSystem;
this._searchSystem.connect('search-updated', Lang.bind(this, this._updateResults));
this.actor = new St.BoxLayout({ name: 'searchResults',
vertical: true });
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,
overlay_scrollbars: true,
style_class: 'search-display vfade' });
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
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);
this.actor.add(this._scrollView, { x_fill: true,
y_fill: true,
expand: true,
x_align: St.Align.START,
y_align: St.Align.START });
this._statusText = new St.Label({ style_class: 'search-statustext' });
this._statusBin = new St.Bin({ x_align: St.Align.MIDDLE,
y_align: St.Align.MIDDLE });
this._content.add(this._statusBin, { expand: true });
this._statusBin.add_actor(this._statusText);
this._providers = this._searchSystem.getProviders();
this._providerDisplays = {};
for (let i = 0; i < this._providers.length; i++) {
this.createProviderDisplay(this._providers[i]);
}
this._highlightDefault = false;
this._defaultResult = null;
},
_onPan: function(action) {
let [dist, dx, dy] = action.get_motion_delta(0);
let adjustment = this._scrollView.vscroll.adjustment;
adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
return false;
},
_keyFocusIn: function(provider, icon) {
Util.ensureActorVisibleInScrollView(this._scrollView, icon);
},
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._providers.length; i++) {
let provider = this._providers[i];
let providerDisplay = this._providerDisplays[provider.id];
providerDisplay.clear();
}
},
reset: function() {
this._searchSystem.reset();
this._statusBin.hide();
this._clearDisplay();
this._defaultResult = null;
},
startingSearch: function() {
this.reset();
this._statusText.set_text(_("Searching…"));
this._statusBin.show();
},
_maybeSetInitialSelection: function() {
let newDefaultResult = null;
for (let i = 0; i < this._providers.length; i++) {
let provider = this._providers[i];
let display = this._providerDisplays[provider.id];
if (!display.actor.visible)
continue;
let firstResult = display.getFirstResult();
if (firstResult) {
newDefaultResult = firstResult;
break; // select this one!
}
}
if (newDefaultResult != this._defaultResult) {
if (this._defaultResult)
this._defaultResult.setSelected(false);
if (newDefaultResult)
newDefaultResult.setSelected(this._highlightDefault);
this._defaultResult = newDefaultResult;
}
},
_updateStatusText: function () {
let haveResults = false;
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."));
this._statusBin.show();
} else {
this._statusBin.hide();
}
},
_updateResults: function(searchSystem, results) {
let terms = searchSystem.getTerms();
let [provider, providerResults] = results;
let display = this._providerDisplays[provider.id];
display.updateSearch(providerResults, terms, Lang.bind(this, function() {
this._maybeSetInitialSelection();
this._updateStatusText();
}));
},
activateDefault: function() {
if (this._defaultResult)
this._defaultResult.activate();
},
highlightDefault: function(highlight) {
this._highlightDefault = highlight;
if (this._defaultResult)
this._defaultResult.setSelected(highlight);
},
navigateFocus: function(direction) {
let rtl = this.actor.get_text_direction() == Clutter.TextDirection.RTL;
if (direction == Gtk.DirectionType.TAB_BACKWARD ||
direction == (rtl ? Gtk.DirectionType.RIGHT
: Gtk.DirectionType.LEFT) ||
direction == Gtk.DirectionType.UP) {
this.actor.navigate_focus(null, direction, false);
return;
}
let from = this._defaultResult ? this._defaultResult.actor : null;
this.actor.navigate_focus(from, 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);
}
});

View File

@ -102,19 +102,12 @@ const _modes = {
} }
}; };
function _getModes(modesLoadedCallback) { function _loadMode(file, info) {
FileUtils.collectFromDatadirsAsync('modes',
{ processFile: _loadMode,
loadedCallback: modesLoadedCallback,
data: _modes });
}
function _loadMode(file, info, loadedData) {
let name = info.get_name(); let name = info.get_name();
let suffix = name.indexOf('.json'); let suffix = name.indexOf('.json');
let modeName = suffix == -1 ? name : name.slice(name, suffix); let modeName = suffix == -1 ? name : name.slice(name, suffix);
if (loadedData.hasOwnProperty(modeName)) if (_modes.hasOwnProperty(modeName))
return; return;
let fileContent, success, tag, newMode; let fileContent, success, tag, newMode;
@ -125,19 +118,24 @@ function _loadMode(file, info, loadedData) {
return; return;
} }
loadedData[modeName] = {}; _modes[modeName] = {};
let propBlacklist = ['unlockDialog']; let propBlacklist = ['unlockDialog'];
for (let prop in loadedData[DEFAULT_MODE]) { for (let prop in _modes[DEFAULT_MODE]) {
if (newMode[prop] !== undefined && if (newMode[prop] !== undefined &&
propBlacklist.indexOf(prop) == -1) propBlacklist.indexOf(prop) == -1)
loadedData[modeName][prop]= newMode[prop]; _modes[modeName][prop] = newMode[prop];
} }
loadedData[modeName]['isPrimary'] = true; _modes[modeName]['isPrimary'] = true;
}
function _loadModes() {
FileUtils.collectFromDatadirs('modes', false, _loadMode);
} }
function listModes() { function listModes() {
_getModes(function(modes) { _loadModes();
let names = Object.getOwnPropertyNames(modes); Mainloop.idle_add(function() {
let names = Object.getOwnPropertyNames(_modes);
for (let i = 0; i < names.length; i++) for (let i = 0; i < names.length; i++)
if (_modes[names[i]].isPrimary) if (_modes[names[i]].isPrimary)
print(names[i]); print(names[i]);
@ -149,17 +147,13 @@ function listModes() {
const SessionMode = new Lang.Class({ const SessionMode = new Lang.Class({
Name: 'SessionMode', Name: 'SessionMode',
init: function() { _init: function() {
_getModes(Lang.bind(this, function(modes) { _loadModes();
this._modes = modes; let isPrimary = (_modes[global.session_mode] &&
let primary = modes[global.session_mode] && _modes[global.session_mode].isPrimary);
modes[global.session_mode].isPrimary; let mode = isPrimary ? global.session_mode : 'user';
let mode = primary ? global.session_mode : 'user'; this._modeStack = [mode];
this._modeStack = [mode]; this._sync();
this._sync();
this.emit('sessions-loaded');
}));
}, },
pushMode: function(mode) { pushMode: function(mode) {
@ -186,13 +180,13 @@ const SessionMode = new Lang.Class({
}, },
_sync: function() { _sync: function() {
let params = this._modes[this.currentMode]; let params = _modes[this.currentMode];
let defaults; let defaults;
if (params.parentMode) if (params.parentMode)
defaults = Params.parse(this._modes[params.parentMode], defaults = Params.parse(_modes[params.parentMode],
this._modes[DEFAULT_MODE]); _modes[DEFAULT_MODE]);
else else
defaults = this._modes[DEFAULT_MODE]; defaults = _modes[DEFAULT_MODE];
params = Params.parse(params, defaults); params = Params.parse(params, defaults);
// A simplified version of Lang.copyProperties, handles // A simplified version of Lang.copyProperties, handles

View File

@ -10,64 +10,67 @@ const Config = imports.misc.config;
const ExtensionSystem = imports.ui.extensionSystem; const ExtensionSystem = imports.ui.extensionSystem;
const ExtensionDownloader = imports.ui.extensionDownloader; const ExtensionDownloader = imports.ui.extensionDownloader;
const ExtensionUtils = imports.misc.extensionUtils; const ExtensionUtils = imports.misc.extensionUtils;
const Hash = imports.misc.hash;
const Main = imports.ui.main; const Main = imports.ui.main;
const Screenshot = imports.ui.screenshot; const Screenshot = imports.ui.screenshot;
const ViewSelector = imports.ui.viewSelector; const ViewSelector = imports.ui.viewSelector;
const GnomeShellIface = <interface name="org.gnome.Shell"> const GnomeShellIface = '<node> \
<method name="Eval"> <interface name="org.gnome.Shell"> \
<arg type="s" direction="in" name="script" /> <method name="Eval"> \
<arg type="b" direction="out" name="success" /> <arg type="s" direction="in" name="script" /> \
<arg type="s" direction="out" name="result" /> <arg type="b" direction="out" name="success" /> \
</method> <arg type="s" direction="out" name="result" /> \
<method name="FocusSearch"/> </method> \
<method name="ShowOSD"> <method name="FocusSearch"/> \
<arg type="a{sv}" direction="in" name="params"/> <method name="ShowOSD"> \
</method> <arg type="a{sv}" direction="in" name="params"/> \
<method name="FocusApp"> </method> \
<arg type="s" direction="in" name="id"/> <method name="FocusApp"> \
</method> <arg type="s" direction="in" name="id"/> \
<method name="ShowApplications" /> </method> \
<method name="GrabAccelerator"> <method name="ShowApplications" /> \
<arg type="s" direction="in" name="accelerator"/> <method name="GrabAccelerator"> \
<arg type="u" direction="in" name="flags"/> <arg type="s" direction="in" name="accelerator"/> \
<arg type="u" direction="out" name="action"/> <arg type="u" direction="in" name="flags"/> \
</method> <arg type="u" direction="out" name="action"/> \
<method name="GrabAccelerators"> </method> \
<arg type="a(su)" direction="in" name="accelerators"/> <method name="GrabAccelerators"> \
<arg type="au" direction="out" name="actions"/> <arg type="a(su)" direction="in" name="accelerators"/> \
</method> <arg type="au" direction="out" name="actions"/> \
<method name="UngrabAccelerator"> </method> \
<arg type="u" direction="in" name="action"/> <method name="UngrabAccelerator"> \
<arg type="b" direction="out" name="success"/> <arg type="u" direction="in" name="action"/> \
</method> <arg type="b" direction="out" name="success"/> \
<signal name="AcceleratorActivated"> </method> \
<arg name="action" type="u" /> <signal name="AcceleratorActivated"> \
<arg name="deviceid" type="u" /> <arg name="action" type="u" /> \
<arg name="timestamp" type="u" /> <arg name="deviceid" type="u" /> \
</signal> <arg name="timestamp" type="u" /> \
<property name="Mode" type="s" access="read" /> </signal> \
<property name="OverviewActive" type="b" access="readwrite" /> <property name="Mode" type="s" access="read" /> \
<property name="ShellVersion" type="s" access="read" /> <property name="OverviewActive" type="b" access="readwrite" /> \
</interface>; <property name="ShellVersion" type="s" access="read" /> \
</interface> \
</node>';
const ScreenSaverIface = <interface name="org.gnome.ScreenSaver"> const ScreenSaverIface = '<node> \
<method name="Lock"> <interface name="org.gnome.ScreenSaver"> \
</method> <method name="Lock"> \
<method name="GetActive"> </method> \
<arg name="active" direction="out" type="b" /> <method name="GetActive"> \
</method> <arg name="active" direction="out" type="b" /> \
<method name="SetActive"> </method> \
<arg name="value" direction="in" type="b" /> <method name="SetActive"> \
</method> <arg name="value" direction="in" type="b" /> \
<method name="GetActiveTime"> </method> \
<arg name="value" direction="out" type="u" /> <method name="GetActiveTime"> \
</method> <arg name="value" direction="out" type="u" /> \
<signal name="ActiveChanged"> </method> \
<arg name="new_value" type="b" /> <signal name="ActiveChanged"> \
</signal> <arg name="new_value" type="b" /> \
</interface>; </signal> \
</interface> \
</node>';
const GnomeShell = new Lang.Class({ const GnomeShell = new Lang.Class({
Name: 'GnomeShellDBus', Name: 'GnomeShellDBus',
@ -79,8 +82,8 @@ const GnomeShell = new Lang.Class({
this._extensionsService = new GnomeShellExtensions(); this._extensionsService = new GnomeShellExtensions();
this._screenshotService = new Screenshot.ScreenshotService(); this._screenshotService = new Screenshot.ScreenshotService();
this._grabbedAccelerators = new Hash.Map(); this._grabbedAccelerators = new Map();
this._grabbers = new Hash.Map(); this._grabbers = new Map();
global.display.connect('accelerator-activated', Lang.bind(this, global.display.connect('accelerator-activated', Lang.bind(this,
function(display, action, deviceid, timestamp) { function(display, action, deviceid, timestamp) {
@ -115,7 +118,7 @@ const GnomeShell = new Lang.Class({
returnValue = ''; returnValue = '';
success = true; success = true;
} catch (e) { } catch (e) {
returnValue = JSON.stringify(e); returnValue = '' + e;
success = false; success = false;
} }
return [success, returnValue]; return [success, returnValue];
@ -129,11 +132,16 @@ const GnomeShell = new Lang.Class({
for (let param in params) for (let param in params)
params[param] = params[param].deep_unpack(); params[param] = params[param].deep_unpack();
let monitorIndex = -1;
if (params['monitor'])
monitorIndex = params['monitor'];
let icon = null; let icon = null;
if (params['icon']) if (params['icon'])
icon = Gio.Icon.new_for_string(params['icon']); icon = Gio.Icon.new_for_string(params['icon']);
Main.osdWindow.setIcon(icon); Main.osdWindow.setIcon(icon);
Main.osdWindow.setMonitor (monitorIndex);
Main.osdWindow.setLabel(params['label']); Main.osdWindow.setLabel(params['label']);
Main.osdWindow.setLevel(params['level']); Main.osdWindow.setLevel(params['level']);
@ -219,9 +227,8 @@ const GnomeShell = new Lang.Class({
}, },
_onGrabberBusNameVanished: function(connection, name) { _onGrabberBusNameVanished: function(connection, name) {
let grabs = this._grabbedAccelerators.items(); let grabs = this._grabbedAccelerators.entries();
for (let i = 0; i < grabs.length; i++) { for (let [action, sender] of grabs) {
let [action, sender] = grabs[i];
if (sender == name) if (sender == name)
this._ungrabAccelerator(action); this._ungrabAccelerator(action);
} }
@ -246,41 +253,43 @@ const GnomeShell = new Lang.Class({
ShellVersion: Config.PACKAGE_VERSION ShellVersion: Config.PACKAGE_VERSION
}); });
const GnomeShellExtensionsIface = <interface name="org.gnome.Shell.Extensions"> const GnomeShellExtensionsIface = '<node> \
<method name="ListExtensions"> <interface name="org.gnome.Shell.Extensions"> \
<arg type="a{sa{sv}}" direction="out" name="extensions" /> <method name="ListExtensions"> \
</method> <arg type="a{sa{sv}}" direction="out" name="extensions" /> \
<method name="GetExtensionInfo"> </method> \
<arg type="s" direction="in" name="extension" /> <method name="GetExtensionInfo"> \
<arg type="a{sv}" direction="out" name="info" /> <arg type="s" direction="in" name="extension" /> \
</method> <arg type="a{sv}" direction="out" name="info" /> \
<method name="GetExtensionErrors"> </method> \
<arg type="s" direction="in" name="extension" /> <method name="GetExtensionErrors"> \
<arg type="as" direction="out" name="errors" /> <arg type="s" direction="in" name="extension" /> \
</method> <arg type="as" direction="out" name="errors" /> \
<signal name="ExtensionStatusChanged"> </method> \
<arg type="s" name="uuid"/> <signal name="ExtensionStatusChanged"> \
<arg type="i" name="state"/> <arg type="s" name="uuid"/> \
<arg type="s" name="error"/> <arg type="i" name="state"/> \
</signal> <arg type="s" name="error"/> \
<method name="InstallRemoteExtension"> </signal> \
<arg type="s" direction="in" name="uuid"/> <method name="InstallRemoteExtension"> \
<arg type="s" direction="out" name="result"/> <arg type="s" direction="in" name="uuid"/> \
</method> <arg type="s" direction="out" name="result"/> \
<method name="UninstallExtension"> </method> \
<arg type="s" direction="in" name="uuid"/> <method name="UninstallExtension"> \
<arg type="b" direction="out" name="success"/> <arg type="s" direction="in" name="uuid"/> \
</method> <arg type="b" direction="out" name="success"/> \
<method name="LaunchExtensionPrefs"> </method> \
<arg type="s" direction="in" name="uuid"/> <method name="LaunchExtensionPrefs"> \
</method> <arg type="s" direction="in" name="uuid"/> \
<method name="ReloadExtension"> </method> \
<arg type="s" direction="in" name="uuid"/> <method name="ReloadExtension"> \
</method> <arg type="s" direction="in" name="uuid"/> \
<method name="CheckForUpdates"> </method> \
</method> <method name="CheckForUpdates"> \
<property name="ShellVersion" type="s" access="read" /> </method> \
</interface>; <property name="ShellVersion" type="s" access="read" /> \
</interface> \
</node>';
const GnomeShellExtensions = new Lang.Class({ const GnomeShellExtensions = new Lang.Class({
Name: 'GnomeShellExtensionsDBus', Name: 'GnomeShellExtensionsDBus',

View File

@ -36,7 +36,7 @@ const EntryMenu = new Lang.Class({
this._passwordItem = null; this._passwordItem = null;
Main.uiGroup.add_actor(this.actor); Main.layoutManager.menuGroup.add_actor(this.actor);
this.actor.hide(); this.actor.hide();
}, },
@ -132,14 +132,14 @@ function _setMenuAlignment(entry, stageX) {
function _onButtonPressEvent(actor, event, entry) { function _onButtonPressEvent(actor, event, entry) {
if (entry.menu.isOpen) { if (entry.menu.isOpen) {
entry.menu.close(BoxPointer.PopupAnimation.FULL); entry.menu.close(BoxPointer.PopupAnimation.FULL);
return true; return Clutter.EVENT_STOP;
} else if (event.get_button() == 3) { } else if (event.get_button() == 3) {
let [stageX, stageY] = event.get_coords(); let [stageX, stageY] = event.get_coords();
_setMenuAlignment(entry, stageX); _setMenuAlignment(entry, stageX);
entry.menu.open(BoxPointer.PopupAnimation.FULL); entry.menu.open(BoxPointer.PopupAnimation.FULL);
return true; return Clutter.EVENT_STOP;
} }
return false; return Clutter.EVENT_PROPAGATE;
}; };
function _onPopup(actor, entry) { function _onPopup(actor, entry) {

View File

@ -521,36 +521,38 @@ const ShellProcessesDialog = new Lang.Class({
}); });
Signals.addSignalMethods(ShellProcessesDialog.prototype); Signals.addSignalMethods(ShellProcessesDialog.prototype);
const GnomeShellMountOpIface = <interface name="org.Gtk.MountOperationHandler"> const GnomeShellMountOpIface = '<node> \
<method name="AskPassword"> <interface name="org.Gtk.MountOperationHandler"> \
<arg type="s" direction="in" name="object_id"/> <method name="AskPassword"> \
<arg type="s" direction="in" name="message"/> <arg type="s" direction="in" name="object_id"/> \
<arg type="s" direction="in" name="icon_name"/> <arg type="s" direction="in" name="message"/> \
<arg type="s" direction="in" name="default_user"/> <arg type="s" direction="in" name="icon_name"/> \
<arg type="s" direction="in" name="default_domain"/> <arg type="s" direction="in" name="default_user"/> \
<arg type="u" direction="in" name="flags"/> <arg type="s" direction="in" name="default_domain"/> \
<arg type="u" direction="out" name="response"/> <arg type="u" direction="in" name="flags"/> \
<arg type="a{sv}" direction="out" name="response_details"/> <arg type="u" direction="out" name="response"/> \
</method> <arg type="a{sv}" direction="out" name="response_details"/> \
<method name="AskQuestion"> </method> \
<arg type="s" direction="in" name="object_id"/> <method name="AskQuestion"> \
<arg type="s" direction="in" name="message"/> <arg type="s" direction="in" name="object_id"/> \
<arg type="s" direction="in" name="icon_name"/> <arg type="s" direction="in" name="message"/> \
<arg type="as" direction="in" name="choices"/> <arg type="s" direction="in" name="icon_name"/> \
<arg type="u" direction="out" name="response"/> <arg type="as" direction="in" name="choices"/> \
<arg type="a{sv}" direction="out" name="response_details"/> <arg type="u" direction="out" name="response"/> \
</method> <arg type="a{sv}" direction="out" name="response_details"/> \
<method name="ShowProcesses"> </method> \
<arg type="s" direction="in" name="object_id"/> <method name="ShowProcesses"> \
<arg type="s" direction="in" name="message"/> <arg type="s" direction="in" name="object_id"/> \
<arg type="s" direction="in" name="icon_name"/> <arg type="s" direction="in" name="message"/> \
<arg type="ai" direction="in" name="application_pids"/> <arg type="s" direction="in" name="icon_name"/> \
<arg type="as" direction="in" name="choices"/> <arg type="ai" direction="in" name="application_pids"/> \
<arg type="u" direction="out" name="response"/> <arg type="as" direction="in" name="choices"/> \
<arg type="a{sv}" direction="out" name="response_details"/> <arg type="u" direction="out" name="response"/> \
</method> <arg type="a{sv}" direction="out" name="response_details"/> \
<method name="Close"/> </method> \
</interface>; <method name="Close"/> \
</interface> \
</node>';
const ShellMountOperationType = { const ShellMountOperationType = {
NONE: 0, NONE: 0,

View File

@ -111,12 +111,12 @@ const Slider = new Lang.Class({
}, },
_startDragging: function(actor, event) { _startDragging: function(actor, event) {
this.startDragging(event); return this.startDragging(event);
}, },
startDragging: function(event) { startDragging: function(event) {
if (this._dragging) if (this._dragging)
return false; return Clutter.EVENT_PROPAGATE;
this._dragging = true; this._dragging = true;
@ -129,7 +129,7 @@ const Slider = new Lang.Class({
let absX, absY; let absX, absY;
[absX, absY] = event.get_coords(); [absX, absY] = event.get_coords();
this._moveHandle(absX, absY); this._moveHandle(absX, absY);
return true; return Clutter.EVENT_STOP;
}, },
_endDragging: function() { _endDragging: function() {
@ -143,7 +143,7 @@ const Slider = new Lang.Class({
this.emit('drag-end'); this.emit('drag-end');
} }
return true; return Clutter.EVENT_STOP;
}, },
scroll: function(event) { scroll: function(event) {
@ -151,7 +151,7 @@ const Slider = new Lang.Class({
let delta; let delta;
if (event.is_pointer_emulated()) if (event.is_pointer_emulated())
return; return Clutter.EVENT_PROPAGATE;
if (direction == Clutter.ScrollDirection.DOWN) { if (direction == Clutter.ScrollDirection.DOWN) {
delta = -SLIDER_SCROLL_STEP; delta = -SLIDER_SCROLL_STEP;
@ -168,17 +168,18 @@ const Slider = new Lang.Class({
this.actor.queue_repaint(); this.actor.queue_repaint();
this.emit('value-changed', this._value); this.emit('value-changed', this._value);
return Clutter.EVENT_STOP;
}, },
_onScrollEvent: function(actor, event) { _onScrollEvent: function(actor, event) {
this.scroll(event); return this.scroll(event);
}, },
_motionEvent: function(actor, event) { _motionEvent: function(actor, event) {
let absX, absY; let absX, absY;
[absX, absY] = event.get_coords(); [absX, absY] = event.get_coords();
this._moveHandle(absX, absY); this._moveHandle(absX, absY);
return true; return Clutter.EVENT_STOP;
}, },
onKeyPressEvent: function (actor, event) { onKeyPressEvent: function (actor, event) {
@ -189,9 +190,9 @@ const Slider = new Lang.Class({
this.actor.queue_repaint(); this.actor.queue_repaint();
this.emit('value-changed', this._value); this.emit('value-changed', this._value);
this.emit('drag-end'); this.emit('drag-end');
return true; return Clutter.EVENT_STOP;
} }
return false; return Clutter.EVENT_PROPAGATE;
}, },
_moveHandle: function(absX, absY) { _moveHandle: function(absX, absY) {

View File

@ -2,6 +2,7 @@
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
const St = imports.gi.St; const St = imports.gi.St;
@ -43,9 +44,7 @@ const ATIndicator = new Lang.Class({
this._hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' }); this._hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
this._hbox.add_child(new St.Icon({ style_class: 'system-status-icon', this._hbox.add_child(new St.Icon({ style_class: 'system-status-icon',
icon_name: 'preferences-desktop-accessibility-symbolic' })); icon_name: 'preferences-desktop-accessibility-symbolic' }));
this._hbox.add_child(new St.Label({ text: '\u25BE', this._hbox.add_child(PopupMenu.unicodeArrow(St.Side.BOTTOM));
y_expand: true,
y_align: Clutter.ActorAlign.CENTER }));
this.actor.add_child(this._hbox); this.actor.add_child(this._hbox);
@ -96,7 +95,7 @@ const ATIndicator = new Lang.Class({
this.actor.visible = alwaysShow || items.some(function(f) { return !!f.state; }); this.actor.visible = alwaysShow || items.some(function(f) { return !!f.state; });
return false; return GLib.SOURCE_REMOVE;
}, },
_queueSyncMenuVisibility: function() { _queueSyncMenuVisibility: function() {

View File

@ -2,17 +2,26 @@
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const GnomeBluetoothApplet = imports.gi.GnomeBluetoothApplet; const Gio = imports.gi.Gio;
const GnomeBluetooth = imports.gi.GnomeBluetooth; const GnomeBluetooth = imports.gi.GnomeBluetooth;
const Lang = imports.lang; const Lang = imports.lang;
const St = imports.gi.St; const St = imports.gi.St;
const Main = imports.ui.main; const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const NotificationDaemon = imports.ui.notificationDaemon;
const PanelMenu = imports.ui.panelMenu; const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
const BUS_NAME = 'org.gnome.SettingsDaemon.Rfkill';
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Rfkill';
const RfkillManagerInterface = '<node> \
<interface name="org.gnome.SettingsDaemon.Rfkill"> \
<property name="BluetoothAirplaneMode" type="b" access="readwrite" /> \
</interface> \
</node>';
const RfkillManagerProxy = Gio.DBusProxy.makeProxyWrapper(RfkillManagerInterface);
const Indicator = new Lang.Class({ const Indicator = new Lang.Class({
Name: 'BTIndicator', Name: 'BTIndicator',
Extends: PanelMenu.SystemIndicator, Extends: PanelMenu.SystemIndicator,
@ -23,237 +32,69 @@ const Indicator = new Lang.Class({
this._indicator = this._addIndicator(); this._indicator = this._addIndicator();
this._indicator.icon_name = 'bluetooth-active-symbolic'; this._indicator.icon_name = 'bluetooth-active-symbolic';
this._proxy = new RfkillManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
Lang.bind(this, function(proxy, error) {
if (error) {
log(error.message);
return;
}
}));
// The Bluetooth menu only appears when Bluetooth is in use, // The Bluetooth menu only appears when Bluetooth is in use,
// so just statically build it with a "Turn Off" menu item. // so just statically build it with a "Turn Off" menu item.
this._item = new PopupMenu.PopupSubMenuMenuItem(_("Bluetooth"), true); this._item = new PopupMenu.PopupSubMenuMenuItem(_("Bluetooth"), true);
this._item.icon.icon_name = 'bluetooth-active-symbolic'; this._item.icon.icon_name = 'bluetooth-active-symbolic';
this._item.menu.addAction(_("Turn Off"), Lang.bind(this, function() { this._item.menu.addAction(_("Turn Off"), Lang.bind(this, function() {
this._applet.killswitch_state = GnomeBluetooth.KillswitchState.SOFT_BLOCKED; this._proxy.BluetoothAirplaneMode = true;
})); }));
this._item.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop'); this._item.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop');
this.menu.addMenuItem(this._item); this.menu.addMenuItem(this._item);
this._applet = new GnomeBluetoothApplet.Applet(); this._client = new GnomeBluetooth.Client();
this._applet.connect('devices-changed', Lang.bind(this, this._sync)); this._model = this._client.get_model();
this._model.connect('row-changed', Lang.bind(this, this._sync));
this._model.connect('row-deleted', Lang.bind(this, this._sync));
this._model.connect('row-inserted', Lang.bind(this, this._sync));
this._sync(); this._sync();
},
this._applet.connect('pincode-request', Lang.bind(this, this._pinRequest)); _getDefaultAdapter: function() {
this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest)); let [ret, iter] = this._model.get_iter_first();
this._applet.connect('auth-request', Lang.bind(this, this._authRequest)); while (ret) {
this._applet.connect('auth-service-request', Lang.bind(this, this._authServiceRequest)); let isDefault = this._model.get_value(iter,
this._applet.connect('cancel-request', Lang.bind(this, this._cancelRequest)); GnomeBluetooth.Column.DEFAULT);
if (isDefault)
return iter;
ret = this._model.iter_next(iter);
}
return null;
},
_getNConnectedDevices: function() {
let adapter = this._getDefaultAdapter();
if (!adapter)
return 0;
let nDevices = 0;
let [ret, iter] = this._model.iter_children(adapter);
while (ret) {
let isConnected = this._model.get_value(iter,
GnomeBluetooth.Column.CONNECTED);
if (isConnected)
nDevices++;
ret = this._model.iter_next(iter);
}
return nDevices;
}, },
_sync: function() { _sync: function() {
let connectedDevices = this._applet.get_devices().filter(function(device) { let nDevices = this._getNConnectedDevices();
return device.connected;
});
let nDevices = connectedDevices.length;
let on = nDevices > 0; let on = nDevices > 0;
this._indicator.visible = on; this._indicator.visible = on;
this._item.actor.visible = on; this._item.actor.visible = on;
if (on) if (on)
this._item.status.text = ngettext("%d Connected Device", "%d Connected Devices").format(nDevices); this._item.status.text = ngettext("%d Connected Device", "%d Connected Devices", nDevices).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) {
this._ensureSource();
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) {
this._ensureSource();
this._source.notify(new ConfirmNotification(this._source, this._applet, device_path, name, long_name, pin));
},
_pinRequest: function(applet, device_path, name, long_name, numeric) {
this._ensureSource();
this._source.notify(new PinNotification(this._source, this._applet, device_path, name, long_name, numeric));
},
_cancelRequest: function() {
this._source.destroy();
}
});
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"),
_("Authorization request from %s").format(name),
{ customContent: true });
this.setResident(true);
this._applet = applet;
this._devicePath = device_path;
this.addBody(_("Device %s wants access to the service '%s'").format(long_name, uuid));
this.addButton('always-grant', _("Always grant access"));
this.addButton('grant', _("Grant this time only"));
this.addButton('reject', _("Reject"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
switch (action) {
case 'always-grant':
this._applet.agent_reply_auth_service(this._devicePath, true, true);
break;
case 'grant':
this._applet.agent_reply_auth_service(this._devicePath, true, false);
break;
case 'reject':
default:
this._applet.agent_reply_auth_service(this._devicePath, false, false);
}
this.destroy();
}));
}
});
const ConfirmNotification = new Lang.Class({
Name: 'ConfirmNotification',
Extends: MessageTray.Notification,
_init: function(source, applet, device_path, name, long_name, pin) {
this.parent(source,
_("Bluetooth"),
/* Translators: argument is the device short name */
_("Pairing confirmation for %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.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"));
this.addButton('does-not-match', _("Does not match"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
if (action == 'matches')
this._applet.agent_reply_confirm(this._devicePath, true);
else
this._applet.agent_reply_confirm(this._devicePath, false);
this.destroy();
}));
}
});
const PinNotification = new Lang.Class({
Name: 'PinNotification',
Extends: MessageTray.Notification,
_init: function(source, applet, device_path, name, long_name, numeric) {
this.parent(source,
_("Bluetooth"),
_("Pairing request for %s").format(name),
{ customContent: true });
this.setResident(true);
this._applet = applet;
this._devicePath = device_path;
this._numeric = numeric;
this.addBody(_("Device %s wants to pair with this computer").format(long_name));
this.addBody(_("Please enter the PIN mentioned on the device."));
this._entry = new St.Entry();
this._entry.connect('key-release-event', Lang.bind(this, function(entry, event) {
let key = event.get_key_symbol();
if (key == Clutter.KEY_Return) {
if (this._canActivateOkButton())
this.emit('action-invoked', 'ok');
return true;
} else if (key == Clutter.KEY_Escape) {
this.emit('action-invoked', 'cancel');
return true;
}
return false;
}));
this.addActor(this._entry);
this.addButton('ok', _("OK"));
this.addButton('cancel', _("Cancel"));
this.setButtonSensitive('ok', this._canActivateOkButton());
this._entry.clutter_text.connect('text-changed', Lang.bind(this,
function() {
this.setButtonSensitive('ok', this._canActivateOkButton());
}));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
if (action == 'ok') {
if (this._numeric) {
let num = parseInt(this._entry.text);
if (isNaN(num)) {
// user reply was empty, or was invalid
// cancel the operation
num = -1;
}
this._applet.agent_reply_passkey(this._devicePath, num);
} else
this._applet.agent_reply_pincode(this._devicePath, this._entry.text);
} else {
if (this._numeric)
this._applet.agent_reply_passkey(this._devicePath, -1);
else
this._applet.agent_reply_pincode(this._devicePath, null);
}
this.destroy();
}));
},
_canActivateOkButton: function() {
// PINs have a fixed length of 6
if (this._numeric)
return this._entry.clutter_text.text.length == 6;
else
return true;
}
}); });

View File

@ -11,9 +11,11 @@ const Slider = imports.ui.slider;
const BUS_NAME = 'org.gnome.SettingsDaemon.Power'; const BUS_NAME = 'org.gnome.SettingsDaemon.Power';
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power'; const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
const BrightnessInterface = <interface name="org.gnome.SettingsDaemon.Power.Screen"> const BrightnessInterface = '<node> \
<property name='Brightness' type='i' access='readwrite'/> <interface name="org.gnome.SettingsDaemon.Power.Screen"> \
</interface>; <property name="Brightness" type="i" access="readwrite"/> \
</interface> \
</node>';
const BrightnessProxy = Gio.DBusProxy.makeProxyWrapper(BrightnessInterface); const BrightnessProxy = Gio.DBusProxy.makeProxyWrapper(BrightnessInterface);
@ -46,7 +48,7 @@ const Indicator = new Lang.Class({
this._item.actor.add(icon); this._item.actor.add(icon);
this._item.actor.add(this._slider.actor, { expand: true }); this._item.actor.add(this._slider.actor, { expand: true });
this._item.actor.connect('button-press-event', Lang.bind(this, function(actor, event) { this._item.actor.connect('button-press-event', Lang.bind(this, function(actor, event) {
this._slider.startDragging(event); return this._slider.startDragging(event);
})); }));
this._item.actor.connect('key-press-event', Lang.bind(this, function(actor, event) { this._item.actor.connect('key-press-event', Lang.bind(this, function(actor, event) {
return this._slider.onKeyPressEvent(actor, event); return this._slider.onKeyPressEvent(actor, event);

View File

@ -41,11 +41,13 @@ const MAX_INPUT_SOURCE_ACTIVATION_TIME = 4000; // ms
const BUS_NAME = 'org.gnome.SettingsDaemon.Keyboard'; const BUS_NAME = 'org.gnome.SettingsDaemon.Keyboard';
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Keyboard'; const OBJECT_PATH = '/org/gnome/SettingsDaemon/Keyboard';
const KeyboardManagerInterface = <interface name="org.gnome.SettingsDaemon.Keyboard"> const KeyboardManagerInterface = '<node> \
<method name="SetInputSource"> <interface name="org.gnome.SettingsDaemon.Keyboard"> \
<arg type="u" direction="in" /> <method name="SetInputSource"> \
</method> <arg type="u" direction="in" /> \
</interface>; </method> \
</interface> \
</node>';
const KeyboardManagerProxy = Gio.DBusProxy.makeProxyWrapper(KeyboardManagerInterface); const KeyboardManagerProxy = Gio.DBusProxy.makeProxyWrapper(KeyboardManagerInterface);
@ -339,9 +341,7 @@ const InputSourceIndicator = new Lang.Class({
this._hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' }); this._hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
this._hbox.add_child(this._container); this._hbox.add_child(this._container);
this._hbox.add_child(new St.Label({ text: '\u25BE', this._hbox.add_child(PopupMenu.unicodeArrow(St.Side.BOTTOM));
y_expand: true,
y_align: Clutter.ActorAlign.CENTER }));
this.actor.add_child(this._hbox); this.actor.add_child(this._hbox);
this.actor.add_style_class_name('panel-status-button'); this.actor.add_style_class_name('panel-status-button');

58
js/ui/status/location.js Normal file
View File

@ -0,0 +1,58 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const PanelMenu = imports.ui.panelMenu;
var GeoclueIface = '<node> \
<interface name="org.freedesktop.GeoClue2.Manager"> \
<property name="InUse" type="b" access="read"/> \
</interface> \
</node>';
const GeoclueManager = Gio.DBusProxy.makeProxyWrapper(GeoclueIface);
const Indicator = new Lang.Class({
Name: 'LocationIndicator',
Extends: PanelMenu.SystemIndicator,
_init: function() {
this.parent();
this._indicator = this._addIndicator();
this._indicator.icon_name = 'find-location-symbolic';
this._sync();
this._watchId = Gio.bus_watch_name(Gio.BusType.SYSTEM,
'org.freedesktop.GeoClue2',
0,
Lang.bind(this, this._onGeoclueAppeared),
Lang.bind(this, this._onGeoclueVanished));
},
_sync: function() {
if (this._proxy == null) {
this._indicator.visible = false;
return;
}
this._indicator.visible = this._proxy.InUse;
},
_onGeoclueAppeared: function() {
// FIXME: This should be done async
this._proxy = new GeoclueManager(Gio.DBus.system,
'org.freedesktop.GeoClue2',
'/org/freedesktop/GeoClue2/Manager');
this._proxy.connect('g-properties-changed', Lang.bind(this, this._sync));
this._sync();
},
_onGeoclueVanished: function() {
this._proxy = null;
this._sync();
}
});

View File

@ -11,18 +11,17 @@ const NMGtk = imports.gi.NMGtk;
const Signals = imports.signals; const Signals = imports.signals;
const St = imports.gi.St; const St = imports.gi.St;
const Hash = imports.misc.hash;
const Main = imports.ui.main; const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu; const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
const MessageTray = imports.ui.messageTray; const MessageTray = imports.ui.messageTray;
const NotificationDaemon = imports.ui.notificationDaemon;
const ModalDialog = imports.ui.modalDialog; const ModalDialog = imports.ui.modalDialog;
const ModemManager = imports.misc.modemManager; const ModemManager = imports.misc.modemManager;
const Util = imports.misc.util; const Util = imports.misc.util;
const NMConnectionCategory = { const NMConnectionCategory = {
INVALID: 'invalid', INVALID: 'invalid',
WIRED: 'wired',
WIRELESS: 'wireless', WIRELESS: 'wireless',
WWAN: 'wwan', WWAN: 'wwan',
VPN: 'vpn' VPN: 'vpn'
@ -73,6 +72,27 @@ function ssidToLabel(ssid) {
return label; return label;
} }
function ensureActiveConnectionProps(active, settings) {
if (!active._connection) {
active._connection = settings.get_connection_by_path(active.connection);
// This list is guaranteed to have only one device in it.
let device = active.get_devices()[0]._delegate;
active._primaryDevice = device;
}
}
function createSettingsAction(label, device) {
let item = new PopupMenu.PopupMenuItem(label);
item.connect('activate', function() {
Util.spawnApp(['gnome-control-center', 'network', 'show-device',
device.get_path()]);
});
return item;
}
const NMConnectionItem = new Lang.Class({ const NMConnectionItem = new Lang.Class({
Name: 'NMConnectionItem', Name: 'NMConnectionItem',
@ -156,7 +176,7 @@ const NMConnectionSection = new Lang.Class({
_init: function(client) { _init: function(client) {
this._client = client; this._client = client;
this._connectionItems = new Hash.Map(); this._connectionItems = new Map();
this._connections = []; this._connections = [];
this._labelSection = new PopupMenu.PopupMenuSection(); this._labelSection = new PopupMenu.PopupMenuSection();
@ -170,12 +190,11 @@ const NMConnectionSection = new Lang.Class({
}, },
destroy: function() { destroy: function() {
this.statusItem.destroy(); this.item.destroy();
this.section.destroy();
}, },
_sync: function() { _sync: function() {
let nItems = this._connectionItems.size(); let nItems = this._connectionItems.size;
this._switchSection.actor.visible = (nItems > 1); this._switchSection.actor.visible = (nItems > 1);
this._labelSection.actor.visible = (nItems == 1); this._labelSection.actor.visible = (nItems == 1);
@ -194,8 +213,7 @@ const NMConnectionSection = new Lang.Class({
_getStatus: function() { _getStatus: function() {
let values = this._connectionItems.values(); let values = this._connectionItems.values();
for (let i = 0; i < values.length; i++) { for (let item of values) {
let item = values[i];
if (item.isActive()) if (item.isActive())
return item.getName(); return item.getName();
} }
@ -268,17 +286,22 @@ const NMConnectionDevice = new Lang.Class({
Extends: NMConnectionSection, Extends: NMConnectionSection,
Abstract: true, Abstract: true,
_init: function(client, device) { _init: function(client, device, settings) {
this.parent(client); this.parent(client);
this._device = device; this._device = device;
this._settings = settings;
this._autoConnectItem = this.item.menu.addAction(_("Connect"), Lang.bind(this, this._autoConnect)); this._autoConnectItem = this.item.menu.addAction(_("Connect"), Lang.bind(this, this._autoConnect));
this.item.menu.addSettingsAction(_("Network Settings"), 'gnome-network-panel.desktop');
this._stateChangedId = this._device.connect('state-changed', Lang.bind(this, this._deviceStateChanged)); this._stateChangedId = this._device.connect('state-changed', Lang.bind(this, this._deviceStateChanged));
this._activeConnectionChangedId = this._device.connect('notify::active-connection', Lang.bind(this, this._activeConnectionChanged)); this._activeConnectionChangedId = this._device.connect('notify::active-connection', Lang.bind(this, this._activeConnectionChanged));
}, },
_autoConnect: function() {
let connection = new NetworkManager.Connection();
this._client.add_and_activate_connection(connection, this._device, null, null);
},
destroy: function() { destroy: function() {
if (this._stateChangedId) { if (this._stateChangedId) {
GObject.Object.prototype.disconnect.call(this._device, this._stateChangedId); GObject.Object.prototype.disconnect.call(this._device, this._stateChangedId);
@ -286,7 +309,7 @@ const NMConnectionDevice = new Lang.Class({
} }
if (this._activeConnectionChangedId) { if (this._activeConnectionChangedId) {
GObject.Object.prototype.disconnect.call(this._device, this._activeConnectionChangedId); GObject.Object.prototype.disconnect.call(this._device, this._activeConnectionChangedId);
this._stateChangedId = 0; this._activeConnectionChangedId = 0;
} }
this.parent(); this.parent();
@ -301,6 +324,7 @@ const NMConnectionDevice = new Lang.Class({
this._activeConnection = this._device.active_connection; this._activeConnection = this._device.active_connection;
if (this._activeConnection) { if (this._activeConnection) {
ensureActiveConnectionProps(this._activeConnection, this._settings);
let item = this._connectionItems.get(this._activeConnection._connection.get_uuid()); let item = this._connectionItems.get(this._activeConnection._connection.get_uuid());
item.setActiveConnection(this._activeConnection); item.setActiveConnection(this._activeConnection);
} }
@ -345,7 +369,7 @@ const NMConnectionDevice = new Lang.Class({
}, },
_sync: function() { _sync: function() {
let nItems = this._connectionItems.size(); let nItems = this._connectionItems.size;
this._autoConnectItem.actor.visible = (nItems == 0); this._autoConnectItem.actor.visible = (nItems == 0);
this.parent(); this.parent();
}, },
@ -356,8 +380,9 @@ const NMConnectionDevice = new Lang.Class({
switch(this._device.state) { switch(this._device.state) {
case NetworkManager.DeviceState.DISCONNECTED: case NetworkManager.DeviceState.DISCONNECTED:
return _("Off");
case NetworkManager.DeviceState.ACTIVATED: case NetworkManager.DeviceState.ACTIVATED:
return ''; return this.parent();
case NetworkManager.DeviceState.UNMANAGED: case NetworkManager.DeviceState.UNMANAGED:
/* Translators: this is for network devices that are physically present but are not /* Translators: this is for network devices that are physically present but are not
under NetworkManager's control (and thus cannot be used in the menu) */ under NetworkManager's control (and thus cannot be used in the menu) */
@ -394,13 +419,58 @@ const NMConnectionDevice = new Lang.Class({
}, },
}); });
const NMDeviceWired = new Lang.Class({
Name: 'NMDeviceWired',
Extends: NMConnectionDevice,
category: NMConnectionCategory.WIRED,
_init: function(client, device, settings) {
this.parent(client, device, settings);
this.item.menu.addMenuItem(createSettingsAction(_("Wired Settings"), device));
},
_isConnected: function() {
if (!this._device.active_connection)
return false;
let state = this._device.active_connection.state;
return state >= NetworkManager.ActiveConnectionState.ACTIVATING;
},
_sync: function() {
this.item.actor.visible = this._isConnected();
this.parent();
},
_getMenuIcon: function() {
if (this._device.active_connection)
return this.getIndicatorIcon();
else
return 'network-wired-disconnected-symbolic';
},
getIndicatorIcon: function() {
let state = this._device.active_connection.state;
if (state == NetworkManager.ActiveConnectionState.ACTIVATING)
return 'network-wired-acquiring-symbolic';
else if (state == NetworkManager.ActiveConnectionState.ACTIVATED)
return 'network-wired-symbolic';
else
return 'network-wired-disconnected-symbolic';
}
});
const NMDeviceModem = new Lang.Class({ const NMDeviceModem = new Lang.Class({
Name: 'NMDeviceModem', Name: 'NMDeviceModem',
Extends: NMConnectionDevice, Extends: NMConnectionDevice,
category: NMConnectionCategory.WWAN, category: NMConnectionCategory.WWAN,
_init: function(client, device) { _init: function(client, device, settings) {
this.parent(client, device); this.parent(client, device, settings);
this.item.menu.addMenuItem(createSettingsAction(_("Mobile Broadband Settings"), device));
this._mobileDevice = null; this._mobileDevice = null;
let capabilities = device.current_capabilities; let capabilities = device.current_capabilities;
@ -414,16 +484,7 @@ const NMDeviceModem = new Lang.Class({
this._mobileDevice = new ModemManager.ModemGsm(device.udi); this._mobileDevice = new ModemManager.ModemGsm(device.udi);
if (this._mobileDevice) { if (this._mobileDevice) {
this._operatorNameId = this._mobileDevice.connect('notify::operator-name', Lang.bind(this, function() { this._operatorNameId = this._mobileDevice.connect('notify::operator-name', Lang.bind(this, this._sync));
if (this._operatorItem) {
let name = this._mobileDevice.operator_name;
if (name) {
this._operatorItem.label.text = name;
this._operatorItem.actor.show();
} else
this._operatorItem.actor.hide();
}
}));
this._signalQualityId = this._mobileDevice.connect('notify::signal-quality', Lang.bind(this, function() { this._signalQualityId = this._mobileDevice.connect('notify::signal-quality', Lang.bind(this, function() {
this.emit('icon-changed'); this.emit('icon-changed');
})); }));
@ -448,6 +509,20 @@ const NMDeviceModem = new Lang.Class({
this.parent(); this.parent();
}, },
_getStatus: function() {
if (!this._client.wwan_hardware_enabled)
return _("Hardware Disabled");
else if (!this._client.wwan_enabled)
/* Translators: this is for a network device that cannot be activated
because it's disabled by rfkill (airplane mode) */
return _("Disabled");
else if (this._device.state == NetworkManager.DeviceState.ACTIVATED &&
this._mobileDevice && this._mobileDevice.operator_name)
return this._mobileDevice.operator_name;
else
return this.parent();
},
_getMenuIcon: function() { _getMenuIcon: function() {
if (this._device.active_connection) if (this._device.active_connection)
return this.getIndicatorIcon(); return this.getIndicatorIcon();
@ -477,16 +552,10 @@ const NMDeviceBluetooth = new Lang.Class({
Extends: NMConnectionDevice, Extends: NMConnectionDevice,
category: NMConnectionCategory.WWAN, category: NMConnectionCategory.WWAN,
_autoConnect: function() { _init: function(client, device, settings) {
// FIXME: DUN devices are configured like modems, so this.parent(client, device, settings);
// We need to spawn the mobile wizard
// but the network panel doesn't support bluetooth at the moment
// so we just create an empty connection and hope
// that this phone supports PAN
let connection = new NetworkManager.Connection(); this.item.menu.addMenuItem(createSettingsAction(_("Mobile Broadband Settings"), device));
this._client.add_and_activate_connection(connection, this._device, null, null);
return true;
}, },
_getMenuIcon: function() { _getMenuIcon: function() {
@ -514,31 +583,30 @@ const NMWirelessDialogItem = new Lang.Class({
this._network = network; this._network = network;
this._ap = network.accessPoints[0]; this._ap = network.accessPoints[0];
this.actor = new St.Button({ style_class: 'nm-dialog-item', this.actor = new St.BoxLayout({ style_class: 'nm-dialog-item',
can_focus: true, can_focus: true,
x_fill: true }); reactive: true });
this.actor.connect('key-focus-in', Lang.bind(this, function() { this.actor.connect('key-focus-in', Lang.bind(this, function() {
this.emit('selected'); this.emit('selected');
})); }));
this.actor.connect('clicked', Lang.bind(this, function() { let action = new Clutter.ClickAction();
action.connect('clicked', Lang.bind(this, function() {
this.actor.grab_key_focus(); this.actor.grab_key_focus();
})); }));
this.actor.add_action(action);
this._content = new St.BoxLayout({ style_class: 'nm-dialog-item-box' });
this.actor.set_child(this._content);
let title = ssidToLabel(this._ap.get_ssid()); let title = ssidToLabel(this._ap.get_ssid());
this._label = new St.Label({ text: title }); this._label = new St.Label({ text: title });
this.actor.label_actor = this._label; this.actor.label_actor = this._label;
this._content.add(this._label, { x_align: St.Align.START }); this.actor.add(this._label, { x_align: St.Align.START });
this._selectedIcon = new St.Icon({ style_class: 'nm-dialog-icon', this._selectedIcon = new St.Icon({ style_class: 'nm-dialog-icon',
icon_name: 'object-select-symbolic' }); icon_name: 'object-select-symbolic' });
this._content.add(this._selectedIcon); this.actor.add(this._selectedIcon);
this._icons = new St.BoxLayout({ style_class: 'nm-dialog-icons' }); this._icons = new St.BoxLayout({ style_class: 'nm-dialog-icons' });
this._content.add(this._icons, { expand: true, x_fill: false, x_align: St.Align.END }); this.actor.add(this._icons, { expand: true, x_fill: false, x_align: St.Align.END });
this._secureIcon = new St.Icon({ style_class: 'nm-dialog-icon' }); this._secureIcon = new St.Icon({ style_class: 'nm-dialog-icon' });
if (this._ap._secType != NMAccessPointSecurity.NONE) if (this._ap._secType != NMAccessPointSecurity.NONE)
@ -547,18 +615,24 @@ const NMWirelessDialogItem = new Lang.Class({
this._signalIcon = new St.Icon({ style_class: 'nm-dialog-icon' }); this._signalIcon = new St.Icon({ style_class: 'nm-dialog-icon' });
this._icons.add_actor(this._signalIcon); this._icons.add_actor(this._signalIcon);
this._sync();
},
_sync: function() {
this._signalIcon.icon_name = this._getSignalIcon();
}, },
updateBestAP: function(ap) { updateBestAP: function(ap) {
this._ap = ap; this._ap = ap;
this._signalIcon.icon_name = this._getIcon(); this._sync();
}, },
setActive: function(isActive) { setActive: function(isActive) {
this._selectedIcon.opacity = isActive ? 255 : 0; this._selectedIcon.opacity = isActive ? 255 : 0;
}, },
_getIcon: function() { _getSignalIcon: function() {
if (this._ap.mode == NM80211Mode.ADHOC) if (this._ap.mode == NM80211Mode.ADHOC)
return 'network-workgroup-symbolic'; return 'network-workgroup-symbolic';
else else
@ -571,7 +645,7 @@ const NMWirelessDialog = new Lang.Class({
Name: 'NMWirelessDialog', Name: 'NMWirelessDialog',
Extends: ModalDialog.ModalDialog, Extends: ModalDialog.ModalDialog,
_init: function(client, device, settings) { _init: function(client, device) {
this.parent({ styleClass: 'nm-dialog' }); this.parent({ styleClass: 'nm-dialog' });
this._client = client; this._client = client;
@ -580,10 +654,7 @@ const NMWirelessDialog = new Lang.Class({
this._networks = []; this._networks = [];
this._buildLayout(); this._buildLayout();
let connections = settings.list_connections(); this._connections = device.get_available_connections();
this._connections = connections.filter(Lang.bind(this, function(connection) {
return device.connection_valid(connection);
}));
this._apAddedId = device.connect('access-point-added', Lang.bind(this, this._accessPointAdded)); this._apAddedId = device.connect('access-point-added', Lang.bind(this, this._accessPointAdded));
this._apRemovedId = device.connect('access-point-removed', Lang.bind(this, this._accessPointRemoved)); this._apRemovedId = device.connect('access-point-removed', Lang.bind(this, this._accessPointRemoved));
@ -695,16 +766,11 @@ const NMWirelessDialog = new Lang.Class({
_connect: function() { _connect: function() {
let network = this._selectedNetwork; let network = this._selectedNetwork;
let accessPoints = network.accessPoints;
if (network.connections.length > 0) { if (network.connections.length > 0) {
let connection = network.connections[0]; let connection = network.connections[0];
for (let i = 0; i < accessPoints.length; i++) { this._client.activate_connection(connection, this._device, null, null);
if (accessPoints[i].connection_valid(connection)) {
this._client.activate_connection(connection, this._device, accessPoints[i].dbus_path, null);
break;
}
}
} else { } else {
let accessPoints = network.accessPoints;
if ((accessPoints[0]._secType == NMAccessPointSecurity.WPA2_ENT) if ((accessPoints[0]._secType == NMAccessPointSecurity.WPA2_ENT)
|| (accessPoints[0]._secType == NMAccessPointSecurity.WPA_ENT)) { || (accessPoints[0]._secType == NMAccessPointSecurity.WPA_ENT)) {
// 802.1x-enabled APs require further configuration, so they're // 802.1x-enabled APs require further configuration, so they're
@ -915,13 +981,13 @@ const NMWirelessDialog = new Lang.Class({
_selectNetwork: function(network) { _selectNetwork: function(network) {
if (this._selectedNetwork) if (this._selectedNetwork)
this._selectedNetwork.item.actor.checked = false; this._selectedNetwork.item.actor.remove_style_pseudo_class('selected');
this._selectedNetwork = network; this._selectedNetwork = network;
this._updateSensitivity(); this._updateSensitivity();
if (this._selectedNetwork) if (this._selectedNetwork)
this._selectedNetwork.item.actor.checked = true; this._selectedNetwork.item.actor.add_style_pseudo_class('selected');
}, },
_createNetworkItem: function(network) { _createNetworkItem: function(network) {
@ -938,10 +1004,9 @@ const NMDeviceWireless = new Lang.Class({
Name: 'NMDeviceWireless', Name: 'NMDeviceWireless',
category: NMConnectionCategory.WIRELESS, category: NMConnectionCategory.WIRELESS,
_init: function(client, device, settings) { _init: function(client, device) {
this._client = client; this._client = client;
this._device = device; this._device = device;
this._settings = settings;
this._description = ''; this._description = '';
@ -952,9 +1017,10 @@ const NMDeviceWireless = new Lang.Class({
this._toggleItem.connect('activate', Lang.bind(this, this._toggleWifi)); this._toggleItem.connect('activate', Lang.bind(this, this._toggleWifi));
this.item.menu.addMenuItem(this._toggleItem); this.item.menu.addMenuItem(this._toggleItem);
this.item.menu.addSettingsAction(_("Network Settings"), 'gnome-network-panel.desktop'); this.item.menu.addMenuItem(createSettingsAction(_("Wi-Fi Settings"), device));
this._wirelessEnabledChangedId = this._device.connect('notify::wireless-enabled', Lang.bind(this, this._sync)); this._wirelessEnabledChangedId = this._client.connect('notify::wireless-enabled', Lang.bind(this, this._sync));
this._wirelessHwEnabledChangedId = this._client.connect('notify::wireless-hardware-enabled', Lang.bind(this, this._sync));
this._activeApChangedId = this._device.connect('notify::active-access-point', Lang.bind(this, this._activeApChanged)); this._activeApChangedId = this._device.connect('notify::active-access-point', Lang.bind(this, this._activeApChanged));
this._stateChangedId = this._device.connect('state-changed', Lang.bind(this, this._deviceStateChanged)); this._stateChangedId = this._device.connect('state-changed', Lang.bind(this, this._deviceStateChanged));
@ -974,6 +1040,14 @@ const NMDeviceWireless = new Lang.Class({
this._activeAccessPoint.disconnect(this._strengthChangedId); this._activeAccessPoint.disconnect(this._strengthChangedId);
this._strengthChangedId = 0; this._strengthChangedId = 0;
} }
if (this._wirelessEnabledChangedId) {
this._client.disconnect(this._wirelessEnabledChangedId);
this._wirelessEnabledChangedId = 0;
}
if (this._wirelessHwEnabledChangedId) {
this._client.disconnect(this._wirelessHwEnabledChangedId);
this._wirelessHwEnabledChangedId = 0;
}
this.item.destroy(); this.item.destroy();
}, },
@ -1000,7 +1074,7 @@ const NMDeviceWireless = new Lang.Class({
}, },
_showDialog: function() { _showDialog: function() {
this._dialog = new NMWirelessDialog(this._client, this._device, this._settings); this._dialog = new NMWirelessDialog(this._client, this._device);
this._dialog.connect('closed', Lang.bind(this, this._dialogClosed)); this._dialog.connect('closed', Lang.bind(this, this._dialogClosed));
this._dialog.open(); this._dialog.open();
}, },
@ -1032,6 +1106,7 @@ const NMDeviceWireless = new Lang.Class({
_sync: function() { _sync: function() {
this._toggleItem.label.text = this._client.wireless_enabled ? _("Turn Off") : _("Turn On"); this._toggleItem.label.text = this._client.wireless_enabled ? _("Turn Off") : _("Turn On");
this._toggleItem.actor.visible = this._client.wireless_hardware_enabled;
this.item.status.text = this._getStatus(); this.item.status.text = this._getStatus();
this.item.icon.icon_name = this._getMenuIcon(); this.item.icon.icon_name = this._getMenuIcon();
@ -1045,10 +1120,17 @@ const NMDeviceWireless = new Lang.Class({
_getStatus: function() { _getStatus: function() {
let ap = this._device.active_access_point; let ap = this._device.active_access_point;
if (!ap)
return _("Off"); // XXX -- interpret actual status
return ssidToLabel(ap.get_ssid()); if (ap)
return ssidToLabel(ap.get_ssid());
else if (!this._client.wireless_hardware_enabled)
return _("Hardware Disabled");
else if (!this._client.wireless_enabled)
return _("Off");
else if (this._device.state == NetworkManager.DeviceState.DISCONNECTED)
return _("Not Connected");
else
return '';
}, },
_getMenuIcon: function() { _getMenuIcon: function() {
@ -1059,7 +1141,8 @@ const NMDeviceWireless = new Lang.Class({
}, },
getIndicatorIcon: function() { getIndicatorIcon: function() {
if (this._device.active_connection.state == NetworkManager.ActiveConnectionState.ACTIVATING) if (this._device.state >= NetworkManager.DeviceState.PREPARE &&
this._device.state < NetworkManager.DeviceState.ACTIVATED)
return 'network-wireless-acquiring-symbolic'; return 'network-wireless-acquiring-symbolic';
let ap = this._device.active_access_point; let ap = this._device.active_access_point;
@ -1117,6 +1200,7 @@ const NMVPNConnectionItem = new Lang.Class({
this.emit('activation-failed', reason); this.emit('activation-failed', reason);
} }
this.emit('icon-changed');
this.parent(); this.parent();
}, },
@ -1158,7 +1242,7 @@ const NMVPNSection = new Lang.Class({
}, },
_sync: function() { _sync: function() {
let nItems = this._connectionItems.size(); let nItems = this._connectionItems.size;
this.item.actor.visible = (nItems > 0); this.item.actor.visible = (nItems > 0);
this.parent(); this.parent();
}, },
@ -1180,9 +1264,10 @@ const NMVPNSection = new Lang.Class({
}, },
setActiveConnections: function(vpnConnections) { setActiveConnections: function(vpnConnections) {
this._connectionItems.values().forEach(function(item) { let connections = this._connectionItems.values();
for (let item of connections) {
item.setActiveConnection(null); item.setActiveConnection(null);
}); }
vpnConnections.forEach(Lang.bind(this, function(a) { vpnConnections.forEach(Lang.bind(this, function(a) {
let item = this._connectionItems.get(a._connection.get_uuid()); let item = this._connectionItems.get(a._connection.get_uuid());
item.setActiveConnection(a); item.setActiveConnection(a);
@ -1195,8 +1280,7 @@ const NMVPNSection = new Lang.Class({
getIndicatorIcon: function() { getIndicatorIcon: function() {
let items = this._connectionItems.values(); let items = this._connectionItems.values();
for (let i = 0; i < items.length; i++) { for (let item of items) {
let item = items[i];
let icon = item.getIndicatorIcon(); let icon = item.getIndicatorIcon();
if (icon) if (icon)
return icon; return icon;
@ -1218,6 +1302,7 @@ const NMApplet = new Lang.Class({
// Device types // Device types
this._dtypes = { }; this._dtypes = { };
this._dtypes[NetworkManager.DeviceType.ETHERNET] = NMDeviceWired;
this._dtypes[NetworkManager.DeviceType.WIFI] = NMDeviceWireless; this._dtypes[NetworkManager.DeviceType.WIFI] = NMDeviceWireless;
this._dtypes[NetworkManager.DeviceType.MODEM] = NMDeviceModem; this._dtypes[NetworkManager.DeviceType.MODEM] = NMDeviceModem;
this._dtypes[NetworkManager.DeviceType.BT] = NMDeviceBluetooth; this._dtypes[NetworkManager.DeviceType.BT] = NMDeviceBluetooth;
@ -1225,6 +1310,7 @@ const NMApplet = new Lang.Class({
// Connection types // Connection types
this._ctypes = { }; this._ctypes = { };
this._ctypes[NetworkManager.SETTING_WIRED_SETTING_NAME] = NMConnectionCategory.WIRED;
this._ctypes[NetworkManager.SETTING_WIRELESS_SETTING_NAME] = NMConnectionCategory.WIRELESS; this._ctypes[NetworkManager.SETTING_WIRELESS_SETTING_NAME] = NMConnectionCategory.WIRELESS;
this._ctypes[NetworkManager.SETTING_BLUETOOTH_SETTING_NAME] = NMConnectionCategory.WWAN; this._ctypes[NetworkManager.SETTING_BLUETOOTH_SETTING_NAME] = NMConnectionCategory.WWAN;
this._ctypes[NetworkManager.SETTING_CDMA_SETTING_NAME] = NMConnectionCategory.WWAN; this._ctypes[NetworkManager.SETTING_CDMA_SETTING_NAME] = NMConnectionCategory.WWAN;
@ -1247,6 +1333,15 @@ const NMApplet = new Lang.Class({
this._tryLateInit(); this._tryLateInit();
}, },
_createDeviceCategory: function() {
let category = {
section: new PopupMenu.PopupMenuSection(),
devices: [ ],
};
this.menu.addMenuItem(category.section);
return category;
},
_tryLateInit: function() { _tryLateInit: function() {
if (!this._client || !this._settings) if (!this._client || !this._settings)
return; return;
@ -1262,17 +1357,9 @@ const NMApplet = new Lang.Class({
this._nmDevices = []; this._nmDevices = [];
this._devices = { }; this._devices = { };
this._devices.wireless = { this._devices.wired = this._createDeviceCategory();
section: new PopupMenu.PopupMenuSection(), this._devices.wireless = this._createDeviceCategory();
devices: [ ], this._devices.wwan = this._createDeviceCategory();
};
this.menu.addMenuItem(this._devices.wireless.section);
this._devices.wwan = {
section: new PopupMenu.PopupMenuSection(),
devices: [ ],
};
this.menu.addMenuItem(this._devices.wwan.section);
this._vpnSection = new NMVPNSection(this._client); this._vpnSection = new NMVPNSection(this._client);
this._vpnSection.connect('activation-failed', Lang.bind(this, this._onActivationFailed)); this._vpnSection.connect('activation-failed', Lang.bind(this, this._onActivationFailed));
@ -1306,7 +1393,7 @@ const NMApplet = new Lang.Class({
if (!this._source) { if (!this._source) {
this._source = new MessageTray.Source(_("Network Manager"), this._source = new MessageTray.Source(_("Network Manager"),
'network-transmit-receive'); 'network-transmit-receive');
this._source.policy = new NotificationDaemon.NotificationApplicationPolicy('gnome-network-panel'); this._source.policy = new MessageTray.NotificationApplicationPolicy('gnome-network-panel');
this._source.connect('destroy', Lang.bind(this, function() { this._source.connect('destroy', Lang.bind(this, function() {
this._source = null; this._source = null;
@ -1418,28 +1505,18 @@ const NMApplet = new Lang.Class({
devices.splice(pos, 1); devices.splice(pos, 1);
}, },
_ensureActiveConnectionProps: function(a) {
if (!a._connection) {
a._connection = this._settings.get_connection_by_path(a.connection);
// This list is guaranteed to have only one device in it.
let device = a.get_devices()[0]._delegate;
a._primaryDevice = device;
}
},
_getMainConnection: function() { _getMainConnection: function() {
let connection; let connection;
connection = this._client.get_primary_connection(); connection = this._client.get_primary_connection();
if (connection) { if (connection) {
this._ensureActiveConnectionProps(connection); ensureActiveConnectionProps(connection, this._settings);
return connection; return connection;
} }
connection = this._client.get_activating_connection(); connection = this._client.get_activating_connection();
if (connection) { if (connection) {
this._ensureActiveConnectionProps(connection); ensureActiveConnectionProps(connection, this._settings);
return connection; return connection;
} }
@ -1475,7 +1552,7 @@ const NMApplet = new Lang.Class({
return (a instanceof NMClient.VPNConnection); return (a instanceof NMClient.VPNConnection);
}); });
vpnConnections.forEach(Lang.bind(this, function(a) { vpnConnections.forEach(Lang.bind(this, function(a) {
this._ensureActiveConnectionProps(a); ensureActiveConnectionProps(a, this._settings);
})); }));
this._vpnSection.setActiveConnections(vpnConnections); this._vpnSection.setActiveConnections(vpnConnections);
@ -1578,6 +1655,7 @@ const NMApplet = new Lang.Class({
_updateIcon: function() { _updateIcon: function() {
if (!this._client.networking_enabled || !this._mainConnection) { if (!this._client.networking_enabled || !this._mainConnection) {
this._primaryIndicator.icon_name = 'network-offline-symbolic'; this._primaryIndicator.icon_name = 'network-offline-symbolic';
this._primaryIndicator.visible = true;
} else { } else {
let dev = this._mainConnection._primaryDevice; let dev = this._mainConnection._primaryDevice;
this._primaryIndicator.visible = (dev != null); this._primaryIndicator.visible = (dev != null);

View File

@ -8,20 +8,22 @@ const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu; const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
const BUS_NAME = 'org.gnome.SettingsDaemon.Power'; const BUS_NAME = 'org.freedesktop.UPower';
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power'; const OBJECT_PATH = '/org/freedesktop/UPower/devices/DisplayDevice';
const PowerManagerInterface = <interface name="org.gnome.SettingsDaemon.Power"> const DisplayDeviceInterface = '<node> \
<method name="GetDevices"> <interface name="org.freedesktop.UPower.Device"> \
<arg type="a(susdut)" direction="out" /> <property name="Type" type="u" access="read"/> \
</method> <property name="State" type="u" access="read"/> \
<method name="GetPrimaryDevice"> <property name="Percentage" type="d" access="read"/> \
<arg type="(susdut)" direction="out" /> <property name="TimeToEmpty" type="x" access="read"/> \
</method> <property name="TimeToFull" type="x" access="read"/> \
<property name="Icon" type="s" access="read" /> <property name="IsPresent" type="b" access="read"/> \
</interface>; <property name="IconName" type="s" access="read"/> \
</interface> \
</node>';
const PowerManagerProxy = Gio.DBusProxy.makeProxyWrapper(PowerManagerInterface); const PowerManagerProxy = Gio.DBusProxy.makeProxyWrapper(DisplayDeviceInterface);
const Indicator = new Lang.Class({ const Indicator = new Lang.Class({
Name: 'PowerIndicator', Name: 'PowerIndicator',
@ -32,7 +34,7 @@ const Indicator = new Lang.Class({
this._indicator = this._addIndicator(); this._indicator = this._addIndicator();
this._proxy = new PowerManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH, this._proxy = new PowerManagerProxy(Gio.DBus.system, BUS_NAME, OBJECT_PATH,
Lang.bind(this, function(proxy, error) { Lang.bind(this, function(proxy, error) {
if (error) { if (error) {
log(error.message); log(error.message);
@ -43,7 +45,7 @@ const Indicator = new Lang.Class({
this._sync(); this._sync();
})); }));
this._item = new PopupMenu.PopupSubMenuMenuItem(_("Battery"), true); this._item = new PopupMenu.PopupSubMenuMenuItem("", true);
this._item.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop'); this._item.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop');
this.menu.addMenuItem(this._item); this.menu.addMenuItem(this._item);
@ -56,11 +58,18 @@ const Indicator = new Lang.Class({
this.menu.setSensitive(sensitive); this.menu.setSensitive(sensitive);
}, },
_statusForDevice: function(device) { _getStatus: function() {
let [device_id, device_type, icon, percentage, state, seconds] = device; let seconds = 0;
if (state == UPower.DeviceState.FULLY_CHARGED) if (this._proxy.State == UPower.DeviceState.FULLY_CHARGED)
return _("Fully Charged"); return _("Fully Charged");
else if (this._proxy.State == UPower.DeviceState.CHARGING)
seconds = this._proxy.TimeToFull;
else if (this._proxy.State == UPower.DeviceState.DISCHARGING)
seconds = this._proxy.TimeToEmpty;
// state is one of PENDING_CHARGING, PENDING_DISCHARGING
else
return _("Estimating…");
let time = Math.round(seconds / 60); let time = Math.round(seconds / 60);
if (time == 0) { if (time == 0) {
@ -72,52 +81,43 @@ const Indicator = new Lang.Class({
let minutes = time % 60; let minutes = time % 60;
let hours = Math.floor(time / 60); let hours = Math.floor(time / 60);
if (state == UPower.DeviceState.DISCHARGING) { if (this._proxy.State == UPower.DeviceState.DISCHARGING) {
// Translators: this is <hours>:<minutes> Remaining (<percentage>) // Translators: this is <hours>:<minutes> Remaining (<percentage>)
return _("%d\u2236%02d Remaining (%d%%)").format(hours, minutes, percentage); return _("%d\u2236%02d Remaining (%d%%)").format(hours, minutes, this._proxy.Percentage);
} }
if (state == UPower.DeviceState.CHARGING) { if (this._proxy.State == UPower.DeviceState.CHARGING) {
// Translators: this is <hours>:<minutes> Until Full (<percentage>) // Translators: this is <hours>:<minutes> Until Full (<percentage>)
return _("%d\u2236%02d Until Full (%d%%)").format(hours, minutes, percentage); return _("%d\u2236%02d Until Full (%d%%)").format(hours, minutes, this._proxy.Percentage);
} }
// state is one of PENDING_CHARGING, PENDING_DISCHARGING return null;
return _("Estimating…");
},
_syncStatusLabel: function() {
this._proxy.GetPrimaryDeviceRemote(Lang.bind(this, function(result, error) {
if (error) {
this._item.actor.hide();
return;
}
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._item.actor.hide();
}
}));
},
_syncIcon: function() {
let icon = this._proxy.Icon;
if (icon) {
let gicon = Gio.icon_new_for_string(icon);
this._indicator.gicon = gicon;
this._item.icon.gicon = gicon;
} else {
// If there's no battery, then we use the power icon.
this._indicator.icon_name = 'system-shutdown-symbolic';
}
}, },
_sync: function() { _sync: function() {
this._syncIcon(); // Do we have batteries or a UPS?
this._syncStatusLabel(); let visible = this._proxy.IsPresent;
} if (visible) {
this._item.actor.show();
} else {
// If there's no battery, then we use the power icon.
this._item.actor.hide();
this._indicator.icon_name = 'system-shutdown-symbolic';
return;
}
// The icons
let icon = this._proxy.IconName;
this._indicator.icon_name = icon;
this._item.icon.icon_name = icon;
// The status label
this._item.status.text = this._getStatus();
// The sub-menu heading
if (this._proxy.Type == UPower.DeviceKind.UPS)
this._item.label.text = _("UPS");
else
this._item.label.text = _("Battery");
},
}); });

View File

@ -9,9 +9,11 @@ const PopupMenu = imports.ui.popupMenu;
const BUS_NAME = 'org.gnome.SettingsDaemon.Rfkill'; const BUS_NAME = 'org.gnome.SettingsDaemon.Rfkill';
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Rfkill'; const OBJECT_PATH = '/org/gnome/SettingsDaemon/Rfkill';
const RfkillManagerInterface = <interface name="org.gnome.SettingsDaemon.Rfkill"> const RfkillManagerInterface = '<node> \
<property name="AirplaneMode" type="b" access="readwrite" /> <interface name="org.gnome.SettingsDaemon.Rfkill"> \
</interface>; <property name="AirplaneMode" type="b" access="readwrite" /> \
</interface> \
</node>';
const RfkillManagerProxy = Gio.DBusProxy.makeProxyWrapper(RfkillManagerInterface); const RfkillManagerProxy = Gio.DBusProxy.makeProxyWrapper(RfkillManagerInterface);

View File

@ -18,10 +18,12 @@ const PopupMenu = imports.ui.popupMenu;
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown'; const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver'; const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
const PRIVACY_SCHEMA = 'org.gnome.desktop.privacy' const PRIVACY_SCHEMA = 'org.gnome.desktop.privacy'
const DISABLE_USER_SWITCH_KEY = 'disable-user-switching'; const DISABLE_USER_SWITCH_KEY = 'disable-user-switching';
const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen'; const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen';
const DISABLE_LOG_OUT_KEY = 'disable-log-out'; const DISABLE_LOG_OUT_KEY = 'disable-log-out';
const DISABLE_RESTART_KEY = 'disable-restart-buttons';
const ALWAYS_SHOW_LOG_OUT_KEY = 'always-show-log-out'; const ALWAYS_SHOW_LOG_OUT_KEY = 'always-show-log-out';
const AltSwitcher = new Lang.Class({ const AltSwitcher = new Lang.Class({
@ -79,7 +81,7 @@ const AltSwitcher = new Lang.Class({
this._sync(); this._sync();
} }
return false; return Clutter.EVENT_PROPAGATE;
}, },
}); });
@ -91,6 +93,7 @@ const Indicator = new Lang.Class({
this.parent(); this.parent();
this._screenSaverSettings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA }); this._screenSaverSettings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
this._loginScreenSettings = new Gio.Settings({ schema: LOGIN_SCREEN_SCHEMA });
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA }); this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
this._privacySettings = new Gio.Settings({ schema: PRIVACY_SCHEMA }); this._privacySettings = new Gio.Settings({ schema: PRIVACY_SCHEMA });
this._orientationSettings = new Gio.Settings({ schema: 'org.gnome.settings-daemon.peripherals.touchscreen' }); this._orientationSettings = new Gio.Settings({ schema: 'org.gnome.settings-daemon.peripherals.touchscreen' });
@ -261,7 +264,10 @@ const Indicator = new Lang.Class({
}, },
_updatePowerOff: function() { _updatePowerOff: function() {
this._powerOffAction.visible = this._haveShutdown && !Main.sessionMode.isLocked; let disabled = Main.sessionMode.isLocked ||
(Main.sessionMode.isGreeter &&
this._loginScreenSettings.get_boolean(DISABLE_RESTART_KEY));
this._powerOffAction.visible = this._haveShutdown && !disabled;
this._updateActionsVisibility(); this._updateActionsVisibility();
}, },
@ -273,7 +279,10 @@ const Indicator = new Lang.Class({
}, },
_updateSuspend: function() { _updateSuspend: function() {
this._suspendAction.visible = this._haveSuspend && !Main.sessionMode.isLocked; let disabled = Main.sessionMode.isLocked ||
(Main.sessionMode.isGreeter &&
this._loginScreenSettings.get_boolean(DISABLE_RESTART_KEY));
this._suspendAction.visible = this._haveShutdown && !disabled;
this._updateActionsVisibility(); this._updateActionsVisibility();
}, },
@ -372,7 +381,11 @@ const Indicator = new Lang.Class({
Main.overview.hide(); Main.overview.hide();
if (Main.screenShield) if (Main.screenShield)
Main.screenShield.lock(false); Main.screenShield.lock(false);
Gdm.goto_login_session_sync(null);
Clutter.threads_add_repaint_func_full(Clutter.RepaintFlags.POST_PAINT, function() {
Gdm.goto_login_session_sync(null);
return false;
});
}, },
_onQuitSessionActivate: function() { _onQuitSessionActivate: function() {

View File

@ -42,7 +42,7 @@ const StreamSlider = new Lang.Class({
this.item.actor.add(this._icon); this.item.actor.add(this._icon);
this.item.actor.add(this._slider.actor, { expand: true }); this.item.actor.add(this._slider.actor, { expand: true });
this.item.actor.connect('button-press-event', Lang.bind(this, function(actor, event) { this.item.actor.connect('button-press-event', Lang.bind(this, function(actor, event) {
this._slider.startDragging(event); return this._slider.startDragging(event);
})); }));
this.item.actor.connect('key-press-event', Lang.bind(this, function(actor, event) { this.item.actor.connect('key-press-event', Lang.bind(this, function(actor, event) {
return this._slider.onKeyPressEvent(actor, event); return this._slider.onKeyPressEvent(actor, event);
@ -94,7 +94,7 @@ const StreamSlider = new Lang.Class({
}, },
scroll: function(event) { scroll: function(event) {
this._slider.scroll(event); return this._slider.scroll(event);
}, },
setValue: function(value) { setValue: function(value) {
@ -276,7 +276,7 @@ const VolumeMenu = new Lang.Class({
}, },
scroll: function(event) { scroll: function(event) {
this._output.scroll(event); return this._output.scroll(event);
}, },
_onControlStateChanged: function() { _onControlStateChanged: function() {
@ -329,6 +329,6 @@ const Indicator = new Lang.Class({
}, },
_onScrollEvent: function(actor, event) { _onScrollEvent: function(actor, event) {
this._volumeMenu.scroll(event); return this._volumeMenu.scroll(event);
} }
}); });

View File

@ -1,6 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk; const Gtk = imports.gi.Gtk;
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
@ -53,7 +54,7 @@ const SwitcherPopup = new Lang.Class({
this.actor.connect('allocate', Lang.bind(this, this._allocate)); this.actor.connect('allocate', Lang.bind(this, this._allocate));
this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
Main.uiGroup.add_actor(this.actor); Main.layoutManager.switcherPopupGroup.add_child(this.actor);
this._haveModal = false; this._haveModal = false;
this._modifierMask = 0; this._modifierMask = 0;
@ -163,6 +164,7 @@ const SwitcherPopup = new Lang.Class({
Main.osdWindow.cancel(); Main.osdWindow.cancel();
this.actor.opacity = 255; this.actor.opacity = 255;
this._initialDelayTimeoutId = 0; this._initialDelayTimeoutId = 0;
return GLib.SOURCE_REMOVE;
})); }));
return true; return true;
}, },
@ -192,7 +194,7 @@ const SwitcherPopup = new Lang.Class({
else else
this._keyPressHandler(keysym, backwards, action); this._keyPressHandler(keysym, backwards, action);
return true; return Clutter.EVENT_STOP;
}, },
_keyReleaseEvent: function(actor, event) { _keyReleaseEvent: function(actor, event) {
@ -202,11 +204,12 @@ const SwitcherPopup = new Lang.Class({
if (state == 0) if (state == 0)
this._finish(event.get_time()); this._finish(event.get_time());
return true; return Clutter.EVENT_STOP;
}, },
_clickedOutside: function(actor, event) { _clickedOutside: function(actor, event) {
this.destroy(); this.destroy();
return Clutter.EVENT_PROPAGATE;
}, },
_scrollHandler: function(direction) { _scrollHandler: function(direction) {
@ -218,6 +221,7 @@ const SwitcherPopup = new Lang.Class({
_scrollEvent: function(actor, event) { _scrollEvent: function(actor, event) {
this._scrollHandler(event.get_scroll_direction()); this._scrollHandler(event.get_scroll_direction());
return Clutter.EVENT_PROPAGATE;
}, },
_itemActivatedHandler: function(n) { _itemActivatedHandler: function(n) {
@ -251,6 +255,7 @@ const SwitcherPopup = new Lang.Class({
_mouseTimedOut: function() { _mouseTimedOut: function() {
this._motionTimeoutId = 0; this._motionTimeoutId = 0;
this.mouseActive = true; this.mouseActive = true;
return GLib.SOURCE_REMOVE;
}, },
_popModal: function() { _popModal: function() {
@ -400,6 +405,7 @@ const SwitcherList = new Lang.Class({
_onItemEnter: function (index) { _onItemEnter: function (index) {
this._itemEntered(index); this._itemEntered(index);
return Clutter.EVENT_PROPAGATE;
}, },
highlight: function(index, justOutline) { highlight: function(index, justOutline) {
@ -446,10 +452,9 @@ const SwitcherList = new Lang.Class({
time: POPUP_SCROLL_TIME, time: POPUP_SCROLL_TIME,
transition: 'easeOutQuad', transition: 'easeOutQuad',
onComplete: Lang.bind(this, function () { onComplete: Lang.bind(this, function () {
if (this._highlighted == 0) { if (this._highlighted == 0)
this._scrollableLeft = false; this._scrollableLeft = false;
this.actor.queue_relayout(); this.actor.queue_relayout();
}
}) })
}); });
}, },
@ -471,10 +476,9 @@ const SwitcherList = new Lang.Class({
time: POPUP_SCROLL_TIME, time: POPUP_SCROLL_TIME,
transition: 'easeOutQuad', transition: 'easeOutQuad',
onComplete: Lang.bind(this, function () { onComplete: Lang.bind(this, function () {
if (this._highlighted == this._items.length - 1) { if (this._highlighted == this._items.length - 1)
this._scrollableRight = false; this._scrollableRight = false;
this.actor.queue_relayout(); this.actor.queue_relayout();
}
}) })
}); });
}, },
@ -509,7 +513,7 @@ const SwitcherList = new Lang.Class({
_getPreferredWidth: function (actor, forHeight, alloc) { _getPreferredWidth: function (actor, forHeight, alloc) {
let [maxChildMin, maxChildNat] = this._maxChildWidth(forHeight); let [maxChildMin, maxChildNat] = this._maxChildWidth(forHeight);
let totalSpacing = this._list.spacing * (this._items.length - 1); let totalSpacing = Math.max(this._list.spacing * (this._items.length - 1), 0);
alloc.min_size = this._items.length * maxChildMin + totalSpacing; alloc.min_size = this._items.length * maxChildMin + totalSpacing;
alloc.natural_size = alloc.min_size; alloc.natural_size = alloc.min_size;
this._minSize = alloc.min_size; this._minSize = alloc.min_size;
@ -539,7 +543,7 @@ const SwitcherList = new Lang.Class({
let childHeight = box.y2 - box.y1; let childHeight = box.y2 - box.y1;
let [maxChildMin, maxChildNat] = this._maxChildWidth(childHeight); let [maxChildMin, maxChildNat] = this._maxChildWidth(childHeight);
let totalSpacing = this._list.spacing * (this._items.length - 1); let totalSpacing = Math.max(this._list.spacing * (this._items.length - 1), 0);
let childWidth = Math.floor(Math.max(0, box.x2 - box.x1 - totalSpacing) / this._items.length); let childWidth = Math.floor(Math.max(0, box.x2 - box.x1 - totalSpacing) / this._items.length);

View File

@ -11,29 +11,8 @@ const Signals = imports.signals;
const Tweener = imports.tweener.tweener; const Tweener = imports.tweener.tweener;
// This is a wrapper around imports.tweener.tweener that adds a bit of // This is a wrapper around imports.tweener.tweener that adds a bit of
// Clutter integration and some additional callbacks: // Clutter integration. If the tweening target is a Clutter.Actor, then
// // the tweenings will automatically be removed if the actor is destroyed.
// 1. If the tweening target is a Clutter.Actor, then the tweenings
// will automatically be removed if the actor is destroyed
//
// 2. If target._delegate.onAnimationStart() exists, it will be
// called when the target starts being animated.
//
// 3. If target._delegate.onAnimationComplete() exists, it will be
// called once the target is no longer being animated.
//
// The onAnimationStart() and onAnimationComplete() callbacks differ
// from the tweener onStart and onComplete parameters, in that (1)
// they track whether or not the target has *any* tweens attached to
// it, as opposed to be called for *each* tween, and (2)
// onAnimationComplete() is always called when the object stops being
// animated, regardless of whether it stopped normally or abnormally.
//
// onAnimationComplete() is called at idle time, which means that if a
// tween completes and then another is added before returning to the
// main loop, the complete callback will not be called (until the new
// tween finishes).
// ActionScript Tweener methods that imports.tweener.tweener doesn't // ActionScript Tweener methods that imports.tweener.tweener doesn't
// currently implement: getTweens, getVersion, registerTransition, // currently implement: getTweens, getVersion, registerTransition,
@ -77,7 +56,6 @@ function _wrapTweening(target, tweeningParameters) {
if (!Gtk.Settings.get_default().gtk_enable_animations) if (!Gtk.Settings.get_default().gtk_enable_animations)
tweeningParameters['time'] = 0.000001; tweeningParameters['time'] = 0.000001;
_addHandler(target, tweeningParameters, 'onStart', _tweenStarted);
_addHandler(target, tweeningParameters, 'onComplete', _tweenCompleted); _addHandler(target, tweeningParameters, 'onComplete', _tweenCompleted);
} }
@ -85,7 +63,7 @@ function _getTweenState(target) {
// If we were paranoid, we could keep a plist mapping targets to // If we were paranoid, we could keep a plist mapping targets to
// states... but we're not that paranoid. // states... but we're not that paranoid.
if (!target.__ShellTweenerState) if (!target.__ShellTweenerState)
_resetTweenState(target); target.__ShellTweenerState = {};
return target.__ShellTweenerState; return target.__ShellTweenerState;
} }
@ -95,8 +73,6 @@ function _resetTweenState(target) {
if (state) { if (state) {
if (state.destroyedId) if (state.destroyedId)
state.actor.disconnect(state.destroyedId); state.actor.disconnect(state.destroyedId);
if (state.idleCompletedId)
Mainloop.source_remove(state.idleCompletedId);
} }
target.__ShellTweenerState = {}; target.__ShellTweenerState = {};
@ -122,32 +98,9 @@ function _actorDestroyed(target) {
Tweener.removeTweens(target); Tweener.removeTweens(target);
} }
function _tweenStarted(target) {
let state = _getTweenState(target);
let delegate = target._delegate;
if (!state.running && delegate && delegate.onAnimationStart)
delegate.onAnimationStart();
state.running = true;
}
function _tweenCompleted(target) { function _tweenCompleted(target) {
let state = _getTweenState(target); if (!isTweening(target))
if (!state.idleCompletedId)
state.idleCompletedId = Mainloop.idle_add(Lang.bind(null, _idleCompleted, target));
}
function _idleCompleted(target) {
let state = _getTweenState(target);
let delegate = target._delegate;
if (!isTweening(target)) {
_resetTweenState(target); _resetTweenState(target);
if (delegate && delegate.onAnimationComplete)
delegate.onAnimationComplete();
}
return false;
} }
function getTweenCount(scope) { function getTweenCount(scope) {

View File

@ -71,7 +71,7 @@ const UnlockDialog = new Lang.Class({
child: otherUserLabel, child: otherUserLabel,
reactive: true, reactive: true,
x_align: St.Align.START, x_align: St.Align.START,
x_fill: true }); x_fill: false });
this._otherUserButton.connect('clicked', Lang.bind(this, this._otherUserClicked)); this._otherUserButton.connect('clicked', Lang.bind(this, this._otherUserClicked));
this._promptBox.add_child(this._otherUserButton); this._promptBox.add_child(this._otherUserButton);
} else { } else {

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