Compare commits

...

950 Commits
3.4.1 ... 3.6.0

Author SHA1 Message Date
8f2a6f8387 Bump version to 3.6.0
Update NEWS
2012-09-25 12:59:31 +02:00
908bf3b117 volume: Fix initial visibility of input
Currently the visibility of input volume is only updated when a stream
is added/removed - apparently no one noticed until now, as in the normal
user session we get away with this as long as we have some startup sound,
but this is not the case in the lock screen, so we may end up showing
input volume incorrectly.

https://bugzilla.gnome.org/show_bug.cgi?id=684611
2012-09-25 12:02:06 +02:00
6a739afd25 gdm: Make SessionList accessible
https://bugzilla.gnome.org/show_bug.cgi?id=684748
2012-09-25 12:02:06 +02:00
506490e32d Updated Hebrew translation. 2012-09-25 11:31:42 +02:00
846771f2a1 Updated Hebrew translation. 2012-09-25 11:31:36 +02:00
759b7584e0 hindi update 2012-09-25 14:39:53 +05:30
e5cb224598 Updated Arabic translation 2012-09-25 11:06:42 +02:00
e32df1b405 Updated Galician translations 2012-09-25 10:48:11 +02:00
db9f91b687 Updated Spanish translation 2012-09-25 10:27:16 +02:00
0941357068 Updated Greek translation 2012-09-25 11:22:01 +03:00
d8993c52d0 Updated Latvian translation 2012-09-25 10:45:06 +03:00
6424b2dd03 Assamese translation updated 2012-09-25 12:29:55 +05:30
2a4eb3ed1a Updated Slovenian translation 2012-09-25 08:58:03 +02:00
c9fa00cce1 Updated Portuguese translation 2012-09-25 07:59:40 +01:00
18eedbc02d keyboard: Disable "tray" button in lock/login screen
It is not possible to summon the tray via shortcut or dwelling
while the screen is locked, so it is odd to allow it from the
on-screen-keyboard.

https://bugzilla.gnome.org/show_bug.cgi?id=683546
2012-09-25 08:25:26 +02:00
ef9f63fe59 keyboard: Ignore focus changes from extended keys
The keyboard is shown/hidden automatically when (un)focusing a
ClutterText actor. This behavior is unwanted when opening the
extended keys popup, so focus changes to the popup are ignored.
However, we also want to ignore focus changes from the popup
to avoid the keyboard hiding itself after pressing an extended
key.

https://bugzilla.gnome.org/show_bug.cgi?id=683546
2012-09-25 08:25:26 +02:00
f8ce788425 keyboard: Fix check for extended keys
The existing check tested for non-existent properties.

https://bugzilla.gnome.org/show_bug.cgi?id=683546
2012-09-25 08:25:25 +02:00
6c1bd95643 keyboard: Ignore focus changes caused by tray showing/hiding
The keyboard is shown/hidden automatically when (un)focusing a
ClutterText actor. This behavior breaks with the message tray now
grabbing/releasing key focus when toggled. Fix this by ignoring
all focus changes to or from the message tray.

https://bugzilla.gnome.org/show_bug.cgi?id=683546
2012-09-25 08:25:25 +02:00
2ed7ee8f71 keyboard: Keep tray after clicking summary item
Currently if a summary item signals that it has handled a click
itself, the tray hides itself. This behavior is wrong for the
On-Screen-Keyboard, which appears as a unit with the tray, so add
a property to opt-out of the default behavior.

https://bugzilla.gnome.org/show_bug.cgi?id=683546
2012-09-25 08:25:25 +02:00
2e63709450 grabHelper: Ignore events from On-Screen-Keyboard
GrabHelper automatically releases grabs when the user clicks outside
the grabbed actors. However at least for the message-tray (which is
the only user of grabHelper at the moment), we must ignore any events
from the On-Screen-Keyboard, to prevent the tray from hiding at every
key press.

https://bugzilla.gnome.org/show_bug.cgi?id=683546
2012-09-25 08:25:24 +02:00
6611d639a8 messageTray: Only update keyboardVisible as necessary
This fixes a case of _updateState() being called recursively,
resulting in stray grab()/ungrab() calls the leave the entire
desktop in a stuck focus state.

https://bugzilla.gnome.org/show_bug.cgi?id=683546
2012-09-25 08:25:24 +02:00
fe124e6ab3 Keyboard: update for the message tray changes
The message tray is now modal and pushes the view up, but the keyboard
is shown below it. Solve this by applying a special styling to the
keyboard and message tray combination, and by not pushing the windows
up when the keyboard is shown.

https://bugzilla.gnome.org/show_bug.cgi?id=683546
2012-09-25 08:25:24 +02:00
fee0a8527d Updated Marathi Translations 2012-09-25 09:03:28 +05:30
2f6b00403f Updated Indonesian translation 2012-09-25 09:55:32 +07:00
dbe2c117e3 update Punjabi Translation 2012-09-25 06:46:18 +05:30
c4b1ccb6d6 Updated Brazilian Portuguese Translation 2012-09-24 21:58:42 -03:00
2c70ee7e43 [l10n] Update Japanese translation 2012-09-25 09:35:23 +09:00
f7826616b8 gdm: Make UserList accessible
https://bugzilla.gnome.org/show_bug.cgi?id=684728
2012-09-25 02:06:02 +02:00
9f476a12dd gdm: Provide an accessible name for powerMenu
https://bugzilla.gnome.org/show_bug.cgi?id=684727
2012-09-25 02:06:02 +02:00
6f3cf0ae50 userMenu: Force reload of background-image on icon changes
When changing the user's avatar image, AccountsService will
overwrite the old image with the new one, so the location
returned by get_icon_file() is always the same.
In order to pick up the change, we need to make sure to clear the
previous image from both StTextureCache and StThemeNode's paint
cache.

https://bugzilla.gnome.org/show_bug.cgi?id=679268
2012-09-24 22:03:06 +02:00
cd024e21f0 st-widget: Add method to clear background-image
For performance reasons, resources required to paint a widget are
aggressively cached; we know of at least one case where our caching
prevents updating the used background-image correctly, so add explicit
API to clear all associated cache data.

https://bugzilla.gnome.org/show_bug.cgi?id=679268
2012-09-24 22:03:05 +02:00
c4c470c1f3 st-theme-node: Add method to invalidate drawing state
StThemeNode caches its resources aggressively to keep the required
work on paint to a minimum - right now, resources are only recreated
on allocation changes.
In order to update the background-image property correctly when the
underlying file changes, resources need to be recreated without a
size change, so add an explicit method for that.

https://bugzilla.gnome.org/show_bug.cgi?id=679268
2012-09-24 22:03:05 +02:00
dc9ad8df80 st-texture-cache: Add API to remove cache data
The current API assumes that image data loaded from files remains
valid during the life time of the shell. This assumption is mostly
valid for image files we provide ourselves (with the exception being
designers working on those files), but not necessarily for "external"
files - provide API to explicitly remove cached data associated with
a URI for those cases.

https://bugzilla.gnome.org/show_bug.cgi?id=679268
2012-09-24 22:03:04 +02:00
74d6225993 Update Czech translation 2012-09-24 18:31:14 +02:00
e1e0c5035d Updated Traditional Chinese translation(Hong Kong and Taiwan) 2012-09-24 22:58:55 +08:00
adf6d0eb82 Update Czech translation by Adam Matousek 2012-09-24 15:43:00 +02:00
90df435345 Added Kyrgyz translation 2012-09-24 14:34:08 +06:00
83d57211db Added 'ky' to LINGUAS 2012-09-24 14:33:12 +06:00
0a8eeb2827 [l10n] Updated Estonian translation 2012-09-24 10:42:16 +03:00
0c324c42f4 [l10n] Update Japanese translation 2012-09-24 11:45:40 +09:00
3adf54a952 Updated Latvian translation 2012-09-23 20:23:09 +03:00
8076c66a4c Updated Russian translation 2012-09-23 20:32:14 +04:00
f9c583a636 Updated Russian translation 2012-09-23 19:50:31 +04:00
30d536b19c hindi update 2012-09-23 20:46:41 +05:30
1957899146 [l10n] Updated German translation 2012-09-23 12:31:15 +02:00
b52c83d88a [l10n]Updated Catalan (Valencian) translation 2012-09-23 00:21:06 +02:00
27ff388413 [l10n] Updated Catalan translation 2012-09-23 00:21:00 +02:00
d4306f7768 update Simplified Chinese (zh_CN) translation 2012-09-23 04:35:44 +08:00
5d0a57c97d Finnish translation update by Jiri Grönroos 2012-09-22 21:54:22 +03:00
a87ba467ae messageTray: Don't open the tray from a dwell if we're in a modal grab
If we're in an alt-tab popup or modal dialog, we shouldn't pop up the tray
at all.

https://bugzilla.gnome.org/show_bug.cgi?id=684458
2012-09-22 15:39:22 -03:00
7df7cd01eb messageTray: Clean up commented code a bit
Since we changed the grabHelper.grab call to be in a conditional,
it's been a little clear what the comment has been referring to.

https://bugzilla.gnome.org/show_bug.cgi?id=684458
2012-09-22 15:39:22 -03:00
098d805a8b [l10n] Updated Italian translation. 2012-09-22 16:54:29 +02:00
4a21034a00 Updated Hebrew translation. 2012-09-22 15:06:30 +03:00
7904e359f2 Updated Hebrew translation. 2012-09-22 15:06:07 +03:00
0c8a94beb8 screenShield: Fix unlock animation
When unlocking succeeds, the transition back to the user session is
animated. However, commit 8cf9baa1 broke the transition by hiding the
actor before starting the animation.

https://bugzilla.gnome.org/show_bug.cgi?id=684591
2012-09-22 13:50:25 +02:00
c13a573792 Updated Danish translation 2012-09-22 12:27:49 +02:00
f9b42e12ae Updated British English translation 2012-09-22 11:18:23 +01:00
5c2031b768 Updated Bulgarian translation 2012-09-22 12:52:52 +03:00
49643882d4 Updated Korean translation 2012-09-22 18:50:34 +09:00
175ddaa3a1 searchDisplay: Fix alignment of section headers in RTL locales
https://bugzilla.gnome.org/show_bug.cgi?id=684379
2012-09-21 21:03:52 +02:00
cc1d6e97b8 Updated Brazilian Portuguese Translation 2012-09-21 15:26:12 -03:00
79b96ab301 Updated Lithuanian translation 2012-09-21 20:53:34 +03:00
44d9316023 Updated Belarusian translation. 2012-09-21 20:02:55 +03:00
5b0c9a74fb Updated Spanish translation 2012-09-21 17:52:32 +02:00
3429fc3e4c userMenu: Close menu immediately on user/session switch
The same reasoning as for commit 1f30670c1d also applies to
VT switches.

https://bugzilla.gnome.org/show_bug.cgi?id=684459
2012-09-21 16:07:38 +02:00
d11d8d5353 Updated translation for Odia. 2012-09-21 17:34:09 +05:30
1742bd6ded Updated Galician translations 2012-09-21 13:18:37 +02:00
9f6e118ea0 Updated Portuguese translation 2012-09-20 22:19:24 +01:00
717bbd3bb5 Updated Serbian translation 2012-09-20 20:23:24 +02:00
e8ebe4de14 popupMenu: Set initial visibility of settings items
With the recent session mode changes, the visibility of settings
items is now only set on sessionMode::updated - while the signal
is emitted when the session mode is initialized, settings items
that are added after that are visible regardless of the allowSettings
setting until the next sessionMode::updated signal is received.
Fix this by explicitly setting the initial visibility of settings
items.

https://bugzilla.gnome.org/show_bug.cgi?id=684473
2012-09-20 20:04:05 +02:00
0ff614ccd4 Don't show network dialogs in the lock screen
Remove the network agent component from the lock screen and unlock
dialog session modes.

https://bugzilla.gnome.org/show_bug.cgi?id=684384
2012-09-20 20:03:23 +02:00
d0a77b7e0c keyboard: Make input source items accessible
https://bugzilla.gnome.org/show_bug.cgi?id=684462
2012-09-20 19:59:27 +02:00
2815889090 Updated Malayalam file 2012-09-20 22:57:20 +05:30
1317956663 Updated Malayalam file 2012-09-20 21:40:54 +05:30
a2303d8895 Updated Malayalam file 2012-09-20 21:39:01 +05:30
2161c793dc Updated Hungarian translation 2012-09-20 14:56:16 +02:00
a2f943db8f Updated Galician translations 2012-09-20 13:20:04 +02:00
23a31b9c00 Update French translation 2012-09-20 12:25:57 +02:00
2591d1b94b Updated Thai translation 2012-09-20 16:23:00 +07:00
300f97f102 Updated Greek translation 2012-09-20 11:10:59 +03:00
7e3f6c3066 Updated Marathi Translations 2012-09-20 12:34:06 +05:30
601d232064 Updated Arabic translation 2012-09-20 06:55:05 +02:00
8854ac84ad Updated Arabic translation 2012-09-20 06:45:35 +02:00
0302c3fbd4 Updated HINDI translation 2012-09-20 09:53:23 +05:30
6b6fdc6cfe Assamese translation updated 2012-09-20 00:14:33 +05:30
7126ce86ec Updated Bulgarian translation 2012-09-19 20:42:24 +03:00
760bf52f75 update Punjabi Translation 2012-09-19 21:27:02 +05:30
6d791d31de Updated Polish translation 2012-09-19 16:42:35 +02:00
21adc98d70 updated Tamil translation 2012-09-19 19:46:23 +05:30
6fd7a56568 Updated Slovenian translation 2012-09-19 13:47:04 +02:00
fba427fcaf Bump version to 3.5.92
Update NEWS
2012-09-19 13:12:02 +02:00
7753361ffb shell-docs: Remove left-over includes
Clean out a couple of references to code that was removed or
marked private.
2012-09-19 13:12:02 +02:00
c1590d9ed7 Revert "messageTray: Fix summary position in RTL locales"
This reverts commit e6ba7c6e40.
The original issue is fixed by Clutter commit 64c7973c7429c.
2012-09-19 13:12:02 +02:00
23c1138a58 screenShield: Fix regression from 114f6f577f 2012-09-19 13:12:01 +02:00
2acb097662 grabHelper: Fix regression for dwelling with mouse down
b203a95a78 introduced a regression
where we forgot to bail out if the pushModal didn't succeed properly.

https://bugzilla.gnome.org/show_bug.cgi?id=684344
2012-09-19 08:10:12 -03:00
449d116af2 Fix 10884ef7f5
Bad rebase and code left in that had no effect.
2012-09-19 13:01:56 +02:00
110240981d Updated Telugu Translation 2012-09-19 16:22:35 +05:30
ac5c6de929 Updated Indonesian translation 2012-09-19 17:33:45 +07:00
51bdc44352 gdm: Fix property typo
Introduced by cc6744055f
2012-09-19 12:11:25 +02:00
bafe34696d build: Install keybinding files for control-center
As some keybindings are now provided by gnome-shell rather than
mutter, it makes sense to expose those in System Settings.

https://bugzilla.gnome.org/show_bug.cgi?id=671010
2012-09-19 11:50:57 +02:00
114f6f577f screenShield: Delay destruction of unlock dialog
While the unlock dialog is created early so that it appears below
the shield while the curtain slides up, it is destroyed immediately
when the shield slides back in.
Keep it around until the shield is down instead.

https://bugzilla.gnome.org/show_bug.cgi?id=684342
2012-09-19 11:50:57 +02:00
1f30670c1d userMenu: Close menu immediately when activating Lock
When locking the screen, the user menu is moved to the opposite
side. Unless we close the menu immediately without animation, the
menu will jump to the other side and fade out while the screen
shield slides down.

https://bugzilla.gnome.org/show_bug.cgi?id=684343
2012-09-19 11:50:57 +02:00
10884ef7f5 ShellUserVerifier: catch DBus errors and report them to the user
Instead of leaving the login or unlock dialogs in an inconsistent state,
catch DBus errors and show an Authentication Error message. The error
details are logged in the session logs.

https://bugzilla.gnome.org/show_bug.cgi?id=683060
2012-09-19 11:50:57 +02:00
5e12e5f42a layout: Add a fake root pixmap actor at startup, and fade it out
This provides us with a smooth transition between plymouth and gdm.

https://bugzilla.gnome.org/show_bug.cgi?id=682428
2012-09-19 11:45:24 +02:00
e7a2e1f268 Updated Thai translation 2012-09-19 12:21:44 +07:00
cc6744055f gdm: Don't fade in the log in dialog
Instead, directly show it.

https://bugzilla.gnome.org/show_bug.cgi?id=682428
2012-09-19 01:41:17 -03:00
e6ba7c6e40 messageTray: Fix summary position in RTL locales
https://bugzilla.gnome.org/show_bug.cgi?id=684214
2012-09-19 03:44:49 +02:00
ea4855e908 Updated Galician translations 2012-09-19 02:47:41 +02:00
5c7da4b0e6 status/keyboard: Fix hidden separator when menu changes while open
Due to the way the IBus API works we might get property changes while
the menu is already open. In that case the separator visibility logic
doesn't work since it only applies on menu open/close. This works
around that issue.

https://bugzilla.gnome.org/show_bug.cgi?id=682314
2012-09-18 21:32:37 +02:00
9659d934ac status/keyboard: Add menu items for IBus Anthy's InputMode, TypingMode
IBus has a properties API which are basically generic knobs into the
engine which are serialized and presented in a way that allows us to
easily build actionable UI elements with them.

Instead of implementing the whole generic system and accepting
everything coming out of the engines, for now, this patch just adds
support for a couple of important IBus Anthy properties.

https://bugzilla.gnome.org/show_bug.cgi?id=682314
2012-09-18 21:32:37 +02:00
de93677271 Allow the shell to run without the screenshield
The screenshield requires gdm 3.5, which can be problematic in
jhbuild configurations, or distributions that don't use GDM as the display
manager. Allow transparent fallback to gnome-screensaver in that case.

https://bugzilla.gnome.org/show_bug.cgi?id=683060
2012-09-18 20:25:09 +02:00
6ef3c628e6 messageTray: Move focus ourselves when entering by keybinding
When the tray is triggered by keybinding rather than dwelling, the
first summary item should be given key focus. Currently this is
achieved by grabbing the focus before toggling the tray, so that
the grabHelper will move the focus for us. However this interferes
with the grabHelper's focus save/restore mechanism - for instance,
after using the keybinding once, the tray will always come up with
the first item focused.

https://bugzilla.gnome.org/show_bug.cgi?id=682243
2012-09-18 18:45:42 +02:00
f2af0be9ac messageTray: Allow to start keynav with Tab
Currently it is only possible to use keynav inside the tray if it
has been triggered with the keyboard shortcut. Make it possible to
initiate keynav by hitting Tab in other cases as well.

https://bugzilla.gnome.org/show_bug.cgi?id=682243
2012-09-18 18:45:41 +02:00
ef7b74a104 grabHelper: Remove support for untracked grabs
https://bugzilla.gnome.org/show_bug.cgi?id=682243
2012-09-18 18:45:41 +02:00
906368d916 messageTray: Add shortcuts to summary boxpointer
Currently opening the summary boxpointer acts as a stop gap for
keynav - the only shortcut still working is "Escape" to hide the
tray altogether.
Change the handling of Escape to only close the summary boxpointer
and allow to use the down arrow as alternative (unless the boxpointer
already processes the key press itself of course, like the chat
entry does). Also add a Delete shortcut to dismiss the open summary
item.

https://bugzilla.gnome.org/show_bug.cgi?id=682243
2012-09-18 18:45:41 +02:00
809cbf58c6 grabHelper: Ungrab the entire stack on "outside clicks"
Currently clicks outside the grabbed actors are handled the same as
the user pressing Escape - a single actor is popped from the grab stack.
However according to the design, outside clicks should release all grabs.

https://bugzilla.gnome.org/show_bug.cgi?id=682243
2012-09-18 18:45:41 +02:00
a5d60050a2 messageTray: Allow to open summary item with up arrow
When using keynav in the top bar, menus may be opened using the
down arrow; in a similar fashion, allow to open the summary
boxpointer with the up arrow.

https://bugzilla.gnome.org/show_bug.cgi?id=682243
2012-09-18 18:45:40 +02:00
2c130c8668 history: Allow events to bubble up when arrowing past the first/last item
Currently the HistoryManager consumes all arrow up/down key presses
unconditionally. Change this to only consume the event if the entry
text was actually changed, e.g. not when trying to move past the
first/last item.

https://bugzilla.gnome.org/show_bug.cgi?id=682243
2012-09-18 16:44:08 +02:00
c0ff02d9c7 Updated Spanish translation 2012-09-18 16:36:42 +02:00
cb627db392 Updated Greek translation 2012-09-18 15:59:15 +03:00
33bd6bc75b Updated gujarati file 2012-09-18 17:00:22 +05:30
ea5bf109cb hindi update 2012-09-18 16:06:01 +05:30
b150a2842e hindi update 2012-09-18 15:59:55 +05:30
2a46c39019 hindi update 2012-09-18 15:17:39 +05:30
5ca5f026c5 Assamese translation updated 2012-09-18 14:56:39 +05:30
15f22add79 Updated Portuguese translation 2012-09-18 07:58:05 +01:00
131fa6a359 Updated Indonesian translation 2012-09-18 11:20:04 +07:00
2d184e1842 Updated Polish translation 2012-09-18 00:04:08 +02:00
95a55a2c1c Updated Hungarian translation 2012-09-17 23:21:16 +02:00
8eecbb5c17 Updated Lithuanian translation 2012-09-17 23:28:55 +03:00
abfdcaa1b5 Updated Russian translation 2012-09-18 00:06:46 +04:00
8ee74e5661 st-theme: Fix extension ordering with !important
Extensions need to have priority over !important styles too.

https://bugzilla.gnome.org/show_bug.cgi?id=684163
2012-09-17 17:03:59 -03:00
ff31ccdd30 grabHelper: Remove unused parameters
Some left-overs from commit b203a95a7 ...

https://bugzilla.gnome.org/show_bug.cgi?id=683546
2012-09-17 21:53:53 +02:00
f6645a41d2 grabHelper: Set _grabbedFromKeynav
This one got lost in commit b203a95a7.

https://bugzilla.gnome.org/show_bug.cgi?id=683546
2012-09-17 21:53:53 +02:00
daceb8105f Updated Slovenian translation 2012-09-17 21:37:35 +02:00
4beba796d7 Wanda: remove unnecessary destroy signal connection
'destroy' is emitted before the actor is unmapped during destruction, so
notify::mapped would emit an exception. Since unmapping is guaranteed,
the 'destroy' signal is unnecessary.

https://bugzilla.gnome.org/show_bug.cgi?id=684154
2012-09-17 19:49:58 +02:00
70c34baafb Wanda: don't recreate the texture if the icon size doesn't change
Wanda flickers if the animation is recreated, but we don't need to if
the size doesn't change (as we're not affected by icon theme changes).

https://bugzilla.gnome.org/show_bug.cgi?id=684154
2012-09-17 19:49:57 +02:00
e71c016477 recorder: Port to new gstreamer vp8enc api
The speed and quality properties have been removed in favor of properties
closer to the upstream library.

Removing the properies from the pipeline would result into a huge
slowdown so we have to map the old values to the new ones.

According to the source code of the old vp8enc element quality maps to
(int)(63 - quality * 6.2) for min_quantizer and max_quantizer, while
speed maps to cpu-used = speed == 0 ? 0 : (speed - 1).

So set min_quantizer and min_quantizer to 13, and cpu-used to 5 based on
the above formulas.

https://bugzilla.gnome.org/show_bug.cgi?id=684206
2012-09-17 19:49:04 +02:00
633bbd8a9e Fix regression from 38f943ef81
In gdm mode there is no activities button, and accessing it causes an
exception.

https://bugzilla.gnome.org/show_bug.cgi?id=684162
2012-09-17 15:31:04 +02:00
4c82df56e9 Updated POTFILES.in 2012-09-16 22:45:07 +02:00
0d62ec5b03 lookingGlass: Fix object inspector for multi-monitor scenarios
Otherwise the object inspector appears off-screen.

https://bugzilla.gnome.org/show_bug.cgi?id=683982
2012-09-16 17:38:18 -03:00
38f943ef81 messageTray: Do not block hot corner when open
https://bugzilla.gnome.org/show_bug.cgi?id=682255
2012-09-16 19:53:49 +02:00
e421953fcd panel: Make activities button's hot corner public
https://bugzilla.gnome.org/show_bug.cgi?id=682255
2012-09-16 19:53:49 +02:00
f39098a4f2 ViewSelector: remove the places & devices search provider
Remove the PlacesManager, its search provider and all associated code.
Places search is now provided by nautilus using the external search
provider API.

https://bugzilla.gnome.org/show_bug.cgi?id=683506
2012-09-16 19:02:22 +02:00
b203a95a78 grabHelper: Rework grabbing code to properly handle stacking
When Dan Winship wrote the GrabHelper code originally, it didn't
handle a grab stack. I wrote the grab stack code hastily when landed
the message tray, not understanding all of the code that was involved
here.

Fix it so that we properly do the operations for each type of grab
when we first need to, and not sometimes when the first grab is taken.

https://bugzilla.gnome.org/show_bug.cgi?id=683546
2012-09-15 10:32:31 -03:00
2bb1a6792a update Simplified Chinese (zh_CN) translation 2012-09-15 17:00:59 +08:00
e30d45a2b0 Updated Bulgarian translation 2012-09-15 08:27:48 +03:00
afcd90ae27 messageTray: Fix _hideTray() being called recursively
_hideTray() is called by _updateState() when the tray is visible
but should be hidden; however, _updateState() may be called again
from within _hideTray() when releasing the GrabHelper grab, so
unless we update the _trayState variable before that, _hideTray()
will be called a second time.

https://bugzilla.gnome.org/show_bug.cgi?id=682243
2012-09-15 00:17:44 +02:00
94c1d5a18c focus-manager: Make groups "refcounted"
Rather than unconditionally removing a focus root in remove_group(),
decrement a counter that add_group() increments, and only actually
remove a focus root when the counter drops to 0.

https://bugzilla.gnome.org/show_bug.cgi?id=682243
2012-09-15 00:17:44 +02:00
4254fa3d38 ScreenShield: Make Return key dismiss screenshield
A couple of people have walked up to me and asked how to get to the
unlock screen from the screen shield. This was partly addressed by
bug 682285, but all three people who asked me about this said they
tried the return key and were surprised when it didn't work.

It sort of makes sense, since the user is "enter"ing the computer or
"return"ing to it.

This commit makes enter work in addition to the existing escape key.

https://bugzilla.gnome.org/show_bug.cgi?id=683889
2012-09-14 16:28:13 -04:00
3b293e91e3 viewSelector: Make the start search event available to input methods
Since we now use the capture phase to feed events into the input
method, we must set the capture flag for the event that starts
searches so that IMs can get at it.

https://bugzilla.gnome.org/show_bug.cgi?id=684040
2012-09-14 21:50:38 +02:00
3d6320295e UnlockDialog: allow typing before the first question from PAM
If the user starts typing right away, assume that the entry is
for a password and don't clear it when the secret request actually
comes. Then, if the user completes typing, we also stash the answer
and send it to GDM right away on the first PAM prompt.

https://bugzilla.gnome.org/show_bug.cgi?id=681576
2012-09-14 18:40:19 +02:00
7654f1ca3e MessageTray: remove bad fast path in hideSummaryBoxPointer
The onUngrab callback already checks if all notifications are destroyed and
hides immediately if so. Previous code instead would leave state handling
in an inconsistent state, by not removing the grab, not setting
summaryBoxPointerState to HIDDEN and not disconnecting various signals.

https://bugzilla.gnome.org/show_bug.cgi?id=684036
2012-09-14 18:29:47 +02:00
e62c66b153 TelepathyClient: fix regression from 6f5b700833
https://bugzilla.gnome.org/show_bug.cgi?id=684035
2012-09-14 18:29:47 +02:00
638507caff fingerprint: autostart fprintd when necessary
commit e333263fd6 changed fingerprint.js
to not throw an exception when fprintd is uninstalled, by adding the
flags DO_NOT_LOAD_PROPERTIES and DO_NOT_AUTO_START

DO_NOT_LOAD_PROPERTIES is correct.  Loading the properties is what makes
it fail at initialization time when not installed.  DO_NOT_AUTO_START is
not correct though. It means fprintd will never get activated implicitly
when we need it.

This commit removes DO_NOT_AUTO_START thus making fprintd start when we
need it, but not fail at initialization time when not around.

https://bugzilla.gnome.org/show_bug.cgi?id=683131
2012-09-14 12:12:46 -04:00
107f5de58e ScreenShield: animate going from the unlock dialog to the lock screen
If the user presses esc in the dialog, tween the lock screen from
the top instead of showing it immediately.

https://bugzilla.gnome.org/show_bug.cgi?id=681143
2012-09-14 17:11:57 +02:00
b1451523ca messageTray: Disable the tray dwell when the user is interacting
Look at the focus window's interaction timestamp to catch the case
where the user is typing and knocks the pointer into the tray or
mouses down to the bottom of the screen and clicks on something.
If the focus window's interaction time differs at the start and
end of the tray dwell then we don't activate the tray.

https://bugzilla.gnome.org/show_bug.cgi?id=683811
2012-09-14 10:42:40 -04:00
f7a95b5edc Update French translation 2012-09-14 11:07:42 +02:00
be290fafe7 ScreenShield: wait for stage mapping before taking a grab
In gdm, we would attempt to become modal during the synchronous initialization,
and this would fail, as X prevents grabs on unmapped windows. Instead,
wait for the stage to be visible before becoming modal.

https://bugzilla.gnome.org/show_bug.cgi?id=683357
2012-09-14 00:54:50 +02:00
b7ae74edb9 Revert showing notifications in the locked screen
As PAM messages are now shown below the password entry, there is no
need for this complexity, and we can just hide all notifications.
Also, this avoids the ambiguity between notification.showWhenLocked and
source.showInLockScreen, which have very different effects.

https://bugzilla.gnome.org/show_bug.cgi?id=683369
2012-09-14 00:06:30 +02:00
b3041ae9fc TelepathyClient: fix includes
The chat menu requires the PopupMenu module.

https://bugzilla.gnome.org/show_bug.cgi?id=683989
2012-09-13 23:59:55 +02:00
7499b04638 Fix insensitive styling for popup menu items
The selector for insensitive popup menu items was wrong (a PopupMenuItem is
a ShellGenericContainer, not a StButton). Fixing it showed that previous
:insensitive tracking was manual for a reason: we have many items that are
not reactive, but don't want the insensitive styling (for example those in
the battery menu).
Fix it by adding a new style-class, popup-inactive-menu-item, that is added
to all new PopupMenuItems that are not activatable.

https://bugzilla.gnome.org/show_bug.cgi?id=683988
2012-09-13 23:48:17 +02:00
bf2d2071fc Keyboard: fix timestamp handling to account for CURRENT_TIME
CLUTTER_CURRENT_TIME (like GDK_CURRENT_TIME and libX11 CurrentTime) is 0,
and thus compares lower than all valid timestamps, meaning that
focus changes without an X11 event in the stack are ignored by
the on screen keyboard.

https://bugzilla.gnome.org/show_bug.cgi?id=664309
2012-09-13 23:48:17 +02:00
488820daec TelepathyClient: don't emit an error if an unknown message is acknowledged
pending-messages-removed is emitted for sent messages too, but we don't
include those in the _pendingMessages list. Avoid useless spew in the session
logs in that case.

https://bugzilla.gnome.org/show_bug.cgi?id=683449
2012-09-13 22:41:17 +02:00
77c15b76b5 Fix extension styling
St.Theme.load_stylesheet() does not queue a theme context change, so
any styling of widgets created before will not be updated. To fix this,
load the stylesheet before the extension builds its own UI in enable()

https://bugzilla.gnome.org/show_bug.cgi?id=682128
2012-09-13 22:41:17 +02:00
e71129aa68 autorunManager: Don't show a right-click menu
Activating the source shouldn't be possible, and neither should
removing it.

https://bugzilla.gnome.org/show_bug.cgi?id=683438
2012-09-13 15:45:07 -03:00
6f5b700833 messageTray: Split out the building of the right menu to a separate method
Besides code cleanliness, this allows us to stub out a right-click menu
for resident sources.

https://bugzilla.gnome.org/show_bug.cgi?id=683438
2012-09-13 15:45:07 -03:00
9627864ca8 status/network: Fix another StIconType regression 2012-09-13 20:32:11 +02:00
2a8625ffae screenShield: Add box-shadow to the shield
According to the mockups, the screen shield should cast a shadow
when lifted.
2012-09-13 19:15:37 +02:00
ed82c3763c recorder: Fix more fallout from session-mode changes 2012-09-13 14:29:59 +02:00
22266899dc shellEntry: Make the entry have a fake focus state when a context menu is open
This means that right-clicking on an entry shouldn't visibly change the theme,
which is unexpected. Make sure that closing the menu refocused the entry, too.

https://bugzilla.gnome.org/show_bug.cgi?id=683509
2012-09-12 14:15:18 -03:00
9dfd1bfa41 shellEntry: Don't use a ClutterClickAction to pop up a menu
This removes support for long press, but fixes the brokenness of
other types of event handling.

https://bugzilla.gnome.org/show_bug.cgi?id=683509
2012-09-12 14:15:17 -03:00
409af28cb7 status/keyboard: Handle IBus engines which don't specify a symbol
In this case, fall back to the 2 or 3 letter language code or, failing
that, to a keyboard symbol.

https://bugzilla.gnome.org/show_bug.cgi?id=683613
2012-09-12 18:54:51 +02:00
4fe604bfe8 status/keyboard: Make IBus display strings consistent with g-c-c
Instead of entries like "Anthy" we want "Japanese (Anthy)".

https://bugzilla.gnome.org/show_bug.cgi?id=683124
2012-09-12 18:54:50 +02:00
49df72ceda st-im-text: Replace key-* handler with captured-event
When using an input method like IBus, the IM is expected to process
key events before anything else. Currently this doesn't always work
as expected, as the event filtering is done in the default handlers
of the key-press and key-release events, e.g. only after other
handlers have been run.
To allow the IM to filter events earlier, move the code to a
captured-event handler instead.

https://bugzilla.gnome.org/show_bug.cgi?id=658325
2012-09-12 18:50:26 +02:00
2db029bcdb dash: Don't underallocate show-apps button
When the dash contains more icons than fit at the minimum icon size,
icons are cut off at the end. This means that the show-apps button
will be the first to disappear, which is problematic given it's the
sole access point for other applications (for those that refuse to
use search at least).
Fix by using a dedicated widget for the dash actor, so that in case
of underallocation only icons above the show-apps button end up being
cut off.

https://bugzilla.gnome.org/show_bug.cgi?id=683340
2012-09-12 16:32:52 +02:00
7fd128eabc Updated Marathi Translations 2012-09-12 16:25:12 +05:30
5e3d8dd3eb Assamese translation updated 2012-09-11 21:01:34 +05:30
785be2f327 Hide the user menu in the GDM greeter lock screen
https://bugzilla.gnome.org/show_bug.cgi?id=683705
2012-09-10 21:23:25 +02:00
8cf9baa132 ScreenShield: move lock status handling to use session mode
Have distinct session modes for the lock screen and the unlock dialog,
and rework the logic in ScreenShield to have the lock-screen mode stack
onto the unlock-dialog mode (where applicable)

https://bugzilla.gnome.org/show_bug.cgi?id=682542
2012-09-10 21:23:25 +02:00
09e3aed770 St: don't focus hidden actors
If an actors is not mapped (visible and all parents visible), then don't
allow navigating focus to it.
This fixes a regression in the keyboard navigation of the panel with
invisibile items.

https://bugzilla.gnome.org/show_bug.cgi?id=683529
2012-09-10 21:23:25 +02:00
452ac297ab PolkitAgent: don't crash if there is no avatar
If we're asking for root's password, we don't show an avatar. Don't
emit an exception in that case.

https://bugzilla.gnome.org/show_bug.cgi?id=683707
2012-09-10 18:40:05 +02:00
7ebb5c6a10 NotificationDaemon: don't fail if a tray icon has no WM_CLASS
If there is no WM_CLASS for a tray icon, it returns null, not the empty
string. Detect that case without an exception.

https://bugzilla.gnome.org/show_bug.cgi?id=683724
2012-09-10 18:40:05 +02:00
00e0d24a6a Updated Thai translation. 2012-09-10 23:19:00 +07:00
8a269041fb messageTray: Restyle the summary counters
They are bigger and show an ellipsis if the count goes over 99. They
now have a blurred background and a drop shadow based on
data/theme/close-window.svg.

https://bugzilla.gnome.org/show_bug.cgi?id=682891
2012-09-10 17:41:30 +02:00
9f48adcff9 MessageTray: fix unexpanded height when receiving multiple telepathy messages
ClutterBinLayout is so amazingly broken: it uses the y_expand property to
find out if the children needs to honor alignment/fill, but that property is
"bubbled up" from the grand-children, so the notificationWidget would notice
the y_expand on the notificationBin (necessary to make the layout manager on
notificationWidget honor the alignment property for the bin), and would
receive the full height of the MessageTray actor from the parent's layout manager,
resulting in a notificationWidget shifting up, with the notification detached
from the screen.

https://bugzilla.gnome.org/show_bug.cgi?id=683628
2012-09-10 10:41:37 +02:00
48e7d732b9 Updated Portuguese translation 2012-09-09 12:38:56 +01:00
30048b74d1 Updated Korean translation 2012-09-09 07:25:27 +09:00
c866b0dbfd Clean up vestiges of the OpenSearch system
https://bugzilla.gnome.org/show_bug.cgi?id=683583
2012-09-07 20:49:35 +02:00
1c79f18b13 stage: Change background color to grey
The stage's background color can visible on screencasts when multiple monitors
with different resolutions are in use.

Change it to from blue to grey to look better as requested by the designers.

https://bugzilla.gnome.org/show_bug.cgi?id=683514
2012-09-07 18:29:20 +02:00
6d704be88b Updated Belarusian translation. 2012-09-07 18:58:44 +03:00
5af389c087 Updated Hebrew translation. 2012-09-07 16:13:59 +03:00
475dde4193 Updated German translation 2012-09-07 09:47:48 +02:00
8c534163e1 Panel: don't animate session mode switches
It causes problems with extensions, and it's excessively distracting.

https://bugzilla.gnome.org/show_bug.cgi?id=683526
2012-09-06 21:38:18 +02:00
657887b241 Revert "userMenu: Don't update the presence icon immediately"
This reverts commit f1ca96bbf0.
We're moving towards atomic panel upgrade, so we don't need this.

https://bugzilla.gnome.org/show_bug.cgi?id=683526
2012-09-06 21:38:17 +02:00
10da35cbef boxpointer: Clean up
https://bugzilla.gnome.org/show_bug.cgi?id=680077
2012-09-06 20:59:58 +02:00
00f15c1075 boxpointer: Avoid malformed boxpointer arrow
If the arrow's origin is so close to the edge that the arrow will not
be isosceles, we try to compensate as follows:
  - We skip the rounded corner and settle for a right angled arrow as
    as shown below.
    |\_____
    |
    |
  - If the arrow was going to be acute angled, we move the position of
    the box to maintain the arrow's accuracy.

https://bugzilla.gnome.org/show_bug.cgi?id=680077
2012-09-06 20:56:04 +02:00
19f92df8ab Updated Slovenian translation 2012-09-06 19:57:27 +02:00
8639cf8060 l10n: Updated Persian translation 2012-09-06 20:44:44 +04:30
2501d29e9d Updated Brazilian Portuguese translation. 2012-09-06 11:32:17 -04:00
8109dd684e sessionMode: Introduce the concept of "primary" modes
With the recent session mode changes, there is now a mix of modes
that are meant to apply to the entire session (specified as parameter
to the --mode command line switch) and temporary modes like the lock
screen; introduce a property to make the difference explicit, and only
allow "primary" modes to be specified on the command line.

https://bugzilla.gnome.org/show_bug.cgi?id=683488
2012-09-06 17:02:51 +02:00
5a259dd6b0 GrabHelper: always navigate focus when grabbing
Users of GrabHelper.grab() espect that the actor parameter (or one of its
children) will receive focus, irrespective of the previous focus location.
This fixes the key focus on the chat entry when expanding the notification.

https://bugzilla.gnome.org/show_bug.cgi?id=683449
2012-09-06 15:20:14 +02:00
2ed28211ed popupMenu: Don't always ignore SubMenu children in width requests
Hidden children are currently ignored in width requests; in the
case of submenu items, this results in abrupt width changes of
open menus when the corresponding SubMenuMenuItem is toggled.
To fix, only ignore SubMenu children when the corresponding
SubMenuMenuItem is hidden as well.

https://bugzilla.gnome.org/show_bug.cgi?id=683485
2012-09-06 13:56:44 +02:00
9d0eaa216f layout: Fix hiding panel for fullscreen apps
Yet another fallout from the sessionMode changes ...

https://bugzilla.gnome.org/show_bug.cgi?id=683487
2012-09-06 13:56:44 +02:00
58477282fe popupSubMenu: Only remove padding when scrollbar is shown
The special padding rules for submenu items currently ensure that
content aligns properly when the scrollbar is shown. While they
work nicely for the network menu, it looks odd for non-scrolled
submenus, so make this case explicit by introducing a :scrolled
pseudo class and adjust the style rules to use it.

https://bugzilla.gnome.org/show_bug.cgi?id=683009
2012-09-06 13:56:44 +02:00
6b016c2528 st-texture-cache: Fix stretched textures
st_texture_cache_load_from_raw() enforces a square ClutterTexture,
resulting in the texture being stretched if the passed in image
data has a different width:height ratio.
Add padding in those cases as we already do when loading from pixbufs.

https://bugzilla.gnome.org/show_bug.cgi?id=683483
2012-09-06 13:56:43 +02:00
b98c5f94ee shellSearchProvider: Fix GetResultMetas definition
The searchProvider interface originally used a GetResultMeta method to
return meta data of a single id; when it was changed to GetResultMetas,
the return value was not updated accordingly.

https://bugzilla.gnome.org/show_bug.cgi?id=683482
2012-09-06 13:56:43 +02:00
ad8bdb929a ShellDBus: fully show the lock screen before returning for Lock
Otherwise, gnome-settings-daemon proceeds with suspension too early,
and we may end up with visible windows on resume.

https://bugzilla.gnome.org/show_bug.cgi?id=683448
2012-09-06 13:15:27 +02:00
5030d59fcc messageTray: Focusing the text entry should force chats to stay open
https://bugzilla.gnome.org/show_bug.cgi?id=682236
2012-09-06 12:05:54 +02:00
ec52928736 messageTray: A variable to indicate that the close button was clicked
Otherwise critical resident notifications can not be closed by
clicking the close button.

https://bugzilla.gnome.org/show_bug.cgi?id=683472
2012-09-06 12:04:11 +02:00
7f479e18e6 Finnish translation update by Jiri Grönroos 2012-09-06 08:59:38 +03:00
7999e9020d Updated Greek translation 2012-09-06 02:40:59 +03:00
43ba93a817 screenShield: Animate the raising of the lock screen with the scroll wheel
A sudden transition to the lock screen is a bit jarring.
2012-09-05 14:51:30 -03:00
b37e02c90a screenShield: Fix typo
We aren't French, no matter how much we may want to be.

https://bugzilla.gnome.org/show_bug.cgi?id=683305
2012-09-05 14:28:57 -03:00
be5df17a4a grabHelper: Clean up the code a bit
I don't remember why I added needsGrab in the first place.
2012-09-05 14:28:45 -03:00
ffbc1fd190 unlockDialog: Fix centering of label in screen shield
The code here was trying to center the label, but that didn't
happen because we allocated the entire space to the label, which
still plonks it at the top.
2012-09-05 14:26:01 -03:00
457fb604dd autorunManager: Fix another regression
We may not have a resident source to destroy if no mounts are found.
2012-09-05 13:47:01 -03:00
09c81f79f6 AutorunManager: fix regression from the components rewrite
Message tray sources cannot be reused after destruction, so connect
to 'destroy' signal and clear out the previous one.
Also, fix some code paths that used the autorun manager incorrectly.

https://bugzilla.gnome.org/show_bug.cgi?id=683377
2012-09-05 16:22:11 +02:00
0725a7d836 SessionMode: add polkit to gdm session
It's needed to shutdown when other users are logged in or inhibitors are
active.

https://bugzilla.gnome.org/show_bug.cgi?id=683400
2012-09-05 16:14:02 +02:00
f957ae71c3 Don't start new components when activating the screen-shield in gdm
Previous code was activating the networkAgent and telepathyClient
in the lock-screen, irrespective of the previous mode.
Now it checks if the session mode is locked down, and if so it refuses
to start new components.

https://bugzilla.gnome.org/show_bug.cgi?id=683400
2012-09-05 16:14:02 +02:00
011c9d1beb Updated Indonesian translation 2012-09-05 21:07:28 +07:00
aed589a98d Updated British English translation 2012-09-05 13:58:08 +01:00
3830e90642 Updated Traditional Chinese translation(Hong Kong and Taiwan) 2012-09-05 20:08:12 +08:00
3710b88ab9 Bluetooth: don't restrict the length of non numeric PINs
The protocol restriction is only for numeric PINs (passkeys, in bluez
jargon). For passwords, any length is allowed.

https://bugzilla.gnome.org/show_bug.cgi?id=683356
2012-09-05 13:15:33 +02:00
5c990f103e Rewrite the layout code of the message tray
Previous code had a mixture of fixed positioning and ClutterBinLayout,
and this was broken badly for autorun notifications.
Rewrite to use ClutterBinLayout and Clutter properties exclusively.

https://bugzilla.gnome.org/show_bug.cgi?id=683378
2012-09-05 12:42:11 +02:00
2890fc9d7d Updated Serbian translation 2012-09-05 10:37:18 +02:00
0223507a2e Updated Arabic translation 2012-09-05 07:08:55 +02:00
59412a9405 Components/Keyring: unregister the system prompter when disabling
Otherwise, we get a critical when enabling again
2012-09-05 01:13:36 +02:00
110d58bbc3 Reintroduce run dialog
Fix regression from 92d8d65543, bad rebase.
2012-09-05 01:10:44 +02:00
eda17defb2 messageTray: Move close button above notification widget stack
This allows us to click on the entire close button, not just the part
of it that's unobscured.
2012-09-04 19:21:31 -03:00
f563fb124e shell-gtk-embed: Fix NULL pointer dereference
Clutter will try to unmap during a dispose if we have a parent, so if we
set our own actor to NULL before the chain up, we're going to attempt to
unmap our own NULL actor. Fix that by swapping the order in which we
chain up.

https://bugzilla.gnome.org/show_bug.cgi?id=672790
2012-09-04 19:21:30 -03:00
14d0a96999 shell-recorder: Fix warning message about unknown escapes
We were showing the percent character here.

https://bugzilla.gnome.org/show_bug.cgi?id=677434
2012-09-04 19:21:30 -03:00
f0474ffccc shell-recorder: Remove the ability to pause the timeline
https://bugzilla.gnome.org/show_bug.cgi?id=677434
2012-09-04 19:21:30 -03:00
11ce6845f2 shell-recorder: Remove count and unique filename settigs
These aren't used anymore.

https://bugzilla.gnome.org/show_bug.cgi?id=677434
2012-09-04 19:21:30 -03:00
70d610b5e4 shell-recorder: Fix accidental fallthrough
Don't append a unique identifier when we asked for a timestamp.

https://bugzilla.gnome.org/show_bug.cgi?id=677434
2012-09-04 19:21:29 -03:00
f5ca649977 shell-recorder: Don't use a default filename
The default filename isn't localized and isn't the same one that
the shell sets. Just remove the fallback mechanism, and abort
recording if somebody didn't set the filename

https://bugzilla.gnome.org/show_bug.cgi?id=677434
2012-09-04 19:21:29 -03:00
8954d33019 Updated Polish translation 2012-09-05 00:20:30 +02:00
2e6205bc05 Updated POTFILES.in 2012-09-05 00:06:19 +02:00
e0dea63079 Updated POTFILES.in 2012-09-04 23:59:30 +02:00
7c244b01c6 Panel: move the _panelContainer down to PanelMenu
Panel already forces each item to be a PanelMenu.Button, so it's better
to have the latter handle the bin container too, instead of attaching
a private property that might collide with internal usage by the indicator.

https://bugzilla.gnome.org/show_bug.cgi?id=683156
2012-09-04 23:52:41 +02:00
3f5edf7c3e VolumeMenu: read output and input if PulseAudio is already ready when constructing
Previously we would only read the default sink and default source when
the connection to PulseAudio succeded. This worked because all VolumeMenu
users where initialized synchronously during shell load.
With the recent session mode changes though, the lock screen menu is
created on demand, and when it loads PA is already connected, so
it doesn't update the sliders.

https://bugzilla.gnome.org/show_bug.cgi?id=683156
2012-09-04 23:52:41 +02:00
92d8d65543 ScreenShield: use session mode to handle the GDM login dialog
Have main.js call .showDialog() when going back from the lock-screen, instead
of using the return value of createUnlockDialog to know if the dialog
was persistent.
_keepDialog is still used as LoginDialog cannot really be destroyed,
and cancelling it does not destroy it.

https://bugzilla.gnome.org/show_bug.cgi?id=683156
2012-09-04 23:52:41 +02:00
cebd8e14e9 screenShield: Zoom out on login
The sudden transition from the lock screen to the user session
may be a bit sudden and overwhelming. Make ourselves more shell-like
by resizing out the screen shield according to mockups.

https://bugzilla.gnome.org/show_bug.cgi?id=683170
2012-09-04 18:42:46 -03:00
16e92a7ca3 sessionMode: Inherit from a more restrictive session mode by default
It makes more sense to define session modes in terms of what you're
adding to the bare shell, not in terms of what you're taking away
from the user session.

https://bugzilla.gnome.org/show_bug.cgi?id=683156
2012-09-04 18:42:45 -03:00
59e2710137 sessionMode: Reindent
This puts all the parameters at the same indent level, which makes the
file much easier to read.

https://bugzilla.gnome.org/show_bug.cgi?id=683156
2012-09-04 18:42:45 -03:00
f1ca96bbf0 userMenu: Don't update the presence icon immediately
If we don't freeze the presence icon, we can end up in a place where
we'll be updating the icon before we fade out the panel indicators when
coming back from the lock screen.

https://bugzilla.gnome.org/show_bug.cgi?id=683156
2012-09-04 18:42:45 -03:00
ec01f5d5ee userMenu: Make the user menu insensitive in the lock screen
And show a lock icon as well.

https://bugzilla.gnome.org/show_bug.cgi?id=683156
2012-09-04 18:42:44 -03:00
2a800e4ce0 Rearchitect the Shell to have a components system
Components are pieces of the shell code that can be added/removed
at runtime, like extension, but are tied more directly to a session
mode. The session polkit agent, the network agent, autorun/automount,
are all components, keyring, recorder and telepathy client are all
now copmonents.

https://bugzilla.gnome.org/show_bug.cgi?id=683156
2012-09-04 18:42:44 -03:00
ca2e09fe8b sessionMode: Allow changing the session mode at runtime
Since we eventually want to add a system for changing the top panel
contents depending on the current state of the shell, let's use the
"session mode" feature for this, and add a mechanism for updating the
session mode at runtime. Add support for every key besides the two
functional keys, and make all the components update automatically when the
session mode is changed. Add a new lock-screen mode, and make the lock
screen change to this when locked.

https://bugzilla.gnome.org/show_bug.cgi?id=683156
2012-09-04 18:42:44 -03:00
7e343f11f2 st-bin: Make sure not to allocate hidden children 2012-09-04 18:42:44 -03:00
db20a54861 Login/UnlockDialog: don't reset immediately if auth fails
Instead of showing a notification, add a small message immediately
below the entry, and give the user two more attempts to login,
before going back to the welcome or lock screen.

https://bugzilla.gnome.org/show_bug.cgi?id=682544
2012-09-04 23:38:46 +02:00
ec4f6b7f91 Updated Hungarian translation 2012-09-04 15:54:39 +02:00
08a69cdcbd Bump version to 3.5.91
Update NEWS
2012-09-04 10:55:28 +02:00
aa9c095c8e panel: Make sure to show the app menu button after unlocking the screen
I'm not sure how we missed this one.

https://bugzilla.gnome.org/show_bug.cgi?id=683154
2012-09-03 19:24:11 -03:00
b257029d3e main: Remove some cruft
Main.hotCorners has been empty for a long, long time.

https://bugzilla.gnome.org/show_bug.cgi?id=683156
2012-09-03 18:14:53 -03:00
cb9062f818 calendar: Launch the calendar server with DBus autostart
The supposed reason for launching the calendar server in a peculiar
way was so that the process would be killed when the Shell was killed,
but that didn't actually work. Launch the calendar server through auto-start,
and persist all throughout the session.

https://bugzilla.gnome.org/show_bug.cgi?id=683156
2012-09-03 18:14:53 -03:00
3a1f623ea3 sessionMode: Remove extraStylesheet
It's been unused ever since the gdm merge.

https://bugzilla.gnome.org/show_bug.cgi?id=683156
2012-09-03 18:14:41 -03:00
47d46e367e ScreenShield/MessageTray: fix crash after the introduction of close buttons
Commit 5c6b1fd0c modified the hierarchy of SummaryItem contents, by
introducing a notificationStackWidget in place of the StackView, but
forgot to update the bits in ScreenShield.NotificationsBox that accessed
and reparented that directly, causing a crash by invalid theme node access.

https://bugzilla.gnome.org/show_bug.cgi?id=682683
2012-09-03 23:08:36 +02:00
e5534e86ab Panel: clean up button construction code
We already could build the right part of the panel declaratively according
to the session mode. Extend that to handle the left and center parts.
Also, move the mapping from the roles to the classes in panel.js, as it shared
by all modes.

https://bugzilla.gnome.org/show_bug.cgi?id=682546
2012-09-03 23:08:36 +02:00
e0ca572572 Updated Polish translation 2012-09-03 19:13:16 +02:00
ca4b86e7ca messageTray: Don't hide the banner after NOTIFICATION_TIMEOUT
... if the notification originally popped up under the pointer, and
the pointer is moving inside it.

https://bugzilla.gnome.org/show_bug.cgi?id=682238
2012-09-03 18:48:10 +02:00
f177bd0b51 ScreenShield: Allow lifting up the curtain with the mouse wheel
On a traditional mouse, or with edge-scrolling on a touchpad, it just
feels natural to unlock by scrolling up.

https://bugzilla.gnome.org/show_bug.cgi?id=683164
2012-09-03 17:19:40 +02:00
11086e8ef8 messageTray: s/_useLongerHideTimeout/_useLongerTrayLeftTimeout/
Typo introduced in 6d2d4fcc77
2012-09-03 16:21:53 +02:00
1237aef727 Updated Spanish translation 2012-09-03 15:58:57 +02:00
79bfea5970 st-widget: Unset hover when setting track_hover to FALSE
https://bugzilla.gnome.org/show_bug.cgi?id=683156
2012-09-03 02:51:22 -03:00
50f8ae6fc7 st-bin: Don't allocate a hidden actor
https://bugzilla.gnome.org/show_bug.cgi?id=683156
2012-09-03 02:51:22 -03:00
73ec86797b Updated Italian translation 2012-09-02 23:19:23 +02:00
703417a760 ShellUserVerifier: fix cancellation
Ensure that all async callbacks check and ignore G_IO_ERROR_CANCELLED.
Ensure that all runs of authentication have their own GCancellable, so
that .begin() can be called multiple times on the same user verifier.
Check for fingerprint reader when beginning authentication, and not
when reset by GDM.

https://bugzilla.gnome.org/show_bug.cgi?id=682544
2012-09-02 23:09:23 +02:00
3013a87bd2 theme: Tweak the padding of #summary-mode
Change the padding values of #summary-mode to be 6px on right and left
to match the values in .summary-source, which determine the padding
between the top and bottom edges of the tray and the items.

https://bugzilla.gnome.org/show_bug.cgi?id=682248
2012-09-02 18:48:00 +02:00
833cb2556b gdm: Fix powerMenu icon name
Fallout from commit c21b1e5fe0
2012-09-02 14:55:45 +02:00
8f4f8fccdb Updated Lithuanian translation 2012-09-02 15:36:29 +03:00
38e4ab1cea updated Tamil translation 2012-09-02 16:53:22 +05:30
366a3e2c35 Updated Norwegian bokmål translation 2012-09-02 12:07:39 +02:00
fcb175ebb3 update Punjabi Translation 2012-09-02 10:05:41 +05:30
a76cc79f88 ScreenShield: add a drop shadow to the animated arrows
Introduce a StShadowHelper to manage drop shadows from JS (which
cannot use Cogl directly), and use it in a new StWidget-derived
JS class to draw the arrow.

https://bugzilla.gnome.org/show_bug.cgi?id=682285
2012-09-01 16:38:51 +02:00
1a6d74fcb2 ScreenShield: rework the arrow in the lock screen
User testing has shown that it is not discoverable that the whole
lock screen can be dragged. A new mockup includes more arrows
and a short animation every 4 seconds.

https://bugzilla.gnome.org/show_bug.cgi?id=682285
2012-09-01 16:38:51 +02:00
eb351b1882 MessageTray: escape the tray when a legacy icon is clicked
Legacy tray icons may want to take a pointer grab to show a popup menu,
and this is incompatible with message tray modality. To solve this,
escape the tray when forwarding clicks to the tray icons, and wait
for the input mode change to actually synthetize the X event.

https://bugzilla.gnome.org/show_bug.cgi?id=682244
2012-09-01 16:38:51 +02:00
43caace1b6 boxpointer: Use the -arrow-rise as padding instead of a magic number
This makes sense if we want to follow what the following comment says:
// We also want to keep it onscreen, and separated from the
// edge by the same distance as the main part of the box is
// separated from its sourceActor

Using a magic number violates the "separated from the edge ...
separated from its sourceActor" part.

https://bugzilla.gnome.org/show_bug.cgi?id=682343
2012-09-01 13:14:45 +02:00
167ed7c35b loginDialog: Fix regression from updating icon
Remove a rogue reference to _updateIcon.
2012-09-01 08:06:41 -03:00
69735940ec messageTray: Summary items should be square and have spacing
Each summary icon is 48x48 with a padding of 6px on each side. Thus,
each summary item is 60px wide. Therefore the summary mode should be
60px high instead of 72px.

Changed the tray actor to use a ClutterBinLayout so that it honors the
y-expand property of its children.

https://bugzilla.gnome.org/show_bug.cgi?id=682248
2012-09-01 12:36:00 +02:00
1a7fad129d Updated Vietnamese translation 2012-09-01 17:21:45 +07:00
6931e6cf0f po/vi: import from Damned Lies 2012-09-01 17:18:22 +07:00
87e021cd2e statusArea: hide power indicator on desktop
On a desktop system the power indicator does not get hidden correctly,
since a blank icon '' is returned instead of null.

https://bugzilla.gnome.org/show_bug.cgi?id=683080
2012-09-01 09:41:24 +02:00
d3f5d94afe Updated Galician translations 2012-09-01 02:22:15 +02:00
a3d8b5e526 Fix some more icon names
Message tray sources should never have symbolic icons.
The VPN secondary icon in the panel instead should.
2012-08-31 23:31:25 +02:00
5e46abfa03 Use UserAvatarWidget to display user avatars
Replace duplicate custom code with a common widget.

https://bugzilla.gnome.org/show_bug.cgi?id=683137
2012-08-31 23:31:25 +02:00
f4740bac65 Updated Polish translation 2012-08-31 22:55:42 +02:00
dc10e61a20 Fix some more symbolic icon names
Reviewed by Jasper St. Pierre in IRC.
2012-08-31 22:46:53 +02:00
82c8aad157 NetworkMenu: remove broken device activation policy
Previously, when toggling a switch on we tried to replicate NM policy and
find a good connection to activate. This is broken in many situations.
Instead, only activate something when we can be sure it's what the user
wants (i.e. when there is only one connection, or when there is none,
and thus connecting will trigger the config dialog)

https://bugzilla.gnome.org/show_bug.cgi?id=683136
2012-08-31 22:35:44 +02:00
4ba1f26e4d searchDisplay: Improve the placement and style of the "No results" text
During user testing, it was shown that several people did not notice the
miniscule text label. Make it large and centered.

https://bugzilla.gnome.org/show_bug.cgi?id=683135
2012-08-31 16:43:52 -03:00
f8805e8311 windowManager: Disable the window dimmer when we close all attached dialogs
This saves some GLSL resources and an FBO.

https://bugzilla.gnome.org/show_bug.cgi?id=683073
2012-08-31 16:43:52 -03:00
c815979f2a magnifier: Don't use some deprecated APIs
Stop using Clutter.Group and Clutter.Rectangle.

https://bugzilla.gnome.org/show_bug.cgi?id=683073
2012-08-31 16:43:52 -03:00
54e5ffcac1 magnifier: Don't set the size of the uiGroup
The magnifier shouldn't mess with global properties like this.

https://bugzilla.gnome.org/show_bug.cgi?id=683073
2012-08-31 16:43:51 -03:00
3fd0502cb9 magnifier: Round the uiGroup to integer positions
This removes the jaggies from text, and other sorts of things.

https://bugzilla.gnome.org/show_bug.cgi?id=683073
2012-08-31 16:43:51 -03:00
8aed51180f magnifier: Use PointerWatcher to poll the mouse pointer
This gives us a more efficient method to watch the mouse pointer.

https://bugzilla.gnome.org/show_bug.cgi?id=683073
2012-08-31 16:43:51 -03:00
e616877fd2 js: Fix up for Clutter.Color changes
Spotted by darkxst on IRC.

https://bugzilla.gnome.org/show_bug.cgi?id=683073
2012-08-31 16:43:51 -03:00
df15ee4074 Updated Arabic translation 2012-08-31 16:06:38 +02:00
4cf3acd0ec Updated Greek translation 2012-08-31 11:10:43 +03:00
77ea16e18f st-im-text: Support surrounding-text
https://bugzilla.gnome.org/show_bug.cgi?id=683015
2012-08-31 02:16:42 +02:00
85d993386a st-im-text: Simplify dispose
There's no need to disconnect signal handlers on a private object that
we are destroying.
2012-08-31 02:16:42 +02:00
14a7da767b status/volume: Fix initial symbolic icon 2012-08-30 21:53:25 +02:00
2f3df71cc1 status/network: Fix VPN symbolic icon 2012-08-30 21:38:39 +02:00
dc13e72b89 messageTray: Use monitor geometry for dwelling
In multi-monitor setups, the screen might extend below the edge of
the monitor that holds the tray. In that case the tray is currently
triggered from a secondary monitor, which is rather surprising;
change the check to use the correct monitor geometry instead.

https://bugzilla.gnome.org/show_bug.cgi?id=683044
2012-08-30 18:52:17 +02:00
2beff9896a Fix a couple of symbolic icons
Some fallout from commit c21b1e5fe0 ...
2012-08-30 14:55:33 +02:00
43af6f4792 Updated Galician translations 2012-08-30 14:18:31 +02:00
04d1a35cb5 PopupMenu: propagate all open-state-changed down to PopupMenuSection
PopupMenuBase relies on open-state-changed to update the separator
visibility, but PopupMenuSection were only emitting when the parent
closed, so the first time the menu was opened, separator visiblity was
wrong.

https://bugzilla.gnome.org/show_bug.cgi?id=682998
2012-08-30 01:10:47 +02:00
f075b36d4f Updated Lithuanian translation 2012-08-29 23:34:28 +03:00
c21b1e5fe0 st: Remove StIconType
GTK+ works by explicitly specifying a -symbolic suffix for all icons.
Do the same here.

https://bugzilla.gnome.org/show_bug.cgi?id=682540
2012-08-29 16:41:09 -03:00
042c1fd54b Updated Spanish translation 2012-08-29 17:02:23 +02:00
ee6086373b st-im-text: Support pre-edit
Use clutter_text_set_preedit_string() to wedge pre-edit text
into ClutterText.

https://bugzilla.gnome.org/show_bug.cgi?id=664041
2012-08-29 14:46:52 +02:00
382310fe51 Updated Polish translation 2012-08-29 01:51:06 +02:00
d52cb510fc Updated POTFILES.in 2012-08-29 01:49:24 +02:00
c1de2788b1 Add a new lock screen menu to combine volume network and power
The design has a combined volume-network-power indicator in the lock
screen, which when opened shows a volume slider. Implement it by abstracting
the volume menu into a PopupMenuSection, and by creating three StIcons
bound to the real ones.

https://bugzilla.gnome.org/show_bug.cgi?id=682540
2012-08-29 01:13:23 +02:00
41dc9e0894 PanelMenu: Allow multiple icons in one SystemStatusButton
This generalizes the code previously used by NetworkMenu, so that all
SystemStatusButton now can add multiple icons.

https://bugzilla.gnome.org/show_bug.cgi?id=682540
2012-08-29 01:13:23 +02:00
d3b0d23d8f StIcon: deprecate StIcon:icon-name and StIcon:icon-type
Reroute setting those properties to a GIcon. API users are expected
to create GIcon directly now.
The advantage is that from a StIcon you can now create a similar one
by accessing :gicon.

https://bugzilla.gnome.org/show_bug.cgi?id=682540
2012-08-29 01:13:23 +02:00
f96dcaccbe MessageTray: clamp unexpanded notification height
The height of an unexpanded notification could include expanded
content if the notification has extra widgets (like actions and images),
so tweening to that cause it to expand visually.
Instead, use the height of the message tray before the restyle
as an upper bound.

https://bugzilla.gnome.org/show_bug.cgi?id=682738
2012-08-29 01:04:28 +02:00
2672f205fd notificationDaemon: Explicitly return false from an idle 2012-08-28 19:50:08 -03:00
1ba21ed367 Updated Galician translations 2012-08-29 00:27:17 +02:00
79408d359c placeDisplay: Remove "Connect to..." item
Opening a dialog without an associated application is odd, so require
users to use nautilus itself to create new connections.

https://bugzilla.gnome.org/show_bug.cgi?id=682817
2012-08-28 21:43:53 +02:00
12c76e53cb Updated Polish translation 2012-08-28 18:54:54 +02:00
ac44426557 Updated Spanish translation 2012-08-28 16:06:50 +02:00
684e5bec15 Updated Norwegian bokmål translation 2012-08-28 07:45:08 +02:00
70d19530c4 Use the term password instead of passphrase
Since that is what the primary label uses as well as the gtk mount
operation.

https://bugzilla.gnome.org/show_bug.cgi?id=682645
2012-08-27 21:16:08 -04:00
e34ab6c5ae Add 1em of padding below the password box
https://bugzilla.gnome.org/show_bug.cgi?id=682645
2012-08-27 21:12:26 -04:00
b3cb9d8459 Fix the spacing in the mount operation auth prompt
We weren't setting the correct style attributes like we
were in the polkit auth dialog.

https://bugzilla.gnome.org/show_bug.cgi?id=682645
2012-08-27 19:03:00 -04:00
df3fa973ad main: Load extensions as late as possible
Since we want extensions to be able to be loaded at any time, that
means that the startup case should be as close to the live-time
install case.

https://bugzilla.gnome.org/show_bug.cgi?id=682822
2012-08-27 17:40:07 -03:00
1f4895428d Implemented FUEL entries to Assamese translation 2012-08-28 00:17:03 +05:30
417941a1bf overview: Fix ShellInfo.setMessage()
Commit 22eea750 made info messages show up in the lock screen, but
as setShowWhenLocked() throws an exception when called on non-transient
notifications, the transient hint has to be set first.

https://bugzilla.gnome.org/show_bug.cgi?id=682268
2012-08-27 12:15:34 +02:00
973a6b21a2 Assamese translation updated 2012-08-27 14:45:09 +05:30
b50604d070 a11y: set a proper label for the "Show Applications" dash button
https://bugzilla.gnome.org/show_bug.cgi?id=682366
2012-08-27 10:43:35 +02:00
a1ecf459b3 a11y: exposing the proper button role while using toggle_mode on St.Button
https://bugzilla.gnome.org/show_bug.cgi?id=682366
2012-08-27 10:43:35 +02:00
6ea80eadd6 a11y: set a label_actor for the summary items on the message tray
https://bugzilla.gnome.org/show_bug.cgi?id=677229
2012-08-27 10:43:35 +02:00
9dafaa2c0c ScreenShield: don't bump the lock screen while animating
Bumping overrides any animation, and causes the installed onComplete
not to run, which in turn breaks suspending from the user menu (as
it listens to lock-screen-shown).
Add a state variable (using an enumeration shared with messageTray)
to control the lock screen, and only bump when the lock is still.

https://bugzilla.gnome.org/show_bug.cgi?id=682746
2012-08-26 23:43:14 +02:00
50e5736c03 Updated Greek translation 2012-08-26 22:56:24 +03:00
46fcfb7629 build: Bump version in jhbuildrc
We were still pointing to the 3.4 moduleset, switch that to the 3.6 one.
2012-08-26 19:46:27 +02:00
8a86540090 Revert the other half of "St: don't attempt to give focus to non reactive actors"
This reverts commit 26d3b1929e.

https://bugzilla.gnome.org/show_bug.cgi?id=667439
2012-08-26 11:56:45 -03:00
785ab8192b shell-app: Fix a memory leak in the running state
We need to free two strings that we allocated here.

https://bugzilla.gnome.org/show_bug.cgi?id=682710
2012-08-26 11:56:08 -03:00
3c386e0c50 gdm: reset cancellable if cancelled.
Otherwise the second attempt tot login after pressing "escape" key
on the login "freeze" and raise an exception about IOError operation
cancelled.

https://bugzilla.gnome.org/show_bug.cgi?id=681537
2012-08-26 14:35:11 +02:00
bec48492ab ScreenShield: hide Removable devices in the lock screen
Showing the removable devices is potentially a security risk (as
they include network shares). Also, a nautilus launched from there
can't be used, so it's just a way to overload the system.

https://bugzilla.gnome.org/show_bug.cgi?id=681143
2012-08-26 14:30:45 +02:00
13a10f8afd Updated Hebrew translation. 2012-08-26 10:46:10 +03:00
00fe97a55a Updated Arabic translation 2012-08-26 06:15:54 +02:00
9e665e4903 Updated Russian translation 2012-08-24 10:47:14 +04:00
efc128e681 workspacesView: Refactor thumb zooming out code
https://bugzilla.gnome.org/show_bug.cgi?id=582650
2012-08-23 23:36:25 -03:00
6851c5443a workspace: Refactor code a bit
https://bugzilla.gnome.org/show_bug.cgi?id=582650
2012-08-23 23:36:25 -03:00
baf08dd688 Updated Polish translation 2012-08-24 04:25:26 +02:00
67115b3e9b extensionDownloader: Fix extension installation
The name of the method is "get_as_bytes"

https://bugzilla.gnome.org/show_bug.cgi?id=682578
2012-08-23 22:57:46 -03:00
4ca2697271 shellDBus: Fix reloadExtension
We need to recreate and reimport the extension here, not just reimport
the same exact code.

https://bugzilla.gnome.org/show_bug.cgi?id=682578
2012-08-23 22:57:46 -03:00
4696bfbb80 extensionSystem: Make unloadExtension take an extension object, not a UUID
For consistency with loadExtension.

https://bugzilla.gnome.org/show_bug.cgi?id=682578
2012-08-23 22:57:45 -03:00
18c62a1987 Make workspace selector more similar to the mockup
This makes the workspace indicator respect the "padding" style attribute.
Also, since we no longer draw the border on top of the thumbnail,
we need to be pixel-precise in allocating the indicator height.

We use this to make the workspace selector more similar to the mockup.

https://bugzilla.gnome.org/show_bug.cgi?id=662087
2012-08-23 21:17:22 +02:00
d60a4e97d6 shell-app: Don't match on comments
Comments are meant to be displayed, not to be searched - we should
rather advertise the use of Keywords, which yields much better results.

https://bugzilla.gnome.org/show_bug.cgi?id=682529
2012-08-23 18:02:56 +02:00
43883a6945 Updated Spanish translation 2012-08-23 17:41:55 +02:00
58ff5c6cc6 a11y: setting label actor for endSessionDialog.ListItem
https://bugzilla.gnome.org/show_bug.cgi?id=677503
2012-08-23 11:14:57 +02:00
3902e8bff0 messageTray: fix dwelling during mouse-down
If the user has the mouse down - for example when they are selecting
text and dragging - then the attempt to get a modal grab will fail.

grabHelper: allow the .grab() function to fail and do nothing in this
modal case if the grab fails.

messageTray: handle grab failure and don't pop up the tray. Change the
logic for tray dwelling so that we only try to pop up the tray once
while the pointer is in the dwell area - this avoids the possiility
that the tray will pop up once the user releases the mouse.

https://bugzilla.gnome.org/show_bug.cgi?id=682385
2012-08-23 01:49:21 -04:00
5d6f37017b Updated Lithuanian translation 2012-08-22 22:29:46 +03:00
ecbf2f1429 messageTray: Handle 'popup-menu' signal on summary items
Now that the message-tray is key navigable, we should enable
triggering the right-click menu by keyboard shortcut as well.

https://bugzilla.gnome.org/show_bug.cgi?id=682486
2012-08-22 21:00:52 +02:00
56dc2eb96e remoteSearch: Don't add providers more than once
Currently we load all search providers from XDG_DATA_DIRS, so if
the same provider is installed several times in different directories,
we happily show duplicate results. To fix, keep track of all remote
providers we add and skip those that already have been loaded from a
different directory.

https://bugzilla.gnome.org/show_bug.cgi?id=682470
2012-08-22 21:00:52 +02:00
2154a22c90 layout: Don't unnecessarily go through Main for a property
We *are* Main.layoutManager, here.

https://bugzilla.gnome.org/show_bug.cgi?id=682429
2012-08-22 15:56:55 -03:00
3fafe0da07 dash: Add tooltip label to show applications icon
Make the tooltip dependent on whether we are currently dragging
on hover or not.

https://bugzilla.gnome.org/show_bug.cgi?id=682445
2012-08-22 15:55:24 -03:00
983535224e dash: Fix some issues with the label API
Be able to call setLabelText more than once.

https://bugzilla.gnome.org/show_bug.cgi?id=682445
2012-08-22 15:55:24 -03:00
88effdd9c3 appMenu: Make show() a no-op in locked state
The app menu is hidden when entering the lock screen, however it
might be shown again while the lock is still in place - we don't
want this ever to be the case, so make show() a no-op while the
screen is locked.

https://bugzilla.gnome.org/show_bug.cgi?id=682475
2012-08-22 20:31:43 +02:00
e9a4843eb0 AutorunManager: Fix fallout from LoginManager rework
AutorunManager relied on AutomountManager to find if the session
was active, and this broke when this stopped exporting a public
method for it. Fix AutorunManager to have its own reference to
the LoginManager.

https://bugzilla.gnome.org/show_bug.cgi?id=682455
2012-08-22 19:16:34 +02:00
c50132b29f theme: improve scrollbars coloring
https://bugzilla.gnome.org/show_bug.cgi?id=682476
2012-08-22 17:27:19 +02:00
e92a6b3c6c workspacesView: Avoid an unnecessary animation
Currently we animate scrolling to the active workspace both when
the number of workspaces changed and after changing the active
workspace. So in case we don't actually change workspace, this
results in an unnecessary animation that may even have unwanted
side effects: when done during the overview transition (e.g. in
the case of opening and activating a window on an empty workspace),
non-active workspaces become visible during the transition.
To fix, don't scroll to the active workspace when the number of
workspaces changes and rely on the 'switch-workspace' signal being
emitted as necessary.

https://bugzilla.gnome.org/show_bug.cgi?id=682002
2012-08-22 16:26:59 +02:00
66a36bf591 Updated Norwegian bokmål translation 2012-08-22 12:35:25 +02:00
39559f0a09 [l10n] Update Japanese translation 2012-08-22 19:07:15 +09:00
712623946f network: Fix icon_size parameter
We forgot to update this to NOTIFICATION_ICON_SIZE

https://bugzilla.gnome.org/show_bug.cgi?id=682412
2012-08-22 00:34:12 -03:00
47ce72e011 Fix modal dialog styling for network secret prompts
https://bugzilla.gnome.org/show_bug.cgi?id=682412
2012-08-22 00:34:12 -03:00
7f8c5c4f64 Updated Polish translation 2012-08-22 04:31:52 +02:00
64fdff6ace Updated Polish translation 2012-08-22 04:28:21 +02:00
fa2b413095 Bump version to 3.5.90
Update NEWS
2012-08-22 00:56:38 +02:00
46b7a95ec8 messageTray: Remove the message tray hot corner
Since dwell at the bottom of the screen is now the primary way of
summoning the tray, removing the hot corner to avoid having two
separate things that can be accidentally triggered.

https://bugzilla.gnome.org/show_bug.cgi?id=682310
2012-08-21 18:27:33 -04:00
55922c0568 Updated POTFILES.in 2012-08-21 20:33:28 +02:00
1c927868d8 network: ignore unrecognized/irrelevant devices/connections
Don't log a warning if an unrecognized device type is seen.

Don't show slave connections in the menu. (Eg, don't show the
individual wired connections making up a bond, since they can't be
used individually.)

Make the icon only reflect the status of connections that are visible
in the menu. (ie, don't show the "connecting" icon when an
unrecognized connection type is connecting, and don't show a
"connected" status if the only active connections are of unrecognized
types.)

https://bugzilla.gnome.org/show_bug.cgi?id=682364
2012-08-21 13:37:54 -04:00
0a1f0e58d0 gdm: Add network login hint
Different networks have different user identifers.

This can be confusing to the user, so to make it clearer,
we try to provide a hint.

https://bugzilla.gnome.org/show_bug.cgi?id=681975
2012-08-21 13:15:29 -04:00
9024c5d7ac gdm: generalize fingerprint message into login hint
The fingerprint message is useful for users that click their
names in the user list to let them know if fingerprint login
is available.

This same place on screen (below the login entry) can potentially
be used for other messages as well.

This commit changes the variable and style names surrounding
this feature to be more generic.

A subsequent commit will leverage this functionality to provide
a hint on how to log in to the local enterprise domain controller
(if relevant).

https://bugzilla.gnome.org/show_bug.cgi?id=681975
2012-08-21 13:15:28 -04:00
370de395f7 a11y: name label and main status chooser item don't neeed to be focusable
This also gets the status combobox focusable again.

https://bugzilla.gnome.org/show_bug.cgi?id=667439
2012-08-21 18:18:42 +02:00
8ebd2ff9eb a11y: adding a proper state for menu items with a dot
https://bugzilla.gnome.org/show_bug.cgi?id=682366
2012-08-21 18:17:21 +02:00
83a1c7283d a11y: exposing percentage label on power menu items
https://bugzilla.gnome.org/show_bug.cgi?id=682366
2012-08-21 18:16:51 +02:00
695f84e14f Style the tray so it is less prominent in the overview
This will help to ensure that it doesn't distract from the other
elements in the overview, such as the dash, window thumbnails and
workspace switcher.

https://bugzilla.gnome.org/show_bug.cgi?id=682342
2012-08-21 10:28:06 -04:00
54f3036f86 messageTray: add dwelling at the bottom of the screen to open the tray
If the user leaves the mouse pointer at the bottom of the screen for a second,
open the tray. This simulates the eventual plan of measuring "pressure" by how
far the pointer is moved past the edge of the screen. Measuring pressure will
take X server changes.

https://bugzilla.gnome.org/show_bug.cgi?id=682310
2012-08-21 02:36:56 -04:00
446583400e pointerWatcher: add a class to track the pointer
Add a class to efficiently poll the pointer position, stopping polling
when the user is idle.

https://bugzilla.gnome.org/show_bug.cgi?id=682310
2012-08-21 02:36:56 -04:00
3776c50986 workspace: Position windows only when needed
Right now, when entering the overview, we compute the window slots about
four or five times, from scratch each time. Move to a queued system where
extraneous calls to positionWindows don't matter.

https://bugzilla.gnome.org/show_bug.cgi?id=582650
2012-08-20 18:22:49 -04:00
4ff0697ee7 workspace: Reindent
https://bugzilla.gnome.org/show_bug.cgi?id=582650
2012-08-20 18:22:49 -04:00
cd43c4983b userMenu: Make presence chooser insensitive when offline
Setting an IM presence only makes sense when connected to the
network, reflect this by making the presence chooser insensitive
when no network is available.

https://bugzilla.gnome.org/show_bug.cgi?id=677982
2012-08-21 00:10:28 +02:00
0f065ebce6 userMenu: Delay restoring presence until the network comes up
Trying to connect to IM servers while offline is pointless, in
particular now that we added a progress indication which makes
the connection attempt very visible.
To fix, wait for the network to become available until restoring
a previous IM presence.

https://bugzilla.gnome.org/show_bug.cgi?id=677982
2012-08-21 00:10:28 +02:00
a2e0e0ad06 messageTray: Adjust the position of the close button
Make this closer to the mockups.

https://bugzilla.gnome.org/show_bug.cgi?id=682253
2012-08-20 18:06:52 -04:00
d721fc2c17 messageTray: Put the notification widget in the center
Rather than stretching the entire screen length

https://bugzilla.gnome.org/show_bug.cgi?id=682253
2012-08-20 18:06:52 -04:00
becaeafeac messageTray: Port the close buttons over to Clutter properties
Rather than computing everything manually, let Clutter calculate
things for us.

https://bugzilla.gnome.org/show_bug.cgi?id=682253
2012-08-20 18:06:52 -04:00
99c97707ac calendar: Fix showing "Next Week" on Sundays
The original code was assuming that getDay() on a Sunday would
return 7 rather than 0. This broke the "Next Week" logic
in several places.
This commit introduces a dayInWeek variable which takes the following
values on the according days:

weekstart = 1:
Mo: 0
Tu: 1
We: 2
Th: 3
Fr: 4
Sa: 5
Su: 6

weekstart = 0:
Su: 0
Mo: 1
Tu: 2
We: 3
Th: 4
Fr: 5
Sa: 6

Using this we can simplify and fix the conditional that decides
whether to show "This week" or "Next week" which was broken on
Sundays.

This commit also fixes the period that gets shown for "Next week"
on Sundays. Due to the bug it was 13 + 1 - 0 or 13 + 0 - 0 on
Sundays:

weekStart = 1:
saturday: saturday + 13 - day_in_week = saturday + 8 = sunday next week
sunday: sunday + 13 - day_in_week = sunday + 7 = sunday next week

weekStart = 0:
friday: friday + 13 - day_in_week = friday + 8 = saturday next week
saturday: saturday + 13 - day_in_week = friday + 7 = saturday next week

https://bugzilla.gnome.org/show_bug.cgi?id=682198
2012-08-20 22:51:45 +02:00
75ed427e04 theme: Put back the transition from overview to desktop for the message tray
https://bugzilla.gnome.org/show_bug.cgi?id=682271
2012-08-20 16:51:12 -04:00
d1e35d11d0 messageTray: Put the lightbox opacity down
https://bugzilla.gnome.org/show_bug.cgi?id=682271
2012-08-20 16:51:12 -04:00
ee0102e86e viewSelector: Allow to close the application view with escape
With the latest changes to the overview, the application view is now
clearly on a different level compared to the window picker. For that
reason it now makes sense to close it on Escape rather than hiding
the overview directly, as we do for search.

https://bugzilla.gnome.org/show_bug.cgi?id=682109
2012-08-20 20:40:01 +02:00
776b71bad5 viewSelector: Allow to start keynav using the down arrow
As the search entry acts like it had keyboard focus, it seems
logical to use the down arrow to move focus to the active page.

https://bugzilla.gnome.org/show_bug.cgi?id=682109
2012-08-20 20:40:01 +02:00
37da87d9e1 perf: Update to latest overlay changes
Removing tabs from the overview broke the perf test for the
application view; update test to the new method of bringing
it up.

https://bugzilla.gnome.org/show_bug.cgi?id=682109
2012-08-20 20:40:01 +02:00
ea14d74141 viewSelector: Clean up focusTrap in search results
Rather than relying on implementation details of StWidget's keyboard
navigation to "hide" the focusTrap from arrow key navigation, implement
the desired behavior explicitly in a custom widget.

https://bugzilla.gnome.org/show_bug.cgi?id=663901
2012-08-20 20:40:00 +02:00
4b60a8deb8 Updated Spanish translation 2012-08-20 20:04:06 +02:00
7cca0a9cd8 Updated Traditional Chinese translation(Hong Kong and Taiwan) 2012-08-20 23:15:37 +08:00
f347aba01b overview: Increase the searchEntry width to 320 pixels
https://bugzilla.gnome.org/show_bug.cgi?id=682109
2012-08-20 14:23:20 +02:00
8c40ded498 viewSelector: Replace tab system with pages
The old tab system is no longer a good fit for the current design, so
replace most of the abstractions with a simpler page system.

https://bugzilla.gnome.org/show_bug.cgi?id=682109
2012-08-20 14:23:19 +02:00
c267a7a7f9 viewSelector: Merge SearchTab into the ViewSelector
Tabs used to provide an abstraction for a page and the control used to
activate it. As the latter has now been replaced with external controls
handled directly in the viewSelector, the abstraction itself doesn't make
much sense anymore. In preparation of replacing it, move the search
handling provided by the SearchTab directly in the viewSelector.

https://bugzilla.gnome.org/show_bug.cgi?id=682109
2012-08-20 14:23:19 +02:00
0959adcfe3 overview: Center the dash vertically
https://bugzilla.gnome.org/show_bug.cgi?id=682109
2012-08-20 14:23:19 +02:00
7d12f47bf8 viewSelector: Remove shortcut to switch between tabs
We no longer have tabs, so it no longer makes sense to keep the shortcut.

https://bugzilla.gnome.org/show_bug.cgi?id=682109
2012-08-20 14:23:19 +02:00
dff673533d viewSelector: Remove tab title bar
All the functionality provided by the tab titles has been replaced, so the
title bar can be removed without loss of functionality.

https://bugzilla.gnome.org/show_bug.cgi?id=682109
2012-08-20 14:23:18 +02:00
816a29d582 viewSelector: Hook up the dash's showApps button
We pass the dash’s showApps button to the viewSelector, and we connect it
to the showing and hiding of the appsView. This is necessary because there
are different mechanisms for switching the views, and it has to stay in
sync with the button’s state.

https://bugzilla.gnome.org/show_bug.cgi?id=682109
2012-08-20 14:23:18 +02:00
27fbe4cd83 dash: Refactor favRemoveTarget to showAppsIcon
In the new designs, we no longer need favRemoveTarget. As it shares a lot
of its functionality with the new showAppsIcon, we refactor and restyle it
accordingly.

https://bugzilla.gnome.org/show_bug.cgi?id=682109
2012-08-20 14:23:18 +02:00
2ee6f49a93 dash: Make the favRemoveTarget always visible
We’ll be repurposing the favRemoveTarget, which calls for it the be
permanently visibe.  The favRemoveTarget used to be added to the dash when
needed and removed again when it wasn’t. This made that it always appeared
at the bottom of the dash. Now that we always show it, we also need to
explicitly define it to be at the bottom of the dash.

https://bugzilla.gnome.org/show_bug.cgi?id=682109
2012-08-20 14:23:18 +02:00
0ea2d98768 overview: Move the entry out of the SearchTab into the overview
The entry should be positioned in the center of the overview. This makes
that its position can’t be set in the viewSelector without making things
overly complicated. Therefore we move the entry to the overview.

https://bugzilla.gnome.org/show_bug.cgi?id=682109
2012-08-20 14:23:17 +02:00
b5dc78a968 viewSelector: Use a fixed set of view tabs
Design calls for views being accessible by other means than the current tab
system, so we have no longer a need for the public viewTab API. Move the
initialization of tabs to the viewSelector and make
viewSelector.addViewTab() private.

https://bugzilla.gnome.org/show_bug.cgi?id=682109
2012-08-20 14:23:17 +02:00
ebe7890a49 Updated Spanish translation 2012-08-20 14:17:50 +02:00
ef4231b9c0 search: Junk the OpenSearch system
The original design for the overview had buttons for searching for
Wikipedia and Google, but in practice this is a bad idea. The buttons
are the default activations, meaning that using the overview as a
fluent motion of launching something - "firefxo<Enter>", will launch
Google/Wikipedia.

https://bugzilla.gnome.org/show_bug.cgi?id=670168
2012-08-19 21:19:17 -04:00
7e2bab48c9 messageTray: Force the tray closed when exiting the overview 2012-08-19 21:15:52 -04:00
eebab34b62 grabHelper: Make sure to return not null when the stack contains fake grabs
We did this if there's nothing in the grab stack, but didn't do this
when there's no real grabs in the grab stack.
2012-08-19 21:15:52 -04:00
05d613f3a3 messageTray: Fix going to left-click/right-click on the same summary item
This code was originally here to close the summary box pointer if it was
already open, but it seems that it isn't necessary, and is causing all
sorts of problems.
2012-08-19 21:15:52 -04:00
5c6b1fd0cc messageTray: add close button to notifications
Displaying a close button provides a discoverable way to close notifications.
Clicking the close button on new notifications, dismisses them, but doesn't
remove the notification source from the message tray if it is resident.

Clicking the close button on summary notifications acts the same way as clicking
"Remove" option in the right click menu, which is to remove the notification
stack and its source, even if it is resident or a tray icon.
2012-08-19 21:15:51 -04:00
f8eab659d9 messageTray: hide removed notification without tweening
This reflects more accurately what happens when a notification is removed.

this._pointerInTray value needs to be set to false in this case.
2012-08-19 21:15:51 -04:00
4a214d8238 messageTray: be sure to remove destroyed notifications from the notification stack
This allows us to correctly identify when the notification stack is empty.
2012-08-19 21:15:51 -04:00
44b4741e35 messageTray: Close the current box pointer when clicking on the same item 2012-08-19 21:15:51 -04:00
95b9863a70 messageTray: Close the current box pointer when clicking on the tray 2012-08-19 21:15:50 -04:00
4dfe3d21e1 messageTray: Always close the tray when clicking outside of it
Even when a summary box pointer item is up.
2012-08-19 21:15:50 -04:00
3568e6d42d grabHelper: Only connect to specific signals when actually taking a grab
When we enter the overview, we don't explicitly don't take a grab, so we
shouldn't connect to key-focus-changed and things like that, otherwise
random overview code will drop our grab for us.

This fixes escape in the overview not dropping when a notification is up.
2012-08-19 21:15:50 -04:00
c3ae4542b2 grabHelper: Fix ungrabbing properly
Make sure to account for modalCount properly, rather than just
tracking modalCount for the last actor on the stack. Additionally,
traverse the popped actors in the reverse order so that onUngrabbed
callbacks are called at the proper place in time.
2012-08-19 21:15:50 -04:00
3031036cb0 grabHelper: Make sure to call onUngrabbed for all popped actors 2012-08-19 21:15:50 -04:00
11dc510ae4 grabHelper: Fix some keyboard focus issues with the message tray
This will need to be revisited.
2012-08-19 21:15:49 -04:00
79c25d1dc8 grabHelper: Clean up
Remove a dummy "return;", and remove an unused parameter.
2012-08-19 21:15:49 -04:00
1fd17b5027 messageTray: Be able to "switch" notifications
If you click on a summary item while another is up, we should
swap without an extra click.
2012-08-19 21:15:49 -04:00
7a6b2abfeb theme: Make sure to select the summary source item when showing the box pointer 2012-08-19 21:15:49 -04:00
fe8e62c6a8 messageTray: keep the desktop clone at integral positions tweening it
Drawing the desktop at non-integral positions is slower because it
turns off the obscured-area clipping logic, so avoid that.
2012-08-19 21:15:48 -04:00
e937fd07e5 theme: Add a new style for .summary-source-button:focus
We don't need .summary-source-button:selected because we are moving the
focus to the summary item when it is clicked and therefore it is always
focused.

https://bugzilla.gnome.org/show_bug.cgi?id=681519
2012-08-19 21:15:48 -04:00
c668d16a00 messageTray: Grab the key focus when entering from the keybinding 2012-08-19 21:15:48 -04:00
c33e2d1971 messageTray: Add the summary view to the focus manager
This makes keynav work between summary items
2012-08-19 21:15:48 -04:00
0711a70398 js: Use global.focus_manager, rather than grabbing it ourselves 2012-08-19 21:15:48 -04:00
cb5095370e messageTray: Use a Lightbox to shade the window_group
This is a visual indication of the fact that a grab is in place.

https://bugzilla.gnome.org/show_bug.cgi?id=681392
2012-08-19 21:15:47 -04:00
8f71920622 messageTray: Correct idleness
If the user is already active when the notification pops up, we
won't get an idle watcher because there's no transition from
active to idle or vice versa. Correct this by initializing the
state correctly from XSync.
2012-08-19 21:15:47 -04:00
3da0e9e86a messageTray: Make it keyboard navigable in the overview
https://bugzilla.gnome.org/show_bug.cgi?id=681519
2012-08-19 18:41:52 -04:00
e8c8fecb35 messageTray: Make the hot corner summon the tray 2012-08-19 18:41:52 -04:00
8bb2c425c6 messageTray: Properly track notificationExpanded
This makes a lot more sense.
2012-08-19 18:41:52 -04:00
1f6d807882 messageTray: Make modal 2012-08-19 18:41:52 -04:00
8d1ed9f88a messageTray: Port to GrabHelper
https://bugzilla.gnome.org/show_bug.cgi?id=671001
2012-08-19 18:41:51 -04:00
d766f865f3 Introduce a new GrabHelper
PopupMenu.PopupMenuManager and MessageTray.FocusGrabber had a lot of
code in common. Let's refactor this common code out into a new class,
"GrabHelper". This replaces FocusGrabber completely, and nukes half
of PopupMenuManager.

Based on a patch by Dan Winship <danw@gnome.org>

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

https://bugzilla.gnome.org/show_bug.cgi?id=671001
2012-08-19 18:41:51 -04:00
c540cb5e16 st-scroll-bar: use clutter_grab_pointer()
StScrollBar was intercepting motion events by using captured-event on
the stage, which required additional dirty tricks, which required
additional hacks. Simplify it by just using clutter_grab_pointer()
instead.

https://bugzilla.gnome.org/show_bug.cgi?id=671001
2012-08-19 18:41:51 -04:00
b36029f6c4 theme: Update message-tray style in the overview
The currently used style works well in "normal" mode, where the
tray is shown to be located beneath the window layer (pushing the
latter up when revealed). It does not work very well in the overview,
so use the same style as dash and workspace switcher there.
2012-08-19 18:41:50 -04:00
a8e6bfcb3d message-tray: Add :overview pseudo class when in overview
The designers would like the message-tray to use different styles
depending on whether it is shown in the overview or not, so use
an :overview pseudo class as the top bar.
2012-08-19 18:41:50 -04:00
9c7222daea messageTray: don't show the tray after new items are added
Because showing the tray now moves the desktop around, we should no
longer show it when it's not triggered by the user.
2012-08-19 18:41:50 -04:00
ab5c0bcfcf messageTray: don't time out banners when the user is inactive
This will ensure that the user doesn't miss notifications.
2012-08-19 18:41:50 -04:00
1a2dcf213e theme: move the message tray's summary box pointer higher
The SummaryBoxPointer was peeking in the message tray. Now it is
raised 4px higher.
2012-08-19 18:41:49 -04:00
6f8e7f07f3 messageTray: restyle the message tray
The patch implements the new visual style for the message tray,
that is now larger and has textured background. The icons are
also larger.

https://bugzilla.gnome.org/show_bug.cgi?id=677213
2012-08-19 18:41:49 -04:00
0ac4787f1a messageTray: remove the labels of the items in the message tray
According to the new visual style for the message tray we no longer
show the title of the source in the message tray.

https://bugzilla.gnome.org/show_bug.cgi?id=677213
2012-08-19 18:41:49 -04:00
16bbe9782b messageTray: Clone, clip & move the window_group when tray is toggled
1) straddling windows get clipped at the monitor boundary
2) we move the bottom monitor and not the primary because that is
   where the tray is
3) to stop the wallpaper from the bottom monitor leaking into the
   primary, we adjust the clip as the clone animates up/down

https://bugzilla.gnome.org/show_bug.cgi?id=681392
2012-08-19 18:41:48 -04:00
5594c119f5 Add screenShield.js into POTFILES.in
https://bugzilla.gnome.org/show_bug.cgi?id=682189
2012-08-19 20:48:43 +09:00
a909017a2b UnlockDialog: Fix "other user" label
The unlock dialog has a mechanism for manually typing in a username
and password.  This mechanism is mislabeled:

Login as another user

when it should be labeled

Log in as another user

This commit adds the space.

Spotted by Stef Walter <stefw@gnome.org>

https://bugzilla.gnome.org/show_bug.cgi?id=681750
2012-08-18 23:03:29 -04:00
9a7914eee9 Consolidate systemd and consolekit in a common abstract class
Various code around had different paths for ConsoleKit and
logind. Consolidate it by making an abstract class that all
callers can use, which hides the implementation details of the
two daemons.

https://bugzilla.gnome.org/show_bug.cgi?id=682096
2012-08-18 18:09:55 +02:00
98c461dcd8 build: Require clutter-1.0 >= 1.11.11
Usage of ClutterDragAction->drag_area was introduced with
b53d18fe1c
2012-08-18 08:56:38 +02:00
5991c8dca3 messageTray: Hook SourceActor up to source icon changes automatically
Instead of manually tracking source icon changes, or requiring a manual
call to _setSummaryIcon, add a way to emit a signal when we're guaranteed
the icon has been changed, and then the source actor will automatically
update the icon.
_setSummaryIcon is still available for sources such as the notification
daemon, that require special treatment for the summary icon (to be used
with tray icons)

https://bugzilla.gnome.org/show_bug.cgi?id=680426
2012-08-18 00:36:13 +02:00
b9226fbcbd screenShield: Add missing import 2012-08-17 19:31:03 +02:00
b6375d3e40 ScreenShield: listen to login managers to unlock the session
Handle locking and unlocking from outside. This fixes coming
back from fast user switching.

https://bugzilla.gnome.org/show_bug.cgi?id=682096
2012-08-17 19:10:27 +02:00
8dc9ceb2bc ScreenShield: make .lock() and .unlock() idempotent
Those methods can be called multiple times in certain situations,
and will be even more so after hooking them to the system bus.
2012-08-17 19:10:27 +02:00
6cb713b0e3 Consolidate ConsoleKit and Systemd code
In preparation for accessing it in the screenshield, factor out
common code for ConsoleKit and Systemd.
Also, clean up ConsoleKit manager, as the daemon is required in
a non systemd installation. In particular:
- We allow it to be autostarted at session startup (or really,
  we expect it to be already there, started by GDM during session
  opening).
- We no longer silently assume that the session is active if
  it can't start.

https://bugzilla.gnome.org/show_bug.cgi?id=682096
2012-08-17 19:10:27 +02:00
dafa27fccd a11y: can_focus=!reactive as a general rule is not true anymore
In some cases can_focus value is taken directly as
!reactive. But this is not the case anymore, as
we are interested on navigate on non reactive items

https://bugzilla.gnome.org/show_bug.cgi?id=667439
2012-08-17 17:32:52 +02:00
190f2f82ac a11y: allow navigation on non reactive items
At this moment non-reactive items are being used in
several cases as non-sensitive, but they still
require keyboard navigation.

https://bugzilla.gnome.org/show_bug.cgi?id=667439
2012-08-17 17:32:16 +02:00
43767ab2b9 Updated Serbian translation 2012-08-17 12:37:51 +02:00
3042e647ff telepathyClient: Pass a proper launch client to launch empathy-accounts
https://bugzilla.gnome.org/show_bug.cgi?id=682045
2012-08-16 20:10:25 -04:00
a004ad6088 telepathyClient: Pass the right amount of parameters
https://bugzilla.gnome.org/show_bug.cgi?id=682045
2012-08-16 20:10:24 -04:00
addb247c33 telepathyClient: Remove some obsolete wrappers
gjs can handle these cases natively now

https://bugzilla.gnome.org/show_bug.cgi?id=682045
2012-08-16 20:10:24 -04:00
8f129e1c9d messageTray: Defer creation of mainIcon
We need to make sure the Source is fully constructed before creating
mainIcon, as SourceActor will query the count of the source.

This fixes Telepathy Chats.

https://bugzilla.gnome.org/show_bug.cgi?id=682045
2012-08-16 20:10:24 -04:00
341b6a1290 UnlockDialog: go back to the lock screen automatically when idle
Now that GDM no longer emits auth failures after 25 seconds, we
need to handle inactivity ourselves.
This has also the advantage that it tracks real inactivity, rather
than a timeout from a fixed point in time.

https://bugzilla.gnome.org/show_bug.cgi?id=682041
2012-08-17 01:32:02 +02:00
6a9b1996e4 ShellIdleMonitor: turn it into a singleton
It doesn't make sense to have multiple ShellIdleMonitors, since
each has its own GDK filter function, but they all get the same
events. In preparation for having it accessed from other places
than the message tray, make it a singleton.

https://bugzilla.gnome.org/show_bug.cgi?id=682041
2012-08-17 01:32:02 +02:00
a5f2d289bd ScreenShield: reduce curtain sliding time to 0.5 seconds
Current time is too long, 0.5 was proved to be more effective
in user testing.

https://bugzilla.gnome.org/show_bug.cgi?id=681143
2012-08-17 01:32:02 +02:00
e3a8228065 Updated Galician translations 2012-08-17 00:35:34 +02:00
fd04c59e63 UnlockDialog: add an explicit cancel button
Pressing escape to cancelling the unlock procedure is not discoverable
enough.
At the same time, fix styling to avoid bluish hover effects.

https://bugzilla.gnome.org/show_bug.cgi?id=681143
2012-08-16 23:18:02 +02:00
f8b1a84b81 ScreenShield: Fix updating visibility of the notification box
If there were no persistent or resident notification sources,
nothing ever would call _updateVisibility, and the box would stay
there empty

https://bugzilla.gnome.org/show_bug.cgi?id=681143
2012-08-16 23:18:02 +02:00
b53d18fe1c ScreenShield: constrain vertical movement of the screen lock
Don't allow dragging the screen lock above and below the screen,
as in particular dragging below breaks the illusion of the curtain.

https://bugzilla.gnome.org/show_bug.cgi?id=681143
2012-08-16 23:18:02 +02:00
d0a94768a6 ScreenShield: bump the lock screen slightly when pressing a key
When pressing a key different from escape (one thus that has no
effect), bump the screen up, to indicate that it eats keyboard
input and it must be lifted up.

https://bugzilla.gnome.org/show_bug.cgi?id=681143
2012-08-16 23:18:02 +02:00
80fde70995 UnlockDialog: remove hover effect from the avatar widget
It is not clickable, so it should not have any hover.

https://bugzilla.gnome.org/show_bug.cgi?id=681143
2012-08-16 22:42:41 +02:00
cbb56b6128 Hide user menu and a11y menu in the screen lock
This is a temporary patch. The user menu button needs to move to
the left, and the a11y menu in some cases needs to be hidden outside
of the screen lock too.

https://bugzilla.gnome.org/show_bug.cgi?id=681143
2012-08-16 22:42:40 +02:00
cbd2188c04 MonitorConstraint: fix clamping monitor index
I will test my patches, I promise.
2012-08-16 22:15:36 +02:00
2b30afa618 ScreenShield: place the lock screen contents only on the primary monitor
Use the new monitor constraint to place the clock and notification
box on the primary monitor only. The background is still extended
to the whole screen.
Get rid of the LockDialogGroup hack, now that ClutterBinLayout
respects fixed position correctly.

https://bugzilla.gnome.org/show_bug.cgi?id=681743
2012-08-16 21:46:04 +02:00
1a65374e21 Port modal dialogs to the new MonitorConstraint
This commit makes ModalDialog use the new MonitorConstraint instead
of custom code to force itself on the right monitor.
At the same it ports wanda, which has something similar to a modal
dialog, but is not using the ModalDialog module.

https://bugzilla.gnome.org/show_bug.cgi?id=681743
2012-08-16 21:45:21 +02:00
3e94f6bc3c LayoutManager: add a ClutterConstraint for tracking monitor sizes
Instead of connecting manually to LayoutManager, or using ShellGenericContainer,
make a ClutterConstraint subclass that can track automatically
a specific monitor index, or the primary monitor.

https://bugzilla.gnome.org/show_bug.cgi?id=681743
2012-08-16 21:45:21 +02:00
1dbbb4f91c Use GDM instead of libaccountsservice for fast user switching
libaccountsservice uses gdmflexiserver internally, which has been
removed from gdm. Instead, use libgdm directly.

https://bugzilla.gnome.org/show_bug.cgi?id=680231
2012-08-16 20:57:30 +02:00
b3b847fadd ScreenShield: fix regression from 2c627cad
A method was renamed but one call site was forgotten.
2012-08-16 19:35:23 +02:00
ff25a5e251 windowManager: Update animation of attached modals (again)
The fade animation we started using after centering attached
modal dialogs didn't work too well. So after going back to the
scale animation, adjust it to scale from the center rather
than the top, which works quite well.

https://bugzilla.gnome.org/show_bug.cgi?id=681601
2012-08-16 09:52:17 +02:00
aaf61cbbbe Revert "windowManager: Update animation of attached modals"
This reverts commit 6ab25cd791.

https://bugzilla.gnome.org/show_bug.cgi?id=681601
2012-08-16 09:52:17 +02:00
ca7c4edd41 windowManager: Update dim effect
The combination of desaturating and lowering the brightness does
not work too well in all cases, in particular for applications
using the dark theme variant. Dropping the desaturation effect and
making the brightness adjustment more profound gives a better
result.

https://bugzilla.gnome.org/show_bug.cgi?id=681601
2012-08-16 09:52:17 +02:00
028e83181b main: Do not hard-code <super> as overlay-key
Use the new OVERLAY_KEY keybinding action instead of special-casing
the overlay-key to make sure the same key will be used in- and outside
the overview.

https://bugzilla.gnome.org/show_bug.cgi?id=665547
2012-08-16 09:44:22 +02:00
31c3783c7c Updated Marathi Translations 2012-08-16 12:26:19 +05:30
f63371dadb messageTray: Set can_focus to true for SummaryItem
https://bugzilla.gnome.org/show_bug.cgi?id=681519
2012-08-15 03:10:30 +02:00
9f07a3dd4a keyringPrompt: Improve styling here as well
Again, center the "Password" label, and add some spacing between
it and the entry.

https://bugzilla.gnome.org/show_bug.cgi?id=681821
2012-08-14 13:14:08 -03:00
06262a903e UserMenu: don't lock the screen if automatic lock disabled
If automatic lock is disabled in the control center, only lock
when explicitly using the Lock menu item, and not for Suspend or
Switch user/session.

https://bugzilla.gnome.org/show_bug.cgi?id=680231
2012-08-14 02:16:22 +02:00
e01e79d9fb Use symbolic icons for workspace switch OSD
https://bugzilla.gnome.org/show_bug.cgi?id=680738
2012-08-13 13:27:40 -04:00
2c627cad10 ScreenShield: prepare the dialog when the user starts dragging
This way if the user drags long enough it will see the dialog
below the curtain, and it will appear as it was always there.

https://bugzilla.gnome.org/show_bug.cgi?id=681143
2012-08-13 17:59:19 +02:00
b33e51be45 Update Punjabi Translation 2012-08-13 07:33:13 +05:30
b17f5b678d Updated German translation 2012-08-12 20:00:46 +02:00
c6a6ae594a Updated Traditional Chinese translation(Hong Kong and Taiwan) 2012-08-12 23:15:32 +08:00
9f02129af3 Updated Hebrew translation. 2012-08-10 14:44:34 +03:00
6037350380 Updated Spanish translation 2012-08-10 11:15:05 +02:00
5de38cd9f6 Assamese translation updated 2012-08-09 12:59:46 +05:30
7950c4afdd messageTray: Add a toggle-message-tray keybinding to toggle the tray
The default value is <Super>m.

https://bugzilla.gnome.org/show_bug.cgi?id=681392
2012-08-09 01:36:24 +02:00
e304854fa5 Bump version to 3.5.5
Update NEWS
2012-08-07 21:26:18 +02:00
d7f54213f1 ScreenShield: when explicitly locking, tween the shield from the top
The curtain should appear to be an overlay on top of the system,
and since it is lifted by dragging up, it makes sense to slide it
down on lock.

https://bugzilla.gnome.org/show_bug.cgi?id=681143
2012-08-07 21:26:17 +02:00
12a71be076 Updated Spanish translation 2012-08-07 21:05:25 +02:00
855f0dbcad UnlockDialog: place the prompt above the entry, rather than on the side
As indicated in the mockups

https://bugzilla.gnome.org/show_bug.cgi?id=681143
2012-08-07 20:35:54 +02:00
03db0d630e UnlockDialog: Move "Login as another user" at the bottom
The Unlock button is more logically related to the entry than
this link, and having the latter in the middle breaks the workflow.

https://bugzilla.gnome.org/show_bug.cgi?id=681143
2012-08-07 20:35:29 +02:00
5a35c57866 ModalDialog: make dialogLayout public
ModalDialog subclasses may want to add content outside contentLayout
(in particular, UnlockDialog wants to have it after the buttons)
2012-08-07 20:35:29 +02:00
d11027e8c8 util: Don't pass too many arguments to GLib.spawn_async 2012-08-07 15:09:28 -03:00
aa733b5e53 Only show new notifications in the screen shield
Rework the count system in Source, to distinguish between the
normal notification count and the count of unseen/unacknowledged
notification. (A notification is considered unacknowledged until
shown, as a banner or inside the summary box pointer).
Includes some code cleanups and a test for multiple notifications
in the same source.

https://bugzilla.gnome.org/show_bug.cgi?id=681143
2012-08-07 18:10:49 +02:00
355ec9ceca UserMenu: fix disconnecting a signal that was never connected
If a disabled account is removed, we receive account-removed without
ever seeing account-enabled, and thus _changingId is undefined.

https://bugzilla.gnome.org/show_bug.cgi?id=681382
2012-08-07 17:44:17 +02:00
aa30c6a323 loginDialog: Remove manual insensitive tracking
StWidget does this for us now
2012-08-07 11:51:12 -03:00
ad5572f275 tests: Remove JHBUILD_TYPELIBDIR
It's unused and crufty, and potentially security leak.
2012-08-07 11:51:12 -03:00
9082b4df48 keyboard: _showLayoutItem is only defined if allowSettings.
Check Main.sessionMode.allowSettings is true before accessing the
actor item of _showLayoutItem.

https://bugzilla.gnome.org/show_bug.cgi?id=681101
2012-08-07 16:50:07 +02:00
c3179583c3 Assamese translation updated 2012-08-07 15:03:43 +05:30
4db87839a6 Updated gujarati file 2012-08-07 15:02:59 +05:30
b018d49dc3 Updated Slovenian translation 2012-08-07 08:44:36 +02:00
83362226cf Updated Serbian translation 2012-08-07 00:24:59 +02:00
1e3796967e NotificationDaemon: don't override the title for app notifications
If a source is associated with an app, ignore the app name provided
by libnotify, as that is often garbage.
This fixes the "XChat-GNOME OSD" problem.

https://bugzilla.gnome.org/show_bug.cgi?id=681143
2012-08-06 23:07:11 +02:00
8d7fa54af4 ScreenShield: fix spacing of notifications and sources
Reduce padding around persistent sources, and ensure that spacing
around resident notifications is only applied once.
Also, add some padding to the clock.

https://bugzilla.gnome.org/show_bug.cgi?id=681143
2012-08-06 23:02:34 +02:00
59cd9b4220 ScreenSheild: make notification view scrollable
Place a maximum height on the notification view, and show scrollbars
if the list of persistent sources would overflow.

https://bugzilla.gnome.org/show_bug.cgi?id=681143
2012-08-06 23:02:34 +02:00
59dfb5866d ScreenShield: update the displayed title when the source changes
Notifications in the lock screen need to watch signals on source
and react accordingly.

https://bugzilla.gnome.org/show_bug.cgi?id=681143
2012-08-06 23:02:31 +02:00
a29ceaa8ec ScreenShield: lower the autodrag threshold to 10%
The threshold is there just to ensure that the user is opening
the curtain on purpose. Requiring 40% is too much for that.

https://bugzilla.gnome.org/show_bug.cgi?id=681143
2012-08-06 21:43:54 +02:00
782e11b96c Remove markup from translated strings
Including the markup in the string makes the translators job
unnecessarily harder.

https://bugzilla.gnome.org/show_bug.cgi?id=681270
2012-08-06 11:59:14 -04:00
fd3be5b7de build: Fix distcheck
I knew it had to be something simple ...
2012-08-06 16:13:54 +02:00
0b9a5c3441 build: Add noise-texture.png 2012-08-06 16:13:37 +02:00
7431c4d611 magnifier: Using properly 'color-saturation'
As description of the setting says, color-saturation ranges from
0.0 (grayscale) to 1.0 (full color), but the real outcome was the
opposite. The reason is that clutter provides a desaturation effect,
and color-saturation was passed directly to that effect. This patch
renames the effect and compute the desaturation value.
2012-08-06 13:02:42 +02:00
ec78dd60fc magnifier: 'color-saturation' is a double not a boolean 2012-08-06 13:02:42 +02:00
b479eec758 Updated Persian translations 2012-08-05 00:12:35 +04:30
413ace2eb1 UnlockDialog: allow going back to the lock screen by pressing escape
https://bugzilla.gnome.org/show_bug.cgi?id=681143
2012-08-03 19:54:48 +02:00
d8390ef77f autorunManager: Remove line that sets summary icon
MessageTray does this automatically for us now.

https://bugzilla.gnome.org/show_bug.cgi?id=680426
2012-08-03 14:26:31 -03:00
e7e56e175a boxpointer: Cope with a missing -arrow-border-color when we have no border
https://bugzilla.gnome.org/show_bug.cgi?id=680426
2012-08-03 14:26:24 -03:00
e875b9cdca status: Clean up imports
https://bugzilla.gnome.org/show_bug.cgi?id=680426
2012-08-03 14:25:35 -03:00
66197b18b6 lookingGlass: Don't pass too many arguments to Clutter.ungrab_*
https://bugzilla.gnome.org/show_bug.cgi?id=680426
2012-08-03 14:25:35 -03:00
ed991f79b1 theme: Use the noise texture for the background of the screen shield
https://bugzilla.gnome.org/show_bug.cgi?id=681143
2012-08-03 14:00:48 -03:00
48b70f358d GdmUtil: don't call GetUserVerifier from the user session
GetUserVerifier can only be called from the greeter session,
and fails with AccessDenied in all other cases. Also, calling it
hides the real error from OpenReauthenticationChannel, which
instead should be logged.

https://bugzilla.gnome.org/show_bug.cgi?id=680750
2012-08-03 18:06:10 +02:00
56adbda2dc build: Add missing file to Makefile 2012-08-03 17:41:56 +02:00
7dbc78c95f st-theme-node: Add repeating backgrounds
Add support for the CSS "background-repeat" property. Currently, this
only supports on/off, rather than allowing tiling in each individual
dimension. It is supported for both the cogl and cairo rendering paths.

https://bugzilla.gnome.org/show_bug.cgi?id=680801
2012-08-03 12:27:20 -03:00
3ffa1e35e8 st-theme-node: Clean up
Replace some faulty indentation, group some booleans to reduce
a bit of memory usage

https://bugzilla.gnome.org/show_bug.cgi?id=680801
2012-08-03 12:27:20 -03:00
114fb4219b tests: Fix borders test
Bad merge

https://bugzilla.gnome.org/show_bug.cgi?id=680801
2012-08-03 12:27:20 -03:00
4d7d66bc1f polkit: Improve styling in polkit dialog
Center the "Password:" label, and add a bit more spacing
2012-08-03 12:22:32 -03:00
e134d2e5a5 theme: Reformat
Put braces on the same lines as the selectors
2012-08-03 12:22:32 -03:00
cca84761a5 theme: Improve legibility of date and time in the lock screen
Adjust the drop shadow to make the date and time easier to read.
2012-08-03 12:22:22 -03:00
c2a5d6f61b theme: Update the look of scrollbars
Make the scrollbars consistent with those in Gtk.
2012-08-03 12:22:12 -03:00
4449007bb4 theme: Common style for entries
This improves the style of the password entry fields and makes them
consistent with entry fields in the overview and chat notifications.
2012-08-03 12:22:08 -03:00
30cd1e84bc theme: Fix unlock button colors
The colors for the new button were far too brash. This makes them
consistent with the mockups.
2012-08-03 12:22:04 -03:00
dcf872b485 js: Remove StatusIconDispatcher
With IBus in the top panel, we don't need it any more

https://bugzilla.gnome.org/show_bug.cgi?id=680800
2012-08-03 11:51:53 -03:00
c9d51ca775 Assamese translation updated 2012-08-03 15:20:28 +05:30
96c9f8058b loginDialog: Indicate whether users are logged in
Unlike the fallback gdm UI, we do not indicate in the user list
whether a user already has an open session or not. This information
is useful, so use a spotlight effect similar to the running-app
indicator to mark logged in users.

https://bugzilla.gnome.org/show_bug.cgi?id=658185
2012-08-01 22:20:35 +02:00
a3f4bca14e loginDialog: Add an :expanded pseudo class to the user list
We want to style user list items differently depending on whether
the list is expanded or shrunk; instead of manually updating the
items' style, we can just expose the :expanded style on the list
itself and use that in the CSS.

https://bugzilla.gnome.org/show_bug.cgi?id=658185
2012-08-01 22:20:35 +02:00
e55f2dd3b9 Updated Russian translation 2012-08-01 23:58:25 +04:00
247566ca1d st-scroll-bar: Fix build warnings
Introduced by commit aa120e0902
2012-08-01 17:38:45 +02:00
aa120e0902 st-scroll-bar: Add support for :active
Add the :active pseudo class to the handle while it is dragged.

https://bugzilla.gnome.org/show_bug.cgi?id=680974
2012-08-01 14:53:48 +02:00
5c7992beef loginDialog: Tweak automatic scroll animation
The current animation time of two seconds may result in some
confusion, as the reason of the behavior only becomes apprent
when the auto-activating item becomes visible; make the animation
a lot faster and ease it out a bit.

https://bugzilla.gnome.org/show_bug.cgi?id=660913
2012-07-31 17:50:52 +02:00
6a615f30dc gdm: Adjust timed-login indicator
Until the recent style changes, the same element was used to indicate
both item focus and progress for timed logins. As focus is now indicated
by the item's background style, rename the indicator from focusBin to
timedLoginIndicator and make some minor adjustments to better fit the
new style:
  - move it next to the icon below the text
  - give it a white color and a shadow
  - update animation to grow from the left instead of the center

https://bugzilla.gnome.org/show_bug.cgi?id=660913
2012-07-31 17:50:52 +02:00
bfea41b771 loginDialog: Update user list style
Rather than changing the text color to indicate hover and an underline
to mark the focused item, use the same semi-transparent white background
as in the overview.

https://bugzilla.gnome.org/show_bug.cgi?id=660913
2012-07-31 17:50:52 +02:00
a2465e0670 bluetooth: Adapt to GnomeBluetooth API break 2012-07-30 19:15:23 +02:00
57044ea9a0 Updated gujarati file 2012-07-30 12:48:18 +05:30
1392be2952 Updated Galician translations 2012-07-30 03:16:49 +02:00
9e503aa69c Updated Slovenian translation 2012-07-29 14:36:50 +02:00
a004389f25 Updated Hungarian translation by Bence Lukacs <lukacs.bence1 at gmail dot com> 2012-07-29 10:27:18 +02:00
d474261912 Updated Spanish translation 2012-07-27 11:33:32 +02:00
144e959cab Updated Arabic translation 2012-07-26 22:35:00 +02:00
626488e659 Updated Telugu Translation 2012-07-27 00:05:10 +05:30
5bae5574f0 Updated Kazakh translation 2012-07-26 09:32:22 +06:00
ce14d6d9db Updated Norwegian bokmål translation 2012-07-25 13:16:51 +02:00
519addf6ac Updated Hebrew translation. 2012-07-24 22:27:31 +03:00
fd87468e36 util: Fix number of parameters passed to spawn_sync
We passed one too many

https://bugzilla.gnome.org/show_bug.cgi?id=680426
2012-07-24 06:11:21 -03:00
75e49610cb st-theme: Make the custom stylesheets have higher priority
https://bugzilla.gnome.org/show_bug.cgi?id=680426
2012-07-24 06:11:21 -03:00
414fe75d02 st-widget: Move reactivity tracking to StWidget, use "insensitive"
This lets use remove another few pieces of code that do the tracking
manually.

https://bugzilla.gnome.org/show_bug.cgi?id=680426
2012-07-24 06:11:21 -03:00
26031eb761 Updated Greek translation 2012-07-24 12:10:32 +03:00
be801dff5f gdm: Add a missing comma 2012-07-21 14:08:04 -04:00
5fd47ed742 Updated POTFILES.in 2012-07-21 19:13:55 +02:00
f7f2f50435 ShellDBus: export screensaver interface
gnome-session and gnome-settings-daemon rely on the screensaver
interface to know the locked state. Since gnome-screensaver is no
longer running, it's up to gnome-shell to provide it.

https://bugzilla.gnome.org/show_bug.cgi?id=619955
2012-07-21 15:40:29 +02:00
e0bb15e572 Login Dialogs: update styling to match mockups
Remove backgrounds, change button styling, reduce font sizes.

https://bugzilla.gnome.org/show_bug.cgi?id=619955
2012-07-21 15:40:29 +02:00
0e4171a87c Screen Shield: animate manual locking
When the screen shield is activated from the user menu, animate
it instead of showing it abruptly. Also, ensure that the animation
had time to finish before calling UPower to suspend, to avoid
showing it when resuming.

https://bugzilla.gnome.org/show_bug.cgi?id=619955
2012-07-21 15:40:28 +02:00
34cb92ff4c Don't track the status of the screensaver on DBus
We are the screensaver now, and internal objects can track the
locking status better themselves. And to do so, add two signals
to ScreenShield.

https://bugzilla.gnome.org/show_bug.cgi?id=619955
2012-07-21 15:40:28 +02:00
22eea750f3 ScreenShield: show notifications in the locked screen
Track screen lock status in the message tray, and filter banner
notifications. The message tray is completely hidden when the screen is
locked, but exceptions can be made for individual transient notifications,
such as shell messages and the on screen keyboard.
Non transient sources are shown in the middle of the lock screen. Resident
notifications (such as those from Rhythmbox) are shown in full, while
persistent ones are displayed as icon and message count.

https://bugzilla.gnome.org/show_bug.cgi?id=619955
2012-07-21 15:40:28 +02:00
c3afe1a83a Show the panel above the screenshield when locked
Track locked status and use it to provide a reduced version of
the panel in the locked screen. Accessibility, input sources and
volume menus are preserved, without the link to the control center.
Network, battery and user menu are reduced to pure indicators,
with no menu.
This is similar to the design but not exactly, because designers
in IRC said that network needs more analysis before exposing, and
because the design didn't account for a11y and IM (so the one menu
metaphor is not really appropriate).

https://bugzilla.gnome.org/show_bug.cgi?id=619955
2012-07-21 15:40:28 +02:00
904ceba6b2 Add a clock to the lock screen
Start implementing the lock screen design by adding a big white
clock in the middle.

https://bugzilla.gnome.org/show_bug.cgi?id=619955
2012-07-21 15:40:28 +02:00
a28d639c3b Consolidate creation of login and unlock dialog in the screenshield
The design calls for the curtain to appear in the gdm greeter too.
Implement this by having the screenshield manage the login dialog
(delegating its creation to SessionMode).

https://bugzilla.gnome.org/show_bug.cgi?id=619955
2012-07-21 15:40:28 +02:00
c0652bcd8f ScreenShield: implement curtain design
This separates the screen shield into two main screens. One is
the lock screen, and it is shown when coming back from idle status
and when failing authentication. The other is the actual unlock
dialog.
Moving from the first to the second is possible by pressing Escape
or by dragging an arrow on the bottom on the screen.

https://bugzilla.gnome.org/show_bug.cgi?id=619955
2012-07-21 15:40:28 +02:00
54dc0fd123 ScreenShield: use LayoutManager for creating the actor
This ensures that the screen shield is created at the right
stacking level, so the message tray is visible in the lock screen
(showing PAM messages, critical notifications and the on screen
keyboard)

https://bugzilla.gnome.org/show_bug.cgi?id=619955
2012-07-21 15:40:28 +02:00
c22a00afee ScreenShield: improve locking/modal policy
Ensure that the lightbox is above everything (including the screenlock
itself) when fading in - this allows for fading while showing the
unlock dialog. Also, don't pushModal again when already locked, or
we won't get out of it.

https://bugzilla.gnome.org/show_bug.cgi?id=619955
2012-07-21 15:40:28 +02:00
2c3ec7846f Add UnlockDialog for unlocking the screen shield
When the screenshield is deactivated, instead of going back to the
session immediately, prompt the user for authentication.
This essentially reinstates what used to be provided by gnome-screensaver.

https://bugzilla.gnome.org/show_bug.cgi?id=619955
2012-07-21 15:40:28 +02:00
46db9edacc Split some common code out of gdm/loginDialog
This will be reused by session unlocking.

https://bugzilla.gnome.org/show_bug.cgi?id=619955
2012-07-21 15:40:28 +02:00
4e6fa56c87 screenShield: add initial functionality
We are replacing the gnome-screensaver module with with a screen shield
that is part of gnome-shell.

This patch fades out the screen on idle and shows a shield with a background
image when there is activity again. The shield can be removed with a key or
button press.

https://bugzilla.gnome.org/show_bug.cgi?id=619955
2012-07-21 15:40:28 +02:00
8970e17911 ModalDialog: add 'default' pseudo-class to default button
Add 'default' parameter to setButtons, that controls the binding
of Return (unless overridden) and applies the 'default' pseudo-class.
Currently it has no effect, but it will start having after the
login dialog redesign.

https://bugzilla.gnome.org/show_bug.cgi?id=619955
2012-07-21 15:40:28 +02:00
ac6c808124 MessageTray: rework icon handling to split model and view
To allow more than one summary icon actor for a source we split
the model of the source icon (which is iconName, if the default
implementation is used, or a GIcon otherwise) and replace
createNotificationIcon() with a generic createIcon(size). Also,
the actual source actor is split into a separate class, that handles
the notification counter automatically.

https://bugzilla.gnome.org/show_bug.cgi?id=619955
2012-07-21 15:40:28 +02:00
26d3b1929e St: don't attempt to give focus to non reactive actors
Non reactive actors don't expect to be interacted with, and thus
should not get keyboard focus.

https://bugzilla.gnome.org/show_bug.cgi?id=619955
2012-07-21 15:40:28 +02:00
a29507e452 St: add :disabled pseudo class when a button is not reactive
The :reactive property is used on StButton to like the :sensitive
property on GtkWidgets, that is, to indicate that the user is not
(yet) expected to click the button, and therefore should affect
styling too.
This allows to remove some code at the JS layer.

https://bugzilla.gnome.org/show_bug.cgi?id=619955
2012-07-21 15:40:28 +02:00
2c073fb005 UserMenu: split user avatar into its own widget
This way, it can be reused in the lock screen without code duplication.

https://bugzilla.gnome.org/show_bug.cgi?id=619955
2012-07-21 15:40:28 +02:00
d9c3b83d1e Panel: fix finding leftmost and rightmost buttons
Previous code would access the array element before checking that
the index was within bounds, and therefore cause a TypeError.
It wasn't noticed earlier because at least one visible children
is in each panel box in all session modes.

https://bugzilla.gnome.org/show_bug.cgi?id=619955
2012-07-21 15:40:27 +02:00
f5e58c500f Modal stack: fix handling of destroyed actors
Destroyed modal actors should be completely removed from the modal
stack automatically, including leaving modality if needed.
This allows for destroying modal dialogs without calling close().

https://bugzilla.gnome.org/show_bug.cgi?id=619955
2012-07-21 15:40:27 +02:00
5e865f5bc4 LayoutManager: reverse the visibleInFullscreen flag
Change visibleInFullscreen to be trackFullscreen. If true, visibility
is fully bound to fullscreen status, if false, no change is made.
This allows to avoid set_skip_paint(), while not messing with
visibility of actors that are sometimes hidden for other reasons.
The flag was reversed because only the panel uses it, so false is
a more useful default.

https://bugzilla.gnome.org/show_bug.cgi?id=619955
2012-07-21 15:40:27 +02:00
01a1255967 ShellEntry: make isPassword param changeable at runtime
Make it possible to control the visibility of "Show/hide text" item
at runtime, to reuse the same entry for both password and non-password
prompts.

https://bugzilla.gnome.org/show_bug.cgi?id=619955
2012-07-21 15:40:27 +02:00
dd80f39049 js: Spurious fixes for cases where we pass extra arguments
https://bugzilla.gnome.org/show_bug.cgi?id=680216
2012-07-19 11:01:03 -04:00
19e4c953ef js: Don't pass extra arguments to add_actor
It's clear that this was supposed to be passed to our own 'add' monkey
patch, but we missed. Remove the ignored arguments.

https://bugzilla.gnome.org/show_bug.cgi?id=680216
2012-07-19 11:01:03 -04:00
ef218c95fc Updated Slovenian translation 2012-07-19 10:10:55 +02:00
eec9dba855 data: Enable translations for desktop files
We were never localizing .desktop files, apparently.

https://bugzilla.gnome.org/show_bug.cgi?id=677893
2012-07-18 20:09:04 -04:00
f46a165886 loginDialog: Scale focusBin instead of resizing it
When setting an explicit size as we do currently, rounding errors
(for instance introduced by padding not specified in pixels) may
affect the parent's size allocation, e.g. making it shrink or grow
each time the size is reset.
Rather than taking care of possible rounding errors, set up focusBin
to take up the available width and use scaling for the animation.

https://bugzilla.gnome.org/show_bug.cgi?id=675076
2012-07-18 23:56:54 +02:00
ec47bd1604 build: Require gsettings-desktop-schemas >= 3.5.4
org.gnome.desktop.a11y.magnifier.color-saturation was shipped in
gsettings-desktop-schemas >=3.5.4

https://bugzilla.gnome.org/show_bug.cgi?id=680170
2012-07-18 09:20:25 -04:00
bfd4016c1c boxpointer style updates
Style updates to match current mockups. Give panel menus a
opaque black background, remove the border from notification
boxpointers and give them a bigger pointer.
2012-07-17 23:04:54 +01:00
63cdb1848e improve calendar layout and legibitily
This fixes some alignment issues with the calendar
events pane. It also makes some legitibility improvements
by using bigger fonts and clearer colors.
2012-07-17 23:04:24 +01:00
10a0762fe7 extensionPrefs: Fix
We accidentally broke two things here when overhauling the
extensions system.

https://bugzilla.gnome.org/show_bug.cgi?id=680064
2012-07-17 12:14:02 -04:00
76472b86ed util: Remove unused imports
https://bugzilla.gnome.org/show_bug.cgi?id=679944
2012-07-17 12:14:02 -04:00
edb96cde70 Bump version to 3.5.4
Update NEWS
Require Mutter 3.5.4
2012-07-17 17:54:41 +02:00
e06ecb8f0c gdm: port from libgdmgreeter to libgdm
When GDM was moved over to GDBus it dropped the libgdmgreeter
library and introduced a new libgdm library with a somewhat
different API.

The main differences in the API are:

1) open_connection is now implicit and automatic
2) conversations don't need to be started explicitly, they're
   started just-in-time when verification is requested
3) The functions are split up between the client, and new
   helper objects that correspond to the dbus interfaces
   they were generated from (one for user verification,
   one for greeter specific operations, and a couple more
   that aren't used by gnome-shell).
4) libgdm supports reauthenticating in an already running
   session, so user switching should now affect the users
   session more like screen unlocking does.

This commit moves the shell over to the new library.

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

https://bugzilla.gnome.org/show_bug.cgi?id=676401
2012-07-17 11:38:28 -04:00
2791d948e9 UserMenu: move Install Updates at the end
Follow the design update and move the Install Updates item at
the end, together with Power Off.

https://bugzilla.gnome.org/show_bug.cgi?id=680080
2012-07-17 12:35:08 +02:00
360e6e790a lookingGlass: Use one red border effect
This makes the code a bit cleaner, and reduces the chances of
having a border effect "leaking", or having two at the same time.

https://bugzilla.gnome.org/show_bug.cgi?id=679944
2012-07-16 20:39:55 -04:00
d0807c8276 lookingGlass: Don't use a global
As part of wanting to reuse some of the looking glass components,
don't use Main.lookingGlass, but instead pass the parent around.
Don't adjust the evaluator just yet, though. We'll split it into
a separate class soon.

https://bugzilla.gnome.org/show_bug.cgi?id=679944
2012-07-16 20:39:55 -04:00
75d0362cd8 link: Remove
The looking glass is the only consumer of the Link, and it's such a
basic wrapper around a button that it's not worth it.

https://bugzilla.gnome.org/show_bug.cgi?id=679944
2012-07-16 20:39:55 -04:00
4f7c554d8d lookingGlass: Remove old signal handler
This 'selected' signal is from the days of the "Heirarchy" tab, before
it was replaced by the "Windows" tab. Those were the days.

https://bugzilla.gnome.org/show_bug.cgi?id=679944
2012-07-16 20:39:55 -04:00
8b81f23caf lookingGlass: Use a more standard chevron
'>>>' is used by the REPL history (as well as plenty of other REPLs).
Using 'js>>>' to enter things makes the history look unaligned and strange.

https://bugzilla.gnome.org/show_bug.cgi?id=679944
2012-07-16 20:39:55 -04:00
0098c2b7f7 Move ibus status icon under keyboard
This makes sense, since we are covering input methods and keyboard
layouts in the same keyboard menu. The effect of this change is that
the ibus status icon no longer appears in the system status area.
2012-07-16 13:31:12 -04:00
f0e03b5e82 WorkspaceSwitcherPopup: fix for dynamic workspace changes
Changing the number of workspaces while the popup was visible (which
happens when moving windows on the last non empty workspace) resulted
in a wrong layout. Fix that, by listening to workspace-added and
workspace-removed signals, and by always requesting an updated size
from the actor.

https://bugzilla.gnome.org/show_bug.cgi?id=679005
2012-07-16 19:15:53 +02:00
be2f1001a5 Updated Spanish translation 2012-07-16 16:44:43 +02:00
cf08b4d56a Updated Telugu Translation 2012-07-16 08:14:26 +05:30
04074f883f ibusCandidatePopup: A candidate popup for IBus input methods
This is an implementation of the org.freedesktop.IBus.Panel API which
shows a shell style popup (BoxPointer) when using an IBus input
method.

Based on code from the ibus-gjs project[1].

[1] https://github.com/fujiwarat/ibus-gjs

https://bugzilla.gnome.org/show_bug.cgi?id=641531
2012-07-16 00:59:13 +02:00
14d3235f1a status/keyboard: Add support for IBus input sources
We connect to the IBus daemon asyncronously and use it to query info
about input sources of the type 'ibus'. In case the daemon is or
becomes unreachable we just skip showing input sources of this type.

https://bugzilla.gnome.org/show_bug.cgi?id=641531
2012-07-16 00:59:11 +02:00
e00c1cbd20 Updated Belarusian translation. 2012-07-15 11:52:59 +03:00
3df3f0d9dc portability: use /bin/sh instead of /bin/bash
There is nothing requiring bash specific features in this script, so
just use /bin/sh which is portable amongst all Unices.

https://bugzilla.gnome.org/show_bug.cgi?id=679847
2012-07-15 10:17:24 +02:00
985db40547 test-gapplication: Untabify, add modeline 2012-07-14 18:27:10 -04:00
c9fa0fdff0 .gitignore: Update 2012-07-14 18:27:10 -04:00
fe69ea305b calendar: Fix grid lines in RTL locales
The calendar grid is build by giving each element right and bottom
borders, all top-most elements a top border, and all left-most
elements a left border. However in RTL locales, we currently add
the left border to the *right-most* elements, resulting in the grid
appearing clipped on the left side.

https://bugzilla.gnome.org/show_bug.cgi?id=679879
2012-07-14 23:27:14 +02:00
ff9088e42b tests: Fix make dist 2012-07-14 11:48:54 +02:00
f556cdf0ca Updated Traditional Chinese translation(Hong Kong and Taiwan) 2012-07-14 14:00:32 +08:00
c671ff74c6 windowManager: Replace custom shader with builtin ClutterEffects
While modal dialogs were attached to the parent's titlebar, it
made sense to leave the top of the parent window at full color.
With the new position of modal dialogs, it makes more sense to dim
the entire parent window, so we can use a combination of Clutter's
BrightnessContrast- and DesaturateEffect instead of our own custom
shader.

https://bugzilla.gnome.org/show_bug.cgi?id=674499
2012-07-14 03:33:57 +02:00
04570ac783 windowManager: Remove 'animate' parameter from (un)dimWindow()
The parameter has become rather pointless since we always pass the
same value.

https://bugzilla.gnome.org/show_bug.cgi?id=674499
2012-07-14 03:33:57 +02:00
6ab25cd791 windowManager: Update animation of attached modals
With modal dialogs no longer being attached to their parents'
titlebar, the current animation no longer works too well. Use
a simple fade animation instead.

https://bugzilla.gnome.org/show_bug.cgi?id=674499
2012-07-14 03:33:56 +02:00
a04350f7ce windowManager: Split _shouldAnimate() into two functions
... instead of using an optional parameter.

https://bugzilla.gnome.org/show_bug.cgi?id=674499
2012-07-14 03:33:56 +02:00
b7018de7e0 magnifier: Fix grayscale effect
Commit 8754b2767c added a grayscale effect to the magnifier,
but missed actually adding it to the actor.

https://bugzilla.gnome.org/show_bug.cgi?id=674499
2012-07-14 00:46:32 +02:00
d212d57466 shell-embedded-window: Remove hacks for old and fixed Clutter bug
We used to use realize/unrealize instead of map/unmap in ShellEmbeddedWindow
because there originally was no map/unmap. The days of this are long gone...

https://bugzilla.gnome.org/show_bug.cgi?id=672790
2012-07-13 17:28:50 -04:00
c4e7d8ed8c Assamese translation updated 2012-07-13 18:30:31 +05:30
464813ecbb Assorted test-gapplication fixes
Move this test toghether with the others, and set the appmenu
only after the application is registered (i.e. in startup)

https://bugzilla.gnome.org/show_bug.cgi?id=678978
2012-07-13 12:29:44 +02:00
9812771dcd dash: hide tooltips when overview begins hiding
Otherwise the tooltip remains visible until the overview animation is
completed, which is odd.

https://bugzilla.gnome.org/show_bug.cgi?id=674241
2012-07-12 23:50:30 -04:00
6f605598de global: don't run a garbage collection on tweeners end
This currently causes the shell to freeze very often in a thread
deadlock, and the gjs garbage collector behavior is currently getting
fixed at the right level in gjs itself.

https://bugzilla.gnome.org/show_bug.cgi?id=679832
2012-07-12 20:11:39 -04:00
85bc8ccccc shell-global: remove shell_global_gc()
It's unused now.

https://bugzilla.gnome.org/show_bug.cgi?id=679832
2012-07-12 20:11:39 -04:00
e756c2dbce js: use System.gc() instead of shell_global_gc()
gjs now offers a gc() method in the System module, no need to use our
own.

https://bugzilla.gnome.org/show_bug.cgi?id=679832
2012-07-12 20:11:38 -04:00
e82fe14f00 shell-global: remove unused shell_global_maybe_gc()
It's not used anywhere, and we're removing the manual garbage collection
invocations anyway.

https://bugzilla.gnome.org/show_bug.cgi?id=679832
2012-07-12 20:11:38 -04:00
de65739c01 windowManager: Remove unused function
https://bugzilla.gnome.org/show_bug.cgi?id=679500
2012-07-12 10:11:57 -04:00
e1ec89a133 extensionSystem: Fix error emitting when enabling/disabling
We don't have an extension object here.
2012-07-11 22:46:59 -04:00
bdb3410d9d st-icon: Fix potential crash involving shadows
If the icon is painted before the new shadow is around, a stale
shadow material will be painted with a NULL shadow spec, resulting
in a crash.

https://bugzilla.gnome.org/show_bug.cgi?id=679776
2012-07-11 21:08:53 -04:00
168e0b5a42 mount-operation: implement show-unmount-progress
Show a notification when we receive a show-unmount-progress signal on
the mount operation we use for unmounting.
The notification will either turn fade out automatically with a
completion message when the unmount successfully completes, or will
disappear in case the operation is aborted underway (for example because
the device has been unplugged in the meantime).

https://bugzilla.gnome.org/show_bug.cgi?id=676125
2012-07-11 20:03:45 -04:00
f906cfe5f6 appMenu: Disable app menu during startup animations
When activating the app menu while displaying a startup notification
animation, the application shown in the menu does not match the
application providing the menu. To avoid this case, make the menu
button unreactive while playing the animation.

https://bugzilla.gnome.org/show_bug.cgi?id=672322
2012-07-10 22:36:45 +02:00
9faac81a37 panel: don't break when indicator has no menu
Status indicators don't have necessarily a menu. If they have not we don't
want to add the menu in the PopupMenuManager.

https://bugzilla.gnome.org/show_bug.cgi?id=678694
2012-07-10 22:21:07 +02:00
b90e7eb95c Updated Hebrew translation. 2012-07-10 23:09:02 +03:00
1e286e43ad extensionDownloader: Add update/blacklist support for extensions
This is a bare-bones copy/replace. It does not implement ChangeLog
support. If we cannot get System Updates integration, I will implement
notification support.

https://bugzilla.gnome.org/show_bug.cgi?id=679099
2012-07-10 14:36:41 -04:00
539993b4f4 extensionDownloader: Move extension loading code to the install dialog
Move the part that loads the extension to the callback. This makes the
next patch a lot cleaner.

https://bugzilla.gnome.org/show_bug.cgi?id=679099
2012-07-10 14:36:41 -04:00
1363d30f79 extensionUtils: Don't crash on startup for an empty directory
https://bugzilla.gnome.org/show_bug.cgi?id=679099
2012-07-10 14:36:41 -04:00
3a48daaa64 Don't build documentation for the recorder when it's disabled
When building with gtk-doc but no recorder, build fails trying to
get the GType of ShellRecorder. Fix it by skipping its header file
when disabled.
2012-07-10 19:36:57 +02:00
d2b4a65e65 Updated Norwegian nynorsk translation 2012-07-10 15:22:02 +02:00
1ead290c23 Updated Norwegian bokmål translation 2012-07-10 08:32:10 +02:00
6658660355 magnifier: fix a copy/paste typo
Introduced in 8754b2767c
2012-07-09 18:12:38 -04:00
85ab019987 Updated translations 2012-07-09 21:47:31 +02:00
460cda2aa1 recorder: Port to GStreamer 1.0 API
GStreamer developers are currently finishing the 0.11 version, which
will become 1.0. Port the screen recorder to the new API.

https://bugzilla.gnome.org/show_bug.cgi?id=679445
2012-07-09 16:35:06 +02:00
2d913578e1 messageTray: don't show the message tray when a new notification is shown
The message tray with its gradient background should only be displayed
when the summary is shown.

https://bugzilla.gnome.org/show_bug.cgi?id=677210
2012-07-08 12:56:09 -04:00
34831796f6 UserMenu: show "Install Updates & Restart" when appropriate
When PackageKit signals that it prepared an update, offer an option
to reboot and apply it, using a helper that will setup the next
reboot and then calling to gnome-session.

https://bugzilla.gnome.org/show_bug.cgi?id=677394
2012-07-07 18:21:35 +02:00
8754b2767c Add a grayscale effect
This commit adds a grayscale effect to the magnifier, similar to
the lightness, brightness and contrast effects that are already there.

The effect is configured with the
org.gnome.desktop.a11y.magnifier.color-saturation setting, which
can take values from 0.0 (grayscale) to 1.0 (full color).

Based on a patch by Matthias Clasen <mclasen@redhat.com>

https://bugzilla.gnome.org/show_bug.cgi?id=676782
2012-07-06 15:18:01 -04:00
04fb688f7d css: Re-add rule lost in commit fd256b6 2012-07-06 19:19:22 +02:00
58dbd285fc Updated Norwegian bokmål translation 2012-07-06 19:01:20 +02:00
cf6f149888 boxpointer: Change 'animate' parameter on show/hide to a bitmask
This allows us to have more control of the animation.

https://bugzilla.gnome.org/show_bug.cgi?id=678337
2012-07-06 16:04:42 +02:00
8d017ceaf1 boxpointer: Flip side if we would end outside the monitor
This flips the BoxPointer if it ends outside the monitor and we have
enough space to fit it inside, on the opposite side of the source
actor.

https://bugzilla.gnome.org/show_bug.cgi?id=678164
2012-07-06 16:03:59 +02:00
02428019fa messageTray: Remove unused method parameter
The alignment of the arrow tip regarding the source actor is set with
setSourceAlignment() and its default is already 0.5 .

https://bugzilla.gnome.org/show_bug.cgi?id=678164
2012-07-06 16:03:49 +02:00
b4464929cb Implemented banner support for the login screen
Based on a patch by Marius Rieder,
https://bugzilla.gnome.org/review?bug=665346
2012-07-06 09:01:25 -04:00
3ea22f8b0e Updated Spanish translation 2012-07-06 12:25:06 +02:00
9745e97e14 Add support for inhibiting automount
When connecting to virtual machines with usb-device redirection, such as Spice
enabled vms, automount may get in the way. Specifically if auto-usbredir is
enabled in the vm-viewer, then the usbredir code and the automount code race
for who gets to the device first.

If the automount code wins the race this is a problem, since usbredir causes a
device-disconnect (iow the usb mass storage driver sees an unplug), so in the
end usbredir always wins, and we end up with a non clean potentially corrupt
filesystem. Also see:
https://bugzilla.redhat.com/show_bug.cgi?id=812972

There for the need exists to be able to inhibit gnome-shell's automounting,
since all other inhibits run through gnome-session, I've chosen to do the same
for the automount-inhibiting. I've also submitted a patch to gnome-session to
reserve flag value 16 for this, see bug 678595.

This patch adds support to gnome-shell to honor this new inhibit flag.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>

https://bugzilla.gnome.org/show_bug.cgi?id=678597
2012-07-06 08:28:29 +02:00
6c6e182ecc Updated Bulgarian translation 2012-07-06 08:44:34 +03:00
7973dd45b7 Updated Bulgarian translation 2012-07-06 08:29:33 +03:00
a1837dde68 lookingGlass: Don't use a signal callback on 'paint' to draw the border
Instead, use a ClutterEffect, the proper API that has existed for since 1.0.
The 'paint' signal will go away for Clutter 2.0.

https://bugzilla.gnome.org/show_bug.cgi?id=679464
2012-07-05 14:20:50 -04:00
4448b65a18 telepathyClient: don't add log messages on presence changes
The log messages about presence changes unnecessarily cluttered the
notification.
Instead, we now present the presence states (online, offline, away, busy)
with an icon placed right next to the avatar. We also no longer show
notifications on presence changes.

https://bugzilla.gnome.org/show_bug.cgi?id=669508
2012-07-05 14:09:09 -04:00
2231c23c4d Updated Arabic translation 2012-07-05 16:58:17 +02:00
f17fc43d6e recorder: Don't crash on pipeline errors
In case of error, the recorder pipeline is reset to NULL, so don't
access current_pipeline->src in that case.

https://bugzilla.gnome.org/show_bug.cgi?id=679445
2012-07-05 16:23:21 +02:00
f9dbe56785 recorder-src: Use normal GObject boilerplate
Gst will drop its own boilerplate in 1.0, so just use G_DEFINE_TYPE.

https://bugzilla.gnome.org/show_bug.cgi?id=679445
2012-07-05 16:23:20 +02:00
8845a2170c networkAgent: use absolute path if configured
Signed-off-by: Clemens Buchacher <drizzd@aon.at>

https://bugzilla.gnome.org/show_bug.cgi?id=679212
2012-07-04 19:39:26 +02:00
a4b1ebd8c3 Assamese translation reviewed 2012-07-04 22:07:11 +05:30
66adeef9bd dateMenu: Use .desktop file to launch calendar
When selecting "Open Calendar" in the date menu, the configured
application is launched via command line, so we don't get any
startup notification. If Evolution is used as calendar application,
launch it via the .desktop file added by the last commit instead in
order to fix the issue.

https://bugzilla.gnome.org/show_bug.cgi?id=677907
2012-07-04 00:24:05 +02:00
20769f68a7 calendar-server: Add .desktop file for evolution calendar
When selecting "Open Calendar" in the date menu, the configured
application is launched via command line, so we don't get any
startup notification. In order to fix the issue at least for our
default calendar, add a hidden .desktop file for evolution's
calendar component.

https://bugzilla.gnome.org/show_bug.cgi?id=677907
2012-07-04 00:24:05 +02:00
e92719b98d userMenu: Don't disconnect account signals when disabled
By disconnecting the 'notify::connection-status' signal as soon as the account
is disabled, we were missing the signal telling us when the status was moving
from CONNECTING/CONNECTED to DISCONNECTED and so the status icon was never
updated.

What we really want is to disconnect the signal when the account is removed
from the account manager as we don't care about it any more.

https://bugzilla.gnome.org/show_bug.cgi?id=669112
2012-07-03 12:37:58 +02:00
59246babea search: Remove createResultContainer() hook
It is now unused since the contacts search provider was the only
consumer, so remove it.

https://bugzilla.gnome.org/show_bug.cgi?id=677442
2012-07-03 12:19:32 +02:00
23e86d7dd5 extensionDownloader: Clean up names of methods
FromUUID is redundant.

https://bugzilla.gnome.org/show_bug.cgi?id=679099
2012-07-02 18:29:56 -04:00
c99e8eb29d extensionSystem: Be saner at error handling
Use our native JS error system in the "extension system" API, only
using the signal/log-based error reporting at the last mile. Additionally,
delete the directory if loading the extension failed, and report the error
back over DBus.

https://bugzilla.gnome.org/show_bug.cgi?id=679099
2012-07-02 18:29:56 -04:00
7949397958 shellDBus: Add a real error reporting system to InstallExtensionRemote
Instead of using the 'extension-state-changed' signal to relay errors,
use DBus's native error mechanism to inform the method caller that the
call has failed. This requires making the method actually asynchronous
so that we don't block the browser, which is stuck waiting for a reply
from the browser plugin. To ensure this, we need to modify the browser
plugin API to ensure its extesion installation method is asynchronous.

Additionally, this lets us remove the awful, broken hacks that we used
when a user clicked the "Cancel" button, replacing it by a DBus return
value.

https://bugzilla.gnome.org/show_bug.cgi?id=679099
2012-07-02 18:29:56 -04:00
1d1359b58f extensionDownloader: Properly error out when downloading/parsing infos
When the extension downloader was originally designed, the information
downloading part was inserted at the last minute, along with the modal
dialog as a security feature to make sure an extension didn't silently
get installed on the user's machines either due to a security issue in
the browser-plugin, or an XSS issue on the extensions website. Correct
the mistake I made when writing the code; instead of dropping an error
on the floor, log it correctly. This "bug" has already bitten a number
of users who forgot to configure proxy settings in the control center.

https://bugzilla.gnome.org/show_bug.cgi?id=679099
2012-07-02 18:29:56 -04:00
8915bb4892 extensionDownloader: Fix errors during error paths during installation
https://bugzilla.gnome.org/show_bug.cgi?id=679099
2012-07-02 18:29:56 -04:00
6a117ac12f browser-plugin: Rework scriptable method argument parsing and dispatch
Since we're going to move to a much more complicated (async!) solution
in a little bit, we're going to require a lot more machinery to handle
that. To help with that, let's rework invocation dispatch so that it's
more generic. Introduce a parse_args system similar to gjs_parse_args,
use X Macros to help with the repetitive parts of the method dispatch.
This shouldn't cause any API breaks, so API_VERSION should still be 4.

https://bugzilla.gnome.org/show_bug.cgi?id=679099
2012-07-02 18:29:56 -04:00
67689f1a6d browser-plugin: Prevent a copy when checking for valid extension UUIDs
Instead of using g_strndup to copy the NPString that gets passed to us
by the plugin host, just use the equally-as-good NPString directly. We
still need to copy the string when passing it over DBus, as there's no
easy way to construct a string GVariant from a length-prefixed string.

https://bugzilla.gnome.org/show_bug.cgi?id=679099
2012-07-02 18:29:56 -04:00
48b83f1ffd extensionDownloader: Fix loading of downloaded extensions
We refactored the ExtensionSystem API to take an extension object,
but forgot to update the downloader.

https://bugzilla.gnome.org/show_bug.cgi?id=679099
2012-07-02 18:29:56 -04:00
7da0f398a5 shellDBus: Split extensions API out into a separate DBus interface
The generic "Shell" interface was getting a bit too crowded.

https://bugzilla.gnome.org/show_bug.cgi?id=679099
2012-07-02 18:29:56 -04:00
7e277fdd4a shellDBus: Clean up 2012-07-02 18:29:55 -04:00
5d10d8566b Updated Vietnamese translation 2012-07-01 11:05:01 +07:00
a10295f584 po/vi: import from Damned Lies 2012-07-01 11:02:53 +07:00
46cf9faa11 PolkitAgent: Look for the right password prompt
Pam seems to give us different strings, sometimes 'Password:',
sometimes 'Password: '. Look for both of these when replacing
them with a translated prompt.

https://bugzilla.gnome.org/show_bug.cgi?id=675300
2012-06-29 17:55:20 -04:00
971341bb53 Updated POTFILES.in 2012-06-29 19:44:23 +02:00
2b2a235a49 network: don't ask for always-ask secrets when interaction isn't allowed
Clearly a typo...

https://bugzilla.gnome.org/show_bug.cgi?id=679091
2012-06-29 09:32:45 -05:00
970b9deeaa shell: Remove ContactSystem
Remove the now unused ShellContactSystem. As a side effect, we no
longer depend on folks now.

https://bugzilla.gnome.org/show_bug.cgi?id=677442
2012-06-29 11:21:50 +02:00
fd256b624c overview: Remove contacts search provider
With gnome-contacts implementing a remote search provider, the
built-in contacts search is now unnecessary duplication; remote it.

https://bugzilla.gnome.org/show_bug.cgi?id=677442
2012-06-29 11:21:50 +02:00
698fb64be9 userMenu: Move "Power off" item to the bottom
The current position below "System Settings" is problematic - the
items are unrelated, and misclicks will result in a scary system
modal dialog that has to be cancelled.
Move items around a bit to avoid this problem.

https://bugzilla.gnome.org/show_bug.cgi?id=678887
2012-06-29 11:21:50 +02:00
9561f77b17 Main: fix ctrl-alt-arrow in the overview
Fixes a regression introduced in de72065, which changed the method names
but forgot to update this code path.

https://bugzilla.gnome.org/show_bug.cgi?id=679005
2012-06-27 22:17:47 +02:00
c3d3d346d4 WindowManager: ignore ctrl-alt-left/right
Fixes a regression introduced in de72065a

https://bugzilla.gnome.org/show_bug.cgi?id=679005
2012-06-27 22:17:46 +02:00
e43fe98263 windowManager: Hold the window on the stage while going between workspaces
Right now the "move to workspace" keyboard shortcut transition isn't smooth. It
shows the window disappearing, hopping to the next workspace, and then sliding
into view. "Pin" the window to the stage while the animation is in progress,
then release it afterwards.

https://bugzilla.gnome.org/show_bug.cgi?id=660839
2012-06-27 19:35:37 +02:00
04dbf15d9b WindowManager: handle move-to-workspace keybindings
Install a custom handler for move-to-workspace-* keybindings that
shows the workspace switcher, which gives the user a sense of
direction when navigating with the keyboard.

https://bugzilla.gnome.org/show_bug.cgi?id=674104
2012-06-27 19:35:37 +02:00
de72065a4a WorkspaceSwitcher: simplify code for handling keybindings
Most of code implementing workspace switches was repeated with
minor differences on each direction. Instead, consolidate it
and use the new meta_workspace_get_neighbor.

https://bugzilla.gnome.org/show_bug.cgi?id=674104
2012-06-27 19:35:37 +02:00
a1bb0ec738 WindowTracker: fix ref counting bug in get_app_for_window()
get_app_for_window() is (transfer full), but shell_app_system_lookup_wmclass()
is (transfer none), so we must reference the result, or crash
occur.

https://bugzilla.gnome.org/show_bug.cgi?id=678992
2012-06-27 19:27:30 +02:00
1a33cd9584 Updated Arabic translation 2012-06-27 12:49:54 +02:00
00279dbd04 theme: Center date label in calendar 2012-06-26 20:34:01 +02:00
eb759cf22f dateMenu: Fix regression that caused no date to be displayed
Commit ef0aa65774 broke the date
display; bring it back.
2012-06-26 14:00:35 -04:00
ae16da4e81 Fix wrong result handling of remote calls
When using dbus-glib, single return values were special-cased to
be returned verbatim rather than as array with a single element.
This is no longer true since switching to GDBus, so fix the places
where the change was overlooked.

https://bugzilla.gnome.org/show_bug.cgi?id=678852
2012-06-26 18:06:26 +02:00
965287e724 Updated Slovenian translation 2012-06-26 09:20:47 +02:00
3d5312e8d2 Bump version to 3.5.3
Update NEWS
Require Mutter 3.5.3
2012-06-26 00:03:48 +02:00
bc91b7dcae remoteSearch: Parse 'DesktopId' field in key file
We strongly expect applications to use the same values for the
'Title'/'Icon' fields in their search provider .ini file as the
'Name'/'Icon' fields in their .desktop file. Rather than requiring
applications to duplicate those fields, allow them to specify a
'DesktopId' field instead to point to the corresponding .desktop
file, which makes it possible to ship search provider files without
translatable strings (which is nice given that merging translations
into search provider files lacks a standard rule).

https://bugzilla.gnome.org/show_bug.cgi?id=678816
2012-06-25 22:57:31 +02:00
4d77eb94ff remoteSearch: Use GIcon instead of icon name
Currently we pass an icon name for the 'icon' parameter of the
RemoteSearchProvider constructor. In hindsight, using a GIcon
instead will give us a bit more flexibility, so change it.

https://bugzilla.gnome.org/show_bug.cgi?id=678816
2012-06-25 22:57:31 +02:00
1b8d03f945 St: reference the StTheme from StThemeNode
This is a partial revert of 7eaf231e56,
which caused segfaults. For some reason, StThemeNodes seem to survive
style changes.
2012-06-25 14:12:07 +02:00
de2dcfeb99 Update Punjabi Translation 2012-06-25 06:56:00 +05:30
c7196a519f tests: Run each test in a function
As the global object of a context is rooted, if we want the GC to act
on these objects we need to take them out of the globals.

https://bugzilla.gnome.org/show_bug.cgi?id=678737
2012-06-24 19:20:48 -04:00
0d82ce5210 tests: Don't use the default stage
https://bugzilla.gnome.org/show_bug.cgi?id=678737
2012-06-24 19:20:48 -04:00
3ce9ad05b3 run-js-test: GC twice after running a test
When running with Valgrind, this helps us ensure that we're managing
memory correctly. We need to GC twice as finalizing an object in the
sweep can unroot objects which were already marked. Technically, it
could be that we'll need to GC more than twice, but GCing twice should
hopefully last us for now.

https://bugzilla.gnome.org/show_bug.cgi?id=678737
2012-06-24 19:20:48 -04:00
ab75faac74 test-theme: Clean up after ourselves
https://bugzilla.gnome.org/show_bug.cgi?id=678737
2012-06-24 19:20:48 -04:00
9e25e13218 test-theme: Fix
St requires GTK+ to be initted.

https://bugzilla.gnome.org/show_bug.cgi?id=678737
2012-06-24 19:20:48 -04:00
69e1503c6d st-widget: Free the inline style
https://bugzilla.gnome.org/show_bug.cgi?id=678737
2012-06-24 19:20:47 -04:00
7eaf231e56 st-theme-node: Don't hold a ref to the theme context or the theme
This would just be a circular reference, which we need to avoid.

https://bugzilla.gnome.org/show_bug.cgi?id=678737
2012-06-24 19:20:47 -04:00
a347a72091 st-widget: Fix st_widget_set_theme
https://bugzilla.gnome.org/show_bug.cgi?id=678737
2012-06-24 19:20:47 -04:00
560daec913 st-widget: Chain up in the dispose handler for accessibility objects
https://bugzilla.gnome.org/show_bug.cgi?id=678737
2012-06-24 19:20:47 -04:00
d9e968d863 Updated Galician translations 2012-06-24 23:34:42 +02:00
556d5d181e st-widget: fix GList leak
clutter_actor_get_children returns a newly allocated GList and it was
not freed.

However, as there's no reason to copy the children list, switch to
iterator api.

https://bugzilla.gnome.org/show_bug.cgi?id=678406
2012-06-24 00:24:42 -04:00
4e4092f9e8 pokit-agent: fix memory leak
https://bugzilla.gnome.org/show_bug.cgi?id=678406
2012-06-24 00:24:42 -04:00
fd62aba71c Updated Greek translation 2012-06-23 01:30:41 +03:00
ef0aa65774 clock: Switch to using GnomeWallClock, delete clock GSettings controls
This avoids us having to poll once a second, among other things.  For
more information, see the linked bug chain.

See https://bugzilla.gnome.org/show_bug.cgi?id=657958 for the new
clock keys.

https://bugzilla.gnome.org/show_bug.cgi?id=657074
2012-06-22 18:10:46 -04:00
6b5f9a647a mount-operation: implement org.Gtk.MountOperationHandler
Use the ShellMountOperation dialogs we have to implement a DBus API
allowing other processes to display them.
Since GtkMountOperation now tries to call into our DBus implementation,
every application that uses a GtkMountOperation will gain integration
with our shell dialogs (but will still handle the actual communication
with GVfs).

https://bugzilla.gnome.org/show_bug.cgi?id=678516
2012-06-22 15:55:56 -04:00
01c07fbea1 userMenu: Rename 'Hidden' to 'Invisible'
Apparently only the Telepathy API calls this status 'Hidden',
Empathy and most network use 'Invisible' (also see
https://bugzilla.gnome.org/show_bug.cgi?id=603472).

https://bugzilla.gnome.org/show_bug.cgi?id=658955
2012-06-22 14:00:32 +02:00
81929c2a92 Updated Greek translation 2012-06-22 13:43:03 +03:00
d9ff8e3122 Updated Slovenian translation 2012-06-22 09:36:52 +02:00
0c4692ae58 appDisplay: Fix recursive directory NoDisplay testing
We were accidentally testing NoDisplay on the wrong directory.

https://bugzilla.gnome.org/show_bug.cgi?id=658176
2012-06-21 16:03:48 -04:00
df56ff4f09 app-system: Don't show items with NoDisplay parents in the search
Additionally, require gnome-menus 3.5.3, as we're using new API
introduced there.

https://bugzilla.gnome.org/show_bug.cgi?id=658176
2012-06-21 16:03:48 -04:00
96cdc9c4eb app-system: Use g_slist_free_full
https://bugzilla.gnome.org/show_bug.cgi?id=658176
2012-06-21 16:03:48 -04:00
3b4ad5cd7d app-system: Clean up imports
https://bugzilla.gnome.org/show_bug.cgi?id=658176
2012-06-21 16:03:48 -04:00
317c6b77f3 Util: update trySpawn to new gjs GError mapping
Error rewriting code used an old version of the gjs GError support,
and set a readonly .message property.

https://bugzilla.gnome.org/show_bug.cgi?id=678502
2012-06-21 18:28:26 +02:00
e5f5a2adaa Updated Spanish translation 2012-06-21 12:30:52 +02:00
594c3174ab mount-operation: fix indentation for some callbacks
Nothing to see here, move along.

https://bugzilla.gnome.org/show_bug.cgi?id=674962
2012-06-20 22:21:23 -04:00
5b7e4bb4a7 mount-operation: don't use global.get_current_time()
If we don't specify it, it will be picked up by default.

https://bugzilla.gnome.org/show_bug.cgi?id=674962
2012-06-20 22:21:23 -04:00
f7c0f826d4 autorun: use GError.matches() to fix some FIXMEs
Discard FAILED_HANDLED errors, now that we can detect them from JS.

https://bugzilla.gnome.org/show_bug.cgi?id=674962
2012-06-20 22:21:23 -04:00
66af7de6d6 automount: re-use the same dialog when passphrase doesn't match
Wait until the completion of the mount operation before dismissing the
passphrase dialog, so in case it fails, we can re-use the same dialog
with an error message (like e.g. PolicyKit auth dialogs) instead of
showing a brand new one.

https://bugzilla.gnome.org/show_bug.cgi?id=674962
2012-06-20 22:21:23 -04:00
d1815a36d0 mount-operation: turn the passphrase prompt into a dialog
Instead of a notification. This also adds space for a checkbox allowing
to remember the passphrase.

https://bugzilla.gnome.org/show_bug.cgi?id=674962
2012-06-20 22:21:23 -04:00
61de3de909 NetworkMenu: prefer wifi/3g over vpn in the panel
Wifi and mobile broadband have signal indicators and are thus
more useful than vpn icons in the panel. Therefore, in the case
we have both wifi/3g and VPN we prefer the former as the "primary
icon" and add a lock next to it.
Behavior when VPN is added to wired or other connections is still
preserved: the wired icon is replaced by vpn.

https://bugzilla.gnome.org/show_bug.cgi?id=672591
2012-06-20 17:23:53 -04:00
36888a34d6 NetworkMenu: sort wifi networks by strength
Sorting by strength is what the other OSes do by default, and it
provides a better UX (by offering your hotspot and router before
the one from your neighbor).

https://bugzilla.gnome.org/show_bug.cgi?id=658946
2012-06-20 23:05:57 +02:00
646435ee3e automount: don't allow autorun for things we mount at startup
We don't want to potentially flood the user with notifications about
mounts when starting up the system.

https://bugzilla.gnome.org/show_bug.cgi?id=660595
2012-06-20 10:15:37 -04:00
cbb8d5b0dc autorun: only obey allowAutorun flag for transient notifications
Only apply the allowAutorun flag for transient notifications, not for
mounts that end up in the resident notification well.

Also, stop looking at volume.can_automount() here, since we already
checked that previously in the mounter, and allowAutorun is enough.

https://bugzilla.gnome.org/show_bug.cgi?id=660595
2012-06-20 10:15:29 -04:00
ebee01b355 autorun: simplify code
Split autorun conditions into separate logic blocks instead of a single
huge if.

https://bugzilla.gnome.org/show_bug.cgi?id=660595
2012-06-20 10:15:22 -04:00
0e3795b2f3 autorun: change logic for ignoring volumes
Previously, a volume was being ignored from autorun if one of these two
conditions were met:
- its mount root file had a native scheme and was mounted in a
  non-hidden location
- it had a volume that could have been automounted, and had a flag set
  by the shell to allow autorun

In order to effectively ignore volumes that we don't mount ourselves
from our notification system, we have to meet both conditions at the
same time instead.

https://bugzilla.gnome.org/show_bug.cgi?id=660595
2012-06-20 10:15:16 -04:00
de73128d47 mount-operation: close the password entry on operation abort
If the device disappears in the middle of a mount operation, make sure
to dismiss the password prompt automatically.

https://bugzilla.gnome.org/show_bug.cgi?id=673787
2012-06-20 10:14:07 -04:00
281b0a3e63 mount-operation: fix exceptions when showing password entry
This is a fallout from some changes in MessageTray.Source, which now
requires either defining the iconName/iconType properties on it, or
implementing createNotificationIcon, and we're not doing any of those.
Fix it by storing the gicon of the source object and using a helper
method to create the icon actor on demand, to avoid any case when the
same actor might be added twice to different containers.

https://bugzilla.gnome.org/show_bug.cgi?id=678428
2012-06-20 10:07:50 -04:00
c414f9ac9d build: Fix the case of building without folks 2012-06-19 23:09:40 +02:00
77242cfec0 build: Make folks optional
The dependency chain spirals out from folks->zeitgeist->xapian...and
I'm really not interested in pulling in all of that into the core
shell.

There is work on splitting out contact search into gnome-contacts; I'd
add a bug link but Bugzilla is down.
2012-06-19 16:34:23 -04:00
8ebbba6eb8 Disable unredirection when a modal operation is active
When the shell takes control of the screen (for example to show
a modal dialog or to lock the screen), it must reestablish itself
on top of the stack, and in particular restore any unredirected
window so that it is composited below the Shell UI.

Reviewed-By: drago01 in IRC.
2012-06-19 19:56:06 +02:00
0804cefbee workspace: Minor cleanup
_hideAllOverlays() has only been used in a single place for quite
some time now, so move its code into the caller and save a loop.

https://bugzilla.gnome.org/show_bug.cgi?id=678416
2012-06-19 18:24:52 +02:00
2404d2935d workspace: Remove unused method
This one hasn't been used for quite a while now ...

https://bugzilla.gnome.org/show_bug.cgi?id=678416
2012-06-19 18:24:51 +02:00
e6f5e21b5d Enable the Screen Reader menu item
https://bugzilla.gnome.org/show_bug.cgi?id=663256
2012-06-19 12:21:05 -04:00
c303c6b5c1 shell-app: Update app menu if necessary
Currently we assume that GTK_UNIQUE_BUS_NAME is shared between all
windows of an application. This assumption does not hold true for
applications that specify G_APPLICATION_NON_UNIQUE, so make sure
to update the menu as necessary.

https://bugzilla.gnome.org/show_bug.cgi?id=676238
2012-06-19 17:57:34 +02:00
f58e8f2a35 Depend on gjs 1.33.2
Shell uses the 'format' module which has been introduced in gjs 1.33.2.

https://bugzilla.gnome.org/show_bug.cgi?id=678396
2012-06-19 14:17:59 +02:00
ededba0c6d telepathyClient: ignore invalidated channels
There is a race if a channel is invalidated during its preparation: the
'invalidated' signal is already emitted so the Shell will never notice.
We fix this by simply checking if the channel is already invalidated when
receiving it from telepathy-glib.

In the approving case, we reject the full ChannelDispatchOperation as we only
support approving one channel at the time.

https://bugzilla.gnome.org/show_bug.cgi?id=677457
2012-06-18 09:56:12 +02:00
e112fa92fe telepathyClient: decline dispatch op when approving unsupported channel type
It shouldn't happen in theory but best to be safe than sorry.

https://bugzilla.gnome.org/show_bug.cgi?id=677457
2012-06-18 09:56:12 +02:00
7524210d1f st-texture-cache: fix GtkIconInfo leak
https://bugzilla.gnome.org/show_bug.cgi?id=678079
2012-06-15 01:05:08 -04:00
201dc05416 ShellContactSystem: fix GSList of utf8 leak
https://bugzilla.gnome.org/show_bug.cgi?id=678079
2012-06-15 01:05:08 -04:00
2b34978993 ShellContactSystem: fix GSList leak
https://bugzilla.gnome.org/show_bug.cgi?id=678079
2012-06-15 01:05:08 -04:00
447246da74 shell-util: fix GFile leak
https://bugzilla.gnome.org/show_bug.cgi?id=678079
2012-06-15 01:05:08 -04:00
1cf2bb6646 ShellContactSystem: fix GeeMapIterator leak
https://bugzilla.gnome.org/show_bug.cgi?id=678079
2012-06-15 01:05:08 -04:00
de1eafb564 Always enable a11y
https://bugzilla.gnome.org/show_bug.cgi?id=678095
2012-06-14 18:24:57 +01:00
22f0099a5d main: NO_GAIL envvar is a noop
It's not used by GTK+ versions we require

https://bugzilla.gnome.org/show_bug.cgi?id=678096
2012-06-14 18:24:57 +01:00
300eb87016 Updated Arabic translation 2012-06-13 20:40:27 +02:00
aa38b16368 l10n: Updated Italian translation 2012-06-13 19:37:39 +02:00
266bfdf739 Updated POTFILES.in 2012-06-12 23:53:31 +02:00
19ef6b0421 shellDBus: Add a method to reload extensions
A common request from extension developers has been to reload their
extensions without restarting the Shell.

https://bugzilla.gnome.org/show_bug.cgi?id=677586
2012-06-12 17:16:09 -04:00
75570b7995 extensionSystem: Add a method to unload an extension
https://bugzilla.gnome.org/show_bug.cgi?id=677586
2012-06-12 17:16:09 -04:00
3290bfae68 extensionSystem: Remove the two-step initialize
Initially, extensions were loaded after they shell had fully created
the session and all objects, but this didn't allow extensions easy
ways to monkey patch prototypes, as most functions had already been
bound. Remove the historical vestigal function, and just merge the
two together.

https://bugzilla.gnome.org/show_bug.cgi?id=677586
2012-06-12 17:16:09 -04:00
0805d7a35f extensionSystem: Make the init function optional
A large amount of extensions have something like this in them:

    function init() {}

Since we have encouraged extension authors to try and not make any
changes in init, it feels weird and strange to have to create an
initialization function that does nothing. From now on, don't require
it.

https://bugzilla.gnome.org/show_bug.cgi?id=677586
2012-06-12 17:16:09 -04:00
11278a0814 extensionSystem: Load/unload stylesheets on enable/disable
I'm not entirely sure why this wasn't caught earlier.

https://bugzilla.gnome.org/show_bug.cgi?id=677586
2012-06-12 17:16:08 -04:00
86de6f5861 extensionUtils: Create and load the extension object when scanning
This reduces some duplicate code when loading extensions.

https://bugzilla.gnome.org/show_bug.cgi?id=677586
2012-06-12 17:16:08 -04:00
498b023989 extensionUtils: Use signals rather than callbacks for finding extensions
This allows us to move to a file-monitor based approach in the future.

Since we need signals, we convert the current set of functions to an
object we attach signals too, leading to the new ExtensionFinder object.

https://bugzilla.gnome.org/show_bug.cgi?id=677586
2012-06-12 17:16:08 -04:00
5265884af9 extensionUtils: Remove userExtensionsDir
Make this less stateful

https://bugzilla.gnome.org/show_bug.cgi?id=677586
2012-06-12 17:16:08 -04:00
cdbe0bbf38 extensionUtils: Remove version check for js-version
This is seldomly used, and isn't checked in SweetTooth. Just remove
this inconsistency here rather than adding infrastructure to manage
and check it elsewhere.

https://bugzilla.gnome.org/show_bug.cgi?id=677586
2012-06-12 17:16:08 -04:00
feef35a8ca extensionUtils: Don't write to the filesystem on start
Create the potentially empty directory when we need to, not when we
don't.

https://bugzilla.gnome.org/show_bug.cgi?id=677586
2012-06-12 17:16:08 -04:00
3ff51da529 extensionSystem: Split off the extension downloader into a separate file
This keeps all UI out of the extension system, leaving it strictly for
loading and unloading extensions.

https://bugzilla.gnome.org/show_bug.cgi?id=677586
2012-06-12 17:16:08 -04:00
496e9f7b16 st: Trigger theme updates on resolution changes
Commit de8a66d4ce removed our own DPI handling for the one found
it Clutter, but broke resolution updates at runtime (for instance
when setting the "Large Text" option in Universal Access).

https://bugzilla.gnome.org/show_bug.cgi?id=677975
2012-06-12 22:40:59 +02:00
0b30dc29a5 Updated Lithuanian translation 2012-06-12 12:19:33 +03:00
15b4d29e70 Updated Norwegian bokmål translation 2012-06-11 22:04:54 +02:00
9eee4b7687 Fix name for the schemas file 2012-06-11 22:04:51 +02:00
196f6c241a appDisplay: Don't show apps in NoDisplay categories in the All view
We explicitly include NoDisplay applications in the ShellAppSystem because
we want app tracking for them, but we explicitly filter NoDisplay applications
out when showing them to the user because we don't want to show them to the
user. We also based our "All" apps view on a flattened list of apps. While
we did check for NoDisplay on the app item itself, we didn't check against
its parents. Refactor the app display view to not use a separate flat list
of applications, but instead a concatenation of all the applications in all
the loaded categories.

https://bugzilla.gnome.org/show_bug.cgi?id=658176
2012-06-11 13:55:59 -04:00
c9296191a8 js: Remove unnecessary versions of clutter_actor_get_children
clutter_actor_get_children requires making a temporary GSList from
a linked list structure, and then creating a JS Array from that GSList.
For simple cases like the number of children, use clutter_actor_get_n_children.

https://bugzilla.gnome.org/show_bug.cgi?id=677426
2012-06-11 13:10:25 -04:00
c7b4022283 extensionSystem: Initialize the HTTP session after parse time
We really shouldn't be creating GObjects while we're still parsing other
files for performance and clarity reasons.

https://bugzilla.gnome.org/show_bug.cgi?id=677586
2012-06-11 13:07:40 -04:00
a7ecc4cdd6 Revert "extensionSystem: Initialize the HTTP session after parse time"
This reverts commit df5298d59c.
2012-06-11 13:06:14 -04:00
df5298d59c extensionSystem: Initialize the HTTP session after parse time
We really shouldn't be creating GObjects while we're still parsing other
files for performance and clarity reasons.
2012-06-11 13:04:56 -04:00
b79d17332e data: Actually substitute @GETTEXT_PACKAGE@ in schema files
https://bugzilla.gnome.org/show_bug.cgi?id=677497
2012-06-11 13:00:33 -04:00
675572a41a Updated Greek translation 2012-06-11 18:31:33 +03:00
0078fe9349 Updated Russian translation 2012-06-11 10:38:29 +04:00
dc004f6eb7 modalDialog: show dialogs on monitor with the mouse pointer
Show the dialog on the monitor containing the pointer, rather than
the monitor with active focused window. This brings it inline with
the behaviour seen when launching applications.

Remove the focusMonitor/focusIndex from LayoutManager. These
properties were only used by the modal dialogs. Remove them since
they are not being used elsewhere.

https://bugzilla.gnome.org/show_bug.cgi?id=642591
2012-06-08 23:05:56 -04:00
07f1a05ab4 Updated Slovenian translation 2012-06-08 21:29:52 +02:00
54292a99af calendar-server: Remove left-over include
GConf-free at last! GConf-free at last!
2012-06-08 19:08:28 +02:00
f5933c8cb8 messageTray: Don't show non urgent notifications while in fullscreen
Don't show non urgent notifications when the primary monitor
is in fullscreen (user watching a movie, having a presentation,
playing a game ...).

Once the user leaves fullscreen show the messagetray so that the user don't
miss any notification (same as in "back from idle").

https://bugzilla.gnome.org/show_bug.cgi?id=677590
2012-06-07 20:36:58 +02:00
6700f86145 LayoutManager: Add primary-fullscreen-changed signal
Add a signal to the LayoutManager which gets emitted once the primary monitor
enters or leaves the fullscreen state.

https://bugzilla.gnome.org/show_bug.cgi?id=677590
2012-06-07 20:36:48 +02:00
b31d22488e calendar: Adapt to Evolution-Data-Server API changes.
Adapt the calendar-server to some major API changes in E-D-S 3.5.3.

More details about the breakage:
http://mbarnes.livejournal.com/4631.html

https://bugzilla.gnome.org/show_bug.cgi?id=677402
2012-06-07 12:50:46 -04:00
6382eeb8fd network: Remove old API
The underscored properties were removed a while ago.

https://bugzilla.gnome.org/show_bug.cgi?id=677515
2012-06-05 18:10:25 -04:00
b07345a55c main: Adapt to new plugin API 2012-06-05 16:33:14 -04:00
06febdce22 Bump version to 3.5.2
Update NEWS
Require Mutter 3.5.2
2012-06-05 22:08:07 +02:00
c31d9d5e3d a11y: do not expose a label text if 'hidden' style class is used
https://bugzilla.gnome.org/show_bug.cgi?id=675341
2012-06-05 18:12:41 +02:00
cda8a545f1 css: add a 'hidden' style class
Used for elements that are not supposed to be shown.

https://bugzilla.gnome.org/show_bug.cgi?id=675341
2012-06-05 18:12:41 +02:00
f850e92524 popupMenu: do not overflow the currentItems
Do not overflow currentItems array. If the Menu section is filled
via model items-changed callback the position in the model passed to
the RemoteMenu _modelChanged can be a to be added asynchronously
action-added. Thus the item does not yet exists in the currentItems.

https://bugzilla.gnome.org/show_bug.cgi?id=676447
2012-06-05 16:28:40 +02:00
cc9d53e038 popupMenu: bypass changeSignal callback if action is already handled
https://bugzilla.gnome.org/show_bug.cgi?id=676447
2012-06-05 16:28:40 +02:00
7b048fc092 endSessionDialog: Factor out _updateDescription from _updateContent
_startTimer adds a tweener to implement the description countdown, which
updates the entire content on each iteration, including the icon. This
causes a significant impact on performance, especially when accessibility
is enabled, as it causes a flood of AtkObject:state-change:showing events.
As the countdown only affects the description, factor out _updateDescription
and use it in _startTimer, and only do a full update of all contents when
necessary.

https://bugzilla.gnome.org/show_bug.cgi?id=674210
2012-06-05 15:57:18 +02:00
4ba4a501fd Updated Spanish translation 2012-06-03 17:57:30 +02:00
2b2cce6896 Updated Galician translations 2012-06-03 15:02:43 +02:00
c1f51a7bf3 searchDisplay: Use the rowLimit we pass to the IconGrid
When displaying children, we need to make sure that we use the rowLimit
property that we pass to iconGrid, rather than assuming that it's
always MAX_SEARCH_RESULTS_ROWS in case some subclass (in an extension maybe)
wants to do something different.

https://bugzilla.gnome.org/show_bug.cgi?id=675527
2012-06-03 01:32:29 -04:00
465556f0d8 Updated Galician translations 2012-06-03 02:39:58 +02:00
db7ac5208a Patch for bug https://bugzilla.gnome.org/show_bug.cgi?id=673630 2012-06-02 15:45:55 +03:00
cfcd1bc014 Updated Hebrew translation. 2012-06-02 12:45:45 +03:00
e63f7e8779 userMenu: Indicate progress on status changes
Depending on the number of accounts, the type or simply the network,
there may be a noticable lag between setting the status and the actual
status change.
Use the new user-status-pending icon to indicate progress.

https://bugzilla.gnome.org/show_bug.cgi?id=659067
2012-06-01 19:53:44 -04:00
7911154bad NetworkMenu: show notifications for failed VPN connections
Refactor NMDeviceVPN to be more like the other NMDevices, including
having a valid getSectionTitle() and emitting signals when the
underlying connection changes state.
Use the existing notification infrastructure to hook these signals
to actual notifications (including some code consolidation).

https://bugzilla.gnome.org/show_bug.cgi?id=676330
2012-06-01 22:39:43 +02:00
ec0730f3e5 status/keyboard: Port to the new input sources settings
https://bugzilla.gnome.org/show_bug.cgi?id=641531
2012-06-01 22:18:59 +02:00
76005f5adf workspacesDisplay: Connect to 'notify::n-workspaces' early
Currently we only connect to the 'notify::n-workspaces' signal the
first time the overview is shown, which means we will miss any
changes to the workspace layout in the meanwhile.
In particular, the decision of whether the workspace switcher should be
shown is taken before the dynamic workspace handling takes over, and is
thus based entirely on the value of the num-workspaces user preference
rather than the actual number of workspaces.
Just connect the signal in _init() (with the nice side-effect to make it
explicit that the signal handler won't ever be disconnected).

https://bugzilla.gnome.org/show_bug.cgi?id=673198
2012-06-01 17:34:48 +02:00
94493cde35 a11y: setting the proper role/label_actor for SearchResult.content
role/label_actor should be used for the actor that will receive
the focus. In some cases it was also (wrongly) set on the
container, so using an AT like Orca, it exposed both.

https://bugzilla.gnome.org/show_bug.cgi?id=672242
2012-06-01 16:10:07 +02:00
a1f68720e5 telepathyClient: Remove C-based TelepathyLogger wrapper
gjs has matured enough to the point where we don't need this

https://bugzilla.gnome.org/show_bug.cgi?id=676850
2012-06-01 08:59:59 -04:00
022376dd56 Added contexts to various Unknown strings
Fixes bug https://bugzilla.gnome.org/show_bug.cgi?id=659968
2012-06-01 14:44:40 +02:00
934e5aacab notificationDaemon.js: convert the hints dict at the beginning of the function
It's used right away to discard some Empathy notifications.

This regression has been introduced during the 3.4 cycle when 'hints' has been
turned to a GVariant.

https://bugzilla.gnome.org/show_bug.cgi?id=675370
2012-06-01 09:40:32 +02:00
35b142f23f NetworkMenu: fix updating connection lists
Ensure that the UI is updated when a connection changes name or id,
even if it was already known by a device.
Also, use less private properties on NMConnection objects, as they
can become stale and cause problems.

https://bugzilla.gnome.org/show_bug.cgi?id=677097
2012-05-31 20:50:02 +02:00
41c3795a7b Add an initial-setup mode
Use the newly introduced kiosk mode functionality, and define an
'initial-setup' mode that is suitable for
https://live.gnome.org/GnomeOS/Design/Whiteboards/InitialSetup

https://bugzilla.gnome.org/show_bug.cgi?id=676697
2012-05-31 09:52:14 -04:00
95d7099133 [l10n] Update Japanese translation 2012-05-31 18:12:53 +09:00
33dc9abb9b Updated Norwegian bokmål translation 2012-05-30 14:19:28 +02:00
5e4edac14d browser-plugin: Fix Opera support
Opera now tries to call NP_SetWindow without trying to check
if it's a NULL pointer or not.

https://bugzilla.gnome.org/show_bug.cgi?id=672192
2012-05-29 18:57:05 -04:00
cb5ae92986 sessionMode: Add 'input-method' to status icons in user mode
We now drop all status icons that are not explicitly enabled, which
breaks the ibus icon. Add it to the list of allowed icons until we
merge it with the keyboard one.

https://bugzilla.gnome.org/show_bug.cgi?id=677058
2012-05-29 22:06:03 +02:00
ebbd295ebe panel: Refuse to add (legacy) status icons not part of the session mode
If a session mode does not use certain status items, we don't want their
fallback implementation to show up either, so filter them out.

https://bugzilla.gnome.org/show_bug.cgi?id=677058
2012-05-29 22:06:03 +02:00
65d23fb9a3 extensionUtils: Support subdirectories in getCurrentExtension
Some extensions may have complex layouts with multiple subdirectories.
Currently, getCurrentExtension doesn't support this, as it uses a regex
and assume's that the last path's component's parent is all that's needed.
Fix this.

https://bugzilla.gnome.org/show_bug.cgi?id=677001
2012-05-29 14:36:16 -04:00
ad6d986172 browser-plugin: Fix enable/disable
This code was broken at the last minute, and an accidentally stale plugin
copy kept it untested.

https://bugzilla.gnome.org/show_bug.cgi?id=676837
2012-05-29 14:36:16 -04:00
985641cc2e Updated Slovenian translation 2012-05-29 09:08:02 +02:00
de0a714081 Updated Lithuanian translation 2012-05-26 23:12:17 +03:00
3f942302d1 extensionSystem: Show an icon in the install dialog
https://bugzilla.gnome.org/show_bug.cgi?id=676837
2012-05-25 18:25:51 -04:00
96396163cf extensionSystem: Replace Shell.write_soup_message_to_stream with GBytes usage
https://bugzilla.gnome.org/show_bug.cgi?id=676837
2012-05-25 18:25:51 -04:00
0c736c4561 extensionSystem: Use Gio.File.new_tmp
https://bugzilla.gnome.org/show_bug.cgi?id=676837
2012-05-25 18:25:51 -04:00
33ad9d1035 extensionSystem: Clean up
https://bugzilla.gnome.org/show_bug.cgi?id=676837
2012-05-25 18:25:51 -04:00
d57658d059 extensionSystem: Remove custom CA file handling
libsoup (well, glib-networking) now handles this automatically

https://bugzilla.gnome.org/show_bug.cgi?id=676837
2012-05-25 18:25:51 -04:00
031206cf1f extensionSystem: Remove now-unused version_tag, clean up other code as well
https://bugzilla.gnome.org/show_bug.cgi?id=676837
2012-05-25 18:25:51 -04:00
12c7cc278d shellDBus: Remove now unused DisableExtension/EnableExtension
These methods were initially introduced when I was planning on having
an explicit DisableExtension/EnableExtension, instead of hooking up
a gsettings notify. This behavior was changed at the last minute, but
the methods were kept to avoid having to change the browser-plugin.

Consumers of this API should just set the GSettings key directly
instead now.

https://bugzilla.gnome.org/show_bug.cgi?id=676837
2012-05-25 18:25:51 -04:00
9d3750b9b8 browser-plugin: Set the 'enabled-extensions' GSettings key directly
This way, we won't have to shuttle over DBus if we want to set an extension's
status outside of a GNOME Shell session.

https://bugzilla.gnome.org/show_bug.cgi?id=676837
2012-05-25 18:25:51 -04:00
ba4b9f229e telepathyClient: fix isChat
As part of commit 9e1a2cf the parent _init was moved before the isChat
was set to true. As the parent set it to false isChat is never true
since this commit.

https://bugzilla.gnome.org/show_bug.cgi?id=676806
2012-05-25 13:14:43 -04:00
850fe98cbb Updated Hebrew translation. 2012-05-25 10:39:30 +03:00
8a9e3e0df2 Switch string formatting to the one inside gjs
https://bugzilla.gnome.org/show_bug.cgi?id=675479
2012-05-24 15:38:44 -04:00
a277569d31 Revert "main: Make sure that --mode parameter is valid"
This reverts commit a7a46bbe1c.

Conflicts:

	js/ui/sessionMode.js
2012-05-24 19:34:06 +02:00
7ed9516884 Updated Spanish translation 2012-05-23 14:19:24 +02:00
ecff2fa2b7 main: Add a --list-modes command line option
Add a command line option to list available modes, i.e. possible
arguments for the --mode switch.

https://bugzilla.gnome.org/show_bug.cgi?id=676156
2012-05-22 19:42:29 +02:00
e49b94658c Remove SessionType type
SessionType has now completely been replaced by the finer-grained
session-mode, so remove the remaining type definition.

https://bugzilla.gnome.org/show_bug.cgi?id=676156
2012-05-22 19:42:28 +02:00
e4f1572a3a sessionMode: Add statusArea property
Add a sessionMode.statusArea property, which specifies both the order
and shell implementation of the status area items for the mode.

https://bugzilla.gnome.org/show_bug.cgi?id=676156
2012-05-22 19:42:28 +02:00
b5b13322d8 sessionMode: Add extraStylesheet property
Add a sessionMode.extraStylesheet property, which may be used for
mode-specific style information.

https://bugzilla.gnome.org/show_bug.cgi?id=676156
2012-05-22 19:42:28 +02:00
c25e7f3c41 sessionMode: Add createSession() hook
Add a sessionMode.createSession() hook, which is executed during
startup to setup mode-specific stuff.

https://bugzilla.gnome.org/show_bug.cgi?id=676156
2012-05-22 19:42:28 +02:00
f6a2c92bfa main: Remove _initUserSession()
In Shell.SessionType.USER mode, two separate setup functions were
used during startup. With the new feature-based checks, the second
one is now almost empty, so move its remaining code into the first
function and remove it.

https://bugzilla.gnome.org/show_bug.cgi?id=676156
2012-05-22 19:42:28 +02:00
ed17418101 sessionMode: Add hasWorkspaces property
Add a sessionMode.hasWorkspaces property, which determines whether
the concept of workspaces should be exposed in the interface or not.

https://bugzilla.gnome.org/show_bug.cgi?id=676156
2012-05-22 19:42:28 +02:00
5264f39209 sessionMode: Add hasRunDialog property
Add a sessionMode.hasRunDialog property, which determines whether
the keyboard shortcut to access the run dialog should be active
or not.

https://bugzilla.gnome.org/show_bug.cgi?id=676156
2012-05-22 19:42:28 +02:00
de69c719fb sessionMode: Add allowKeybindingsWhenModal property
Add a sessionMode.allowKeybindingsWhenModal property, which determines
whether keybindings should still be handled while a modal dialog is
up or not.

https://bugzilla.gnome.org/show_bug.cgi?id=676156
2012-05-22 19:42:28 +02:00
ab3173487d sessionMode: Add allowExtensions property
Add a sessionMode.allowExtensions property, which determines whether
installed and enabled extensions should be loaded or ignored.

https://bugzilla.gnome.org/show_bug.cgi?id=676156
2012-05-22 19:42:28 +02:00
a3fcb8c284 sessionMode: Add allowSettings property
Add a sessionMode.allowSettings property, which determines whether
menus in the top bar should allow access to System Settings or not.

https://bugzilla.gnome.org/show_bug.cgi?id=676156
2012-05-22 19:42:28 +02:00
ba92cfa064 sessionMode: Add showCalendarEvents property
Add a sessionMode.showCalendarEvents property, which determines
whether the calendar menu should contain an events section or not.

https://bugzilla.gnome.org/show_bug.cgi?id=676156
2012-05-22 19:42:28 +02:00
6bee51ed33 sessionMode: Add hasAppMenu property
Add a sessionMode.hasAppMenu property, which determines whether
the top bar should contain a menu for the active application or
not.

https://bugzilla.gnome.org/show_bug.cgi?id=676156
2012-05-22 19:42:28 +02:00
122bca49ea sessionMode: Add hasOverview property
Add a sessionMode.hasOverview property, which determines whether
the mode should give access to the overview or not.

https://bugzilla.gnome.org/show_bug.cgi?id=676156
2012-05-22 19:42:28 +02:00
19318a1eeb global: Remove session_type property
ShellGlobal:session-type is now unsed, remove it.

https://bugzilla.gnome.org/show_bug.cgi?id=676156
2012-05-22 19:42:28 +02:00
a7a46bbe1c main: Make sure that --mode parameter is valid
Instead of falling back to a set of default values or crashing the
window manager when an invalid mode is specified, check the value
of the ShellGlobal:session-mode property before taking over as WM
and make a clean exit if it cannot be resolved to an existent mode.

https://bugzilla.gnome.org/show_bug.cgi?id=676156
2012-05-22 19:42:27 +02:00
3d26224180 Delegate mode information to a dedicated object
Rather than accessing global.session_type / global.session_mode
all over the place, delegate mode information to a dedicated
sessionMode object. While not very useful for now, we will replace
checks for a particular mode with checks for particular properties
that sessionMode defines based on global.session_mode.

https://bugzilla.gnome.org/show_bug.cgi?id=676156
2012-05-22 19:42:27 +02:00
940ddb104c global: Add session-mode property
Add a session-mode property on ShellGlobal which corresponds to the
new --mode switch. Make the existing ShellGlobal:session-type property
readonly and base it on ShellGlobal:session-mode to avoid conflicts.

https://bugzilla.gnome.org/show_bug.cgi?id=676156
2012-05-22 19:42:27 +02:00
5617f91281 main: Add generic --mode switch
We are going to need "special" modes other than GDM, for instance
for the initial setup[0]. Rather than adding one command line switch
per mode (and logic to deal with multiple switches being given at
the same time), add a generic --mode switch to specify the mode as
string.

[0] https://live.gnome.org/GnomeOS/Design/Whiteboards/InitialSetup

https://bugzilla.gnome.org/show_bug.cgi?id=676156
2012-05-22 19:42:27 +02:00
a9a863aab4 Updated Spanish translation 2012-05-22 10:46:27 +02:00
ca26347dea userMenu: Offer "Switch Session" item on multi-session systems
Since commit 518282e169, we hide both "Switch User" and "Log out"
on single-user systems. However, if there is more than one session
available, users may still want to get back to the login manager
to change sessions.
Add both "Log out" and "Switch Session" items in that case.

https://bugzilla.gnome.org/show_bug.cgi?id=675802
2012-05-22 07:36:36 +02:00
41a14e808e Updated Spanish translation 2012-05-21 13:57:39 +02:00
6452501275 Updated Galician translations 2012-05-20 18:14:15 +02:00
b61ada72cc shell-app: Raise windows in reverse order to preserve the stacking
ShellApp keeps track of windows sorted by most recently used first
which means that when raising windows we need to start from the end of
the list to finish with the correct stacking order.

This patch just makes the code agree with the comment which was
already there.

https://bugzilla.gnome.org/show_bug.cgi?id=676371
2012-05-19 17:32:12 +02:00
ace42d845c Updated Korean translation 2012-05-19 22:58:08 +09:00
850b6f28e5 main: Add prototype for _shell_link_to_shell_js()
It's pointless to have a prototype for a dummy function, but at
least on F17, gcc's -Werror includes -Wmissing-prototypes ...

https://bugzilla.gnome.org/show_bug.cgi?id=676347
2012-05-19 02:41:07 +02:00
3c81e9f0e7 Prevent the link to libgnome-shell-js.so from being removed
Add a dummy call to shell_js_add_extension_importer() to ensure that the
link to libgnome-shell-js.so is not removed when using -Wl,--as-needed,
which is the default on many distros.

https://bugzilla.gnome.org/show_bug.cgi?id=670477
2012-05-18 18:30:51 -04:00
e20ea19f34 Link the gnome-shell binary to libgnome-shell-js.so
Depending on the exact linker options and versions, rpath's written
into the binary may, or may not, be honored by dlopen(). Work around
this by simply linking the gnome-shell binary to gnome-shell-js, since
then dlopen() doesn't need to search paths.

https://bugzilla.gnome.org/show_bug.cgi?id=670477
2012-05-18 18:29:02 -04:00
ce041a3190 js: Don't use global.log*
These are "deprecated", and are just references to the gjs logging functoins

https://bugzilla.gnome.org/show_bug.cgi?id=675790
2012-05-18 14:09:00 -04:00
3a01aaf7fb extensionSystem: Don't spam stdout with "Loaded extension"
https://bugzilla.gnome.org/show_bug.cgi?id=675790
2012-05-18 14:09:00 -04:00
ec4a2aae95 notificationDaemon: Fix copy-paste typo
Since this line was introduced in 7458d3e, "Approve file transfer channels",
it's quite obvious that the line was meant to filter out transfer channel
notifications.

https://bugzilla.gnome.org/show_bug.cgi?id=676175
2012-05-18 14:08:12 -04:00
de8a66d4ce st: Remove our own font DPI resolution implementation
Clutter has had clutter_backend_get_resolution for a little while.

https://bugzilla.gnome.org/show_bug.cgi?id=672807
2012-05-18 13:57:12 -04:00
dc3d3acb3b global: Remove XSettings font handling code
Clutter now has its own XSettings implementation, ClutterSettings, which
handles this automatically now.

https://bugzilla.gnome.org/show_bug.cgi?id=672807
2012-05-18 13:57:12 -04:00
48d6eb168f bluetooth: Enforce a PIN length of 6 digits
Bluetooth PINs are required to have 6 digits, so enforce that
condition by making the PIN request notification's confirm
button insensitive unless the entered PIN has the correct length.

https://bugzilla.gnome.org/show_bug.cgi?id=651251
2012-05-18 17:41:19 +02:00
6190d6c962 messageTray: Allow to make notification buttons insensitive
The availability of a notification action may depend on conditions,
so add a method to control the sensitivity of buttons which have
been added with addButton().

https://bugzilla.gnome.org/show_bug.cgi?id=651251
2012-05-18 17:39:50 +02:00
76616dc98c bluetooth: zero-pad PIN
Bluetooth PINs always consist of 6 digits, so make sure to add
leading zeros as necessary.

https://bugzilla.gnome.org/show_bug.cgi?id=651251
2012-05-18 17:39:50 +02:00
82c2f5221d panel: Check for appMenu button's reactivity before opening
When the appMenu is not available, for instance when no windows are
open (on the current workspace), we make its actor unreactive to
"hide" it from keynav. However the menu can still be triggered
erroneously when using the corresponding keyboard shortcut, so
add a check for the actor's reactivity there as well.

https://bugzilla.gnome.org/show_bug.cgi?id=676316
2012-05-18 17:38:06 +02:00
a3bbb7be14 [l10n] Updated Indonesian translation 2012-05-17 22:19:18 +07:00
9e1a2cfeac Start using MessageTray.Source directly instead of having to subclass it
For most subclasses, this is a direct swap -- a lot of the time, the
constructor was a blank class that override createNotificationIcon,
and called _setSummaryIcon in _init.

https://bugzilla.gnome.org/show_bug.cgi?id=661236
2012-05-17 08:32:22 -04:00
c6fabe504a messageTray: Make Source usable without subclassing
Rather than ask most users of Source to subclass it to simply set their icon,
just allow them to create a new instance and add it without any complex magic.

https://bugzilla.gnome.org/show_bug.cgi?id=661236
2012-05-17 08:32:22 -04:00
61a17d7fab Magnifier: Add brightness and contrast functionality
Added clutter effects class that does lightness inversion.
Modified the magnifier to use this inversion effect.

https://bugzilla.gnome.org/show_bug.cgi?id=639851
2012-05-16 18:59:33 -04:00
865cfa5211 Magnifier: Add brightness and contrast functionality
Modified the magnifier to use the CluttterBrightnessContrastEffect
for changing overall brightness and/or contrast of a ZoomRegion.

https://bugzilla.gnome.org/show_bug.cgi?id=639851
2012-05-16 18:59:32 -04:00
e2b857adae Updated Japanese translation. 2012-05-15 00:53:03 +09:00
f3924ccd91 l10n: Updated Italian translation 2012-05-12 20:09:29 +02:00
86e3e59530 Updated Galician translations 2012-05-11 11:31:43 +02:00
02e4726ba6 util: Don't double-fork when spawning from Alt-F2
This breaks polkit.

See http://bugzilla.redhat.com//show_bug.cgi?id=819275

https://bugzilla.gnome.org/show_bug.cgi?id=675789
2012-05-10 15:05:05 -04:00
66e470e073 Updated Spanish translation 2012-05-10 18:06:54 +02:00
afaa5c24d6 userMenu: Update to latest mockups
Technically those are minor changes, but people will love us anyway:

  - change the default of the <alt>ernative to "Power Off"
  - remove "Online Accounts" item
  - regroup the remaining items
  - remove ellipses from labels

https://bugzilla.gnome.org/show_bug.cgi?id=675802
2012-05-10 15:54:37 +02:00
518282e169 userMenu: Hide "Log out" item on single user machines
Ignoring remote logins, the "Log out" action is meaningless for
single user setups. Do not show it in that case.

https://bugzilla.gnome.org/show_bug.cgi?id=675802
2012-05-10 15:54:37 +02:00
d955adbbad userMenu: More show()/hide() refactoring
Here's one show()/hide() sequence left out in commit 723a1c843 ...
2012-05-10 15:54:37 +02:00
83d3225e57 userMenu: Fix 'Lock' lockdown setting
Oops, embarrassing typo ...

https://bugzilla.gnome.org/show_bug.cgi?id=675802
2012-05-10 15:23:13 +02:00
6fa45975bf Updated Marathi Translations 2012-05-10 10:02:04 +05:30
e6087efb40 NetworkAgent: disallow multiple requests for the same connection/setting
It doesn't make sense to have multiple requests for the same
connection/setting combination at the same time, since we would be
asking the user twice for the same password. Instead, report cancellation
to NetworkManager if this happens.
Note that does make sense to have multiple requests in sequence though
(they could have different flags), but this is not affected.

https://bugzilla.gnome.org/show_bug.cgi?id=674961
2012-05-07 22:06:26 +02:00
4ce2f80a2f searchDisplay: Remove the sync search completed code path
Now that all searches are async we can remove the code path for the
SearchSystem::search-completed signal which is no longer useful.

This patch ends up fixing the status text not being updated for when
there are no results.

https://bugzilla.gnome.org/show_bug.cgi?id=675328
2012-05-07 21:45:45 +02:00
0862d1c804 searchDisplay: Remove a couple of no longer used variables
https://bugzilla.gnome.org/show_bug.cgi?id=675328
2012-05-07 21:45:42 +02:00
333e380340 search: Remove the ability to add synchronous search providers
As shown in the previous commits, synchronous search is easily implemented
by the asynchronous search API. The only reason we still have a
synchronous search API is of historical reasons. Well, we're not a museum,
and git log can keep our fossils safe if need be....

https://bugzilla.gnome.org/show_bug.cgi?id=675328
2012-05-07 15:29:46 -03:00
58f77a19ed search: Port all search providers over to the async API
https://bugzilla.gnome.org/show_bug.cgi?id=675328
2012-05-07 15:29:32 -03:00
f2d883dab2 placeDisplay: Create our own PlacesManager
Since we don't have a section showing off the available places somewhere,
we don't need a global PlacesManager.

https://bugzilla.gnome.org/show_bug.cgi?id=675328
2012-05-07 15:29:20 -03:00
7ba8c7c2b5 search: Remove unused MUTLIPLE_* match types
https://bugzilla.gnome.org/show_bug.cgi?id=675328
2012-05-07 15:21:24 -03:00
0bf6c93faa search: Allow synchronous searches to be defined with the async API
To allow this to happen, we need to make sure that we don't overwrite the
previousResults when calling the async method. Note that this is a bug of
some sort, we were already using this synchronous style when a remote
search failed.

https://bugzilla.gnome.org/show_bug.cgi?id=675328
2012-05-07 15:21:24 -03:00
b9f0158278 placeDisplay: use new bookmark file location
GTK+ 3.5 is placing the bookmarks file in
$XDG_CONFIG_HOME/gtk-3.0/bookmarks. Adapt to that change.

https://bugzilla.gnome.org/show_bug.cgi?id=675443
2012-05-07 08:21:59 -04:00
25ee41f344 theme: use a smaller border-radius for top bar
- plays better with window decorations and maximized windows
- https://bugzilla.gnome.org/show_bug.cgi?id=672430
2012-05-07 10:36:28 +02:00
5436634829 a11y: Using the proper label_actor for date menu on top panel
On the code two labels are created. One is used on the date menu
itself, and the other for the menu icon at the top panel. The wrong
label was used as the label_actor for the top panel menu icon.

https://bugzilla.gnome.org/show_bug.cgi?id=675307
2012-05-03 20:47:11 +02:00
915524e1ab main: Close the recorder instead of pausing it
Pausing means that we will continue to use the same output file when
the keybinding is activated again. While useful to record a single
video in chunks, it doesn't seem to be how most users understand the
keybinding. Closing the recorder will close the file and create a new
one the next time the keybinding is pressed.

https://bugzilla.gnome.org/show_bug.cgi?id=675128
2012-05-03 09:27:53 +02:00
d579cd1605 test-recorder: Create the recorder only after the stage is realized
The recorder needs the stage's XID so it can only be created after the
stage is realized.

https://bugzilla.gnome.org/show_bug.cgi?id=675301
2012-05-03 09:27:53 +02:00
cb5941ec55 test-recorder: Explicitly make the clutter API coordinates doubles
That's what clutter expects and it would crash otherwise.

https://bugzilla.gnome.org/show_bug.cgi?id=675301
2012-05-03 09:27:53 +02:00
a5ac183d86 network: Fix an accidental copy-paste error
Logic swap accidentally introduced in commit 723a1c843.

https://bugzilla.gnome.org/show_bug.cgi?id=672272
2012-05-02 16:31:29 -04:00
a36de92bb9 gactionmuxer: Fix list_actions
The code there before was just completely wrong

https://bugzilla.gnome.org/show_bug.cgi?id=673177
2012-05-02 16:26:07 -04:00
01f9d551f1 messageTray: Fix scroll bugginess in summary items
Two nested scroll views were fighing with each other. The reason isn't
particularly important and has to do with some silliness involving
StAdjustment. The visible effect was that scrolling up and down when
in a summary item view would appear to be glitchy, and sometimes not
work at all.

To fix, make sure that the scroll view we don't care about is disabled
when in a summary mode.

https://bugzilla.gnome.org/show_bug.cgi?id=661615
2012-05-02 16:04:46 -04:00
399df66b18 messageTray: notification should be a style class name, not a global name
There have been multiple notifications ever since we had a notification stack
per source, so the idea of one notification being on screen at a time has long
been dead.

https://bugzilla.gnome.org/show_bug.cgi?id=661615
2012-05-02 16:04:05 -04:00
723a1c843a Refactor show()/hide() sequences
We seem to have a lot of code that does something along the lines of:

    if (condition)
        actor.show();
    else
        actor.hide();

ClutterActor already has such a thing for exactly this purpose: the 'visible'
property. Use it instead of the mess above.

https://bugzilla.gnome.org/show_bug.cgi?id=672272
2012-05-02 12:43:58 -04:00
e333263fd6 gdm: don't fail if fprintd unavailable
fingerprint support is optional so we shouldn't try to start
fprintd upfront and croak if it fails.

https://bugzilla.gnome.org/show_bug.cgi?id=675006
2012-05-02 11:44:29 -04:00
507df9eea1 gnome-shell-plugin: Remove useless dispose/finalize handlers
https://bugzilla.gnome.org/show_bug.cgi?id=672807
2012-05-02 10:20:22 -04:00
a9a3687ea0 st-bin: Remove unnecessary foreach implementation
https://bugzilla.gnome.org/show_bug.cgi?id=672807
2012-05-02 10:20:22 -04:00
f05c649c61 Updated Greek translation 2012-05-02 13:53:42 +03:00
a2dfba1842 l10n: Updated Italian translation 2012-04-30 15:34:24 +02:00
c199da4dfa Updated Norwegian bokmål translation 2012-04-30 11:13:44 +02:00
ee9033e12f gitignore: Update to ignore calendar schemas 2012-04-29 19:20:04 -04:00
54788d750e searchDisplay: Reset the keyboard focus after displaying async results
We hide the content around displaying new search results to prevent
flickering, unfortunately, one side effect of hiding an actor is
losing key focus if it currently is on the that actor or any
child. This could happen in the case of async results showing up after
the user had moved focus to the search results.

This patch works around that issue by saving the key focus and
resetting it back after displaying the new async results.

https://bugzilla.gnome.org/show_bug.cgi?id=675078
2012-04-30 01:10:08 +02:00
78e894c6f2 lookingGlass: Remove the "Errors" tab
We already have one too many logging systems. Remove the errors tab
and make global.log/global.logError point to window.log/window.logError
instead.

https://bugzilla.gnome.org/show_bug.cgi?id=675104
2012-04-29 17:12:07 -04:00
32107ba8b5 environment: Allow window.log to take multiple arguments
https://bugzilla.gnome.org/show_bug.cgi?id=675104
2012-04-29 17:12:05 -04:00
6122f65e7a NetworkMenu: use network-offline while loading
network-error is partially red and thus can be distracting at
login. Use network-offline instead.

https://bugzilla.gnome.org/show_bug.cgi?id=674426
2012-04-29 18:40:03 +02:00
43fd29f9bf Revert "popupMenu: Fix RemoteMenu items with boolean state action"
This reverts commit 7293ddb22c.
2012-04-29 10:38:52 +02:00
54c624b356 Updated Brazilian Portuguese translation 2012-04-28 10:46:58 -03:00
8c33adfd29 dash: Destroy tooltip label when item is removed 2012-04-27 15:50:48 -04:00
2e8881b77c notificationDaemon: Match app based on WM_CLASS
Most tray applets won't have an associated window in the same PID. We
need to fall back to the WM_CLASS in this case.

https://bugzilla.gnome.org/show_bug.cgi?id=673761
2012-04-27 15:49:18 -04:00
64aa729edd jhbuild wrapper: Clean up environment variable logic
Lots of things here were unused. First, we don't need to calculate
the js_dir when not running from the source tree, which makes the
branch mostly empty, meaning we can merge the two branches.
2012-04-27 15:48:53 -04:00
ccf95b738d jhbuild wrapper: Re-set typelib path to the src dir
This allows us running uninstalled. While we're at it, though, remove
JHBUILD_TYPELIBDIR. jhbuild shell should add its own stuff to GI_TYPELIB_PATH,
and we don't want to half reimplement jhbuild. The wrapper script should be
solely for the case of running from the source directory, and not care about
jhbuild at all.
2012-04-27 15:48:53 -04:00
b2847fedd3 jhbuild wrapper: Don't crash if we can't start the shell
If start_shell throws an exception for whatever reason, the finally
handler will run before shell has been set, leading to a "referencing
an undefined local variable" error.
2012-04-27 15:48:53 -04:00
8befcb9bba shell-network-agent: don't crash if a request isn't found
If a request isn't found in shell_network_agent_set_password() or
shell_network_agent_respond(), then g_return_if_fail() rather than
crashing. OTOH, if a request is not found in
shell_network_agent_cancel_get_secrets(), then just ignore it
silently, since that could legitimately happen.

https://bugzilla.gnome.org/show_bug.cgi?id=674961
2012-04-27 15:47:53 -04:00
988fc52303 Updated Galician translations 2012-04-27 17:07:47 +02:00
7293ddb22c popupMenu: Fix RemoteMenu items with boolean state action
Stateful actions are expected to pass their state when activated,
but we currently only do this for actions with a string state.

https://bugzilla.gnome.org/show_bug.cgi?id=674932
2012-04-27 12:30:16 +02:00
f23c118e81 Updated Hebrew translation. 2012-04-26 15:30:48 +03:00
f68b3be35a data: Add Evolution calendar settings gschema to EXTRA_DIST
3d95e7bb11 forgot to add this
gschema file to dist
2012-04-26 08:40:01 +02:00
3d95e7bb11 Mirror Evolution calendar settings into our own schema
Right now, we are hard-depending on the presence of Evolution by
using its settings schemas. This is likely to be unpopular, and
also causes instability if someone happens not to have Evolution
installed, so install a schema that has the same data path as
the Evolution schema, but a different name and install that
for the keys we need.

To avoid a string-freeze break, we rely on the translations in
Evolution - if Evolution isn't installed, the key descriptions
will be untranslated in dconf-editor.

https://bugzilla.gnome.org/show_bug.cgi?id=674424
2012-04-25 14:39:02 -04:00
337c484f01 Updated Korean translation 2012-04-26 01:08:27 +09:00
5d98e2bf04 st-theme-node-drawing: Don't use GL types
This swaps a use of GLfloat for a regular float. Cogl might stop
including a GL header in its public headers soon so this would fix a
compilation error.

https://bugzilla.gnome.org/show_bug.cgi?id=672711
2012-04-25 15:47:04 +01:00
6f300d0cc6 Updated Spanish translation 2012-04-25 12:17:17 +02:00
3422e1dca7 popupMenu: make sure to break the grab when the slider is not visible
Otherwise the grab will persist for example after closing the PopupMenu
with Escape.

https://bugzilla.gnome.org/show_bug.cgi?id=672713
2012-04-24 22:37:19 +02:00
963c6ae567 workspace: Use the position hints when available also for the window overlay
Otherwise when dropping a window in the current workspace the overlay will
appear from the top left corner.

https://bugzilla.gnome.org/show_bug.cgi?id=674323
2012-04-24 22:37:19 +02:00
6304169926 workspace: hide the overlay for a zoomed window when exiting the overivew
Otherwise it remains visible until the overview animation is completed.

https://bugzilla.gnome.org/show_bug.cgi?id=674323
2012-04-24 22:37:19 +02:00
221afde55e jhbuild wrapper: clean up imports
Final commit in the series, remove imports that were only used
by moved or deleted functionality.
2012-04-24 21:20:49 +02:00
0ae87270ad jhbuild wrapper: don't try to "restore GNOME"
If run under jhbuild, gnome-shell is in PATH, so trying to restore
is just running it again, likely failing in the same way (with the
additional problem of detaching the shell from the terminal). Also,
this is using deprecated GConf keys, and deprecated pygobject
bindings.
2012-04-24 21:20:49 +02:00
9d33baec70 jhbuild wrapper: don't set unneeded environment variables
The jhbuild wrapper is expected to be run through jhbuild run, or
in the uninstalled case inside a jhbuild shell, so there should
be no need to set XDG_DATA_DIRS and friends.
2012-04-24 21:20:49 +02:00
0f37b22cdb jhbuild wrapper: don't muck with session activated services
dconf-daemon is available since 2.32, so there is no need to
start it manually (and in fact, it should not be running normally).
Similarly, notify-osd and notification-daemon should not be
dbus activated, but rather autostarted by gnome-session, and
therefore not running in a gnome-shell session.
2012-04-24 21:20:49 +02:00
47afd87e84 jhbuild wrapper: move performance measurement to a separate tool
Introduce a new gnome-shell-perf-tool, which can be used instead
of the old gnome-shell-jhbuild wrapper to gather data about gnome-shell
performance and submit to shell-perf.gnome.org. This runs the
shell with no extra setup beyond the WM_CLASS filter, so it can
be used for a jhbuild setup or for an installed shell.
2012-04-24 21:20:49 +02:00
700c06023e st: Clean up scroll event code
Currently the scroll event code only handles scroll events if the
adjustment's value is within the "lower" and "upper" limits. The
likely intent was to pass events to a parent scroll view when
reaching the bounds (uh, nested scroll views!), but apparently
we never made use of this, as the upper bound is actually wrong
(an adjustment's maximum value is upper - page_size, not upper).
Just handle all scroll events unconditionally and rely on the
bound checks in StAdjustment.

https://bugzilla.gnome.org/show_bug.cgi?id=672413
2012-04-24 20:45:01 +02:00
4fea5b5ca3 main: Move 'toggle-recording' binding into the shell
The keybinding to toggle the screen recorder was implemented as a
signal on MetaScreen, as keybindings could only be defined in mutter
core. As this is no longer the case, we can move the binding into the
shell where it belongs.

https://bugzilla.gnome.org/show_bug.cgi?id=674377
2012-04-24 17:37:44 +02:00
521bddc1cc [l10n] Update Japanese translation
Fix a typo
2012-04-21 22:42:20 +09:00
257 changed files with 57871 additions and 50866 deletions

7
.gitignore vendored
View File

@ -16,6 +16,7 @@ config.log
config.status
config
configure
data/50-gnome-shell-*.xml
data/gnome-shell.desktop
data/gnome-shell.desktop.in
data/gnome-shell-extension-prefs.desktop
@ -23,6 +24,8 @@ data/gnome-shell-extension-prefs.desktop.in
data/gschemas.compiled
data/org.gnome.shell.gschema.xml
data/org.gnome.shell.gschema.valid
data/org.gnome.shell.evolution.calendar.gschema.xml
data/org.gnome.shell.evolution.calendar.gschema.valid
docs/reference/*/*.args
docs/reference/*/*.bak
docs/reference/*/*.hierarchy
@ -48,6 +51,7 @@ po/gnome-shell.pot
po/*.header
po/*.sed
po/*.sin
po/.intltool-merge-cache
po/Makefile.in.in
po/Makevars.template
po/POTFILES
@ -60,6 +64,8 @@ src/*-enum-types.[ch]
src/*-marshal.[ch]
src/Makefile
src/Makefile.in
src/calendar-server/evolution-calendar.desktop
src/calendar-server/evolution-calendar.desktop.in
src/calendar-server/org.gnome.Shell.CalendarServer.service
src/gnome-shell
src/gnome-shell-calendar-server
@ -68,6 +74,7 @@ src/gnome-shell-extension-prefs
src/gnome-shell-hotplug-sniffer
src/gnome-shell-jhbuild
src/gnome-shell-perf-helper
src/gnome-shell-perf-tool
src/gnome-shell-real
src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service
src/run-js-test

412
NEWS
View File

@ -1,3 +1,415 @@
3.6.0
=====
* keyboard: Make input source items accessible [Florian; #684462]
* Don't show network dialogs in the lock screen [Giovanni; #684384]
* popupMenu: Fix initial visibility of settings items [Florian; #684473]
* userMenu: Close menu immediately on user/session switch [Florian; #684459]
* Fix alignment of search section headers in RTL locales [Florian; #684379]
* screenShield: Fix unlock animation [Florian; #684591]
* Don't open the tray from a dwell while in a modal grab [Jasper; #684458]
* userMenu: Fix texture updates on icon changes [Florian; #679268]
* Fix a11y support in the login screen [Florian, Ray; #684727, #684728, #684748]
* Make On-Screen-Keyboard usable with new message tray [Giovanni, Florian;
#683546]
* Fix initial visibility of input volume in lock-screen [Florian; #684611]
Contributors:
Giovanni Campagna, Florian Müllner, Jasper St. Pierre, Ray Strode
Translations:
Matej Urbančič [sl], Dr.T.Vasudevan [ta], Piotr Drąg [pl], A S Alam [pa],
Alexander Shopov [bg], Nilamdyuti Goswami [as], Chandan Kumar [hi],
Khaled Hosny [ar], Ibrahim Saed [ar], Sandeep Sheshrao Shedmake [mr],
Tom Tryfonidis [el], Theppitak Karoonboonyanan [th], Alexandre Franke [fr],
Fran Diéguez [gl], Gabor Kelemen [hu], Ani Peter [ml], Daniel Mustieles [es],
Мирослав Николић [sr, sr@latin], Duarte Loreto [pt], ManojKumar Giri [or],
Ihar Hrachyshka [be], Aurimas Černius [lt], Djavan Fagundes [pt_BR],
Changwoo Ryu [ko], Bruce Cowan [en_GB], Kris Thomsen [da], Gil Forcada [ca],
Yaron Shahrabani [he], Milo Casagrande [it], Ville-Pekka Vainio [fi],
YunQiang Su [zh_CN], Carles Ferrando [ca@valencia], Mario Blättermann [de],
Rajesh Ranjan [hi], Yuri Myasoedov [ru], Rūdolfs Mazurs [lv],
Jiro Matsuzawa [ja], Mattias Põldaru [et], Timur Zhamakeev [ky],
Petr Kovar [cs], Chao-Hsiung Liao [zh_HK,zh_TW], Andika Triwidada [id]
3.5.92
======
* Login/UnlockDialog: Don't reset immediately if auth fails [Giovanni; #682544]
* Allow changing session mode at runtime [Jasper, Giovanni; #683156]
* Add zoom out animation on login [Jasper; #683170]
* Bluetooth: don't restrict the length of non numeric PINs [Giovanni; #683356]
* Force chat notification to stay open when focusing entry [Debarshi; #682236]
* Make sure the screen is fully locked before suspending [Giovanni; #683448]
* st-texture-cache: Fix a case of distorted textures [Florian; #683483]
* popupSubMenu: Fix padding for non-scrolled submenus [Florian; #683009]
* popupMenu: Fix width changes on submenu open/close [Florian; #683485]
* boxpointer: Avoid malformed boxpointer arrow [Debarshi; #680077]
* Change stage background color to grey [Adel; #683514]
* messageTray: Update style of summary counters [Debarshi; #682891]
* Don't fail if a legacy tray icon has no WM_CLASS [Giovanni; #683724]
* PolkitAgent: Fix a crash if there is no avatar [Giovanni; #683707]
* Hide the a11y menu in the lock screen, but show it in the login screen
[Giovanni; #682542]
* Fix show-apps button dropping off the dash [Florian; #683340]
* Fix committing strings to shell entries from input method [Florian; #658325]
* Make IBus display strings consistent with control-center [Rui; #683124]
* Fix missing short codes for some input sources [Rui; #683613]
* Remove support for long-press from entry context menus [Jasper; #683509]
* screenShield: Add box-shadow to the shield [Florian]
* Don't show a right-click menu for the hotplug source [Jasper; #683438]
* Fix extension styling [Giovanni; #682128]
* Fix on-screen keyboard not working with system-modal dialogs
[Giovanni; #664309]
* Fix insensitive styling for popup menu items [Giovanni; #683988]
* Disable the message tray dwell when the user is interacting [Owen; #683811]
* Animate going from the unlock dialog to the lock screen [Giovanni; #681143]
* Autostart fprintd when necessary [Ray; #683131]
* UnlockDialog: Allow typing before the first PAM question [Giovanni; #681576]
* Make Return key dismiss screenshield [Ray; #683889]
* Fix keyboard navigation in the message tray [Florian; #682243]
* Remove the places & devices search provider [Giovanni; #683506]
* Enable hot corner while the message tray is up [Florian; #682255]
* Port screen recorder to new GStreamer vp8enc API [Adel; #684206]
* Fix fish flickering [Giovanni; #684154]
* Fix extension ordering with !important [Jasper; #684163]
* Allow the shell to run without the screenshield [Giovanni; #683060]
* Add menu items for IBus Anthy's InputMode, TypingMode [Rui; #682314]
* Improve transition to the login dialog [Jasper; #682428]
* Keep unlock dialog around until shield animation ends [Florian; #684342]
* Expose shell keybindings in System Settings [Florian; #671010]
* Misc. bugfixes and cleanups [Debarshi, Florian, Giovanni, Jasper, Rico, Rui;
#672790, #677434, #683305, #683357, #683369, #683377, #683378, #683400,
#683449, #683472, #683482, #683487, #683488, #683526, #683529, #683546,
#683583, #683628, #683705, #683982, #683989, #684035, #684036, #684040,
#684162, #684214, #684343]
Contributors:
Giovanni Campagna, Adel Gadllah, Rui Matos, Florian Müllner, Debarshi Ray,
Jasper St. Pierre, Ray Strode, Owen Taylor, Rico Tzschichholz
Translations:
Gabor Kelemen [hu], Piotr Drąg [pl], Khaled Hosny [ar],
Мирослав Николић [sr, sr@latin], Chao-Hsiung Liao [zh_HK, zh_TW],
Bruce Cowan [en_GB], Dirgita [id], Tom Tryfonidis [el], Timo Jyrinki [fi],
Adorilson Bezerra [pt_BR], Arash Mousavi [fa], Matej Urbančič [sl],
Christian Kirbach [de], Yaron Shahrabani [he], Ihar Hrachyshka [be],
Changwoo Ryu [ko], Duarte Loreto [pt], Theppitak Karoonboonyanan [th],
Nilamdyuti Goswami [as], Sandeep Sheshrao Shedmake [mr],
Alexandre Franke [fr], Ivaylo Valkov [bg], tuhaihe [zh_CN],
Yuri Myasoedov [ru], Aurimas Černius [lt], Andika Triwidada [id],
Rajesh Ranjan [hi], Sweta Kothari [gu], Daniel Mustieles [es],
Fran Diéguez [gl], Praveen Illa [te]
3.5.91
======
* Improve modal dialog styling of network secret prompts [Jasper; #682412]
* Fix visibility of non-active workspaces during overview transition
[Florian; #682002]
* Improve scrollbar theming [Cosimo; #682476]
* Make sure the app menu remains hidden in locked state [Florian; #682475]
* Add tooltip to show-applications icon [Jasper; #682445]
* Do not add duplicate remote search providers [Florian; #682470]
* Handle 'popup-menu' signal on summary items [Florian; #682486]
* Fix dwelling during mouse-down [Owen; #682385]
* Set label actor for endSessionDialog.ListItem [Alejandro; #677503]
* Don't match on comments when searching applications [Florian; #682529]
* Make workspace selector more similar to the mockup [Stefano; #662087]
* Fix extension installation and reloading [Jasper; #682578]
* Hide removable devices in the lock screen [Giovanni; #681143]
* Reset cancellable after hitting Escape on login screen [Alban; #681537]
* Fix suspend from the user menu [Giovanni; #682746]
* Set label actor for summary items in message tray [Alejandro; #677229]
* Set label for the "Show applications" dash button [Alejandro; #682366]
* Load extensions as late as possible [Jasper; #682822]
* Improve mount operation dialogs [Jon; #682645]
* Remove "Connect to ..." item from places search [Florian; #682817]
* Don't auto-expand notifications with actions [Giovanni; #682738]
* Add a new lock screen menu to combine volume network and power
[Giovanni; #682540]
* Add support for pre-edit to StIMText [Daiki; #664041]
* Remove StIconType [Jasper, Florian, Rui, Giovanni, Debarshi; #682540]
* Use monitor geometry for dwelling [Florian; #683044]
* Add support for surrounding-text to StIMText [Daiki; #683015]
* Improve the placement and style of the "No results" text [Jasper; #683135]
* Remove broken network device activation policy [Giovanni; #683136]
* Hide power status icon when no battery is present [Tim; #683080]
* Ensure summary items are square and have spacing [Debarshi; #682248]
* Fix close buttons overlapping screen edge [Debarshi; #682343]
* Escape the tray when a legacy icon is clicked [Giovanni; #682244]
* Update arrow in the screen shield to match latest mockups [Giovanni; #682285]
* Allow lifting the screen shield with the mouse wheel [Giovanni; #683164]
* Make sure to show the app menu after unlocking the screen [Jasper; #683154]
* Misc bug fixes and cleanups [Debarshi, Florian, Giovanni, Jasper, Rui;
#582650, #667439, #682238, #682268, #682429, #682455, #682544, #682546,
#682683, #682710, #682998, #683073, #683137, #683156]
Contributors:
Alban Browaeys, Giovanni Campagna, Cosimo Cecchi, Stefano Facchini,
Adel Gadllah, Tim Lunn, Rui Matos, William Jon McCann, Florian Müllner,
Alejandro Piñeiro, Debarshi Ray, Jasper St. Pierre, Owen Taylor, Daiki Ueno
Translations:
Piotr Drąg [pl], Takayuki KUSANO [ja], Kjartan Maraas [nb],
Aurimas Černius [lt], Daniel Mustieles [es], Yuri Myasoedov [ru],
Khaled Hosny [ar], Yaron Shahrabani [he], Tom Tryfonidis [el],
Nilamdyuti Goswami [as], Fran Diéguez [gl], Nguyễn Thái Ngọc Duy [vi],
A S Alam [pa], Dr.T.Vasudevan [ta], Luca Ferretti [it]
3.5.90
======
* Use symbolic icons for workspace switch OSD [Jon; #680738]
* Lock screen improvements:
- Hide user menu and a11y menu in the screen lock [Giovanni; #681143]
- Bump the lock screen slightly when pressing a key [Giovanni; #681143]
- Constrain vertical movement of the screen shield [Giovanni; #681143]
- Return to lock screen on idle [Giovanni; #682041]
- Unlock screen automatically after fast-user switching [Giovanni; #682096]
- Fix "other user" label [Ray; #681750]
* Constrain content of system modals to primary monitor [#681743]
* Respect automatic lock setting on suspend/user-switch [Giovanni; #680231]
* Improve styling of keyring prompt [Jasper; #681821]
* Do not hard-code <super> as overlay-key [Florian; #665547]
* Update style of attached modal dialogs [Florian; #681601]
* a11y: allow navigation on non reactive items [Alejandro; #667439, #667439]
* Implement mode-less overview design [Joost, Florian; #682109]
* Implement message-tray redesign:
- Restyle the message tray [Ana, Allan, Florian; #677213, #682342]
- Move the desktop upwards when showing the tray [Debarshi; #681392]
- Add a close button to notifications [Ana, Jasper; #682253]
- Add a keybinding to toggle the tray [Debarshi; #681392]
- Make the tray keyboard navigable [Debarshi; #681519]
- Add dwelling at the bottom of the screen to open the tray [Owen; #682310]
- Don't time out banners when the user is inactive [Marina, Jasper]
- Misc fixes and cleanups [Jasper, Marina]
* Fix showing "Next Week" on Sundays [Sebastian; #682198]
* Delay restoring IM presence until the network comes up [Florian; #677982]
* Display enterprise login hint [Ray; #681975]
* Ignore unrecognized/irrelevant network devices/connections [Dan; #682364]
* Misc bug fixes and cleanups: [Dan, Florian, Jasper, Jiro, Piotr, Rico;
#643687, #682045, #682189]
Contributors:
Giovanni Campagna, Allan Day, Piotr Drąg, William Jon McCann,
Sebastian Keller, Jiro Matsuzawa, Florian Müllner, Alejandro Piñeiro,
Debarshi Ray, Ana Risteska, Jasper St. Pierre, Ray Strode, Owen Taylor,
Rico Tzschichholz, Joost Verdoorn, Dan Winship, Marina Zhurakhinskaya
Translations:
Nilamdyuti Goswami [as], Daniel Mustieles [es], Yaron Shahrabani [he],
Chao-Hsiung Liao [zh_HK, zh_TW], Tobias Endrigkeit [de], A S Alam [pa],
Sandeep Sheshrao Shedmake [mr], Fran Diéguez [gl],
Мирослав Николић [sr, sr@latin]
3.5.5
=====
* Update style to match mockups [Allan]
- improve calendar layout and legibility
- update notifications and menus
- use a common style for entries
- update scrollbars to match GTK+
- improve clock/unlock button in lock screen
- update polkit dialogs [Jasper]
* Fix login dialog growing when selecting different users [Florian; #675076]
* Implement screen lock in the shell [Giovanni]
- restructure login code to be shared with session unlock [#619955]
- add initial screen shield / unlock dialog implementation [#619955]
- implement (optional) notification list on lock shield [#619955]
- update login dialog style to match lock screen [#619955]
- filter notifications to only show new ones on the screen lock [#681143]
- make notifications scrollable if necessary [#681143]
- use correct application names in notifications [#681143]
- allow to return to the shield by pressing Escape [#681143]
* Minor login dialog improvements [Florian]
- update style to match the overall visuals [#660913]
- indicate whether users are logged in [#658185]
* Add support for background-repeat CSS property [Jasper; #680801]
* Add :active pseudo class on scroll handles [Florian]
* Remove markup from translated strings [Matthias; #681270]
* Misc bug fixes and cleanups: [Alban, Florian, Giovanni, Jasper, Jeremy,
Matthias, Piotr; #677893, #679944, #680064, #680170, #680216, #680426,
#681101, #681382]
Contributors:
Jeremy Bicha, Alban Browaeys, Giovanni Campagna, Matthias Clasen, Allan Day,
Piotr Drąg , Florian Müllner, Jasper St. Pierre
Translations:
Matej Urbančič [sl], Tom Tryfonidis [el], Yaron Shahrabani [he],
Kjartan Maraas [nb], Baurzhan Muftakhidinov [kk], Praveen Illa [te],
Khaled Hosny [ar], Daniel Mustieles [es], Gabor Kelemen [hu],
Fran Diéguez [gl], Sweta Kothari [gu], Aleksej Kabanov [ru],
Nilamdyuti Goswami [as], Arash Mousavi [fa], Мирослав Николић [sr, sr@latin]
3.5.4
=====
* Fix wrong result handling of remote calls [Florian; #678852]
* dateMenu: Fix regression that caused no date to be displayed [Colin]
* WindowTracker: Fix refcounting bug in get_app_for_window() [Giovanni; #678992]
* Show the workspace switcher for move-to-workspace keybinding
[Giovanni, Jasper; #674104, #660839, #679005]
* userMenu: Move "Power off" item to the bottom [Florian; #678887]
* Remove contacts search provider [Florian, Rui; #677442]
* network: don't ask for always-ask secrets when interaction isn't allowed
[Dan; #679091]
* PolkitAgent: Look for the right password prompt [Matthias; #675300]
* Implement extension updates [Jasper; #679099]
* userMenu: Don't disconnect account signals when disabled [Guillaume; #669112]
* Fix startup notification when opening calendar [Florian; #677907]
* networkAgent: use absolute path if configured [Clemens; #679212]
* recorder: Port to GStreamer-1.0 API [Florian; #679445]
* telepathyClient: don't add log messages on presence changes [Ana; #669508]
* lookingGlass: Don't use a signal callback on 'paint' to draw the border
[Jasper; #679464]
* Add support for inhibiting automount [Hans; #678597]
* Implemented banner support for the login screen [Matthias, Marius; #665346]
* boxpointer: Flip side if we would end outside the monitor [Rui; #678164]
* boxpointer: Change 'animate' parameter on show/hide to a bitmask
[Rui; #678337]
* Add a grayscale effect [Matthias, Jasper, Florian: #676782, #674499]
* UserMenu: show "Install Updates & Restart" when appropriate
[Giovanni; #677394, #680080]
* messageTray: don't show the message tray when a new notification is shown
[Ana; #677210]
* panel: don't break when indicator has no menu [Jean-Philippe; #678694]
* appMenu: Disable app menu during startup animations [Florian; #672322]
* autorun: Add a notification when unmounting drives [Cosimo; #676125]
* st-icon: Fix potential crash involving shadows [Jasper; #679776]
* Remove manual garbage collection on tweeners end [Cosimo; #679832]
* dash: hide tooltips when overview begins hiding [Stefano; #674241]
* Update modal dialog animation for new centered position [Florian; #674499]
* calendar: Fix grid lines in RTL locales [Florian; #679879]
* Integrate IBus with keyboard indicator [Rui; #641531]
* Move ibus status icon under keyboard [Matthias]
* gdm: port from libgdmgreeter to libgdm [Ray; #676401]
* Misc bug fixes and cleanups [Antoine, Cosimo, Giovanni, Jasper, Rico;
#678978, #672790, #679847, #679944]
Contributors:
Jean-Philippe Braun, Clemens Buchacher, Giovanni Campagna, Cosimo Cecchi,
Matthias Clasen, Hans de Goede, Guillaume Desmottes, Stefano Facchini,
Antoine Jacoutot, Rui Matos, Florian Müllner, Marius Rieder, Ana Risteska,
Jasper St. Pierre, Rico Tzschichholz, Colin Walters, Dan Williams
Translations:
Matej Urbančič [sl], Khaled Hosny [ar], Nguyễn Thái Ngọc Duy [vi],
Nilamdyuti Goswami [as], Alexander Shopov [bg], Ivaylo Valkov [bg],
Daniel Mustieles [es], Kjartan Maraas [nb,nn], Yaron Shahrabani [he],
Nilamdyuti Goswami [as], Chao-Hsiung Liao [zh_HK, zh_TW], Ihar Hrachyshka [be],
Praveen Illa [te]
3.5.3
=====
* calendar: Adapt to Evolution-Data-Server API changes [Matthew; #677402]
* messageTray: Don't show non urgent notifications while in fullscreen
[Adel; #677590]
* modalDialog: show dialogs on monitor with the mouse pointer [Tim; #642591]
* extensionSystem: Prepare for extension updating system [Jasper; #677586]
* appDisplay: Don't show apps in NoDisplay categories in the All view
[Jasper; #658176]
* st: Trigger theme updates on resolution changes [Florian; #677975]
* Always enable a11y [Bastien; #678095]
* telepathyClient: ignore invalidated channels [Guillaume; #677457]
* shell-app: Update app menu if necessary [Florian; #676238]
* Enable the Screen Reader menu item [Matthias; #663256]
* Disable unredirection when a modal operation is active [Giovanni]
* Make folks optional [Colin]
* Improve mount-operation support [Cosimo]
- Fix exception when showing password entry [#678428]
- Close the password entry on operation abort [#673787]
- autorun: Don't allow autorun for things we mount on startup [#660595]
- Turn passphrase prompt into a dialog [#674962]
- Implement org.Gtk.MountOperationHandler [#678516]
* Network menu improvements
- Sort Wifi networks by strength [Giovanni; #658946]
- Prefer wifi/3g over VPN in the panel [Cosimo; #672591]
* clock: Switch to using GnomeWallClock [Colin; #657074]
* remoteSearch: Allow to reference .desktop file for Title/Icon
[Florian; #678816]
* Fix memory leaks [Jasper, Pavel; #678079, #678406, #678737]
* Misc fixes [Florian, Giovanni, Guillaume, Jasper, Kjartan, Piotr, Rui;
#658955, #677497, #678396, #678502]
* Misc cleanups [Bastien, Florian, Jasper; #677426, #677515, #678096, #678416]
Contributors:
Matthew Barnes, Giovanni Campagna, Cosimo Cecchi, Matthias Clasen,
Guillaume Desmottes, Piotr Drąg, Adel Gadllah, Tim L, Kjartan Maraas,
Rui Matos, Florian Müllner, Bastien Nocera, Jasper St. Pierre, Colin Walters
Translations:
Matej Urbančič [sl], Yuri Kozlov [ru], Tom Tryfonidis [el],
Kjartan Maraas [nb], Žygimantas Beručka [lt], Luca Ferretti [it],
Khaled Hosny [ar], Daniel Mustieles [es], Fran Diéguez [gl], A S Alam [pa]
3.5.2
=====
* main: Move 'toggle-recording' binding into the shell [Florian; #674377]
* popupMenu: make sure to break the grab when the slider is not visible
[Stefano; #672713]
* st-theme-node-drawing: Don't use GL types [Neil; #672711]
* Mirror Evolution calendar settings into our own schema [Owen; #674424]
* shell-network-agent: don't crash if a request isn't found [Dan; #674961]
* notificationDaemon: Match app based on WM_CLASS [Jasper; #673761]
* NetworkMenu: use network-offline while loading [Giovanni; #674426]
* lookingGlass: Remove the Errors tab [Jasper; #675104]
* searchDisplay: Reset keyboard focus after displaying async results
[Rui; #675078]
* gdm: don't fail if fprintd is unavailable [Ray; #675006]
* messageTray: Fix scrolling up [Jasper; #661615]
* main: Close the recorder instead of pausing it [Rui; #675128]
* Accessibility [Alejandro]
- Use the proper label_actor for date menu on top panel [#675307]
- Set the proper role/label_actor for SearchResult.content [#672242]
- do not expose a label text if 'hidden' style class is used [#675341]
* Magnifier: Add brightness and contrast functionality [Joseph; #639851]
* theme: use a smaller border-radius for top bar [Jakub; #672430]
* placeDisplay: use new bookmark file location [Matthias; #675443]
* port all synchronous search providers to the async API [Jasper, Rui; #675328]
* NetworkAgent: disallow multiple requests for the same connection/setting
[Giovanni; #674961]
* userMenu: Update to latest mockups [Florian; #675802]
* util: Don't double-fork when spawning from Alt-F2 [Colin; #675789]
* messageTray: Make Source usable without subclassing [Jasper; #661236]
* panel: Check for appMenu button's reactivity before opening [Florian; #676316]
* Fix formatting of bluetooth passkey [Florian; #651251]
* notificationDaemon: Filter out file-transfer notifications [Jasper; #676175]
* Don't use global.log() [Jasper; #675790]
* Fix broken extension loading in some distributions [Owen, Alexandre; #670477]
* shell-app: Raise windows in reverse order to preserve the stacking
[Rui; #676371]
* Generalize gdm-mode [Florian; #676156]
* Switch string formatting to the one inside gjs [Jasper; #675479]
* extensionUtils: Support subdirectories in getCurrentExtension
[Jasper; #677001]
* panel: Refuse to add (legacy) status icons not part of the session mode
[Florian; #677058]
* Add an initial-setup mode [Matthias; #676697]
* status/keyboard: Port to the new input sources settings [Rui; #641531]
* NetworkMenu: show notifications for failed VPN connections [Giovanni; #676330]
* userMenu: Indicate progress on status changes [Florian; #659067]
* recorder: Honor "disable-save-to-disk" lockdown key [Rūdolfs; #673630]
* searchDisplay: Use the rowLimit we pass to the IconGrid [Christian; #675527]
* endSessionDialog: Factor out _updateDescription from _updateContent
[Alejandro; #674210]
* Fix empathy's appMenu freezing the shell [Alban; #676447]
* Code cleanups [Florian, Giovanni, Jasper; #672807, #672413, #676837, #676850,
#672272]
* Misc bug fixes [Alban, Florian, Giovanni, Guillaume, Jasper, Piotr, Rico,
Ron, Rui, Stefano; #659968, #672192, #673177, #673198, #674323, #675301,
#675370, #676347, #676806, #677097]
Contributors:
Alban Browaeys, Giovanni Campagna, Matthias Clasen, Guillaume Desmottes,
Piotr Drąg, Stefano Facchini, Rui Matos, Rūdolfs Mazurs, Florian Müllner,
Alejandro Piñeiro, Neil Roberts, Alexandre Rostovtsev, Joseph Scheuhammer,
Jakub Steiner, Jasper St. Pierre, Ray Strode, Owen Taylor, Rico Tzschichholz,
Colin Walters, Dan Winship, Ron Yorston
Translations:
OKANO Takayoshi [ja], Daniel Mustieles [es], Changwoo Ryu [ko],
Yaron Shahrabani [he], Fran Diéguez [gl], Jonh Wendell [pt_BR],
Kjartan Maraas [nb], Luca Ferretti [it], Tom Tryfonidis [el],
Sandeep Sheshrao Shedmake [mr], Takanori MATSUURA [ja], Dirgita [id],
Mantas Kriaučiūnas [lt], Matej Urbančič [sl], Jiro Matsuzawa [ja]
3.4.1
=====
* Fix crash that occurred when an icon theme change caused unexpected

View File

@ -41,7 +41,7 @@
"It can be used only by extensions.gnome.org"
#define PLUGIN_MIME_STRING "application/x-gnome-shell-integration::Gnome Shell Integration Dummy Content-Type";
#define PLUGIN_API_VERSION 3
#define PLUGIN_API_VERSION 5
typedef struct {
GDBusProxy *proxy;
@ -163,6 +163,7 @@ NP_Initialize(NPNetscapeFuncs *pfuncs, NPPluginFuncs *plugin)
plugin->newp = NPP_New;
plugin->destroy = NPP_Destroy;
plugin->getvalue = NPP_GetValue;
plugin->setwindow = NPP_SetWindow;
return NPERR_NO_ERROR;
}
@ -224,7 +225,7 @@ NPP_New(NPMIMEType mimetype,
NULL, /* interface info */
"org.gnome.Shell",
"/org/gnome/Shell",
"org.gnome.Shell",
"org.gnome.Shell.Extensions",
NULL, /* GCancellable */
&error);
if (!data->proxy)
@ -267,6 +268,7 @@ typedef struct {
NPObject parent;
NPP instance;
GDBusProxy *proxy;
GSettings *settings;
NPObject *listener;
NPObject *restart_listener;
gint signal_id;
@ -323,6 +325,9 @@ on_shell_appeared (GDBusConnection *connection,
}
}
#define SHELL_SCHEMA "org.gnome.shell"
#define ENABLED_EXTENSIONS_KEY "enabled-extensions"
static NPObject *
plugin_object_allocate (NPP instance,
NPClass *klass)
@ -332,6 +337,7 @@ plugin_object_allocate (NPP instance,
obj->instance = instance;
obj->proxy = g_object_ref (data->proxy);
obj->settings = g_settings_new (SHELL_SCHEMA);
obj->signal_id = g_signal_connect (obj->proxy, "g-signal",
G_CALLBACK (on_shell_signal), obj);
@ -367,39 +373,14 @@ plugin_object_deallocate (NPObject *npobj)
g_slice_free (PluginObject, obj);
}
static NPIdentifier api_version_id;
static NPIdentifier shell_version_id;
static NPIdentifier get_info_id;
static NPIdentifier list_extensions_id;
static NPIdentifier enable_extension_id;
static NPIdentifier install_extension_id;
static NPIdentifier uninstall_extension_id;
static NPIdentifier onextension_changed_id;
static NPIdentifier onrestart_id;
static NPIdentifier get_errors_id;
static NPIdentifier launch_extension_prefs_id;
static bool
plugin_object_has_method (NPObject *npobj,
NPIdentifier name)
{
return (name == get_info_id ||
name == list_extensions_id ||
name == enable_extension_id ||
name == install_extension_id ||
name == uninstall_extension_id ||
name == get_errors_id ||
name == launch_extension_prefs_id);
}
static inline gboolean
uuid_is_valid (const gchar *uuid)
uuid_is_valid (NPString string)
{
gsize i;
for (i = 0; uuid[i]; i ++)
for (i = 0; i < string.UTF8Length; i++)
{
gchar c = uuid[i];
gchar c = string.UTF8Characters[i];
if (c < 32 || c >= 127)
return FALSE;
@ -463,8 +444,73 @@ jsonify_variant (GVariant *variant,
}
static gboolean
plugin_list_extensions (PluginObject *obj,
NPVariant *result)
parse_args (const gchar *format_str,
uint32_t argc,
const NPVariant *argv,
...)
{
va_list args;
gsize i;
gboolean ret = FALSE;
if (strlen (format_str) != argc)
return FALSE;
va_start (args, argv);
for (i = 0; format_str[i]; i++)
{
gpointer arg_location;
const NPVariant arg = argv[i];
arg_location = va_arg (args, gpointer);
switch (format_str[i])
{
case 'u':
{
NPString string;
if (!NPVARIANT_IS_STRING (arg))
goto out;
string = NPVARIANT_TO_STRING (arg);
if (!uuid_is_valid (string))
goto out;
*(gchar **) arg_location = g_strndup (string.UTF8Characters, string.UTF8Length);
}
break;
case 'b':
if (!NPVARIANT_IS_BOOLEAN (arg))
goto out;
*(gboolean *) arg_location = NPVARIANT_TO_BOOLEAN (arg);
break;
case 'o':
if (!NPVARIANT_IS_OBJECT (arg))
goto out;
*(NPObject **) arg_location = NPVARIANT_TO_OBJECT (arg);
}
}
ret = TRUE;
out:
va_end (args);
return ret;
}
static gboolean
plugin_list_extensions (PluginObject *obj,
uint32_t argc,
const NPVariant *args,
NPVariant *result)
{
GError *error = NULL;
GVariant *res;
@ -488,91 +534,159 @@ plugin_list_extensions (PluginObject *obj,
}
static gboolean
plugin_enable_extension (PluginObject *obj,
NPString uuid,
gboolean enabled)
plugin_enable_extension (PluginObject *obj,
uint32_t argc,
const NPVariant *argv,
NPVariant *result)
{
gchar *uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
if (!uuid_is_valid (uuid_str))
gboolean ret;
gchar *uuid;
gboolean enabled;
gsize length;
gchar **uuids;
const gchar **new_uuids;
if (!parse_args ("ub", argc, argv, &uuid, &enabled))
return FALSE;
uuids = g_settings_get_strv (obj->settings, ENABLED_EXTENSIONS_KEY);
length = g_strv_length (uuids);
if (enabled)
{
g_free (uuid_str);
return FALSE;
new_uuids = g_new (const gchar *, length + 2); /* New key, NULL */
memcpy (new_uuids, uuids, length * sizeof (*new_uuids));
new_uuids[length] = uuid;
new_uuids[length + 1] = NULL;
}
else
{
gsize i = 0, j = 0;
new_uuids = g_new (const gchar *, length);
for (i = 0; i < length; i ++)
{
if (g_str_equal (uuids[i], uuid))
continue;
new_uuids[j] = uuids[i];
j++;
}
new_uuids[j] = NULL;
}
g_dbus_proxy_call (obj->proxy,
(enabled ? "EnableExtension" : "DisableExtension"),
g_variant_new ("(s)", uuid_str),
G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout */
NULL, /* cancellable */
NULL, /* callback */
NULL /* user_data */);
ret = g_settings_set_strv (obj->settings,
ENABLED_EXTENSIONS_KEY,
new_uuids);
g_free (uuid_str);
g_strfreev (uuids);
g_free (new_uuids);
g_free (uuid);
return TRUE;
return ret;
}
typedef struct _AsyncClosure AsyncClosure;
struct _AsyncClosure {
PluginObject *obj;
NPObject *callback;
NPObject *errback;
};
static void
install_extension_cb (GObject *proxy,
GAsyncResult *async_res,
gpointer user_data)
{
AsyncClosure *async_closure = (AsyncClosure *) user_data;
GError *error = NULL;
GVariant *res;
NPVariant args[1];
NPVariant result = { NPVariantType_Void };
NPObject *callback;
res = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), async_res, &error);
if (res == NULL)
{
if (g_dbus_error_is_remote_error (error))
g_dbus_error_strip_remote_error (error);
STRINGZ_TO_NPVARIANT (error->message, args[0]);
callback = async_closure->errback;
}
else
{
char *string_result;
g_variant_get (res, "(&s)", &string_result);
STRINGZ_TO_NPVARIANT (string_result, args[0]);
callback = async_closure->callback;
}
funcs.invokeDefault (async_closure->obj->instance,
callback, args, 1, &result);
funcs.releasevariantvalue (&result);
funcs.releaseobject (async_closure->callback);
funcs.releaseobject (async_closure->errback);
g_slice_free (AsyncClosure, async_closure);
}
static gboolean
plugin_install_extension (PluginObject *obj,
NPString uuid,
NPString version_tag)
plugin_install_extension (PluginObject *obj,
uint32_t argc,
const NPVariant *argv,
NPVariant *result)
{
gchar *uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
gchar *version_tag_str;
gchar *uuid;
NPObject *callback, *errback;
AsyncClosure *async_closure;
if (!uuid_is_valid (uuid_str))
{
g_free (uuid_str);
return FALSE;
}
if (!parse_args ("uoo", argc, argv, &uuid, &callback, &errback))
return FALSE;
version_tag_str = g_strndup (version_tag.UTF8Characters,
version_tag.UTF8Length);
async_closure = g_slice_new (AsyncClosure);
async_closure->obj = obj;
async_closure->callback = funcs.retainobject (callback);
async_closure->errback = funcs.retainobject (errback);
g_dbus_proxy_call (obj->proxy,
"InstallRemoteExtension",
g_variant_new ("(ss)",
uuid_str,
version_tag_str),
g_variant_new ("(s)", uuid),
G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout */
NULL, /* cancellable */
NULL, /* callback */
NULL /* user_data */);
install_extension_cb,
async_closure);
g_free (uuid_str);
g_free (version_tag_str);
g_free (uuid);
return TRUE;
}
static gboolean
plugin_uninstall_extension (PluginObject *obj,
NPString uuid,
NPVariant *result)
plugin_uninstall_extension (PluginObject *obj,
uint32_t argc,
const NPVariant *argv,
NPVariant *result)
{
GError *error = NULL;
GVariant *res;
gchar *uuid_str;
gchar *uuid;
uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
if (!uuid_is_valid (uuid_str))
{
g_free (uuid_str);
return FALSE;
}
if (!parse_args ("u", argc, argv, &uuid))
return FALSE;
res = g_dbus_proxy_call_sync (obj->proxy,
"UninstallExtension",
g_variant_new ("(s)",
uuid_str),
g_variant_new ("(s)", uuid),
G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout */
NULL, /* cancellable */
&error);
g_free (uuid_str);
g_free (uuid);
if (!res)
{
@ -585,30 +699,27 @@ plugin_uninstall_extension (PluginObject *obj,
}
static gboolean
plugin_get_info (PluginObject *obj,
NPString uuid,
NPVariant *result)
plugin_get_info (PluginObject *obj,
uint32_t argc,
const NPVariant *argv,
NPVariant *result)
{
GError *error = NULL;
GVariant *res;
gchar *uuid_str;
gchar *uuid;
uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
if (!uuid_is_valid (uuid_str))
{
g_free (uuid_str);
return FALSE;
}
if (!parse_args ("u", argc, argv, &uuid))
return FALSE;
res = g_dbus_proxy_call_sync (obj->proxy,
"GetExtensionInfo",
g_variant_new ("(s)", uuid_str),
g_variant_new ("(s)", uuid),
G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout */
NULL, /* cancellable */
&error);
g_free (uuid_str);
g_free (uuid);
if (!res)
{
@ -621,31 +732,26 @@ plugin_get_info (PluginObject *obj,
}
static gboolean
plugin_get_errors (PluginObject *obj,
NPString uuid,
NPVariant *result)
plugin_get_errors (PluginObject *obj,
uint32_t argc,
const NPVariant *argv,
NPVariant *result)
{
GError *error = NULL;
GVariant *res;
gchar *uuid_str;
gchar *uuid;
uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
if (!uuid_is_valid (uuid_str))
{
g_free (uuid_str);
return FALSE;
}
if (!parse_args ("u", argc, argv, &uuid))
return FALSE;
res = g_dbus_proxy_call_sync (obj->proxy,
"GetExtensionErrors",
g_variant_new ("(s)", uuid_str),
g_variant_new ("(s)", uuid),
G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout */
NULL, /* cancellable */
&error);
g_free (uuid_str);
if (!res)
{
g_warning ("Failed to retrieve errors: %s", error->message);
@ -657,29 +763,25 @@ plugin_get_errors (PluginObject *obj,
}
static gboolean
plugin_launch_extension_prefs (PluginObject *obj,
NPString uuid,
NPVariant *result)
plugin_launch_extension_prefs (PluginObject *obj,
uint32_t argc,
const NPVariant *argv,
NPVariant *result)
{
gchar *uuid_str;
gchar *uuid;
uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
if (!uuid_is_valid (uuid_str))
{
g_free (uuid_str);
return FALSE;
}
if (!parse_args ("u", argc, argv, &uuid))
return FALSE;
g_dbus_proxy_call (obj->proxy,
"LaunchExtensionPrefs",
g_variant_new ("(s)", uuid_str),
g_variant_new ("(s)", uuid),
G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout */
NULL, /* cancellable */
NULL, /* callback */
NULL /* user_data */);
g_free (uuid_str);
return TRUE;
}
@ -733,10 +835,40 @@ plugin_get_shell_version (PluginObject *obj,
return ret;
}
#define METHODS \
METHOD (list_extensions) \
METHOD (get_info) \
METHOD (enable_extension) \
METHOD (install_extension) \
METHOD (uninstall_extension) \
METHOD (get_errors) \
METHOD (launch_extension_prefs) \
/* */
#define METHOD(x) \
static NPIdentifier x##_id;
METHODS
#undef METHOD
static NPIdentifier api_version_id;
static NPIdentifier shell_version_id;
static NPIdentifier onextension_changed_id;
static NPIdentifier onrestart_id;
static bool
plugin_object_has_method (NPObject *npobj,
NPIdentifier name)
{
#define METHOD(x) (name == (x##_id)) ||
/* expands to (name == list_extensions_id) || FALSE; */
return METHODS FALSE;
#undef METHOD
}
static bool
plugin_object_invoke (NPObject *npobj,
NPIdentifier name,
const NPVariant *args,
const NPVariant *argv,
uint32_t argc,
NPVariant *result)
{
@ -748,61 +880,13 @@ plugin_object_invoke (NPObject *npobj,
VOID_TO_NPVARIANT (*result);
if (!plugin_object_has_method (npobj, name))
return FALSE;
#define METHOD(x) \
if (name == x##_id) \
return plugin_##x (obj, argc, argv, result);
METHODS
#undef METHOD
if (name == list_extensions_id)
return plugin_list_extensions (obj, result);
else if (name == get_info_id)
{
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
return plugin_get_info (obj, NPVARIANT_TO_STRING(args[0]), result);
}
else if (name == enable_extension_id)
{
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
if (!NPVARIANT_IS_BOOLEAN(args[1])) return FALSE;
return plugin_enable_extension (obj,
NPVARIANT_TO_STRING(args[0]),
NPVARIANT_TO_BOOLEAN(args[1]));
}
else if (name == install_extension_id)
{
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
if (!NPVARIANT_IS_STRING(args[1])) return FALSE;
return plugin_install_extension (obj,
NPVARIANT_TO_STRING(args[0]),
NPVARIANT_TO_STRING(args[1]));
}
else if (name == uninstall_extension_id)
{
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
return plugin_uninstall_extension (obj,
NPVARIANT_TO_STRING(args[0]),
result);
}
else if (name == get_errors_id)
{
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
return plugin_get_errors (obj,
NPVARIANT_TO_STRING(args[0]),
result);
}
else if (name == launch_extension_prefs_id)
{
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
return plugin_launch_extension_prefs (obj,
NPVARIANT_TO_STRING(args[0]),
result);
}
return TRUE;
return FALSE;
}
static bool
@ -946,3 +1030,12 @@ NPP_GetValue(NPP instance,
return NPERR_NO_ERROR;
}
/* Opera tries to call NPP_SetWindow without checking the
* NULL pointer beforehand. */
NPError
NPP_SetWindow(NPP instance,
NPWindow *window)
{
return NPERR_NO_ERROR;
}

View File

@ -1,5 +1,5 @@
AC_PREREQ(2.63)
AC_INIT([gnome-shell],[3.4.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_INIT([gnome-shell],[3.6.0],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_SRCDIR([src/shell-global.c])
@ -44,15 +44,15 @@ AC_SUBST(PYTHON)
# We need at least this, since gst_plugin_register_static() was added
# in 0.10.16, but nothing older than 0.10.21 has been tested.
GSTREAMER_MIN_VERSION=0.10.16
GSTREAMER_MIN_VERSION=0.11.92
recorder_modules=
build_recorder=false
AC_MSG_CHECKING([for GStreamer (needed for recording functionality)])
if $PKG_CONFIG --exists gstreamer-0.10 '>=' $GSTREAMER_MIN_VERSION ; then
if $PKG_CONFIG --exists gstreamer-1.0 '>=' $GSTREAMER_MIN_VERSION ; then
AC_MSG_RESULT(yes)
build_recorder=true
recorder_modules="gstreamer-0.10 gstreamer-base-0.10 x11"
recorder_modules="gstreamer-1.0 gstreamer-base-1.0 x11"
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes gl)
else
AC_MSG_RESULT(no)
@ -60,30 +60,32 @@ fi
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
CLUTTER_MIN_VERSION=1.9.16
CLUTTER_MIN_VERSION=1.11.11
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
GJS_MIN_VERSION=1.29.18
MUTTER_MIN_VERSION=3.4.1
FOLKS_MIN_VERSION=0.5.2
GJS_MIN_VERSION=1.33.2
MUTTER_MIN_VERSION=3.6.0
GTK_MIN_VERSION=3.3.9
GIO_MIN_VERSION=2.31.6
LIBECAL_MIN_VERSION=2.32.0
LIBEDATASERVER_MIN_VERSION=1.2.0
LIBEDATASERVERUI_MIN_VERSION=2.91.6
LIBECAL_MIN_VERSION=3.5.3
LIBEDATASERVER_MIN_VERSION=3.5.3
LIBEDATASERVERUI_MIN_VERSION=3.5.3
TELEPATHY_GLIB_MIN_VERSION=0.17.5
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
POLKIT_MIN_VERSION=0.100
STARTUP_NOTIFICATION_MIN_VERSION=0.11
GCR_MIN_VERSION=3.3.90
GNOME_DESKTOP_REQUIRED_VERSION=3.5.1
GNOME_MENUS_REQUIRED_VERSION=3.5.3
# Collect more than 20 libraries for a prize!
PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
libxml-2.0
gtk+-3.0 >= $GTK_MIN_VERSION
folks >= $FOLKS_MIN_VERSION
atk-bridge-2.0
libmutter >= $MUTTER_MIN_VERSION
gjs-internals-1.0 >= $GJS_MIN_VERSION
libgnome-menu-3.0 $recorder_modules
libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION
$recorder_modules
gdk-x11-3.0 libsoup-2.4
gl
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
@ -95,7 +97,8 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
libnm-glib libnm-util gnome-keyring-1
gcr-3 >= $GCR_MIN_VERSION)
gcr-3 >= $GCR_MIN_VERSION
gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED_VERSION)
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
@ -103,13 +106,10 @@ 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)
GJS_VERSION=`$PKG_CONFIG --modversion gjs-internals-1.0`
AC_DEFINE_UNQUOTED([GJS_VERSION], ["$GJS_VERSION"], [The version of GJS we're linking to])
AC_SUBST([GJS_VERSION], ["$GJS_VERSION"])
GNOME_KEYBINDINGS_KEYSDIR=`$PKG_CONFIG --variable keysdir gnome-keybindings`
AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
JHBUILD_TYPELIBDIR="$INTROSPECTION_TYPELIBDIR"
AC_SUBST(JHBUILD_TYPELIBDIR)
saved_CFLAGS=$CFLAGS
saved_LIBS=$LIBS
@ -123,7 +123,7 @@ 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.2 x11)
PKG_CHECK_MODULES(TRAY, gtk+-3.0)
PKG_CHECK_MODULES(GVC, libpulse libpulse-mainloop-glib gobject-2.0)
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 0.1.7)
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.5.4)
AC_MSG_CHECKING([for bluetooth support])
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
@ -239,31 +239,6 @@ AC_ARG_ENABLE(jhbuild-wrapper-script,
AS_HELP_STRING([--enable-jhbuild-wrapper-script],[Make "gnome-shell" script work for jhbuild]),,enable_jhbuild_wrapper_script=no)
AM_CONDITIONAL(USE_JHBUILD_WRAPPER_SCRIPT, test "x$enable_jhbuild_wrapper_script" = xyes)
AC_MSG_CHECKING([location of system Certificate Authority list])
AC_ARG_WITH(ca-certificates,
[AC_HELP_STRING([--with-ca-certificates=@<:@path@:>@],
[path to system Certificate Authority list])])
if test "$with_ca_certificates" = "no"; then
AC_MSG_RESULT([disabled])
else
if test -z "$with_ca_certificates"; then
for f in /etc/pki/tls/certs/ca-bundle.crt \
/etc/ssl/certs/ca-certificates.crt; do
if test -f "$f"; then
with_ca_certificates="$f"
fi
done
if test -z "$with_ca_certificates"; then
AC_MSG_ERROR([could not find. Use --with-ca-certificates=path to set, or --without-ca-certificates to disable])
fi
fi
AC_MSG_RESULT($with_ca_certificates)
AC_DEFINE_UNQUOTED(SHELL_SYSTEM_CA_FILE, ["$with_ca_certificates"], [The system TLS CA list])
fi
AC_SUBST(SHELL_SYSTEM_CA_FILE,["$with_ca_certificates"])
BROWSER_PLUGIN_DIR="${BROWSER_PLUGIN_DIR:-"\${libdir}/mozilla/plugins"}"
AC_ARG_VAR([BROWSER_PLUGIN_DIR],[Where to install the plugin to])
@ -277,6 +252,7 @@ AC_CONFIG_FILES([
docs/reference/st/Makefile
docs/reference/st/st-docs.sgml
js/Makefile
src/calendar-server/evolution-calendar.desktop.in
src/Makefile
browser-plugin/Makefile
tests/Makefile

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<KeyListEntries schema="org.gnome.shell.keybindings"
group="system"
_name="Screenshots"
wm_name="GNOME Shell"
package="gnome-shell">
<KeyListEntry name="toggle-recording"
_description="Record a screencast"/>
</KeyListEntries>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<KeyListEntries schema="org.gnome.shell.keybindings"
group="system"
_name="System"
wm_name="GNOME Shell"
package="gnome-shell">
<KeyListEntry name="toggle-message-tray"
_description="Show the message tray"/>
</KeyListEntries>

View File

@ -8,14 +8,7 @@ desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
-e "s|@VERSION[@]|$(VERSION)|" \
$< > $@ || rm $@
# Placeholder until we add intltool
%.desktop:%.desktop.in
$(AM_V_GEN) sed s/^_// < $< > $@ || rm $@
searchprovidersdir = $(pkgdatadir)/open-search-providers
dist_searchproviders_DATA = \
open-search-providers/google.xml \
open-search-providers/wikipedia.xml
@INTLTOOL_DESKTOP_RULE@
introspectiondir = $(datadir)/dbus-1/interfaces
introspection_DATA = org.gnome.ShellSearchProvider.xml
@ -36,26 +29,39 @@ dist_theme_DATA = \
theme/dash-placeholder.svg \
theme/filter-selected-ltr.svg \
theme/filter-selected-rtl.svg \
theme/gdm.css \
theme/gnome-shell.css \
theme/logged-in-indicator.svg \
theme/message-tray-background.png \
theme/noise-texture.png \
theme/panel-button-border.svg \
theme/panel-button-highlight-narrow.svg \
theme/panel-button-highlight-wide.svg \
theme/process-working.svg \
theme/running-indicator.svg \
theme/scroll-hhandle.svg \
theme/scroll-vhandle.svg \
theme/source-button-border.svg \
theme/summary-counter.svg \
theme/toggle-off-us.svg \
theme/toggle-off-intl.svg \
theme/toggle-on-us.svg \
theme/toggle-on-intl.svg \
theme/ws-switch-arrow-up.svg \
theme/ws-switch-arrow-down.svg
theme/ws-switch-arrow-up.png \
theme/ws-switch-arrow-down.png
keysdir = @GNOME_KEYBINDINGS_KEYSDIR@
keys_in_files = \
50-gnome-shell-screenshot.xml.in \
50-gnome-shell-system.xml.in \
$(NULL)
keys_DATA = $(keys_in_files:.xml.in=.xml)
gsettings_SCHEMAS = org.gnome.shell.gschema.xml
@INTLTOOL_XML_NOMERGE_RULE@
%.gschema.xml.in: %.gschema.xml.in.in Makefile
$(AM_V_GEN) sed -e 's|@GETTEXT_PACKAGE[@]|$(GETTEXT_PACKAGE)|g' \
$< > $@ || rm $@
@GSETTINGS_RULES@
# We need to compile schemas at make time
@ -68,24 +74,21 @@ all-local: gschemas.compiled
convertdir = $(datadir)/GConf/gsettings
convert_DATA = gnome-shell-overrides.convert
shadersdir = $(pkgdatadir)/shaders
shaders_DATA = \
shaders/dim-window.glsl
EXTRA_DIST = \
gnome-shell.desktop.in.in \
gnome-shell-extension-prefs.desktop.in.in \
$(introspection_DATA) \
$(menu_DATA) \
$(shaders_DATA) \
$(convert_DATA) \
org.gnome.shell.gschema.xml.in
$(keys_in_files) \
org.gnome.shell.gschema.xml.in.in
CLEANFILES = \
gnome-shell.desktop.in \
gnome-shell-extension-prefs.in \
$(desktop_DATA) \
$(keys_DATA) \
$(gsettings_SCHEMAS) \
gschemas.compiled
gschemas.compiled \
org.gnome.shell.gschema.valid \
org.gnome.shell.gschema.xml.in

View File

@ -1,7 +0,0 @@
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
<ShortName>Google</ShortName>
<Description>Google Search</Description>
<InputEncoding>UTF-8</InputEncoding>
<Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAAAAEAGABoAwAAFgAAACgAAAAQAAAAIAAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADs9Pt8xetPtu9FsfFNtu%2BTzvb2%2B%2Fne4dFJeBw0egA%2FfAJAfAA8ewBBegAAAAD%2B%2FPtft98Mp%2BwWsfAVsvEbs%2FQeqvF8xO7%2F%2F%2F63yqkxdgM7gwE%2FggM%2BfQA%2BegBDeQDe7PIbotgQufcMufEPtfIPsvAbs%2FQvq%2Bfz%2Bf%2F%2B%2B%2FZKhR05hgBBhQI8hgBAgAI9ewD0%2B%2Fg3pswAtO8Cxf4Kw%2FsJvvYAqupKsNv%2B%2Fv7%2F%2FP5VkSU0iQA7jQA9hgBDgQU%2BfQH%2F%2Ff%2FQ6fM4sM4KsN8AteMCruIqqdbZ7PH8%2Fv%2Fg6Nc%2Fhg05kAA8jAM9iQI%2BhQA%2BgQDQu6b97uv%2F%2F%2F7V8Pqw3eiWz97q8%2Ff%2F%2F%2F%2F7%2FPptpkkqjQE4kwA7kAA5iwI8iAA8hQCOSSKdXjiyflbAkG7u2s%2F%2B%2F%2F39%2F%2F7r8utrqEYtjQE8lgA7kwA7kwA9jwA9igA9hACiWSekVRyeSgiYSBHx6N%2F%2B%2Fv7k7OFRmiYtlAA5lwI7lwI4lAA7kgI9jwE9iwI4iQCoVhWcTxCmb0K%2BooT8%2Fv%2F7%2F%2F%2FJ2r8fdwI1mwA3mQA3mgA8lAE8lAE4jwA9iwE%2BhwGfXifWvqz%2B%2Ff%2F58u%2Fev6Dt4tr%2B%2F%2F2ZuIUsggA7mgM6mAM3lgA5lgA6kQE%2FkwBChwHt4dv%2F%2F%2F728ei1bCi7VAC5XQ7kz7n%2F%2F%2F6bsZkgcB03lQA9lgM7kwA2iQktZToPK4r9%2F%2F%2F9%2F%2F%2FSqYK5UwDKZAS9WALIkFn%2B%2F%2F3%2F%2BP8oKccGGcIRJrERILYFEMwAAuEAAdX%2F%2Ff7%2F%2FP%2B%2BfDvGXQLIZgLEWgLOjlf7%2F%2F%2F%2F%2F%2F9QU90EAPQAAf8DAP0AAfMAAOUDAtr%2F%2F%2F%2F7%2B%2Fu2bCTIYwDPZgDBWQDSr4P%2F%2Fv%2F%2F%2FP5GRuABAPkAA%2FwBAfkDAPAAAesAAN%2F%2F%2B%2Fz%2F%2F%2F64g1C5VwDMYwK8Yg7y5tz8%2Fv%2FV1PYKDOcAAP0DAf4AAf0AAfYEAOwAAuAAAAD%2F%2FPvi28ymXyChTATRrIb8%2F%2F3v8fk6P8MAAdUCAvoAAP0CAP0AAfYAAO4AAACAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAA</Image>
<Url type="text/html" method="GET" template="http://www.google.com/search?q={searchTerms}"/>
</OpenSearchDescription>

View File

@ -1,44 +0,0 @@
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
<ShortName>Wikipedia</ShortName>
<Description>Wikipedia, the free encyclopedia</Description>
<InputEncoding>UTF-8</InputEncoding>
<Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAQAAEABAAoAQAAFgAAACgAAAAQAAAAIAAAAAEABAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAEAgQAhIOEAMjHyABIR0gA6ejpAGlqaQCpqKkAKCgoAPz9%2FAAZGBkAmJiYANjZ2ABXWFcAent6ALm6uQA8OjwAiIiIiIiIiIiIiI4oiL6IiIiIgzuIV4iIiIhndo53KIiIiB%2FWvXoYiIiIfEZfWBSIiIEGi%2FfoqoiIgzuL84i9iIjpGIoMiEHoiMkos3FojmiLlUipYliEWIF%2BiDe0GoRa7D6GPbjcu1yIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</Image>
<Url type="text/html" method="GET" template="http://{language}.wikipedia.org/wiki/Special:Search?search={searchTerms}"/>
<!-- The criterion for being below is being listed with more than 100,000
articles on http://meta.wikimedia.org/wiki/List_of_Wikipedias -->
<Language>ar</Language>
<Language>bg</Language>
<Language>ca</Language>
<Language>cs</Language>
<Language>da</Language>
<Language>de</Language>
<Language>en</Language>
<Language>eo</Language>
<Language>es</Language>
<Language>fa</Language>
<Language>fi</Language>
<Language>fr</Language>
<Language>he</Language>
<Language>hu</Language>
<Language>id</Language>
<Language>it</Language>
<Language>ja</Language>
<Language>ko</Language>
<Language>lt</Language>
<Language>nl</Language>
<Language>no</Language>
<Language>pl</Language>
<Language>pt</Language>
<Language>ro</Language>
<Language>ru</Language>
<Language>sk</Language>
<Language>sl</Language>
<Language>sr</Language>
<Language>sv</Language>
<Language>tr</Language>
<Language>uk</Language>
<Language>vi</Language>
<Language>vo</Language>
<Language>war</Language>
<Language>zh</Language>
</OpenSearchDescription>

View File

@ -108,7 +108,7 @@
</doc:summary>
</doc:doc>
</arg>
<arg type="a{sv}" direction="out">
<arg type="aa{sv}" direction="out">
<doc:doc>
<doc:summary>
<doc:para>

View File

@ -39,10 +39,6 @@
will be displayed in the favorites area.
</_description>
</key>
<key name="disabled-open-search-providers" type="as">
<default>[]</default>
<_summary>disabled OpenSearch providers</_summary>
</key>
<key name="command-history" type="as">
<default>[]</default>
<_summary>History for command (Alt-F2) dialog</_summary>
@ -61,7 +57,6 @@ value here is from the TpConnectionPresenceType enumeration.</_summary>
<_summary>Internally used to store the last session presence status for the user. The
value here is from the GsmPresenceStatus enumeration.</_summary>
</key>
<child name="clock" schema="org.gnome.shell.clock"/>
<child name="calendar" schema="org.gnome.shell.calendar"/>
<child name="recorder" schema="org.gnome.shell.recorder"/>
<child name="keybindings" schema="org.gnome.shell.keybindings"/>
@ -88,6 +83,20 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
Keybinding to open the application menu.
</_description>
</key>
<key name="toggle-message-tray" type="as">
<default>["&lt;Super&gt;m"]</default>
<_summary>Keybinding to toggle the visibility of the message tray</_summary>
<_description>
Keybinding to toggle the visibility of the message tray.
</_description>
</key>
<key name="toggle-recording" type="as">
<default><![CDATA[['<Control><Shift><Alt>r']]]></default>
<_summary>Keybinding to toggle the screen recorder</_summary>
<_description>
Keybinding to start/stop the builtin screen recorder.
</_description>
</key>
</schema>
<schema id="org.gnome.shell.keyboard" path="/org/gnome/shell/keyboard/"
@ -101,24 +110,6 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
</key>
</schema>
<schema id="org.gnome.shell.clock" path="/org/gnome/shell/clock/"
gettext-domain="@GETTEXT_PACKAGE@">
<key name="show-seconds" type="b">
<default>false</default>
<_summary>Show time with seconds</_summary>
<_description>
If true, display seconds in time.
</_description>
</key>
<key name="show-date" type="b">
<default>false</default>
<_summary>Show date in clock</_summary>
<_description>
If true, display date in the clock, in addition to time.
</_description>
</key>
</schema>
<schema id="org.gnome.shell.recorder" path="/org/gnome/shell/recorder/"
gettext-domain="@GETTEXT_PACKAGE@">
<key name="framerate" type="i">
@ -141,7 +132,7 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
take care of its own output - this might be used to send the output
to an icecast server via shout2send or similar. When unset or set
to an empty value, the default pipeline will be used. This is currently
'vp8enc quality=8 speed=6 threads=%T ! queue ! webmmux'
'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux'
and records to WEBM using the VP8 codec. %T is used as a placeholder
for a guess at the optimal thread count on the system.
</_description>

View File

@ -1,27 +0,0 @@
#version 110
uniform sampler2D tex;
uniform float fraction;
uniform float height;
const float c = -0.2;
const float border_max_height = 60.0;
mat4 contrast = mat4 (1.0 + c, 0.0, 0.0, 0.0,
0.0, 1.0 + c, 0.0, 0.0,
0.0, 0.0, 1.0 + c, 0.0,
0.0, 0.0, 0.0, 1.0);
vec4 off = vec4(0.633, 0.633, 0.633, 0);
void main()
{
vec4 color = texture2D(tex, cogl_tex_coord_in[0].xy);
float y = height * cogl_tex_coord_in[0].y;
// To reduce contrast, blend with a mid gray
cogl_color_out = color * contrast - off * c * color.a;
// We only fully dim at a distance of BORDER_MAX_HEIGHT from the top and
// when the fraction is 1.0. For other locations and fractions we linearly
// interpolate back to the original undimmed color, so the top of the window
// is at full color.
cogl_color_out = color + (cogl_color_out - color) * max(min(y / border_max_height, 1.0), 0.0);
cogl_color_out = color + (cogl_color_out - color) * fraction;
}

View File

@ -10,11 +10,11 @@
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="28"
height="25"
width="29"
height="29"
id="svg10621"
version="1.1"
inkscape:version="0.48.1 r9760"
inkscape:version="0.48.2 r9819"
sodipodi:docname="calendar-today.svg">
<defs
id="defs10623">
@ -118,6 +118,17 @@
fx="51"
fy="30"
r="42" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient34508-1-3"
id="radialGradient3113"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
cx="51"
cy="30"
fx="51"
fy="30"
r="42" />
</defs>
<sodipodi:namedview
id="base"
@ -127,20 +138,29 @@
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="15.839192"
inkscape:cx="8.3750933"
inkscape:cy="8.0837211"
inkscape:cx="20.652108"
inkscape:cy="11.839084"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
showgrid="true"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="1440"
inkscape:window-height="843"
inkscape:window-width="1280"
inkscape:window-height="741"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:window-maximized="1" />
inkscape:window-y="27"
inkscape:window-maximized="1"
borderlayer="true">
<inkscape:grid
type="xygrid"
id="grid3109"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata10626">
<rdf:RDF>
@ -157,31 +177,28 @@
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-469.08263,-536.99307)">
<g
id="g3003">
<path
inkscape:export-ydpi="90"
inkscape:export-xdpi="90"
inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/motion/textures/panel.png"
transform="matrix(0.43692393,0,0,1.3783114,460.60467,517.48289)"
sodipodi:end="6.2831853"
sodipodi:start="3.1415927"
d="M 9,29.999999 C 9.0000011,21.163443 27.804042,14 51.000002,14 74.195961,14 93,21.163444 93,30 l -42,0 z"
sodipodi:ry="16"
sodipodi:rx="42"
sodipodi:cy="30"
sodipodi:cx="51"
id="path34506-3"
style="opacity:0.4625;color:#000000;fill:url(#radialGradient2997);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
sodipodi:type="arc" />
<rect
y="558.85046"
x="468.96878"
height="3.1425927"
width="28.149134"
id="rect2996"
style="fill:#ffffff;fill-opacity:0.50196078;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none" />
</g>
transform="translate(-469.08263,-532.99307)">
<path
sodipodi:type="arc"
style="opacity:0.4625;color:#000000;fill:url(#radialGradient3113);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="path34506-3"
sodipodi:cx="51"
sodipodi:cy="30"
sodipodi:rx="42"
sodipodi:ry="16"
d="M 9,29.999999 A 42,16 0 0 1 93,30 l -42,0 z"
sodipodi:start="3.1415927"
sodipodi:end="6.2831853"
transform="matrix(0.43692393,0,0,1.3783114,461.29951,517.6437)"
inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/motion/textures/panel.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<rect
style="fill:#ffffff;fill-opacity:0.50196078;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none"
id="rect2996"
width="31"
height="3"
x="468.08264"
y="558.99304" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -1,180 +0,0 @@
/* Copyright 2011, Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* Login Dialog */
.login-dialog-title {
font-size: 14pt;
font-weight: bold;
color: #666666;
padding-bottom: 2em;
}
.login-dialog {
border-radius: 16px;
min-height: 150px;
max-height: 700px;
min-width: 350px;
}
.login-dialog-prompt-fingerprint-message {
font-size: 10.5pt;
}
.login-dialog-user-list-view {
-st-vfade-offset: 1em;
}
.login-dialog-user-list {
spacing: 12px;
}
.login-dialog-user-list-item {
color: #666666;
}
.login-dialog-user-list-item:ltr {
padding-right: 1em;
}
.login-dialog-user-list-item:rtl {
padding-left: 1em;
}
.login-dialog-user-list-item .login-dialog-user-list-item-name {
font-size: 20pt;
padding-left: 1em;
color: #666666;
}
.login-dialog-user-list-item:hover .login-dialog-user-list-item-name {
color: white;
}
.login-dialog-user-list-item:focus .login-dialog-user-list-item-name {
color: white;
text-shadow: black 0px 2px 2px;
}
.login-dialog-user-list-item-vertical-layout {
spacing: 2px;
}
.login-dialog-user-list-item .login-dialog-user-list-item-focus-bin {
background-color: rgba(0,0,0,0.0);
height: 2px;
}
.login-dialog-user-list-item:focus .login-dialog-user-list-item-focus-bin {
background-color: #666666;
}
.login-dialog-user-list-item-icon {
border: 2px solid #8b8b8b;
border-radius: 8px;
width: 64px;
height: 64px;
}
.login-dialog-not-listed-button {
padding-top: 2em;
}
.login-dialog-not-listed-label {
font-size: 14pt;
font-weight: bold;
color: #666666;
}
.login-dialog-not-listed-button:hover .login-dialog-not-listed-label {
color: white;
}
.login-dialog-prompt-layout {
padding-bottom: 32px;
}
.login-dialog-prompt-label {
color: white;
font-size: 20pt;
}
.login-dialog-prompt-entry {
padding: 4px;
border-radius: 4px;
border: 2px solid #5656cc;
color: black;
background-color: white;
caret-color: black;
caret-size: 1px;
width: 15em;
}
.login-dialog-prompt-entry .capslock-warning {
icon-size: 16px;
warning-color: #999;
}
.login-dialog-prompt-entry:insensitive {
color: rgba(0,0,0,0.7);
border: 2px solid #565656;
}
.login-dialog-session-list {
color: #ffffff;
font-size: 10.5pt;
}
.login-dialog-session-list-button {
padding: 4px;
}
.login-dialog-session-list-button:focus {
background-color: #4c4c4c;
}
.login-dialog-session-list-button:active {
background-color: #4c4c4c;
}
.login-dialog-session-list-button:hover {
font-weight: bold;
}
.login-dialog-session-list-scroll-view {
background-gradient-start: rgba(80,80,80,0.3);
background-gradient-end: rgba(80,80,80,0.7);
background-gradient-direction: vertical;
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.9);
border-radius: 8px;
border: 1px solid rgba(80,80,80,1.0);
padding: .5em;
}
.login-dialog-session-list-item:focus {
background-color: #666666;
}
.login-dialog-session-list-triangle {
padding-right: .5em;
}
.login-dialog-session-list-item-box {
spacing: .25em;
}
.login-dialog-session-list-item-dot {
width: .75em;
height: .75em;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,130 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="300"
height="80"
id="svg7355"
version="1.1"
inkscape:version="0.48.2 r9819"
sodipodi:docname="logged-in-indicator.svg">
<metadata
id="metadata4175">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#2c1cff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:window-width="1440"
inkscape:window-height="843"
id="namedview4173"
showgrid="false"
inkscape:zoom="2.8760889"
inkscape:cx="106.00403"
inkscape:cy="80.68078"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="g30864" />
<defs
id="defs7357">
<radialGradient
xlink:href="#linearGradient36429"
id="radialGradient7461"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.5919312,0,0,0.57582113,-20.687059,48.400487)"
cx="47.428951"
cy="167.16817"
fx="47.428951"
fy="167.16817"
r="37" />
<linearGradient
id="linearGradient36429">
<stop
id="stop36431"
offset="0"
style="stop-color:#ffffff;stop-opacity:1;" />
<stop
id="stop36433"
offset="1"
style="stop-color:#ffffff;stop-opacity:0;" />
</linearGradient>
<radialGradient
xlink:href="#linearGradient36471"
id="radialGradient7463"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.1891549,0,0,0.55513246,-9.281289,36.12653)"
cx="49.067139"
cy="242.50381"
fx="49.067139"
fy="242.50381"
r="37.00671" />
<linearGradient
id="linearGradient36471">
<stop
id="stop36473"
offset="0"
style="stop-color:#ffffff;stop-opacity:1;" />
<stop
id="stop36475"
offset="1"
style="stop-color:#ffffff;stop-opacity:0;" />
</linearGradient>
<radialGradient
r="37.00671"
fy="242.50381"
fx="49.067139"
cy="242.50381"
cx="49.067139"
gradientTransform="matrix(3.4218418,0,0,0.03365337,-61.309005,138.5071)"
gradientUnits="userSpaceOnUse"
id="radialGradient7488"
xlink:href="#linearGradient36471" />
</defs>
<g
id="layer1"
transform="matrix(1.6213276,0,0,1.6213276,-431.6347,-272.5745)">
<g
style="display:inline"
id="g30864"
transform="translate(255.223,70.118091)">
<rect
ry="3.4593496"
rx="8.8641119"
y="76.159348"
x="12.596948"
height="71.116341"
width="182.22595"
id="rect14000"
style="opacity:0.371875;fill:url(#radialGradient7461);fill-opacity:1;stroke:none" />
<path
id="rect34520"
d="m 194.80022,146.83551 -182.559919,0"
style="opacity:0.35;fill:none;stroke:url(#radialGradient7488);stroke-width:0.61184424;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
connector-curvature="0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

View File

@ -9,7 +9,7 @@
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="21"
width="17"
height="10"
id="svg2"
version="1.1"
@ -66,9 +66,9 @@
<rect
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none"
id="rect3796"
width="3"
width="7"
height="2"
x="9"
x="5"
y="8" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -1,64 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="10"
height="4"
id="svg2"
version="1.1"
inkscape:version="0.47 r22583"
sodipodi:docname="scroll-hhandle.svg">
<defs
id="defs4">
</defs>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#323232;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="rect3592"
width="2"
height="4"
x="0"
y="0"
rx="0"
ry="0" />
<use
x="0"
y="0"
xlink:href="#rect3592"
id="use2825"
transform="translate(8,0)"
width="10"
height="4" />
<use
x="0"
y="0"
xlink:href="#use2825"
id="use2827"
transform="translate(-4,0)"
width="10"
height="4" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -1,62 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="4"
height="10"
id="svg2"
version="1.1"
inkscape:version="0.47 r22583"
sodipodi:docname="scroll-hhandle.svg">
<metadata
id="metadata7">
<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
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#323232;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="rect3592"
width="2"
height="4"
x="0"
y="-4"
rx="0"
ry="0"
transform="matrix(0,1,-1,0,0,0)" />
<use
x="0"
y="0"
xlink:href="#rect3592"
id="use3705"
transform="translate(0,4)"
width="4"
height="10" />
<use
x="0"
y="0"
xlink:href="#use3705"
id="use3707"
transform="translate(0,4)"
width="4"
height="10" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.0"
id="Foreground"
x="0px"
y="0px"
width="32"
height="32"
viewBox="0 0 23.272727 23.272727"
enable-background="new 0 0 16 16"
xml:space="preserve"
sodipodi:version="0.32"
inkscape:version="0.48.2 r9819"
sodipodi:docname="summary-counter.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
id="metadata2399"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
id="defs2397"><linearGradient
id="linearGradient3173"><stop
style="stop-color:#c4c4c4;stop-opacity:1;"
offset="0"
id="stop3175" /><stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="1"
id="stop3177" /></linearGradient><inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 8 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="16 : 8 : 1"
inkscape:persp3d-origin="8 : 5.3333333 : 1"
id="perspective2401" /><filter
color-interpolation-filters="sRGB"
inkscape:collect="always"
id="filter16494-4"
x="-0.20989846"
width="1.4197969"
y="-0.20903821"
height="1.4180764"><feGaussianBlur
inkscape:collect="always"
stdDeviation="1.3282637"
id="feGaussianBlur16496-8" /></filter><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient16498-6"
id="radialGradient16504-1"
cx="7.6582627"
cy="5.8191104"
fx="7.6582627"
fy="5.8191104"
r="8.6928644"
gradientTransform="matrix(1.0474339,0,0,1.0517402,-0.3632615,-0.42032492)"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
id="linearGradient16498-6"><stop
style="stop-color:#9FD0FF;stop-opacity:1"
offset="0"
id="stop16500-8" /><stop
style="stop-color:#3465A4;stop-opacity:1"
offset="1"
id="stop16502-0" /></linearGradient></defs><sodipodi:namedview
inkscape:window-height="709"
inkscape:window-width="1366"
inkscape:pageshadow="2"
inkscape:pageopacity="0"
guidetolerance="10.0"
gridtolerance="10.0"
objecttolerance="10.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#000000"
id="base"
showgrid="false"
inkscape:zoom="11.313708"
inkscape:cx="15.386407"
inkscape:cy="13.739577"
inkscape:window-x="0"
inkscape:window-y="1179"
inkscape:current-layer="g16402-8"
showguides="true"
inkscape:guide-bbox="true"
borderlayer="true"
inkscape:showpageshadow="false"
inkscape:window-maximized="1"><inkscape:grid
type="xygrid"
id="grid11246"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" /></sodipodi:namedview><g
style="display:inline"
id="g16402-8"
transform="translate(4.7533483,2.8238929)"><g
id="g3175-4"
transform="translate(-0.89995416,0.94028614)"><path
sodipodi:type="inkscape:offset"
inkscape:radius="0"
inkscape:original="M 7.65625 0.125 C 3.2589349 0.125 -0.3125 3.7070002 -0.3125 8.125 C -0.3125 12.543001 3.2589349 16.125 7.65625 16.125 C 12.053566 16.125 15.625 12.543001 15.625 8.125 C 15.625 3.7070002 12.053566 0.125 7.65625 0.125 z "
xlink:href="#path2394-32"
style="opacity:0.52994014;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.18181825;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter16494-4);enable-background:accumulate"
id="path16480-5"
inkscape:href="#path2394-32"
d="m 7.65625,0.125 c -4.3973151,0 -7.96875,3.5820002 -7.96875,8 0,4.418001 3.5714349,8 7.96875,8 4.397316,0 7.96875,-3.581999 7.96875,-8 0,-4.4179998 -3.571434,-8 -7.96875,-8 z"
transform="translate(0,1.028519)" /><path
clip-rule="evenodd"
d="m -0.30428257,8.1237596 c 0,-4.4179998 3.56522987,-7.9999996 7.96254497,-7.9999996 4.3973156,0 7.9625456,3.5819998 7.9625456,7.9999996 0,4.4180014 -3.56523,8.0000004 -7.9625456,8.0000004 -4.3973151,0 -7.96254497,-3.581999 -7.96254497,-8.0000004 z"
id="path2394-32"
style="color:#000000;fill:url(#radialGradient16504-1);fill-opacity:1;fill-rule:nonzero;stroke:#eeeeec;stroke-width:1.4545455;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
sodipodi:nodetypes="csssc"
inkscape:connector-curvature="0" /><g
id="g3172-6" /></g></g></svg>

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 850 B

View File

@ -1,376 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
width="96"
height="96"
id="svg25070"
inkscape:version="0.48.0 r9654"
sodipodi:docname="ws-switch-arrow-down.svg">
<metadata
id="metadata3353">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="718"
inkscape:window-height="480"
id="namedview3351"
showgrid="false"
inkscape:zoom="2.6979167"
inkscape:cx="48"
inkscape:cy="48"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:window-maximized="0"
inkscape:current-layer="svg25070" />
<defs
id="defs25072">
<linearGradient
x1="-86.552246"
y1="185.439"
x2="-83.37072"
y2="197.31261"
id="linearGradient24957"
xlink:href="#linearGradient4034-0-4"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(6,0)" />
<linearGradient
id="linearGradient4034-0-4">
<stop
id="stop4036-5-7"
style="stop-color:#eeeeec;stop-opacity:1"
offset="0" />
<stop
id="stop4038-9-6"
style="stop-color:#babdb6;stop-opacity:1"
offset="1" />
</linearGradient>
<filter
x="0"
y="0"
width="1"
height="1"
color-interpolation-filters="sRGB"
id="filter24765">
<feColorMatrix
result="fbSourceGraphic"
values="1"
type="saturate"
id="feColorMatrix24767" />
<feColorMatrix
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
in="fbSourceGraphic"
id="feColorMatrix24769" />
</filter>
<linearGradient
x1="-74.520325"
y1="169.06032"
x2="-74.520325"
y2="205.94189"
id="linearGradient24955"
xlink:href="#linearGradient4632-1-3-9-3-2"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-5,0)" />
<linearGradient
id="linearGradient4632-1-3-9-3-2">
<stop
id="stop4634-1-8-3-9-0"
style="stop-color:#eeeeec;stop-opacity:1"
offset="0" />
<stop
id="stop4636-1-9-9-8-8"
style="stop-color:#ffffff;stop-opacity:1"
offset="0.0274937" />
<stop
id="stop4638-8-3-9-6-6"
style="stop-color:#f2f2f2;stop-opacity:1"
offset="0.274937" />
<stop
id="stop4640-8-5-7-8-9"
style="stop-color:#eeeeec;stop-opacity:1"
offset="0.38707438" />
<stop
id="stop4642-5-41-9-6-9"
style="stop-color:#d9dad8;stop-opacity:1"
offset="0.66528589" />
<stop
id="stop4644-5-2-7-9-2"
style="stop-color:#dfe0dd;stop-opacity:1"
offset="0.76745707" />
<stop
id="stop4646-3-2-3-7-3"
style="stop-color:#f0f0f0;stop-opacity:1"
offset="1" />
</linearGradient>
<radialGradient
cx="-33.412369"
cy="185.74171"
r="2.3554697"
fx="-33.412369"
fy="185.74171"
id="radialGradient24959"
xlink:href="#linearGradient4869-4-1"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0075,0,0,1.0075,-5.4544,-1.25141)" />
<linearGradient
id="linearGradient4869-4-1">
<stop
id="stop4871-6-2"
style="stop-color:#ffffff;stop-opacity:1"
offset="0" />
<stop
id="stop4879-7-4"
style="stop-color:#eeeeec;stop-opacity:1"
offset="0.31807542" />
<stop
id="stop4877-6-1"
style="stop-color:#c8c9c6;stop-opacity:1"
offset="0.74691135" />
<stop
id="stop4873-1-0"
style="stop-color:#d3d7cf;stop-opacity:1"
offset="1" />
</linearGradient>
<filter
x="0"
y="0"
width="1"
height="1"
color-interpolation-filters="sRGB"
id="filter25011">
<feColorMatrix
result="fbSourceGraphic"
values="1"
type="saturate"
id="feColorMatrix25013" />
<feColorMatrix
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
in="fbSourceGraphic"
id="feColorMatrix25015" />
</filter>
<radialGradient
cx="-33.412369"
cy="185.74171"
r="2.3554697"
fx="-33.412369"
fy="185.74171"
id="radialGradient24961"
xlink:href="#linearGradient4869-4-0"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0075,0,0,1.0075,-5.4544,-1.25141)" />
<linearGradient
id="linearGradient4869-4-0">
<stop
id="stop4871-6-8"
style="stop-color:#ffffff;stop-opacity:1"
offset="0" />
<stop
id="stop4879-7-5"
style="stop-color:#eeeeec;stop-opacity:1"
offset="0.31807542" />
<stop
id="stop4877-6-5"
style="stop-color:#c8c9c6;stop-opacity:1"
offset="0.74691135" />
<stop
id="stop4873-1-4"
style="stop-color:#d3d7cf;stop-opacity:1"
offset="1" />
</linearGradient>
<filter
x="0"
y="0"
width="1"
height="1"
color-interpolation-filters="sRGB"
id="filter25023">
<feColorMatrix
result="fbSourceGraphic"
values="1"
type="saturate"
id="feColorMatrix25025" />
<feColorMatrix
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
in="fbSourceGraphic"
id="feColorMatrix25027" />
</filter>
<linearGradient
x1="-39.858727"
y1="184.61784"
x2="-38.244785"
y2="188.84898"
id="linearGradient24963"
xlink:href="#linearGradient4941"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient4941">
<stop
id="stop4943"
style="stop-color:#ffffff;stop-opacity:1"
offset="0" />
<stop
id="stop4945"
style="stop-color:#ffffff;stop-opacity:0"
offset="1" />
</linearGradient>
<filter
x="0"
y="0"
width="1"
height="1"
color-interpolation-filters="sRGB"
id="filter25033">
<feColorMatrix
result="fbSourceGraphic"
values="1"
type="saturate"
id="feColorMatrix25035" />
<feColorMatrix
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
in="fbSourceGraphic"
id="feColorMatrix25037" />
</filter>
<linearGradient
x1="-39.858727"
y1="184.61784"
x2="-38.244785"
y2="188.84898"
id="linearGradient24965"
xlink:href="#linearGradient4941-7"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient4941-7">
<stop
id="stop4943-2"
style="stop-color:#ffffff;stop-opacity:1"
offset="0" />
<stop
id="stop4945-5"
style="stop-color:#ffffff;stop-opacity:0"
offset="1" />
</linearGradient>
<filter
x="0"
y="0"
width="1"
height="1"
color-interpolation-filters="sRGB"
id="filter25043">
<feColorMatrix
result="fbSourceGraphic"
values="1"
type="saturate"
id="feColorMatrix25045" />
<feColorMatrix
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
in="fbSourceGraphic"
id="feColorMatrix25047" />
</filter>
<filter
x="0"
y="0"
width="1"
height="1"
color-interpolation-filters="sRGB"
id="filter25049">
<feColorMatrix
result="fbSourceGraphic"
values="1"
type="saturate"
id="feColorMatrix25051" />
<feColorMatrix
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
in="fbSourceGraphic"
id="feColorMatrix25053" />
</filter>
<filter
x="0"
y="0"
width="1"
height="1"
color-interpolation-filters="sRGB"
id="filter25055">
<feColorMatrix
result="fbSourceGraphic"
values="1"
type="saturate"
id="feColorMatrix25057" />
<feColorMatrix
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
in="fbSourceGraphic"
id="feColorMatrix25059" />
</filter>
</defs>
<g
transform="matrix(0,1,-1,0,48.0003,4.1307112e-7)"
id="layer1">
<g
transform="matrix(-2,0,0,2,-97.2497,-374.967)"
id="g4030-1-8"
style="stroke:#000000;stroke-opacity:1;display:inline">
<path
d="m -72.5,173.5 -14,14 14,14"
id="path3165-7-3"
style="color:#000000;fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
inkscape:connector-curvature="0" />
</g>
<path
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
transform="matrix(-3.34328,0,0,3.34328,-89.2797,-623.176)"
id="path4050-2-7-9-4"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
inkscape:connector-curvature="0" />
<path
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
transform="matrix(-3.34328,0,0,3.34328,-111.2797,-623.176)"
id="path4050-2-7-9-4-8"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
inkscape:connector-curvature="0" />
<path
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
transform="matrix(-2.86565,0,0,2.86565,-70.8457,-534.143)"
id="path4050-2-7-9-4-0"
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
inkscape:connector-curvature="0" />
<path
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
transform="matrix(-2.86565,0,0,2.86565,-92.8457,-534.143)"
id="path4050-2-7-9-4-0-9"
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
inkscape:connector-curvature="0" />
<path
d="m 47.87528,-34.0295 c 1.53896,0.0448 3.0511,0.70928 4.125,1.8125 l 32.25,32.25 -32.25,32.25 c -2.2253,2.2253 -6.2747,2.2253 -8.5,0 -2.2253,-2.22528 -2.2253,-6.2747 0,-8.5 l 23.75,-23.75 -23.75,-23.75 c -1.73168,-1.6731 -2.295,-4.44228 -1.3546,-6.65894 0.94042,-2.21668 3.32312,-3.73604 5.7296,-3.65356 z"
id="path3165-7-3-1"
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;opacity:0.35;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
inkscape:connector-curvature="0" />
<path
d="m 41.8316,28.09418 c -0.014,-1.58898 0.54158,-3.18406 1.66868,-4.31118 l 23.75,-23.75 m -25.1046,-30.40894 c 0.94042,-2.21668 3.32312,-3.73604 5.7296,-3.65356 1.53896,0.0448 3.0511,0.70928 4.125,1.8125 l 32.25,32.25"
id="path3165-7-3-1-9"
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
inkscape:connector-curvature="0" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 841 B

View File

@ -1,447 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="96"
height="96"
id="svg25070"
version="1.1"
inkscape:version="0.48.0 r9654"
sodipodi:docname="ws-switch-arrow-up.svg">
<defs
id="defs25072">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 24 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="48 : 24 : 1"
inkscape:persp3d-origin="24 : 16 : 1"
id="perspective25078" />
<inkscape:perspective
id="perspective24985"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4034-0-4"
id="linearGradient24957"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(6)"
x1="-86.552246"
y1="185.439"
x2="-83.37072"
y2="197.31261" />
<linearGradient
inkscape:collect="always"
id="linearGradient4034-0-4">
<stop
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"
offset="0"
id="stop4036-5-7" />
<stop
style="stop-color: rgb(186, 189, 182); stop-opacity: 1;"
offset="1"
id="stop4038-9-6" />
</linearGradient>
<filter
id="filter24765"
inkscape:label="Invert"
x="0"
y="0"
width="1"
height="1"
inkscape:menu="Color"
inkscape:menu-tooltip="Invert colors"
color-interpolation-filters="sRGB">
<feColorMatrix
id="feColorMatrix24767"
type="saturate"
values="1"
result="fbSourceGraphic" />
<feColorMatrix
id="feColorMatrix24769"
in="fbSourceGraphic"
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
</filter>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4632-1-3-9-3-2"
id="linearGradient24955"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-5)"
x1="-74.520325"
y1="169.06032"
x2="-74.520325"
y2="205.94189" />
<linearGradient
id="linearGradient4632-1-3-9-3-2">
<stop
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"
offset="0"
id="stop4634-1-8-3-9-0" />
<stop
id="stop4636-1-9-9-8-8"
offset="0.0274937"
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" />
<stop
id="stop4638-8-3-9-6-6"
offset="0.274937"
style="stop-color: rgb(242, 242, 242); stop-opacity: 1;" />
<stop
id="stop4640-8-5-7-8-9"
offset="0.38707438"
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" />
<stop
id="stop4642-5-41-9-6-9"
offset="0.66528589"
style="stop-color: rgb(217, 218, 216); stop-opacity: 1;" />
<stop
id="stop4644-5-2-7-9-2"
offset="0.76745707"
style="stop-color: rgb(223, 224, 221); stop-opacity: 1;" />
<stop
style="stop-color: rgb(240, 240, 240); stop-opacity: 1;"
offset="1"
id="stop4646-3-2-3-7-3" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient4869-4-1"
id="radialGradient24959"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0075, 0, 0, 1.0075, -5.4544, -1.25141)"
cx="-33.412369"
cy="185.74171"
fx="-33.412369"
fy="185.74171"
r="2.3554697" />
<linearGradient
id="linearGradient4869-4-1">
<stop
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
offset="0"
id="stop4871-6-2" />
<stop
id="stop4879-7-4"
offset="0.31807542"
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" />
<stop
id="stop4877-6-1"
offset="0.74691135"
style="stop-color: rgb(200, 201, 198); stop-opacity: 1;" />
<stop
style="stop-color: rgb(211, 215, 207); stop-opacity: 1;"
offset="1"
id="stop4873-1-0" />
</linearGradient>
<filter
id="filter25011"
inkscape:label="Invert"
x="0"
y="0"
width="1"
height="1"
inkscape:menu="Color"
inkscape:menu-tooltip="Invert colors"
color-interpolation-filters="sRGB">
<feColorMatrix
id="feColorMatrix25013"
type="saturate"
values="1"
result="fbSourceGraphic" />
<feColorMatrix
id="feColorMatrix25015"
in="fbSourceGraphic"
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
</filter>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient4869-4-0"
id="radialGradient24961"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0075, 0, 0, 1.0075, -5.4544, -1.25141)"
cx="-33.412369"
cy="185.74171"
fx="-33.412369"
fy="185.74171"
r="2.3554697" />
<linearGradient
id="linearGradient4869-4-0">
<stop
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
offset="0"
id="stop4871-6-8" />
<stop
id="stop4879-7-5"
offset="0.31807542"
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" />
<stop
id="stop4877-6-5"
offset="0.74691135"
style="stop-color: rgb(200, 201, 198); stop-opacity: 1;" />
<stop
style="stop-color: rgb(211, 215, 207); stop-opacity: 1;"
offset="1"
id="stop4873-1-4" />
</linearGradient>
<filter
id="filter25023"
inkscape:label="Invert"
x="0"
y="0"
width="1"
height="1"
inkscape:menu="Color"
inkscape:menu-tooltip="Invert colors"
color-interpolation-filters="sRGB">
<feColorMatrix
id="feColorMatrix25025"
type="saturate"
values="1"
result="fbSourceGraphic" />
<feColorMatrix
id="feColorMatrix25027"
in="fbSourceGraphic"
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
</filter>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4941"
id="linearGradient24963"
gradientUnits="userSpaceOnUse"
x1="-39.858727"
y1="184.61784"
x2="-38.244785"
y2="188.84898" />
<linearGradient
inkscape:collect="always"
id="linearGradient4941">
<stop
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
offset="0"
id="stop4943" />
<stop
style="stop-color: rgb(255, 255, 255); stop-opacity: 0;"
offset="1"
id="stop4945" />
</linearGradient>
<filter
id="filter25033"
inkscape:label="Invert"
x="0"
y="0"
width="1"
height="1"
inkscape:menu="Color"
inkscape:menu-tooltip="Invert colors"
color-interpolation-filters="sRGB">
<feColorMatrix
id="feColorMatrix25035"
type="saturate"
values="1"
result="fbSourceGraphic" />
<feColorMatrix
id="feColorMatrix25037"
in="fbSourceGraphic"
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
</filter>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4941-7"
id="linearGradient24965"
gradientUnits="userSpaceOnUse"
x1="-39.858727"
y1="184.61784"
x2="-38.244785"
y2="188.84898" />
<linearGradient
inkscape:collect="always"
id="linearGradient4941-7">
<stop
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
offset="0"
id="stop4943-2" />
<stop
style="stop-color: rgb(255, 255, 255); stop-opacity: 0;"
offset="1"
id="stop4945-5" />
</linearGradient>
<filter
id="filter25043"
inkscape:label="Invert"
x="0"
y="0"
width="1"
height="1"
inkscape:menu="Color"
inkscape:menu-tooltip="Invert colors"
color-interpolation-filters="sRGB">
<feColorMatrix
id="feColorMatrix25045"
type="saturate"
values="1"
result="fbSourceGraphic" />
<feColorMatrix
id="feColorMatrix25047"
in="fbSourceGraphic"
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
</filter>
<filter
id="filter25049"
inkscape:label="Invert"
x="0"
y="0"
width="1"
height="1"
inkscape:menu="Color"
inkscape:menu-tooltip="Invert colors"
color-interpolation-filters="sRGB">
<feColorMatrix
id="feColorMatrix25051"
type="saturate"
values="1"
result="fbSourceGraphic" />
<feColorMatrix
id="feColorMatrix25053"
in="fbSourceGraphic"
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
</filter>
<filter
id="filter25055"
inkscape:label="Invert"
x="0"
y="0"
width="1"
height="1"
inkscape:menu="Color"
inkscape:menu-tooltip="Invert colors"
color-interpolation-filters="sRGB">
<feColorMatrix
id="feColorMatrix25057"
type="saturate"
values="1"
result="fbSourceGraphic" />
<feColorMatrix
id="feColorMatrix25059"
in="fbSourceGraphic"
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
</filter>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.8284271"
inkscape:cx="-12.356322"
inkscape:cy="57.536221"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="1200"
inkscape:window-height="840"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:window-maximized="0" />
<metadata
id="metadata25075">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer"
transform="translate(0, 48)">
<g
id="g3181"
transform="matrix(0,1,-1,0,48.0003,-48)">
<g
style="stroke:#000000;stroke-opacity:1;display:inline"
transform="matrix(2,0,0,2,193.25,-374.967)"
id="g4030-1-8">
<path
style="color:#000000;fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
d="m -72.5,173.5 -14,14 14,14"
id="path3165-7-3"
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0" />
</g>
<path
transform="matrix(3.34328,0,0,3.34328,185.28,-623.176)"
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
sodipodi:ry="2.09375"
sodipodi:rx="2.09375"
sodipodi:cy="186.40625"
sodipodi:cx="-38.59375"
id="path4050-2-7-9-4"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
sodipodi:type="arc" />
<path
transform="matrix(3.34328,0,0,3.34328,207.28,-623.176)"
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
sodipodi:ry="2.09375"
sodipodi:rx="2.09375"
sodipodi:cy="186.40625"
sodipodi:cx="-38.59375"
id="path4050-2-7-9-4-8"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
sodipodi:type="arc" />
<path
transform="matrix(2.86565,0,0,2.86565,166.846,-534.143)"
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
sodipodi:ry="2.09375"
sodipodi:rx="2.09375"
sodipodi:cy="186.40625"
sodipodi:cx="-38.59375"
id="path4050-2-7-9-4-0"
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
sodipodi:type="arc" />
<path
transform="matrix(2.86565,0,0,2.86565,188.846,-534.143)"
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
sodipodi:ry="2.09375"
sodipodi:rx="2.09375"
sodipodi:cy="186.40625"
sodipodi:cx="-38.59375"
id="path4050-2-7-9-4-0-9"
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
sodipodi:type="arc" />
<path
transform="matrix(2,0,0,2,-586,-765.967)"
sodipodi:nodetypes="ccccscccsc"
id="path3165-7-3-1"
d="m 317.06251,365.96875 c -0.76948,0.0224 -1.52555,0.35464 -2.0625,0.90625 l -16.125,16.125 16.125,16.125 c 1.11265,1.11265 3.13735,1.11265 4.25,0 1.11265,-1.11264 1.11265,-3.13735 0,-4.25 l -11.875,-11.875 11.875,-11.875 c 0.86584,-0.83655 1.1475,-2.22114 0.6773,-3.32947 -0.47021,-1.10834 -1.66156,-1.86802 -2.8648,-1.82678 z"
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;opacity:0.35;color:#000000;fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
inkscape:connector-curvature="0" />
<path
transform="matrix(2,0,0,2,-586,-765.967)"
sodipodi:nodetypes="ccccccc"
id="path3165-7-3-1-9"
d="m 320.08435,397.03059 c 0.007,-0.79449 -0.27079,-1.59203 -0.83434,-2.15559 L 307.37501,383 m 12.5523,-15.20447 c -0.47021,-1.10834 -1.66156,-1.86802 -2.8648,-1.82678 -0.76948,0.0224 -1.52555,0.35464 -2.0625,0.90625 L 298.87501,383"
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:none;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
inkscape:connector-curvature="0" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@ -68,6 +68,10 @@ IGNORE_HFILES= \
gactionobserver.h \
shell-recorder-src.h
if !BUILD_RECORDER
IGNORE_HFILES += shell-recorder.h
endif
# Images to copy into HTML directory.
# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
HTML_IMAGES=

View File

@ -29,8 +29,6 @@
<chapter>
<title>Search</title>
<xi:include href="xml/shell-app-system.xml"/>
<xi:include href="xml/shell-contact-system.xml"/>
<xi:include href="xml/shell-doc-system.xml"/>
</chapter>
<chapter>
<title>Tray Icons</title>
@ -42,7 +40,6 @@
<chapter>
<title>Recorder</title>
<xi:include href="xml/shell-recorder.xml"/>
<xi:include href="xml/shell-recorder-src.xml"/>
</chapter>
<chapter>
<title>Integration helpers and utilities</title>

View File

@ -1,3 +1,4 @@
NULL =
EXTRA_DIST = misc/config.js.in
CLEANFILES = misc/config.js
@ -6,9 +7,7 @@ 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|[@]GJS_VERSION@|$(GJS_VERSION)|g" \
-e "s|[@]HAVE_BLUETOOTH@|$(HAVE_BLUETOOTH)|g" \
-e "s|[@]SHELL_SYSTEM_CA_FILE@|$(SHELL_SYSTEM_CA_FILE)|g" \
-e "s|[@]GETTEXT_PACKAGE@|$(GETTEXT_PACKAGE)|g" \
-e "s|[@]datadir@|$(datadir)|g" \
-e "s|[@]libexecdir@|$(libexecdir)|g" \
@ -19,78 +18,75 @@ jsdir = $(pkgdatadir)/js
nobase_dist_js_DATA = \
gdm/batch.js \
gdm/consoleKit.js \
gdm/fingerprint.js \
gdm/loginDialog.js \
gdm/powerMenu.js \
gdm/systemd.js \
gdm/realmd.js \
gdm/util.js \
extensionPrefs/main.js \
misc/config.js \
misc/extensionUtils.js \
misc/fileUtils.js \
misc/format.js \
misc/gnomeSession.js \
misc/history.js \
misc/jsParse.js \
misc/loginManager.js \
misc/modemManager.js \
misc/params.js \
misc/screenSaver.js \
misc/util.js \
perf/core.js \
ui/altTab.js \
ui/appDisplay.js \
ui/appFavorites.js \
ui/automountManager.js \
ui/autorunManager.js \
ui/boxpointer.js \
ui/calendar.js \
ui/checkBox.js \
ui/contactDisplay.js \
ui/ctrlAltTab.js \
ui/dash.js \
ui/dateMenu.js \
ui/dnd.js \
ui/endSessionDialog.js \
ui/environment.js \
ui/extensionSystem.js \
ui/extensionDownloader.js \
ui/environment.js \
ui/flashspot.js \
ui/ibusCandidatePopup.js\
ui/grabHelper.js \
ui/iconGrid.js \
ui/keyboard.js \
ui/keyringPrompt.js \
ui/layout.js \
ui/lightbox.js \
ui/link.js \
ui/lookingGlass.js \
ui/magnifier.js \
ui/magnifierDBus.js \
ui/main.js \
ui/messageTray.js \
ui/modalDialog.js \
ui/networkAgent.js \
ui/sessionMode.js \
ui/shellEntry.js \
ui/shellMountOperation.js \
ui/notificationDaemon.js \
ui/overview.js \
ui/panel.js \
ui/panelMenu.js \
ui/placeDisplay.js \
ui/polkitAuthenticationAgent.js \
ui/pointerWatcher.js \
ui/popupMenu.js \
ui/remoteSearch.js \
ui/runDialog.js \
ui/screenShield.js \
ui/scripting.js \
ui/search.js \
ui/searchDisplay.js \
ui/shellDBus.js \
ui/statusIconDispatcher.js \
ui/status/accessibility.js \
ui/status/keyboard.js \
ui/status/lockScreenMenu.js \
ui/status/network.js \
ui/status/power.js \
ui/status/volume.js \
ui/status/bluetooth.js \
ui/telepathyClient.js \
ui/tweener.js \
ui/unlockDialog.js \
ui/userMenu.js \
ui/viewSelector.js \
ui/wanda.js \
@ -100,4 +96,13 @@ nobase_dist_js_DATA = \
ui/workspaceThumbnail.js \
ui/workspacesView.js \
ui/workspaceSwitcherPopup.js \
ui/xdndHandler.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/recorder.js \
ui/components/telepathyClient.js \
ui/components/keyring.js \
$(NULL)

View File

@ -6,15 +6,14 @@ const GObject = imports.gi.GObject;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Pango = imports.gi.Pango;
const Format = imports.format;
const _ = Gettext.gettext;
const Config = imports.misc.config;
const Format = imports.misc.format;
const ExtensionUtils = imports.misc.extensionUtils;
const GnomeShellIface = <interface name="org.gnome.Shell">
const GnomeShellIface = <interface name="org.gnome.Shell.Extensions">
<signal name="ExtensionStatusChanged">
<arg type="s" name="uuid"/>
<arg type="i" name="state"/>
@ -162,7 +161,7 @@ const Application = new Lang.Class({
vbox.add(toolbar);
let toolitem;
let label = new Gtk.Label({ label: _("<b>Extension</b>"),
let label = new Gtk.Label({ label: '<b>' + _("Extension") + '</b>',
use_markup: true });
toolitem = new Gtk.ToolItem({ child: label });
toolbar.add(toolitem);
@ -202,24 +201,18 @@ const Application = new Lang.Class({
},
_scanExtensions: function() {
ExtensionUtils.scanExtensions(Lang.bind(this, function(uuid, dir, type) {
if (ExtensionUtils.extensions[uuid] !== undefined)
return;
let extension;
try {
extension = ExtensionUtils.createExtensionObject(uuid, dir, type);
} catch(e) {
global.logError('' + e);
return;
}
let iter = this._model.append();
this._model.set(iter, [0, 1], [uuid, extension.metadata.name]);
this._extensionIters[uuid] = iter;
}));
let finder = new ExtensionUtils.ExtensionFinder();
finder.connect('extension-found', Lang.bind(this, this._extensionFound));
finder.scanExtensions();
},
_extensionFound: function(signals, extension) {
let iter = this._model.append();
this._model.set(iter, [0, 1], [extension.uuid, extension.metadata.name]);
this._extensionIters[extension.uuid] = iter;
},
_onActivate: function() {
this._window.present();
},
@ -257,7 +250,7 @@ function initEnvironment() {
},
logError: function(s) {
global.log('ERROR: ' + s);
log('ERROR: ' + s);
},
userdatadir: GLib.build_filenamev([GLib.get_user_data_dir(), 'gnome-shell'])
@ -268,7 +261,6 @@ function initEnvironment() {
function main(argv) {
initEnvironment();
ExtensionUtils.init();
Gettext.bindtextdomain(Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
Gettext.textdomain(Config.GETTEXT_PACKAGE);

View File

@ -1,22 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio;
const ConsoleKitManagerIface = <interface name='org.freedesktop.ConsoleKit.Manager'>
<method name='CanRestart'>
<arg type='b' direction='out'/>
</method>
<method name='CanStop'>
<arg type='b' direction='out'/>
</method>
<method name='Restart' />
<method name='Stop' />
</interface>;
const ConsoleKitProxy = Gio.DBusProxy.makeProxyWrapper(ConsoleKitManagerIface);
function ConsoleKitManager() {
return new ConsoleKitProxy(Gio.DBus.system,
'org.freedesktop.ConsoleKit',
'/org/freedesktop/ConsoleKit/Manager');
};

View File

@ -11,10 +11,16 @@ const FprintManagerIface = <interface name='net.reactivated.Fprint.Manager'>
</method>
</interface>;
const FprintManagerProxy = Gio.DBusProxy.makeProxyWrapper(FprintManagerIface);
const FprintManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(FprintManagerIface);
function FprintManager() {
return new FprintManagerProxy(Gio.DBus.system,
'net.reactivated.Fprint',
'/net/reactivated/Fprint/Manager');
};
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.system,
g_interface_name: FprintManagerInfo.name,
g_interface_info: FprintManagerInfo,
g_name: 'net.reactivated.Fprint',
g_object_path: '/net/reactivated/Fprint/Manager',
g_flags: (Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
self.init(null);
return self;
}

View File

@ -30,83 +30,24 @@ const Pango = imports.gi.Pango;
const Signals = imports.signals;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const GdmGreeter = imports.gi.GdmGreeter;
const Gdm = imports.gi.Gdm;
const Batch = imports.gdm.batch;
const Fprint = imports.gdm.fingerprint;
const GdmUtil = imports.gdm.util;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog;
const Tweener = imports.ui.tweener;
const UserMenu = imports.ui.userMenu;
const _PASSWORD_SERVICE_NAME = 'gdm-password';
const _FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
const _FADE_ANIMATION_TIME = 0.16;
const _RESIZE_ANIMATION_TIME = 0.25;
const _SCROLL_ANIMATION_TIME = 2.0;
const _SCROLL_ANIMATION_TIME = 0.5;
const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
const _LOGO_ICON_NAME_SIZE = 48;
const _LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
const _FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
const _LOGO_KEY = 'logo';
let _loginDialog = null;
function _fadeInActor(actor) {
let hold = new Batch.Hold();
if (actor.opacity == 255 && actor.visible)
return null;
actor.show();
let [minHeight, naturalHeight] = actor.get_preferred_height(-1);
actor.opacity = 0;
actor.set_height(0);
Tweener.addTween(actor,
{ opacity: 255,
height: naturalHeight,
time: _FADE_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: function() {
actor.set_height(-1);
hold.release();
},
onCompleteScope: this
});
return hold;
}
function _fadeOutActor(actor) {
let hold = new Batch.Hold();
if (!actor.visible) {
actor.opacity = 0;
return null;
}
if (actor.opacity == 0) {
actor.hide();
return null;
}
Tweener.addTween(actor,
{ opacity: 0,
height: 0,
time: _FADE_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: function() {
actor.hide();
actor.set_height(-1);
hold.release();
},
onCompleteScope: this
});
return hold;
}
function _smoothlyResizeActor(actor, width, height) {
let finalWidth;
let finalHeight;
@ -148,95 +89,60 @@ const UserListItem = new Lang.Class({
this._userChangedId = this.user.connect('changed',
Lang.bind(this, this._onUserChanged));
this._verticalBox = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-vertical-layout',
vertical: true });
let layout = new St.BoxLayout({ vertical: false });
this.actor = new St.Button({ style_class: 'login-dialog-user-list-item',
can_focus: true,
child: this._verticalBox,
child: layout,
reactive: true,
x_align: St.Align.START,
x_fill: true });
let layout = new St.BoxLayout({ vertical: false });
this._verticalBox.add(layout,
{ y_fill: true,
x_fill: true,
expand: true });
this._focusBin = new St.Bin({ style_class: 'login-dialog-user-list-item-focus-bin' });
this._verticalBox.add(this._focusBin,
{ x_fill: false,
x_align: St.Align.MIDDLE,
y_fill: false,
expand: true });
this._iconBin = new St.Bin();
layout.add(this._iconBin);
this._userAvatar = new UserMenu.UserAvatarWidget(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,
{ y_fill: false,
y_align: St.Align.MIDDLE,
expand: true });
layout.add(textLayout, { expand: true });
this._nameLabel = new St.Label({ text: this.user.get_real_name(),
style_class: 'login-dialog-user-list-item-name' });
textLayout.add(this._nameLabel);
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._updateIcon();
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 });
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
this._onUserChanged();
},
_onUserChanged: function() {
this._nameLabel.set_text(this.user.get_real_name());
this._updateIcon();
this._userAvatar.update();
this._updateLoggedIn();
},
_setIconFromFile: function(iconFile, styleClass) {
if (styleClass)
this._iconBin.set_style_class_name(styleClass);
this._iconBin.set_style(null);
syncStyleClasses: function() {
this._updateLoggedIn();
this._iconBin.child = null;
if (iconFile) {
this._iconBin.show();
// We use background-image instead of, say, St.TextureCache
// so the theme writers can add a rounded frame around the image
// and so theme writers can pick the icon size.
this._iconBin.set_style('background-image: url("' + iconFile + '");' +
'background-size: contain;');
} else {
this._iconBin.hide();
}
},
_setIconFromName: function(iconName, styleClass) {
if (styleClass)
this._iconBin.set_style_class_name(styleClass);
this._iconBin.set_style(null);
if (iconName != null) {
let icon = new St.Icon();
icon.set_icon_name(iconName)
this._iconBin.child = icon;
this._iconBin.show();
} else {
this._iconBin.child = null;
this._iconBin.hide();
}
},
_updateIcon: function() {
let iconFileName = this.user.get_icon_file();
let gicon = null;
if (GLib.file_test(iconFileName, GLib.FileTest.EXISTS))
this._setIconFromFile(iconFileName, 'login-dialog-user-list-item-icon');
if (global.stage.get_key_focus() == this.actor)
this.actor.add_style_pseudo_class('focus');
else
this._setIconFromName('avatar-default', 'login-dialog-user-list-item-icon');
this.actor.remove_style_pseudo_class('focus');
},
_updateLoggedIn: function() {
if (this.user.is_logged_in())
this.actor.add_style_pseudo_class('logged-in');
else
this.actor.remove_style_pseudo_class('logged-in');
},
_onClicked: function() {
@ -244,25 +150,19 @@ const UserListItem = new Lang.Class({
},
fadeOutName: function() {
return _fadeOutActor(this._nameLabel);
return GdmUtil.fadeOutActor(this._nameLabel);
},
fadeInName: function() {
return _fadeInActor(this._nameLabel);
return GdmUtil.fadeInActor(this._nameLabel);
},
showFocusAnimation: function(time) {
showTimedLoginIndicator: function(time) {
let hold = new Batch.Hold();
let node = this.actor.get_theme_node();
let padding = node.get_horizontal_padding();
let box = this._verticalBox.get_allocation_box();
Tweener.removeTweens(this._focusBin);
this._focusBin.width = 0;
Tweener.addTween(this._focusBin,
{ width: (box.x2 - box.x1 - padding),
this.hideTimedLoginIndicator();
Tweener.addTween(this._timedLoginIndicator,
{ scale_x: 1.,
time: time,
transition: 'linear',
onComplete: function() {
@ -271,6 +171,11 @@ const UserListItem = new Lang.Class({
onCompleteScope: this
});
return hold;
},
hideTimedLoginIndicator: function() {
Tweener.removeTweens(this._timedLoginIndicator);
this._timedLoginIndicator.scale_x = 0.;
}
});
Signals.addSignalMethods(UserListItem.prototype);
@ -284,13 +189,10 @@ const UserList = new Lang.Class({
Gtk.PolicyType.AUTOMATIC);
this._box = new St.BoxLayout({ vertical: true,
style_class: 'login-dialog-user-list' });
style_class: 'login-dialog-user-list',
pseudo_class: 'expanded' });
this.actor.add_actor(this._box,
{ x_fill: true,
y_fill: true,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
this.actor.add_actor(this._box);
this._items = {};
this.actor.connect('key-focus-in', Lang.bind(this, this._moveFocusToItems));
@ -310,7 +212,7 @@ const UserList = new Lang.Class({
_showItem: function(item) {
let tasks = [function() {
return _fadeInActor(item.actor);
return GdmUtil.fadeInActor(item.actor);
},
function() {
@ -367,17 +269,21 @@ const UserList = new Lang.Class({
for (let userName in this._items) {
let item = this._items[userName];
item.actor.set_hover(false);
item.actor.reactive = false;
item.actor.can_focus = false;
item._focusBin.width = 0;
item.syncStyleClasses();
item._timedLoginIndicator.scale_x = 0.;
if (item != exception)
tasks.push(function() {
return _fadeOutActor(item.actor);
return GdmUtil.fadeOutActor(item.actor);
});
}
this._box.remove_style_pseudo_class('expanded');
let batch = new Batch.ConsecutiveBatch(this,
[function() {
return _fadeOutActor(this.actor.vscroll);
return GdmUtil.fadeOutActor(this.actor.vscroll);
},
new Batch.ConcurrentBatch(this, tasks)
@ -421,12 +327,16 @@ const UserList = new Lang.Class({
for (let userName in this._items) {
let item = this._items[userName];
item.actor.sync_hover();
item.actor.reactive = true;
item.actor.can_focus = true;
item.syncStyleClasses();
tasks.push(function() {
return this._showItem(item);
});
}
this._box.add_style_pseudo_class('expanded');
let batch = new Batch.ConsecutiveBatch(this,
[function() {
this.takeOverWhitespace();
@ -444,7 +354,7 @@ const UserList = new Lang.Class({
},
function() {
return _fadeInActor(this.actor.vscroll);
return GdmUtil.fadeInActor(this.actor.vscroll);
}]);
return batch.run();
},
@ -459,7 +369,7 @@ const UserList = new Lang.Class({
Tweener.addTween (adjustment,
{ value: value,
time: _SCROLL_ANIMATION_TIME,
transition: 'linear' });
transition: 'easeOutQuad' });
},
jumpToItem: function(item) {
@ -511,7 +421,6 @@ const UserList = new Lang.Class({
Lang.bind(this,
function() {
this.scrollToItem(item);
item.showFocusAnimation(0);
}));
this._moveFocusToItems();
@ -553,10 +462,7 @@ const SessionListItem = new Lang.Class({
this._box = new St.BoxLayout({ style_class: 'login-dialog-session-list-item-box' });
this.actor.add_actor(this._box,
{ expand: true,
x_fill: true,
y_fill: true });
this.actor.add_actor(this._box);
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
this._dot = new St.DrawingArea({ style_class: 'login-dialog-session-list-item-dot' });
@ -566,11 +472,9 @@ const SessionListItem = new Lang.Class({
let label = new St.Label({ style_class: 'login-dialog-session-list-item-label',
text: name });
this.actor.label_actor = label;
this._box.add_actor(label,
{ expand: true,
x_fill: true,
y_fill: true });
this._box.add_actor(label);
},
setShowDot: function(show) {
@ -614,10 +518,7 @@ const SessionList = new Lang.Class({
x_fill: true,
y_fill: true });
let box = new St.BoxLayout();
this._button.add_actor(box,
{ x_fill: true,
y_fill: true,
expand: true });
this._button.add_actor(box);
this._triangle = new St.Label({ style_class: 'login-dialog-session-list-triangle',
text: '\u25B8' });
@ -625,30 +526,18 @@ const SessionList = new Lang.Class({
let label = new St.Label({ style_class: 'login-dialog-session-list-label',
text: _("Session...") });
box.add_actor(label,
{ x_fill: true,
y_fill: true,
expand: true });
box.add_actor(label);
this._button.connect('clicked',
Lang.bind(this, this._onClicked));
this._box.add_actor(this._button,
{ x_fill: true,
y_fill: true,
expand: true });
this._box.add_actor(this._button);
this._scrollView = new St.ScrollView({ style_class: 'login-dialog-session-list-scroll-view'});
this._scrollView.set_policy(Gtk.PolicyType.NEVER,
Gtk.PolicyType.AUTOMATIC);
this._box.add_actor(this._scrollView,
{ x_fill: true,
y_fill: true,
expand: true });
this._box.add_actor(this._scrollView);
this._itemList = new St.BoxLayout({ style_class: 'login-dialog-session-item-list',
vertical: true });
this._scrollView.add_actor(this._itemList,
{ x_fill: true,
y_fill: true,
expand: true });
this._scrollView.add_actor(this._itemList);
this._scrollView.hide();
this.isOpen = false;
this._populate();
@ -701,7 +590,7 @@ const SessionList = new Lang.Class({
this._activeSessionId = null;
this._items = {};
let ids = GdmGreeter.get_session_ids();
let ids = Gdm.get_session_ids();
ids.sort();
if (ids.length <= 1) {
@ -713,14 +602,10 @@ const SessionList = new Lang.Class({
}
for (let i = 0; i < ids.length; i++) {
let [sessionName, sessionDescription] = GdmGreeter.get_session_name_and_description(ids[i]);
let [sessionName, sessionDescription] = Gdm.get_session_name_and_description(ids[i]);
let item = new SessionListItem(ids[i], sessionName);
this._itemList.add_actor(item.actor,
{ x_align: St.Align.START,
y_align: St.Align.START,
x_fill: true,
y_fill: true });
this._itemList.add_actor(item.actor);
this._items[ids[i]] = item;
if (!this._activeSessionId)
@ -739,52 +624,54 @@ const LoginDialog = new Lang.Class({
Name: 'LoginDialog',
Extends: ModalDialog.ModalDialog,
_init: function() {
this.parent({ shellReactive: true, styleClass: 'login-dialog' });
_init: function(parentActor) {
this.parent({ shellReactive: true,
styleClass: 'login-dialog',
parentActor: parentActor,
shouldFadeIn: false });
this.connect('destroy',
Lang.bind(this, this._onDestroy));
this.connect('opened',
Lang.bind(this, this._onOpened));
this._userManager = AccountsService.UserManager.get_default()
this._greeterClient = new GdmGreeter.Client();
this._greeterClient = new Gdm.Client();
this._greeterClient.open_connection();
this._greeter = this._greeterClient.get_greeter_sync(null);
this._greeterClient.call_start_conversation(_PASSWORD_SERVICE_NAME);
this._greeter.connect('default-session-name-changed',
Lang.bind(this, this._onDefaultSessionChanged));
this._greeterClient.connect('reset',
Lang.bind(this, this._onReset));
this._greeterClient.connect('default-session-changed',
Lang.bind(this, this._onDefaultSessionChanged));
this._greeterClient.connect('info',
Lang.bind(this, this._onInfo));
this._greeterClient.connect('problem',
Lang.bind(this, this._onProblem));
this._greeterClient.connect('info-query',
Lang.bind(this, this._onInfoQuery));
this._greeterClient.connect('secret-info-query',
Lang.bind(this, this._onSecretInfoQuery));
this._greeterClient.connect('session-opened',
Lang.bind(this, this._onSessionOpened));
this._greeterClient.connect('timed-login-requested',
Lang.bind(this, this._onTimedLoginRequested));
this._greeterClient.connect('authentication-failed',
Lang.bind(this, this._onAuthenticationFailed));
this._greeterClient.connect('conversation-stopped',
Lang.bind(this, this._onConversationStopped));
this._greeter.connect('session-opened',
Lang.bind(this, this._onSessionOpened));
this._greeter.connect('timed-login-requested',
Lang.bind(this, this._onTimedLoginRequested));
this._settings = new Gio.Settings({ schema: _LOGIN_SCREEN_SCHEMA });
this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient);
this._userVerifier.connect('ask-question', Lang.bind(this, this._askQuestion));
this._userVerifier.connect('show-message', Lang.bind(this, this._showMessage));
this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
this._userVerifier.connect('show-login-hint', Lang.bind(this, this._showLoginHint));
this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._hideLoginHint));
this._fprintManager = new Fprint.FprintManager();
this._startFingerprintConversationIfNeeded();
this._settings.connect('changed::' + _LOGO_KEY,
this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
this._settings.connect('changed::' + GdmUtil.LOGO_KEY,
Lang.bind(this, this._updateLogo));
this._settings.connect('changed::' + GdmUtil.BANNER_MESSAGE_KEY,
Lang.bind(this, this._updateBanner));
this._settings.connect('changed::' + GdmUtil.BANNER_MESSAGE_TEXT_KEY,
Lang.bind(this, this._updateBanner));
this._logoBox = new St.Bin({ style_class: 'login-dialog-logo-box' });
this.contentLayout.add(this._logoBox);
this._updateLogo();
this._bannerLabel = new St.Label({ style_class: 'login-dialog-banner',
text: '' });
this.contentLayout.add(this._bannerLabel);
this._updateBanner();
this._titleLabel = new St.Label({ style_class: 'login-dialog-title',
text: C_("title", "Sign In") });
@ -829,17 +716,18 @@ const LoginDialog = new Lang.Class({
x_fill: true,
y_fill: false,
x_align: St.Align.START });
// Translators: this message is shown below the password entry field
// to indicate the user can swipe their finger instead
this._promptFingerprintMessage = new St.Label({ text: _("(or swipe finger)"),
style_class: 'login-dialog-prompt-fingerprint-message' });
this._promptFingerprintMessage.hide();
this._promptBox.add(this._promptFingerprintMessage);
this._promptMessage = new St.Label({ visible: false });
this._promptBox.add(this._promptMessage, { x_fill: true });
this._promptLoginHint = new St.Label({ style_class: 'login-dialog-prompt-login-hint-message' });
this._promptLoginHint.hide();
this._promptBox.add(this._promptLoginHint);
this._sessionList = new SessionList();
this._sessionList.connect('session-activated',
Lang.bind(this, function(list, sessionId) {
this._greeterClient.call_select_session (sessionId);
this._greeter.call_select_session_sync (sessionId, null);
}));
this._promptBox.add(this._sessionList.actor,
@ -887,25 +775,9 @@ const LoginDialog = new Lang.Class({
},
_startFingerprintConversationIfNeeded: function() {
this._haveFingerprintReader = false;
if (!this._settings.get_boolean(_FINGERPRINT_AUTHENTICATION_KEY))
return;
this._fprintManager.GetDefaultDeviceRemote(Gio.DBusCallFlags.NONE, Lang.bind(this,
function(device, error) {
if (!error && device)
this._haveFingerprintReader = true;
if (this._haveFingerprintReader)
this._greeterClient.call_start_conversation(_FINGERPRINT_SERVICE_NAME);
}));
},
_updateLogo: function() {
this._logoBox.child = null;
let path = this._settings.get_string(_LOGO_KEY);
let path = this._settings.get_string(GdmUtil.LOGO_KEY);
if (path) {
let file = Gio.file_new_for_path(path);
@ -917,9 +789,20 @@ const LoginDialog = new Lang.Class({
},
_updateBanner: function() {
let enabled = this._settings.get_boolean(GdmUtil.BANNER_MESSAGE_KEY);
let text = this._settings.get_string(GdmUtil.BANNER_MESSAGE_TEXT_KEY);
if (enabled && text) {
this._bannerLabel.set_text(text);
this._fadeInBanner();
} else {
this._fadeOutBanner();
}
},
_onReset: function(client, serviceName) {
this._greeterClient.call_start_conversation(_PASSWORD_SERVICE_NAME);
this._startFingerprintConversationIfNeeded();
this._promptMessage.hide();
let tasks = [this._hidePrompt,
@ -929,7 +812,7 @@ const LoginDialog = new Lang.Class({
function() {
this._sessionList.close();
this._promptFingerprintMessage.hide();
this._promptLoginHint.hide();
this._userList.actor.show();
this._userList.actor.opacity = 255;
return this._userList.showItems();
@ -950,61 +833,51 @@ const LoginDialog = new Lang.Class({
this._sessionList.setActiveSession(sessionId);
},
_onInfo: function(client, serviceName, info) {
// We don't display fingerprint messages, because they
// have words like UPEK in them. Instead we use the messages
// as a cue to display our own message.
if (serviceName == _FINGERPRINT_SERVICE_NAME &&
this._haveFingerprintReader &&
(!this._promptFingerprintMessage.visible ||
this._promptFingerprintMessage.opacity != 255)) {
_fadeInActor(this._promptFingerprintMessage);
return;
}
if (serviceName != _PASSWORD_SERVICE_NAME)
return;
Main.notifyError(info);
_showMessage: function(userVerifier, message, styleClass) {
this._promptMessage.text = message;
this._promptMessage.styleClass = styleClass;
GdmUtil.fadeInActor(this._promptMessage);
},
_onProblem: function(client, serviceName, problem) {
// we don't want to show auth failed messages to
// users who haven't enrolled their fingerprint.
if (serviceName != _PASSWORD_SERVICE_NAME)
return;
Main.notifyError(problem);
_showLoginHint: function(verifier, message) {
this._promptLoginHint.set_text(message)
GdmUtil.fadeInActor(this._promptLoginHint);
},
_onCancel: function(client) {
this._greeterClient.call_cancel();
_hideLoginHint: function() {
GdmUtil.fadeOutActor(this._promptLoginHint);
this._promptLoginHint.set_text('');
},
cancel: function() {
this._userVerifier.cancel();
},
_fadeInPrompt: function() {
let tasks = [function() {
return _fadeInActor(this._promptLabel);
return GdmUtil.fadeInActor(this._promptLabel);
},
function() {
return _fadeInActor(this._promptEntry);
return GdmUtil.fadeInActor(this._promptEntry);
},
function() {
// Show it with 0 opacity so we preallocate space for it
// in the event we need to fade in the message
this._promptFingerprintMessage.opacity = 0;
this._promptFingerprintMessage.show();
this._promptLoginHint.opacity = 0;
this._promptLoginHint.show();
},
function() {
return _fadeInActor(this._promptBox);
return GdmUtil.fadeInActor(this._promptBox);
},
function() {
if (this._user && this._user.is_logged_in())
return null;
return _fadeInActor(this._sessionList.actor);
return GdmUtil.fadeInActor(this._sessionList.actor);
},
function() {
@ -1019,13 +892,14 @@ const LoginDialog = new Lang.Class({
_showPrompt: function() {
let hold = new Batch.Hold();
let buttons = [{ action: Lang.bind(this, this._onCancel),
let buttons = [{ action: Lang.bind(this, this.cancel),
label: _("Cancel"),
key: Clutter.Escape },
{ action: Lang.bind(this, function() {
hold.release();
}),
label: C_("button", "Sign In") }];
label: C_("button", "Sign In"),
default: true }];
this._promptEntryActivateCallbackId = this._promptEntry.clutter_text.connect('activate',
Lang.bind(this, function() {
@ -1060,13 +934,12 @@ const LoginDialog = new Lang.Class({
this.setButtons([]);
let tasks = [function() {
return _fadeOutActor(this._promptBox);
return GdmUtil.fadeOutActor(this._promptBox);
},
function() {
this._promptFingerprintMessage.hide();
this._promptLoginHint.hide();
this._promptEntry.reactive = true;
this._promptEntry.remove_style_pseudo_class('insensitive');
this._promptEntry.set_text('');
}];
@ -1075,43 +948,26 @@ const LoginDialog = new Lang.Class({
return batch.run();
},
_askQuestion: function(serviceName, question) {
_askQuestion: function(verifier, serviceName, question, passwordChar) {
this._promptLabel.set_text(question);
this._promptEntry.set_text('');
this._promptEntry.clutter_text.set_password_char(passwordChar);
let tasks = [this._showPrompt,
function() {
let _text = this._promptEntry.get_text();
this._promptEntry.reactive = false;
this._promptEntry.add_style_pseudo_class('insensitive');
this._greeterClient.call_answer_query(serviceName, _text);
this._userVerifier.answerQuery(serviceName, _text);
}];
let batch = new Batch.ConsecutiveBatch(this, tasks);
return batch.run();
},
_onInfoQuery: function(client, serviceName, question) {
// We only expect questions to come from the main auth service
if (serviceName != _PASSWORD_SERVICE_NAME)
return;
this._promptEntry.set_text('');
this._promptEntry.clutter_text.set_password_char('');
this._askQuestion(serviceName, question);
},
_onSecretInfoQuery: function(client, serviceName, secretQuestion) {
// We only expect secret requests to come from the main auth service
if (serviceName != _PASSWORD_SERVICE_NAME)
return;
this._promptEntry.set_text('');
this._promptEntry.clutter_text.set_password_char('\u25cf');
this._askQuestion(serviceName, secretQuestion);
},
_onSessionOpened: function(client, serviceName) {
this._greeterClient.call_start_session_when_ready(serviceName, true);
this._greeter.call_start_session_when_ready_sync(serviceName, true, null);
},
_waitForItemForUser: function(userName) {
@ -1138,7 +994,7 @@ const LoginDialog = new Lang.Class({
_showTimedLoginAnimation: function() {
this._timedLoginItem.actor.grab_key_focus();
return this._timedLoginItem.showFocusAnimation(this._timedLoginAnimationTime);
return this._timedLoginItem.showTimedLoginIndicator(this._timedLoginAnimationTime);
},
_blockTimedLoginUntilIdle: function() {
@ -1179,7 +1035,6 @@ const LoginDialog = new Lang.Class({
// item.
if (!this.is_loaded) {
this._userList.jumpToItem(this._timedLoginItem);
this._timedLoginItem.showFocusAnimation(0);
}
},
@ -1193,7 +1048,7 @@ const LoginDialog = new Lang.Class({
function() {
this._timedLoginBatch = null;
this._greeterClient.call_begin_auto_login(userName);
this._greeter.call_begin_auto_login_sync(userName, null);
}];
this._timedLoginBatch = new Batch.ConsecutiveBatch(this, tasks);
@ -1207,6 +1062,9 @@ const LoginDialog = new Lang.Class({
this._timedLoginBatch = null;
}
if (this._timedLoginItem)
this._timedLoginItem.hideTimedLoginIndicator();
let userName = this._timedLoginItem.user.get_user_name();
if (userName)
@ -1236,21 +1094,6 @@ const LoginDialog = new Lang.Class({
}));
},
_onAuthenticationFailed: function(client) {
this._greeterClient.call_cancel();
},
_onConversationStopped: function(client, serviceName) {
// if the password service fails, then cancel everything.
// But if, e.g., fingerprint fails, still give
// password authentication a chance to succeed
if (serviceName == _PASSWORD_SERVICE_NAME) {
this._greeterClient.call_cancel();
} else if (serviceName == _FINGERPRINT_SERVICE_NAME) {
_fadeOutActor(this._promptFingerprintMessage);
}
},
_onNotListedClicked: function(user) {
let tasks = [function() {
return this._userList.hideItems();
@ -1269,7 +1112,10 @@ const LoginDialog = new Lang.Class({
this._fadeOutLogo]),
function() {
this._greeterClient.call_begin_verification(_PASSWORD_SERVICE_NAME);
let hold = new Batch.Hold();
this._userVerifier.begin(null, hold);
return hold;
}];
let batch = new Batch.ConsecutiveBatch(this, tasks);
@ -1277,30 +1123,47 @@ const LoginDialog = new Lang.Class({
},
_fadeInLogo: function() {
return _fadeInActor(this._logoBox);
return GdmUtil.fadeInActor(this._logoBox);
},
_fadeOutLogo: function() {
return _fadeOutActor(this._logoBox);
return GdmUtil.fadeOutActor(this._logoBox);
},
_fadeInBanner: function() {
return GdmUtil.fadeInActor(this._bannerLabel);
},
_fadeOutBanner: function() {
return GdmUtil.fadeOutActor(this._bannerLabel);
},
_fadeInTitleLabel: function() {
return _fadeInActor(this._titleLabel);
return GdmUtil.fadeInActor(this._titleLabel);
},
_fadeOutTitleLabel: function() {
return _fadeOutActor(this._titleLabel);
return GdmUtil.fadeOutActor(this._titleLabel);
},
_fadeInNotListedButton: function() {
return _fadeInActor(this._notListedButton);
return GdmUtil.fadeInActor(this._notListedButton);
},
_fadeOutNotListedButton: function() {
return _fadeOutActor(this._notListedButton);
return GdmUtil.fadeOutActor(this._notListedButton);
},
_beginVerificationForUser: function(userName) {
let hold = new Batch.Hold();
this._userVerifier.begin(userName, hold);
return hold;
},
_onUserListActivated: function(activatedItem) {
let userName;
let tasks = [function() {
this._userList.actor.reactive = false;
return this._userList.pinInPlace();
@ -1327,12 +1190,9 @@ const LoginDialog = new Lang.Class({
},
function() {
let userName = activatedItem.user.get_user_name();
this._greeterClient.call_begin_verification_for_user(_PASSWORD_SERVICE_NAME,
userName);
userName = activatedItem.user.get_user_name();
if (this._haveFingerprintReader)
this._greeterClient.call_begin_verification_for_user(_FINGERPRINT_SERVICE_NAME, userName);
return this._beginVerificationForUser(userName);
}];
this._user = activatedItem.user;

View File

@ -21,8 +21,7 @@
const Lang = imports.lang;
const UPowerGlib = imports.gi.UPowerGlib;
const ConsoleKit = imports.gdm.consoleKit;
const Systemd = imports.gdm.systemd;
const LoginManager = imports.misc.loginManager;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
@ -32,13 +31,11 @@ const PowerMenuButton = new Lang.Class({
Extends: PanelMenu.SystemStatusButton,
_init: function() {
this.parent('system-shutdown', null);
/* Translators: accessible name of the power menu in the login screen */
this.parent('system-shutdown-symbolic', _("Power"));
this._upClient = new UPowerGlib.Client();
if (Systemd.haveSystemd())
this._systemdLoginManager = new Systemd.SystemdLoginManager();
else
this._consoleKitManager = new ConsoleKit.ConsoleKitManager();
this._loginManager = LoginManager.getLoginManager();
this._createSubMenu();
@ -60,92 +57,29 @@ const PowerMenuButton = new Lang.Class({
},
_updateVisibility: function() {
if (!this._haveSuspend && !this._haveShutdown && !this._haveRestart)
this.actor.hide();
else
this.actor.show();
let shouldBeVisible = (this._haveSuspend || this._haveShutdown || this._haveRestart);
this.actor.visible = shouldBeVisible;
},
_updateHaveShutdown: function() {
if (Systemd.haveSystemd()) {
this._systemdLoginManager.CanPowerOffRemote(Lang.bind(this,
function(result, error) {
if (!error)
this._haveShutdown = result != 'no';
else
this._haveShutdown = false;
if (this._haveShutdown)
this._powerOffItem.actor.show();
else
this._powerOffItem.actor.hide();
this._updateVisibility();
}));
} else {
this._consoleKitManager.CanStopRemote(Lang.bind(this,
function(result, error) {
if (!error)
this._haveShutdown = result;
else
this._haveShutdown = false;
if (this._haveShutdown) {
this._powerOffItem.actor.show();
} else {
this._powerOffItem.actor.hide();
}
this._updateVisibility();
}));
}
this._loginManager.canPowerOff(Lang.bind(this, function(result) {
this._haveShutdown = result;
this._powerOffItem.actor.visible = this._haveShutdown;
this._updateVisibility();
}));
},
_updateHaveRestart: function() {
if (Systemd.haveSystemd()) {
this._systemdLoginManager.CanRebootRemote(Lang.bind(this,
function(result, error) {
if (!error)
this._haveRestart = result != 'no';
else
this._haveRestart = false;
if (this._haveRestart)
this._restartItem.actor.show();
else
this._restartItem.actor.hide();
this._updateVisibility();
}));
} else {
this._consoleKitManager.CanRestartRemote(Lang.bind(this,
function(result, error) {
if (!error)
this._haveRestart = result;
else
this._haveRestart = false;
if (this._haveRestart) {
this._restartItem.actor.show();
} else {
this._restartItem.actor.hide();
}
this._updateVisibility();
}));
}
this._loginManager.canReboot(Lang.bind(this, function(result) {
this._haveRestart = result;
this._restartItem.actor.visible = this._haveRestart;
this._updateVisibility();
}));
},
_updateHaveSuspend: function() {
this._haveSuspend = this._upClient.get_can_suspend();
if (this._haveSuspend)
this._suspendItem.actor.show();
else
this._suspendItem.actor.hide();
this._suspendItem.actor.visible = this._haveSuspend;
this._updateVisibility();
},
@ -177,19 +111,13 @@ const PowerMenuButton = new Lang.Class({
if (!this._haveRestart)
return;
if (Systemd.haveSystemd())
this._systemdLoginManager.RebootRemote(true);
else
this._consoleKitManager.RestartRemote();
this._loginManager.reboot();
},
_onActivatePowerOff: function() {
if (!this._haveShutdown)
return;
if (Systemd.haveSystemd())
this._systemdLoginManager.PowerOffRemote(true);
else
this._consoleKitManager.StopRemote();
this._loginManager.powerOff();
}
});

139
js/gdm/realmd.js Normal file
View File

@ -0,0 +1,139 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const ProviderIface = <interface name='org.freedesktop.realmd.Provider'>
<property name="Name" type="s" access="read"/>
<property name="Version" type="s" access="read"/>
<property name="Realms" type="ao" access="read"/>
<method name="Discover">
<arg name="string" type="s" direction="in"/>
<arg name="options" type="a{sv}" direction="in"/>
<arg name="relevance" type="i" direction="out"/>
<arg name="realm" type="ao" direction="out"/>
</method>
</interface>;
const Provider = Gio.DBusProxy.makeProxyWrapper(ProviderIface);
const ServiceIface = <interface name="org.freedesktop.realmd.Service">
<method name="Cancel">
<arg name="operation" type="s" direction="in"/>
</method>
<method name="Release" />
<method name="SetLocale">
<arg name="locale" type="s" direction="in"/>
</method>
<signal name="Diagnostics">
<arg name="data" type="s"/>
<arg name="operation" type="s"/>
</signal>
</interface>;
const Service = Gio.DBusProxy.makeProxyWrapper(ServiceIface);
const RealmIface = <interface name="org.freedesktop.realmd.Realm">
<property name="Name" type="s" access="read"/>
<property name="Configured" type="s" access="read"/>
<property name="Details" type="a(ss)" access="read"/>
<property name="LoginFormats" type="as" access="read"/>
<property name="LoginPolicy" type="s" access="read"/>
<property name="PermittedLogins" type="as" access="read"/>
<property name="SupportedInterfaces" type="as" access="read"/>
<method name="ChangeLoginPolicy">
<arg name="login_policy" type="s" direction="in"/>
<arg name="permitted_add" type="as" direction="in"/>
<arg name="permitted_remove" type="as" direction="in"/>
<arg name="options" type="a{sv}" direction="in"/>
</method>
<method name="Deconfigure">
<arg name="options" type="a{sv}" direction="in"/>
</method>
</interface>;
const Realm = Gio.DBusProxy.makeProxyWrapper(RealmIface);
const Manager = new Lang.Class({
Name: 'Manager',
_init: function(parentActor) {
this._aggregateProvider = Provider(Gio.DBus.system,
'org.freedesktop.realmd',
'/org/freedesktop/realmd',
Lang.bind(this, this._reloadRealms))
this._realms = {};
this._aggregateProvider.connect('g-properties-changed',
Lang.bind(this, function(proxy, properties) {
if ('Realms' in properties.deep_unpack())
this._reloadRealms();
}));
},
_reloadRealms: function() {
let realmPaths = this._aggregateProvider.Realms;
if (!realmPaths)
return;
for (let i = 0; i < realmPaths.length; i++) {
let realm = Realm(Gio.DBus.system,
'org.freedesktop.realmd',
realmPaths[i],
Lang.bind(this, this._onRealmLoaded));
}
},
_reloadRealm: function(realm) {
if (!realm.Configured) {
if (this._realms[realm.get_object_path()])
delete this._realms[realm.get_object_path()];
return;
}
this._realms[realm.get_object_path()] = realm;
this._updateLoginFormat();
},
_onRealmLoaded: function(realm, error) {
if (error)
return;
this._reloadRealm(realm);
realm.connect('g-properties-changed',
Lang.bind(this, function(proxy, properties) {
if ('Configured' in properties.deep_unpack())
this._reloadRealm();
}));
},
_updateLoginFormat: function() {
let newLoginFormat;
for (let realmPath in this._realms) {
let realm = this._realms[realmPath];
if (realm.LoginFormats && realm.LoginFormats.length > 0) {
newLoginFormat = realm.LoginFormats[0];
break;
}
}
if (this._loginFormat != newLoginFormat) {
this._loginFormat = newLoginFormat;
this.emit('login-format-changed', newLoginFormat);
}
},
get loginFormat() {
if (this._loginFormat !== undefined)
return this._loginFormat;
this._updateLoginFormat();
return this._loginFormat;
}
});
Signals.addSignalMethods(Manager.prototype)

View File

@ -1,31 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager'>
<method name='PowerOff'>
<arg type='b' direction='in'/>
</method>
<method name='Reboot'>
<arg type='b' direction='in'/>
</method>
<method name='CanPowerOff'>
<arg type='s' direction='out'/>
</method>
<method name='CanReboot'>
<arg type='s' direction='out'/>
</method>
</interface>;
const SystemdLoginManagerProxy = Gio.DBusProxy.makeProxyWrapper(SystemdLoginManagerIface);
function SystemdLoginManager() {
return new SystemdLoginManagerProxy(Gio.DBus.system,
'org.freedesktop.login1',
'/org/freedesktop/login1');
};
function haveSystemd() {
return GLib.access("/sys/fs/cgroup/systemd", 0) >= 0;
}

369
js/gdm/util.js Normal file
View File

@ -0,0 +1,369 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Signals = imports.signals;
const Batch = imports.gdm.batch;
const Fprint = imports.gdm.fingerprint;
const Realmd = imports.gdm.realmd;
const Main = imports.ui.main;
const Params = imports.misc.params;
const Tweener = imports.ui.tweener;
const PASSWORD_SERVICE_NAME = 'gdm-password';
const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
const FADE_ANIMATION_TIME = 0.16;
const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
const BANNER_MESSAGE_KEY = 'banner-message-enable';
const BANNER_MESSAGE_TEXT_KEY = 'banner-message-text';
const ALLOWED_FAILURES_KEY = 'allowed-failures';
const LOGO_KEY = 'logo';
function fadeInActor(actor) {
if (actor.opacity == 255 && actor.visible)
return null;
let hold = new Batch.Hold();
actor.show();
let [minHeight, naturalHeight] = actor.get_preferred_height(-1);
actor.opacity = 0;
actor.set_height(0);
Tweener.addTween(actor,
{ opacity: 255,
height: naturalHeight,
time: FADE_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: function() {
this.set_height(-1);
hold.release();
},
});
return hold;
}
function fadeOutActor(actor) {
if (!actor.visible || actor.opacity == 0) {
actor.opacity = 0;
actor.hide();
return null;
}
let hold = new Batch.Hold();
Tweener.addTween(actor,
{ opacity: 0,
height: 0,
time: FADE_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: function() {
this.hide();
this.set_height(-1);
hold.release();
},
});
return hold;
}
const ShellUserVerifier = new Lang.Class({
Name: 'ShellUserVerifier',
_init: function(client, params) {
params = Params.parse(params, { reauthenticationOnly: false });
this._reauthOnly = params.reauthenticationOnly;
this._client = client;
this._settings = new Gio.Settings({ schema: LOGIN_SCREEN_SCHEMA });
this._fprintManager = new Fprint.FprintManager();
this._realmManager = new Realmd.Manager();
this._failCounter = 0;
},
begin: function(userName, hold) {
this._cancellable = new Gio.Cancellable();
this._hold = hold;
this._userName = userName;
this._checkForFingerprintReader();
if (userName) {
// If possible, reauthenticate an already running session,
// so any session specific credentials get updated appropriately
this._client.open_reauthentication_channel(userName, this._cancellable,
Lang.bind(this, this._reauthenticationChannelOpened));
} else {
this._client.get_user_verifier(this._cancellable, Lang.bind(this, this._userVerifierGot));
}
},
cancel: function() {
if (this._cancellable)
this._cancellable.cancel();
if (this._userVerifier)
this._userVerifier.call_cancel_sync(null);
},
clear: function() {
if (this._cancellable) {
this._cancellable.cancel();
this._cancellable = null;
}
if (this._userVerifier) {
this._userVerifier.run_dispose();
this._userVerifier = null;
}
},
answerQuery: function(serviceName, answer) {
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
},
_checkForFingerprintReader: function() {
this._haveFingerprintReader = false;
if (!this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY))
return;
this._fprintManager.GetDefaultDeviceRemote(Gio.DBusCallFlags.NONE, this._cancellable, Lang.bind(this,
function(device, error) {
if (!error && device)
this._haveFingerprintReader = true;
}));
},
_reportInitError: function(where, error) {
logError(error, where);
this._hold.relase();
this.emit('show-message', _("Authentication error"), 'login-dialog-message-warning');
this._verificationFailed(false);
},
_reauthenticationChannelOpened: function(client, result) {
try {
this._userVerifier = client.open_reauthentication_channel_finish(result);
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
return;
} catch(e if e.matches(Gio.DBusError, Gio.DBusError.ACCESS_DENIED) &&
!this._reauthOnly) {
// Gdm emits org.freedesktop.DBus.Error.AccessDenied when there is
// no session to reauthenticate. Fall back to performing verification
// from this login session
client.get_user_verifier(this._cancellable, Lang.bind(this, this._userVerifierGot));
return;
} catch(e) {
this._reportInitError('Failed to open reauthentication channel', e);
return;
}
this._connectSignals();
this._beginVerification();
this._hold.release();
},
_userVerifierGot: function(client, result) {
try {
this._userVerifier = client.get_user_verifier_finish(result);
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
return;
} catch(e) {
this._reportInitError('Failed to obtain user verifier', e);
return;
}
this._connectSignals();
this._beginVerification();
this._hold.release();
},
_connectSignals: function() {
this._userVerifier.connect('info', Lang.bind(this, this._onInfo));
this._userVerifier.connect('problem', Lang.bind(this, this._onProblem));
this._userVerifier.connect('info-query', Lang.bind(this, this._onInfoQuery));
this._userVerifier.connect('secret-info-query', Lang.bind(this, this._onSecretInfoQuery));
this._userVerifier.connect('conversation-stopped', Lang.bind(this, this._onConversationStopped));
this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
},
_beginVerification: function() {
this._hold.acquire();
if (this._userName) {
this._userVerifier.call_begin_verification_for_user(PASSWORD_SERVICE_NAME,
this._userName,
this._cancellable,
Lang.bind(this, function(obj, result) {
try {
obj.call_begin_verification_for_user_finish(result);
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
return;
} catch(e) {
this._reportInitError('Failed to start verification for user', e);
return;
}
this._hold.release();
}));
if (this._haveFingerprintReader) {
this._hold.acquire();
this._userVerifier.call_begin_verification_for_user(FINGERPRINT_SERVICE_NAME,
this._userName,
this._cancellable,
Lang.bind(this, function(obj, result) {
try {
obj.call_begin_verification_for_user_finish(result);
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
return;
} catch(e) {
this._reportInitError('Failed to start fingerprint verification for user', e);
return;
}
this._hold.release();
}));
}
} else {
this._userVerifier.call_begin_verification(PASSWORD_SERVICE_NAME,
this._cancellable,
Lang.bind(this, function(obj, result) {
try {
obj.call_begin_verification_finish(result);
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
return;
} catch(e) {
this._reportInitError('Failed to start verification', e);
return;
}
this._hold.release();
}));
}
},
_onInfo: function(client, serviceName, info) {
// We don't display fingerprint messages, because they
// have words like UPEK in them. Instead we use the messages
// as a cue to display our own message.
if (serviceName == FINGERPRINT_SERVICE_NAME &&
this._haveFingerprintReader) {
// Translators: this message is shown below the password entry field
// to indicate the user can swipe their finger instead
this.emit('show-login-hint', _("(or swipe finger)"));
} else if (serviceName == PASSWORD_SERVICE_NAME) {
this.emit('show-message', info, 'login-dialog-message-info');
}
},
_onProblem: function(client, serviceName, problem) {
// we don't want to show auth failed messages to
// users who haven't enrolled their fingerprint.
if (serviceName != PASSWORD_SERVICE_NAME)
return;
this.emit('show-message', problem, 'login-dialog-message-warning');
},
_showRealmLoginHint: function() {
if (this._realmManager.loginFormat) {
let hint = this._realmManager.loginFormat;
hint = hint.replace(/%U/g, 'user');
hint = hint.replace(/%D/g, 'DOMAIN');
hint = hint.replace(/%[^UD]/g, '');
// Translators: this message is shown below the username entry field
// to clue the user in on how to login to the local network realm
this.emit('show-login-hint',
_("(e.g., user or %s)").format(hint));
}
},
_onInfoQuery: function(client, serviceName, question) {
// We only expect questions to come from the main auth service
if (serviceName != PASSWORD_SERVICE_NAME)
return;
this._showRealmLoginHint();
this._realmLoginHintSignalId = this._realmManager.connect('login-format-changed',
Lang.bind(this, this._showRealmLoginHint));
this.emit('ask-question', serviceName, question, '');
},
_onSecretInfoQuery: function(client, serviceName, secretQuestion) {
// We only expect secret requests to come from the main auth service
if (serviceName != PASSWORD_SERVICE_NAME)
return;
this.emit('ask-question', serviceName, secretQuestion, '\u25cf');
},
_onReset: function() {
this.clear();
// Clear previous attempts to authenticate
this._failCounter = 0;
this.emit('reset');
},
_onVerificationComplete: function() {
this.emit('verification-complete');
},
_verificationFailed: function(retry) {
// For Not Listed / enterprise logins, immediately reset
// the dialog
// Otherwise, we allow ALLOWED_FAILURES attempts. After that, we
// go back to the welcome screen.
let canRetry = retry && this._userName &&
this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY);
if (canRetry) {
this._failCounter++;
this.clear();
this.begin(this._userName, new Batch.Hold());
} else {
// Allow some time to see the message, then reset everything
Mainloop.timeout_add(3000, Lang.bind(this, function() {
this.cancel();
this._onReset();
}));
}
this.emit('verification-failed');
},
_onConversationStopped: function(client, serviceName) {
// if the password service fails, then cancel everything.
// But if, e.g., fingerprint fails, still give
// password authentication a chance to succeed
if (serviceName == PASSWORD_SERVICE_NAME) {
this._verificationFailed(true);
}
this.emit('hide-login-hint');
if (this._realmLoginHintSignalId) {
this._realmManager.disconnect(this._realmLoginHintSignalId);
this._realmLoginHintSignalId = 0;
}
},
});
Signals.addSignalMethods(ShellUserVerifier.prototype);

View File

@ -4,12 +4,8 @@
const PACKAGE_NAME = '@PACKAGE_NAME@';
/* The version of this package */
const PACKAGE_VERSION = '@PACKAGE_VERSION@';
/* The version of GJS we're linking to */
const GJS_VERSION = '@GJS_VERSION@';
/* 1 if gnome-bluetooth is available, 0 otherwise */
const HAVE_BLUETOOTH = @HAVE_BLUETOOTH@;
/* The system TLS CA list */
const SHELL_SYSTEM_CA_FILE = '@SHELL_SYSTEM_CA_FILE@';
/* gettext package */
const GETTEXT_PACKAGE = '@GETTEXT_PACKAGE@';
/* locale dir */

View File

@ -3,6 +3,9 @@
// Common utils for the extension system and the extension
// preferences tool
const Lang = imports.lang;
const Signals = imports.signals;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const ShellJS = imports.gi.ShellJS;
@ -14,9 +17,6 @@ const ExtensionType = {
PER_USER: 2
};
// GFile for user extensions
var userExtensionsDir = null;
// Maps uuid -> metadata object
const extensions = {};
@ -40,13 +40,18 @@ function getCurrentExtension() {
throw new Error('Could not find current extension');
let path = match[1];
let uuid = GLib.path_get_basename(GLib.path_get_dirname(path));
let file = Gio.File.new_for_path(path);
let extension = extensions[uuid];
if (extension === undefined)
throw new Error('Could not find current extension');
// Walk up the directory tree, looking for an extesion with
// the same UUID as a directory name.
while (file != null) {
let extension = extensions[file.get_basename()];
if (extension !== undefined)
return extension;
file = file.get_parent();
}
return extension;
throw new Error('Could not find current extension');
}
/**
@ -83,9 +88,6 @@ function isOutOfDate(extension) {
if (!versionCheck(extension.metadata['shell-version'], Config.PACKAGE_VERSION))
return true;
if (extension.metadata['js-version'] && !versionCheck(extension.metadata['js-version'], Config.GJS_VERSION))
return true;
return false;
}
@ -120,7 +122,7 @@ function createExtensionObject(uuid, dir, type) {
// Encourage people to add this
if (!meta.url) {
global.log('Warning: Missing "url" property in metadata.json');
log('Warning: Missing "url" property in %s/metadata.json'.format(uuid));
}
if (uuid != meta.uuid) {
@ -150,45 +152,55 @@ function installImporter(extension) {
_extension = null;
}
function init() {
let userExtensionsPath = GLib.build_filenamev([global.userdatadir, 'extensions']);
userExtensionsDir = Gio.file_new_for_path(userExtensionsPath);
try {
if (!userExtensionsDir.query_exists(null))
userExtensionsDir.make_directory_with_parents(null);
} catch (e) {
global.logError('' + e);
}
}
const ExtensionFinder = new Lang.Class({
Name: 'ExtensionFinder',
function scanExtensionsInDirectory(callback, dir, type) {
let fileEnum;
let file, info;
try {
fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
} catch(e) {
global.logError('' + e);
return;
}
_scanExtensionsInDirectory: function(dir, type) {
let fileEnum;
let file, info;
try {
fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
} catch(e) {
logError(e, 'Could not enumerate extensions directory');
return;
}
while ((info = fileEnum.next_file(null)) != null) {
let fileType = info.get_file_type();
if (fileType != Gio.FileType.DIRECTORY)
continue;
let uuid = info.get_name();
let extensionDir = dir.get_child(uuid);
callback(uuid, extensionDir, type);
}
fileEnum.close(null);
}
while ((info = fileEnum.next_file(null)) != null) {
let fileType = info.get_file_type();
if (fileType != Gio.FileType.DIRECTORY)
continue;
let uuid = info.get_name();
let extensionDir = dir.get_child(uuid);
function scanExtensions(callback) {
let systemDataDirs = GLib.get_system_data_dirs();
scanExtensionsInDirectory(callback, userExtensionsDir, ExtensionType.PER_USER);
for (let i = 0; i < systemDataDirs.length; i++) {
let dirPath = GLib.build_filenamev([systemDataDirs[i], 'gnome-shell', 'extensions']);
let dir = Gio.file_new_for_path(dirPath);
if (dir.query_exists(null))
scanExtensionsInDirectory(callback, dir, ExtensionType.SYSTEM);
let existing = extensions[uuid];
if (existing) {
log('Extension %s already installed in %s. %s will not be loaded'.format(uuid, existing.path, extensionDir.get_path()));
continue;
}
let extension;
try {
extension = createExtensionObject(uuid, extensionDir, type);
} catch(e) {
logError(e, 'Could not load extension %s'.format(uuid));
continue;
}
this.emit('extension-found', extension);
}
fileEnum.close(null);
},
scanExtensions: function() {
let userExtensionsDir = Gio.File.new_for_path(GLib.build_filenamev([global.userdatadir, 'extensions']));
this._scanExtensionsInDirectory(userExtensionsDir, ExtensionType.PER_USER);
let systemDataDirs = GLib.get_system_data_dirs();
for (let i = 0; i < systemDataDirs.length; i++) {
let dirPath = GLib.build_filenamev([systemDataDirs[i], 'gnome-shell', 'extensions']);
let dir = Gio.file_new_for_path(dirPath);
if (dir.query_exists(null))
this._scanExtensionsInDirectory(dir, ExtensionType.SYSTEM);
}
}
}
});
Signals.addSignalMethods(ExtensionFinder.prototype);

View File

@ -28,7 +28,7 @@ function deleteGFile(file) {
return file['delete'](null);
}
function recursivelyDeleteDir(dir) {
function recursivelyDeleteDir(dir, deleteParent) {
let children = dir.enumerate_children('standard::name,standard::type',
Gio.FileQueryInfoFlags.NONE, null);
@ -39,8 +39,29 @@ function recursivelyDeleteDir(dir) {
if (type == Gio.FileType.REGULAR)
deleteGFile(child);
else if (type == Gio.FileType.DIRECTORY)
recursivelyDeleteDir(child);
recursivelyDeleteDir(child, true);
}
deleteGFile(dir);
if (deleteParent)
deleteGFile(dir);
}
function recursivelyMoveDir(srcDir, destDir) {
let children = srcDir.enumerate_children('standard::name,standard::type',
Gio.FileQueryInfoFlags.NONE, null);
if (!destDir.query_exists(null))
destDir.make_directory_with_parents(null);
let info, child;
while ((info = children.next_file(null)) != null) {
let type = info.get_file_type();
let srcChild = srcDir.get_child(info.get_name());
let destChild = destDir.get_child(info.get_name());
log([srcChild.get_path(), destChild.get_path()]);
if (type == Gio.FileType.REGULAR)
srcChild.move(destChild, Gio.FileCopyFlags.NONE, null, null);
else if (type == Gio.FileType.DIRECTORY)
recursivelyMoveDir(srcChild, destChild);
}
}

View File

@ -1,71 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const ShellJS = imports.gi.ShellJS;
/*
* This function is intended to extend the String object and provide
* an String.format API for string formatting.
* It has to be set up using String.prototype.format = Format.format;
* Usage:
* "somestring %s %d".format('hello', 5);
* It supports %s, %d, %x and %f, for %f it also support precisions like
* "%.2f".format(1.526). All specifiers can be prefixed with a minimum
* field width, e.g. "%5s".format("foo"). Unless the width is prefixed
* with '0', the formatted string will be padded with spaces.
*/
function format() {
let str = this;
let i = 0;
let args = arguments;
return str.replace(/%(I+)?([0-9]+)?(?:\.([0-9]+))?(.)/g, function (str, flagsGroup, widthGroup, precisionGroup, genericGroup) {
if (precisionGroup != '' && genericGroup != 'f')
throw new Error("Precision can only be specified for 'f'");
let hasAlternativeIntFlag = (flagsGroup.indexOf('I') != -1);
if (hasAlternativeIntFlag && genericGroup != 'd')
throw new Error("Alternative output digits can only be specfied for 'd'");
let fillChar = (widthGroup[0] == '0') ? '0' : ' ';
let width = parseInt(widthGroup, 10) || 0;
function fillWidth(s, c, w) {
let fill = '';
for (let i = 0; i < w; i++)
fill += c;
return fill.substr(s.length) + s;
}
let s = '';
switch (genericGroup) {
case '%':
return '%';
break;
case 's':
s = args[i++].toString();
break;
case 'd':
let intV = parseInt(args[i++]);
if (hasAlternativeIntFlag)
s = ShellJS.format_int_alternative_output(intV);
else
s = intV.toString();
break;
case 'x':
s = parseInt(args[i++]).toString(16);
break;
case 'f':
if (precisionGroup == '')
s = parseFloat(args[i++]).toString();
else
s = parseFloat(args[i++]).toFixed(parseInt(precisionGroup));
break;
default:
throw new Error('Unsupported conversion character %' + genericGroup);
}
return fillWidth(s, fillChar, width);
});
}

View File

@ -50,9 +50,20 @@ const SessionManagerIface = <interface name="org.gnome.SessionManager">
<arg type="u" direction="in" />
</method>
<method name="Shutdown" />
<method name="Reboot" />
<method name="CanShutdown">
<arg type="b" direction="out" />
</method>
<method name="IsInhibited">
<arg type="u" direction="in" />
<arg type="b" direction="out" />
</method>
<signal name="InhibitorAdded">
<arg type="o" direction="out"/>
</signal>
<signal name="InhibitorRemoved">
<arg type="o" direction="out"/>
</signal>
</interface>;
var SessionManagerProxy = Gio.DBusProxy.makeProxyWrapper(SessionManagerIface);

View File

@ -41,24 +41,26 @@ const HistoryManager = new Lang.Class({
this._historyIndex = this._history.length;
},
prevItem: function(text) {
_setPrevItem: function(text) {
if (this._historyIndex <= 0)
return text;
return false;
if (text)
this._history[this._historyIndex] = text;
this._historyIndex--;
return this._indexChanged();
this._indexChanged();
return true;
},
nextItem: function(text) {
_setNextItem: function(text) {
if (this._historyIndex >= this._history.length)
return text;
return false;
if (text)
this._history[this._historyIndex] = text;
this._historyIndex++;
return this._indexChanged();
this._indexChanged();
return true;
},
lastItem: function() {
@ -83,11 +85,9 @@ const HistoryManager = new Lang.Class({
_onEntryKeyPress: function(entry, event) {
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_Up) {
this.prevItem(entry.get_text());
return true;
return this._setPrevItem(entry.get_text());
} else if (symbol == Clutter.KEY_Down) {
this.nextItem(entry.get_text());
return true;
return this._setNextItem(entry.get_text());
}
return false;
},
@ -98,8 +98,6 @@ const HistoryManager = new Lang.Class({
if (this._entry)
this._entry.set_text(current);
return current;
},
_save: function() {

197
js/misc/loginManager.js Normal file
View File

@ -0,0 +1,197 @@
// -*- 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 Shell = imports.gi.Shell;
const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager'>
<method name='PowerOff'>
<arg type='b' direction='in'/>
</method>
<method name='Reboot'>
<arg type='b' direction='in'/>
</method>
<method name='CanPowerOff'>
<arg type='s' direction='out'/>
</method>
<method name='CanReboot'>
<arg type='s' direction='out'/>
</method>
</interface>;
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
<signal name='Lock' />
<signal name='Unlock' />
</interface>;
const SystemdLoginManager = Gio.DBusProxy.makeProxyWrapper(SystemdLoginManagerIface);
const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface);
const ConsoleKitManagerIface = <interface name='org.freedesktop.ConsoleKit.Manager'>
<method name='CanRestart'>
<arg type='b' direction='out'/>
</method>
<method name='CanStop'>
<arg type='b' direction='out'/>
</method>
<method name='Restart' />
<method name='Stop' />
<method name='GetCurrentSession'>
<arg type='o' direction='out' />
</method>
</interface>;
const ConsoleKitSessionIface = <interface name='org.freedesktop.ConsoleKit.Session'>
<method name='IsActive'>
<arg type='b' direction='out' />
</method>
<signal name='ActiveChanged'>
<arg type='b' direction='out' />
</signal>
<signal name='Lock' />
<signal name='Unlock' />
</interface>;
const ConsoleKitSession = Gio.DBusProxy.makeProxyWrapper(ConsoleKitSessionIface);
const ConsoleKitManager = Gio.DBusProxy.makeProxyWrapper(ConsoleKitManagerIface);
function haveSystemd() {
return GLib.access("/sys/fs/cgroup/systemd", 0) >= 0;
}
let _loginManager = null;
/**
* LoginManager:
* An abstraction over systemd/logind and ConsoleKit.
*
*/
function getLoginManager() {
if (_loginManager == null) {
if (haveSystemd())
_loginManager = new LoginManagerSystemd();
else
_loginManager = new LoginManagerConsoleKit();
}
return _loginManager;
}
const LoginManagerSystemd = new Lang.Class({
Name: 'LoginManagerSystemd',
_init: function() {
this._proxy = new SystemdLoginManager(Gio.DBus.system,
'org.freedesktop.login1',
'/org/freedesktop/login1');
},
// Having this function is a bit of a hack since the Systemd and ConsoleKit
// session objects have different interfaces - but in both cases there are
// Lock/Unlock signals, and that's all we count upon at the moment.
getCurrentSessionProxy: function() {
if (!this._currentSession) {
this._currentSession = new SystemdLoginSession(Gio.DBus.system,
'org.freedesktop.login1',
'/org/freedesktop/login1/session/' +
GLib.getenv('XDG_SESSION_ID'));
}
return this._currentSession;
},
get sessionActive() {
return Shell.session_is_active_for_systemd();
},
canPowerOff: function(asyncCallback) {
this._proxy.CanPowerOffRemote(function(result, error) {
if (error)
asyncCallback(false);
else
asyncCallback(result[0] != 'no');
});
},
canReboot: function(asyncCallback) {
this._proxy.CanRebootRemote(function(result, error) {
if (error)
asyncCallback(false);
else
asyncCallback(result[0] != 'no');
});
},
powerOff: function() {
this._proxy.PowerOffRemote(true);
},
reboot: function() {
this._proxy.RebootRemote(true);
}
});
const LoginManagerConsoleKit = new Lang.Class({
Name: 'LoginManagerConsoleKit',
_init: function() {
this._proxy = new ConsoleKitManager(Gio.DBus.system,
'org.freedesktop.ConsoleKit',
'/org/freedesktop/ConsoleKit/Manager');
},
// Having this function is a bit of a hack since the Systemd and ConsoleKit
// session objects have different interfaces - but in both cases there are
// Lock/Unlock signals, and that's all we count upon at the moment.
getCurrentSessionProxy: function() {
if (!this._currentSession) {
let [currentSessionId] = this._proxy.GetCurrentSessionSync();
this._currentSession = new ConsoleKitSession(Gio.DBus.system,
'org.freedesktop.ConsoleKit',
currentSessionId);
}
return this._currentSession;
},
get sessionActive() {
if (this._sessionActive !== undefined)
return this._sessionActive;
let session = this.getCurrentSessionProxy();
session.connectSignal('ActiveChanged', Lang.bind(this, function(object, senderName, [isActive]) {
this._sessionActive = isActive;
}));
[this._sessionActive] = session.IsActiveSync();
return this._sessionActive;
},
canPowerOff: function(asyncCallback) {
this._proxy.CanStopRemote(function(result, error) {
if (error)
asyncCallback(false);
else
asyncCallback(result[0]);
});
},
canReboot: function(asyncCallback) {
this._proxy.CanRestartRemote(function(result, error) {
if (error)
asyncCallback(false);
else
asyncCallback(result[0]);
});
},
powerOff: function() {
this._proxy.StopRemote();
},
reboot: function() {
this._proxy.RestartRemote();
}
});

View File

@ -1,48 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const ScreenSaverIface = <interface name="org.gnome.ScreenSaver">
<method name="GetActive">
<arg type="b" direction="out" />
</method>
<method name="Lock" />
<method name="SetActive">
<arg type="b" direction="in" />
</method>
<signal name="ActiveChanged">
<arg type="b" direction="out" />
</signal>
</interface>;
const ScreenSaverInfo = Gio.DBusInterfaceInfo.new_for_xml(ScreenSaverIface);
function ScreenSaverProxy() {
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.session,
g_interface_name: ScreenSaverInfo.name,
g_interface_info: ScreenSaverInfo,
g_name: 'org.gnome.ScreenSaver',
g_object_path: '/org/gnome/ScreenSaver',
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
self.init(null);
self.screenSaverActive = false;
self.connectSignal('ActiveChanged', function(proxy, senderName, [isActive]) {
self.screenSaverActive = isActive;
});
self.connect('notify::g-name-owner', function() {
if (self.g_name_owner) {
self.GetActiveRemote(function(result, excp) {
if (result) {
let [isActive] = result;
self.screenSaverActive = isActive;
}
});
} else
self.screenSaverActive = false;
});
return self;
}

View File

@ -1,9 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gdk = imports.gi.Gdk;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Shell = imports.gi.Shell;
const Main = imports.ui.main;
@ -83,24 +80,33 @@ function spawnCommandLine(command_line) {
// this will throw an error.
function trySpawn(argv)
{
var success, pid;
try {
GLib.spawn_async(null, argv, null,
GLib.SpawnFlags.SEARCH_PATH,
null, null);
[success, pid] = GLib.spawn_async(null, argv, null,
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
null);
} catch (err) {
if (err.code == GLib.SpawnError.G_SPAWN_ERROR_NOENT) {
err.message = _("Command not found");
} else {
/* Rewrite the error in case of ENOENT */
if (err.matches(GLib.SpawnError, GLib.SpawnError.NOENT)) {
throw new GLib.SpawnError({ code: GLib.SpawnError.NOENT,
message: _("Command not found") });
} else if (err instanceof GLib.Error) {
// The exception from gjs contains an error string like:
// Error invoking GLib.spawn_command_line_async: Failed to
// execute child process "foo" (No such file or directory)
// We are only interested in the part in the parentheses. (And
// we can't pattern match the text, since it gets localized.)
err.message = err.message.replace(/.*\((.+)\)/, '$1');
let message = err.message.replace(/.*\((.+)\)/, '$1');
throw new (err.constructor)({ code: err.code,
message: message });
} else {
throw err;
}
throw err;
}
// Dummy child watch; we don't want to double-fork internally
// because then we lose the parent-child relationship, which
// can break polkit. See https://bugzilla.redhat.com//show_bug.cgi?id=819275
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function () {}, null);
}
// trySpawnCommandLine:
@ -144,7 +150,7 @@ function killall(processName) {
// whatever...
let argv = ['pkill', '-f', '^([^ ]*/)?' + processName + '($| )'];
GLib.spawn_sync(null, argv, null, GLib.SpawnFlags.SEARCH_PATH, null, null);
GLib.spawn_sync(null, argv, null, GLib.SpawnFlags.SEARCH_PATH, null);
// It might be useful to return success/failure, but we'd need
// a wrapper around WIFEXITED and WEXITSTATUS. Since none of
// the current callers care, we don't bother.

View File

@ -1,5 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const System = imports.system;
const Main = imports.ui.main;
const Scripting = imports.ui.scripting;
@ -99,7 +101,7 @@ function run() {
Main.overview.hide();
yield Scripting.waitLeisure();
global.gc();
System.gc();
yield Scripting.sleep(1000);
Scripting.collectStatistics();
Scripting.scriptEvent('afterShowHide');
@ -113,10 +115,10 @@ function run() {
for (let i = 0; i < 2; i++) {
Scripting.scriptEvent('applicationsShowStart');
Main.overview._viewSelector.switchTab('applications');
Main.overview._dash.showAppsButton.checked = true;
yield Scripting.waitLeisure();
Scripting.scriptEvent('applicationsShowDone');
Main.overview._viewSelector.switchTab('windows');
Main.overview._dash.showAppsButton.checked = false;
yield Scripting.waitLeisure();
}
}

View File

@ -22,6 +22,7 @@ const Search = imports.ui.search;
const Tweener = imports.ui.tweener;
const Workspace = imports.ui.workspace;
const Params = imports.misc.params;
const Util = imports.misc.util;
const MAX_APPLICATION_WORK_MILLIS = 75;
const MENU_POPUP_TIMEOUT = 600;
@ -36,6 +37,7 @@ const AlphabeticalView = new Lang.Class({
this._pendingAppLaterId = 0;
this._appIcons = {}; // desktop file id
this._allApps = [];
let box = new St.BoxLayout({ vertical: true });
box.add(this._grid.actor, { y_align: St.Align.START, expand: true });
@ -60,16 +62,22 @@ const AlphabeticalView = new Lang.Class({
}));
},
_removeAll: function() {
removeAll: function() {
this._grid.removeAll();
this._appIcons = {};
this._allApps = [];
},
_addApp: function(app) {
addApp: function(app) {
var id = app.get_id();
let appIcon = new AppWellIcon(app);
if (this._appIcons[id] !== undefined)
return;
this._grid.addItem(appIcon.actor);
let appIcon = new AppWellIcon(app);
let pos = Util.insertSorted(this._allApps, app, function(a, b) {
return a.compare_by_name(b);
});
this._grid.addItem(appIcon.actor, pos);
appIcon.actor.connect('key-focus-in', Lang.bind(this, this._ensureIconVisible));
this._appIcons[id] = appIcon;
@ -120,14 +128,6 @@ const AlphabeticalView = new Lang.Class({
icon.actor.visible = true;
}
}
},
setAppList: function(apps) {
this._removeAll();
for (var i = 0; i < apps.length; i++) {
var app = apps[i];
this._addApp(app);
}
}
});
@ -147,7 +147,6 @@ const ViewByCategories = new Lang.Class({
// (used only before the actor is mapped the first time)
this._currentCategory = -2;
this._categories = [];
this._apps = null;
this._categoryBox = new St.BoxLayout({ vertical: true,
reactive: true,
@ -204,16 +203,19 @@ const ViewByCategories = new Lang.Class({
if (nextType == GMenu.TreeItemType.ENTRY) {
var entry = iter.get_entry();
var app = this._appSystem.lookup_app_by_tree_entry(entry);
if (!entry.get_app_info().get_nodisplay())
if (!entry.get_app_info().get_nodisplay()) {
this._view.addApp(app);
appList.push(app);
}
} else if (nextType == GMenu.TreeItemType.DIRECTORY) {
if (!dir.get_is_nodisplay())
this._loadCategory(iter.get_directory(), appList);
var itemDir = iter.get_directory();
if (!itemDir.get_is_nodisplay())
this._loadCategory(itemDir, appList);
}
}
},
_addCategory: function(name, index, dir, allApps) {
_addCategory: function(name, index, dir) {
let button = new St.Button({ label: GLib.markup_escape_text (name, -1),
style_class: 'app-filter',
x_align: St.Align.START,
@ -225,7 +227,6 @@ const ViewByCategories = new Lang.Class({
var apps;
if (dir == null) {
apps = allApps;
this._allCategoryButton = button;
} else {
apps = [];
@ -239,6 +240,7 @@ const ViewByCategories = new Lang.Class({
},
_removeAll: function() {
this._view.removeAll();
this._categories = [];
this._categoryBox.destroy_all_children();
},
@ -246,13 +248,8 @@ const ViewByCategories = new Lang.Class({
refresh: function() {
this._removeAll();
var allApps = Shell.AppSystem.get_default().get_all();
allApps.sort(function(a, b) {
return a.compare_by_name(b);
});
/* Translators: Filter to display all applications */
this._addCategory(_("All"), -1, null, allApps);
this._addCategory(_("All"), -1, null);
var tree = this._appSystem.get_tree();
var root = tree.get_root_directory();
@ -270,7 +267,6 @@ const ViewByCategories = new Lang.Class({
}
}
this._view.setAppList(allApps);
this._selectCategory(-1);
if (this._focusDummy) {
@ -312,11 +308,10 @@ const AppSearchProvider = new Lang.Class({
_init: function() {
this.parent(_("APPLICATIONS"));
this._appSys = Shell.AppSystem.get_default();
},
getResultMetas: function(apps) {
getResultMetas: function(apps, callback) {
let metas = [];
for (let i = 0; i < apps.length; i++) {
let app = apps[i];
@ -327,15 +322,15 @@ const AppSearchProvider = new Lang.Class({
}
});
}
return metas;
callback(metas);
},
getInitialResultSet: function(terms) {
return this._appSys.initial_search(terms);
this.searchSystem.pushResults(this, this._appSys.initial_search(terms));
},
getSubsearchResultSet: function(previousResults, terms) {
return this._appSys.subsearch(previousResults, terms);
this.searchSystem.pushResults(this, this._appSys.subsearch(previousResults, terms));
},
activateResult: function(app, params) {
@ -378,7 +373,7 @@ const SettingsSearchProvider = new Lang.Class({
this._gnomecc = this._appSys.lookup_app('gnome-control-center.desktop');
},
getResultMetas: function(prefs) {
getResultMetas: function(prefs, callback) {
let metas = [];
for (let i = 0; i < prefs.length; i++) {
let pref = prefs[i];
@ -389,15 +384,15 @@ const SettingsSearchProvider = new Lang.Class({
}
});
}
return metas;
callback(metas);
},
getInitialResultSet: function(terms) {
return this._appSys.search_settings(terms);
this.searchSystem.pushResults(this, this._appSys.search_settings(terms));
},
getSubsearchResultSet: function(previousResults, terms) {
return this._appSys.search_settings(terms);
this.searchSystem.pushResults(this, this._appSys.search_settings(terms));
},
activateResult: function(pref, params) {

View File

@ -1,290 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Params = imports.misc.params;
const Shell = imports.gi.Shell;
const Main = imports.ui.main;
const ShellMountOperation = imports.ui.shellMountOperation;
const ScreenSaver = imports.misc.screenSaver;
// GSettings keys
const SETTINGS_SCHEMA = 'org.gnome.desktop.media-handling';
const SETTING_ENABLE_AUTOMOUNT = 'automount';
const AUTORUN_EXPIRE_TIMEOUT_SECS = 10;
const ConsoleKitSessionIface = <interface name="org.freedesktop.ConsoleKit.Session">
<method name="IsActive">
<arg type="b" direction="out" />
</method>
<signal name="ActiveChanged">
<arg type="b" direction="out" />
</signal>
</interface>;
const ConsoleKitSessionProxy = Gio.DBusProxy.makeProxyWrapper(ConsoleKitSessionIface);
const ConsoleKitManagerIface = <interface name="org.freedesktop.ConsoleKit.Manager">
<method name="GetCurrentSession">
<arg type="o" direction="out" />
</method>
</interface>;
const ConsoleKitManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(ConsoleKitManagerIface);
function ConsoleKitManager() {
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.system,
g_interface_name: ConsoleKitManagerInfo.name,
g_interface_info: ConsoleKitManagerInfo,
g_name: 'org.freedesktop.ConsoleKit',
g_object_path: '/org/freedesktop/ConsoleKit/Manager',
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
self._updateSessionActive = function() {
if (self.g_name_owner) {
self.GetCurrentSessionRemote(function([session]) {
self._ckSession = new ConsoleKitSessionProxy(Gio.DBus.system, 'org.freedesktop.ConsoleKit', session);
self._ckSession.connectSignal('ActiveChanged', function(object, senderName, [isActive]) {
self.sessionActive = isActive;
});
self._ckSession.IsActiveRemote(function([isActive]) {
self.sessionActive = isActive;
});
});
} else {
self.sessionActive = true;
}
};
self.connect('notify::g-name-owner',
Lang.bind(self, self._updateSessionActive));
self._updateSessionActive();
self.init(null);
return self;
}
function haveSystemd() {
return GLib.access("/sys/fs/cgroup/systemd", 0) >= 0;
}
const AutomountManager = new Lang.Class({
Name: 'AutomountManager',
_init: function() {
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
this._volumeQueue = [];
if (!haveSystemd())
this.ckListener = new ConsoleKitManager();
this._ssProxy = new ScreenSaver.ScreenSaverProxy();
this._ssProxy.connectSignal('ActiveChanged',
Lang.bind(this, this._screenSaverActiveChanged));
this._volumeMonitor = Gio.VolumeMonitor.get();
this._volumeMonitor.connect('volume-added',
Lang.bind(this,
this._onVolumeAdded));
this._volumeMonitor.connect('volume-removed',
Lang.bind(this,
this._onVolumeRemoved));
this._volumeMonitor.connect('drive-connected',
Lang.bind(this,
this._onDriveConnected));
this._volumeMonitor.connect('drive-disconnected',
Lang.bind(this,
this._onDriveDisconnected));
this._volumeMonitor.connect('drive-eject-button',
Lang.bind(this,
this._onDriveEjectButton));
Mainloop.idle_add(Lang.bind(this, this._startupMountAll));
},
_screenSaverActiveChanged: function(object, senderName, [isActive]) {
if (!isActive) {
this._volumeQueue.forEach(Lang.bind(this, function(volume) {
this._checkAndMountVolume(volume);
}));
}
// clear the queue anyway
this._volumeQueue = [];
},
_startupMountAll: function() {
let volumes = this._volumeMonitor.get_volumes();
volumes.forEach(Lang.bind(this, function(volume) {
this._checkAndMountVolume(volume, { checkSession: false,
useMountOp: false });
}));
return false;
},
isSessionActive: function() {
// Return whether the current session is active, using the
// right mechanism: either systemd if available or ConsoleKit
// as fallback.
if (haveSystemd())
return Shell.session_is_active_for_systemd();
return this.ckListener.sessionActive;
},
_onDriveConnected: function() {
// if we're not in the current ConsoleKit session,
// or screensaver is active, don't play sounds
if (!this.isSessionActive())
return;
if (this._ssProxy.screenSaverActive)
return;
global.play_theme_sound(0, 'device-added-media');
},
_onDriveDisconnected: function() {
// if we're not in the current ConsoleKit session,
// or screensaver is active, don't play sounds
if (!this.isSessionActive())
return;
if (this._ssProxy.screenSaverActive)
return;
global.play_theme_sound(0, 'device-removed-media');
},
_onDriveEjectButton: function(monitor, drive) {
// TODO: this code path is not tested, as the GVfs volume monitor
// doesn't emit this signal just yet.
if (!this.isSessionActive())
return;
// we force stop/eject in this case, so we don't have to pass a
// mount operation object
if (drive.can_stop()) {
drive.stop
(Gio.MountUnmountFlags.FORCE, null, null,
Lang.bind(this, function(drive, res) {
try {
drive.stop_finish(res);
} catch (e) {
log("Unable to stop the drive after drive-eject-button " + e.toString());
}
}));
} else if (drive.can_eject()) {
drive.eject_with_operation
(Gio.MountUnmountFlags.FORCE, null, null,
Lang.bind(this, function(drive, res) {
try {
drive.eject_with_operation_finish(res);
} catch (e) {
log("Unable to eject the drive after drive-eject-button " + e.toString());
}
}));
}
},
_onVolumeAdded: function(monitor, volume) {
this._checkAndMountVolume(volume);
},
_checkAndMountVolume: function(volume, params) {
params = Params.parse(params, { checkSession: true,
useMountOp: true });
if (params.checkSession) {
// if we're not in the current ConsoleKit session,
// don't attempt automount
if (!this.isSessionActive())
return;
if (this._ssProxy.screenSaverActive) {
if (this._volumeQueue.indexOf(volume) == -1)
this._volumeQueue.push(volume);
return;
}
}
// Volume is already mounted, don't bother.
if (volume.get_mount())
return;
if (!this._settings.get_boolean(SETTING_ENABLE_AUTOMOUNT) ||
!volume.should_automount() ||
!volume.can_mount()) {
// allow the autorun to run anyway; this can happen if the
// mount gets added programmatically later, even if
// should_automount() or can_mount() are false, like for
// blank optical media.
this._allowAutorun(volume);
this._allowAutorunExpire(volume);
return;
}
if (params.useMountOp) {
let operation = new ShellMountOperation.ShellMountOperation(volume);
this._mountVolume(volume, operation.mountOp);
} else {
this._mountVolume(volume, null);
}
},
_mountVolume: function(volume, operation) {
this._allowAutorun(volume);
volume.mount(0, operation, null,
Lang.bind(this, this._onVolumeMounted));
},
_onVolumeMounted: function(volume, res) {
this._allowAutorunExpire(volume);
try {
volume.mount_finish(res);
} catch (e) {
let string = e.toString();
// FIXME: needs proper error code handling instead of this
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
if (string.indexOf('No key available with this passphrase') != -1)
this._reaskPassword(volume);
else
log('Unable to mount volume ' + volume.get_name() + ': ' + string);
}
},
_onVolumeRemoved: function(monitor, volume) {
this._volumeQueue =
this._volumeQueue.filter(function(element) {
return (element != volume);
});
},
_reaskPassword: function(volume) {
let operation = new ShellMountOperation.ShellMountOperation(volume, { reaskPassword: true });
this._mountVolume(volume, operation.mountOp);
},
_allowAutorun: function(volume) {
volume.allowAutorun = true;
},
_allowAutorunExpire: function(volume) {
Mainloop.timeout_add_seconds(AUTORUN_EXPIRE_TIMEOUT_SECS, function() {
volume.allowAutorun = false;
return false;
});
}
});

View File

@ -9,6 +9,13 @@ const Shell = imports.gi.Shell;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const PopupAnimation = {
NONE: 0,
SLIDE: 1 << 0,
FADE: 1 << 1,
FULL: ~0,
};
const POPUP_ANIMATION_TIME = 0.15;
/**
@ -18,7 +25,10 @@ const POPUP_ANIMATION_TIME = 0.15;
*
* An actor which displays a triangle "arrow" pointing to a given
* side. The .bin property is a container in which content can be
* placed. The arrow position may be controlled via setArrowOrigin().
* placed. The arrow position may be controlled via
* setArrowOrigin(). The arrow side might be temporarily flipped
* depending on the box size and source position to keep the box
* totally inside the monitor if possible.
*
*/
const BoxPointer = new Lang.Class({
@ -26,6 +36,7 @@ const BoxPointer = new Lang.Class({
_init: function(arrowSide, binProperties) {
this._arrowSide = arrowSide;
this._userArrowSide = arrowSide;
this._arrowOrigin = 0;
this.actor = new St.Bin({ x_fill: true,
y_fill: true });
@ -65,11 +76,16 @@ const BoxPointer = new Lang.Class({
show: function(animate, onComplete) {
let themeNode = this.actor.get_theme_node();
let rise = themeNode.get_length('-arrow-rise');
let animationTime = (animate & PopupAnimation.FULL) ? POPUP_ANIMATION_TIME : 0;
if (animate & PopupAnimation.FADE)
this.opacity = 0;
else
this.opacity = 255;
this.opacity = 0;
this.actor.show();
if (animate) {
if (animate & PopupAnimation.SLIDE) {
switch (this._arrowSide) {
case St.Side.TOP:
this.yOffset = -rise;
@ -95,7 +111,7 @@ const BoxPointer = new Lang.Class({
if (onComplete)
onComplete();
}),
time: POPUP_ANIMATION_TIME });
time: animationTime });
},
hide: function(animate, onComplete) {
@ -103,8 +119,10 @@ const BoxPointer = new Lang.Class({
let yOffset = 0;
let themeNode = this.actor.get_theme_node();
let rise = themeNode.get_length('-arrow-rise');
let fade = (animate & PopupAnimation.FADE);
let animationTime = (animate & PopupAnimation.FULL) ? POPUP_ANIMATION_TIME : 0;
if (animate) {
if (animate & PopupAnimation.SLIDE) {
switch (this._arrowSide) {
case St.Side.TOP:
yOffset = rise;
@ -123,13 +141,14 @@ const BoxPointer = new Lang.Class({
this._muteInput();
Tweener.addTween(this, { opacity: 0,
Tweener.addTween(this, { opacity: fade ? 0 : 255,
xOffset: xOffset,
yOffset: yOffset,
transition: 'linear',
time: POPUP_ANIMATION_TIME,
time: animationTime,
onComplete: Lang.bind(this, function () {
this.actor.hide();
this.opacity = 0;
this.xOffset = 0;
this.yOffset = 0;
if (onComplete)
@ -199,8 +218,27 @@ const BoxPointer = new Lang.Class({
}
this.bin.allocate(childBox, flags);
if (this._sourceActor && this._sourceActor.mapped)
if (this._sourceActor && this._sourceActor.mapped) {
this._reposition(this._sourceActor, this._arrowAlignment);
if (this._shouldFlip()) {
switch (this._arrowSide) {
case St.Side.TOP:
this._arrowSide = St.Side.BOTTOM;
break;
case St.Side.BOTTOM:
this._arrowSide = St.Side.TOP;
break;
case St.Side.LEFT:
this._arrowSide = St.Side.RIGHT;
break;
case St.Side.RIGHT:
this._arrowSide = St.Side.LEFT;
break;
}
this._reposition(this._sourceActor, this._arrowAlignment);
}
}
},
_drawBorder: function(area) {
@ -214,7 +252,6 @@ const BoxPointer = new Lang.Class({
let halfBorder = borderWidth / 2;
let halfBase = Math.floor(base/2);
let borderColor = themeNode.get_color('-arrow-border-color');
let backgroundColor = themeNode.get_color('-arrow-background-color');
let [width, height] = area.get_surface_size();
@ -225,7 +262,6 @@ const BoxPointer = new Lang.Class({
boxWidth -= rise;
}
let cr = area.get_context();
Clutter.cairo_set_source_color(cr, borderColor);
// Translate so that box goes from 0,0 to boxWidth,boxHeight,
// with the arrow poking out of that
@ -238,14 +274,51 @@ const BoxPointer = new Lang.Class({
let [x1, y1] = [halfBorder, halfBorder];
let [x2, y2] = [boxWidth - halfBorder, boxHeight - halfBorder];
let skipTopLeft = false;
let skipTopRight = false;
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;
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.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._arrowOrigin < (x1 + (borderRadius + halfBase))) {
cr.lineTo(this._arrowOrigin, y1 - rise);
cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y1);
} else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y1);
cr.lineTo(this._arrowOrigin, y1 - rise);
if (skipTopLeft) {
cr.moveTo(x1, y2 - borderRadius);
cr.lineTo(x1, y1 - rise);
cr.lineTo(x1 + halfBase, y1);
} else if (skipTopRight) {
cr.lineTo(x2 - halfBase, y1);
cr.lineTo(x2, y1 - rise);
cr.lineTo(x2, y1 + borderRadius);
} else {
cr.lineTo(this._arrowOrigin - halfBase, y1);
cr.lineTo(this._arrowOrigin, y1 - rise);
@ -253,19 +326,20 @@ const BoxPointer = new Lang.Class({
}
}
cr.lineTo(x2 - borderRadius, y1);
// top-right corner
cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius,
3*Math.PI/2, Math.PI*2);
if (!skipTopRight) {
cr.lineTo(x2 - borderRadius, y1);
cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius,
3*Math.PI/2, Math.PI*2);
}
if (this._arrowSide == St.Side.RIGHT) {
if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
cr.lineTo(x2 + rise, this._arrowOrigin);
cr.lineTo(x2, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase);
} else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
cr.lineTo(x2, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase);
cr.lineTo(x2 + rise, this._arrowOrigin);
if (skipTopRight) {
cr.lineTo(x2 + rise, y1);
cr.lineTo(x2 + rise, y1 + halfBase);
} else if (skipBottomRight) {
cr.lineTo(x2, y2 - halfBase);
cr.lineTo(x2 + rise, y2);
cr.lineTo(x2 - borderRadius, y2);
} else {
cr.lineTo(x2, this._arrowOrigin - halfBase);
cr.lineTo(x2 + rise, this._arrowOrigin);
@ -273,19 +347,20 @@ const BoxPointer = new Lang.Class({
}
}
cr.lineTo(x2, y2 - borderRadius);
// bottom-right corner
cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius,
0, Math.PI/2);
if (!skipBottomRight) {
cr.lineTo(x2, y2 - borderRadius);
cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius,
0, Math.PI/2);
}
if (this._arrowSide == St.Side.BOTTOM) {
if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) {
cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y2);
cr.lineTo(this._arrowOrigin, y2 + rise);
} else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
cr.lineTo(this._arrowOrigin, y2 + rise);
cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y2);
if (skipBottomLeft) {
cr.lineTo(x1 + halfBase, y2);
cr.lineTo(x1, y2 + rise);
cr.lineTo(x1, y2 - borderRadius);
} else if (skipBottomRight) {
cr.lineTo(x2, y2 + rise);
cr.lineTo(x2 - halfBase, y2);
} else {
cr.lineTo(this._arrowOrigin + halfBase, y2);
cr.lineTo(this._arrowOrigin, y2 + rise);
@ -293,19 +368,20 @@ const BoxPointer = new Lang.Class({
}
}
cr.lineTo(x1 + borderRadius, y2);
// bottom-left corner
cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius,
Math.PI/2, Math.PI);
if (!skipBottomLeft) {
cr.lineTo(x1 + borderRadius, y2);
cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius,
Math.PI/2, Math.PI);
}
if (this._arrowSide == St.Side.LEFT) {
if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
cr.lineTo(x1, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase);
cr.lineTo(x1 - rise, this._arrowOrigin);
} else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
cr.lineTo(x1 - rise, this._arrowOrigin);
cr.lineTo(x1, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase);
if (skipTopLeft) {
cr.lineTo(x1, y1 + halfBase);
cr.lineTo(x1 - rise, y1);
cr.lineTo(x1 + borderRadius, y1);
} else if (skipBottomLeft) {
cr.lineTo(x1 - rise, y2)
cr.lineTo(x1 - rise, y2 - halfBase);
} else {
cr.lineTo(x1, this._arrowOrigin + halfBase);
cr.lineTo(x1 - rise, this._arrowOrigin);
@ -313,20 +389,26 @@ const BoxPointer = new Lang.Class({
}
}
cr.lineTo(x1, y1 + borderRadius);
// top-left corner
cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius,
Math.PI, 3*Math.PI/2);
if (!skipTopLeft) {
cr.lineTo(x1, y1 + borderRadius);
cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius,
Math.PI, 3*Math.PI/2);
}
Clutter.cairo_set_source_color(cr, backgroundColor);
cr.fillPreserve();
Clutter.cairo_set_source_color(cr, borderColor);
cr.setLineWidth(borderWidth);
cr.stroke();
if (borderWidth > 0) {
let borderColor = themeNode.get_color('-arrow-border-color');
Clutter.cairo_set_source_color(cr, borderColor);
cr.setLineWidth(borderWidth);
cr.stroke();
}
},
setPosition: function(sourceActor, alignment) {
this._arrowSide = this._userArrowSide;
// We need to show it now to force an allocation,
// so that we can query the correct size.
this.actor.show();
@ -343,11 +425,7 @@ const BoxPointer = new Lang.Class({
if (!this._sourceActor)
return;
// We need to show it now to force an allocation,
// so that we can query the correct size.
this.actor.show();
this._reposition(this._sourceActor, this._arrowAlignment);
this.setPosition(this._sourceActor, this._arrowAlignment);
},
_reposition: function(sourceActor, alignment) {
@ -368,10 +446,9 @@ const BoxPointer = new Lang.Class({
let arrowBase = themeNode.get_length('-arrow-base');
let borderRadius = themeNode.get_length('-arrow-border-radius');
let margin = (4 * borderRadius + borderWidth + arrowBase);
let halfMargin = margin / 2;
let themeNode = this.actor.get_theme_node();
let gap = themeNode.get_length('-boxpointer-gap');
let padding = themeNode.get_length('-arrow-rise');
let resX, resY;
@ -390,29 +467,66 @@ const BoxPointer = new Lang.Class({
break;
}
// Now align and position the pointing axis, making sure
// it fits on screen
// Now align and position the pointing axis, making sure it fits on
// screen. If the arrowOrigin is so close to the edge that the arrow
// will not be isosceles, we try to compensate as follows:
// - We skip the rounded corner and settle for a right angled arrow
// as shown below. See _drawBorder for further details.
// |\_____
// |
// |
// - If the arrow was going to be acute angled, we move the position
// of the box to maintain the arrow's accuracy.
let arrowOrigin;
let halfBase = Math.floor(arrowBase/2);
let halfBorder = borderWidth / 2;
let halfMargin = margin / 2;
let [x1, y1] = [halfBorder, halfBorder];
let [x2, y2] = [natWidth - halfBorder, natHeight - halfBorder];
switch (this._arrowSide) {
case St.Side.TOP:
case St.Side.BOTTOM:
resX = sourceCenterX - (halfMargin + (natWidth - margin) * alignment);
resX = Math.max(resX, monitor.x + 10);
resX = Math.min(resX, monitor.x + monitor.width - (10 + natWidth));
this.setArrowOrigin(sourceCenterX - resX);
resX = Math.max(resX, monitor.x + padding);
resX = Math.min(resX, monitor.x + monitor.width - (padding + natWidth));
arrowOrigin = sourceCenterX - resX;
if (arrowOrigin <= (x1 + (borderRadius + halfBase))) {
if (arrowOrigin > x1)
resX += (arrowOrigin - x1);
arrowOrigin = x1;
} else if (arrowOrigin >= (x2 - (borderRadius + halfBase))) {
if (arrowOrigin < x2)
resX -= (x2 - arrowOrigin);
arrowOrigin = x2;
}
break;
case St.Side.LEFT:
case St.Side.RIGHT:
resY = sourceCenterY - (halfMargin + (natHeight - margin) * alignment);
resY = Math.max(resY, monitor.y + 10);
resY = Math.min(resY, monitor.y + monitor.height - (10 + natHeight));
resY = Math.max(resY, monitor.y + padding);
resY = Math.min(resY, monitor.y + monitor.height - (padding + natHeight));
this.setArrowOrigin(sourceCenterY - resY);
arrowOrigin = sourceCenterY - resY;
if (arrowOrigin <= (y1 + (borderRadius + halfBase))) {
if (arrowOrigin > y1)
resY += (arrowOrigin - y1);
arrowOrigin = y1;
} else if (arrowOrigin >= (y2 - (borderRadius + halfBase))) {
if (arrowOrigin < y2)
resX -= (y2 - arrowOrigin);
arrowOrigin = y2;
}
break;
}
this.setArrowOrigin(arrowOrigin);
let parent = this.actor.get_parent();
let success, x, y;
while (!success) {
@ -446,6 +560,39 @@ const BoxPointer = new Lang.Class({
-(this._yPosition + this._yOffset));
},
_shouldFlip: function() {
let sourceAllocation = Shell.util_get_transformed_allocation(this._sourceActor);
let boxAllocation = Shell.util_get_transformed_allocation(this.actor);
let boxWidth = boxAllocation.x2 - boxAllocation.x1;
let boxHeight = boxAllocation.y2 - boxAllocation.y1;
let monitor = Main.layoutManager.findMonitorForActor(this.actor);
switch (this._arrowSide) {
case St.Side.TOP:
if (boxAllocation.y2 > monitor.y + monitor.height &&
boxHeight < sourceAllocation.y1 - monitor.y)
return true;
break;
case St.Side.BOTTOM:
if (boxAllocation.y1 < monitor.y &&
boxHeight < monitor.y + monitor.height - sourceAllocation.y2)
return true;
break;
case St.Side.LEFT:
if (boxAllocation.x2 > monitor.x + monitor.width &&
boxWidth < sourceAllocation.x1 - monitor.x)
return true;
break;
case St.Side.RIGHT:
if (boxAllocation.x1 < monitor.x &&
boxWidth < monitor.x + monitor.width - sourceAllocation.x2)
return true;
break;
}
return false;
},
set xOffset(offset) {
this._xOffset = offset;
this._shiftActor();

View File

@ -204,12 +204,11 @@ const CalendarServerInfo = Gio.DBusInterfaceInfo.new_for_xml(CalendarServerIfac
function CalendarServer() {
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.session,
g_interface_name: CalendarServerInfo.name,
g_interface_info: CalendarServerInfo,
g_name: 'org.gnome.Shell.CalendarServer',
g_object_path: '/org/gnome/Shell/CalendarServer',
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
g_interface_name: CalendarServerInfo.name,
g_interface_info: CalendarServerInfo,
g_name: 'org.gnome.Shell.CalendarServer',
g_object_path: '/org/gnome/Shell/CalendarServer',
g_flags: Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES });
self.init(null);
return self;
@ -340,22 +339,10 @@ const DBusEventSource = new Lang.Class({
});
Signals.addSignalMethods(DBusEventSource.prototype);
// Calendar:
// @eventSource: is an object implementing the EventSource API, e.g. the
// requestRange(), getEvents(), hasEvents() methods and the ::changed signal.
const Calendar = new Lang.Class({
Name: 'Calendar',
_init: function(eventSource) {
if (eventSource) {
this._eventSource = eventSource;
this._eventSource.connect('changed', Lang.bind(this,
function() {
this._update(false);
}));
}
_init: function() {
this._weekStart = Shell.util_get_week_start();
this._weekdate = NaN;
this._digitWidth = NaN;
@ -392,6 +379,24 @@ const Calendar = new Lang.Class({
this._buildHeader ();
},
// @eventSource: is an object implementing the EventSource API, e.g. the
// requestRange(), getEvents(), hasEvents() methods and the ::changed signal.
setEventSource: function(eventSource) {
if (this._eventSource) {
this._eventSource.disconnect(this._eventSourceChangedId);
this._eventSource = null;
}
this._eventSource = eventSource;
if (this._eventSource) {
this._eventSourceChangedId = this._eventSource.connect('changed', Lang.bind(this, function() {
this._update(false);
}));
this._update(true);
}
},
// Sets the calendar to show a specific date
setDate: function(date, forceReload) {
if (!_sameDay(date, this._selectedDate)) {
@ -448,7 +453,7 @@ const Calendar = new Lang.Class({
}
// All the children after this are days, and get removed when we update the calendar
this._firstDayIndex = this.actor.get_children().length;
this._firstDayIndex = this.actor.get_n_children();
},
_onStyleChange: function(actor, event) {
@ -551,6 +556,7 @@ const Calendar = new Lang.Class({
let row = 2;
while (true) {
let button = new St.Button({ label: iter.getDate().toString() });
let rtl = button.get_text_direction() == Clutter.TextDirection.RTL;
if (!this._eventSource)
button.reactive = false;
@ -571,7 +577,10 @@ const Calendar = new Lang.Class({
// Hack used in lieu of border-collapse - see gnome-shell.css
if (row == 2)
styleClass = 'calendar-day-top ' + styleClass;
if (iter.getDay() == this._weekStart)
let leftMost = rtl ? iter.getDay() == (this._weekStart + 6) % 7
: iter.getDay() == this._weekStart;
if (leftMost)
styleClass = 'calendar-day-left ' + styleClass;
if (_sameDay(now, iter))
@ -618,16 +627,25 @@ Signals.addSignalMethods(Calendar.prototype);
const EventsList = new Lang.Class({
Name: 'EventsList',
_init: function(eventSource) {
_init: function() {
this.actor = new St.BoxLayout({ vertical: true, style_class: 'events-header-vbox'});
this._date = new Date();
this._eventSource = eventSource;
this._eventSource.connect('changed', Lang.bind(this, this._update));
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
this._desktopSettings.connect('changed', Lang.bind(this, this._update));
this._weekStart = Shell.util_get_week_start();
},
this._update();
setEventSource: function(eventSource) {
if (this._eventSource) {
this._eventSource.disconnect(this._eventSourceChangedId);
this._eventSource = null;
}
this._eventSource = eventSource;
if (this._eventSource) {
this._eventSourceChangedId = this._eventSource.connect('changed', Lang.bind(this, this._update));
}
},
_addEvent: function(dayNameBox, timeBox, eventTitleBox, includeDayName, day, time, desc) {
@ -713,13 +731,15 @@ const EventsList = new Lang.Class({
let tomorrowEnd = new Date(dayEnd.getTime() + 86400 * 1000);
this._addPeriod(_("Tomorrow"), tomorrowBegin, tomorrowEnd, false, true);
if (dayEnd.getDay() <= 4 + this._weekStart) {
let dayInWeek = (dayEnd.getDay() - this._weekStart + 7) % 7;
if (dayInWeek < 5) {
/* If now is within the first 5 days we show "This week" and
* include events up until and including Saturday/Sunday
* (depending on whether a week starts on Sunday/Monday).
*/
let thisWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
let thisWeekEnd = new Date(dayEnd.getTime() + (6 + this._weekStart - dayEnd.getDay()) * 86400 * 1000);
let thisWeekEnd = new Date(dayEnd.getTime() + (6 - dayInWeek) * 86400 * 1000);
this._addPeriod(_("This week"), thisWeekBegin, thisWeekEnd, true, false);
} else {
/* otherwise it's one of the two last days of the week ... show
@ -727,7 +747,7 @@ const EventsList = new Lang.Class({
* Saturday/Sunday
*/
let nextWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
let nextWeekEnd = new Date(dayEnd.getTime() + (13 + this._weekStart - dayEnd.getDay()) * 86400 * 1000);
let nextWeekEnd = new Date(dayEnd.getTime() + (13 - dayInWeek) * 86400 * 1000);
this._addPeriod(_("Next week"), nextWeekBegin, nextWeekEnd, true, false);
}
},

View File

@ -0,0 +1,65 @@
const Lang = imports.lang;
const Main = imports.ui.main;
const ComponentManager = new Lang.Class({
Name: 'ComponentManager',
_init: function() {
this._allComponents = {};
this._enabledComponents = [];
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._sessionUpdated();
},
_sessionUpdated: function() {
let newEnabledComponents = Main.sessionMode.components;
newEnabledComponents.filter(Lang.bind(this, function(name) {
return this._enabledComponents.indexOf(name) == -1;
})).forEach(Lang.bind(this, function(name) {
this._enableComponent(name);
}));
this._enabledComponents.filter(Lang.bind(this, function(name) {
return newEnabledComponents.indexOf(name) == -1;
})).forEach(Lang.bind(this, function(name) {
this._disableComponent(name);
}));
this._enabledComponents = newEnabledComponents;
},
_importComponent: function(name) {
let module = imports.ui.components[name];
return module.Component;
},
_ensureComponent: function(name) {
let component = this._allComponents[name];
if (component)
return component;
if (Main.sessionMode.isLocked)
return null;
let constructor = this._importComponent(name);
component = new constructor();
this._allComponents[name] = component;
return component;
},
_enableComponent: function(name) {
let component = this._ensureComponent(name);
if (component)
component.enable();
},
_disableComponent: function(name) {
let component = this._allComponents[name];
if (component == null)
return;
component.disable();
}
});

View File

@ -0,0 +1,241 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Params = imports.misc.params;
const Shell = imports.gi.Shell;
const GnomeSession = imports.misc.gnomeSession;
const LoginManager = imports.misc.loginManager;
const Main = imports.ui.main;
const ShellMountOperation = imports.ui.shellMountOperation;
const GNOME_SESSION_AUTOMOUNT_INHIBIT = 16;
// GSettings keys
const SETTINGS_SCHEMA = 'org.gnome.desktop.media-handling';
const SETTING_ENABLE_AUTOMOUNT = 'automount';
const AUTORUN_EXPIRE_TIMEOUT_SECS = 10;
const AutomountManager = new Lang.Class({
Name: 'AutomountManager',
_init: function() {
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
this._volumeQueue = [];
this._session = new GnomeSession.SessionManager();
this._session.connectSignal('InhibitorAdded',
Lang.bind(this, this._InhibitorsChanged));
this._session.connectSignal('InhibitorRemoved',
Lang.bind(this, this._InhibitorsChanged));
this._inhibited = false;
this._loginManager = LoginManager.getLoginManager();
this._volumeMonitor = Gio.VolumeMonitor.get();
},
enable: function() {
this._volumeAddedId = this._volumeMonitor.connect('volume-added', Lang.bind(this, this._onVolumeAdded));
this._volumeRemovedId = this._volumeMonitor.connect('volume-removed', Lang.bind(this, this._onVolumeRemoved));
this._driveConnectedId = this._volumeMonitor.connect('drive-connected', Lang.bind(this, this._onDriveConnected));
this._driveDisconnectedId = this._volumeMonitor.connect('drive-disconnected', Lang.bind(this, this._onDriveDisconnected));
this._driveEjectButtonId = this._volumeMonitor.connect('drive-eject-button', Lang.bind(this, this._onDriveEjectButton));
this._mountAllId = Mainloop.idle_add(Lang.bind(this, this._startupMountAll));
},
disable: function() {
this._volumeMonitor.disconnect(this._volumeAddedId);
this._volumeMonitor.disconnect(this._volumeRemovedId);
this._volumeMonitor.disconnect(this._driveConnectedId);
this._volumeMonitor.disconnect(this._driveDisconnectedId);
this._volumeMonitor.disconnect(this._driveEjectButtonId);
if (this._mountAllId > 0) {
Mainloop.source_remove(this._mountAllId);
this._mountAllId = 0;
}
},
_InhibitorsChanged: function(object, senderName, [inhibtor]) {
this._session.IsInhibitedRemote(GNOME_SESSION_AUTOMOUNT_INHIBIT,
Lang.bind(this,
function(result, error) {
if (!error) {
this._inhibited = result[0];
}
}));
},
_startupMountAll: function() {
let volumes = this._volumeMonitor.get_volumes();
volumes.forEach(Lang.bind(this, function(volume) {
this._checkAndMountVolume(volume, { checkSession: false,
useMountOp: false,
allowAutorun: false });
}));
this._mountAllId = 0;
return false;
},
_onDriveConnected: function() {
// if we're not in the current ConsoleKit session,
// or screensaver is active, don't play sounds
if (!this._loginManager.sessionActive)
return;
global.play_theme_sound(0, 'device-added-media');
},
_onDriveDisconnected: function() {
// if we're not in the current ConsoleKit session,
// or screensaver is active, don't play sounds
if (!this._loginManager.sessionActive)
return;
global.play_theme_sound(0, 'device-removed-media');
},
_onDriveEjectButton: function(monitor, drive) {
// TODO: this code path is not tested, as the GVfs volume monitor
// doesn't emit this signal just yet.
if (!this._loginManager.sessionActive)
return;
// we force stop/eject in this case, so we don't have to pass a
// mount operation object
if (drive.can_stop()) {
drive.stop
(Gio.MountUnmountFlags.FORCE, null, null,
Lang.bind(this, function(drive, res) {
try {
drive.stop_finish(res);
} catch (e) {
log("Unable to stop the drive after drive-eject-button " + e.toString());
}
}));
} else if (drive.can_eject()) {
drive.eject_with_operation
(Gio.MountUnmountFlags.FORCE, null, null,
Lang.bind(this, function(drive, res) {
try {
drive.eject_with_operation_finish(res);
} catch (e) {
log("Unable to eject the drive after drive-eject-button " + e.toString());
}
}));
}
},
_onVolumeAdded: function(monitor, volume) {
this._checkAndMountVolume(volume);
},
_checkAndMountVolume: function(volume, params) {
params = Params.parse(params, { checkSession: true,
useMountOp: true,
allowAutorun: true });
if (params.checkSession) {
// if we're not in the current ConsoleKit session,
// don't attempt automount
if (!this._loginManager.sessionActive)
return;
}
if (this._inhibited)
return;
// Volume is already mounted, don't bother.
if (volume.get_mount())
return;
if (!this._settings.get_boolean(SETTING_ENABLE_AUTOMOUNT) ||
!volume.should_automount() ||
!volume.can_mount()) {
// allow the autorun to run anyway; this can happen if the
// mount gets added programmatically later, even if
// should_automount() or can_mount() are false, like for
// blank optical media.
this._allowAutorun(volume);
this._allowAutorunExpire(volume);
return;
}
if (params.useMountOp) {
let operation = new ShellMountOperation.ShellMountOperation(volume);
this._mountVolume(volume, operation, params.allowAutorun);
} else {
this._mountVolume(volume, null, params.allowAutorun);
}
},
_mountVolume: function(volume, operation, allowAutorun) {
if (allowAutorun)
this._allowAutorun(volume);
let mountOp = operation ? operation.mountOp : null;
volume._operation = operation;
volume.mount(0, mountOp, null,
Lang.bind(this, this._onVolumeMounted));
},
_onVolumeMounted: function(volume, res) {
this._allowAutorunExpire(volume);
try {
volume.mount_finish(res);
this._closeOperation(volume);
} catch (e) {
// FIXME: we will always get G_IO_ERROR_FAILED from the gvfs udisks
// backend in this case, see
// https://bugs.freedesktop.org/show_bug.cgi?id=51271
if (e.message.indexOf('No key available with this passphrase') != -1) {
this._reaskPassword(volume);
} else {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
log('Unable to mount volume ' + volume.get_name() + ': ' + e.toString());
this._closeOperation(volume);
}
}
},
_onVolumeRemoved: function(monitor, volume) {
this._volumeQueue =
this._volumeQueue.filter(function(element) {
return (element != volume);
});
},
_reaskPassword: function(volume) {
let existingDialog = volume._operation ? volume._operation.borrowDialog() : null;
let operation =
new ShellMountOperation.ShellMountOperation(volume,
{ existingDialog: existingDialog });
this._mountVolume(volume, operation);
},
_closeOperation: function(volume) {
if (volume._operation)
volume._operation.close();
},
_allowAutorun: function(volume) {
volume.allowAutorun = true;
},
_allowAutorunExpire: function(volume) {
Mainloop.timeout_add_seconds(AUTORUN_EXPIRE_TIMEOUT_SECS, function() {
volume.allowAutorun = false;
return false;
});
}
});
const Component = AutomountManager;

View File

@ -4,6 +4,7 @@ const Lang = imports.lang;
const Gio = imports.gi.Gio;
const St = imports.gi.St;
const LoginManager = imports.misc.loginManager;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const ShellMountOperation = imports.ui.shellMountOperation;
@ -23,12 +24,14 @@ const AutorunSetting = {
};
// misc utils
function ignoreAutorunForMount(mount) {
function shouldAutorunMount(mount, forTransient) {
let root = mount.get_root();
let volume = mount.get_volume();
if ((root.is_native() && !isMountRootHidden(root)) ||
(volume && volume.allowAutorun && volume.should_automount()))
if (!volume || (!volume.allowAutorun && forTransient))
return false;
if (!root.is_native() || isMountRootHidden(root))
return false;
return true;
@ -140,23 +143,44 @@ const AutorunManager = new Lang.Class({
Name: 'AutorunManager',
_init: function() {
this._loginManager = LoginManager.getLoginManager();
this._volumeMonitor = Gio.VolumeMonitor.get();
this._volumeMonitor.connect('mount-added',
Lang.bind(this,
this._onMountAdded));
this._volumeMonitor.connect('mount-removed',
Lang.bind(this,
this._onMountRemoved));
this._transDispatcher = new AutorunTransientDispatcher(this);
},
this._transDispatcher = new AutorunTransientDispatcher();
this._createResidentSource();
_ensureResidentSource: function() {
if (this._residentSource)
return;
this._residentSource = new AutorunResidentSource(this);
let destroyId = this._residentSource.connect('destroy', Lang.bind(this, function() {
this._residentSource.disconnect(destroyId);
this._residentSource = null;
}));
},
enable: function() {
this._scanMounts();
this._mountAddedId = this._volumeMonitor.connect('mount-added', Lang.bind(this, this._onMountAdded));
this._mountRemovedId = this._volumeMonitor.connect('mount-removed', Lang.bind(this, this._onMountRemoved));
},
disable: function() {
if (this._residentSource)
this._residentSource.destroy();
this._volumeMonitor.disconnect(this._mountAddedId);
this._volumeMonitor.disconnect(this._mountRemovedId);
},
_scanMounts: function() {
let mounts = this._volumeMonitor.get_mounts();
mounts.forEach(Lang.bind(this, function (mount) {
let discoverer = new ContentTypeDiscoverer(Lang.bind (this,
function (mount, apps) {
this._ensureResidentSource();
this._residentSource.addMount(mount, apps);
}));
@ -164,22 +188,16 @@ const AutorunManager = new Lang.Class({
}));
},
_createResidentSource: function() {
this._residentSource = new AutorunResidentSource();
this._residentSource.connect('destroy',
Lang.bind(this,
this._createResidentSource));
},
_onMountAdded: function(monitor, mount) {
// don't do anything if our session is not the currently
// active one
if (!Main.automountManager.isSessionActive())
if (!this._loginManager.sessionActive)
return;
let discoverer = new ContentTypeDiscoverer(Lang.bind (this,
function (mount, apps, contentTypes) {
this._transDispatcher.addMount(mount, apps, contentTypes);
this._ensureResidentSource();
this._residentSource.addMount(mount, apps);
}));
@ -188,7 +206,8 @@ const AutorunManager = new Lang.Class({
_onMountRemoved: function(monitor, mount) {
this._transDispatcher.removeMount(mount);
this._residentSource.removeMount(mount);
if (this._residentSource)
this._residentSource.removeMount(mount);
},
ejectMount: function(mount) {
@ -224,11 +243,9 @@ const AutorunManager = new Lang.Class({
try {
mount.unmount_with_operation_finish(res);
} catch (e) {
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
// but we can't access the error code from JS.
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
log('Unable to eject the mount ' + mount.get_name()
+ ': ' + e.toString());
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
log('Unable to eject the mount ' + mount.get_name()
+ ': ' + e.toString());
}
},
@ -236,11 +253,9 @@ const AutorunManager = new Lang.Class({
try {
source.eject_with_operation_finish(res);
} catch (e) {
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
// but we can't access the error code from JS.
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
log('Unable to eject the drive ' + source.get_name()
+ ': ' + e.toString());
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
log('Unable to eject the drive ' + source.get_name()
+ ': ' + e.toString());
}
},
@ -248,11 +263,9 @@ const AutorunManager = new Lang.Class({
try {
drive.stop_finish(res);
} catch (e) {
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
// but we can't access the error code from JS.
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
log('Unable to stop the drive ' + drive.get_name()
+ ': ' + e.toString());
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
log('Unable to stop the drive ' + drive.get_name()
+ ': ' + e.toString());
}
},
});
@ -261,17 +274,22 @@ const AutorunResidentSource = new Lang.Class({
Name: 'AutorunResidentSource',
Extends: MessageTray.Source,
_init: function() {
this.parent(_("Removable Devices"));
_init: function(manager) {
this.parent(_("Removable Devices"), 'media-removable');
this.showInLockScreen = false;
this._mounts = [];
this._notification = new AutorunResidentNotification(this);
this._setSummaryIcon(this.createNotificationIcon());
this._manager = manager;
this._notification = new AutorunResidentNotification(this._manager, this);
},
buildRightClickMenu: function() {
return null;
},
addMount: function(mount, apps) {
if (ignoreAutorunForMount(mount))
if (!shouldAutorunMount(mount, false))
return;
let filtered = this._mounts.filter(function (element) {
@ -310,12 +328,6 @@ const AutorunResidentSource = new Lang.Class({
Main.messageTray.add(this);
this.pushNotification(this._notification);
}
},
createNotificationIcon: function() {
return new St.Icon ({ icon_name: 'media-removable',
icon_type: St.IconType.FULLCOLOR,
icon_size: this.ICON_SIZE });
}
});
@ -323,7 +335,7 @@ const AutorunResidentNotification = new Lang.Class({
Name: 'AutorunResidentNotification',
Extends: MessageTray.Notification,
_init: function(source) {
_init: function(manager, source) {
this.parent(source, source.title, null, { customContent: true });
// set the notification as resident
@ -331,6 +343,7 @@ const AutorunResidentNotification = new Lang.Class({
this._layout = new St.BoxLayout ({ style_class: 'hotplug-resident-box',
vertical: true });
this._manager = manager;
this.addActor(this._layout,
{ x_expand: true,
@ -393,7 +406,7 @@ const AutorunResidentNotification = new Lang.Class({
}));
ejectButton.connect('clicked', Lang.bind(this, function() {
Main.autorunManager.ejectMount(mount);
this._manager.ejectMount(mount);
}));
return item;
@ -403,7 +416,8 @@ const AutorunResidentNotification = new Lang.Class({
const AutorunTransientDispatcher = new Lang.Class({
Name: 'AutorunTransientDispatcher',
_init: function() {
_init: function(manager) {
this._manager = manager;
this._sources = [];
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
},
@ -446,7 +460,7 @@ const AutorunTransientDispatcher = new Lang.Class({
return;
// add a new source
this._sources.push(new AutorunTransientSource(mount, apps));
this._sources.push(new AutorunTransientSource(this._manager, mount, apps));
},
addMount: function(mount, apps, contentTypes) {
@ -455,7 +469,7 @@ const AutorunTransientDispatcher = new Lang.Class({
return;
// if the mount doesn't want to be autorun, return
if (ignoreAutorunForMount(mount))
if (!shouldAutorunMount(mount, true))
return;
let setting = this._getAutorunSettingForType(contentTypes[0]);
@ -499,23 +513,23 @@ const AutorunTransientSource = new Lang.Class({
Name: 'AutorunTransientSource',
Extends: MessageTray.Source,
_init: function(mount, apps) {
this.parent(mount.get_name());
_init: function(manager, mount, apps) {
this._manager = manager;
this.mount = mount;
this.apps = apps;
this._notification = new AutorunTransientNotification(this);
this._setSummaryIcon(this.createNotificationIcon());
this.parent(mount.get_name());
this._notification = new AutorunTransientNotification(this._manager, this);
// add ourselves as a source, and popup the notification
Main.messageTray.add(this);
this.notify(this._notification);
},
createNotificationIcon: function() {
createIcon: function(size) {
return new St.Icon({ gicon: this.mount.get_icon(),
icon_size: this.ICON_SIZE });
icon_size: size });
}
});
@ -523,9 +537,10 @@ const AutorunTransientNotification = new Lang.Class({
Name: 'AutorunTransientNotification',
Extends: MessageTray.Notification,
_init: function(source) {
_init: function(manager, source) {
this.parent(source, source.title, null, { customContent: true });
this._manager = manager;
this._box = new St.BoxLayout({ style_class: 'hotplug-transient-box',
vertical: true });
this.addActor(this._box);
@ -594,10 +609,11 @@ const AutorunTransientNotification = new Lang.Class({
style_class: 'hotplug-notification-item' });
button.connect('clicked', Lang.bind(this, function() {
Main.autorunManager.ejectMount(this._mount);
this._manager.ejectMount(this._mount);
}));
return button;
}
});
const Component = AutorunManager;

View File

@ -65,7 +65,8 @@ const KeyringDialog = new Lang.Class({
key: Clutter.Escape
},
{ label: '',
action: Lang.bind(this, this._onContinueButton)
action: Lang.bind(this, this._onContinueButton),
default: true
}]
this.setButtons(buttons);
@ -83,7 +84,10 @@ const KeyringDialog = new Lang.Class({
if (this.prompt.password_visible) {
let label = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
label.set_text(_("Password:"));
table.add(label, { row: row, col: 0, x_expand: false, x_fill: true, x_align: St.Align.START });
table.add(label, { row: row, col: 0,
x_expand: false, x_fill: true,
x_align: St.Align.START,
y_fill: false, y_align: St.Align.MIDDLE });
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
text: '',
can_focus: true});
@ -99,7 +103,10 @@ const KeyringDialog = new Lang.Class({
if (this.prompt.confirm_visible) {
var label = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
label.set_text(_("Type again:"));
table.add(label, { row: row, col: 0, x_expand: false, x_fill: true, x_align: St.Align.START });
table.add(label, { row: row, col: 0,
x_expand: false, x_fill: true,
x_align: St.Align.START,
y_fill: false, y_align: St.Align.MIDDLE });
this._confirmEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
text: '',
can_focus: true});
@ -185,23 +192,36 @@ const KeyringDialog = new Lang.Class({
},
_onContinueButton: function() {
this.prompt.complete()
this.prompt.complete();
},
_onCancelButton: function() {
this.prompt.cancel()
this.prompt.cancel();
},
});
function init() {
prompter = new Gcr.SystemPrompter();
prompter.connect('new-prompt', function(prompter) {
let dialog = new KeyringDialog();
return dialog.prompt;
});
const KeyringPrompter = new Lang.Class({
Name: 'KeyringPrompter',
let connection = Gio.DBus.session;
prompter.register(connection);
Gio.bus_own_name_on_connection (connection, 'org.gnome.keyring.SystemPrompter',
Gio.BusNameOwnerFlags.REPLACE, null, null);
}
_init: function() {
this._prompter = new Gcr.SystemPrompter();
this._prompter.connect('new-prompt', function(prompter) {
let dialog = new KeyringDialog();
return dialog.prompt;
});
this._dbusId = null;
},
enable: function() {
this._prompter.register(Gio.DBus.session);
this._dbusId = Gio.DBus.session.own_name('org.gnome.keyring.SystemPrompter',
Gio.BusNameOwnerFlags.REPLACE, null, null);
},
disable: function() {
this._prompter.unregister(false);
Gio.DBus.session.unown_name(this._dbusId);
}
});
const Component = KeyringPrompter;

View File

@ -1,23 +1,4 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/*
* Copyright 2011 Giovanni Campagna <scampa.giovanni@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* 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.
*
*/
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
@ -135,7 +116,10 @@ const NetworkSecretDialog = new Lang.Class({
} else
secret.valid = true;
secretTable.add(label, { row: pos, col: 0, x_expand: false, x_fill: true, x_align: St.Align.START, y_align: St.Align.START });
secretTable.add(label, { row: pos, col: 0,
x_expand: false, x_fill: true,
x_align: St.Align.START,
y_fill: false, y_align: St.Align.MIDDLE });
secretTable.add(secret.entry, { row: pos, col: 1, x_expand: true, x_fill: true, y_align: St.Align.END });
pos++;
@ -147,7 +131,7 @@ const NetworkSecretDialog = new Lang.Class({
this._okButton = { label: _("Connect"),
action: Lang.bind(this, this._onOk),
key: Clutter.KEY_Return,
default: true
};
this.setButtons([{ label: _("Cancel"),
@ -166,10 +150,6 @@ const NetworkSecretDialog = new Lang.Class({
this._okButton.button.reactive = valid;
this._okButton.button.can_focus = valid;
if (valid)
this._okButton.button.remove_style_pseudo_class('disabled');
else
this._okButton.button.add_style_pseudo_class('disabled');
},
_onOk: function() {
@ -604,7 +584,7 @@ const NetworkAgent = new Lang.Class({
Name: 'NetworkAgent',
_init: function() {
this._native = new Shell.NetworkAgent({ auto_register: true,
this._native = new Shell.NetworkAgent({ auto_register: false,
identifier: 'org.gnome.Shell.NetworkAgent' });
this._dialogs = { };
@ -614,6 +594,14 @@ const NetworkAgent = new Lang.Class({
this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest));
},
enable: function() {
this._native.register();
},
disable: function() {
this._native.unregister();
},
_newRequest: function(agent, requestId, connection, settingName, hints, flags) {
if (settingName == 'vpn') {
this._vpnRequest(requestId, connection, hints, flags);
@ -683,7 +671,10 @@ const NetworkAgent = new Lang.Class({
try {
externalUIMode = keyfile.get_boolean('GNOME', 'supports-external-ui-mode');
} catch(e) { } // ignore errors if key does not exist
let path = GLib.build_filenamev([Config.LIBEXECDIR, binary]);
let path = binary;
if (!GLib.path_is_absolute(path)) {
path = GLib.build_filenamev([Config.LIBEXECDIR, path]);
}
if (GLib.file_test(path, GLib.FileTest.IS_EXECUTABLE))
this._vpnBinaries[service] = { fileName: path, externalUIMode: externalUIMode };
@ -700,3 +691,4 @@ const NetworkAgent = new Lang.Class({
}
}
});
const Component = NetworkAgent;

View File

@ -1,24 +1,4 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/*
* Copyright 2010 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* 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.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
const Lang = imports.lang;
const Signals = imports.signals;
@ -33,8 +13,12 @@ const Mainloop = imports.mainloop;
const Polkit = imports.gi.Polkit;
const PolkitAgent = imports.gi.PolkitAgent;
const Components = imports.ui.components;
const ModalDialog = imports.ui.modalDialog;
const ShellEntry = imports.ui.shellEntry;
const UserMenu = imports.ui.userMenu;
const DIALOG_ICON_SIZE = 48;
const AuthenticationDialog = new Lang.Class({
Name: 'AuthenticationDialog',
@ -117,9 +101,11 @@ const AuthenticationDialog = new Lang.Class({
let userBox = new St.BoxLayout({ style_class: 'polkit-dialog-user-layout',
vertical: false });
messageBox.add(userBox);
this._userIcon = new St.Icon();
this._userIcon.hide();
userBox.add(this._userIcon,
this._userAvatar = new UserMenu.UserAvatarWidget(this._user,
{ iconSize: DIALOG_ICON_SIZE,
styleClass: 'polkit-dialog-user-icon' });
this._userAvatar.actor.hide();
userBox.add(this._userAvatar.actor,
{ x_fill: true,
y_fill: false,
x_align: St.Align.END,
@ -135,17 +121,17 @@ const AuthenticationDialog = new Lang.Class({
this._onUserChanged();
this._passwordBox = new St.BoxLayout({ vertical: false });
this._passwordBox = new St.BoxLayout({ vertical: false, style_class: 'prompt-dialog-password-box' });
messageBox.add(this._passwordBox);
this._passwordLabel = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
this._passwordBox.add(this._passwordLabel);
this._passwordBox.add(this._passwordLabel, { y_fill: false, y_align: St.Align.MIDDLE });
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
text: "",
can_focus: true});
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivate));
this._passwordBox.add(this._passwordEntry,
{expand: true });
{ expand: true });
this.setInitialKeyFocus(this._passwordEntry);
this._passwordBox.hide();
@ -167,6 +153,7 @@ const AuthenticationDialog = new Lang.Class({
*/
this._nullMessageLabel = new St.Label({ style_class: 'prompt-dialog-null-label',
text: 'abc'});
this._nullMessageLabel.add_style_class_name('hidden');
this._nullMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._nullMessageLabel.clutter_text.line_wrap = true;
messageBox.add(this._nullMessageLabel);
@ -177,7 +164,8 @@ const AuthenticationDialog = new Lang.Class({
key: Clutter.Escape
},
{ label: _("Authenticate"),
action: Lang.bind(this, this._onAuthenticateButtonPressed)
action: Lang.bind(this, this._onAuthenticateButtonPressed),
default: true
}]);
this._doneEmitted = false;
@ -268,7 +256,7 @@ const AuthenticationDialog = new Lang.Class({
_onSessionRequest: function(session, request, echo_on) {
// Cheap localization trick
if (request == 'Password:')
if (request == 'Password:' || request == 'Password: ')
this._passwordLabel.set_text(_("Password:"));
else
this._passwordLabel.set_text(request);
@ -311,19 +299,9 @@ const AuthenticationDialog = new Lang.Class({
},
_onUserChanged: function() {
if (this._user.is_loaded) {
if (this._userIcon) {
let iconFileName = this._user.get_icon_file();
let iconFile = Gio.file_new_for_path(iconFileName);
let icon;
if (iconFile.query_exists(null)) {
icon = new Gio.FileIcon({file: iconFile});
} else {
icon = new Gio.ThemedIcon({name: 'avatar-default'});
}
this._userIcon.set_gicon (icon);
this._userIcon.show();
}
if (this._user.is_loaded && this._userAvatar) {
this._userAvatar.update();
this._userAvatar.actor.show();
}
},
@ -339,11 +317,20 @@ const AuthenticationAgent = new Lang.Class({
Name: 'AuthenticationAgent',
_init: function() {
this._currentDialog = null;
this._isCompleting = false;
this._handle = null;
this._native = new Shell.PolkitAuthenticationAgent();
this._native.connect('initiate', Lang.bind(this, this._onInitiate));
this._native.connect('cancel', Lang.bind(this, this._onCancel));
this._currentDialog = null;
this._isCompleting = false;
},
enable: function() {
this._native.register();
},
disable: function() {
this._native.unregister();
},
_onInitiate: function(nativeAgent, actionId, message, iconName, cookie, userNames) {
@ -401,6 +388,4 @@ const AuthenticationAgent = new Lang.Class({
}
});
function init() {
let agent = new AuthenticationAgent();
}
const Component = AuthenticationAgent;

View File

@ -0,0 +1,58 @@
const Lang = imports.lang;
const Gio = imports.gi.Gio;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Recorder = new Lang.Class({
Name: 'Recorder',
_init: function() {
this._recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
this._desktopLockdownSettings = new Gio.Settings({ schema: 'org.gnome.desktop.lockdown' });
this._bindingSettings = new Gio.Settings({ schema: 'org.gnome.shell.keybindings' });
this._recorder = null;
},
enable: function() {
global.display.add_keybinding('toggle-recording',
this._bindingSettings,
Meta.KeyBindingFlags.NONE, Lang.bind(this, this._toggleRecorder));
},
disable: function() {
global.display.remove_keybinding('toggle-recording');
},
_ensureRecorder: function() {
if (this._recorder == null)
this._recorder = new Shell.Recorder({ stage: global.stage });
return this._recorder;
},
_toggleRecorder: function() {
let recorder = this._ensureRecorder();
if (recorder.is_recording()) {
recorder.close();
Meta.enable_unredirect_for_screen(global.screen);
} else if (!this._desktopLockdownSettings.get_boolean('disable-save-to-disk')) {
// read the parameters from GSettings always in case they have changed
recorder.set_framerate(this._recorderSettings.get_int('framerate'));
/* Translators: this is a filename used for screencast recording */
// xgettext:no-c-format
recorder.set_filename(_("Screencast from %d %t") + '.' + this._recorderSettings.get_string('file-extension'));
let pipeline = this._recorderSettings.get_string('pipeline');
if (!pipeline.match(/^\s*$/))
recorder.set_pipeline(pipeline);
else
recorder.set_pipeline(null);
Meta.disable_unredirect_for_screen(global.screen);
recorder.record();
}
}
});
const Component = Recorder;

View File

@ -11,10 +11,10 @@ const Tpl = imports.gi.TelepathyLogger;
const Tp = imports.gi.TelepathyGLib;
const History = imports.misc.history;
const Params = imports.misc.params;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const Params = imports.misc.params;
const PopupMenu = imports.ui.popupMenu;
// See Notification.appendMessage
const SCROLLBACK_IMMEDIATE_TIME = 60; // 1 minute
@ -63,10 +63,10 @@ function makeMessageFromTplEvent(event) {
};
}
const Client = new Lang.Class({
Name: 'Client',
const TelepathyClient = new Lang.Class({
Name: 'TelepathyClient',
_init : function() {
_init: function() {
// channel path -> ChatSource
this._chatSources = {};
this._chatState = Tp.ChannelChatState.ACTIVE;
@ -89,9 +89,9 @@ const Client = new Lang.Class({
// channel matching its filters is detected.
// The second argument, recover, means _observeChannels will be run
// for any existing channel as well.
this._tpClient = new Shell.TpClient({ 'account-manager': this._accountManager,
'name': 'GnomeShell',
'uniquify-name': true })
this._tpClient = new Shell.TpClient({ name: 'GnomeShell',
account_manager: this._accountManager,
uniquify_name: true });
this._tpClient.set_observe_channels_func(
Lang.bind(this, this._observeChannels));
this._tpClient.set_approve_channels_func(
@ -99,6 +99,10 @@ const Client = new Lang.Class({
this._tpClient.set_handle_channels_func(
Lang.bind(this, this._handleChannels));
// Watch subscription requests and connection errors
this._subscriptionSource = null;
this._accountSource = null;
// Workaround for gjs not supporting GPtrArray in signals.
// See BGO bug #653941 for context.
this._tpClient.set_contact_list_changed_func(
@ -108,21 +112,26 @@ const Client = new Lang.Class({
// needed
this._tpClient.set_delegated_channels_callback(
Lang.bind(this, this._delegatedChannelsCb));
},
enable: function() {
try {
this._tpClient.register();
} catch (e) {
throw new Error('Couldn\'t register Telepathy client. Error: \n' + e);
}
// Watch subscription requests and connection errors
this._subscriptionSource = null;
this._accountSource = null;
this._accountManagerValidityChangedId = this._accountManager.connect('account-validity-changed',
Lang.bind(this, this._accountValidityChanged));
this._accountManager.connect('account-validity-changed',
Lang.bind(this, this._accountValidityChanged));
if (!this._accountManager.is_prepared(Tp.AccountManager.get_feature_quark_core()))
this._accountManager.prepare_async(null, Lang.bind(this, this._accountManagerPrepared));
},
this._accountManager.prepare_async(null, Lang.bind(this, this._accountManagerPrepared));
disable: function() {
this._tpClient.unregister();
this._accountManager.disconnect(this._accountManagerValidityChangedId);
this._accountManagerValidityChangedId = 0;
},
_observeChannels: function(observer, account, conn, channels,
@ -132,6 +141,9 @@ const Client = new Lang.Class({
let channel = channels[i];
let [targetHandle, targetHandleType] = channel.get_handle();
if (channel.get_invalidated())
continue;
/* Only observe contact text channels */
if ((!(channel instanceof Tp.TextChannel)) ||
targetHandleType != Tp.HandleType.CONTACT)
@ -181,6 +193,9 @@ const Client = new Lang.Class({
continue;
}
if (channel.get_invalidated())
continue;
// 'notify' will be true when coming from an actual HandleChannels
// call, and not when from a successful Claim call. The point is
// we don't want to notify for a channel we just claimed which
@ -205,13 +220,15 @@ const Client = new Lang.Class({
// We can only approve the rooms if we have been invited to it
let selfContact = channel.group_get_self_contact();
if (selfContact == null) {
Shell.decline_dispatch_op(context, 'Not invited to the room');
context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT,
message: 'Not invited to the room' }));
return;
}
let [invited, inviter, reason, msg] = channel.group_get_local_pending_contact_info(selfContact);
if (!invited) {
Shell.decline_dispatch_op(context, 'Not invited to the room');
context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT,
message: 'Not invited to the room' }));
return;
}
@ -231,12 +248,21 @@ const Client = new Lang.Class({
let channel = channels[0];
let chanType = channel.get_channel_type();
if (channel.get_invalidated()) {
context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT,
message: 'Channel is invalidated' }));
return;
}
if (chanType == Tp.IFACE_CHANNEL_TYPE_TEXT)
this._approveTextChannel(account, conn, channel, dispatchOp, context);
else if (chanType == Tp.IFACE_CHANNEL_TYPE_CALL)
this._approveCall(account, conn, channel, dispatchOp, context);
else if (chanType == Tp.IFACE_CHANNEL_TYPE_FILE_TRANSFER)
this._approveFileTransfer(account, conn, channel, dispatchOp, context);
else
context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT,
message: 'Unsupported channel type' }));
},
_approveTextChannel: function(account, conn, channel, dispatchOp, context) {
@ -365,8 +391,8 @@ const Client = new Lang.Class({
_ensureSubscriptionSource: function() {
if (this._subscriptionSource == null) {
this._subscriptionSource = new MultiNotificationSource(
_("Subscription request"), 'gtk-dialog-question');
this._subscriptionSource = new MessageTray.Source(_("Subscription request"),
'gtk-dialog-question');
Main.messageTray.add(this._subscriptionSource);
this._subscriptionSource.connect('destroy', Lang.bind(this, function () {
this._subscriptionSource = null;
@ -401,8 +427,8 @@ const Client = new Lang.Class({
_ensureAccountSource: function() {
if (this._accountSource == null) {
this._accountSource = new MultiNotificationSource(
_("Connection error"), 'gtk-dialog-error');
this._accountSource = new MessageTray.Source(_("Connection error"),
'gtk-dialog-error');
Main.messageTray.add(this._accountSource);
this._accountSource.connect('destroy', Lang.bind(this, function () {
this._accountSource = null;
@ -418,14 +444,13 @@ const ChatSource = new Lang.Class({
Extends: MessageTray.Source,
_init: function(account, conn, channel, contact, client) {
this.parent(contact.get_alias());
this.isChat = true;
this._account = account;
this._contact = contact;
this._client = client;
this.parent(contact.get_alias());
this.isChat = true;
this._pendingMessages = [];
this._conn = conn;
@ -446,8 +471,6 @@ const ChatSource = new Lang.Class({
this._receivedId = this._channel.connect('message-received', Lang.bind(this, this._messageReceived));
this._pendingId = this._channel.connect('pending-message-removed', Lang.bind(this, this._pendingRemoved));
this._setSummaryIcon(this.createNotificationIcon());
this._notifyAliasId = this._contact.connect('notify::alias', Lang.bind(this, this._updateAlias));
this._notifyAvatarId = this._contact.connect('notify::avatar-file', Lang.bind(this, this._updateAvatarIcon));
this._presenceChangedId = this._contact.connect('presence-changed', Lang.bind(this, this._presenceChanged));
@ -459,6 +482,22 @@ const ChatSource = new Lang.Class({
this._getLogMessages();
},
buildRightClickMenu: function() {
let item;
let rightClickMenu = this.parent();
item = new PopupMenu.PopupMenuItem('');
item.actor.connect('notify::mapped', Lang.bind(this, function() {
item.label.set_text(this.isMuted ? _("Unmute") : _("Mute"));
}));
item.connect('activate', Lang.bind(this, function() {
this.setMuted(!this.isMuted);
this.emit('done-displaying-content');
}));
rightClickMenu.add(item.actor);
return rightClickMenu;
},
_updateAlias: function() {
let oldAlias = this.title;
let newAlias = this._contact.get_alias();
@ -470,9 +509,9 @@ const ChatSource = new Lang.Class({
this._notification.appendAliasChange(oldAlias, newAlias);
},
createNotificationIcon: function() {
createIcon: function(size) {
this._iconBox = new St.Bin({ style_class: 'avatar-box' });
this._iconBox._size = this.ICON_SIZE;
this._iconBox._size = size;
let textureCache = St.TextureCache.get_default();
let file = this._contact.get_avatar_file();
@ -481,16 +520,45 @@ const ChatSource = new Lang.Class({
this._iconBox.child = textureCache.load_uri_async(uri, this._iconBox._size, this._iconBox._size);
} else {
this._iconBox.child = new St.Icon({ icon_name: 'avatar-default',
icon_type: St.IconType.FULLCOLOR,
icon_size: this._iconBox._size });
}
return this._iconBox;
},
createSecondaryIcon: function() {
let iconBox = new St.Bin();
iconBox.child = new St.Icon({ style_class: 'secondary-icon' });
let presenceType = this._contact.get_presence_type();
switch (presenceType) {
case Tp.ConnectionPresenceType.AVAILABLE:
iconBox.child.icon_name = 'user-available';
break;
case Tp.ConnectionPresenceType.BUSY:
iconBox.child.icon_name = 'user-busy';
break;
case Tp.ConnectionPresenceType.OFFLINE:
iconBox.child.icon_name = 'user-offline';
break;
case Tp.ConnectionPresenceType.HIDDEN:
iconBox.child.icon_name = 'user-invisible';
break;
case Tp.ConnectionPresenceType.AWAY:
iconBox.child.icon_name = 'user-away';
break;
case Tp.ConnectionPresenceType.EXTENDED_AWAY:
iconBox.child.icon_name = 'user-idle';
break;
default:
iconBox.child.icon_name = 'user-offline';
}
return iconBox;
},
_updateAvatarIcon: function() {
this._setSummaryIcon(this.createNotificationIcon());
this._notification.update(this._notification.title, null, { customContent: true, icon: this.createNotificationIcon() });
this.iconUpdated();
this._notification.update(this._notification.title, null, { customContent: true });
},
open: function(notification) {
@ -510,10 +578,10 @@ const ChatSource = new Lang.Class({
_getLogMessages: function() {
let logManager = Tpl.LogManager.dup_singleton();
let entity = Tpl.Entity.new_from_tp_contact(this._contact, Tpl.EntityType.CONTACT);
Shell.get_contact_events(logManager,
this._account, entity,
SCROLLBACK_HISTORY_LINES,
Lang.bind(this, this._displayPendingMessages));
logManager.get_filtered_events_async(this._account, entity,
Tpl.EventTypeMask.TEXT, SCROLLBACK_HISTORY_LINES,
null, Lang.bind(this, this._displayPendingMessages));
},
_displayPendingMessages: function(logManager, result) {
@ -535,7 +603,7 @@ const ChatSource = new Lang.Class({
this._pendingMessages.push(message);
}
this._updateCount();
this.countUpdated();
let showTimestamp = false;
@ -581,8 +649,17 @@ const ChatSource = new Lang.Class({
this.destroy();
},
_updateCount: function() {
this._setCount(this._pendingMessages.length, this._pendingMessages.length > 0);
/* All messages are new messages for Telepathy sources */
get count() {
return this._pendingMessages.length;
},
get unseenCount() {
return this.count;
},
get countVisible() {
return this.count > 0;
},
_messageReceived: function(channel, message) {
@ -590,7 +667,7 @@ const ChatSource = new Lang.Class({
return;
this._pendingMessages.push(message);
this._updateCount();
this.countUpdated();
message = makeMessageFromTpMessage(message, NotificationDirection.RECEIVED);
this._notification.appendMessage(message);
@ -652,38 +729,14 @@ const ChatSource = new Lang.Class({
},
_presenceChanged: function (contact, presence, status, message) {
let msg, shouldNotify, title;
if (this._presence == presence)
return;
let msg, title;
title = GLib.markup_escape_text(this.title, -1);
if (presence == Tp.ConnectionPresenceType.AVAILABLE) {
msg = _("%s is online.").format(title);
shouldNotify = (this._presence == Tp.ConnectionPresenceType.OFFLINE);
} else if (presence == Tp.ConnectionPresenceType.OFFLINE) {
presence = Tp.ConnectionPresenceType.OFFLINE;
msg = _("%s is offline.").format(title);
shouldNotify = (this._presence != Tp.ConnectionPresenceType.OFFLINE);
} else if (presence == Tp.ConnectionPresenceType.AWAY ||
presence == Tp.ConnectionPresenceType.EXTENDED_AWAY) {
msg = _("%s is away.").format(title);
shouldNotify = false;
} else if (presence == Tp.ConnectionPresenceType.BUSY) {
msg = _("%s is busy.").format(title);
shouldNotify = false;
} else
return;
this._presence = presence;
this._notification.update(this._notification.title, null, { customContent: true, secondaryIcon: this.createSecondaryIcon() });
if (message)
msg += ' <i>(' + GLib.markup_escape_text(message, -1) + ')</i>';
this._notification.appendPresence(msg, shouldNotify);
if (shouldNotify)
this.notify();
},
_pendingRemoved: function(channel, message) {
@ -691,10 +744,8 @@ const ChatSource = new Lang.Class({
if (idx >= 0) {
this._pendingMessages.splice(idx, 1);
this._updateCount();
this.countUpdated();
}
else
throw new Error('Message not in our pending list: ' + message);
},
_ackMessages: function() {
@ -710,7 +761,7 @@ const ChatNotification = new Lang.Class({
Extends: MessageTray.Notification,
_init: function(source) {
this.parent(source, source.title, null, { customContent: true });
this.parent(source, source.title, null, { customContent: true, secondaryIcon: source.createSecondaryIcon() });
this.setResident(true);
this._responseEntry = new St.Entry({ style_class: 'chat-response',
@ -719,6 +770,14 @@ const ChatNotification = new Lang.Class({
this._responseEntry.clutter_text.connect('text-changed', Lang.bind(this, this._onEntryChanged));
this.setActionArea(this._responseEntry);
this._responseEntry.clutter_text.connect('key-focus-in', Lang.bind(this, function() {
this.focused = true;
}));
this._responseEntry.clutter_text.connect('key-focus-out', Lang.bind(this, function() {
this.focused = false;
this.emit('unfocused');
}));
this._oldMaxScrollAdjustment = 0;
this._createScrollArea();
this._lastGroup = null;
@ -803,7 +862,7 @@ const ChatNotification = new Lang.Class({
let groups = this._contentArea.get_children();
for (let i = 0; i < groups.length; i++) {
let group = groups[i];
if (group.get_children().length == 0)
if (group.get_n_children() == 0)
group.destroy();
}
},
@ -920,19 +979,6 @@ const ChatNotification = new Lang.Class({
return false;
},
appendPresence: function(text, asTitle) {
if (asTitle)
this.update(text, null, { customContent: true, titleMarkup: true });
else
this.update(this.source.title, null, { customContent: true });
let label = this._append({ body: text,
group: 'meta',
styles: ['chat-meta-message'] });
this._filterMessages();
},
appendAliasChange: function(oldAlias, newAlias) {
oldAlias = GLib.markup_escape_text(oldAlias, -1);
newAlias = GLib.markup_escape_text(newAlias, -1);
@ -1002,10 +1048,9 @@ const ApproverSource = new Lang.Class({
Extends: MessageTray.Source,
_init: function(dispatchOp, text, gicon) {
this.parent(text);
this._gicon = gicon;
this._setSummaryIcon(this.createNotificationIcon());
this.parent(text);
this._dispatchOp = dispatchOp;
@ -1026,10 +1071,9 @@ const ApproverSource = new Lang.Class({
this.parent();
},
createNotificationIcon: function() {
createIcon: function(size) {
return new St.Icon({ gicon: this._gicon,
icon_type: St.IconType.FULLCOLOR,
icon_size: this.ICON_SIZE });
icon_size: size });
}
});
@ -1151,40 +1195,6 @@ const FileTransferNotification = new Lang.Class({
}
});
// A notification source that can embed multiple notifications
const MultiNotificationSource = new Lang.Class({
Name: 'MultiNotificationSource',
Extends: MessageTray.Source,
_init: function(title, icon) {
this.parent(title);
this._icon = icon;
this._setSummaryIcon(this.createNotificationIcon());
this._nbNotifications = 0;
},
notify: function(notification) {
this.parent(notification);
this._nbNotifications += 1;
// Display the source while there is at least one notification
notification.connect('destroy', Lang.bind(this, function () {
this._nbNotifications -= 1;
if (this._nbNotifications == 0)
this.destroy();
}));
},
createNotificationIcon: function() {
return new St.Icon({ gicon: Gio.icon_new_for_string(this._icon),
icon_type: St.IconType.FULLCOLOR,
icon_size: this.ICON_SIZE });
}
});
// Subscription request
const SubscriptionRequestNotification = new Lang.Class({
Name: 'SubscriptionRequestNotification',
@ -1214,7 +1224,6 @@ const SubscriptionRequestNotification = new Lang.Class({
}
else {
iconBox.child = new St.Icon({ icon_name: 'avatar-default',
icon_type: St.IconType.FULLCOLOR,
icon_size: iconBox._size });
}
@ -1359,15 +1368,14 @@ const AccountNotification = new Lang.Class({
case 'reconnect':
// If it fails again, a new notification should pop up with the
// new error.
account.reconnect_async(null, null);
account.reconnect_async(null);
break;
case 'edit':
let cmd = '/usr/bin/empathy-accounts'
+ ' --select-account=%s'
.format(account.get_path_suffix());
let app_info = Gio.app_info_create_from_commandline(cmd, null, 0,
null);
app_info.launch([], null, null);
let app_info = Gio.app_info_create_from_commandline(cmd, null, 0);
app_info.launch([], global.create_app_launch_context());
break;
}
this.destroy();
@ -1422,3 +1430,4 @@ const AccountNotification = new Lang.Class({
this.parent();
}
});
const Component = TelepathyClient;

View File

@ -1,196 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Folks = imports.gi.Folks
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Atk = imports.gi.Atk;
const Util = imports.misc.util;
const IconGrid = imports.ui.iconGrid;
const Search = imports.ui.search;
const SearchDisplay = imports.ui.searchDisplay;
const MAX_SEARCH_RESULTS_ROWS = 1;
const ICON_SIZE = 81;
function launchContact(id) {
Util.spawn(['gnome-contacts', '-i', id]);
}
/* This class represents a shown contact search result in the overview */
const Contact = new Lang.Class({
Name: 'Contact',
_init: function(id) {
this._contactSys = Shell.ContactSystem.get_default();
this.individual = this._contactSys.get_individual(id);
this.actor = new St.Bin({ style_class: 'contact',
reactive: true,
can_focus: true,
track_hover: true,
accessible_role: Atk.Role.PUSH_BUTTON });
let content = new St.BoxLayout( { style_class: 'contact-content',
vertical: false });
this.actor.set_child(content);
let icon = new St.Icon({ icon_type: St.IconType.FULLCOLOR,
icon_size: ICON_SIZE,
style_class: 'contact-icon' });
if (this.individual.avatar != null)
icon.gicon = this.individual.avatar;
else
icon.icon_name = 'avatar-default';
content.add(icon, { x_fill: true,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
let details = new St.BoxLayout({ style_class: 'contact-details',
vertical: true });
content.add(details, { x_fill: true,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
let email = this._contactSys.get_email_for_display(this.individual);
let aliasText = this.individual.alias ||
this.individual.full_name ||
this.individual.nickname ||
email ||
_("Unknown");
let aliasLabel = new St.Label({ text: aliasText,
style_class: 'contact-details-alias' });
details.add(aliasLabel, { x_fill: true,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
this.actor.label_actor = aliasLabel;
let presence = this._createPresence(this.individual.presence_type);
details.add(presence, { x_fill: false,
y_fill: true,
x_align: St.Align.START,
y_align: St.Align.END });
},
_createPresence: function(presence) {
let text;
let iconName;
switch(presence) {
case Folks.PresenceType.AVAILABLE:
text = _("Available");
iconName = 'user-available';
break;
case Folks.PresenceType.AWAY:
case Folks.PresenceType.EXTENDED_AWAY:
text = _("Away");
iconName = 'user-away';
break;
case Folks.PresenceType.BUSY:
text = _("Busy");
iconName = 'user-busy';
break;
case Folks.PresenceType.OFFLINE:
text = _("Offline");
iconName = 'user-offline';
break;
default:
text = '';
iconName = null;
}
let box = new St.BoxLayout({ vertical: false,
style_class: 'contact-details-status' });
if (iconName) {
let icon = new St.Icon({ icon_name: iconName,
icon_type: St.IconType.FULLCOLOR,
icon_size: 16,
style_class: 'contact-details-status-icon' });
box.add(icon, { x_fill: true,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
}
let label = new St.Label({ text: text });
box.add(label, { x_fill: true,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.START });
return box;
},
createIcon: function(size) {
let tc = St.TextureCache.get_default();
let icon = this.individual.avatar;
if (icon != null) {
return tc.load_gicon(null, icon, size);
} else {
return tc.load_icon_name(null, 'avatar-default', St.IconType.FULLCOLOR, size);
}
},
});
/* Searches for and returns contacts */
const ContactSearchProvider = new Lang.Class({
Name: 'ContactSearchProvider',
Extends: Search.SearchProvider,
_init: function() {
this.parent(_("CONTACTS"));
this._contactSys = Shell.ContactSystem.get_default();
},
getResultMetas: function(ids) {
let metas = [];
for (let i = 0; i < ids.length; i++) {
let contact = new Contact(ids[i]);
metas.push({ 'id': ids[i],
'name': contact.alias,
'createIcon': function(size) {
return contact.createIcon(size);
}
});
}
return metas;
},
getInitialResultSet: function(terms) {
return this._contactSys.initial_search(terms);
},
getSubsearchResultSet: function(previousResults, terms) {
return this._contactSys.subsearch(previousResults, terms);
},
createResultActor: function(resultMeta, terms) {
let contact = new Contact(resultMeta.id);
return contact.actor;
},
createResultContainerActor: function() {
let grid = new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
xAlign: St.Align.START });
grid.actor.style_class = 'contact-grid';
let actor = new SearchDisplay.GridSearchResults(this, grid);
return actor;
},
activateResult: function(id, params) {
launchContact(id);
}
});

View File

@ -27,7 +27,6 @@ const CtrlAltTabManager = new Lang.Class({
_init: function() {
this._items = [];
this._focusManager = St.FocusManager.get_for_stage(global.stage);
},
addGroup: function(root, name, icon, params) {
@ -41,11 +40,11 @@ const CtrlAltTabManager = new Lang.Class({
this._items.push(item);
root.connect('destroy', Lang.bind(this, function() { this.removeGroup(root); }));
this._focusManager.add_group(root);
global.focus_manager.add_group(root);
},
removeGroup: function(root) {
this._focusManager.remove_group(root);
global.focus_manager.remove_group(root);
for (let i = 0; i < this._items.length; i++) {
if (this._items[i].root == root) {
this._items.splice(i, 1);
@ -319,7 +318,6 @@ const CtrlAltTabSwitcher = new Lang.Class({
let icon = item.iconActor;
if (!icon) {
icon = new St.Icon({ icon_name: item.iconName,
icon_type: St.IconType.SYMBOLIC,
icon_size: POPUP_APPICON_SIZE });
}
box.add(icon, { x_fill: false, y_fill: false } );

View File

@ -124,16 +124,19 @@ const DashItemContainer = new Lang.Class({
},
setLabelText: function(text) {
if (this.label == null)
if (this.label == null) {
this.label = new St.Label({ style_class: 'dash-label'});
Main.layoutManager.addChrome(this.label);
this.label.hide();
}
this.label.set_text(text);
Main.layoutManager.addChrome(this.label);
this.label.hide();
},
hideLabel: function () {
this.label.opacity = 255;
if (this.label == null)
return;
Tweener.addTween(this.label,
{ opacity: 0,
time: DASH_ITEM_LABEL_HIDE_TIME,
@ -168,7 +171,17 @@ const DashItemContainer = new Lang.Class({
});
},
destroy: function() {
if (this.label)
this.label.destroy();
this.actor.destroy();
},
animateOutAndDestroy: function() {
if (this.label)
this.label.destroy();
if (this.child == null) {
this.actor.destroy();
return;
@ -217,36 +230,47 @@ const DashItemContainer = new Lang.Class({
}
});
const RemoveFavoriteIcon = new Lang.Class({
Name: 'RemoveFavoriteIcon',
const ShowAppsIcon = new Lang.Class({
Name: 'ShowAppsIcon',
Extends: DashItemContainer,
_init: function() {
this.parent();
this._iconBin = new St.Bin({ style_class: 'remove-favorite' });
this.toggleButton = new St.Button({ style_class: 'show-apps',
track_hover: true,
can_focus: true,
toggle_mode: true });
this._iconActor = null;
this.icon = new IconGrid.BaseIcon(_("Remove"),
this.icon = new IconGrid.BaseIcon(_("Show Applications"),
{ setSizeManually: true,
showLabel: false,
createIcon: Lang.bind(this, this._createIcon) });
this._iconBin.set_child(this.icon.actor);
this._iconBin._delegate = this;
this.toggleButton.add_actor(this.icon.actor);
this.toggleButton._delegate = this;
this.setChild(this._iconBin);
this.setChild(this.toggleButton);
this.setHover(false);
this.toggleButton.label_actor = this.label;
},
_createIcon: function(size) {
this._iconActor = new St.Icon({ icon_name: 'user-trash',
style_class: 'remove-favorite-icon',
icon_size: size });
this._iconActor = new St.Icon({ icon_name: 'view-grid-symbolic',
icon_size: size,
style_class: 'show-apps-icon',
track_hover: true });
return this._iconActor;
},
setHover: function(hovered) {
this._iconBin.set_hover(hovered);
this.toggleButton.set_hover(hovered);
if (this._iconActor)
this._iconActor.set_hover(hovered);
if (hovered)
this.setLabelText(_("Remove from Favorites"));
else
this.setLabelText(_("Show Applications"));
},
// Rely on the dragged item being a favorite
@ -286,6 +310,40 @@ const DragPlaceholderItem = new Lang.Class({
}
});
const DashActor = new Lang.Class({
Name: 'DashActor',
Extends: St.Widget,
_init: function() {
let layout = new Clutter.BoxLayout({ orientation: Clutter.Orientation.VERTICAL });
this.parent({ name: 'dash',
layout_manager: layout,
clip_to_allocation: true });
},
vfunc_allocate: function(box, flags) {
let contentBox = this.get_theme_node().get_content_box(box);
let availWidth = contentBox.x2 - contentBox.x1;
let availHeight = contentBox.y2 - contentBox.y1;
this.set_allocation(box, flags);
let [appIcons, showAppsButton] = this.get_children();
let [minHeight, natHeight] = showAppsButton.get_preferred_height(availWidth);
let childBox = new Clutter.ActorBox();
childBox.x1 = 0;
childBox.x2 = availWidth;
childBox.y1 = 0;
childBox.y2 = availHeight - natHeight;
appIcons.allocate(childBox, flags);
childBox.y1 = availHeight - natHeight;
childBox.y2 = availHeight;
showAppsButton.allocate(childBox, flags);
}
});
const Dash = new Lang.Class({
Name: 'Dash',
@ -297,17 +355,25 @@ const Dash = new Lang.Class({
this._dragPlaceholder = null;
this._dragPlaceholderPos = -1;
this._animatingPlaceholdersCount = 0;
this._favRemoveTarget = null;
this._showLabelTimeoutId = 0;
this._resetHoverTimeoutId = 0;
this._labelShowing = false;
this._box = new St.BoxLayout({ name: 'dash',
vertical: true,
this._container = new DashActor();
this._box = new St.BoxLayout({ vertical: true,
clip_to_allocation: true });
this._box._delegate = this;
this._container.add_actor(this._box);
this.actor = new St.Bin({ y_align: St.Align.START, child: this._box });
this._showAppsIcon = new ShowAppsIcon();
this._showAppsIcon.icon.setIconSize(this.iconSize);
this._hookUpLabel(this._showAppsIcon);
this.showAppsButton = this._showAppsIcon.toggleButton;
this._container.add_actor(this._showAppsIcon.actor);
this.actor = new St.Bin({ child: this._container });
this.actor.connect('notify::height', Lang.bind(this,
function() {
if (this._maxHeight != this.actor.height)
@ -360,14 +426,6 @@ const Dash = new Lang.Class({
_endDrag: function() {
this._clearDragPlaceholder();
if (this._favRemoveTarget) {
this._favRemoveTarget.animateOutAndDestroy();
this._favRemoveTarget.actor.connect('destroy', Lang.bind(this,
function() {
this._favRemoveTarget = null;
}));
this._adjustIconSize();
}
DND.removeDragMonitor(this._dragMonitor);
},
@ -386,28 +444,13 @@ const Dash = new Lang.Class({
let srcIsFavorite = (id in favorites);
if (srcIsFavorite &&
app.get_state() != Shell.AppState.RUNNING &&
dragEvent.source.actor &&
this.actor.contains (dragEvent.source.actor) &&
this._favRemoveTarget == null) {
this._favRemoveTarget = new RemoveFavoriteIcon();
this._favRemoveTarget.icon.setIconSize(this.iconSize);
this._box.add(this._favRemoveTarget.actor);
this._adjustIconSize();
this._favRemoveTarget.animateIn();
}
let showAppsHovered =
this._showAppsIcon.actor.contains(dragEvent.targetActor);
let favRemoveHovered = false;
if (this._favRemoveTarget)
favRemoveHovered =
this._favRemoveTarget.actor.contains(dragEvent.targetActor);
if (!this._box.contains(dragEvent.targetActor) || favRemoveHovered)
if (!this._box.contains(dragEvent.targetActor) || showAppsHovered)
this._clearDragPlaceholder();
if (this._favRemoveTarget)
this._favRemoveTarget.setHover(favRemoveHovered);
this._showAppsIcon.setHover(showAppsHovered);
return DND.DragMotionResult.CONTINUE;
},
@ -423,6 +466,17 @@ const Dash = new Lang.Class({
Main.queueDeferredWork(this._workId);
},
_hookUpLabel: function(item) {
item.child.connect('notify::hover', Lang.bind(this, function() {
this._onHover(item);
}));
Main.overview.connect('hiding', Lang.bind(this, function() {
this._labelShowing = false;
item.hideLabel();
}));
},
_createAppItem: function(app) {
let display = new AppDisplay.AppWellIcon(app,
{ setSizeManually: true,
@ -442,18 +496,14 @@ const Dash = new Lang.Class({
item.setLabelText(app.get_name());
// Override default AppWellIcon label_actor
display.actor.label_actor = item.label;
display.icon.setIconSize(this.iconSize);
display.actor.connect('notify::hover',
Lang.bind(this, function() {
this._onHover(item, display)
}));
this._hookUpLabel(item);
return item;
},
_onHover: function (item, display) {
if (display.actor.get_hover() && !display.isMenuUp) {
_onHover: function (item) {
if (item.child.get_hover() && !item.child._delegate.isMenuUp) {
if (this._showLabelTimeoutId == 0) {
let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT;
this._showLabelTimeoutId = Mainloop.timeout_add(timeout,
@ -464,7 +514,7 @@ const Dash = new Lang.Class({
}));
if (this._resetHoverTimeoutId > 0) {
Mainloop.source_remove(this._resetHoverTimeoutId);
this._resetHoverTimeoutId = 0;
this._resetHoverTimeoutId = 0;
}
}
} else {
@ -494,18 +544,12 @@ const Dash = new Lang.Class({
!actor._delegate.animatingOut;
});
if (iconChildren.length == 0) {
this._box.add_style_pseudo_class('empty');
return;
}
this._box.remove_style_pseudo_class('empty');
iconChildren.push(this._showAppsIcon.actor);
if (this._maxHeight == -1)
return;
let themeNode = this._box.get_theme_node();
let themeNode = this._container.get_theme_node();
let maxAllocation = new Clutter.ActorBox({ x1: 0, y1: 0,
x2: 42 /* whatever */,
y2: this._maxHeight });
@ -531,7 +575,6 @@ const Dash = new Lang.Class({
[minHeight, natHeight] = iconChildren[0].get_preferred_height(-1);
}
// Subtract icon padding and box spacing from the available height
availHeight -= iconChildren.length * (natHeight - this.iconSize) +
(iconChildren.length - 1) * spacing;
@ -691,7 +734,7 @@ const Dash = new Lang.Class({
if (Main.overview.visible)
item.animateOutAndDestroy();
else
item.actor.destroy();
item.destroy();
}
this._adjustIconSize();

View File

@ -2,6 +2,7 @@
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const GnomeDesktop = imports.gi.GnomeDesktop;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Cairo = imports.cairo;
@ -16,14 +17,6 @@ const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Calendar = imports.ui.calendar;
const UPowerGlib = imports.gi.UPowerGlib;
// in org.gnome.desktop.interface
const CLOCK_FORMAT_KEY = 'clock-format';
// in org.gnome.shell.clock
const CLOCK_SHOW_DATE_KEY = 'show-date';
const CLOCK_SHOW_SECONDS_KEY = 'show-seconds';
function _onVertSepRepaint (area)
{
@ -45,9 +38,7 @@ const DateMenuButton = new Lang.Class({
Name: 'DateMenuButton',
Extends: PanelMenu.Button,
_init: function(params) {
params = Params.parse(params, { showEvents: true });
_init: function() {
let item;
let hbox;
let vbox;
@ -62,8 +53,8 @@ const DateMenuButton = new Lang.Class({
// role ATK_ROLE_MENU like other elements of the panel.
this.actor.accessible_role = Atk.Role.LABEL;
this._clock = new St.Label();
this.actor.add_actor(this._clock);
this._clockDisplay = new St.Label();
this.actor.add_actor(this._clockDisplay);
hbox = new St.BoxLayout({name: 'calendarArea' });
this.menu.addActor(hbox);
@ -75,20 +66,12 @@ const DateMenuButton = new Lang.Class({
// Date
this._date = new St.Label();
this.actor.label_actor = this._date;
this.actor.label_actor = this._clockDisplay;
this._date.style_class = 'datemenu-date-label';
vbox.add(this._date);
if (params.showEvents) {
this._eventSource = new Calendar.DBusEventSource();
this._eventList = new Calendar.EventsList(this._eventSource);
} else {
this._eventSource = null;
this._eventList = null;
}
// Calendar
this._calendar = new Calendar.Calendar(this._eventSource);
this._eventList = new Calendar.EventsList();
this._calendar = new Calendar.Calendar();
this._calendar.connect('selected-date-changed',
Lang.bind(this, function(calendar, date) {
@ -110,27 +93,23 @@ const DateMenuButton = new Lang.Class({
item.actor.reparent(vbox);
}
if (params.showEvents) {
// Add vertical separator
this._separator = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
pseudo_class: 'highlighted' });
this._separator.connect('repaint', Lang.bind(this, _onVertSepRepaint));
hbox.add(this._separator);
item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
pseudo_class: 'highlighted' });
item.connect('repaint', Lang.bind(this, _onVertSepRepaint));
hbox.add(item);
// Fill up the second column
vbox = new St.BoxLayout({ name: 'calendarEventsArea',
vertical: true });
hbox.add(vbox, { expand: true });
// Fill up the second column
vbox = new St.BoxLayout({name: 'calendarEventsArea',
vertical: true});
hbox.add(vbox, { expand: true });
// Event list
vbox.add(this._eventList.actor, { expand: true });
// Event list
vbox.add(this._eventList.actor, { expand: true });
item = new PopupMenu.PopupMenuItem(_("Open Calendar"));
item.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
item.actor.can_focus = false;
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
}
this._openCalendarItem = new PopupMenu.PopupMenuItem(_("Open Calendar"));
this._openCalendarItem.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
this._openCalendarItem.actor.can_focus = false;
vbox.add(this._openCalendarItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
// Whenever the menu is opened, select today
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
@ -157,77 +136,55 @@ const DateMenuButton = new Lang.Class({
// Done with hbox for calendar and event list
// Track changes to clock settings
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
this._clockSettings = new Gio.Settings({ schema: 'org.gnome.shell.clock' });
this._desktopSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
this._clockSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
// https://bugzilla.gnome.org/show_bug.cgi?id=655129
this._upClient = new UPowerGlib.Client();
this._upClient.connect('notify-resume', Lang.bind(this, this._updateClockAndDate));
// Start the clock
this._clock = new GnomeDesktop.WallClock();
this._clock.connect('notify::clock', Lang.bind(this, this._updateClockAndDate));
this._updateClockAndDate();
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._sessionUpdated();
},
_setEventsVisibility: function(visible) {
this._separator.visible = visible;
this._eventList.visible = visible;
this._openCalendarItem.visible = visible;
},
_setEventSource: function(eventSource) {
this._calendar.setEventSource(eventSource);
this._eventList.setEventSource(eventSource);
},
_sessionUpdated: function() {
let eventSource;
let showEvents = Main.sessionMode.showCalendarEvents;
if (showEvents) {
eventSource = new Calendar.DBusEventSource();
} else {
eventSource = null;
}
this._setEventSource(eventSource);
this._setEventsVisibility(showEvents);
},
_updateClockAndDate: function() {
let format = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);
let showDate = this._clockSettings.get_boolean(CLOCK_SHOW_DATE_KEY);
let showSeconds = this._clockSettings.get_boolean(CLOCK_SHOW_SECONDS_KEY);
let clockFormat;
let dateFormat;
switch (format) {
case '24h':
if (showDate)
/* Translators: This is the time format with date used
in 24-hour mode. */
clockFormat = showSeconds ? _("%a %b %e, %R:%S")
: _("%a %b %e, %R");
else
/* Translators: This is the time format without date used
in 24-hour mode. */
clockFormat = showSeconds ? _("%a %R:%S")
: _("%a %R");
break;
case '12h':
default:
if (showDate)
/* Translators: This is a time format with date used
for AM/PM. */
clockFormat = showSeconds ? _("%a %b %e, %l:%M:%S %p")
: _("%a %b %e, %l:%M %p");
else
/* Translators: This is a time format without date used
for AM/PM. */
clockFormat = showSeconds ? _("%a %l:%M:%S %p")
: _("%a %l:%M %p");
break;
}
let displayDate = new Date();
this._clock.set_text(displayDate.toLocaleFormat(clockFormat));
this._clockDisplay.set_text(this._clock.clock);
/* Translators: This is the date format to use when the calendar popup is
* shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
*/
dateFormat = _("%A %B %e, %Y");
let dateFormat = _("%A %B %e, %Y");
let displayDate = new Date();
this._date.set_text(displayDate.toLocaleFormat(dateFormat));
Mainloop.timeout_add_seconds(1, Lang.bind(this, this._updateClockAndDate));
return false;
},
_onOpenCalendarActivate: function() {
this.menu.close();
let calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' });
let tool = calendarSettings.get_string('exec');
if (tool.length == 0 || tool == 'evolution') {
if (tool.length == 0 || tool.substr(0, 9) == 'evolution') {
// TODO: pass the selected day
Util.spawn(['evolution', '-c', 'calendar']);
let app = Shell.AppSystem.get_default().lookup_app('evolution-calendar.desktop');
app.activate();
} else {
let needTerm = calendarSettings.get_boolean('needs-term');
if (needTerm) {

View File

@ -31,10 +31,10 @@ const St = imports.gi.St;
const Shell = imports.gi.Shell;
const GnomeSession = imports.misc.gnomeSession;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog;
const Tweener = imports.ui.tweener;
const UserMenu = imports.ui.userMenu;
let _endSessionDialog = null;
@ -90,7 +90,7 @@ const shutdownDialogContent = {
label: C_("button", "Restart") },
{ signal: 'ConfirmedShutdown',
label: C_("button", "Power Off") }],
iconName: 'system-shutdown',
iconName: 'system-shutdown-symbolic',
iconStyleClass: 'end-session-dialog-shutdown-icon'
};
@ -105,7 +105,7 @@ const restartDialogContent = {
endDescription: _("Restarting the system."),
confirmButtons: [{ signal: 'ConfirmedReboot',
label: C_("button", "Restart") }],
iconName: 'system-shutdown',
iconName: 'system-shutdown-symbolic',
iconStyleClass: 'end-session-dialog-shutdown-icon'
};
@ -161,6 +161,7 @@ const ListItem = new Lang.Class({
this._descriptionLabel = new St.Label({ text: this._reason,
style_class: 'end-session-dialog-app-list-item-description' });
this.actor.label_actor = this._nameLabel;
textLayout.add(this._descriptionLabel,
{ expand: true,
x_fill: true });
@ -280,21 +281,17 @@ const EndSessionDialog = new Lang.Class({
scrollView.hide();
this._applicationList = new St.BoxLayout({ vertical: true });
scrollView.add_actor(this._applicationList,
{ x_fill: true,
y_fill: true,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
scrollView.add_actor(this._applicationList);
this._applicationList.connect('actor-added',
Lang.bind(this, function() {
if (this._applicationList.get_children().length == 1)
if (this._applicationList.get_n_children() == 1)
scrollView.show();
}));
this._applicationList.connect('actor-removed',
Lang.bind(this, function() {
if (this._applicationList.get_children().length == 0)
if (this._applicationList.get_n_children() == 0)
scrollView.hide();
}));
@ -307,42 +304,7 @@ const EndSessionDialog = new Lang.Class({
this._user.disconnect(this._userChangedId);
},
_setIconFromFile: function(iconFile, styleClass) {
if (styleClass)
this._iconBin.set_style_class_name(styleClass);
this._iconBin.set_style(null);
this._iconBin.child = null;
if (iconFile) {
this._iconBin.show();
this._iconBin.set_style('background-image: url("' + iconFile + '");' +
'background-size: contain;');
} else {
this._iconBin.hide();
}
},
_setIconFromName: function(iconName, styleClass) {
if (styleClass)
this._iconBin.set_style_class_name(styleClass);
this._iconBin.set_style(null);
if (iconName != null) {
let textureCache = St.TextureCache.get_default();
let icon = textureCache.load_icon_name(this._iconBin.get_theme_node(),
iconName,
St.IconType.SYMBOLIC,
_DIALOG_ICON_SIZE);
this._iconBin.child = icon;
this._iconBin.show();
} else {
this._iconBin.child = null;
this._iconBin.hide();
}
},
_updateContent: function() {
_updateDescription: function() {
if (this.state != ModalDialog.State.OPENING &&
this.state != ModalDialog.State.OPENED)
return;
@ -352,17 +314,6 @@ const EndSessionDialog = new Lang.Class({
let subject = dialogContent.subject;
let description;
if (this._user.is_loaded && !dialogContent.iconName) {
let iconFile = this._user.get_icon_file();
if (GLib.file_test(iconFile, GLib.FileTest.EXISTS))
this._setIconFromFile(iconFile, dialogContent.iconStyleClass);
else
this._setIconFromName('avatar-default', dialogContent.iconStyleClass);
} else if (dialogContent.iconName) {
this._setIconFromName(dialogContent.iconName,
dialogContent.iconStyleClass);
}
if (this._inhibitors.length > 0) {
this._stopTimer();
description = dialogContent.inhibitedDescription;
@ -395,6 +346,27 @@ const EndSessionDialog = new Lang.Class({
_setLabelText(this._descriptionLabel, description);
},
_updateContent: function() {
if (this.state != ModalDialog.State.OPENING &&
this.state != ModalDialog.State.OPENED)
return;
let dialogContent = DialogContent[this._type];
if (dialogContent.iconName) {
this._iconBin.child = new St.Icon({ icon_name: dialogContent.iconName,
icon_size: _DIALOG_ICON_SIZE,
style_class: dialogContent.iconStyleClass });
} else {
let avatarWidget = new UserMenu.UserAvatarWidget(this._user,
{ iconSize: _DIALOG_ICON_SIZE,
styleClass: dialogContent.iconStyleClass });
this._iconBin.child = avatarWidget.actor;
avatarWidget.update();
}
this._updateDescription();
},
_updateButtons: function() {
let dialogContent = DialogContent[this._type];
let buttons = [{ action: Lang.bind(this, this.cancel),
@ -441,7 +413,7 @@ const EndSessionDialog = new Lang.Class({
{ _secondsLeft: 0,
time: this._secondsLeft,
transition: 'linear',
onUpdate: Lang.bind(this, this._updateContent),
onUpdate: Lang.bind(this, this._updateDescription),
onComplete: Lang.bind(this, function() {
let dialogContent = DialogContent[this._type];
let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];

View File

@ -39,11 +39,19 @@ function _patchContainerClass(containerClass) {
};
}
function _makeLoggingFunc(func) {
return function() {
return func([].join.call(arguments, ', '));
};
}
function init() {
// Add some bindings to the global JS namespace; (gjs keeps the web
// browser convention of having that namespace be called 'window'.)
window.global = Shell.Global.get();
window.log = _makeLoggingFunc(window.log);
window._ = Gettext.gettext;
window.C_ = Gettext.pgettext;
window.ngettext = Gettext.ngettext;
@ -82,7 +90,7 @@ function init() {
}
// OK, now things are initialized enough that we can import shell JS
const Format = imports.misc.format;
const Format = imports.format;
const Tweener = imports.ui.tweener;
Tweener.init();

View File

@ -0,0 +1,270 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Soup = imports.gi.Soup;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Config = imports.misc.config;
const ExtensionUtils = imports.misc.extensionUtils;
const ExtensionSystem = imports.ui.extensionSystem;
const FileUtils = imports.misc.fileUtils;
const ModalDialog = imports.ui.modalDialog;
const _signals = ExtensionSystem._signals;
const REPOSITORY_URL_BASE = 'https://extensions.gnome.org';
const REPOSITORY_URL_DOWNLOAD = REPOSITORY_URL_BASE + '/download-extension/%s.shell-extension.zip';
const REPOSITORY_URL_INFO = REPOSITORY_URL_BASE + '/extension-info/';
const REPOSITORY_URL_UPDATE = REPOSITORY_URL_BASE + '/update-info/';
let _httpSession;
function installExtension(uuid, invocation) {
let params = { uuid: uuid,
shell_version: Config.PACKAGE_VERSION };
let message = Soup.form_request_new_from_hash('GET', REPOSITORY_URL_INFO, params);
_httpSession.queue_message(message, function(session, message) {
if (message.status_code != Soup.KnownStatusCode.OK) {
ExtensionSystem.logExtensionError(uuid, 'downloading info: ' + message.status_code);
invocation.return_dbus_error('org.gnome.Shell.DownloadInfoError', message.status_code.toString());
return;
}
let info;
try {
info = JSON.parse(message.response_body.data);
} catch (e) {
ExtensionSystem.logExtensionError(uuid, 'parsing info: ' + e);
invocation.return_dbus_error('org.gnome.Shell.ParseInfoError', e.toString());
return;
}
let dialog = new InstallExtensionDialog(uuid, info, invocation);
dialog.open(global.get_current_time());
});
}
function uninstallExtension(uuid) {
let extension = ExtensionUtils.extensions[uuid];
if (!extension)
return false;
// Don't try to uninstall system extensions
if (extension.type != ExtensionUtils.ExtensionType.PER_USER)
return false;
if (!ExtensionSystem.unloadExtension(extension))
return false;
FileUtils.recursivelyDeleteDir(extension.dir, true);
return true;
}
function gotExtensionZipFile(session, message, uuid, dir, callback, errback) {
if (message.status_code != Soup.KnownStatusCode.OK) {
errback('DownloadExtensionError', message.status_code);
return;
}
try {
if (!dir.query_exists(null))
dir.make_directory_with_parents(null);
} catch (e) {
errback('CreateExtensionDirectoryError', e);
return;
}
let [file, stream] = Gio.File.new_tmp('XXXXXX.shell-extension.zip');
let contents = message.response_body.flatten().get_as_bytes();
stream.output_stream.write_bytes(contents, null);
stream.close(null);
let [success, pid] = GLib.spawn_async(null,
['unzip', '-uod', dir.get_path(), '--', file.get_path()],
null,
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
null);
if (!success) {
errback('ExtractExtensionError');
return;
}
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function(pid, status) {
GLib.spawn_close_pid(pid);
if (status != 0)
errback('ExtractExtensionError');
else
callback();
});
}
function updateExtension(uuid) {
// This gets a bit tricky. We want the update to be seamless -
// if we have any error during downloading or extracting, we
// want to not unload the current version.
let oldExtensionTmpDir = GLib.Dir.make_tmp('XXXXXX-shell-extension');
let newExtensionTmpDir = GLib.Dir.make_tmp('XXXXXX-shell-extension');
let params = { shell_version: Config.PACKAGE_VERSION };
let url = REPOSITORY_URL_DOWNLOAD.format(uuid);
let message = Soup.form_request_new_from_hash('GET', url, params);
_httpSession.queue_message(message, Lang.bind(this, function(session, message) {
gotExtensionZipFile(session, message, uuid, newExtensionTmpDir, function() {
let oldExtension = ExtensionUtils.extensions[uuid];
let extensionDir = oldExtension.dir;
if (!ExtensionSystem.unloadExtension(oldExtension))
return;
FileUtils.recursivelyMoveDir(extensionDir, oldExtensionTmpDir);
FileUtils.recursivelyMoveDir(newExtensionTmpDir, extensionDir);
let extension = ExtensionUtils.createExtensionObject(uuid, extensionDir, ExtensionUtils.ExtensionType.PER_USER);
try {
ExtensionSystem.loadExtension(extension);
} catch(e) {
ExtensionSystem.unloadExtension(extension);
logError(e, 'Error loading extension %s'.format(uuid));
FileUtils.recursivelyDeleteDir(extensionDir, false);
FileUtils.recursivelyMoveDir(oldExtensionTmpDir, extensionDir);
// Restore what was there before. We can't do much if we
// fail here.
ExtensionSystem.loadExtension(oldExtension);
return;
}
FileUtils.recursivelyDeleteDir(oldExtensionTmpDir, true);
}, function(code, message) {
log('Error while updating extension %s: %s (%s)'.format(uuid, code, message ? message : ''));
});
}));
}
function checkForUpdates() {
let metadatas = {};
for (let uuid in ExtensionUtils.extensions) {
metadatas[uuid] = ExtensionUtils.extensions[uuid].metadata;
}
let params = { shell_version: Config.PACKAGE_VERSION,
installed: JSON.stringify(metadatas) };
let url = REPOSITORY_URL_UPDATE;
let message = Soup.form_request_new_from_hash('GET', url, params);
_httpSession.queue_message(message, function(session, message) {
if (message.status_code != Soup.KnownStatusCode.OK)
return;
let operations = JSON.parse(message.response_body.data);
for (let uuid in operations) {
let operation = operations[uuid];
if (operation == 'blacklist')
uninstallExtension(uuid);
else if (operation == 'upgrade' || operation == 'downgrade')
updateExtension(uuid);
}
});
}
const InstallExtensionDialog = new Lang.Class({
Name: 'InstallExtensionDialog',
Extends: ModalDialog.ModalDialog,
_init: function(uuid, info, invocation) {
this.parent({ styleClass: 'extension-dialog' });
this._uuid = uuid;
this._info = info;
this._invocation = invocation;
this.setButtons([{ label: _("Cancel"),
action: Lang.bind(this, this._onCancelButtonPressed),
key: Clutter.Escape
},
{ label: _("Install"),
action: Lang.bind(this, this._onInstallButtonPressed),
default: true
}]);
let message = _("Download and install '%s' from extensions.gnome.org?").format(info.name);
let box = new St.BoxLayout();
this.contentLayout.add(box);
let gicon = new Gio.FileIcon({ file: Gio.File.new_for_uri(REPOSITORY_URL_BASE + info.icon) })
let icon = new St.Icon({ gicon: gicon });
box.add(icon);
let label = new St.Label({ text: message });
box.add(label);
},
_onCancelButtonPressed: function(button, event) {
this.close(global.get_current_time());
this._invocation.return_value(GLib.Variant.new('(s)', ['cancelled']));
},
_onInstallButtonPressed: function(button, event) {
let params = { shell_version: Config.PACKAGE_VERSION };
let url = REPOSITORY_URL_DOWNLOAD.format(this._uuid);
let message = Soup.form_request_new_from_hash('GET', url, params);
let uuid = this._uuid;
let dir = Gio.File.new_for_path(GLib.build_filenamev([global.userdatadir, 'extensions', uuid]));
let invocation = this._invocation;
function errback(code, message) {
invocation.return_dbus_error('org.gnome.Shell.' + code, message ? message.toString() : '');
}
function callback() {
// Add extension to 'enabled-extensions' for the user, always...
let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY);
if (enabledExtensions.indexOf(uuid) == -1) {
enabledExtensions.push(uuid);
global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
}
let extension = ExtensionUtils.createExtensionObject(uuid, dir, ExtensionUtils.ExtensionType.PER_USER);
try {
ExtensionSystem.loadExtension(extension);
} catch(e) {
uninstallExtension(uuid);
errback('LoadExtensionError', e);
return;
}
invocation.return_value(GLib.Variant.new('(s)', 'successful'));
}
_httpSession.queue_message(message, Lang.bind(this, function(session, message) {
gotExtensionZipFile(session, message, uuid, dir, callback, errback);
}));
this.close(global.get_current_time());
}
});
function init() {
_httpSession = new Soup.SessionAsync({ ssl_use_system_ca_file: true });
// See: https://bugzilla.gnome.org/show_bug.cgi?id=655189 for context.
// _httpSession.add_feature(new Soup.ProxyResolverDefault());
Soup.Session.prototype.add_feature.call(_httpSession, new Soup.ProxyResolverDefault());
}

View File

@ -3,19 +3,12 @@
const Lang = imports.lang;
const Signals = imports.signals;
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Soup = imports.gi.Soup;
const Config = imports.misc.config;
const ExtensionUtils = imports.misc.extensionUtils;
const FileUtils = imports.misc.fileUtils;
const ModalDialog = imports.ui.modalDialog;
const API_VERSION = 1;
const Main = imports.ui.main;
const ExtensionState = {
ENABLED: 1,
@ -30,29 +23,6 @@ const ExtensionState = {
UNINSTALLED: 99
};
const REPOSITORY_URL_BASE = 'https://extensions.gnome.org';
const REPOSITORY_URL_DOWNLOAD = REPOSITORY_URL_BASE + '/download-extension/%s.shell-extension.zip';
const REPOSITORY_URL_INFO = REPOSITORY_URL_BASE + '/extension-info/';
const _httpSession = new Soup.SessionAsync();
// The unfortunate state of gjs, gobject-introspection and libsoup
// means that I have to do a hack to add a feature.
// See: https://bugzilla.gnome.org/show_bug.cgi?id=655189 for context.
if (Soup.Session.prototype.add_feature != null)
Soup.Session.prototype.add_feature.call(_httpSession, new Soup.ProxyResolverDefault());
function _getCertFile() {
let localCert = GLib.build_filenamev([global.userdatadir, 'extensions.gnome.org.crt']);
if (GLib.file_test(localCert, GLib.FileTest.EXISTS))
return localCert;
else
return Config.SHELL_SYSTEM_CA_FILE;
}
_httpSession.ssl_ca_file = _getCertFile();
// Arrays of uuids
var enabledExtensions;
// Contains the order that extensions were enabled in.
@ -69,89 +39,8 @@ const disconnect = Lang.bind(_signals, _signals.disconnect);
const ENABLED_EXTENSIONS_KEY = 'enabled-extensions';
function installExtensionFromUUID(uuid, version_tag) {
let params = { uuid: uuid,
version_tag: version_tag,
shell_version: Config.PACKAGE_VERSION,
api_version: API_VERSION.toString() };
let message = Soup.form_request_new_from_hash('GET', REPOSITORY_URL_INFO, params);
_httpSession.queue_message(message,
function(session, message) {
let info = JSON.parse(message.response_body.data);
let dialog = new InstallExtensionDialog(uuid, version_tag, info.name);
dialog.open(global.get_current_time());
});
}
function uninstallExtensionFromUUID(uuid) {
let extension = ExtensionUtils.extensions[uuid];
if (!extension)
return false;
// Try to disable it -- if it's ERROR'd, we can't guarantee that,
// but it will be removed on next reboot, and hopefully nothing
// broke too much.
disableExtension(uuid);
// Don't try to uninstall system extensions
if (extension.type != ExtensionUtils.ExtensionType.PER_USER)
return false;
extension.state = ExtensionState.UNINSTALLED;
_signals.emit('extension-state-changed', extension);
delete ExtensionUtils.extensions[uuid];
FileUtils.recursivelyDeleteDir(Gio.file_new_for_path(extension.path));
return true;
}
function gotExtensionZipFile(session, message, uuid) {
if (message.status_code != Soup.KnownStatusCode.OK) {
logExtensionError(uuid, 'downloading extension: ' + message.status_code);
return;
}
// FIXME: use a GFile mkstemp-type method once one exists
let fd, tmpzip;
try {
[fd, tmpzip] = GLib.file_open_tmp('XXXXXX.shell-extension.zip');
} catch (e) {
logExtensionError(uuid, 'tempfile: ' + e.toString());
return;
}
let stream = new Gio.UnixOutputStream({ fd: fd });
let dir = ExtensionUtils.userExtensionsDir.get_child(uuid);
Shell.write_soup_message_to_stream(stream, message);
stream.close(null);
let [success, pid] = GLib.spawn_async(null,
['unzip', '-uod', dir.get_path(), '--', tmpzip],
null,
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
null);
if (!success) {
logExtensionError(uuid, 'extract: could not extract');
return;
}
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function(pid, status) {
GLib.spawn_close_pid(pid);
// Add extension to 'enabled-extensions' for the user, always...
let enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
if (enabledExtensions.indexOf(uuid) == -1) {
enabledExtensions.push(uuid);
global.settings.set_strv(ENABLED_EXTENSIONS_KEY, enabledExtensions);
}
loadExtension(dir, ExtensionUtils.ExtensionType.PER_USER, true);
});
}
var initted = false;
var enabled;
function disableExtension(uuid) {
let extension = ExtensionUtils.extensions[uuid];
@ -178,23 +67,23 @@ function disableExtension(uuid) {
try {
ExtensionUtils.extensions[uuid].stateObj.disable();
} catch(e) {
logExtensionError(uuid, e.toString());
logExtensionError(uuid, e);
}
}
try {
extension.stateObj.disable();
} catch(e) {
logExtensionError(uuid, e.toString());
return;
if (extension.stylesheet) {
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
theme.unload_stylesheet(extension.stylesheet.get_path());
}
extension.stateObj.disable();
for (let i = 0; i < order.length; i++) {
let uuid = order[i];
try {
ExtensionUtils.extensions[uuid].stateObj.enable();
} catch(e) {
logExtensionError(uuid, e.toString());
logExtensionError(uuid, e);
}
}
@ -217,68 +106,86 @@ function enableExtension(uuid) {
extensionOrder.push(uuid);
try {
extension.stateObj.enable();
} catch(e) {
logExtensionError(uuid, e.toString());
return;
let stylesheetFile = extension.dir.get_child('stylesheet.css');
if (stylesheetFile.query_exists(null)) {
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
theme.load_stylesheet(stylesheetFile.get_path());
extension.stylesheet = stylesheetFile;
}
extension.stateObj.enable();
extension.state = ExtensionState.ENABLED;
_signals.emit('extension-state-changed', extension);
}
function logExtensionError(uuid, message, state) {
function logExtensionError(uuid, error) {
let extension = ExtensionUtils.extensions[uuid];
if (!extension)
return;
let message = '' + error;
if (error.state)
extension.state = error.state;
else
extension.state = ExtensionState.ERROR;
if (!extension.errors)
extension.errors = [];
extension.errors.push(message);
global.logError('Extension "%s" had error: %s'.format(uuid, message));
state = state || ExtensionState.ERROR;
log('Extension "%s" had error: %s'.format(uuid, message));
_signals.emit('extension-state-changed', { uuid: uuid,
error: message,
state: state });
state: extension.state });
}
function loadExtension(dir, type, enabled) {
let uuid = dir.get_basename();
let extension;
if (ExtensionUtils.extensions[uuid] != undefined) {
global.logError('Extension "%s" is already loaded'.format(uuid));
return;
}
try {
extension = ExtensionUtils.createExtensionObject(uuid, dir, type);
} catch(e) {
logExtensionError(uuid, e.message);
return;
}
function loadExtension(extension) {
// Default to error, we set success as the last step
extension.state = ExtensionState.ERROR;
if (ExtensionUtils.isOutOfDate(extension)) {
logExtensionError(uuid, 'extension is not compatible with current GNOME Shell and/or GJS version', ExtensionState.OUT_OF_DATE);
extension.state = ExtensionState.OUT_OF_DATE;
return;
let error = new Error('extension is not compatible with current GNOME Shell and/or GJS version');
error.state = ExtensionState.OUT_OF_DATE;
throw error;
}
let enabled = enabledExtensions.indexOf(extension.uuid) != -1;
if (enabled) {
initExtension(uuid);
initExtension(extension.uuid);
if (extension.state == ExtensionState.DISABLED)
enableExtension(uuid);
enableExtension(extension.uuid);
} else {
extension.state = ExtensionState.INITIALIZED;
}
_signals.emit('extension-state-changed', extension);
global.log('Loaded extension ' + uuid);
}
function unloadExtension(extension) {
// Try to disable it -- if it's ERROR'd, we can't guarantee that,
// but it will be removed on next reboot, and hopefully nothing
// broke too much.
disableExtension(extension.uuid);
extension.state = ExtensionState.UNINSTALLED;
_signals.emit('extension-state-changed', extension);
delete ExtensionUtils.extensions[extension.uuid];
return true;
}
function reloadExtension(oldExtension) {
// Grab the things we'll need to pass to createExtensionObject
// to reload it.
let { uuid: uuid, dir: dir, type: type } = oldExtension;
// Then unload the old extension.
unloadExtension(oldExtension);
// Now, recreate the extension and load it.
let newExtension = ExtensionUtils.createExtensionObject(uuid, dir, type);
loadExtension(newExtension);
}
function initExtension(uuid) {
@ -289,76 +196,43 @@ function initExtension(uuid) {
throw new Error("Extension was not properly created. Call loadExtension first");
let extensionJs = dir.get_child('extension.js');
if (!extensionJs.query_exists(null)) {
logExtensionError(uuid, 'Missing extension.js');
return;
}
let stylesheetPath = null;
let themeContext = St.ThemeContext.get_for_stage(global.stage);
let theme = themeContext.get_theme();
let stylesheetFile = dir.get_child('stylesheet.css');
if (stylesheetFile.query_exists(null)) {
try {
theme.load_stylesheet(stylesheetFile.get_path());
} catch (e) {
logExtensionError(uuid, 'Stylesheet parse error: ' + e);
return;
}
}
if (!extensionJs.query_exists(null))
throw new Error('Missing extension.js');
let extensionModule;
let extensionState = null;
try {
ExtensionUtils.installImporter(extension);
extensionModule = extension.imports.extension;
} catch (e) {
if (stylesheetPath != null)
theme.unload_stylesheet(stylesheetPath);
logExtensionError(uuid, '' + e);
return;
}
if (!extensionModule.init) {
logExtensionError(uuid, 'missing \'init\' function');
return;
}
ExtensionUtils.installImporter(extension);
extensionModule = extension.imports.extension;
try {
if (extensionModule.init) {
extensionState = extensionModule.init(extension);
} catch (e) {
if (stylesheetPath != null)
theme.unload_stylesheet(stylesheetPath);
logExtensionError(uuid, 'Failed to evaluate init function:' + e);
return;
}
if (!extensionState)
extensionState = extensionModule;
extension.stateObj = extensionState;
if (!extensionState.enable) {
logExtensionError(uuid, 'missing \'enable\' function');
return;
}
if (!extensionState.disable) {
logExtensionError(uuid, 'missing \'disable\' function');
return;
}
extension.state = ExtensionState.DISABLED;
_signals.emit('extension-loaded', uuid);
}
function onEnabledExtensionsChanged() {
let newEnabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
if (!enabled)
return;
// Find and enable all the newly enabled extensions: UUIDs found in the
// new setting, but not in the old one.
newEnabledExtensions.filter(function(uuid) {
return enabledExtensions.indexOf(uuid) == -1;
}).forEach(function(uuid) {
enableExtension(uuid);
try {
enableExtension(uuid);
} catch(e) {
logExtensionError(uuid, e);
}
});
// Find and disable all the newly disabled extensions: UUIDs found in the
@ -366,87 +240,67 @@ function onEnabledExtensionsChanged() {
enabledExtensions.filter(function(item) {
return newEnabledExtensions.indexOf(item) == -1;
}).forEach(function(uuid) {
disableExtension(uuid);
try {
disableExtension(uuid);
} catch(e) {
logExtensionError(uuid, e);
}
});
enabledExtensions = newEnabledExtensions;
}
function init() {
ExtensionUtils.init();
function _loadExtensions() {
global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged);
enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
}
function loadExtensions() {
ExtensionUtils.scanExtensions(function(uuid, dir, type) {
let enabled = enabledExtensions.indexOf(uuid) != -1;
loadExtension(dir, type, enabled);
let finder = new ExtensionUtils.ExtensionFinder();
finder.connect('extension-found', function(signals, extension) {
try {
loadExtension(extension);
} catch(e) {
logExtensionError(extension.uuid, e);
}
});
finder.scanExtensions();
}
const InstallExtensionDialog = new Lang.Class({
Name: 'InstallExtensionDialog',
Extends: ModalDialog.ModalDialog,
function enableAllExtensions() {
if (enabled)
return;
_init: function(uuid, version_tag, name) {
this.parent({ styleClass: 'extension-dialog' });
this._uuid = uuid;
this._version_tag = version_tag;
this._name = name;
this.setButtons([{ label: _("Cancel"),
action: Lang.bind(this, this._onCancelButtonPressed),
key: Clutter.Escape
},
{ label: _("Install"),
action: Lang.bind(this, this._onInstallButtonPressed)
}]);
let message = _("Download and install '%s' from extensions.gnome.org?").format(name);
this._descriptionLabel = new St.Label({ text: message });
this.contentLayout.add(this._descriptionLabel,
{ y_fill: true,
y_align: St.Align.START });
},
_onCancelButtonPressed: function(button, event) {
this.close(global.get_current_time());
// Even though the extension is already "uninstalled", send through
// a state-changed signal for any users who want to know if the install
// went through correctly -- using proper async DBus would block more
// traditional clients like the plugin
let meta = { uuid: this._uuid,
state: ExtensionState.UNINSTALLED,
error: '' };
_signals.emit('extension-state-changed', meta);
},
_onInstallButtonPressed: function(button, event) {
let state = { uuid: this._uuid,
state: ExtensionState.DOWNLOADING,
error: '' };
_signals.emit('extension-state-changed', state);
let params = { version_tag: this._version_tag,
shell_version: Config.PACKAGE_VERSION,
api_version: API_VERSION.toString() };
let url = REPOSITORY_URL_DOWNLOAD.format(this._uuid);
let message = Soup.form_request_new_from_hash('GET', url, params);
_httpSession.queue_message(message,
Lang.bind(this, function(session, message) {
gotExtensionZipFile(session, message, this._uuid);
}));
this.close(global.get_current_time());
if (!initted) {
_loadExtensions();
initted = true;
} else {
enabledExtensions.forEach(function(uuid) {
enableExtension(uuid);
});
}
});
enabled = true;
}
function disableAllExtensions() {
if (!enabled)
return;
if (initted) {
enabledExtensions.forEach(function(uuid) {
disableExtension(uuid);
});
}
enabled = false;
}
function _sessionUpdated() {
if (Main.sessionMode.allowExtensions)
enableAllExtensions();
else
disableAllExtensions();
}
function init() {
Main.sessionMode.connect('updated', _sessionUpdated);
_sessionUpdated();
}

358
js/ui/grabHelper.js Normal file
View File

@ -0,0 +1,358 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Main = imports.ui.main;
const Params = imports.misc.params;
function _navigateActor(actor) {
if (!actor)
return;
let needsGrab = true;
if (actor instanceof St.Widget)
needsGrab = !actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
if (needsGrab)
actor.grab_key_focus();
}
// GrabHelper:
// @owner: the actor that owns the GrabHelper
//
// Creates a new GrabHelper object, for dealing with keyboard and pointer grabs
// associated with a set of actors.
//
// Note that the grab can be automatically dropped at any time by the user, and
// your code just needs to deal with it; you shouldn't adjust behavior directly
// after you call ungrab(), but instead pass an 'onUngrab' callback when you
// call grab().
const GrabHelper = new Lang.Class({
Name: 'GrabHelper',
_init: function(owner) {
this._owner = owner;
this._grabStack = [];
this._actors = [];
this._capturedEventId = 0;
this._eventId = 0;
this._keyFocusNotifyId = 0;
this._focusWindowChangedId = 0;
this._ignoreRelease = false;
this._modalCount = 0;
this._grabFocusCount = 0;
},
// addActor:
// @actor: an actor
//
// Adds @actor to the set of actors that are allowed to process events
// during a grab.
addActor: function(actor) {
actor.__grabHelperDestroyId = actor.connect('destroy', Lang.bind(this, function() { this.removeActor(actor); }));
this._actors.push(actor);
},
// removeActor:
// @actor: an actor
//
// Removes @actor from the set of actors that are allowed to
// process events during a grab.
removeActor: function(actor) {
let index = this._actors.indexOf(actor);
if (index != -1)
this._actors.splice(index, 1);
if (actor.__grabHelperDestroyId) {
actor.disconnect(actor.__grabHelperDestroyId);
delete actor.__grabHelperDestroyId;
}
},
_isWithinGrabbedActor: function(actor) {
while (actor) {
if (this._actors.indexOf(actor) != -1)
return true;
actor = actor.get_parent();
}
return false;
},
get currentGrab() {
return this._grabStack[this._grabStack.length - 1] || {};
},
_findStackIndex: function(actor) {
if (!actor)
return -1;
for (let i = 0; i < this._grabStack.length; i++) {
if (this._grabStack[i].actor === actor)
return i;
}
return -1;
},
isActorGrabbed: function(actor) {
return this._findStackIndex(actor) >= 0;
},
// grab:
// @params: A bunch of parameters, see below
//
// Grabs the mouse and keyboard, according to the GrabHelper's
// parameters. If @newFocus is not %null, then the keyboard focus
// is moved to the first #StWidget:can-focus widget inside it.
//
// The grab will automatically be dropped if:
// - The user clicks outside the grabbed actors
// - The user types Escape
// - The keyboard focus is moved outside the grabbed actors
// - A window is focused
//
// If @params.actor is not null, then it will be focused as the
// new actor. If you attempt to grab an already focused actor, the
// request to be focused will be ignored. The actor will not be
// added to the grab stack, so do not call a paired ungrab().
//
// If @params contains { modal: true }, then grab() will push a modal
// on the owner of the GrabHelper. As long as there is at least one
// { modal: true } actor on the grab stack, the grab will be kept.
// When the last { modal: true } actor is ungrabbed, then the modal
// will be dropped. A modal grab can fail if there is already a grab
// in effect from aother application; in this case the function returns
// false and nothing happens. Non-modal grabs can never fail.
//
// If @params contains { grabFocus: true }, then if you call grab()
// while the shell is outside the overview, it will set the stage
// input mode to %Shell.StageInputMode.FOCUSED, and ungrab() will
// revert it back, and re-focus the previously-focused window (if
// another window hasn't been explicitly focused before then).
grab: function(params) {
params = Params.parse(params, { actor: null,
modal: false,
grabFocus: false,
onUngrab: null });
let focus = global.stage.key_focus;
let hadFocus = focus && this._isWithinGrabbedActor(focus);
let newFocus = params.actor;
if (this.isActorGrabbed(params.actor))
return true;
params.savedFocus = focus;
if (params.modal && !this._takeModalGrab())
return false;
if (params.grabFocus && !this._takeFocusGrab(hadFocus))
return false;
if (hadFocus || params.grabFocus)
_navigateActor(newFocus);
this._grabStack.push(params);
return true;
},
_takeModalGrab: function() {
let firstGrab = (this._modalCount == 0);
if (firstGrab) {
if (!Main.pushModal(this._owner))
return false;
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
this._eventId = global.stage.connect('event', Lang.bind(this, this._onEvent));
}
this._modalCount++;
return true;
},
_releaseModalGrab: function() {
this._modalCount--;
if (this._modalCount > 0)
return;
if (this._capturedEventId > 0) {
global.stage.disconnect(this._capturedEventId);
this._capturedEventId = 0;
}
if (this._eventId > 0) {
global.stage.disconnect(this._eventId);
this._eventId = 0;
}
Main.popModal(this._owner);
global.sync_pointer();
},
_takeFocusGrab: function(hadFocus) {
let firstGrab = (this._grabFocusCount == 0);
if (firstGrab) {
let metaDisplay = global.screen.get_display();
this._grabbedFromKeynav = hadFocus;
this._preGrabInputMode = global.stage_input_mode;
this._prevFocusedWindow = metaDisplay.focus_window;
if (this._preGrabInputMode == Shell.StageInputMode.NONREACTIVE ||
this._preGrabInputMode == Shell.StageInputMode.NORMAL) {
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
}
this._keyFocusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
this._focusWindowChangedId = metaDisplay.connect('notify::focus-window', Lang.bind(this, this._focusWindowChanged));
}
this._grabFocusCount++;
return true;
},
_releaseFocusGrab: function() {
this._grabFocusCount--;
if (this._grabFocusCount > 0)
return;
if (this._keyFocusNotifyId > 0) {
global.stage.disconnect(this._keyFocusNotifyId);
this._keyFocusNotifyId = 0;
}
if (!this._focusWindowChanged > 0) {
let metaDisplay = global.screen.get_display();
metaDisplay.disconnect(this._focusWindowChangedId);
this._focusWindowChangedId = 0;
}
let prePopInputMode = global.stage_input_mode;
if (this._grabbedFromKeynav) {
if (this._preGrabInputMode == Shell.StageInputMode.FOCUSED &&
prePopInputMode != Shell.StageInputMode.FULLSCREEN)
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
}
if (this._prevFocusedWindow) {
let metaDisplay = global.screen.get_display();
if (!metaDisplay.focus_window) {
metaDisplay.set_input_focus_window(this._prevFocusedWindow,
false, global.get_current_time());
}
}
},
// ignoreRelease:
//
// Make sure that the next button release event evaluated by the
// capture event handler returns false. This is designed for things
// like the ComboBoxMenu that go away on press, but need to eat
// the next release event.
ignoreRelease: function() {
this._ignoreRelease = true;
},
// ungrab:
// @params: The parameters for the grab; see below.
//
// Pops an actor from the grab stack, potentially dropping the grab.
//
// If the actor that was popped from the grab stack was not the actor
// That was passed in, this call is ignored.
ungrab: function(params) {
params = Params.parse(params, { actor: this.currentGrab.actor });
let grabStackIndex = this._findStackIndex(params.actor);
if (grabStackIndex < 0)
return;
let focus = global.stage.key_focus;
let hadFocus = focus && this._isWithinGrabbedActor(focus);
let poppedGrabs = this._grabStack.slice(grabStackIndex);
// "Pop" all newly ungrabbed actors off the grab stack
// by truncating the array.
this._grabStack.length = grabStackIndex;
for (let i = poppedGrabs.length - 1; i >= 0; i--) {
let poppedGrab = poppedGrabs[i];
if (poppedGrab.onUngrab)
poppedGrab.onUngrab();
if (poppedGrab.modal)
this._releaseModalGrab();
if (poppedGrab.grabFocus)
this._releaseFocusGrab();
}
if (hadFocus) {
let poppedGrab = poppedGrabs[0];
_navigateActor(poppedGrab.savedFocus);
}
},
_onCapturedEvent: function(actor, event) {
let type = event.type();
let press = type == Clutter.EventType.BUTTON_PRESS;
let release = type == Clutter.EventType.BUTTON_RELEASE;
let button = press || release;
if (release && this._ignoreRelease) {
this._ignoreRelease = false;
return false;
}
if (!button && this._modalCount == 0)
return false;
if (this._isWithinGrabbedActor(event.get_source()))
return false;
if (Main.keyboard.shouldTakeEvent(event))
return false;
if (button) {
// If we have a press event, ignore the next event,
// which should be a release event.
if (press)
this._ignoreRelease = true;
this.ungrab({ actor: this._grabStack[0].actor });
}
return this._modalCount > 0;
},
// We catch 'event' rather than 'key-press-event' so that we get
// a chance to run before the overview's own Escape check
_onEvent: function(actor, event) {
if (event.type() == Clutter.EventType.KEY_PRESS &&
event.get_key_symbol() == Clutter.KEY_Escape) {
this.ungrab();
return true;
}
return false;
},
_onKeyFocusChanged: function() {
let focus = global.stage.key_focus;
if (!focus || !this._isWithinGrabbedActor(focus))
this.ungrab();
},
_focusWindowChanged: function() {
let metaDisplay = global.screen.get_display();
if (metaDisplay.focus_window != null)
this.ungrab();
}
});

228
js/ui/ibusCandidatePopup.js Normal file
View File

@ -0,0 +1,228 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const IBus = imports.gi.IBus;
const Lang = imports.lang;
const St = imports.gi.St;
const BoxPointer = imports.ui.boxpointer;
const Main = imports.ui.main;
const PopupMenu = imports.ui.popupMenu;
const MAX_CANDIDATES_PER_PAGE = 16;
const CandidateArea = new Lang.Class({
Name: 'CandidateArea',
Extends: PopupMenu.PopupBaseMenuItem,
_init: function() {
this.parent({ reactive: false });
// St.Table exhibits some sizing problems so let's go with a
// clutter layout manager for now.
this._table = new Clutter.Actor();
this.addActor(this._table);
this._tableLayout = new Clutter.TableLayout();
this._table.set_layout_manager(this._tableLayout);
this._indexLabels = [];
this._candidateLabels = [];
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
this._indexLabels.push(new St.Label({ style_class: 'candidate-index' }));
this._candidateLabels.push(new St.Label({ style_class: 'candidate-label' }));
}
this._orientation = -1;
this._cursorPosition = 0;
},
_setOrientation: function(orientation) {
if (this._orientation == orientation)
return;
this._orientation = orientation;
this._table.remove_all_children();
if (this._orientation == IBus.Orientation.HORIZONTAL)
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
this._tableLayout.pack(this._indexLabels[i], i*2, 0);
this._tableLayout.pack(this._candidateLabels[i], i*2 + 1, 0);
}
else // VERTICAL || SYSTEM
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
this._tableLayout.pack(this._indexLabels[i], 0, i);
this._tableLayout.pack(this._candidateLabels[i], 1, i);
}
},
setCandidates: function(indexes, candidates, orientation, cursorPosition, cursorVisible) {
this._setOrientation(orientation);
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
let visible = i < candidates.length;
this._indexLabels[i].visible = visible;
this._candidateLabels[i].visible = visible;
if (!visible)
continue;
this._indexLabels[i].text = ((indexes && indexes[i]) ? indexes[i] : '%x.'.format(i + 1));
this._candidateLabels[i].text = candidates[i];
}
this._candidateLabels[this._cursorPosition].remove_style_pseudo_class('selected');
this._cursorPosition = cursorPosition;
if (cursorVisible)
this._candidateLabels[cursorPosition].add_style_pseudo_class('selected');
},
});
const CandidatePopup = new Lang.Class({
Name: 'CandidatePopup',
Extends: PopupMenu.PopupMenu,
_init: function() {
this._cursor = new St.Bin({ opacity: 0 });
Main.uiGroup.add_actor(this._cursor);
this.parent(this._cursor, 0, St.Side.TOP);
this.actor.hide();
Main.uiGroup.add_actor(this.actor);
this._preeditTextItem = new PopupMenu.PopupMenuItem('', { reactive: false });
this._preeditTextItem.actor.hide();
this.addMenuItem(this._preeditTextItem);
this._auxTextItem = new PopupMenu.PopupMenuItem('', { reactive: false });
this._auxTextItem.actor.hide();
this.addMenuItem(this._auxTextItem);
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._lookupTableItem = new CandidateArea();
this._lookupTableItem.actor.hide();
this.addMenuItem(this._lookupTableItem);
this._panelService = null;
},
setPanelService: function(panelService) {
this._panelService = panelService;
if (!panelService)
return;
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);
}));
panelService.connect('update-preedit-text',
Lang.bind(this, function(ps, text, cursorPosition, visible) {
if (visible)
this._preeditTextItem.actor.show();
else
this._preeditTextItem.actor.hide();
this._updateVisibility();
this._preeditTextItem.actor.label_actor.text = text.get_text();
let attrs = text.get_attributes();
if (attrs)
this._setTextAttributes(this._preeditTextItem.actor.label_actor.clutter_text,
attrs);
}));
panelService.connect('show-preedit-text',
Lang.bind(this, function(ps) {
this._preeditTextItem.actor.show();
this._updateVisibility();
}));
panelService.connect('hide-preedit-text',
Lang.bind(this, function(ps) {
this._preeditTextItem.actor.hide();
this._updateVisibility();
}));
panelService.connect('update-auxiliary-text',
Lang.bind(this, function(ps, text, visible) {
if (visible)
this._auxTextItem.actor.show();
else
this._auxTextItem.actor.hide();
this._updateVisibility();
this._auxTextItem.actor.label_actor.text = text.get_text();
}));
panelService.connect('show-auxiliary-text',
Lang.bind(this, function(ps) {
this._auxTextItem.actor.show();
this._updateVisibility();
}));
panelService.connect('hide-auxiliary-text',
Lang.bind(this, function(ps) {
this._auxTextItem.actor.hide();
this._updateVisibility();
}));
panelService.connect('update-lookup-table',
Lang.bind(this, function(ps, lookupTable, visible) {
if (visible)
this._lookupTableItem.actor.show();
else
this._lookupTableItem.actor.hide();
this._updateVisibility();
let cursorPos = lookupTable.get_cursor_pos();
let pageSize = lookupTable.get_page_size();
let page = ((cursorPos == 0) ? 0 : Math.floor(cursorPos / pageSize));
let startIndex = page * pageSize;
let endIndex = Math.min((page + 1) * pageSize,
lookupTable.get_number_of_candidates());
let indexes = [];
let indexLabel;
for (let i = 0; indexLabel = lookupTable.get_label(i); ++i)
indexes.push(indexLabel.get_text());
let candidates = [];
for (let i = startIndex; i < endIndex; ++i)
candidates.push(lookupTable.get_candidate(i).get_text());
this._lookupTableItem.setCandidates(indexes,
candidates,
lookupTable.get_orientation(),
cursorPos % pageSize,
lookupTable.is_cursor_visible());
}));
panelService.connect('show-lookup-table',
Lang.bind(this, function(ps) {
this._lookupTableItem.actor.show();
this._updateVisibility();
}));
panelService.connect('hide-lookup-table',
Lang.bind(this, function(ps) {
this._lookupTableItem.actor.hide();
this._updateVisibility();
}));
panelService.connect('focus-out',
Lang.bind(this, function(ps) {
this.close(BoxPointer.PopupAnimation.NONE);
}));
},
_updateVisibility: function() {
let isVisible = (this._preeditTextItem.actor.visible ||
this._auxTextItem.actor.visible ||
this._lookupTableItem.actor.visible);
if (isVisible)
this.open(BoxPointer.PopupAnimation.NONE);
else
this.close(BoxPointer.PopupAnimation.NONE);
},
_setTextAttributes: function(clutterText, ibusAttrList) {
let attr;
for (let i = 0; attr = ibusAttrList.get(i); ++i)
if (attr.get_attr_type() == IBus.AttrType.BACKGROUND)
clutterText.set_selection(attr.get_start_index(), attr.get_end_index());
}
});

View File

@ -282,6 +282,10 @@ const IconGrid = new Lang.Class({
return this._computeLayout(rowWidth)[0];
},
getRowLimit: function() {
return this._rowLimit;
},
_computeLayout: function (forWidth) {
let nColumns = 0;
let usedWidth = 0;
@ -305,21 +309,22 @@ const IconGrid = new Lang.Class({
this._grid.queue_relayout();
},
removeAll: function () {
this._grid.get_children().forEach(Lang.bind(this, function (child) {
child.destroy();
}));
removeAll: function() {
this._grid.destroy_all_children();
},
addItem: function(actor) {
this._grid.add_actor(actor);
addItem: function(actor, index) {
if (index !== undefined)
this._grid.insert_child_at_index(actor, index);
else
this._grid.add_actor(actor);
},
getItemAtIndex: function(index) {
return this._grid.get_children()[index];
return this._grid.get_child_at_index(index);
},
visibleItemsCount: function() {
return this._grid.get_children().length - this._grid.get_n_skip_paint();
return this._grid.get_n_children() - this._grid.get_n_skip_paint();
}
});

View File

@ -93,7 +93,7 @@ const Key = new Lang.Class({
this._getExtendedKeys();
this.actor._extended_keys = this._extended_keyboard;
this._boxPointer.actor.hide();
Main.layoutManager.addChrome(this._boxPointer.actor, { visibleInFullscreen: true });
Main.layoutManager.addChrome(this._boxPointer.actor);
}
},
@ -175,7 +175,7 @@ const Key = new Lang.Class({
this.actor.fake_release();
this._boxPointer.actor.raise_top();
this._boxPointer.setPosition(this.actor, 0.5);
this._boxPointer.show(true);
this._boxPointer.show(BoxPointer.PopupAnimation.FULL);
this.actor.set_hover(false);
if (!this._grabbed) {
Main.pushModal(this.actor);
@ -186,7 +186,7 @@ const Key = new Lang.Class({
} else {
if (this._grabbed)
this._ungrab();
this._boxPointer.hide(true);
this._boxPointer.hide(BoxPointer.PopupAnimation.FULL);
}
}
});
@ -200,8 +200,10 @@ const Keyboard = new Lang.Class({
this._impl.export(Gio.DBus.session, '/org/gnome/Caribou/Keyboard');
this.actor = null;
this._focusInTray = false;
this._focusInExtendedKeys = false;
this._timestamp = global.get_current_time();
this._timestamp = global.display.get_current_time_roundtrip();
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
this._keyboardSettings = new Gio.Settings({ schema: KEYBOARD_SCHEMA });
@ -288,7 +290,15 @@ const Keyboard = new Lang.Class({
// Showing an extended key popup and clicking a key from the extended keys
// will grab focus, but ignore that
if (focus && (focus._extended_keys || (focus._key && focus._key.extended_key)))
let extendedKeysWereFocused = this._focusInExtendedKeys;
this._focusInExtendedKeys = focus && (focus._extended_keys || focus.extended_key);
if (this._focusInExtendedKeys || extendedKeysWereFocused)
return;
// Ignore focus changes caused by message tray showing/hiding
let trayWasFocused = this._focusInTray;
this._focusInTray = (focus && Main.messageTray.actor.contains(focus));
if (this._focusInTray || trayWasFocused)
return;
let time = global.get_current_time();
@ -339,6 +349,13 @@ const Keyboard = new Lang.Class({
trayButton.reactive = true;
trayButton.remove_style_pseudo_class('grayed');
}));
Main.sessionMode.connect('updated', Lang.bind(this, function() {
trayButton.reactive = !Main.sessionMode.isLocked;
if (Main.sessionMode.isLocked)
trayButton.add_style_pseudo_class('grayed');
else
trayButton.remove_style_pseudo_class('grayed');
}));
return trayButton;
},
@ -468,6 +485,12 @@ const Keyboard = new Lang.Class({
}
},
shouldTakeEvent: function(event) {
let actor = event.get_source();
return Main.layoutManager.keyboardBox.contains(actor) ||
actor._extended_keys || actor.extended_key;
},
show: function () {
this._redraw();
@ -494,15 +517,30 @@ const Keyboard = new Lang.Class({
this._moveTemporarily();
},
// _compareTimestamp:
//
// Compare two timestamps taking into account
// CURRENT_TIME (0)
_compareTimestamp: function(one, two) {
if (one == two)
return 0;
if (one == Clutter.CURRENT_TIME)
return 1;
if (two == Clutter.CURRENT_TIME)
return -1;
return one - two;
},
// D-Bus methods
Show: function(timestamp) {
if (!this._enableKeyboard)
return;
if (timestamp - this._timestamp < 0)
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
return;
this._timestamp = timestamp;
if (timestamp != Clutter.CURRENT_TIME)
this._timestamp = timestamp;
this.show();
},
@ -510,10 +548,11 @@ const Keyboard = new Lang.Class({
if (!this._enableKeyboard)
return;
if (timestamp - this._timestamp < 0)
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
return;
this._timestamp = timestamp;
if (timestamp != Clutter.CURRENT_TIME)
this._timestamp = timestamp;
this.hide();
},
@ -541,16 +580,9 @@ const KeyboardSource = new Lang.Class({
Extends: MessageTray.Source,
_init: function(keyboard) {
this.parent(_("Keyboard"));
this._keyboard = keyboard;
this._setSummaryIcon(this.createNotificationIcon());
},
createNotificationIcon: function() {
return new St.Icon({ icon_name: 'input-keyboard',
icon_type: St.IconType.SYMBOLIC,
icon_size: this.ICON_SIZE });
this.parent(_("Keyboard"), 'input-keyboard-symbolic');
this.keepTrayOnSummaryClick = true;
},
handleSummaryClick: function() {

View File

@ -1,6 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GObject = imports.gi.GObject;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
@ -11,12 +12,85 @@ const St = imports.gi.St;
const DND = imports.ui.dnd;
const Main = imports.ui.main;
const Params = imports.misc.params;
const ScreenSaver = imports.misc.screenSaver;
const Tweener = imports.ui.tweener;
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
const STARTUP_ANIMATION_TIME = 0.2;
const KEYBOARD_ANIMATION_TIME = 0.5;
const PLYMOUTH_TRANSITION_TIME = 1;
const MonitorConstraint = new Lang.Class({
Name: 'MonitorConstraint',
Extends: Clutter.Constraint,
Properties: {'primary': GObject.ParamSpec.boolean('primary',
'Primary', 'Track primary monitor',
GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE,
false),
'index': GObject.ParamSpec.int('index',
'Monitor index', 'Track specific monitor',
GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE,
-1, 64, -1)},
_init: function(props) {
this._primary = false;
this._index = -1;
this.parent(props);
},
get primary() {
return this._primary;
},
set primary(v) {
this._primary = v;
if (this.actor)
this.actor.queue_relayout();
this.notify('primary');
},
get index() {
return this._index;
},
set index(v) {
this._index = v;
if (this.actor)
this.actor.queue_relayout();
this.notify('index');
},
vfunc_set_actor: function(actor) {
if (actor) {
if (!this._monitorsChangedId) {
this._monitorsChangedId = Main.layoutManager.connect('monitors-changed', Lang.bind(this, function() {
this.actor.queue_relayout();
}));
}
} else {
if (this._monitorsChangedId)
Main.layoutManager.disconnect(this._monitorsChangedId);
this._monitorsChangedId = 0;
}
this.parent(actor);
},
vfunc_update_allocation: function(actor, actorBox) {
if (!this._primary && this._index < 0)
return;
let monitor;
if (this._primary) {
monitor = Main.layoutManager.primaryMonitor;
} else {
let index = Math.min(this._index, Main.layoutManager.monitors.length - 1);
monitor = Main.layoutManager.monitors[index];
}
actorBox.init_rect(monitor.x, monitor.y, monitor.width, monitor.height);
}
});
const LayoutManager = new Lang.Class({
Name: 'LayoutManager',
@ -27,32 +101,46 @@ const LayoutManager = new Lang.Class({
this.primaryMonitor = null;
this.primaryIndex = -1;
this._hotCorners = [];
this._rootPixmap = null;
this._leftPanelBarrier = 0;
this._rightPanelBarrier = 0;
this._trayBarrier = 0;
this._chrome = new Chrome(this);
this.screenShieldGroup = new St.Widget({ name: 'screenShieldGroup',
visible: false,
clip_to_allocation: true,
layout_manager: new Clutter.BinLayout(),
});
this.addChrome(this.screenShieldGroup);
this.panelBox = new St.BoxLayout({ name: 'panelBox',
vertical: true });
this.addChrome(this.panelBox, { affectsStruts: true });
this.addChrome(this.panelBox, { affectsStruts: true,
trackFullscreen: true });
this.panelBox.connect('allocation-changed',
Lang.bind(this, this._updatePanelBarriers));
this.trayBox = new St.BoxLayout({ name: 'trayBox' });
this.addChrome(this.trayBox, { visibleInFullscreen: true });
this.trayBox = new St.Widget({ name: 'trayBox',
layout_manager: new Clutter.BinLayout() });
this.addChrome(this.trayBox);
this.trayBox.connect('allocation-changed',
Lang.bind(this, this._updateTrayBarrier));
this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox',
reactive: true,
track_hover: true });
this.addChrome(this.keyboardBox, { visibleInFullscreen: true });
this.addChrome(this.keyboardBox);
this._keyboardHeightNotifyId = 0;
global.screen.connect('monitors-changed',
Lang.bind(this, this._monitorsChanged));
this._monitorsChanged();
this._chrome.connect('primary-fullscreen-changed', Lang.bind(this, function(chrome, state) {
this.emit('primary-fullscreen-changed', state);
}));
},
// This is called by Main after everything else is constructed;
@ -145,6 +233,9 @@ const LayoutManager = new Lang.Class({
},
_updateBoxes: function() {
this.screenShieldGroup.set_position(0, 0);
this.screenShieldGroup.set_size(global.screen_width, global.screen_height);
this.panelBox.set_position(this.primaryMonitor.x, this.primaryMonitor.y);
this.panelBox.set_size(this.primaryMonitor.width, -1);
@ -224,33 +315,47 @@ const LayoutManager = new Lang.Class({
return false;
},
get focusIndex() {
let focusWindow = global.display.focus_window;
if (focusWindow) {
let wrect = focusWindow.get_outer_rect();
for (let i = 0; i < this.monitors.length; i++) {
let monitor = this.monitors[i];
if (monitor.x <= wrect.x && monitor.y <= wrect.y &&
monitor.x + monitor.width > wrect.x &&
monitor.y + monitor.height > wrect.y)
return i;
}
}
return this.primaryIndex;
},
get focusMonitor() {
return this.monitors[this.focusIndex];
get currentMonitor() {
let index = global.screen.get_current_monitor();
return this.monitors[index];
},
_startupAnimation: function() {
this.panelBox.anchor_y = this.panelBox.height;
let plymouthTransitionRunning = false;
// If we're the greeter, put up the xrootpmap actor
// and fade it out to have a nice transition from plymouth
// to the greeter. Otherwise, we'll just animate the panel,
// as usual.
if (Main.sessionMode.isGreeter) {
this._rootPixmap = global.create_xrootpmap_texture();
if (this._rootPixmap != null) {
Main.uiGroup.add_actor(this._rootPixmap);
Tweener.addTween(this._rootPixmap,
{ opacity: 0,
time: PLYMOUTH_TRANSITION_TIME,
transition: 'linear',
onComplete: this._fadeRootpmapComplete,
onCompleteScope: this });
plymouthTransitionRunning = true;
}
}
if (!plymouthTransitionRunning)
this._fadeRootpmapComplete();
},
_fadeRootpmapComplete: function() {
// Don't animate the strut
this._chrome.freezeUpdateRegions();
this.panelBox.anchor_y = this.panelBox.height;
if (this._rootPixmap != null) {
this._rootPixmap.destroy();
this._rootPixmap = null;
}
Tweener.addTween(this.panelBox,
{ anchor_y: 0,
time: STARTUP_ANIMATION_TIME,
@ -265,7 +370,6 @@ const LayoutManager = new Lang.Class({
},
showKeyboard: function () {
Main.messageTray.hide();
this.keyboardBox.raise_top();
Tweener.addTween(this.keyboardBox,
{ anchor_y: this.keyboardBox.height,
@ -279,6 +383,8 @@ const LayoutManager = new Lang.Class({
time: KEYBOARD_ANIMATION_TIME,
transition: 'easeOutQuad'
});
this.emit('keyboard-visible-changed', true);
},
_showKeyboardComplete: function() {
@ -293,7 +399,6 @@ const LayoutManager = new Lang.Class({
},
hideKeyboard: function (immediate) {
Main.messageTray.hide();
if (this._keyboardHeightNotifyId) {
this.keyboardBox.disconnect(this._keyboardHeightNotifyId);
this._keyboardHeightNotifyId = 0;
@ -310,6 +415,8 @@ const LayoutManager = new Lang.Class({
time: immediate ? 0 : KEYBOARD_ANIMATION_TIME,
transition: 'easeOutQuad'
});
this.emit('keyboard-visible-changed', false);
},
_hideKeyboardComplete: function() {
@ -331,8 +438,10 @@ const LayoutManager = new Lang.Class({
// the window manager struts. Changes to @actor's visibility will
// NOT affect whether or not the strut is present, however.
//
// If %visibleInFullscreen in @params is %true, the actor will be
// visible even when a fullscreen window should be covering it.
// If %trackFullscreen in @params is %true, the actor's visibility
// will be bound to the presence of fullscreen windows on the same
// monitor (it will be hidden whenever a fullscreen window is visible,
// and shown otherwise)
addChrome: function(actor, params) {
this._chrome.addActor(actor, params);
},
@ -346,10 +455,8 @@ const LayoutManager = new Lang.Class({
// struts or input region to cover specific children.
//
// @params can have any of the same values as in addChrome(),
// though some possibilities don't make sense (eg, trying to have
// a %visibleInFullscreen child of a non-%visibleInFullscreen
// parent). By default, @actor has the same params as its chrome
// ancestor.
// though some possibilities don't make sense. By default, @actor has
// the same params as its chrome ancestor.
trackChrome: function(actor, params) {
this._chrome.trackActor(actor, params);
},
@ -555,7 +662,7 @@ const HotCorner = new Lang.Class({
// workspace content.
const defaultParams = {
visibleInFullscreen: false,
trackFullscreen: false,
affectsStruts: false,
affectsInputRegion: true
};
@ -582,24 +689,13 @@ const Chrome = new Lang.Class({
global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._queueUpdateRegions));
this._screenSaverActive = false;
this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy();
this._screenSaverProxy.connectSignal('ActiveChanged', Lang.bind(this, function(proxy, senderName, [isActive]) {
this._onScreenSaverActiveChanged(isActive);
}));
this._screenSaverProxy.GetActiveRemote(Lang.bind(this, function(result, err) {
if (!err)
this._onScreenSaverActiveChanged(result[0]);
}));
this._relayout();
},
init: function() {
Main.overview.connect('showing',
Lang.bind(this, this._overviewShowing));
Main.overview.connect('hidden',
Lang.bind(this, this._overviewHidden));
Main.overview.connect('showing', Lang.bind(this, this._overviewShowing));
Main.overview.connect('hidden', Lang.bind(this, this._overviewHidden));
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
},
addActor: function(actor, params) {
@ -694,19 +790,18 @@ const Chrome = new Lang.Class({
_updateVisibility: function() {
for (let i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i], visible;
if (!actorData.trackFullscreen)
continue;
if (!actorData.isToplevel)
continue;
if (this._screenSaverActive)
visible = false;
else if (this._inOverview)
if (this._inOverview || !Main.sessionMode.hasWindows)
visible = true;
else if (!actorData.visibleInFullscreen &&
this.findMonitorForActor(actorData.actor).inFullscreen)
else if (this.findMonitorForActor(actorData.actor).inFullscreen)
visible = false;
else
visible = true;
Main.uiGroup.set_skip_paint(actorData.actor, !visible);
actorData.actor.visible = visible;
}
},
@ -722,6 +817,11 @@ const Chrome = new Lang.Class({
this._queueUpdateRegions();
},
_sessionUpdated: function() {
this._updateVisibility();
this._queueUpdateRegions();
},
_relayout: function() {
this._monitors = this._layoutManager.monitors;
this._primaryMonitor = this._layoutManager.primaryMonitor;
@ -731,12 +831,6 @@ const Chrome = new Lang.Class({
this._queueUpdateRegions();
},
_onScreenSaverActiveChanged: function(screenSaverActive) {
this._screenSaverActive = screenSaverActive;
this._updateVisibility();
this._queueUpdateRegions();
},
_findMonitorForRect: function(x, y, w, h) {
// First look at what monitor the center of the rectangle is at
let cx = x + w/2;
@ -852,6 +946,8 @@ const Chrome = new Lang.Class({
for (let i = 0; i < this._monitors.length; i++)
wasInFullscreen[i] = this._monitors[i].inFullscreen;
let primaryWasInFullscreen = this._primaryMonitor.inFullscreen;
this._updateFullscreen();
let changed = false;
@ -861,10 +957,15 @@ const Chrome = new Lang.Class({
break;
}
}
if (changed) {
this._updateVisibility();
this._queueUpdateRegions();
}
if (primaryWasInFullscreen != this._primaryMonitor.inFullscreen) {
this.emit('primary-fullscreen-changed', this._primaryMonitor.inFullscreen);
}
},
updateRegions: function() {
@ -979,3 +1080,5 @@ const Chrome = new Lang.Class({
return false;
}
});
Signals.addSignalMethods(Chrome.prototype);

View File

@ -8,6 +8,8 @@ const St = imports.gi.St;
const Params = imports.misc.params;
const Tweener = imports.ui.tweener;
const DEFAULT_FADE_FACTOR = 0.4;
/**
* Lightbox:
* @container: parent Clutter.Container
@ -15,7 +17,8 @@ const Tweener = imports.ui.tweener;
* - inhibitEvents: whether to inhibit events for @container
* - width: shade actor width
* - height: shade actor height
* - fadeTime: seconds used to fade in/out
* - fadeInTime: seconds used to fade in
* - fadeOutTime: seconds used to fade out
*
* Lightbox creates a dark translucent "shade" actor to hide the
* contents of @container, and allows you to specify particular actors
@ -38,12 +41,16 @@ const Lightbox = new Lang.Class({
params = Params.parse(params, { inhibitEvents: false,
width: null,
height: null,
fadeTime: null
fadeInTime: null,
fadeOutTime: null,
fadeFactor: DEFAULT_FADE_FACTOR
});
this._container = container;
this._children = container.get_children();
this._fadeTime = params.fadeTime;
this._fadeInTime = params.fadeInTime;
this._fadeOutTime = params.fadeOutTime;
this._fadeFactor = params.fadeFactor;
this.actor = new St.Bin({ x: 0,
y: 0,
style_class: 'lightbox',
@ -52,6 +59,7 @@ const Lightbox = new Lang.Class({
container.add_actor(this.actor);
this.actor.raise_top();
this.actor.hide();
this.shown = false;
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
@ -93,24 +101,30 @@ const Lightbox = new Lang.Class({
},
show: function() {
if (this._fadeTime) {
if (this._fadeInTime) {
this.shown = false;
this.actor.opacity = 0;
Tweener.addTween(this.actor,
{ opacity: 255,
time: this._fadeTime,
transition: 'easeOutQuad'
{ opacity: 255 * this._fadeFactor,
time: this._fadeInTime,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this.shown = true;
})
});
} else {
this.actor.opacity = 255;
this.actor.opacity = 255 * this._fadeFactor;
this.shown = true;
}
this.actor.show();
},
hide: function() {
if (this._fadeTime) {
this.shown = false;
if (this._fadeOutTime) {
Tweener.addTween(this.actor,
{ opacity: 0,
time: this._fadeTime,
time: this._fadeOutTime,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this.actor.hide();

View File

@ -1,21 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Signals = imports.signals;
const St = imports.gi.St;
const Link = new Lang.Class({
Name: 'Link',
_init : function(props) {
let realProps = { reactive: true,
track_hover: true,
style_class: 'shell-link' };
// The user can pass in reactive: false to override the above and get
// a non-reactive link (a link to the current page, perhaps)
Lang.copyProperties(props, realProps);
this.actor = new St.Button(realProps);
}
});
Signals.addSignalMethods(Link.prototype);

View File

@ -12,16 +12,18 @@ const Shell = imports.gi.Shell;
const Signals = imports.signals;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const System = imports.system;
const History = imports.misc.history;
const ExtensionSystem = imports.ui.extensionSystem;
const ExtensionUtils = imports.misc.extensionUtils;
const Link = imports.ui.link;
const ShellEntry = imports.ui.shellEntry;
const Tweener = imports.ui.tweener;
const Main = imports.ui.main;
const JsParse = imports.misc.jsParse;
const CHEVRON = '>>> ';
/* Imports...feel free to add here as needed */
var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
'const GLib = imports.gi.GLib; ' +
@ -36,9 +38,8 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
/* Utility functions...we should probably be able to use these
* in the shell core code too. */
'const stage = global.stage; ' +
'const color = function(pixel) { let c= new Clutter.Color(); c.from_pixel(pixel); return c; }; ' +
/* Special lookingGlass functions */
'const it = Main.lookingGlass.getIt(); ' +
'const it = Main.lookingGlass.getIt(); ' +
'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ';
const HISTORY_KEY = 'looking-glass-history';
@ -261,9 +262,8 @@ function objectToString(o) {
const ObjLink = new Lang.Class({
Name: 'ObjLink',
Extends: Link.Link,
_init: function(o, title) {
_init: function(lookingGlass, o, title) {
let text;
if (title)
text = title;
@ -272,24 +272,30 @@ const ObjLink = new Lang.Class({
text = GLib.markup_escape_text(text, -1);
this._obj = o;
this.parent({ label: text });
this.actor = new St.Button({ reactive: true,
track_hover: true,
style_class: 'shell-link',
label: text });
this.actor.get_child().single_line_mode = true;
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
this._lookingGlass = lookingGlass;
},
_onClicked: function (link) {
Main.lookingGlass.inspectObject(this._obj, this.actor);
this._lookingGlass.inspectObject(this._obj, this.actor);
}
});
const Result = new Lang.Class({
Name: 'Result',
_init : function(command, o, index) {
_init: function(lookingGlass, command, o, index) {
this.index = index;
this.o = o;
this.actor = new St.BoxLayout({ vertical: true });
this._lookingGlass = lookingGlass;
let cmdTxt = new St.Label({ text: command });
cmdTxt.clutter_text.ellipsize = Pango.EllipsizeMode.END;
@ -299,7 +305,7 @@ const Result = new Lang.Class({
let resultTxt = new St.Label({ text: 'r(' + index + ') = ' });
resultTxt.clutter_text.ellipsize = Pango.EllipsizeMode.END;
box.add(resultTxt);
let objLink = new ObjLink(o);
let objLink = new ObjLink(this._lookingGlass, o);
box.add(objLink.actor);
let line = new Clutter.Rectangle({ name: 'Separator' });
let padBin = new St.Bin({ name: 'Separator', x_fill: true, y_fill: true });
@ -311,16 +317,18 @@ const Result = new Lang.Class({
const WindowList = new Lang.Class({
Name: 'WindowList',
_init : function () {
_init: function(lookingGlass) {
this.actor = new St.BoxLayout({ name: 'Windows', vertical: true, style: 'spacing: 8px' });
let tracker = Shell.WindowTracker.get_default();
this._updateId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._updateWindowList));
global.display.connect('window-created', Lang.bind(this, this._updateWindowList));
tracker.connect('tracked-windows-changed', Lang.bind(this, this._updateWindowList));
this._lookingGlass = lookingGlass;
},
_updateWindowList: function() {
this.actor.get_children().forEach(function (actor) { actor.destroy(); });
this.actor.destroy_all_children();
let windows = global.get_window_actors();
let tracker = Shell.WindowTracker.get_default();
for (let i = 0; i < windows.length; i++) {
@ -332,7 +340,7 @@ const WindowList = new Lang.Class({
}
let box = new St.BoxLayout({ vertical: true });
this.actor.add(box);
let windowLink = new ObjLink(metaWindow, metaWindow.title);
let windowLink = new ObjLink(this._lookingGlass, metaWindow, metaWindow.title);
box.add(windowLink.actor, { x_align: St.Align.START, x_fill: false });
let propsBox = new St.BoxLayout({ vertical: true, style: 'padding-left: 6px;' });
box.add(propsBox);
@ -343,7 +351,7 @@ const WindowList = new Lang.Class({
let propBox = new St.BoxLayout({ style: 'spacing: 6px; ' });
propsBox.add(propBox);
propBox.add(new St.Label({ text: 'app: ' }), { y_fill: false });
let appLink = new ObjLink(app, app.get_id());
let appLink = new ObjLink(this._lookingGlass, app, app.get_id());
propBox.add(appLink.actor, { y_fill: false });
propBox.add(icon, { y_fill: false });
} else {
@ -357,18 +365,21 @@ Signals.addSignalMethods(WindowList.prototype);
const ObjInspector = new Lang.Class({
Name: 'ObjInspector',
_init : function () {
_init: function(lookingGlass) {
this._obj = null;
this._previousObj = null;
this._parentList = [];
this.actor = new St.ScrollView({ x_fill: true, y_fill: true });
this.actor = new St.ScrollView({ pivot_point: new Clutter.Point({ x: 0.5, y: 0.5 }),
x_fill: true, y_fill: true });
this.actor.get_hscroll_bar().hide();
this._container = new St.BoxLayout({ name: 'LookingGlassPropertyInspector',
style_class: 'lg-dialog',
vertical: true });
this.actor.add_actor(this._container);
this._lookingGlass = lookingGlass;
},
selectObject: function(obj, skipPrevious) {
@ -378,7 +389,7 @@ const ObjInspector = new Lang.Class({
this._previousObj = null;
this._obj = obj;
this._container.get_children().forEach(function (child) { child.destroy(); });
this._container.destroy_all_children();
let hbox = new St.BoxLayout({ style_class: 'lg-obj-inspector-title' });
this._container.add_actor(hbox);
@ -412,7 +423,7 @@ const ObjInspector = new Lang.Class({
let link;
try {
let prop = obj[propName];
link = new ObjLink(prop).actor;
link = new ObjLink(this._lookingGlass, prop).actor;
} catch (e) {
link = new St.Label({ text: '<error>' });
}
@ -433,10 +444,6 @@ const ObjInspector = new Lang.Class({
this.actor.show();
if (sourceActor) {
this.actor.set_scale(0, 0);
let [sourceX, sourceY] = sourceActor.get_transformed_position();
let [sourceWidth, sourceHeight] = sourceActor.get_transformed_size();
this.actor.move_anchor_point(Math.floor(sourceX + sourceWidth / 2),
Math.floor(sourceY + sourceHeight / 2));
Tweener.addTween(this.actor, { scale_x: 1, scale_y: 1,
transition: 'easeOutQuad',
time: 0.2 });
@ -457,7 +464,7 @@ const ObjInspector = new Lang.Class({
_onInsert: function() {
let obj = this._obj;
this.close();
Main.lookingGlass.insertObject(obj);
this._lookingGlass.insertObject(obj);
},
_onBack: function() {
@ -465,34 +472,36 @@ const ObjInspector = new Lang.Class({
}
});
function addBorderPaintHook(actor) {
let signalId = actor.connect_after('paint',
function () {
let color = new Cogl.Color();
color.init_from_4ub(0xff, 0, 0, 0xc4);
Cogl.set_source_color(color);
const RedBorderEffect = new Lang.Class({
Name: 'RedBorderEffect',
Extends: Clutter.Effect,
let geom = actor.get_allocation_geometry();
let width = 2;
vfunc_paint: function() {
let actor = this.get_actor();
actor.continue_paint();
// clockwise order
Cogl.rectangle(0, 0, geom.width, width);
Cogl.rectangle(geom.width - width, width,
geom.width, geom.height);
Cogl.rectangle(0, geom.height,
geom.width - width, geom.height - width);
Cogl.rectangle(0, geom.height - width,
width, width);
});
let color = new Cogl.Color();
color.init_from_4ub(0xff, 0, 0, 0xc4);
Cogl.set_source_color(color);
actor.queue_redraw();
return signalId;
}
let geom = actor.get_allocation_geometry();
let width = 2;
// clockwise order
Cogl.rectangle(0, 0, geom.width, width);
Cogl.rectangle(geom.width - width, width,
geom.width, geom.height);
Cogl.rectangle(0, geom.height,
geom.width - width, geom.height - width);
Cogl.rectangle(0, geom.height - width,
width, width);
},
});
const Inspector = new Lang.Class({
Name: 'Inspector',
_init: function() {
_init: function(lookingGlass) {
let container = new Shell.GenericContainer({ width: 0,
height: 0 });
container.connect('allocate', Lang.bind(this, this._allocate));
@ -506,9 +515,6 @@ const Inspector = new Lang.Class({
this._displayText = new St.Label();
eventHandler.add(this._displayText, { expand: true });
this._borderPaintTarget = null;
this._borderPaintId = null;
eventHandler.connect('destroy', Lang.bind(this, this._onDestroy));
eventHandler.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
eventHandler.connect('button-press-event', Lang.bind(this, this._onButtonPressEvent));
eventHandler.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
@ -523,6 +529,8 @@ const Inspector = new Lang.Class({
// out, or move the pointer outside of _pointerTarget.
this._target = null;
this._pointerTarget = null;
this._lookingGlass = lookingGlass;
},
_allocate: function(actor, box, flags) {
@ -543,18 +551,13 @@ const Inspector = new Lang.Class({
},
_close: function() {
Clutter.ungrab_pointer(this._eventHandler);
Clutter.ungrab_keyboard(this._eventHandler);
Clutter.ungrab_pointer();
Clutter.ungrab_keyboard();
this._eventHandler.destroy();
this._eventHandler = null;
this.emit('closed');
},
_onDestroy: function() {
if (this._borderPaintTarget != null)
this._borderPaintTarget.disconnect(this._borderPaintId);
},
_onKeyPressEvent: function (actor, event) {
if (event.get_key_symbol() == Clutter.Escape)
this._close();
@ -623,56 +626,12 @@ const Inspector = new Lang.Class({
this._displayText.text = '';
this._displayText.text = position + ' ' + this._target;
if (this._borderPaintTarget != this._target) {
if (this._borderPaintTarget != null)
this._borderPaintTarget.disconnect(this._borderPaintId);
this._borderPaintTarget = this._target;
this._borderPaintId = addBorderPaintHook(this._target);
}
this._lookingGlass.setBorderPaintTarget(this._target);
}
});
Signals.addSignalMethods(Inspector.prototype);
const ErrorLog = new Lang.Class({
Name: 'ErrorLog',
_init: function() {
this.actor = new St.BoxLayout();
this.text = new St.Label();
this.actor.add(this.text);
// We need to override StLabel's default ellipsization when
// using line_wrap; otherwise ClutterText's layout is going
// to constrain both the width and height, which prevents
// scrolling.
this.text.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this.text.clutter_text.line_wrap = true;
this.actor.connect('notify::mapped', Lang.bind(this, this._renderText));
},
_formatTime: function(d){
function pad(n) { return n < 10 ? '0' + n : n; }
return d.getUTCFullYear()+'-'
+ pad(d.getUTCMonth()+1)+'-'
+ pad(d.getUTCDate())+'T'
+ pad(d.getUTCHours())+':'
+ pad(d.getUTCMinutes())+':'
+ pad(d.getUTCSeconds())+'Z';
},
_renderText: function() {
if (!this.actor.mapped)
return;
let text = this.text.text;
let stack = Main._getAndClearErrorStack();
for (let i = 0; i < stack.length; i++) {
let logItem = stack[i];
text += logItem.category + ' t=' + this._formatTime(new Date(logItem.timestamp)) + ' ' + logItem.message + '\n';
}
this.text.text = text;
}
});
const Memory = new Lang.Class({
Name: 'Memory',
@ -701,7 +660,7 @@ const Memory = new Lang.Class({
this._gcbutton = new St.Button({ label: 'Full GC',
style_class: 'lg-obj-inspector-button' });
this._gcbutton.connect('clicked', Lang.bind(this, function () { global.gc(); this._renderText(); }));
this._gcbutton.connect('clicked', Lang.bind(this, function () { System.gc(); this._renderText(); }));
this.actor.add(this._gcbutton, { x_align: St.Align.START,
x_fill: false });
@ -762,13 +721,13 @@ const Extensions = new Lang.Class({
let extension = actor._extension;
let uri = extension.dir.get_uri();
Gio.app_info_launch_default_for_uri(uri, global.create_app_launch_context());
Main.lookingGlass.close();
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());
Main.lookingGlass.close();
this._lookingGlass.close();
},
_onViewErrors: function (actor) {
@ -832,24 +791,33 @@ const Extensions = new Lang.Class({
text: this._stateToString(extension.state) });
metaBox.add(state);
let viewsource = new Link.Link({ label: _("View Source") });
viewsource.actor._extension = extension;
viewsource.actor.connect('clicked', Lang.bind(this, this._onViewSource));
metaBox.add(viewsource.actor);
let viewsource = new St.Button({ reactive: true,
track_hover: true,
style_class: 'shell-link',
label: _("View Source") });
viewsource._extension = extension;
viewsource.connect('clicked', Lang.bind(this, this._onViewSource));
metaBox.add(viewsource);
if (extension.metadata.url) {
let webpage = new Link.Link({ label: _("Web Page") });
webpage.actor._extension = extension;
webpage.actor.connect('clicked', Lang.bind(this, this._onWebPage));
metaBox.add(webpage.actor);
let webpage = new St.Button({ reactive: true,
track_hover: true,
style_class: 'shell-link',
label: _("Web Page") });
webpage._extension = extension;
webpage.connect('clicked', Lang.bind(this, this._onWebPage));
metaBox.add(webpage);
}
let viewerrors = new Link.Link({ label: _("Show Errors") });
viewerrors.actor._extension = extension;
viewerrors.actor._parentBox = box;
viewerrors.actor._isShowing = false;
viewerrors.actor.connect('clicked', Lang.bind(this, this._onViewErrors));
metaBox.add(viewerrors.actor);
let viewerrors = new St.Button({ reactive: true,
track_hover: true,
style_class: 'shell-link',
label: _("Show Errors") });
viewerrors._extension = extension;
viewerrors._parentBox = box;
viewerrors._isShowing = false;
viewerrors.connect('clicked', Lang.bind(this, this._onViewErrors));
metaBox.add(viewerrors);
return box;
}
@ -860,8 +828,7 @@ const LookingGlass = new Lang.Class({
_init : function() {
this._borderPaintTarget = null;
this._borderPaintId = 0;
this._borderDestroyId = 0;
this._redBorderEffect = new RedBorderEffect();
this._open = false;
@ -891,19 +858,18 @@ const LookingGlass = new Lang.Class({
Main.layoutManager.keyboardBox.connect('allocation-changed',
Lang.bind(this, this._queueResize));
this._objInspector = new ObjInspector();
this._objInspector = new ObjInspector(this);
Main.uiGroup.add_actor(this._objInspector.actor);
this._objInspector.actor.hide();
let toolbar = new St.BoxLayout({ name: 'Toolbar' });
this.actor.add_actor(toolbar);
let inspectIcon = new St.Icon({ icon_name: 'gtk-color-picker',
icon_type: St.IconType.FULLCOLOR,
icon_size: 24 });
toolbar.add_actor(inspectIcon);
inspectIcon.reactive = true;
inspectIcon.connect('button-press-event', Lang.bind(this, function () {
let inspector = new Inspector();
let inspector = new Inspector(this);
inspector.connect('target', Lang.bind(this, function(i, target, stageX, stageY) {
this._pushResult('<inspect x:' + stageX + ' y:' + stageY + '>',
target);
@ -933,23 +899,16 @@ const LookingGlass = new Lang.Class({
this._entryArea = new St.BoxLayout({ name: 'EntryArea' });
this._evalBox.add_actor(this._entryArea);
let label = new St.Label({ text: 'js>>> ' });
let label = new St.Label({ text: CHEVRON });
this._entryArea.add(label);
this._entry = new St.Entry({ can_focus: true });
ShellEntry.addContextMenu(this._entry);
this._entryArea.add(this._entry, { expand: true });
this._windowList = new WindowList();
this._windowList.connect('selected', Lang.bind(this, function(list, window) {
notebook.selectIndex(0);
this._pushResult('<window selection>', window);
}));
this._windowList = new WindowList(this);
notebook.appendPage('Windows', this._windowList.actor);
this._errorLog = new ErrorLog();
notebook.appendPage('Errors', this._errorLog.actor);
this._memory = new Memory();
notebook.appendPage('Memory', this._memory.actor);
@ -1001,23 +960,22 @@ const LookingGlass = new Lang.Class({
+ 'font-family: "' + fontDesc.get_family() + '";';
},
setBorderPaintTarget: function(obj) {
if (this._borderPaintTarget != null)
this._borderPaintTarget.remove_effect(this._redBorderEffect);
this._borderPaintTarget = obj;
if (this._borderPaintTarget != null)
this._borderPaintTarget.add_effect(this._redBorderEffect);
},
_pushResult: function(command, obj) {
let index = this._results.length + this._offset;
let result = new Result('>>> ' + command, obj, index);
let result = new Result(this, CHEVRON + command, obj, index);
this._results.push(result);
this._resultsArea.add(result.actor);
if (this._borderPaintTarget != null) {
this._borderPaintTarget.disconnect(this._borderPaintId);
this._borderPaintTarget = null;
}
if (obj instanceof Clutter.Actor) {
this._borderPaintTarget = obj;
this._borderPaintId = addBorderPaintHook(obj);
this._borderDestroyId = obj.connect('destroy', Lang.bind(this, function () {
this._borderDestroyId = 0;
this._borderPaintTarget = null;
}));
}
if (obj instanceof Clutter.Actor)
this.setBorderPaintTarget(obj);
let children = this._resultsArea.get_children();
if (children.length > this._maxItems) {
this._results.shift();
@ -1131,8 +1089,8 @@ const LookingGlass = new Lang.Class({
this.actor.width = myWidth;
this.actor.height = myHeight;
this._objInspector.actor.set_size(Math.floor(myWidth * 0.8), Math.floor(myHeight * 0.8));
this._objInspector.actor.set_position(this.actor.x + Math.floor(myWidth * 0.1),
this._targetY + Math.floor(myHeight * 0.1));
this._objInspector.actor.set_position(primary.x + this.actor.x + Math.floor(myWidth * 0.1),
primary.y + this._targetY + Math.floor(myHeight * 0.1));
},
insertObject: function(obj) {
@ -1198,11 +1156,7 @@ const LookingGlass = new Lang.Class({
this._open = false;
Tweener.removeTweens(this.actor);
if (this._borderPaintTarget != null) {
this._borderPaintTarget.disconnect(this._borderPaintId);
this._borderPaintTarget.disconnect(this._borderDestroyId);
this._borderPaintTarget = null;
}
this.setBorderPaintTarget(null);
Main.popModal(this._entry);

View File

@ -12,10 +12,11 @@ const Signals = imports.signals;
const Main = imports.ui.main;
const MagnifierDBus = imports.ui.magnifierDBus;
const Params = imports.misc.params;
const PointerWatcher = imports.ui.pointerWatcher;
const MOUSE_POLL_FREQUENCY = 50;
const CROSSHAIRS_CLIP_SIZE = [100, 100];
const NO_CHANGE = 0.0;
// Settings
const APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
@ -24,6 +25,14 @@ const SHOW_KEY = 'screen-magnifier-enabled';
const MAGNIFIER_SCHEMA = 'org.gnome.desktop.a11y.magnifier';
const SCREEN_POSITION_KEY = 'screen-position';
const MAG_FACTOR_KEY = 'mag-factor';
const INVERT_LIGHTNESS_KEY = 'invert-lightness';
const COLOR_SATURATION_KEY = 'color-saturation';
const BRIGHT_RED_KEY = 'brightness-red';
const BRIGHT_GREEN_KEY = 'brightness-green';
const BRIGHT_BLUE_KEY = 'brightness-blue';
const CONTRAST_RED_KEY = 'contrast-red';
const CONTRAST_GREEN_KEY = 'contrast-green';
const CONTRAST_BLUE_KEY = 'contrast-blue';
const LENS_MODE_KEY = 'lens-mode';
const CLAMP_MODE_KEY = 'scroll-at-edges';
const MOUSE_TRACKING_KEY = 'mouse-tracking';
@ -47,7 +56,7 @@ const Magnifier = new Lang.Class({
let xfixesCursor = Shell.XFixesCursor.get_for_stage(global.stage);
this._mouseSprite = new Clutter.Texture();
xfixesCursor.update_texture_image(this._mouseSprite);
this._cursorRoot = new Clutter.Group();
this._cursorRoot = new Clutter.Actor();
this._cursorRoot.add_actor(this._mouseSprite);
// Create the first ZoomRegion and initialize it according to the
@ -127,11 +136,8 @@ const Magnifier = new Lang.Class({
* Turn on mouse tracking, if not already doing so.
*/
startTrackingMouse: function() {
if (!this._mouseTrackingId)
this._mouseTrackingId = Mainloop.timeout_add(
MOUSE_POLL_FREQUENCY,
Lang.bind(this, this.scrollToMousePos)
);
if (!this._pointerWatch)
this._pointerWatch = PointerWatcher.getPointerWatcher().addWatch(MOUSE_POLL_FREQUENCY, Lang.bind(this, this.scrollToMousePos));
},
/**
@ -139,10 +145,10 @@ const Magnifier = new Lang.Class({
* Turn off mouse tracking, if not already doing so.
*/
stopTrackingMouse: function() {
if (this._mouseTrackingId)
Mainloop.source_remove(this._mouseTrackingId);
if (this._pointerWatch)
this._pointerWatch.remove();
this._mouseTrackingId = null;
this._pointerWatch = null;
},
/**
@ -294,8 +300,7 @@ const Magnifier = new Lang.Class({
*/
setCrosshairsColor: function(color) {
if (this._crossHairs) {
let clutterColor = new Clutter.Color();
clutterColor.from_string(color);
let [res, clutterColor] = Clutter.Color.from_string(color);
this._crossHairs.setColor(clutterColor);
}
},
@ -443,6 +448,25 @@ const Magnifier = new Lang.Class({
aPref = this._settings.get_enum(MOUSE_TRACKING_KEY);
if (aPref)
zoomRegion.setMouseTrackingMode(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);
}
let showCrosshairs = this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY);
@ -465,6 +489,25 @@ const Magnifier = new Lang.Class({
this._settings.connect('changed::' + MOUSE_TRACKING_KEY,
Lang.bind(this, this._updateMouseTrackingMode));
this._settings.connect('changed::' + INVERT_LIGHTNESS_KEY,
Lang.bind(this, this._updateInvertLightness));
this._settings.connect('changed::' + COLOR_SATURATION_KEY,
Lang.bind(this, this._updateColorSaturation));
this._settings.connect('changed::' + BRIGHT_RED_KEY,
Lang.bind(this, this._updateBrightness));
this._settings.connect('changed::' + BRIGHT_GREEN_KEY,
Lang.bind(this, this._updateBrightness));
this._settings.connect('changed::' + BRIGHT_BLUE_KEY,
Lang.bind(this, this._updateBrightness));
this._settings.connect('changed::' + CONTRAST_RED_KEY,
Lang.bind(this, this._updateContrast));
this._settings.connect('changed::' + CONTRAST_GREEN_KEY,
Lang.bind(this, this._updateContrast));
this._settings.connect('changed::' + CONTRAST_BLUE_KEY,
Lang.bind(this, this._updateContrast));
this._settings.connect('changed::' + SHOW_CROSS_HAIRS_KEY,
Lang.bind(this, function() {
this.setCrosshairsVisible(this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY));
@ -540,7 +583,47 @@ const Magnifier = new Lang.Class({
this._settings.get_enum(MOUSE_TRACKING_KEY)
);
}
}
},
_updateInvertLightness: function() {
// Applies only to the first zoom region.
if (this._zoomRegions.length) {
this._zoomRegions[0].setInvertLightness(
this._settings.get_boolean(INVERT_LIGHTNESS_KEY)
);
}
},
_updateColorSaturation: function() {
// Applies only to the first zoom region.
if (this._zoomRegions.length) {
this._zoomRegions[0].setColorSaturation(
this._settings.get_double(COLOR_SATURATION_KEY)
);
}
},
_updateBrightness: function() {
// Applies only to the first zoom region.
if (this._zoomRegions.length) {
let brightness = {};
brightness.r = this._settings.get_double(BRIGHT_RED_KEY);
brightness.g = this._settings.get_double(BRIGHT_GREEN_KEY);
brightness.b = this._settings.get_double(BRIGHT_BLUE_KEY);
this._zoomRegions[0].setBrightness(brightness);
}
},
_updateContrast: function() {
// Applies only to the first zoom region.
if (this._zoomRegions.length) {
let contrast = {};
contrast.r = this._settings.get_double(CONTRAST_RED_KEY);
contrast.g = this._settings.get_double(CONTRAST_GREEN_KEY);
contrast.b = this._settings.get_double(CONTRAST_BLUE_KEY);
this._zoomRegions[0].setContrast(contrast);
}
},
});
Signals.addSignalMethods(Magnifier.prototype);
@ -554,6 +637,10 @@ const ZoomRegion = new Lang.Class({
this._clampScrollingAtEdges = false;
this._lensMode = false;
this._screenPosition = GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN;
this._invertLightness = false;
this._colorSaturation = 1.0;
this._brightness = { r: NO_CHANGE, g: NO_CHANGE, b: NO_CHANGE };
this._contrast = { r: NO_CHANGE, g: NO_CHANGE, b: NO_CHANGE };
this._magView = null;
this._background = null;
@ -879,6 +966,107 @@ const ZoomRegion = new Lang.Class({
}
},
/**
* setInvertLightness:
* Set whether to invert the lightness of the magnified view.
* @flag Boolean to either invert brightness (true), or not (false).
*/
setInvertLightness: function(flag) {
this._invertLightness = flag;
if (this._magShaderEffects)
this._magShaderEffects.setInvertLightness(this._invertLightness);
},
/**
* getInvertLightness:
* Retrieve whether the lightness is inverted.
* @return Boolean indicating inversion (true), or not (false).
*/
getInvertLightness: function() {
return this._invertLightness;
},
/**
* setColorSaturation:
* Set the color saturation of the magnified view.
* @sauration A value from 0.0 to 1.0 that defines the color
* saturation, with 0.0 defining no color (grayscale),
* and 1.0 defining full color.
*/
setColorSaturation: function(saturation) {
this._colorSaturation = saturation;
if (this._magShaderEffects)
this._magShaderEffects.setColorSaturation(this._colorSaturation);
},
/**
* getColorSaturation:
* Retrieve the color saturation of the magnified view.
*/
getColorSaturation: function() {
return this._colorSaturation;
},
/**
* setBrightness:
* Alter the brightness of the magnified view.
* @brightness Object containing the contrast for the red, green,
* and blue channels. Values of 0.0 represent "standard"
* brightness (no change), whereas values less or greater than
* 0.0 indicate decreased or incresaed brightness, respectively.
*/
setBrightness: function(brightness) {
this._brightness.r = brightness.r;
this._brightness.g = brightness.g;
this._brightness.b = brightness.b;
if (this._magShaderEffects)
this._magShaderEffects.setBrightness(this._brightness);
},
/**
* getBrightness:
* Retrive the current brightness of the Zoom Region.
* @return Object containing the brightness change for the red, green,
* and blue channels.
*/
getBrightness: function() {
let brightness = {};
brightness.r = this._brightness.r;
brightness.g = this._brightness.g;
brightness.b = this._brightness.b;
return brightness;
},
/**
* setContrast:
* Alter the contrast of the magnified view.
* @contrast Object containing the contrast for the red, green,
* and blue channels. Values of 0.0 represent "standard"
* contrast (no change), whereas values less or greater than
* 0.0 indicate decreased or incresaed contrast, respectively.
*/
setContrast: function(contrast) {
this._contrast.r = contrast.r;
this._contrast.g = contrast.g;
this._contrast.b = contrast.b;
if (this._magShaderEffects)
this._magShaderEffects.setContrast(this._contrast);
},
/**
* getContrast:
* Retreive the contrast of the magnified view.
* @return Object containing the contrast for the red, green,
* and blue channels.
*/
getContrast: function() {
let contrast = {};
contrast.r = this._contrast.r;
contrast.g = this._contrast.g;
contrast.b = this._contrast.b;
return contrast;
},
//// Private methods ////
_createActors: function() {
@ -889,21 +1077,21 @@ const ZoomRegion = new Lang.Class({
// hide the magnified region from CLUTTER_PICK_ALL
Shell.util_set_hidden_from_pick (this._magView, true);
// Append a Clutter.Group to clip the contents of the magnified view.
let mainGroup = new Clutter.Group({ clip_to_allocation: true });
// Add a group to clip the contents of the magnified view.
let mainGroup = new Clutter.Actor({ clip_to_allocation: true });
this._magView.set_child(mainGroup);
// 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.Rectangle({ color: Main.DEFAULT_BACKGROUND_COLOR });
this._background = new Clutter.Actor({ background_color: Main.DEFAULT_BACKGROUND_COLOR,
width: global.screen_width,
height: global.screen_height });
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 });
mainGroup.add_actor(this._uiGroupClone);
Main.uiGroup.set_size(global.screen_width, global.screen_height);
this._background.set_size(global.screen_width, global.screen_height);
// Add either the given mouseSourceActor to the ZoomRegion, or a clone of
// it.
@ -917,6 +1105,13 @@ const ZoomRegion = new Lang.Class({
this._crossHairsActor = this._crossHairs.addToZoomRegion(this, this._mouseActor);
else
this._crossHairsActor = null;
// Contrast and brightness effects.
this._magShaderEffects = new MagShaderEffects(this._uiGroupClone);
this._magShaderEffects.setColorSaturation(this._colorSaturation);
this._magShaderEffects.setInvertLightness(this._invertLightness);
this._magShaderEffects.setBrightness(this._brightness);
this._magShaderEffects.setContrast(this._contrast);
},
_destroyActors: function() {
@ -925,6 +1120,8 @@ const ZoomRegion = new Lang.Class({
if (this._crossHairs)
this._crossHairs.removeFromParent(this._crossHairsActor);
this._magShaderEffects.destroyEffects();
this._magShaderEffects = null;
this._magView.destroy();
this._magView = null;
this._background = null;
@ -1128,7 +1325,7 @@ const ZoomRegion = new Lang.Class({
this._mouseActor.set_scale(this._xMagFactor, this._yMagFactor);
let [x, y] = this._screenToViewPort(0, 0);
this._uiGroupClone.set_position(x, y);
this._uiGroupClone.set_position(Math.round(x), Math.round(y));
this._updateMousePosition();
},
@ -1156,7 +1353,6 @@ const ZoomRegion = new Lang.Class({
if (!this.isActive())
return;
Main.uiGroup.set_size(global.screen_width, global.screen_height);
this._background.set_size(global.screen_width, global.screen_height);
if (this._screenPosition == GDesktopEnums.MagnifierScreenPosition.NONE)
@ -1180,15 +1376,15 @@ const Crosshairs = new Lang.Class({
let groupWidth = global.screen_width * 3;
let groupHeight = global.screen_height * 3;
this._actor = new Clutter.Group({
this._actor = new Clutter.Actor({
clip_to_allocation: false,
width: groupWidth,
height: groupHeight
});
this._horizLeftHair = new Clutter.Rectangle();
this._horizRightHair = new Clutter.Rectangle();
this._vertTopHair = new Clutter.Rectangle();
this._vertBottomHair = new Clutter.Rectangle();
this._horizLeftHair = new Clutter.Actor();
this._horizRightHair = new Clutter.Actor();
this._vertTopHair = new Clutter.Actor();
this._vertBottomHair = new Clutter.Actor();
this._actor.add_actor(this._horizLeftHair);
this._actor.add_actor(this._horizRightHair);
this._actor.add_actor(this._vertTopHair);
@ -1228,10 +1424,7 @@ const Crosshairs = new Lang.Class({
crosshairsActor = new Clutter.Clone({ source: this._actor });
this._clones.push(crosshairsActor);
}
if (this._actor.visible)
crosshairsActor.show();
else
crosshairsActor.hide();
crosshairsActor.visible = this._actor.visible;
container.add_actor(crosshairsActor);
container.raise_child(magnifiedMouse, crosshairsActor);
@ -1262,10 +1455,10 @@ const Crosshairs = new Lang.Class({
* @clutterColor: The color as a Clutter.Color.
*/
setColor: function(clutterColor) {
this._horizLeftHair.set_color(clutterColor);
this._horizRightHair.set_color(clutterColor);
this._vertTopHair.set_color(clutterColor);
this._vertBottomHair.set_color(clutterColor);
this._horizLeftHair.background_color = clutterColor;
this._horizRightHair.background_color = clutterColor;
this._vertTopHair.background_color = clutterColor;
this._vertBottomHair.background_color = clutterColor;
},
/**
@ -1274,9 +1467,7 @@ const Crosshairs = new Lang.Class({
* @color: The color as a Clutter.Color.
*/
getColor: function() {
let clutterColor = new Clutter.Color();
this._horizLeftHair.get_color(clutterColor);
return clutterColor;
return this._horizLeftHair.get_color();
},
/**
@ -1436,3 +1627,144 @@ const Crosshairs = new Lang.Class({
this._vertBottomHair.set_position((groupWidth - thickness) / 2, bottom);
}
});
const MagShaderEffects = new Lang.Class({
Name: 'MagShaderEffects',
_init: function(uiGroupClone) {
this._inverse = new Shell.InvertLightnessEffect();
this._brightnessContrast = new Clutter.BrightnessContrastEffect();
this._colorDesaturation = new Clutter.DesaturateEffect();
this._inverse.set_enabled(false);
this._brightnessContrast.set_enabled(false);
this._magView = uiGroupClone;
this._magView.add_effect(this._inverse);
this._magView.add_effect(this._brightnessContrast);
this._magView.add_effect(this._colorDesaturation);
},
/**
* destroyEffects:
* Remove contrast and brightness effects from the magnified view, and
* lose the reference to the actor they were applied to. Don't use this
* object after calling this.
*/
destroyEffects: function() {
this._magView.clear_effects();
this._colorDesaturation = null;
this._brightnessContrast = null;
this._inverse = null;
this._magView = null;
},
/**
* setInvertLightness:
* Enable/disable invert lightness effect.
* @invertFlag: Enabled flag.
*/
setInvertLightness: function(invertFlag) {
this._inverse.set_enabled(invertFlag);
},
/**
* getInvertLightness:
* Report whether the inversion effect is enabled.
* @return: Boolean.
*/
getInvertLightness: function() {
return this._inverse.get_enabled();
},
setColorSaturation: function(factor) {
this._colorDesaturation.set_factor(1.0 - factor);
},
getColorSaturation: function() {
return 1.0 - this._colorDesaturation.get_factor();
},
/**
* setBrightness:
* Set the brightness of the magnified view.
* @brightness: Object containing the brightness for the red, green,
* and blue channels. Values of 0.0 represent "standard"
* brightness (no change), whereas values less or greater than
* 0.0 indicate decreased or incresaed brightness,
* respectively.
*/
setBrightness: function(brightness) {
let bRed = brightness.r;
let bGreen = brightness.g;
let bBlue = brightness.b;
this._brightnessContrast.set_brightness_full(bRed, bGreen, bBlue);
// Enable the effect if the brightness OR contrast change are such that
// it modifies the brightness and/or contrast.
let [cRed, cGreen, cBlue] = this._brightnessContrast.get_contrast();
this._brightnessContrast.set_enabled(
(bRed != NO_CHANGE || bGreen != NO_CHANGE || bBlue != NO_CHANGE ||
cRed != NO_CHANGE || cGreen != NO_CHANGE || cBlue != NO_CHANGE)
);
},
/**
* getBrightness:
* Retrieve current brightness of the magnified view.
* @return: Object containing the brightness for the red, green,
* and blue channels. Values of 0.0 represent "standard"
* brightness (no change), whereas values less or greater than
* 0.0 indicate decreased or incresaed brightness, respectively.
*/
getBrightness: function() {
let result = {};
let [bRed, bGreen, bBlue] = this._brightnessContrast.get_brightness();
result.r = bRed;
result.g = bGreen;
result.b = bBlue;
return result;
},
/**
* Set the contrast of the magnified view.
* @contrast: Object containing the contrast for the red, green,
* and blue channels. Values of 0.0 represent "standard"
* contrast (no change), whereas values less or greater than
* 0.0 indicate decreased or incresaed contrast, respectively.
*/
setContrast: function(contrast) {
let cRed = contrast.r;
let cGreen = contrast.g;
let cBlue = contrast.b;
this._brightnessContrast.set_contrast_full(cRed, cGreen, cBlue);
// Enable the effect if the contrast OR brightness change are such that
// it modifies the brightness and/or contrast.
// should be able to use Clutter.color_equal(), but that complains of
// a null first argument.
let [bRed, bGreen, bBlue] = this._brightnessContrast.get_brightness();
this._brightnessContrast.set_enabled(
cRed != NO_CHANGE || cGreen != NO_CHANGE || cBlue != NO_CHANGE ||
bRed != NO_CHANGE || bGreen != NO_CHANGE || bBlue != NO_CHANGE
);
},
/**
* Retrieve current contrast of the magnified view.
* @return: Object containing the contrast for the red, green,
* and blue channels. Values of 0.0 represent "standard"
* contrast (no change), whereas values less or greater than
* 0.0 indicate decreased or incresaed contrast, respectively.
*/
getContrast: function() {
let resutl = {};
let [cRed, cGreen, cBlue] = this._brightnessContrast.get_contrast();
result.r = cRed;
result.g = cGreen;
result.b = cBlue;
return result;
}
});

View File

@ -10,158 +10,83 @@ const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const AutomountManager = imports.ui.automountManager;
const AutorunManager = imports.ui.autorunManager;
const Components = imports.ui.components;
const CtrlAltTab = imports.ui.ctrlAltTab;
const EndSessionDialog = imports.ui.endSessionDialog;
const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent;
const KeyringPrompt = imports.ui.keyringPrompt;
const Environment = imports.ui.environment;
const ExtensionSystem = imports.ui.extensionSystem;
const ExtensionDownloader = imports.ui.extensionDownloader;
const Keyboard = imports.ui.keyboard;
const MessageTray = imports.ui.messageTray;
const Overview = imports.ui.overview;
const Panel = imports.ui.panel;
const PlaceDisplay = imports.ui.placeDisplay;
const RunDialog = imports.ui.runDialog;
const Layout = imports.ui.layout;
const LookingGlass = imports.ui.lookingGlass;
const NetworkAgent = imports.ui.networkAgent;
const NotificationDaemon = imports.ui.notificationDaemon;
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
const ScreenShield = imports.ui.screenShield;
const Scripting = imports.ui.scripting;
const SessionMode = imports.ui.sessionMode;
const ShellDBus = imports.ui.shellDBus;
const TelepathyClient = imports.ui.telepathyClient;
const ShellMountOperation = imports.ui.shellMountOperation;
const UnlockDialog = imports.ui.unlockDialog;
const WindowManager = imports.ui.windowManager;
const Magnifier = imports.ui.magnifier;
const XdndHandler = imports.ui.xdndHandler;
const StatusIconDispatcher = imports.ui.statusIconDispatcher;
const Util = imports.misc.util;
const OVERRIDES_SCHEMA = 'org.gnome.shell.overrides';
const DEFAULT_BACKGROUND_COLOR = new Clutter.Color();
DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff);
const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
let automountManager = null;
let autorunManager = null;
let componentManager = null;
let panel = null;
let hotCorners = [];
let placesManager = null;
let overview = null;
let runDialog = null;
let lookingGlass = null;
let wm = null;
let messageTray = null;
let screenShield = null;
let notificationDaemon = null;
let windowAttentionHandler = null;
let telepathyClient = null;
let ctrlAltTabManager = null;
let recorder = null;
let sessionMode = null;
let shellDBusService = null;
let shellMountOpDBusService = null;
let screenSaverDBus = null;
let modalCount = 0;
let modalActorFocusStack = [];
let uiGroup = null;
let magnifier = null;
let xdndHandler = null;
let statusIconDispatcher = null;
let keyboard = null;
let layoutManager = null;
let networkAgent = null;
let _errorLogStack = [];
let _startDate;
let _defaultCssStylesheet = null;
let _cssStylesheet = null;
let _gdmCssStylesheet = null;
let _overridesSettings = null;
let background = null;
function _createUserSession() {
// Load the calendar server. Note that we are careful about
// not loading any events until the user presses the clock
global.launch_calendar_server();
placesManager = new PlaceDisplay.PlacesManager();
telepathyClient = new TelepathyClient.Client();
automountManager = new AutomountManager.AutomountManager();
autorunManager = new AutorunManager.AutorunManager();
networkAgent = new NetworkAgent.NetworkAgent();
}
function _createGDMSession() {
// We do this this here instead of at the top to prevent GDM
// related code from getting loaded in normal user sessions
const LoginDialog = imports.gdm.loginDialog;
let loginDialog = new LoginDialog.LoginDialog();
loginDialog.connect('loaded', function() {
loginDialog.open();
});
}
function _initRecorder() {
let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
global.screen.connect('toggle-recording', function() {
if (recorder == null) {
recorder = new Shell.Recorder({ stage: global.stage });
}
if (recorder.is_recording()) {
recorder.pause();
Meta.enable_unredirect_for_screen(global.screen);
} else {
// read the parameters from GSettings always in case they have changed
recorder.set_framerate(recorderSettings.get_int('framerate'));
/* Translators: this is a filename used for screencast recording */
// xgettext:no-c-format
recorder.set_filename(_("Screencast from %d %t") + '.' + recorderSettings.get_string('file-extension'));
let pipeline = recorderSettings.get_string('pipeline');
if (!pipeline.match(/^\s*$/))
recorder.set_pipeline(pipeline);
else
recorder.set_pipeline(null);
Meta.disable_unredirect_for_screen(global.screen);
recorder.record();
}
});
}
function _initUserSession() {
_initRecorder();
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, false, -1, 1);
ExtensionSystem.init();
ExtensionSystem.loadExtensions();
Meta.keybindings_set_custom_handler('panel-run-dialog', function() {
getRunDialog().open();
});
Meta.keybindings_set_custom_handler('panel-main-menu', function () {
overview.toggle();
});
global.display.connect('overlay-key', Lang.bind(overview, overview.toggle));
function _sessionUpdated() {
Meta.keybindings_set_custom_handler('panel-run-dialog', sessionMode.hasRunDialog ? openRunDialog : null);
if (sessionMode.isGreeter)
screenShield.showDialog();
}
function start() {
// Monkey patch utility functions into the global proxy;
// This is easier and faster than indirecting down into global
// if we want to call back up into JS.
global.logError = _logError;
global.log = _logDebug;
// These are here so we don't break compatibility.
global.logError = window.log;
global.log = window.log;
// Chain up async errors reported from C
global.connect('notify-error', function (global, msg, detail) { notifyError(msg, detail); });
Gio.DesktopAppInfo.set_desktop_env('GNOME');
sessionMode = new SessionMode.SessionMode();
shellDBusService = new ShellDBus.GnomeShell();
shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
// also initialize ShellAppSystem first. ShellAppSystem
@ -183,7 +108,6 @@ function start() {
global.stage.no_clear_hint = true;
_defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
_gdmCssStylesheet = global.datadir + '/theme/gdm.css';
loadTheme();
// Set up stage hierarchy to group all UI actors under one container.
@ -211,47 +135,40 @@ function start() {
layoutManager = new Layout.LayoutManager();
xdndHandler = new XdndHandler.XdndHandler();
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
// This overview object is just a stub for non-user sessions
overview = new Overview.Overview({ isDummy: global.session_type != Shell.SessionType.USER });
overview = new Overview.Overview();
magnifier = new Magnifier.Magnifier();
statusIconDispatcher = new StatusIconDispatcher.StatusIconDispatcher();
if (UnlockDialog.isSupported())
screenShield = new ScreenShield.ScreenShield();
else
screenShield = new ScreenShield.ScreenShieldFallback();
panel = new Panel.Panel();
wm = new WindowManager.WindowManager();
messageTray = new MessageTray.MessageTray();
keyboard = new Keyboard.Keyboard();
notificationDaemon = new NotificationDaemon.NotificationDaemon();
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
if (global.session_type == Shell.SessionType.USER)
_createUserSession();
else if (global.session_type == Shell.SessionType.GDM)
_createGDMSession();
panel.startStatusArea();
componentManager = new Components.ComponentManager();
layoutManager.init();
keyboard.init();
overview.init();
if (global.session_type == Shell.SessionType.USER)
_initUserSession();
statusIconDispatcher.start(messageTray.actor);
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
false, -1, 1);
Meta.keybindings_set_custom_handler('panel-main-menu', Lang.bind(overview, overview.toggle));
global.display.connect('overlay-key', Lang.bind(overview, overview.toggle));
sessionMode.connect('update', _sessionUpdated);
_sessionUpdated();
// Provide the bus object for gnome-session to
// initiate logouts.
EndSessionDialog.init();
// Attempt to become a PolicyKit authentication agent
PolkitAuthenticationAgent.init()
// Become a prompter for gnome keyring
KeyringPrompt.init();
_startDate = new Date();
global.stage.connect('captured-event', _globalKeyPressHandler);
_log('info', 'loaded at ' + _startDate);
log('GNOME Shell started at ' + _startDate);
let perfModuleName = GLib.getenv("SHELL_PERF_MODULE");
@ -271,6 +188,9 @@ function start() {
global.screen.connect('restacked', _windowsRestacked);
_nWorkspacesChanged();
ExtensionDownloader.init();
ExtensionSystem.init();
}
let _workspaces = [];
@ -494,9 +414,6 @@ function loadTheme() {
let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
if (global.session_type == Shell.SessionType.GDM)
theme.load_stylesheet(_gdmCssStylesheet);
if (previousTheme) {
let customStylesheets = previousTheme.get_custom_stylesheets();
@ -537,59 +454,6 @@ function notifyError(msg, details) {
notify(msg, details);
}
/**
* _log:
* @category: string message type ('info', 'error')
* @msg: A message string
* ...: Any further arguments are converted into JSON notation,
* and appended to the log message, separated by spaces.
*
* Log a message into the LookingGlass error
* stream. This is primarily intended for use by the
* extension system as well as debugging.
*/
function _log(category, msg) {
let text = msg;
if (arguments.length > 2) {
text += ': ';
for (let i = 2; i < arguments.length; i++) {
text += JSON.stringify(arguments[i]);
if (i < arguments.length - 1)
text += ' ';
}
}
_errorLogStack.push({timestamp: new Date().getTime(),
category: category,
message: text });
}
function _logError(msg) {
return _log('error', msg);
}
function _logDebug(msg) {
return _log('debug', msg);
}
// Used by the error display in lookingGlass.js
function _getAndClearErrorStack() {
let errors = _errorLogStack;
_errorLogStack = [];
return errors;
}
function logStackTrace(msg) {
try {
throw new Error();
} catch (e) {
// e.stack must have at least two lines, with the first being
// logStackTrace() (which we strip off), and the second being
// our caller.
let trace = e.stack.substr(e.stack.indexOf('\n') + 1);
log(msg ? (msg + '\n' + trace) : trace);
}
}
function isWindowActorDisplayedOnWorkspace(win, workspaceIndex) {
return win.get_workspace() == workspaceIndex ||
(win.get_meta_window() && win.get_meta_window().is_on_all_workspaces());
@ -612,6 +476,11 @@ function _globalKeyPressHandler(actor, event) {
if (event.type() != Clutter.EventType.KEY_PRESS)
return false;
if (!sessionMode.allowKeybindingsWhenModal) {
if (modalCount > (overview.visible ? 1 : 0))
return false;
}
let symbol = event.get_key_symbol();
let keyCode = event.get_key_code();
let ignoredModifiers = global.display.get_ignored_modifier_mask();
@ -620,48 +489,49 @@ function _globalKeyPressHandler(actor, event) {
// This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType
let action = global.display.get_keybinding_action(keyCode, modifierState);
// Other bindings are only available to the user session when the overview is up and
// no modal dialog is present.
if (global.session_type == Shell.SessionType.USER && (!overview.visible || modalCount > 1))
return false;
// This isn't a Meta.KeyBindingAction yet
if (symbol == Clutter.Super_L || symbol == Clutter.Super_R) {
overview.hide();
return true;
}
if (action == Meta.KeyBindingAction.SWITCH_PANELS) {
ctrlAltTabManager.popup(modifierState & Clutter.ModifierType.SHIFT_MASK,
modifierState);
return true;
}
// None of the other bindings are relevant outside of the user's session
if (global.session_type != Shell.SessionType.USER)
return false;
switch (action) {
// left/right would effectively act as synonyms for up/down if we enabled them;
// but that could be considered confusing; we also disable them in the main view.
//
// case Meta.KeyBindingAction.WORKSPACE_LEFT:
// if (!sessionMode.hasWorkspaces)
// return false;
//
// wm.actionMoveWorkspaceLeft();
// return true;
// case Meta.KeyBindingAction.WORKSPACE_RIGHT:
// if (!sessionMode.hasWorkspaces)
// return false;
//
// wm.actionMoveWorkspaceRight();
// return true;
case Meta.KeyBindingAction.WORKSPACE_UP:
wm.actionMoveWorkspaceUp();
if (!sessionMode.hasWorkspaces)
return false;
wm.actionMoveWorkspace(Meta.MotionDirection.UP);
return true;
case Meta.KeyBindingAction.WORKSPACE_DOWN:
wm.actionMoveWorkspaceDown();
if (!sessionMode.hasWorkspaces)
return false;
wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
return true;
case Meta.KeyBindingAction.PANEL_RUN_DIALOG:
case Meta.KeyBindingAction.COMMAND_2:
getRunDialog().open();
if (!sessionMode.hasRunDialog)
return false;
openRunDialog();
return true;
case Meta.KeyBindingAction.PANEL_MAIN_MENU:
case Meta.KeyBindingAction.OVERLAY_KEY:
overview.hide();
return true;
}
@ -677,6 +547,10 @@ function _findModal(actor) {
return -1;
}
function isInModalStack(actor) {
return _findModal(actor) != -1;
}
/**
* pushModal:
* @actor: #ClutterActor which will be given keyboard focus
@ -709,6 +583,7 @@ function pushModal(actor, timestamp, options) {
log('pushModal: invocation of begin_modal failed');
return false;
}
Meta.disable_unredirect_for_screen(global.screen);
}
global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
@ -717,7 +592,7 @@ function pushModal(actor, timestamp, options) {
let actorDestroyId = actor.connect('destroy', function() {
let index = _findModal(actor);
if (index >= 0)
modalActorFocusStack.splice(index, 1);
popModal(actor);
});
let curFocus = global.stage.get_key_focus();
let curFocusDestroyId;
@ -789,6 +664,7 @@ function popModal(actor, timestamp) {
global.end_modal(timestamp);
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
Meta.enable_unredirect_for_screen(global.screen);
}
function createLookingGlass() {
@ -798,11 +674,11 @@ function createLookingGlass() {
return lookingGlass;
}
function getRunDialog() {
function openRunDialog() {
if (runDialog == null) {
runDialog = new RunDialog.RunDialog();
}
return runDialog;
runDialog.open();
}
/**
@ -931,7 +807,8 @@ function initializeDeferredWork(actor, callback, props) {
function queueDeferredWork(workId) {
let data = _deferredWorkData[workId];
if (!data) {
global.logError('invalid work id ', workId);
let message = 'Invalid work id %d'.format(workId);
logError(new Error(message), message);
return;
}
if (_deferredWorkQueue.indexOf(workId) < 0)

File diff suppressed because it is too large Load Diff

View File

@ -14,6 +14,7 @@ const Atk = imports.gi.Atk;
const Params = imports.misc.params;
const Layout = imports.ui.layout;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
@ -35,17 +36,20 @@ const ModalDialog = new Lang.Class({
_init: function(params) {
params = Params.parse(params, { shellReactive: false,
styleClass: null });
styleClass: null,
parentActor: Main.uiGroup,
shouldFadeIn: true });
this.state = State.CLOSED;
this._hasModal = false;
this._shellReactive = params.shellReactive;
this._shouldFadeIn = params.shouldFadeIn;
this._group = new St.Widget({ visible: false,
x: 0,
y: 0,
accessible_role: Atk.Role.DIALOG });
Main.uiGroup.add_actor(this._group);
params.parentActor.add_actor(this._group);
let constraint = new Clutter.BindConstraint({ source: global.stage,
coordinate: Clutter.BindCoordinate.ALL });
@ -54,15 +58,17 @@ const ModalDialog = new Lang.Class({
this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy));
this._actionKeys = {};
this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
this._group.connect('key-release-event', Lang.bind(this, this._onKeyReleaseEvent));
this._backgroundBin = new St.Bin();
this._monitorConstraint = new Layout.MonitorConstraint();
this._backgroundBin.add_constraint(this._monitorConstraint);
this._group.add_actor(this._backgroundBin);
this._dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
vertical: true });
this.dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
vertical: true });
if (params.styleClass != null) {
this._dialogLayout.add_style_class_name(params.styleClass);
this.dialogLayout.add_style_class_name(params.styleClass);
}
if (!this._shellReactive) {
@ -75,29 +81,29 @@ const ModalDialog = new Lang.Class({
this._eventBlocker = new Clutter.Group({ reactive: true });
stack.add_actor(this._eventBlocker);
stack.add_actor(this._dialogLayout);
stack.add_actor(this.dialogLayout);
} else {
this._backgroundBin.child = this._dialogLayout;
this._backgroundBin.child = this.dialogLayout;
}
this.contentLayout = new St.BoxLayout({ vertical: true });
this._dialogLayout.add(this.contentLayout,
{ x_fill: true,
y_fill: true,
x_align: St.Align.MIDDLE,
y_align: St.Align.START });
this.dialogLayout.add(this.contentLayout,
{ x_fill: true,
y_fill: true,
x_align: St.Align.MIDDLE,
y_align: St.Align.START });
this._buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
visible: false,
vertical: false });
this._dialogLayout.add(this._buttonLayout,
{ expand: true,
x_align: St.Align.MIDDLE,
y_align: St.Align.END });
this.dialogLayout.add(this._buttonLayout,
{ expand: true,
x_align: St.Align.MIDDLE,
y_align: St.Align.END });
global.focus_manager.add_group(this._dialogLayout);
this._initialKeyFocus = this._dialogLayout;
global.focus_manager.add_group(this.dialogLayout);
this._initialKeyFocus = this.dialogLayout;
this._initialKeyFocusDestroyId = 0;
this._savedKeyFocus = null;
},
@ -106,6 +112,10 @@ const ModalDialog = new Lang.Class({
this._group.destroy();
},
setActionKey: function(key, action) {
this._actionKeys[key] = action;
},
setButtons: function(buttons) {
let hadChildren = this._buttonLayout.get_children() > 0;
@ -119,11 +129,17 @@ const ModalDialog = new Lang.Class({
let label = buttonInfo['label'];
let action = buttonInfo['action'];
let key = buttonInfo['key'];
let isDefault = buttonInfo['default'];
if (isDefault && !key)
key = Clutter.KEY_Return;
buttonInfo.button = new St.Button({ style_class: 'modal-dialog-button',
reactive: true,
can_focus: true,
label: label });
if (isDefault)
buttonInfo.button.add_style_pseudo_class('default');
let x_alignment;
if (buttons.length == 1)
@ -167,12 +183,16 @@ const ModalDialog = new Lang.Class({
},
_onKeyPressEvent: function(object, keyPressEvent) {
let symbol = keyPressEvent.get_key_symbol();
_onKeyReleaseEvent: function(object, event) {
let symbol = event.get_key_symbol();
let action = this._actionKeys[symbol];
if (action)
if (action) {
action();
return true;
}
return false;
},
_onGroupDestroy: function() {
@ -180,21 +200,18 @@ const ModalDialog = new Lang.Class({
},
_fadeOpen: function() {
let monitor = Main.layoutManager.focusMonitor;
this._backgroundBin.set_position(monitor.x, monitor.y);
this._backgroundBin.set_size(monitor.width, monitor.height);
this._monitorConstraint.index = global.screen.get_current_monitor();
this.state = State.OPENING;
this._dialogLayout.opacity = 255;
this.dialogLayout.opacity = 255;
if (this._lightbox)
this._lightbox.show();
this._group.opacity = 0;
this._group.show();
Tweener.addTween(this._group,
{ opacity: 255,
time: OPEN_AND_CLOSE_TIME,
time: this._shouldFadeIn ? OPEN_AND_CLOSE_TIME : 0,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
@ -211,7 +228,7 @@ const ModalDialog = new Lang.Class({
this._initialKeyFocus = actor;
this._initialKeyFocusDestroyId = actor.connect('destroy', Lang.bind(this, function() {
this._initialKeyFocus = this._dialogLayout;
this._initialKeyFocus = this.dialogLayout;
this._initialKeyFocusDestroyId = 0;
}));
},
@ -304,7 +321,7 @@ const ModalDialog = new Lang.Class({
return;
this.popModal(timestamp);
Tweener.addTween(this._dialogLayout,
Tweener.addTween(this.dialogLayout,
{ opacity: 0,
time: FADE_OUT_DIALOG_TIME,
transition: 'easeOutQuad',

View File

@ -1,6 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GdkPixbuf = imports.gi.GdkPixbuf;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
@ -87,6 +88,21 @@ const rewriteRules = {
]
};
const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
'bluetooth-applet': 'bluetooth',
'gnome-volume-control-applet': 'volume', // renamed to gnome-sound-applet
// when moved to control center
'gnome-sound-applet': 'volume',
'nm-applet': 'network',
'gnome-power-manager': 'battery',
'keyboard': 'keyboard',
'a11y-keyboard': 'a11y',
'kbd-scrolllock': 'keyboard',
'kbd-numlock': 'keyboard',
'kbd-capslock': 'keyboard',
'ibus-ui-gtk': 'keyboard'
};
const NotificationDaemon = new Lang.Class({
Name: 'NotificationDaemon',
@ -99,18 +115,19 @@ const NotificationDaemon = new Lang.Class({
this._notifications = {};
this._busProxy = new Bus();
Main.statusIconDispatcher.connect('message-icon-added', Lang.bind(this, this._onTrayIconAdded));
Main.statusIconDispatcher.connect('message-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
this._trayManager = new Shell.TrayManager();
this._trayManager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded));
this._trayManager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
Shell.WindowTracker.get_default().connect('notify::focus-app',
Lang.bind(this, this._onFocusAppChanged));
Main.overview.connect('hidden',
Lang.bind(this, this._onFocusAppChanged));
this._trayManager.manage_stage(global.stage, Main.messageTray.actor);
},
_iconForNotificationData: function(icon, hints, size) {
let textureCache = St.TextureCache.get_default();
_iconForNotificationData: function(icon, hints) {
// If an icon is not specified, we use 'image-data' or 'image-path' hint for an icon
// and don't show a large image. There are currently many applications that use
// notify_notification_set_icon_from_pixbuf() from libnotify, which in turn sets
@ -121,20 +138,18 @@ const NotificationDaemon = new Lang.Class({
// a large image.
if (icon) {
if (icon.substr(0, 7) == 'file://')
return textureCache.load_uri_async(icon, size, size);
return new Gio.FileIcon({ file: Gio.File.new_for_uri(icon) });
else if (icon[0] == '/') {
let uri = GLib.filename_to_uri(icon, null);
return textureCache.load_uri_async(uri, size, size);
return new Gio.FileIcon({ file: Gio.File.new_for_path(icon) });
} else
return new St.Icon({ icon_name: icon,
icon_type: St.IconType.FULLCOLOR,
icon_size: size });
return new Gio.ThemedIcon({ name: icon });
} else if (hints['image-data']) {
let [width, height, rowStride, hasAlpha,
bitsPerSample, nChannels, data] = hints['image-data'];
return textureCache.load_from_raw(data, hasAlpha, width, height, rowStride, size);
return Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha,
bitsPerSample, width, height, rowStride);
} else if (hints['image-path']) {
return textureCache.load_uri_async(GLib.filename_to_uri(hints['image-path'], null), size, size);
return new Gio.FileIcon({ file: Gio.File.new_for_path(hints['image-path']) });
} else {
let stockIcon;
switch (hints.urgency) {
@ -146,9 +161,7 @@ const NotificationDaemon = new Lang.Class({
stockIcon = 'gtk-dialog-error';
break;
}
return new St.Icon({ icon_name: stockIcon,
icon_type: St.IconType.FULLCOLOR,
icon_size: size });
return new Gio.ThemedIcon({ name: stockIcon });
}
},
@ -221,12 +234,19 @@ const NotificationDaemon = new Lang.Class({
let [appName, replacesId, icon, summary, body, actions, hints, timeout] = params;
let id;
for (let hint in hints) {
// unpack the variants
hints[hint] = hints[hint].deep_unpack();
}
hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
// Filter out chat, presence, calls and invitation notifications from
// Empathy, since we handle that information from telepathyClient.js
if (appName == 'Empathy' && (hints['category'] == 'im.received' ||
hints['category'] == 'x-empathy.im.room-invitation' ||
hints['category'] == 'x-empathy.call.incoming' ||
hints['category'] == 'x-empathy.call.incoming"' ||
hints['category'] == 'x-empathy.transfer.incoming' ||
hints['category'] == 'x-empathy.im.subscription-request' ||
hints['category'] == 'presence.online' ||
hints['category'] == 'presence.offline')) {
@ -236,6 +256,7 @@ const NotificationDaemon = new Lang.Class({
Mainloop.idle_add(Lang.bind(this,
function () {
this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
return false;
}));
return invocation.return_value(GLib.Variant.new('(u)', [id]));
}
@ -249,13 +270,6 @@ const NotificationDaemon = new Lang.Class({
}
}
for (let hint in hints) {
// unpack the variants
hints[hint] = hints[hint].deep_unpack();
}
hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
// Be compatible with the various hints for image data and image path
// 'image-data' and 'image-path' are the latest name of these hints, introduced in 1.2
@ -341,7 +355,9 @@ const NotificationDaemon = new Lang.Class({
[ndata.id, ndata.icon, ndata.summary, ndata.body,
ndata.actions, ndata.hints, ndata.notification];
let iconActor = this._iconForNotificationData(icon, hints, source.ICON_SIZE);
let gicon = this._iconForNotificationData(icon, hints);
let iconActor = new St.Icon({ gicon: gicon,
icon_size: MessageTray.NOTIFICATION_ICON_SIZE });
if (notification == null) {
notification = new MessageTray.Notification(source, summary, body,
@ -421,8 +437,8 @@ const NotificationDaemon = new Lang.Class({
// of the 'transient' hint with hints['transient'] rather than hints.transient
notification.setTransient(hints['transient'] == true);
let sourceIconActor = source.useNotificationIcon ? this._iconForNotificationData(icon, hints, source.ICON_SIZE) : null;
source.processNotification(notification, sourceIconActor);
let sourceGIcon = source.useNotificationIcon ? this._iconForNotificationData(icon, hints) : null;
source.processNotification(notification, sourceGIcon);
},
CloseNotification: function(id) {
@ -483,7 +499,11 @@ const NotificationDaemon = new Lang.Class({
},
_onTrayIconAdded: function(o, icon) {
let source = this._getSource(icon.title || icon.wm_class || _("Unknown"), icon.pid, null, null, icon);
let wmClass = icon.wm_class ? icon.wm_class.toLowerCase() : '';
if (STANDARD_TRAY_ICON_IMPLEMENTATIONS[wmClass] !== undefined)
return;
let source = this._getSource(icon.title || icon.wm_class || C_("program", "Unknown"), icon.pid, null, null, icon);
},
_onTrayIconRemoved: function(o, icon) {
@ -534,11 +554,11 @@ const Source = new Lang.Class({
this.destroy();
},
processNotification: function(notification, icon) {
if (!this.app)
this._setApp();
if (!this.app && icon)
this._setSummaryIcon(icon);
processNotification: function(notification, gicon) {
if (gicon)
this._gicon = gicon;
if (!this.trayIcon)
this.iconUpdated();
let tracker = Shell.WindowTracker.get_default();
if (notification.resident && this.app && tracker.focus_app == this.app)
@ -563,26 +583,36 @@ const Source = new Lang.Class({
this.notifications.length > 0)
return false;
if (Main.overview.visible) {
// We can't just connect to Main.overview's 'hidden' signal,
// because it's emitted *before* it calls popModal()...
let id = global.connect('notify::stage-input-mode', Lang.bind(this,
function () {
global.disconnect(id);
this.trayIcon.click(event);
}));
Main.overview.hide();
} else {
let id = global.connect('notify::stage-input-mode', Lang.bind(this, function () {
global.disconnect(id);
this.trayIcon.click(event);
}
}));
Main.overview.hide();
return true;
},
_getApp: function() {
let app;
app = Shell.WindowTracker.get_default().get_app_from_pid(this.pid);
if (app != null)
return app;
if (this.trayIcon) {
app = Shell.AppSystem.get_default().lookup_wmclass(this.trayIcon.wmclass);
if (app != null)
return app;
}
return null;
},
_setApp: function() {
if (this.app)
return;
this.app = Shell.WindowTracker.get_default().get_app_from_pid(this.pid);
this.app = this._getApp();
if (!this.app)
return;
@ -590,10 +620,20 @@ const Source = new Lang.Class({
// notification-based icons (ie, not a trayicon) or if it was unset before
if (!this.trayIcon) {
this.useNotificationIcon = false;
this._setSummaryIcon(this.app.create_icon_texture (this.ICON_SIZE));
this.iconUpdated();
}
},
setTitle: function(title) {
// Do nothing if .app is set, we don't want to override the
// app name with whatever is provided through libnotify (usually
// garbage)
if (this.app)
return;
this.parent(title);
},
open: function(notification) {
this.destroyNonResidentNotifications();
this.openApp();
@ -622,5 +662,20 @@ const Source = new Lang.Class({
}
this.parent();
},
createIcon: function(size) {
if (this.trayIcon) {
return new Clutter.Clone({ width: size,
height: size,
source: this.trayIcon });
} else if (this.app) {
return this.app.create_icon_texture(size);
} else if (this._gicon) {
return new St.Icon({ gicon: this._gicon,
icon_size: size });
} else {
return null;
}
}
});

View File

@ -10,21 +10,14 @@ const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Gdk = imports.gi.Gdk;
const AppDisplay = imports.ui.appDisplay;
const ContactDisplay = imports.ui.contactDisplay;
const Dash = imports.ui.dash;
const DND = imports.ui.dnd;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const Panel = imports.ui.panel;
const Params = imports.misc.params;
const PlaceDisplay = imports.ui.placeDisplay;
const RemoteSearch = imports.ui.remoteSearch;
const Tweener = imports.ui.tweener;
const ViewSelector = imports.ui.viewSelector;
const Wanda = imports.ui.wanda;
const WorkspacesView = imports.ui.workspacesView;
const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
// Time for initial animation going into Overview mode
@ -77,13 +70,12 @@ const ShellInfo = new Lang.Class({
let notification = null;
if (this._source.notifications.length == 0) {
notification = new MessageTray.Notification(this._source, text, null);
notification.setTransient(true);
} else {
notification = this._source.notifications[0];
notification.update(text, null, { clear: true });
}
notification.setTransient(true);
this._undoCallback = undoCallback;
if (undoCallback) {
notification.addButton('system-undo',
@ -99,18 +91,21 @@ const ShellInfo = new Lang.Class({
const Overview = new Lang.Class({
Name: 'Overview',
_init : function(params) {
params = Params.parse(params, { isDummy: false });
_init: function() {
this._overviewCreated = false;
this.isDummy = params.isDummy;
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._sessionUpdated();
},
// We only have an overview in user sessions, so
// create a dummy overview in other cases
if (this.isDummy) {
this.animationInProgress = false;
this.visible = false;
_createOverview: function() {
if (this._overviewCreated)
return;
}
if (this.isDummy)
return;
this._overviewCreated = true;
// The main BackgroundActor is inside global.window_group which is
// hidden when displaying the overview, so we create a new
@ -147,8 +142,6 @@ const Overview = new Lang.Class({
this._capturedEventId = 0;
this._buttonPressId = 0;
this._workspacesDisplay = null;
this.visible = false; // animating to overview, in overview, animating out
this._shown = false; // show() and not hide()
this._shownTemporarily = false; // showTemporarily() and not hideTemporarily()
@ -185,6 +178,11 @@ const Overview = new Lang.Class({
this._needsFakePointerEvent = false;
},
_sessionUpdated: function() {
this.isDummy = !Main.sessionMode.hasOverview;
this._createOverview();
},
// The members we construct that are implemented in JS might
// want to access the overview as Main.overview to connect
// signal handlers and so forth. So we create them after
@ -195,30 +193,23 @@ const Overview = new Lang.Class({
this._shellInfo = new ShellInfo();
this._viewSelector = new ViewSelector.ViewSelector();
this._searchEntry = new St.Entry({ name: 'searchEntry',
/* Translators: this is the text displayed
in the search entry when no search is
active; it should not exceed ~30
characters. */
hint_text: _("Type to search..."),
track_hover: true,
can_focus: true });
this._group.add_actor(this._searchEntry);
this._dash = new Dash.Dash();
this._viewSelector = new ViewSelector.ViewSelector(this._searchEntry,
this._dash.showAppsButton);
this._group.add_actor(this._viewSelector.actor);
this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay();
this._viewSelector.addViewTab('windows', _("Windows"), this._workspacesDisplay.actor, 'text-x-generic');
let appView = new AppDisplay.AllAppDisplay();
this._viewSelector.addViewTab('applications', _("Applications"), appView.actor, 'system-run');
// Default search providers
// Wanda comes obviously first
this.addSearchProvider(new Wanda.WandaSearchProvider());
this.addSearchProvider(new AppDisplay.AppSearchProvider());
this.addSearchProvider(new AppDisplay.SettingsSearchProvider());
this.addSearchProvider(new PlaceDisplay.PlaceSearchProvider());
this.addSearchProvider(new ContactDisplay.ContactSearchProvider());
// Load remote search providers provided by applications
RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider));
this._group.add_actor(this._dash.actor);
// TODO - recalculate everything when desktop size changes
this._dash = new Dash.Dash();
this._group.add_actor(this._dash.actor);
this._dash.actor.add_constraint(this._viewSelector.constrainY);
this._dash.actor.add_constraint(this._viewSelector.constrainHeight);
this.dashIconSize = this._dash.iconSize;
this._dash.connect('icon-size-changed',
@ -228,7 +219,7 @@ const Overview = new Lang.Class({
// Translators: this is the name of the dock/favorites area on
// the left of the overview
Main.ctrlAltTabManager.addGroup(this._dash.actor, _("Dash"), 'user-bookmarks');
Main.ctrlAltTabManager.addGroup(this._dash.actor, _("Dash"), 'user-bookmarks-symbolic');
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
this._relayout();
@ -504,13 +495,13 @@ const Overview = new Lang.Class({
this._coverPane.set_position(0, contentY);
this._coverPane.set_size(primary.width, contentHeight);
let dashWidth = Math.round(DASH_SPLIT_FRACTION * primary.width);
let viewWidth = primary.width - dashWidth - this._spacing;
let viewHeight = contentHeight - 2 * this._spacing;
let viewY = contentY + this._spacing;
let viewX = rtl ? 0 : dashWidth + this._spacing;
let searchWidth = this._searchEntry.get_width();
let searchHeight = this._searchEntry.get_height();
let searchX = (primary.width - searchWidth) / 2;
let searchY = contentY + this._spacing;
// Set the dash's x position - y is handled by a constraint
let dashWidth = Math.round(DASH_SPLIT_FRACTION * primary.width);
let dashY = searchY + searchHeight + this._spacing;
let dashX;
if (rtl) {
this._dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
@ -518,8 +509,14 @@ const Overview = new Lang.Class({
} else {
dashX = 0;
}
this._dash.actor.set_x(dashX);
let viewX = rtl ? 0 : dashWidth + this._spacing;
let viewY = searchY + searchHeight + this._spacing;
let viewWidth = primary.width - dashWidth - this._spacing;
let viewHeight = contentHeight - this._spacing - viewY;
this._searchEntry.set_position(searchX, searchY);
this._dash.actor.set_position(dashX, dashY);
this._viewSelector.actor.set_position(viewX, viewY);
this._viewSelector.actor.set_size(viewWidth, viewHeight);
},
@ -569,6 +566,28 @@ const Overview = new Lang.Class({
Lang.bind(this, this._onButtonPress));
},
fadeInDesktop: function() {
this._desktopFade.opacity = 0;
this._desktopFade.show();
Tweener.addTween(this._desktopFade,
{ opacity: 255,
time: ANIMATION_TIME,
transition: 'easeOutQuad' });
},
fadeOutDesktop: function() {
if (!this._desktopFade.child)
this._desktopFade.child = this._getDesktopClone();
this._desktopFade.opacity = 255;
this._desktopFade.show();
Tweener.addTween(this._desktopFade,
{ opacity: 0,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
},
_animateVisible: function() {
if (this.visible || this.animationInProgress)
return;
@ -589,21 +608,7 @@ const Overview = new Lang.Class({
global.window_group.hide();
this._group.show();
this._background.show();
this._workspacesDisplay.show();
if (!this._desktopFade.child)
this._desktopFade.child = this._getDesktopClone();
if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows()) {
this._desktopFade.opacity = 255;
this._desktopFade.show();
Tweener.addTween(this._desktopFade,
{ opacity: 0,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
}
this._viewSelector.show();
this._group.opacity = 0;
Tweener.addTween(this._group,
@ -730,16 +735,7 @@ const Overview = new Lang.Class({
this.animationInProgress = true;
this._hideInProgress = true;
if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows()) {
this._desktopFade.opacity = 0;
this._desktopFade.show();
Tweener.addTween(this._desktopFade,
{ opacity: 255,
time: ANIMATION_TIME,
transition: 'easeOutQuad' });
}
this._workspacesDisplay.zoomFromOverview();
this._viewSelector.zoomFromOverview();
// Make other elements fade out.
Tweener.addTween(this._group,
@ -781,8 +777,7 @@ const Overview = new Lang.Class({
global.window_group.show();
this._workspacesDisplay.hide();
this._viewSelector.hide();
this._desktopFade.hide();
this._background.hide();
this._group.hide();

View File

@ -14,6 +14,7 @@ const St = imports.gi.St;
const Signals = imports.signals;
const Atk = imports.gi.Atk;
const Config = imports.misc.config;
const CtrlAltTab = imports.ui.ctrlAltTab;
const DND = imports.ui.dnd;
@ -21,7 +22,6 @@ const Layout = imports.ui.layout;
const Overview = imports.ui.overview;
const PopupMenu = imports.ui.popupMenu;
const PanelMenu = imports.ui.panelMenu;
const DateMenu = imports.ui.dateMenu;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
@ -32,33 +32,6 @@ const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
const SPINNER_ANIMATION_TIME = 0.2;
const STANDARD_STATUS_AREA_ORDER = ['a11y', 'keyboard', 'volume', 'bluetooth', 'network', 'battery', 'userMenu'];
const STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION = {
'a11y': imports.ui.status.accessibility.ATIndicator,
'volume': imports.ui.status.volume.Indicator,
'battery': imports.ui.status.power.Indicator,
'keyboard': imports.ui.status.keyboard.XKBIndicator,
'userMenu': imports.ui.userMenu.UserMenuButton
};
if (Config.HAVE_BLUETOOTH)
STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION['bluetooth'] = imports.ui.status.bluetooth.Indicator;
try {
STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION['network'] = imports.ui.status.network.NMApplet;
} catch(e) {
log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
}
const GDM_STATUS_AREA_ORDER = ['a11y', 'display', 'keyboard', 'volume', 'battery', 'powerMenu'];
const GDM_STATUS_AREA_SHELL_IMPLEMENTATION = {
'a11y': imports.ui.status.accessibility.ATIndicator,
'volume': imports.ui.status.volume.Indicator,
'battery': imports.ui.status.power.Indicator,
'keyboard': imports.ui.status.keyboard.XKBIndicator,
'powerMenu': imports.gdm.powerMenu.PowerMenuButton
};
// To make sure the panel corners blend nicely with the panel,
// we draw background and borders the same way, e.g. drawing
// them as filled shapes from the outside inwards instead of
@ -249,14 +222,14 @@ const AppMenuButton = new Lang.Class({
Name: 'AppMenuButton',
Extends: PanelMenu.Button,
_init: function(menuManager) {
_init: function(panel) {
this.parent(0.0, null, true);
this.actor.accessible_role = Atk.Role.MENU;
this._startingApps = [];
this._menuManager = menuManager;
this._menuManager = panel.menuManager;
this._targetApp = null;
this._appMenuNotifyId = 0;
this._actionGroupNotifyId = 0;
@ -313,7 +286,7 @@ const AppMenuButton = new Lang.Class({
},
show: function() {
if (this._visible)
if (this._visible || Main.screenShield.locked)
return;
this._visible = true;
@ -374,6 +347,7 @@ const AppMenuButton = new Lang.Class({
return;
this._stop = true;
this.actor.reactive = true;
Tweener.addTween(this._spinner.actor,
{ opacity: 0,
time: SPINNER_ANIMATION_TIME,
@ -388,6 +362,7 @@ const AppMenuButton = new Lang.Class({
startAnimation: function() {
this._stop = false;
this.actor.reactive = false;
this._spinner.actor.show();
},
@ -626,8 +601,8 @@ const ActivitiesButton = new Lang.Class({
this.actor.label_actor = this._label;
this._hotCorner = new Layout.HotCorner();
container.add_actor(this._hotCorner.actor);
this.hotCorner = new Layout.HotCorner();
container.add_actor(this.hotCorner.actor);
// Hack up our menu...
this.menu.open = Lang.bind(this, this._onMenuOpenRequest);
@ -677,10 +652,10 @@ const ActivitiesButton = new Lang.Class({
}
hotBox.x1 = Math.round(x);
hotBox.x2 = hotBox.x1 + this._hotCorner.actor.width;
hotBox.x2 = hotBox.x1 + this.hotCorner.actor.width;
hotBox.y1 = Math.round(y);
hotBox.y2 = hotBox.y1 + this._hotCorner.actor.height;
this._hotCorner.actor.allocate(hotBox, flags);
hotBox.y2 = hotBox.y1 + this.hotCorner.actor.height;
this.hotCorner.actor.allocate(hotBox, flags);
},
handleDragOver: function(source, actor, x, y, time) {
@ -702,7 +677,7 @@ const ActivitiesButton = new Lang.Class({
_onCapturedEvent: function(actor, event) {
if (event.type() == Clutter.EventType.BUTTON_PRESS) {
if (!this._hotCorner.shouldToggleOverviewOnClick())
if (!this.hotCorner.shouldToggleOverviewOnClick())
return true;
}
return false;
@ -779,10 +754,11 @@ const PanelCorner = new Lang.Class({
return null;
// Start at the back and work backward
let index = children.length - 1;
while (!children[index].visible && index >= 0)
index--;
let index;
for (index = children.length - 1; index >= 0; index--) {
if (children[index].visible)
break;
}
if (index < 0)
return null;
@ -803,10 +779,11 @@ const PanelCorner = new Lang.Class({
return null;
// Start at the front and work forward
let index = 0;
while (!children[index].visible && index < children.length)
index++;
let index;
for (index = 0; index < children.length; index++) {
if (children[index].visible)
break;
}
if (index == children.length)
return null;
@ -916,6 +893,29 @@ const PanelCorner = new Lang.Class({
}
});
const PANEL_ITEM_IMPLEMENTATIONS = {
'activities': ActivitiesButton,
'appMenu': AppMenuButton,
'dateMenu': imports.ui.dateMenu.DateMenuButton,
'a11y': imports.ui.status.accessibility.ATIndicator,
'volume': imports.ui.status.volume.Indicator,
'battery': imports.ui.status.power.Indicator,
'lockScreen': imports.ui.status.lockScreenMenu.Indicator,
'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
'powerMenu': imports.gdm.powerMenu.PowerMenuButton,
'userMenu': imports.ui.userMenu.UserMenuButton
};
if (Config.HAVE_BLUETOOTH)
PANEL_ITEM_IMPLEMENTATIONS['bluetooth'] =
imports.ui.status.bluetooth.Indicator;
try {
PANEL_ITEM_IMPLEMENTATIONS['network'] =
imports.ui.status.network.NMApplet;
} catch(e) {
log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
}
const Panel = new Lang.Class({
Name: 'Panel',
@ -925,7 +925,7 @@ const Panel = new Lang.Class({
reactive: true });
this.actor._delegate = this;
this._statusArea = {};
this.statusArea = {};
Main.overview.connect('shown', Lang.bind(this, function () {
this.actor.add_style_class_name('in-overview');
@ -934,7 +934,7 @@ const Panel = new Lang.Class({
this.actor.remove_style_class_name('in-overview');
}));
this._menus = new PopupMenu.PopupMenuManager(this);
this.menuManager = new PopupMenu.PopupMenuManager(this);
this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
this.actor.add_actor(this._leftBox);
@ -961,43 +961,12 @@ const Panel = new Lang.Class({
this.actor.connect('allocate', Lang.bind(this, this._allocate));
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
/* Button on the left side of the panel. */
if (global.session_type == Shell.SessionType.USER) {
this._activitiesButton = new ActivitiesButton();
this._activities = this._activitiesButton.actor;
this._leftBox.add(this._activities);
// The activities button has a pretend menu, so as to integrate
// more cleanly with the rest of the panel
this._menus.addMenu(this._activitiesButton.menu);
this._appMenu = new AppMenuButton(this._menus);
this._leftBox.add(this._appMenu.actor);
}
/* center */
if (global.session_type == Shell.SessionType.USER)
this._dateMenu = new DateMenu.DateMenuButton({ showEvents: true });
else
this._dateMenu = new DateMenu.DateMenuButton({ showEvents: false });
this._centerBox.add(this._dateMenu.actor, { y_fill: true });
this._menus.addMenu(this._dateMenu.menu);
/* right */
if (global.session_type == Shell.SessionType.GDM) {
this._status_area_order = GDM_STATUS_AREA_ORDER;
this._status_area_shell_implementation = GDM_STATUS_AREA_SHELL_IMPLEMENTATION;
} else {
this._status_area_order = STANDARD_STATUS_AREA_ORDER;
this._status_area_shell_implementation = STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION;
}
Main.statusIconDispatcher.connect('status-icon-added', Lang.bind(this, this._onTrayIconAdded));
Main.statusIconDispatcher.connect('status-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
Main.layoutManager.panelBox.add(this.actor);
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'start-here',
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'start-here-symbolic',
{ sortGroup: CtrlAltTab.SortGroup.TOP });
Main.sessionMode.connect('updated', Lang.bind(this, this._updatePanel));
this._updatePanel();
},
_getPreferredWidth: function(actor, forHeight, alloc) {
@ -1114,84 +1083,126 @@ const Panel = new Lang.Class({
},
openAppMenu: function() {
let menu = this._appMenu.menu;
if (Main.overview.visible || menu.isOpen)
return;
let indicator = this.statusArea.appMenu;
if (!indicator) // appMenu not supported by current session mode
return;
let menu = indicator.menu;
if (!indicator.actor.reactive || menu.isOpen)
return;
menu.open();
menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
},
startStatusArea: function() {
for (let i = 0; i < this._status_area_order.length; i++) {
let role = this._status_area_order[i];
let constructor = this._status_area_shell_implementation[role];
set boxOpacity(value) {
let isReactive = value > 0;
this._leftBox.opacity = value;
this._leftBox.reactive = isReactive;
this._centerBox.opacity = value;
this._centerBox.reactive = isReactive;
this._rightBox.opacity = value;
this._rightBox.reactive = isReactive;
},
get boxOpacity() {
return this._leftBox.opacity;
},
_updatePanel: function() {
let panel = Main.sessionMode.panel;
this._hideIndicators();
this._updateBox(panel.left, this._leftBox);
this._updateBox(panel.center, this._centerBox);
this._updateBox(panel.right, this._rightBox);
},
_initBox: function(elements, box) {
for (let i = 0; i < elements.length; i++) {
let role = elements[i];
let constructor = PANEL_ITEM_IMPLEMENTATIONS[role];
if (!constructor) {
// This icon is not implemented (this is a bug)
// panel icon is not supported (can happen for
// bluetooth or network)
continue;
}
let indicator = new constructor();
this.addToStatusArea(role, indicator, i);
}
},
_insertStatusItem: function(actor, position) {
let children = this._rightBox.get_children();
let i;
for (i = children.length - 1; i >= 0; i--) {
let rolePosition = children[i]._rolePosition;
if (position > rolePosition) {
this._rightBox.insert_child_at_index(actor, i + 1);
break;
_hideIndicators: function() {
for (let role in PANEL_ITEM_IMPLEMENTATIONS) {
let indicator = this.statusArea[role];
if (!indicator)
continue;
if (indicator.menu)
indicator.menu.close();
indicator.container.hide();
}
},
_ensureIndicator: function(role) {
let indicator = this.statusArea[role];
if (!indicator) {
let constructor = PANEL_ITEM_IMPLEMENTATIONS[role];
if (!constructor) {
// This icon is not implemented (this is a bug)
return null;
}
indicator = new constructor(this);
this.statusArea[role] = indicator;
}
if (i == -1) {
// If we didn't find a position, we must be first
this._rightBox.insert_child_at_index(actor, 0);
}
actor._rolePosition = position;
return indicator;
},
addToStatusArea: function(role, indicator, position) {
if (this._statusArea[role])
_updateBox: function(elements, box) {
let nChildren = box.get_n_children();
for (let i = 0; i < elements.length; i++) {
let role = elements[i];
let indicator = this._ensureIndicator(role);
if (indicator == null)
continue;
this._addToPanelBox(role, indicator, i + nChildren, box);
}
},
_addToPanelBox: function(role, indicator, position, box) {
let container = indicator.container;
container.show();
let parent = container.get_parent();
if (parent)
parent.remove_actor(container);
box.insert_child_at_index(container, position);
if (indicator.menu)
this.menuManager.addMenu(indicator.menu);
this.statusArea[role] = indicator;
let destroyId = indicator.connect('destroy', Lang.bind(this, function(emitter) {
delete this.statusArea[role];
emitter.disconnect(destroyId);
container.destroy();
}));
},
addToStatusArea: function(role, indicator, position, box) {
if (this.statusArea[role])
throw new Error('Extension point conflict: there is already a status indicator for role ' + role);
if (!(indicator instanceof PanelMenu.Button))
throw new TypeError('Status indicator must be an instance of PanelMenu.Button');
if (!position)
position = 0;
this._insertStatusItem(indicator.actor, position);
this._menus.addMenu(indicator.menu);
this._statusArea[role] = indicator;
let destroyId = indicator.connect('destroy', Lang.bind(this, function(emitter) {
this._statusArea[role] = null;
emitter.disconnect(destroyId);
}));
position = position || 0;
let boxes = {
left: this._leftBox,
center: this._centerBox,
right: this._rightBox
};
let boxContainer = boxes[box] || this._rightBox;
this.statusArea[role] = indicator;
this._addToPanelBox(role, indicator, position, boxContainer);
return indicator;
},
_onTrayIconAdded: function(o, icon, role) {
if (this._status_area_shell_implementation[role]) {
// This icon is legacy, and replaced by a Shell version
// Hide it
return;
}
icon.height = PANEL_ICON_SIZE;
let buttonBox = new PanelMenu.ButtonBox();
let box = buttonBox.actor;
box.add_actor(icon);
this._insertStatusItem(box, this._status_area_order.indexOf(role));
},
_onTrayIconRemoved: function(o, icon) {
let box = icon.get_parent();
if (box && box._delegate instanceof PanelMenu.ButtonBox)
box.destroy();
},
}
});

View File

@ -1,6 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
@ -20,6 +21,10 @@ const ButtonBox = new Lang.Class({
this.actor = new Shell.GenericContainer(params);
this.actor._delegate = this;
this.container = new St.Bin({ y_fill: true,
x_fill: true,
child: this.actor });
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this.actor.connect('allocate', Lang.bind(this, this._allocate));
@ -114,6 +119,12 @@ const Button = new Lang.Class({
this.setName(nameText);
},
setSensitive: function(sensitive) {
this.actor.reactive = sensitive;
this.actor.can_focus = sensitive;
this.actor.track_hover = sensitive;
},
setName: function(text) {
if (text != null) {
// This is the easiest way to provide a accessible name to
@ -175,8 +186,7 @@ const Button = new Lang.Class({
_onMenuKeyPress: function(actor, event) {
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
let focusManager = St.FocusManager.get_for_stage(global.stage);
let group = focusManager.get_group(this.actor);
let group = global.focus_manager.get_group(this.actor);
if (group) {
let direction = (symbol == Clutter.KEY_Left) ? Gtk.DirectionType.LEFT : Gtk.DirectionType.RIGHT;
group.navigate_focus(this.actor, direction, false);
@ -224,19 +234,36 @@ const SystemStatusButton = new Lang.Class({
_init: function(iconName, nameText) {
this.parent(0.0, nameText);
this._iconActor = new St.Icon({ icon_name: iconName,
icon_type: St.IconType.SYMBOLIC,
style_class: 'system-status-icon' });
this.actor.add_actor(this._iconActor);
this.actor.add_style_class_name('panel-status-button');
this._box = new St.BoxLayout({ style_class: 'panel-status-button-box' });
this.actor.add_actor(this._box);
if (iconName)
this.setIcon(iconName);
},
addIcon: function(gicon) {
let icon = new St.Icon({ gicon: gicon,
style_class: 'system-status-icon' });
this._box.add_actor(icon);
return icon;
},
setIcon: function(iconName) {
this._iconActor.icon_name = iconName;
// Need to first add a NULL GIcon and then set icon_name, to ensure
// compatibility with -symbolic fallbacks
if (!this.mainIcon)
this.mainIcon = this.addIcon(null);
this.mainIcon.icon_name = iconName;
},
setGIcon: function(gicon) {
this._iconActor.gicon = gicon;
if (this.mainIcon)
this.mainIcon.gicon = gicon;
else
this.mainIcon = this.addIcon(gicon);
}
});

View File

@ -1,434 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Shell = imports.gi.Shell;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Signals = imports.signals;
const St = imports.gi.St;
const DND = imports.ui.dnd;
const Main = imports.ui.main;
const Params = imports.misc.params;
const Search = imports.ui.search;
const Util = imports.misc.util;
/**
* Represents a place object, which is most normally a bookmark entry,
* a mount/volume, or a special place like the Home Folder, Computer, and Network.
*
* @name: String title
* @iconFactory: A JavaScript callback which will create an icon texture given a size parameter
* @launch: A JavaScript callback to launch the entry
*/
const PlaceInfo = new Lang.Class({
Name: 'PlaceInfo',
_init: function(id, name, iconFactory, launch) {
this.id = id;
this.name = name;
this._lowerName = name.toLowerCase();
this.iconFactory = iconFactory;
this.launch = launch;
},
matchTerms: function(terms) {
let mtype = Search.MatchType.NONE;
for (let i = 0; i < terms.length; i++) {
let term = terms[i];
let idx = this._lowerName.indexOf(term);
if (idx == 0) {
mtype = Search.MatchType.PREFIX;
} else if (idx > 0) {
if (mtype == Search.MatchType.NONE)
mtype = Search.MatchType.SUBSTRING;
} else {
return Search.MatchType.NONE;
}
}
return mtype;
},
isRemovable: function() {
return false;
}
});
// Helper function to translate launch parameters into a GAppLaunchContext
function _makeLaunchContext(params)
{
params = Params.parse(params, { workspace: -1,
timestamp: 0 });
let launchContext = global.create_app_launch_context();
if (params.workspace != -1)
launchContext.set_desktop(params.workspace);
if (params.timestamp != 0)
launchContext.set_timestamp(params.timestamp);
return launchContext;
}
const PlaceDeviceInfo = new Lang.Class({
Name: 'PlaceDeviceInfo',
Extends: PlaceInfo,
_init: function(mount) {
this._mount = mount;
this.name = mount.get_name();
this._lowerName = this.name.toLowerCase();
this.id = 'mount:' + mount.get_root().get_uri();
},
iconFactory: function(size) {
let icon = this._mount.get_icon();
return St.TextureCache.get_default().load_gicon(null, icon, size);
},
launch: function(params) {
Gio.app_info_launch_default_for_uri(this._mount.get_root().get_uri(),
_makeLaunchContext(params));
},
isRemovable: function() {
return this._mount.can_unmount();
},
remove: function() {
if (!this.isRemovable())
return;
if (this._mount.can_eject())
this._mount.eject(0, null, Lang.bind(this, this._removeFinish));
else
this._mount.unmount(0, null, Lang.bind(this, this._removeFinish));
},
_removeFinish: function(o, res, data) {
try {
if (this._mount.can_eject())
this._mount.eject_finish(res);
else
this._mount.unmount_finish(res);
} catch (e) {
let message = _("Failed to unmount '%s'").format(o.get_name());
Main.overview.setMessage(message,
Lang.bind(this, this.remove),
_("Retry"));
}
}
});
const PlacesManager = new Lang.Class({
Name: 'PlacesManager',
_init: function() {
this._defaultPlaces = [];
this._mounts = [];
this._bookmarks = [];
let homeFile = Gio.file_new_for_path (GLib.get_home_dir());
let homeUri = homeFile.get_uri();
let homeLabel = Shell.util_get_label_for_uri (homeUri);
let homeIcon = Shell.util_get_icon_for_uri (homeUri);
this._home = new PlaceInfo('special:home', homeLabel,
function(size) {
return St.TextureCache.get_default().load_gicon(null, homeIcon, size);
},
function(params) {
Gio.app_info_launch_default_for_uri(homeUri, _makeLaunchContext(params));
});
let desktopPath = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP);
let desktopFile = Gio.file_new_for_path (desktopPath);
let desktopUri = desktopFile.get_uri();
let desktopLabel = Shell.util_get_label_for_uri (desktopUri);
let desktopIcon = Shell.util_get_icon_for_uri (desktopUri);
this._desktopMenu = new PlaceInfo('special:desktop', desktopLabel,
function(size) {
return St.TextureCache.get_default().load_gicon(null, desktopIcon, size);
},
function(params) {
Gio.app_info_launch_default_for_uri(desktopUri, _makeLaunchContext(params));
});
this._connect = new PlaceInfo('special:connect', _("Connect to..."),
function (size) {
// do NOT use St.Icon here, it crashes the shell
// see wanda.js for details
return St.TextureCache.get_default().load_icon_name(null,
'applications-internet',
St.IconType.FULLCOLOR,
size);
},
function (params) {
// BUG: nautilus-connect-server doesn't have a desktop file, so we can't
// launch it with the workspace from params. It's probably pretty rare
// and odd to drag this place onto a workspace in any case
Util.spawn(['nautilus-connect-server']);
});
this._defaultPlaces.push(this._home);
this._defaultPlaces.push(this._desktopMenu);
this._defaultPlaces.push(this._connect);
/*
* Show devices, code more or less ported from nautilus-places-sidebar.c
*/
this._volumeMonitor = Gio.VolumeMonitor.get();
this._volumeMonitor.connect('volume-added', Lang.bind(this, this._updateDevices));
this._volumeMonitor.connect('volume-removed',Lang.bind(this, this._updateDevices));
this._volumeMonitor.connect('volume-changed', Lang.bind(this, this._updateDevices));
this._volumeMonitor.connect('mount-added', Lang.bind(this, this._updateDevices));
this._volumeMonitor.connect('mount-removed', Lang.bind(this, this._updateDevices));
this._volumeMonitor.connect('mount-changed', Lang.bind(this, this._updateDevices));
this._volumeMonitor.connect('drive-connected', Lang.bind(this, this._updateDevices));
this._volumeMonitor.connect('drive-disconnected', Lang.bind(this, this._updateDevices));
this._volumeMonitor.connect('drive-changed', Lang.bind(this, this._updateDevices));
this._updateDevices();
this._bookmarksPath = GLib.build_filenamev([GLib.get_home_dir(), '.gtk-bookmarks']);
this._bookmarksFile = Gio.file_new_for_path(this._bookmarksPath);
this._monitor = this._bookmarksFile.monitor_file(Gio.FileMonitorFlags.NONE, null);
this._bookmarkTimeoutId = 0;
this._monitor.connect('changed', Lang.bind(this, function () {
if (this._bookmarkTimeoutId > 0)
return;
/* Defensive event compression */
this._bookmarkTimeoutId = Mainloop.timeout_add(100, Lang.bind(this, function () {
this._bookmarkTimeoutId = 0;
this._reloadBookmarks();
return false;
}));
}));
this._reloadBookmarks();
},
_updateDevices: function() {
this._mounts = [];
/* first go through all connected drives */
let drives = this._volumeMonitor.get_connected_drives();
for (let i = 0; i < drives.length; i++) {
let volumes = drives[i].get_volumes();
for(let j = 0; j < volumes.length; j++) {
let mount = volumes[j].get_mount();
if(mount != null) {
this._addMount(mount);
}
}
}
/* add all volumes that is not associated with a drive */
let volumes = this._volumeMonitor.get_volumes();
for(let i = 0; i < volumes.length; i++) {
if(volumes[i].get_drive() != null)
continue;
let mount = volumes[i].get_mount();
if(mount != null) {
this._addMount(mount);
}
}
/* add mounts that have no volume (/etc/mtab mounts, ftp, sftp,...) */
let mounts = this._volumeMonitor.get_mounts();
for(let i = 0; i < mounts.length; i++) {
if(mounts[i].is_shadowed())
continue;
if(mounts[i].get_volume())
continue;
this._addMount(mounts[i]);
}
/* We emit two signals, one for a generic 'all places' update
* and the other for one specific to mounts. We do this because
* clients like PlaceDisplay may only care about places in general
* being updated while clients like DashPlaceDisplay care which
* specific type of place got updated.
*/
this.emit('mounts-updated');
this.emit('places-updated');
},
_reloadBookmarks: function() {
this._bookmarks = [];
if (!GLib.file_test(this._bookmarksPath, GLib.FileTest.EXISTS))
return;
let bookmarksContent = Shell.get_file_contents_utf8_sync(this._bookmarksPath);
let bookmarks = bookmarksContent.split('\n');
let bookmarksToLabel = {};
let bookmarksOrder = [];
for (let i = 0; i < bookmarks.length; i++) {
let bookmarkLine = bookmarks[i];
let components = bookmarkLine.split(' ');
let bookmark = components[0];
if (bookmark in bookmarksToLabel)
continue;
let label = null;
if (components.length > 1)
label = components.slice(1).join(' ');
bookmarksToLabel[bookmark] = label;
bookmarksOrder.push(bookmark);
}
for (let i = 0; i < bookmarksOrder.length; i++) {
let bookmark = bookmarksOrder[i];
let label = bookmarksToLabel[bookmark];
let file = Gio.file_new_for_uri(bookmark);
if (!file.query_exists(null))
continue;
if (label == null)
label = Shell.util_get_label_for_uri(bookmark);
if (label == null)
continue;
let icon = Shell.util_get_icon_for_uri(bookmark);
let item = new PlaceInfo('bookmark:' + bookmark, label,
function(size) {
return St.TextureCache.get_default().load_gicon(null, icon, size);
},
function(params) {
Gio.app_info_launch_default_for_uri(bookmark, _makeLaunchContext(params));
});
this._bookmarks.push(item);
}
/* See comment in _updateDevices for explanation why there are two signals. */
this.emit('bookmarks-updated');
this.emit('places-updated');
},
_addMount: function(mount) {
let devItem = new PlaceDeviceInfo(mount);
this._mounts.push(devItem);
},
getAllPlaces: function () {
return this.getDefaultPlaces().concat(this.getBookmarks(), this.getMounts());
},
getDefaultPlaces: function () {
return this._defaultPlaces;
},
getBookmarks: function () {
return this._bookmarks;
},
getMounts: function () {
return this._mounts;
},
_lookupIndexById: function(sourceArray, id) {
for (let i = 0; i < sourceArray.length; i++) {
let place = sourceArray[i];
if (place.id == id)
return i;
}
return -1;
},
lookupPlaceById: function(id) {
let colonIdx = id.indexOf(':');
let type = id.substring(0, colonIdx);
let sourceArray = null;
if (type == 'special')
sourceArray = this._defaultPlaces;
else if (type == 'mount')
sourceArray = this._mounts;
else if (type == 'bookmark')
sourceArray = this._bookmarks;
return sourceArray[this._lookupIndexById(sourceArray, id)];
},
_removeById: function(sourceArray, id) {
sourceArray.splice(this._lookupIndexById(sourceArray, id), 1);
}
});
Signals.addSignalMethods(PlacesManager.prototype);
const PlaceSearchProvider = new Lang.Class({
Name: 'PlaceSearchProvider',
Extends: Search.SearchProvider,
_init: function() {
this.parent(_("PLACES & DEVICES"));
},
getResultMetas: function(resultIds) {
let metas = [];
for (let i = 0; i < resultIds.length; i++) {
let placeInfo = Main.placesManager.lookupPlaceById(resultIds[i]);
if (!placeInfo)
metas.push(null);
else
metas.push({ 'id': resultIds[i],
'name': placeInfo.name,
'createIcon': function(size) {
return placeInfo.iconFactory(size);
}
});
}
return metas;
},
activateResult: function(id, params) {
let placeInfo = Main.placesManager.lookupPlaceById(id);
placeInfo.launch(params);
},
_compareResultMeta: function (idA, idB) {
let infoA = Main.placesManager.lookupPlaceById(idA);
let infoB = Main.placesManager.lookupPlaceById(idB);
return infoA.name.localeCompare(infoB.name);
},
_searchPlaces: function(places, terms) {
let multiplePrefixResults = [];
let prefixResults = [];
let multipleSubstringResults = [];
let substringResults = [];
terms = terms.map(String.toLowerCase);
for (let i = 0; i < places.length; i++) {
let place = places[i];
let mtype = place.matchTerms(terms);
if (mtype == Search.MatchType.MULTIPLE_PREFIX)
multiplePrefixResults.push(place.id);
else if (mtype == Search.MatchType.PREFIX)
prefixResults.push(place.id);
else if (mtype == Search.MatchType.MULTIPLE_SUBSTRING)
multipleSubstringResults.push(place.id);
else if (mtype == Search.MatchType.SUBSTRING)
substringResults.push(place.id);
}
multiplePrefixResults.sort(this._compareResultMeta);
prefixResults.sort(this._compareResultMeta);
multipleSubstringResults.sort(this._compareResultMeta);
substringResults.sort(this._compareResultMeta);
return multiplePrefixResults.concat(prefixResults.concat(multipleSubstringResults.concat(substringResults)));
},
getInitialResultSet: function(terms) {
let places = Main.placesManager.getAllPlaces();
return this._searchPlaces(places, terms);
},
getSubsearchResultSet: function(previousResults, terms) {
let places = previousResults.map(function (id) { return Main.placesManager.lookupPlaceById(id); });
return this._searchPlaces(places, terms);
}
});

126
js/ui/pointerWatcher.js Normal file
View File

@ -0,0 +1,126 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
// We stop polling if the user is idle for more than this amount of time
const IDLE_TIME = 1000;
// This file implements a reasonably efficient system for tracking the position
// of the mouse pointer. We simply query the pointer from the X server in a loop,
// but we turn off the polling when the user is idle.
let _pointerWatcher = null;
function getPointerWatcher() {
if (_pointerWatcher == null)
_pointerWatcher = new PointerWatcher();
return _pointerWatcher;
}
const PointerWatch = new Lang.Class({
Name: 'PointerWatch',
_init: function(watcher, interval, callback) {
this.watcher = watcher;
this.interval = interval;
this.callback = callback;
},
// remove:
// remove this watch. This function may safely be called
// while the callback is executing.
remove: function() {
this.watcher._removeWatch(this);
}
});
const PointerWatcher = new Lang.Class({
Name: 'PointerWatcher',
_init: function() {
let idleMonitor = Shell.IdleMonitor.get();
idleMonitor.add_watch(IDLE_TIME,
Lang.bind(this, this._onIdleMonitorWatch));
this._idle = idleMonitor.get_idletime() > IDLE_TIME;
this._watches = [];
this.pointerX = null;
this.pointerY = null;
},
// addWatch:
// @interval: hint as to the time resolution needed. When the user is
// not idle, the position of the pointer will be queried at least
// once every this many milliseconds.
// @callback: function to call when the pointer position changes - takes
// two arguments, X and Y.
//
// Set up a watch on the position of the mouse pointer. Returns a
// PointerWatch object which has a remove() method to remove the watch.
addWatch: function(interval, callback) {
// Avoid unreliably calling the watch for the current position
this._updatePointer();
let watch = new PointerWatch(this, interval, callback);
this._watches.push(watch);
this._updateTimeout();
return watch;
},
_removeWatch: function(watch) {
for (let i = 0; i < this._watches.length; i++) {
if (this._watches[i] == watch) {
this._watches.splice(i, 1);
this._updateTimeout();
return;
}
}
},
_onIdleMonitorWatch: function(monitor, id, userBecameIdle) {
this._idle = userBecameIdle;
if (!userBecameIdle)
this._updatePointer();
this._updateTimeout();
},
_updateTimeout: function() {
if (this._timeoutId) {
Mainloop.source_remove(this._timeoutId);
this._timeoutId = 0;
}
if (this._idle || this._watches.length == 0)
return;
let minInterval = this._watches[0].interval;
for (let i = 1; i < this._watches.length; i++)
minInterval = Math.min(this._watches[i].interval, minInterval);
this._timeoutId = Mainloop.timeout_add(minInterval,
Lang.bind(this, this._onTimeout));
},
_onTimeout: function() {
this._updatePointer();
return true;
},
_updatePointer: function() {
let [x, y, mods] = global.get_pointer();
if (this.pointerX == x && this.pointerY == y)
return;
this.pointerX = x;
this.pointerY = y;
for (let i = 0; i < this._watches.length;) {
let watch = this._watches[i];
watch.callback(x, y);
if (watch == this._watches[i]) // guard against self-removal
i++;
}
}
});

View File

@ -37,12 +37,13 @@ const PopupBaseMenuItem = new Lang.Class({
activate: true,
hover: true,
sensitive: true,
style_class: null
style_class: null,
can_focus: true
});
this.actor = new Shell.GenericContainer({ style_class: 'popup-menu-item',
reactive: params.reactive,
track_hover: params.reactive,
can_focus: params.reactive,
can_focus: params.can_focus,
accessible_role: Atk.Role.MENU_ITEM});
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
@ -60,6 +61,9 @@ const PopupBaseMenuItem = new Lang.Class({
this.setSensitive(this.sensitive);
if (!this._activatable)
this.actor.add_style_class_name('popup-inactive-menu-item');
if (params.style_class)
this.actor.add_style_class_name(params.style_class);
@ -69,10 +73,9 @@ const PopupBaseMenuItem = new Lang.Class({
}
if (params.reactive && params.hover)
this.actor.connect('notify::hover', Lang.bind(this, this._onHoverChanged));
if (params.reactive) {
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('key-focus-in', Lang.bind(this, this._onKeyFocusIn));
this.actor.connect('key-focus-out', Lang.bind(this, this._onKeyFocusOut));
},
_onStyleChanged: function (actor) {
@ -136,10 +139,6 @@ const PopupBaseMenuItem = new Lang.Class({
this.actor.reactive = sensitive;
this.actor.can_focus = sensitive;
if (sensitive)
this.actor.remove_style_pseudo_class('insensitive');
else
this.actor.add_style_pseudo_class('insensitive');
this.emit('sensitive-changed', sensitive);
},
@ -184,12 +183,14 @@ const PopupBaseMenuItem = new Lang.Class({
this._dot = new St.DrawingArea({ style_class: 'popup-menu-item-dot' });
this._dot.connect('repaint', Lang.bind(this, this._onRepaintDot));
this.actor.add_actor(this._dot);
this.actor.add_accessible_state (Atk.StateType.CHECKED);
} else {
if (!this._dot)
return;
this._dot.destroy();
this._dot = null;
this.actor.remove_accessible_state (Atk.StateType.CHECKED);
}
},
@ -400,7 +401,8 @@ const PopupSeparatorMenuItem = new Lang.Class({
Extends: PopupBaseMenuItem,
_init: function () {
this.parent({ reactive: false });
this.parent({ reactive: false,
can_focus: false});
this._drawingArea = new St.DrawingArea({ style_class: 'popup-separator-menu-item' });
this.addActor(this._drawingArea, { span: -1, expand: true });
@ -546,6 +548,10 @@ const PopupSliderMenuItem = new Lang.Class({
this._slider.connect('repaint', Lang.bind(this, this._sliderRepaint));
this.actor.connect('button-press-event', Lang.bind(this, this._startDragging));
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
this.actor.connect('notify::mapped', Lang.bind(this, function() {
if (!this.actor.mapped)
this._endDragging();
}));
this._releaseId = this._motionId = 0;
this._dragging = false;
@ -715,7 +721,8 @@ const Switch = new Lang.Class({
_init: function(state) {
this.actor = new St.Bin({ style_class: 'toggle-switch',
accessible_role: Atk.Role.CHECK_BOX});
accessible_role: Atk.Role.CHECK_BOX,
can_focus: true });
// Translators: this MUST be either "toggle-switch-us"
// (for toggle switches containing the English words
// "ON" and "OFF") or "toggle-switch-intl" (for toggle
@ -759,7 +766,7 @@ const PopupSwitchMenuItem = new Lang.Class({
{ expand: true, span: -1, align: St.Align.END });
this._statusLabel = new St.Label({ text: '',
style_class: 'popup-inactive-menu-item'
style_class: 'popup-status-menu-item'
});
this._statusBin.child = this._switch.actor;
},
@ -769,12 +776,10 @@ const PopupSwitchMenuItem = new Lang.Class({
this._statusLabel.text = text;
this._statusBin.child = this._statusLabel;
this.actor.reactive = false;
this.actor.can_focus = false;
this.actor.accessible_role = Atk.Role.MENU_ITEM;
} else {
this._statusBin.child = this._switch.actor;
this.actor.reactive = true;
this.actor.can_focus = true;
this.actor.accessible_role = Atk.Role.CHECK_MENU_ITEM;
}
this.checkAccessibleState();
@ -865,12 +870,15 @@ const PopupMenuBase = new Lang.Class({
// for the menu which causes its prelight state to freeze
this.blockSourceEvents = false;
// Can be set while a menu is up to let all events through without special
// menu handling useful for scrollbars in menus, and probably not otherwise.
this.passEvents = false;
this._activeMenuItem = null;
this._childMenus = [];
this._settingsActions = { };
this._sessionUpdatedId = Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
},
_sessionUpdated: function() {
this._setSettingsVisibility(Main.sessionMode.allowSettings);
},
addAction: function(title, callback) {
@ -884,10 +892,6 @@ const PopupMenuBase = new Lang.Class({
},
addSettingsAction: function(title, desktopFile) {
// Don't allow user settings to get edited unless we're in a user session
if (global.session_type != Shell.SessionType.USER)
return null;
let menuItem = this.addAction(title, function() {
let app = Shell.AppSystem.get_default().lookup_setting(desktopFile);
@ -899,11 +903,22 @@ const PopupMenuBase = new Lang.Class({
Main.overview.hide();
app.activate();
});
menuItem.actor.visible = Main.sessionMode.allowSettings;
this._settingsActions[desktopFile] = menuItem;
return menuItem;
},
_setSettingsVisibility: function(visible) {
for (let id in this._settingsActions) {
let item = this._settingsActions[id];
item.actor.visible = visible;
}
},
isEmpty: function() {
return this.box.get_children().length == 0;
return this.box.get_n_children() == 0;
},
isChildMenu: function(menu) {
@ -939,7 +954,7 @@ const PopupMenuBase = new Lang.Class({
_connectSubMenuSignals: function(object, menu) {
object._subMenuActivateId = menu.connect('activate', Lang.bind(this, function() {
this.emit('activate');
this.close(true);
this.close(BoxPointer.PopupAnimation.FULL);
}));
object._subMenuActiveChangeId = menu.connect('active-changed', Lang.bind(this, function(submenu, submenuItem) {
if (this._activeMenuItem && this._activeMenuItem != submenuItem)
@ -974,7 +989,7 @@ const PopupMenuBase = new Lang.Class({
}));
menuItem._activateId = menuItem.connect('activate', Lang.bind(this, function (menuItem, event) {
this.emit('activate', menuItem);
this.close(true);
this.close(BoxPointer.PopupAnimation.FULL);
}));
// the weird name is to avoid a conflict with some random property
// the menuItem may have, called destroyId
@ -1044,14 +1059,17 @@ const PopupMenuBase = new Lang.Class({
if (menuItem instanceof PopupMenuSection) {
this._connectSubMenuSignals(menuItem, menuItem);
menuItem._closingId = this.connect('open-state-changed',
menuItem._parentOpenStateChangedId = this.connect('open-state-changed',
function(self, open) {
if (!open)
menuItem.close(false);
if (open)
menuItem.open();
else
menuItem.close();
});
menuItem.connect('destroy', Lang.bind(this, function() {
menuItem.disconnect(menuItem._subMenuActivateId);
menuItem.disconnect(menuItem._subMenuActiveChangeId);
this.disconnect(menuItem._parentOpenStateChangedId);
this.length--;
}));
@ -1064,7 +1082,7 @@ const PopupMenuBase = new Lang.Class({
this._connectItemSignals(menuItem);
menuItem._closingId = this.connect('open-state-changed', function(self, open) {
if (!open)
menuItem.menu.close(false);
menuItem.menu.close(BoxPointer.PopupAnimation.FADE);
});
} else if (menuItem instanceof PopupSeparatorMenuItem) {
this._connectItemSignals(menuItem);
@ -1086,7 +1104,8 @@ const PopupMenuBase = new Lang.Class({
let columnWidths = [];
let items = this.box.get_children();
for (let i = 0; i < items.length; i++) {
if (!items[i].visible)
if (!items[i].visible &&
!(items[i]._delegate instanceof PopupSubMenu && items[i-1].visible))
continue;
if (items[i]._delegate instanceof PopupBaseMenuItem || items[i]._delegate instanceof PopupMenuBase) {
let itemColumnWidths = items[i]._delegate.getColumnWidths();
@ -1151,9 +1170,9 @@ const PopupMenuBase = new Lang.Class({
toggle: function() {
if (this.isOpen)
this.close(true);
this.close(BoxPointer.PopupAnimation.FULL);
else
this.open(true);
this.open(BoxPointer.PopupAnimation.FULL);
},
destroy: function() {
@ -1161,6 +1180,9 @@ const PopupMenuBase = new Lang.Class({
this.actor.destroy();
this.emit('destroy');
Main.sessionMode.disconnect(this._sessionUpdatedId);
this._sessionUpdatedId = 0;
}
});
Signals.addSignalMethods(PopupMenuBase.prototype);
@ -1214,7 +1236,7 @@ const PopupMenu = new Lang.Class({
_onKeyPressEvent: function(actor, event) {
if (event.get_key_symbol() == Clutter.Escape) {
this.close(true);
this.close(BoxPointer.PopupAnimation.FULL);
return true;
}
@ -1277,24 +1299,6 @@ const PopupSubMenu = new Lang.Class({
hscrollbar_policy: Gtk.PolicyType.NEVER,
vscrollbar_policy: Gtk.PolicyType.NEVER });
// StScrollbar plays dirty tricks with events, calling
// clutter_set_motion_events_enabled (FALSE) during the scroll; this
// confuses our event tracking, so we just turn it off during the
// scroll.
let vscroll = this.actor.get_vscroll_bar();
vscroll.connect('scroll-start',
Lang.bind(this, function() {
let topMenu = this._getTopMenu();
if (topMenu)
topMenu.passEvents = true;
}));
vscroll.connect('scroll-stop',
Lang.bind(this, function() {
let topMenu = this._getTopMenu();
if (topMenu)
topMenu.passEvents = false;
}));
this.actor.add_actor(this.box);
this.actor._delegate = this;
this.actor.clip_to_allocation = true;
@ -1344,6 +1348,11 @@ const PopupSubMenu = new Lang.Class({
this.actor.vscrollbar_policy =
needsScrollbar ? Gtk.PolicyType.AUTOMATIC : Gtk.PolicyType.NEVER;
if (needsScrollbar)
this.actor.add_style_pseudo_class('scrolled');
else
this.actor.remove_style_pseudo_class('scrolled');
// It looks funny if we animate with a scrollbar (at what point is
// the scrollbar added?) so just skip that case
if (animate && needsScrollbar)
@ -1416,7 +1425,7 @@ const PopupSubMenu = new Lang.Class({
// Move focus back to parent menu if the user types Left.
if (this.isOpen && event.get_key_symbol() == Clutter.KEY_Left) {
this.close(true);
this.close(BoxPointer.PopupAnimation.FULL);
this.sourceActor._delegate.setActive(true);
return true;
}
@ -1450,7 +1459,7 @@ const PopupMenuSection = new Lang.Class({
// deliberately ignore any attempt to open() or close(), but emit the
// corresponding signal so children can still pick it up
open: function(animate) { this.emit('open-state-changed', true); },
open: function() { this.emit('open-state-changed', true); },
close: function() { this.emit('open-state-changed', false); },
destroy: function() {
@ -1498,7 +1507,7 @@ const PopupSubMenuMenuItem = new Lang.Class({
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_Right) {
this.menu.open(true);
this.menu.open(BoxPointer.PopupAnimation.FULL);
this.menu.actor.navigate_focus(null, Gtk.DirectionType.DOWN, false);
return true;
} else if (symbol == Clutter.KEY_Left && this.menu.isOpen) {
@ -1510,7 +1519,7 @@ const PopupSubMenuMenuItem = new Lang.Class({
},
activate: function(event) {
this.menu.open(true);
this.menu.open(BoxPointer.PopupAnimation.FULL);
},
_onButtonReleaseEvent: function(actor) {
@ -1537,7 +1546,7 @@ const PopupComboMenu = new Lang.Class({
_onKeyPressEvent: function(actor, event) {
if (event.get_key_symbol() == Clutter.Escape) {
this.close(true);
this.close(BoxPointer.PopupAnimation.FULL);
return true;
}
@ -1889,10 +1898,6 @@ const RemoteMenu = new Lang.Class({
}
item.actor.reactive = item.actor.can_focus = action.enabled;
if (action.enabled)
item.actor.remove_style_pseudo_class('insensitive');
else
item.actor.add_style_pseudo_class('insensitive');
destroyId = item.connect('destroy', Lang.bind(this, function() {
item.disconnect(destroyId);
@ -1917,7 +1922,7 @@ const RemoteMenu = new Lang.Class({
while (k0 < currentItems.length && currentItems[k0]._ignored)
k0++;
// find the right menu item matching the model item
for (j0 = 0; j0 < position; j0++, k0++) {
for (j0 = 0; k0 < currentItems.length && j0 < position; j0++, k0++) {
if (currentItems[k0]._ignored)
k0++;
}
@ -1927,7 +1932,7 @@ const RemoteMenu = new Lang.Class({
for (k = k0; k < currentItems.length; k++)
currentItems[k].destroy();
} else {
for (j = j0, k = k0; j < j0 + removed; j++, k++) {
for (j = j0, k = k0; k < currentItems.length && j < j0 + removed; j++, k++) {
currentItems[k].destroy();
if (currentItems[k]._ignored)
@ -1958,8 +1963,9 @@ const RemoteMenu = new Lang.Class({
k++;
}
} else if (changeSignal) {
let signalId = this.actionGroup.connect(changeSignal, Lang.bind(this, function() {
this.actionGroup.disconnect(signalId);
let signalId = this.actionGroup.connect(changeSignal, Lang.bind(this, function(actionGroup, actionName) {
actionGroup.disconnect(signalId);
if (this._actions[actionName]) return;
// force a full update
this._modelChanged(model, 0, -1, model.get_n_items(), target);
@ -2024,11 +2030,6 @@ const RemoteMenu = new Lang.Class({
for (let i = 0; i < action.items.length; i++) {
let item = action.items[i];
item.actor.reactive = item.actor.can_focus = action.enabled;
if (action.enabled)
item.actor.remove_style_pseudo_class('insensitive');
else
item.actor.add_style_pseudo_class('insensitive');
}
}
}
@ -2056,6 +2057,9 @@ const PopupMenuManager = new Lang.Class({
},
addMenu: function(menu, position) {
if (this._findMenu(menu) > -1)
return;
let menudata = {
menu: menu,
openStateChangeId: menu.connect('open-state-changed', Lang.bind(this, this._onMenuOpenState)),
@ -2190,11 +2194,11 @@ const PopupMenuManager = new Lang.Class({
let oldMenu = this._activeMenu;
this._activeMenu = null;
for (let i = this._menuStack.length - 1; i >= 0; i--)
this._menuStack[i].close(false);
oldMenu.close(false);
newMenu.open(false);
this._menuStack[i].close(BoxPointer.PopupAnimation.FADE);
oldMenu.close(BoxPointer.PopupAnimation.FADE);
newMenu.open(BoxPointer.PopupAnimation.FADE);
} else
newMenu.open(true);
newMenu.open(BoxPointer.PopupAnimation.FULL);
},
_onMenuSourceEnter: function(menu) {
@ -2279,9 +2283,6 @@ const PopupMenuManager = new Lang.Class({
this._owner.menuEventFilter(event))
return true;
if (this._activeMenu != null && this._activeMenu.passEvents)
return false;
if (this._didPop) {
this._didPop = false;
return true;
@ -2309,6 +2310,6 @@ const PopupMenuManager = new Lang.Class({
_closeMenu: function() {
if (this._activeMenu != null)
this._activeMenu.close(true);
this._activeMenu.close(BoxPointer.PopupAnimation.FULL);
}
});

View File

@ -34,16 +34,17 @@ var SearchProviderProxy = Gio.DBusProxy.makeProxyWrapper(SearchProviderIface);
function loadRemoteSearchProviders(addProviderCallback) {
let dataDirs = GLib.get_system_data_dirs();
let loadedProviders = {};
for (let i = 0; i < dataDirs.length; i++) {
let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', 'search-providers']);
let dir = Gio.file_new_for_path(path);
if (!dir.query_exists(null))
continue;
loadRemoteSearchProvidersFromDir(dir, addProviderCallback);
loadRemoteSearchProvidersFromDir(dir, loadedProviders, addProviderCallback);
}
};
function loadRemoteSearchProvidersFromDir(dir, addProviderCallback) {
function loadRemoteSearchProvidersFromDir(dir, loadedProviders, addProviderCallback) {
let dirPath = dir.get_path();
FileUtils.listDirAsync(dir, Lang.bind(this, function(files) {
for (let i = 0; i < files.length; i++) {
@ -62,15 +63,34 @@ function loadRemoteSearchProvidersFromDir(dir, addProviderCallback) {
let remoteProvider, title;
try {
let group = KEY_FILE_GROUP;
let icon = keyfile.get_string(group, 'Icon');
let busName = keyfile.get_string(group, 'BusName');
let objectPath = keyfile.get_string(group, 'ObjectPath');
title = keyfile.get_locale_string(group, 'Title', null);
if (loadedProviders[objectPath])
continue;
let appInfo = null;
try {
let desktopId = keyfile.get_string(group, 'DesktopId');
appInfo = Gio.DesktopAppInfo.new(desktopId);
} catch (e) {
}
let icon;
if (appInfo) {
icon = appInfo.get_icon();
title = appInfo.get_name();
} else {
let iconName = keyfile.get_string(group, 'Icon');
icon = new Gio.ThemedIcon({ name: iconName });
title = keyfile.get_locale_string(group, 'Title', null);
}
remoteProvider = new RemoteSearchProvider(title,
icon,
busName,
objectPath);
loadedProviders[objectPath] = remoteProvider;
} catch(e) {
log('Failed to add search provider "%s": %s'.format(title, e.toString()));
continue;
@ -91,15 +111,13 @@ const RemoteSearchProvider = new Lang.Class({
dbusName, dbusPath);
this.parent(title.toUpperCase());
this.async = true;
this._cancellable = new Gio.Cancellable();
},
createIcon: function(size, meta) {
if (meta['gicon']) {
return new St.Icon({ gicon: Gio.icon_new_for_string(meta['gicon']),
icon_size: size,
icon_type: St.IconType.FULLCOLOR });
icon_size: size });
} else if (meta['icon-data']) {
let [width, height, rowStride, hasAlpha,
bitsPerSample, nChannels, data] = meta['icon-data'];
@ -110,8 +128,7 @@ const RemoteSearchProvider = new Lang.Class({
// Ugh, but we want to fall back to something ...
return new St.Icon({ icon_name: 'text-x-generic',
icon_size: size,
icon_type: St.IconType.FULLCOLOR });
icon_size: size });
},
_getResultsFinished: function(results, error) {
@ -120,7 +137,7 @@ const RemoteSearchProvider = new Lang.Class({
this.searchSystem.pushResults(this, results[0]);
},
getInitialResultSetAsync: function(terms) {
getInitialResultSet: function(terms) {
this._cancellable.cancel();
this._cancellable.reset();
try {
@ -133,7 +150,7 @@ const RemoteSearchProvider = new Lang.Class({
}
},
getSubsearchResultSetAsync: function(previousResults, newTerms) {
getSubsearchResultSet: function(previousResults, newTerms) {
this._cancellable.cancel();
this._cancellable.reset();
try {
@ -164,7 +181,7 @@ const RemoteSearchProvider = new Lang.Class({
callback(resultMetas);
},
getResultMetasAsync: function(ids, callback) {
getResultMetas: function(ids, callback) {
this._cancellable.cancel();
this._cancellable.reset();
try {

895
js/ui/screenShield.js Normal file
View File

@ -0,0 +1,895 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Cairo = imports.cairo;
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const GnomeDesktop = imports.gi.GnomeDesktop;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Signals = imports.signals;
const St = imports.gi.St;
const TweenerEquations = imports.tweener.equations;
const GnomeSession = imports.misc.gnomeSession;
const Layout = imports.ui.layout;
const LoginManager = imports.misc.loginManager;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const Overview = imports.ui.overview;
const MessageTray = imports.ui.messageTray;
const ShellDBus = imports.ui.shellDBus;
const Tweener = imports.ui.tweener;
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
const LOCK_ENABLED_KEY = 'lock-enabled';
const CURTAIN_SLIDE_TIME = 0.5;
// fraction of screen height the arrow must reach before completing
// the slide up automatically
const ARROW_DRAG_THRESHOLD = 0.1;
// Parameters for the arrow animation
const N_ARROWS = 3;
const ARROW_ANIMATION_TIME = 0.6;
const ARROW_ANIMATION_PEAK_OPACITY = 0.4;
// The distance in px that the lock screen will move to when pressing
// a key that has no effect in the lock screen (bumping it)
const BUMP_SIZE = 25;
const BUMP_TIME = 0.3;
const SUMMARY_ICON_SIZE = 48;
// Lightbox fading times
// STANDARD_FADE_TIME is used when the session goes idle, while
// SHORT_FADE_TIME is used when requesting lock explicitly from the user menu
const STANDARD_FADE_TIME = 10;
const SHORT_FADE_TIME = 0.8;
const Clock = new Lang.Class({
Name: 'ScreenShieldClock',
CLOCK_FORMAT_KEY: 'clock-format',
CLOCK_SHOW_SECONDS_KEY: 'clock-show-seconds',
_init: function() {
this.actor = new St.BoxLayout({ style_class: 'screen-shield-clock',
vertical: true });
this._time = new St.Label({ style_class: 'screen-shield-clock-time' });
this._date = new St.Label({ style_class: 'screen-shield-clock-date' });
this.actor.add(this._time, { x_align: St.Align.MIDDLE });
this.actor.add(this._date, { x_align: St.Align.MIDDLE });
this._wallClock = new GnomeDesktop.WallClock({ time_only: true });
this._wallClock.connect('notify::clock', Lang.bind(this, this._updateClock));
this._updateClock();
},
_updateClock: function() {
this._time.text = this._wallClock.clock;
let date = new Date();
/* Translators: This is a time format for a date in
long format */
this._date.text = date.toLocaleFormat(_("%A, %B %d"));
},
destroy: function() {
this.actor.destroy();
this._wallClock.run_dispose();
}
});
const NotificationsBox = new Lang.Class({
Name: 'NotificationsBox',
_init: function() {
this.actor = new St.BoxLayout({ vertical: true,
name: 'screenShieldNotifications',
style_class: 'screen-shield-notifications-box' });
this._residentNotificationBox = new St.BoxLayout({ vertical: true,
style_class: 'screen-shield-notifications-box' });
let scrollView = new St.ScrollView({ x_fill: false, x_align: St.Align.MIDDLE });
this._persistentNotificationBox = new St.BoxLayout({ vertical: true,
style_class: 'screen-shield-notifications-box' });
scrollView.add_actor(this._persistentNotificationBox);
this.actor.add(this._residentNotificationBox, { x_fill: true });
this.actor.add(scrollView, { x_fill: true, x_align: St.Align.MIDDLE });
this._items = [];
Main.messageTray.getSummaryItems().forEach(Lang.bind(this, function(item) {
this._summaryItemAdded(Main.messageTray, item, true);
}));
this._updateVisibility();
this._summaryAddedId = Main.messageTray.connect('summary-item-added', Lang.bind(this, this._summaryItemAdded));
},
destroy: function() {
if (this._summaryAddedId) {
Main.messageTray.disconnect(this._summaryAddedId);
this._summaryAddedId = 0;
}
for (let i = 0; i < this._items.length; i++)
this._removeItem(this._items[i]);
this._items = [];
this.actor.destroy();
},
_updateVisibility: function() {
if (this._residentNotificationBox.get_n_children() > 0) {
this.actor.show();
return;
}
let children = this._persistentNotificationBox.get_children();
this.actor.visible = children.some(function(a) { return a.visible; });
},
_sourceIsResident: function(source) {
return source.hasResidentNotification() && !source.isChat;
},
_makeNotificationCountText: function(count, isChat) {
if (isChat)
return ngettext("%d new message", "%d new messages", count).format(count);
else
return ngettext("%d new notification", "%d new notifications", count).format(count);
},
_makeNotificationSource: function(source) {
let box = new St.BoxLayout({ style_class: 'screen-shield-notification-source' });
let sourceActor = new MessageTray.SourceActor(source, SUMMARY_ICON_SIZE);
box.add(sourceActor.actor, { y_fill: true });
let textBox = new St.BoxLayout({ vertical: true });
box.add(textBox);
let label = new St.Label({ text: source.title,
style_class: 'screen-shield-notification-label' });
textBox.add(label);
let count = source.unseenCount;
let countLabel = new St.Label({ text: this._makeNotificationCountText(count, source.isChat),
style_class: 'screen-shield-notification-count-text' });
textBox.add(countLabel);
box.visible = count != 0;
return [box, countLabel];
},
_summaryItemAdded: function(tray, item, dontUpdateVisibility) {
// Ignore transient sources, or sources explicitly marked not to show
// in the lock screen
if (item.source.isTransient || !item.source.showInLockScreen)
return;
let obj = {
item: item,
source: item.source,
resident: this._sourceIsResident(item.source),
contentUpdatedId: 0,
sourceDestroyId: 0,
sourceBox: null,
countLabel: null,
};
if (obj.resident) {
this._residentNotificationBox.add(item.notificationStackWidget);
item.closeButtonVisible = false;
item.prepareNotificationStackForShowing();
} else {
[obj.sourceBox, obj.countLabel] = this._makeNotificationSource(item.source);
this._persistentNotificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.MIDDLE });
}
obj.contentUpdatedId = item.connect('content-updated', Lang.bind(this, this._onItemContentUpdated));
obj.sourceCountChangedId = item.source.connect('count-updated', Lang.bind(this, this._onSourceChanged));
obj.sourceTitleChangedId = item.source.connect('title-changed', Lang.bind(this, this._onSourceChanged));
obj.sourceDestroyId = item.source.connect('destroy', Lang.bind(this, this._onSourceDestroy));
this._items.push(obj);
if (!dontUpdateVisibility)
this._updateVisibility();
},
_findSource: function(source) {
for (let i = 0; i < this._items.length; i++) {
if (this._items[i].source == source)
return i;
}
return -1;
},
_onItemContentUpdated: function(item) {
let obj = this._items[this._findSource(item.source)];
this._updateItem(obj);
},
_onSourceChanged: function(source) {
let obj = this._items[this._findSource(source)];
this._updateItem(obj);
},
_updateItem: function(obj) {
let itemShouldBeResident = this._sourceIsResident(obj.source);
if (itemShouldBeResident && obj.resident) {
// Nothing to do here, the actor is already updated
return;
}
if (obj.resident && !itemShouldBeResident) {
// make into a regular item
obj.item.doneShowingNotificationStack();
this._residentNotificationBox.remove_actor(obj.item.notificationStackWidget);
[obj.sourceBox, obj.countLabel] = this._makeNotificationSource(obj.source);
this._persistentNotificationBox.add(obj.sourceBox);
} else if (itemShouldBeResident && !obj.resident) {
// make into a resident item
obj.sourceBox.destroy();
obj.sourceBox = obj.countLabel = null;
obj.resident = true;
this._residentNotificationBox.add(obj.item.notificationStackWidget);
obj.item.closeButtonVisible = false;
obj.item.prepareNotificationStackForShowing();
} else {
// just update the counter
let count = obj.source.unseenCount;
obj.countLabel.text = this._makeNotificationCountText(count, obj.source.isChat);
obj.sourceBox.visible = count != 0;
}
this._updateVisibility();
},
_onSourceDestroy: function(source) {
let idx = this._findSource(source);
this._removeItem(this._items[idx]);
this._items.splice(idx, 1);
this._updateVisibility();
},
_removeItem: function(obj) {
if (obj.resident) {
obj.item.doneShowingNotificationStack();
this._residentNotificationBox.remove_actor(obj.item.notificationStackWidget);
} else {
obj.sourceBox.destroy();
}
obj.item.disconnect(obj.contentUpdatedId);
obj.source.disconnect(obj.sourceDestroyId);
obj.source.disconnect(obj.sourceCountChangedId);
},
});
const Arrow = new Lang.Class({
Name: 'Arrow',
Extends: St.Bin,
_init: function(params) {
this.parent(params);
this.x_fill = this.y_fill = true;
this.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS);
this._drawingArea = new St.DrawingArea();
this._drawingArea.connect('repaint', Lang.bind(this, this._drawArrow));
this.child = this._drawingArea;
this._shadowHelper = null;
this._shadowWidth = this._shadowHeight = 0;
},
_drawArrow: function(arrow) {
let cr = arrow.get_context();
let [w, h] = arrow.get_surface_size();
let node = this.get_theme_node();
let thickness = node.get_length('-arrow-thickness');
Clutter.cairo_set_source_color(cr, node.get_foreground_color());
cr.setLineCap(Cairo.LineCap.ROUND);
cr.setLineWidth(thickness);
cr.moveTo(thickness / 2, h - thickness / 2);
cr.lineTo(w/2, thickness);
cr.lineTo(w - thickness / 2, h - thickness / 2);
cr.stroke();
},
vfunc_style_changed: function() {
let node = this.get_theme_node();
this._shadow = node.get_shadow('-arrow-shadow');
if (this._shadow)
this._shadowHelper = St.ShadowHelper.new(this._shadow);
else
this._shadowHelper = null;
},
vfunc_paint: function() {
if (this._shadowHelper) {
this._shadowHelper.update(this._drawingArea);
let allocation = this._drawingArea.get_allocation_box();
let paintOpacity = this._drawingArea.get_paint_opacity();
this._shadowHelper.paint(allocation, paintOpacity);
}
this._drawingArea.paint();
}
});
/**
* To test screen shield, make sure to kill gnome-screensaver.
*
* If you are setting org.gnome.desktop.session.idle-delay directly in dconf,
* rather than through System Settings, you also need to set
* org.gnome.settings-daemon.plugins.power.sleep-display-ac and
* org.gnome.settings-daemon.plugins.power.sleep-display-battery to the same value.
* This will ensure that the screen blanks at the right time when it fades out.
* https://bugzilla.gnome.org/show_bug.cgi?id=668703 explains the dependance.
*/
const ScreenShield = new Lang.Class({
Name: 'ScreenShield',
_init: function() {
this.actor = Main.layoutManager.screenShieldGroup;
this._lockScreenState = MessageTray.State.HIDDEN;
this._lockScreenGroup = new St.Widget({ x_expand: true,
y_expand: true,
reactive: true,
can_focus: true,
name: 'lockScreenGroup',
});
this._lockScreenGroup.connect('key-release-event',
Lang.bind(this, this._onLockScreenKeyRelease));
this._lockScreenGroup.connect('scroll-event',
Lang.bind(this, this._onLockScreenScroll));
this._lockScreenContents = new St.Widget({ layout_manager: new Clutter.BinLayout(),
name: 'lockScreenContents' });
this._lockScreenContents.add_constraint(new Layout.MonitorConstraint({ primary: true }));
this._background = new St.Bin({ style_class: 'screen-shield-background',
child: Meta.BackgroundActor.new_for_screen(global.screen) });
this._lockScreenGroup.add_actor(this._background);
this._lockScreenGroup.add_actor(this._lockScreenContents);
this._arrowContainer = new St.BoxLayout({ style_class: 'screen-shield-arrows',
vertical: true,
x_align: Clutter.ActorAlign.CENTER,
y_align: Clutter.ActorAlign.END,
// HACK: without these, ClutterBinLayout
// ignores alignment properties on the actor
x_expand: true,
y_expand: true });
for (let i = 0; i < N_ARROWS; i++) {
let arrow = new Arrow({ opacity: 0 });
this._arrowContainer.add_actor(arrow);
}
this._lockScreenContents.add_actor(this._arrowContainer);
let dragArea = new Clutter.Rect({ origin: new Clutter.Point({ x: 0, y: -global.screen_height, }),
size: new Clutter.Size({ width: global.screen_width,
height: global.screen_height }) });
let action = new Clutter.DragAction({ drag_axis: Clutter.DragAxis.Y_AXIS,
drag_area: dragArea });
action.connect('drag-begin', Lang.bind(this, this._onDragBegin));
action.connect('drag-end', Lang.bind(this, this._onDragEnd));
this._lockScreenGroup.add_action(action);
this._lockDialogGroup = new St.Widget({ x_expand: true,
y_expand: true,
pivot_point: new Clutter.Point({ x: 0.5, y: 0.5 }),
name: 'lockDialogGroup' });
this.actor.add_actor(this._lockDialogGroup);
this.actor.add_actor(this._lockScreenGroup);
this._presence = new GnomeSession.Presence(Lang.bind(this, function(proxy, error) {
if (error) {
logError(error, 'Error while reading gnome-session presence');
return;
}
this._onStatusChanged(proxy.status);
}));
this._presence.connectSignal('StatusChanged', Lang.bind(this, function(proxy, senderName, [status]) {
this._onStatusChanged(status);
}));
this._screenSaverDBus = new ShellDBus.ScreenSaverDBus(this);
this._loginManager = LoginManager.getLoginManager();
this._loginSession = this._loginManager.getCurrentSessionProxy();
this._loginSession.connectSignal('Lock', Lang.bind(this, function() { this.lock(false); }));
this._loginSession.connectSignal('Unlock', Lang.bind(this, function() { this.unlock(); }));
this._settings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
this._isModal = false;
this._hasLockScreen = false;
this._isGreeter = false;
this._isActive = false;
this._inUnlockAnimation = false;
this._lightbox = new Lightbox.Lightbox(Main.uiGroup,
{ inhibitEvents: true,
fadeInTime: STANDARD_FADE_TIME,
fadeFactor: 1 });
},
_onLockScreenKeyRelease: function(actor, event) {
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_Escape ||
symbol == Clutter.KEY_Return ||
symbol == Clutter.KEY_KP_Enter) {
this._ensureUnlockDialog();
this._hideLockScreen(true);
return true;
}
// Don't bump if the lock screen is not showing or is
// animating, as the bump overrides the animation and would
// remove any onComplete handler
if (this._lockScreenState == MessageTray.State.SHOWN)
this._bumpLockScreen();
return true;
},
_onLockScreenScroll: function(actor, event) {
if (this._lockScreenState != MessageTray.State.SHOWN)
return false;
let delta = 0;
if (event.get_scroll_direction() == Clutter.ScrollDirection.UP)
delta = 5;
else if (event.get_scroll_direction() == Clutter.ScrollDirection.SMOOTH)
delta = Math.max(0, event.get_scroll_delta()[0]);
this._lockScreenScrollCounter += delta;
// 7 standard scrolls to lift up
if (this._lockScreenScrollCounter > 35) {
this._ensureUnlockDialog();
this._hideLockScreen(true);
}
return true;
},
_animateArrows: function() {
let arrows = this._arrowContainer.get_children();
let unitaryDelay = ARROW_ANIMATION_TIME / (arrows.length + 1);
let maxOpacity = 255 * ARROW_ANIMATION_PEAK_OPACITY;
for (let i = 0; i < arrows.length; i++) {
arrows.opacity = 0;
Tweener.addTween(arrows[i],
{ opacity: 0,
delay: unitaryDelay * (N_ARROWS - (i + 1)),
time: ARROW_ANIMATION_TIME,
transition: function(t, b, c, d) {
if (t < d/2)
return TweenerEquations.easeOutQuad(t, 0, maxOpacity, d/2);
else
return TweenerEquations.easeInQuad(t - d/2, maxOpacity, -maxOpacity, d/2);
}
});
}
return true;
},
_onDragBegin: function() {
Tweener.removeTweens(this._lockScreenGroup);
this._lockScreenState = MessageTray.State.HIDING;
this._ensureUnlockDialog();
},
_onDragEnd: function(action, actor, eventX, eventY, modifiers) {
if (this._lockScreenGroup.y < -(ARROW_DRAG_THRESHOLD * global.stage.height)) {
// Complete motion automatically
this._hideLockScreen(true);
} else {
// restore the lock screen to its original place
// try to use the same speed as the normal animation
let h = global.stage.height;
let time = CURTAIN_SLIDE_TIME * (-this._lockScreenGroup.y) / h;
Tweener.removeTweens(this._lockScreenGroup);
Tweener.addTween(this._lockScreenGroup,
{ y: 0,
time: time,
transition: 'linear',
onComplete: function() {
this._lockScreenGroup.fixed_position_set = false;
this._lockScreenState = MessageTray.State.SHOWN;
},
onCompleteScope: this,
});
// If we have a unlock dialog, cancel it
if (this._dialog) {
this._dialog.cancel();
if (!this._isGreeter) {
this._dialog = null;
}
}
}
},
_onStatusChanged: function(status) {
if (status == GnomeSession.PresenceStatus.IDLE) {
if (this._dialog) {
this._dialog.cancel();
if (!this._isGreeter) {
this._dialog = null;
}
}
if (!this._isModal) {
Main.pushModal(this.actor);
this._isModal = true;
}
if (!this._isActive)
this._lightbox.show();
} else {
let lightboxWasShown = this._lightbox.shown;
this._lightbox.hide();
let shouldLock = lightboxWasShown && this._settings.get_boolean(LOCK_ENABLED_KEY);
if (shouldLock || this._isActive) {
this.lock(false);
} else if (this._isModal) {
this.unlock();
}
}
},
showDialog: function() {
// Ensure that the stage window is mapped, before taking a grab
// otherwise X errors out
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
if (!this._isModal) {
Main.pushModal(this.actor);
this._isModal = true;
}
return false;
}));
this.actor.show();
this._isGreeter = Main.sessionMode.isGreeter;
this._ensureUnlockDialog();
this._hideLockScreen(false);
},
_bumpLockScreen: function() {
Tweener.removeTweens(this._lockScreenGroup);
Tweener.addTween(this._lockScreenGroup,
{ y: -BUMP_SIZE,
time: BUMP_TIME / 2,
transition: 'easeOutQuad',
onComplete: function() {
Tweener.addTween(this,
{ y: 0,
time: BUMP_TIME / 2,
transition: 'easeInQuad' });
}
});
},
_hideLockScreen: function(animate) {
this._lockScreenState = MessageTray.State.HIDING;
if (animate) {
// Tween the lock screen out of screen
// try to use the same speed regardless of original position
let h = global.stage.height;
let time = CURTAIN_SLIDE_TIME * (h + this._lockScreenGroup.y) / h;
Tweener.removeTweens(this._lockScreenGroup);
Tweener.addTween(this._lockScreenGroup,
{ y: -h,
time: time,
transition: 'linear',
onComplete: function() {
this._lockScreenState = MessageTray.State.HIDDEN;
this._lockScreenGroup.hide();
},
onCompleteScope: this,
});
} else {
this._lockScreenState = MessageTray.State.HIDDEN;
this._lockScreenGroup.hide();
}
if (Main.sessionMode.currentMode == 'lock-screen')
Main.sessionMode.popMode('lock-screen');
},
_ensureUnlockDialog: function() {
if (!this._dialog) {
let constructor = Main.sessionMode.unlockDialog;
this._dialog = new constructor(this._lockDialogGroup);
if (!this._dialog) {
// This session mode has no locking capabilities
this.unlock();
return;
}
this._dialog.connect('loaded', Lang.bind(this, function() {
if (!this._dialog.open()) {
log('Could not open login dialog: failed to acquire grab');
this.unlock();
}
}));
this._dialog.connect('failed', Lang.bind(this, this._onUnlockFailed));
this._dialog.connect('unlocked', Lang.bind(this, this._onUnlockSucceded));
}
},
_onUnlockFailed: function() {
this._resetLockScreen(true, false);
},
_onUnlockSucceded: function() {
this._tweenUnlocked();
},
_resetLockScreen: function(animateLockScreen, animateLockDialog) {
this._ensureLockScreen();
this._lockDialogGroup.scale_x = 1;
this._lockDialogGroup.scale_y = 1;
this._lockScreenGroup.show();
this._lockScreenState = MessageTray.State.SHOWING;
if (animateLockScreen) {
this._lockScreenGroup.y = -global.screen_height;
Tweener.removeTweens(this._lockScreenGroup);
Tweener.addTween(this._lockScreenGroup,
{ y: 0,
time: SHORT_FADE_TIME,
transition: 'linear',
onComplete: function() {
this._lockScreenShown();
},
onCompleteScope: this
});
} else {
this._lockScreenGroup.fixed_position_set = false;
this._lockScreenShown();
}
if (animateLockDialog) {
this._lockDialogGroup.opacity = 0;
Tweener.removeTweens(this._lockDialogGroup);
Tweener.addTween(this._lockDialogGroup,
{ opacity: 255,
time: SHORT_FADE_TIME,
transition: 'easeOutQuad' });
} else {
this._lockDialogGroup.opacity = 255;
}
this._lockScreenGroup.grab_key_focus();
if (Main.sessionMode.currentMode != 'lock-screen')
Main.sessionMode.pushMode('lock-screen');
},
_lockScreenShown: function() {
if (this._dialog && !this._isGreeter) {
this._dialog.destroy();
this._dialog = null;
}
if (this._arrowAnimationId)
Mainloop.source_remove(this._arrowAnimationId);
this._arrowAnimationId = Mainloop.timeout_add(6000, Lang.bind(this, this._animateArrows));
this._animateArrows();
this._lockScreenState = MessageTray.State.SHOWN;
this._lockScreenGroup.fixed_position_set = false;
this._lockScreenScrollCounter = 0;
this.emit('lock-screen-shown');
},
// Some of the actors in the lock screen are heavy in
// resources, so we only create them when needed
_ensureLockScreen: function() {
if (this._hasLockScreen)
return;
this._lockScreenContentsBox = new St.BoxLayout({ x_align: Clutter.ActorAlign.CENTER,
y_align: Clutter.ActorAlign.CENTER,
x_expand: true,
y_expand: true,
vertical: true });
this._clock = new Clock();
this._lockScreenContentsBox.add(this._clock.actor, { x_fill: true,
y_fill: true });
this._lockScreenContents.add_actor(this._lockScreenContentsBox);
if (this._settings.get_boolean('show-notifications')) {
this._notificationsBox = new NotificationsBox();
this._lockScreenContentsBox.add(this._notificationsBox.actor, { x_fill: true,
y_fill: true,
expand: true });
}
this._hasLockScreen = true;
},
_clearLockScreen: function() {
this._clock.destroy();
this._clock = null;
if (this._notificationsBox) {
this._notificationsBox.destroy();
this._notificationsBox = null;
}
this._lockScreenContentsBox.destroy();
if (this._arrowAnimationId) {
Mainloop.source_remove(this._arrowAnimationId);
this._arrowAnimationId = 0;
}
this._hasLockScreen = false;
},
get locked() {
return this._isActive;
},
_tweenUnlocked: function() {
this._inUnlockAnimation = true;
this.unlock();
Tweener.addTween(this._lockDialogGroup, {
scale_x: 0,
scale_y: 0,
time: Overview.ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: function() {
if (this._dialog) {
this._dialog.destroy();
this._dialog = null;
}
this.actor.hide();
this._inUnlockAnimation = false;
},
onCompleteScope: this
});
},
unlock: function() {
if (this._hasLockScreen)
this._clearLockScreen();
if (this._dialog && !this._isGreeter) {
this._dialog.destroy();
this._dialog = null;
}
this._lightbox.hide();
if (this._isModal) {
Main.popModal(this.actor);
this._isModal = false;
}
if (!this._inUnlockAnimation)
this.actor.hide();
if (Main.sessionMode.currentMode == 'lock-screen')
Main.sessionMode.popMode('lock-screen');
if (Main.sessionMode.currentMode == 'unlock-dialog')
Main.sessionMode.popMode('unlock-dialog');
this._isActive = false;
this.emit('lock-status-changed');
},
lock: function(animate) {
if (!this._isModal) {
Main.pushModal(this.actor);
this._isModal = true;
}
this.actor.show();
if (Main.sessionMode.currentMode != 'unlock-dialog' &&
Main.sessionMode.currentMode != 'lock-screen') {
this._isGreeter = Main.sessionMode.isGreeter;
if (!this._isGreeter)
Main.sessionMode.pushMode('unlock-dialog');
}
this._resetLockScreen(animate, animate);
this._isActive = true;
this.emit('lock-status-changed');
},
});
Signals.addSignalMethods(ScreenShield.prototype);
/* Fallback code to handle session locking using gnome-screensaver,
in case the required GDM dependency is not there
*/
const ScreenShieldFallback = new Lang.Class({
Name: 'ScreenShieldFallback',
_init: function() {
this._proxy = new Gio.DBusProxy({ g_connection: Gio.DBus.session,
g_name: 'org.gnome.ScreenSaver',
g_object_path: '/org/gnome/ScreenSaver',
g_interface_name: 'org.gnome.ScreenSaver',
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES),
});
this._proxy.init(null);
this._proxy.connect('g-signal', Lang.bind(this, this._onSignal));
this._proxy.connect('notify::g-name-owner', Lang.bind(this, this._onNameOwnerChanged));
},
_onNameOwnerChanged: function(object, pspec) {
if (this._proxy.g_name_owner)
[this._locked] = this._proxy.call_sync('GetActive', null,
Gio.DBusCallFlags.NONE, -1, null).deep_unpack();
else
this._locked = false;
this.emit('lock-status-changed', this._locked);
},
_onSignal: function(proxy, senderName, signalName, params) {
if (signalName == 'ActiveChanged') {
[this._locked] = params.deep_unpack();
this.emit('lock-status-changed', this._locked);
}
},
get locked() {
return this._locked;
},
lock: function() {
this._proxy.call('Lock', null, Gio.DBusCallFlags.NONE, -1, null,
Lang.bind(this, function(proxy, result) {
proxy.call_finish(result);
this.emit('lock-screen-shown');
}));
},
unlock: function() {
this._proxy.call('SetActive', GLib.Variant.new('(b)', false),
Gio.DBusCallFlags.NONE, -1, null, null);
},
});
Signals.addSignalMethods(ScreenShieldFallback.prototype);

View File

@ -10,17 +10,13 @@ const Util = imports.misc.util;
const FileUtils = imports.misc.fileUtils;
const Main = imports.ui.main;
const DISABLED_OPEN_SEARCH_PROVIDERS_KEY = 'disabled-open-search-providers';
// Not currently referenced by the search API, but
// this enumeration can be useful for provider
// implementations.
const MatchType = {
NONE: 0,
SUBSTRING: 1,
MULTIPLE_SUBSTRING: 2,
PREFIX: 3,
MULTIPLE_PREFIX: 4
PREFIX: 2
};
const SearchResultDisplay = new Lang.Class({
@ -53,7 +49,7 @@ const SearchResultDisplay = new Lang.Class({
* Remove all results from this display.
*/
clear: function() {
this.actor.get_children().forEach(function (actor) { actor.destroy(); });
this.actor.destroy_all_children();
},
/**
@ -72,11 +68,8 @@ const SearchResultDisplay = new Lang.Class({
* Subclass this object to add a new result type
* to the search system, then call registerProvider()
* in SearchSystem with an instance.
* By default, search is synchronous and uses the
* Search is asynchronous and uses the
* getInitialResultSet()/getSubsearchResultSet() methods.
* For asynchronous search, set the async property to true
* and implement getInitialResultSetAsync()/getSubsearchResultSetAsync()
* instead.
*/
const SearchProvider = new Lang.Class({
Name: 'SearchProvider',
@ -84,7 +77,6 @@ const SearchProvider = new Lang.Class({
_init: function(title) {
this.title = title;
this.searchSystem = null;
this.async = false;
},
/**
@ -95,7 +87,7 @@ const SearchProvider = new Lang.Class({
* therefore a single term of length one or two), or when
* a new term is added.
*
* Should return an array of result identifier strings representing
* Should "return" an array of result identifier strings representing
* items which match the given search terms. This
* is expected to be a substring match on the metadata for a given
* item. Ordering of returned results is up to the discretion of the provider,
@ -105,6 +97,9 @@ const SearchProvider = new Lang.Class({
* description) before single matches
* * Put items which match on a prefix before non-prefix substring matches
*
* We say "return" above, but in order to make the query asynchronous, use
* this.searchSystem.pushResults();. The return value should be ignored.
*
* This function should be fast; do not perform unindexed full-text searches
* or network queries.
*/
@ -112,18 +107,6 @@ const SearchProvider = new Lang.Class({
throw new Error('Not implemented');
},
/**
* getInitialResultSetAsync:
* @terms: Array of search terms, treated as logical AND
*
* Like getInitialResultSet(), but the method should return immediately
* without a return value - use SearchSystem.pushResults() when the
* corresponding results are ready.
*/
getInitialResultSetAsync: function(terms) {
throw new Error('Not implemented');
},
/**
* getSubsearchResultSet:
* @previousResults: Array of item identifiers
@ -136,62 +119,26 @@ const SearchProvider = new Lang.Class({
*
* This allows search providers to only search through the previous
* result set, rather than possibly performing a full re-query.
*
* Similar to getInitialResultSet, the return value for this will
* be ignored; use this.searchSystem.pushResults();.
*/
getSubsearchResultSet: function(previousResults, newTerms) {
throw new Error('Not implemented');
},
/**
* getSubsearchResultSetAsync:
* @previousResults: Array of item identifiers
* @newTerms: Updated search terms
*
* Like getSubsearchResultSet(), but the method should return immediately
* without a return value - use SearchSystem.pushResults() when the
* corresponding results are ready.
*/
getSubsearchResultSetAsync: function(previousResults, newTerms) {
throw new Error('Not implemented');
},
/**
* getResultMetas:
* @ids: Result identifier strings
*
* Return an array of objects with 'id', 'name', (both strings) and
* Call callback with array of objects with 'id', 'name', (both strings) and
* 'createIcon' (function(size) returning a Clutter.Texture) properties
* with the same number of members as @ids
*/
getResultMetas: function(ids) {
getResultMetas: function(ids, callback) {
throw new Error('Not implemented');
},
/**
* getResultMetasAsync:
* @ids: Result identifier strings
* @callback: callback to pass the results to when ready
*
* Like getResultMetas(), but the method should return immediately
* without a return value - pass the results to the provided @callback
* when ready.
*/
getResultMetasAsync: function(ids, callback) {
throw new Error('Not implemented');
},
/**
* createResultContainer:
*
* Search providers may optionally override this to render their
* results in a custom fashion. The default implementation
* will create a vertical list.
*
* Returns: An instance of SearchResultDisplay.
*/
createResultContainerActor: function() {
return null;
},
/**
* createResultActor:
* @resultMeta: Object with result metadata
@ -220,99 +167,6 @@ const SearchProvider = new Lang.Class({
});
Signals.addSignalMethods(SearchProvider.prototype);
const OpenSearchSystem = new Lang.Class({
Name: 'OpenSearchSystem',
_init: function() {
this._providers = [];
global.settings.connect('changed::' + DISABLED_OPEN_SEARCH_PROVIDERS_KEY, Lang.bind(this, this._refresh));
this._refresh();
},
getProviders: function() {
let res = [];
for (let i = 0; i < this._providers.length; i++)
res.push({ id: i, name: this._providers[i].name });
return res;
},
setSearchTerms: function(terms) {
this._terms = terms;
},
_checkSupportedProviderLanguage: function(provider) {
if (provider.url.search(/{language}/) == -1)
return true;
let langs = GLib.get_language_names();
langs.push('en');
let lang = null;
for (let i = 0; i < langs.length; i++) {
for (let k = 0; k < provider.langs.length; k++) {
if (langs[i] == provider.langs[k])
lang = langs[i];
}
if (lang)
break;
}
provider.lang = lang;
return lang != null;
},
activateResult: function(id, params) {
let searchTerms = this._terms.join(' ');
let url = this._providers[id].url.replace('{searchTerms}', encodeURIComponent(searchTerms));
if (url.match('{language}'))
url = url.replace('{language}', this._providers[id].lang);
try {
Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context());
} catch (e) {
// TODO: remove this after glib will be removed from moduleset
// In the default jhbuild, gio is in our prefix but gvfs is not
Util.spawn(['gvfs-open', url])
}
Main.overview.hide();
},
_addProvider: function(fileName) {
let path = global.datadir + '/open-search-providers/' + fileName;
let source = Shell.get_file_contents_utf8_sync(path);
let [success, name, url, langs, icon_uri] = Shell.parse_search_provider(source);
let provider ={ name: name,
url: url,
id: this._providers.length,
icon_uri: icon_uri,
langs: langs };
if (this._checkSupportedProviderLanguage(provider)) {
this._providers.push(provider);
this.emit('changed');
}
},
_refresh: function() {
this._providers = [];
let names = global.settings.get_strv(DISABLED_OPEN_SEARCH_PROVIDERS_KEY);
let file = Gio.file_new_for_path(global.datadir + '/open-search-providers');
FileUtils.listDirAsync(file, Lang.bind(this, function(files) {
for (let i = 0; i < files.length; i++) {
let enabled = true;
let name = files[i].get_name();
for (let k = 0; k < names.length; k++)
if (names[k] == name)
enabled = false;
if (enabled)
this._addProvider(name);
}
}));
}
});
Signals.addSignalMethods(OpenSearchSystem.prototype);
const SearchSystem = new Lang.Class({
Name: 'SearchSystem',
@ -379,42 +233,33 @@ const SearchSystem = new Lang.Class({
}
}
let previousResultsArr = this._previousResults;
let results = [];
this._previousTerms = terms;
this._previousResults = results;
if (isSubSearch) {
for (let i = 0; i < this._providers.length; i++) {
let [provider, previousResults] = this._previousResults[i];
let [provider, previousResults] = previousResultsArr[i];
try {
if (provider.async) {
provider.getSubsearchResultSetAsync(previousResults, terms);
results.push([provider, []]);
} else {
let providerResults = provider.getSubsearchResultSet(previousResults, terms);
results.push([provider, providerResults]);
}
results.push([provider, []]);
provider.getSubsearchResultSet(previousResults, terms);
} catch (error) {
global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
log('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
}
}
} else {
for (let i = 0; i < this._providers.length; i++) {
let provider = this._providers[i];
try {
if (provider.async) {
provider.getInitialResultSetAsync(terms);
results.push([provider, []]);
} else {
let providerResults = provider.getInitialResultSet(terms);
results.push([provider, providerResults]);
}
results.push([provider, []]);
provider.getInitialResultSet(terms);
} catch (error) {
global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
log('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
}
}
}
this._previousTerms = terms;
this._previousResults = results;
this.emit('search-completed', results);
},
});
Signals.addSignalMethods(SearchSystem.prototype);

View File

@ -5,6 +5,7 @@ const Lang = imports.lang;
const Gtk = imports.gi.Gtk;
const Meta = imports.gi.Meta;
const St = imports.gi.St;
const Atk = imports.gi.Atk;
const DND = imports.ui.dnd;
const IconGrid = imports.ui.iconGrid;
@ -33,12 +34,13 @@ const SearchResult = new Lang.Class({
content = new St.Bin({ style_class: 'search-result-content',
reactive: true,
can_focus: true,
track_hover: true });
track_hover: true,
accessible_role: Atk.Role.PUSH_BUTTON });
let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
{ createIcon: this.metaInfo['createIcon'] });
content.set_child(icon.actor);
this._dragActorSource = icon.icon;
this.actor.label_actor = icon.label;
content.label_actor = icon.label;
} else {
if (content._delegate && content._delegate.getDragActorSource)
this._dragActorSource = content._delegate.getDragActorSource();
@ -119,13 +121,7 @@ const GridSearchResults = new Lang.Class({
if (results.length == 0)
return;
if (provider.async) {
provider.getResultMetasAsync(results,
Lang.bind(this, this.renderResults));
} else {
let metas = provider.getResultMetas(results);
this.renderResults(metas);
}
provider.getResultMetas(results, Lang.bind(this, this.renderResults));
}));
}));
this._notDisplayedResult = [];
@ -135,7 +131,7 @@ const GridSearchResults = new Lang.Class({
getResultsForDisplay: function() {
let alreadyVisible = this._pendingClear ? 0 : this._grid.visibleItemsCount();
let canDisplay = this._grid.childrenInRow(this._width) * MAX_SEARCH_RESULTS_ROWS
let canDisplay = this._grid.childrenInRow(this._width) * this._grid.getRowLimit()
- alreadyVisible;
let numResults = Math.min(this._notDisplayedResult.length, canDisplay);
@ -177,11 +173,9 @@ const GridSearchResults = new Lang.Class({
const SearchResults = new Lang.Class({
Name: 'SearchResults',
_init: function(searchSystem, openSearchSystem) {
_init: function(searchSystem) {
this._searchSystem = searchSystem;
this._searchSystem.connect('search-updated', Lang.bind(this, this._updateCurrentResults));
this._searchSystem.connect('search-completed', Lang.bind(this, this._updateResults));
this._openSearchSystem = openSearchSystem;
this._searchSystem.connect('search-updated', Lang.bind(this, this._updateResults));
this.actor = new St.BoxLayout({ name: 'searchResults',
vertical: true });
@ -196,7 +190,7 @@ const SearchResults = new Lang.Class({
scrollView.add_actor(this._content);
this.actor.add(scrollView, { x_fill: true,
y_fill: false,
y_fill: true,
expand: true,
x_align: St.Align.START,
y_align: St.Align.START });
@ -211,87 +205,37 @@ const SearchResults = new Lang.Class({
}));
this._statusText = new St.Label({ style_class: 'search-statustext' });
this._content.add(this._statusText);
this._statusBin = new St.Bin({ x_align: St.Align.MIDDLE,
y_align: St.Align.MIDDLE });
this._content.add(this._statusBin, { expand: true });
this._statusBin.add_actor(this._statusText);
this._providers = this._searchSystem.getProviders();
this._providerMeta = [];
this._providerMetaResults = {};
for (let i = 0; i < this._providers.length; i++) {
this.createProviderMeta(this._providers[i]);
this._providerMetaResults[this.providers[i].title] = [];
}
this._searchProvidersBox = new St.BoxLayout({ style_class: 'search-providers-box' });
this.actor.add(this._searchProvidersBox);
this._openSearchProviders = [];
this._openSearchSystem.connect('changed', Lang.bind(this, this._updateOpenSearchProviderButtons));
this._updateOpenSearchProviderButtons();
this._highlightDefault = false;
this._defaultResult = null;
},
_updateOpenSearchProviderButtons: function() {
for (let i = 0; i < this._openSearchProviders.length; i++)
this._openSearchProviders[i].actor.destroy();
this._openSearchProviders = this._openSearchSystem.getProviders();
for (let i = 0; i < this._openSearchProviders.length; i++)
this._createOpenSearchProviderButton(this._openSearchProviders[i]);
},
_createOpenSearchProviderButton: function(provider) {
let button = new St.Button({ style_class: 'dash-search-button',
reactive: true,
can_focus: true,
x_fill: true,
y_align: St.Align.MIDDLE });
let bin = new St.Bin({ x_fill: false,
x_align:St.Align.MIDDLE });
button.connect('clicked', Lang.bind(this, function() {
this._openSearchSystem.activateResult(provider.id);
}));
let title = new St.Label({ text: provider.name,
style_class: 'dash-search-button-label' });
button.label_actor = title;
bin.set_child(title);
button.set_child(bin);
provider.actor = button;
button.setSelected = function(selected) {
if (selected)
button.add_style_pseudo_class('selected');
else
button.remove_style_pseudo_class('selected');
};
button.activate = Lang.bind(this, function() {
this._openSearchSystem.activateResult(provider.id);
});
button.actor = button;
this._searchProvidersBox.add(button);
},
createProviderMeta: function(provider) {
let providerBox = new St.BoxLayout({ style_class: 'search-section',
vertical: true });
let title = new St.Label({ style_class: 'search-section-header',
text: provider.title });
providerBox.add(title);
providerBox.add(title, { x_fill: false, x_align: St.Align.START });
let resultDisplayBin = new St.Bin({ style_class: 'search-section-results',
x_fill: true,
y_fill: true });
providerBox.add(resultDisplayBin, { expand: true });
let resultDisplay = provider.createResultContainerActor();
if (resultDisplay == null) {
resultDisplay = new GridSearchResults(provider);
}
let resultDisplay = new GridSearchResults(provider);
resultDisplayBin.set_child(resultDisplay.actor);
this._providerMeta.push({ provider: provider,
actor: providerBox,
resultDisplay: resultDisplay,
hasPendingResults: false });
resultDisplay: resultDisplay });
this._content.add(providerBox);
},
@ -307,7 +251,6 @@ const SearchResults = new Lang.Class({
},
_clearDisplay: function() {
this._visibleResultsCount = 0;
for (let i = 0; i < this._providerMeta.length; i++) {
let meta = this._providerMeta[i];
meta.resultDisplay.clear();
@ -323,14 +266,14 @@ const SearchResults = new Lang.Class({
reset: function() {
this._searchSystem.reset();
this._statusText.hide();
this._statusBin.hide();
this._clearDisplay();
},
startingSearch: function() {
this.reset();
this._statusText.set_text(_("Searching..."));
this._statusText.show();
this._statusBin.show();
},
doSearch: function (searchString) {
@ -346,8 +289,6 @@ const SearchResults = new Lang.Class({
for (let i = 0; i < this._providerMeta.length; i++) {
let meta = this._providerMeta[i];
if (meta.hasPendingResults)
return;
if (!meta.actor.visible)
continue;
@ -359,9 +300,6 @@ const SearchResults = new Lang.Class({
}
}
if (!newDefaultResult)
newDefaultResult = this._searchProvidersBox.get_first_child();
if (newDefaultResult != this._defaultResult) {
if (this._defaultResult)
this._defaultResult.setSelected(false);
@ -372,71 +310,57 @@ const SearchResults = new Lang.Class({
}
},
_updateCurrentResults: function(searchSystem, results) {
let terms = searchSystem.getTerms();
let [provider, providerResults] = results;
let meta = this._metaForProvider(provider);
meta.hasPendingResults = false;
this._updateProviderResults(provider, providerResults, terms);
},
_updateStatusText: function () {
let haveResults = false;
_updateProviderResults: function(provider, providerResults, terms) {
let meta = this._metaForProvider(provider);
if (providerResults.length == 0) {
this._clearDisplayForProvider(provider);
meta.resultDisplay.setResults([], []);
} else {
this._providerMetaResults[provider.title] = providerResults;
meta.resultDisplay.setResults(providerResults, terms);
let results = meta.resultDisplay.getResultsForDisplay();
if (provider.async) {
provider.getResultMetasAsync(results, Lang.bind(this,
function(metas) {
this._clearDisplayForProvider(provider);
meta.actor.show();
this._content.hide();
meta.resultDisplay.renderResults(metas);
this._maybeSetInitialSelection();
this._content.show();
}));
} else {
let metas = provider.getResultMetas(results);
this._clearDisplayForProvider(provider);
meta.actor.show();
meta.resultDisplay.renderResults(metas);
for (let i = 0; i < this._providerMeta.length; ++i)
if (this._providerMeta[i].resultDisplay.getFirstResult()) {
haveResults = true;
break;
}
if (!haveResults) {
this._statusText.set_text(_("No results."));
this._statusBin.show();
} else {
this._statusBin.hide();
}
this._maybeSetInitialSelection();
},
_updateResults: function(searchSystem, results) {
if (results.length == 0) {
this._statusText.set_text(_("No matching results."));
this._statusText.show();
} else {
this._statusText.hide();
}
let terms = searchSystem.getTerms();
this._openSearchSystem.setSearchTerms(terms);
let [provider, providerResults] = results;
let meta = this._metaForProvider(provider);
// To avoid CSS transitions causing flickering when the first search
// result stays the same, we hide the content while filling in the
// results.
this._content.hide();
if (providerResults.length == 0) {
this._clearDisplayForProvider(provider);
meta.resultDisplay.setResults([], []);
this._maybeSetInitialSelection();
this._updateStatusText();
} else {
meta.resultDisplay.setResults(providerResults, terms);
let results = meta.resultDisplay.getResultsForDisplay();
for (let i = 0; i < results.length; i++) {
let [provider, providerResults] = results[i];
let meta = this._metaForProvider(provider);
meta.hasPendingResults = provider.async;
if (!meta.hasPendingResults)
this._updateProviderResults(provider, providerResults, terms);
provider.getResultMetas(results, Lang.bind(this, function(metas) {
this._clearDisplayForProvider(provider);
meta.actor.show();
// Hiding drops the key focus if we have it
let focus = global.stage.get_key_focus();
// To avoid CSS transitions causing flickering when
// the first search result stays the same, we hide the
// content while filling in the results.
this._content.hide();
meta.resultDisplay.renderResults(metas);
this._maybeSetInitialSelection();
this._updateStatusText();
this._content.show();
if (this._content.contains(focus))
global.stage.set_key_focus(focus);
}));
}
this._content.show();
return true;
},
activateDefault: function() {

160
js/ui/sessionMode.js Normal file
View File

@ -0,0 +1,160 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Signals = imports.signals;
const Main = imports.ui.main;
const Params = imports.misc.params;
const DEFAULT_MODE = 'restrictive';
const _modes = {
'restrictive': {
hasOverview: false,
showCalendarEvents: false,
allowSettings: false,
allowExtensions: false,
allowKeybindingsWhenModal: false,
hasRunDialog: false,
hasWorkspaces: false,
hasWindows: false,
hasNotifications: false,
isLocked: false,
isGreeter: false,
isPrimary: false,
unlockDialog: null,
components: [],
panel: {
left: [],
center: [],
right: []
},
},
'gdm': {
allowKeybindingsWhenModal: true,
hasNotifications: true,
isGreeter: true,
isPrimary: true,
unlockDialog: imports.gdm.loginDialog.LoginDialog,
components: ['polkitAgent'],
panel: {
left: [],
center: ['dateMenu'],
right: ['a11y', 'display', 'keyboard',
'volume', 'battery', 'powerMenu']
}
},
'lock-screen': {
isLocked: true,
isGreeter: undefined,
unlockDialog: undefined,
components: ['polkitAgent', 'telepathyClient'],
panel: {
left: ['userMenu'],
center: [],
right: ['lockScreen']
},
},
'unlock-dialog': {
isLocked: true,
unlockDialog: undefined,
components: ['polkitAgent', 'telepathyClient'],
panel: {
left: ['userMenu'],
center: [],
right: ['a11y', 'keyboard', 'lockScreen']
},
},
'initial-setup': {
isPrimary: true,
components: ['keyring'],
panel: {
left: [],
center: ['dateMenu'],
right: ['a11y', 'keyboard', 'volume']
}
},
'user': {
hasOverview: true,
showCalendarEvents: true,
allowSettings: true,
allowExtensions: true,
hasRunDialog: true,
hasWorkspaces: true,
hasWindows: true,
hasNotifications: true,
isLocked: false,
isPrimary: true,
unlockDialog: imports.ui.unlockDialog.UnlockDialog,
components: ['networkAgent', 'polkitAgent', 'telepathyClient',
'keyring', 'recorder', 'autorunManager', 'automountManager'],
panel: {
left: ['activities', 'appMenu'],
center: ['dateMenu'],
right: ['a11y', 'keyboard', 'volume', 'bluetooth',
'network', 'battery', 'userMenu']
}
}
};
function listModes() {
let modes = Object.getOwnPropertyNames(_modes);
for (let i = 0; i < modes.length; i++)
if (_modes[modes[i]].isPrimary)
print(modes[i]);
}
const SessionMode = new Lang.Class({
Name: 'SessionMode',
_init: function() {
global.connect('notify::session-mode', Lang.bind(this, this._sync));
let mode = _modes[global.session_mode].isPrimary ? global.session_mode
: 'user';
this._modeStack = [mode];
this._sync();
},
pushMode: function(mode) {
this._modeStack.push(mode);
this._sync();
},
popMode: function(mode) {
if (this.currentMode != mode || this._modeStack.length === 1)
throw new Error("Invalid SessionMode.popMode");
this._modeStack.pop();
this._sync();
},
switchMode: function(to) {
if (this.currentMode == to)
return;
this._modeStack[this._modeStack.length - 1] = to;
this._sync();
},
get currentMode() {
return this._modeStack[this._modeStack.length - 1];
},
_sync: function() {
let params = _modes[this.currentMode];
params = Params.parse(params, _modes[DEFAULT_MODE]);
// A simplified version of Lang.copyProperties, handles
// undefined as a special case for "no change / inherit from previous mode"
for (let prop in params) {
if (params[prop] !== undefined)
this[prop] = params[prop];
}
this.emit('updated');
}
});
Signals.addSignalMethods(SessionMode.prototype);

View File

@ -7,6 +7,7 @@ const Shell = imports.gi.Shell;
const Config = imports.misc.config;
const ExtensionSystem = imports.ui.extensionSystem;
const ExtensionDownloader = imports.ui.extensionDownloader;
const ExtensionUtils = imports.misc.extensionUtils;
const Flashspot = imports.ui.flashspot;
const Main = imports.ui.main;
@ -17,17 +18,6 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
<arg type="b" direction="out" name="success" />
<arg type="s" direction="out" name="result" />
</method>
<method name="ListExtensions">
<arg type="a{sa{sv}}" direction="out" name="extensions" />
</method>
<method name="GetExtensionInfo">
<arg type="s" direction="in" name="extension" />
<arg type="a{sv}" direction="out" name="info" />
</method>
<method name="GetExtensionErrors">
<arg type="s" direction="in" name="extension" />
<arg type="as" direction="out" name="errors" />
</method>
<method name="ScreenshotArea">
<arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="y"/>
@ -56,30 +46,21 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
<arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/>
</method>
<method name="EnableExtension">
<arg type="s" direction="in" name="uuid"/>
</method>
<method name="DisableExtension">
<arg type="s" direction="in" name="uuid"/>
</method>
<method name="InstallRemoteExtension">
<arg type="s" direction="in" name="uuid"/>
<arg type="s" direction="in" name="version"/>
</method>
<method name="UninstallExtension">
<arg type="s" direction="in" name="uuid"/>
<arg type="b" direction="out" name="success"/>
</method>
<method name="LaunchExtensionPrefs">
<arg type="s" direction="in" name="uuid"/>
</method>
<property name="OverviewActive" type="b" access="readwrite" />
<property name="ApiVersion" type="i" access="read" />
<property name="ShellVersion" type="s" access="read" />
<signal name="ExtensionStatusChanged">
<arg type="s" name="uuid"/>
<arg type="i" name="state"/>
<arg type="s" name="error"/>
</interface>;
const ScreenSaverIface = <interface name="org.gnome.ScreenSaver">
<method name="Lock">
</method>
<method name="GetActive">
<arg name="active" direction="out" type="b" />
</method>
<method name="SetActive">
<arg name="value" direction="in" type="u" />
</method>
<signal name="ActiveChanged">
<arg name="new_value" type="b" />
</signal>
</interface>;
@ -89,8 +70,8 @@ const GnomeShell = new Lang.Class({
_init: function() {
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
ExtensionSystem.connect('extension-state-changed',
Lang.bind(this, this._extensionStateChanged));
this._extensionsSerivce = new GnomeShellExtensions();
},
/**
@ -202,6 +183,67 @@ const GnomeShell = new Lang.Class({
flashspot.fire();
},
get OverviewActive() {
return Main.overview.visible;
},
set OverviewActive(visible) {
if (visible)
Main.overview.show();
else
Main.overview.hide();
},
ShellVersion: Config.PACKAGE_VERSION
});
const GnomeShellExtensionsIface = <interface name="org.gnome.Shell.Extensions">
<method name="ListExtensions">
<arg type="a{sa{sv}}" direction="out" name="extensions" />
</method>
<method name="GetExtensionInfo">
<arg type="s" direction="in" name="extension" />
<arg type="a{sv}" direction="out" name="info" />
</method>
<method name="GetExtensionErrors">
<arg type="s" direction="in" name="extension" />
<arg type="as" direction="out" name="errors" />
</method>
<signal name="ExtensionStatusChanged">
<arg type="s" name="uuid"/>
<arg type="i" name="state"/>
<arg type="s" name="error"/>
</signal>
<method name="InstallRemoteExtension">
<arg type="s" direction="in" name="uuid"/>
<arg type="s" direction="out" name="result"/>
</method>
<method name="UninstallExtension">
<arg type="s" direction="in" name="uuid"/>
<arg type="b" direction="out" name="success"/>
</method>
<method name="LaunchExtensionPrefs">
<arg type="s" direction="in" name="uuid"/>
</method>
<method name="ReloadExtension">
<arg type="s" direction="in" name="uuid"/>
</method>
<method name="CheckForUpdates">
</method>
<property name="ShellVersion" type="s" access="read" />
</interface>;
const GnomeShellExtensions = new Lang.Class({
Name: 'GnomeShellExtensionsDBus',
_init: function() {
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellExtensionsIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
ExtensionSystem.connect('extension-state-changed',
Lang.bind(this, this._extensionStateChanged));
},
ListExtensions: function() {
let out = {};
for (let uuid in ExtensionUtils.extensions) {
@ -260,26 +302,12 @@ const GnomeShell = new Lang.Class({
return extension.errors;
},
EnableExtension: function(uuid) {
let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY);
if (enabledExtensions.indexOf(uuid) == -1)
enabledExtensions.push(uuid);
global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
},
DisableExtension: function(uuid) {
let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY);
while (enabledExtensions.indexOf(uuid) != -1)
enabledExtensions.splice(enabledExtensions.indexOf(uuid), 1);
global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
},
InstallRemoteExtension: function(uuid, version_tag) {
ExtensionSystem.installExtensionFromUUID(uuid, version_tag);
InstallRemoteExtensionAsync: function([uuid], invocation) {
return ExtensionDownloader.installExtension(uuid, invocation);
},
UninstallExtension: function(uuid) {
return ExtensionSystem.uninstallExtensionFromUUID(uuid);
return ExtensionDownloader.uninstallExtension(uuid);
},
LaunchExtensionPrefs: function(uuid) {
@ -289,19 +317,18 @@ const GnomeShell = new Lang.Class({
['extension:///' + uuid], -1, null);
},
get OverviewActive() {
return Main.overview.visible;
ReloadExtension: function(uuid) {
let extension = ExtensionUtils.extensions[uuid];
if (!extension)
return;
ExtensionSystem.reloadExtension(extension);
},
set OverviewActive(visible) {
if (visible)
Main.overview.show();
else
Main.overview.hide();
CheckForUpdates: function() {
ExtensionDownloader.checkForUpdates();
},
ApiVersion: ExtensionSystem.API_VERSION,
ShellVersion: Config.PACKAGE_VERSION,
_extensionStateChanged: function(_, newState) {
@ -309,3 +336,42 @@ const GnomeShell = new Lang.Class({
GLib.Variant.new('(sis)', [newState.uuid, newState.state, newState.error]));
}
});
const ScreenSaverDBus = new Lang.Class({
Name: 'ScreenSaverDBus',
_init: function(screenShield) {
this.parent();
this._screenShield = screenShield;
screenShield.connect('lock-status-changed', Lang.bind(this, function(shield) {
this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [shield.locked]));
}));
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenSaverIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/ScreenSaver');
Gio.DBus.session.own_name('org.gnome.ScreenSaver', Gio.BusNameOwnerFlags.REPLACE, null, null);
},
LockAsync: function(parameters, invocation) {
let tmpId = this._screenShield.connect('lock-screen-shown', Lang.bind(this, function() {
this._screenShield.disconnect(tmpId);
invocation.return_value(null);
}));
this._screenShield.lock(true);
},
SetActive: function(active) {
if (active)
this._screenShield.lock(true);
else
this._screenShield.unlock();
},
GetActive: function() {
return this._screenShield.locked;
}
});

View File

@ -7,7 +7,7 @@ const Main = imports.ui.main;
const Params = imports.misc.params;
const PopupMenu = imports.ui.popupMenu;
const _EntryMenu = new Lang.Class({
const EntryMenu = new Lang.Class({
Name: 'ShellEntryMenu',
Extends: PopupMenu.PopupMenu,
@ -34,18 +34,37 @@ const _EntryMenu = new Lang.Class({
this._pasteItem = item;
this._passwordItem = null;
if (params.isPassword) {
item = new PopupMenu.PopupMenuItem('');
item.connect('activate', Lang.bind(this,
this._onPasswordActivated));
this.addMenuItem(item);
this._passwordItem = item;
}
if (params.isPassword)
this._makePasswordItem();
Main.uiGroup.add_actor(this.actor);
this.actor.hide();
},
_makePasswordItem: function() {
let item = new PopupMenu.PopupMenuItem('');
item.connect('activate', Lang.bind(this,
this._onPasswordActivated));
this.addMenuItem(item);
this._passwordItem = item;
},
get isPassword() {
return this._passwordItem != null;
},
set isPassword(v) {
if (v == this.isPassword)
return;
if (v)
this._makePasswordItem();
else {
this._passwordItem.destroy();
this._passwordItem = null;
}
},
open: function() {
this._updatePasteItem();
this._updateCopyItem();
@ -57,6 +76,12 @@ const _EntryMenu = new Lang.Class({
this.actor.grab_key_focus();
this.parent();
this._entry.add_style_pseudo_class('focus');
},
close: function() {
this._entry.grab_key_focus();
this.parent();
},
_updateCopyItem: function() {
@ -104,65 +129,42 @@ const _EntryMenu = new Lang.Class({
function _setMenuAlignment(entry, stageX) {
let [success, entryX, entryY] = entry.transform_stage_point(stageX, 0);
if (success)
entry._menu.setSourceAlignment(entryX / entry.width);
entry.menu.setSourceAlignment(entryX / entry.width);
};
function _onClicked(action, actor) {
let entry = actor._menu ? actor : actor.get_parent();
if (entry._menu.isOpen) {
entry._menu.close();
} else if (action.get_button() == 3) {
let [stageX, stageY] = action.get_coords();
function _onButtonPressEvent(actor, event, entry) {
if (entry.menu.isOpen) {
entry.menu.close();
return true;
} else if (event.get_button() == 3) {
let [stageX, stageY] = event.get_coords();
_setMenuAlignment(entry, stageX);
entry._menu.open();
}
};
function _onLongPress(action, actor, state) {
let entry = actor._menu ? actor : actor.get_parent();
if (state == Clutter.LongPressState.QUERY)
return action.get_button() == 1 && !entry._menu.isOpen;
if (state == Clutter.LongPressState.ACTIVATE) {
let [stageX, stageY] = action.get_coords();
_setMenuAlignment(entry, stageX);
entry._menu.open();
entry.menu.open();
return true;
}
return false;
};
function _onPopup(actor) {
let entry = actor._menu ? actor : actor.get_parent();
function _onPopup(actor, entry) {
let [success, textX, textY, lineHeight] = entry.clutter_text.position_to_coords(-1);
if (success)
entry._menu.setSourceAlignment(textX / entry.width);
entry._menu.open();
entry.menu.setSourceAlignment(textX / entry.width);
entry.menu.open();
};
function addContextMenu(entry, params) {
if (entry._menu)
if (entry.menu)
return;
entry._menu = new _EntryMenu(entry, params);
entry.menu = new EntryMenu(entry, params);
entry._menuManager = new PopupMenu.PopupMenuManager({ actor: entry });
entry._menuManager.addMenu(entry._menu);
entry._menuManager.addMenu(entry.menu);
let clickAction;
// Add a click action to both the entry and its clutter_text; the former
// Add an event handler to both the entry and its clutter_text; the former
// so padding is included in the clickable area, the latter because the
// event processing of ClutterText prevents event-bubbling.
clickAction = new Clutter.ClickAction();
clickAction.connect('clicked', _onClicked);
clickAction.connect('long-press', _onLongPress);
entry.clutter_text.add_action(clickAction);
entry.clutter_text.connect('button-press-event', Lang.bind(null, _onButtonPressEvent, entry));
entry.connect('button-press-event', Lang.bind(null, _onButtonPressEvent, entry));
clickAction = new Clutter.ClickAction();
clickAction.connect('clicked', _onClicked);
clickAction.connect('long-press', _onLongPress);
entry.add_action(clickAction);
entry.connect('popup-menu', _onPopup);
entry.connect('popup-menu', Lang.bind(null, _onPopup, entry));
}

View File

@ -1,17 +1,21 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Signals = imports.signals;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Pango = imports.gi.Pango;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const CheckBox = imports.ui.checkBox;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const ModalDialog = imports.ui.modalDialog;
const Params = imports.misc.params;
const ShellEntry = imports.ui.shellEntry;
const LIST_ITEM_ICON_SIZE = 48;
@ -48,6 +52,11 @@ function _setLabelsForMessage(dialog, message) {
_setLabelText(dialog.descriptionLabel, labels[1]);
}
function _createIcon(gicon) {
return new St.Icon({ gicon: gicon,
style_class: 'shell-mount-operation-icon' })
}
/* -------------------------------------------------------- */
const ListItem = new Lang.Class({
@ -91,11 +100,11 @@ const ShellMountOperation = new Lang.Class({
Name: 'ShellMountOperation',
_init: function(source, params) {
params = Params.parse(params, { reaskPassword: false });
this._reaskPassword = params.reaskPassword;
params = Params.parse(params, { existingDialog: null });
this._dialog = null;
this._dialogId = 0;
this._existingDialog = params.existingDialog;
this._processesDialog = null;
this.mountOp = new Shell.MountOperation();
@ -107,99 +116,177 @@ const ShellMountOperation = new Lang.Class({
this.mountOp.connect('show-processes-2',
Lang.bind(this, this._onShowProcesses2));
this.mountOp.connect('aborted',
Lang.bind(this, this._onAborted));
Lang.bind(this, this.close));
this.mountOp.connect('show-unmount-progress',
Lang.bind(this, this._onShowUnmountProgress));
this._icon = new St.Icon({ gicon: source.get_icon(),
style_class: 'shell-mount-operation-icon' });
this._gicon = source.get_icon();
},
_closeExistingDialog: function() {
if (!this._existingDialog)
return;
this._existingDialog.close();
this._existingDialog = null;
},
_onAskQuestion: function(op, message, choices) {
this._dialog = new ShellMountQuestionDialog(this._icon);
this._closeExistingDialog();
this._dialog = new ShellMountQuestionDialog(this._gicon);
this._dialog.connect('response',
Lang.bind(this, function(object, choice) {
this.mountOp.set_choice(choice);
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
this._dialogId = this._dialog.connect('response', Lang.bind(this,
function(object, choice) {
this.mountOp.set_choice(choice);
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
this._dialog.close(global.get_current_time());
this._dialog = null;
}));
this.close();
}));
this._dialog.update(message, choices);
this._dialog.open(global.get_current_time());
this._dialog.open();
},
_onAskPassword: function(op, message) {
this._notificationShowing = true;
this._source = new ShellMountPasswordSource(message, this._icon, this._reaskPassword);
_onAskPassword: function(op, message, defaultUser, defaultDomain, flags) {
if (this._existingDialog) {
this._dialog = this._existingDialog;
this._dialog.reaskPassword();
} else {
this._dialog = new ShellMountPasswordDialog(message, this._gicon, flags);
}
this._source.connect('password-ready',
Lang.bind(this, function(source, password) {
this.mountOp.set_password(password);
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
this._dialogId = this._dialog.connect('response', Lang.bind(this,
function(object, choice, password, remember) {
if (choice == -1) {
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
} else {
if (remember)
this.mountOp.set_password_save(Gio.PasswordSave.PERMANENTLY);
else
this.mountOp.set_password_save(Gio.PasswordSave.NEVER);
this._notificationShowing = false;
this._source.destroy();
}));
this._source.connect('destroy',
Lang.bind(this, function() {
if (!this._notificationShowing)
return;
this._notificationShowing = false;
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
}));
this.mountOp.set_password(password);
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
}
}));
this._dialog.open();
},
_onAborted: function(op) {
if (!this._dialog)
return;
close: function(op) {
this._closeExistingDialog();
this._processesDialog = null;
this._dialog.close(global.get_current_time());
this._dialog = null;
if (this._dialog) {
this._dialog.close();
this._dialog = null;
}
if (this._notifier) {
this._notifier.done();
this._notifier = null;
}
},
_onShowProcesses2: function(op) {
this._closeExistingDialog();
let processes = op.get_show_processes_pids();
let choices = op.get_show_processes_choices();
let message = op.get_show_processes_message();
if (!this._processesDialog) {
this._processesDialog = new ShellProcessesDialog(this._icon);
this._processesDialog = new ShellProcessesDialog(this._gicon);
this._dialog = this._processesDialog;
this._processesDialog.connect('response',
Lang.bind(this, function(object, choice) {
if (choice == -1) {
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
} else {
this.mountOp.set_choice(choice);
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
}
this._dialogId = this._processesDialog.connect('response', Lang.bind(this,
function(object, choice) {
if (choice == -1) {
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
} else {
this.mountOp.set_choice(choice);
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
}
this._processesDialog.close(global.get_current_time());
this._dialog = null;
}));
this._processesDialog.open(global.get_current_time());
this.close();
}));
this._processesDialog.open();
}
this._processesDialog.update(message, processes, choices);
},
_onShowUnmountProgress: function(op, message, timeLeft, bytesLeft) {
if (!this._notifier)
this._notifier = new ShellUnmountNotifier();
if (bytesLeft == 0)
this._notifier.done(message);
else
this._notifier.show(message);
},
borrowDialog: function() {
if (this._dialogId != 0) {
this._dialog.disconnect(this._dialogId);
this._dialogId = 0;
}
return this._dialog;
}
});
const ShellUnmountNotifier = new Lang.Class({
Name: 'ShellUnmountNotifier',
Extends: MessageTray.Source,
_init: function() {
this.parent('', 'media-removable');
this._notification = null;
Main.messageTray.add(this);
},
show: function(message) {
let [header, text] = message.split('\n', 2);
if (!this._notification) {
this._notification = new MessageTray.Notification(this, header, text);
this._notification.setTransient(true);
this._notification.setUrgency(MessageTray.Urgency.CRITICAL);
} else {
this._notification.update(header, text);
}
this.notify(this._notification);
},
done: function(message) {
if (this._notification) {
this._notification.destroy();
this._notification = null;
}
if (message) {
let notification = new MessageTray.Notification(this, message, null);
notification.setTransient(true);
this.notify(notification);
}
}
});
const ShellMountQuestionDialog = new Lang.Class({
Name: 'ShellMountQuestionDialog',
Extends: ModalDialog.ModalDialog,
_init: function(icon) {
_init: function(gicon) {
this.parent({ styleClass: 'mount-question-dialog' });
let mainContentLayout = new St.BoxLayout();
this.contentLayout.add(mainContentLayout, { x_fill: true,
y_fill: false });
this._iconBin = new St.Bin({ child: icon });
this._iconBin = new St.Bin({ child: _createIcon(gicon) });
mainContentLayout.add(this._iconBin,
{ x_fill: true,
y_fill: false,
@ -234,62 +321,108 @@ const ShellMountQuestionDialog = new Lang.Class({
});
Signals.addSignalMethods(ShellMountQuestionDialog.prototype);
const ShellMountPasswordSource = new Lang.Class({
Name: 'ShellMountPasswordSource',
Extends: MessageTray.Source,
const ShellMountPasswordDialog = new Lang.Class({
Name: 'ShellMountPasswordDialog',
Extends: ModalDialog.ModalDialog,
_init: function(message, icon, reaskPassword) {
_init: function(message, gicon, flags) {
let strings = message.split('\n');
this.parent(strings[0]);
this.parent({ styleClass: 'prompt-dialog' });
this._notification = new ShellMountPasswordNotification(this, strings, icon, reaskPassword);
let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
vertical: false });
this.contentLayout.add(mainContentBox);
// add ourselves as a source, and popup the notification
Main.messageTray.add(this);
this.notify(this._notification);
},
});
Signals.addSignalMethods(ShellMountPasswordSource.prototype);
let icon = _createIcon(gicon);
mainContentBox.add(icon,
{ x_fill: true,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.START });
const ShellMountPasswordNotification = new Lang.Class({
Name: 'ShellMountPasswordNotification',
Extends: MessageTray.Notification,
this._messageBox = new St.BoxLayout({ style_class: 'prompt-dialog-message-layout',
vertical: true });
mainContentBox.add(this._messageBox,
{ y_align: St.Align.START, expand: true, x_fill: true, y_fill: true });
_init: function(source, strings, icon, reaskPassword) {
this.parent(source, strings[0], null, { customContent: true, icon: icon });
// set the notification to transient and urgent, so that it
// expands out
this.setTransient(true);
this.setUrgency(MessageTray.Urgency.CRITICAL);
let subject = new St.Label({ style_class: 'prompt-dialog-headline' });
this._messageBox.add(subject,
{ y_fill: false,
y_align: St.Align.START });
if (strings[0])
subject.set_text(strings[0]);
let description = new St.Label({ style_class: 'prompt-dialog-description' });
description.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
description.clutter_text.line_wrap = true;
this._messageBox.add(description,
{ y_fill: true,
y_align: St.Align.START });
if (strings[1])
this.addBody(strings[1]);
description.set_text(strings[1]);
if (reaskPassword) {
let label = new St.Label({ style_class: 'mount-password-reask',
text: _("Wrong password, please try again") });
this._passwordBox = new St.BoxLayout({ vertical: false, style_class: 'prompt-dialog-password-box' });
this._messageBox.add(this._passwordBox);
this.addActor(label);
this._passwordLabel = new St.Label(({ style_class: 'prompt-dialog-password-label',
text: _("Password") }));
this._passwordBox.add(this._passwordLabel, { y_fill: false, y_align: St.Align.MIDDLE });
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
text: "",
can_focus: true});
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivate));
this._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
this._passwordBox.add(this._passwordEntry, {expand: true });
this.setInitialKeyFocus(this._passwordEntry);
this._errorMessageLabel = new St.Label({ style_class: 'prompt-dialog-error-label',
text: _("Sorry, that didn\'t work. Please try again.") });
this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._errorMessageLabel.clutter_text.line_wrap = true;
this._errorMessageLabel.hide();
this._messageBox.add(this._errorMessageLabel);
if (flags & Gio.AskPasswordFlags.SAVING_SUPPORTED) {
this._rememberChoice = new CheckBox.CheckBox();
this._rememberChoice.getLabelActor().text = _("Remember Password");
this._rememberChoice.actor.checked = true;
this._messageBox.add(this._rememberChoice.actor);
} else {
this._rememberChoice = null;
}
this._responseEntry = new St.Entry({ style_class: 'mount-password-entry',
can_focus: true });
this.setActionArea(this._responseEntry);
let buttons = [{ label: _("Cancel"),
action: Lang.bind(this, this._onCancelButton),
key: Clutter.Escape
},
{ label: _("Unlock"),
action: Lang.bind(this, this._onUnlockButton),
default: true
}];
this._responseEntry.clutter_text.connect('activate',
Lang.bind(this, this._onEntryActivated));
this._responseEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
this._responseEntry.grab_key_focus();
this.setButtons(buttons);
},
_onEntryActivated: function() {
let text = this._responseEntry.get_text();
if (text == '')
return;
reaskPassword: function() {
this._passwordEntry.set_text('');
this._errorMessageLabel.show();
},
this.source.emit('password-ready', text);
_onCancelButton: function() {
this.emit('response', -1, '', false);
},
_onUnlockButton: function() {
this._onEntryActivate();
},
_onEntryActivate: function() {
this.emit('response', 1,
this._passwordEntry.get_text(),
this._rememberChoice &&
this._rememberChoice.actor.checked);
}
});
@ -297,14 +430,14 @@ const ShellProcessesDialog = new Lang.Class({
Name: 'ShellProcessesDialog',
Extends: ModalDialog.ModalDialog,
_init: function(icon) {
_init: function(gicon) {
this.parent({ styleClass: 'show-processes-dialog' });
let mainContentLayout = new St.BoxLayout();
this.contentLayout.add(mainContentLayout, { x_fill: true,
y_fill: false });
this._iconBin = new St.Bin({ child: icon });
this._iconBin = new St.Bin({ child: _createIcon(gicon) });
mainContentLayout.add(this._iconBin,
{ x_fill: true,
y_fill: false,
@ -338,21 +471,17 @@ const ShellProcessesDialog = new Lang.Class({
scrollView.hide();
this._applicationList = new St.BoxLayout({ vertical: true });
scrollView.add_actor(this._applicationList,
{ x_fill: true,
y_fill: true,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
scrollView.add_actor(this._applicationList);
this._applicationList.connect('actor-added',
Lang.bind(this, function() {
if (this._applicationList.get_children().length == 1)
if (this._applicationList.get_n_children() == 1)
scrollView.show();
}));
this._applicationList.connect('actor-removed',
Lang.bind(this, function() {
if (this._applicationList.get_children().length == 0)
if (this._applicationList.get_n_children() == 0)
scrollView.hide();
}));
},
@ -386,3 +515,253 @@ const ShellProcessesDialog = new Lang.Class({
}
});
Signals.addSignalMethods(ShellProcessesDialog.prototype);
const GnomeShellMountOpIface = <interface name="org.Gtk.MountOperationHandler">
<method name="AskPassword">
<arg type="s" direction="in" name="object_id"/>
<arg type="s" direction="in" name="message"/>
<arg type="s" direction="in" name="icon_name"/>
<arg type="s" direction="in" name="default_user"/>
<arg type="s" direction="in" name="default_domain"/>
<arg type="u" direction="in" name="flags"/>
<arg type="u" direction="out" name="response"/>
<arg type="a{sv}" direction="out" name="response_details"/>
</method>
<method name="AskQuestion">
<arg type="s" direction="in" name="object_id"/>
<arg type="s" direction="in" name="message"/>
<arg type="s" direction="in" name="icon_name"/>
<arg type="as" direction="in" name="choices"/>
<arg type="u" direction="out" name="response"/>
<arg type="a{sv}" direction="out" name="response_details"/>
</method>
<method name="ShowProcesses">
<arg type="s" direction="in" name="object_id"/>
<arg type="s" direction="in" name="message"/>
<arg type="s" direction="in" name="icon_name"/>
<arg type="ai" direction="in" name="application_pids"/>
<arg type="as" direction="in" name="choices"/>
<arg type="u" direction="out" name="response"/>
<arg type="a{sv}" direction="out" name="response_details"/>
</method>
<method name="Close"/>
</interface>;
const ShellMountOperationType = {
NONE: 0,
ASK_PASSWORD: 1,
ASK_QUESTION: 2,
SHOW_PROCESSES: 3
};
const GnomeShellMountOpHandler = new Lang.Class({
Name: 'GnomeShellMountOpHandler',
_init: function() {
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellMountOpIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gtk/MountOperationHandler');
Gio.bus_own_name_on_connection(Gio.DBus.session, 'org.gtk.MountOperationHandler',
Gio.BusNameOwnerFlags.REPLACE, null, null);
this._dialog = null;
this._volumeMonitor = Gio.VolumeMonitor.get();
this._ensureEmptyRequest();
},
_ensureEmptyRequest: function() {
this._currentId = null;
this._currentInvocation = null;
this._currentType = ShellMountOperationType.NONE;
},
_clearCurrentRequest: function(response, details) {
if (this._currentInvocation) {
this._currentInvocation.return_value(
GLib.Variant.new('(ua{sv})', [response, details]));
}
this._ensureEmptyRequest();
},
_setCurrentRequest: function(invocation, id, type) {
let oldId = this._currentId;
let oldType = this._currentType;
let requestId = id + '@' + invocation.get_sender();
this._clearCurrentRequest(Gio.MountOperationResult.UNHANDLED, {});
this._currentInvocation = invocation;
this._currentId = requestId;
this._currentType = type;
if (this._dialog && (oldId == requestId) && (oldType == type))
return true;
return false;
},
_closeDialog: function() {
if (this._dialog) {
this._dialog.close();
this._dialog = null;
}
},
_createGIcon: function(iconName) {
let realIconName = iconName ? iconName : 'drive-harddisk';
return new Gio.ThemedIcon({ name: realIconName,
use_default_fallbacks: true });
},
/**
* AskPassword:
* @id: an opaque ID identifying the object for which the operation is requested
* The ID must be unique in the context of the calling process.
* @message: the message to display
* @icon_name: the name of an icon to display
* @default_user: the default username for display
* @default_domain: the default domain for display
* @flags: a set of GAskPasswordFlags
* @response: a GMountOperationResult
* @response_details: a dictionary containing the response details as
* entered by the user. The dictionary MAY contain the following properties:
* - "password" -> (s): a password to be used to complete the mount operation
* - "password_save" -> (u): a GPasswordSave
*
* The dialog will stay visible until clients call the Close() method, or
* another dialog becomes visible.
* Calling AskPassword again for the same id will have the effect to clear
* the existing dialog and update it with a message indicating the previous
* attempt went wrong.
*/
AskPasswordAsync: function(params, invocation) {
let [id, message, iconName, defaultUser, defaultDomain, flags] = params;
if (this._setCurrentRequest(invocation, id, ShellMountOperationType.ASK_PASSWORD)) {
this._dialog.reaskPassword();
return;
}
this._closeDialog();
this._dialog = new ShellMountPasswordDialog(message, this._createGIcon(iconName), flags);
this._dialog.connect('response', Lang.bind(this,
function(object, choice, password, remember) {
let details = {};
let response;
if (choice == -1) {
response = Gio.MountOperationResult.ABORTED;
} else {
response = Gio.MountOperationResult.HANDLED;
let passSave = remember ? Gio.PasswordSave.PERMANENTLY : Gio.PasswordSave.NEVER;
details['password_save'] = GLib.Variant.new('u', passSave);
details['password'] = GLib.Variant.new('s', password);
}
this._clearCurrentRequest(response, details);
}));
this._dialog.open();
},
/**
* AskQuestion:
* @id: an opaque ID identifying the object for which the operation is requested
* The ID must be unique in the context of the calling process.
* @message: the message to display
* @icon_name: the name of an icon to display
* @choices: an array of choice strings
* GetResponse:
* @response: a GMountOperationResult
* @response_details: a dictionary containing the response details as
* entered by the user. The dictionary MAY contain the following properties:
* - "choice" -> (i): the chosen answer among the array of strings passed in
*
* The dialog will stay visible until clients call the Close() method, or
* another dialog becomes visible.
* Calling AskQuestion again for the same id will have the effect to clear
* update the dialog with the new question.
*/
AskQuestionAsync: function(params, invocation) {
let [id, message, iconName, choices] = params;
if (this._setCurrentRequest(invocation, id, ShellMountOperationType.ASK_QUESTION)) {
this._dialog.update(message, choices);
return;
}
this._closeDialog();
this._dialog = new ShellMountQuestionDialog(this._createGIcon(iconName), message);
this._dialog.connect('response', Lang.bind(this,
function(object, choice) {
this._clearCurrentRequest(Gio.MountOperationResult.HANDLED,
{ choice: GLib.Variant.new('i', choice) });
}));
this._dialog.update(message, choices);
this._dialog.open();
},
/**
* ShowProcesses:
* @id: an opaque ID identifying the object for which the operation is requested
* The ID must be unique in the context of the calling process.
* @message: the message to display
* @icon_name: the name of an icon to display
* @application_pids: the PIDs of the applications to display
* @choices: an array of choice strings
* @response: a GMountOperationResult
* @response_details: a dictionary containing the response details as
* entered by the user. The dictionary MAY contain the following properties:
* - "choice" -> (i): the chosen answer among the array of strings passed in
*
* The dialog will stay visible until clients call the Close() method, or
* another dialog becomes visible.
* Calling ShowProcesses again for the same id will have the effect to clear
* the existing dialog and update it with the new message and the new list
* of processes.
*/
ShowProcessesAsync: function(params, invocation) {
let [id, message, iconName, applicationPids, choices] = params;
if (this._setCurrentRequest(invocation, id, ShellMountOperationType.SHOW_PROCESSES)) {
this._dialog.update(message, applicationPids, choices);
return;
}
this._closeDialog();
this._dialog = new ShellProcessesDialog(this._createGIcon(iconName));
this._dialog.connect('response', Lang.bind(this,
function(object, choice) {
let response;
let details = {};
if (choice == -1) {
response = Gio.MountOperationResult.ABORTED;
} else {
response = Gio.MountOperationResult.HANDLED;
details['choice'] = GLib.Variant.new('i', choice);
}
this._clearCurrentRequest(response, details);
}));
this._dialog.update(message, applicationPids, choices);
this._dialog.open();
},
/**
* Close:
*
* Closes a dialog previously opened by AskPassword, AskQuestion or ShowProcesses.
* If no dialog is open, does nothing.
*/
Close: function(params, invocation) {
this._clearCurrentRequest(Gio.MountOperationResult.UNHANDLED, {});
this._closeDialog();
}
});

View File

@ -1,18 +1,10 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const GDesktopEnums = imports.gi.GDesktopEnums;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Util = imports.misc.util;
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
const KEY_STICKY_KEYS_ENABLED = 'stickykeys-enable';
@ -44,7 +36,7 @@ const ATIndicator = new Lang.Class({
Extends: PanelMenu.SystemStatusButton,
_init: function() {
this.parent('preferences-desktop-accessibility', _("Accessibility"));
this.parent('preferences-desktop-accessibility-symbolic', _("Accessibility"));
let highContrast = this._buildHCItem();
this.menu.addMenuItem(highContrast);
@ -56,9 +48,9 @@ const ATIndicator = new Lang.Class({
let textZoom = this._buildFontItem();
this.menu.addMenuItem(textZoom);
// let screenReader = this._buildItem(_("Screen Reader"), APPLICATIONS_SCHEMA,
// 'screen-reader-enabled');
// this.menu.addMenuItem(screenReader);
let screenReader = this._buildItem(_("Screen Reader"), APPLICATIONS_SCHEMA,
'screen-reader-enabled');
this.menu.addMenuItem(screenReader);
let screenKeyboard = this._buildItem(_("Screen Keyboard"), APPLICATIONS_SCHEMA,
'screen-keyboard-enabled');

View File

@ -1,15 +1,11 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gdk = imports.gi.Gdk;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const GnomeBluetoothApplet = imports.gi.GnomeBluetoothApplet;
const Gtk = imports.gi.Gtk;
const GnomeBluetooth = imports.gi.GnomeBluetooth;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
@ -28,7 +24,7 @@ const Indicator = new Lang.Class({
Extends: PanelMenu.SystemStatusButton,
_init: function() {
this.parent('bluetooth-disabled', _("Bluetooth"));
this.parent('bluetooth-disabled-symbolic', _("Bluetooth"));
this._applet = new GnomeBluetoothApplet.Applet();
@ -36,11 +32,11 @@ const Indicator = new Lang.Class({
this._applet.connect('notify::killswitch-state', Lang.bind(this, this._updateKillswitch));
this._killswitch.connect('toggled', Lang.bind(this, function() {
let current_state = this._applet.killswitch_state;
if (current_state != GnomeBluetoothApplet.KillswitchState.HARD_BLOCKED &&
current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER) {
if (current_state != GnomeBluetooth.KillswitchState.HARD_BLOCKED &&
current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER) {
this._applet.killswitch_state = this._killswitch.state ?
GnomeBluetoothApplet.KillswitchState.UNBLOCKED:
GnomeBluetoothApplet.KillswitchState.SOFT_BLOCKED;
GnomeBluetooth.KillswitchState.UNBLOCKED:
GnomeBluetooth.KillswitchState.SOFT_BLOCKED;
} else
this._killswitch.setToggleState(false);
}));
@ -94,10 +90,10 @@ const Indicator = new Lang.Class({
_updateKillswitch: function() {
let current_state = this._applet.killswitch_state;
let on = current_state == GnomeBluetoothApplet.KillswitchState.UNBLOCKED;
let has_adapter = current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER;
let can_toggle = current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER &&
current_state != GnomeBluetoothApplet.KillswitchState.HARD_BLOCKED;
let on = current_state == GnomeBluetooth.KillswitchState.UNBLOCKED;
let has_adapter = current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER;
let can_toggle = current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER &&
current_state != GnomeBluetooth.KillswitchState.HARD_BLOCKED;
this._killswitch.setToggleState(on);
if (can_toggle)
@ -106,17 +102,14 @@ const Indicator = new Lang.Class({
/* TRANSLATORS: this means that bluetooth was disabled by hardware rfkill */
this._killswitch.setStatus(_("hardware disabled"));
if (has_adapter)
this.actor.show();
else
this.actor.hide();
this.actor.visible = has_adapter;
if (on) {
this._discoverable.actor.show();
this.setIcon('bluetooth-active');
this.setIcon('bluetooth-active-symbolic');
} else {
this._discoverable.actor.hide();
this.setIcon('bluetooth-disabled');
this.setIcon('bluetooth-disabled-symbolic');
}
},
@ -308,7 +301,7 @@ const Indicator = new Lang.Class({
_ensureSource: function() {
if (!this._source) {
this._source = new Source();
this._source = new MessageTray.Source(_("Bluetooth"), 'bluetooth-active');
Main.messageTray.add(this._source);
}
},
@ -333,35 +326,6 @@ const Indicator = new Lang.Class({
}
});
const Source = new Lang.Class({
Name: 'BluetoothSource',
Extends: MessageTray.Source,
_init: function() {
this.parent(_("Bluetooth"));
this._setSummaryIcon(this.createNotificationIcon());
},
notify: function(notification) {
this._private_destroyId = notification.connect('destroy', Lang.bind(this, function(notification) {
if (this.notification == notification) {
// the destroyed notification is the last for this source
this.notification.disconnect(this._private_destroyId);
this.destroy();
}
}));
this.parent(notification);
},
createNotificationIcon: function() {
return new St.Icon({ icon_name: 'bluetooth-active',
icon_type: St.IconType.SYMBOLIC,
icon_size: this.ICON_SIZE });
}
});
const AuthNotification = new Lang.Class({
Name: 'AuthNotification',
Extends: MessageTray.Notification,
@ -412,7 +376,7 @@ const ConfirmNotification = new Lang.Class({
this._applet = applet;
this._devicePath = device_path;
this.addBody(_("Device %s wants to pair with this computer").format(long_name));
this.addBody(_("Please confirm whether the PIN '%s' matches the one on the device.").format(pin));
this.addBody(_("Please confirm whether the PIN '%06d' matches the one on the device.").format(pin));
this.addButton('matches', _("Matches"));
this.addButton('does-not-match', _("Does not match"));
@ -448,7 +412,8 @@ const PinNotification = new Lang.Class({
this._entry.connect('key-release-event', Lang.bind(this, function(entry, event) {
let key = event.get_key_symbol();
if (key == Clutter.KEY_Return) {
this.emit('action-invoked', 'ok');
if (this._canActivateOkButton())
this.emit('action-invoked', 'ok');
return true;
} else if (key == Clutter.KEY_Escape) {
this.emit('action-invoked', 'cancel');
@ -461,6 +426,12 @@ const PinNotification = new Lang.Class({
this.addButton('ok', _("OK"));
this.addButton('cancel', _("Cancel"));
this.setButtonSensitive('ok', this._canActivateOkButton());
this._entry.clutter_text.connect('text-changed', Lang.bind(this,
function() {
this.setButtonSensitive('ok', this._canActivateOkButton());
}));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
if (action == 'ok') {
if (this._numeric) {
@ -483,6 +454,14 @@ const PinNotification = new Lang.Class({
}));
},
_canActivateOkButton: function() {
// PINs have a fixed length of 6
if (this._numeric)
return this._entry.clutter_text.text.length == 6;
else
return true;
},
grabFocus: function(lockTray) {
this.parent(lockTray);
global.stage.set_key_focus(this._entry);

View File

@ -1,45 +1,196 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GdkPixbuf = imports.gi.GdkPixbuf;
const Gkbd = imports.gi.Gkbd;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const GnomeDesktop = imports.gi.GnomeDesktop;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
try {
var IBus = imports.gi.IBus;
if (!('new_async' in IBus.Bus))
throw "IBus version is too old";
const IBusCandidatePopup = imports.ui.ibusCandidatePopup;
} catch (e) {
var IBus = null;
log(e);
}
const Main = imports.ui.main;
const PopupMenu = imports.ui.popupMenu;
const PanelMenu = imports.ui.panelMenu;
const Util = imports.misc.util;
const DESKTOP_INPUT_SOURCES_SCHEMA = 'org.gnome.desktop.input-sources';
const KEY_CURRENT_INPUT_SOURCE = 'current';
const KEY_INPUT_SOURCES = 'sources';
const INPUT_SOURCE_TYPE_XKB = 'xkb';
const INPUT_SOURCE_TYPE_IBUS = 'ibus';
const IBusManager = new Lang.Class({
Name: 'IBusManager',
_init: function(readyCallback) {
if (!IBus)
return;
IBus.init();
this._readyCallback = readyCallback;
this._candidatePopup = new IBusCandidatePopup.CandidatePopup();
this._ibus = null;
this._panelService = null;
this._engines = {};
this._ready = false;
this._registerPropertiesId = 0;
this._nameWatcherId = Gio.DBus.session.watch_name(IBus.SERVICE_IBUS,
Gio.BusNameWatcherFlags.NONE,
Lang.bind(this, this._onNameAppeared),
Lang.bind(this, this._clear));
},
_clear: function() {
if (this._panelService)
this._panelService.destroy();
if (this._ibus)
this._ibus.destroy();
this._ibus = null;
this._panelService = null;
this._candidatePopup.setPanelService(null);
this._engines = {};
this._ready = false;
this._registerPropertiesId = 0;
},
_onNameAppeared: function() {
this._ibus = IBus.Bus.new_async();
this._ibus.connect('connected', Lang.bind(this, this._onConnected));
},
_onConnected: function() {
this._ibus.list_engines_async(-1, null, Lang.bind(this, this._initEngines));
this._ibus.request_name_async(IBus.SERVICE_PANEL,
IBus.BusNameFlag.REPLACE_EXISTING,
-1, null,
Lang.bind(this, this._initPanelService));
this._ibus.connect('disconnected', Lang.bind(this, this._clear));
},
_initEngines: function(ibus, result) {
let enginesList = this._ibus.list_engines_async_finish(result);
if (enginesList) {
for (let i = 0; i < enginesList.length; ++i) {
let name = enginesList[i].get_name();
this._engines[name] = enginesList[i];
}
} else {
this._clear();
return;
}
this._updateReadiness();
},
_initPanelService: function(ibus, result) {
let success = this._ibus.request_name_async_finish(result);
if (success) {
this._panelService = new IBus.PanelService({ connection: this._ibus.get_connection(),
object_path: IBus.PATH_PANEL });
this._candidatePopup.setPanelService(this._panelService);
// Need to set this to get 'global-engine-changed' emitions
this._ibus.set_watch_ibus_signal(true);
this._ibus.connect('global-engine-changed', Lang.bind(this, this._resetProperties));
this._panelService.connect('update-property', Lang.bind(this, this._updateProperty));
this._resetProperties();
} else {
this._clear();
return;
}
this._updateReadiness();
},
_updateReadiness: function() {
this._ready = (Object.keys(this._engines).length > 0 &&
this._panelService != null);
if (this._ready && this._readyCallback)
this._readyCallback();
},
_resetProperties: function() {
this.emit('properties-registered', null);
if (this._registerPropertiesId != 0)
return;
this._registerPropertiesId =
this._panelService.connect('register-properties', Lang.bind(this, function(p, props) {
if (!props.get(0))
return;
this._panelService.disconnect(this._registerPropertiesId);
this._registerPropertiesId = 0;
this.emit('properties-registered', props);
}));
},
_updateProperty: function(panel, prop) {
this.emit('property-updated', prop);
},
activateProperty: function(key, state) {
this._panelService.property_activate(key, state);
},
hasProperties: function(id) {
if (id == 'anthy')
return true;
return false;
},
getEngineDesc: function(id) {
if (!IBus || !this._ready)
return null;
return this._engines[id];
}
});
Signals.addSignalMethods(IBusManager.prototype);
const LayoutMenuItem = new Lang.Class({
Name: 'LayoutMenuItem',
Extends: PopupMenu.PopupBaseMenuItem,
_init: function(config, id, indicator, long_name) {
_init: function(displayName, shortName) {
this.parent();
this._config = config;
this._id = id;
this.label = new St.Label({ text: long_name });
this.indicator = indicator;
this.label = new St.Label({ text: displayName });
this.indicator = new St.Label({ text: shortName });
this.addActor(this.label);
this.addActor(this.indicator);
},
activate: function(event) {
this.parent(event);
this._config.lock_group(this._id);
this.actor.label_actor = this.label;
}
});
const XKBIndicator = new Lang.Class({
Name: 'XKBIndicator',
const InputSourceIndicator = new Lang.Class({
Name: 'InputSourceIndicator',
Extends: PanelMenu.Button,
_propertiesWhitelist: [
'InputMode',
'TypingMode',
'DictMode'
],
_init: function() {
this.parent(0.0, _("Keyboard"));
@ -50,122 +201,325 @@ const XKBIndicator = new Lang.Class({
this.actor.add_actor(this._container);
this.actor.add_style_class_name('panel-status-button');
this._iconActor = new St.Icon({ icon_name: 'keyboard', icon_type: St.IconType.SYMBOLIC, style_class: 'system-status-icon' });
this._container.add_actor(this._iconActor);
this._labelActors = [ ];
this._layoutItems = [ ];
this._labelActors = {};
this._layoutItems = {};
this._showFlags = false;
this._config = Gkbd.Configuration.get();
this._config.connect('changed', Lang.bind(this, this._syncConfig));
this._config.connect('group-changed', Lang.bind(this, this._syncGroup));
this._config.start_listen();
this._settings = new Gio.Settings({ schema: DESKTOP_INPUT_SOURCES_SCHEMA });
this._settings.connect('changed::' + KEY_CURRENT_INPUT_SOURCE, Lang.bind(this, this._currentInputSourceChanged));
this._settings.connect('changed::' + KEY_INPUT_SOURCES, Lang.bind(this, this._inputSourcesChanged));
this._syncConfig();
this._currentSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
this._xkbInfo = new GnomeDesktop.XkbInfo();
this._propSeparator = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(this._propSeparator);
this._propSection = new PopupMenu.PopupMenuSection();
this.menu.addMenuItem(this._propSection);
this._propSection.actor.hide();
this._properties = null;
this._ibusManager = new IBusManager(Lang.bind(this, this._inputSourcesChanged));
this._ibusManager.connect('properties-registered', Lang.bind(this, this._ibusPropertiesRegistered));
this._ibusManager.connect('property-updated', Lang.bind(this, this._ibusPropertyUpdated));
this._inputSourcesChanged();
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._showLayoutItem = this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, this._showLayout));
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._sessionUpdated();
if (global.session_type == Shell.SessionType.USER) {
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, function() {
Main.overview.hide();
Util.spawn(['gkbd-keyboard-display', '-g', String(this._config.get_current_group() + 1)]);
}));
}
this.menu.addSettingsAction(_("Region and Language Settings"), 'gnome-region-panel.desktop');
},
_adjustGroupNames: function(names) {
// Disambiguate duplicate names with a subscript
// This is O(N^2) to avoid sorting names
// but N <= 4 so who cares?
for (let i = 0; i < names.length; i++) {
let name = names[i];
let cnt = 0;
for (let j = i + 1; j < names.length; j++) {
if (names[j] == name) {
cnt++;
// U+2081 SUBSCRIPT ONE
names[j] = name + String.fromCharCode(0x2081 + cnt);
}
}
if (cnt != 0)
names[i] = name + '\u2081';
}
return names;
_sessionUpdated: function() {
// re-using "allowSettings" for the keyboard layout is a bit shady,
// but at least for now it is used as "allow popping up windows
// from shell menus"; we can always add a separate sessionMode
// option if need arises.
this._showLayoutItem.visible = Main.sessionMode.allowSettings;
},
_syncConfig: function() {
this._showFlags = this._config.if_flags_shown();
if (this._showFlags) {
this._container.set_skip_paint(this._iconActor, false);
} else {
this._container.set_skip_paint(this._iconActor, true);
}
_currentInputSourceChanged: function() {
let nVisibleSources = Object.keys(this._layoutItems).length;
let newCurrentSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
let newLayoutItem = this._layoutItems[newCurrentSourceIndex];
let hasProperties;
let groups = this._config.get_group_names();
if (groups.length > 1) {
this.actor.show();
} else {
if (newLayoutItem)
hasProperties = this._ibusManager.hasProperties(newLayoutItem.ibusEngineId);
else
hasProperties = false;
if (!newLayoutItem || (nVisibleSources < 2 && !hasProperties)) {
// This source index might be invalid if we weren't able
// to build a menu item for it, so we hide ourselves since
// we can't fix it here. *shrug*
// We also hide if we have only one visible source unless
// it's an IBus source with properties.
this.menu.close();
this.actor.hide();
return;
}
for (let i = 0; i < this._layoutItems.length; i++)
this.actor.show();
if (this._layoutItems[this._currentSourceIndex]) {
this._layoutItems[this._currentSourceIndex].setShowDot(false);
this._container.set_skip_paint(this._labelActors[this._currentSourceIndex], true);
}
newLayoutItem.setShowDot(true);
let newLabelActor = this._labelActors[newCurrentSourceIndex];
this._container.set_skip_paint(newLabelActor, false);
if (hasProperties)
newLabelActor.set_text(newLayoutItem.indicator.get_text());
this._currentSourceIndex = newCurrentSourceIndex;
},
_inputSourcesChanged: function() {
let sources = this._settings.get_value(KEY_INPUT_SOURCES);
let nSources = sources.n_children();
for (let i in this._layoutItems)
this._layoutItems[i].destroy();
for (let i = 0; i < this._labelActors.length; i++)
for (let i in this._labelActors)
this._labelActors[i].destroy();
let short_names = this._adjustGroupNames(this._config.get_short_group_names());
this._layoutItems = {};
this._labelActors = {};
this._selectedLayout = null;
this._layoutItems = [ ];
this._selectedLabel = null;
this._labelActors = [ ];
for (let i = 0; i < groups.length; i++) {
let icon_name = this._config.get_group_name(i);
let actor;
if (this._showFlags)
actor = new St.Icon({ icon_name: icon_name, icon_type: St.IconType.SYMBOLIC, style_class: 'popup-menu-icon' });
else
actor = new St.Label({ text: short_names[i] });
let item = new LayoutMenuItem(this._config, i, actor, groups[i]);
item._short_group_name = short_names[i];
item._icon_name = icon_name;
this._layoutItems.push(item);
let infos = [];
let infosByShortName = {};
for (let i = 0; i < nSources; i++) {
let info = { exists: false };
let [type, id] = sources.get_child_value(i).deep_unpack();
if (type == INPUT_SOURCE_TYPE_XKB) {
[info.exists, info.displayName, info.shortName, , ] =
this._xkbInfo.get_layout_info(id);
} else if (type == INPUT_SOURCE_TYPE_IBUS) {
let engineDesc = this._ibusManager.getEngineDesc(id);
if (engineDesc) {
let language = IBus.get_language_name(engineDesc.get_language());
info.exists = true;
info.displayName = language + ' (' + engineDesc.get_longname() + ')';
info.shortName = this._makeEngineShortName(engineDesc);
info.ibusEngineId = id;
}
}
if (!info.exists)
continue;
info.sourceIndex = i;
if (!(info.shortName in infosByShortName))
infosByShortName[info.shortName] = [];
infosByShortName[info.shortName].push(info);
infos.push(info);
}
for (let i = 0; i < infos.length; i++) {
let info = infos[i];
if (infosByShortName[info.shortName].length > 1) {
let sub = infosByShortName[info.shortName].indexOf(info) + 1;
info.shortName += String.fromCharCode(0x2080 + sub);
}
let item = new LayoutMenuItem(info.displayName, info.shortName);
item.ibusEngineId = info.ibusEngineId;
this._layoutItems[info.sourceIndex] = item;
this.menu.addMenuItem(item, i);
item.connect('activate', Lang.bind(this, function() {
this._settings.set_value(KEY_CURRENT_INPUT_SOURCE,
GLib.Variant.new_uint32(info.sourceIndex));
}));
let shortLabel = new St.Label({ text: short_names[i] });
this._labelActors.push(shortLabel);
let shortLabel = new St.Label({ text: info.shortName });
this._labelActors[info.sourceIndex] = shortLabel;
this._container.add_actor(shortLabel);
this._container.set_skip_paint(shortLabel, true);
}
this._syncGroup();
this._currentInputSourceChanged();
},
_syncGroup: function() {
let selected = this._config.get_current_group();
_showLayout: function() {
Main.overview.hide();
if (this._selectedLayout) {
this._selectedLayout.setShowDot(false);
this._selectedLayout = null;
let sources = this._settings.get_value(KEY_INPUT_SOURCES);
let current = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
let [type, id] = sources.get_child_value(current).deep_unpack();
let xkbLayout = '';
let xkbVariant = '';
if (type == INPUT_SOURCE_TYPE_XKB) {
[, , , xkbLayout, xkbVariant] = this._xkbInfo.get_layout_info(id);
} else if (type == INPUT_SOURCE_TYPE_IBUS) {
let engineDesc = this._ibusManager.getEngineDesc(id);
if (engineDesc) {
xkbLayout = engineDesc.get_layout();
xkbVariant = '';
}
}
if (this._selectedLabel) {
this._container.set_skip_paint(this._selectedLabel, true);
this._selectedLabel = null;
if (!xkbLayout || xkbLayout.length == 0)
return;
let description = xkbLayout;
if (xkbVariant.length > 0)
description = description + '\t' + xkbVariant;
Util.spawn(['gkbd-keyboard-display', '-l', description]);
},
_makeEngineShortName: function(engineDesc) {
let symbol = engineDesc.get_symbol();
if (symbol && symbol[0])
return symbol;
let langCode = engineDesc.get_language().split('_', 1)[0];
if (langCode.length == 2 || langCode.length == 3)
return langCode.toLowerCase();
return String.fromCharCode(0x2328); // keyboard glyph
},
_propertyWhitelisted: function(prop) {
for (let i = 0; i < this._propertiesWhitelist.length; ++i) {
let key = prop.get_key();
if (key.substr(0, this._propertiesWhitelist[i].length) == this._propertiesWhitelist[i])
return true;
}
return false;
},
let item = this._layoutItems[selected];
item.setShowDot(true);
_ibusPropertiesRegistered: function(im, props) {
this._properties = props;
this._buildPropSection();
},
this._iconActor.icon_name = item._icon_name;
this._selectedLabel = this._labelActors[selected];
this._container.set_skip_paint(this._selectedLabel, this._showFlags);
_ibusPropertyUpdated: function(im, prop) {
if (!this._propertyWhitelisted(prop))
return;
this._selectedLayout = item;
if (this._updateSubProperty(this._properties, prop))
this._buildPropSection();
},
_updateSubProperty: function(props, prop) {
if (!props)
return false;
let p;
for (let i = 0; (p = props.get(i)) != null; ++i) {
if (p.get_key() == prop.get_key() && p.get_prop_type() == prop.get_prop_type()) {
p.update(prop);
return true;
} else if (p.get_prop_type() == IBus.PropType.MENU) {
if (this._updateSubProperty(p.get_sub_props(), prop))
return true;
}
}
return false;
},
_updateIndicatorLabel: function(text) {
let layoutItem = this._layoutItems[this._currentSourceIndex];
let hasProperties;
if (layoutItem)
hasProperties = this._ibusManager.hasProperties(layoutItem.ibusEngineId);
else
hasProperties = false;
if (hasProperties)
this._labelActors[this._currentSourceIndex].set_text(text);
},
_buildPropSection: function() {
this._propSeparator.actor.hide();
this._propSection.actor.hide();
this._propSection.removeAll();
this._buildPropSubMenu(this._propSection, this._properties);
if (!this._propSection.isEmpty()) {
this._propSection.actor.show();
this._propSeparator.actor.show();
}
},
_buildPropSubMenu: function(menu, props) {
if (!props)
return;
let radioGroup = [];
let p;
for (let i = 0; (p = props.get(i)) != null; ++i) {
let prop = p;
if (!this._propertyWhitelisted(prop) ||
!prop.get_visible())
continue;
if (prop.get_key() == 'InputMode') {
let text;
if (prop.get_symbol)
text = prop.get_symbol().get_text();
else
text = prop.get_label().get_text();
if (text && text.length > 0 && text.length < 3)
this._updateIndicatorLabel(text);
}
let item;
let type = prop.get_prop_type();
if (type == IBus.PropType.MENU) {
item = new PopupMenu.PopupSubMenuMenuItem(prop.get_label().get_text());
this._buildPropSubMenu(item.menu, prop.get_sub_props());
} else if (type == IBus.PropType.RADIO) {
item = new PopupMenu.PopupMenuItem(prop.get_label().get_text());
item.prop = prop;
radioGroup.push(item);
item.radioGroup = radioGroup;
item.setShowDot(prop.get_state() == IBus.PropState.CHECKED);
item.connect('activate', Lang.bind(this, function() {
if (item.prop.get_state() == IBus.PropState.CHECKED)
return;
let group = item.radioGroup;
for (let i = 0; i < group.length; ++i) {
if (group[i] == item) {
item.setShowDot(true);
item.prop.set_state(IBus.PropState.CHECKED);
this._ibusManager.activateProperty(item.prop.get_key(),
IBus.PropState.CHECKED);
} else {
group[i].setShowDot(false);
group[i].prop.set_state(IBus.PropState.UNCHECKED);
this._ibusManager.activateProperty(group[i].prop.get_key(),
IBus.PropState.UNCHECKED);
}
}
}));
} else {
continue;
}
item.setSensitive(prop.get_sensitive());
menu.addMenuItem(item);
}
},
_containerGetPreferredWidth: function(container, for_height, alloc) {
@ -173,15 +527,11 @@ const XKBIndicator = new Lang.Class({
// for the height of all children, but we ignore the results
// for those we don't actually display.
let max_min_width = 0, max_natural_width = 0;
if (this._showFlags)
[max_min_width, max_natural_width] = this._iconActor.get_preferred_width(for_height);
for (let i = 0; i < this._labelActors.length; i++) {
for (let i in this._labelActors) {
let [min_width, natural_width] = this._labelActors[i].get_preferred_width(for_height);
if (!this._showFlags) {
max_min_width = Math.max(max_min_width, min_width);
max_natural_width = Math.max(max_natural_width, natural_width);
}
max_min_width = Math.max(max_min_width, min_width);
max_natural_width = Math.max(max_natural_width, natural_width);
}
alloc.min_size = max_min_width;
@ -190,15 +540,11 @@ const XKBIndicator = new Lang.Class({
_containerGetPreferredHeight: function(container, for_width, alloc) {
let max_min_height = 0, max_natural_height = 0;
if (this._showFlags)
[max_min_height, max_natural_height] = this._iconActor.get_preferred_height(for_width);
for (let i = 0; i < this._labelActors.length; i++) {
for (let i in this._labelActors) {
let [min_height, natural_height] = this._labelActors[i].get_preferred_height(for_width);
if (!this._showFlags) {
max_min_height = Math.max(max_min_height, min_height);
max_natural_height = Math.max(max_natural_height, natural_height);
}
max_min_height = Math.max(max_min_height, min_height);
max_natural_height = Math.max(max_natural_height, natural_height);
}
alloc.min_size = max_min_height;
@ -212,8 +558,7 @@ const XKBIndicator = new Lang.Class({
box.y2 -= box.y1;
box.y1 = 0;
this._iconActor.allocate_align_fill(box, 0.5, 0, false, false, flags);
for (let i = 0; i < this._labelActors.length; i++)
for (let i in this._labelActors)
this._labelActors[i].allocate_align_fill(box, 0.5, 0, false, false, flags);
}
});

View File

@ -0,0 +1,57 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GObject = imports.gi.GObject;
const Lang = imports.lang;
const St = imports.gi.St;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const VolumeMenu = imports.ui.status.volume;
const Indicator = new Lang.Class({
Name: 'LockScreenMenuIndicator',
Extends: PanelMenu.SystemStatusButton,
_init: function() {
this.parent(null, _("Volume, network, battery"));
this._volume = Main.panel.statusArea.volume;
if (this._volume) {
this._volumeIcon = this.addIcon(null);
this._volume.mainIcon.bind_property('gicon', this._volumeIcon, 'gicon',
GObject.BindingFlags.SYNC_CREATE);
this._volume.mainIcon.bind_property('visible', this._volumeIcon, 'visible',
GObject.BindingFlags.SYNC_CREATE);
this._volumeControl = VolumeMenu.getMixerControl();
this._volumeMenu = new VolumeMenu.VolumeMenu(this._volumeControl);
this.menu.addMenuItem(this._volumeMenu);
}
this._network = Main.panel.statusArea.network;
if (this._network) {
this._networkIcon = this.addIcon(null);
this._network.mainIcon.bind_property('gicon', this._networkIcon, 'gicon',
GObject.BindingFlags.SYNC_CREATE);
this._network.mainIcon.bind_property('visible', this._networkIcon, 'visible',
GObject.BindingFlags.SYNC_CREATE);
this._networkSecondaryIcon = this.addIcon(null);
this._network.secondaryIcon.bind_property('gicon', this._networkSecondaryIcon, 'gicon',
GObject.BindingFlags.SYNC_CREATE);
this._network.secondaryIcon.bind_property('visible', this._networkSecondaryIcon, 'visible',
GObject.BindingFlags.SYNC_CREATE);
}
this._battery = Main.panel.statusArea.battery;
if (this._battery) {
this._batteryIcon = this.addIcon(null);
this._battery.mainIcon.bind_property('gicon', this._batteryIcon, 'gicon',
GObject.BindingFlags.SYNC_CREATE);
this._battery.mainIcon.bind_property('visible', this._batteryIcon, 'visible',
GObject.BindingFlags.SYNC_CREATE);
}
}
});

File diff suppressed because it is too large Load Diff

View File

@ -2,14 +2,10 @@
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Util = imports.misc.util;
const BUS_NAME = 'org.gnome.SettingsDaemon';
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
@ -56,7 +52,7 @@ const Indicator = new Lang.Class({
Extends: PanelMenu.SystemStatusButton,
_init: function() {
this.parent('battery-missing', _("Battery"));
this.parent('battery-missing-symbolic', _("Battery"));
this._proxy = new PowerManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH);
@ -148,16 +144,21 @@ const Indicator = new Lang.Class({
}));
},
_devicesChanged: function() {
_syncIcon: function() {
let icon = this._proxy.Icon;
let hasIcon = false;
if (icon) {
let gicon = Gio.icon_new_for_string(icon);
this.setGIcon(gicon);
this.actor.show();
} else {
this.menu.close();
this.actor.hide();
hasIcon = true;
}
this.mainIcon.visible = hasIcon;
this.actor.visible = hasIcon;
},
_devicesChanged: function() {
this._syncIcon();
this._readPrimaryDevice();
this._readOtherDevices();
}
@ -176,7 +177,6 @@ const DeviceItem = new Lang.Class({
this._label = new St.Label({ text: this._deviceTypeToString(device_type) });
this._icon = new St.Icon({ gicon: Gio.icon_new_for_string(icon),
icon_type: St.IconType.SYMBOLIC,
style_class: 'popup-menu-icon' });
this._box.add_actor(this._icon);
@ -185,6 +185,8 @@ const DeviceItem = new Lang.Class({
let percentLabel = new St.Label({ text: C_("percent of battery remaining", "%d%%").format(Math.round(percentage)) });
this.addActor(percentLabel, { align: St.Align.END });
//FIXME: ideally we would like to expose this._label and percentLabel
this.actor.label_actor = percentLabel;
},
_deviceTypeToString: function(type) {
@ -212,7 +214,7 @@ const DeviceItem = new Lang.Class({
case UPDeviceType.COMPUTER:
return _("Computer");
default:
return _("Unknown");
return C_("device", "Unknown");
}
}
});

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