Compare commits

...

349 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
190 changed files with 16895 additions and 19253 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

25
COPYING
View File

@ -1,8 +1,8 @@
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.
@ -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,7 +55,7 @@ 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
@ -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
@ -278,7 +278,7 @@ 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
@ -303,10 +303,9 @@ 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.
@ -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

97
NEWS
View File

@ -1,3 +1,100 @@
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 3.10.1
====== ======
* Make sure lock screen is drawn once before switching user [Giovanni; #708051] * Make sure lock screen is drawn once before switching user [Giovanni; #708051]

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.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.38.1 GJS_MIN_VERSION=1.39.0
MUTTER_MIN_VERSION=3.10.1 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

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,14 +29,6 @@
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">
<default>[ 'Utilities', 'Sundry' ]</default>
<_summary>List of categories that should be displayed as folders</_summary>
<_description>
Each category name in this list will be represented as folder in the
application view, rather than being displayed inline in the main view.
</_description>
</key>
<key name="app-picker-view" type="u"> <key name="app-picker-view" type="u">
<default>0</default> <default>0</default>
<summary>App Picker View</summary> <summary>App Picker View</summary>
@ -124,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

@ -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;
} }
@ -1947,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 {
@ -1970,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;
@ -2093,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;
@ -2319,6 +2330,8 @@ StScrollBar StButton#vhandle:active {
.login-dialog-user-list-item { .login-dialog-user-list-item {
border-radius: 5px; 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 {
@ -2329,24 +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: 20px;
padding-left: 18px;
font-weight: bold;
}
.login-dialog-user-list:expanded .login-dialog-user-list-item {
color: #bfbfbf;
}
.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: #bfbfbf;
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);
} }
@ -2373,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: 3px;
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;
@ -2387,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;
} }
@ -2426,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;
} }
@ -2495,11 +2488,18 @@ StScrollBar StButton#vhandle:active {
font-size: 20px; font-size: 20px;
font-weight: bold; font-weight: bold;
text-align: left; text-align: left;
padding-left: 18px;
color:white; 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,
@ -2512,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;
} }
@ -2624,4 +2624,5 @@ StScrollBar StButton#vhandle:active {
.background-menu { .background-menu {
-boxpointer-gap: 4px; -boxpointer-gap: 4px;
-arrow-rise: 0px;
} }

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,112 +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/oVirt.js \
gdm/realmd.js \ js_resource_dist_files = $(filter-out misc/config.js, $(js_resource_files))
gdm/util.js \
extensionPrefs/main.js \ EXTRA_DIST = \
misc/config.js \ $(js_resource_dist_files) \
misc/extensionUtils.js \ js-resources.gresource.xml \
misc/fileUtils.js \ misc/config.js.in \
misc/gnomeSession.js \ $(NULL)
misc/hash.js \
misc/history.js \ CLEANFILES = \
misc/jsParse.js \ $(js_built_sources) \
misc/loginManager.js \
misc/modemManager.js \
misc/objectManager.js \
misc/params.js \
misc/smartcardManager.js \
misc/util.js \
perf/core.js \
ui/altTab.js \
ui/animation.js \
ui/appDisplay.js \
ui/appFavorites.js \
ui/backgroundMenu.js \
ui/background.js \
ui/boxpointer.js \
ui/calendar.js \
ui/checkBox.js \
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

@ -80,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,
@ -93,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',
@ -111,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 });
@ -263,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) {

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() {
@ -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);
}, },

View File

@ -4,11 +4,13 @@ const Gio = imports.gi.Gio;
const Lang = imports.lang; const Lang = imports.lang;
const Signals = imports.signals; const Signals = imports.signals;
const OVirtCredentialsIface = <interface name='org.ovirt.vdsm.Credentials'> const OVirtCredentialsIface = '<node> \
<signal name="UserAuthenticated"> <interface name="org.ovirt.vdsm.Credentials"> \
<arg type="s" name="token"/> <signal name="UserAuthenticated"> \
</signal> <arg type="s" name="token"/> \
</interface>; </signal> \
</interface> \
</node>';
const OVirtCredentialsInfo = Gio.DBusInterfaceInfo.new_for_xml(OVirtCredentialsIface); const OVirtCredentialsInfo = Gio.DBusInterfaceInfo.new_for_xml(OVirtCredentialsIface);

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

@ -250,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;
})); }));
}, },

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

@ -89,7 +89,7 @@ function spawnApp(argv) {
let app = Gio.AppInfo.create_from_commandline(argv.join(' '), null, let app = Gio.AppInfo.create_from_commandline(argv.join(' '), null,
Gio.AppInfoCreateFlags.SUPPORTS_STARTUP_NOTIFICATION); Gio.AppInfoCreateFlags.SUPPORTS_STARTUP_NOTIFICATION);
let context = global.create_app_launch_context(); let context = global.create_app_launch_context(0, -1);
app.launch([], context); app.launch([], context);
} catch(err) { } catch(err) {
_handleSpawnError(argv[0], err); _handleSpawnError(argv[0], err);
@ -153,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));
@ -754,15 +786,6 @@ const AppDisplay = new Lang.Class({
initialView = Views.ALL; initialView = Views.ALL;
this._showView(initialView); 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) {
@ -796,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);
@ -871,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);
@ -886,18 +863,32 @@ const AppSearchProvider = new Lang.Class({
return results.slice(0, maxNumber); return results.slice(0, maxNumber);
}, },
getInitialResultSet: function(terms) { getInitialResultSet: function(terms, callback, cancellable) {
this.searchSystem.setResults(this, this._appSys.initial_search(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);
}, },
getSubsearchResultSet: function(previousResults, terms) { getSubsearchResultSet: function(previousResults, terms, callback, cancellable) {
this.searchSystem.setResults(this, this._appSys.subsearch(previousResults, terms)); this.getInitialResultSet(terms, callback, cancellable);
}, },
activateResult: function(app) { 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);
@ -913,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);
} }
}); });
@ -940,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',
@ -964,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]);
@ -1041,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,
@ -1055,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() {
@ -1076,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) {
@ -1154,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',
@ -1207,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() {
@ -1272,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,
@ -1352,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) {
@ -1370,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();
} }
@ -1429,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();
@ -1481,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
@ -1492,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.
@ -1506,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._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(); 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());
}));
}
} }
}, },
@ -1543,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,8 +113,8 @@ 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);
}, },
@ -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 });
for (let i = 0; i < this._pendingFileLoads.length; i++) { let caller = { monitorIndex: params.monitorIndex,
if (this._pendingFileLoads[i].filename == params.filename &&
this._pendingFileLoads[i].style == params.style) {
this._pendingFileLoads[i].callers.push({ shouldCopy: true,
monitorIndex: params.monitorIndex,
effects: params.effects, effects: params.effects,
onFinished: params.onFinished }); cancellable: params.cancellable,
return; onFinished: params.onFinished };
}
}
this._pendingFileLoads.push({ filename: params.filename,
style: params.style,
callers: [{ shouldCopy: false,
monitorIndex: params.monitorIndex,
effects: params.effects,
onFinished: params.onFinished }] });
let content = new Meta.Background({ meta_screen: global.screen,
monitor: params.monitorIndex,
effects: params.effects });
content.load_file_async(params.filename,
params.style,
params.cancellable,
Lang.bind(this,
function(object, result) {
try {
content.load_file_finish(result);
this._monitorFile(params.filename);
this._images.push(content);
} catch(e) {
content = null;
}
let info = null;
for (let i = 0; i < this._pendingFileLoads.length; i++) { for (let i = 0; i < this._pendingFileLoads.length; i++) {
let pendingLoad = this._pendingFileLoads[i]; let pendingLoad = this._pendingFileLoads[i];
if (pendingLoad.filename != params.filename || if (pendingLoad.filename == params.filename && pendingLoad.style == params.style) {
pendingLoad.style != params.style) info = pendingLoad;
continue; break;
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); if (!info)
info = this._loadImageContentInternal(params.filename, params.style);
info.callers.push(caller);
if (caller.cancellable) {
caller.cancellable.connect(Lang.bind(this, function() {
let idx = info.callers.indexOf(caller);
info.callers.splice(idx, 1);
if (info.callers.length == 0) {
info.cancellable.cancel();
let idx = this._pendingFileLoads.indexOf(info);
this._pendingFileLoads.splice(idx, 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,7 +327,6 @@ 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;
@ -376,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;
})); }));
}, },
@ -415,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);
}, },
@ -485,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();
@ -524,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;
})); }));
}, },
@ -544,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) {
@ -590,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;
}, },
@ -644,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',
@ -747,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');
}) })
@ -798,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);
} }
@ -62,7 +59,5 @@ function addBackgroundMenu(actor) {
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();
@ -284,6 +287,7 @@ const BoxPointer = new Lang.Class({
let skipBottomLeft = false; let skipBottomLeft = false;
let skipBottomRight = false; let skipBottomRight = false;
if (rise) {
switch (this._arrowSide) { switch (this._arrowSide) {
case St.Side.TOP: case St.Side.TOP:
if (this._arrowOrigin == x1) if (this._arrowOrigin == x1)
@ -313,9 +317,10 @@ const BoxPointer = new Lang.Class({
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);
@ -337,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);
@ -358,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);
@ -379,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))
return;
this._selectedDate = date; this._selectedDate = date;
this._update(forceReload); this._update();
this.emit('selected-date-changed', new Date(this._selectedDate)); this.emit('selected-date-changed', new Date(this._selectedDate));
} else {
if (forceReload)
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

@ -45,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' });
@ -136,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);

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

@ -446,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;
@ -544,14 +545,13 @@ 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);
@ -621,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);
@ -631,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 */
@ -675,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
@ -960,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);
@ -973,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) {
@ -1011,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() {
@ -1095,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();
})); }));
} }
@ -1136,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();
})); }));
} }
@ -1176,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();
})); }));
} }
@ -1239,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"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
switch (action) {
case 'decline':
contact.remove_async(function(src, result) { contact.remove_async(function(src, result) {
src.remove_finish(result)}); src.remove_finish(result);
break; });
case 'accept': }));
this.addAction(_("Accept"), Lang.bind(this, function() {
// Authorize the contact and request to see his status as well // Authorize the contact and request to see his status as well
contact.authorize_publication_async(function(src, result) { contact.authorize_publication_async(function(src, result) {
src.authorize_publication_finish(result)}); src.authorize_publication_finish(result);
});
contact.request_subscription_async('', function(src, result) { contact.request_subscription_async('', function(src, result) {
src.request_subscription_finish(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',
@ -1358,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() {
this.connect('action-invoked', Lang.bind(this, function(self, action) {
switch (action) {
case 'view':
let cmd = 'empathy-accounts --select-account=' + let cmd = 'empathy-accounts --select-account=' +
account.get_path_suffix(); account.get_path_suffix();
let app_info = Gio.app_info_create_from_commandline(cmd, null, 0); let app_info = Gio.app_info_create_from_commandline(cmd, null, 0);
app_info.launch([], global.create_app_launch_context()); app_info.launch([], global.create_app_launch_context(0, -1));
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());
} }
try {
extension.stateObj.disable(); 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,9 +93,11 @@ function disableExtension(uuid) {
extensionOrder.splice(orderIdx, 1); extensionOrder.splice(orderIdx, 1);
if ( extension.state != ExtensionState.ERROR ) {
extension.state = ExtensionState.DISABLED; extension.state = ExtensionState.DISABLED;
_signals.emit('extension-state-changed', extension); _signals.emit('extension-state-changed', extension);
} }
}
function enableExtension(uuid) { function enableExtension(uuid) {
let extension = ExtensionUtils.extensions[uuid]; let extension = ExtensionUtils.extensions[uuid];
@ -117,10 +123,15 @@ function enableExtension(uuid) {
} }
} }
try {
extension.stateObj.enable(); 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) {
try {
extensionState = extensionModule.init(extension); 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() {
@ -252,6 +280,9 @@ const LayoutManager = new Lang.Class({
this._inOverview = true; this._inOverview = true;
this._updateVisibility(); this._updateVisibility();
this._updateRegions(); 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[monitorIndex] = 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,11 +69,17 @@ 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;
if (params.radialEffect)
this.actor = new RadialShaderQuad({ x: 0,
y: 0,
reactive: params.inhibitEvents });
else
this.actor = new St.Bin({ x: 0, this.actor = new St.Bin({ x: 0,
y: 0, y: 0,
style_class: 'lightbox', style_class: 'lightbox',

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);
Main.layoutManager.panelBox.connect('allocation-changed',
Lang.bind(this, this._queueResize)); 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.
@ -503,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;
@ -836,17 +831,7 @@ 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,
@ -858,43 +843,37 @@ const Notification = new Lang.Class({
global.focus_manager.add_group(this._buttonBox); 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;
}
this._buttonBox.add(button); this._buttonBox.add(button);
button.connect('clicked', Lang.bind(this, this._onActionInvoked, id)); button.connect('clicked', Lang.bind(this, function() {
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) {
@ -913,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');
}, },
@ -1026,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');
@ -1141,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
@ -1300,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;
@ -1360,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');
@ -1418,9 +1379,7 @@ const Source = new Lang.Class({
if (this.notifications.indexOf(notification) >= 0) if (this.notifications.indexOf(notification) >= 0)
return; return;
notification.connect('clicked', Lang.bind(this, this.open));
notification.connect('destroy', Lang.bind(this, this._onNotificationDestroy)); notification.connect('destroy', Lang.bind(this, this._onNotificationDestroy));
this.notifications.push(notification); this.notifications.push(notification);
this.emit('notification-added', notification); this.emit('notification-added', notification);
@ -1567,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() {
@ -1652,7 +1611,7 @@ const MessageTrayMenu = new Lang.Class({
this._accountManager.prepare_async(null, 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 = new PopupMenu.PopupSwitchMenuItem(_("Notifications"));
this._busyItem.connect('toggled', Lang.bind(this, this._updatePresence)); this._busyItem.connect('toggled', Lang.bind(this, this._updatePresence));
@ -1664,7 +1623,7 @@ const MessageTrayMenu = new Lang.Class({
this._clearItem = this.addAction(_("Clear Messages"), function() { this._clearItem = this.addAction(_("Clear Messages"), function() {
let toDestroy = tray.getSources().filter(function(source) { let toDestroy = tray.getSources().filter(function(source) {
return source.isClearable; return source.isClearable;
}) });
toDestroy.forEach(function(source) { toDestroy.forEach(function(source) {
source.destroy(); source.destroy();
@ -1827,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',
@ -1941,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;
@ -1978,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() {
@ -2032,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() {
@ -2141,7 +2101,8 @@ 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)
@ -2162,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) {
@ -2212,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);
}, },
@ -2375,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() {
@ -2392,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;
@ -2409,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);
@ -2420,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();
} }
} }
@ -2469,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) {
@ -2541,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();
@ -2701,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) {
@ -2792,7 +2763,7 @@ 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,
@ -2838,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;
@ -2899,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() {
@ -2943,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;
@ -2963,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,11 +106,11 @@ const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
'ibus-ui-gtk': 'keyboard' 'ibus-ui-gtk': 'keyboard'
}; };
const NotificationDaemon = new Lang.Class({ const FdoNotificationDaemon = new Lang.Class({
Name: 'NotificationDaemon', Name: 'FdoNotificationDaemon',
_init: function() { _init: function() {
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(NotificationDaemonIface, this); this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(FdoNotificationsIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/freedesktop/Notifications'); this._dbusImpl.export(Gio.DBus.session, '/org/freedesktop/Notifications');
this._sources = []; this._sources = [];
@ -115,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));
@ -176,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.
@ -199,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);
// We don't want to override a persistent notification
// with a transient one from the same sender, so we
// 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); let source = this._lookupSource(title, pid, trayIcon);
if (source) { if (source) {
source.setTitle(title); source.setTitle(title);
return source; 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, source.connect('destroy', Lang.bind(this, function() {
function() {
let index = this._sources.indexOf(source); let index = this._sources.indexOf(source);
if (index >= 0) if (index >= 0)
this._sources.splice(index, 1); this._sources.splice(index, 1);
})); }));
}
Main.messageTray.add(source); Main.messageTray.add(source);
return source; return source;
@ -252,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]));
} }
@ -296,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;
@ -331,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.
// Removing the entries once the source is destroyed
// would result in the entries associated with transient
// 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; this._senderToPid[sender] = pid;
source.connect('destroy', Lang.bind(this, function() { source.connect('destroy', Lang.bind(this, function() {
delete this._senderToPid[sender]; 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,
@ -376,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
@ -413,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() {
this._emitActionInvoked(ndata.id, actionId);
})); }));
else
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);
@ -517,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) {
@ -553,7 +558,7 @@ 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 MessageTray.NotificationApplicationPolicy(id); return new MessageTray.NotificationApplicationPolicy(id);
} else { } else {
@ -643,7 +648,7 @@ const Source = new Lang.Class({
this.parent(title); this.parent(title);
}, },
open: function(notification) { open: function() {
this.openApp(); this.openApp();
this.destroyNonResidentNotifications(); this.destroyNonResidentNotifications();
}, },
@ -685,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,6 +125,7 @@ const OsdWindow = new Lang.Class({
setLevel: function(level) { setLevel: function(level) {
this._level.actor.visible = (level != undefined); this._level.actor.visible = (level != undefined);
if (level) {
if (this.actor.visible) if (this.actor.visible)
Tweener.addTween(this._level, Tweener.addTween(this._level,
{ level: level, { level: level,
@ -129,6 +133,7 @@ const OsdWindow = new Lang.Class({
transition: 'easeOutQuad' }); transition: 'easeOutQuad' });
else else
this._level.level = level; 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
@ -149,7 +148,7 @@ const Overview = new Lang.Class({
this._coverPane = new Clutter.Actor({ opacity: 0, this._coverPane = new Clutter.Actor({ opacity: 0,
reactive: true }); reactive: true });
Main.layoutManager.overviewGroup.add_child(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);
@ -66,9 +67,9 @@ const SlideLayout = new Lang.Class({
let alignX = (realDirection == SlideDirection.LEFT) ? (availWidth - natWidth) : 0; let alignX = (realDirection == SlideDirection.LEFT) ? (availWidth - natWidth) : 0;
let actorBox = new Clutter.ActorBox(); let actorBox = new Clutter.ActorBox();
actorBox.x1 = alignX; actorBox.x1 = box.x1 + alignX + this._translationX;
actorBox.x2 = actorBox.x1 + child.x_expand ? availWidth : natWidth; actorBox.x2 = actorBox.x1 + (child.x_expand ? availWidth : natWidth);
actorBox.y1 = 0; actorBox.y1 = box.y1;
actorBox.y2 = actorBox.y1 + availHeight; actorBox.y2 = actorBox.y1 + availHeight;
child.allocate(actorBox, flags); child.allocate(actorBox, flags);
@ -90,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({
@ -99,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;
@ -109,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));
@ -119,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' });
}, },
@ -151,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() {
@ -184,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() {
@ -209,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
}, },
@ -225,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();
} }
}); });
@ -244,16 +257,15 @@ const ThumbnailsSlider = new Lang.Class({
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;
@ -273,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();
@ -323,29 +328,22 @@ const DashSlider = new Lang.Class({
// available allocation // available allocation
this._dash.actor.x_expand = true; this._dash.actor.x_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();
}, },
@ -462,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

@ -602,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) {
@ -617,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) {
@ -628,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;
} }
}); });
@ -822,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);
@ -896,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 });
@ -983,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();
@ -1008,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,
@ -1020,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

@ -126,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) {
@ -134,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) {
@ -928,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;
} }
}); });
@ -1056,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);
@ -1071,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;
} }
}); });
@ -1102,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); }));
} }
@ -1114,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
@ -1164,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,8 +205,10 @@ const RemoteSearchProvider = new Lang.Class({
bitsPerSample, width, height, rowStride); bitsPerSample, width, height, rowStride);
} }
return new St.Icon({ gicon: gicon, if (gicon)
icon = new St.Icon({ gicon: gicon,
icon_size: size }); icon_size: size });
return icon;
}, },
filterResults: function(results, maxNumber) { filterResults: function(results, maxNumber) {
@ -217,40 +221,33 @@ const RemoteSearchProvider = new Lang.Class({
return regularResults.slice(0, maxNumber).concat(specialResults.slice(0, maxNumber)); return regularResults.slice(0, maxNumber).concat(specialResults.slice(0, maxNumber));
}, },
_getResultsFinished: function(results, error) { _getResultsFinished: function(results, error, callback) {
if (error) 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._cancellable.reset();
try {
this.proxy.GetInitialResultSetRemote(terms, this.proxy.GetInitialResultSetRemote(terms,
Lang.bind(this, this._getResultsFinished), Lang.bind(this, this._getResultsFinished, callback),
this._cancellable); 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._cancellable.reset();
try {
this.proxy.GetSubsearchResultSetRemote(previousResults, newTerms, this.proxy.GetSubsearchResultSetRemote(previousResults, newTerms,
Lang.bind(this, this._getResultsFinished), Lang.bind(this, this._getResultsFinished, callback),
this._cancellable); 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;
} }
@ -272,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._cancellable.reset();
try {
this.proxy.GetResultMetasRemote(ids, this.proxy.GetResultMetasRemote(ids,
Lang.bind(this, this._getResultMetasFinished, callback), Lang.bind(this, this._getResultMetasFinished, callback),
this._cancellable); cancellable);
} catch(e) {
log('Error calling GetResultMetas for provider %s: %s'.format(this.id, e.toString()));
callback([]);
}
}, },
activateResult: function(id) { activateResult: function(id) {
@ -293,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));
} }
}); });
@ -302,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,7 +17,6 @@ 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 OVirt = imports.gdm.oVirt;
const LoginManager = imports.misc.loginManager; const LoginManager = imports.misc.loginManager;
@ -115,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);
})); }));
@ -130,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);
} }
@ -198,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' });
@ -240,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,
@ -419,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() {
@ -461,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,
@ -471,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',
@ -521,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');
@ -582,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',
@ -652,13 +653,13 @@ 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;
}, },
@ -673,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) &&
@ -685,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)
@ -705,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() {
@ -756,7 +757,7 @@ const ScreenShield = new Lang.Class({
}); });
} }
return true; return GLib.SOURCE_CONTINUE;
}, },
_onDragBegin: function() { _onDragBegin: function() {
@ -852,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;
})); }));
} }
@ -917,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))
@ -1007,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;
@ -1101,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) {
@ -1114,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)
@ -1228,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,
@ -1250,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);
@ -1274,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,7 +259,7 @@ 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 });

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,567 +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(actor) {
this.emit('key-focus-in', actor);
},
_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 = this.provider.filterResults(providerResults, 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('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;
},
_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, actor) {
Util.ensureActorVisibleInScrollView(this._scrollView, actor);
},
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;
@ -94,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,16 +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 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,
@ -22,32 +32,63 @@ 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;
@ -56,203 +97,4 @@ const Indicator = new Lang.Class({
if (on) if (on)
this._item.status.text = ngettext("%d Connected Device", "%d Connected Devices", nDevices).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 MessageTray.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);

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,7 +11,6 @@ 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;
@ -22,6 +21,7 @@ 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'
@ -176,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();
@ -194,7 +194,7 @@ const NMConnectionSection = new Lang.Class({
}, },
_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);
@ -213,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();
} }
@ -298,6 +297,11 @@ const NMConnectionDevice = new Lang.Class({
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);
@ -305,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();
@ -365,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();
}, },
@ -415,6 +419,48 @@ 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,
@ -512,18 +558,6 @@ const NMDeviceBluetooth = new Lang.Class({
this.item.menu.addMenuItem(createSettingsAction(_("Mobile Broadband Settings"), device)); this.item.menu.addMenuItem(createSettingsAction(_("Mobile Broadband Settings"), device));
}, },
_autoConnect: function() {
// FIXME: DUN devices are configured like modems, so
// 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._client.add_and_activate_connection(connection, this._device, null, null);
return true;
},
_getMenuIcon: function() { _getMenuIcon: function() {
if (this._device.active_connection) if (this._device.active_connection)
return this.getIndicatorIcon(); return this.getIndicatorIcon();
@ -549,33 +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.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
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)
@ -606,16 +637,6 @@ const NMWirelessDialogItem = new Lang.Class({
return 'network-workgroup-symbolic'; return 'network-workgroup-symbolic';
else else
return 'network-wireless-signal-' + signalToIcon(this._ap.strength) + '-symbolic'; return 'network-wireless-signal-' + signalToIcon(this._ap.strength) + '-symbolic';
},
_onKeyPressEvent: function(actor, event) {
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return
|| symbol == Clutter.KEY_KP_Enter) {
this.emit('connect');
return true;
}
return false;
} }
}); });
Signals.addSignalMethods(NMWirelessDialogItem.prototype); Signals.addSignalMethods(NMWirelessDialogItem.prototype);
@ -624,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;
@ -633,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));
@ -748,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
@ -968,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) {
@ -984,9 +997,6 @@ const NMWirelessDialog = new Lang.Class({
Util.ensureActorVisibleInScrollView(this._scrollView, network.item.actor); Util.ensureActorVisibleInScrollView(this._scrollView, network.item.actor);
this._selectNetwork(network); this._selectNetwork(network);
})); }));
network.item.connect('connect', Lang.bind(this, function() {
this._connect();
}));
}, },
}); });
@ -994,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 = '';
@ -1065,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();
}, },
@ -1233,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();
}, },
@ -1255,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);
@ -1270,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;
@ -1293,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;
@ -1300,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;
@ -1322,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;
@ -1337,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));

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,44 +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…");
}, },
_sync: function() { _sync: function() {
function isBattery(result) { // Do we have batteries or a UPS?
if (!result) let visible = this._proxy.IsPresent;
return false; if (visible) {
let [device] = result;
let [, deviceType] = device;
return (deviceType == UPower.DeviceKind.BATTERY);
}
this._proxy.GetPrimaryDeviceRemote(Lang.bind(this, function(result, error) {
if (isBattery(result)) {
let [device] = result;
let [,, icon] = device;
let gicon = Gio.icon_new_for_string(icon);
this._indicator.gicon = gicon;
this._item.icon.gicon = gicon;
this._item.status.text = this._statusForDevice(device);
this._item.actor.show(); this._item.actor.show();
} else { } else {
// If there's no battery, then we use the power icon. // If there's no battery, then we use the power icon.
this._indicator.icon_name = 'system-shutdown-symbolic';
this._item.actor.hide(); 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();
}, },

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 {

View File

@ -1,7 +1,8 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
// //
// A widget showing the user avatar and name // A widget showing the user avatar and name
const Clutter = imports.gi.Clutter;
const AccountsService = imports.gi.AccountsService; const AccountsService = imports.gi.AccountsService;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
@ -56,6 +57,90 @@ const Avatar = new Lang.Class({
} }
}); });
const UserWidgetLabel = new Lang.Class({
Name: 'UserWidgetLabel',
Extends: St.Widget,
_init: function(user) {
this.parent({ layout_manager: new Clutter.BinLayout() });
this._user = user;
this._realNameLabel = new St.Label({ style_class: 'user-widget-label',
y_align: Clutter.ActorAlign.CENTER });
this.add_child(this._realNameLabel);
this._userNameLabel = new St.Label({ style_class: 'user-widget-label',
y_align: Clutter.ActorAlign.CENTER });
this.add_child(this._userNameLabel);
this._currentLabel = null;
this._userLoadedId = this._user.connect('notify::is-loaded', Lang.bind(this, this._updateUser));
this._userChangedId = this._user.connect('changed', Lang.bind(this, this._updateUser));
this._updateUser();
// We can't override the destroy vfunc because that might be called during
// object finalization, and we can't call any JS inside a GC finalize callback,
// so we use a signal, that will be disconnected by GObject the first time
// the actor is destroyed (which is guaranteed to be as part of a normal
// destroy() call from JS, possibly from some ancestor)
this.connect('destroy', Lang.bind(this, this._onDestroy));
},
_onDestroy: function() {
if (this._userLoadedId != 0) {
this._user.disconnect(this._userLoadedId);
this._userLoadedId = 0;
}
if (this._userChangedId != 0) {
this._user.disconnect(this._userChangedId);
this._userChangedId = 0;
}
},
vfunc_allocate: function(box, flags) {
this.set_allocation(box, flags);
let availWidth = box.x2 - box.x1;
let availHeight = box.y2 - box.y1;
let [minRealNameWidth, minRealNameHeight,
natRealNameWidth, natRealNameHeight] = this._realNameLabel.get_preferred_size();
let [minUserNameWidth, minUserNameHeight,
natUserNameWidth, natUserNameHeight] = this._userNameLabel.get_preferred_size();
if (natRealNameWidth <= availWidth)
this._currentLabel = this._realNameLabel;
else
this._currentLabel = this._userNameLabel;
let childBox = new Clutter.ActorBox();
childBox.x1 = 0;
childBox.y1 = 0;
childBox.x2 = availWidth;
childBox.y2 = availHeight;
this._currentLabel.allocate(childBox, flags);
},
vfunc_paint: function() {
this._currentLabel.paint();
},
_updateUser: function() {
if (this._user.is_loaded) {
this._realNameLabel.text = this._user.get_real_name();
this._userNameLabel.text = this._user.get_user_name();
} else {
this._realNameLabel.text = '';
this._userNameLabel.text = '';
}
},
});
const UserWidget = new Lang.Class({ const UserWidget = new Lang.Class({
Name: 'UserWidget', Name: 'UserWidget',
@ -67,21 +152,13 @@ const UserWidget = new Lang.Class({
this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._avatar = new Avatar(user); this._avatar = new Avatar(user);
this.actor.add(this._avatar.actor, this.actor.add_child(this._avatar.actor);
{ x_fill: true, y_fill: true });
this._label = new St.Label({ style_class: 'user-widget-label' }); this._label = new UserWidgetLabel(user);
this.actor.add(this._label, this.actor.add_child(this._label);
{ expand: true,
x_fill: true,
y_fill: false,
y_align: St.Align.MIDDLE });
this._userLoadedId = this._user.connect('notify::is-loaded', this._userLoadedId = this._user.connect('notify::is-loaded', Lang.bind(this, this._updateUser));
Lang.bind(this, this._updateUser)); this._userChangedId = this._user.connect('changed', Lang.bind(this, this._updateUser));
this._userChangedId = this._user.connect('changed',
Lang.bind(this, this._updateUser));
if (this._user.is_loaded)
this._updateUser(); this._updateUser();
}, },
@ -98,11 +175,6 @@ const UserWidget = new Lang.Class({
}, },
_updateUser: function() { _updateUser: function() {
if (this._user.is_loaded)
this._label.text = this._user.get_real_name();
else
this._label.text = '';
this._avatar.update(); this._avatar.update();
} }
}); });

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 Gtk = imports.gi.Gtk; const Gtk = imports.gi.Gtk;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta; const Meta = imports.gi.Meta;
@ -14,12 +15,9 @@ const AppDisplay = imports.ui.appDisplay;
const Main = imports.ui.main; const Main = imports.ui.main;
const OverviewControls = imports.ui.overviewControls; const OverviewControls = imports.ui.overviewControls;
const Params = imports.misc.params; const Params = imports.misc.params;
const RemoteSearch = imports.ui.remoteSearch;
const Search = imports.ui.search; const Search = imports.ui.search;
const SearchDisplay = imports.ui.searchDisplay;
const ShellEntry = imports.ui.shellEntry; const ShellEntry = imports.ui.shellEntry;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const Wanda = imports.ui.wanda;
const WorkspacesView = imports.ui.workspacesView; const WorkspacesView = imports.ui.workspacesView;
const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings'; const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings';
@ -66,8 +64,6 @@ const ViewSelector = new Lang.Class({
this._searchActive = false; this._searchActive = false;
this._searchTimeoutId = 0; this._searchTimeoutId = 0;
this._searchSystem = new Search.SearchSystem();
this._entry = searchEntry; this._entry = searchEntry;
ShellEntry.addContextMenu(this._entry); ShellEntry.addContextMenu(this._entry);
@ -103,24 +99,11 @@ const ViewSelector = new Lang.Class({
this._appsPage = this._addPage(this.appDisplay.actor, this._appsPage = this._addPage(this.appDisplay.actor,
_("Applications"), 'view-grid-symbolic'); _("Applications"), 'view-grid-symbolic');
this._searchResults = new SearchDisplay.SearchResults(this._searchSystem); this._searchResults = new Search.SearchResults();
this._searchPage = this._addPage(this._searchResults.actor, this._searchPage = this._addPage(this._searchResults.actor,
_("Search"), 'edit-find-symbolic', _("Search"), 'edit-find-symbolic',
{ a11yFocus: this._entry }); { a11yFocus: this._entry });
this._searchSettings = new Gio.Settings({ schema: Search.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));
// Default search providers
// Wanda comes obviously first
this.addSearchProvider(new Wanda.WandaSearchProvider());
this.addSearchProvider(new AppDisplay.AppSearchProvider());
// Load remote search providers provided by applications
RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider));
// Since the entry isn't inside the results container we install this // Since the entry isn't inside the results container we install this
// dummy widget as the last results container child so that we can // dummy widget as the last results container child so that we can
// include the entry in the keynav tab path // include the entry in the keynav tab path
@ -135,13 +118,11 @@ const ViewSelector = new Lang.Class({
this._stageKeyPressId = 0; this._stageKeyPressId = 0;
Main.overview.connect('showing', Lang.bind(this, Main.overview.connect('showing', Lang.bind(this,
function () { function () {
this._resetShowAppsButton();
this._stageKeyPressId = global.stage.connect('key-press-event', this._stageKeyPressId = global.stage.connect('key-press-event',
Lang.bind(this, this._onStageKeyPress)); Lang.bind(this, this._onStageKeyPress));
})); }));
Main.overview.connect('hiding', Lang.bind(this, Main.overview.connect('hiding', Lang.bind(this,
function () { function () {
this._resetShowAppsButton();
if (this._stageKeyPressId != 0) { if (this._stageKeyPressId != 0) {
global.stage.disconnect(this._stageKeyPressId); global.stage.disconnect(this._stageKeyPressId);
this._stageKeyPressId = 0; this._stageKeyPressId = 0;
@ -175,24 +156,28 @@ const ViewSelector = new Lang.Class({
}, },
show: function() { show: function() {
this._activePage = this._workspacesPage;
this.reset(); this.reset();
this._appsPage.hide();
this._searchPage.hide(); this._showAppsBlocked = true;
this._showAppsButton.checked = false;
this._showAppsBlocked = false;
this._workspacesPage.opacity = 255;
this._workspacesDisplay.show(); this._workspacesDisplay.show();
this._activePage = null;
this._syncActivePage();
if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows()) if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows())
Main.overview.fadeOutDesktop(); Main.overview.fadeOutDesktop();
this._showPage(this._workspacesPage, true);
}, },
zoomFromOverview: function() { leaveOverview: function() {
this._workspacesDisplay.zoomFromOverview();
if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows()) if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows())
Main.overview.fadeInDesktop(); Main.overview.fadeInDesktop();
// If we're not on the windows page, don't zoom back the primary monitor.
let zoomPrimary = (this._activePage == this._workspacesPage);
this._workspacesDisplay.leaveOverview(zoomPrimary);
}, },
setWorkspacesFullGeometry: function(geom) { setWorkspacesFullGeometry: function(geom) {
@ -201,12 +186,16 @@ const ViewSelector = new Lang.Class({
hide: function() { hide: function() {
this._workspacesDisplay.hide(); this._workspacesDisplay.hide();
if (this._activePage)
this._activePage.hide();
this._activePage = null;
}, },
_addPage: function(actor, name, a11yIcon, params) { _addPage: function(actor, name, a11yIcon, params) {
params = Params.parse(params, { a11yFocus: null }); params = Params.parse(params, { a11yFocus: null });
let page = new St.Bin({ child: actor, let page = new St.Bin({ child: actor,
visible: false,
x_align: St.Align.START, x_align: St.Align.START,
y_align: St.Align.START, y_align: St.Align.START,
x_fill: true, x_fill: true,
@ -221,6 +210,7 @@ const ViewSelector = new Lang.Class({
this._a11yFocusPage(page); this._a11yFocusPage(page);
}) })
});; });;
page.hide();
this.actor.add_actor(page); this.actor.add_actor(page);
return page; return page;
}, },
@ -230,7 +220,7 @@ const ViewSelector = new Lang.Class({
oldPage.hide(); oldPage.hide();
this.emit('page-empty'); this.emit('page-empty');
this._activePage.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
this._activePage.show(); this._activePage.show();
Tweener.addTween(this._activePage, Tweener.addTween(this._activePage,
{ opacity: 255, { opacity: 255,
@ -239,7 +229,7 @@ const ViewSelector = new Lang.Class({
}); });
}, },
_showPage: function(page, noFade) { _setActivePage: function(page) {
if (page == this._activePage) if (page == this._activePage)
return; return;
@ -247,7 +237,7 @@ const ViewSelector = new Lang.Class({
this._activePage = page; this._activePage = page;
this.emit('page-changed'); this.emit('page-changed');
if (oldPage && !noFade) if (oldPage)
Tweener.addTween(oldPage, Tweener.addTween(oldPage,
{ opacity: 0, { opacity: 0,
time: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME, time: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME,
@ -266,27 +256,32 @@ const ViewSelector = new Lang.Class({
page.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false); page.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
}, },
_onShowAppsButtonToggled: function() { _getActivePage: function() {
if (this._showAppsBlocked) if (this._searchActive)
return; return this._searchPage;
else if (this._showAppsButton.checked)
this._showPage(this._showAppsButton.checked ? return this._appsPage;
this._appsPage : this._workspacesPage); else
return this._workspacesPage;
}, },
_resetShowAppsButton: function() { _syncActivePage: function() {
this._showAppsBlocked = true; let activePage = this._getActivePage();
this._showAppsButton.checked = false; if (activePage == this._activePage)
this._showAppsBlocked = false; return;
this._setActivePage(activePage);
},
this._showPage(this._workspacesPage, true); _onShowAppsButtonToggled: function() {
if (!this._showAppsBlocked)
this._syncActivePage();
}, },
_onStageKeyPress: function(actor, event) { _onStageKeyPress: function(actor, event) {
// Ignore events while anything but the overview has // Ignore events while anything but the overview has
// pushed a modal (system modals, looking glass, ...) // pushed a modal (system modals, looking glass, ...)
if (Main.modalCount > 1) if (Main.modalCount > 1)
return false; return Clutter.EVENT_PROPAGATE;
let modifiers = event.get_state(); let modifiers = event.get_state();
let symbol = event.get_key_symbol(); let symbol = event.get_key_symbol();
@ -298,33 +293,11 @@ const ViewSelector = new Lang.Class({
this._showAppsButton.checked = false; this._showAppsButton.checked = false;
else else
Main.overview.hide(); Main.overview.hide();
return true; return Clutter.EVENT_STOP;
} else if (this._shouldTriggerSearch(symbol)) { } else if (this._shouldTriggerSearch(symbol)) {
this.startSearch(event); this.startSearch(event);
} else if (!this._searchActive) {
if (symbol == Clutter.Tab || symbol == Clutter.Down) {
this._activePage.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
return true;
} else if (symbol == Clutter.ISO_Left_Tab) {
this._activePage.navigate_focus(null, Gtk.DirectionType.TAB_BACKWARD, false);
return true;
} }
} return Clutter.EVENT_PROPAGATE;
return false;
},
_searchCancelled: function() {
this._showPage(this._showAppsButton.checked ? this._appsPage
: this._workspacesPage);
// Leave the entry focused when it doesn't have any text;
// when replacing a selected search term, Clutter emits
// two 'text-changed' signals, one for deleting the previous
// text and one for the new one - the second one is handled
// incorrectly when we remove focus
// (https://bugzilla.gnome.org/show_bug.cgi?id=636341) */
if (this._text.text != '')
this.reset();
}, },
reset: function () { reset: function () {
@ -417,8 +390,18 @@ const ViewSelector = new Lang.Class({
} }
this._entry.set_secondary_icon(null); this._entry.set_secondary_icon(null);
this._searchCancelled();
// Leave the entry focused when it doesn't have any text;
// when replacing a selected search term, Clutter emits
// two 'text-changed' signals, one for deleting the previous
// text and one for the new one - the second one is handled
// incorrectly when we remove focus
// (https://bugzilla.gnome.org/show_bug.cgi?id=636341) */
if (this._text.text != '')
this.reset();
} }
this._syncActivePage();
}, },
_onKeyPress: function(entry, event) { _onKeyPress: function(entry, event) {
@ -426,7 +409,7 @@ const ViewSelector = new Lang.Class({
if (symbol == Clutter.Escape) { if (symbol == Clutter.Escape) {
if (this._isActivated()) { if (this._isActivated()) {
this.reset(); this.reset();
return true; return Clutter.EVENT_STOP;
} }
} else if (this._searchActive) { } else if (this._searchActive) {
let arrowNext, nextDirection; let arrowNext, nextDirection;
@ -440,18 +423,18 @@ const ViewSelector = new Lang.Class({
if (symbol == Clutter.Tab) { if (symbol == Clutter.Tab) {
this._searchResults.navigateFocus(Gtk.DirectionType.TAB_FORWARD); this._searchResults.navigateFocus(Gtk.DirectionType.TAB_FORWARD);
return true; return Clutter.EVENT_STOP;
} else if (symbol == Clutter.ISO_Left_Tab) { } else if (symbol == Clutter.ISO_Left_Tab) {
this._focusTrap.can_focus = false; this._focusTrap.can_focus = false;
this._searchResults.navigateFocus(Gtk.DirectionType.TAB_BACKWARD); this._searchResults.navigateFocus(Gtk.DirectionType.TAB_BACKWARD);
this._focusTrap.can_focus = true; this._focusTrap.can_focus = true;
return true; return Clutter.EVENT_STOP;
} else if (symbol == Clutter.Down) { } else if (symbol == Clutter.Down) {
this._searchResults.navigateFocus(Gtk.DirectionType.DOWN); this._searchResults.navigateFocus(Gtk.DirectionType.DOWN);
return true; return Clutter.EVENT_STOP;
} else if (symbol == arrowNext && this._text.position == -1) { } else if (symbol == arrowNext && this._text.position == -1) {
this._searchResults.navigateFocus(nextDirection); this._searchResults.navigateFocus(nextDirection);
return true; return Clutter.EVENT_STOP;
} else if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) { } else if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) {
// We can't connect to 'activate' here because search providers // We can't connect to 'activate' here because search providers
// might want to do something with the modifiers in activateDefault. // might want to do something with the modifiers in activateDefault.
@ -460,10 +443,10 @@ const ViewSelector = new Lang.Class({
this._doSearch(); this._doSearch();
} }
this._searchResults.activateDefault(); this._searchResults.activateDefault();
return true; return Clutter.EVENT_STOP;
} }
} }
return false; return Clutter.EVENT_PROPAGATE;
}, },
_onCapturedEvent: function(actor, event) { _onCapturedEvent: function(actor, event) {
@ -478,51 +461,16 @@ const ViewSelector = new Lang.Class({
} }
} }
return false; return Clutter.EVENT_PROPAGATE;
}, },
_doSearch: function () { _doSearch: function () {
this._searchTimeoutId = 0; this._searchTimeoutId = 0;
let terms = getTermsForSearchString(this._entry.get_text()); let terms = getTermsForSearchString(this._entry.get_text());
this._searchResults.setTerms(terms);
this._searchSystem.updateSearchResults(terms); return GLib.SOURCE_REMOVE;
this._showPage(this._searchPage);
},
_shouldUseSearchProvider: function(provider) {
// the disable-external GSetting only affects remote providers
if (!provider.isRemoteProvider)
return true;
if (this._searchSettings.get_boolean('disable-external'))
return false;
let appId = provider.appInfo.get_id();
let disable = this._searchSettings.get_strv('disabled');
return disable.indexOf(appId) == -1;
},
_reloadRemoteProviders: function() {
// removeSearchProvider() modifies the provider list we iterate on,
// so make a copy first
let remoteProviders = this._searchSystem.getRemoteProviders().slice(0);
remoteProviders.forEach(Lang.bind(this, this.removeSearchProvider));
RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider));
},
addSearchProvider: function(provider) {
if (!this._shouldUseSearchProvider(provider))
return;
this._searchSystem.registerProvider(provider);
this._searchResults.createProviderDisplay(provider);
},
removeSearchProvider: function(provider) {
this._searchSystem.unregisterProvider(provider);
this._searchResults.destroyProviderDisplay(provider);
}, },
getActivePage: function() { getActivePage: function() {
@ -534,13 +482,6 @@ const ViewSelector = new Lang.Class({
return ViewPage.SEARCH; return ViewPage.SEARCH;
}, },
setActivePage: function(page) {
if (page == ViewPage.WINDOWS)
this._showPage(this._workspacesPage);
else
this._showPage(this._appsPage);
},
fadeIn: function() { fadeIn: function() {
let actor = this._activePage; let actor = this._activePage;
Tweener.addTween(actor, { opacity: 255, Tweener.addTween(actor, { opacity: 255,

View File

@ -1,160 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const GdkPixbuf = imports.gi.GdkPixbuf;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const St = imports.gi.St;
const IconGrid = imports.ui.iconGrid;
const Layout = imports.ui.layout;
const Main = imports.ui.main;
const Panel = imports.ui.panel;
const FISH_NAME = 'wanda';
const FISH_FILENAME = 'wanda.png';
const FISH_SPEED = 300;
const FISH_COMMAND = 'fortune';
// The size of an individual frame in the animation
const FISH_HEIGHT = 22;
const FISH_WIDTH = 36;
const FISH_GROUP = 'Fish Animation';
const MAGIC_FISH_KEY = 'free the fish';
const WandaIcon = new Lang.Class({
Name: 'WandaIcon',
Extends: IconGrid.BaseIcon,
_init : function(fish, label, params) {
this.parent(label, params);
this._fish = fish;
this._imageFile = GLib.build_filenamev([global.datadir, fish + '.png']);
this._imgHeight = FISH_HEIGHT;
this._imgWidth = FISH_WIDTH;
},
createIcon: function(iconSize) {
this._animations = new Panel.Animation(this._imageFile, this._imgWidth, this._imgHeight, FISH_SPEED);
this._animations.play();
return this._animations.actor;
},
_createIconTexture: function(size) {
if (size == this.iconSize)
return;
this.parent(size);
}
});
const WandaIconBin = new Lang.Class({
Name: 'WandaIconBin',
_init: function(fish, label, params) {
this.actor = new St.Bin({ reactive: true,
track_hover: true });
this.icon = new WandaIcon(fish, label, params);
this.actor.child = this.icon.actor;
this.actor.label_actor = this.icon.label;
},
});
const FortuneDialog = new Lang.Class({
Name: 'FortuneDialog',
_init: function(name, command) {
let text;
try {
let [res, stdout, stderr, status] = GLib.spawn_command_line_sync(command);
text = String.fromCharCode.apply(null, stdout);
} catch(e) {
text = _("Sorry, no wisdom for you today:\n%s").format(e.message);
}
this._title = new St.Label({ style_class: 'prompt-dialog-headline',
text: _("%s the Oracle says").format(name) });
this._label = new St.Label({ style_class: 'prompt-dialog-description',
text: text });
this._label.clutter_text.line_wrap = true;
this._box = new St.BoxLayout({ vertical: true,
style_class: 'prompt-dialog' // this is just to force a reasonable width
});
this._box.add(this._title, { align: St.Align.MIDDLE });
this._box.add(this._label, { expand: true });
this._button = new St.Button({ button_mask: St.ButtonMask.ONE,
style_class: 'modal-dialog',
reactive: true });
this._button.connect('clicked', Lang.bind(this, this.destroy));
this._button.child = this._box;
this._bin = new St.Bin({ x_align: St.Align.MIDDLE,
y_align: St.Align.MIDDLE });
this._bin.add_constraint(new Layout.MonitorConstraint({ primary: true }));
this._bin.add_actor(this._button);
Main.layoutManager.addChrome(this._bin);
GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 10, Lang.bind(this, this.destroy));
},
destroy: function() {
this._bin.destroy();
}
});
function capitalize(str) {
return str[0].toUpperCase() + str.substring(1, str.length);
}
const WandaSearchProvider = new Lang.Class({
Name: 'WandaSearchProvider',
_init: function() {
this.id = 'wanda';
},
getResultMetas: function(fish, callback) {
callback([{ 'id': fish[0], // there may be many fish in the sea, but
// only one which speaks the truth!
'name': capitalize(fish[0]),
'createIcon': function(iconSize) {
return new St.Icon({ gicon: Gio.icon_new_for_string('face-smile'),
icon_size: iconSize });
}
}]);
},
filterResults: function(results) {
return results;
},
getInitialResultSet: function(terms) {
if (terms.join(' ') == MAGIC_FISH_KEY) {
this.searchSystem.setResults(this, [ FISH_NAME ]);
} else {
this.searchSystem.setResults(this, []);
}
},
getSubsearchResultSet: function(previousResults, terms) {
this.getInitialResultSet(terms);
},
activateResult: function(fish) {
if (this._dialog)
this._dialog.destroy();
this._dialog = new FortuneDialog(capitalize(fish), FISH_COMMAND);
},
createResultObject: function (resultMeta, terms) {
return new WandaIconBin(resultMeta.id, resultMeta.name);
}
});

View File

@ -16,7 +16,7 @@ const WindowAttentionHandler = new Lang.Class({
_getTitleAndBanner: function(app, window) { _getTitleAndBanner: function(app, window) {
let title = app.get_name(); let title = app.get_name();
let banner = _("'%s' is ready").format(window.get_title()); let banner = _("%s is ready").format(window.get_title());
return [title, banner] return [title, banner]
}, },
@ -39,6 +39,9 @@ const WindowAttentionHandler = new Lang.Class({
let [title, banner] = this._getTitleAndBanner(app, window); let [title, banner] = this._getTitleAndBanner(app, window);
let notification = new MessageTray.Notification(source, title, banner); let notification = new MessageTray.Notification(source, title, banner);
notification.connect('clicked', function() {
source.open();
});
notification.setForFeedback(true); notification.setForFeedback(true);
source.notify(notification); source.notify(notification);
@ -79,7 +82,7 @@ const Source = new Lang.Class({
return this._app.create_icon_texture(size); return this._app.create_icon_texture(size);
}, },
open : function(notification) { open: function() {
Main.activateWindow(this._window); Main.activateWindow(this._window);
this.destroy(); this.destroy();
} }

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