Compare commits

...

604 Commits

Author SHA1 Message Date
4590903159 Bump version to 3.12.1
Update NEWS.
2014-04-15 23:37:57 +02:00
8b6fcbea50 layout: Don't always extend struts to the screen edge
NetWM struts are defined in terms of screen edges (rather than monitor
edges), which works poorly with vertical monitor layouts (as it renders
entire monitors unusable). Don't extend struts in those cases.

https://bugzilla.gnome.org/show_bug.cgi?id=663690
2014-04-15 23:36:33 +02:00
c6067ffa1d extension-prefs: Force linking with libgnome-shell-js
Use the same hack we use for the main executable to fool the linker
when using --as-needed.

https://bugzilla.gnome.org/show_bug.cgi?id=727948
2014-04-15 23:36:33 +02:00
7b4254da4e Revert "global: Run maybe_gc at leisure again"
It's causing segfaults so let's back it out for 3.12.1

This reverts commit 2f576e3076.
2014-04-15 14:14:47 +02:00
2f576e3076 global: Run maybe_gc at leisure again
The deadlocks are now fixed this should keep memory usage low,
avoid memory fragmentation and prevent the gc from running at
inconvient times.
2014-04-14 23:11:30 +02:00
60d7c3ad86 Updated Serbian translation 2014-04-14 19:33:40 +02:00
43fdc15ac0 l10n: Update Japanese translation 2014-04-15 00:36:18 +09:00
f082a58071 Updated Danish translation 2014-04-13 21:58:56 +02:00
ae5dfb5740 Updated Hungarian translation 2014-04-13 15:42:58 +02:00
e8fdba4f66 Updated Latvian translation 2014-04-12 12:36:23 +03:00
1030cdfe8a layout: Change setDummyCursorPosition to also set the size
If we are being used to follow a text entry cursor we also need to set
the size so that the popup menu avoids covering it.

https://bugzilla.gnome.org/show_bug.cgi?id=727579
2014-04-11 16:51:31 +02:00
ca2d626b59 Added Khmer translation 2014-04-10 23:35:19 +02:00
2b4eaf6b3f Updated Indonesian translation 2014-04-10 23:35:11 +02:00
6ce6e77d2a Tajik translation updated 2014-04-09 11:38:37 +05:00
0117fcb0e7 location: Don't hide location menu
If geoclue reports that we can't aquire location, we hide the menu. This
will typically happen when user is offline (and doesn't have a 3G
modem). This is likely not what we want since this like a temporary
situation and user would want the ability to toggle geolocation still
even if its currently not possible any applications to query the
location.

https://bugzilla.gnome.org/show_bug.cgi?id=727398
2014-04-08 14:11:28 +01:00
79bebe849d Updated Portuguese translation 2014-04-07 21:34:08 +00:00
25eadc5559 Updated Italian translation 2014-04-06 12:17:24 +00:00
a5784484e0 doap: update URLs 2014-04-04 19:32:09 +02:00
a701b006c5 Updated Italian translation 2014-04-03 07:08:23 +00:00
2d68bbf94e theme: Fix bad antialising on panel menu buttons
https://bugzilla.gnome.org/show_bug.cgi?id=727336
2014-04-01 19:47:28 +02:00
df305314c1 a11y: setting accessible role and label for windows at the overview
https://bugzilla.gnome.org/show_bug.cgi?id=726670
2014-04-01 13:56:53 +02:00
d03239c009 [l10n] Update Catalan translation 2014-03-31 23:53:35 +02:00
71ccad4399 Updated Galician translations 2014-03-31 23:35:41 +02:00
c916d43688 Updated Slovenian translation 2014-03-31 20:51:33 +02:00
3e6d0bc252 Updated French translation 2014-03-31 16:26:37 +00:00
79d0a848a4 Updated Russian translation 2014-03-31 15:48:54 +00:00
a6af33d450 Updated Lithuanian translation 2014-03-30 23:02:54 +03:00
c6664adcce Updated Swedish translation 2014-03-30 19:05:44 +00:00
4e8a9470d1 Updated Basque language 2014-03-30 12:56:28 +02:00
1b88df9439 update zh_CN translation 2014-03-30 16:57:39 +08:00
ec42278654 Updated German translation 2014-03-29 08:36:27 +01:00
f435f249d0 Updated Korean translation 2014-03-29 16:13:54 +09:00
9c88fec4fc texture-cache: use scale factor for load_uri_async()
https://bugzilla.gnome.org/show_bug.cgi?id=726907
2014-03-28 10:53:01 -07:00
9ecf466ce1 texture-cache: use scale factor to load background and borders
https://bugzilla.gnome.org/show_bug.cgi?id=726907
2014-03-28 10:53:01 -07:00
a22fdea0e3 border-image: add support for scale factor
In a later commit we'll add support for rendering borders and
backgrounds with scale factor.

https://bugzilla.gnome.org/show_bug.cgi?id=726907
2014-03-28 10:53:01 -07:00
90e52d7266 texture-cache: always call gdk_pixbuf_loader_set_size
Just use the regular image size if we don't want to scale it. This is a
refactor in preparation for the next commit.

https://bugzilla.gnome.org/show_bug.cgi?id=726907
2014-03-28 10:53:01 -07:00
74d9b6c2bf texture-cache: use scale factor to load sliced image
We need to use a GdkPixbufLoader instead of the straightforward
gdk_pixbuf_new_from_file(), since we want to load the image already
scaled if possible - e.g. if it's an SVG file.

https://bugzilla.gnome.org/show_bug.cgi?id=726907
2014-03-28 10:53:01 -07:00
b1e9873de5 Updated Spanish translation 2014-03-28 13:44:52 +01:00
6fdc52a64a Updated Indonesian translation 2014-03-28 11:58:53 +00:00
b7d6792de9 Updated Greek translation 2014-03-28 09:34:14 +02:00
c78dc55e65 Updated Czech translation 2014-03-27 22:17:34 +01:00
af74bded14 Updated Brazilian Portuguese translation 2014-03-27 20:53:59 +00:00
e3c9a9c3e4 Updated Hebrew translation 2014-03-27 20:25:06 +02:00
c68eecaf1c Updated Polish translation 2014-03-27 19:23:43 +01:00
b0bdf7f6c3 location: Improved language
* 'Turn On' -> 'Enable'
* 'Turn Off' -> 'Disable'
* 'Off' -> 'Disabled'
* 'On' ->  'In Use' or 'Enabled' depending on whether or not service is
  in use.

https://bugzilla.gnome.org/show_bug.cgi?id=726498
2014-03-27 17:42:11 +00:00
d0f69a72dc appDisplay: Ensure the currently focused icon is viewable
We either scroll or paginate to the correct place when an icon gets
key focus.

https://bugzilla.gnome.org/show_bug.cgi?id=726759
2014-03-26 11:42:34 +01:00
8d8c75d32d iconGrid: Add a key-focus-in signal
This fires whenever a grid's child emits its own key-focus-in signal.

https://bugzilla.gnome.org/show_bug.cgi?id=726759
2014-03-26 11:42:33 +01:00
e339e2658d search: Ensure that the default result is visible in the scroll view
The default result is set to selected when key focus enters the search
entry. We must also scroll the view if needed.

https://bugzilla.gnome.org/show_bug.cgi?id=726759
2014-03-26 11:42:32 +01:00
bc4a75a732 Update Arabic translation 2014-03-26 01:41:24 +02:00
e09e1bc3f5 Specifically ask for Telepathy 0.x
Telepathy 1.0 will not be compatible, and will probably require
source changes. telepathy-glib 0.12 and telepathy-logger 0.2 are
the 0.x ABIs (they were the first stable-branches to have g-i).

Bug: https://bugzilla.gnome.org/show_bug.cgi?id=721704
Reviewed-by: Giovanni Campagna
2014-03-25 17:19:28 +00:00
87c50eb495 Bump version to 3.12.0
Update NEWS.
2014-03-25 16:24:42 +01:00
be291ee4f9 loginScreen: reset greeter when coming back to login screen
When a user logs in to a wayland session, we keep the login screen
running on the X server with the login screen running in a deactivated mode.

This commit makes sure it get reactivated when the user comes back to
the VT (from user switching, logout or just ctrl-alt-f1).

https://bugzilla.gnome.org/show_bug.cgi?id=726989
2014-03-25 10:41:12 -04:00
e634b49859 Updated slovak translation 2014-03-24 23:40:30 +01:00
4dddaefa41 Updated Japanese translation 2014-03-23 23:22:30 +00:00
cda60455f0 Update Danish translation 2014-03-23 23:52:56 +01:00
a42b8870b0 Update Danish translation 2014-03-23 23:36:42 +01:00
daa66a6de6 Updated Danish translation 2014-03-23 23:29:51 +01:00
6c6aed84bc Updated Danish translation 2014-03-23 23:09:56 +01:00
c59314acc1 Updated Serbian translation 2014-03-23 11:12:07 +01:00
dd3cc78be5 French translation change for "Location" 2014-03-22 15:35:49 +01:00
54b0b6eec5 Updated Traditional Chinese translation(Hong Kong and Taiwan) 2014-03-22 20:04:22 +08:00
41654b22b3 Updated Slovenian translation 2014-03-22 10:03:14 +01:00
07cc60d65a Updated Slovenian translation 2014-03-21 21:44:00 +01:00
a7f9dc5114 Updated Telugu translation 2014-03-21 22:04:33 +05:30
b5ae23d544 Updated Arabic translation 2014-03-21 11:07:50 +01:00
75347cb4f7 Updated Arabic translation 2014-03-21 10:53:17 +01:00
03a44b6ec2 Updated Basque language 2014-03-20 22:56:30 +01:00
17d2349c49 Updated Russian translation 2014-03-20 23:00:58 +04:00
4e98c44052 Update Aragonese translation 2014-03-20 19:39:33 +01:00
0bef281d66 Bump version to 3.11.92
Update NEWS.
2014-03-19 22:07:59 +01:00
54af25ec24 Updated Danish translation 2014-03-19 22:04:16 +01:00
86ab02f400 Updated Hungarian translation 2014-03-19 09:47:15 +00:00
ae01cd143f Updated Norwegian bokmål translation. 2014-03-18 22:52:17 +01:00
2974b29f15 layout: Create a group for modal dialogs
This way we can ensure that they're always stacked under the OSK.

https://bugzilla.gnome.org/show_bug.cgi?id=719451
2014-03-18 17:03:39 +01:00
1b78dd662b modalDialog: Remove unused parentActor param
No ModalDialog users set this so let's hardcode it.

https://bugzilla.gnome.org/show_bug.cgi?id=719451
2014-03-18 17:03:38 +01:00
c22264a0ca BluetoothMenu: fix visibility
In addition to BluetoothAirplaneMode, we need to check also
BluetoothHasAirplaneMode, which is indicative of bluetooth rfkill
devices (and by extension bluetooth adapters).
This prevents showing the menu if there is no adapter present.

https://bugzilla.gnome.org/show_bug.cgi?id=725057
2014-03-17 16:45:30 +01:00
48c3e3f534 Tajik translation updated 2014-03-17 12:26:07 +05:00
6ccc134ba6 Updated Greek translation 2014-03-16 22:22:29 +02:00
9aa36d7851 Finnish translation update by Jiri Grönroos 2014-03-16 20:35:48 +02:00
3278f77739 Updated Hebrew translation. 2014-03-16 16:15:10 +02:00
5d24f48e3b Updated Ukrainian translation 2014-03-16 15:57:12 +02:00
573c1c86cc focus-manager: Make C ANSI-compliant 2014-03-15 15:44:40 -04:00
374c5967ba Updated French translation 2014-03-15 15:58:04 +00:00
66b71a36ce app-system: Filter out stale apps from cache
Since rebasing our AppSystem on GLib's facilities, we only ever
append to the id-to-app cache. So if an application is uninstalled,
shell_app_system_lookup_app() will still happily return it if it
was cached previously. For instance if a favorite app is uninstalled,
it keeps lurking in the dash until a restart.
To fix, filter out removed apps from the cache when handling
GAppInfoMonitor::installed-changed.

https://bugzilla.gnome.org/show_bug.cgi?id=726414
2014-03-15 15:56:30 +01:00
e70fd5a57a location: Make insensitive when locked
Most system menu entries are disabled on the lock/login screen,
there is no good reason for the location entry to be an exception.

https://bugzilla.gnome.org/show_bug.cgi?id=726319
2014-03-15 14:19:02 +01:00
4589ce4d78 bluetooth: Make insensitive when locked
Most system menu entries are disabled on the lock/login screen;
there is no good reason why users should be allowed to turn bluetooth
on/off (but not e.g. Wifi), so disable the entry as well.

https://bugzilla.gnome.org/show_bug.cgi?id=726319
2014-03-15 14:19:01 +01:00
d6197b0904 background: Destroy redundant backgrounds right after loading
When reacting to background settings changes, we may end up queuing
more than one load. The redundant backgrounds are expected to be
destroyed when the previous background has faded out; however since
commit 933f38390b, the tweened actor is the same for all
consecutive load operations and we end up with a single onComplete
handler, ergo a single destroyed actor.
As new backgrounds are always added to the bottom, we are not only
piling up additional background actors, but break changing backgrounds
more than once, as the correct background ends up being covered by
previously added redundant actors.
Fix this by destroying redundant actors right after loading rather
than waiting for the fade animation to complete.

https://bugzilla.gnome.org/show_bug.cgi?id=726120
2014-03-15 00:43:15 +01:00
30fb2b0d99 appDisplay: Improve icons of folders with few apps
Folders use a 2x2 grid of the first 4 app icons as icon - if a folder
contains less apps, we currently skip the corresponding grid positions.
As a result, the overall size request may be smaller than the one for
other icons, making the folder icon look out of place.
Fix this by always using a full grid as folder icon, using dummy actors
as necessary.

https://bugzilla.gnome.org/show_bug.cgi?id=726322
2014-03-15 00:23:50 +01:00
5cdefc324d ScreenShield: wake up the screen when resuming from suspend
At some point ScreenShield had code to do this, I don't know when
it was lost, but it makes sense and avoids having to move the mouse
just to see the shield.

https://bugzilla.gnome.org/show_bug.cgi?id=726378
2014-03-14 23:27:20 +01:00
b222d0fe44 appDisplay: Add some spacing in folder icons
With the switch to a table layout in commit f959cafb36, setting
alignments to place the individual icons at the outer edge of the grid
stopped working. Remove that code and add some explicit spacing instead.

https://bugzilla.gnome.org/show_bug.cgi?id=726323
2014-03-14 17:21:57 +01:00
fe4fddf0d5 gnome-shell-wayland.desktop: Add --display-server
This is now required by mutter to run as a native wayland compositor.
2014-03-14 16:53:59 +01:00
c675c93733 messageTray: Set the state to SHOWN when the notification is updated
If the notification is updated while SHOWING, we'll overwrite the
tween updating it to the new 'y' position, but forget to update the
state to SHOWN at the end of our transition. Make sure to always set
the state to SHOWN at the end.

https://bugzilla.gnome.org/show_bug.cgi?id=704844
2014-03-14 11:13:17 -04:00
29485ff24b layout: really queue region update before redraw
This code may have worked when written in 2009, but later gjs commit
b5e467d89aea43a8e32a1138d232c8a32e6b0785 removed the priority
parameter from idle_add.

Now, when running a constantly-updating client (es2gears) on an
embedded platform, _updateRegions() does not get called and I see
unresponsive window decorations.

Update the code to use meta_later_add() like other parts of the
shell, which is actually slightly better in this case anyway.
Solves the unresponsiveness problem.

https://bugzilla.gnome.org/show_bug.cgi?id=585500
2014-03-14 06:16:42 -06:00
f6ed3d9f88 authPrompt: Don't ever ask for a username if smartcard service is in foreground
The smartcard service is put in the foreground in two cases:

1) If password service is disabled by admin configuratoin
2) if a smartcard is inserted

In either case we don't want to ask the user to pick a user from the
userlist.  We currently only avoid asking in case 2.

This commit fixes case 1.

https://bugzilla.gnome.org/show_bug.cgi?id=726263
2014-03-13 15:08:00 -04:00
39a36cb510 Updated German translation 2014-03-13 20:01:15 +01:00
ff5550c82b util: fix "login card" smartcard detection on unlock screen
We only want to react to the card the user logged in with, at
the unlock screen.  We check "at the unlock screen" by checking
the "reauthenticating" state variable.  That variable is the
wrong one, though. It gets set too late, and in some cases, gets
set at the login screen, too.  We should be checking this._reauthOnly
instead.

This commit fixes that.

https://bugzilla.gnome.org/show_bug.cgi?id=726262
2014-03-13 14:01:02 -04:00
7d5ce1a159 screenShield: fix lifting when inserting smartcard
If a user inserts the smartcard they logged in with into the system,
it's supposed to lift the shield and prompt for pin.  That doesn't
happen because the parameter list of the smartcard-inserted signal
handler is wrong.

This commit fixes that.

https://bugzilla.gnome.org/show_bug.cgi?id=726262
2014-03-13 14:01:01 -04:00
c492415386 hidpi: Make sure gdk and clutter scaling stays disabled when the scale factor changes
Not doing that could led to messy situations when the user changes the scale factor
at runtime.

https://bugzilla.gnome.org/show_bug.cgi?id=726238
2014-03-13 14:16:11 +01:00
5616bbd45b hidpi: Listen for gtk-xft-dpi instead of monitors-changed
Currently we update the scale factor on startup and when we get a
monitors-changed signal, which is not the only cases where the setting changes. We cannot listen for gdk-window-scaling-factor changes because it is not
exported to gdk.

So use gtk-xft-dpi which also indicates a scale factor change.

When someone changes gtk-xft-dpi directly without changing the scale factor
we will just re-read the gdk-window-scaling-factor so no harm is done.

https://bugzilla.gnome.org/show_bug.cgi?id=726238
2014-03-13 14:16:11 +01:00
e117aa5297 Calendar: force-rebuild the calendar when the events change
Don't overoptimize and skip the rebuild when the month is the same,
as the calendar depends on the events too.

https://bugzilla.gnome.org/show_bug.cgi?id=726119
2014-03-13 14:12:38 +01:00
17ac1382df Updated Portuguese translation 2014-03-12 22:47:49 +00:00
057a026ea4 MessageTray: don't destroy notifications when the user presses ESC
Doing so is inconsistent with the behavior in the summary, and
is quite annoying when dealing with chats (because there is no way
to unfocus a chat notification with the keyboard only)

https://bugzilla.gnome.org/show_bug.cgi?id=724178
2014-03-12 19:33:35 +01:00
6ce6e86318 TelepathyClient: destroy an existing account notification when the user goes offline
We already filter new notifications caused by the explicit
user action, but we don't do so for changes to existing
notifications.

https://bugzilla.gnome.org/show_bug.cgi?id=723976
2014-03-12 19:26:42 +01:00
492558a2d2 search: survive the provider reporting the wrong number of metas
If a provider crashes during GetResultMetas, or returns the wrong
number of metas, report it as failing but don't cause a JS exception.

https://bugzilla.gnome.org/show_bug.cgi?id=725020
2014-03-12 19:26:41 +01:00
b78e00f372 search: destroy result actors before forgetting about them
We can't let live (ie, never destroyed) actors undergo GC, because
they will emit :destroy signals during finalization and assert/crash
libmozjs. Properly destroy all actors before letting the GC
free them.

https://bugzilla.gnome.org/show_bug.cgi?id=724798
2014-03-12 19:26:41 +01:00
c2cc504837 NetworkMenu: destroy the wifi dialog when the associate device disappears
When the device is removed by NetworkManager, we can't use it
to connect, so the dialog is useless and should be destroyed.

https://bugzilla.gnome.org/show_bug.cgi?id=723935
2014-03-12 19:26:41 +01:00
ac76940530 StTextureCache: don't crash if we fail to allocate a framebuffer
It's possible that FBO creation fails due to hw limits or the
driver not exposing the EXT_framebuffer_object extension.
In that case, just give up on creating square icons.

https://bugzilla.gnome.org/show_bug.cgi?id=724977
2014-03-12 19:26:41 +01:00
55d1c7e2ab background: fix cancellable issue
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.

Based on work from Jasper St. Pierre <jstpierre@macheye.net>

https://bugzilla.gnome.org/show_bug.cgi?id=722149
2014-03-12 12:15:43 -04:00
fdf264ff64 background: get rid of nested loop when finishing file loading
At the moment when a file is loaded, we iterate through the list of
pending file loads and ignore any unrelated to the file, then iterate
all the callers of the related file loads and finish them.

In fact, there can only ever be one pending file load related to the
file, and we already know it, so we can avoid the ugly nested loops.

https://bugzilla.gnome.org/show_bug.cgi?id=722149
2014-03-12 12:15:42 -04:00
e917b7ce0f background: refactor file loading
This commit moves the code around a bit such that the
caller gets allocated up front and then a file load is either
found or created to attach the caller to.

Functionally, the code is the same, it's just now factored in a way
that will make it easier to fix a bug with cancellation later.

https://bugzilla.gnome.org/show_bug.cgi?id=722149
2014-03-12 12:12:47 -04:00
ec6facb9e7 background: always copy background content when loading into cache
Copying is actually a lightweight operation, so trying to avoid it just adds
code complexity for little gain.

Based on work from Jasper St. Pierre <jstpierre@macheye.net>

https://bugzilla.gnome.org/show_bug.cgi?id=722149
2014-03-12 12:12:47 -04:00
60f3c09f90 Updated Czech translation 2014-03-12 15:53:48 +01:00
afdfd6cebc Updated Russian translation 2014-03-11 22:30:13 +04:00
edd66c40d9 ScreenShield: send a signal to GSD to wake up the screen
Instead of poking through IDLETIME, which confuses the state tracking
and can prevent automatic suspend, send a special signal to GSD
when the screen is to be waken up for a notification.

Someday we'll bring over all the state tracking and avoid this
ping-pong between gnome-shell and gnome-settings-daemon, but
that day's not today.

https://bugzilla.gnome.org/show_bug.cgi?id=712706
2014-03-11 18:10:05 +01:00
fc4bc5277a Lightbox: complete radial effect for modal dialogs
Rework the radial effect to use similar code to MetaBackground,
and adjust parameters to make it more noticeable.

https://bugzilla.gnome.org/show_bug.cgi?id=725830
2014-03-11 17:14:07 +01:00
821768a414 network: Don't show network-offline-symbolic in the top bar
This is against the designs.

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

https://bugzilla.gnome.org/show_bug.cgi?id=721459
2014-03-11 10:01:07 -04:00
522f3bf171 NMConnectionSection: don't remove connections that were never added
NMApplet will call removeConnection() unconditionally on all sections,
including those that had nothing to do with the connection in the first
place.

Fixes:

Gjs-WARNING **: JS ERROR: TypeError: this._connectionItems.get(...) is undefined
NMConnectionSection<.removeConnection@resource:///org/gnome/shell/ui/status/network.js:323
wrapper@resource:///org/gnome/gjs/modules/lang.js:169
NMApplet<._connectionRemoved@resource:///org/gnome/shell/ui/status/network.js:1885
wrapper@resource:///org/gnome/gjs/modules/lang.js:169

https://bugzilla.gnome.org/show_bug.cgi?id=725958
2014-03-10 17:49:43 +01:00
fb7400ab85 Updated Chinese (Taiwan) translation 2014-03-09 04:26:03 +00:00
210128f22b Updated Lithuanian translation 2014-03-08 21:49:25 +02:00
78ae233823 Updated Chinese (China) translation 2014-03-08 14:04:28 +00:00
8f25da7cea Updated Latvian translation 2014-03-08 11:48:25 +02:00
7f52fdb435 update Punjabi Translation for gnome-shell: Alam 2014-03-07 19:31:38 -06:00
0ba05b29b9 loginDialog: Defer loading user list until needed
Loading the user list can be expensive, for instance when there is
a large number of users and/or their avatars have to be fetched over
the network. In case the user list is disabled anyway, there is no
point in doing that work just to hide it, so stop doing that.

https://bugzilla.gnome.org/show_bug.cgi?id=725905
2014-03-08 00:29:11 +01:00
ef8123e3a2 loginDialog: Add missing return value
_loadUserList() may be used as idle handler, so we should explicitly
return a boolean.

https://bugzilla.gnome.org/show_bug.cgi?id=725905
2014-03-08 00:29:10 +01:00
257e1f3096 calendar: Grab key focus when user changes day
When the user changes the active day by mouse click or keyboard focus
plus key press on a day in the grid, always move the keyboard focus to
the newly activated day.

This basically restores functionality that was introduced in commit
31478e9fb4 but got lost again in the re-factoring in commit
cc4659f5c6.

https://bugzilla.gnome.org/show_bug.cgi?id=725606
2014-03-07 19:18:32 +01:00
6441ae77d9 Updated Indonesian translation 2014-03-07 06:48:50 +00:00
3f0938072f Updated Kazakh translation 2014-03-07 02:35:31 +00:00
2fe06a28aa Updated Czech translation 2014-03-06 18:18:06 +01:00
38750ba798 LookingGlass: add GObject and Gio to the ns available inline
Having to go through import.gi is awfully inconvenient when one
is using the looking glass as a quick gjs console, and we're
already importing all sorts of stuff there.

https://bugzilla.gnome.org/show_bug.cgi?id=725832
2014-03-06 16:57:41 +01:00
b4c01f8905 Updated Galician translations 2014-03-06 12:27:41 +01:00
104d70c88e Updated Friulian translation 2014-03-06 11:11:35 +01:00
133a350f2f Updated Brazilian Portuguese translation 2014-03-06 08:38:30 +00:00
496ab55357 Updated Friulian translation 2014-03-06 04:59:38 +01:00
a391758e31 Updated Friulian translation 2014-03-06 04:23:40 +01:00
eaf8ad4949 Updated Friulian translation 2014-03-06 03:59:10 +01:00
2f583bdcf3 Updated Friulian translation 2014-03-06 03:45:24 +01:00
db19012a41 Bump version to 3.11.91
Update NEWS.
2014-03-06 00:23:41 +01:00
62be46884e calendar: Don't forget to rebuild the calendar
Don't forget to rebuild the calendar when changing the setting
'org.gnome.shell.calendar show-weekdate'. This wasn't happening anymore
and changing the setting resulted in a calendar without the days
grid.

https://bugzilla.gnome.org/show_bug.cgi?id=725533
2014-03-05 20:14:56 +01:00
3f2e6a48a9 Updated Hebrew translation 2014-03-05 18:58:56 +02:00
ff124e5f74 Updated Spanish translation 2014-03-05 16:58:35 +01:00
c07421c195 Calendar: make current date label clickable
Once you start navigating between months, you can't return to the
current day.  However, the current day is always displayed above the
calendar grid.  Fix this by making the current date clickable; when
clicked, the calendar grid jumps back to that day.

https://bugzilla.gnome.org/show_bug.cgi?id=641366
2014-03-05 16:40:56 +01:00
de8348d3b9 Updated Spanish translation 2014-03-05 16:39:11 +01:00
184df8a853 Updated Korean translation 2014-03-05 19:55:27 +09:00
12768a147c Updated Norwegian bokmål translation 2014-03-05 06:47:35 +01:00
94161cea37 Updated Polish translation 2014-03-04 18:18:35 +01:00
52a4ef7cf7 Updated Hebrew translation 2014-03-04 18:30:55 +02:00
3432f71500 NetworkMenu: use Connected instead of profile name for wired and wwan
The profile name is most often not useful (because it's an
autogenerated string like "Wired Connection 1"), and even when it
is, it's already available in the submenu.

https://bugzilla.gnome.org/show_bug.cgi?id=725586
2014-03-04 16:08:02 +01:00
8282aa6c24 Updated Galician translations 2014-03-03 17:34:02 +01:00
59f9eaa1c9 NetworkAgent: fix initial secrets requests after 17726abb
While the named commit was correct for VPN connections, it didn't
work correctly for the initial secrets requests like when connecting
to a new access point.  In that case, secrets *should* be requested
when none are found, but only if interaction is enabled.  The
bits of 17726abb which removed checking secrets against the hints
*were* correct, but 17726abb removed too much.

Also, to ensure passwords don't get inadvertently cleared when
simply reading them from the keyring, don't save passwords
unless something might have changed.

https://bugzilla.gnome.org/show_bug.cgi?id=724779
2014-03-03 15:17:43 +01:00
4433b735c4 Updated Lithuanian translation 2014-03-02 19:57:10 +02:00
9cd30fa6b5 Updated Polish translation 2014-03-02 17:24:18 +01:00
2c7bbfb500 Fix a typo 2014-03-02 17:21:34 +01:00
51a1d23bf9 Finnish translation update by Jiri Grönroos 2014-03-01 20:15:45 +02:00
c02e6e82bc Update Arabic translation 2014-02-28 23:09:15 +02:00
e37a3fa7e6 perf: Restore shell after runs
Currently running the perf tool results into no wm running
afterwards making it hard for the user to get the results from a terminal
and generally does not make it easy for users to run it to gather numbers.

So restore the shell after the test has completed.

https://bugzilla.gnome.org/show_bug.cgi?id=724870
2014-02-28 21:40:31 +01:00
e23c2ffecc plugin: Don't query for swap_events support directly
There is no need for the duplication, clutter already exports
this information.

https://bugzilla.gnome.org/show_bug.cgi?id=724870
2014-02-28 21:40:31 +01:00
744f11d045 Updated Spanish translation 2014-02-28 14:13:08 +01:00
b7eb1f3e8b Don't use get_core_device
clutter_device_manager_get_core_device calls XIGetClientPointer, which
requires a round-trip to the server. Since we do this on StWidget
creation, this means a full round-trip for every created StWidget.
Replace this with get_device with the ID of the VCP/VCK, since mutter
doesn't support MPX, and we know this is what the device is.
2014-02-27 14:55:29 -05:00
3f28091e52 Tajik translation updated 2014-02-27 12:13:18 +05:00
b4ee86955d messageTray: Make sure that the source actor is square
Commit b7e1539699 removed the size to support hidpi but that caused the
actor to no longer be square. Fix that by going back to setting a size
but apply the scale factor before doing so.
2014-02-26 18:15:38 +01:00
8b866efe92 location: Adapt to new Geoclue agent interface
To be able to correctly setup dbus policy, I had to change the expected
agent D-Bus API a bit: Now object path is fixed and not different for
each user.

https://bugzilla.gnome.org/show_bug.cgi?id=725082
2014-02-25 12:45:32 +00:00
fb61ab8df7 Updated Spanish translation 2014-02-24 17:28:11 +01:00
990956ece7 Updated Galician translations 2014-02-24 15:22:12 +01:00
414b592d53 Updated Hebrew translation 2014-02-24 15:25:37 +02:00
751154d9da shell-util: Fix warning in shell_util_cursor_tracker_to_clutter
If the sprite is NULL, like if a Wayland app wanted to hide the cursor,
then we need to hide the ClutterTexture on our side, as ClutterTexture
has no easy way to tell it to paint nothing.
2014-02-23 10:54:32 -05:00
29addc499c shell-global: Only set the scale factor if get_setting succeeded
If gdk_screen_get_setting fails, like if it's running without XSettings,
then the GValue will have a value of 0. A lot of code tries to divide by
the scale factor. This produces NaN, and combined with the fact that NaN
is "leaky", we very quickly end up spinning out of control.
2014-02-23 10:54:32 -05:00
caa98de581 Updated Hebrew translation 2014-02-23 14:16:30 +02:00
1fd1ec4312 Updated Chinese (China) translation 2014-02-23 09:57:11 +00:00
f4607626e4 workspaceThumbnails: Really fix DND creating new workspaces
The same problem spotted in commit 89a2dc71fc is present in
some more places, fix those as well.

https://bugzilla.gnome.org/show_bug.cgi?id=724686
2014-02-23 00:29:29 +01:00
b494c15e4b shell-js: Kill some compile warnings
jsapi.h has some bad warnings with gcc. gjs-module.h already includes
jsapi.h and uses a complicated set of GCC pragmas to mask them out, so
just kill our include.
2014-02-22 17:42:03 -05:00
3c0defa125 Updated Belarusian translation. 2014-02-22 11:27:32 +01:00
f2df4d95de Updated Czech translation 2014-02-22 08:51:14 +01:00
fabcf20e06 Updated Kazakh translation 2014-02-21 17:25:04 +00:00
b9510b9ab7 Updated Spanish translation 2014-02-21 13:10:31 +01:00
52a2ebad04 Updated Brazilian Portuguese translation 2014-02-21 12:02:52 +00:00
89a2dc71fc workspaceThumbnails: Fix creating new workspace via DND
Commit 61a58ff3c9 replaced the (removed in commit 254afc50223a7)
MetaWindowActor.get_workspace() method by MetaWindow.get_workspace(),
but did not take into account that the return values differ - the
former returns the workspace index, the latter the workspace object.

https://bugzilla.gnome.org/show_bug.cgi?id=724686
2014-02-21 11:48:55 +01:00
adb0de43d8 Updated Indonesian translation 2014-02-21 06:46:22 +00:00
e2a811a720 Updated Greek translation 2014-02-20 23:20:56 +02:00
5bcafc5c17 Updated Lithuanian translation 2014-02-20 22:42:10 +02:00
2d24536caf shellDBus: Fix LaunchExtensionPreferences()
Commit 5dedc5d8ba removed "unused" functionality which was
still used by that method. Launch it via GAppInfo instead, which
still supports passing URIs.

https://bugzilla.gnome.org/show_bug.cgi?id=724813
2014-02-20 20:27:02 +01:00
b088c4086b Tajik translation updated 2014-02-20 17:00:40 +05:00
3c58f4abd3 Bump version to 3.11.90
Update NEWS.
2014-02-20 00:31:55 +01:00
e2a9b27b2b Revert "viewSelector: Give the active page key focus when it is shown"
This broke keyboard navigation in the app picker, which is not an
acceptable regression for the release.

This reverts commit ec2bb039ae.
2014-02-20 00:29:14 +01:00
fcd5f06c09 windowManager: Animate tile previews
With tile previews being implemented as Clutter actors in the shell, we
can now easily add fancy animations when showing/hiding the preview.
Besides looking more polished, the animations also help understanding
what will happen to the window when the drag is finished.

https://bugzilla.gnome.org/show_bug.cgi?id=665758
2014-02-20 00:29:14 +01:00
6d93c8b3fd windowManager: Implement tile previews
Mutter now delegates tile previews to compositor plugins, so
add a simple implementation based on the UI previously provided
by mutter.

https://bugzilla.gnome.org/show_bug.cgi?id=665758
2014-02-20 00:29:14 +01:00
2663e1be5d window-tracker: Be more cautious when setting focus app
Since we started tracking non-interesting windows, we can no longer
assume that if we manage to find an app associated with the focus window,
it should appear focused - we now can find apps for docks, the DESKTOP
window etc.
To restore the old behavior, make sure that the focus window or one of
its parents is "interesting".

https://bugzilla.gnome.org/show_bug.cgi?id=722928
2014-02-20 00:29:14 +01:00
0fa6be4614 endSessionDialog: Add extra strings for translation
Add two extra, currently unused strings in order to make the life easier
for translators and avoid breaking the upcoming string freezes.

https://bugzilla.gnome.org/show_bug.cgi?id=722898
2014-02-20 00:27:35 +01:00
46163a6607 endSessionDialog: Warn when trying to install updates on battery power
Interrupting update installation can mess up the package database quite
a bit and could lead to totally destroying the distro installtion. To
avoid running out of juice during an upgrade, warn when someone tries to
install updates on battery power.

https://bugzilla.gnome.org/show_bug.cgi?id=722898
2014-02-20 00:27:35 +01:00
645ef093f7 endSessionDialog: Offer offline updates in the shutdown dialog
This adds a checkbox for installing software updates to the shut down
dialog. The implementation relies on an already prepared offline update
and uses PackageKit's pk-trigger-offline-update helper to trigger the
installation.

https://bugzilla.gnome.org/show_bug.cgi?id=722898
2014-02-20 00:27:35 +01:00
7551e134da endSessionDialog: Fix the "Restart & Install" button
This moves the dialog type overriding that gnome-software uses to bring
up restartInstallDialogContent from _sync() to OpenAsync(), in order to
ensure the type is overridden early enough to show the correct buttons.

While at this, make sure the & symbol in the button's label is escaped
to avoid runtime warnings, now that the label actually gets used.

https://bugzilla.gnome.org/show_bug.cgi?id=722898
2014-02-20 00:27:35 +01:00
5bec5fb6cb endSessionDialog: Simplify CSS padding handling
... so that we add more items to messageLayout without having to
hardcode the same padding in each of the children.

https://bugzilla.gnome.org/show_bug.cgi?id=722898
2014-02-20 00:27:35 +01:00
c176af4da5 endSessionDialog: Use a 48px icon as per mockups
https://bugzilla.gnome.org/show_bug.cgi?id=722898
2014-02-20 00:27:35 +01:00
2631f03108 windowManager: Don't remove the active workspace
Currently workspaces (except for the last one) are removed when
they become empty. While we do have special treatment for the
case where the currently active workspace is removed, we just
move directly without animations to the last workspace to avoid
ending up on a "random" workspace. However this behavior is still
a bit confusing, so keep the workspace around instead until the
user decides to move to another one.

https://bugzilla.gnome.org/show_bug.cgi?id=709064
2014-02-19 21:52:52 +01:00
525c71658b Updated Norwegian bokmål translation 2014-02-19 19:45:45 +01:00
10e5778deb Updated Brazilian Portuguese translation 2014-02-19 18:04:26 +00:00
6512a5fd6b browser_plugin: Expose version validation boolean
Expose the version validation boolean through the public JS
API so that e.g.o can make use of it.

https://bugzilla.gnome.org/show_bug.cgi?id=724683
2014-02-19 17:07:38 +01:00
1af40b1345 extensionSystem: Add a gsettings key to disable version validation
Add a key 'disable-extension-version-validation' key that disables
the validation of extension's claimed to be supported shell version
with the shell version and just load all extensions unconditionally.

https://bugzilla.gnome.org/show_bug.cgi?id=724683
2014-02-19 17:07:38 +01:00
0418b68051 popupMenu: Use an image for arrows
Until now the arrows were the associated arrow
character of the font. This cause some problems like
different arrows for different fonts, and size can be
altered because of the font size.
To solve that, use an image for the arrows.

https://bugzilla.gnome.org/show_bug.cgi?id=720206
2014-02-19 13:44:27 +01:00
a7283864e8 popupMenu: Use relative rotation for arrows
Currently the animation of the arrows doesn't take
into account previous rotation.
Since in a incoming patch we will use one arrow
and rotate it to generate the four directions, we
need that the animation use relative rotation.

https://bugzilla.gnome.org/show_bug.cgi?id=720206
2014-02-19 13:44:26 +01:00
4950bad2a7 popupMenu: Rename _arrow_rotation to follow JS covention 2014-02-19 13:44:26 +01:00
470ac0eae3 shell_global: Use correct agruments for in update_scale_factor
Otherwise we crash ...

https://bugzilla.gnome.org/show_bug.cgi?id=705410
2014-02-18 23:40:34 +01:00
87abbf9b20 hdpi: Revert hacks
Revert the hacks that where added in response to a bug caused by
commit ba459f4d20

https://bugzilla.gnome.org/show_bug.cgi?id=705410
2014-02-18 23:40:34 +01:00
3e7e88cd5f popupMenu: Make destroy() a bit more robust
Currently destroying a menu's actor and calling its destroy() method
are subtly different - the latter will also result in the menu being
removed from the corresponding menu manager.
There is no good justification for that behavior, so make both actions
behave the same.

https://bugzilla.gnome.org/show_bug.cgi?id=724690
2014-02-18 21:27:24 +01:00
b7e1539699 messageTray: don't force a size on SourceActor's iconBin
Supports hidpi icons this way.

https://bugzilla.gnome.org/show_bug.cgi?id=724607
2014-02-18 07:46:54 -08:00
8492f2ba24 osdWindow: scale by scaleFactor
Since we calculate this dimensions from the size of the monitor, the
result was too large. Scale it by scaleFactor instead.

https://bugzilla.gnome.org/show_bug.cgi?id=724607
2014-02-18 07:46:07 -08:00
737f4eb1c1 dateMenu: Update gnome-clocks' desktop file
https://bugzilla.gnome.org/show_bug.cgi?id=724282
2014-02-18 14:52:48 +01:00
58191ea66b telepathyClient: Use locale format for timestamps
Until now the timestamps were using 24h format.
Check gsetting clock-format to know when
the user is using 12h format or 24h format and
make the timestamp acordingly.

https://bugzilla.gnome.org/show_bug.cgi?id=715158
2014-02-18 11:27:34 +01:00
1f786df462 NetworkMenu: add VPN settings menu item
Add a menu item that launches the VPN page of the network panel,
or the network panel generically if multiple VPNs are configured.

https://bugzilla.gnome.org/show_bug.cgi?id=709167
2014-02-18 00:17:11 +01:00
fa4c481aed telepathyClient: Use ratio char for timestamps 2014-02-17 20:52:51 +01:00
d555fd7883 ShellApp: remove impossible check
The action muxer is created and destroyed with the running state,
it can never be NULL.

https://bugzilla.gnome.org/show_bug.cgi?id=722554
2014-02-17 20:44:58 +01:00
fe7ece1f5a NetworkMenu: use radio items instead of switches for multiple profiles
Multiple connections for the same device are mutually exclusive, so
a switch is not the appropriate UI metaphor. Use a radio item instead,
and provide a separate "Turn off" item to disconnect.
Behavior when there is only one connection is not changed, there
is a single Connect/Turn off item.

https://bugzilla.gnome.org/show_bug.cgi?id=723570
2014-02-17 20:44:58 +01:00
2bb3aed729 NetworkMenu: sort connections by name only
We don't watch for timestamp changes, and sorting by name keeps
the order consistent and predictable. In practice, there should
be at most 3 or 4 elements, so the user will always read all them
at once and sorting is irrelevant.

https://bugzilla.gnome.org/show_bug.cgi?id=723570
2014-02-17 20:44:57 +01:00
488a42696c NetworkMenu: fix connection name update
If the connection name is changed, the UUID doesn't necessarily,
so checkConnection would take the early return path. Make sure
we update the existing menu item too.

https://bugzilla.gnome.org/show_bug.cgi?id=723570
2014-02-17 20:44:57 +01:00
f43ff45683 NetworkMenu: update for latest designs
Distinguish reaching the full internet from being in a non-routed
LAN, and show a different icon when being an hotspot master.

https://bugzilla.gnome.org/show_bug.cgi?id=723570
2014-02-17 20:44:57 +01:00
bde1451896 NMDeviceWired: don't unconditionally access device.active_connection
The active_connection might be null, but the NMApplet code might
still belive the device wrapper is the primary connection (because
dbus signals are emitted one after the other).
(Also, remove an old and wrong comment about bluetooth in wwan code)

Fixes:

(gnome-shell:22511): Gjs-WARNING **: JS ERROR: Exception in callback for signal: icon-changed: TypeError: this._device.active_connection is null
NMDeviceWired<.getIndicatorIcon@resource:///org/gnome/shell/ui/status/network.js:475
wrapper@resource:///org/gnome/gjs/modules/lang.js:169
NMApplet<._updateIcon@resource:///org/gnome/shell/ui/status/network.js:1790
wrapper@resource:///org/gnome/gjs/modules/lang.js:169
_emit@resource:///org/gnome/gjs/modules/signals.js:124
NMConnectionSection<._addConnection/<@resource:///org/gnome/shell/ui/status/network.js:265
_emit@resource:///org/gnome/gjs/modules/signals.js:124
NMConnectionItem<._sync@resource:///org/gnome/shell/ui/status/network.js:137
wrapper@resource:///org/gnome/gjs/modules/lang.js:169
NMConnectionItem<.setActiveConnection@resource:///org/gnome/shell/ui/status/network.js:169
wrapper@resource:///org/gnome/gjs/modules/lang.js:169
NMConnectionDevice<._activeConnectionChanged@resource:///org/gnome/shell/ui/status/network.js:327
wrapper@resource:///org/gnome/gjs/modules/lang.js:169

https://bugzilla.gnome.org/show_bug.cgi?id=723570
2014-02-17 20:44:57 +01:00
fff2ca6f26 telephatyIdle: Time stamps next conversation line
To give more context to the chat notification bubble, put the
time stamp next to the last conversation line instead of
a new line.

https://bugzilla.gnome.org/show_bug.cgi?id=708031
2014-02-17 20:00:00 +01:00
bec57a6cee Revert "location: Allow user to disable GPS"
This reverts commit 5d05b66902.

Had a long discussion with Bastien Nocera and Allan Day on IRC about
this and in the end we decided to go with the simple on/off controls for
now.

Conflicts:
	js/ui/status/location.js

https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-17 15:49:34 +00:00
a012ca4fac layout: Don't query the monitor size twice 2014-02-17 13:42:20 +01:00
3ba49b0a50 layout: Protect against broken monitor size reports
VNC / XRDP reports nonsensial (i.e 0) monitor dimensions causing us to
end up with a dpi of "Infinity" and thus scale even though we shouldn't.

https://bugzilla.redhat.com/show_bug.cgi?id=1065788
2014-02-17 13:38:55 +01:00
314aa024b5 appDisplay: Add time to view switch animation
Add more time to the fade in/out animation when switching
views so they don't switch abruptly and add a delay
between view switch animations to avoid morphing.

https://bugzilla.gnome.org/show_bug.cgi?id=722331
2014-02-17 13:31:52 +01:00
598f750859 messageTray: Add source actor without count label
Split the current implementation of SourceActor into
SourceActor and SourceActorWithLabel.
In this manner we can use source actors withouth count labels,
required in the screenShield to not clash with the count
text label.

https://bugzilla.gnome.org/show_bug.cgi?id=709275
2014-02-17 13:22:39 +01:00
8057848458 location: Rename _onoffAction to _onOffAction
It was just a typo that this patch now corrects.

https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-17 12:08:40 +00:00
e80c28a530 location: Mark 2 user-visible strings for translation
https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-17 12:08:40 +00:00
5d05b66902 location: Allow user to disable GPS
When exact accuracy (i-e GPS) is available, allow user to disable that.
When users don't want application to get their precise location, they
can now opt for network-based geolocation only, which can be
street-level at best.

https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-17 12:08:40 +00:00
59634b2cf5 location: Rename _updateMenuVisibility to _updateMenu
We'll soon be doing more than just toggling the accuracy of menu itself
in this function. Hence the renaming.

https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-17 12:08:40 +00:00
7c3892f5a2 location: disconnect from 'g-properties-changed' signal
https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-17 12:08:40 +00:00
19406a238b location: 'MaxAccuracyLevel' property is now read-only
https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-17 12:08:40 +00:00
d6146197dd location,schema: Keep max accuracy in gsetting
Instead of relying on geoclue to store this user configuration, lets
keep it in gsettings. Geoclue is a system service and therefore is not
the appropriate entity to keep this info.

https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-17 12:08:39 +00:00
38f241479c location: Coding style fixes
No space between identifier and '('.

https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-17 12:08:39 +00:00
aa45999824 location: Rename _setAccuracy to setMaxAccuracyLevel
More descriptive name to avoid any confusion.

https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-17 12:08:39 +00:00
3b7593ed7f altTab: Scale thumbnails by the scale factor
Fixes their size on high dpi.

https://bugzilla.gnome.org/show_bug.cgi?id=705410
2014-02-16 16:55:55 +01:00
f959cafb36 appDisplay: don't force size on folder icon actor
Use a table layout instead.

https://bugzilla.gnome.org/show_bug.cgi?id=705410
2014-02-16 07:49:25 -08:00
e92d204d42 dash: account for scale factor to determine icon size
https://bugzilla.gnome.org/show_bug.cgi?id=705410
2014-02-16 07:49:25 -08:00
f543161234 st-icon: remove custom size request/allocate
Use a layout manager instead. This has the effect of not enforcing a
priv->icon_size size request, since that value is unscaled.

https://bugzilla.gnome.org/show_bug.cgi?id=705410
2014-02-16 07:49:25 -08:00
9cc1017912 iconGrid: don't force icon size to the BaseIcon
Since we might get a scaled up version for HiDpi.

https://bugzilla.gnome.org/show_bug.cgi?id=705410
2014-02-16 07:49:25 -08:00
fc719c19f9 altTab: account for scale factor when computing icon size
https://bugzilla.gnome.org/show_bug.cgi?id=705410
2014-02-16 07:49:24 -08:00
ad97fc6855 altTab: don't override icon size request
Instead of overriding the actor's request with the icon size, just set
the new icon size on the actors, and let the default handler take the
preferred size of children.

https://bugzilla.gnome.org/show_bug.cgi?id=705410
2014-02-16 07:49:24 -08:00
407dc74502 shell-app: finish support for loading with scale
https://bugzilla.gnome.org/show_bug.cgi?id=705410
2014-02-16 07:49:24 -08:00
e5e764b402 texture-cache: require icon scale to load gicon
To support HiDpi.

https://bugzilla.gnome.org/show_bug.cgi?id=705410
2014-02-16 07:49:24 -08:00
65ad65fe52 Updated Brazilian Portuguese translation 2014-02-16 06:23:41 +00:00
8d09d20510 Updated Hebrew translation 2014-02-15 20:16:43 +02:00
5a5b04b2b0 Updated POTFILES.in 2014-02-15 20:13:20 +02:00
3113bac8e6 location: React to changes in available accuracy level
GPS could be plugged in and out, network can go up and down and
therefore available accuracy level can also change.

https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-15 17:41:26 +00:00
9217f2c916 location: Don't show menu if geoclue is totally incapable
If geoclue can't provide location at all (no GPS or network), don't
bother to show controls that don't serve any purpose.

https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-15 17:41:26 +00:00
32a49b7846 location: Provide a way to disable geolocation
Now that we are indicating 'geolocation in use' to user, we better also
provide at least a way to disable geolocation. Once this is in place, we
can provide slightly better controls rather than simply on/off switch.

https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-15 17:41:26 +00:00
12ef034b7b location: Make _connectToGeoclue() reentrent
https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-15 17:41:26 +00:00
e70e4a21f2 location: Rename _onGeoclueAppeared to _connectToGeoclue
Rename _onGeoclueAppeared to _connectToGeoclue as in the following patch
we'll call it from other contexts then geoclue appearing.

https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-15 17:41:26 +00:00
7826fb4f04 location: Create Geoclue proxy asynchronously
https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-15 17:41:26 +00:00
8f1b8909dc Fix position/size changed disconnects
Commit 3f7a989d38 changed the signals to match mutter api changes,
but did not update disconnects, so do that.
2014-02-15 13:07:14 +01:00
3f7a989d38 Update to changes for MetaWindow size-changed / position-changed signals
These have changed upstream to be on the MetaWindow, rather than
the MetaWindowActor. Follow suit.
2014-02-14 23:49:54 -05:00
4d3fd7598d appDisplay: Re-add _getCategories
It was introduced by one of the commits reverted by 812a61939e,
but is now used for app folders as well; add it back.
2014-02-14 14:02:31 +01:00
620e3cef20 appDisplay: Open new window on middle-click
The current middle-click action of opening a new window on a new
workspace is a bit peculiar; it is also not overly hard to achieve
the same result by moving a new window to the desired workspace or
selecting a workspace before opening a new window. Just opening
a new window is also a more common action, so having a shortcut
available that doesn't require a modifier is a good idea as well;
change the middle-click behavior accordingly.

https://bugzilla.gnome.org/show_bug.cgi?id=695010
2014-02-14 12:01:48 +01:00
812a61939e Revert "appDisplay: Special case terminal launching"
Having terminal launchers behave differently than any other launchers
is non-obvious and confusing. Remove the special-casing to restore
consistency, we will make the new window action more accessible
instead.
This reverts commit 68faba6bde, 4ed0f3e5f0
and 887a21afb9.

https://bugzilla.gnome.org/show_bug.cgi?id=695010
2014-02-14 12:01:48 +01:00
793c6c2f7b Magnifier: clip the UI group clone to the allocation
Without clipping, we show parts of the message tray and workspace
thumbnails that are normally clipped by the screen.

https://bugzilla.gnome.org/show_bug.cgi?id=724305
2014-02-13 19:43:44 +01:00
f8f4d0f646 Magnifier: use the system noise texture for the dead area
The design says that the noise texture is the implicit bottom
layer, and so it's appropriate to use it togheter with the default
color to cover the dead area outside the screen which becomes
visibile when scrolling.

https://bugzilla.gnome.org/show_bug.cgi?id=724305
2014-02-13 19:43:44 +01:00
3a92aa751f Magnifier: don't listen for focus/tracker events if the magnifier is not active
In addition to checking the current settings, check also if the
zoom region is active before registering the event listener.
This way, we avoid DBus traffic for events we're not interested in.

Also, make FocusCaretTracker resilient to multiple register/deregister
calls (which can now happen).

https://bugzilla.gnome.org/show_bug.cgi?id=724305
2014-02-13 19:43:41 +01:00
6882273aa0 Magnifier: demote exceptions reading focus/caret position
It's possible that the DBus call goes in timeout (which is bad,
but unavoidable, given that atspi is synchronous) or fails
because the component has been removed (race condition). Those
errors are not dependent on gnome-shell, but on faulty applications
(mostly).
If they happen, log a debug message and continue.

https://bugzilla.gnome.org/show_bug.cgi?id=724305
2014-02-13 19:31:00 +01:00
3b0197620f Magnifier: fix a warning when calling setActive() with the same value
We must not call enable/disable_unredirect if we didn't change the
active value, otherwise mutter gets confused and emits warnings.

https://bugzilla.gnome.org/show_bug.cgi?id=724293
2014-02-13 19:31:00 +01:00
f6240e114c Don't send all debug messages to Telepathy
The log handler can be invoked at bad times, and in particular
it can be invoked from gsignal with the signal lock taken.
At that time, calling into arbitrary high-level APIs can
cause a dead-lock.
Instead, only send to telepathy the tp-glib debug messages.
Everything else is in the journal anyway.

https://bugzilla.gnome.org/show_bug.cgi?id=724256
2014-02-13 13:03:37 +01:00
0f3c129b95 layout: Set high dpi scaling factor
Set the scaling factor when the dpi is above the hidpi threshold.

https://bugzilla.gnome.org/show_bug.cgi?id=705410
2014-02-12 22:53:37 +01:00
6f87b01c47 Revert "shell-global: Set high dpi scaling factor"
This reverts commit ba459f4d20.

This breaks gdm.
2014-02-12 22:53:11 +01:00
32110a9866 layout: Remove stray reference to parentSetId
This was removed in a4dea25d76.
2014-02-12 14:13:08 -05:00
ba459f4d20 shell-global: Set high dpi scaling factor
Set the scaling factor based on the xsetting.

https://bugzilla.gnome.org/show_bug.cgi?id=705410
2014-02-12 19:45:53 +01:00
d868e6bfaf st: Add high dpi support
Add a scale_factor property to StThemeContext that can
be used to enable (integer) scaling of pixel values.

https://bugzilla.gnome.org/show_bug.cgi?id=705410
2014-02-12 19:45:47 +01:00
9f3499a7c3 make NetworkManager optional
NetworkManager is only available on Linux.

https://bugzilla.gnome.org/show_bug.cgi?id=669495
2014-02-11 18:04:44 -05:00
ccec7732a7 calendar server: fix _LDFLAGS vs _LDADD mixup
_LDFLAGS is for flags.  _LDADD is for LIBS.

https://bugzilla.gnome.org/show_bug.cgi?id=724184
2014-02-11 17:46:44 -05:00
3b980a173f Updated Assamese translation 2014-02-11 13:07:53 +00:00
246139f90b Updated Ukranian 2014-02-09 23:08:26 +02:00
4e85fb7d8d Updated German translation 2014-02-09 20:17:24 +00:00
ab32411b0c Updated Norwegian bokmål translation 2014-02-09 19:46:37 +01:00
477f28a6bd LayoutManager: untrack actors that are destroyed
If an actor is destroyed, calling get_transformed_size()/position()
can return bogus values and trigger JS exceptions.
This can happen if a tracked actor comes from an extension
(such as classic mode's window-list) and that extension is
deactivated.

https://bugzilla.gnome.org/show_bug.cgi?id=723661
2014-02-09 19:30:02 +01:00
96ef0a178d autogen.sh: use #!/bin/sh instead of #!/bin/bash
We don't have any bashisms in this file, so we may as well use sh.

https://bugzilla.gnome.org/show_bug.cgi?id=722342
2014-02-09 11:00:04 -05:00
ab603e7ef7 Updated Aragonese translation 2014-02-09 13:25:09 +01:00
d52104a62a Updated Kazakh translation 2014-02-09 04:11:08 +00:00
8d8d1cfdd6 Magnifier: Restore crosshairs
This patch is to restore broken crosshairs so they may be used
once more

https://bugzilla.gnome.org/show_bug.cgi?id=723709
	magnifier.js
2014-02-08 23:47:40 +00:00
5451751513 Updated Czech translation 2014-02-08 11:22:36 +01:00
92ae26bb9f Fix username shadows in Lock/Loginscreen
https://bugzilla.gnome.org/show_bug.cgi?id=723833
2014-02-07 16:20:14 +00:00
3c8ee0c8cb Updated Spanish translation 2014-02-07 14:47:46 +01:00
20f76b8118 Tajik translation updated 2014-02-07 09:59:45 +05:00
d8eeeead18 Updated Galician translations 2014-02-07 00:59:18 +01:00
5452162bc3 Updated Brazilian Portuguese translation 2014-02-06 18:53:18 +00:00
a4adcba405 Revert "network: Use NMDevice.get_available_connections(); for NMWirelessDialog"
This reverts commit a36bfced47.

get_available_connections() returns the connections that are
available right now, ie, with the currently visible APs, but
that might change while the dialog is open, so we can't use it.

https://bugzilla.gnome.org/show_bug.cgi?id=709128
2014-02-06 19:45:24 +01:00
66da594382 Updated Hebrew translation 2014-02-06 20:30:25 +02:00
aa426842f2 BluetoothMenu: show the submenu when active but not connected
Following the updated designs for system status, show the submenu
but not the indicator when bluetooth radio is on but no device
is connected.

https://bugzilla.gnome.org/show_bug.cgi?id=709353
2014-02-06 19:07:48 +01:00
ed53a45228 NetworkMenu: show the device name instead of Bluetooth
To distinguish the bluetooth network menu from the bluetooth
menu proper, use the device name as the label.
Also, replace Connect with "Use as Internet connection"

https://bugzilla.gnome.org/show_bug.cgi?id=709353
2014-02-06 19:07:47 +01:00
2ddbcb2369 Updated Brazilian Portuguese translation 2014-02-06 17:35:33 +00:00
61a58ff3c9 Fix regression after mutter commit 254afc50223a707c3afa7c9f638681199f41809e
The "old and unused API" was not so unused after all :)
2014-02-06 18:15:36 +01:00
638aee65c0 Update Arabic translation 2014-02-06 15:32:37 +02:00
f21c49f8da Slider: put imports in order
Bug https://bugzilla.gnome.org/show_bug.cgi?id=712649
2014-02-05 19:36:50 +00:00
583d2cb4e4 Magnifier: take x,y from center of focused widget
bug https://bugzilla.gnome.org/show_bug.cgi?id=720951
2014-02-05 18:21:42 +00:00
aa70dcfc8f Updated Spanish translation 2014-02-05 13:48:04 +01:00
ffb61c425b Tajik translation updated 2014-02-05 10:59:16 +05:00
858cf5e0c9 Bump version to 3.11.5
Update NEWS.
2014-02-05 00:17:32 +01:00
881dd4666e Updated Hebrew translation
Signed-off-by: Yosef Or Boczko <yoseforb@src.gnome.org>
2014-02-05 00:32:33 +02:00
c4aeaf7fe8 NetworkMenu: use the carrier state to the decide if wired should be shown
The design says "when a network cable is connected", not
"When a network cable is connected and the link is up and we
have an IP etc. etc." (which is what ACTIVATED would imply).

https://bugzilla.gnome.org/show_bug.cgi?id=723570
2014-02-04 22:25:11 +01:00
ea1f5a8fc6 NMWirelessDialog: show an informative message when airplane mode is on
Rather than just showing "No networks", inform the user if airplane
mode is on or if wifi is off, and allow the user to change that
from the dialog (if possible).

https://bugzilla.gnome.org/show_bug.cgi?id=709128
2014-02-04 22:25:06 +01:00
7f1e420a0a NMWirelessDialog: request a scan when there are no visible access points
Ideally, we should keep scanning while the dialog is open, and
stop (or reduce the frequency) when it's closed. Forcing a new
scan when the dialog is opened empty is a close approximation
and increases the probability that the user will find the AP
he needs.

https://bugzilla.gnome.org/show_bug.cgi?id=709128
2014-02-04 22:24:01 +01:00
1272eaf07f NMWirelessDialog: fix removing access points
We must destroy the actor, not the item, because the latter doesn't
have a destroy() method.

https://bugzilla.gnome.org/show_bug.cgi?id=709128
2014-02-04 22:24:00 +01:00
2fe760cc4b rfkill: split out the dbus handling parts from the indicator
status.network wants to watch for airplane mode too, we can
share the code with a manager singleton.

https://bugzilla.gnome.org/show_bug.cgi?id=709128
2014-02-04 22:24:00 +01:00
df3a50bae8 AirplaneMode: don't show a "Turn off" item if HW enabled
If airplane mode is enabled with an HW switch, we can't turn it
off from the menu, so make the menu item insensitive and
replace it with a helpful tip.

https://bugzilla.gnome.org/show_bug.cgi?id=709685
2014-02-04 22:24:00 +01:00
0db3605f33 Magnifier: Disable unredirect when active
The unredirect feature does not apply to the magnifier and it
prevents users from gaming whilst it is on so disable when magnifier
is active and allow magnifier users to game!

Bug https://bugzilla.gnome.org/show_bug.cgi?id=708985
2014-02-04 16:58:25 +01:00
173fa92116 Updated Indonesian translation 2014-02-04 14:16:26 +00:00
02ca58c1eb Updated Hebrew translation. 2014-02-03 16:30:03 +02:00
4a22fe58bf updated kn.po 2014-02-03 16:51:29 +05:30
3fc478a14b overview: don't add a null desktop clone
If there is no desktop window, getDesktopClone() returns null.
In that case, we know that no animation is needed.

Fixes a regression from b97f3a9ecf

https://bugzilla.gnome.org/show_bug.cgi?id=723523
2014-02-03 11:55:50 +01:00
203bc674fe Updated Traditional Chinese translation(Hong Kong and Taiwan) 2014-02-02 20:36:23 +08:00
a4a9f0a04c Updated Kazakh translation 2014-02-01 15:12:11 +00:00
918e7fffeb Updated Tajik translation 2014-02-01 12:55:49 +00:00
e1648de661 Updated Brazilian Portuguese translation 2014-02-01 10:05:14 +00:00
f7c94e6343 Updated Greek translation 2014-02-01 08:16:19 +02: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
222 changed files with 37048 additions and 30563 deletions

2
.gitignore vendored
View File

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

41
COPYING
View File

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

View File

@ -1,7 +1,11 @@
# Point to our macro directory and pick up user flags from the environment
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
SUBDIRS += man

237
NEWS
View File

@ -1,3 +1,240 @@
3.12.1
======
* Ensure the currently focused app icon is viewable [Rui; #726759]
* Improve language in location menu [Zeeshan; #726498]
* Improve HiDpi support [Cosimo; #726907]
* Set accessible role for window previews [Alejandro; #726670]
* Fix bad antialiasing on panel menu buttons [Carlos; #727336]
* Don't hide location menu [Zeeshan; #727398]
* Fix IM candidate window obscuring current text [Rui; #727579]
* Fix extension-prefs tool when linked with --as-needed [Florian; #727948]
* Don't always extend struts to the screen edge [Florian; #663690]
Contributors:
Zeeshan Ali (Khattak), Cosimo Cecchi, Piotr Drąg, Rui Matos, Simon McVittie,
Florian Müllner, Alejandro Piñeiro, Carlos Soriano
Translations:
Khaled Hosny [ar], Piotr Drąg [pl], Yosef Or Boczko [he],
Antonio Fernandes C. Neto [pt_BR], Marek Černocký [cs], maria thukididu [el],
Andika Triwidada [id], Daniel Mustieles [es], Changwoo Ryu [ko],
Benjamin Steinwender [de], Sphinx Jiang [zh_CN],
Inaki Larranaga Murgoitio [eu], Marcus Lundblad [sv], Aurimas Černius [lt],
Stas Solovey [ru], Alexandre Franke [fr], Matej Urbančič [sl],
Fran Diéguez [gl], Pau Iranzo [ca], Luca Ferretti [it], Milo Casagrande [it],
Tiago S [pt], Victor Ibragimov [tg], Dirgita [id], Khoem Sokhem [km],
Rūdolfs Mazurs [lv], Balázs Úr [hu], Ask H. Larsen [da], Ikuya Awashiro [ja],
Мирослав Николић [sr, sr@latin]
3.12.0
======
* gdm: Reset greeter when coming back to login screen [Jasper; #726989]
Contributors:
Jasper St. Pierre
Translations:
Daniel Martinez [an], Yuri Myasoedov [ru], Inaki Larranaga Murgoitio [eu],
Abderrahim Kitouni [ar], Praveen Illa [te], Matej Urbančič [sl],
Chao-Hsiung Liao [zh_HK, zh_TW], Frédéric Péters [fr],
Мирослав Николић [sr, sr@latin], Ask H. Larsen [da], Kenneth Nielsen [da],
Jiro Matsuzawa [ja], Dušan Kazik [sk]
3.11.92
=======
* calendar: Grab key focus after changing day [Volker; #725606]
* gdm: Don't load user list if disabled [Florian; #725905]
* Don't show network-offline in the top bar [Jasper; #725340]
* Improve radial shade effect of modal dialogs [Giovanni; #725830]
* Fix broken suspend-on-idle functionality [Giovanni; #712706]
* Close wifi selection dialog when device disappears [Giovanni; #723935]
* Don't close chats when pressing Escape [Giovanni; #724178]
* Improve smartcard support in login/lock screen [Ray; #726262, #726263]
* Wake up screen when resuming from suspend [Giovanni; #726378]
* Make bluetooth and location items insensitive when locked [Florian; #726319]
* Don't show bluetooth icon when there is no adapter [Giovanni; #725057]
* Make sure to keep the OSK on top of modal dialogs [Rui; #719451]
* Misc. bug fixes and cleanups [Giovanni, Ray, Adel, Daniel, Jasper, Florian;
#725832, #725958, #722149, #724977, #724798, #725020, #723976, #726119,
#726238, #585500, #704844, #726323, #726322, #726120, #726414]
Contributors:
Giovanni Campagna, Daniel Drake, Adel Gadllah, Rui Matos, Florian Müllner,
Volker Sobek, Jasper St. Pierre, Ray Strode
Translations:
Fabio Tomat [fur], Rafael Ferreira [pt_BR], Fran Diéguez [gl],
Marek Černocký [cs], Baurzhan Muftakhidinov [kk], Andika Triwidada [id],
A S Alam [pa], Rūdolfs Mazurs [lv], Wylmer Wang [zh_CN],
Aurimas Černius [lt], Cheng-Chia Tseng [zh_TW], Stas Solovey [ru],
Tiagosdot [pt], Benjamin Steinwender [de], Frédéric Peters [fr],
Daniel Korostil [uk], Yaron Shahrabani [he], Ville-Pekka Vainio [fi],
maria thukididu [el], Victor Ibragimov [tg], Kjartan Maraas [nb],
Gábor Kelemen [hu], Ask H. Larsen [da]
3.11.91
=======
* Don't use network profile name in menu [Giovanni; #725586]
* calendar: Make date label clickable to return to current date [Vit; #641366]
* Misc. bug fixes [Florian, Zeeshan, Adel, Jasper, Dan, Volker; #724813,
#724686, #725082, #724870, #724779, #725533]
Contributors:
Zeeshan Ali (Khattak), Giovanni Campagna, Piotr Drąg, Adel Gadllah,
Florian Müllner, Volker Sobek, Vit Stanislav, Jasper St. Pierre, Dan Williams
Translations:
Victor Ibragimov [tg], Aurimas Černius [lt], Dimitris Spingos [el],
Andika Triwidada [id], Rafael Ferreira [pt_BR], Daniel Mustieles [es],
Baurzhan Muftakhidinov [kk], Marek Černocký [cs], Ihar Hrachyshka [be],
eternalhui [zh_CN], Yosef Or Boczko [he], Fran Diéguez [gl],
Khaled Hosny [ar], Ville-Pekka Vainio [fi], Piotr Drąg [pl],
Kjartan Maraas [nb], Changwoo Ryu [ko]
3.11.90
=======
* Stop showing two bluetooth entries [Giovanni; #709353]
* Improve styling of login/lock screen [Reda; #723833]
* Fix magnifier crosshairs [Magdalen; #723709]
* Make NetworkManager support optional [Michael; #669495]
* Make middle-click open a new instance [Florian; #695010]
* Scale the UI on high resolution displays [Cosimo, Adel; #705410, #724607]
* Remove notification counter on screen shield [Carlos; #709275]
* Improve app picker transition [Carlos; #722331]
* Add geolocation indicator to status menu [Zeeshan; #723684]
* Improve timestamps in chat notifications [Carlos; #708031, #715158]
* Improve network menus [Giovanni; #723570]
* Add "VPN Setting" item to VPN submenu [Giovanni; #709167]
* Improve appearance of disclosure arrows [Carlos; #720206]
* Add GSetting key to disable version validation of extensions [Adel; #724683]
* Delay auto-removing empty workspaces [Florian; #709064]
* Offer offline updates in the shutdown dialog [Kalev; #722898]
* Animate tile previews [Florian; #665758]
* Misc. bug fixes and cleanups [Giovanni, Ryan, Debarshi, Florian; #709128,
#722342, #723661, #724184, #724256, #724293, #724305, #722554, #724282,
#724690, #722928]
Contributors:
Zeeshan Ali (Khattak), Magdalen Berns, Michael Biebl, Giovanni Campagna,
Cosimo Cecchi, Adel Gadllah, Reda Lazri, Kalev Lember, Ryan Lortie,
Florian Müllner, Debarshi Ray, Carlos Soriano, Jasper St. Pierre,
Colin Walters
Translations:
Victor Ibragimov [tg], Daniel Mustieles [es], Khaled Hosny [ar],
Enrico Nicoletto [pt_BR], Yosef Or Boczko [he], Fran Diéguez [gl],
Marek Černocký [cs], Baurzhan Muftakhidinov [kk], Jorge Pérez Pérez [an],
Kjartan Maraas [nb], David Lüder [de], Daniel Korostil [uk], ngoswami [as],
Rafael Ferreira [pt_BR]
3.11.5
======
* Fix extension preference tool [Florian; #722334]
* Fix keyboard activation of legacy tray icons [Giovanni; #721267]
* Add radial background shade for modal dialogs [Giovanni; #669798]
* Show attached modal windows in the overview [Giovanni; #650843]
* Add support for desktop actions [Giovanni; #669603]
* Indicate in system status when location service is used [Zeeshan; #709372]
* Add support for extended app folder schema [Jasper; #723179]
* Show status icon for wired network connections [Jasper; #708966]
* Indicate airplane mode in network selection dialog [Giovanni; #709128]
* Misc bug fixes and cleanups [Florian, Sebastian, Giovanni, Tim, Matt, Jasper;
#722417, #722494, #722547, #722593, #722434, #722787, #722690, #722840,
#722660, #722812, #723197, #722927, #723306, #723308, #723523, #709685,
#723570]
Contributors:
Zeeshan Ali (Khattak), Magdalen Berns, Giovanni Campagna, William Jon McCann,
Sebastian Keller, Tim Lunn, Florian Müllner, Carlos Soriano,
Jasper St. Pierre, Rico Tzschichholz, Matt Watson
Translations:
Marek Černocký [cs], Mattias Põldaru [et], Tong Hui [zh_CN],
Victor Ibragimov [tg], Enrico Nicoletto [pt_BR], Daniel Mustieles [es],
Fran Diéguez [gl], Kjartan Maraas [nb], Nilamdyuti Goswami [as],
Aurimas Černius [lt], Stas Solovey [ru], Yosef Or Boczko [he],
Jorge Pérez Pérez [an], Dimitris Spingos [el], Baurzhan Muftakhidinov [kk],
Chao-Hsiung Liao [zh_HK, zh_TW], Shankar Prasad [kn], Yaron Shahrabani [he],
Andika Triwidada [id]
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]

2
README
View File

@ -8,7 +8,7 @@ For more information about GNOME Shell, including instructions on how
to build GNOME Shell from source and how to get involved with the project,
see:
http://live.gnome.org/GnomeShell
https://wiki.gnome.org/Projects/GnomeShell
Bugs should be reported at http://bugzilla.gnome.org against the 'gnome-shell'
product.

View File

@ -1,4 +1,4 @@
#!/bin/bash
#!/bin/sh
# Run this to generate all the initial makefiles, etc.
srcdir=`dirname $0`

View File

@ -13,9 +13,7 @@
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Jasper St. Pierre <jstpierre@mecheye.net>
@ -43,6 +41,8 @@
#define PLUGIN_API_VERSION 5
#define EXTENSION_DISABLE_VERSION_CHECK_KEY "disable-extension-version-validation"
typedef struct {
GDBusProxy *proxy;
} PluginData;
@ -833,6 +833,16 @@ plugin_get_shell_version (PluginObject *obj,
return ret;
}
static gboolean
plugin_get_version_validation_enabled (PluginObject *obj,
NPVariant *result)
{
gboolean is_enabled = !g_settings_get_boolean (obj->settings, EXTENSION_DISABLE_VERSION_CHECK_KEY);
BOOLEAN_TO_NPVARIANT(is_enabled, *result);
return TRUE;
}
#define METHODS \
METHOD (list_extensions) \
METHOD (get_info) \
@ -852,6 +862,8 @@ static NPIdentifier api_version_id;
static NPIdentifier shell_version_id;
static NPIdentifier onextension_changed_id;
static NPIdentifier onrestart_id;
static NPIdentifier version_validation_enabled_id;
static bool
plugin_object_has_method (NPObject *npobj,
@ -894,7 +906,8 @@ plugin_object_has_property (NPObject *npobj,
return (name == onextension_changed_id ||
name == onrestart_id ||
name == api_version_id ||
name == shell_version_id);
name == shell_version_id ||
name == version_validation_enabled_id);
}
static bool
@ -912,6 +925,8 @@ plugin_object_get_property (NPObject *npobj,
return plugin_get_api_version (obj, result);
else if (name == shell_version_id)
return plugin_get_shell_version (obj, result);
else if (name == version_validation_enabled_id)
return plugin_get_version_validation_enabled (obj, result);
else if (name == onextension_changed_id)
{
if (obj->listener)
@ -990,6 +1005,7 @@ init_methods_and_properties (void)
/* this is the JS public API; it is manipulated through NPIdentifiers for speed */
api_version_id = funcs.getstringidentifier ("apiVersion");
shell_version_id = funcs.getstringidentifier ("shellVersion");
version_validation_enabled_id = funcs.getstringidentifier ("versionValidationEnabled");
get_info_id = funcs.getstringidentifier ("getExtensionInfo");
list_extensions_id = funcs.getstringidentifier ("listExtensions");

View File

@ -1,5 +1,5 @@
AC_PREREQ(2.63)
AC_INIT([gnome-shell],[3.11.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_INIT([gnome-shell],[3.12.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_SRCDIR([src/shell-global.c])
@ -58,10 +58,25 @@ fi
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
GJS_MIN_VERSION=1.38.1
MUTTER_MIN_VERSION=3.11.1
GJS_MIN_VERSION=1.39.0
MUTTER_MIN_VERSION=3.12.1
GTK_MIN_VERSION=3.7.9
GIO_MIN_VERSION=2.37.0
LIBECAL_MIN_VERSION=3.5.3
@ -71,7 +86,6 @@ POLKIT_MIN_VERSION=0.100
STARTUP_NOTIFICATION_MIN_VERSION=0.11
GCR_MIN_VERSION=3.7.5
GNOME_DESKTOP_REQUIRED_VERSION=3.7.90
GNOME_MENUS_REQUIRED_VERSION=3.5.3
NETWORKMANAGER_MIN_VERSION=0.9.8
PULSE_MIN_VERS=2.0
@ -81,7 +95,6 @@ SHARED_PCS="gio-unix-2.0 >= $GIO_MIN_VERSION
gtk+-3.0 >= $GTK_MIN_VERSION
atk-bridge-2.0
gjs-internals-1.0 >= $GJS_MIN_VERSION
libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION
$recorder_modules
gdk-x11-3.0 libsoup-2.4
xtst
@ -92,9 +105,10 @@ SHARED_PCS="gio-unix-2.0 >= $GIO_MIN_VERSION
libcanberra libcanberra-gtk3
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
polkit-agent-1 >= $POLKIT_MIN_VERSION
libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION
libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
libsecret-unstable gcr-base-3 >= $GCR_MIN_VERSION"
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(MUTTER, libmutter >= $MUTTER_MIN_VERSION)
@ -110,25 +124,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(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
PKG_CHECK_MODULES(TRAY, gtk+-3.0)
PKG_CHECK_MODULES(GVC, libpulse >= $PULSE_MIN_VERS libpulse-mainloop-glib gobject-2.0)
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.7.4)
PKG_CHECK_MODULES(CARIBOU, caribou-1.0 >= 0.4.8)
AC_MSG_CHECKING([for bluetooth support])
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.9.0],
[BLUETOOTH_DIR=`$PKG_CONFIG --variable=applet_libdir gnome-bluetooth-1.0`
BLUETOOTH_LIBS=`$PKG_CONFIG --variable=applet_libs gnome-bluetooth-1.0`
AC_SUBST([BLUETOOTH_LIBS],["$BLUETOOTH_LIBS"])
AC_SUBST([BLUETOOTH_DIR],["$BLUETOOTH_DIR"])
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])
AC_SUBST([HAVE_BLUETOOTH],[1])
AC_MSG_RESULT([yes])],
AC_ARG_ENABLE(browser-plugin,
[AS_HELP_STRING([--enable-browser-plugin],
[Enable browser plugin [default=yes]])],,
enable_browser_plugin=yes)
AS_IF([test x$enable_browser_plugin = xyes], [
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
])
AM_CONDITIONAL(BUILD_BROWSER_PLUGIN, test x$enable_browser_plugin = xyes)
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_SUBST([HAVE_BLUETOOTH],[0])
AC_MSG_RESULT([no])])
AC_SUBST([HAVE_BLUETOOTH],[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)
@ -148,6 +162,9 @@ AC_SUBST(MUTTER_TYPELIB_DIR)
GJS_CONSOLE=`$PKG_CONFIG --variable=gjs_console gjs-1.0`
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(mallinfo)
AC_CHECK_HEADERS([sys/resource.h])
@ -163,6 +180,38 @@ if test "$langinfo_ok" = "yes"; then
[Define if _NL_TIME_FIRST_WEEKDAY is available])
fi
AC_ARG_ENABLE(networkmanager,
AS_HELP_STRING([--disable-networkmanager],
[disable NetworkManager support @<:@default=auto@:>@]),,
[enable_networkmanager=auto])
if test "x$enable_networkmanager" != "xno"; then
PKG_CHECK_MODULES(NETWORKMANAGER,
[libnm-glib
libnm-util >= $NETWORKMANAGER_MIN_VERSION
libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
libsecret-unstable],
[have_networkmanager=yes],
[have_networkmanager=no])
GNOME_SHELL_CFLAGS="$GNOME_SHELL_CFLAGS $NETWORKMANAGER_CFLAGS"
GNOME_SHELL_LIBS="$GNOME_SHELL_LIBS $NETWORKMANAGER_LIBS"
else
have_networkmanager="no (disabled)"
fi
if test "x$have_networkmanager" = "xyes"; then
AC_DEFINE(HAVE_NETWORKMANAGER, [1], [Define if we have NetworkManager])
AC_SUBST([HAVE_NETWORKMANAGER], [1])
else
if test "x$enable_networkmanager" = "xyes"; then
AC_MSG_ERROR([Couldn't find NetworkManager.])
fi
AC_SUBST([HAVE_NETWORKMANAGER], [0])
fi
AM_CONDITIONAL(HAVE_NETWORKMANAGER, test "$have_networkmanager" = "yes")
# Sets GLIB_GENMARSHAL and GLIB_MKENUMS
AM_PATH_GLIB_2_0()
@ -204,3 +253,15 @@ AC_CONFIG_FILES([
man/Makefile
])
AC_OUTPUT
echo "
Build configuration:
Prefix: ${prefix}
Source code location: ${srcdir}
Compiler: ${CC}
Compiler Warnings: $enable_compile_warnings
Support for NetworkManager: $have_networkmanager
Support for GStreamer recording: $build_recorder
"

View File

@ -39,6 +39,7 @@ dist_theme_DATA = \
theme/filter-selected-rtl.svg \
theme/gnome-shell.css \
theme/logged-in-indicator.svg \
theme/menu-arrow-symbolic.svg \
theme/message-tray-background.png \
theme/more-results.svg \
theme/noise-texture.png \

View File

@ -2,7 +2,7 @@
Type=Application
_Name=GNOME Shell (wayland compositor)
_Comment=Window management and application launching
Exec=@bindir@/mutter-launch -- gnome-shell-wayland --wayland
Exec=@bindir@/mutter-launch -- gnome-shell-wayland --wayland --display-server
X-GNOME-Bugzilla-Bugzilla=GNOME
X-GNOME-Bugzilla-Product=gnome-shell
X-GNOME-Bugzilla-Component=general

View File

@ -13,12 +13,21 @@
</key>
<key name="enabled-extensions" type="as">
<default>[]</default>
<_summary>Uuids of extensions to enable</_summary>
<_summary>UUIDs of extensions to enable</_summary>
<_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
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>
</key>
<key name="disable-extension-version-validation" type="b">
<default>false</default>
<_summary>Disables the validation of extension version compatibility</_summary>
<_description>
GNOME Shell will only load extensions that claim to support the current
running version. Enabling this option will disable this check and try to
load all extensions regardless of the versions they claim to support.
</_description>
</key>
<key name="favorite-apps" type="as">
@ -29,14 +38,6 @@
will be displayed in the favorites area.
</_description>
</key>
<key name="app-folder-categories" type="as">
<default>[ 'Utilities', 'Sundry' ]</default>
<_summary>List of categories that should be displayed as folders</_summary>
<_description>
Each category name in this list will be represented as folder in the
application view, rather than being displayed inline in the main view.
</_description>
</key>
<key name="app-picker-view" type="u">
<default>0</default>
<summary>App Picker View</summary>
@ -54,10 +55,10 @@
</key>
<key name="always-show-log-out" type="b">
<default>false</default>
<_summary>Always show the 'Log out' menuitem in the user menu.</_summary>
<_summary>Always show the 'Log out' menu item in the user menu.</_summary>
<_description>
This key overrides the automatic hiding of the 'Log out'
menuitem in single-user, single-session situations.
menu item in single-user, single-session situations.
</_description>
</key>
<key name="remember-mount-password" type="b">
@ -73,6 +74,7 @@
<child name="calendar" schema="org.gnome.shell.calendar"/>
<child name="keybindings" schema="org.gnome.shell.keybindings"/>
<child name="keyboard" schema="org.gnome.shell.keyboard"/>
<child name="location" schema="org.gnome.shell.location"/>
</schema>
<schema id="org.gnome.shell.calendar" path="/org/gnome/shell/calendar/"
@ -124,6 +126,11 @@
Keybinding to focus the active notification.
</_description>
</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 id="org.gnome.shell.keyboard" path="/org/gnome/shell/keyboard/"
@ -137,6 +144,32 @@
</key>
</schema>
<enum id="org.gnome.shell.geoclue.AccuracyLevel">
<value value="0" nick="off"/>
<value value="1" nick="country"/>
<value value="4" nick="city"/>
<value value="5" nick="neighborhood"/>
<value value="6" nick="street"/>
<value value="8" nick="exact"/>
</enum>
<schema id="org.gnome.shell.location"
path="/org/gnome/shell/location/"
gettext-domain="@GETTEXT_PACKAGE@">
<key name="max-accuracy-level" enum="org.gnome.shell.geoclue.AccuracyLevel">
<default>'exact'</default>
<_summary>The maximum accuracy level of location.</_summary>
<_description>
Configures the maximum level of location accuracy applications are
allowed to see. Valid options are 'off' (disable location tracking),
'country', 'city', 'neighborhood', 'street', and 'exact' (typically
requires GPS receiver). Please keep in mind that this only controls
what GeoClue will allow applications to see and they can find user's
location on their own using network resources (albeit with street-level
accuracy at best).
</_description>
</key>
</schema>
<schema id="org.gnome.shell.app-switcher"
path="/org/gnome/shell/app-switcher/"
gettext-domain="@GETTEXT_PACKAGE@">

View File

@ -157,8 +157,9 @@ StScrollBar StButton#vhandle:active {
min-width: 200px;
}
.unicode-arrow {
font-size: 120%;
.popup-menu-arrow {
width: 16px;
height: 16px;
}
.popup-submenu-menu-item:open {
@ -289,6 +290,20 @@ StScrollBar StButton#vhandle:active {
spacing: 10px;
}
.nm-dialog-airplane-box {
spacing: 12px;
}
.nm-dialog-airplane-headline {
font-size: 1.1em;
font-weight: bold;
text-align: center;
}
.nm-dialog-airplane-text {
color: #999999;
}
.nm-dialog-header-icon {
icon-size: 32px;
}
@ -655,7 +670,7 @@ StScrollBar StButton#vhandle:active {
color: #e6e6e6;
border-radius: 32px; /* wish we could do 50% */
padding: 13px;
border: 1px solid #5f5f5f; /* using rgba() is flaky unfortunately */
border: 2px solid #5f5f5f; /* using rgba() is flaky unfortunately */
}
.system-menu-action:hover,
@ -663,7 +678,7 @@ StScrollBar StButton#vhandle:active {
color: white;
background-color: #4c4c4c;
border: none;
padding: 14px;
padding: 15px;
}
.system-menu-action:active {
@ -964,6 +979,8 @@ StScrollBar StButton#vhandle:active {
.app-folder-icon {
padding: 5px;
spacing-rows: 5px;
spacing-columns: 5px;
}
.dash-item-container > StButton {
@ -1289,12 +1306,18 @@ StScrollBar StButton#vhandle:active {
font-weight: bold;
text-align: center;
color: #eeeeec;
border-radius: 4px;
}
.datemenu-date-label:hover,
.datemenu-date-label:focus {
background-color: #999999;
}
.datemenu-date-label:active {
background-color: #aaaaaa;
}
.calendar-day-base {
font-size: 9pt;
text-align: center;
@ -1453,6 +1476,10 @@ StScrollBar StButton#vhandle:active {
color: #999999;
}
.no-networks-box {
spacing: 12px;
}
.notification {
border-radius: 10px 10px 0px 0px;
background: rgba(0,0,0,0.9);
@ -1630,8 +1657,8 @@ StScrollBar StButton#vhandle:active {
color: #888888;
}
.chat-group-sent, .chat-group-meta {
padding: 8px 0;
.chat-empty-line {
font-size: 4px;
}
.chat-received {
@ -1656,6 +1683,7 @@ StScrollBar StButton#vhandle:active {
.chat-meta-message {
padding-left: 4px;
font-size: 9pt;
font-weight: bold;
color: #bbbbbb;
}
@ -1857,6 +1885,27 @@ StScrollBar StButton#vhandle:active {
border-radius: 8px;
}
/* Tile previews */
.tile-preview {
background-color: rgba(74, 144, 217, 0.35);
border: 1px solid #4a90d9; /* Adwaita selected bg color */
}
.tile-preview-left.on-primary {
/* keep in sync with -panel-corner-radius */
border-radius: 6px 0 0 0;
}
.tile-preview-right.on-primary {
/* keep in sync with -panel-corner-radius */
border-radius: 0 6px 0 0;
}
.tile-preview-left.tile-preview-right.on-primary {
/* keep in sync with -panel-corner-radius */
border-radius: 6px 6px 0 0;
}
/* Modal Dialogs */
/* Dialog Subject Text Style */
@ -1927,44 +1976,57 @@ StScrollBar StButton#vhandle:active {
padding-top: 20px;
}
.end-session-dialog-subject {
.end-session-dialog-layout {
padding-left: 17px;
padding-bottom: 20px;
}
.end-session-dialog-subject:rtl {
padding-left: 0px;
.end-session-dialog-layout:rtl {
padding-right: 17px;
}
.end-session-dialog-description {
padding-left: 17px;
width: 28em;
padding-bottom: 10px;
}
.end-session-dialog-description:rtl {
padding-right: 17px;
width: 28em;
padding-bottom: 10px;
text-align: right;
}
.end-session-dialog-warning {
width: 28em;
color: #f57900;
padding-top: 6px;
}
.end-session-dialog-warning:rtl {
width: 28em;
color: #f57900;
padding-top: 6px;
text-align: right;
}
.end-session-dialog-logout-icon {
border: 2px solid #8b8b8b;
border-radius: 5px;
width: 32px;
height: 32px;
width: 48px;
height: 48px;
background-size: contain;
}
.end-session-dialog-shutdown-icon {
color: #bebebe;
width: 32px;
height: 32px;
width: 48px;
height: 48px;
}
.end-session-dialog-inhibitor-layout {
spacing: 16px;
max-height: 200px;
padding-right: 50px;
padding-left: 50px;
padding-right: 65px;
padding-left: 65px;
}
.end-session-dialog-session-list,
@ -1976,6 +2038,10 @@ StScrollBar StButton#vhandle:active {
font-weight: bold;
}
.end-session-dialog-list-header:rtl {
text-align: right;
}
.end-session-dialog-app-list-item,
.end-session-dialog-session-list-item {
spacing: 1em;
@ -2095,6 +2161,10 @@ StScrollBar StButton#vhandle:active {
color: #666666;
}
.prompt-dialog-description:rtl {
text-align: right;
}
.prompt-dialog-password-box {
spacing: 1em;
padding-bottom: 1em;
@ -2321,6 +2391,8 @@ StScrollBar StButton#vhandle:active {
.login-dialog-user-list-item {
border-radius: 5px;
padding: .2em;
color: #bfbfbf;
text-shadow: black 0px 2px 2px;
}
.login-dialog-user-list-item:ltr {
@ -2331,24 +2403,6 @@ StScrollBar StButton#vhandle:active {
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 {
background-color: rgba(255,255,255,0.1);
}
@ -2375,13 +2429,6 @@ StScrollBar StButton#vhandle:active {
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 {
font-size: 10.5pt;
font-weight: bold;
@ -2402,12 +2449,13 @@ StScrollBar StButton#vhandle:active {
color: #E8E8E8;
}
.login-dialog-username {
.login-dialog-username,
.user-widget-label {
font-size: 16pt;
font-weight: bold;
text-align: left;
padding-left: 15px;
text-shadow: black 0px 4px 3px 0px;
text-shadow: rgba(0, 0, 0, 0.5) 0px 2px 1px 0px;
}
.login-dialog-prompt-layout {
@ -2432,6 +2480,7 @@ StScrollBar StButton#vhandle:active {
}
.login-dialog-session-list-button:hover,
.login-dialog-session-list-button:focus,
.login-dialog-session-list-button:active {
color: white;
}
@ -2498,12 +2547,14 @@ StScrollBar StButton#vhandle:active {
}
.user-widget-label {
font-size: 20px;
font-weight: bold;
text-align: left;
}
.user-widget-label:ltr {
padding-left: 18px;
color:white;
text-shadow: black 0px 4px 3px 0px;
}
.user-widget-label:rtl {
padding-right: 18px;
}
/* Screen shield */
@ -2630,4 +2681,5 @@ StScrollBar StButton#vhandle:active {
.background-menu {
-boxpointer-gap: 4px;
-arrow-rise: 0px;
}

View File

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
id="svg3863"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="menu-arrow.svg">
<defs
id="defs3865" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="15.836083"
inkscape:cx="-3.1641676"
inkscape:cy="11.823817"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
showguides="true"
inkscape:guide-bbox="true"
inkscape:window-width="1366"
inkscape:window-height="702"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:snap-bbox="true">
<sodipodi:guide
orientation="1,0"
position="15.996443,16.922964"
id="guide3873" />
<sodipodi:guide
orientation="0,1"
position="28.041217,3.1256134"
id="guide3875" />
<sodipodi:guide
orientation="0,1"
position="-0.80372916,24.469088"
id="guide3877" />
<sodipodi:guide
orientation="1,0"
position="3.0363102,34.649657"
id="guide3879" />
<sodipodi:guide
orientation="1,0"
position="29.023553,28.577037"
id="guide3881" />
<inkscape:grid
type="xygrid"
id="grid2988" />
</sodipodi:namedview>
<metadata
id="metadata3868">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer"
transform="translate(0,-16)">
<path
style="fill:#ffffff;fill-opacity:1;stroke:none"
d="m 4,23 8,0 -4,5 z"
id="path3883"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 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_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
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.
include $(top_srcdir)/gtk-doc.make

View File

@ -17,17 +17,15 @@ packages. If you are interested in building GNOME Shell from source,
we would recommend building from version control using the build
script described at:
http://live.gnome.org/GnomeShell
https://wiki.gnome.org/Projects/GnomeShell
Not only will that give you the very latest version of this rapidly
changing project, it will be much easier than get GNOME Shell and
its dependencies to build from tarballs.</description>
<!--
<homepage rdf:resource="http://live.gnome.org/GnomeShell" />
-->
<homepage rdf:resource="https://wiki.gnome.org/Projects/GnomeShell" />
<mailing-list rdf:resource="http://mail.gnome.org/mailman/listinfo/gnome-shell-list" />
<download-page rdf:resource="http://download.gnome.org/sources/gnome-shell/" />
<bug-database rdf:resource="http://bugzilla.gnome.org/browse.cgi?product=gnome-shell" />
<bug-database rdf:resource="https://bugzilla.gnome.org/browse.cgi?product=gnome-shell" />
<category rdf:resource="http://api.gnome.org/doap-extensions#desktop" />

View File

@ -1,123 +1,38 @@
NULL =
EXTRA_DIST = misc/config.js.in
CLEANFILES = misc/config.js
BUILT_SOURCES =
misc/config.js: misc/config.js.in Makefile
[ -d $(@D) ] || $(mkdir_p) $(@D) ; \
sed -e "s|[@]PACKAGE_NAME@|$(PACKAGE_NAME)|g" \
-e "s|[@]PACKAGE_VERSION@|$(PACKAGE_VERSION)|g" \
-e "s|[@]HAVE_BLUETOOTH@|$(HAVE_BLUETOOTH)|g" \
-e "s|[@]HAVE_NETWORKMANAGER@|$(HAVE_NETWORKMANAGER)|g" \
-e "s|[@]GETTEXT_PACKAGE@|$(GETTEXT_PACKAGE)|g" \
-e "s|[@]datadir@|$(datadir)|g" \
-e "s|[@]libexecdir@|$(libexecdir)|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 = \
gdm/authPrompt.js \
gdm/batch.js \
gdm/fingerprint.js \
gdm/loginDialog.js \
gdm/oVirt.js \
gdm/realmd.js \
gdm/util.js \
extensionPrefs/main.js \
misc/config.js \
misc/extensionUtils.js \
misc/fileUtils.js \
misc/gnomeSession.js \
misc/hash.js \
misc/history.js \
misc/jsParse.js \
misc/loginManager.js \
misc/modemManager.js \
misc/objectManager.js \
misc/params.js \
misc/smartcardManager.js \
misc/util.js \
perf/core.js \
ui/altTab.js \
ui/animation.js \
ui/appDisplay.js \
ui/appFavorites.js \
ui/backgroundMenu.js \
ui/background.js \
ui/boxpointer.js \
ui/calendar.js \
ui/checkBox.js \
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/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/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 \
js_built_sources = js-resources.c js-resources.h
BUILT_SOURCES += $(js_built_sources)
all-local: $(js_built_sources)
js_resource_dist_files = $(filter-out misc/config.js, $(js_resource_files))
EXTRA_DIST = \
$(js_resource_dist_files) \
js-resources.gresource.xml \
misc/config.js.in \
$(NULL)
CLEANFILES = \
$(js_built_sources) \
$(NULL)

View File

@ -206,11 +206,11 @@ const Application = new Lang.Class({
_scanExtensions: function() {
let finder = new ExtensionUtils.ExtensionFinder();
finder.connect('extension-found', Lang.bind(this, this._extensionFound));
finder.connect('extensions-loaded', Lang.bind(this, this._extensionsLoaded));
finder.scanExtensions();
this._extensionsLoaded();
},
_extensionFound: function(signals, extension) {
_extensionFound: function(finder, extension) {
let iter = this._model.append();
this._model.set(iter, [0, 1], [extension.uuid, extension.metadata.name]);
this._extensionIters[extension.uuid] = iter;

View File

@ -80,6 +80,7 @@ const AuthPrompt = new Lang.Class({
if (event.get_key_symbol() == Clutter.KEY_Escape) {
this.cancel();
}
return Clutter.EVENT_PROPAGATE;
}));
this._userWell = new St.Bin({ x_fill: true,
@ -93,7 +94,7 @@ const AuthPrompt = new Lang.Class({
this.actor.add(this._label,
{ expand: true,
x_fill: true,
x_fill: false,
y_fill: true,
x_align: St.Align.START });
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,
styleClass: 'login-dialog-message' });
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',
vertical: false });
@ -263,10 +264,8 @@ const AuthPrompt = new Lang.Class({
},
_onReset: function() {
if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED) {
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
this.reset();
}
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
this.reset();
},
addActorToDefaultButtonWell: function(actor) {
@ -451,8 +450,7 @@ const AuthPrompt = new Lang.Class({
// respond to the request with the username
beginRequestType = BeginRequestType.PROVIDE_USERNAME;
} else if (this._userVerifier.serviceIsForeground(GdmUtil.OVIRT_SERVICE_NAME) ||
(this.smartcardDetected &&
this._userVerifier.serviceIsForeground(GdmUtil.SMARTCARD_SERVICE_NAME))) {
this._userVerifier.serviceIsForeground(GdmUtil.SMARTCARD_SERVICE_NAME)) {
// We don't need to know the username if the user preempted the login screen
// with a smartcard or with preauthenticated oVirt credentials
beginRequestType = BeginRequestType.DONT_PROVIDE_USERNAME;

View File

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

View File

@ -13,9 +13,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
const AccountsService = imports.gi.AccountsService;
@ -38,6 +36,7 @@ const BoxPointer = imports.ui.boxpointer;
const CtrlAltTab = imports.ui.ctrlAltTab;
const GdmUtil = imports.gdm.util;
const Layout = imports.ui.layout;
const LoginManager = imports.misc.loginManager;
const Main = imports.ui.main;
const PopupMenu = imports.ui.popupMenu;
const Realmd = imports.gdm.realmd;
@ -59,7 +58,7 @@ const UserListItem = new Lang.Class({
this._userChangedId = this.user.connect('changed',
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',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
can_focus: true,
@ -68,39 +67,18 @@ const UserListItem = new Lang.Class({
x_align: St.Align.START,
x_fill: true });
this._userAvatar = new UserWidget.Avatar(this.user,
{ styleClass: 'login-dialog-user-list-item-icon' });
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._userWidget = new UserWidget.UserWidget(this.user);
layout.add(this._userWidget.actor);
this._timedLoginIndicator = new St.Bin({ style_class: 'login-dialog-timed-login-indicator',
scale_x: 0 });
textLayout.add(this._timedLoginIndicator,
{ x_fill: true,
x_align: St.Align.MIDDLE,
y_fill: false,
y_align: St.Align.END });
layout.add(this._timedLoginIndicator);
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
this._onUserChanged();
},
_onUserChanged: function() {
this._nameLabel.set_text(this.user.get_real_name());
this._userAvatar.update();
this._updateLoggedIn();
},
syncStyleClasses: function() {
this._updateLoggedIn();
},
@ -189,7 +167,6 @@ const UserList = new Lang.Class({
for (let userName in this._items) {
let item = this._items[userName];
item.actor.sync_hover();
item.syncStyleClasses();
}
},
@ -478,18 +455,6 @@ const LoginDialog = new Lang.Class({
this.actor.add_child(this._logoBin);
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',
Lang.bind(this, function(userList, item) {
this._onUserListActivated(item);
@ -505,7 +470,31 @@ const LoginDialog = new Lang.Class({
this._sessionMenuButton.actor.show();
this._authPrompt.addActorToDefaultButtonWell(this._sessionMenuButton.actor);
},
this._disableUserList = undefined;
this._userListLoaded = false;
LoginManager.getLoginManager().getCurrentSessionProxy(Lang.bind(this, this._gotGreeterSessionProxy));
// If the user list is enabled, it should take key focus; make sure the
// screen shield is initialized first to prevent it from stealing the
// focus later
Main.layoutManager.connect('startup-complete',
Lang.bind(this, this._updateDisableUserList));
},
_ensureUserListLoaded: function() {
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() {
let disableUserList = this._settings.get_boolean(GdmUtil.DISABLE_USER_LIST_KEY);
@ -548,9 +537,12 @@ const LoginDialog = new Lang.Class({
return;
this._logoBin.destroy_all_children();
if (this._logoFileUri)
if (this._logoFileUri) {
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
this._logoBin.add_child(this._textureCache.load_uri_async(this._logoFileUri,
-1, _LOGO_ICON_HEIGHT));
-1, _LOGO_ICON_HEIGHT,
scaleFactor));
}
},
_updateLogo: function() {
@ -649,6 +641,36 @@ const LoginDialog = new Lang.Class({
this._showPrompt();
},
_loginScreenSessionActivated: function() {
if (this._authPrompt.verificationStatus != AuthPrompt.AuthPromptStatus.VERIFICATION_SUCCEEDED)
return;
Tweener.addTween(this.actor,
{ opacity: 255,
time: _FADE_ANIMATION_TIME,
transition: 'easeOutQuad',
onUpdate: function() {
let children = Main.layoutManager.uiGroup.get_children();
for (let i = 0; i < children.length; i++) {
if (children[i] != Main.layoutManager.screenShieldGroup)
children[i].opacity = this.actor.opacity;
}
},
onUpdateScope: this,
onComplete: function() {
this._authPrompt.reset();
},
onCompleteScope: this });
},
_gotGreeterSessionProxy: function(proxy) {
proxy.connect('g-properties-changed', Lang.bind(this, function() {
if (proxy.Active)
this._loginScreenSessionActivated();
}));
},
_startSession: function(serviceName) {
Tweener.addTween(this.actor,
{ opacity: 0,
@ -666,7 +688,7 @@ const LoginDialog = new Lang.Class({
onComplete: function() {
Mainloop.idle_add(Lang.bind(this, function() {
this._greeter.call_start_session_when_ready_sync(serviceName, true, null);
return false;
return GLib.SOURCE_REMOVE;
}));
},
onCompleteScope: this });
@ -721,6 +743,7 @@ const LoginDialog = new Lang.Class({
function() {
this._timedLoginAnimationTime -= _TIMED_LOGIN_IDLE_THRESHOLD;
hold.release();
return GLib.SOURCE_REMOVE;
});
return hold;
},
@ -785,7 +808,7 @@ const LoginDialog = new Lang.Class({
global.stage.connect('captured-event',
Lang.bind(this, function(actor, event) {
if (this._timedLoginDelay == undefined)
return false;
return Clutter.EVENT_PROPAGATE;
if (event.type() == Clutter.EventType.KEY_PRESS ||
event.type() == Clutter.EventType.BUTTON_PRESS) {
@ -798,7 +821,7 @@ const LoginDialog = new Lang.Class({
this._resetTimedLogin();
}
return false;
return Clutter.EVENT_PROPAGATE;
}));
},
@ -824,6 +847,7 @@ const LoginDialog = new Lang.Class({
},
_showUserList: function() {
this._ensureUserListLoaded();
this._authPrompt.hide();
this._sessionMenuButton.close();
this._setUserListExpanded(true);
@ -867,14 +891,17 @@ const LoginDialog = new Lang.Class({
},
_loadUserList: function() {
if (this._userListLoaded)
return GLib.SOURCE_REMOVE;
this._userListLoaded = true;
let users = this._userManager.list_users();
for (let i = 0; i < users.length; i++) {
this._userList.addUser(users[i]);
}
this._updateDisableUserList();
this._userManager.connect('user-added',
Lang.bind(this, function(userManager, user) {
this._userList.addUser(user);
@ -884,6 +911,8 @@ const LoginDialog = new Lang.Class({
Lang.bind(this, function(userManager, user) {
this._userList.removeUser(user);
}));
return GLib.SOURCE_REMOVE;
},
open: function() {
@ -907,6 +936,10 @@ const LoginDialog = new Lang.Class({
Main.ctrlAltTabManager.removeGroup(this.dialogLayout);
},
cancel: function() {
this._authPrompt.cancel();
},
addCharacter: function(unichar) {
this._authPrompt.addCharacter(unichar);
},

View File

@ -250,6 +250,7 @@ const ShellUserVerifier = new Lang.Class({
Lang.bind(this, function() {
this._messageQueueTimeoutId = 0;
this._queueMessageTimeout();
return GLib.SOURCE_REMOVE;
}));
},
@ -297,7 +298,7 @@ const ShellUserVerifier = new Lang.Class({
if (!this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY))
smartcardDetected = false;
else if (this.reauthenticating)
else if (this._reauthOnly)
smartcardDetected = this._smartcardManager.hasInsertedLoginToken();
else
smartcardDetected = this._smartcardManager.hasInsertedTokens();

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

@ -6,6 +6,8 @@ const PACKAGE_NAME = '@PACKAGE_NAME@';
const PACKAGE_VERSION = '@PACKAGE_VERSION@';
/* 1 if gnome-bluetooth is available, 0 otherwise */
const HAVE_BLUETOOTH = @HAVE_BLUETOOTH@;
/* 1 if networkmanager is available, 0 otherwise */
const HAVE_NETWORKMANAGER = @HAVE_NETWORKMANAGER@;
/* gettext package */
const GETTEXT_PACKAGE = '@GETTEXT_PACKAGE@';
/* locale dir */

View File

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

View File

@ -5,80 +5,27 @@ const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Params = imports.misc.params;
function listDirAsync(file, callback) {
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;
}
function collectFromDatadirs(subdir, includeUserDir, processFile) {
let dataDirs = GLib.get_system_data_dirs();
if (params.includeUserDir)
if (includeUserDir)
dataDirs.unshift(GLib.get_user_data_dir());
loadState.numLoading = dataDirs.length;
for (let i = 0; i < dataDirs.length; i++) {
let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', subdir]);
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

@ -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) {
return this._setNextItem(entry.get_text());
}
return false;
return Clutter.EVENT_PROPAGATE;
},
_indexChanged: function() {

View File

@ -39,6 +39,7 @@ const SystemdLoginSessionIface = '<node> \
<interface name="org.freedesktop.login1.Session"> \
<signal name="Lock" /> \
<signal name="Unlock" /> \
<property name="Active" type="b" access="read" /> \
</interface> \
</node>';

View File

@ -89,7 +89,7 @@ function spawnApp(argv) {
let app = Gio.AppInfo.create_from_commandline(argv.join(' '), null,
Gio.AppInfoCreateFlags.SUPPORTS_STARTUP_NOTIFICATION);
let context = global.create_app_launch_context();
let context = global.create_app_launch_context(0, -1);
app.launch([], context);
} catch(err) {
_handleSpawnError(argv[0], err);
@ -153,7 +153,7 @@ function trySpawnCommandLine(command_line) {
}
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);
}

View File

@ -2,6 +2,7 @@
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
@ -23,7 +24,7 @@ const WINDOW_PREVIEW_SIZE = 128;
const APP_ICON_SIZE = 96;
const APP_ICON_SIZE_SMALL = 48;
const iconSizes = [96, 64, 48, 32, 22];
const baseIconSizes = [96, 64, 48, 32, 22];
const AppIconMode = {
THUMBNAIL_ONLY: 1,
@ -312,7 +313,7 @@ const AppSwitcherPopup = new Lang.Class({
this._createThumbnails();
this._thumbnailTimeoutId = 0;
this._thumbnailsFocused = false;
return false;
return GLib.SOURCE_REMOVE;
},
_destroyThumbnails : function() {
@ -429,7 +430,6 @@ const AppIcon = new Lang.Class({
set_size: function(size) {
this.icon = this.app.create_icon_texture(size);
this._iconBin.set_size(size, size);
this._iconBin.child = this.icon;
}
});
@ -459,9 +459,10 @@ const AppSwitcher = new Lang.Class({
appIcon.cachedWindows = allWindows.filter(function(w) {
return windowTracker.get_window_app (w) == appIcon.app;
});
if (workspace == null || appIcon.cachedWindows.length > 0) {
if (appIcon.cachedWindows.length > 0)
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;
@ -477,12 +478,13 @@ const AppSwitcher = new Lang.Class({
Mainloop.source_remove(this._mouseTimeOutId);
},
_getPreferredHeight: function (actor, forWidth, alloc) {
_setIconSize: function() {
let j = 0;
while(this._items.length > 1 && this._items[j].style_class != 'item-box') {
j++;
}
let themeNode = this._items[j].get_theme_node();
let iconPadding = themeNode.get_horizontal_padding();
let iconBorder = themeNode.get_border_width(St.Side.LEFT) + themeNode.get_border_width(St.Side.RIGHT);
let [iconMinHeight, iconNaturalHeight] = this.icons[j].label.get_preferred_height(-1);
@ -493,19 +495,22 @@ const AppSwitcher = new Lang.Class({
let primary = Main.layoutManager.primaryMonitor;
let parentPadding = this.actor.get_parent().get_theme_node().get_horizontal_padding();
let availWidth = primary.width - parentPadding - this.actor.get_theme_node().get_horizontal_padding();
let height = 0;
for(let i = 0; i < iconSizes.length; i++) {
this._iconSize = iconSizes[i];
height = iconSizes[i] + iconSpacing;
let w = height * this._items.length + totalSpacing;
if (w <= availWidth)
break;
}
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
let iconSizes = baseIconSizes.map(function(s) {
return s * scaleFactor;
});
if (this._items.length == 1) {
this._iconSize = iconSizes[0];
height = iconSizes[0] + iconSpacing;
this._iconSize = baseIconSizes[0];
} else {
for(let i = 0; i < baseIconSizes.length; i++) {
this._iconSize = baseIconSizes[i];
let height = iconSizes[i] + iconSpacing;
let w = height * this._items.length + totalSpacing;
if (w <= availWidth)
break;
}
}
for(let i = 0; i < this.icons.length; i++) {
@ -513,9 +518,11 @@ const AppSwitcher = new Lang.Class({
break;
this.icons[i].set_size(this._iconSize);
}
},
alloc.min_size = height;
alloc.natural_size = height;
_getPreferredHeight: function (actor, forWidth, alloc) {
this._setIconSize();
this.parent(actor, forWidth, alloc);
},
_allocate: function (actor, box, flags) {
@ -547,7 +554,7 @@ const AppSwitcher = new Lang.Class({
Lang.bind(this, function () {
this._enterItem(index);
this._mouseTimeOutId = 0;
return false;
return GLib.SOURCE_REMOVE;
}));
} else
this._itemEntered(index);
@ -648,17 +655,19 @@ const ThumbnailList = new Lang.Class({
totalPadding += this.actor.get_theme_node().get_horizontal_padding() + this.actor.get_theme_node().get_vertical_padding();
let [labelMinHeight, labelNaturalHeight] = this._labels[0].get_preferred_height(-1);
let spacing = this._items[0].child.get_theme_node().get_length('spacing');
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
let thumbnailSize = THUMBNAIL_DEFAULT_SIZE * scaleFactor;
availHeight = Math.min(availHeight - labelNaturalHeight - totalPadding - spacing, THUMBNAIL_DEFAULT_SIZE);
availHeight = Math.min(availHeight - labelNaturalHeight - totalPadding - spacing, thumbnailSize);
let binHeight = availHeight + this._items[0].get_theme_node().get_vertical_padding() + this.actor.get_theme_node().get_vertical_padding() - spacing;
binHeight = Math.min(THUMBNAIL_DEFAULT_SIZE, binHeight);
binHeight = Math.min(thumbnailSize, binHeight);
for (let i = 0; i < this._thumbnailBins.length; i++) {
let mutterWindow = this._windows[i].get_compositor_private();
if (!mutterWindow)
continue;
let clone = _createWindowClone(mutterWindow, THUMBNAIL_DEFAULT_SIZE);
let clone = _createWindowClone(mutterWindow, thumbnailSize);
this._thumbnailBins[i].set_height(binHeight);
this._thumbnailBins[i].add_actor(clone);
this._clones.push(clone);

View File

@ -1,5 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const St = imports.gi.St;
@ -20,7 +21,9 @@ const Animation = new Lang.Class({
this._isPlaying = false;
this._timeoutId = 0;
this._frame = 0;
this._animations = St.TextureCache.get_default().load_sliced_image (filename, width, height,
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
this._animations = St.TextureCache.get_default().load_sliced_image (filename, width, height, scaleFactor,
Lang.bind(this, this._animationsLoaded));
this.actor.set_child(this._animations);
},
@ -59,7 +62,7 @@ const Animation = new Lang.Class({
_update: function() {
this._showFrame(this._frame + 1);
return true;
return GLib.SOURCE_CONTINUE;
},
_animationsLoaded: function() {

View File

@ -5,7 +5,6 @@ const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk;
const GMenu = imports.gi.GMenu;
const Shell = imports.gi.Shell;
const Lang = imports.lang;
const Signals = imports.signals;
@ -47,24 +46,40 @@ const INDICATORS_ANIMATION_MAX_TIME = 0.75;
const PAGE_SWITCH_TRESHOLD = 0.2;
const PAGE_SWITCH_TIME = 0.3;
// Recursively load a GMenuTreeDirectory; we could put this in ShellAppSystem too
function _loadCategory(dir, view) {
let iter = dir.iter();
let appSystem = Shell.AppSystem.get_default();
let nextType;
while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
if (nextType == GMenu.TreeItemType.ENTRY) {
let entry = iter.get_entry();
let app = appSystem.lookup_app_by_tree_entry(entry);
if (!entry.get_app_info().get_nodisplay())
view.addApp(app);
} else if (nextType == GMenu.TreeItemType.DIRECTORY) {
let itemDir = iter.get_directory();
if (!itemDir.get_is_nodisplay())
_loadCategory(itemDir, view);
const VIEWS_SWITCH_TIME = 0.4;
const VIEWS_SWITCH_ANIMATION_DELAY = 0.1;
function _getCategories(info) {
let categoriesStr = info.get_categories();
if (!categoriesStr)
return [];
return categoriesStr.split(';');
}
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({
Name: 'BaseAppView',
@ -84,6 +99,9 @@ const BaseAppView = new Lang.Class({
else
this._grid = new IconGrid.IconGrid(gridParams);
this._grid.connect('key-focus-in', Lang.bind(this, function(grid, actor) {
this._keyFocusIn(actor);
}));
// Standard hack for ClutterBinLayout
this._grid.actor.x_expand = true;
@ -91,46 +109,43 @@ const BaseAppView = new Lang.Class({
this._allItems = [];
},
_keyFocusIn: function(actor) {
// Nothing by default
},
removeAll: function() {
this._grid.removeAll();
this._grid.destroyAll();
this._items = {};
this._allItems = [];
},
_getItemId: function(item) {
throw new Error('Not implemented');
_redisplay: function() {
this.removeAll();
this._loadApps();
},
_createItemIcon: function(item) {
throw new Error('Not implemented');
getAllItems: function() {
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) {
throw new Error('Not implemented');
},
_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;
return a.name.localeCompare(b.name);
},
loadGrid: function() {
this._allItems.sort(Lang.bind(this, this._compareItems));
for (let i = 0; i < this._allItems.length; i++) {
let id = this._getItemId(this._allItems[i]);
if (!id)
continue;
this._grid.addItem(this._items[id]);
}
this._allItems.sort(this._compareItems);
this._allItems.forEach(Lang.bind(this, function(item) {
this._grid.addItem(item);
}));
this.emit('view-loaded');
},
@ -243,7 +258,8 @@ const PageIndicators = new Lang.Class({
Tweener.addTween(children[i],
{ translation_x: 0,
time: INDICATORS_BASE_TIME + delay * i,
transition: 'easeInOutQuad'
transition: 'easeInOutQuad',
delay: VIEWS_SWITCH_ANIMATION_DELAY
});
}
}
@ -281,7 +297,7 @@ const AllView = new Lang.Class({
this._pageIndicators.actor.connect('scroll-event', Lang.bind(this, this._onScroll));
this.actor.add_actor(this._pageIndicators.actor);
this._folderIcons = [];
this.folderIcons = [];
this._stack = new St.Widget({ layout_manager: new Clutter.BinLayout() });
let box = new St.BoxLayout({ vertical: true });
@ -346,6 +362,76 @@ const AllView = new Lang.Class({
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() {
@ -415,7 +501,7 @@ const AllView = new Lang.Class({
_onScroll: function(actor, event) {
if (this._displayingPopup)
return true;
return Clutter.EVENT_STOP;
let direction = event.get_scroll_direction();
if (direction == Clutter.ScrollDirection.UP)
@ -423,7 +509,7 @@ const AllView = new Lang.Class({
else if (direction == Clutter.ScrollDirection.DOWN)
this.goToPage(this._currentPage + 1);
return true;
return Clutter.EVENT_STOP;
},
_onPan: function(action) {
@ -454,63 +540,17 @@ const AllView = new Lang.Class({
_onKeyPressEvent: function(actor, event) {
if (this._displayingPopup)
return true;
return Clutter.EVENT_STOP;
if (event.get_key_symbol() == Clutter.Page_Up) {
this.goToPage(this._currentPage - 1);
return true;
return Clutter.EVENT_STOP;
} else if (event.get_key_symbol() == Clutter.Page_Down) {
this.goToPage(this._currentPage + 1);
return true;
return Clutter.EVENT_STOP;
}
return false;
},
_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));
return Clutter.EVENT_PROPAGATE;
},
addFolderPopup: function(popup) {
@ -525,7 +565,7 @@ const AllView = new Lang.Class({
}));
},
_ensureIconVisible: function(icon) {
_keyFocusIn: function(icon) {
let itemPage = this._grid.getItemPage(icon);
this.goToPage(itemPage);
},
@ -577,8 +617,8 @@ const AllView = new Lang.Class({
this._availWidth = availWidth;
this._availHeight = availHeight;
// Update folder views
for (let i = 0; i < this._folderIcons.length; i++)
this._folderIcons[i].adaptToSize(availWidth, availHeight);
for (let i = 0; i < this.folderIcons.length; i++)
this.folderIcons[i].adaptToSize(availWidth, availHeight);
}
});
Signals.addSignalMethods(AllView.prototype);
@ -607,13 +647,18 @@ const FrequentView = new Lang.Class({
this._noFrequentAppsLabel.hide();
this._usage = Shell.AppUsage.get_default();
this.actor.connect('notify::mapped', Lang.bind(this, function() {
if (this.actor.mapped)
this._redisplay();
}));
},
hasUsefulData: function() {
return this._usage.get_most_used("").length >= MIN_FREQUENT_APPS_COUNT;
},
loadApps: function() {
_loadApps: function() {
let mostUsed = this._usage.get_most_used ("");
let hasUsefulData = this.hasUsefulData();
this._noFrequentAppsLabel.visible = !hasUsefulData;
@ -691,16 +736,6 @@ const AppDisplay = new Lang.Class({
Name: 'AppDisplay',
_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.connect('changed::remember-app-usage',
Lang.bind(this, this._updateFrequentVisibility));
@ -754,22 +789,15 @@ const AppDisplay = new Lang.Class({
initialView = Views.ALL;
this._showView(initialView);
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) {
for (let i = 0; i < this._views.length; i++) {
let actor = this._views[i].view.actor;
let params = { time: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME,
opacity: (i == activeIndex) ? 255 : 0 };
let params = { time: VIEWS_SWITCH_TIME,
opacity: (i == activeIndex) ? 255 : 0,
delay: (i == activeIndex) ? VIEWS_SWITCH_ANIMATION_DELAY : 0 };
if (i == activeIndex)
actor.visible = true;
else
@ -796,52 +824,6 @@ const AppDisplay = new Lang.Class({
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) {
this._showView(Views.ALL);
this._views[Views.ALL].view.selectApp(id);
@ -871,8 +853,8 @@ const AppSearchProvider = new Lang.Class({
getResultMetas: function(apps, callback) {
let metas = [];
for (let i = 0; i < apps.length; i++) {
let app = apps[i];
metas.push({ 'id': app,
let app = this._appSys.lookup_app(apps[i]);
metas.push({ 'id': app.get_id(),
'name': app.get_name(),
'createIcon': function(size) {
return app.create_icon_texture(size);
@ -886,15 +868,29 @@ const AppSearchProvider = new Lang.Class({
return results.slice(0, maxNumber);
},
getInitialResultSet: function(terms) {
this.searchSystem.setResults(this, this._appSys.initial_search(terms));
getInitialResultSet: function(terms, callback, cancellable) {
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) {
this.searchSystem.setResults(this, this._appSys.subsearch(previousResults, terms));
getSubsearchResultSet: function(previousResults, terms, callback, cancellable) {
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 modifiers = event ? event.get_state() : 0;
let openNewWindow = modifiers & Clutter.ModifierType.CONTROL_MASK;
@ -913,8 +909,8 @@ const AppSearchProvider = new Lang.Class({
app.open_new_window(workspace);
},
createResultObject: function (resultMeta, terms) {
let app = resultMeta['id'];
createResultObject: function (resultMeta) {
let app = this._appSys.lookup_app(resultMeta['id']);
return new AppIcon(app);
}
});
@ -940,36 +936,27 @@ const FolderView = new Lang.Class({
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);
_keyFocusIn: function(actor) {
Util.ensureActorVisibleInScrollView(this.actor, actor);
},
createFolderIcon: function(size) {
let icon = new St.Widget({ layout_manager: new Clutter.BinLayout(),
style_class: 'app-folder-icon',
width: size, height: size });
let layout = new Clutter.TableLayout();
let icon = new St.Widget({ layout_manager: layout,
style_class: 'app-folder-icon' });
layout.hookup_style(icon);
let subSize = Math.floor(FOLDER_SUBICON_FRACTION * size);
let aligns = [ Clutter.ActorAlign.START, Clutter.ActorAlign.END ];
for (let i = 0; i < Math.min(this._allItems.length, 4); i++) {
let texture = this._allItems[i].create_icon_texture(subSize);
let bin = new St.Bin({ child: texture,
x_expand: true, y_expand: true });
bin.set_x_align(aligns[i % 2]);
bin.set_y_align(aligns[Math.floor(i / 2)]);
icon.add_actor(bin);
let numItems = this._allItems.length;
for (let i = 0; i < 4; i++) {
let bin;
if (i < numItems) {
let texture = this._allItems[i].app.create_icon_texture(subSize);
bin = new St.Bin({ child: texture });
} else {
bin = new St.Bin({ width: subSize, height: subSize });
}
layout.pack(bin, i % 2, Math.floor(i / 2));
}
return icon;
@ -1041,10 +1028,12 @@ const FolderView = new Lang.Class({
const FolderIcon = new Lang.Class({
Name: 'FolderIcon',
_init: function(dir, parentView) {
this._dir = dir;
_init: function(id, path, parentView) {
this.id = id;
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',
button_mask: St.ButtonMask.ONE,
toggle_mode: true,
@ -1055,15 +1044,11 @@ const FolderIcon = new Lang.Class({
// whether we need to update arrow side, position etc.
this._popupInvalidated = false;
let label = this._dir.get_name();
this.icon = new IconGrid.BaseIcon(label,
{ createIcon: Lang.bind(this, this._createIcon), setSizeManually: true });
this.icon = new IconGrid.BaseIcon('', { createIcon: Lang.bind(this, this._createIcon), setSizeManually: true });
this.actor.set_child(this.icon.actor);
this.actor.label_actor = this.icon.label;
this.view = new FolderView();
_loadCategory(dir, this.view);
this.view.loadGrid();
this.actor.connect('clicked', Lang.bind(this,
function() {
@ -1076,6 +1061,63 @@ const FolderIcon = new Lang.Class({
if (!this.actor.mapped && this._popup)
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) {
@ -1154,6 +1196,7 @@ const FolderIcon = new Lang.Class({
this._popupInvalidated = true;
},
});
Signals.addSignalMethods(FolderIcon.prototype);
const AppFolderPopup = new Lang.Class({
Name: 'AppFolderPopup',
@ -1207,13 +1250,13 @@ const AppFolderPopup = new Lang.Class({
_onKeyPress: function(actor, event) {
if (!this._isOpen)
return false;
return Clutter.EVENT_PROPAGATE;
if (event.get_key_symbol() != Clutter.KEY_Escape)
return false;
return Clutter.EVENT_PROPAGATE;
this.popdown();
return true;
return Clutter.EVENT_STOP;
},
toggle: function() {
@ -1272,6 +1315,9 @@ const AppIcon = new Lang.Class({
_init : function(app, iconParams) {
this.app = app;
this.id = app.get_id();
this.name = app.get_name();
this.actor = new St.Button({ style_class: 'app-well-app',
reactive: true,
button_mask: St.ButtonMask.ONE | St.ButtonMask.TWO,
@ -1352,13 +1398,15 @@ const AppIcon = new Lang.Class({
this._removeMenuTimeout();
this._menuTimeoutId = Mainloop.timeout_add(MENU_POPUP_TIMEOUT,
Lang.bind(this, function() {
this._menuTimeoutId = 0;
this.popupMenu();
return GLib.SOURCE_REMOVE;
}));
} else if (button == 3) {
this.popupMenu();
return true;
return Clutter.EVENT_STOP;
}
return false;
return Clutter.EVENT_PROPAGATE;
},
_onClicked: function(actor, button) {
@ -1367,10 +1415,6 @@ const AppIcon = new Lang.Class({
if (button == 1) {
this._onActivate(Clutter.get_current_event());
} else if (button == 2) {
// Last workspace is always empty
let launchWorkspace = global.screen.get_workspace_by_index(global.screen.n_workspaces - 1);
launchWorkspace.activate(global.get_current_time());
this.emit('launching');
this.app.open_new_window(-1);
Main.overview.hide();
}
@ -1429,7 +1473,6 @@ const AppIcon = new Lang.Class({
},
_onActivate: function (event) {
this.emit('launching');
let modifiers = event.get_state();
if (modifiers & Clutter.ModifierType.CONTROL_MASK
@ -1481,8 +1524,6 @@ const AppIconMenu = new Lang.Class({
this._source = source;
this.connect('activate', Lang.bind(this, this._onActivate));
this.actor.add_style_class_name('app-well-menu');
// Chain our visibility and lifecycle to that of the source
@ -1498,7 +1539,9 @@ const AppIconMenu = new Lang.Class({
_redisplay: function() {
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
// of the current desktop and other windows.
@ -1506,25 +1549,54 @@ const AppIconMenu = new Lang.Class({
let separatorShown = windows.length > 0 && windows[0].get_workspace() != activeWorkspace;
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();
separatorShown = true;
}
let item = this._appendMenuItem(windows[i].title);
item._window = windows[i];
let item = this._appendMenuItem(window.title);
item.connect('activate', Lang.bind(this, function() {
this.emit('activate-window', window);
}));
}
if (!this._source.app.is_window_backed()) {
if (windows.length > 0)
this._appendSeparator();
this._appendSeparator();
this._newWindowMenuItem = this._appendMenuItem(_("New Window"));
this._newWindowMenuItem.connect('activate', Lang.bind(this, function() {
this._source.app.open_new_window(-1);
this.emit('activate-window', null);
}));
this._appendSeparator();
let appInfo = this._source.app.get_app_info();
let actions = appInfo.list_actions();
for (let i = 0; i < actions.length; i++) {
let action = actions[i];
let item = this._appendMenuItem(appInfo.get_action_name(action));
item.connect('activate', Lang.bind(this, function(emitter, event) {
this._source.app.launch_action(action, event.get_time(), -1);
this.emit('activate-window', null);
}));
}
this._appendSeparator();
let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
this._newWindowMenuItem = this._appendMenuItem(_("New Window"));
this._appendSeparator();
this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ? _("Remove from Favorites")
: _("Add to Favorites"));
if (isFavorite) {
let item = this._appendMenuItem(_("Remove from Favorites"));
item.connect('activate', Lang.bind(this, function() {
let favs = AppFavorites.getAppFavorites();
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 +1615,6 @@ const AppIconMenu = new Lang.Class({
popup: function(activatingButton) {
this._redisplay();
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);

View File

@ -50,11 +50,9 @@ const BackgroundCache = new Lang.Class({
effects: Meta.BackgroundEffects.NONE });
let content = null;
let candidateContent = null;
for (let i = 0; i < this._patterns.length; i++) {
if (!this._patterns[i])
continue;
if (this._patterns[i].get_shading() != params.shadingType)
continue;
@ -88,7 +86,6 @@ const BackgroundCache = new Lang.Class({
}
this._patterns.push(content);
return content;
},
@ -116,9 +113,9 @@ const BackgroundCache = new Lang.Class({
_removeContent: function(contentList, content) {
let index = contentList.indexOf(content);
if (index >= 0)
contentList.splice(index, 1);
if (index < 0)
throw new Error("Trying to remove invalid content: " + content);
contentList.splice(index, 1);
},
removePatternContent: function(content) {
@ -128,12 +125,32 @@ const BackgroundCache = new Lang.Class({
removeImageContent: function(content) {
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];
this._removeContent(this._images, content);
},
_attachCallerToFileLoad: function(caller, fileLoad) {
fileLoad.callers.push(caller);
if (!caller.cancellable)
return;
caller.cancellable.connect(Lang.bind(this, function() {
let idx = fileLoad.callers.indexOf(caller);
fileLoad.callers.splice(idx, 1);
if (fileLoad.callers.length == 0) {
fileLoad.cancellable.cancel();
let idx = this._pendingFileLoads.indexOf(fileLoad);
this._pendingFileLoads.splice(idx, 1);
}
}));
},
_loadImageContent: function(params) {
params = Params.parse(params, { monitorIndex: 0,
style: null,
@ -142,27 +159,28 @@ const BackgroundCache = new Lang.Class({
cancellable: null,
onFinished: null });
let caller = { monitorIndex: params.monitorIndex,
effects: params.effects,
cancellable: params.cancellable,
onFinished: params.onFinished };
for (let i = 0; i < this._pendingFileLoads.length; i++) {
if (this._pendingFileLoads[i].filename == params.filename &&
this._pendingFileLoads[i].style == params.style) {
this._pendingFileLoads[i].callers.push({ shouldCopy: true,
monitorIndex: params.monitorIndex,
effects: params.effects,
onFinished: params.onFinished });
let fileLoad = this._pendingFileLoads[i];
if (fileLoad.filename == params.filename &&
fileLoad.style == params.style) {
this._attachCallerToFileLoad(caller, fileLoad);
return;
}
}
this._pendingFileLoads.push({ filename: params.filename,
style: params.style,
callers: [{ shouldCopy: false,
monitorIndex: params.monitorIndex,
effects: params.effects,
onFinished: params.onFinished }] });
let fileLoad = { filename: params.filename,
style: params.style,
cancellable: new Gio.Cancellable(),
callers: [] };
this._attachCallerToFileLoad(caller, fileLoad);
let content = new Meta.Background({ meta_screen: global.screen,
monitor: params.monitorIndex,
effects: params.effects });
let content = new Meta.Background({ meta_screen: global.screen });
content.load_file_async(params.filename,
params.style,
@ -173,31 +191,26 @@ const BackgroundCache = new Lang.Class({
content.load_file_finish(result);
this._monitorFile(params.filename);
this._images.push(content);
} catch(e) {
content = null;
}
for (let i = 0; i < this._pendingFileLoads.length; i++) {
let pendingLoad = this._pendingFileLoads[i];
if (pendingLoad.filename != params.filename ||
pendingLoad.style != params.style)
continue;
for (let i = 0; i < fileLoad.callers.length; i++) {
let caller = fileLoad.callers[i];
if (caller.onFinished) {
let newContent;
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);
if (content) {
newContent = content.copy(caller.monitorIndex, caller.effects);
this._images.push(newContent);
}
}
this._pendingFileLoads.splice(i, 1);
caller.onFinished(newContent);
}
}
let idx = this._pendingFileLoads.indexOf(fileLoad);
this._pendingFileLoads.splice(idx, 1);
}));
},
@ -210,11 +223,9 @@ const BackgroundCache = new Lang.Class({
onFinished: null });
let content = null;
let candidateContent = null;
for (let i = 0; i < this._images.length; i++) {
if (!this._images[i])
continue;
if (this._images[i].get_style() != params.style)
continue;
@ -222,7 +233,7 @@ const BackgroundCache = new Lang.Class({
continue;
if (params.style == GDesktopEnums.BackgroundStyle.SPANNED &&
this._images[i].monitor_index != this._monitorIndex)
this._images[i].monitor != params.monitorIndex)
continue;
candidateContent = this._images[i];
@ -262,6 +273,7 @@ const BackgroundCache = new Lang.Class({
if (params.onLoaded) {
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
params.onLoaded(this._animation);
return GLib.SOURCE_REMOVE;
}));
}
}
@ -276,6 +288,7 @@ const BackgroundCache = new Lang.Class({
if (params.onLoaded) {
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
params.onLoaded(this._animation);
return GLib.SOURCE_REMOVE;
}));
}
}));
@ -375,7 +388,7 @@ const Background = new Lang.Class({
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
this.emit('loaded');
return false;
return GLib.SOURCE_REMOVE;
}));
},
@ -414,27 +427,26 @@ const Background = new Lang.Class({
this._fileWatches[filename] = signalId;
},
_addImage: function(content, index, filename) {
content.brightness = this._brightness;
content.vignette_sharpness = this._vignetteSharpness;
_ensureImage: function(index) {
if (this._images[index])
return;
let actor = new Meta.BackgroundActor();
actor.content = content;
// The background pattern is the first actor in
// the group, and all images should be above that.
this.actor.insert_child_at_index(actor, index + 1);
this._images[index] = actor;
this._watchCacheFile(filename);
},
_updateImage: function(content, index, filename) {
_updateImage: function(index, content, filename) {
content.brightness = this._brightness;
content.vignette_sharpness = this._vignetteSharpness;
this._cache.removeImageContent(this._images[index].content);
this._images[index].content = content;
let image = this._images[index];
if (image.content)
this._cache.removeImageContent(content);
image.content = content;
this._watchCacheFile(filename);
},
@ -482,11 +494,8 @@ const Background = new Lang.Class({
return;
}
if (!this._images[i]) {
this._addImage(content, i, files[i]);
} else {
this._updateImage(content, i, files[i]);
}
this._ensureImage(i);
this._updateImage(i, content, files[i]);
if (numPendingImages == 0) {
this._setLoaded();
@ -521,7 +530,7 @@ const Background = new Lang.Class({
Lang.bind(this, function() {
this._updateAnimationTimeoutId = 0;
this._updateAnimation();
return false;
return GLib.SOURCE_REMOVE;
}));
},
@ -541,30 +550,33 @@ const Background = new Lang.Class({
});
},
_loadFile: function(filename) {
_loadImage: function(filename) {
this._cache.getImageContent({ monitorIndex: this._monitorIndex,
effects: this._effects,
style: this._style,
filename: filename,
cancellable: this._cancellable,
onFinished: Lang.bind(this, function(content) {
if (!content) {
if (!this._cancellable.is_cancelled())
this._loadAnimation(filename);
return;
if (content) {
this._ensureImage(0);
this._updateImage(0, content, filename);
}
this._addImage(content, 0, filename);
this._setLoaded();
})
});
},
_loadFile: function(filename) {
if (filename.endsWith('.xml'))
this._loadAnimation(filename);
else
this._loadImage(filename);
},
_load: function () {
this._cache = getBackgroundCache();
this._loadPattern(this._cache);
this._loadPattern();
this._style = this._settings.get_enum(BACKGROUND_STYLE_KEY);
if (this._style == GDesktopEnums.BackgroundStyle.NONE) {
@ -638,7 +650,13 @@ const SystemBackground = new Lang.Class({
this.emit('loaded');
})
});
}
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
},
_onDestroy: function() {
this._cache.removeImageContent(this.actor.content);
},
});
Signals.addSignalMethods(SystemBackground.prototype);
@ -726,29 +744,31 @@ const BackgroundManager = new Lang.Class({
}
},
_updateBackground: function(background, monitorIndex) {
let newBackground = this._createBackground(monitorIndex);
newBackground.vignetteSharpness = background.vignetteSharpness;
newBackground.brightness = background.brightness;
newBackground.visible = background.visible;
_updateBackground: function() {
let newBackground = this._createBackground();
newBackground.vignetteSharpness = this.background.vignetteSharpness;
newBackground.brightness = this.background.brightness;
newBackground.visible = this.background.visible;
newBackground.loadedSignalId = newBackground.connect('loaded',
Lang.bind(this, function() {
newBackground.disconnect(newBackground.loadedSignalId);
newBackground.loadedSignalId = 0;
Tweener.addTween(background.actor,
if (this._newBackground != newBackground) {
/* Not interesting, we queued another load */
newBackground.actor.destroy();
return;
}
Tweener.addTween(this.background.actor,
{ opacity: 0,
time: FADE_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
if (this._newBackground == newBackground) {
this.background = newBackground;
this._newBackground = null;
} else {
newBackground.actor.destroy();
}
background.actor.destroy();
this.background.actor.destroy();
this.background = newBackground;
this._newBackground = null;
this.emit('changed');
})
@ -776,7 +796,7 @@ const BackgroundManager = new Lang.Class({
background.changeSignalId = background.connect('changed', Lang.bind(this, function() {
background.disconnect(background.changeSignalId);
background.changeSignalId = 0;
this._updateBackground(background, this._monitorIndex);
this._updateBackground();
}));
background.actor.connect('destroy', Lang.bind(this, function() {

View File

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

View File

@ -69,7 +69,7 @@ const BoxPointer = new Lang.Class({
_muteInput: function() {
if (this._capturedEventId == 0)
this._capturedEventId = this.actor.connect('captured-event',
function() { return true; });
function() { return Clutter.EVENT_STOP; });
},
_unmuteInput: function() {
@ -121,6 +121,9 @@ const BoxPointer = new Lang.Class({
},
hide: function(animate, onComplete) {
if (!this.actor.visible)
return;
let xOffset = 0;
let yOffset = 0;
let themeNode = this.actor.get_theme_node();
@ -284,38 +287,40 @@ const BoxPointer = new Lang.Class({
let skipBottomLeft = false;
let skipBottomRight = false;
switch (this._arrowSide) {
case St.Side.TOP:
if (this._arrowOrigin == x1)
skipTopLeft = true;
else if (this._arrowOrigin == x2)
skipTopRight = true;
break;
if (rise) {
switch (this._arrowSide) {
case St.Side.TOP:
if (this._arrowOrigin == x1)
skipTopLeft = true;
else if (this._arrowOrigin == x2)
skipTopRight = true;
break;
case St.Side.RIGHT:
if (this._arrowOrigin == y1)
skipTopRight = true;
else if (this._arrowOrigin == y2)
skipBottomRight = true;
break;
case St.Side.RIGHT:
if (this._arrowOrigin == y1)
skipTopRight = true;
else if (this._arrowOrigin == y2)
skipBottomRight = true;
break;
case St.Side.BOTTOM:
if (this._arrowOrigin == x1)
skipBottomLeft = true;
else if (this._arrowOrigin == x2)
skipBottomRight = true;
break;
case St.Side.BOTTOM:
if (this._arrowOrigin == x1)
skipBottomLeft = true;
else if (this._arrowOrigin == x2)
skipBottomRight = true;
break;
case St.Side.LEFT:
if (this._arrowOrigin == y1)
skipTopLeft = true;
else if (this._arrowOrigin == y2)
skipBottomLeft = true;
break;
case St.Side.LEFT:
if (this._arrowOrigin == y1)
skipTopLeft = true;
else if (this._arrowOrigin == y2)
skipBottomLeft = true;
break;
}
}
cr.moveTo(x1 + borderRadius, y1);
if (this._arrowSide == St.Side.TOP) {
if (this._arrowSide == St.Side.TOP && rise) {
if (skipTopLeft) {
cr.moveTo(x1, y2 - borderRadius);
cr.lineTo(x1, y1 - rise);
@ -337,7 +342,7 @@ const BoxPointer = new Lang.Class({
3*Math.PI/2, Math.PI*2);
}
if (this._arrowSide == St.Side.RIGHT) {
if (this._arrowSide == St.Side.RIGHT && rise) {
if (skipTopRight) {
cr.lineTo(x2 + rise, y1);
cr.lineTo(x2 + rise, y1 + halfBase);
@ -358,7 +363,7 @@ const BoxPointer = new Lang.Class({
0, Math.PI/2);
}
if (this._arrowSide == St.Side.BOTTOM) {
if (this._arrowSide == St.Side.BOTTOM && rise) {
if (skipBottomLeft) {
cr.lineTo(x1 + halfBase, y2);
cr.lineTo(x1, y2 + rise);
@ -379,7 +384,7 @@ const BoxPointer = new Lang.Class({
Math.PI/2, Math.PI);
}
if (this._arrowSide == St.Side.LEFT) {
if (this._arrowSide == St.Side.LEFT && rise) {
if (skipTopLeft) {
cr.lineTo(x1, y1 + halfBase);
cr.lineTo(x1 - rise, y1);

View File

@ -17,16 +17,18 @@ const SHOW_WEEKDATE_KEY = 'show-weekdate';
// in org.gnome.desktop.interface
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) {
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
* Sunday are non-work days (not true in e.g. Israel, it's Sunday and
* Monday there)
@ -329,25 +331,22 @@ const DBusEventSource = new Lang.Class({
return;
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._curRequestEnd.getTime() / 1000,
forceReload,
Lang.bind(this, this._onEventsReceived),
callFlags);
Gio.DBusCallFlags.NONE);
}
},
requestRange: function(begin, end, forceReload) {
if (forceReload || !(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
requestRange: function(begin, end) {
if (!(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
this.isLoading = true;
this._lastRequestBegin = begin;
this._lastRequestEnd = end;
this._curRequestBegin = begin;
this._curRequestEnd = end;
this._loadEvents(forceReload);
this._loadEvents(false);
}
},
@ -404,6 +403,8 @@ const Calendar = new Lang.Class({
// Start off with the current date
this._selectedDate = new Date();
this._shouldDateGrabFocus = false;
this.actor = new St.Table({ homogeneous: false,
style_class: 'calendar',
reactive: true });
@ -419,21 +420,21 @@ const Calendar = new Lang.Class({
setEventSource: function(eventSource) {
this._eventSource = eventSource;
this._eventSource.connect('changed', Lang.bind(this, function() {
this._update(false);
this._rebuildCalendar();
this._update();
}));
this._update(true);
this._rebuildCalendar();
this._update();
},
// Sets the calendar to show a specific date
setDate: function(date, forceReload) {
if (!_sameDay(date, this._selectedDate)) {
this._selectedDate = date;
this._update(forceReload);
this.emit('selected-date-changed', new Date(this._selectedDate));
} else {
if (forceReload)
this._update(forceReload);
}
setDate: function(date) {
if (_sameDay(date, this._selectedDate))
return;
this._selectedDate = date;
this._update();
this.emit('selected-date-changed', new Date(this._selectedDate));
},
_buildHeader: function() {
@ -497,6 +498,7 @@ const Calendar = new Lang.Class({
this._onNextMonthButtonClicked();
break;
}
return Clutter.EVENT_PROPAGATE;
},
_onPrevMonthButtonClicked: function() {
@ -520,7 +522,7 @@ const Calendar = new Lang.Class({
this._backButton.grab_key_focus();
this.setDate(newDate, false);
this.setDate(newDate);
},
_onNextMonthButtonClicked: function() {
@ -544,28 +546,26 @@ const Calendar = new Lang.Class({
this._forwardButton.grab_key_focus();
this.setDate(newDate, false);
this.setDate(newDate);
},
_onSettingsChange: function() {
this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY);
this._buildHeader();
this._update(false);
this._rebuildCalendar();
this._update();
},
_update: function(forceReload) {
_rebuildCalendar: 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);
// Remove everything but the topBox and the weekday labels
let children = this.actor.get_children();
for (let i = this._firstDayIndex; i < children.length; i++)
children[i].destroy();
this._buttons = [];
// 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
@ -583,11 +583,13 @@ const Calendar = new Lang.Class({
// Actually computing the number of weeks is complex, but we know that the
// problematic categories (2 and 4) always start on week start, and that
// all months at the end have 6 weeks.
let beginDate = new Date(this._selectedDate);
beginDate.setDate(1);
beginDate.setSeconds(0);
beginDate.setHours(12);
this._calendarBegin = new Date(beginDate);
let year = beginDate.getYear();
let daysToWeekStart = (7 + beginDate.getDay() - this._weekStart) % 7;
@ -608,13 +610,10 @@ const Calendar = new Lang.Class({
if (this._eventSource.isDummy)
button.reactive = false;
let iterStr = iter.toUTCString();
button._date = new Date(iter);
button.connect('clicked', Lang.bind(this, function() {
this._shouldDateGrabFocus = true;
let newlySelectedDate = new Date(iterStr);
this.setDate(newlySelectedDate, false);
this.setDate(button._date);
this._shouldDateGrabFocus = false;
}));
@ -622,9 +621,9 @@ const Calendar = new Lang.Class({
let styleClass = 'calendar-day-base calendar-day';
if (_isWorkDay(iter))
styleClass += ' calendar-work-day'
styleClass += ' calendar-work-day';
else
styleClass += ' calendar-nonwork-day'
styleClass += ' calendar-nonwork-day';
// Hack used in lieu of border-collapse - see gnome-shell.css
if (row == 2)
@ -641,7 +640,7 @@ const Calendar = new Lang.Class({
styleClass += ' calendar-other-month-day';
if (hasEvents)
styleClass += ' calendar-day-with-events'
styleClass += ' calendar-day-with-events';
button.style_class = styleClass;
@ -649,12 +648,7 @@ const Calendar = new Lang.Class({
this.actor.add(button,
{ row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7 });
if (_sameDay(this._selectedDate, iter)) {
button.add_style_pseudo_class('active');
if (this._shouldDateGrabFocus)
button.grab_key_focus();
}
this._buttons.push(button);
if (this._useWeekdate && iter.getDay() == 4) {
let label = new St.Label({ text: _getCalendarWeekForDate(iter).toString(),
@ -668,9 +662,32 @@ const Calendar = new Lang.Class({
if (iter.getDay() == this._weekStart)
row++;
}
// Signal to the event source that we are interested in events
// 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');
if (this._shouldDateGrabFocus)
button.grab_key_focus();
}
else
button.remove_style_pseudo_class('active');
}));
}
});

View File

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

View File

@ -64,7 +64,7 @@ function startAppForMount(app, mount) {
try {
retval = app.launch(files,
global.create_app_launch_context())
global.create_app_launch_context(0, -1))
} catch (e) {
log('Unable to launch the application ' + app.get_name()
+ ': ' + e.toString());

View File

@ -45,7 +45,9 @@ const KeyringDialog = new Lang.Class({
this.prompt.bind_property('message', subject, 'text', GObject.BindingFlags.SYNC_CREATE);
this._messageBox.add(subject,
{ y_fill: false,
{ x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
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.line_wrap = true;
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', warning, 'text', GObject.BindingFlags.SYNC_CREATE);

View File

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

View File

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

View File

@ -1,5 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
@ -28,6 +29,8 @@ const SCROLLBACK_HISTORY_LINES = 10;
// See Notification._onEntryChanged
const COMPOSING_STOP_TIMEOUT = 5;
const CLOCK_FORMAT_KEY = 'clock-format';
const NotificationDirection = {
SENT: 'chat-sent',
RECEIVED: 'chat-received'
@ -621,7 +624,11 @@ const ChatSource = new Lang.Class({
this.notify();
},
_channelClosed: function() {
destroy: function(reason) {
if (this._destroyed)
return;
this._destroyed = true;
this._channel.disconnect(this._closedId);
this._channel.disconnect(this._receivedId);
this._channel.disconnect(this._pendingId);
@ -631,7 +638,14 @@ const ChatSource = new Lang.Class({
this._contact.disconnect(this._notifyAvatarId);
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 */
@ -675,7 +689,7 @@ const ChatSource = new Lang.Class({
this._notifyTimeoutId = 0;
return false;
return GLib.SOURCE_REMOVE;
},
// This is called for both messages we send from
@ -894,14 +908,14 @@ const ChatNotification = new Lang.Class({
let group = props.group;
if (group != this._lastGroup) {
let style = 'chat-group-' + group;
this._lastGroup = group;
this._lastGroupActor = new St.BoxLayout({ style_class: style,
vertical: true });
this.addActor(this._lastGroupActor);
let emptyLine = new St.Label({ style_class: 'chat-empty-line' });
this.addActor(emptyLine);
}
this._lastGroupActor.add(body, props.childProps);
this._lastMessageBox = new St.BoxLayout({ vertical: false });
this._lastMessageBox.add(body, props.childProps);
this.addActor(this._lastMessageBox);
this.updated();
@ -930,50 +944,102 @@ const ChatNotification = new Lang.Class({
let format;
// Show only the hour if date is on today
if(daysAgo < 1){
format = "<b>%H:%M</b>";
}
// Show the word "Yesterday" and time if date is on yesterday
else if(daysAgo <2){
/* Translators: this is the word "Yesterday" followed by a time string. i.e. "Yesterday, 14:30"*/
// xgettext:no-c-format
format = _("<b>Yesterday</b>, <b>%H:%M</b>");
}
// Show a week day and time if date is in the last week
else if (daysAgo < 7) {
/* Translators: this is the week day name followed by a time string. i.e. "Monday, 14:30*/
// xgettext:no-c-format
format = _("<b>%A</b>, <b>%H:%M</b>");
let desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
let clockFormat = desktopSettings.get_string(CLOCK_FORMAT_KEY);
} else if (date.getYear() == now.getYear()) {
/* Translators: this is the month name and day number followed by a time string. i.e. "May 25, 14:30"*/
// xgettext:no-c-format
format = _("<b>%B</b> <b>%d</b>, <b>%H:%M</b>");
} else {
/* Translators: this is the month name, day number, year number followed by a time string. i.e. "May 25 2012, 14:30"*/
// xgettext:no-c-format
format = _("<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> ");
}
switch (clockFormat) {
case '24h':
// Show only the time if date is on today
if(daysAgo < 1){
/* Translators: Time in 24h format */
format = _("%H\u2236%M");
}
// Show the word "Yesterday" and time if date is on yesterday
else if(daysAgo <2){
/* Translators: this is the word "Yesterday" followed by a
time string in 24h format. i.e. "Yesterday, 14:30" */
// xgettext:no-c-format
format = _("Yesterday, %H\u2236%M");
}
// Show a week day and time if date is in the last week
else if (daysAgo < 7) {
/* Translators: this is the week day name followed by a time
string in 24h format. i.e. "Monday, 14:30" */
// xgettext:no-c-format
format = _("%A, %H\u2236%M");
} else if (date.getYear() == now.getYear()) {
/* Translators: this is the month name and day number
followed by a time string in 24h format.
i.e. "May 25, 14:30" */
// xgettext:no-c-format
format = _("%B %d, %H\u2236%M");
} else {
/* Translators: this is the month name, day number, year
number followed by a time string in 24h format.
i.e. "May 25 2012, 14:30" */
// xgettext:no-c-format
format = _("%B %d %Y, %H\u2236%M");
}
break;
default:
/* explicit fall-through */
case '12h':
// Show only the time if date is on today
if(daysAgo < 1){
/* Translators: Time in 24h format */
format = _("%l\u2236%M %p");
}
// Show the word "Yesterday" and time if date is on yesterday
else if(daysAgo <2){
/* Translators: this is the word "Yesterday" followed by a
time string in 12h format. i.e. "Yesterday, 2:30 pm" */
// xgettext:no-c-format
format = _("Yesterday, %l\u2236%M %p");
}
// Show a week day and time if date is in the last week
else if (daysAgo < 7) {
/* Translators: this is the week day name followed by a time
string in 12h format. i.e. "Monday, 2:30 pm" */
// xgettext:no-c-format
format = _("%A, %l\u2236%M %p");
} else if (date.getYear() == now.getYear()) {
/* Translators: this is the month name and day number
followed by a time string in 12h format.
i.e. "May 25, 2:30 pm" */
// xgettext:no-c-format
format = _("%B %d, %l\u2236%M %p");
} else {
/* Translators: this is the month name, day number, year
number followed by a time string in 12h format.
i.e. "May 25 2012, 2:30 pm"*/
// xgettext:no-c-format
format = _("%B %d %Y, %l\u2236%M %p");
}
break;
}
return date.toLocaleFormat(format);
},
appendTimestamp: function() {
this._timestampTimeoutId = 0;
let lastMessageTime = this._history[0].time;
let lastMessageDate = new Date(lastMessageTime * 1000);
let timeLabel = this._append({ body: this._formatTimestamp(lastMessageDate),
group: 'meta',
styles: ['chat-meta-message'],
childProps: { expand: true, x_fill: false,
x_align: St.Align.END },
noTimestamp: true,
timestamp: lastMessageTime });
let timeLabel = new St.Label({ text: this._formatTimestamp(lastMessageDate),
style_class: 'chat-meta-message',
x_expand: true,
y_expand: true,
x_align: Clutter.ActorAlign.END,
y_align: Clutter.ActorAlign.END });
this._lastMessageBox.add_actor(timeLabel);
this._filterMessages();
return false;
return GLib.SOURCE_REMOVE;
},
appendAliasChange: function(oldAlias, newAlias) {
@ -1011,7 +1077,7 @@ const ChatNotification = new Lang.Class({
this.source.setChatState(Tp.ChannelChatState.PAUSED);
return false;
return GLib.SOURCE_REMOVE;
},
_onEntryChanged: function() {
@ -1204,7 +1270,8 @@ const SubscriptionRequestNotification = new Lang.Class({
if (file) {
let uri = file.get_uri();
iconBox.child = textureCache.load_uri_async(uri, iconBox._size, iconBox._size);
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
iconBox.child = textureCache.load_uri_async(uri, iconBox._size, iconBox._size, scaleFactor);
}
else {
iconBox.child = new St.Icon({ icon_name: 'avatar-default',
@ -1337,7 +1404,7 @@ const AccountNotification = new Lang.Class({
let cmd = 'empathy-accounts --select-account=' +
account.get_path_suffix();
let app_info = Gio.app_info_create_from_commandline(cmd, null, 0);
app_info.launch([], global.create_app_launch_context());
app_info.launch([], global.create_app_launch_context(0, -1));
}));
this._enabledId = account.connect('notify::enabled',
@ -1355,7 +1422,12 @@ const AccountNotification = new Lang.Class({
if (status == Tp.ConnectionStatus.CONNECTED) {
this.destroy();
} else if (status == Tp.ConnectionStatus.DISCONNECTED) {
this.update(this.title, this._getMessage(account.connection_error));
let connectionError = account.connection_error;
if (connectionError == Tp.error_get_dbus_name(Tp.Error.CANCELLED))
this.destroy();
else
this.update(this.title, this._getMessage(connectionError));
}
}));
},

View File

@ -1,6 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Signals = imports.signals;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
@ -380,6 +381,8 @@ const DashActor = new Lang.Class({
}
});
const baseIconSizes = [ 16, 22, 24, 32, 48, 64 ];
const Dash = new Lang.Class({
Name: 'Dash',
@ -576,7 +579,8 @@ const Dash = new Lang.Class({
Lang.bind(this, function() {
this._labelShowing = true;
item.showLabel();
return false;
this._showLabelTimeoutId = 0;
return GLib.SOURCE_REMOVE;
}));
if (this._resetHoverTimeoutId > 0) {
Mainloop.source_remove(this._resetHoverTimeoutId);
@ -592,7 +596,8 @@ const Dash = new Lang.Class({
this._resetHoverTimeoutId = Mainloop.timeout_add(DASH_ITEM_HOVER_TIMEOUT,
Lang.bind(this, function() {
this._labelShowing = false;
return false;
this._resetHoverTimeoutId = 0;
return GLib.SOURCE_REMOVE;
}));
}
}
@ -629,25 +634,24 @@ const Dash = new Lang.Class({
let minHeight, natHeight;
// Enforce the current icon size during the size request
let [currentWidth, currentHeight] = firstIcon.icon.get_size();
firstIcon.icon.set_size(this.iconSize, this.iconSize);
firstIcon.setIconSize(this.iconSize);
[minHeight, natHeight] = firstButton.get_preferred_height(-1);
firstIcon.icon.set_size(currentWidth, currentHeight);
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
let iconSizes = baseIconSizes.map(function(s) {
return s * scaleFactor;
});
// Subtract icon padding and box spacing from the available height
availHeight -= iconChildren.length * (natHeight - this.iconSize) +
availHeight -= iconChildren.length * (natHeight - this.iconSize * scaleFactor) +
(iconChildren.length - 1) * spacing;
let availSize = availHeight / iconChildren.length;
let iconSizes = [ 16, 22, 24, 32, 48, 64 ];
let newIconSize = 16;
let newIconSize = baseIconSizes[0];
for (let i = 0; i < iconSizes.length; i++) {
if (iconSizes[i] < availSize)
newIconSize = iconSizes[i];
newIconSize = baseIconSizes[i];
}
if (newIconSize == this.iconSize)

View File

@ -63,9 +63,14 @@ const DateMenuButton = new Lang.Class({
hbox.add(vbox);
// Date
this._date = new St.Label({ style_class: 'datemenu-date-label',
can_focus: true });
vbox.add(this._date);
this._date = new St.Button({ style_class: 'datemenu-date-label',
can_focus: true,
});
this._date.connect('clicked',
Lang.bind(this, function() {
this._calendar.setDate(new Date(), false);
}));
vbox.add(this._date, { x_fill: false });
this._eventList = new Calendar.EventsList();
this._calendar = new Calendar.Calendar();
@ -113,22 +118,7 @@ const DateMenuButton = new Lang.Class({
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
if (isOpen) {
let now = new Date();
/* Passing true to setDate() forces events to be reloaded. We
* 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
this._calendar.setDate(now);
}
}));
@ -201,7 +191,7 @@ const DateMenuButton = new Lang.Class({
*/
let dateFormat = _("%A %B %e, %Y");
let displayDate = new Date();
this._date.set_text(displayDate.toLocaleFormat(dateFormat));
this._date.set_label(displayDate.toLocaleFormat(dateFormat));
},
_getCalendarApp: function() {
@ -217,7 +207,7 @@ const DateMenuButton = new Lang.Class({
},
_getClockApp: function() {
return Shell.AppSystem.get_default().lookup_app('gnome-clocks.desktop');
return Shell.AppSystem.get_default().lookup_app('org.gnome.clocks.desktop');
},
_onOpenCalendarActivate: function() {
@ -226,7 +216,7 @@ const DateMenuButton = new Lang.Class({
let app = this._getCalendarApp();
if (app.get_id() == 'evolution.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() {

View File

@ -106,10 +106,10 @@ const _Draggable = new Lang.Class({
_onButtonPress : function (actor, event) {
if (event.get_button() != 1)
return false;
return Clutter.EVENT_PROPAGATE;
if (Tweener.getTweenCount(actor))
return false;
return Clutter.EVENT_PROPAGATE;
this._buttonDown = true;
this._grabActor();
@ -118,7 +118,7 @@ const _Draggable = new Lang.Class({
this._dragStartX = stageX;
this._dragStartY = stageY;
return false;
return Clutter.EVENT_PROPAGATE;
},
_grabActor: function() {
@ -164,11 +164,11 @@ const _Draggable = new Lang.Class({
} else if (this._dragActor != null && !this._animationInProgress) {
// Drag must have been cancelled with Esc.
this._dragComplete();
return true;
return Clutter.EVENT_STOP;
} else {
// Drag has never started.
this._ungrabActor();
return false;
return Clutter.EVENT_PROPAGATE;
}
// 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
@ -184,11 +184,11 @@ const _Draggable = new Lang.Class({
let symbol = event.get_key_symbol();
if (symbol == Clutter.Escape) {
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) {
this._dragActor = this.actor._delegate.getDragActor();
this._dragActor.reparent(Main.uiGroup);
Main.uiGroup.add_child(this._dragActor);
this._dragActor.raise_top();
Shell.util_set_hidden_from_pick(this._dragActor, true);
@ -285,7 +285,8 @@ const _Draggable = new Lang.Class({
this._dragOffsetX = actorStageX - this._dragStartX;
this._dragOffsetY = actorStageY - this._dragStartY;
this._dragActor.reparent(Main.uiGroup);
this._dragOrigParent.remove_actor(this._dragActor);
Main.uiGroup.add_child(this._dragActor);
this._dragActor.raise_top();
Shell.util_set_hidden_from_pick(this._dragActor, true);
}
@ -345,6 +346,7 @@ const _Draggable = new Lang.Class({
},
_updateDragHover : function () {
this._updateHoverId = 0;
let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
this._dragX, this._dragY);
let dragEvent = {
@ -360,7 +362,7 @@ const _Draggable = new Lang.Class({
let result = motionFunc(dragEvent);
if (result != DragMotionResult.CONTINUE) {
global.screen.set_cursor(DRAG_CURSOR_MAP[result]);
return false;
return GLib.SOURCE_REMOVE;
}
}
}
@ -378,18 +380,18 @@ const _Draggable = new Lang.Class({
0);
if (result != DragMotionResult.CONTINUE) {
global.screen.set_cursor(DRAG_CURSOR_MAP[result]);
return false;
return GLib.SOURCE_REMOVE;
}
}
target = target.get_parent();
}
global.screen.set_cursor(Meta.Cursor.DND_IN_DRAG);
return false;
return GLib.SOURCE_REMOVE;
},
_queueUpdateDragHover: function() {
if (this._updateHoverId)
GLib.source_remove(this._updateHoverId);
return;
this._updateHoverId = GLib.idle_add(GLib.PRIORITY_DEFAULT,
Lang.bind(this, this._updateDragHover));
@ -555,7 +557,8 @@ const _Draggable = new Lang.Class({
_onAnimationComplete : function (dragActor, eventTime) {
if (this._dragOrigParent) {
dragActor.reparent(this._dragOrigParent);
Main.uiGroup.remove_child(this._dragActor);
this._dragOrigParent.add_actor(this._dragActor);
dragActor.set_scale(this._dragOrigScale, this._dragOrigScale);
dragActor.set_position(this._dragOrigX, this._dragOrigY);
} else {

View File

@ -13,9 +13,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
const Lang = imports.lang;
@ -27,9 +25,11 @@ const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Pango = imports.gi.Pango;
const Polkit = imports.gi.Polkit;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const CheckBox = imports.ui.checkBox;
const GnomeSession = imports.misc.gnomeSession;
const LoginManager = imports.misc.loginManager;
const ModalDialog = imports.ui.modalDialog;
@ -38,8 +38,10 @@ const UserWidget = imports.ui.userWidget;
let _endSessionDialog = null;
const TRIGGER_OFFLINE_UPDATE = '/usr/libexec/pk-trigger-offline-update';
const _ITEM_ICON_SIZE = 48;
const _DIALOG_ICON_SIZE = 32;
const _DIALOG_ICON_SIZE = 48;
const GSM_SESSION_MANAGER_LOGOUT_FORCE = 2;
@ -73,6 +75,7 @@ const logoutDialogContent = {
"You will be logged out automatically in %d seconds.",
seconds).format(seconds);
},
showBatteryWarning: false,
confirmButtons: [{ signal: 'ConfirmedLogout',
label: C_("button", "Log Out") }],
iconStyleClass: 'end-session-dialog-logout-icon',
@ -81,11 +84,14 @@ const logoutDialogContent = {
const shutdownDialogContent = {
subject: C_("title", "Power Off"),
subjectWithUpdates: C_("title", "Install Updates & Power Off"),
description: function(seconds) {
return ngettext("The system will power off automatically in %d second.",
"The system will power off automatically in %d seconds.",
seconds).format(seconds);
},
checkBoxText: C_("checkbox", "Install pending software updates"),
showBatteryWarning: true,
confirmButtons: [{ signal: 'ConfirmedReboot',
label: C_("button", "Restart") },
{ signal: 'ConfirmedShutdown',
@ -102,6 +108,7 @@ const restartDialogContent = {
"The system will restart automatically in %d seconds.",
seconds).format(seconds);
},
showBatteryWarning: false,
confirmButtons: [{ signal: 'ConfirmedReboot',
label: C_("button", "Restart") }],
iconName: 'view-refresh-symbolic',
@ -117,8 +124,11 @@ const restartInstallDialogContent = {
"The system will automatically restart and install updates in %d seconds.",
seconds).format(seconds);
},
showBatteryWarning: true,
confirmButtons: [{ signal: 'ConfirmedReboot',
label: C_("button", "Restart & Install") }],
label: C_("button", "Restart &amp; Install") }],
unusedFutureButtonForTranslation: C_("button", "Install &amp; Power Off"),
unusedFutureCheckBoxForTranslation: C_("checkbox", "Power off after updates are installed"),
iconName: 'view-refresh-symbolic',
iconStyleClass: 'end-session-dialog-shutdown-icon',
showOtherSessions: true,
@ -145,6 +155,14 @@ const LogindSessionIface = '<node> \
const LogindSession = Gio.DBusProxy.makeProxyWrapper(LogindSessionIface);
const UPowerIface = '<node> \
<interface name="org.freedesktop.UPower"> \
<property name="OnBattery" type="b" access="read"/> \
</interface> \
</node>';
const UPowerProxy = Gio.DBusProxy.makeProxyWrapper(UPowerIface);
function findAppFromInhibitor(inhibitor) {
let desktopFile;
try {
@ -197,6 +215,18 @@ function _setLabelText(label, text) {
}
}
function _setCheckBoxLabel(checkBox, text) {
let label = checkBox.getLabelActor();
if (text) {
label.set_text(text);
checkBox.actor.show();
} else {
label.set_text('');
checkBox.actor.hide();
}
}
function init() {
// This always returns the same singleton object
// By instantiating it initially, we register the
@ -216,6 +246,20 @@ const EndSessionDialog = new Lang.Class({
this._userManager = AccountsService.UserManager.get_default();
this._user = this._userManager.get_user(GLib.get_user_name());
this._updatesFile = Gio.File.new_for_path('/system-update');
this._preparedUpdateFile = Gio.File.new_for_path('/var/lib/PackageKit/prepared-update');
this._powerProxy = new UPowerProxy(Gio.DBus.system,
'org.freedesktop.UPower',
'/org/freedesktop/UPower',
Lang.bind(this, function(proxy, error) {
if (error) {
log(error.message);
return;
}
this._powerProxy.connect('g-properties-changed',
Lang.bind(this, this._sync));
this._sync();
}));
this._secondsLeft = 0;
this._totalSecondsToStayOpen = 0;
@ -242,14 +286,17 @@ const EndSessionDialog = new Lang.Class({
x_align: St.Align.END,
y_align: St.Align.START });
let messageLayout = new St.BoxLayout({ vertical: true });
let messageLayout = new St.BoxLayout({ vertical: true,
style_class: 'end-session-dialog-layout' });
mainContentLayout.add(messageLayout,
{ y_align: St.Align.START });
this._subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject' });
messageLayout.add(this._subjectLabel,
{ y_fill: false,
{ x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
this._descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description' });
@ -260,6 +307,16 @@ const EndSessionDialog = new Lang.Class({
{ y_fill: true,
y_align: St.Align.START });
this._checkBox = new CheckBox.CheckBox();
this._checkBox.actor.connect('clicked', Lang.bind(this, this._sync));
messageLayout.add(this._checkBox.actor);
this._batteryWarning = new St.Label({ style_class: 'end-session-dialog-warning',
text: _("Running on battery power: please plug in before installing updates.") });
this._batteryWarning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._batteryWarning.clutter_text.line_wrap = true;
messageLayout.add(this._batteryWarning);
this._scrollView = new St.ScrollView({ style_class: 'end-session-dialog-list' });
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
this.contentLayout.add(this._scrollView,
@ -285,6 +342,12 @@ const EndSessionDialog = new Lang.Class({
this._inhibitorSection.add_actor(this._sessionHeader);
this._inhibitorSection.add_actor(this._sessionList);
try {
this._updatesPermission = Polkit.Permission.new_sync("org.freedesktop.packagekit.trigger-offline-update", null, null);
} catch(e) {
log('No permission to trigger offline updates: %s'.format(e.toString()));
}
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(EndSessionDialogIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/SessionManager/EndSessionDialog');
},
@ -299,13 +362,22 @@ const EndSessionDialog = new Lang.Class({
if (!open)
return;
if (this._type == 2 && this._updatesFile.query_exists(null))
this._type = 3;
let dialogContent = DialogContent[this._type];
let subject = dialogContent.subject;
// Use different title when we are installing updates
if (dialogContent.subjectWithUpdates && this._checkBox.actor.checked)
subject = dialogContent.subjectWithUpdates;
if (dialogContent.showBatteryWarning) {
// Warn when running on battery power
if (this._powerProxy.OnBattery && this._checkBox.actor.checked)
this._batteryWarning.opacity = 255;
else
this._batteryWarning.opacity = 0;
}
let description;
let displayTime = _roundSecondsToInterval(this._totalSecondsToStayOpen,
this._secondsLeft,
@ -388,15 +460,75 @@ const EndSessionDialog = new Lang.Class({
},
_confirm: function(signal) {
this._fadeOutDialog();
this._stopTimer();
this._dbusImpl.emit_signal(signal, null);
let callback = Lang.bind(this, function() {
this._fadeOutDialog();
this._stopTimer();
this._dbusImpl.emit_signal(signal, null);
});
// Offline update not available; just emit the signal
if (!this._checkBox.actor.visible) {
callback();
return;
}
// Trigger the offline update as requested
if (this._checkBox.actor.checked) {
switch (signal) {
case "ConfirmedReboot":
this._triggerOfflineUpdateReboot(callback);
break;
case "ConfirmedShutdown":
// To actually trigger the offline update, we need to
// reboot to do the upgrade. When the upgrade is complete,
// the computer will shut down automatically.
signal = "ConfirmedReboot";
this._triggerOfflineUpdateShutdown(callback);
break;
default:
callback();
break;
}
} else {
this._triggerOfflineUpdateCancel(callback);
}
},
_onOpened: function() {
this._sync();
},
_triggerOfflineUpdateReboot: function(callback) {
this._pkexecSpawn([TRIGGER_OFFLINE_UPDATE, 'reboot'], callback);
},
_triggerOfflineUpdateShutdown: function(callback) {
this._pkexecSpawn([TRIGGER_OFFLINE_UPDATE, 'power-off'], callback);
},
_triggerOfflineUpdateCancel: function(callback) {
this._pkexecSpawn([TRIGGER_OFFLINE_UPDATE, '--cancel'], callback);
},
_pkexecSpawn: function(argv, callback) {
let ret, pid;
try {
[ret, pid] = GLib.spawn_async(null, ['pkexec'].concat(argv), null,
GLib.SpawnFlags.DO_NOT_REAP_CHILD | GLib.SpawnFlags.SEARCH_PATH,
null);
} catch (e) {
log('Error spawning "pkexec %s": %s'.format(argv.join(' '), e.toString()));
callback();
return;
}
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function(pid, status) {
GLib.spawn_close_pid(pid);
callback();
});
},
_startTimer: function() {
let startTime = GLib.get_monotonic_time();
this._secondsLeft = this._totalSecondsToStayOpen;
@ -409,14 +541,15 @@ const EndSessionDialog = new Lang.Class({
this._secondsLeft = this._totalSecondsToStayOpen - secondsElapsed;
if (this._secondsLeft > 0) {
this._sync();
return true;
return GLib.SOURCE_CONTINUE;
}
let dialogContent = DialogContent[this._type];
let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
this._confirm(button.signal);
this._timerId = 0;
return false;
return GLib.SOURCE_REMOVE;
}));
},
@ -543,6 +676,9 @@ const EndSessionDialog = new Lang.Class({
this._totalSecondsToStayOpen = totalSecondsToStayOpen;
this._type = type;
if (this._type == 2 && this._updatesFile.query_exists(null))
this._type = 3;
this._applications = [];
this._applicationList.destroy_all_children();
@ -555,6 +691,8 @@ const EndSessionDialog = new Lang.Class({
return;
}
let dialogContent = DialogContent[this._type];
for (let i = 0; i < inhibitorObjectPaths.length; i++) {
let inhibitor = new GnomeSession.Inhibitor(inhibitorObjectPaths[i], Lang.bind(this, function(proxy, error) {
this._onInhibitorLoaded(proxy);
@ -563,9 +701,23 @@ const EndSessionDialog = new Lang.Class({
this._applications.push(inhibitor);
}
if (DialogContent[type].showOtherSessions)
if (dialogContent.showOtherSessions)
this._loadSessions();
let preparedUpdate = this._preparedUpdateFile.query_exists(null);
let updateAlreadyTriggered = this._updatesFile.query_exists(null);
let updatesAllowed = this._updatesPermission && this._updatesPermission.allowed;
_setCheckBoxLabel(this._checkBox, dialogContent.checkBoxText);
this._checkBox.actor.visible = (dialogContent.checkBoxText && preparedUpdate && updatesAllowed);
this._checkBox.actor.checked = (preparedUpdate && updateAlreadyTriggered);
// We show the warning either together with the checkbox, or when
// updates have already been triggered, but the user doesn't have
// enough permissions to cancel them.
this._batteryWarning.visible = (dialogContent.showBatteryWarning &&
(this._checkBox.actor.visible || preparedUpdate && updateAlreadyTriggered && !updatesAllowed));
this._updateButtons();
if (!this.open(timestamp)) {

View File

@ -5,6 +5,8 @@ imports.gi.versions.Gio = '2.0';
imports.gi.versions.Gdk = '3.0';
imports.gi.versions.GdkPixbuf = '2.0';
imports.gi.versions.Gtk = '3.0';
imports.gi.versions.TelepathyGLib = '0.12';
imports.gi.versions.TelepathyLogger = '0.2';
const Clutter = imports.gi.Clutter;;
const Gettext = imports.gettext;

View File

@ -201,7 +201,7 @@ const InstallExtensionDialog = new Lang.Class({
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();
this.contentLayout.add(box);

View File

@ -38,6 +38,7 @@ const connect = Lang.bind(_signals, _signals.connect);
const disconnect = Lang.bind(_signals, _signals.disconnect);
const ENABLED_EXTENSIONS_KEY = 'enabled-extensions';
const EXTENSION_DISABLE_VERSION_CHECK_KEY = 'disable-extension-version-validation';
var initted = false;
var enabled;
@ -156,7 +157,9 @@ function loadExtension(extension) {
// Default to error, we set success as the last step
extension.state = ExtensionState.ERROR;
if (ExtensionUtils.isOutOfDate(extension)) {
let checkVersion = !global.settings.get_boolean(EXTENSION_DISABLE_VERSION_CHECK_KEY);
if (checkVersion && ExtensionUtils.isOutOfDate(extension)) {
extension.state = ExtensionState.OUT_OF_DATE;
} else {
let enabled = enabledExtensions.indexOf(extension.uuid) != -1;
@ -253,7 +256,7 @@ function onEnabledExtensionsChanged() {
newEnabledExtensions.filter(function(uuid) {
return enabledExtensions.indexOf(uuid) == -1;
}).forEach(function(uuid) {
enableExtension(uuid);
enableExtension(uuid);
});
// Find and disable all the newly disabled extensions: UUIDs found in the
@ -261,18 +264,29 @@ function onEnabledExtensionsChanged() {
enabledExtensions.filter(function(item) {
return newEnabledExtensions.indexOf(item) == -1;
}).forEach(function(uuid) {
disableExtension(uuid);
disableExtension(uuid);
});
enabledExtensions = newEnabledExtensions;
}
function _onVersionValidationChanged() {
if (Main.sessionMode.allowExtensions) {
enabledExtensions.forEach(function(uuid) {
if (ExtensionUtils.extensions[uuid])
reloadExtension(ExtensionUtils.extensions[uuid]);
});
}
}
function _loadExtensions() {
global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged);
global.settings.connect('changed::' + EXTENSION_DISABLE_VERSION_CHECK_KEY, _onVersionValidationChanged);
enabledExtensions = getEnabledExtensions();
let finder = new ExtensionUtils.ExtensionFinder();
finder.connect('extension-found', function(signals, extension) {
finder.connect('extension-found', function(finder, extension) {
loadExtension(extension);
});
finder.scanExtensions();

View File

@ -34,7 +34,11 @@ const FocusCaretTracker = new Lang.Class({
_init: function() {
Atspi.init();
Atspi.set_timeout(250, 250);
this._atspiListener = Atspi.EventListener.new(Lang.bind(this, this._onChanged));
this._focusListenerRegistered = false;
this._caretListenerRegistered = false;
},
_onChanged: function(event) {
@ -45,21 +49,39 @@ const FocusCaretTracker = new Lang.Class({
},
registerFocusListener: function() {
return this._atspiListener.register(STATECHANGED + ':focused') &&
this._atspiListener.register(STATECHANGED + ':selected');
if (this._focusListenerRegistered)
return;
// Ignore the return value, we get an exception if they fail
// And they should never fail
this._atspiListener.register(STATECHANGED + ':focused');
this._atspiListener.register(STATECHANGED + ':selected');
this._focusListenerRegistered = true;
},
registerCaretListener: function() {
return this._atspiListener.register(CARETMOVED);
if (this._caretListenerRegistered)
return;
this._atspiListener.register(CARETMOVED);
this._caretListenerRegistered = true;
},
deregisterFocusListener: function() {
return this._atspiListener.deregister(STATECHANGED + ':focused') &&
this._atspiListener.deregister(STATECHANGED + ':selected');
if (!this._focusListenerRegistered)
return;
this._atspiListener.deregister(STATECHANGED + ':focused');
this._atspiListener.deregister(STATECHANGED + ':selected');
this._focusListenerRegistered = false;
},
deregisterCaretListener: function() {
return this._atspiListener.deregister(CARETMOVED);
if (!this._caretListenerRegistered)
return;
this._atspiListener.deregister(CARETMOVED);
this._caretListenerRegistered = false;
}
});
Signals.addSignalMethods(FocusCaretTracker.prototype);

View File

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

View File

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

View File

@ -143,11 +143,6 @@ const BaseIcon = new Lang.Class({
this.icon = this.createIcon(this.iconSize);
this._iconBin.child = this.icon;
// The icon returned by createIcon() might actually be smaller than
// the requested icon size (for instance StTextureCache does this
// for fallback icons), so set the size explicitly.
this._iconBin.set_size(this.iconSize, this.iconSize);
},
_onStyleChanged: function() {
@ -219,6 +214,20 @@ const IconGrid = new Lang.Class({
this._grid.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this._grid.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this._grid.connect('allocate', Lang.bind(this, this._allocate));
this._grid.connect('actor-added', Lang.bind(this, this._childAdded));
this._grid.connect('actor-removed', Lang.bind(this, this._childRemoved));
},
_keyFocusIn: function(actor) {
this.emit('key-focus-in', actor);
},
_childAdded: function(grid, child) {
child._iconGridKeyFocusInId = child.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
},
_childRemoved: function(grid, child) {
child.disconnect(child._iconGridKeyFocusInId);
},
_getPreferredWidth: function (grid, forHeight, alloc) {
@ -413,15 +422,18 @@ const IconGrid = new Lang.Class({
},
removeAll: function() {
this._items = [];
this._grid.remove_all_children();
},
destroyAll: function() {
this._items = [];
this._grid.destroy_all_children();
},
addItem: function(item, index) {
if (!item.icon || !item.icon instanceof BaseIcon) {
log('Only items with a BaseIcon icon property can be added to IconGrid');
return;
}
if (!item.icon instanceof BaseIcon)
throw new Error('Only items with a BaseIcon icon property can be added to IconGrid');
this._items.push(item);
if (index !== undefined)
@ -430,6 +442,10 @@ const IconGrid = new Lang.Class({
this._grid.add_actor(item.actor);
},
removeItem: function(item) {
this._grid.remove_child(item.actor);
},
getItemAtIndex: function(index) {
return this._grid.get_child_at_index(index);
},
@ -525,6 +541,7 @@ const IconGrid = new Lang.Class({
}
}
});
Signals.addSignalMethods(IconGrid.prototype);
const PaginatedIconGrid = new Lang.Class({
Name: 'PaginatedIconGrid',

View File

@ -82,8 +82,16 @@ const Key = new Lang.Class({
style_class: 'keyboard-key' });
button.key_width = this._key.width;
button.connect('button-press-event', Lang.bind(this, function () { this._key.press(); }));
button.connect('button-release-event', Lang.bind(this, function () { this._key.release(); }));
button.connect('button-press-event', Lang.bind(this,
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;
},
@ -106,8 +114,16 @@ const Key = new Lang.Class({
let label = this._getUnichar(extended_key);
let key = new St.Button({ label: label, style_class: 'keyboard-key' });
key.extended_key = extended_key;
key.connect('button-press-event', Lang.bind(this, function () { extended_key.press(); }));
key.connect('button-release-event', Lang.bind(this, function () { extended_key.release(); }));
key.connect('button-press-event', Lang.bind(this,
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._boxPointer.bin.add_actor(this._extended_keyboard);
@ -252,7 +268,10 @@ const Keyboard = new Lang.Class({
if (!this._showIdleId)
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) {
@ -294,7 +313,7 @@ const Keyboard = new Lang.Class({
else if (release && this._capturedPress)
this._hideSubkeys();
return true;
return Clutter.EVENT_STOP;
},
_addRows : function (keys, layout) {
@ -438,7 +457,6 @@ const Keyboard = new Lang.Class({
_createSource: function () {
if (this._source == null) {
this._source = new KeyboardSource(this);
this._source.setTransient(true);
Main.messageTray.add(this._source);
}
},
@ -480,6 +498,7 @@ const Keyboard = new Lang.Class({
Lang.bind(this, function() {
this._clearKeyboardRestTimer();
this._show(monitor);
return GLib.SOURCE_REMOVE;
}));
},
@ -505,6 +524,7 @@ const Keyboard = new Lang.Class({
Lang.bind(this, function() {
this._clearKeyboardRestTimer();
this._hide();
return GLib.SOURCE_REMOVE;
}));
},

View File

@ -4,7 +4,6 @@ const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
@ -213,12 +212,21 @@ const LayoutManager = new Lang.Class({
this.addChrome(this.trayBox);
this._setupTrayPressure();
this.modalDialogGroup = new St.Widget({ name: 'modalDialogGroup',
layout_manager: new Clutter.BinLayout() });
this.uiGroup.add_actor(this.modalDialogGroup);
this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox',
reactive: true,
track_hover: true });
this.addChrome(this.keyboardBox);
this._keyboardHeightNotifyId = 0;
// A dummy actor that tracks the mouse or text cursor, based on the
// position and size set in setDummyCursorGeometry.
this.dummyCursor = new St.Widget({ width: 0, height: 0 });
this.uiGroup.add_actor(this.dummyCursor);
global.stage.remove_actor(global.top_window_group);
this.uiGroup.add_actor(global.top_window_group);
@ -352,26 +360,26 @@ const LayoutManager = new Lang.Class({
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,
layoutManager: this,
monitorIndex: monitorIndex });
BackgroundMenu.addBackgroundMenu(bgManager.background.actor);
bgManager.connect('changed', Lang.bind(this, function() {
BackgroundMenu.addBackgroundMenu(bgManager.background.actor);
}));
bgManager.connect('changed', Lang.bind(this, this._addBackgroundMenu));
this._addBackgroundMenu(bgManager);
this._bgManagers[monitorIndex] = bgManager;
return bgManager.background;
return bgManager;
},
_createSecondaryBackgrounds: function() {
_showSecondaryBackgrounds: function() {
for (let i = 0; i < this.monitors.length; i++) {
if (i != this.primaryIndex) {
let background = this._createBackground(i);
let background = this._bgManagers[i].background;
background.actor.show();
background.actor.opacity = 0;
Tweener.addTween(background.actor,
{ opacity: 255,
@ -381,10 +389,6 @@ const LayoutManager = new Lang.Class({
}
},
_createPrimaryBackground: function() {
this._createBackground(this.primaryIndex);
},
_updateBackgrounds: function() {
let i;
for (i = 0; i < this._bgManagers.length; i++)
@ -395,11 +399,12 @@ const LayoutManager = new Lang.Class({
if (Main.sessionMode.isGreeter)
return;
if (this._startingUp)
return;
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();
}
},
@ -595,7 +600,7 @@ const LayoutManager = new Lang.Class({
if (Main.sessionMode.isGreeter) {
this.panelBox.translation_y = -this.panelBox.height;
} else {
this._createPrimaryBackground();
this._updateBackgrounds();
// We need to force an update of the regions now before we scale
// the UI group to get the coorect allocation for the struts.
@ -610,7 +615,7 @@ const LayoutManager = new Lang.Class({
this.uiGroup.set_pivot_point(x / global.screen_width,
y / global.screen_height);
this.uiGroup.scale_x = this.uiGroup.scale_y = 0.5;
this.uiGroup.scale_x = this.uiGroup.scale_y = 0.75;
this.uiGroup.opacity = 0;
global.window_group.set_clip(monitor.x, monitor.y, monitor.width, monitor.height);
}
@ -625,7 +630,7 @@ const LayoutManager = new Lang.Class({
// when the system is bogged down
GLib.idle_add(GLib.PRIORITY_LOW, Lang.bind(this, function() {
this._startupAnimation();
return false;
return GLib.SOURCE_REMOVE;
}));
},
@ -673,7 +678,7 @@ const LayoutManager = new Lang.Class({
this.keyboardBox.show();
if (!Main.sessionMode.isGreeter) {
this._createSecondaryBackgrounds();
this._showSecondaryBackgrounds();
global.window_group.remove_clip();
}
@ -723,6 +728,21 @@ const LayoutManager = new Lang.Class({
this._updateRegions();
},
// setDummyCursorGeometry:
//
// 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 and has the right size.
setDummyCursorGeometry: function(x, y, w, h) {
this.dummyCursor.set_position(Math.round(x), Math.round(y));
this.dummyCursor.set_size(Math.round(w), Math.round(h));
},
// addChrome:
// @actor: an actor to add to the chrome
// @params: (optional) additional params
@ -814,13 +834,12 @@ const LayoutManager = new Lang.Class({
let actorData = Params.parse(params, defaultParams);
actorData.actor = actor;
actorData.isToplevel = actor.get_parent() == this.uiGroup;
actorData.visibleId = actor.connect('notify::visible',
Lang.bind(this, this._queueUpdateRegions));
actorData.allocationId = actor.connect('notify::allocation',
Lang.bind(this, this._queueUpdateRegions));
actorData.parentSetId = actor.connect('parent-set',
Lang.bind(this, this._actorReparented));
actorData.destroyId = actor.connect('destroy',
Lang.bind(this, this._untrackActor));
// Note that destroying actor will unset its parent, so we don't
// need to connect to 'destroy' too.
@ -838,22 +857,11 @@ const LayoutManager = new Lang.Class({
this._trackedActors.splice(i, 1);
actor.disconnect(actorData.visibleId);
actor.disconnect(actorData.allocationId);
actor.disconnect(actorData.parentSetId);
actor.disconnect(actorData.destroyId);
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() {
let windowsVisible = Main.sessionMode.hasWindows && !this._inOverview;
@ -864,8 +872,6 @@ const LayoutManager = new Lang.Class({
let actorData = this._trackedActors[i], visible;
if (!actorData.trackFullscreen)
continue;
if (!actorData.isToplevel)
continue;
if (!windowsVisible)
visible = true;
@ -905,8 +911,8 @@ const LayoutManager = new Lang.Class({
return;
if (!this._updateRegionIdle)
this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this._updateRegions),
Meta.PRIORITY_BEFORE_REDRAW);
this._updateRegionIdle = Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
Lang.bind(this, this._updateRegions));
},
_getWindowActorsForWorkspace: function(workspace) {
@ -937,7 +943,7 @@ const LayoutManager = new Lang.Class({
let rects = [], struts = [], i;
if (this._updateRegionIdle) {
Mainloop.source_remove(this._updateRegionIdle);
Meta.later_remove(this._updateRegionIdle);
delete this._updateRegionIdle;
}
@ -1011,19 +1017,39 @@ const LayoutManager = new Lang.Class({
continue;
// Ensure that the strut rects goes all the way to the screen edge,
// as this really what mutter expects.
// as this really what mutter expects. However skip this step
// in cases where this would render an entire monitor unusable.
switch (side) {
case Meta.Side.TOP:
y1 = 0;
let hasMonitorsAbove = this.monitors.some(Lang.bind(this,
function(mon) {
return this._isAboveOrBelowPrimary(mon) &&
mon.y < primary.y;
}));
if (!hasMonitorsAbove)
y1 = 0;
break;
case Meta.Side.BOTTOM:
y2 = global.screen_height;
if (this.primaryIndex == this.bottomIndex)
y2 = global.screen_height;
break;
case Meta.Side.LEFT:
x1 = 0;
let hasMonitorsLeft = this.monitors.some(Lang.bind(this,
function(mon) {
return !this._isAboveOrBelowPrimary(mon) &&
mon.x < primary.x;
}));
if (!hasMonitorsLeft)
x1 = 0;
break;
case Meta.Side.RIGHT:
x2 = global.screen_width;
let hasMonitorsRight = this.monitors.some(Lang.bind(this,
function(mon) {
return !this._isAboveOrBelowPrimary(mon) &&
mon.x > primary.x;
}));
if (!hasMonitorsRight)
x2 = global.screen_width;
break;
}
@ -1042,7 +1068,7 @@ const LayoutManager = new Lang.Class({
workspace.set_builtin_struts(struts);
}
return false;
return GLib.SOURCE_REMOVE;
}
});
Signals.addSignalMethods(LayoutManager.prototype);
@ -1229,20 +1255,20 @@ const HotCorner = new Lang.Class({
this._entered = true;
this._toggleOverview();
}
return false;
return Clutter.EVENT_PROPAGATE;
},
_onCornerLeft : function(actor, event) {
if (event.get_related() != this.actor)
this._entered = false;
// Consume event, otherwise this will confuse onEnvironsLeft
return true;
return Clutter.EVENT_STOP;
},
_onEnvironsLeft : function(actor, event) {
if (event.get_related() != this._corner)
this._entered = false;
return false;
return Clutter.EVENT_PROPAGATE;
}
});

View File

@ -5,11 +5,67 @@ const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Signals = imports.signals;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Params = imports.misc.params;
const Tweener = imports.ui.tweener;
const DEFAULT_FADE_FACTOR = 0.4;
const VIGNETTE_BRIGHTNESS = 0.8;
const VIGNETTE_SHARPNESS = 0.7;
const VIGNETTE_DECLARATIONS = '\
uniform float brightness;\n\
uniform float vignette_sharpness;\n';
const VIGNETTE_CODE = '\
cogl_color_out.a = cogl_color_in.a;\n\
cogl_color_out.rgb = vec3(0.0, 0.0, 0.0);\n\
vec2 position = cogl_tex_coord_in[0].xy - 0.5;\n\
float t = length(2.0 * position);\n\
t = clamp(t, 0.0, 1.0);\n\
float pixel_brightness = mix(1.0, 1.0 - vignette_sharpness, t);\n\
cogl_color_out.a = cogl_color_out.a * (1 - pixel_brightness * brightness);';
const RadialShaderQuad = new Lang.Class({
Name: 'RadialShaderQuad',
Extends: Shell.GLSLQuad,
_init: function(params) {
this.parent(params);
this._brightnessLocation = this.get_uniform_location('brightness');
this._sharpnessLocation = this.get_uniform_location('vignette_sharpness');
this.brightness = 1.0;
this.vignetteSharpness = 0.0;
},
vfunc_build_pipeline: function() {
this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT,
VIGNETTE_DECLARATIONS, VIGNETTE_CODE, true);
},
get brightness() {
return this._brightness;
},
set brightness(v) {
this._brightness = v;
this.set_uniform_float(this._brightnessLocation,
1, [this._brightness]);
},
get vignetteSharpness() {
return this._sharpness;
},
set vignetteSharpness(v) {
this._sharpness = v;
this.set_uniform_float(this._sharpnessLocation,
1, [this._sharpness]);
}
});
/**
* Lightbox:
@ -43,15 +99,23 @@ const Lightbox = new Lang.Class({
width: null,
height: null,
fadeFactor: DEFAULT_FADE_FACTOR,
radialEffect: false,
});
this._container = container;
this._children = container.get_children();
this._fadeFactor = params.fadeFactor;
this.actor = new St.Bin({ x: 0,
y: 0,
style_class: 'lightbox',
reactive: params.inhibitEvents });
this._radialEffect = params.radialEffect;
if (params.radialEffect)
this.actor = new RadialShaderQuad({ x: 0,
y: 0,
reactive: params.inhibitEvents });
else
this.actor = new St.Bin({ x: 0,
y: 0,
opacity: 0,
style_class: 'lightbox',
reactive: params.inhibitEvents });
container.add_actor(this.actor);
this.actor.raise_top();
@ -101,9 +165,18 @@ const Lightbox = new Lang.Class({
fadeInTime = fadeInTime || 0;
Tweener.removeTweens(this.actor);
if (fadeInTime != 0) {
this.shown = false;
this.actor.opacity = 0;
if (this._radialEffect) {
Tweener.addTween(this.actor,
{ brightness: VIGNETTE_BRIGHTNESS,
vignetteSharpness: VIGNETTE_SHARPNESS,
time: fadeInTime,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this.shown = true;
this.emit('shown');
})
});
} else {
Tweener.addTween(this.actor,
{ opacity: 255 * this._fadeFactor,
time: fadeInTime,
@ -113,11 +186,8 @@ const Lightbox = new Lang.Class({
this.emit('shown');
})
});
} else {
this.actor.opacity = 255 * this._fadeFactor;
this.shown = true;
this.emit('shown');
}
this.actor.show();
},
@ -126,7 +196,18 @@ const Lightbox = new Lang.Class({
this.shown = false;
Tweener.removeTweens(this.actor);
if (fadeOutTime != 0) {
if (this._radialEffect) {
Tweener.addTween(this.actor,
{ brightness: 1.0,
vignetteSharpness: 0.0,
opacity: 0,
time: fadeOutTime,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this.actor.hide();
})
});
} else {
Tweener.addTween(this.actor,
{ opacity: 0,
time: fadeOutTime,
@ -135,8 +216,6 @@ const Lightbox = new Lang.Class({
this.actor.hide();
})
});
} else {
this.actor.hide();
}
},

View File

@ -27,6 +27,8 @@ const CHEVRON = '>>> ';
/* Imports...feel free to add here as needed */
var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
'const GLib = imports.gi.GLib; ' +
'const GObject = imports.gi.GObject; ' +
'const Gio = imports.gi.Gio; ' +
'const Gtk = imports.gi.Gtk; ' +
'const Mainloop = imports.mainloop; ' +
'const Meta = imports.gi.Meta; ' +
@ -109,6 +111,7 @@ const AutoComplete = new Lang.Class({
}
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",
@ -558,7 +561,7 @@ const Inspector = new Lang.Class({
_onKeyPressEvent: function (actor, event) {
if (event.get_key_symbol() == Clutter.Escape)
this._close();
return true;
return Clutter.EVENT_STOP;
},
_onButtonPressEvent: function (actor, event) {
@ -567,7 +570,7 @@ const Inspector = new Lang.Class({
this.emit('target', this._target, stageX, stageY);
}
this._close();
return true;
return Clutter.EVENT_STOP;
},
_onScrollEvent: function (actor, event) {
@ -601,12 +604,12 @@ const Inspector = new Lang.Class({
default:
break;
}
return true;
return Clutter.EVENT_STOP;
},
_onMotionEvent: function (actor, event) {
this._update(event);
return true;
return Clutter.EVENT_STOP;
},
_update: function(event) {
@ -669,13 +672,13 @@ const Extensions = new Lang.Class({
_onViewSource: function (actor) {
let extension = actor._extension;
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();
},
_onWebPage: function (actor) {
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();
},
@ -828,7 +831,7 @@ const LookingGlass = new Lang.Class({
global.stage.set_key_focus(this._entry);
}));
this.actor.hide();
return true;
return Clutter.EVENT_STOP;
}));
let gcIcon = new St.Icon({ icon_name: 'gnome-fs-trash-full',
@ -841,7 +844,9 @@ const LookingGlass = new Lang.Class({
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();
@ -1063,7 +1068,7 @@ const LookingGlass = new Lang.Class({
} else {
this.close();
}
return true;
return Clutter.EVENT_STOP;
}
// Ctrl+PgUp and Ctrl+PgDown switches tabs in the notebook view
if (modifierState & Clutter.ModifierType.CONTROL_MASK) {
@ -1073,7 +1078,7 @@ const LookingGlass = new Lang.Class({
this._notebook.nextTab();
}
}
return false;
return Clutter.EVENT_PROPAGATE;
},
open : function() {

View File

@ -11,6 +11,7 @@ const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Signals = imports.signals;
const Background = imports.ui.background;
const FocusCaretTracker = imports.ui.focusCaretTracker;
const Main = imports.ui.main;
const MagnifierDBus = imports.ui.magnifierDBus;
@ -57,20 +58,6 @@ const Magnifier = new Lang.Class({
// Magnifier is a manager of ZoomRegions.
this._zoomRegions = [];
// Export to dbus.
magDBusService = new MagnifierDBus.ShellMagnifier();
let showAtLaunch = this._settingsInit();
this.setActive(showAtLaunch);
},
_initialize: function() {
if (this._initialized)
return;
this._initialized = true;
this._settingsInitLate();
// Create small clutter tree for the magnified mouse.
let cursorTracker = Meta.CursorTracker.get_for_screen(global.screen);
this._mouseSprite = new Clutter.Texture();
@ -86,11 +73,15 @@ const Magnifier = new Lang.Class({
let aZoomRegion = new ZoomRegion(this, this._cursorRoot);
this._zoomRegions.push(aZoomRegion);
this._settingsInitRegion(aZoomRegion);
let showAtLaunch = this._settingsInit(aZoomRegion);
aZoomRegion.scrollContentsTo(this.xMouse, this.yMouse);
cursorTracker.connect('cursor-changed', Lang.bind(this, this._updateMouseSprite));
this._cursorTracker = cursorTracker;
// Export to dbus.
magDBusService = new MagnifierDBus.ShellMagnifier();
this.setActive(showAtLaunch);
},
/**
@ -115,20 +106,21 @@ const Magnifier = new Lang.Class({
* @activate: Boolean to activate or de-activate the magnifier.
*/
setActive: function(activate) {
if (activate == this.isActive())
return;
if (activate)
this._initialize();
let isActive = this.isActive();
this._zoomRegions.forEach (function(zoomRegion, index, array) {
zoomRegion.setActive(activate);
});
if (activate)
this.startTrackingMouse();
else
this.stopTrackingMouse();
if (isActive != activate) {
if (activate) {
Meta.disable_unredirect_for_screen(global.screen);
this.startTrackingMouse();
} else {
Meta.enable_unredirect_for_screen(global.screen);
this.stopTrackingMouse();
}
}
// Make sure system mouse pointer is shown when all zoom regions are
// invisible.
@ -448,68 +440,64 @@ const Magnifier = new Lang.Class({
this._mouseSprite.set_anchor_point(xHot, yHot);
},
_settingsInitRegion: function(zoomRegion) {
// Mag factor is accurate to two decimal places.
let aPref = parseFloat(this._settings.get_double(MAG_FACTOR_KEY).toFixed(2));
if (aPref != 0.0)
zoomRegion.setMagFactor(aPref, aPref);
aPref = this._settings.get_enum(SCREEN_POSITION_KEY);
if (aPref)
zoomRegion.setScreenPosition(aPref);
zoomRegion.setLensMode(this._settings.get_boolean(LENS_MODE_KEY));
zoomRegion.setClampScrollingAtEdges(!this._settings.get_boolean(CLAMP_MODE_KEY));
aPref = this._settings.get_enum(MOUSE_TRACKING_KEY);
if (aPref)
zoomRegion.setMouseTrackingMode(aPref);
aPref = this._settings.get_enum(FOCUS_TRACKING_KEY);
if (aPref)
zoomRegion.setFocusTrackingMode(aPref);
aPref = this._settings.get_enum(CARET_TRACKING_KEY);
if (aPref)
zoomRegion.setCaretTrackingMode(aPref);
aPref = this._settings.get_boolean(INVERT_LIGHTNESS_KEY);
if (aPref)
zoomRegion.setInvertLightness(aPref);
aPref = this._settings.get_double(COLOR_SATURATION_KEY);
if (aPref)
zoomRegion.setColorSaturation(aPref);
let bc = {};
bc.r = this._settings.get_double(BRIGHT_RED_KEY);
bc.g = this._settings.get_double(BRIGHT_GREEN_KEY);
bc.b = this._settings.get_double(BRIGHT_BLUE_KEY);
zoomRegion.setBrightness(bc);
bc.r = this._settings.get_double(CONTRAST_RED_KEY);
bc.g = this._settings.get_double(CONTRAST_GREEN_KEY);
bc.b = this._settings.get_double(CONTRAST_BLUE_KEY);
zoomRegion.setContrast(bc);
},
_settingsInit: function() {
_settingsInit: function(zoomRegion) {
this._appSettings = new Gio.Settings({ schema: APPLICATIONS_SCHEMA });
this._settings = new Gio.Settings({ schema: MAGNIFIER_SCHEMA });
this._appSettings.connect('changed::' + SHOW_KEY, Lang.bind(this, function() {
let active = this._appSettings.get_boolean(SHOW_KEY);
this.setActive(active);
}));
if (zoomRegion) {
// Mag factor is accurate to two decimal places.
let aPref = parseFloat(this._settings.get_double(MAG_FACTOR_KEY).toFixed(2));
if (aPref != 0.0)
zoomRegion.setMagFactor(aPref, aPref);
return this._appSettings.get_boolean(SHOW_KEY);
},
aPref = this._settings.get_enum(SCREEN_POSITION_KEY);
if (aPref)
zoomRegion.setScreenPosition(aPref);
zoomRegion.setLensMode(this._settings.get_boolean(LENS_MODE_KEY));
zoomRegion.setClampScrollingAtEdges(!this._settings.get_boolean(CLAMP_MODE_KEY));
aPref = this._settings.get_enum(MOUSE_TRACKING_KEY);
if (aPref)
zoomRegion.setMouseTrackingMode(aPref);
aPref = this._settings.get_enum(FOCUS_TRACKING_KEY);
if (aPref)
zoomRegion.setFocusTrackingMode(aPref);
aPref = this._settings.get_enum(CARET_TRACKING_KEY);
if (aPref)
zoomRegion.setCaretTrackingMode(aPref);
aPref = this._settings.get_boolean(INVERT_LIGHTNESS_KEY);
if (aPref)
zoomRegion.setInvertLightness(aPref);
aPref = this._settings.get_double(COLOR_SATURATION_KEY);
if (aPref)
zoomRegion.setColorSaturation(aPref);
let bc = {};
bc.r = this._settings.get_double(BRIGHT_RED_KEY);
bc.g = this._settings.get_double(BRIGHT_GREEN_KEY);
bc.b = this._settings.get_double(BRIGHT_BLUE_KEY);
zoomRegion.setBrightness(bc);
bc.r = this._settings.get_double(CONTRAST_RED_KEY);
bc.g = this._settings.get_double(CONTRAST_GREEN_KEY);
bc.b = this._settings.get_double(CONTRAST_BLUE_KEY);
zoomRegion.setContrast(bc);
}
_settingsInitLate: function() {
let showCrosshairs = this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY);
this.addCrosshairs();
this.setCrosshairsVisible(showCrosshairs);
this._appSettings.connect('changed::' + SHOW_KEY,
Lang.bind(this, function() {
this.setActive(this._appSettings.get_boolean(SHOW_KEY));
}));
this._settings.connect('changed::' + SCREEN_POSITION_KEY,
Lang.bind(this, this._updateScreenPosition));
this._settings.connect('changed::' + MAG_FACTOR_KEY,
@ -573,6 +561,7 @@ const Magnifier = new Lang.Class({
Lang.bind(this, function() {
this.setCrosshairsClip(this._settings.get_boolean(CROSS_HAIRS_CLIP_KEY));
}));
return this._appSettings.get_boolean(SHOW_KEY);
},
_updateScreenPosition: function() {
@ -731,8 +720,16 @@ const ZoomRegion = new Lang.Class({
let component = event.source.get_component_iface();
if (!component || event.detail1 != 1)
return;
let extents = component.get_extents(Atspi.CoordType.SCREEN);
[this._xFocus, this._yFocus] = [extents.x, extents.y]
let extents;
try {
extents = component.get_extents(Atspi.CoordType.SCREEN);
} catch(e) {
log('Failed to read extents of focused component: ' + e.message);
return;
}
[this._xFocus, this._yFocus] = [extents.x + (extents.width / 2),
extents.y + (extents.height / 2)];
this._centerFromFocusPosition();
},
@ -740,7 +737,14 @@ const ZoomRegion = new Lang.Class({
let text = event.source.get_text_iface();
if (!text)
return;
let extents = text.get_character_extents(text.get_caret_offset(), 0);
let extents;
try {
extents = text.get_character_extents(text.get_caret_offset(), 0);
} catch(e) {
log('Failed to read extents of text caret: ' + e.message);
return;
}
[this._xCaret, this._yCaret] = [extents.x, extents.y];
this._centerFromCaretPosition();
},
@ -763,6 +767,9 @@ const ZoomRegion = new Lang.Class({
} else {
this._destroyActors();
}
this._syncCaretTracking();
this._syncFocusTracking();
},
/**
@ -822,10 +829,7 @@ const ZoomRegion = new Lang.Class({
*/
setFocusTrackingMode: function(mode) {
this._focusTrackingMode = mode;
if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.NONE)
this._focusCaretTracker.deregisterFocusListener();
else
this._focusCaretTracker.registerFocusListener();
this._syncFocusTracking();
},
/**
@ -834,10 +838,27 @@ const ZoomRegion = new Lang.Class({
*/
setCaretTrackingMode: function(mode) {
this._caretTrackingMode = mode;
if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.NONE)
this._focusCaretTracker.deregisterCaretListener();
this._syncCaretTracking();
},
_syncFocusTracking: function() {
let enabled = this._focusTrackingMode != GDesktopEnums.MagnifierFocusTrackingMode.NONE &&
this.isActive();
if (enabled)
this._focusCaretTracker.registerFocusListener();
else
this._focusCaretTracker.deregisterFocusListener();
},
_syncCaretTracking: function() {
let enabled = this._caretTrackingMode != GDesktopEnums.MagnifierCaretTrackingMode.NONE &&
this.isActive();
if (enabled)
this._focusCaretTracker.registerCaretListener();
else
this._focusCaretTracker.deregisterCaretListener();
},
/**
@ -1178,13 +1199,17 @@ const ZoomRegion = new Lang.Class({
// Add a background for when the magnified uiGroup is scrolled
// out of view (don't want to see desktop showing through).
this._background = new Clutter.Actor({ background_color: Main.DEFAULT_BACKGROUND_COLOR,
layout_manager: new Clutter.BinLayout(),
width: global.screen_width,
height: global.screen_height });
let noiseTexture = (new Background.SystemBackground()).actor;
this._background.add_actor(noiseTexture);
mainGroup.add_actor(this._background);
// Clone the group that contains all of UI on the screen. This is the
// chrome, the windows, etc.
this._uiGroupClone = new Clutter.Clone({ source: Main.uiGroup });
this._uiGroupClone = new Clutter.Clone({ source: Main.uiGroup,
clip_to_allocation: true });
mainGroup.add_actor(this._uiGroupClone);
// Add either the given mouseSourceActor to the ZoomRegion, or a clone of

View File

@ -112,11 +112,6 @@ function start() {
Gio.DesktopAppInfo.set_desktop_env('GNOME');
sessionMode = new SessionMode.SessionMode();
sessionMode.connect('sessions-loaded', _sessionsLoaded);
sessionMode.init();
}
function _sessionsLoaded() {
sessionMode.connect('updated', _sessionUpdated);
_initializePrefs();
_initializeUI();
@ -614,7 +609,7 @@ function queueDeferredWork(workId) {
_deferredTimeoutId = Mainloop.timeout_add_seconds(DEFERRED_TIMEOUT_SECONDS, function () {
_runAllDeferredWork();
_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 GnomeSession = imports.misc.gnomeSession;
const GrabHelper = imports.ui.grabHelper;
const Hash = imports.misc.hash;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const PointerWatcher = imports.ui.pointerWatcher;
@ -76,7 +75,7 @@ const Urgency = {
NORMAL: 1,
HIGH: 2,
CRITICAL: 3
}
};
function _fixMarkup(text, allowMarkup) {
if (allowMarkup) {
@ -187,7 +186,7 @@ const URLHighlighter = new Lang.Class({
// The MessageTray doesn't actually hide us, so
// we need to check for paint opacities as well.
if (!actor.visible || actor.get_paint_opacity() == 0)
return false;
return Clutter.EVENT_PROPAGATE;
// Keep Notification.actor from seeing this and taking
// 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) {
if (!actor.visible || actor.get_paint_opacity() == 0)
return false;
return Clutter.EVENT_PROPAGATE;
let urlId = this._findUrlAtPos(event);
if (urlId != -1) {
@ -204,14 +203,14 @@ const URLHighlighter = new Lang.Class({
if (url.indexOf(':') == -1)
url = 'http://' + url;
Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context());
return true;
Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context(0, -1));
return Clutter.EVENT_STOP;
}
return false;
return Clutter.EVENT_PROPAGATE;
}));
this.actor.connect('motion-event', Lang.bind(this, function(actor, event) {
if (!actor.visible || actor.get_paint_opacity() == 0)
return false;
return Clutter.EVENT_PROPAGATE;
let urlId = this._findUrlAtPos(event);
if (urlId != -1 && !this._cursorChanged) {
@ -221,16 +220,17 @@ const URLHighlighter = new Lang.Class({
global.screen.set_cursor(Meta.Cursor.DEFAULT);
this._cursorChanged = false;
}
return false;
return Clutter.EVENT_PROPAGATE;
}));
this.actor.connect('leave-event', Lang.bind(this, function() {
if (!this.actor.visible || this.actor.get_paint_opacity() == 0)
return;
return Clutter.EVENT_PROPAGATE;
if (this._cursorChanged) {
this._cursorChanged = false;
global.screen.set_cursor(Meta.Cursor.DEFAULT);
}
return Clutter.EVENT_PROPAGATE;
}));
},
@ -1001,6 +1001,9 @@ const Notification = new Lang.Class({
Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
Lang.bind(this,
function() {
if (this._destroyed)
return false;
if (this._canExpandContent()) {
this._addBannerBody();
this._table.add_style_class_name('multi-line-notification');
@ -1159,32 +1162,12 @@ const SourceActor = new Lang.Class({
}));
this._actorDestroyed = false;
this._counterLabel = new St.Label( {x_align: Clutter.ActorAlign.CENTER,
x_expand: true,
y_align: Clutter.ActorAlign.CENTER,
y_expand: true });
this._counterBin = new St.Bin({ style_class: 'summary-source-counter',
child: this._counterLabel,
layout_manager: new Clutter.BinLayout() });
this._counterBin.hide();
this._counterBin.connect('style-changed', Lang.bind(this, function() {
let themeNode = this._counterBin.get_theme_node();
this._counterBin.translation_x = themeNode.get_length('-shell-counter-overlap-x');
this._counterBin.translation_y = themeNode.get_length('-shell-counter-overlap-y');
}));
this._iconBin = new St.Bin({ width: size,
height: size,
x_fill: true,
y_fill: true });
let scale_factor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
this._iconBin = new St.Bin({ x_fill: true,
height: size * scale_factor,
width: size * scale_factor });
this.actor.add_actor(this._iconBin);
this.actor.add_actor(this._counterBin);
this._source.connect('count-updated', Lang.bind(this, this._updateCount));
this._updateCount();
this._source.connect('icon-updated', Lang.bind(this, this._updateIcon));
this._updateIcon();
@ -1208,6 +1191,48 @@ const SourceActor = new Lang.Class({
_allocate: function(actor, box, flags) {
// the iconBin should fill our entire box
this._iconBin.allocate(box, flags);
},
_updateIcon: function() {
if (this._actorDestroyed)
return;
if (!this._iconSet)
this._iconBin.child = this._source.createIcon(this._size);
}
});
const SourceActorWithLabel = new Lang.Class({
Name: 'SourceActorWithLabel',
Extends: SourceActor,
_init: function(source, size) {
this.parent(source, size);
this._counterLabel = new St.Label({ x_align: Clutter.ActorAlign.CENTER,
x_expand: true,
y_align: Clutter.ActorAlign.CENTER,
y_expand: true });
this._counterBin = new St.Bin({ style_class: 'summary-source-counter',
child: this._counterLabel,
layout_manager: new Clutter.BinLayout() });
this._counterBin.hide();
this._counterBin.connect('style-changed', Lang.bind(this, function() {
let themeNode = this._counterBin.get_theme_node();
this._counterBin.translation_x = themeNode.get_length('-shell-counter-overlap-x');
this._counterBin.translation_y = themeNode.get_length('-shell-counter-overlap-y');
}));
this.actor.add_actor(this._counterBin);
this._source.connect('count-updated', Lang.bind(this, this._updateCount));
this._updateCount();
},
_allocate: function(actor, box, flags) {
this.parent(actor, box, flags);
let childBox = new Clutter.ActorBox();
@ -1230,14 +1255,6 @@ const SourceActor = new Lang.Class({
this._counterBin.allocate(childBox, flags);
},
_updateIcon: function() {
if (this._actorDestroyed)
return;
if (!this._iconSet)
this._iconBin.child = this._source.createIcon(this._size);
},
_updateCount: function() {
if (this._actorDestroyed)
return;
@ -1263,7 +1280,6 @@ const Source = new Lang.Class({
this.title = title;
this.iconName = iconName;
this.isTransient = false;
this.isChat = false;
this.isMuted = false;
this.keepTrayOnSummaryClick = false;
@ -1323,10 +1339,6 @@ const Source = new Lang.Class({
return rightClickMenu;
},
setTransient: function(isTransient) {
this.isTransient = isTransient;
},
setTitle: function(newTitle) {
this.title = newTitle;
this.emit('title-changed');
@ -1355,7 +1367,7 @@ const Source = new Lang.Class({
if (this._mainIcon)
return;
this._mainIcon = new SourceActor(this, this.SOURCE_ICON_SIZE);
this._mainIcon = new SourceActorWithLabel(this, this.SOURCE_ICON_SIZE);
},
// Unlike createIcon, this always returns the same actor;
@ -1528,9 +1540,9 @@ const SummaryItem = new Lang.Class({
_onKeyPress: function(actor, event) {
if (event.get_key_symbol() == Clutter.KEY_Up) {
actor.emit('clicked', 1);
return true;
return Clutter.EVENT_STOP;
}
return false;
return Clutter.EVENT_PROPAGATE;
},
prepareNotificationStackForShowing: function() {
@ -1625,7 +1637,7 @@ const MessageTrayMenu = new Lang.Class({
this._clearItem = this.addAction(_("Clear Messages"), function() {
let toDestroy = tray.getSources().filter(function(source) {
return source.isClearable;
})
});
toDestroy.forEach(function(source) {
source.destroy();
@ -1788,6 +1800,7 @@ const MessageTray = new Lang.Class({
this._setClickedSummaryItem(null);
this._updateState();
actor.grab_key_focus();
return Clutter.EVENT_PROPAGATE;
}));
global.focus_manager.add_group(this.actor);
this._summary = new St.BoxLayout({ style_class: 'message-tray-summary',
@ -1902,7 +1915,7 @@ const MessageTray = new Lang.Class({
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._expandActiveNotification));
this._sources = new Hash.Map();
this._sources = new Map();
this._chatSummaryItemsCount = 0;
this._trayDwellTimeoutId = 0;
@ -1939,7 +1952,7 @@ const MessageTray = new Lang.Class({
},
_updateNoMessagesLabel: function() {
this._noMessages.visible = this._sources.size() == 0;
this._noMessages.visible = this._sources.size == 0;
},
_sessionUpdated: function() {
@ -1993,32 +2006,37 @@ const MessageTray = new Lang.Class({
this._trayDwellTimeoutId = 0;
if (Main.layoutManager.bottomMonitor.inFullscreen)
return false;
return GLib.SOURCE_REMOVE;
// 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
// overview we have to take the overview's modal push into account
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
// dwell (by clicking or typing), don't activate the message tray
let focusWindow = global.display.focus_window;
let currentUserTime = focusWindow ? focusWindow.user_time : 0;
if (currentUserTime != this._trayDwellUserTime)
return false;
return GLib.SOURCE_REMOVE;
this.openTray();
return false;
return GLib.SOURCE_REMOVE;
},
_onNotificationKeyRelease: function(actor, event) {
if (event.get_key_symbol() == Clutter.KEY_Escape && event.get_state() == 0) {
this._closeNotification();
return true;
this._expireNotification();
return Clutter.EVENT_STOP;
}
return false;
return Clutter.EVENT_PROPAGATE;
},
_expireNotification: function() {
this._notificationExpired = true;
this._updateState();
},
_closeNotification: function() {
@ -2102,7 +2120,8 @@ const MessageTray = new Lang.Class({
},
_removeSource: function(source) {
let [, obj] = this._sources.delete(source);
let obj = this._sources.get(source);
this._sources.delete(source);
let summaryItem = obj.summaryItem;
if (source.isChat)
@ -2123,7 +2142,7 @@ const MessageTray = new Lang.Class({
},
getSources: function() {
return this._sources.keys();
return [k for (k of this._sources.keys())];
},
_onSourceEnableChanged: function(policy, source) {
@ -2339,7 +2358,7 @@ const MessageTray = new Lang.Class({
this._updateNotificationTimeout(0);
this._updateState();
}
return false;
return GLib.SOURCE_REMOVE;
},
_escapeTray: function() {
@ -2356,6 +2375,13 @@ const MessageTray = new Lang.Class({
// _updateState() figures out what (if anything) needs to be done
// at the present time.
_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.
this._notificationQueue = this._notificationQueue.filter(function(n) {
return !n.acknowledged;
@ -2373,20 +2399,19 @@ const MessageTray = new Lang.Class({
this._showNotification();
}
} else if (this._notificationState == State.SHOWN) {
let pinned = this._pointerInNotification && !this._notificationRemoved;
let expired = (this._userActiveWhileNotificationShown &&
this._notificationTimeoutId == 0 &&
!(this._notification.urgency == Urgency.CRITICAL) &&
this._notification.urgency != Urgency.CRITICAL &&
!this._notification.focused &&
!this._pointerInNotification);
!this._pointerInNotification) || this._notificationExpired;
let mustClose = (this._notificationRemoved || !hasNotifications || expired || this._traySummoned);
if (mustClose) {
let animate = hasNotifications && !this._notificationRemoved;
this._hideNotification(animate);
} else if (pinned && !this._notification.expanded) {
} else if (this._pointerInNotification && !this._notification.expanded) {
this._expandNotification(false);
} else if (pinned) {
} else if (this._pointerInNotification) {
this._ensureNotificationFocused();
}
}
@ -2433,11 +2458,16 @@ const MessageTray = new Lang.Class({
this._desktopCloneState == State.SHOWN);
let desktopCloneShouldBeVisible = (trayShouldBeVisible);
if (!desktopCloneIsVisible && desktopCloneShouldBeVisible) {
if (!desktopCloneIsVisible && desktopCloneShouldBeVisible)
this._showDesktopClone();
} else if (desktopCloneIsVisible && !desktopCloneShouldBeVisible) {
else if (desktopCloneIsVisible && !desktopCloneShouldBeVisible)
this._hideDesktopClone();
}
this._updatingState = false;
// Clean transient variables that are used to communicate actions
// to updateState()
this._notificationExpired = false;
},
_tween: function(actor, statevar, value, params) {
@ -2665,7 +2695,7 @@ const MessageTray = new Lang.Class({
this._lastSeenMouseX = x;
this._lastSeenMouseY = y;
return false;
return GLib.SOURCE_REMOVE;
},
_hideNotification: function(animate) {
@ -2756,12 +2786,17 @@ const MessageTray = new Lang.Class({
} else if (this._notification.y != expandedY) {
// Tween also opacity here, to override a possible tween that's
// currently hiding the notification.
this._tween(this._notificationWidget, '_notificationState', State.SHOWN,
{ y: expandedY,
opacity: 255,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
Tweener.addTween(this._notificationWidget,
{ y: expandedY,
opacity: 255,
time: ANIMATION_TIME,
transition: 'easeOutQuad',
// HACK: Drive the state machine here better,
// instead of overwriting tweens
onComplete: Lang.bind(this, function() {
this._notificationState = State.SHOWN;
}),
});
}
},
@ -2802,13 +2837,13 @@ const MessageTray = new Lang.Class({
Lang.bind(this, this._onSourceDoneDisplayingContent));
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.show();
this._adjustSummaryBoxPointerPosition();
this._grabHelper.grab({ actor: this._summaryBoxPointer.bin.child,
onUngrab: Lang.bind(this, this._onSummaryBoxPointerUngrabbed) });
this._summaryBoxPointerState = State.SHOWING;
this._summaryBoxPointer.show(BoxPointer.PopupAnimation.FULL, Lang.bind(this, function() {
this._summaryBoxPointerState = State.SHOWN;
@ -2863,13 +2898,13 @@ const MessageTray = new Lang.Class({
case Clutter.KEY_Escape:
this._setClickedSummaryItem(null);
this._updateState();
return true;
return Clutter.EVENT_STOP;
case Clutter.KEY_Delete:
this._clickedSummaryItem.source.destroy();
this._escapeTray();
return true;
return Clutter.EVENT_STOP;
}
return false;
return Clutter.EVENT_PROPAGATE;
},
_onSummaryBoxPointerUngrabbed: function() {
@ -2907,8 +2942,6 @@ const MessageTray = new Lang.Class({
this._summaryBoxPointerItem.doneShowingNotificationStack();
this._summaryBoxPointerItem = null;
if (source.isTransient && !this._reNotifyAfterHideNotification)
source.destroy(NotificationDestroyedReason.EXPIRED);
if (this._reNotifyAfterHideNotification) {
this._onNotify(this._reNotifyAfterHideNotification.source, this._reNotifyAfterHideNotification);
this._reNotifyAfterHideNotification = null;
@ -2927,7 +2960,6 @@ const SystemNotificationSource = new Lang.Class({
_init: function() {
this.parent(_("System Information"), 'dialog-information-symbolic');
this.setTransient(true);
},
open: function() {

View File

@ -41,7 +41,6 @@ const ModalDialog = new Lang.Class({
_init: function(params) {
params = Params.parse(params, { shellReactive: false,
styleClass: null,
parentActor: Main.uiGroup,
keybindingMode: Shell.KeyBindingMode.SYSTEM_MODAL,
shouldFadeIn: true,
destroyOnClose: true });
@ -57,7 +56,7 @@ const ModalDialog = new Lang.Class({
x: 0,
y: 0,
accessible_role: Atk.Role.DIALOG });
params.parentActor.add_actor(this._group);
Main.layoutManager.modalDialogGroup.add_actor(this._group);
let constraint = new Clutter.BindConstraint({ source: global.stage,
coordinate: Clutter.BindCoordinate.ALL });
@ -89,7 +88,8 @@ const ModalDialog = new Lang.Class({
if (!this._shellReactive) {
this._lightbox = new Lightbox.Lightbox(this._group,
{ inhibitEvents: true });
{ inhibitEvents: true,
radialEffect: true });
this._lightbox.highlight(this._backgroundBin);
this._eventBlocker = new Clutter.Actor({ reactive: true });
@ -229,6 +229,7 @@ const ModalDialog = new Lang.Class({
_onKeyPressEvent: function(object, event) {
this._pressedKey = event.get_key_symbol();
return Clutter.EVENT_PROPAGATE;
},
_onKeyReleaseEvent: function(object, event) {
@ -237,21 +238,21 @@ const ModalDialog = new Lang.Class({
let symbol = event.get_key_symbol();
if (symbol != pressedKey)
return false;
return Clutter.EVENT_PROPAGATE;
let buttonInfo = this._buttonKeys[symbol];
if (!buttonInfo)
return false;
return Clutter.EVENT_PROPAGATE;
let button = buttonInfo['button'];
let action = buttonInfo['action'];
if (action && button.reactive) {
action();
return true;
return Clutter.EVENT_STOP;
}
return false;
return Clutter.EVENT_PROPAGATE;
},
_onGroupDestroy: function() {

View File

@ -181,12 +181,10 @@ const FdoNotificationDaemon = new Lang.Class({
},
// Returns the source associated with ndata.notification if it is set.
// Otherwise, returns the source associated with the title and pid if
// such source is stored in this._sources and the notification is not
// transient. If the existing or requested source is associated with
// a tray icon and passed in pid matches a pid of an existing source,
// the title match is ignored to enable representing a tray icon and
// notifications from the same application with a single source.
// If the existing or requested source is associated with a tray icon
// and passed in pid matches a pid of an existing 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
// pid is provided.
@ -204,32 +202,20 @@ const FdoNotificationDaemon = new Lang.Class({
if (ndata && ndata.notification)
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);
if (source) {
source.setTitle(title);
return source;
}
let source = this._lookupSource(title, pid, trayIcon);
if (source) {
source.setTitle(title);
return source;
}
let source = new FdoNotificationDaemonSource(title, pid, sender, trayIcon, ndata ? ndata.hints['desktop-entry'] : null);
source.setTransient(isForTransientNotification);
if (!isForTransientNotification) {
this._sources.push(source);
source.connect('destroy', Lang.bind(this,
function() {
let index = this._sources.indexOf(source);
if (index >= 0)
this._sources.splice(index, 1);
}));
}
this._sources.push(source);
source.connect('destroy', Lang.bind(this, function() {
let index = this._sources.indexOf(source);
if (index >= 0)
this._sources.splice(index, 1);
}));
Main.messageTray.add(source);
return source;
@ -261,7 +247,7 @@ const FdoNotificationDaemon = new Lang.Class({
Mainloop.idle_add(Lang.bind(this,
function () {
this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
return false;
return GLib.SOURCE_REMOVE;
}));
return invocation.return_value(GLib.Variant.new('(u)', [id]));
}
@ -336,20 +322,10 @@ const FdoNotificationDaemon = new Lang.Class({
let [pid] = result;
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;
source.connect('destroy', Lang.bind(this, function() {
delete this._senderToPid[sender];
}));
}
this._senderToPid[sender] = pid;
source.connect('destroy', Lang.bind(this, function() {
delete this._senderToPid[sender];
}));
this._notifyForSource(source, ndata);
}));
@ -582,7 +558,7 @@ const FdoNotificationDaemonSource = new Lang.Class({
},
_createPolicy: function() {
if (this.app) {
if (this.app && this.app.get_app_info()) {
let id = this.app.get_id().replace(/\.desktop$/,'');
return new MessageTray.NotificationApplicationPolicy(id);
} else {

View File

@ -1,6 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const St = imports.gi.St;
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, radius, radius, Math.PI, 1.5 * Math.PI);
cr.fill();
cr.$dispose();
}
});
@ -77,7 +79,8 @@ const OsdWindow = new Lang.Class({
y_expand: true,
x_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',
vertical: true });
this.actor.add_actor(this._box);
@ -122,13 +125,15 @@ const OsdWindow = new Lang.Class({
setLevel: function(level) {
this._level.actor.visible = (level != undefined);
if (this.actor.visible)
Tweener.addTween(this._level,
{ level: level,
time: LEVEL_ANIMATION_TIME,
transition: 'easeOutQuad' });
else
this._level.level = level;
if (level) {
if (this.actor.visible)
Tweener.addTween(this._level,
{ level: level,
time: LEVEL_ANIMATION_TIME,
transition: 'easeOutQuad' });
else
this._level.level = level;
}
},
show: function() {
@ -172,6 +177,7 @@ const OsdWindow = new Lang.Class({
Meta.enable_unredirect_for_screen(global.screen);
})
});
return GLib.SOURCE_REMOVE;
},
_reset: function() {
@ -182,14 +188,21 @@ const OsdWindow = new Lang.Class({
_monitorsChanged: function() {
/* 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 scaleh = monitor.height / 480.0;
let scale = Math.min(scalew, scaleh);
this._popupSize = 110 * Math.max(1, scale);
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
this._icon.icon_size = this._popupSize / (2 * scaleFactor);
this._box.translation_y = monitor.height / 4;
this._icon.icon_size = this._popupSize / 2;
this._box.style_changed();
},
@ -205,6 +218,27 @@ const OsdWindow = new Lang.Class({
let minWidth = this._popupSize - verticalPadding - leftBorder - rightBorder;
let minHeight = this._popupSize - horizontalPadding - topBorder - bottomBorder;
this._box.style = 'min-height: %dpx;'.format(Math.max(minWidth, minHeight));
// minWidth/minHeight here are in real pixels,
// but the theme takes measures in unscaled dimensions
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
this._box.style = 'min-height: %dpx;'.format(Math.max(minWidth, minHeight) / scaleFactor);
},
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 -*-
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Meta = imports.gi.Meta;
const Mainloop = imports.mainloop;
@ -13,6 +14,7 @@ const Gdk = imports.gi.Gdk;
const Background = imports.ui.background;
const DND = imports.ui.dnd;
const LayoutManager = imports.ui.layout;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const OverviewControls = imports.ui.overviewControls;
@ -112,9 +114,6 @@ const Overview = new Lang.Class({
// rendering options without duplicating the texture data.
let monitor = Main.layoutManager.primaryMonitor;
this._desktopFade = new St.Bin();
Main.layoutManager.overviewGroup.add_child(this._desktopFade);
let layout = new Clutter.BinLayout();
this._stack = new Clutter.Actor({ layout_manager: layout });
this._stack.add_constraint(new LayoutManager.MonitorConstraint({ primary: true }));
@ -133,6 +132,9 @@ const Overview = new Lang.Class({
Main.layoutManager.overviewGroup.add_child(this._backgroundGroup);
this._bgManagers = [];
this._desktopFade = new St.Widget();
Main.layoutManager.overviewGroup.add_child(this._desktopFade);
this._activationTime = 0;
this.visible = false; // animating to overview, in overview, animating out
@ -147,7 +149,7 @@ const Overview = new Lang.Class({
this._coverPane = new Clutter.Actor({ opacity: 0,
reactive: true });
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);
Main.layoutManager.overviewGroup.add_child(this._stack);
@ -195,11 +197,7 @@ const Overview = new Lang.Class({
Tweener.addTween(background,
{ brightness: 1.0,
time: SHADE_ANIMATION_TIME,
transition: 'easeOutQuad'
});
Tweener.addTween(background,
{ vignetteSharpness: 0.0,
vignetteSharpness: 0.0,
time: SHADE_ANIMATION_TIME,
transition: 'easeOutQuad'
});
@ -212,12 +210,8 @@ const Overview = new Lang.Class({
let background = backgrounds[i]._delegate;
Tweener.addTween(background,
{ brightness: 0.8,
time: SHADE_ANIMATION_TIME,
transition: 'easeOutQuad'
});
Tweener.addTween(background,
{ vignetteSharpness: 0.7,
{ brightness: Lightbox.VIGNETTE_BRIGHTNESS,
vignetteSharpness: Lightbox.VIGNETTE_SHARPNESS,
time: SHADE_ANIMATION_TIME,
transition: 'easeOutQuad'
});
@ -363,11 +357,13 @@ const Overview = new Lang.Class({
this._lastHoveredWindow = dragEvent.targetActor._delegate.metaWindow;
this._windowSwitchTimeoutId = Mainloop.timeout_add(DND_WINDOW_SWITCH_TIMEOUT,
Lang.bind(this, function() {
this._windowSwitchTimeoutId = 0;
this._needsFakePointerEvent = true;
Main.activateWindow(dragEvent.targetActor._delegate.metaWindow,
this._windowSwitchTimestamp);
this.hide();
this._lastHoveredWindow = null;
return GLib.SOURCE_REMOVE;
}));
}
@ -376,6 +372,7 @@ const Overview = new Lang.Class({
_onScrollEvent: function(actor, event) {
this.emit('scroll-event', event);
return Clutter.EVENT_PROPAGATE;
},
addAction: function(action) {
@ -443,17 +440,17 @@ const Overview = new Lang.Class({
this._inDrag = false;
},
beginWindowDrag: function(source) {
this.emit('window-drag-begin');
beginWindowDrag: function(clone) {
this.emit('window-drag-begin', clone);
this._inDrag = true;
},
cancelledWindowDrag: function(source) {
this.emit('window-drag-cancelled');
cancelledWindowDrag: function(clone) {
this.emit('window-drag-cancelled', clone);
},
endWindowDrag: function(source) {
this.emit('window-drag-end');
endWindowDrag: function(clone) {
this.emit('window-drag-end', clone);
this._inDrag = false;
},
@ -489,8 +486,13 @@ const Overview = new Lang.Class({
},
fadeOutDesktop: function() {
if (!this._desktopFade.child)
this._desktopFade.child = this._getDesktopClone();
if (!this._desktopFade.get_n_children()) {
let clone = this._getDesktopClone();
if (!clone)
return;
this._desktopFade.add_child(clone);
}
this._desktopFade.opacity = 255;
this._desktopFade.show();

View File

@ -36,6 +36,7 @@ const SlideLayout = new Lang.Class({
_init: function(params) {
this._slideX = 1;
this._translationX = 0;
this._direction = SlideDirection.LEFT;
this.parent(params);
@ -66,9 +67,9 @@ const SlideLayout = new Lang.Class({
let alignX = (realDirection == SlideDirection.LEFT) ? (availWidth - natWidth) : 0;
let actorBox = new Clutter.ActorBox();
actorBox.x1 = alignX;
actorBox.x2 = actorBox.x1 + child.x_expand ? availWidth : natWidth;
actorBox.y1 = 0;
actorBox.x1 = box.x1 + alignX + this._translationX;
actorBox.x2 = actorBox.x1 + (child.x_expand ? availWidth : natWidth);
actorBox.y1 = box.y1;
actorBox.y2 = actorBox.y1 + availHeight;
child.allocate(actorBox, flags);
@ -90,7 +91,16 @@ const SlideLayout = new Lang.Class({
get slideDirection() {
return this._direction;
}
},
set translationX(value) {
this._translationX = value;
this.layout_changed();
},
get translationX() {
return this._translationX;
},
});
const SlidingControl = new Lang.Class({
@ -99,8 +109,8 @@ const SlidingControl = new Lang.Class({
_init: function(params) {
params = Params.parse(params, { slideDirection: SlideDirection.LEFT });
this.visible = true;
this.inDrag = false;
this._visible = true;
this._inDrag = false;
this.layout = new SlideLayout();
this.layout.slideDirection = params.slideDirection;
@ -109,6 +119,7 @@ const SlidingControl = new Lang.Class({
clip_to_allocation: true });
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-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));
},
getSlide: function() {
_getSlide: function() {
throw new Error('getSlide() must be overridden');
},
updateSlide: function() {
Tweener.addTween(this.layout, { slideX: this.getSlide(),
_updateSlide: function() {
Tweener.addTween(this.layout, { slideX: this._getSlide(),
time: SIDE_CONTROLS_ANIMATION_TIME,
transition: 'easeOutQuad' });
},
@ -151,28 +162,30 @@ const SlidingControl = new Lang.Class({
let translationEnd = 0;
let translation = this._getTranslation();
if (this.visible) {
if (this._visible) {
translationStart = translation;
} else {
translationEnd = translation;
}
if (this.actor.translation_x == translationEnd)
if (this.layout.translationX == translationEnd)
return;
this.actor.translation_x = translationStart;
Tweener.addTween(this.actor, { translation_x: translationEnd,
time: SIDE_CONTROLS_ANIMATION_TIME,
transition: 'easeOutQuad'
});
this.layout.translationX = translationStart;
Tweener.addTween(this.layout, { translationX: translationEnd,
time: SIDE_CONTROLS_ANIMATION_TIME,
transition: 'easeOutQuad' });
},
_onOverviewShowing: function() {
// reset any translation and make sure the actor is visible when
// entering the overview
this.visible = true;
this.layout.slideX = this.getSlide();
this.actor.translation_x = 0;
this._visible = true;
this.layout.slideX = this._getSlide();
this.layout.translationX = this._getTranslation();
this.slideIn();
},
_onOverviewHiding: function() {
this.slideOut();
},
_onWindowDragBegin: function() {
@ -184,14 +197,14 @@ const SlidingControl = new Lang.Class({
},
_onDragBegin: function() {
this.inDrag = true;
this.actor.translation_x = 0;
this.updateSlide();
this._inDrag = true;
this.layout.translationX = 0;
this._updateSlide();
},
_onDragEnd: function() {
this.inDrag = false;
this.updateSlide();
this._inDrag = false;
this._updateSlide();
},
fadeIn: function() {
@ -209,13 +222,13 @@ const SlidingControl = new Lang.Class({
},
slideIn: function() {
this.visible = true;
this._visible = true;
this._updateTranslation();
// we will update slideX and the translation from pageEmpty
},
slideOut: function() {
this.visible = false;
this._visible = false;
this._updateTranslation();
// 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
// the next page, since slideIn or slideOut might have been called,
// changing the visiblity
this.layout.slideX = this.getSlide();
this.layout.slideX = this._getSlide();
this._updateTranslation();
}
});
@ -244,16 +257,15 @@ const ThumbnailsSlider = new Lang.Class({
this.actor.track_hover = true;
this.actor.add_actor(this._thumbnailsBox.actor);
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));
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._updateSlide));
this.actor.connect('notify::hover', Lang.bind(this, this._updateSlide));
this._thumbnailsBox.actor.bind_property('visible', this.actor, 'visible', GObject.BindingFlags.SYNC_CREATE);
},
_getAlwaysZoomOut: function() {
// Always show the pager when hover, during a drag, or if workspaces are
// actually used, e.g. there are windows on more than one
let alwaysZoomOut = this.actor.hover || this.inDrag || !Meta.prefs_get_dynamic_workspaces() || global.screen.n_workspaces > 2;
let alwaysZoomOut = this.actor.hover || this._inDrag || !Meta.prefs_get_dynamic_workspaces() || global.screen.n_workspaces > 2;
if (!alwaysZoomOut) {
let monitors = Main.layoutManager.monitors;
@ -273,20 +285,13 @@ const ThumbnailsSlider = new Lang.Class({
return alwaysZoomOut;
},
_onOverviewShowing: function() {
this.visible = true;
this.layout.slideX = this.getSlide();
this.actor.translation_x = this._getTranslation();
this.slideIn();
},
getNonExpandedWidth: function() {
let child = this.actor.get_first_child();
return child.get_theme_node().get_length('visible-width');
},
getSlide: function() {
if (!this.visible)
_getSlide: function() {
if (!this._visible)
return 0;
let alwaysZoomOut = this._getAlwaysZoomOut();
@ -323,29 +328,22 @@ const DashSlider = new Lang.Class({
// available allocation
this._dash.actor.x_expand = true;
this.actor.x_expand = true;
this.actor.x_align = Clutter.ActorAlign.START;
this.actor.y_expand = true;
this.actor.add_actor(this._dash.actor);
this._dash.connect('icon-size-changed', Lang.bind(this, this.updateSlide));
Main.overview.connect('hiding', Lang.bind(this, this.slideOut));
this._dash.connect('icon-size-changed', Lang.bind(this, this._updateSlide));
},
getSlide: function() {
if (this.visible || this.inDrag)
_getSlide: function() {
if (this._visible || this._inDrag)
return 1;
else
return 0;
},
_onOverviewShowing: function() {
this.visible = true;
this.layout.slideX = this.getSlide();
this.actor.translation_x = this._getTranslation();
this.slideIn();
},
_onWindowDragBegin: function() {
this.fadeHalf();
},
@ -462,9 +460,6 @@ const MessagesIndicator = new Lang.Class({
if (source.trayIcon)
return;
if (source.isTransient)
return;
source.connect('count-updated', Lang.bind(this, this._updateCount));
this._sources.push(source);
this._updateCount();

View File

@ -213,7 +213,7 @@ const AppMenuButton = new Lang.Class({
this._label = new TextShadower();
this._label.actor.y_align = Clutter.ActorAlign.CENTER;
this._hbox.add_actor(this._label.actor);
this._arrow = PopupMenu.unicodeArrow(St.Side.BOTTOM);
this._arrow = PopupMenu.arrowIcon(St.Side.BOTTOM);
this._hbox.add_actor(this._arrow);
this._iconBottomClip = 0;
@ -602,14 +602,15 @@ const ActivitiesButton = new Lang.Class({
_onCapturedEvent: function(actor, event) {
if (event.type() == Clutter.EventType.BUTTON_PRESS) {
if (!Main.overview.shouldToggleByCornerOrButton())
return true;
return Clutter.EVENT_STOP;
}
return false;
return Clutter.EVENT_PROPAGATE;
},
_onButtonRelease: function() {
Main.overview.toggle();
this.menu.close();
return Clutter.EVENT_PROPAGATE;
},
_onKeyRelease: function(actor, event) {
@ -617,6 +618,7 @@ const ActivitiesButton = new Lang.Class({
if (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_space) {
Main.overview.toggle();
}
return Clutter.EVENT_PROPAGATE;
},
_xdndToggleOverview: function(actor) {
@ -628,6 +630,7 @@ const ActivitiesButton = new Lang.Class({
Mainloop.source_remove(this._xdndTimeOut);
this._xdndTimeOut = 0;
return GLib.SOURCE_REMOVE;
}
});
@ -809,7 +812,11 @@ const AggregateMenu = new Lang.Class({
this._indicators = new St.BoxLayout({ style_class: 'panel-status-indicators-box' });
this.actor.add_child(this._indicators);
this._network = new imports.ui.status.network.NMApplet();
if (Config.HAVE_NETWORKMANAGER) {
this._network = new imports.ui.status.network.NMApplet();
} else {
this._network = null;
}
if (Config.HAVE_BLUETOOTH) {
this._bluetooth = new imports.ui.status.bluetooth.Indicator();
} else {
@ -822,24 +829,31 @@ const AggregateMenu = new Lang.Class({
this._brightness = new imports.ui.status.brightness.Indicator();
this._system = new imports.ui.status.system.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._network.indicators);
this._indicators.add_child(this._location.indicators);
if (this._network) {
this._indicators.add_child(this._network.indicators);
}
if (this._bluetooth) {
this._indicators.add_child(this._bluetooth.indicators);
}
this._indicators.add_child(this._rfkill.indicators);
this._indicators.add_child(this._volume.indicators);
this._indicators.add_child(this._power.indicators);
this._indicators.add_child(PopupMenu.unicodeArrow(St.Side.BOTTOM));
this._indicators.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM));
this.menu.addMenuItem(this._volume.menu);
this.menu.addMenuItem(this._brightness.menu);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addMenuItem(this._network.menu);
if (this._network) {
this.menu.addMenuItem(this._network.menu);
}
if (this._bluetooth) {
this.menu.addMenuItem(this._bluetooth.menu);
}
this.menu.addMenuItem(this._location.menu);
this.menu.addMenuItem(this._rfkill.menu);
this.menu.addMenuItem(this._power.menu);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
@ -983,23 +997,23 @@ const Panel = new Lang.Class({
_onButtonPress: function(actor, event) {
if (Main.modalCount > 0)
return false;
return Clutter.EVENT_PROPAGATE;
if (event.get_source() != actor)
return false;
return Clutter.EVENT_PROPAGATE;
let button = event.get_button();
if (button != 1)
return false;
return Clutter.EVENT_PROPAGATE;
let focusWindow = global.display.focus_window;
if (!focusWindow)
return false;
return Clutter.EVENT_PROPAGATE;
let dragWindow = focusWindow.is_attached_dialog() ? focusWindow.get_transient_for()
: focusWindow;
if (!dragWindow)
return false;
return Clutter.EVENT_PROPAGATE;
let rect = dragWindow.get_outer_rect();
let [stageX, stageY] = event.get_coords();
@ -1008,7 +1022,7 @@ const Panel = new Lang.Class({
stageX > rect.x && stageX < rect.x + rect.width;
if (!allowDrag)
return false;
return Clutter.EVENT_PROPAGATE;
global.display.begin_grab_op(global.screen,
dragWindow,
@ -1020,7 +1034,7 @@ const Panel = new Lang.Class({
event.get_time(),
stageX, stageY);
return true;
return Clutter.EVENT_STOP;
},
toggleAppMenu: function() {

View File

@ -137,29 +137,30 @@ const Button = new Lang.Class({
_onButtonPress: function(actor, event) {
if (!this.menu)
return;
return Clutter.EVENT_PROPAGATE;
this.menu.toggle();
return Clutter.EVENT_PROPAGATE;
},
_onSourceKeyPress: function(actor, event) {
if (!this.menu)
return false;
return Clutter.EVENT_PROPAGATE;
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
this.menu.toggle();
return true;
return Clutter.EVENT_STOP;
} else if (symbol == Clutter.KEY_Escape && this.menu.isOpen) {
this.menu.close();
return true;
return Clutter.EVENT_STOP;
} else if (symbol == Clutter.KEY_Down) {
if (!this.menu.isOpen)
this.menu.toggle();
this.menu.actor.navigate_focus(this.actor, Gtk.DirectionType.DOWN, false);
return true;
return Clutter.EVENT_STOP;
} else
return false;
return Clutter.EVENT_PROPAGATE;
},
_onVisibilityChanged: function() {
@ -172,7 +173,7 @@ const Button = new Lang.Class({
_onMenuKeyPress: function(actor, event) {
if (global.focus_manager.navigate_from_event(event))
return true;
return Clutter.EVENT_STOP;
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
@ -180,10 +181,10 @@ const Button = new Lang.Class({
if (group) {
let direction = (symbol == Clutter.KEY_Left) ? Gtk.DirectionType.LEFT : Gtk.DirectionType.RIGHT;
group.navigate_focus(this.actor, direction, false);
return true;
return Clutter.EVENT_STOP;
}
}
return false;
return Clutter.EVENT_PROPAGATE;
},
_onOpenStateChanged: function(menu, open) {

View File

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

View File

@ -45,28 +45,35 @@ function isPopupMenuItemVisible(child) {
/**
* @side Side to which the arrow points.
*/
function unicodeArrow(side) {
let arrowChar;
function arrowIcon(side) {
let rotation;
switch (side) {
case St.Side.TOP:
arrowChar = '\u25B4';
rotation = 180;
break;
case St.Side.RIGHT:
arrowChar = '\u25B8';
rotation = - 90;
break;
case St.Side.BOTTOM:
arrowChar = '\u25BE';
rotation = 0;
break;
case St.Side.LEFT:
arrowChar = '\u25C2';
rotation = 90;
break;
}
return new St.Label({ text: arrowChar,
style_class: 'unicode-arrow',
accessible_role: Atk.Role.ARROW,
y_expand: true,
y_align: Clutter.ActorAlign.CENTER });
let gicon = new Gio.FileIcon({ file: Gio.File.new_for_path(global.datadir +
'/theme/menu-arrow-symbolic.svg') });
let arrow = new St.Icon({ style_class: 'popup-menu-arrow',
gicon: gicon,
accessible_role: Atk.Role.ARROW,
y_expand: true,
y_align: Clutter.ActorAlign.CENTER });
arrow.rotation_angle_z = rotation;
return arrow;
}
const PopupBaseMenuItem = new Lang.Class({
@ -111,6 +118,7 @@ const PopupBaseMenuItem = new Lang.Class({
this.actor.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn));
this.actor.connect('key-focus-out', Lang.bind(this, this._onKeyFocusOut));
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
},
_getTopMenu: function() {
@ -126,7 +134,7 @@ const PopupBaseMenuItem = new Lang.Class({
_onButtonReleaseEvent: function (actor, event) {
this.activate(event);
return true;
return Clutter.EVENT_STOP;
},
_onKeyPressEvent: function (actor, event) {
@ -134,9 +142,9 @@ const PopupBaseMenuItem = new Lang.Class({
if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
this.activate(event);
return true;
return Clutter.EVENT_STOP;
}
return false;
return Clutter.EVENT_PROPAGATE;
},
_onKeyFocusIn: function (actor) {
@ -192,6 +200,9 @@ const PopupBaseMenuItem = new Lang.Class({
destroy: function() {
this.actor.destroy();
},
_onDestroy: function() {
this.emit('destroy');
},
@ -868,14 +879,14 @@ const PopupSubMenu = new Lang.Class({
if (animate) {
let [minHeight, naturalHeight] = this.actor.get_preferred_height(-1);
this.actor.height = 0;
this.actor._arrow_rotation = this._arrow.rotation_angle_z;
this.actor._arrowRotation = this._arrow.rotation_angle_z;
Tweener.addTween(this.actor,
{ _arrow_rotation: 90,
{ _arrowRotation: this.actor._arrowRotation + 90,
height: naturalHeight,
time: 0.25,
onUpdateScope: this,
onUpdate: function() {
this._arrow.rotation_angle_z = this.actor._arrow_rotation;
this._arrow.rotation_angle_z = this.actor._arrowRotation;
},
onCompleteScope: this,
onComplete: function() {
@ -883,7 +894,7 @@ const PopupSubMenu = new Lang.Class({
}
});
} else {
this._arrow.rotation_angle_z = 90;
this._arrow.rotation_angle_z = this.actor._arrowRotation + 90;
}
},
@ -901,14 +912,14 @@ const PopupSubMenu = new Lang.Class({
animate = false;
if (animate) {
this.actor._arrow_rotation = this._arrow.rotation_angle_z;
this.actor._arrowRotation = this._arrow.rotation_angle_z;
Tweener.addTween(this.actor,
{ _arrow_rotation: 0,
{ _arrowRotation: this.actor._arrowRotation - 90,
height: 0,
time: 0.25,
onUpdateScope: this,
onUpdate: function() {
this._arrow.rotation_angle_z = this.actor._arrow_rotation;
this._arrow.rotation_angle_z = this.actor._arrowRotation;
},
onCompleteScope: this,
onComplete: function() {
@ -917,7 +928,7 @@ const PopupSubMenu = new Lang.Class({
},
});
} else {
this._arrow.rotation_angle_z = 0;
this._arrow.rotation_angle_z = this.actor._arrowRotation - 90;
this.actor.hide();
}
},
@ -928,10 +939,10 @@ const PopupSubMenu = new Lang.Class({
if (this.isOpen && event.get_key_symbol() == Clutter.KEY_Left) {
this.close(BoxPointer.PopupAnimation.FULL);
this.sourceActor._delegate.setActive(true);
return true;
return Clutter.EVENT_STOP;
}
return false;
return Clutter.EVENT_PROPAGATE;
}
});
@ -989,7 +1000,7 @@ const PopupSubMenuMenuItem = new Lang.Class({
y_align: Clutter.ActorAlign.CENTER });
this.actor.add_child(this.status);
this._triangle = unicodeArrow(St.Side.RIGHT);
this._triangle = arrowIcon(St.Side.RIGHT);
this._triangle.pivot_point = new Clutter.Point({ x: 0.5, y: 0.6 });
this._triangleBin = new St.Widget({ y_expand: true,
@ -1056,10 +1067,10 @@ const PopupSubMenuMenuItem = new Lang.Class({
if (symbol == Clutter.KEY_Right) {
this._setOpenState(true);
this.menu.actor.navigate_focus(null, Gtk.DirectionType.DOWN, false);
return true;
return Clutter.EVENT_STOP;
} else if (symbol == Clutter.KEY_Left && this._getOpenState()) {
this._setOpenState(false);
return true;
return Clutter.EVENT_STOP;
}
return this.parent(actor, event);
@ -1071,6 +1082,7 @@ const PopupSubMenuMenuItem = new Lang.Class({
_onButtonReleaseEvent: function(actor) {
this._setOpenState(!this._getOpenState());
return Clutter.EVENT_PROPAGATE;
}
});
@ -1102,7 +1114,7 @@ const PopupMenuManager = new Lang.Class({
if (source) {
if (!menu.blockSourceEvents)
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); }));
}
@ -1114,7 +1126,7 @@ const PopupMenuManager = new Lang.Class({
removeMenu: function(menu) {
if (menu == this.activeMenu)
this._closeMenu(menu);
this._closeMenu(false, menu);
let position = this._findMenu(menu);
if (position == -1) // not a menu we manage
@ -1164,13 +1176,13 @@ const PopupMenuManager = new Lang.Class({
_onMenuSourceEnter: function(menu) {
if (!this._grabHelper.grabbed)
return false;
return Clutter.EVENT_PROPAGATE;
if (this._grabHelper.isActorGrabbed(menu.actor))
return false;
return Clutter.EVENT_PROPAGATE;
this._changeMenu(menu);
return false;
return Clutter.EVENT_PROPAGATE;
},
_onMenuDestroy: function(menu) {

View File

@ -7,6 +7,7 @@ const Lang = imports.lang;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const FileUtils = imports.misc.fileUtils;
const Search = imports.ui.search;
const KEY_FILE_GROUP = 'Shell Search Provider';
@ -62,7 +63,7 @@ const SearchProvider2Iface = '<node> \
var SearchProviderProxyInfo = Gio.DBusInterfaceInfo.new_for_xml(SearchProviderIface);
var SearchProvider2ProxyInfo = Gio.DBusInterfaceInfo.new_for_xml(SearchProvider2Iface);
function loadRemoteSearchProviders(addProviderCallback) {
function loadRemoteSearchProviders(callback) {
let objectPaths = {};
let loadedProviders = [];
@ -116,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 });
if (searchSettings.get_boolean('disable-external')) {
callback([]);
return;
}
FileUtils.collectFromDatadirs('search-providers', false, loadRemoteSearchProvider);
let sortOrder = searchSettings.get_strv('sort-order');
// Special case gnome-control-center to be always active and always first
sortOrder.unshift('gnome-control-center.desktop');
loadedProviders = 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) {
let idxA, idxB;
let appIdA, appIdB;
@ -170,7 +166,7 @@ function loadRemoteSearchProviders(addProviderCallback) {
return (idxA - idxB);
});
loadedProviders.forEach(addProviderCallback);
callback(loadedProviders);
}
const RemoteSearchProvider = new Lang.Class({
@ -192,12 +188,12 @@ const RemoteSearchProvider = new Lang.Class({
this.appInfo = appInfo;
this.id = appInfo.get_id();
this.isRemoteProvider = true;
this._cancellable = new Gio.Cancellable();
},
createIcon: function(size, meta) {
let gicon;
let gicon = null;
let icon = null;
if (meta['icon']) {
gicon = Gio.icon_deserialize(meta['icon']);
} else if (meta['gicon']) {
@ -209,8 +205,10 @@ const RemoteSearchProvider = new Lang.Class({
bitsPerSample, width, height, rowStride);
}
return new St.Icon({ gicon: gicon,
icon_size: size });
if (gicon)
icon = new St.Icon({ gicon: gicon,
icon_size: size });
return icon;
},
filterResults: function(results, maxNumber) {
@ -223,29 +221,27 @@ const RemoteSearchProvider = new Lang.Class({
return regularResults.slice(0, maxNumber).concat(specialResults.slice(0, maxNumber));
},
_getResultsFinished: function(results, error) {
_getResultsFinished: function(results, error, callback) {
if (error) {
if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
log('Received error from DBus search provider %s: %s'.format(this.id, String(error)));
} else {
this.searchSystem.setResults(this, results[0]);
callback([]);
return;
}
callback(results[0]);
},
getInitialResultSet: function(terms) {
this._cancellable.cancel();
this._cancellable.reset();
getInitialResultSet: function(terms, callback, cancellable) {
this.proxy.GetInitialResultSetRemote(terms,
Lang.bind(this, this._getResultsFinished),
this._cancellable);
Lang.bind(this, this._getResultsFinished, callback),
cancellable);
},
getSubsearchResultSet: function(previousResults, newTerms) {
this._cancellable.cancel();
this._cancellable.reset();
getSubsearchResultSet: function(previousResults, newTerms, callback, cancellable) {
this.proxy.GetSubsearchResultSetRemote(previousResults, newTerms,
Lang.bind(this, this._getResultsFinished),
this._cancellable);
Lang.bind(this, this._getResultsFinished, callback),
cancellable);
},
_getResultMetasFinished: function(results, error, callback) {
@ -273,12 +269,10 @@ const RemoteSearchProvider = new Lang.Class({
callback(resultMetas);
},
getResultMetas: function(ids, callback) {
this._cancellable.cancel();
this._cancellable.reset();
getResultMetas: function(ids, callback, cancellable) {
this.proxy.GetResultMetasRemote(ids,
Lang.bind(this, this._getResultMetasFinished, callback),
this._cancellable);
cancellable);
},
activateResult: function(id) {
@ -289,7 +283,7 @@ const RemoteSearchProvider = new Lang.Class({
// 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
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));
}
});

View File

@ -73,7 +73,9 @@ const RunDialog = new Lang.Class({
let label = new St.Label({ style_class: 'run-dialog-label',
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',
can_focus: true });
@ -101,6 +103,8 @@ const RunDialog = new Lang.Class({
this._errorMessage.clutter_text.line_wrap = true;
this._errorBox.add(this._errorMessage, { expand: true,
x_align: St.Align.START,
x_fill: false,
y_align: St.Align.MIDDLE,
y_fill: false });
@ -124,7 +128,7 @@ const RunDialog = new Lang.Class({
!this.pushModal())
this.close();
return true;
return Clutter.EVENT_STOP;
}
if (symbol == Clutter.Tab) {
let text = o.get_text();
@ -138,9 +142,9 @@ const RunDialog = new Lang.Class({
o.insert_text(postfix, -1);
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);
try {
Gio.app_info_launch_default_for_uri(file.get_uri(),
global.create_app_launch_context());
global.create_app_launch_context(0, -1));
} catch (e) {
// The exception from gjs contains an error string like:
// 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 GnomeSession = imports.misc.gnomeSession;
const Hash = imports.misc.hash;
const Layout = imports.ui.layout;
const OVirt = imports.gdm.oVirt;
const LoginManager = imports.misc.loginManager;
@ -115,7 +114,7 @@ const NotificationsBox = new Lang.Class({
this.actor.add(this._musicBin);
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) {
this._sourceAdded(Main.messageTray, source, true);
}));
@ -130,9 +129,8 @@ const NotificationsBox = new Lang.Class({
this._sourceAddedId = 0;
}
let items = this._sources.items();
for (let i = 0; i < items.length; i++) {
let [source, obj] = items[i];
let items = this._sources.entries();
for (let [source, obj] of items) {
this._removeSource(source, obj);
}
@ -198,8 +196,8 @@ const NotificationsBox = new Lang.Class({
let body = '';
if (n.bannerBodyText) {
body = n.bannerBodyMarkup ? n.bannerBodyText :
GLib.markup_escape_text(n.bannerBodyMarkup, -1);
body = n.bannerBodyMarkup ? n.bannerBodyText
: GLib.markup_escape_text(n.bannerBodyText, -1);
}
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) {
// Ignore transient sources
if (source.isTransient)
return;
let obj = {
visible: source.policy.showInLockScreen,
detailed: source.policy.detailsInLockScreen,
@ -307,7 +301,7 @@ const NotificationsBox = new Lang.Class({
});
this._updateVisibility();
Shell.util_wake_up_screen();
this.emit('wake-up-screen');
}
},
@ -333,7 +327,7 @@ const NotificationsBox = new Lang.Class({
this._updateVisibility();
if (obj.sourceBox.visible)
Shell.util_wake_up_screen();
this.emit('wake-up-screen');
},
_visibleChanged: function(source, obj) {
@ -348,7 +342,7 @@ const NotificationsBox = new Lang.Class({
this._updateVisibility();
if (obj.sourceBox.visible)
Shell.util_wake_up_screen();
this.emit('wake-up-screen');
},
_detailedChanged: function(source, obj) {
@ -386,6 +380,7 @@ const NotificationsBox = new Lang.Class({
this._sources.delete(source);
},
});
Signals.addSignalMethods(NotificationsBox.prototype);
const Arrow = new Lang.Class({
Name: 'Arrow',
@ -419,6 +414,7 @@ const Arrow = new Lang.Class({
cr.lineTo(w/2, thickness);
cr.lineTo(w - thickness / 2, h - thickness / 2);
cr.stroke();
cr.$dispose();
},
vfunc_style_changed: function() {
@ -541,7 +537,7 @@ const ScreenShield = new Lang.Class({
this._smartcardManager = SmartcardManager.getSmartcardManager();
this._smartcardManager.connect('smartcard-inserted',
Lang.bind(this, function(token) {
Lang.bind(this, function(manager, token) {
if (this._isLocked && token.UsedToLogin)
this._liftShield(true, 0);
}));
@ -673,11 +669,11 @@ const ScreenShield = new Lang.Class({
// down after cancel.
if (this._lockScreenState != MessageTray.State.SHOWN)
return false;
return Clutter.EVENT_PROPAGATE;
let isEnter = (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_KP_Enter);
if (!isEnter && !(GLib.unichar_isprint(unichar) || symbol == Clutter.KEY_Escape))
return false;
return Clutter.EVENT_PROPAGATE;
if (this._isLocked &&
this._ensureUnlockDialog(true, true) &&
@ -685,12 +681,12 @@ const ScreenShield = new Lang.Class({
this._dialog.addCharacter(unichar);
this._liftShield(true, 0);
return true;
return Clutter.EVENT_STOP;
},
_onLockScreenScroll: function(actor, event) {
if (this._lockScreenState != MessageTray.State.SHOWN)
return false;
return Clutter.EVENT_PROPAGATE;
let delta = 0;
if (event.get_scroll_direction() == Clutter.ScrollDirection.UP)
@ -705,7 +701,7 @@ const ScreenShield = new Lang.Class({
this._liftShield(true, 0);
}
return true;
return Clutter.EVENT_STOP;
},
_inhibitSuspend: function() {
@ -733,7 +729,7 @@ const ScreenShield = new Lang.Class({
} else {
this._inhibitSuspend();
this._onUserBecameActive();
this._wakeUpScreen();
}
},
@ -756,7 +752,7 @@ const ScreenShield = new Lang.Class({
});
}
return true;
return GLib.SOURCE_CONTINUE;
},
_onDragBegin: function() {
@ -852,7 +848,7 @@ const ScreenShield = new Lang.Class({
Lang.bind(this, function() {
this._lockTimeoutId = 0;
this.lock(false);
return false;
return GLib.SOURCE_REMOVE;
}));
}
@ -1101,7 +1097,7 @@ const ScreenShield = new Lang.Class({
global.stage.disconnect(motionId);
}
return false;
return Clutter.EVENT_PROPAGATE;
}));
this._cursorTracker.set_pointer_visible(false);
@ -1114,6 +1110,7 @@ const ScreenShield = new Lang.Class({
Mainloop.timeout_add(1000 * MANUAL_FADE_TIME, Lang.bind(this, function() {
this._activateFade(this._shortLightbox, MANUAL_FADE_TIME);
return GLib.SOURCE_REMOVE;
}));
} else {
if (params.fadeToBlack)
@ -1155,6 +1152,7 @@ const ScreenShield = new Lang.Class({
this._lockScreenContents.add_actor(this._lockScreenContentsBox);
this._notificationsBox = new NotificationsBox();
this._wakeUpScreenId = this._notificationsBox.connect('wake-up-screen', Lang.bind(this, this._wakeUpScreen));
this._lockScreenContentsBox.add(this._notificationsBox.actor, { x_fill: true,
y_fill: true,
expand: true });
@ -1162,11 +1160,17 @@ const ScreenShield = new Lang.Class({
this._hasLockScreen = true;
},
_wakeUpScreen: function() {
this._onUserBecameActive();
this.emit('wake-up-screen');
},
_clearLockScreen: function() {
this._clock.destroy();
this._clock = null;
if (this._notificationsBox) {
this._notificationsBox.disconnect(this._wakeUpScreenId);
this._notificationsBox.destroy();
this._notificationsBox = null;
}

View File

@ -6,7 +6,6 @@ const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const Hash = imports.misc.hash;
const Main = imports.ui.main;
const ScreencastIface = '<node> \
@ -42,13 +41,13 @@ const ScreencastService = new Lang.Class({
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));
},
get isRecording() {
return this._recorders.size() > 0;
return this._recorders.size > 0;
},
_ensureRecorderForSender: function(sender) {
@ -69,8 +68,7 @@ const ScreencastService = new Lang.Class({
if (Main.sessionMode.allowScreencast)
return;
for (let sender in this._recorders.keys())
this._recorders.delete(sender);
this._recorders.clear();
this.emit('updated');
},
@ -105,8 +103,10 @@ const ScreencastService = new Lang.Class({
ScreencastAsync: function(params, invocation) {
let returnValue = [false, ''];
if (!Main.sessionMode.allowScreencast)
if (!Main.sessionMode.allowScreencast) {
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
return;
}
let sender = invocation.get_sender();
let recorder = this._ensureRecorderForSender(sender);
@ -124,8 +124,10 @@ const ScreencastService = new Lang.Class({
ScreencastAreaAsync: function(params, invocation) {
let returnValue = [false, ''];
if (!Main.sessionMode.allowScreencast)
if (!Main.sessionMode.allowScreencast) {
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
return;
}
let sender = invocation.get_sender();
let recorder = this._ensureRecorderForSender(sender);
@ -133,6 +135,16 @@ const ScreencastService = new Lang.Class({
if (!recorder.is_recording()) {
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_area(x, y, width, height);
this._applyOptionalParameters(recorder, options);

View File

@ -79,7 +79,9 @@ const ScreenshotService = new Lang.Class({
ScreenshotAreaAsync : function (params, invocation) {
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,
"Invalid params");
return;
@ -204,12 +206,12 @@ const SelectArea = new Lang.Class({
if (event.get_key_symbol() == Clutter.Escape)
this._destroy(null, false);
return;
return Clutter.EVENT_PROPAGATE;
},
_onMotionEvent: function(actor, event) {
if (this._startX == -1 || this._startY == -1)
return false;
return Clutter.EVENT_PROPAGATE;
[this._lastX, this._lastY] = event.get_coords();
let geometry = this._getGeometry();
@ -217,19 +219,19 @@ const SelectArea = new Lang.Class({
this._rubberband.set_position(geometry.x, geometry.y);
this._rubberband.set_size(geometry.width, geometry.height);
return false;
return Clutter.EVENT_PROPAGATE;
},
_onButtonPress: function(actor, event) {
[this._startX, this._startY] = event.get_coords();
this._rubberband.set_position(this._startX, this._startY);
return false;
return Clutter.EVENT_PROPAGATE;
},
_onButtonRelease: function(actor, event) {
this._destroy(this._getGeometry(), true);
return false;
return Clutter.EVENT_PROPAGATE;
},
_destroy: function(geometry, fade) {

View File

@ -1,6 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
@ -41,7 +42,7 @@ function sleep(milliseconds) {
Mainloop.timeout_add(milliseconds, function() {
if (cb)
cb();
return false;
return GLib.SOURCE_REMOVE;
});
return function(callback) {

View File

@ -2,18 +2,20 @@
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Gio = imports.gi.Gio;
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 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 Search = imports.ui.search;
const Util = imports.misc.util;
const SEARCH_PROVIDERS_SCHEMA = 'org.gnome.desktop.search-providers';
@ -26,88 +28,95 @@ const SearchSystem = new Lang.Class({
_init: function() {
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) {
provider.searchSystem = this;
addProvider: function(provider) {
this._providers.push(provider);
if (provider.isRemoteProvider)
this._remoteProviders.push(provider);
this.emit('providers-changed');
},
unregisterProvider: function (provider) {
let index = this._providers.indexOf(provider);
if (index == -1)
return;
provider.searchSystem = null;
this._providers.splice(index, 1);
_reloadRemoteProviders: function() {
let remoteProviders = this._providers.filter(function(provider) {
return provider.isRemoteProvider;
});
remoteProviders.forEach(Lang.bind(this, function(provider) {
this._unregisterProvider(provider);
}));
let remoteIndex = this._remoteProviders.indexOf(provider);
if (remoteIndex != -1)
this._remoteProviders.splice(remoteIndex, 1);
RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, function(providers) {
providers.forEach(Lang.bind(this, this._registerProvider));
}));
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() {
return this._providers;
},
getRemoteProviders: function() {
return this._remoteProviders;
},
getTerms: function() {
return this._previousTerms;
return this._terms;
},
reset: function() {
this._previousTerms = [];
this._previousResults = [];
this._terms = [];
this._results = {};
},
setResults: function(provider, results) {
let i = this._providers.indexOf(provider);
if (i == -1)
return;
this._previousResults[i] = [provider, results];
this.emit('search-updated', this._previousResults[i]);
_gotResults: function(results, provider) {
this._results[provider.id] = results;
this.emit('search-updated', provider, results);
},
updateSearchResults: function(terms) {
setTerms: function(terms) {
this._cancellable.cancel();
this._cancellable.reset();
let previousResults = this._results;
let previousTerms = this._terms;
this.reset();
if (!terms)
return;
let searchString = terms.join(' ');
let previousSearchString = this._previousTerms.join(' ');
let previousSearchString = previousTerms.join(' ');
if (searchString == previousSearchString)
return;
let isSubSearch = false;
if (this._previousTerms.length > 0)
if (previousTerms.length > 0)
isSubSearch = searchString.indexOf(previousSearchString) == 0;
let previousResultsArr = this._previousResults;
this._terms = terms;
let results = [];
this._previousTerms = terms;
this._previousResults = results;
if (isSubSearch) {
for (let i = 0; i < this._providers.length; i++) {
let [provider, previousResults] = previousResultsArr[i];
results.push([provider, []]);
provider.getSubsearchResultSet(previousResults, terms);
}
} else {
for (let i = 0; i < this._providers.length; i++) {
let provider = this._providers[i];
results.push([provider, []]);
provider.getInitialResultSet(terms);
}
}
this._providers.forEach(Lang.bind(this, function(provider) {
let previousProviderResults = previousResults[provider.id];
if (isSubSearch && previousProviderResults)
provider.getSubsearchResultSet(previousProviderResults, terms, Lang.bind(this, this._gotResults, provider), this._cancellable);
else
provider.getInitialResultSet(terms, Lang.bind(this, this._gotResults, provider), this._cancellable);
}));
}
});
Signals.addSignalMethods(SearchSystem.prototype);
@ -135,10 +144,9 @@ const MaxWidthBin = new Lang.Class({
const SearchResult = new Lang.Class({
Name: 'SearchResult',
_init: function(provider, metaInfo, terms) {
_init: function(provider, metaInfo) {
this.provider = provider;
this.metaInfo = metaInfo;
this.terms = terms;
this.actor = new St.Button({ reactive: true,
can_focus: true,
@ -151,8 +159,7 @@ const SearchResult = new Lang.Class({
},
activate: function() {
this.provider.activateResult(this.metaInfo.id, this.terms);
Main.overview.toggle();
this.emit('activate', this.metaInfo.id);
},
setSelected: function(selected) {
@ -162,6 +169,7 @@ const SearchResult = new Lang.Class({
this.actor.remove_style_pseudo_class('selected');
}
});
Signals.addSignalMethods(SearchResult.prototype);
const ListSearchResult = new Lang.Class({
Name: 'ListSearchResult',
@ -169,8 +177,8 @@ const ListSearchResult = new Lang.Class({
ICON_SIZE: 64,
_init: function(provider, metaInfo, terms) {
this.parent(provider, metaInfo, terms);
_init: function(provider, metaInfo) {
this.parent(provider, metaInfo);
this.actor.style_class = 'list-search-result';
this.actor.x_fill = true;
@ -214,12 +222,12 @@ const GridSearchResult = new Lang.Class({
Name: 'GridSearchResult',
Extends: SearchResult,
_init: function(provider, metaInfo, terms) {
this.parent(provider, metaInfo, terms);
_init: function(provider, metaInfo) {
this.parent(provider, metaInfo);
this.actor.style_class = 'grid-search-result';
let content = provider.createResultObject(metaInfo, terms);
let content = provider.createResultObject(metaInfo);
let dragSource = null;
if (content == null) {
@ -292,6 +300,10 @@ const SearchResultsBase = new Lang.Class({
let separator = new Separator.HorizontalSeparator({ style_class: 'search-section-separator' });
this.actor.add(separator.actor);
this._resultDisplays = {};
this._cancellable = new Gio.Cancellable();
},
destroy: function() {
@ -303,6 +315,9 @@ const SearchResultsBase = new Lang.Class({
},
clear: function() {
for (let resultId in this._resultDisplays)
this._resultDisplays[resultId].actor.destroy();
this._resultDisplays = {};
this._clearResultDisplay();
this.actor.hide();
},
@ -311,9 +326,48 @@ const SearchResultsBase = new Lang.Class({
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(true);
} else {
this._cancellable.cancel();
this._cancellable.reset();
this.provider.getResultMetas(metasNeeded, Lang.bind(this, function(metas) {
if (metas.length == 0) {
callback(false);
return;
}
if (metas.length != metasNeeded.length) {
log('Wrong number of result metas returned by search provider');
callback(false);
return;
}
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(true);
}), this._cancellable);
}
},
updateSearch: function(providerResults, terms, callback) {
this._terms = terms;
@ -326,15 +380,19 @@ const SearchResultsBase = new Lang.Class({
let results = this.provider.filterResults(providerResults, maxResults);
let hasMoreResults = results.length < providerResults.length;
this.provider.getResultMetas(results, Lang.bind(this, function(metas) {
this.clear();
this._ensureResultActors(results, Lang.bind(this, function(successful) {
this._clearResultDisplay();
if (!successful)
return;
// 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);
results.forEach(Lang.bind(this, function(resultId) {
this._addItem(this._resultDisplays[resultId]);
}));
this._setMoreIconVisible(hasMoreResults && this.provider.canLaunchSearch);
this.actor.show();
callback();
@ -379,16 +437,16 @@ const ListSearchResults = new Lang.Class({
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.remove_all_children();
},
_clearResultDisplay: function () {
this._content.destroy_all_children();
_createResultDisplay: function(meta) {
return new ListSearchResult(this.provider, meta);
},
_addItem: function(display) {
this._content.add_actor(display.actor);
},
getFirstResult: function() {
@ -421,7 +479,8 @@ const GridSearchResults = new Lang.Class({
_renderResults: function(metas) {
for (let i = 0; i < metas.length; i++) {
let display = new GridSearchResult(this.provider, metas[i], this._terms);
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);
}
@ -431,6 +490,14 @@ const GridSearchResults = new Lang.Class({
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;
@ -443,10 +510,7 @@ 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));
_init: function() {
this.actor = new St.BoxLayout({ name: 'searchResults',
vertical: true });
@ -481,14 +545,14 @@ const SearchResults = new Lang.Class({
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;
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) {
@ -502,31 +566,29 @@ const SearchResults = new Lang.Class({
Util.ensureActorVisibleInScrollView(this._scrollView, actor);
},
createProviderDisplay: function(provider) {
let providerDisplay = null;
_ensureProviderDisplay: function(provider) {
if (provider.display)
return;
if (provider.appInfo) {
let providerDisplay;
if (provider.appInfo)
providerDisplay = new ListSearchResults(provider);
} else {
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);
provider.display = providerDisplay;
},
destroyProviderDisplay: function(provider) {
this._providerDisplays[provider.id].destroy();
delete this._providerDisplays[provider.id];
_updateProviderDisplays: function() {
this._searchSystem.getProviders().forEach(Lang.bind(this, this._ensureProviderDisplay));
},
_clearDisplay: function() {
for (let i = 0; i < this._providers.length; i++) {
let provider = this._providers[i];
let providerDisplay = this._providerDisplays[provider.id];
providerDisplay.clear();
}
this._searchSystem.getProviders().forEach(function(provider) {
provider.display.clear();
});
},
reset: function() {
@ -542,12 +604,17 @@ const SearchResults = new Lang.Class({
this._statusBin.show();
},
setTerms: function(terms) {
this._searchSystem.setTerms(terms);
},
_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];
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;
@ -562,24 +629,21 @@ const SearchResults = new Lang.Class({
if (newDefaultResult != this._defaultResult) {
if (this._defaultResult)
this._defaultResult.setSelected(false);
if (newDefaultResult)
if (newDefaultResult) {
newDefaultResult.setSelected(this._highlightDefault);
if (this._highlightDefault)
Util.ensureActorVisibleInScrollView(this._scrollView, newDefaultResult.actor);
}
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;
}
}
let haveResults = this._searchSystem.getProviders().some(function(provider) {
let display = provider.display;
return (display.getFirstResult() != null);
});
if (!haveResults) {
this._statusText.set_text(_("No results."));
@ -589,12 +653,11 @@ const SearchResults = new Lang.Class({
}
},
_updateResults: function(searchSystem, results) {
_updateResults: function(searchSystem, provider, results) {
let terms = searchSystem.getTerms();
let [provider, providerResults] = results;
let display = this._providerDisplays[provider.id];
let display = provider.display;
display.updateSearch(providerResults, terms, Lang.bind(this, function() {
display.updateSearch(results, terms, Lang.bind(this, function() {
this._maybeSetInitialSelection();
this._updateStatusText();
}));
@ -607,8 +670,11 @@ const SearchResults = new Lang.Class({
highlightDefault: function(highlight) {
this._highlightDefault = highlight;
if (this._defaultResult)
if (this._defaultResult) {
this._defaultResult.setSelected(highlight);
if (highlight)
Util.ensureActorVisibleInScrollView(this._scrollView, this._defaultResult.actor);
}
},
navigateFocus: function(direction) {

View File

@ -10,6 +10,8 @@ const FileUtils = imports.misc.fileUtils;
const Main = imports.ui.main;
const Params = imports.misc.params;
const Config = imports.misc.config;
const DEFAULT_MODE = 'restrictive';
const _modes = {
@ -92,8 +94,12 @@ const _modes = {
isLocked: false,
isPrimary: true,
unlockDialog: imports.ui.unlockDialog.UnlockDialog,
components: ['networkAgent', 'polkitAgent', 'telepathyClient',
components: Config.HAVE_NETWORKMANAGER ?
['networkAgent', 'polkitAgent', 'telepathyClient',
'keyring', 'autorunManager', 'automountManager'] :
['polkitAgent', 'telepathyClient',
'keyring', 'autorunManager', 'automountManager'],
panel: {
left: ['activities', 'appMenu'],
center: ['dateMenu'],
@ -102,19 +108,12 @@ const _modes = {
}
};
function _getModes(modesLoadedCallback) {
FileUtils.collectFromDatadirsAsync('modes',
{ processFile: _loadMode,
loadedCallback: modesLoadedCallback,
data: _modes });
}
function _loadMode(file, info, loadedData) {
function _loadMode(file, info) {
let name = info.get_name();
let suffix = name.indexOf('.json');
let modeName = suffix == -1 ? name : name.slice(name, suffix);
if (loadedData.hasOwnProperty(modeName))
if (_modes.hasOwnProperty(modeName))
return;
let fileContent, success, tag, newMode;
@ -125,19 +124,24 @@ function _loadMode(file, info, loadedData) {
return;
}
loadedData[modeName] = {};
_modes[modeName] = {};
let propBlacklist = ['unlockDialog'];
for (let prop in loadedData[DEFAULT_MODE]) {
for (let prop in _modes[DEFAULT_MODE]) {
if (newMode[prop] !== undefined &&
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() {
_getModes(function(modes) {
let names = Object.getOwnPropertyNames(modes);
_loadModes();
Mainloop.idle_add(function() {
let names = Object.getOwnPropertyNames(_modes);
for (let i = 0; i < names.length; i++)
if (_modes[names[i]].isPrimary)
print(names[i]);
@ -149,17 +153,13 @@ function listModes() {
const SessionMode = new Lang.Class({
Name: 'SessionMode',
init: function() {
_getModes(Lang.bind(this, function(modes) {
this._modes = modes;
let primary = modes[global.session_mode] &&
modes[global.session_mode].isPrimary;
let mode = primary ? global.session_mode : 'user';
this._modeStack = [mode];
this._sync();
this.emit('sessions-loaded');
}));
_init: function() {
_loadModes();
let isPrimary = (_modes[global.session_mode] &&
_modes[global.session_mode].isPrimary);
let mode = isPrimary ? global.session_mode : 'user';
this._modeStack = [mode];
this._sync();
},
pushMode: function(mode) {
@ -186,13 +186,13 @@ const SessionMode = new Lang.Class({
},
_sync: function() {
let params = this._modes[this.currentMode];
let params = _modes[this.currentMode];
let defaults;
if (params.parentMode)
defaults = Params.parse(this._modes[params.parentMode],
this._modes[DEFAULT_MODE]);
defaults = Params.parse(_modes[params.parentMode],
_modes[DEFAULT_MODE]);
else
defaults = this._modes[DEFAULT_MODE];
defaults = _modes[DEFAULT_MODE];
params = Params.parse(params, defaults);
// A simplified version of Lang.copyProperties, handles

View File

@ -10,7 +10,6 @@ const Config = imports.misc.config;
const ExtensionSystem = imports.ui.extensionSystem;
const ExtensionDownloader = imports.ui.extensionDownloader;
const ExtensionUtils = imports.misc.extensionUtils;
const Hash = imports.misc.hash;
const Main = imports.ui.main;
const Screenshot = imports.ui.screenshot;
const ViewSelector = imports.ui.viewSelector;
@ -70,6 +69,7 @@ const ScreenSaverIface = '<node> \
<signal name="ActiveChanged"> \
<arg name="new_value" type="b" /> \
</signal> \
<signal name="WakeUpScreen" /> \
</interface> \
</node>';
@ -83,8 +83,8 @@ const GnomeShell = new Lang.Class({
this._extensionsService = new GnomeShellExtensions();
this._screenshotService = new Screenshot.ScreenshotService();
this._grabbedAccelerators = new Hash.Map();
this._grabbers = new Hash.Map();
this._grabbedAccelerators = new Map();
this._grabbers = new Map();
global.display.connect('accelerator-activated', Lang.bind(this,
function(display, action, deviceid, timestamp) {
@ -119,7 +119,7 @@ const GnomeShell = new Lang.Class({
returnValue = '';
success = true;
} catch (e) {
returnValue = JSON.stringify(e);
returnValue = '' + e;
success = false;
}
return [success, returnValue];
@ -133,11 +133,16 @@ const GnomeShell = new Lang.Class({
for (let param in params)
params[param] = params[param].deep_unpack();
let monitorIndex = -1;
if (params['monitor'])
monitorIndex = params['monitor'];
let icon = null;
if (params['icon'])
icon = Gio.Icon.new_for_string(params['icon']);
Main.osdWindow.setIcon(icon);
Main.osdWindow.setMonitor (monitorIndex);
Main.osdWindow.setLabel(params['label']);
Main.osdWindow.setLevel(params['level']);
@ -223,9 +228,8 @@ const GnomeShell = new Lang.Class({
},
_onGrabberBusNameVanished: function(connection, name) {
let grabs = this._grabbedAccelerators.items();
for (let i = 0; i < grabs.length; i++) {
let [action, sender] = grabs[i];
let grabs = this._grabbedAccelerators.entries();
for (let [action, sender] of grabs) {
if (sender == name)
this._ungrabAccelerator(action);
}
@ -368,8 +372,10 @@ const GnomeShellExtensions = new Lang.Class({
LaunchExtensionPrefs: function(uuid) {
let appSys = Shell.AppSystem.get_default();
let app = appSys.lookup_app('gnome-shell-extension-prefs.desktop');
app.launch(global.display.get_current_time_roundtrip(),
['extension:///' + uuid], -1, null);
let info = app.get_app_info();
let timestamp = global.display.get_current_time_roundtrip();
info.launch_uris(['extension:///' + uuid],
global.create_app_launch_context(timestamp, -1));
},
ReloadExtension: function(uuid) {
@ -402,6 +408,9 @@ const ScreenSaverDBus = new Lang.Class({
screenShield.connect('active-changed', Lang.bind(this, function(shield) {
this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [shield.active]));
}));
screenShield.connect('wake-up-screen', Lang.bind(this, function(shield) {
this._dbusImpl.emit_signal('WakeUpScreen', null);
}));
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenSaverIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/ScreenSaver');

View File

@ -132,14 +132,14 @@ function _setMenuAlignment(entry, stageX) {
function _onButtonPressEvent(actor, event, entry) {
if (entry.menu.isOpen) {
entry.menu.close(BoxPointer.PopupAnimation.FULL);
return true;
return Clutter.EVENT_STOP;
} else if (event.get_button() == 3) {
let [stageX, stageY] = event.get_coords();
_setMenuAlignment(entry, stageX);
entry.menu.open(BoxPointer.PopupAnimation.FULL);
return true;
return Clutter.EVENT_STOP;
}
return false;
return Clutter.EVENT_PROPAGATE;
};
function _onPopup(actor, entry) {

View File

@ -1,11 +1,11 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Atk = imports.gi.Atk;
const Cairo = imports.cairo;
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const St = imports.gi.St;
const Signals = imports.signals;
const Atk = imports.gi.Atk;
const SLIDER_SCROLL_STEP = 0.05; /* Slider scrolling step in % */
@ -111,12 +111,12 @@ const Slider = new Lang.Class({
},
_startDragging: function(actor, event) {
this.startDragging(event);
return this.startDragging(event);
},
startDragging: function(event) {
if (this._dragging)
return false;
return Clutter.EVENT_PROPAGATE;
this._dragging = true;
@ -129,7 +129,7 @@ const Slider = new Lang.Class({
let absX, absY;
[absX, absY] = event.get_coords();
this._moveHandle(absX, absY);
return true;
return Clutter.EVENT_STOP;
},
_endDragging: function() {
@ -143,7 +143,7 @@ const Slider = new Lang.Class({
this.emit('drag-end');
}
return true;
return Clutter.EVENT_STOP;
},
scroll: function(event) {
@ -151,7 +151,7 @@ const Slider = new Lang.Class({
let delta;
if (event.is_pointer_emulated())
return;
return Clutter.EVENT_PROPAGATE;
if (direction == Clutter.ScrollDirection.DOWN) {
delta = -SLIDER_SCROLL_STEP;
@ -168,17 +168,18 @@ const Slider = new Lang.Class({
this.actor.queue_repaint();
this.emit('value-changed', this._value);
return Clutter.EVENT_STOP;
},
_onScrollEvent: function(actor, event) {
this.scroll(event);
return this.scroll(event);
},
_motionEvent: function(actor, event) {
let absX, absY;
[absX, absY] = event.get_coords();
this._moveHandle(absX, absY);
return true;
return Clutter.EVENT_STOP;
},
onKeyPressEvent: function (actor, event) {
@ -189,9 +190,9 @@ const Slider = new Lang.Class({
this.actor.queue_repaint();
this.emit('value-changed', this._value);
this.emit('drag-end');
return true;
return Clutter.EVENT_STOP;
}
return false;
return Clutter.EVENT_PROPAGATE;
},
_moveHandle: function(absX, absY) {

View File

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

View File

@ -2,16 +2,27 @@
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const GnomeBluetoothApplet = imports.gi.GnomeBluetoothApplet;
const Gio = imports.gi.Gio;
const GnomeBluetooth = imports.gi.GnomeBluetooth;
const Lang = imports.lang;
const St = imports.gi.St;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const PanelMenu = imports.ui.panelMenu;
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" /> \
<property name="BluetoothHasAirplaneMode" type="b" access="read" /> \
</interface> \
</node>';
const RfkillManagerProxy = Gio.DBusProxy.makeProxyWrapper(RfkillManagerInterface);
const Indicator = new Lang.Class({
Name: 'BTIndicator',
Extends: PanelMenu.SystemIndicator,
@ -22,238 +33,76 @@ const Indicator = new Lang.Class({
this._indicator = this._addIndicator();
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;
}
this._sync();
}));
this._proxy.connect('g-properties-changed', Lang.bind(this, this._sync));
// The Bluetooth menu only appears when Bluetooth is in use,
// so just statically build it with a "Turn Off" menu item.
this._item = new PopupMenu.PopupSubMenuMenuItem(_("Bluetooth"), true);
this._item.icon.icon_name = 'bluetooth-active-symbolic';
this._item.menu.addAction(_("Turn Off"), Lang.bind(this, function() {
this._applet.killswitch_state = GnomeBluetooth.KillswitchState.SOFT_BLOCKED;
this._proxy.BluetoothAirplaneMode = true;
}));
this._item.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop');
this.menu.addMenuItem(this._item);
this._applet = new GnomeBluetoothApplet.Applet();
this._applet.connect('devices-changed', Lang.bind(this, this._sync));
this._client = new GnomeBluetooth.Client();
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));
Main.sessionMode.connect('updated', Lang.bind(this, this._sync));
this._sync();
},
this._applet.connect('pincode-request', Lang.bind(this, this._pinRequest));
this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest));
this._applet.connect('auth-request', Lang.bind(this, this._authRequest));
this._applet.connect('auth-service-request', Lang.bind(this, this._authServiceRequest));
this._applet.connect('cancel-request', Lang.bind(this, this._cancelRequest));
_getDefaultAdapter: function() {
let [ret, iter] = this._model.get_iter_first();
while (ret) {
let isDefault = this._model.get_value(iter,
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() {
let connectedDevices = this._applet.get_devices().filter(function(device) {
return device.connected;
});
let nDevices = connectedDevices.length;
let nDevices = this._getNConnectedDevices();
let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
let on = nDevices > 0;
this._indicator.visible = on;
this._item.actor.visible = on;
this.menu.setSensitive(sensitive);
this._indicator.visible = nDevices > 0;
this._item.actor.visible = this._proxy.BluetoothHasAirplaneMode && !this._proxy.BluetoothAirplaneMode;
if (on)
if (nDevices > 0)
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.addAction('allow', _("Allow"));
this.addAction('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.addAction('always-grant', _("Always grant access"));
this.addAction('grant', _("Grant this time only"));
this.addAction('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.addAction('matches', _("Matches"));
this.addAction('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._ok();
return true;
} else if (key == Clutter.KEY_Escape) {
this._cancel();
return true;
}
return false;
}));
this.addActor(this._entry);
let okButton = this.addAction(_("OK"), Lang.bind(this, this._ok));
this.addAction(_("Cancel"), Lang.bind(this, this._cancel));
okButton.reactive = this._canActivateOkButton();
this._entry.clutter_text.connect('text-changed', Lang.bind(this, function() {
okButton.reactive = this._canActivateOkButton();
}));
},
_ok: function() {
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);
}
this.destroy();
},
_cancel: function() {
if (this._numeric)
this._applet.agent_reply_passkey(this._devicePath, -1);
else
this._applet.agent_reply_pincode(this._devicePath, null);
this.destroy();
this._item.status.text = _("Not Connected");
},
_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

@ -48,7 +48,7 @@ const Indicator = new Lang.Class({
this._item.actor.add(icon);
this._item.actor.add(this._slider.actor, { expand: true });
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) {
return this._slider.onKeyPressEvent(actor, event);

View File

@ -341,7 +341,7 @@ const InputSourceIndicator = new Lang.Class({
this._hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
this._hbox.add_child(this._container);
this._hbox.add_child(PopupMenu.unicodeArrow(St.Side.BOTTOM));
this._hbox.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM));
this.actor.add_child(this._hbox);
this.actor.add_style_class_name('panel-status-button');

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

@ -0,0 +1,201 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Shell = imports.gi.Shell;
const LOCATION_SCHEMA = 'org.gnome.shell.location';
const MAX_ACCURACY_LEVEL = 'max-accuracy-level';
var GeoclueIface = '<node> \
<interface name="org.freedesktop.GeoClue2.Manager"> \
<property name="InUse" type="b" access="read"/> \
<property name="AvailableAccuracyLevel" type="u" access="read"/> \
<method name="AddAgent"> \
<arg name="id" type="s" direction="in"/> \
</method> \
</interface> \
</node>';
const GeoclueManager = Gio.DBusProxy.makeProxyWrapper(GeoclueIface);
var AgentIface = '<node> \
<interface name="org.freedesktop.GeoClue2.Agent"> \
<property name="MaxAccuracyLevel" type="u" access="read"/> \
<method name="AuthorizeApp"> \
<arg name="desktop_id" type="s" direction="in"/> \
<arg name="req_accuracy_level" type="u" direction="in"/> \
<arg name="authorized" type="b" direction="out"/> \
<arg name="allowed_accuracy_level" type="u" direction="out"/> \
</method> \
</interface> \
</node>';
const Indicator = new Lang.Class({
Name: 'LocationIndicator',
Extends: PanelMenu.SystemIndicator,
_init: function() {
this.parent();
this._settings = new Gio.Settings({ schema: LOCATION_SCHEMA });
this._settings.connect('changed::' + MAX_ACCURACY_LEVEL,
Lang.bind(this, this._onMaxAccuracyLevelChanged));
this._indicator = this._addIndicator();
this._indicator.icon_name = 'find-location-symbolic';
this._item = new PopupMenu.PopupSubMenuMenuItem(_("Location"), true);
this._item.icon.icon_name = 'find-location-symbolic';
this._agent = Gio.DBusExportedObject.wrapJSObject(AgentIface, this);
this._agent.export(Gio.DBus.system, '/org/freedesktop/GeoClue2/Agent');
this._item.status.text = _("Enabled");
this._onOffAction = this._item.menu.addAction(_("Disable"), Lang.bind(this, this._onOnOffAction));
this.menu.addMenuItem(this._item);
this._watchId = Gio.bus_watch_name(Gio.BusType.SYSTEM,
'org.freedesktop.GeoClue2',
0,
Lang.bind(this, this._connectToGeoclue),
Lang.bind(this, this._onGeoclueVanished));
Main.sessionMode.connect('updated', Lang.bind(this, this._onSessionUpdated));
this._onSessionUpdated();
this._onMaxAccuracyLevelChanged();
this._connectToGeoclue();
},
get MaxAccuracyLevel() {
return this._getMaxAccuracyLevel();
},
// We (and geoclue) have currently no way to reliably identifying apps so
// for now, lets just authorize all apps as long as they provide a valid
// desktop ID. We also ensure they don't get more accuracy than global max.
AuthorizeApp: function(desktop_id, reqAccuracyLevel) {
var appSystem = Shell.AppSystem.get_default();
var app = appSystem.lookup_app(desktop_id + ".desktop");
if (app == null) {
return [false, 0];
}
let allowedAccuracyLevel = clamp(reqAccuracyLevel, 0, this._getMaxAccuracyLevel());
return [true, allowedAccuracyLevel];
},
_syncIndicator: function() {
if (this._proxy == null) {
this._indicator.visible = false;
return;
}
this._indicator.visible = this._proxy.InUse;
this._updateMenuLabels();
},
_connectToGeoclue: function() {
if (this._proxy != null || this._connecting)
return false;
this._connecting = true;
new GeoclueManager(Gio.DBus.system,
'org.freedesktop.GeoClue2',
'/org/freedesktop/GeoClue2/Manager',
Lang.bind(this, this._onProxyReady));
return true;
},
_onProxyReady: function(proxy, error) {
if (error != null) {
log(error.message);
this._connecting = false;
return;
}
this._proxy = proxy;
this._propertiesChangedId = this._proxy.connect('g-properties-changed',
Lang.bind(this, this._onGeocluePropsChanged));
this._availableAccuracyLevel = this._proxy.AvailableAccuracyLevel;
this._syncIndicator();
this._proxy.AddAgentRemote('gnome-shell', Lang.bind(this, this._onAgentRegistered));
},
_onAgentRegistered: function(result, error) {
this._connecting = false;
this._notifyMaxAccuracyLevel();
if (error != null)
log(error.message);
},
_onGeoclueVanished: function() {
if (this._propertiesChangedId) {
this._proxy.disconnect(this._propertiesChangedId);
this._propertiesChangedId = 0;
}
this._proxy = null;
this._syncIndicator();
},
_onOnOffAction: function() {
if (this._getMaxAccuracyLevel() == 0)
this._settings.set_enum(MAX_ACCURACY_LEVEL, this._availableAccuracyLevel);
else
this._settings.set_enum(MAX_ACCURACY_LEVEL, 0);
},
_onSessionUpdated: function() {
let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
this.menu.setSensitive(sensitive);
},
_updateMenuLabels: function() {
if (this._getMaxAccuracyLevel() == 0) {
this._item.status.text = _("Disabled");
this._onOffAction.label.text = _("Enable");
} else {
this._item.status.text = this._indicator.visible ? _("In Use") : _("Enabled");
this._onOffAction.label.text = _("Disable");
}
},
_onMaxAccuracyLevelChanged: function() {
this._updateMenuLabels();
// Gotta ensure geoclue is up and we are registered as agent to it
// before we emit the notify for this property change.
if (!this._connectToGeoclue())
this._notifyMaxAccuracyLevel();
},
_getMaxAccuracyLevel: function() {
return this._settings.get_enum(MAX_ACCURACY_LEVEL);
},
_notifyMaxAccuracyLevel: function() {
let variant = new GLib.Variant('u', this._getMaxAccuracyLevel());
this._agent.emit_property_changed('MaxAccuracyLevel', variant);
},
_onGeocluePropsChanged: function(proxy, properties) {
let unpacked = properties.deep_unpack();
if ("InUse" in unpacked)
this._syncIndicator();
if ("AvailableAccuracyLevel" in unpacked)
this._availableAccuracyLevel = this._proxy.AvailableAccuracyLevel;
}
});
function clamp(value, min, max) {
return Math.max(min, Math.min(max, value));
}

View File

@ -9,19 +9,22 @@ const NetworkManager = imports.gi.NetworkManager;
const NMClient = imports.gi.NMClient;
const NMGtk = imports.gi.NMGtk;
const Signals = imports.signals;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Hash = imports.misc.hash;
const Animation = imports.ui.animation;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const MessageTray = imports.ui.messageTray;
const ModalDialog = imports.ui.modalDialog;
const ModemManager = imports.misc.modemManager;
const Rfkill = imports.ui.status.rfkill;
const Util = imports.misc.util;
const NMConnectionCategory = {
INVALID: 'invalid',
WIRED: 'wired',
WIRELESS: 'wireless',
WWAN: 'wwan',
VPN: 'vpn'
@ -102,18 +105,34 @@ const NMConnectionItem = new Lang.Class({
this._activeConnection = null;
this._activeConnectionChangedId = 0;
this._buildUI();
this._sync();
},
_buildUI: function() {
this.labelItem = new PopupMenu.PopupMenuItem('');
this.labelItem.connect('activate', Lang.bind(this, this._toggle));
this.switchItem = new PopupMenu.PopupSwitchMenuItem(connection.get_id(), false);
this.switchItem.connect('toggled', Lang.bind(this, this._toggle));
this._sync();
this.radioItem = new PopupMenu.PopupMenuItem(this._connection.get_id(), false);
this.radioItem.connect('activate', Lang.bind(this, this._activate));
},
destroy: function() {
this.labelItem.destroy();
this.switchItem.destroy();
this.radioItem.destroy();
},
updateForConnection: function(connection) {
// connection should always be the same object
// (and object path) as this._connection, but
// this can be false if NetworkManager was restarted
// and picked up connections in a different order
// Just to be safe, we set it here again
this._connection = connection;
this.radioItem.label.text = connection.get_id();
this._sync();
this.emit('name-changed');
},
getName: function() {
@ -129,9 +148,8 @@ const NMConnectionItem = new Lang.Class({
_sync: function() {
let isActive = this.isActive();
this.labelItem.label.text = isActive ? _("Turn Off") : _("Connect");
this.switchItem.setToggleState(isActive);
this.switchItem.setStatus(this._getStatus());
this.labelItem.label.text = isActive ? _("Turn Off") : this._section.getConnectLabel();
this.radioItem.setOrnament(isActive ? PopupMenu.Ornament.DOT : PopupMenu.Ornament.NONE);
this.emit('icon-changed');
},
@ -144,8 +162,11 @@ const NMConnectionItem = new Lang.Class({
this._sync();
},
_getStatus: function() {
return null;
_activate: function() {
if (this._activeConnection == null)
this._section.activateConnection(this._connection);
this._sync();
},
_connectionStateChanged: function(ac, newstate, reason) {
@ -176,15 +197,15 @@ const NMConnectionSection = new Lang.Class({
_init: function(client) {
this._client = client;
this._connectionItems = new Hash.Map();
this._connectionItems = new Map();
this._connections = [];
this._labelSection = new PopupMenu.PopupMenuSection();
this._switchSection = new PopupMenu.PopupMenuSection();
this._radioSection = new PopupMenu.PopupMenuSection();
this.item = new PopupMenu.PopupSubMenuMenuItem('', true);
this.item.menu.addMenuItem(this._labelSection);
this.item.menu.addMenuItem(this._switchSection);
this.item.menu.addMenuItem(this._radioSection);
this.connect('icon-changed', Lang.bind(this, this._sync));
},
@ -194,9 +215,9 @@ const NMConnectionSection = new Lang.Class({
},
_sync: function() {
let nItems = this._connectionItems.size();
let nItems = this._connectionItems.size;
this._switchSection.actor.visible = (nItems > 1);
this._radioSection.actor.visible = (nItems > 1);
this._labelSection.actor.visible = (nItems == 1);
this.item.status.text = this._getStatus();
@ -211,19 +232,12 @@ const NMConnectionSection = new Lang.Class({
this.item.label.text = '';
},
_getStatus: function() {
let values = this._connectionItems.values();
for (let i = 0; i < values.length; i++) {
let item = values[i];
if (item.isActive())
return item.getName();
}
return _("Off");
_getMenuIcon: function() {
return this.getIndicatorIcon();
},
_hasConnection: function(connection) {
return this._connectionItems.has(connection.get_uuid());
getConnectLabel: function() {
return _("Connect");
},
_connectionValid: function(connection) {
@ -231,10 +245,7 @@ const NMConnectionSection = new Lang.Class({
},
_connectionSortFunction: function(one, two) {
if (one._timestamp == two._timestamp)
return GLib.utf8_collate(one.get_id(), two.get_id());
return two._timestamp - one._timestamp;
return GLib.utf8_collate(one.get_id(), two.get_id());
},
_makeConnectionItem: function(connection) {
@ -245,10 +256,20 @@ const NMConnectionSection = new Lang.Class({
if (!this._connectionValid(connection))
return;
if (this._hasConnection(connection))
return;
// This function is called everytime connection is added or updated
// In the usual case, we already added this connection and UUID
// didn't change. So we need to check if we already have an item,
// and update it for properties in the connection that changed
// (the only one we care about is the name)
// But it's also possible we didn't know about this connection
// (eg, during coldplug, or because it was updated and suddenly
// it's valid for this device), in which case we add a new item
this._addConnection(connection);
let item = this._connectionItems.get(connection.get_uuid());
if (item)
item.updateForConnection(connection);
else
this._addConnection(connection);
},
_addConnection: function(connection) {
@ -262,17 +283,23 @@ const NMConnectionSection = new Lang.Class({
item.connect('activation-failed', Lang.bind(this, function(item, reason) {
this.emit('activation-failed', reason);
}));
item.connect('name-changed', Lang.bind(this, this._sync));
let pos = Util.insertSorted(this._connections, connection, Lang.bind(this, this._connectionSortFunction));
this._labelSection.addMenuItem(item.labelItem, pos);
this._switchSection.addMenuItem(item.switchItem, pos);
this._radioSection.addMenuItem(item.radioItem, pos);
this._connectionItems.set(connection.get_uuid(), item);
this._sync();
},
removeConnection: function(connection) {
this._connectionItems.get(connection.get_uuid()).destroy();
this._connectionItems.delete(connection.get_uuid());
let uuid = connection.get_uuid();
let item = this._connectionItems.get(uuid);
if (item == undefined)
return;
item.destroy();
this._connectionItems.delete(uuid);
let pos = this._connections.indexOf(connection);
this._connections.splice(pos, 1);
@ -293,11 +320,24 @@ const NMConnectionDevice = new Lang.Class({
this._settings = settings;
this._autoConnectItem = this.item.menu.addAction(_("Connect"), Lang.bind(this, this._autoConnect));
this._deactivateItem = this._radioSection.addAction(_("Turn Off"), Lang.bind(this, this.deactivateConnection));
this._stateChangedId = this._device.connect('state-changed', Lang.bind(this, this._deviceStateChanged));
this._activeConnectionChangedId = this._device.connect('notify::active-connection', Lang.bind(this, this._activeConnectionChanged));
},
_canReachInternet: function() {
if (this._client.primary_connection != this._device.active_connection)
return true;
return this._client.connectivity == NetworkManager.ConnectivityState.FULL;
},
_autoConnect: function() {
let connection = new NetworkManager.Connection();
this._client.add_and_activate_connection(connection, this._device, null, null);
},
destroy: function() {
if (this._stateChangedId) {
GObject.Object.prototype.disconnect.call(this._device, this._stateChangedId);
@ -305,7 +345,7 @@ const NMConnectionDevice = new Lang.Class({
}
if (this._activeConnectionChangedId) {
GObject.Object.prototype.disconnect.call(this._device, this._activeConnectionChangedId);
this._stateChangedId = 0;
this._activeConnectionChangedId = 0;
}
this.parent();
@ -365,8 +405,9 @@ const NMConnectionDevice = new Lang.Class({
},
_sync: function() {
let nItems = this._connectionItems.size();
let nItems = this._connectionItems.size;
this._autoConnectItem.actor.visible = (nItems == 0);
this._deactivateItem.actor.visible = this._device.state > NetworkManager.DeviceState.DISCONNECTED;
this.parent();
},
@ -378,7 +419,7 @@ const NMConnectionDevice = new Lang.Class({
case NetworkManager.DeviceState.DISCONNECTED:
return _("Off");
case NetworkManager.DeviceState.ACTIVATED:
return this.parent();
return _("Connected");
case NetworkManager.DeviceState.UNMANAGED:
/* Translators: this is for network devices that are physically present but are not
under NetworkManager's control (and thus cannot be used in the menu) */
@ -415,6 +456,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));
},
_hasCarrier: function() {
if (this._device instanceof NMClient.DeviceEthernet)
return this._device.carrier;
else
return true;
},
_sync: function() {
this.item.actor.visible = this._hasCarrier();
this.parent();
},
getIndicatorIcon: function() {
if (this._device.active_connection) {
let state = this._device.active_connection.state;
if (state == NetworkManager.ActiveConnectionState.ACTIVATING) {
return 'network-wired-acquiring-symbolic';
} else if (state == NetworkManager.ActiveConnectionState.ACTIVATED) {
if (this._canReachInternet())
return 'network-wired-symbolic';
else
return 'network-wired-no-route-symbolic';
} else {
return 'network-wired-disconnected-symbolic';
}
} else
return 'network-wired-disconnected-symbolic';
}
});
const NMDeviceModem = new Lang.Class({
Name: 'NMDeviceModem',
Extends: NMConnectionDevice,
@ -477,28 +560,20 @@ const NMDeviceModem = new Lang.Class({
return this.parent();
},
_getMenuIcon: function() {
if (this._device.active_connection)
return this.getIndicatorIcon();
else
getIndicatorIcon: function() {
if (this._device.active_connection) {
if (this._device.active_connection.state == NetworkManager.ActiveConnectionState.ACTIVATING)
return 'network-cellular-acquiring-symbolic';
return this._getSignalIcon();
} else {
return 'network-cellular-signal-none-symbolic';
}
},
_getSignalIcon: function() {
return 'network-cellular-signal-' + signalToIcon(this._mobileDevice.signal_quality) + '-symbolic';
},
getIndicatorIcon: function() {
if (this._device.active_connection.state == NetworkManager.ActiveConnectionState.ACTIVATING)
return 'network-cellular-acquiring-symbolic';
if (!this._mobileDevice) {
// this can happen for bluetooth in PAN mode
return 'network-cellular-connected-symbolic';
}
return this._getSignalIcon();
}
});
const NMDeviceBluetooth = new Lang.Class({
@ -512,33 +587,26 @@ const NMDeviceBluetooth = new Lang.Class({
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;
_getDescription: function() {
return this._device.name;
},
_getMenuIcon: function() {
if (this._device.active_connection)
return this.getIndicatorIcon();
else
return 'network-cellular-signal-none-symbolic';
getConnectLabel: function() {
return _("Use as Internet connection");
},
getIndicatorIcon: function() {
let state = this._device.active_connection.state;
if (state == NetworkManager.ActiveConnectionState.ACTIVATING)
return 'network-cellular-acquiring-symbolic';
else if (state == NetworkManager.ActiveConnectionState.ACTIVATED)
return 'network-cellular-connected-symbolic';
else
if (this._device.active_connection) {
let state = this._device.active_connection.state;
if (state == NetworkManager.ActiveConnectionState.ACTIVATING)
return 'network-cellular-acquiring-symbolic';
else if (state == NetworkManager.ActiveConnectionState.ACTIVATED)
return 'network-cellular-connected-symbolic';
else
return 'network-cellular-signal-none-symbolic';
} else {
return 'network-cellular-signal-none-symbolic';
}
}
});
@ -617,6 +685,13 @@ const NMWirelessDialog = new Lang.Class({
this._client = client;
this._device = device;
this._wirelessEnabledChangedId = this._client.connect('notify::wireless-enabled',
Lang.bind(this, this._syncView));
this._rfkill = Rfkill.getRfkillManager();
this._airplaneModeChangedId = this._rfkill.connect('airplane-mode-changed',
Lang.bind(this, this._syncView));
this._networks = [];
this._buildLayout();
@ -638,6 +713,12 @@ const NMWirelessDialog = new Lang.Class({
this._selectedNetwork = null;
this._activeApChanged();
this._updateSensitivity();
this._syncView();
if (accessPoints.length == 0) {
/* If there are no visible access points, request a scan */
this._device.request_scan_simple(null);
}
},
destroy: function() {
@ -653,6 +734,14 @@ const NMWirelessDialog = new Lang.Class({
GObject.Object.prototype.disconnect.call(this._device, this._activeApChangedId);
this._activeApChangedId = 0;
}
if (this._wirelessEnabledChangedId) {
this._client.disconnect(this._wirelessEnabledChangedId);
this._wirelessEnabledChangedId = 0;
}
if (this._airplaneModeChangedId) {
this._rfkill.disconnect(this._airplaneModeChangedId);
this._airplaneModeChangedId = 0;
}
this.parent();
},
@ -674,13 +763,44 @@ const NMWirelessDialog = new Lang.Class({
},
_updateSensitivity: function() {
let connectSensitive = this._selectedNetwork && (this._selectedNetwork != this._activeNetwork);
let connectSensitive = this._client.wireless_enabled && this._selectedNetwork && (this._selectedNetwork != this._activeNetwork);
this._connectButton.reactive = connectSensitive;
this._connectButton.can_focus = connectSensitive;
},
_updateVisibility: function() {
this._noNetworksLabel.visible = (this._networks.length == 0);
_syncView: function() {
if (this._rfkill.airplaneMode) {
this._airplaneBox.show();
this._airplaneIcon.icon_name = 'airplane-mode-symbolic';
this._airplaneHeadline.text = _("Airplane Mode is On");
this._airplaneText.text = _("Wi-Fi is disabled when airplane mode is on.");
this._airplaneButton.label = _("Turn Off Airplane Mode");
this._airplaneButton.visible = !this._rfkill.hwAirplaneMode;
this._airplaneInactive.visible = this._rfkill.hwAirplaneMode;
this._noNetworksBox.hide();
} else if (!this._client.wireless_enabled) {
this._airplaneBox.show();
this._airplaneIcon.icon_name = 'dialog-information-symbolic';
this._airplaneHeadline.text = _("Wi-Fi is Off");
this._airplaneText.text = _("Wi-Fi needs to be turned on in order to connect to a network.");
this._airplaneButton.label = _("Turn On Wi-Fi");
this._airplaneButton.show();
this._airplaneInactive.hide();
this._noNetworksBox.hide();
} else {
this._airplaneBox.hide();
this._noNetworksBox.visible = (this._networks.length == 0);
}
if (this._noNetworksBox.visible)
this._noNetworksSpinner.play();
else
this._noNetworksSpinner.stop();
},
_buildLayout: function() {
@ -714,11 +834,43 @@ const NMWirelessDialog = new Lang.Class({
this._scrollView.add_actor(this._itemBox);
this._stack.add_child(this._scrollView);
this._noNetworksLabel = new St.Label({ style_class: 'no-networks-label',
this._noNetworksBox = new St.BoxLayout({ vertical: true,
style_class: 'no-networks-box',
x_align: Clutter.ActorAlign.CENTER,
y_align: Clutter.ActorAlign.CENTER });
this._noNetworksSpinner = new Animation.AnimatedIcon(global.datadir + '/theme/process-working.svg', 24, 24);
this._noNetworksBox.add_actor(this._noNetworksSpinner.actor);
this._noNetworksBox.add_actor(new St.Label({ style_class: 'no-networks-label',
text: _("No Networks") }));
this._stack.add_child(this._noNetworksBox);
this._airplaneBox = new St.BoxLayout({ vertical: true,
style_class: 'nm-dialog-airplane-box',
x_align: Clutter.ActorAlign.CENTER,
y_align: Clutter.ActorAlign.CENTER,
text: _("No Networks") });
this._stack.add_child(this._noNetworksLabel);
y_align: Clutter.ActorAlign.CENTER });
this._airplaneIcon = new St.Icon({ icon_size: 48 });
this._airplaneHeadline = new St.Label({ style_class: 'nm-dialog-airplane-headline' });
this._airplaneText = new St.Label({ style_class: 'nm-dialog-airplane-text' });
let airplaneSubStack = new St.Widget({ layout_manager: new Clutter.BinLayout });
this._airplaneButton = new St.Button({ style_class: 'modal-dialog-button' });
this._airplaneButton.connect('clicked', Lang.bind(this, function() {
if (this._rfkill.airplaneMode)
this._rfkill.airplaneMode = false;
else
this._client.wireless_enabled = true;
}));
airplaneSubStack.add_actor(this._airplaneButton);
this._airplaneInactive = new St.Label({ style_class: 'nm-dialog-airplane-text',
text: _("Use hardware switch to turn off") });
airplaneSubStack.add_actor(this._airplaneInactive);
this._airplaneBox.add(this._airplaneIcon, { x_align: St.Align.MIDDLE });
this._airplaneBox.add(this._airplaneHeadline, { x_align: St.Align.MIDDLE });
this._airplaneBox.add(this._airplaneText, { x_align: St.Align.MIDDLE });
this._airplaneBox.add(airplaneSubStack, { x_align: St.Align.MIDDLE });
this._stack.add_child(this._airplaneBox);
this.contentLayout.add(this._stack, { expand: true });
@ -735,16 +887,11 @@ const NMWirelessDialog = new Lang.Class({
_connect: function() {
let network = this._selectedNetwork;
let accessPoints = network.accessPoints;
if (network.connections.length > 0) {
let connection = network.connections[0];
for (let i = 0; i < accessPoints.length; i++) {
if (accessPoints[i].connection_valid(connection)) {
this._client.activate_connection(connection, this._device, accessPoints[i].dbus_path, null);
break;
}
}
this._client.activate_connection(connection, this._device, null, null);
} else {
let accessPoints = network.accessPoints;
if ((accessPoints[0]._secType == NMAccessPointSecurity.WPA2_ENT)
|| (accessPoints[0]._secType == NMAccessPointSecurity.WPA_ENT)) {
// 802.1x-enabled APs require further configuration, so they're
@ -916,7 +1063,7 @@ const NMWirelessDialog = new Lang.Class({
this._itemBox.insert_child_at_index(network.item.actor, newPos);
}
this._updateVisibility();
this._syncView();
},
_accessPointRemoved: function(device, accessPoint) {
@ -931,14 +1078,14 @@ const NMWirelessDialog = new Lang.Class({
network.accessPoints.splice(res.ap, 1);
if (network.accessPoints.length == 0) {
network.item.destroy();
network.item.actor.destroy();
this._networks.splice(res.network, 1);
} else {
network.item.updateBestAP(network.accessPoints[0]);
this._resortItems();
}
this._updateVisibility();
this._syncView();
},
_resortItems: function() {
@ -1023,6 +1170,10 @@ const NMDeviceWireless = new Lang.Class({
this._client.disconnect(this._wirelessHwEnabledChangedId);
this._wirelessHwEnabledChangedId = 0;
}
if (this._dialog) {
this._dialog.destroy();
this._dialog = null;
}
this.item.destroy();
},
@ -1096,7 +1247,12 @@ const NMDeviceWireless = new Lang.Class({
_getStatus: function() {
let ap = this._device.active_access_point;
if (ap)
if (this._isHotSpotMaster())
return _("Hotspot Active");
else if (this._device.state >= NetworkManager.DeviceState.PREPARE &&
this._device.state < NetworkManager.DeviceState.ACTIVATED)
return _("Connecting");
else if (ap)
return ssidToLabel(ap.get_ssid());
else if (!this._client.wireless_hardware_enabled)
return _("Hardware Disabled");
@ -1115,20 +1271,52 @@ const NMDeviceWireless = new Lang.Class({
return 'network-wireless-signal-none-symbolic';
},
_canReachInternet: function() {
if (this._client.primary_connection != this._device.active_connection)
return true;
return this._client.connectivity == NetworkManager.ConnectivityState.FULL;
},
_isHotSpotMaster: function() {
if (!this._device.active_connection)
return false;
let connection = this._settings.get_connection_by_path(this._device.active_connection.connection);
if (!connection)
return false;
let ip4config = connection.get_setting_ip4_config();
if (!ip4config)
return false;
return ip4config.get_method() == NetworkManager.SETTING_IP4_CONFIG_METHOD_SHARED;
},
getIndicatorIcon: function() {
if (this._device.state >= NetworkManager.DeviceState.PREPARE &&
this._device.state < NetworkManager.DeviceState.ACTIVATED)
if (this._device.state < NetworkManager.DeviceState.PREPARE)
return 'network-wireless-disconnected-symbolic';
if (this._device.state < NetworkManager.DeviceState.ACTIVATED)
return 'network-wireless-acquiring-symbolic';
if (this._isHotSpotMaster())
return 'network-wireless-hotspot-symbolic';
let ap = this._device.active_access_point;
if (!ap) {
if (this._device.mode != NM80211Mode.ADHOC)
log('An active wireless connection, in infrastructure mode, involves no access point?');
return 'network-wireless-connected-symbolic';
if (this._canReachInternet())
return 'network-wireless-connected-symbolic';
else
return 'network-wireless-no-route-symbolic';
}
return 'network-wireless-signal-' + signalToIcon(ap.strength) + '-symbolic';
if (this._canReachInternet())
return 'network-wireless-signal-' + signalToIcon(ap.strength) + '-symbolic';
else
return 'network-wireless-no-route-symbolic';
},
});
Signals.addSignalMethods(NMDeviceWireless.prototype);
@ -1144,6 +1332,22 @@ const NMVPNConnectionItem = new Lang.Class({
return this._activeConnection.vpn_state != NetworkManager.VPNConnectionState.DISCONNECTED;
},
_buildUI: function() {
this.labelItem = new PopupMenu.PopupMenuItem('');
this.labelItem.connect('activate', Lang.bind(this, this._toggle));
this.radioItem = new PopupMenu.PopupSwitchMenuItem(this._connection.get_id(), false);
this.radioItem.connect('toggled', Lang.bind(this, this._toggle));
},
_sync: function() {
let isActive = this.isActive();
this.labelItem.label.text = isActive ? _("Turn Off") : this._section.getConnectLabel();
this.radioItem.setToggleState(isActive);
this.radioItem.setStatus(this._getStatus());
this.emit('icon-changed');
},
_getStatus: function() {
if (this._activeConnection == null)
return null;
@ -1213,19 +1417,53 @@ const NMVPNSection = new Lang.Class({
_init: function(client) {
this.parent(client);
this._vpnSettings = new PopupMenu.PopupMenuItem('');
this.item.menu.addMenuItem(this._vpnSettings);
this._vpnSettings.connect('activate', Lang.bind(this, this._onSettingsActivate));
this._sync();
},
_sync: function() {
let nItems = this._connectionItems.size();
let nItems = this._connectionItems.size;
this.item.actor.visible = (nItems > 0);
if (nItems > 1)
this._vpnSettings.label.text = _("Network Settings");
else
this._vpnSettings.label.text = _("VPN Settings");
this.parent();
},
_onSettingsActivate: function() {
let nItems = this._connectionItems.size;
if (nItems > 1) {
let appSys = Shell.AppSystem.get_default();
let app = appSys.lookup_app('gnome-network-panel.desktop');
app.launch(0, -1);
} else {
let connection = this._connections[0];
Util.spawnApp(['gnome-control-center', 'network', 'show-device',
connection.get_path()]);
}
},
_getDescription: function() {
return _("VPN");
},
_getStatus: function() {
let values = this._connectionItems.values();
for (let item of values) {
if (item.isActive())
return item.getName();
}
return _("Off");
},
_getMenuIcon: function() {
return this.getIndicatorIcon() || 'network-vpn-symbolic';
},
@ -1239,9 +1477,10 @@ const NMVPNSection = new Lang.Class({
},
setActiveConnections: function(vpnConnections) {
this._connectionItems.values().forEach(function(item) {
let connections = this._connectionItems.values();
for (let item of connections) {
item.setActiveConnection(null);
});
}
vpnConnections.forEach(Lang.bind(this, function(a) {
let item = this._connectionItems.get(a._connection.get_uuid());
item.setActiveConnection(a);
@ -1254,8 +1493,7 @@ const NMVPNSection = new Lang.Class({
getIndicatorIcon: function() {
let items = this._connectionItems.values();
for (let i = 0; i < items.length; i++) {
let item = items[i];
for (let item of items) {
let icon = item.getIndicatorIcon();
if (icon)
return icon;
@ -1277,6 +1515,7 @@ const NMApplet = new Lang.Class({
// Device types
this._dtypes = { };
this._dtypes[NetworkManager.DeviceType.ETHERNET] = NMDeviceWired;
this._dtypes[NetworkManager.DeviceType.WIFI] = NMDeviceWireless;
this._dtypes[NetworkManager.DeviceType.MODEM] = NMDeviceModem;
this._dtypes[NetworkManager.DeviceType.BT] = NMDeviceBluetooth;
@ -1284,6 +1523,7 @@ const NMApplet = new Lang.Class({
// Connection types
this._ctypes = { };
this._ctypes[NetworkManager.SETTING_WIRED_SETTING_NAME] = NMConnectionCategory.WIRED;
this._ctypes[NetworkManager.SETTING_WIRELESS_SETTING_NAME] = NMConnectionCategory.WIRELESS;
this._ctypes[NetworkManager.SETTING_BLUETOOTH_SETTING_NAME] = NMConnectionCategory.WWAN;
this._ctypes[NetworkManager.SETTING_CDMA_SETTING_NAME] = NMConnectionCategory.WWAN;
@ -1306,6 +1546,15 @@ const NMApplet = new Lang.Class({
this._tryLateInit();
},
_createDeviceCategory: function() {
let category = {
section: new PopupMenu.PopupMenuSection(),
devices: [ ],
};
this.menu.addMenuItem(category.section);
return category;
},
_tryLateInit: function() {
if (!this._client || !this._settings)
return;
@ -1321,17 +1570,9 @@ const NMApplet = new Lang.Class({
this._nmDevices = [];
this._devices = { };
this._devices.wireless = {
section: new PopupMenu.PopupMenuSection(),
devices: [ ],
};
this.menu.addMenuItem(this._devices.wireless.section);
this._devices.wwan = {
section: new PopupMenu.PopupMenuSection(),
devices: [ ],
};
this.menu.addMenuItem(this._devices.wwan.section);
this._devices.wired = this._createDeviceCategory();
this._devices.wireless = this._createDeviceCategory();
this._devices.wwan = this._createDeviceCategory();
this._vpnSection = new NMVPNSection(this._client);
this._vpnSection.connect('activation-failed', Lang.bind(this, this._onActivationFailed));
@ -1601,7 +1842,6 @@ const NMApplet = new Lang.Class({
let connectionSettings = connection.get_setting_by_name(NetworkManager.SETTING_CONNECTION_SETTING_NAME);
connection._type = connectionSettings.type;
connection._section = this._ctypes[connection._type] || NMConnectionCategory.INVALID;
connection._timestamp = connectionSettings.timestamp;
let section = connection._section;
@ -1626,8 +1866,7 @@ const NMApplet = new Lang.Class({
_updateIcon: function() {
if (!this._client.networking_enabled || !this._mainConnection) {
this._primaryIndicator.icon_name = 'network-offline-symbolic';
this._primaryIndicator.visible = true;
this._primaryIndicator.visible = false;
} else {
let dev = this._mainConnection._primaryDevice;
this._primaryIndicator.visible = (dev != null);

View File

@ -2,6 +2,7 @@
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Signals = imports.signals;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
@ -12,11 +13,55 @@ const OBJECT_PATH = '/org/gnome/SettingsDaemon/Rfkill';
const RfkillManagerInterface = '<node> \
<interface name="org.gnome.SettingsDaemon.Rfkill"> \
<property name="AirplaneMode" type="b" access="readwrite" /> \
<property name="HardwareAirplaneMode" type="b" access="read" /> \
</interface> \
</node>';
const RfkillManagerProxy = Gio.DBusProxy.makeProxyWrapper(RfkillManagerInterface);
const RfkillManager = new Lang.Class({
Name: 'RfkillManager',
_init: function() {
this._proxy = new RfkillManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
Lang.bind(this, function(proxy, error) {
if (error) {
log(error.message);
return;
}
this._proxy.connect('g-properties-changed',
Lang.bind(this, this._changed));
this._changed();
}));
},
get airplaneMode() {
return this._proxy.AirplaneMode;
},
set airplaneMode(v) {
this._proxy.AirplaneMode = v;
},
get hwAirplaneMode() {
return this._proxy.HardwareAirplaneMode;
},
_changed: function() {
this.emit('airplane-mode-changed');
}
});
Signals.addSignalMethods(RfkillManager.prototype);
var _manager;
function getRfkillManager() {
if (_manager != null)
return _manager;
_manager = new RfkillManager();
return _manager;
}
const Indicator = new Lang.Class({
Name: 'RfkillIndicator',
Extends: PanelMenu.SystemIndicator,
@ -24,16 +69,8 @@ const Indicator = new Lang.Class({
_init: function() {
this.parent();
this._proxy = new RfkillManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
Lang.bind(this, function(proxy, error) {
if (error) {
log(error.message);
return;
}
this._proxy.connect('g-properties-changed',
Lang.bind(this, this._sync));
this._sync();
}));
this._manager = getRfkillManager();
this._manager.connect('airplane-mode-changed', Lang.bind(this, this._sync));
this._indicator = this._addIndicator();
this._indicator.icon_name = 'airplane-mode-symbolic';
@ -45,7 +82,7 @@ const Indicator = new Lang.Class({
this._item = new PopupMenu.PopupSubMenuMenuItem(_("Airplane Mode"), true);
this._item.icon.icon_name = 'airplane-mode-symbolic';
this._item.status.text = _("On");
this._item.menu.addAction(_("Turn Off"), Lang.bind(this, function() {
this._offItem = this._item.menu.addAction(_("Turn Off"), Lang.bind(this, function() {
this._proxy.AirplaneMode = false;
}));
this._item.menu.addSettingsAction(_("Network Settings"), 'gnome-network-panel.desktop');
@ -53,8 +90,18 @@ const Indicator = new Lang.Class({
},
_sync: function() {
let airplaneMode = this._proxy.AirplaneMode;
let airplaneMode = this._manager.airplaneMode;
let hwAirplaneMode = this._manager.hwAirplaneMode;
let changed = (airplaneMode != this._indicator.visible) ||
(hwAirplaneMode != this._offItem.actor.visible);
this._indicator.visible = airplaneMode;
this._item.actor.visible = airplaneMode;
this._offItem.setSensitive(!hwAirplaneMode);
if (hwAirplaneMode)
this._offItem.label.text = _("Use hardware switch to turn off");
else
this._offItem.label.text = _("Turn Off");
},
});

View File

@ -18,10 +18,12 @@ const PopupMenu = imports.ui.popupMenu;
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
const PRIVACY_SCHEMA = 'org.gnome.desktop.privacy'
const DISABLE_USER_SWITCH_KEY = 'disable-user-switching';
const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen';
const DISABLE_LOG_OUT_KEY = 'disable-log-out';
const DISABLE_RESTART_KEY = 'disable-restart-buttons';
const ALWAYS_SHOW_LOG_OUT_KEY = 'always-show-log-out';
const AltSwitcher = new Lang.Class({
@ -79,7 +81,7 @@ const AltSwitcher = new Lang.Class({
this._sync();
}
return false;
return Clutter.EVENT_PROPAGATE;
},
});
@ -91,6 +93,7 @@ const Indicator = new Lang.Class({
this.parent();
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._privacySettings = new Gio.Settings({ schema: PRIVACY_SCHEMA });
this._orientationSettings = new Gio.Settings({ schema: 'org.gnome.settings-daemon.peripherals.touchscreen' });
@ -261,7 +264,10 @@ const Indicator = new Lang.Class({
},
_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();
},
@ -273,7 +279,10 @@ const Indicator = new Lang.Class({
},
_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();
},

View File

@ -42,7 +42,7 @@ const StreamSlider = new Lang.Class({
this.item.actor.add(this._icon);
this.item.actor.add(this._slider.actor, { expand: true });
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) {
return this._slider.onKeyPressEvent(actor, event);
@ -94,7 +94,7 @@ const StreamSlider = new Lang.Class({
},
scroll: function(event) {
this._slider.scroll(event);
return this._slider.scroll(event);
},
setValue: function(value) {
@ -276,7 +276,7 @@ const VolumeMenu = new Lang.Class({
},
scroll: function(event) {
this._output.scroll(event);
return this._output.scroll(event);
},
_onControlStateChanged: function() {
@ -329,6 +329,6 @@ const Indicator = new Lang.Class({
},
_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 -*-
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
@ -163,6 +164,7 @@ const SwitcherPopup = new Lang.Class({
Main.osdWindow.cancel();
this.actor.opacity = 255;
this._initialDelayTimeoutId = 0;
return GLib.SOURCE_REMOVE;
}));
return true;
},
@ -192,7 +194,7 @@ const SwitcherPopup = new Lang.Class({
else
this._keyPressHandler(keysym, backwards, action);
return true;
return Clutter.EVENT_STOP;
},
_keyReleaseEvent: function(actor, event) {
@ -202,11 +204,12 @@ const SwitcherPopup = new Lang.Class({
if (state == 0)
this._finish(event.get_time());
return true;
return Clutter.EVENT_STOP;
},
_clickedOutside: function(actor, event) {
this.destroy();
return Clutter.EVENT_PROPAGATE;
},
_scrollHandler: function(direction) {
@ -218,6 +221,7 @@ const SwitcherPopup = new Lang.Class({
_scrollEvent: function(actor, event) {
this._scrollHandler(event.get_scroll_direction());
return Clutter.EVENT_PROPAGATE;
},
_itemActivatedHandler: function(n) {
@ -251,6 +255,7 @@ const SwitcherPopup = new Lang.Class({
_mouseTimedOut: function() {
this._motionTimeoutId = 0;
this.mouseActive = true;
return GLib.SOURCE_REMOVE;
},
_popModal: function() {
@ -400,6 +405,7 @@ const SwitcherList = new Lang.Class({
_onItemEnter: function (index) {
this._itemEntered(index);
return Clutter.EVENT_PROPAGATE;
},
highlight: function(index, justOutline) {
@ -446,10 +452,9 @@ const SwitcherList = new Lang.Class({
time: POPUP_SCROLL_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function () {
if (this._highlighted == 0) {
if (this._highlighted == 0)
this._scrollableLeft = false;
this.actor.queue_relayout();
}
this.actor.queue_relayout();
})
});
},
@ -471,10 +476,9 @@ const SwitcherList = new Lang.Class({
time: POPUP_SCROLL_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function () {
if (this._highlighted == this._items.length - 1) {
if (this._highlighted == this._items.length - 1)
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) {
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.natural_size = alloc.min_size;
this._minSize = alloc.min_size;
@ -539,7 +543,7 @@ const SwitcherList = new Lang.Class({
let childHeight = box.y2 - box.y1;
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);

View File

@ -11,29 +11,8 @@ const Signals = imports.signals;
const Tweener = imports.tweener.tweener;
// This is a wrapper around imports.tweener.tweener that adds a bit of
// Clutter integration and some additional callbacks:
//
// 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).
// Clutter integration. If the tweening target is a Clutter.Actor, then
// the tweenings will automatically be removed if the actor is destroyed.
// ActionScript Tweener methods that imports.tweener.tweener doesn't
// currently implement: getTweens, getVersion, registerTransition,
@ -77,7 +56,6 @@ function _wrapTweening(target, tweeningParameters) {
if (!Gtk.Settings.get_default().gtk_enable_animations)
tweeningParameters['time'] = 0.000001;
_addHandler(target, tweeningParameters, 'onStart', _tweenStarted);
_addHandler(target, tweeningParameters, 'onComplete', _tweenCompleted);
}
@ -85,7 +63,7 @@ function _getTweenState(target) {
// If we were paranoid, we could keep a plist mapping targets to
// states... but we're not that paranoid.
if (!target.__ShellTweenerState)
_resetTweenState(target);
target.__ShellTweenerState = {};
return target.__ShellTweenerState;
}
@ -95,8 +73,6 @@ function _resetTweenState(target) {
if (state) {
if (state.destroyedId)
state.actor.disconnect(state.destroyedId);
if (state.idleCompletedId)
Mainloop.source_remove(state.idleCompletedId);
}
target.__ShellTweenerState = {};
@ -122,32 +98,9 @@ function _actorDestroyed(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) {
let state = _getTweenState(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)) {
if (!isTweening(target))
_resetTweenState(target);
if (delegate && delegate.onAnimationComplete)
delegate.onAnimationComplete();
}
return false;
}
function getTweenCount(scope) {

View File

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

View File

@ -1,7 +1,8 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
//
// A widget showing the user avatar and name
const Clutter = imports.gi.Clutter;
const AccountsService = imports.gi.AccountsService;
const GLib = imports.gi.GLib;
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({
Name: 'UserWidget',
@ -67,22 +152,14 @@ const UserWidget = new Lang.Class({
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._avatar = new Avatar(user);
this.actor.add(this._avatar.actor,
{ x_fill: true, y_fill: true });
this.actor.add_child(this._avatar.actor);
this._label = new St.Label({ style_class: 'user-widget-label' });
this.actor.add(this._label,
{ expand: true,
x_fill: true,
y_fill: false,
y_align: St.Align.MIDDLE });
this._label = new UserWidgetLabel(user);
this.actor.add_child(this._label);
this._userLoadedId = this._user.connect('notify::is-loaded',
Lang.bind(this, this._updateUser));
this._userChangedId = this._user.connect('changed',
Lang.bind(this, this._updateUser));
if (this._user.is_loaded)
this._updateUser();
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();
},
_onDestroy: function() {
@ -98,11 +175,6 @@ const UserWidget = new Lang.Class({
},
_updateUser: function() {
if (this._user.is_loaded)
this._label.text = this._user.get_real_name();
else
this._label.text = '';
this._avatar.update();
}
});

View File

@ -2,6 +2,7 @@
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
@ -14,7 +15,6 @@ const AppDisplay = imports.ui.appDisplay;
const Main = imports.ui.main;
const OverviewControls = imports.ui.overviewControls;
const Params = imports.misc.params;
const RemoteSearch = imports.ui.remoteSearch;
const Search = imports.ui.search;
const ShellEntry = imports.ui.shellEntry;
const Tweener = imports.ui.tweener;
@ -64,8 +64,6 @@ const ViewSelector = new Lang.Class({
this._searchActive = false;
this._searchTimeoutId = 0;
this._searchSystem = new Search.SearchSystem();
this._entry = searchEntry;
ShellEntry.addContextMenu(this._entry);
@ -101,22 +99,11 @@ const ViewSelector = new Lang.Class({
this._appsPage = this._addPage(this.appDisplay.actor,
_("Applications"), 'view-grid-symbolic');
this._searchResults = new Search.SearchResults(this._searchSystem);
this._searchResults = new Search.SearchResults();
this._searchPage = this._addPage(this._searchResults.actor,
_("Search"), 'edit-find-symbolic',
{ a11yFocus: this._entry });
this._searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
this._searchSettings.connect('changed::disabled', Lang.bind(this, this._reloadRemoteProviders));
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
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
// dummy widget as the last results container child so that we can
// include the entry in the keynav tab path
@ -171,17 +158,14 @@ const ViewSelector = new Lang.Class({
},
show: function() {
this._activePage = this._workspacesPage;
this.reset();
this._appsPage.hide();
this._searchPage.hide();
this._workspacesDisplay.show();
this._activePage = null;
this._showPage(this._workspacesPage);
if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows())
Main.overview.fadeOutDesktop();
this._showPage(this._workspacesPage, true);
},
zoomFromOverview: function() {
@ -217,6 +201,7 @@ const ViewSelector = new Lang.Class({
this._a11yFocusPage(page);
})
});;
page.hide();
this.actor.add_actor(page);
return page;
},
@ -282,7 +267,7 @@ const ViewSelector = new Lang.Class({
// Ignore events while anything but the overview has
// pushed a modal (system modals, looking glass, ...)
if (Main.modalCount > 1)
return false;
return Clutter.EVENT_PROPAGATE;
let modifiers = event.get_state();
let symbol = event.get_key_symbol();
@ -294,7 +279,7 @@ const ViewSelector = new Lang.Class({
this._showAppsButton.checked = false;
else
Main.overview.hide();
return true;
return Clutter.EVENT_STOP;
} else if (this._shouldTriggerSearch(symbol)) {
this.startSearch(event);
} else if (!this._searchActive) {
@ -306,7 +291,7 @@ const ViewSelector = new Lang.Class({
return true;
}
}
return false;
return Clutter.EVENT_PROPAGATE;
},
_searchCancelled: function() {
@ -422,7 +407,7 @@ const ViewSelector = new Lang.Class({
if (symbol == Clutter.Escape) {
if (this._isActivated()) {
this.reset();
return true;
return Clutter.EVENT_STOP;
}
} else if (this._searchActive) {
let arrowNext, nextDirection;
@ -436,18 +421,18 @@ const ViewSelector = new Lang.Class({
if (symbol == Clutter.Tab) {
this._searchResults.navigateFocus(Gtk.DirectionType.TAB_FORWARD);
return true;
return Clutter.EVENT_STOP;
} else if (symbol == Clutter.ISO_Left_Tab) {
this._focusTrap.can_focus = false;
this._searchResults.navigateFocus(Gtk.DirectionType.TAB_BACKWARD);
this._focusTrap.can_focus = true;
return true;
return Clutter.EVENT_STOP;
} else if (symbol == Clutter.Down) {
this._searchResults.navigateFocus(Gtk.DirectionType.DOWN);
return true;
return Clutter.EVENT_STOP;
} else if (symbol == arrowNext && this._text.position == -1) {
this._searchResults.navigateFocus(nextDirection);
return true;
return Clutter.EVENT_STOP;
} else if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) {
// We can't connect to 'activate' here because search providers
// might want to do something with the modifiers in activateDefault.
@ -456,10 +441,10 @@ const ViewSelector = new Lang.Class({
this._doSearch();
}
this._searchResults.activateDefault();
return true;
return Clutter.EVENT_STOP;
}
}
return false;
return Clutter.EVENT_PROPAGATE;
},
_onCapturedEvent: function(actor, event) {
@ -474,7 +459,7 @@ const ViewSelector = new Lang.Class({
}
}
return false;
return Clutter.EVENT_PROPAGATE;
},
_doSearch: function () {
@ -482,43 +467,10 @@ const ViewSelector = new Lang.Class({
let terms = getTermsForSearchString(this._entry.get_text());
this._searchSystem.updateSearchResults(terms);
this._searchResults.setTerms(terms);
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);
return GLib.SOURCE_REMOVE;
},
getActivePage: function() {
@ -530,13 +482,6 @@ const ViewSelector = new Lang.Class({
return ViewPage.SEARCH;
},
setActivePage: function(page) {
if (page == ViewPage.WINDOWS)
this._showPage(this._workspacesPage);
else
this._showPage(this._appsPage);
},
fadeIn: function() {
let actor = this._activePage;
Tweener.addTween(actor, { opacity: 255,

View File

@ -16,7 +16,7 @@ const WindowAttentionHandler = new Lang.Class({
_getTitleAndBanner: function(app, window) {
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]
},

View File

@ -106,11 +106,11 @@ const DisplayChangeDialog = new Lang.Class({
/* mutter already takes care of failing at timeout */
this._timeoutId = 0;
this.close();
return false;
return GLib.SOURCE_REMOVE;
}
this._descriptionLabel.text = this._formatCountDown();
return true;
return GLib.SOURCE_CONTINUE;
},
_onFailure: function() {
@ -191,6 +191,7 @@ const WorkspaceTracker = new Lang.Class({
tracker.connect('startup-sequence-changed', Lang.bind(this, this._queueCheckWorkspaces));
global.screen.connect('notify::n-workspaces', Lang.bind(this, this._nWorkspacesChanged));
global.window_manager.connect('switch-workspace', Lang.bind(this, this._queueCheckWorkspaces));
global.screen.connect('window-entered-monitor', Lang.bind(this, this._windowEnteredMonitor));
global.screen.connect('window-left-monitor', Lang.bind(this, this._windowLeftMonitor));
@ -232,12 +233,13 @@ const WorkspaceTracker = new Lang.Class({
let windows = global.get_window_actors();
for (i = 0; i < windows.length; i++) {
let win = windows[i];
let actor = windows[i];
let win = actor.get_meta_window();
if (win.get_meta_window().is_on_all_workspaces())
if (win.is_on_all_workspaces())
continue;
let workspaceIndex = win.get_workspace();
let workspaceIndex = win.get_workspace().index();
emptyWorkspaces[workspaceIndex] = false;
}
@ -248,13 +250,7 @@ const WorkspaceTracker = new Lang.Class({
}
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
let removingCurrentWorkspace = (emptyWorkspaces[activeWorkspaceIndex] &&
activeWorkspaceIndex < emptyWorkspaces.length - 1);
if (removingCurrentWorkspace) {
// "Merge" the empty workspace we are removing with the one at the end
this._wm.blockAnimations();
}
emptyWorkspaces[activeWorkspaceIndex] = false;
// Delete other empty workspaces; do it from the end to avoid index changes
for (i = emptyWorkspaces.length - 2; i >= 0; i--) {
@ -262,11 +258,6 @@ const WorkspaceTracker = new Lang.Class({
global.screen.remove_workspace(this._workspaces[i], global.get_current_time());
}
if (removingCurrentWorkspace) {
global.screen.get_workspace_by_index(global.screen.n_workspaces - 1).activate(global.get_current_time());
this._wm.unblockAnimations();
}
this._checkWorkspacesId = 0;
return false;
},
@ -278,7 +269,7 @@ const WorkspaceTracker = new Lang.Class({
workspace._keepAliveId = Mainloop.timeout_add(duration, Lang.bind(this, function() {
workspace._keepAliveId = 0;
this._queueCheckWorkspaces();
return false;
return GLib.SOURCE_REMOVE;
}));
},
@ -290,7 +281,7 @@ const WorkspaceTracker = new Lang.Class({
workspace._lastRemovedWindow = null;
this._queueCheckWorkspaces();
}
return false;
return GLib.SOURCE_REMOVE;
}));
},
@ -367,6 +358,93 @@ const WorkspaceTracker = new Lang.Class({
}
});
const TilePreview = new Lang.Class({
Name: 'TilePreview',
_init: function() {
this.actor = new St.Widget();
global.window_group.add_actor(this.actor);
this._reset();
this._showing = false;
},
show: function(window, tileRect, monitorIndex) {
let windowActor = window.get_compositor_private();
if (!windowActor)
return;
global.window_group.set_child_below_sibling(this.actor, windowActor);
if (this._rect && this._rect.equal(tileRect))
return;
let changeMonitor = (this._monitorIndex == -1 ||
this._monitorIndex != monitorIndex);
this._monitorIndex = monitorIndex;
this._rect = tileRect;
let monitor = Main.layoutManager.monitors[monitorIndex];
this._updateStyle(monitor);
if (!this._showing || changeMonitor) {
let monitorRect = new Meta.Rectangle({ x: monitor.x,
y: monitor.y,
width: monitor.width,
height: monitor.height });
let [, rect] = window.get_outer_rect().intersect(monitorRect);
this.actor.set_size(rect.width, rect.height);
this.actor.set_position(rect.x, rect.y);
this.actor.opacity = 0;
}
this._showing = true;
this.actor.show();
Tweener.addTween(this.actor,
{ x: tileRect.x,
y: tileRect.y,
width: tileRect.width,
height: tileRect.height,
opacity: 255,
time: WINDOW_ANIMATION_TIME,
transition: 'easeOutQuad'
});
},
hide: function() {
if (!this._showing)
return;
this._showing = false;
Tweener.addTween(this.actor,
{ opacity: 0,
time: WINDOW_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, this._reset)
});
},
_reset: function() {
this.actor.hide();
this._rect = null;
this._monitorIndex = -1;
},
_updateStyle: function(monitor) {
let styles = ['tile-preview'];
if (this._monitorIndex == Main.layoutManager.primaryIndex)
styles.push('on-primary');
if (this._rect.x == monitor.x)
styles.push('tile-preview-left');
if (this._rect.x + this._rect.width == monitor.x + monitor.width)
styles.push('tile-preview-right');
this.actor.style_class = styles.join(' ');
}
});
const WindowManager = new Lang.Class({
Name: 'WindowManager',
@ -397,6 +475,8 @@ const WindowManager = new Lang.Class({
}));
this._shellwm.connect('switch-workspace', Lang.bind(this, this._switchWorkspace));
this._shellwm.connect('show-tile-preview', Lang.bind(this, this._showTilePreview));
this._shellwm.connect('hide-tile-preview', Lang.bind(this, this._hideTilePreview));
this._shellwm.connect('minimize', Lang.bind(this, this._minimizeWindow));
this._shellwm.connect('maximize', Lang.bind(this, this._maximizeWindow));
this._shellwm.connect('unmaximize', Lang.bind(this, this._unmaximizeWindow));
@ -406,6 +486,8 @@ const WindowManager = new Lang.Class({
this._shellwm.connect('confirm-display-change', Lang.bind(this, this._confirmDisplayChange));
this._workspaceSwitcherPopup = null;
this._tilePreview = null;
this.setCustomKeybindingHandler('switch-to-workspace-left',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
@ -555,6 +637,12 @@ const WindowManager = new Lang.Class({
Shell.KeyBindingMode.LOGIN_SCREEN,
Lang.bind(this, this._startA11ySwitcher));
this.addKeybinding('pause-resume-tweens',
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
Meta.KeyBindingFlags.NONE,
Shell.KeyBindingMode.ALL,
Lang.bind(this, this._toggleTweens));
this.addKeybinding('open-application-menu',
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
Meta.KeyBindingFlags.NONE,
@ -742,15 +830,13 @@ const WindowManager = new Lang.Class({
if (shouldDim && !window._dimmed) {
window._dimmed = true;
this._dimmedWindows.push(window);
if (!Main.overview.visible)
this._dimWindow(window);
this._dimWindow(window);
} else if (!shouldDim && window._dimmed) {
window._dimmed = false;
this._dimmedWindows = this._dimmedWindows.filter(function(win) {
return win != window;
});
if (!Main.overview.visible)
this._undimWindow(window);
this._undimWindow(window);
}
},
@ -975,25 +1061,29 @@ const WindowManager = new Lang.Class({
wgroup.add_actor(switchData.movingWindowBin);
for (let i = 0; i < windows.length; i++) {
let window = windows[i];
let actor = windows[i];
let window = actor.get_meta_window();
if (!window.meta_window.showing_on_its_workspace())
if (!window.showing_on_its_workspace())
continue;
if (this._movingWindow && window.meta_window == this._movingWindow) {
switchData.movingWindow = { window: window,
parent: window.get_parent() };
if (window.is_on_all_workspaces())
continue;
let record = { window: actor,
parent: actor.get_parent() };
if (this._movingWindow && window == this._movingWindow) {
switchData.movingWindow = record;
switchData.windows.push(switchData.movingWindow);
window.reparent(switchData.movingWindowBin);
} else if (window.get_workspace() == from) {
switchData.windows.push({ window: window,
parent: window.get_parent() });
window.reparent(switchData.outGroup);
} else if (window.get_workspace() == to) {
switchData.windows.push({ window: window,
parent: window.get_parent() });
window.reparent(switchData.inGroup);
window.show();
actor.reparent(switchData.movingWindowBin);
} else if (window.get_workspace().index() == from) {
switchData.windows.push(record);
actor.reparent(switchData.outGroup);
} else if (window.get_workspace().index() == to) {
switchData.windows.push(record);
actor.reparent(switchData.inGroup);
actor.show();
}
}
@ -1047,6 +1137,18 @@ const WindowManager = new Lang.Class({
shellwm.completed_switch_workspace();
},
_showTilePreview: function(shellwm, window, tileRect, monitorIndex) {
if (!this._tilePreview)
this._tilePreview = new TilePreview();
this._tilePreview.show(window, tileRect, monitorIndex);
},
_hideTilePreview: function(shellwm) {
if (!this._tilePreview)
return;
this._tilePreview.hide();
},
_startAppSwitcher : function(display, screen, window, binding) {
/* prevent a corner case where both popups show up at once */
if (this._workspaceSwitcherPopup != null)
@ -1083,6 +1185,15 @@ const WindowManager = new Lang.Class({
Main.panel.toggleAppMenu();
},
_toggleTweens: function() {
this._tweensPaused = !this._tweensPaused;
const OrigTweener = imports.tweener.tweener;
if (this._tweensPaused)
OrigTweener.pauseAllTweens();
else
OrigTweener.resumeAllTweens();
},
_showWorkspaceSwitcher : function(display, screen, window, binding) {
if (!Main.sessionMode.hasWorkspaces)
return;

View File

@ -1,28 +1,27 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Pango = imports.gi.Pango;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Atk = imports.gi.Atk;
const Signals = imports.signals;
const DND = imports.ui.dnd;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const Overview = imports.ui.overview;
const Panel = imports.ui.panel;
const Tweener = imports.ui.tweener;
const FOCUS_ANIMATION_TIME = 0.15;
const WindowManager = imports.ui.windowManager;
const WINDOW_DND_SIZE = 256;
const WINDOW_CLONE_MAXIMUM_SCALE = 0.7;
const LIGHTBOX_FADE_TIME = 0.1;
const CLOSE_BUTTON_FADE_TIME = 0.1;
const DRAGGING_WINDOW_OPACITY = 100;
@ -39,6 +38,68 @@ function _interpolate(start, end, step) {
return start + (end - start) * step;
}
const WindowCloneLayout = new Lang.Class({
Name: 'WindowCloneLayout',
Extends: Clutter.LayoutManager,
_init: function(boundingBox) {
this.parent();
this._boundingBox = boundingBox;
},
get boundingBox() {
return this._boundingBox;
},
set boundingBox(b) {
this._boundingBox = b;
this.layout_changed();
},
_makeBoxForWindow: function(window) {
// We need to adjust the position of the actor because of the
// consequences of invisible borders -- in reality, the texture
// has an extra set of "padding" around it that we need to trim
// down.
// The outer rect (from which we compute the bounding box)
// paradoxically is the smaller rectangle, containing the positions
// of the visible frame. The input rect contains everything,
// including the invisible border padding.
let inputRect = window.get_input_rect();
let box = new Clutter.ActorBox();
box.set_origin(inputRect.x - this._boundingBox.x,
inputRect.y - this._boundingBox.y);
box.set_size(inputRect.width, inputRect.height);
return box;
},
vfunc_get_preferred_height: function(container, forWidth) {
return [this._boundingBox.height, this._boundingBox.height];
},
vfunc_get_preferred_width: function(container, forHeight) {
return [this._boundingBox.width, this._boundingBox.width];
},
vfunc_allocate: function(container, box, flags) {
let clone = container.get_children().forEach(function (child) {
let realWindow;
if (child == container._delegate._windowClone)
realWindow = container._delegate.realWindow;
else
realWindow = child.source;
child.allocate(this._makeBoxForWindow(realWindow.meta_window),
flags);
}, this);
},
});
const WindowClone = new Lang.Class({
Name: 'WindowClone',
@ -48,10 +109,7 @@ const WindowClone = new Lang.Class({
this.metaWindow._delegate = this;
this._workspace = workspace;
let [borderX, borderY] = this._getInvisibleBorderPadding();
this._windowClone = new Clutter.Clone({ source: realWindow.get_texture(),
x: -borderX,
y: -borderY });
this._windowClone = new Clutter.Clone({ source: realWindow.get_texture() });
// We expect this.actor to be used for all interaction rather than
// this._windowClone; as the former is reactive and the latter
// is not, this just works for most cases. However, for DND all
@ -59,20 +117,14 @@ const WindowClone = new Lang.Class({
// To avoid this, we hide it from pick.
Shell.util_set_hidden_from_pick(this._windowClone, true);
this.origX = realWindow.x + borderX;
this.origY = realWindow.y + borderY;
let outerRect = realWindow.meta_window.get_outer_rect();
// The MetaShapedTexture that we clone has a size that includes
// the invisible border; this is inconvenient; rather than trying
// to compensate all over the place we insert a ClutterActor into
// the hierarchy that is sized to only the visible portion.
this.actor = new Clutter.Actor({ reactive: true,
x: this.origX,
y: this.origY,
width: outerRect.width,
height: outerRect.height });
this.actor = new St.Widget({ reactive: true,
can_focus: true,
accessible_role: Atk.Role.PUSH_BUTTON,
layout_manager: new WindowCloneLayout() });
this.actor.add_child(this._windowClone);
@ -82,18 +134,27 @@ const WindowClone = new Lang.Class({
this._dragSlot = [0, 0, 0, 0];
this._stackAbove = null;
this._sizeChangedId = this.realWindow.connect('size-changed',
this._windowClone._updateId = this.metaWindow.connect('size-changed',
Lang.bind(this, this._onRealWindowSizeChanged));
this._realWindowDestroyId = this.realWindow.connect('destroy',
Lang.bind(this, this._disconnectRealWindowSignals));
this._windowClone._destroyId = this.realWindow.connect('destroy', Lang.bind(this, function() {
// First destroy the clone and then destroy everything
// This will ensure that we never see it in the _disconnectSignals loop
this._windowClone.destroy();
this.destroy();
}));
this._updateAttachedDialogs();
this._computeBoundingBox();
this.actor.x = this._boundingBox.x;
this.actor.y = this._boundingBox.y;
let clickAction = new Clutter.ClickAction();
clickAction.connect('clicked', Lang.bind(this, this._onClicked));
clickAction.connect('long-press', Lang.bind(this, this._onLongPress));
this.actor.add_action(clickAction);
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPress));
this.actor.connect('enter-event', Lang.bind(this, this._onEnter));
this._draggable = DND.makeDraggable(this.actor,
{ restoreOnSuccess: true,
@ -119,6 +180,83 @@ const WindowClone = new Lang.Class({
return this._slot;
},
deleteAll: function() {
// Delete all windows, starting from the bottom-most (most-modal) one
let windows = this.actor.get_children();
for (let i = windows.length - 1; i >= 1; i--) {
let realWindow = windows[i].source;
let metaWindow = realWindow.meta_window;
metaWindow.delete(global.get_current_time());
}
this.metaWindow.delete(global.get_current_time());
},
addAttachedDialog: function(win) {
this._doAddAttachedDialog(win, win.get_compositor_private());
this._computeBoundingBox();
this.emit('size-changed');
},
_doAddAttachedDialog: function(metaWin, realWin) {
let clone = new Clutter.Clone({ source: realWin });
clone._updateId = metaWin.connect('size-changed', Lang.bind(this, function() {
this._computeBoundingBox();
this.emit('size-changed');
}));
clone._destroyId = realWin.connect('destroy', Lang.bind(this, function() {
clone.destroy();
this._computeBoundingBox();
this.emit('size-changed');
}));
this.actor.add_child(clone);
},
_updateAttachedDialogs: function() {
let iter = Lang.bind(this, function(win) {
let actor = win.get_compositor_private();
if (!actor)
return false;
if (!win.is_attached_dialog())
return false;
this._doAddAttachedDialog(win, actor);
win.foreach_transient(iter);
return true;
});
this.metaWindow.foreach_transient(iter);
},
get boundingBox() {
return this._boundingBox;
},
getOriginalPosition: function() {
return [this._boundingBox.x, this._boundingBox.y];
},
_computeBoundingBox: function() {
let rect = this.metaWindow.get_outer_rect();
this.actor.get_children().forEach(function (child) {
let realWindow;
if (child == this._windowClone)
realWindow = this.realWindow;
else
realWindow = child.source;
let metaWindow = realWindow.meta_window;
rect = rect.union(metaWindow.get_outer_rect());
}, this);
this._boundingBox = rect;
this.actor.layout_manager.boundingBox = rect;
},
// Find the actor just below us, respecting reparenting done
// by DND code
getActualStackAbove: function() {
@ -152,44 +290,26 @@ const WindowClone = new Lang.Class({
this.actor.destroy();
},
_disconnectRealWindowSignals: function() {
if (this._sizeChangedId > 0)
this.realWindow.disconnect(this._sizeChangedId);
this._sizeChangedId = 0;
_disconnectSignals: function() {
this.actor.get_children().forEach(Lang.bind(this, function (child) {
let realWindow;
if (child == this._windowClone)
realWindow = this.realWindow;
else
realWindow = child.source;
if (this._realWindowDestroyId > 0)
this.realWindow.disconnect(this._realWindowDestroyId);
this._realWindowDestroyId = 0;
},
_getInvisibleBorderPadding: function() {
// We need to adjust the position of the actor because of the
// consequences of invisible borders -- in reality, the texture
// has an extra set of "padding" around it that we need to trim
// down.
// The outer rect paradoxically is the smaller rectangle,
// containing the positions of the visible frame. The input
// rect contains everything, including the invisible border
// padding.
let outerRect = this.metaWindow.get_outer_rect();
let inputRect = this.metaWindow.get_input_rect();
let [borderX, borderY] = [outerRect.x - inputRect.x,
outerRect.y - inputRect.y];
return [borderX, borderY];
realWindow.meta_window.disconnect(child._updateId);
realWindow.disconnect(child._destroyId);
}));
},
_onRealWindowSizeChanged: function() {
let [borderX, borderY] = this._getInvisibleBorderPadding();
let outerRect = this.metaWindow.get_outer_rect();
this.actor.set_size(outerRect.width, outerRect.height);
this._windowClone.set_position(-borderX, -borderY);
this._computeBoundingBox();
this.emit('size-changed');
},
_onDestroy: function() {
this._disconnectRealWindowSignals();
this._disconnectSignals();
this.metaWindow._delegate = null;
this.actor._delegate = null;
@ -202,11 +322,30 @@ const WindowClone = new Lang.Class({
this.disconnectAll();
},
_onClicked: function(action, actor) {
_activate: function() {
this._selected = true;
this.emit('selected', global.get_current_time());
},
_onKeyPress: function(actor, event) {
let symbol = event.get_key_symbol();
let isEnter = (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_KP_Enter);
if (isEnter) {
this._activate();
return true;
}
return false;
},
_onEnter: function() {
this.actor.grab_key_focus();
},
_onClicked: function(action, actor) {
this._activate();
},
_onLongPress: function(action, actor, state) {
// Take advantage of the Clutter policy to consider
// a long-press canceled when the pointer movement
@ -288,6 +427,7 @@ const WindowOverlay = new Lang.Class({
text: metaWindow.title });
title.clutter_text.ellipsize = Pango.EllipsizeMode.END;
title._spacing = 0;
windowClone.actor.label_actor = title;
this._updateCaptionId = metaWindow.connect('notify::title',
Lang.bind(this, function(w) {
@ -306,6 +446,10 @@ const WindowOverlay = new Lang.Class({
Lang.bind(this, this._onEnter));
windowClone.actor.connect('leave-event',
Lang.bind(this, this._onLeave));
windowClone.actor.connect('key-focus-in',
Lang.bind(this, this._onEnter));
windowClone.actor.connect('key-focus-out',
Lang.bind(this, this._onLeave));
this._windowAddedId = 0;
@ -434,7 +578,7 @@ const WindowOverlay = new Lang.Class({
Lang.bind(this,
this._onWindowAdded));
metaWindow.delete(global.get_current_time());
this._windowClone.deleteAll();
},
_windowCanClose: function() {
@ -453,7 +597,7 @@ const WindowOverlay = new Lang.Class({
Mainloop.idle_add(Lang.bind(this,
function() {
this._windowClone.emit('selected');
return false;
return GLib.SOURCE_REMOVE;
}));
}
},
@ -517,15 +661,17 @@ const WindowOverlay = new Lang.Class({
// as the close button will be shown as needed when the overlays
// are shown again
if (this._hidden)
return;
return Clutter.EVENT_PROPAGATE;
this._animateVisible();
this.emit('show-close-button');
return Clutter.EVENT_PROPAGATE;
},
_onLeave: function() {
if (this._idleToggleCloseId == 0)
this._idleToggleCloseId = Mainloop.timeout_add(750, Lang.bind(this, this._idleToggleCloseButton));
return Clutter.EVENT_PROPAGATE;
},
_idleToggleCloseButton: function() {
@ -535,7 +681,7 @@ const WindowOverlay = new Lang.Class({
!this.closeButton.has_pointer)
this._animateInvisible();
return false;
return GLib.SOURCE_REMOVE;
},
hideCloseButton: function() {
@ -892,6 +1038,19 @@ function padArea(area, padding) {
};
}
function rectEqual(one, two) {
if (one == two)
return true;
if (!one || !two)
return false;
return (one.x == two.x &&
one.y == two.y &&
one.width == two.width &&
one.height == two.height);
}
/**
* @metaWorkspace: a #Meta.Workspace, or null
*/
@ -967,11 +1126,17 @@ const Workspace = new Lang.Class({
},
setFullGeometry: function(geom) {
if (rectEqual(this._fullGeometry, geom))
return;
this._fullGeometry = geom;
this._recalculateWindowPositions(WindowPositionFlags.NONE);
},
setActualGeometry: function(geom) {
if (rectEqual(this._actualGeometry, geom))
return;
this._actualGeometry = geom;
if (this._actualGeometryLater)
@ -1184,17 +1349,18 @@ const Workspace = new Lang.Class({
// store current cursor position
this._cursorX = x;
this._cursorY = y;
return true;
return GLib.SOURCE_CONTINUE;
}
let actorUnderPointer = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
for (let i = 0; i < this._windows.length; i++) {
if (this._windows[i].actor == actorUnderPointer)
return true;
return GLib.SOURCE_CONTINUE;
}
this._recalculateWindowPositions(WindowPositionFlags.ANIMATE);
return false;
this._repositionWindowsId = 0;
return GLib.SOURCE_REMOVE;
},
_doRemoveWindow : function(metaWin) {
@ -1269,7 +1435,7 @@ const Workspace = new Lang.Class({
metaWin.get_compositor_private() &&
metaWin.get_workspace() == this.metaWorkspace)
this._doAddWindow(metaWin);
return false;
return GLib.SOURCE_REMOVE;
}));
return;
}
@ -1279,9 +1445,29 @@ const Workspace = new Lang.Class({
if (this._lookupIndex (metaWin) != -1)
return;
if (!this._isMyWindow(win) || !this._isOverviewWindow(win))
if (!this._isMyWindow(win))
return;
if (!this._isOverviewWindow(win)) {
if (metaWin.is_attached_dialog()) {
let parent = metaWin.get_transient_for();
while (parent.is_attached_dialog())
parent = metaWin.get_transient_for();
let idx = this._lookupIndex (parent);
if (idx < 0) {
// parent was not created yet, it will take care
// of the dialog when created
return;
}
let clone = this._windows[idx];
clone.addAttachedDialog(metaWin);
}
return;
}
let [clone, overlay] = this._addWindowClone(win, false);
if (win._overviewHint) {
@ -1369,9 +1555,11 @@ const Workspace = new Lang.Class({
overlay.hide();
if (clone.metaWindow.showing_on_its_workspace()) {
let [origX, origY] = clone.getOriginalPosition();
Tweener.addTween(clone.actor,
{ x: clone.origX,
y: clone.origY,
{ x: origX,
y: origY,
scale_x: 1.0,
scale_y: 1.0,
time: Overview.ANIMATION_TIME,
@ -1435,8 +1623,7 @@ const Workspace = new Lang.Class({
// Tests if @win should be shown in the Overview
_isOverviewWindow : function (win) {
let tracker = Shell.WindowTracker.get_default();
return tracker.is_window_interesting(win.get_meta_window());
return !win.get_meta_window().skip_taskbar;
},
// Create a clone of a (non-desktop) window and add it to the window list
@ -1449,17 +1636,17 @@ const Workspace = new Lang.Class({
clone.connect('selected',
Lang.bind(this, this._onCloneSelected));
clone.connect('drag-begin',
Lang.bind(this, function(clone) {
Main.overview.beginWindowDrag();
Lang.bind(this, function() {
Main.overview.beginWindowDrag(clone);
overlay.hide();
}));
clone.connect('drag-cancelled',
Lang.bind(this, function(clone) {
Main.overview.cancelledWindowDrag();
Lang.bind(this, function() {
Main.overview.cancelledWindowDrag(clone);
}));
clone.connect('drag-end',
Lang.bind(this, function(clone) {
Main.overview.endWindowDrag();
Lang.bind(this, function() {
Main.overview.endWindowDrag(clone);
overlay.show();
}));
clone.connect('size-changed',

View File

@ -1,6 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
@ -156,6 +157,7 @@ const WorkspaceSwitcherPopup = new Lang.Class({
onComplete: function() { this.destroy(); },
onCompleteScope: this
});
return GLib.SOURCE_REMOVE;
},
destroy: function() {

View File

@ -2,6 +2,7 @@
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
@ -13,6 +14,7 @@ const Background = imports.ui.background;
const DND = imports.ui.dnd;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const WindowManager = imports.ui.windowManager;
const Workspace = imports.ui.workspace;
const WorkspacesView = imports.ui.workspacesView;
@ -31,20 +33,49 @@ const WORKSPACE_KEEP_ALIVE_TIME = 100;
const OVERRIDE_SCHEMA = 'org.gnome.shell.overrides';
/* A layout manager that requests size only for primary_actor, but then allocates
all using a fixed layout */
const PrimaryActorLayout = new Lang.Class({
Name: 'PrimaryActorLayout',
Extends: Clutter.FixedLayout,
_init: function(primaryActor) {
this.parent();
this.primaryActor = primaryActor;
},
vfunc_get_preferred_width: function(forHeight) {
return this.primaryActor.get_preferred_width(forHeight);
},
vfunc_get_preferred_height: function(forWidth) {
return this.primaryActor.get_preferred_height(forWidth);
},
});
const WindowClone = new Lang.Class({
Name: 'WindowClone',
_init : function(realWindow) {
this.actor = new Clutter.Clone({ source: realWindow.get_texture(),
this.clone = new Clutter.Clone({ source: realWindow });
/* Can't use a Shell.GenericContainer because of DND and reparenting... */
this.actor = new Clutter.Actor({ layout_manager: new PrimaryActorLayout(this.clone),
reactive: true });
this.actor._delegate = this;
this.actor.add_child(this.clone);
this.realWindow = realWindow;
this.metaWindow = realWindow.meta_window;
this._positionChangedId = this.realWindow.connect('position-changed',
Lang.bind(this, this._onPositionChanged));
this._realWindowDestroyedId = this.realWindow.connect('destroy',
Lang.bind(this, this._disconnectRealWindowSignals));
this.clone._updateId = this.metaWindow.connect('position-changed',
Lang.bind(this, this._onPositionChanged));
this.clone._destroyId = this.realWindow.connect('destroy', Lang.bind(this, function() {
// First destroy the clone and then destroy everything
// This will ensure that we never see it in the _disconnectSignals loop
this.clone.destroy();
this.destroy();
}));
this._onPositionChanged();
this.actor.connect('button-release-event',
@ -60,6 +91,21 @@ const WindowClone = new Lang.Class({
this._draggable.connect('drag-cancelled', Lang.bind(this, this._onDragCancelled));
this._draggable.connect('drag-end', Lang.bind(this, this._onDragEnd));
this.inDrag = false;
let iter = Lang.bind(this, function(win) {
let actor = win.get_compositor_private();
if (!actor)
return false;
if (!win.is_attached_dialog())
return false;
this._doAddAttachedDialog(win, actor);
win.foreach_transient(iter);
return true;
});
this.metaWindow.foreach_transient(iter);
},
// Find the actor just below us, respecting reparenting done
@ -97,25 +143,46 @@ const WindowClone = new Lang.Class({
this.actor.destroy();
},
addAttachedDialog: function(win) {
this._doAddAttachedDialog(win, win.get_compositor_private());
},
_doAddAttachedDialog: function(metaDialog, realDialog) {
let clone = new Clutter.Clone({ source: realDialog });
this._updateDialogPosition(realDialog, clone);
clone._updateId = metaDialog.connect('position-changed',
Lang.bind(this, this._updateDialogPosition, clone));
clone._destroyId = realDialog.connect('destroy', Lang.bind(this, function() {
clone.destroy();
}));
this.actor.add_child(clone);
},
_updateDialogPosition: function(realDialog, cloneDialog) {
let metaDialog = realDialog.meta_window;
let dialogRect = metaDialog.get_outer_rect();
let rect = this.metaWindow.get_outer_rect();
cloneDialog.set_position(dialogRect.x - rect.x, dialogRect.y - rect.y);
},
_onPositionChanged: function() {
let rect = this.metaWindow.get_outer_rect();
this.actor.set_position(this.realWindow.x, this.realWindow.y);
},
_disconnectRealWindowSignals: function() {
if (this._positionChangedId != 0) {
this.realWindow.disconnect(this._positionChangedId);
this._positionChangedId = 0;
}
_disconnectSignals: function() {
this.actor.get_children().forEach(function(child) {
let realWindow = child.source;
if (this._realWindowDestroyedId != 0) {
this.realWindow.disconnect(this._realWindowDestroyedId);
this._realWindowDestroyedId = 0;
}
realWindow.meta_window.disconnect(child._updateId);
realWindow.disconnect(child._destroyId);
});
},
_onDestroy: function() {
this._disconnectRealWindowSignals();
this._disconnectSignals();
this.actor._delegate = null;
@ -130,7 +197,7 @@ const WindowClone = new Lang.Class({
_onButtonRelease : function (actor, event) {
this.emit('selected', event.get_time());
return true;
return Clutter.EVENT_STOP;
},
_onDragBegin : function (draggable, time) {
@ -325,7 +392,7 @@ const WorkspaceThumbnail = new Lang.Class({
metaWin.get_compositor_private() &&
metaWin.get_workspace() == this.metaWorkspace)
this._doAddWindow(metaWin);
return false;
return GLib.SOURCE_REMOVE;
}));
return;
}
@ -343,10 +410,26 @@ const WorkspaceThumbnail = new Lang.Class({
if (this._lookupIndex (metaWin) != -1)
return;
if (!this._isMyWindow(win) || !this._isOverviewWindow(win))
if (!this._isMyWindow(win))
return;
let clone = this._addWindowClone(win);
if (this._isOverviewWindow(win)) {
this._addWindowClone(win);
} else if (metaWin.is_attached_dialog()) {
let parent = metaWin.get_transient_for();
while (parent.is_attached_dialog())
parent = metaWin.get_transient_for();
let idx = this._lookupIndex (parent);
if (idx < 0) {
// parent was not created yet, it will take care
// of the dialog when created
return;
}
let clone = this._windows[idx];
clone.addAttachedDialog(metaWin);
}
},
_windowAdded : function(metaWorkspace, metaWin) {
@ -424,8 +507,7 @@ const WorkspaceThumbnail = new Lang.Class({
// Tests if @win should be shown in the Overview
_isOverviewWindow : function (win) {
let tracker = Shell.WindowTracker.get_default();
return tracker.is_window_interesting(win.get_meta_window()) &&
return !win.get_meta_window().skip_taskbar &&
win.get_meta_window().showing_on_its_workspace();
},
@ -438,16 +520,16 @@ const WorkspaceThumbnail = new Lang.Class({
this.activate(time);
}));
clone.connect('drag-begin',
Lang.bind(this, function(clone) {
Main.overview.beginWindowDrag();
Lang.bind(this, function() {
Main.overview.beginWindowDrag(clone);
}));
clone.connect('drag-cancelled',
Lang.bind(this, function(clone) {
Main.overview.cancelledWindowDrag();
Lang.bind(this, function() {
Main.overview.cancelledWindowDrag(clone);
}));
clone.connect('drag-end',
Lang.bind(this, function(clone) {
Main.overview.endWindowDrag();
Lang.bind(this, function() {
Main.overview.endWindowDrag(clone);
}));
this._contents.add_actor(clone.actor);
@ -561,7 +643,7 @@ const ThumbnailsBox = new Lang.Class({
this._thumbnails = [];
this.actor.connect('button-press-event', function() { return true; });
this.actor.connect('button-press-event', function() { return Clutter.EVENT_STOP; });
this.actor.connect('button-release-event', Lang.bind(this, this._onButtonRelease));
Main.overview.connect('showing',
@ -606,7 +688,7 @@ const ThumbnailsBox = new Lang.Class({
}
}
return true;
return Clutter.EVENT_STOP;
},
_onDragBegin: function() {
@ -722,22 +804,25 @@ const ThumbnailsBox = new Lang.Class({
[newWorkspaceIndex, this._dropPlaceholderPos] = [this._dropPlaceholderPos, -1];
// Nab all the windows below us.
let windows = global.get_window_actors().filter(function(win) {
let windows = global.get_window_actors().filter(function(winActor) {
// If the window is attached to an ancestor, we don't need/want to move it
if (!!win.meta_window.get_transient_for())
let window = winActor.meta_window;
if (window.get_transient_for() != null)
return false;
if (isWindow)
return win.get_workspace() >= newWorkspaceIndex && win != source;
return window.get_workspace().index() >= newWorkspaceIndex && winActor != source;
else
return win.get_workspace() >= newWorkspaceIndex;
return window.get_workspace().index() >= newWorkspaceIndex;
});
this._spliceIndex = newWorkspaceIndex;
// ... move them down one.
windows.forEach(function(win) {
win.meta_window.change_workspace_by_index(win.get_workspace() + 1, true);
windows.forEach(function(winActor) {
let window = winActor.meta_window;
window.change_workspace_by_index(window.get_workspace().index() + 1, true);
});
if (isWindow)

View File

@ -23,75 +23,93 @@ const MAX_WORKSPACES = 16;
const OVERRIDE_SCHEMA = 'org.gnome.shell.overrides';
function rectEqual(one, two) {
if (one == two)
return true;
const WorkspacesViewBase = new Lang.Class({
Name: 'WorkspacesViewBase',
if (!one || !two)
return false;
return (one.x == two.x &&
one.y == two.y &&
one.width == two.width &&
one.height == two.height);
}
const WorkspacesView = new Lang.Class({
Name: 'WorkspacesView',
_init: function(workspaces) {
_init: function(monitorIndex) {
this.actor = new St.Widget({ style_class: 'workspaces-view',
reactive: true });
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
global.focus_manager.add_group(this.actor);
// The actor itself isn't a drop target, so we don't want to pick on its area
this.actor.set_size(0, 0);
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this.actor.connect('style-changed', Lang.bind(this,
function() {
let node = this.actor.get_theme_node();
this._spacing = node.get_length('spacing');
this._updateWorkspaceActors(false);
}));
this._monitorIndex = monitorIndex;
this._fullGeometry = null;
this._actualGeometry = null;
this._spacing = 0;
this._inDrag = false;
this._windowDragBeginId = Main.overview.connect('window-drag-begin', Lang.bind(this, this._dragBegin));
this._windowDragEndId = Main.overview.connect('window-drag-end', Lang.bind(this, this._dragEnd));
},
_onDestroy: function() {
this._dragEnd();
if (this._windowDragBeginId > 0) {
Main.overview.disconnect(this._windowDragBeginId);
this._windowDragBeginId = 0;
}
if (this._windowDragEndId > 0) {
Main.overview.disconnect(this._windowDragEndId);
this._windowDragEndId = 0;
}
},
_dragBegin: function(overview, clone) {
this._inDrag = true;
this._setReservedSlot(clone);
},
_dragEnd: function() {
this._inDrag = false;
this._setReservedSlot(null);
},
destroy: function() {
this.actor.destroy();
},
setFullGeometry: function(geom) {
this._fullGeometry = geom;
this._syncGeometry();
},
setActualGeometry: function(geom) {
this._actualGeometry = geom;
this._syncGeometry();
},
});
const WorkspacesView = new Lang.Class({
Name: 'WorkspacesView',
Extends: WorkspacesViewBase,
_init: function(monitorIndex) {
this.parent(monitorIndex);
this._animating = false; // tweening
this._scrolling = false; // swipe-scrolling
this._animatingScroll = false; // programatically updating the adjustment
this._inDrag = false; // dragging a window
this._settings = new Gio.Settings({ schema: OVERRIDE_SCHEMA });
this._updateExtraWorkspacesId =
this._settings.connect('changed::workspaces-only-on-primary',
Lang.bind(this, this._updateExtraWorkspaces));
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
this._workspaces = workspaces;
this.scrollAdjustment = new St.Adjustment({ value: activeWorkspaceIndex,
lower: 0,
page_increment: 1,
page_size: 1,
step_increment: 0,
upper: 0 });
this.scrollAdjustment.connect('notify::value',
Lang.bind(this, this._onScroll));
// Add workspace actors
for (let w = 0; w < global.screen.n_workspaces; w++)
this.actor.add_actor(this._workspaces[w].actor);
this._workspaces[activeWorkspaceIndex].actor.raise_top();
this._workspaces = [];
this._updateWorkspaces();
this._updateWorkspacesId = global.screen.connect('notify::n-workspaces', Lang.bind(this, this._updateWorkspaces));
this._extraWorkspaces = [];
this._updateExtraWorkspaces();
// Position/scale the desktop windows and their children after the
// workspaces have been created. This cannot be done first because
// window movement depends on the Workspaces object being accessible
// as an Overview member.
this._overviewShowingId =
Main.overview.connect('showing',
Lang.bind(this, function() {
for (let w = 0; w < this._workspaces.length; w++)
this._workspaces[w].zoomToOverview();
for (let w = 0; w < this._extraWorkspaces.length; w++)
this._extraWorkspaces[w].zoomToOverview();
}));
this._overviewShownId =
Main.overview.connect('shown',
Lang.bind(this, function() {
@ -99,72 +117,21 @@ const WorkspacesView = new Lang.Class({
this._fullGeometry.width, this._fullGeometry.height);
}));
this.scrollAdjustment = new St.Adjustment({ value: activeWorkspaceIndex,
lower: 0,
page_increment: 1,
page_size: 1,
step_increment: 0,
upper: this._workspaces.length });
this.scrollAdjustment.connect('notify::value',
Lang.bind(this, this._onScroll));
this._switchWorkspaceNotifyId =
global.window_manager.connect('switch-workspace',
Lang.bind(this, this._activeWorkspaceChanged));
this._itemDragBeginId = Main.overview.connect('item-drag-begin',
Lang.bind(this, this._dragBegin));
this._itemDragEndId = Main.overview.connect('item-drag-end',
Lang.bind(this, this._dragEnd));
this._windowDragBeginId = Main.overview.connect('window-drag-begin',
Lang.bind(this, this._dragBegin));
this._windowDragEndId = Main.overview.connect('window-drag-end',
Lang.bind(this, this._dragEnd));
},
_updateExtraWorkspaces: function() {
this._destroyExtraWorkspaces();
if (!this._settings.get_boolean('workspaces-only-on-primary'))
return;
let monitors = Main.layoutManager.monitors;
for (let i = 0; i < monitors.length; i++) {
if (i == Main.layoutManager.primaryIndex)
continue;
let ws = new Workspace.Workspace(null, i);
ws.setFullGeometry(monitors[i]);
ws.setActualGeometry(monitors[i]);
Main.layoutManager.overviewGroup.add_actor(ws.actor);
this._extraWorkspaces.push(ws);
}
},
_destroyExtraWorkspaces: function() {
for (let m = 0; m < this._extraWorkspaces.length; m++)
this._extraWorkspaces[m].destroy();
this._extraWorkspaces = [];
},
setFullGeometry: function(geom) {
if (rectEqual(this._fullGeometry, geom))
return;
this._fullGeometry = geom;
_setReservedSlot: function(clone) {
for (let i = 0; i < this._workspaces.length; i++)
this._workspaces[i].setFullGeometry(geom);
this._workspaces[i].setReservedSlot(clone);
},
setActualGeometry: function(geom) {
if (rectEqual(this._actualGeometry, geom))
return;
this._actualGeometry = geom;
_syncGeometry: function() {
for (let i = 0; i < this._workspaces.length; i++)
this._workspaces[i].setActualGeometry(geom);
this._workspaces[i].setFullGeometry(this._fullGeometry);
for (let i = 0; i < this._workspaces.length; i++)
this._workspaces[i].setActualGeometry(this._actualGeometry);
},
getActiveWorkspace: function() {
@ -172,29 +139,22 @@ const WorkspacesView = new Lang.Class({
return this._workspaces[active];
},
hide: function() {
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
let activeWorkspace = this._workspaces[activeWorkspaceIndex];
activeWorkspace.actor.raise_top();
zoomToOverview: function() {
for (let w = 0; w < this._workspaces.length; w++)
this._workspaces[w].zoomToOverview();
this._updateWorkspaceActors(false);
},
zoomFromOverview: function() {
this.actor.remove_clip();
for (let w = 0; w < this._workspaces.length; w++)
this._workspaces[w].zoomFromOverview();
for (let w = 0; w < this._extraWorkspaces.length; w++)
this._extraWorkspaces[w].zoomFromOverview();
},
destroy: function() {
this.actor.destroy();
},
syncStacking: function(stackIndices) {
for (let i = 0; i < this._workspaces.length; i++)
this._workspaces[i].syncStacking(stackIndices);
for (let i = 0; i < this._extraWorkspaces.length; i++)
this._extraWorkspaces[i].syncStacking(stackIndices);
},
_scrollToActive: function() {
@ -216,7 +176,7 @@ const WorkspacesView = new Lang.Class({
Tweener.removeTweens(workspace.actor);
let y = (w - active) * (this._fullGeometry.height + this._spacing);
let y = (w - active) * this._fullGeometry.height;
if (showAnimation) {
let params = { y: y,
@ -276,25 +236,34 @@ const WorkspacesView = new Lang.Class({
});
},
updateWorkspaces: function(oldNumWorkspaces, newNumWorkspaces) {
let active = global.screen.get_active_workspace_index();
_updateWorkspaces: function() {
let newNumWorkspaces = global.screen.n_workspaces;
Tweener.addTween(this.scrollAdjustment,
{ upper: newNumWorkspaces,
time: WORKSPACE_SWITCH_TIME,
transition: 'easeOutQuad'
});
this.scrollAdjustment.upper = newNumWorkspaces;
if (newNumWorkspaces > oldNumWorkspaces) {
for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
this._workspaces[w].setFullGeometry(this._fullGeometry);
if (this._actualGeometry)
this._workspaces[w].setActualGeometry(this._actualGeometry);
this.actor.add_actor(this._workspaces[w].actor);
let needsUpdate = false;
for (let j = 0; j < newNumWorkspaces; j++) {
let metaWorkspace = global.screen.get_workspace_by_index(j);
let workspace;
if (j >= this._workspaces.length) { /* added */
workspace = new Workspace.Workspace(metaWorkspace, this._monitorIndex);
this.actor.add_actor(workspace.actor);
this._workspaces[j] = workspace;
} else {
workspace = this._workspaces[j];
if (workspace.metaWorkspace != metaWorkspace) { /* removed */
workspace.destroy();
this._workspaces.splice(j, 1);
} /* else kept */
}
this._updateWorkspaceActors(false);
}
if (this._fullGeometry)
this._updateWorkspaceActors(false);
this._syncGeometry();
},
_activeWorkspaceChanged: function(wm, from, to, direction) {
@ -305,70 +274,12 @@ const WorkspacesView = new Lang.Class({
},
_onDestroy: function() {
this._destroyExtraWorkspaces();
this.parent();
this.scrollAdjustment.run_dispose();
Main.overview.disconnect(this._overviewShowingId);
Main.overview.disconnect(this._overviewShownId);
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
this._settings.disconnect(this._updateExtraWorkspacesId);
if (this._inDrag)
this._dragEnd();
if (this._itemDragBeginId > 0) {
Main.overview.disconnect(this._itemDragBeginId);
this._itemDragBeginId = 0;
}
if (this._itemDragEndId > 0) {
Main.overview.disconnect(this._itemDragEndId);
this._itemDragEndId = 0;
}
if (this._windowDragBeginId > 0) {
Main.overview.disconnect(this._windowDragBeginId);
this._windowDragBeginId = 0;
}
if (this._windowDragEndId > 0) {
Main.overview.disconnect(this._windowDragEndId);
this._windowDragEndId = 0;
}
},
_dragBegin: function() {
if (this._scrolling)
return;
this._inDrag = true;
this._firstDragMotion = true;
this._dragMonitor = {
dragMotion: Lang.bind(this, this._onDragMotion)
};
DND.addDragMonitor(this._dragMonitor);
},
_onDragMotion: function(dragEvent) {
if (Main.overview.animationInProgress)
return DND.DragMotionResult.CONTINUE;
if (this._firstDragMotion) {
this._firstDragMotion = false;
for (let i = 0; i < this._workspaces.length; i++)
this._workspaces[i].setReservedSlot(dragEvent.dragActor._delegate);
for (let i = 0; i < this._extraWorkspaces.length; i++)
this._extraWorkspaces[i].setReservedSlot(dragEvent.dragActor._delegate);
}
return DND.DragMotionResult.CONTINUE;
},
_dragEnd: function() {
DND.removeDragMonitor(this._dragMonitor);
this._inDrag = false;
for (let i = 0; i < this._workspaces.length; i++)
this._workspaces[i].setReservedSlot(null);
for (let i = 0; i < this._extraWorkspaces.length; i++)
this._extraWorkspaces[i].setReservedSlot(null);
global.screen.disconnect(this._updateWorkspacesId);
},
startSwipeScroll: function() {
@ -427,12 +338,58 @@ const WorkspacesView = new Lang.Class({
});
Signals.addSignalMethods(WorkspacesView.prototype);
const ExtraWorkspaceView = new Lang.Class({
Name: 'ExtraWorkspaceView',
Extends: WorkspacesViewBase,
_init: function(monitorIndex) {
this.parent(monitorIndex);
this._workspace = new Workspace.Workspace(null, monitorIndex);
this.actor.add_actor(this._workspace.actor);
},
_setReservedSlot: function(clone) {
this._workspace.setReservedSlot(clone);
},
_syncGeometry: function() {
this._workspace.setFullGeometry(this._fullGeometry);
this._workspace.setActualGeometry(this._actualGeometry);
},
zoomToOverview: function() {
this._workspace.zoomToOverview();
},
zoomFromOverview: function() {
this._workspace.zoomFromOverview();
},
syncStacking: function(stackIndices) {
this._workspace.syncStacking(stackIndices);
},
startSwipeScroll: function() {
},
endSwipeScroll: function() {
},
});
const DelegateFocusNavigator = new Lang.Class({
Name: 'DelegateFocusNavigator',
Extends: St.Widget,
vfunc_navigate_focus: function(from, direction) {
return this._delegate.navigateFocus(from, direction);
},
});
const WorkspacesDisplay = new Lang.Class({
Name: 'WorkspacesDisplay',
_init: function() {
this.actor = new St.Widget({ clip_to_allocation: true });
this.actor = new DelegateFocusNavigator({ clip_to_allocation: true });
this.actor._delegate = this;
this.actor.connect('notify::allocation', Lang.bind(this, this._updateWorkspacesActualGeometry));
this.actor.connect('parent-set', Lang.bind(this, this._parentSet));
@ -448,7 +405,7 @@ const WorkspacesDisplay = new Lang.Class({
Main.overview.addAction(clickAction);
this.actor.bind_property('mapped', clickAction, 'enabled', GObject.BindingFlags.SYNC_CREATE);
let panAction = new Clutter.PanAction();
let panAction = new Clutter.PanAction({ threshold_trigger_edge: Clutter.GestureTriggerEdge.AFTER });
panAction.connect('pan', Lang.bind(this, this._onPan));
panAction.connect('gesture-begin', Lang.bind(this, function() {
for (let i = 0; i < this._workspacesViews.length; i++)
@ -456,7 +413,6 @@ const WorkspacesDisplay = new Lang.Class({
return true;
}));
panAction.connect('gesture-cancel', Lang.bind(this, function() {
clickAction.release();
for (let i = 0; i < this._workspacesViews.length; i++)
this._workspacesViews[i].endSwipeScroll();
}));
@ -471,7 +427,6 @@ const WorkspacesDisplay = new Lang.Class({
this._primaryIndex = Main.layoutManager.primaryIndex;
this._workspacesViews = [];
this._workspaces = [];
this._primaryScrollAdjustment = null;
this._settings = new Gio.Settings({ schema: OVERRIDE_SCHEMA });
@ -480,9 +435,6 @@ const WorkspacesDisplay = new Lang.Class({
this._workspacesOnlyOnPrimaryChanged));
this._workspacesOnlyOnPrimaryChanged();
global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._workspacesChanged));
this._switchWorkspaceNotifyId = 0;
this._notifyOpacityId = 0;
@ -498,8 +450,14 @@ const WorkspacesDisplay = new Lang.Class({
return false;
},
navigateFocus: function(from, direction) {
return this._getPrimaryView().actor.navigate_focus(from, direction, false);
},
show: function() {
this._updateWorkspacesViews();
for (let i = 0; i < this._workspacesViews.length; i++)
this._workspacesViews[i].zoomToOverview();
this._restackedNotifyId =
Main.overview.connect('windows-restacked',
@ -509,9 +467,8 @@ const WorkspacesDisplay = new Lang.Class({
},
zoomFromOverview: function() {
for (let i = 0; i < this._workspacesViews.length; i++) {
this._workspacesViews[i].hide();
}
for (let i = 0; i < this._workspacesViews.length; i++)
this._workspacesViews[i].zoomFromOverview();
},
hide: function() {
@ -543,46 +500,38 @@ const WorkspacesDisplay = new Lang.Class({
this._workspacesViews[i].destroy();
this._workspacesViews = [];
this._workspaces = [];
let monitors = Main.layoutManager.monitors;
for (let i = 0; i < monitors.length; i++) {
let view;
if (this._workspacesOnlyOnPrimary && i != this._primaryIndex)
continue; // we are only interested in the primary monitor
view = new ExtraWorkspaceView(i);
else
view = new WorkspacesView(i);
let monitorWorkspaces = [];
for (let w = 0; w < global.screen.n_workspaces; w++) {
let metaWorkspace = global.screen.get_workspace_by_index(w);
monitorWorkspaces.push(new Workspace.Workspace(metaWorkspace, i));
}
this._workspaces.push(monitorWorkspaces);
let view = new WorkspacesView(monitorWorkspaces);
view.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
if (this._workspacesOnlyOnPrimary || i == this._primaryIndex) {
if (i == this._primaryIndex) {
this._scrollAdjustment = view.scrollAdjustment;
this._scrollAdjustment.connect('notify::value',
Lang.bind(this, this._scrollValueChanged));
}
this._workspacesViews.push(view);
Main.layoutManager.overviewGroup.add_actor(view.actor);
}
this._updateWorkspacesFullGeometry();
this._updateWorkspacesActualGeometry();
for (let i = 0; i < this._workspacesViews.length; i++)
Main.layoutManager.overviewGroup.add_actor(this._workspacesViews[i].actor);
},
_scrollValueChanged: function() {
if (this._workspacesOnlyOnPrimary)
return;
for (let i = 0; i < this._workspacesViews.length; i++) {
if (i == this._primaryIndex)
continue;
let adjustment = this._workspacesViews[i].scrollAdjustment;
if (!adjustment)
continue;
// the adjustments work in terms of workspaces, so the
// values map directly
adjustment.value = this._scrollAdjustment.value;
@ -592,10 +541,7 @@ const WorkspacesDisplay = new Lang.Class({
_getPrimaryView: function() {
if (!this._workspacesViews.length)
return null;
if (this._workspacesOnlyOnPrimary)
return this._workspacesViews[0];
else
return this._workspacesViews[this._primaryIndex];
return this._workspacesViews[this._primaryIndex];
},
activeWorkspaceHasMaximizedWindows: function() {
@ -641,15 +587,9 @@ const WorkspacesDisplay = new Lang.Class({
return;
let monitors = Main.layoutManager.monitors;
let m = 0;
for (let i = 0; i < monitors.length; i++) {
if (i == this._primaryIndex) {
this._workspacesViews[m].setFullGeometry(this._fullGeometry);
m++;
} else if (!this._workspacesOnlyOnPrimary) {
this._workspacesViews[m].setFullGeometry(monitors[i]);
m++;
}
let geometry = (i == this._primaryIndex) ? this._fullGeometry : monitors[i];
this._workspacesViews[i].setFullGeometry(geometry);
}
},
@ -660,18 +600,12 @@ const WorkspacesDisplay = new Lang.Class({
let [x, y] = this.actor.get_transformed_position();
let width = this.actor.allocation.x2 - this.actor.allocation.x1;
let height = this.actor.allocation.y2 - this.actor.allocation.y1;
let geometry = { x: x, y: y, width: width, height: height };
let primaryGeometry = { x: x, y: y, width: width, height: height };
let monitors = Main.layoutManager.monitors;
let m = 0;
for (let i = 0; i < monitors.length; i++) {
if (i == this._primaryIndex) {
this._workspacesViews[m].setActualGeometry(geometry);
m++;
} else if (!this._workspacesOnlyOnPrimary) {
this._workspacesViews[m].setActualGeometry(monitors[i]);
m++;
}
let geometry = (i == this._primaryIndex) ? primaryGeometry : monitors[i];
this._workspacesViews[i].setActualGeometry(geometry);
}
},
@ -680,63 +614,9 @@ const WorkspacesDisplay = new Lang.Class({
this._workspacesViews[i].syncStacking(stackIndices);
},
_workspacesChanged: function() {
if (!this._workspacesViews.length)
return;
let oldNumWorkspaces = this._workspaces[0].length;
let newNumWorkspaces = global.screen.n_workspaces;
let active = global.screen.get_active_workspace_index();
let lostWorkspaces = [];
if (newNumWorkspaces > oldNumWorkspaces) {
let monitors = Main.layoutManager.monitors;
let m = 0;
for (let i = 0; i < monitors.length; i++) {
if (this._workspacesOnlyOnPrimary &&
i != this._primaryIndex)
continue;
// Assume workspaces are only added at the end
for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
let metaWorkspace = global.screen.get_workspace_by_index(w);
this._workspaces[m][w] =
new Workspace.Workspace(metaWorkspace, i);
}
m++;
}
} else {
// Assume workspaces are only removed sequentially
// (e.g. 2,3,4 - not 2,4,7)
let removedIndex;
let removedNum = oldNumWorkspaces - newNumWorkspaces;
for (let w = 0; w < oldNumWorkspaces; w++) {
let metaWorkspace = global.screen.get_workspace_by_index(w);
if (this._workspaces[0][w].metaWorkspace != metaWorkspace) {
removedIndex = w;
break;
}
}
for (let i = 0; i < this._workspaces.length; i++) {
lostWorkspaces = this._workspaces[i].splice(removedIndex,
removedNum);
for (let l = 0; l < lostWorkspaces.length; l++) {
lostWorkspaces[l].disconnectAll();
lostWorkspaces[l].destroy();
}
}
}
for (let i = 0; i < this._workspacesViews.length; i++)
this._workspacesViews[i].updateWorkspaces(oldNumWorkspaces,
newNumWorkspaces);
},
_onScrollEvent: function(actor, event) {
if (!this.actor.mapped)
return false;
return Clutter.EVENT_PROPAGATE;
let activeWs = global.screen.get_active_workspace();
let ws;
switch (event.get_scroll_direction()) {
@ -747,10 +627,10 @@ const WorkspacesDisplay = new Lang.Class({
ws = activeWs.get_neighbor(Meta.MotionDirection.DOWN);
break;
default:
return false;
return Clutter.EVENT_PROPAGATE;
}
Main.wm.actionMoveWorkspace(ws);
return true;
return Clutter.EVENT_STOP;
}
});
Signals.addSignalMethods(WorkspacesDisplay.prototype);

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