Compare commits

...

300 Commits
3.5.2 ... 3.5.5

Author SHA1 Message Date
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
173 changed files with 17533 additions and 16136 deletions

3
.gitignore vendored
View File

@ -50,6 +50,7 @@ po/gnome-shell.pot
po/*.header
po/*.sed
po/*.sin
po/.intltool-merge-cache
po/Makefile.in.in
po/Makevars.template
po/POTFILES
@ -62,6 +63,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

140
NEWS
View File

@ -1,3 +1,143 @@
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]

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 4
#define PLUGIN_API_VERSION 5
typedef struct {
GDBusProxy *proxy;
@ -225,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)
@ -373,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;
@ -469,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;
@ -494,21 +534,20 @@ 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)
{
gboolean ret;
gchar *uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
gchar *uuid;
gboolean enabled;
gsize length;
gchar **uuids;
const gchar **new_uuids;
if (!uuid_is_valid (uuid_str))
{
g_free (uuid_str);
return FALSE;
}
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);
@ -517,7 +556,7 @@ plugin_enable_extension (PluginObject *obj,
{
new_uuids = g_new (const gchar *, length + 2); /* New key, NULL */
memcpy (new_uuids, uuids, length * sizeof (*new_uuids));
new_uuids[length] = uuid_str;
new_uuids[length] = uuid;
new_uuids[length + 1] = NULL;
}
else
@ -526,7 +565,7 @@ plugin_enable_extension (PluginObject *obj,
new_uuids = g_new (const gchar *, length);
for (i = 0; i < length; i ++)
{
if (g_str_equal (uuids[i], uuid_str))
if (g_str_equal (uuids[i], uuid))
continue;
new_uuids[j] = uuids[i];
@ -542,63 +581,112 @@ plugin_enable_extension (PluginObject *obj,
g_strfreev (uuids);
g_free (new_uuids);
g_free (uuid_str);
g_free (uuid);
return ret;
}
static gboolean
plugin_install_extension (PluginObject *obj,
NPString uuid)
{
gchar *uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
typedef struct _AsyncClosure AsyncClosure;
if (!uuid_is_valid (uuid_str))
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)
{
g_free (uuid_str);
return FALSE;
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,
uint32_t argc,
const NPVariant *argv,
NPVariant *result)
{
gchar *uuid;
NPObject *callback, *errback;
AsyncClosure *async_closure;
if (!parse_args ("uoo", argc, argv, &uuid, &callback, &errback))
return FALSE;
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 ("(s)", uuid_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 (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)
{
@ -611,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)
{
@ -647,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);
@ -683,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;
}
@ -759,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)
{
@ -774,59 +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;
return plugin_install_extension (obj,
NPVARIANT_TO_STRING(args[0]));
}
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

View File

@ -1,5 +1,5 @@
AC_PREREQ(2.63)
AC_INIT([gnome-shell],[3.5.2],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_INIT([gnome-shell],[3.5.5],[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)
@ -62,29 +62,30 @@ AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
CLUTTER_MIN_VERSION=1.9.16
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
GJS_MIN_VERSION=1.29.18
MUTTER_MIN_VERSION=3.5.2
FOLKS_MIN_VERSION=0.5.2
GJS_MIN_VERSION=1.33.2
MUTTER_MIN_VERSION=3.5.5
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
@ -105,10 +106,6 @@ 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"])
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
saved_CFLAGS=$CFLAGS
@ -123,7 +120,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 >= 3.5.1)
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],
@ -252,6 +249,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

@ -8,9 +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 $@
@INTLTOOL_DESKTOP_RULE@
searchprovidersdir = $(pkgdatadir)/open-search-providers
dist_searchproviders_DATA = \
@ -36,15 +34,14 @@ 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/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/toggle-off-us.svg \
theme/toggle-off-intl.svg \
@ -53,9 +50,14 @@ dist_theme_DATA = \
theme/ws-switch-arrow-up.svg \
theme/ws-switch-arrow-down.svg
gsettings_SCHEMAS = org.gnome.shell.gschema.xml org.gnome.shell.evolution.calendar.gschema.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,25 +70,19 @@ 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.evolution.calendar.gschema.xml.in \
org.gnome.shell.gschema.xml.in
org.gnome.shell.gschema.xml.in.in
CLEANFILES = \
gnome-shell.desktop.in \
gnome-shell-extension-prefs.in \
$(desktop_DATA) \
$(gsettings_SCHEMAS) \
gschemas.compiled
gschemas.compiled \
org.gnome.shell.gschema.valid \
org.gnome.shell.gschema.xml.in

View File

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- NOTE: This schema is a GNOME 3.4 workaround - it uses the same path
as org.gnome.evolution.calendar, but avoids us requiring Evolution
be installed. In GNOME 3.6 the selected state will become a flag
on the calendar. Because the translations are in Evolution,
this is untranslated and in POTFILES.skip.
-->
<schemalist>
<schema path="/org/gnome/evolution/calendar/" id="org.gnome.shell.evolution.calendar" gettext-domain="evolution">
<key type="as" name="selected-calendars">
<default>[]</default>
<summary>List of selected calendars</summary>
<description>List of calendars to load</description>
</key>
<key type="as" name="selected-tasks">
<default>[]</default>
<summary>List of selected task lists</summary>
<description>List of task lists to load</description>
</key>
</schema>
</schemalist>

View File

@ -61,7 +61,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"/>
@ -108,24 +107,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">

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;
}

View File

@ -58,54 +58,48 @@ stage {
/* Scroll Bars */
StScrollBar
{
StScrollBar {
padding: 0px;
}
StScrollView.vfade
{
StScrollView.vfade {
-st-vfade-offset: 68px;
}
StScrollView.hfade
{
StScrollView.hfade {
-st-hfade-offset: 68px;
}
StScrollView StScrollBar
{
min-width: 16px;
min-height: 16px;
StScrollView StScrollBar {
min-width: 14px;
min-height: 14px;
}
StScrollBar StBin#trough {
background-color: #080808;
border: 1px solid #2d2d2d;
background-color: rgba(0,0,0,0.3);
border-radius: 8px;
}
StScrollBar StButton#vhandle
{
background-image: url("scroll-vhandle.svg");
background-color: #252525;
border: 1px solid #080808;
StScrollBar StButton#vhandle {
background-color: #959797;
border: 2px solid #242424;
border-radius: 8px;
}
StScrollBar StButton#hhandle
{
background-image: url("scroll-hhandle.svg");
background-color: #252525;
border: 1px solid #080808;
StScrollBar StButton#hhandle {
background-color: #959797;
border: 2px solid #242424;
border-radius: 8px;
}
StScrollBar StButton#hhandle:hover,
StScrollBar StButton#vhandle:hover
{
background-color: #292929;
StScrollBar StButton#vhandle:hover {
background-color: #c2c3c3;
}
StScrollBar StButton#hhandle:active,
StScrollBar StButton#vhandle:active {
background-color: #729fcf;
}
/* Check Boxes */
@ -136,7 +130,7 @@ StScrollBar StButton#vhandle:hover
.popup-menu-boxpointer {
-arrow-border-radius: 8px;
-arrow-background-color: rgba(0,0,0,0.9);
-arrow-background-color: black;
-arrow-border-width: 2px;
-arrow-border-color: #a5a5a5;
-arrow-base: 24px;
@ -202,7 +196,7 @@ StScrollBar StButton#vhandle:hover
background-color: #4c4c4c;
}
.popup-menu-item:insensitive {
StButton.popup-menu-item:insensitive {
color: #9f9f9f;
}
@ -333,6 +327,79 @@ StScrollBar StButton#vhandle:hover
color: #9f9f9f;
}
/* Entries */
#searchEntry,
.notification StEntry,
.login-dialog-prompt-entry,
.prompt-dialog-password-entry {
color: rgb(64, 64, 64);
caret-color: rgb(64, 64, 64);
font-size: 12pt;
caret-size: 1px;
selected-color: black;
padding: 4px 12px;
}
#searchEntry,
.notification StEntry {
border: 2px solid rgba(245,245,245,0.2);
background-gradient-start: rgba(5,5,6,0.1);
background-gradient-end: rgba(254,254,254,0.1);
background-gradient-direction: vertical;
transition-duration: 300;
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.6);
}
#searchEntry:focus,
#searchEntry:hover,
.notification StEntry:focus,
.login-dialog-prompt-entry,
.prompt-dialog-password-entry {
border: 2px solid rgb(136,138,133);
background-gradient-start: rgb(200,200,200);
background-gradient-end: white;
background-gradient-direction: vertical;
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.6);
}
.notification StEntry:focus,
.prompt-dialog-password-entry:focus,
.login-dialog-prompt-entry:focus {
border: 2px solid #3465a4;
}
#searchEntry {
color: rgb(128, 128, 128);
caret-color: rgb(128, 128, 128);
}
#searchEntry:focus {
color: rgb(64, 64, 64);
caret-color: rgb(64, 64, 64);
font-weight: bold;
transition-duration: 0;
}
.notification StEntry,
.prompt-dialog-password-entry,
.login-dialog-prompt-entry {
border-radius: 5px;
padding: 4px 4px;
}
.prompt-dialog-password-entry .capslock-warning,
.login-dialog-prompt-entry .capslock-warning {
icon-size: 16px;
warning-color: #999;
padding: 0 4px;
}
.login-dialog-prompt-entry:insensitive {
color: rgba(0,0,0,0.7);
border: 2px solid #565656;
}
/* Panel */
#panel {
@ -436,6 +503,10 @@ StScrollBar StButton#vhandle:hover
-boxpointer-gap: 4px
}
#networkMenu {
spacing: 4px;
}
/* User Menu */
#panelUserMenu {
@ -456,6 +527,7 @@ StScrollBar StButton#vhandle:hover
border-radius: 5px;
width: 48pt;
height: 48pt;
background-size: contain;
}
.status-chooser-user-icon:hover {
@ -588,38 +660,8 @@ StScrollBar StButton#vhandle:hover
}
#searchEntry {
padding: 4px 12px;
border-radius: 17px;
font-size: 12pt;
color: rgb(128, 128, 128);
border: 2px solid rgba(245,245,245,0.2);
background-gradient-start: rgba(5,5,6,0.1);
background-gradient-end: rgba(254,254,254,0.1);
background-gradient-direction: vertical;
selected-color: black;
caret-color: rgb(128, 128, 128);
caret-size: 1px;
width: 250px;
transition-duration: 300;
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.6);
}
#searchEntry:focus,
#searchEntry:hover {
border: 2px solid rgb(136,138,133);
background-gradient-start: rgb(200,200,200);
background-gradient-end: white;
background-gradient-direction: vertical;
}
#searchEntry:hover {
transition-duration: 300;
}
#searchEntry:focus {
color: rgb(64, 64, 64);
font-weight: bold;
transition-duration: 0;
}
.search-entry-icon {
@ -799,58 +841,8 @@ StScrollBar StButton#vhandle:hover
transition-duration: 100;
}
/* Contacts */
.contact-grid {
spacing: 36px;
-shell-grid-horizontal-item-size: 272px; /* 2 * -shell-grid-horizontal-item-size + spacing */
-shell-grid-vertical-item-size: 118px;
}
.contact {
width: 272px; /* Same width as two normal results + spacing */
height: 118px; /* Aspect ratio = 1.75. Normal US business card ratio */
border-radius: 4px;
padding: 3px;
border: 1px rgba(0,0,0,0);
transition-duration: 100;
}
.contact-content {
border-radius: 7px;
padding: 8px;
width: 232px;
height: 84px;
background-color: rgba(0.0, 0.0, 0.0, 0.5);
color: white;
}
.contact-icon {
border-radius: 4px;
}
.contact-details {
padding: 0px 6px 22px 10px;
}
.contact-details-alias {
font-size: 18px;
padding-bottom: 8px;
}
.contact-details-status-icon {
padding-right: 4px;
}
.contact:hover {
background-color: rgba(255,255,255,0.1);
transition-duration: 100;
}
.contact:focus,
.app-well-app:focus > .overview-icon,
.search-result-content:focus > .overview-icon,
.contact:selected,
.app-well-app:selected > .overview-icon,
.search-result-content:selected > .overview-icon {
background-color: rgba(255,255,255,0.33);
@ -858,8 +850,7 @@ StScrollBar StButton#vhandle:hover
/* LookingGlass */
#LookingGlassDialog
{
#LookingGlassDialog {
background-color: rgba(0,0,0,0.80);
spacing: 4px;
padding: 4px;
@ -867,8 +858,7 @@ StScrollBar StButton#vhandle:hover
border-radius: 4px;
}
#LookingGlassDialog > #Toolbar
{
#LookingGlassDialog > #Toolbar {
border: 1px solid grey;
border-radius: 4px;
}
@ -904,53 +894,44 @@ StScrollBar StButton#vhandle:hover
padding-bottom: 8px;
}
.lg-dialog StEntry
{
.lg-dialog StEntry {
selection-background-color: #bbbbbb;
selected-color: #333333;
}
.lg-completions-text
{
.lg-completions-text {
font-size: .9em;
font-style: italic;
}
.lg-obj-inspector-title
{
.lg-obj-inspector-title {
spacing: 4px;
}
.lg-obj-inspector-button
{
.lg-obj-inspector-button {
border: 1px solid gray;
padding: 4px;
border-radius: 4px;
}
.lg-obj-inspector-button:hover
{
.lg-obj-inspector-button:hover {
border: 1px solid #ffffff;
}
.lg-dialog .shell-link
{
.lg-dialog .shell-link {
color: #999999;
}
.lg-dialog .shell-link:hover
{
.lg-dialog .shell-link:hover {
color: #dddddd;
}
#LookingGlassDialog StBoxLayout#EvalBox
{
#LookingGlassDialog StBoxLayout#EvalBox {
padding: 4px;
spacing: 4px;
}
#LookingGlassDialog StBoxLayout#ResultsArea
{
#LookingGlassDialog StBoxLayout#ResultsArea {
spacing: 4px;
}
@ -988,7 +969,7 @@ StScrollBar StButton#vhandle:hover
#calendarEventsArea {
/* this is the width of the second column of the popup */
min-width: 400px;
min-width: 320px;
}
.calendar-vertical-separator {
@ -997,29 +978,22 @@ StScrollBar StButton#vhandle:hover
width: 0.3em;
}
#calendarPopup {
border-radius: 5px;
background: rgba(0,0,0,0.9);
border: 1px solid rgba(128,128,128,0.45);
color: white;
}
#calendarPopup .calendar {
padding: 10px;
}
.calendar {
padding: .4em 1.75em;
padding: .4em 1.75em .8em 1.75em;
spacing-rows: 0px;
spacing-columns: 0px;
}
.calendar-month-label {
color: #666666;
font-size: 7.5pt;
color: #888a85;
font-size: 9.5pt;
font-weight: bold;
padding-bottom: 8px;
padding-top: 8px;
font-weight: bold;
}
.calendar-change-month-back {
@ -1059,13 +1033,14 @@ StScrollBar StButton#vhandle:hover
}
.datemenu-date-label {
padding: .4em 1.75em;
color: #cccccc;
padding: .4em 1.7em;
font-weight: bold;
text-align: center;
color: #eeeeec;
}
.calendar-day-base {
font-size: 7.5pt;
font-size: 9pt;
text-align: center;
width: 2.4em;
height: 2.4em;
@ -1073,33 +1048,37 @@ StScrollBar StButton#vhandle:hover
.calendar-day-base:hover {
background-color: #777777;
color: #fff;
}
.calendar-day-base:active {
font-size: 9pt;
background-color: #555555;
color: white;
}
.calendar-day-heading {
color: #666666;
padding-top: 1em;
color: #888a85;
padding-top: .2em;
height: 1.7em;
}
.calendar-week-number {
color: #666666;
color: #babdb6;
font-weight: bold;
}
/* Hack used in lieu of border-collapse - see calendar.js */
.calendar-day {
border: 1px solid #333333;
color: #888888;
border: 1px solid #505050;
color: #babdb6;
border-top-width: 0;
border-left-width: 0;
}
.calendar-day-top {
border-top-width: 1px;
}
.calendar-day-left {
border-left-width: 1px;
}
@ -1114,7 +1093,6 @@ StScrollBar StButton#vhandle:hover
.calendar-today {
background-image: url("calendar-today.svg");
text-shadow: black 0px 2px 2px;
color: #ffffff;
font-weight: bold;
}
@ -1124,38 +1102,33 @@ StScrollBar StButton#vhandle:hover
.calendar-day-with-events {
font-weight: bold;
color: #cccccc;
color: white;
}
.events-header-vbox {
spacing: 6pt;
padding-right: 1.75em;
padding-right: .5em;
}
.events-header-vbox:rtl {
padding-right: 0em;
padding-left: 1.75em;
padding-left: .5em;
}
.events-header-hbox {
padding: 0.3em;
padding: 0.3em 1.4em;
}
.events-day-header {
font-size: 9pt;
font-weight: bold;
color: rgba(153, 153, 153, 1.0);
padding-left: 1.8em;
padding-top: 0.8em;
color: #999999;
padding: 0.4em 1.4em 0em 1.4em;
}
.events-day-header:rtl {
padding-left: 0em;
padding-right: 0.3em;
padding: 0em 1.4em 0.4em 1.4em;
}
.events-day-dayname {
font-size: 9pt;
color: rgba(153, 153, 153, 1.0);
text-align: left;
}
@ -1165,7 +1138,6 @@ StScrollBar StButton#vhandle:hover
}
.events-day-time {
font-size: 9pt;
color: #fff;
text-align: right;
}
@ -1175,7 +1147,6 @@ StScrollBar StButton#vhandle:hover
}
.events-day-task {
font-size: 9pt;
color: rgba(153, 153, 153, 1.0);
}
@ -1184,13 +1155,13 @@ StScrollBar StButton#vhandle:hover
}
.events-time-box {
min-width: 53pt;
padding-right: 6pt;
min-width: 48pt;
padding-right: 12pt;
}
.events-time-box:rtl {
padding-right: 0px;
padding-left: 6pt;
padding-left: 12pt;
}
.events-event-box {
@ -1232,11 +1203,9 @@ StScrollBar StButton#vhandle:hover
.summary-boxpointer {
-arrow-border-radius: 8px;
-arrow-background-color: rgba(0,0,0,0.9);
-arrow-border-width: 2px;
-arrow-border-color: #a5a5a5;
-arrow-base: 24px;
-arrow-rise: 11px;
-arrow-background-color: rgba(0,0,0,0.8);
-arrow-base: 36px;
-arrow-rise: 18px;
color: white;
}
@ -1251,17 +1220,17 @@ StScrollBar StButton#vhandle:hover
padding-bottom: 12px;
}
#summary-notification-stack-scrollview {
.summary-notification-stack-scrollview {
max-height: 18em;
padding-top: 6px;
padding-bottom: 6px;
}
#summary-notification-stack-scrollview:ltr {
.summary-notification-stack-scrollview:ltr {
padding-right: 8px;
}
#summary-notification-stack-scrollview:rtl {
.summary-notification-stack-scrollview:rtl {
padding-left: 8px;
}
@ -1309,6 +1278,10 @@ StScrollBar StButton#vhandle:hover
padding: 8px;
}
.secondary-icon {
icon-size: 1.09em;
}
.hotplug-transient-box {
spacing: 6px;
padding: 2px 72px 2px 12px;
@ -1410,30 +1383,7 @@ StScrollBar StButton#vhandle:hover
}
.notification StEntry {
padding: 4px;
border-radius: 4px;
color: #a8a8a8;
selected-color: black;
border: 1px solid rgba(245,245,245,0.2);
background-gradient-direction: vertical;
background-gradient-start: rgb(200,200,200);
background-gradient-end: white;
transition-duration: 300;
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.6);
caret-color: #a8a8a8;
caret-size: 1px;
}
.notification StEntry:focus {
border: 1px solid #8b8b8b;
color: #333333;
background-gradient-direction: vertical;
background-gradient-start: rgb(200,200,200);
background-gradient-end: white;
caret-color: #545454;
selection-background-color: #808080;
}
/* The spacing and padding on the summary is tricky; we want to keep
@ -1683,7 +1633,7 @@ StScrollBar StButton#vhandle:hover
padding: 4px 32px 5px;
}
.modal-dialog-button:disabled {
.modal-dialog-button:insensitive {
color: rgb(60, 60, 60);
}
@ -1715,7 +1665,7 @@ StScrollBar StButton#vhandle:hover
}
.lightbox {
background-color: rgba(0, 0, 0, 0.4);
background-color: black;
}
.flashspot {
@ -1909,32 +1859,8 @@ StScrollBar StButton#vhandle:hover
color: #666666;
}
.prompt-dialog-password-label:ltr {
padding-right: 0.5em;
}
.prompt-dialog-password-label:rtl {
padding-left: 0.5em;
}
.prompt-dialog-password-entry {
background-gradient-start: rgb(236,236,236);
background-gradient-end: white;
background-gradient-direction: vertical;
color: black;
selected-color: white;
border-radius: 5px;
border: 2px solid #555753;
}
.prompt-dialog-password-entry:focus {
border: 2px solid #3465a4;
}
.prompt-dialog-password-entry .capslock-warning {
icon-size: 16px;
warning-color: #999;
padding: 0 4px;
.prompt-dialog-password-box {
spacing: 1em;
}
.prompt-dialog-error-label {
@ -2047,3 +1973,296 @@ StScrollBar StButton#vhandle:hover
-arrow-rise: 10px;
-boxpointer-gap: 5px;
}
/* IBus Candidate Popup */
.candidate-index {
padding: 0.5em 0.5em 0.5em 0.5em;
}
.candidate-label {
padding: 0.5em 0.5em 0.5em 0.5em;
}
.candidate-label:selected {
border-radius: 4px;
background-color: rgba(255,255,255,0.33);
}
/* Login Dialog */
.login-dialog-banner {
font-size: 10pt;
font-weight: bold;
text-align: center;
color: #666666;
padding-bottom: 1em;
}
.login-dialog-title {
font-size: 14pt;
font-weight: bold;
color: #666666;
padding-bottom: 2em;
}
.login-dialog {
/* Reset border and background */
border: none;
background-color: transparent;
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;
padding: .2em;
}
.login-dialog-user-list-item {
color: #666666;
border-radius: 10px;
padding: .2em;
}
.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;
}
.login-dialog-user-list-item:hover .login-dialog-user-list-item-name,
.login-dialog-user-list:expanded .login-dialog-user-list-item:focus .login-dialog-user-list-item-name,
.login-dialog-user-list:expanded .login-dialog-user-list-item:logged-in {
color: white;
text-shadow: black 0px 2px 2px;
}
.login-dialog-user-list-item:hover {
background-color: rgba(255,255,255,0.1);
}
.login-dialog-user-list:expanded .login-dialog-user-list-item:focus {
background-color: rgba(255,255,255,0.33);
}
.login-dialog-user-list:expanded .login-dialog-user-list-item:logged-in {
background-image: url("logged-in-indicator.svg");
background-size: contain;
}
.login-dialog-user-list-item-text-box {
padding: 0 0.5em;
}
.login-dialog-user-list-item .login-dialog-timed-login-indicator {
background-color: rgba(0,0,0,0.0);
height: 2px;
}
.login-dialog-user-list-item:focus .login-dialog-timed-login-indicator {
background-color: #8b8b8b;
}
.login-dialog-user-list-item-icon {
border: 2px solid #8b8b8b;
border-radius: 8px;
width: 64px;
height: 64px;
}
.login-dialog-not-listed-label {
font-size: 10.5pt;
font-weight: bold;
color: #666666;
padding-top: 1em;
}
.login-dialog-not-listed-button:hover .login-dialog-not-listed-label {
color: #E8E8E8;
}
.login-dialog-username {
font-size: 16pt;
font-weight: bold;
text-align: left;
padding-left: 15px;
text-shadow: black 4px 4px 3px 0px;
}
.login-dialog-prompt-layout {
padding-top: 24px;
padding-bottom: 12px;
spacing: 8px;
}
.login-dialog-prompt-label {
color: #eeeeee;
font-size: 14px;
}
.login-dialog-prompt-entry {
width: 15em;
}
.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;
}
.login-dialog .modal-dialog-button {
border: 1px solid white;
border-radius: 5px;
padding: 3px 18px;
}
.login-dialog .modal-dialog-button:default {
background-gradient-start: #6793c4;
background-gradient-end: #335d8f;
background-gradient-direction: vertical;
border: 2px solid #16335d;
}
.login-dialog .modal-dialog-button:hover {
background-gradient-start: #74a0d0;
background-gradient-end: #436d9f;
}
.login-dialog .modal-dialog-button:active,
.login-dialog .modal-dialog-button:pressed {
background-gradient-start: #436d9f;
background-gradient-end: #74a0d0;
}
.unlock-dialog-user-name-container {
spacing: .4em;
}
/* Screen shield */
#lockDialogGroup {
background: #2e3436 url(noise-texture.png);
background-repeat: repeat;
}
#lockScreenGroup .arrow {
color: #333333;
width: 100px;
height: 50px;
}
.screen-shield-clock {
color: white;
text-shadow: 0px 1px 2px rgba(0,0,0,0.6);
font-weight: bold;
text-align: center;
padding-bottom: 1.5em;
}
.screen-shield-clock-time {
font-size: 86px;
text-shadow: 0px 2px 2px rgba(0,0,0,0.4);
}
.screen-shield-clock-date {
font-size: 48px;
}
#screenShieldNotifications {
border-radius: 8px;
background-color: rgba(0.0, 0.0, 0.0, 0.9);
border: 2px solid #868686;
max-height: 500px;
padding: 12px 0;
}
.screen-shield-notifications-box {
spacing: 12px;
}
.screen-shield-notification-source {
padding: 0 24px;
spacing: 5px;
}
.screen-shield-notification-label {
font-size: 1.2em;
font-weight: bold;
}
/* Remove background from notifications, otherwise
opacity is doubled and they look darker
*/
.screen-shield-notifications-box .notification {
background-color: transparent;
}
/* Override padding on resident notifications, since
the notifications box has its own spacing
*/
.screen-shield-notifications-box .summary-notification-stack-scrollview {
padding-top: 0px;
padding-bottom: 0px;
}

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: 78 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

@ -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

@ -6,9 +6,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" \
@ -24,6 +22,7 @@ nobase_dist_js_DATA = \
gdm/loginDialog.js \
gdm/powerMenu.js \
gdm/systemd.js \
gdm/util.js \
extensionPrefs/main.js \
misc/config.js \
misc/extensionUtils.js \
@ -33,7 +32,6 @@ nobase_dist_js_DATA = \
misc/jsParse.js \
misc/modemManager.js \
misc/params.js \
misc/screenSaver.js \
misc/util.js \
perf/core.js \
ui/altTab.js \
@ -44,7 +42,6 @@ nobase_dist_js_DATA = \
ui/boxpointer.js \
ui/calendar.js \
ui/checkBox.js \
ui/contactDisplay.js \
ui/ctrlAltTab.js \
ui/dash.js \
ui/dateMenu.js \
@ -52,13 +49,14 @@ nobase_dist_js_DATA = \
ui/endSessionDialog.js \
ui/environment.js \
ui/extensionSystem.js \
ui/extensionDownloader.js \
ui/flashspot.js \
ui/ibusCandidatePopup.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 \
@ -78,11 +76,11 @@ nobase_dist_js_DATA = \
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/network.js \
@ -91,6 +89,7 @@ nobase_dist_js_DATA = \
ui/status/bluetooth.js \
ui/telepathyClient.js \
ui/tweener.js \
ui/unlockDialog.js \
ui/userMenu.js \
ui/viewSelector.js \
ui/wanda.js \

View File

@ -13,8 +13,7 @@ const _ = Gettext.gettext;
const Config = imports.misc.config;
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) {
logError(e, 'Could not create extensions object');
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();
},
@ -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

@ -30,83 +30,23 @@ 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 _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,43 +88,37 @@ 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);
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);
textLayout.add(this._nameLabel,
{ y_fill: false,
y_align: St.Align.MIDDLE,
expand: true });
this._timedLoginIndicator = new St.Bin({ style_class: 'login-dialog-timed-login-indicator',
scale_x: 0 });
textLayout.add(this._timedLoginIndicator,
{ x_fill: true,
x_align: St.Align.MIDDLE,
y_fill: false,
y_align: St.Align.END });
this._updateIcon();
this._updateLoggedIn();
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
},
@ -192,6 +126,7 @@ const UserListItem = new Lang.Class({
_onUserChanged: function() {
this._nameLabel.set_text(this.user.get_real_name());
this._updateIcon();
this._updateLoggedIn();
},
_setIconFromFile: function(iconFile, styleClass) {
@ -239,30 +174,40 @@ const UserListItem = new Lang.Class({
this._setIconFromName('avatar-default', 'login-dialog-user-list-item-icon');
},
syncStyleClasses: function() {
this._updateLoggedIn();
if (global.stage.get_key_focus() == this.actor)
this.actor.add_style_pseudo_class('focus');
else
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() {
this.emit('activate');
},
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 +216,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 +234,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 +257,7 @@ const UserList = new Lang.Class({
_showItem: function(item) {
let tasks = [function() {
return _fadeInActor(item.actor);
return GdmUtil.fadeInActor(item.actor);
},
function() {
@ -367,17 +314,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 +372,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 +399,7 @@ const UserList = new Lang.Class({
},
function() {
return _fadeInActor(this.actor.vscroll);
return GdmUtil.fadeInActor(this.actor.vscroll);
}]);
return batch.run();
},
@ -459,7 +414,7 @@ const UserList = new Lang.Class({
Tweener.addTween (adjustment,
{ value: value,
time: _SCROLL_ANIMATION_TIME,
transition: 'linear' });
transition: 'easeOutQuad' });
},
jumpToItem: function(item) {
@ -511,7 +466,6 @@ const UserList = new Lang.Class({
Lang.bind(this,
function() {
this.scrollToItem(item);
item.showFocusAnimation(0);
}));
this._moveFocusToItems();
@ -553,10 +507,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' });
@ -567,10 +518,7 @@ const SessionListItem = new Lang.Class({
let label = new St.Label({ style_class: 'login-dialog-session-list-item-label',
text: name });
this._box.add_actor(label,
{ expand: true,
x_fill: true,
y_fill: true });
this._box.add_actor(label);
},
setShowDot: function(show) {
@ -614,10 +562,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 +570,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 +634,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 +646,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 +668,55 @@ 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
});
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('verification-failed', Lang.bind(this, this._onVerificationFailed));
this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
this._fprintManager = new Fprint.FprintManager();
this._startFingerprintConversationIfNeeded();
this._settings.connect('changed::' + _LOGO_KEY,
this._userVerifier.connect('show-fingerprint-prompt', Lang.bind(this, this._showFingerprintPrompt));
this._userVerifier.connect('hide-fingerprint-prompt', Lang.bind(this, this._hideFingerprintPrompt));
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") });
@ -839,7 +771,7 @@ const LoginDialog = new Lang.Class({
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 +819,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,10 +833,19 @@ const LoginDialog = new Lang.Class({
},
_onReset: function(client, serviceName) {
this._greeterClient.call_start_conversation(_PASSWORD_SERVICE_NAME);
this._startFingerprintConversationIfNeeded();
_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) {
let tasks = [this._hidePrompt,
new Batch.ConcurrentBatch(this, [this._fadeInTitleLabel,
@ -950,43 +875,25 @@ 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);
_showFingerprintPrompt: function() {
GdmUtil.fadeInActor(this._promptFingerprintMessage);
},
_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);
_hideFingerprintPrompt: function() {
GdmUtil.fadeOutActor(this._promptFingerprintMessage);
},
_onCancel: function(client) {
this._greeterClient.call_cancel();
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() {
@ -997,14 +904,14 @@ const LoginDialog = new Lang.Class({
},
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 +926,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 +968,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._promptEntry.reactive = true;
this._promptEntry.remove_style_pseudo_class('insensitive');
this._promptEntry.set_text('');
}];
@ -1075,43 +982,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 +1028,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 +1069,6 @@ const LoginDialog = new Lang.Class({
// item.
if (!this.is_loaded) {
this._userList.jumpToItem(this._timedLoginItem);
this._timedLoginItem.showFocusAnimation(0);
}
},
@ -1193,7 +1082,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 +1096,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,19 +1128,8 @@ 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);
}
_onVerificationFailed: function() {
this._userVerifier.cancel();
},
_onNotListedClicked: function(user) {
@ -1269,7 +1150,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 +1161,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 +1228,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

@ -70,7 +70,7 @@ const PowerMenuButton = new Lang.Class({
this._systemdLoginManager.CanPowerOffRemote(Lang.bind(this,
function(result, error) {
if (!error)
this._haveShutdown = result != 'no';
this._haveShutdown = result[0] != 'no';
else
this._haveShutdown = false;
@ -81,7 +81,7 @@ const PowerMenuButton = new Lang.Class({
this._consoleKitManager.CanStopRemote(Lang.bind(this,
function(result, error) {
if (!error)
this._haveShutdown = result;
this._haveShutdown = result[0];
else
this._haveShutdown = false;
@ -97,7 +97,7 @@ const PowerMenuButton = new Lang.Class({
this._systemdLoginManager.CanRebootRemote(Lang.bind(this,
function(result, error) {
if (!error)
this._haveRestart = result != 'no';
this._haveRestart = result[0] != 'no';
else
this._haveRestart = false;
@ -108,7 +108,7 @@ const PowerMenuButton = new Lang.Class({
this._consoleKitManager.CanRestartRemote(Lang.bind(this,
function(result, error) {
if (!error)
this._haveRestart = result;
this._haveRestart = result[0];
else
this._haveRestart = false;

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

@ -0,0 +1,268 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Signals = imports.signals;
const Batch = imports.gdm.batch;
const Fprint = imports.gdm.fingerprint;
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 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._cancellable = new Gio.Cancellable();
this._fprintManager = new Fprint.FprintManager();
this._checkForFingerprintReader();
},
begin: function(userName, hold) {
this._hold = hold;
this._userName = userName;
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() {
this._cancellable.cancel();
if (this._userVerifier)
this._userVerifier.call_cancel_sync(null);
},
clear: function() {
this._cancellable.cancel();
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;
}));
},
_reauthenticationChannelOpened: function(client, result) {
try {
this._userVerifier = client.open_reauthentication_channel_finish(result);
this._connectSignals();
this._beginVerification();
this._hold.release();
} catch (e) {
if (this._reauthOnly) {
logError(e, 'Failed to open reauthentication channel');
this._hold.release();
this.emit('verification-failed');
return;
}
// If there's no session running, or it otherwise fails, then fall back
// to performing verification from this login session
client.get_user_verifier(this._cancellable, Lang.bind(this, this._userVerifierGot));
}
},
_userVerifierGot: function(client, result) {
this._userVerifier = client.get_user_verifier_finish(result);
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) {
obj.call_begin_verification_for_user_finish(result);
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) {
obj.call_begin_verification_for_user_finish(result);
this._hold.release();
}));
}
} else {
this._userVerifier.call_begin_verification(PASSWORD_SERVICE_NAME,
this._cancellable,
Lang.bind(this, function(obj, result) {
obj.call_begin_verification_finish(result);
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) {
this.emit('show-fingerprint-prompt');
} else if (serviceName == PASSWORD_SERVICE_NAME) {
Main.notifyError(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;
Main.notifyError(problem);
},
_onInfoQuery: function(client, serviceName, question) {
// We only expect questions to come from the main auth service
if (serviceName != PASSWORD_SERVICE_NAME)
return;
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._userVerifier.run_dispose();
this._userVerifier = null;
this._checkForFingerprintReader();
this.emit('reset');
},
_onVerificationComplete: function() {
this.emit('verification-complete');
},
_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.emit('verification-failed');
} else if (serviceName == FINGERPRINT_SERVICE_NAME) {
this.emit('hide-fingerprint-prompt');
}
},
});
Signals.addSignalMethods(ShellUserVerifier.prototype);

View File

@ -4,8 +4,6 @@
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@;
/* gettext package */

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 = {};
@ -88,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;
}
@ -155,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) {
logError(e, 'Could not create extensions directory');
}
}
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) {
logError(e, 'Could not enumerate extensions directory');
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

@ -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

@ -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;
@ -87,20 +84,24 @@ function trySpawn(argv)
try {
[success, pid] = GLib.spawn_async(null, argv, null,
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
null, null);
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
@ -149,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');

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) {

View File

@ -9,7 +9,9 @@ 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;
const GnomeSession = imports.misc.gnomeSession;
const GNOME_SESSION_AUTOMOUNT_INHIBIT = 16;
// GSettings keys
const SETTINGS_SCHEMA = 'org.gnome.desktop.media-handling';
@ -79,13 +81,17 @@ const AutomountManager = new Lang.Class({
_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;
if (!haveSystemd())
this.ckListener = new ConsoleKitManager();
this._ssProxy = new ScreenSaver.ScreenSaverProxy();
this._ssProxy.connectSignal('ActiveChanged',
Lang.bind(this, this._screenSaverActiveChanged));
Main.screenShield.connect('lock-status-changed', Lang.bind(this, this._lockStatusChanged));
this._volumeMonitor = Gio.VolumeMonitor.get();
@ -108,8 +114,18 @@ const AutomountManager = new Lang.Class({
Mainloop.idle_add(Lang.bind(this, this._startupMountAll));
},
_screenSaverActiveChanged: function(object, senderName, [isActive]) {
if (!isActive) {
_InhibitorsChanged: function(object, senderName, [inhibtor]) {
this._session.IsInhibitedRemote(GNOME_SESSION_AUTOMOUNT_INHIBIT,
Lang.bind(this,
function(result, error) {
if (!error) {
this._inhibited = result[0];
}
}));
},
_lockStatusChanged: function(shield, locked) {
if (!locked) {
this._volumeQueue.forEach(Lang.bind(this, function(volume) {
this._checkAndMountVolume(volume);
}));
@ -123,7 +139,8 @@ const AutomountManager = new Lang.Class({
let volumes = this._volumeMonitor.get_volumes();
volumes.forEach(Lang.bind(this, function(volume) {
this._checkAndMountVolume(volume, { checkSession: false,
useMountOp: false });
useMountOp: false,
allowAutorun: false });
}));
return false;
@ -146,7 +163,7 @@ const AutomountManager = new Lang.Class({
if (!this.isSessionActive())
return;
if (this._ssProxy.screenSaverActive)
if (Main.screenShield.locked)
return;
global.play_theme_sound(0, 'device-added-media');
@ -158,7 +175,7 @@ const AutomountManager = new Lang.Class({
if (!this.isSessionActive())
return;
if (this._ssProxy.screenSaverActive)
if (Main.screenShield.locked)
return;
global.play_theme_sound(0, 'device-removed-media');
@ -201,7 +218,8 @@ const AutomountManager = new Lang.Class({
_checkAndMountVolume: function(volume, params) {
params = Params.parse(params, { checkSession: true,
useMountOp: true });
useMountOp: true,
allowAutorun: true });
if (params.checkSession) {
// if we're not in the current ConsoleKit session,
@ -209,7 +227,7 @@ const AutomountManager = new Lang.Class({
if (!this.isSessionActive())
return;
if (this._ssProxy.screenSaverActive) {
if (Main.screenShield.locked) {
if (this._volumeQueue.indexOf(volume) == -1)
this._volumeQueue.push(volume);
@ -217,6 +235,9 @@ const AutomountManager = new Lang.Class({
}
}
if (this._inhibited)
return;
// Volume is already mounted, don't bother.
if (volume.get_mount())
return;
@ -236,15 +257,20 @@ const AutomountManager = new Lang.Class({
if (params.useMountOp) {
let operation = new ShellMountOperation.ShellMountOperation(volume);
this._mountVolume(volume, operation.mountOp);
this._mountVolume(volume, operation, params.allowAutorun);
} else {
this._mountVolume(volume, null);
this._mountVolume(volume, null, params.allowAutorun);
}
},
_mountVolume: function(volume, operation) {
this._allowAutorun(volume);
volume.mount(0, operation, null,
_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));
},
@ -253,15 +279,19 @@ const AutomountManager = new Lang.Class({
try {
volume.mount_finish(res);
this._closeOperation(volume);
} 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)
// 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
log('Unable to mount volume ' + volume.get_name() + ': ' + string);
} else {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
log('Unable to mount volume ' + volume.get_name() + ': ' + e.toString());
this._closeOperation(volume);
}
}
},
@ -273,8 +303,16 @@ const AutomountManager = new Lang.Class({
},
_reaskPassword: function(volume) {
let operation = new ShellMountOperation.ShellMountOperation(volume, { reaskPassword: true });
this._mountVolume(volume, operation.mountOp);
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) {

View File

@ -23,12 +23,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;
@ -224,11 +226,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 +236,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 +246,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());
}
},
});
@ -270,7 +266,7 @@ const AutorunResidentSource = new Lang.Class({
},
addMount: function(mount, apps) {
if (ignoreAutorunForMount(mount))
if (!shouldAutorunMount(mount, false))
return;
let filtered = this._mounts.filter(function (element) {
@ -448,7 +444,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,16 +495,15 @@ const AutorunTransientSource = new Lang.Class({
this.parent(mount.get_name());
this._notification = new AutorunTransientNotification(this);
this._setSummaryIcon(this.createNotificationIcon());
// 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 });
}
});

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
@ -321,12 +357,18 @@ const BoxPointer = new Lang.Class({
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 +385,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) {
@ -446,6 +484,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

@ -448,7 +448,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 +551,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 +572,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))

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 ||
C_("contact", "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, callback) {
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);
}
});
}
callback(metas);
},
getInitialResultSet: function(terms) {
this.searchSystem.pushResults(this, this._contactSys.initial_search(terms));
},
getSubsearchResultSet: function(previousResults, terms) {
this.searchSystem.pushResults(this, 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

@ -133,7 +133,6 @@ const DashItemContainer = new Lang.Class({
},
hideLabel: function () {
this.label.opacity = 255;
Tweener.addTween(this.label,
{ opacity: 0,
time: DASH_ITEM_LABEL_HIDE_TIME,
@ -459,6 +458,13 @@ const Dash = new Lang.Class({
Lang.bind(this, function() {
this._onHover(item, display)
}));
Main.overview.connect('hiding',
Lang.bind(this, function() {
this._labelShowing = false;
item.hideLabel();
}));
return item;
},
@ -474,7 +480,7 @@ const Dash = new Lang.Class({
}));
if (this._resetHoverTimeoutId > 0) {
Mainloop.source_remove(this._resetHoverTimeoutId);
this._resetHoverTimeoutId = 0;
this._resetHoverTimeoutId = 0;
}
}
} else {

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)
{
@ -60,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);
@ -73,7 +66,7 @@ const DateMenuButton = new Lang.Class({
// Date
this._date = new St.Label();
this.actor.label_actor = this._clock;
this.actor.label_actor = this._clockDisplay;
this._date.style_class = 'datemenu-date-label';
vbox.add(this._date);
@ -155,77 +148,29 @@ 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();
},
_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,7 +31,6 @@ 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;
@ -280,21 +279,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();
}));

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(uuid))
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().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(uuid))
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(uuid);
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,11 @@
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 ExtensionState = {
ENABLED: 1,
@ -30,16 +22,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({ 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());
// Arrays of uuids
var enabledExtensions;
// Contains the order that extensions were enabled in.
@ -56,80 +38,6 @@ const disconnect = Lang.bind(_signals, _signals.disconnect);
const ENABLED_EXTENSIONS_KEY = 'enabled-extensions';
function installExtensionFromUUID(uuid) {
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) {
let info = JSON.parse(message.response_body.data);
let dialog = new InstallExtensionDialog(uuid, info);
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;
}
let [file, stream] = Gio.File.new_tmp('XXXXXX.shell-extension.zip');
let dir = ExtensionUtils.userExtensionsDir.get_child(uuid);
let contents = message.response_body.flatten().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) {
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);
});
}
function disableExtension(uuid) {
let extension = ExtensionUtils.extensions[uuid];
if (!extension)
@ -155,23 +63,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);
}
}
@ -194,62 +102,55 @@ function enableExtension(uuid) {
extensionOrder.push(uuid);
try {
extension.stateObj.enable();
} catch(e) {
logExtensionError(uuid, e.toString());
return;
extension.stateObj.enable();
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.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);
log('Extension "%s" had error: %s'.format(uuid, message));
state = state || ExtensionState.ERROR;
_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) {
log('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;
}
@ -257,6 +158,23 @@ function loadExtension(dir, type, enabled) {
_signals.emit('extension-state-changed', extension);
}
function unloadExtension(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);
extension.state = ExtensionState.UNINSTALLED;
_signals.emit('extension-state-changed', extension);
delete ExtensionUtils.extensions[uuid];
return true;
}
function initExtension(uuid) {
let extension = ExtensionUtils.extensions[uuid];
let dir = extension.dir;
@ -265,64 +183,24 @@ 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);
}
@ -334,7 +212,11 @@ function onEnabledExtensionsChanged() {
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
@ -342,88 +224,27 @@ 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,
_init: function(uuid, info) {
this.parent({ styleClass: 'extension-dialog' });
this._uuid = uuid;
this._info = info;
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(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());
// 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 = { shell_version: Config.PACKAGE_VERSION };
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());
}
});

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

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

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);

View File

@ -11,7 +11,6 @@ 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;
@ -33,26 +32,37 @@ const LayoutManager = new Lang.Class({
this._chrome = new Chrome(this);
this.screenShieldGroup = new St.Widget({ name: 'screenShieldGroup',
visible: false,
clip_to_allocation: true,
});
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.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 +155,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,26 +237,9 @@ 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 Main.layoutManager.monitors[index];
},
_startupAnimation: function() {
@ -331,8 +327,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 +344,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 +551,7 @@ const HotCorner = new Lang.Class({
// workspace content.
const defaultParams = {
visibleInFullscreen: false,
trackFullscreen: false,
affectsStruts: false,
affectsInputRegion: true
};
@ -568,6 +564,7 @@ const Chrome = new Lang.Class({
this._monitors = [];
this._inOverview = false;
this._isLocked = false;
this._updateRegionIdle = 0;
this._freezeUpdateCount = 0;
@ -582,16 +579,6 @@ 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();
},
@ -600,6 +587,8 @@ const Chrome = new Lang.Class({
Lang.bind(this, this._overviewShowing));
Main.overview.connect('hidden',
Lang.bind(this, this._overviewHidden));
Main.screenShield.connect('lock-status-changed',
Lang.bind(this, this._lockStatusChanged));
},
addActor: function(actor, params) {
@ -694,19 +683,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 || this._isLocked)
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 +710,12 @@ const Chrome = new Lang.Class({
this._queueUpdateRegions();
},
_lockStatusChanged: function(shield, locked) {
this._isLocked = locked;
this._updateVisibility();
this._queueUpdateRegions();
},
_relayout: function() {
this._monitors = this._layoutManager.monitors;
this._primaryMonitor = this._layoutManager.primaryMonitor;
@ -731,12 +725,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 +840,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 +851,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 +974,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; ' +
@ -38,7 +40,7 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
'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 +263,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 +273,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 +306,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 +318,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 +341,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 +352,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,7 +366,7 @@ Signals.addSignalMethods(WindowList.prototype);
const ObjInspector = new Lang.Class({
Name: 'ObjInspector',
_init : function () {
_init: function(lookingGlass) {
this._obj = null;
this._previousObj = null;
@ -369,6 +378,8 @@ const ObjInspector = new Lang.Class({
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>' });
}
@ -457,7 +468,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 +476,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 +519,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 +533,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 +555,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,12 +630,7 @@ 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);
}
});
@ -662,7 +664,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 });
@ -723,13 +725,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) {
@ -793,24 +795,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;
}
@ -821,8 +832,7 @@ const LookingGlass = new Lang.Class({
_init : function() {
this._borderPaintTarget = null;
this._borderPaintId = 0;
this._borderDestroyId = 0;
this._redBorderEffect = new RedBorderEffect();
this._open = false;
@ -852,7 +862,7 @@ 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();
@ -864,7 +874,7 @@ const LookingGlass = new Lang.Class({
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);
@ -894,18 +904,14 @@ 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._memory = new Memory();
@ -959,23 +965,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();
@ -1156,11 +1161,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

@ -26,6 +26,7 @@ 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';
@ -456,6 +457,10 @@ const Magnifier = new Lang.Class({
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);
@ -490,6 +495,8 @@ const Magnifier = new Lang.Class({
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));
@ -591,6 +598,15 @@ const Magnifier = new Lang.Class({
}
},
_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) {
@ -626,6 +642,7 @@ const ZoomRegion = new Lang.Class({
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 };
@ -973,6 +990,27 @@ const ZoomRegion = new Lang.Class({
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.
@ -1074,6 +1112,7 @@ const ZoomRegion = new Lang.Class({
// 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);
@ -1602,12 +1641,14 @@ const MagShaderEffects = new Lang.Class({
_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);
},
/**
@ -1618,6 +1659,7 @@ const MagShaderEffects = new Lang.Class({
*/
destroyEffects: function() {
this._magView.clear_effects();
this._colorDesaturation = null;
this._brightnessContrast = null;
this._inverse = null;
this._magView = null;
@ -1641,6 +1683,14 @@ const MagShaderEffects = new Lang.Class({
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.

View File

@ -18,6 +18,7 @@ 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;
@ -29,14 +30,16 @@ 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 ShellMountOperation = imports.ui.shellMountOperation;
const TelepathyClient = imports.ui.telepathyClient;
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';
@ -52,6 +55,7 @@ let runDialog = null;
let lookingGlass = null;
let wm = null;
let messageTray = null;
let screenShield = null;
let notificationDaemon = null;
let windowAttentionHandler = null;
let telepathyClient = null;
@ -59,12 +63,13 @@ 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;
@ -89,14 +94,21 @@ function createUserSession() {
}
function createGDMSession() {
screenShield.showDialog();
}
function createGDMLoginDialog(parentActor) {
// 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();
});
let loginDialog = new LoginDialog.LoginDialog(parentActor);
return [loginDialog, true];
}
function createSessionUnlockDialog(parentActor) {
let dialog = new UnlockDialog.UnlockDialog(parentActor);
return [dialog, false];
}
function createInitialSetupSession() {
@ -149,6 +161,7 @@ function start() {
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
@ -199,7 +212,8 @@ function start() {
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
overview = new Overview.Overview();
magnifier = new Magnifier.Magnifier();
statusIconDispatcher = new StatusIconDispatcher.StatusIconDispatcher();
screenShield = new ScreenShield.ScreenShield();
screenSaverDBus = new ShellDBus.ScreenSaverDBus();
panel = new Panel.Panel();
wm = new WindowManager.WindowManager();
messageTray = new MessageTray.MessageTray();
@ -220,7 +234,7 @@ function start() {
false, -1, 1);
if (sessionMode.allowExtensions) {
ExtensionSystem.init();
ExtensionDownloader.init();
ExtensionSystem.loadExtensions();
}
@ -239,8 +253,6 @@ function start() {
Lang.bind(overview, overview.toggle));
}
statusIconDispatcher.start(messageTray.actor);
// Provide the bus object for gnome-session to
// initiate logouts.
EndSessionDialog.init();
@ -520,6 +532,7 @@ function notify(msg, details) {
messageTray.add(source);
let notification = new MessageTray.Notification(source, msg, details);
notification.setTransient(true);
notification.setShowWhenLocked(true);
source.notify(notification);
}
@ -607,13 +620,13 @@ function _globalKeyPressHandler(actor, event) {
if (!sessionMode.hasWorkspaces)
return false;
wm.actionMoveWorkspaceUp();
wm.actionMoveWorkspace(Meta.MotionDirection.UP);
return true;
case Meta.KeyBindingAction.WORKSPACE_DOWN:
if (!sessionMode.hasWorkspaces)
return false;
wm.actionMoveWorkspaceDown();
wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
return true;
case Meta.KeyBindingAction.PANEL_RUN_DIALOG:
case Meta.KeyBindingAction.COMMAND_2:
@ -638,6 +651,10 @@ function _findModal(actor) {
return -1;
}
function isInModalStack(actor) {
return _findModal(actor) != -1;
}
/**
* pushModal:
* @actor: #ClutterActor which will be given keyboard focus
@ -670,6 +687,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);
@ -678,7 +696,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;
@ -750,6 +768,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() {

View File

@ -394,7 +394,7 @@ Signals.addSignalMethods(FocusGrabber.prototype);
// the content area (as with addBody()).
//
// By default, the icon shown is created by calling
// source.createNotificationIcon(). However, if @params contains an 'icon'
// source.createIcon(). However, if @params contains an 'icon'
// parameter, the passed in icon will be used.
//
// If @params contains a 'titleMarkup', 'bannerMarkup', or
@ -420,6 +420,8 @@ const Notification = new Lang.Class({
// 'transient' is a reserved keyword in JS, so we have to use an alternate variable name
this.isTransient = false;
this.expanded = false;
this.showWhenLocked = false;
this.acknowledged = false;
this._destroyed = false;
this._useActionIcons = false;
this._customContent = false;
@ -493,6 +495,7 @@ const Notification = new Lang.Class({
params = Params.parse(params, { customContent: false,
body: null,
icon: null,
secondaryIcon: null,
titleMarkup: false,
bannerMarkup: false,
bodyMarkup: false,
@ -507,6 +510,11 @@ const Notification = new Lang.Class({
this._icon = null;
}
if (this._secondaryIcon && (params.secondaryIcon || params.clear)) {
this._secondaryIcon.destroy();
this._secondaryIcon = null;
}
// We always clear the content area if we don't have custom
// content because it might contain the @banner that didn't
// fit in the banner mode.
@ -533,7 +541,7 @@ const Notification = new Lang.Class({
this._table.remove_style_class_name('multi-line-notification');
if (!this._icon) {
this._icon = params.icon || this.source.createNotificationIcon();
this._icon = params.icon || this.source.createIcon(this.source.ICON_SIZE);
this._table.add(this._icon, { row: 0,
col: 0,
x_expand: false,
@ -542,6 +550,13 @@ const Notification = new Lang.Class({
y_align: St.Align.START });
}
if (!this._secondaryIcon) {
this._secondaryIcon = params.secondaryIcon;
if (this._secondaryIcon)
this._bannerBox.add_actor(this._secondaryIcon);
}
this.title = title;
title = title ? _fixMarkup(title.replace(/\n/g, ' '), params.titleMarkup) : '';
this._titleLabel.clutter_text.set_markup('<b>' + title + '</b>');
@ -756,7 +771,7 @@ const Notification = new Lang.Class({
button.label = label;
}
if (this._buttonBox.get_children().length > 0)
if (this._buttonBox.get_n_children() > 0)
this._buttonFocusManager.remove_group(this._buttonBox);
this._buttonBox.add(button);
@ -785,10 +800,6 @@ const Notification = new Lang.Class({
return;
button.reactive = sensitive;
if (sensitive)
button.remove_style_pseudo_class('insensitive');
else
button.add_style_pseudo_class('insensitive');
},
setUrgency: function(urgency) {
@ -803,6 +814,14 @@ const Notification = new Lang.Class({
this.isTransient = isTransient;
},
setShowWhenLocked: function(show) {
if (show && !this.isTransient) {
throw new Error('ShowWhenLocked can only be set on a transient notification');
}
this.showWhenLocked = show;
},
setUseActionIcons: function(useIcons) {
this._useActionIcons = useIcons;
},
@ -815,8 +834,15 @@ const Notification = new Lang.Class({
let [titleMin, titleNat] = this._titleLabel.get_preferred_width(forHeight);
let [bannerMin, bannerNat] = this._bannerLabel.get_preferred_width(forHeight);
alloc.min_size = titleMin;
alloc.natural_size = titleNat + this._spacing + bannerNat;
if (this._secondaryIcon) {
let [secondaryIconMin, secondaryIconNat] = this._secondaryIcon.get_preferred_width(forHeight);
alloc.min_size = secondaryIconMin + this._spacing + titleMin;
alloc.natural_size = secondaryIconNat + this._spacing + titleNat + this._spacing + bannerNat;
} else {
alloc.min_size = titleMin;
alloc.natural_size = titleNat + this._spacing + bannerNat;
}
},
_bannerBoxGetPreferredHeight: function(actor, forWidth, alloc) {
@ -831,14 +857,42 @@ const Notification = new Lang.Class({
let [titleMinH, titleNatH] = this._titleLabel.get_preferred_height(availWidth);
let [bannerMinW, bannerNatW] = this._bannerLabel.get_preferred_width(availWidth);
let rtl = (this._titleDirection == Clutter.TextDirection.RTL);
let x = rtl ? availWidth : 0;
if (this._secondaryIcon) {
let [iconMinW, iconNatW] = this._secondaryIcon.get_preferred_width(-1);
let [iconMinH, iconNatH] = this._secondaryIcon.get_preferred_height(availWidth);
let secondaryIconBox = new Clutter.ActorBox();
let secondaryIconBoxW = Math.min(iconNatW, availWidth);
// allocate secondary icon box
if (rtl) {
secondaryIconBox.x1 = x - secondaryIconBoxW;
secondaryIconBox.x2 = x;
x = x - (secondaryIconBoxW + this._spacing);
} else {
secondaryIconBox.x1 = x;
secondaryIconBox.x2 = x + secondaryIconBoxW;
x = x + secondaryIconBoxW + this._spacing;
}
secondaryIconBox.y1 = 0;
// Using titleNatH ensures that the secondary icon is centered vertically
secondaryIconBox.y2 = titleNatH;
availWidth = availWidth - (secondaryIconBoxW + this._spacing);
this._secondaryIcon.allocate(secondaryIconBox, flags);
}
let titleBox = new Clutter.ActorBox();
let titleBoxW = Math.min(titleNatW, availWidth);
if (this._titleDirection == Clutter.TextDirection.RTL) {
if (rtl) {
titleBox.x1 = availWidth - titleBoxW;
titleBox.x2 = availWidth;
} else {
titleBox.x1 = 0;
titleBox.x2 = titleBoxW;
titleBox.x1 = x;
titleBox.x2 = titleBox.x1 + titleBoxW;
}
titleBox.y1 = 0;
titleBox.y2 = titleNatH;
@ -852,7 +906,7 @@ const Notification = new Lang.Class({
} else {
let bannerBox = new Clutter.ActorBox();
if (this._titleDirection == Clutter.TextDirection.RTL) {
if (rtl) {
bannerBox.x1 = 0;
bannerBox.x2 = titleBox.x1 - this._spacing;
@ -981,24 +1035,19 @@ const Notification = new Lang.Class({
});
Signals.addSignalMethods(Notification.prototype);
const Source = new Lang.Class({
Name: 'MessageTraySource',
const SourceActor = new Lang.Class({
Name: 'SourceActor',
ICON_SIZE: 24,
_init: function(title, iconName, iconType) {
this.title = title;
this.iconName = iconName;
this.iconType = iconType;
_init: function(source, size) {
this._source = source;
this.actor = new Shell.GenericContainer();
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this.actor.connect('allocate', Lang.bind(this, this._allocate));
this.actor.connect('destroy', Lang.bind(this,
function() {
this._actorDestroyed = true;
}));
this.actor.connect('destroy', Lang.bind(this, function() {
this._actorDestroyed = true;
}));
this._actorDestroyed = false;
this._counterLabel = new St.Label();
@ -1006,21 +1055,20 @@ const Source = new Lang.Class({
child: this._counterLabel });
this._counterBin.hide();
this._iconBin = new St.Bin({ width: this.ICON_SIZE,
height: this.ICON_SIZE,
this._iconBin = new St.Bin({ width: size,
height: size,
x_fill: true,
y_fill: true });
this.actor.add_actor(this._iconBin);
this.actor.add_actor(this._counterBin);
this.isTransient = false;
this.isChat = false;
this.isMuted = false;
this._source.connect('count-updated', Lang.bind(this, this._updateCount));
this._updateCount();
},
this.notifications = [];
this._setSummaryIcon(this.createNotificationIcon());
setIcon: function(icon) {
this._iconBin.child = icon;
},
_getPreferredWidth: function (actor, forHeight, alloc) {
@ -1058,20 +1106,49 @@ const Source = new Lang.Class({
this._counterBin.allocate(childBox, flags);
},
_setCount: function(count, visible) {
if (isNaN(parseInt(count)))
throw new Error("Invalid notification count: " + count);
_updateCount: function() {
if (this._actorDestroyed)
return;
this._counterBin.visible = visible;
this._counterLabel.set_text(count.toString());
this._counterBin.visible = this._source.countVisible;
this._counterLabel.set_text(this._source.count.toString());
},
});
const Source = new Lang.Class({
Name: 'MessageTraySource',
ICON_SIZE: 24,
_init: function(title, iconName, iconType) {
this.title = title;
this.iconName = iconName;
this.iconType = iconType;
this.isTransient = false;
this.isChat = false;
this.isMuted = false;
this.notifications = [];
this.mainIcon = new SourceActor(this, this.ICON_SIZE);
this._setSummaryIcon(this.createIcon(this.ICON_SIZE));
},
_updateCount: function() {
let count = this.notifications.length;
this._setCount(count, count > 1);
get count() {
return this.notifications.length;
},
get unseenCount() {
return this.notifications.filter(function(n) { return !n.acknowledged; }).length;
},
get countVisible() {
return this.count > 1;
},
countUpdated: function() {
this.emit('count-updated');
},
setTransient: function(isTransient) {
@ -1090,19 +1167,19 @@ const Source = new Lang.Class({
this.emit('muted-changed');
},
// Called to create a new icon actor (of size this.ICON_SIZE).
// Called to create a new icon actor.
// Provides a sane default implementation, override if you need
// something more fancy.
createNotificationIcon: function() {
createIcon: function(size) {
return new St.Icon({ icon_name: this.iconName,
icon_type: this.iconType,
icon_size: this.ICON_SIZE });
icon_size: size });
},
// Unlike createNotificationIcon, this always returns the same actor;
// Unlike createIcon, this always returns the same actor;
// there is only one summary icon actor for a Source.
getSummaryIcon: function() {
return this.actor;
return this.mainIcon.actor;
},
pushNotification: function(notification) {
@ -1122,13 +1199,14 @@ const Source = new Lang.Class({
if (this.notifications.length == 0)
this._lastNotificationRemoved();
this._updateCount();
this.countUpdated();
}));
this._updateCount();
this.countUpdated();
},
notify: function(notification) {
notification.acknowledged = false;
this.pushNotification(notification);
if (!this.isMuted)
this.emit('notify', notification);
@ -1148,9 +1226,7 @@ const Source = new Lang.Class({
//// Protected methods ////
_setSummaryIcon: function(icon) {
if (this._iconBin.child)
this._iconBin.child.destroy();
this._iconBin.child = icon;
this.mainIcon.setIcon(icon);
},
open: function(notification) {
@ -1162,12 +1238,16 @@ const Source = new Lang.Class({
if (!this.notifications[i].resident)
this.notifications[i].destroy();
this._updateCount();
this.countUpdated();
},
// Default implementation is to destroy this source, but subclasses can override
_lastNotificationRemoved: function() {
this.destroy();
},
hasResidentNotification: function() {
return this.notifications.some(function(n) { return n.resident; });
}
});
Signals.addSignalMethods(Source.prototype);
@ -1206,12 +1286,12 @@ const SummaryItem = new Lang.Class({
this._sourceBox.add(this._sourceTitleBin, { expand: true, y_fill: false });
this.actor.child = this._sourceBox;
this.notificationStackView = new St.ScrollView({ name: source.isChat ? '' : 'summary-notification-stack-scrollview',
this.notificationStackView = new St.ScrollView({ style_class: source.isChat ? '' : 'summary-notification-stack-scrollview',
vscrollbar_policy: source.isChat ? Gtk.PolicyType.NEVER : Gtk.PolicyType.AUTOMATIC,
hscrollbar_policy: Gtk.PolicyType.NEVER,
style_class: 'vfade' });
this.notificationStack = new St.BoxLayout({ name: 'summary-notification-stack',
vertical: true });
hscrollbar_policy: Gtk.PolicyType.NEVER });
this.notificationStackView.add_style_class_name('vfade');
this.notificationStack = new St.BoxLayout({ style_class: 'summary-notification-stack',
vertical: true });
this.notificationStackView.add_actor(this.notificationStack);
this._stackedNotifications = [];
@ -1284,7 +1364,7 @@ const SummaryItem = new Lang.Class({
},
prepareNotificationStackForShowing: function() {
if (this.notificationStack.get_children().length > 0)
if (this.notificationStack.get_n_children() > 0)
return;
for (let i = 0; i < this.source.notifications.length; i++) {
@ -1293,7 +1373,6 @@ const SummaryItem = new Lang.Class({
},
doneShowingNotificationStack: function() {
let notificationActors = this.notificationStack.get_children();
for (let i = 0; i < this._stackedNotifications.length; i++) {
let stackedNotification = this._stackedNotifications[i];
let notification = stackedNotification.notification;
@ -1323,7 +1402,7 @@ const SummaryItem = new Lang.Class({
this._stackedNotifications.push(stackedNotification);
if (!this.source.isChat)
notification.enableScrolling(false);
if (this.notificationStack.get_children().length > 0)
if (this.notificationStack.get_n_children() > 0)
notification.setIconVisible(false);
this.notificationStack.add(notification.actor);
notification.expand(false);
@ -1410,7 +1489,7 @@ const MessageTray = new Lang.Class({
track_hover: true });
this._summaryBoxPointer.actor.style_class = 'summary-boxpointer';
this._summaryBoxPointer.actor.hide();
Main.layoutManager.addChrome(this._summaryBoxPointer.actor, { visibleInFullscreen: true });
Main.layoutManager.addChrome(this._summaryBoxPointer.actor);
this._summaryBoxPointerItem = null;
this._summaryBoxPointerContentUpdatedId = 0;
@ -1469,6 +1548,7 @@ const MessageTray = new Lang.Class({
this._overviewVisible = Main.overview.visible;
this._notificationRemoved = false;
this._reNotifyAfterHideNotification = null;
this._inFullscreen = false;
this._corner = new Clutter.Rectangle({ width: 1,
height: 1,
@ -1479,11 +1559,12 @@ const MessageTray = new Lang.Class({
Main.layoutManager.trackChrome(this._corner);
Main.layoutManager.trayBox.add_actor(this.actor);
this.actor.y = this.actor.height;
this.actor.y = 0;
Main.layoutManager.trackChrome(this.actor);
Main.layoutManager.trackChrome(this._notificationBin);
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._setSizePosition));
Main.layoutManager.connect('primary-fullscreen-changed', Lang.bind(this, this._onFullscreenChanged));
this._setSizePosition();
@ -1508,6 +1589,9 @@ const MessageTray = new Lang.Class({
}
}));
this._isScreenLocked = false;
Main.screenShield.connect('lock-status-changed', Lang.bind(this, this._onScreenLockStatusChanged));
this._summaryItems = [];
// We keep a list of new summary items that were added to the summary since the last
// time it was shown to the user. We automatically show the summary to the user if there
@ -1614,6 +1698,12 @@ const MessageTray = new Lang.Class({
// *first* and not show the summary item until after it hides.
// So postpone calling _updateState() a tiny bit.
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() { this._updateState(); return false; }));
this.emit('summary-item-added', summaryItem);
},
getSummaryItems: function() {
return this._summaryItems;
},
_onSourceDestroy: function(source) {
@ -1688,6 +1778,11 @@ const MessageTray = new Lang.Class({
this._notificationQueue.splice(index, 1);
},
_onScreenLockStatusChanged: function(screenShield, locked) {
this._isScreenLocked = locked;
this._updateState();
},
_lock: function() {
this._locked = true;
},
@ -1714,7 +1809,7 @@ const MessageTray = new Lang.Class({
_onNotify: function(source, notification) {
if (this._summaryBoxPointerItem && this._summaryBoxPointerItem.source == source) {
if (this._summaryBoxPointerState == State.HIDING)
if (this._summaryBoxPointerState == State.HIDING) {
// We are in the process of hiding the summary box pointer.
// If there is an update for one of the notifications or
// a new notification to be added to the notification stack
@ -1725,6 +1820,14 @@ const MessageTray = new Lang.Class({
// need to be able to re-parent its actor to a different
// part of the stage.
this._reNotifyAfterHideNotification = notification;
} else {
// The summary box pointer is showing or shown (otherwise,
// this._summaryBoxPointerItem would be null)
// Immediately mark the notification as acknowledged, as it's
// not going into the queue
notification.acknowledged = true;
}
return;
}
@ -1886,7 +1989,7 @@ const MessageTray = new Lang.Class({
_onTrayHoverChanged: function() {
if (this.actor.hover) {
// Don't do anything if the one pixel area at the bottom is hovered over while the tray is hidden.
if (this._trayState == State.HIDDEN)
if (this._trayState == State.HIDDEN && this._notificationState == State.HIDDEN)
return;
// Don't do anything if this._useLongerTrayLeftTimeout is true, meaning the notification originally
@ -1957,6 +2060,11 @@ const MessageTray = new Lang.Class({
this._updateState();
},
_onFullscreenChanged: function(obj, state) {
this._inFullscreen = state;
this._updateState();
},
_onStatusChanged: function(status) {
if (status == GnomeSession.PresenceStatus.BUSY) {
// remove notification and allow the summary to be closed now
@ -2013,18 +2121,36 @@ const MessageTray = new Lang.Class({
// at the present time.
_updateState: function() {
// Notifications
let notificationUrgent = this._notificationQueue.length > 0 && this._notificationQueue[0].urgency == Urgency.CRITICAL;
let notificationsPending = this._notificationQueue.length > 0 && (!this._busy || notificationUrgent);
let notificationQueue = this._notificationQueue.filter(Lang.bind(this, function(notification) {
if (this._isScreenLocked)
return notification.showWhenLocked;
else
return true;
}));
let notificationUrgent = notificationQueue.length > 0 && notificationQueue[0].urgency == Urgency.CRITICAL;
// notificationsLimited is false when the screen is locked, because they go through
// different filtering, and we want to show non urgent messages at times
let notificationsLimited = (this._busy || this._inFullscreen) && !this._isScreenLocked;
let notificationsPending = notificationQueue.length > 0 && (!notificationsLimited || notificationUrgent);
let nextNotification = notificationQueue.length > 0 ? notificationQueue[0] : null;
let notificationPinned = this._pointerInTray && !this._pointerInSummary && !this._notificationRemoved;
let notificationExpanded = this._notificationBin.y < 0;
let notificationExpired = (this._notificationTimeoutId == 0 && !(this._notification && this._notification.urgency == Urgency.CRITICAL) && !this._pointerInTray && !this._locked && !(this._pointerInKeyboard && notificationExpanded)) || this._notificationRemoved;
let notificationExpanded = this._notificationBin.y < - this.actor.height;
let notificationExpired = this._notificationTimeoutId == 0 &&
!(this._notification && this._notification.urgency == Urgency.CRITICAL) &&
!this._pointerInTray &&
!this._locked &&
!(this._pointerInKeyboard && notificationExpanded);
let notificationLockedOut = this._isScreenLocked && (this._notification && !this._notification.showWhenLocked);
let notificationMustClose = this._notificationRemoved || notificationLockedOut || notificationExpired;
let canShowNotification = notificationsPending && this._summaryState == State.HIDDEN;
if (this._notificationState == State.HIDDEN) {
if (canShowNotification)
this._showNotification();
if (canShowNotification) {
this._showNotification(nextNotification);
this._notificationQueue.splice(this._notificationQueue.indexOf(nextNotification), 1);
}
} else if (this._notificationState == State.SHOWN) {
if (notificationExpired)
if (notificationMustClose)
this._hideNotification();
else if (notificationPinned && !notificationExpanded)
this._expandNotification(false);
@ -2045,12 +2171,12 @@ const MessageTray = new Lang.Class({
let summaryOptionalInOverview = this._overviewVisible && !this._locked && !summaryHovered;
let mustHideSummary = (notificationsPending && (notificationUrgent || summaryOptionalInOverview))
|| notificationsVisible;
|| notificationsVisible || this._isScreenLocked;
if (this._summaryState == State.HIDDEN && !mustHideSummary) {
if (summarySummoned) {
this._showSummary(0);
} else if (notificationsDone && !this._busy) {
} else if (notificationsDone && !this._busy && !this._inFullscreen) {
if (this._backFromAway && this._unseenNotifications.length > 0)
this._showSummary(LONGER_SUMMARY_TIMEOUT);
else if (this._newSummaryItems.length > 0)
@ -2096,8 +2222,7 @@ const MessageTray = new Lang.Class({
// Tray itself
let trayIsVisible = (this._trayState == State.SHOWING ||
this._trayState == State.SHOWN);
let trayShouldBeVisible = (!notificationsDone ||
this._summaryState == State.SHOWING ||
let trayShouldBeVisible = (this._summaryState == State.SHOWING ||
this._summaryState == State.SHOWN);
if (!trayIsVisible && trayShouldBeVisible)
this._showTray();
@ -2137,7 +2262,7 @@ const MessageTray = new Lang.Class({
_hideTray: function() {
this._tween(this.actor, '_trayState', State.HIDDEN,
{ y: this.actor.height,
{ y: 0,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
@ -2163,8 +2288,8 @@ const MessageTray = new Lang.Class({
}
},
_showNotification: function() {
this._notification = this._notificationQueue.shift();
_showNotification: function(notification) {
this._notification = notification;
this._unseenNotifications.push(this._notification);
if (this._idleMonitorWatchId == 0)
this._idleMonitorWatchId = this.idleMonitor.add_watch(1000,
@ -2174,7 +2299,7 @@ const MessageTray = new Lang.Class({
this._notificationBin.child = this._notification.actor;
this._notificationBin.opacity = 0;
this._notificationBin.y = this.actor.height;
this._notificationBin.y = 0;
this._notificationBin.show();
this._updateShowingNotification();
@ -2194,6 +2319,8 @@ const MessageTray = new Lang.Class({
},
_updateShowingNotification: function() {
this._notification.acknowledged = true;
Tweener.removeTweens(this._notificationBin);
// We auto-expand notifications with CRITICAL urgency.
@ -2209,7 +2336,8 @@ const MessageTray = new Lang.Class({
// We tween all notifications to full opacity. This ensures that both new notifications and
// notifications that might have been in the process of hiding get full opacity.
//
// We tween any notification showing in the banner mode to banner height (this._notificationBin.y = 0).
// We tween any notification showing in the banner mode to banner height
// (this._notificationBin.y = -this.actor.height).
// This ensures that both new notifications and notifications in the banner mode that might
// have been in the process of hiding are shown with the banner height.
//
@ -2226,7 +2354,7 @@ const MessageTray = new Lang.Class({
onCompleteScope: this
};
if (!this._notification.expanded)
tweenParams.y = 0;
tweenParams.y = - this.actor.height;
this._tween(this._notificationBin, '_notificationState', State.SHOWN, tweenParams);
},
@ -2315,7 +2443,7 @@ const MessageTray = new Lang.Class({
},
_onNotificationExpanded: function() {
let expandedY = this.actor.height - this._notificationBin.height;
let expandedY = - this._notificationBin.height;
// Don't animate the notification to its new position if it has shrunk:
// there will be a very visible "gap" that breaks the illusion.
@ -2395,10 +2523,17 @@ const MessageTray = new Lang.Class({
this._summaryBoxPointerDoneDisplayingId = this._summaryBoxPointerItem.connect('done-displaying-content',
Lang.bind(this, this._escapeTray));
if (this._clickedSummaryItemMouseButton == 1) {
this._notificationQueue = this._notificationQueue.filter( Lang.bind(this,
function(notification) {
return this._summaryBoxPointerItem.source != notification.source;
}));
let newQueue = [];
for (let i = 0; i < this._notificationQueue.length; i++) {
let notification = this._notificationQueue[i];
let sameSource = this._summaryBoxPointerItem.source == notification.source;
if (sameSource)
notification.acknowledged = true;
else
newQueue.push(notification);
}
this._notificationQueue = newQueue;
this._summaryBoxPointerItem.prepareNotificationStackForShowing();
this._summaryBoxPointer.bin.child = this._summaryBoxPointerItem.notificationStackView;
this._summaryBoxPointerItem.scrollTo(St.Side.BOTTOM);
@ -2423,24 +2558,23 @@ const MessageTray = new Lang.Class({
this._summaryBoxPointerState = State.SHOWING;
this._clickedSummaryItem.actor.add_style_pseudo_class('selected');
this._summaryBoxPointer.show(true, Lang.bind(this, function() {
this._summaryBoxPointer.show(BoxPointer.PopupAnimation.FULL, Lang.bind(this, function() {
this._summaryBoxPointerState = State.SHOWN;
}));
},
_onSummaryBoxPointerContentUpdated: function() {
if (this._summaryBoxPointerItem.notificationStack.get_children().length == 0)
if (this._summaryBoxPointerItem.notificationStack.get_n_children() == 0)
this._hideSummaryBoxPointer();
this._adjustSummaryBoxPointerPosition();
},
_adjustSummaryBoxPointerPosition: function() {
// The position of the arrow origin should be the same as center of this._clickedSummaryItem.actor
if (!this._clickedSummaryItem)
return;
this._summaryBoxPointer.setPosition(this._clickedSummaryItem.actor, 0, 0.5);
this._summaryBoxPointer.setPosition(this._clickedSummaryItem.actor, 0);
},
_unsetClickedSummaryItem: function() {
@ -2463,7 +2597,7 @@ const MessageTray = new Lang.Class({
// We should be sure to hide the box pointer if all notifications in it are destroyed while
// it is hiding, so that we don't show an an animation of an empty blob being hidden.
if (this._summaryBoxPointerState == State.HIDING &&
this._summaryBoxPointerItem.notificationStack.get_children().length == 0) {
this._summaryBoxPointerItem.notificationStack.get_n_children() == 0) {
this._summaryBoxPointer.actor.hide();
return;
}
@ -2478,7 +2612,7 @@ const MessageTray = new Lang.Class({
this._summaryBoxPointer.actor.hide();
this._hideSummaryBoxPointerCompleted();
} else {
this._summaryBoxPointer.hide(true, Lang.bind(this, this._hideSummaryBoxPointerCompleted));
this._summaryBoxPointer.hide(BoxPointer.PopupAnimation.FULL, Lang.bind(this, this._hideSummaryBoxPointerCompleted));
}
},
@ -2513,6 +2647,7 @@ const MessageTray = new Lang.Class({
this._updateState();
}
});
Signals.addSignalMethods(MessageTray.prototype);
const SystemNotificationSource = new Lang.Class({
Name: 'SystemNotificationSource',
@ -2520,6 +2655,7 @@ const SystemNotificationSource = new Lang.Class({
_init: function() {
this.parent(_("System Information"), 'dialog-information', St.IconType.SYMBOLIC);
this.setTransient(true);
},
open: function() {

View File

@ -35,7 +35,9 @@ const ModalDialog = new Lang.Class({
_init: function(params) {
params = Params.parse(params, { shellReactive: false,
styleClass: null });
styleClass: null,
parentActor: Main.uiGroup
});
this.state = State.CLOSED;
this._hasModal = false;
@ -45,7 +47,7 @@ const ModalDialog = new Lang.Class({
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 +56,15 @@ 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._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 +77,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 +108,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 +125,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 +179,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,14 +196,14 @@ const ModalDialog = new Lang.Class({
},
_fadeOpen: function() {
let monitor = Main.layoutManager.focusMonitor;
let monitor = Main.layoutManager.currentMonitor;
this._backgroundBin.set_position(monitor.x, monitor.y);
this._backgroundBin.set_size(monitor.width, monitor.height);
this.state = State.OPENING;
this._dialogLayout.opacity = 255;
this.dialogLayout.opacity = 255;
if (this._lightbox)
this._lightbox.show();
this._group.opacity = 0;
@ -211,7 +227,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 +320,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

@ -147,7 +147,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"),
@ -165,11 +165,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() {
@ -683,7 +678,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 };

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 });
}
},
@ -341,7 +354,10 @@ 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_type: St.IconType.FULLCOLOR,
icon_size: source.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,6 +499,10 @@ const NotificationDaemon = new Lang.Class({
},
_onTrayIconAdded: function(o, icon) {
let wmClass = 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);
},
@ -534,11 +554,10 @@ 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;
this._setSummaryIcon(this.createIcon(this.ICON_SIZE));
let tracker = Shell.WindowTracker.get_default();
if (notification.resident && this.app && tracker.focus_app == this.app)
@ -606,10 +625,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._setSummaryIcon(this.createIcon(this.ICON_SIZE));
}
},
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();
@ -640,8 +669,18 @@ const Source = new Lang.Class({
this.parent();
},
createNotificationIcon: function() {
// We set the summary icon ourselves.
return null;
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

@ -11,10 +11,8 @@ 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;
@ -77,6 +75,7 @@ const ShellInfo = new Lang.Class({
let notification = null;
if (this._source.notifications.length == 0) {
notification = new MessageTray.Notification(this._source, text, null);
notification.setShowWhenLocked(true);
} else {
notification = this._source.notifications[0];
notification.update(text, null, { clear: true });
@ -208,7 +207,6 @@ const Overview = new Lang.Class({
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));

View File

@ -346,6 +346,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,
@ -360,6 +361,7 @@ const AppMenuButton = new Lang.Class({
startAnimation: function() {
this._stop = false;
this.actor.reactive = false;
this._spinner.actor.show();
},
@ -465,6 +467,13 @@ const AppMenuButton = new Lang.Class({
this._sync();
},
setLockedState: function(locked) {
if (locked)
this.hide();
else
this._sync();
},
_sync: function() {
let tracker = Shell.WindowTracker.get_default();
let focusedApp = tracker.focus_app;
@ -751,10 +760,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;
@ -775,10 +785,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;
@ -906,6 +917,8 @@ const Panel = new Lang.Class({
this.actor.remove_style_class_name('in-overview');
}));
Main.screenShield.connect('lock-status-changed', Lang.bind(this, this._onLockStateChanged));
this._menus = new PopupMenu.PopupMenuManager(this);
this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
@ -954,10 +967,6 @@ const Panel = new Lang.Class({
this._centerBox.add(this._dateMenu.actor, { y_fill: true });
this._menus.addMenu(this._dateMenu.menu);
/* right */
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',
{ sortGroup: CtrlAltTab.SortGroup.TOP });
@ -1126,38 +1135,27 @@ const Panel = new Lang.Class({
if (!position)
position = 0;
this._insertStatusItem(indicator.actor, position);
this._menus.addMenu(indicator.menu);
if (indicator.menu)
this._menus.addMenu(indicator.menu);
this._statusArea[role] = indicator;
let destroyId = indicator.connect('destroy', Lang.bind(this, function(emitter) {
this._statusArea[role] = null;
delete this._statusArea[role];
emitter.disconnect(destroyId);
}));
return indicator;
},
_onTrayIconAdded: function(o, icon, role) {
if (Main.sessionMode.statusArea.implementation[role]) {
// This icon is legacy, and replaced by a Shell version
// Hide it
return;
}
_onLockStateChanged: function(shield, locked) {
if (this._activitiesButton)
this._activitiesButton.setLockedState(locked);
if (this._appMenu)
this._appMenu.setLockedState(locked);
if (this._dateMenu)
this._dateMenu.setLockedState(locked);
if (Main.sessionMode.statusArea.order.indexOf(role) == -1)
return;
icon.height = PANEL_ICON_SIZE;
let buttonBox = new PanelMenu.ButtonBox();
let box = buttonBox.actor;
box.add_actor(icon);
this._insertStatusItem(box, Main.sessionMode.statusArea.order.indexOf(role));
},
_onTrayIconRemoved: function(o, icon) {
let box = icon.get_parent();
if (box && box._delegate instanceof PanelMenu.ButtonBox)
box.destroy();
for (let id in this._statusArea)
this._statusArea[id].setLockedState(locked);
},
});

View File

@ -145,6 +145,13 @@ const Button = new Lang.Class({
}
},
setLockedState: function(locked) {
// default behaviour is to hide completely
if (locked)
this.menu.close();
this.actor.visible = !locked;
},
_onButtonPress: function(actor, event) {
if (!this.menu)
return;

View File

@ -135,17 +135,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();
@ -178,7 +178,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;
@ -269,7 +270,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);

View File

@ -134,12 +134,7 @@ const PopupBaseMenuItem = new Lang.Class({
this.sensitive = sensitive;
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);
},
@ -773,12 +768,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();
@ -875,6 +868,7 @@ const PopupMenuBase = new Lang.Class({
this._activeMenuItem = null;
this._childMenus = [];
this._settingsActions = { };
},
addAction: function(title, callback) {
@ -902,11 +896,21 @@ const PopupMenuBase = new Lang.Class({
Main.overview.hide();
app.activate();
});
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) {
@ -942,7 +946,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)
@ -977,7 +981,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
@ -1050,7 +1054,7 @@ const PopupMenuBase = new Lang.Class({
menuItem._closingId = this.connect('open-state-changed',
function(self, open) {
if (!open)
menuItem.close(false);
menuItem.close(BoxPointer.PopupAnimation.FADE);
});
menuItem.connect('destroy', Lang.bind(this, function() {
menuItem.disconnect(menuItem._subMenuActivateId);
@ -1067,7 +1071,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);
@ -1154,9 +1158,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() {
@ -1217,7 +1221,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;
}
@ -1419,7 +1423,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;
}
@ -1501,7 +1505,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) {
@ -1513,7 +1517,7 @@ const PopupSubMenuMenuItem = new Lang.Class({
},
activate: function(event) {
this.menu.open(true);
this.menu.open(BoxPointer.PopupAnimation.FULL);
},
_onButtonReleaseEvent: function(actor) {
@ -1540,7 +1544,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;
}
@ -1891,11 +1895,7 @@ 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');
item.actor.reactive = action.enabled;
destroyId = item.connect('destroy', Lang.bind(this, function() {
item.disconnect(destroyId);
@ -2027,12 +2027,7 @@ const RemoteMenu = new Lang.Class({
if (action.items.length) {
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');
item.actor.reactive = action.enabled;
}
}
}
@ -2194,11 +2189,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) {
@ -2313,6 +2308,6 @@ const PopupMenuManager = new Lang.Class({
_closeMenu: function() {
if (this._activeMenu != null)
this._activeMenu.close(true);
this._activeMenu.close(BoxPointer.PopupAnimation.FULL);
}
});

View File

@ -62,10 +62,25 @@ 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);
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,

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

@ -0,0 +1,615 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const GnomeDesktop = imports.gi.GnomeDesktop;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Signals = imports.signals;
const St = imports.gi.St;
const GnomeSession = imports.misc.gnomeSession;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const Tweener = imports.ui.tweener;
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
const LOCK_ENABLED_KEY = 'lock-enabled';
const CURTAIN_SLIDE_TIME = 0.8;
// fraction of screen height the arrow must reach before completing
// the slide up automatically
const ARROW_DRAG_TRESHOLD = 0.1;
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);
}));
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 iconClone = source.createIcon(SUMMARY_ICON_SIZE);
let sourceActor = new MessageTray.SourceActor(source, SUMMARY_ICON_SIZE);
sourceActor.setIcon(iconClone);
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) {
// Ignore transient sources
if (item.source.isTransient)
return;
let obj = {
item: item,
source: item.source,
resident: this._sourceIsResident(item.source),
contentUpdatedId: 0,
sourceDestroyId: 0,
sourceBox: null,
countLabel: null,
};
if (obj.resident) {
item.prepareNotificationStackForShowing();
this._residentNotificationBox.add(item.notificationStackView);
} 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);
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
this._residentNotificationBox.remove_actor(obj.item.notificationStackView);
[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;
obj.item.prepareNotificationStackForShowing();
this._residentNotificationBox.add(obj.item.notificationStackView);
} 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) {
this._residentNotificationBox.remove_actor(obj.item.notificationStackView);
obj.item.doneShowingNotificationStack();
} else {
obj.sourceBox.destroy();
}
obj.item.disconnect(obj.contentUpdatedId);
obj.source.disconnect(obj.sourceDestroyId);
obj.source.disconnect(obj.sourceCountChangedId);
},
});
const LockDialogGroup = new Lang.Class({
Name: 'LockDialogGroup',
Extends: St.Widget,
_init: function(params) {
this.parent(params);
},
vfunc_get_preferred_height: function(forWidth) {
return [global.screen_height, global.screen_height];
},
vfunc_get_preferred_width: function(forHeight) {
return [global.screen_width, global.screen_width];
}
});
/**
* 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._lockScreenGroup = new St.Widget({ x_expand: true,
y_expand: true,
reactive: true,
can_focus: true,
layout_manager: new Clutter.BinLayout(),
name: 'lockScreenGroup',
});
this._lockScreenGroup.connect('key-release-event',
Lang.bind(this, this._onLockScreenKeyRelease));
this._background = Meta.BackgroundActor.new_for_screen(global.screen);
this._lockScreenGroup.add_actor(this._background);
// FIXME: build the rest of the lock screen here
this._arrow = new St.DrawingArea({ style_class: 'arrow',
reactive: 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
});
this._arrow.connect('repaint', Lang.bind(this, this._drawArrow));
this._lockScreenGroup.add_actor(this._arrow);
let action = new Clutter.DragAction({ drag_axis: Clutter.DragAxis.Y_AXIS });
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 LockDialogGroup({ 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._settings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
this._isModal = false;
this._isLocked = false;
this._hasLockScreen = false;
this._lightbox = new Lightbox.Lightbox(Main.uiGroup,
{ inhibitEvents: true,
fadeInTime: STANDARD_FADE_TIME,
fadeFactor: 1 });
},
_onLockScreenKeyRelease: function(actor, event) {
if (event.get_key_symbol() == Clutter.KEY_Escape) {
this._showUnlockDialog(true);
return true;
}
return false;
},
_drawArrow: function() {
let cr = this._arrow.get_context();
let [w, h] = this._arrow.get_surface_size();
let node = this._arrow.get_theme_node();
Clutter.cairo_set_source_color(cr, node.get_foreground_color());
cr.moveTo(0, h);
cr.lineTo(w/2, 0);
cr.lineTo(w, h);
cr.fill();
},
_onDragBegin: function() {
Tweener.removeTweens(this._lockScreenGroup);
},
_onDragEnd: function(action, actor, eventX, eventY, modifiers) {
if (this._lockScreenGroup.y < -(ARROW_DRAG_TRESHOLD * global.stage.height)) {
// Complete motion automatically
this._showUnlockDialog(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.fixed_position_set = false;
}
});
}
},
_onStatusChanged: function(status) {
if (status == GnomeSession.PresenceStatus.IDLE) {
if (this._dialog) {
this._dialog.cancel();
if (!this._keepDialog) {
this._dialog = null;
}
}
if (!this._isModal) {
Main.pushModal(this.actor);
this._isModal = true;
}
if (!this._isLocked)
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._isLocked) {
this.lock(false);
} else if (this._isModal) {
this.unlock();
}
}
},
showDialog: function() {
this.lock(true);
this._showUnlockDialog(false);
},
_showUnlockDialog: function(animate) {
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: Lang.bind(this, this._hideLockScreen),
});
} else {
this._hideLockScreen();
}
if (!this._dialog) {
[this._dialog, this._keepDialog] = Main.sessionMode.createUnlockDialog(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));
}
if (this._keepDialog) {
// Notify the other components that even though we are showing the
// screenshield, we're not in a locked state
// (this happens for the gdm greeter)
this._isLocked = false;
this.emit('lock-status-changed', false);
}
},
_onUnlockFailed: function() {
this._dialog.destroy();
this._dialog = null;
this._resetLockScreen(false);
},
_onUnlockSucceded: function() {
this.unlock();
},
_hideLockScreen: function() {
this._arrow.hide();
this._lockScreenGroup.hide();
},
_resetLockScreen: function(animate) {
this._lockScreenGroup.show();
this._arrow.show();
if (animate) {
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._lockScreenGroup.fixed_position_set = false;
this.emit('lock-screen-shown');
},
onCompleteScope: this
});
this._lockDialogGroup.opacity = 0;
Tweener.removeTweens(this._lockDialogGroup);
Tweener.addTween(this._lockDialogGroup,
{ opacity: 255,
time: SHORT_FADE_TIME,
transition: 'easeOutQuad' });
} else {
this._lockScreenGroup.fixed_position_set = false;
this._lockDialogGroup.opacity = 255;
this.emit('lock-screen-shown');
}
this._lockScreenGroup.grab_key_focus();
},
// Some of the actors in the lock screen are heavy in
// resources, so we only create them when needed
_prepareLockScreen: function() {
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._lockScreenGroup.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();
this._hasLockScreen = false;
},
get locked() {
return this._isLocked;
},
unlock: function() {
if (this._hasLockScreen)
this._clearLockScreen();
if (this._keepDialog) {
// The dialog must be kept alive,
// so immediately go back to it
// This will also reset _isLocked
this._showUnlockDialog(false);
return;
}
if (this._dialog) {
this._dialog.destroy();
this._dialog = null;
}
this._lightbox.hide();
Main.popModal(this.actor);
this.actor.hide();
this._isModal = false;
this._isLocked = false;
this.emit('lock-status-changed', false);
},
lock: function(animate) {
if (!this._hasLockScreen)
this._prepareLockScreen();
if (!this._isModal) {
Main.pushModal(this.actor);
this._isModal = true;
}
this._isLocked = true;
this.actor.show();
this._resetLockScreen(animate);
this.emit('lock-status-changed', true);
},
});
Signals.addSignalMethods(ScreenShield.prototype);

View File

@ -51,7 +51,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();
},
/**
@ -141,19 +141,6 @@ const SearchProvider = new Lang.Class({
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

View File

@ -275,10 +275,7 @@ const SearchResults = new Lang.Class({
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,

View File

@ -39,7 +39,8 @@ const _modes = {
hasRunDialog: false,
hasWorkspaces: false,
createSession: Main.createGDMSession,
extraStylesheet: global.datadir + '/theme/gdm.css',
createUnlockDialog: Main.createGDMLoginDialog,
extraStylesheet: null,
statusArea: {
order: [
'a11y', 'display', 'keyboard',
@ -86,6 +87,7 @@ const _modes = {
hasRunDialog: true,
hasWorkspaces: true,
createSession: Main.createUserSession,
createUnlockDialog: Main.createSessionUnlockDialog,
extraStylesheet: null,
statusArea: {
order: [
@ -113,6 +115,8 @@ const SessionMode = new Lang.Class({
this._createSession = params.createSession;
delete params.createSession;
this._createUnlockDialog = params.createUnlockDialog;
delete params.createUnlockDialog;
Lang.copyProperties(params, this);
},
@ -120,5 +124,12 @@ const SessionMode = new Lang.Class({
createSession: function() {
if (this._createSession)
this._createSession();
}
},
createUnlockDialog: function() {
if (this._createUnlockDialog)
return this._createUnlockDialog.apply(this, arguments);
else
return null;
},
});

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,23 +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="InstallRemoteExtension">
<arg type="s" direction="in" name="uuid"/>
</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>;
@ -82,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();
},
/**
@ -195,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) {
@ -253,12 +302,12 @@ const GnomeShell = new Lang.Class({
return extension.errors;
},
InstallRemoteExtension: function(uuid) {
ExtensionSystem.installExtensionFromUUID(uuid);
InstallRemoteExtensionAsync: function([uuid], invocation) {
return ExtensionDownloader.installExtension(uuid, invocation);
},
UninstallExtension: function(uuid) {
return ExtensionSystem.uninstallExtensionFromUUID(uuid);
return ExtensionDownloader.uninstallExtension(uuid);
},
LaunchExtensionPrefs: function(uuid) {
@ -268,19 +317,15 @@ const GnomeShell = new Lang.Class({
['extension:///' + uuid], -1, null);
},
get OverviewActive() {
return Main.overview.visible;
ReloadExtension: function(uuid) {
ExtensionSystem.unloadExtension(uuid);
ExtensionSystem.loadExtension(uuid);
},
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) {
@ -288,3 +333,33 @@ const GnomeShell = new Lang.Class({
GLib.Variant.new('(sis)', [newState.uuid, newState.state, newState.error]));
}
});
const ScreenSaverDBus = new Lang.Class({
Name: 'ScreenSaverDBus',
_init: function() {
this.parent();
Main.screenShield.connect('lock-status-changed', Lang.bind(this, function(shield, locked) {
this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [locked]));
}));
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenSaverIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/ScreenSaver');
},
Lock: function() {
Main.screenShield.lock(true);
},
SetActive: function(active) {
if (active)
Main.screenShield.lock(true);
else
Main.screenShield.unlock();
},
GetActive: function() {
return Main.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();
@ -104,50 +123,50 @@ 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();
let entry = actor.menu ? actor : actor.get_parent();
if (entry._menu.isOpen) {
entry._menu.close();
if (entry.menu.isOpen) {
entry.menu.close();
} else if (action.get_button() == 3) {
let [stageX, stageY] = action.get_coords();
_setMenuAlignment(entry, stageX);
entry._menu.open();
entry.menu.open();
}
};
function _onLongPress(action, actor, state) {
let entry = actor._menu ? actor : actor.get_parent();
let entry = actor.menu ? actor : actor.get_parent();
if (state == Clutter.LongPressState.QUERY)
return action.get_button() == 1 && !entry._menu.isOpen;
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 false;
};
function _onPopup(actor) {
let entry = actor._menu ? actor : actor.get_parent();
let entry = actor.menu ? actor : actor.get_parent();
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;

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', St.IconType.FULLCOLOR);
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,63 +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);
this._setSummaryIcon(icon);
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 });
this._messageBox.add(this._passwordBox);
this.addActor(label);
this._passwordLabel = new St.Label(({ style_class: 'prompt-dialog-password-label',
text: _("Passphrase") }));
this._passwordBox.add(this._passwordLabel);
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 Passphrase");
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);
}
});
@ -298,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,
@ -339,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();
}));
},
@ -387,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';
@ -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');
@ -83,6 +75,10 @@ const ATIndicator = new Lang.Class({
this.menu.addSettingsAction(_("Universal Access Settings"), 'gnome-universal-access-panel.desktop');
},
setLockedState: function(locked) {
this.menu.setSettingsVisibility(!locked);
},
_buildItemExtended: function(string, initial_value, writable, on_set) {
let widget = new PopupMenu.PopupSwitchMenuItem(string, initial_value);
if (!writable)

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;
@ -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);
}));
@ -92,12 +88,17 @@ const Indicator = new Lang.Class({
this._applet.connect('cancel-request', Lang.bind(this, this._cancelRequest));
},
setLockedState: function(locked) {
this._isLocked = locked;
this._updateKillswitch();
},
_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,7 +107,7 @@ const Indicator = new Lang.Class({
/* TRANSLATORS: this means that bluetooth was disabled by hardware rfkill */
this._killswitch.setStatus(_("hardware disabled"));
this.actor.visible = has_adapter;
this.actor.visible = !this._isLocked && has_adapter;
if (on) {
this._discoverable.actor.show();

View File

@ -1,7 +1,5 @@
// -*- 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 GnomeDesktop = imports.gi.GnomeDesktop;
@ -9,6 +7,16 @@ const Lang = imports.lang;
const Shell = imports.gi.Shell;
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;
@ -19,6 +27,102 @@ 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._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;
},
_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);
} 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();
},
getEngineDesc: function(id) {
if (!IBus || !this._ready)
return null;
return this._engines[id];
}
});
const LayoutMenuItem = new Lang.Class({
Name: 'LayoutMenuItem',
@ -58,6 +162,8 @@ const InputSourceIndicator = new Lang.Class({
this._currentSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
this._xkbInfo = new GnomeDesktop.XkbInfo();
this._ibusManager = new IBusManager(Lang.bind(this, this._inputSourcesChanged));
this._inputSourcesChanged();
// re-using "allowSettings" for the keyboard layout is a bit shady,
@ -66,11 +172,18 @@ const InputSourceIndicator = new Lang.Class({
// option if need arises.
if (Main.sessionMode.allowSettings) {
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, this._showLayout));
this._showLayoutItem = this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, this._showLayout));
}
this.menu.addSettingsAction(_("Region and Language Settings"), 'gnome-region-panel.desktop');
},
setLockedState: function(locked) {
if (Main.sessionMode.allowSettings) {
this._showLayoutItem.actor.visible = !locked;
}
this.menu.setSettingsVisibility(!locked);
},
_currentInputSourceChanged: function() {
let nVisibleSources = Object.keys(this._layoutItems).length;
if (nVisibleSources < 2)
@ -120,13 +233,20 @@ const InputSourceIndicator = new Lang.Class({
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)
continue;
let info = {};
[info.exists, info.displayName, info.shortName, , ] =
this._xkbInfo.get_layout_info(id);
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) {
info.exists = true;
info.displayName = engineDesc.get_longname();
info.shortName = engineDesc.get_symbol();
}
}
if (!info.exists)
continue;
@ -175,8 +295,19 @@ const InputSourceIndicator = new Lang.Class({
let sources = this._settings.get_value(KEY_INPUT_SOURCES);
let current = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
let id = sources.get_child_value(current).deep_unpack()[1];
let [, , , xkbLayout, xkbVariant] = this._xkbInfo.get_layout_info(id);
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 (!xkbLayout || xkbLayout.length == 0)
return;

View File

@ -1,12 +1,9 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const ByteArray = imports.byteArray;
const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const NetworkManager = imports.gi.NetworkManager;
const NMClient = imports.gi.NMClient;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
@ -101,11 +98,10 @@ const NMNetworkMenuItem = new Lang.Class({
Name: 'NMNetworkMenuItem',
Extends: PopupMenu.PopupBaseMenuItem,
_init: function(accessPoints, title, params) {
_init: function(bestAP, title, params) {
this.parent(params);
accessPoints = sortAccessPoints(accessPoints);
this.bestAP = accessPoints[0];
this.bestAP = bestAP;
if (!title) {
let ssid = this.bestAP.get_ssid();
@ -127,24 +123,10 @@ const NMNetworkMenuItem = new Lang.Class({
this.bestAP._secType != NMAccessPointSecurity.NONE)
this._secureIcon.icon_name = 'network-wireless-encrypted';
this._icons.add_actor(this._secureIcon);
this._accessPoints = [ ];
for (let i = 0; i < accessPoints.length; i++) {
let ap = accessPoints[i];
// need a wrapper object here, because the access points can be shared
// between many NMNetworkMenuItems
let apObj = {
ap: ap,
updateId: ap.connect('notify::strength', Lang.bind(this, this._updated))
};
this._accessPoints.push(apObj);
}
},
_updated: function(ap) {
if (ap.strength > this.bestAP.strength)
this.bestAP = ap;
updateBestAP: function(ap) {
this.bestAP = ap;
this._signalIcon.icon_name = this._getIcon();
},
@ -153,36 +135,6 @@ const NMNetworkMenuItem = new Lang.Class({
return 'network-workgroup';
else
return 'network-wireless-signal-' + signalToIcon(this.bestAP.strength);
},
updateAccessPoints: function(accessPoints) {
for (let i = 0; i < this._accessPoints.length; i++) {
let apObj = this._accessPoints[i];
apObj.ap.disconnect(apObj.updateId);
apObj.updateId = 0;
}
accessPoints = sortAccessPoints(accessPoints);
this.bestAP = accessPoints[0];
this._accessPoints = [ ];
for (let i = 0; i < accessPoints; i++) {
let ap = accessPoints[i];
let apObj = {
ap: ap,
updateId: ap.connect('notify::strength', Lang.bind(this, this._updated))
};
this._accessPoints.push(apObj);
}
},
destroy: function() {
for (let i = 0; i < this._accessPoints.length; i++) {
let apObj = this._accessPoints[i];
apObj.ap.disconnect(apObj.updateId);
apObj.updateId = 0;
}
this.parent();
}
});
@ -297,7 +249,7 @@ const NMDevice = new Lang.Class({
this._client = client;
this._connections = [ ];
for (let i = 0; i < connections.length; i++) {
if (!connections[i]._uuid)
if (!connections[i].get_uuid())
continue;
if (!this.connectionValid(connections[i]))
continue;
@ -613,7 +565,7 @@ const NMDevice = new Lang.Class({
let title;
let active = this._activeConnection._connection;
if (active) {
title = active._name;
title = active.get_id();
} else {
/* TRANSLATORS: this is the indication that a connection for another logged in user is active,
and we cannot access its settings (including the name) */
@ -1026,6 +978,7 @@ const NMDeviceWireless = new Lang.Class({
obj.ssidText = ssidToLabel(obj.ssid);
this._networks.push(obj);
}
ap._updateId = ap.connect('notify::strength', Lang.bind(this, this._onApStrengthChanged));
// Check if some connection is valid for this AP
for (let j = 0; j < validConnections.length; j++) {
@ -1037,6 +990,10 @@ const NMDeviceWireless = new Lang.Class({
}
}
// Sort APs within each network by strength
for (let i = 0; i < this._networks.length; i++)
sortAccessPoints(this._networks[i].accessPoints);
if (this.device.active_access_point) {
let networkPos = this._findNetwork(this.device.active_access_point);
@ -1119,7 +1076,7 @@ const NMDeviceWireless = new Lang.Class({
// the user toggles the switch and has more than one wireless device)
if (this._networks.length > 0) {
let connection = this._createAutomaticConnection(this._networks[0]);
let accessPoints = sortAccessPoints(this._networks[0].accessPoints);
let accessPoints = this._networks[0].accessPoints;
this._client.add_and_activate_connection(connection, this.device, accessPoints[0].dbus_path, null);
}
},
@ -1189,6 +1146,13 @@ const NMDeviceWireless = new Lang.Class({
else if (!oneHasConnection && twoHasConnection)
return 1;
let oneStrength = one.accessPoints[0].strength;
let twoStrength = two.accessPoints[0].strength;
// place stronger connections first
if (oneStrength != twoStrength)
return oneStrength < twoStrength ? 1 : -1;
let oneHasSecurity = one.security != NMAccessPointSecurity.NONE;
let twoHasSecurity = two.security != NMAccessPointSecurity.NONE;
@ -1238,6 +1202,28 @@ const NMDeviceWireless = new Lang.Class({
return -1;
},
_onApStrengthChanged: function(ap) {
let res = this._findExistingNetwork(ap);
if (res == null) {
// Uhm... stale signal?
return;
}
let network = this._networks[res.network];
network.accessPoints.splice(res.ap, 1);
Util.insertSorted(network.accessPoints, ap, function(one, two) {
return two.strength - one.strength;
});
this._networks.splice(res.network, 1);
let newPos = Util.insertSorted(this._networks, network, Lang.bind(this, this._networkSortFunction));
if (newPos != res.network) {
this._clearSection();
this._queueCreateSection();
}
},
_accessPointAdded: function(device, accessPoint) {
if (accessPoint.get_ssid() == null) {
// This access point is not visible yet
@ -1257,9 +1243,11 @@ const NMDeviceWireless = new Lang.Class({
return;
}
apObj.accessPoints.push(accessPoint);
Util.insertSorted(apObj.accessPoints, accessPoint, function(one, two) {
return two.strength - one.strength;
});
if (apObj.item)
apObj.item.updateAccessPoints(apObj.accessPoints);
apObj.item.updateBestAP(apObj.accessPoints[0]);
} else {
apObj = { ssid: accessPoint.get_ssid(),
mode: accessPoint.mode,
@ -1270,6 +1258,7 @@ const NMDeviceWireless = new Lang.Class({
};
apObj.ssidText = ssidToLabel(apObj.ssid);
}
accessPoint._updateId = accessPoint.connect('notify::strength', Lang.bind(this, this._onApStrengthChanged));
// check if this enables new connections for this group
for (let i = 0; i < this._connections.length; i++) {
@ -1277,23 +1266,26 @@ const NMDeviceWireless = new Lang.Class({
if (accessPoint.connection_valid(connection) &&
apObj.connections.indexOf(connection) == -1) {
apObj.connections.push(connection);
// this potentially changes the order
needsupdate = true;
}
}
if (pos == -1 || needsupdate) {
if (pos != -1)
this._networks.splice(pos, 1);
pos = Util.insertSorted(this._networks, apObj, this._networkSortFunction);
if (pos != -1)
this._networks.splice(pos, 1);
let newPos = Util.insertSorted(this._networks, apObj, this._networkSortFunction);
// Queue an update of the UI if we changed the order
if (newPos != pos) {
this._clearSection();
this._queueCreateSection();
}
},
_accessPointRemoved: function(device, accessPoint) {
if (accessPoint._updateId) {
accessPoint.disconnect(accessPoint._updateId);
accessPoint._updateId = 0;
}
let res = this._findExistingNetwork(accessPoint);
if (res == null) {
@ -1335,17 +1327,30 @@ const NMDeviceWireless = new Lang.Class({
this._overflowItem = null;
}
}
this._networks.splice(res.network, 1);
} else if (apObj.item)
apObj.item.updateAccessPoints(apObj.accessPoints);
this._networks.splice(res.network, 1);
} else {
let okPrev = true, okNext = true;
if (res.network > 0)
okPrev = this._networkSortFunction(this._networks[res.network - 1], apObj) >= 0;
if (res.network < this._networks.length-1)
okNext = this._networkSortFunction(this._networks[res.network + 1], apObj) <= 0;
if (!okPrev || !okNext) {
this._clearSection();
this._queueCreateSection();
} else if (apObj.item) {
apObj.item.updateBestAP(apObj.accessPoints[0]);
}
}
},
_createAPItem: function(connection, accessPointObj, useConnectionName) {
let item = new NMNetworkMenuItem(accessPointObj.accessPoints, useConnectionName ? connection._name : undefined);
let item = new NMNetworkMenuItem(accessPointObj.accessPoints[0], useConnectionName ? connection.get_id() : undefined);
item._connection = connection;
item.connect('activate', Lang.bind(this, function() {
let accessPoints = sortAccessPoints(accessPointObj.accessPoints);
let accessPoints = accessPointObj.accessPoints;
for (let i = 0; i < accessPoints.length; i++) {
if (accessPoints[i].connection_valid(connection)) {
this._client.activate_connection(connection, this.device, accessPoints[i].dbus_path, null);
@ -1452,27 +1457,19 @@ const NMDeviceWireless = new Lang.Class({
},
_createActiveConnectionItem: function() {
let icon, title;
if (this._activeConnection && this._activeConnection._connection) {
let connection = this._activeConnection._connection;
if (this._activeNetwork)
this._activeConnectionItem = new NMNetworkMenuItem(this._activeNetwork.accessPoints, undefined,
{ reactive: false });
else
this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(connection._name,
'network-wireless-connected',
{ reactive: false });
} else {
// We cannot read the connection (due to ACL, or API incompatibility), but we still show signal if we have it
let menuItem;
if (this._activeNetwork)
this._activeConnectionItem = new NMNetworkMenuItem(this._activeNetwork.accessPoints, undefined,
{ reactive: false });
else
this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(_("Connected (private)"),
'network-wireless-connected',
{ reactive: false });
}
let title;
if (this._activeConnection && this._activeConnection._connection)
title = this._activeConnection._connection.get_id();
else
title = _("Connected (private)");
if (this._activeNetwork)
this._activeConnectionItem = new NMNetworkMenuItem(this.device.active_access_point, undefined,
{ reactive: false });
else
this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(title,
'network-wireless-connected',
{ reactive: false });
this._activeConnectionItem.setShowDot(true);
},
@ -1512,9 +1509,9 @@ const NMDeviceWireless = new Lang.Class({
apObj.item.menu.addMenuItem(this._createAPItem(apObj.connections[i], apObj, true));
}
} else {
apObj.item = new NMNetworkMenuItem(apObj.accessPoints);
apObj.item = new NMNetworkMenuItem(apObj.accessPoints[0]);
apObj.item.connect('activate', Lang.bind(this, function() {
let accessPoints = sortAccessPoints(apObj.accessPoints);
let accessPoints = apObj.accessPoints;
if ( (accessPoints[0]._secType == NMAccessPointSecurity.WPA2_ENT)
|| (accessPoints[0]._secType == NMAccessPointSecurity.WPA_ENT)) {
// 802.1x-enabled APs require further configuration, so they're
@ -1567,10 +1564,25 @@ const NMDeviceWireless = new Lang.Class({
const NMApplet = new Lang.Class({
Name: 'NMApplet',
Extends: PanelMenu.SystemStatusButton,
Extends: PanelMenu.Button,
_init: function() {
this.parent('network-offline', _("Network"));
this.parent(0.0, _('Network'));
this._box = new St.BoxLayout({ name: 'networkMenu' });
this.actor.add_actor (this._box);
this.actor.add_style_class_name('panel-status-button');
this._primaryIcon = new St.Icon({ icon_name: 'network-offline',
icon_type: St.IconType.SYMBOLIC,
style_class: 'system-status-icon' });
this._box.add_actor(this._primaryIcon);
this._secondaryIcon = new St.Icon({ icon_name: 'network-vpn',
icon_type: St.IconType.SYMBOLIC,
style_class: 'system-status-icon',
visible: false });
this._box.add_actor(this._secondaryIcon);
this._client = NMClient.Client.new();
@ -1588,6 +1600,7 @@ const NMApplet = new Lang.Class({
this._connections = [ ];
this._mainConnection = null;
this._vpnConnection = null;
this._activeAccessPointUpdateId = 0;
this._activeAccessPoint = null;
this._mobileUpdateId = 0;
@ -1678,6 +1691,19 @@ const NMApplet = new Lang.Class({
}));
},
setIcon: function(iconName) {
this._primaryIcon.icon_name = iconName;
},
setLockedState: function(locked) {
// FIXME: more design discussion is needed before we can
// expose part of this menu
if (locked)
this.menu.close();
this.actor.reactive = !locked;
},
_ensureSource: function() {
if (!this._source) {
this._source = new MessageTray.Source(_("Network Manager"),
@ -1851,6 +1877,8 @@ const NMApplet = new Lang.Class({
this._activeConnections = newActiveConnections;
this._mainConnection = null;
this._vpnConnection = null;
let activating = null;
let default_ip4 = null;
let default_ip6 = null;
@ -1884,10 +1912,10 @@ const NMApplet = new Lang.Class({
default_ip4 = a;
if (a.default6)
default_ip6 = a;
if (a._type == 'vpn')
active_vpn = a;
if (a.state == NetworkManager.ActiveConnectionState.ACTIVATING)
else if (a.state == NetworkManager.ActiveConnectionState.ACTIVATING)
activating = a;
if (!a._primaryDevice) {
@ -1916,7 +1944,8 @@ const NMApplet = new Lang.Class({
}
}
this._mainConnection = activating || active_vpn || default_ip4 || default_ip6 || this._activeConnections[0] || null;
this._mainConnection = activating || default_ip4 || default_ip6 || this._activeConnections[0] || null;
this._vpnConnection = active_vpn;
},
_notifyActivated: function(activeConnection) {
@ -2062,9 +2091,6 @@ const NMApplet = new Lang.Class({
case NMConnectionCategory.WIRED:
this.setIcon('network-wired-acquiring');
break;
case NMConnectionCategory.VPN:
this.setIcon('network-vpn-acquiring');
break;
default:
// fallback to a generic connected icon
// (it could be a private connection of some other user)
@ -2127,9 +2153,6 @@ const NMApplet = new Lang.Class({
this.setIcon('network-cellular-signal-' + signalToIcon(dev.mobileDevice.signal_quality));
hasMobileIcon = true;
break;
case NMConnectionCategory.VPN:
this.setIcon('network-vpn');
break;
default:
// fallback to a generic connected icon
// (it could be a private connection of some other user)
@ -2138,6 +2161,25 @@ const NMApplet = new Lang.Class({
}
}
// update VPN indicator
if (this._vpnConnection) {
let vpnIconName = 'network-vpn';
if (this._vpnConnection.state == NetworkManager.ActiveConnectionState.ACTIVATING)
vpnIconName = 'network-vpn-acquiring';
// only show a separate icon when we're using a wireless/3g connection
if (mc._section == NMConnectionCategory.WIRELESS ||
mc._section == NMConnectionCategory.WWAN) {
this._secondaryIcon.icon_name = vpnIconName;
this._secondaryIcon.visible = true;
} else {
this.setIcon(vpnIconName);
this._secondaryIcon.visible = false;
}
} else {
this._secondaryIcon.visible = false;
}
// cleanup stale signal connections
if (!hasApIcon && this._activeAccessPointUpdateId) {

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';
@ -80,6 +76,12 @@ const Indicator = new Lang.Class({
this._devicesChanged();
},
setLockedState: function(locked) {
if (locked)
this.menu.close();
this.actor.reactive = !locked;
},
_readPrimaryDevice: function() {
this._proxy.GetPrimaryDeviceRemote(Lang.bind(this, function(result, error) {
if (error) {

View File

@ -2,16 +2,11 @@
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
const Gvc = imports.gi.Gvc;
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 VOLUME_ADJUSTMENT_STEP = 0.05; /* Volume adjustment step in % */
@ -62,6 +57,10 @@ const Indicator = new Lang.Class({
this._control.open();
},
setLockedState: function(locked) {
this.menu.setSettingsVisibility(!locked);
},
_onScrollEvent: function(actor, event) {
let direction = event.get_scroll_direction();
let currentVolume = this._output.volume;

View File

@ -1,63 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const MessageTray = imports.ui.messageTray;
const NotificationDaemon = imports.ui.notificationDaemon;
const Util = imports.misc.util;
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': 'input-method'
};
const StatusIconDispatcher = new Lang.Class({
Name: 'StatusIconDispatcher',
_init: function() {
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));
// Yet-another-Ubuntu-workaround - we have to kill their
// app-indicators, so that applications fall back to normal
// status icons
// http://bugzilla.gnome.org/show_bug.cgi=id=621382
Util.killall('indicator-application-service');
},
start: function(themeWidget) {
this._traymanager.manage_stage(global.stage, themeWidget);
},
_onTrayIconAdded: function(o, icon) {
let wmClass = (icon.wm_class || 'unknown').toLowerCase();
let role = STANDARD_TRAY_ICON_IMPLEMENTATIONS[wmClass];
if (role)
this.emit('status-icon-added', icon, role);
else
this.emit('message-icon-added', icon);
},
_onTrayIconRemoved: function(o, icon) {
let wmClass = (icon.wm_class || 'unknown').toLowerCase();
let role = STANDARD_TRAY_ICON_IMPLEMENTATIONS[wmClass];
if (role)
this.emit('status-icon-removed', icon);
else
this.emit('message-icon-removed', icon);
}
});
Signals.addSignalMethods(StatusIconDispatcher.prototype);

View File

@ -132,6 +132,9 @@ const Client = new Lang.Class({
let channel = channels[i];
let [targetHandle, targetHandleType] = channel.get_handle();
if (Shell.is_channel_invalidated(channel))
continue;
/* Only observe contact text channels */
if ((!(channel instanceof Tp.TextChannel)) ||
targetHandleType != Tp.HandleType.CONTACT)
@ -181,6 +184,9 @@ const Client = new Lang.Class({
continue;
}
if (Shell.is_channel_invalidated(channel))
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
@ -231,12 +237,19 @@ const Client = new Lang.Class({
let channel = channels[0];
let chanType = channel.get_channel_type();
if (Shell.is_channel_invalidated(channel)) {
Shell.decline_dispatch_op(context, '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
Shell.decline_dispatch_op(context, 'Unsupported channel type');
},
_approveTextChannel: function(account, conn, channel, dispatchOp, context) {
@ -469,9 +482,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();
@ -487,9 +500,40 @@ const ChatSource = new Lang.Class({
return this._iconBox;
},
createSecondaryIcon: function() {
let iconBox = new St.Bin();
iconBox.child = new St.Icon({ style_class: 'secondary-icon',
icon_type: St.IconType.FULLCOLOR });
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._setSummaryIcon(this.createIcon(this.ICON_SIZE));
this._notification.update(this._notification.title, null, { customContent: true });
},
open: function(notification) {
@ -534,7 +578,7 @@ const ChatSource = new Lang.Class({
this._pendingMessages.push(message);
}
this._updateCount();
this.countUpdated();
let showTimestamp = false;
@ -580,8 +624,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) {
@ -589,7 +642,7 @@ const ChatSource = new Lang.Class({
return;
this._pendingMessages.push(message);
this._updateCount();
this.countUpdated();
message = makeMessageFromTpMessage(message, NotificationDirection.RECEIVED);
this._notification.appendMessage(message);
@ -651,38 +704,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) {
@ -690,7 +719,7 @@ 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);
@ -709,7 +738,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',
@ -802,7 +831,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();
}
},
@ -919,19 +948,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);
@ -1024,9 +1040,9 @@ const ApproverSource = new Lang.Class({
this.parent();
},
createNotificationIcon: function() {
createIcon: function(size) {
return new St.Icon({ gicon: this._gicon,
icon_size: this.ICON_SIZE });
icon_size: size });
}
});

227
js/ui/unlockDialog.js Normal file
View File

@ -0,0 +1,227 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const AccountsService = imports.gi.AccountsService;
const Clutter = imports.gi.Clutter;
const Gdm = imports.gi.Gdm;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Signals = imports.signals;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog;
const ShellEntry = imports.ui.shellEntry;
const Tweener = imports.ui.tweener;
const UserMenu = imports.ui.userMenu;
const Batch = imports.gdm.batch;
const GdmUtil = imports.gdm.util;
// A widget showing the user avatar and name
const UserWidget = new Lang.Class({
Name: 'UserWidget',
_init: function(user) {
this._user = user;
this.actor = new St.BoxLayout({ style_class: 'unlock-dialog-user-name-container',
vertical: false });
this._avatar = new UserMenu.UserAvatarWidget(user);
this.actor.add(this._avatar.actor,
{ x_fill: true, y_fill: true });
this._label = new St.Label({ style_class: 'login-dialog-username' });
this.actor.add(this._label,
{ expand: true,
x_fill: true,
y_align: St.Align.MIDDLE });
this._userLoadedId = this._user.connect('notify::is-loaded',
Lang.bind(this, this._updateUser));
this._userChangedId = this._user.connect('changed',
Lang.bind(this, this._updateUser));
if (this._user.is_loaded)
this._updateUser();
},
destroy: function() {
if (this._userLoadedId != 0) {
this._user.disconnect(this._userLoadedId);
this._userLoadedId = 0;
}
if (this._userChangedId != 0) {
this._user.disconnect(this._userChangedId);
this._userChangedId = 0;
}
this.actor.destroy();
},
_updateUser: function() {
if (this._user.is_loaded)
this._label.text = this._user.get_real_name();
else
this._label.text = '';
this._avatar.update();
}
});
const UnlockDialog = new Lang.Class({
Name: 'UnlockDialog',
Extends: ModalDialog.ModalDialog,
_init: function(parentActor) {
this.parent({ shellReactive: true,
styleClass: 'login-dialog',
parentActor: parentActor
});
this._userManager = AccountsService.UserManager.get_default();
this._userName = GLib.get_user_name();
this._user = this._userManager.get_user(this._userName);
this._greeterClient = new Gdm.Client();
this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient, { reauthenticationOnly: true });
this._userVerifier.connect('reset', Lang.bind(this, this._reset));
this._userVerifier.connect('ask-question', Lang.bind(this, this._onAskQuestion));
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
this._userVerifier.connect('verification-failed', Lang.bind(this, this._onVerificationFailed));
this._userVerifier.connect('show-fingerprint-prompt', Lang.bind(this, this._showFingerprintPrompt));
this._userVerifier.connect('hide-fingerprint-prompt', Lang.bind(this, this._hideFingerprintPrompt));
this._userWidget = new UserWidget(this._user);
this.contentLayout.add_actor(this._userWidget.actor);
this._promptLayout = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
vertical: true });
this._promptLabel = new St.Label({ style_class: 'login-dialog-prompt-label' });
this._promptLayout.add(this._promptLabel,
{ x_align: St.Align.START });
this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
can_focus: true });
ShellEntry.addContextMenu(this._promptEntry);
this.setInitialKeyFocus(this._promptEntry);
this._promptEntry.clutter_text.connect('activate', Lang.bind(this, this._doUnlock));
this._promptLayout.add(this._promptEntry,
{ expand: true,
x_fill: true });
this.contentLayout.add_actor(this._promptLayout);
// 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.contentLayout.add_actor(this._promptFingerprintMessage);
this._okButton = { label: _("Unlock"),
action: Lang.bind(this, this._doUnlock),
default: true };
this.setButtons([this._okButton]);
this.setActionKey(Clutter.KEY_Escape, Lang.bind(this, this._escape));
this._updateOkButton(false);
this._reset();
let otherUserLabel = new St.Label({ text: _("Login as another user"),
style_class: 'login-dialog-not-listed-label' });
this._otherUserButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
can_focus: true,
child: otherUserLabel,
reactive: true,
x_align: St.Align.START,
x_fill: true });
this._otherUserButton.connect('clicked', Lang.bind(this, this._otherUserClicked));
this.dialogLayout.add(this._otherUserButton,
{ x_align: St.Align.START,
x_fill: false });
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
this.emit('loaded');
return false;
}));
},
_updateOkButton: function(sensitive) {
this._okButton.button.reactive = sensitive;
},
_reset: function() {
this._userVerifier.begin(this._userName, new Batch.Hold());
},
_onAskQuestion: function(verifier, serviceName, question, passwordChar) {
this._promptLabel.text = question;
this._promptEntry.text = '';
this._promptEntry.clutter_text.set_password_char(passwordChar);
this._promptEntry.menu.isPassword = passwordChar != '';
this._currentQuery = serviceName;
this._updateOkButton(true);
},
_showFingerprintPrompt: function() {
GdmUtil.fadeInActor(this._promptFingerprintMessage);
},
_hideFingerprintPrompt: function() {
GdmUtil.fadeOutActor(this._promptFingerprintMessage);
},
_doUnlock: function() {
if (!this._currentQuery)
return;
let query = this._currentQuery;
this._currentQuery = null;
this._updateOkButton(false);
this._userVerifier.answerQuery(query, this._promptEntry.text);
},
_onVerificationComplete: function() {
this._userVerifier.clear();
this.emit('unlocked');
},
_onVerificationFailed: function() {
this._userVerifier.cancel();
this.emit('failed');
},
_escape: function() {
this._onVerificationFailed();
},
_otherUserClicked: function(button, event) {
this._userManager.goto_login_session();
this._userVerifier.cancel();
this.emit('failed');
},
destroy: function() {
this._userVerifier.clear();
this.parent();
},
cancel: function() {
this._userVerifier.cancel(null);
this.destroy();
},
});

View File

@ -1,7 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const AccountsService = imports.gi.AccountsService;
const GdmGreeter = imports.gi.GdmGreeter;
const Gdm = imports.gi.Gdm;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
@ -16,7 +16,6 @@ const GnomeSession = imports.misc.gnomeSession;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const ScreenSaver = imports.misc.screenSaver;
const Util = imports.misc.util;
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
@ -41,6 +40,34 @@ const IMStatus = {
// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
// Copyright (C) 2008,2009 Red Hat, Inc.
const UserAvatarWidget = new Lang.Class({
Name: 'UserAvatarWidget',
_init: function(user) {
this._user = user;
this.actor = new St.Bin({ style_class: 'status-chooser-user-icon',
track_hover: true,
reactive: true });
},
update: function() {
let iconFile = this._user.get_icon_file();
if (!GLib.file_test(iconFile, GLib.FileTest.EXISTS))
iconFile = null;
if (iconFile) {
let file = Gio.File.new_for_path(iconFile);
this.actor.child = null;
this.actor.style = 'background-image: url("%s");'.format(iconFile);
} else {
this.actor.style = null;
this.actor.child = new St.Icon({ icon_name: 'avatar-default',
icon_type: St.IconType.SYMBOLIC,
icon_size: DIALOG_ICON_SIZE });
}
}
});
const IMStatusItem = new Lang.Class({
Name: 'IMStatusItem',
@ -108,7 +135,11 @@ const IMStatusChooserItem = new Lang.Class({
this.parent({ reactive: false,
style_class: 'status-chooser' });
this._iconBin = new St.Button({ style_class: 'status-chooser-user-icon' });
this._userManager = AccountsService.UserManager.get_default();
this._user = this._userManager.get_user(GLib.get_user_name());
this._avatar = new UserAvatarWidget(this._user);
this._iconBin = new St.Button({ child: this._avatar.actor });
this.addActor(this._iconBin);
this._iconBin.connect('clicked', Lang.bind(this,
@ -133,7 +164,7 @@ const IMStatusChooserItem = new Lang.Class({
item = new IMStatusItem(_("Busy"), 'user-busy');
this._combo.addMenuItem(item, IMStatus.BUSY);
item = new IMStatusItem(_("Hidden"), 'user-invisible');
item = new IMStatusItem(_("Invisible"), 'user-invisible');
this._combo.addMenuItem(item, IMStatus.HIDDEN);
item = new IMStatusItem(_("Away"), 'user-away');
@ -186,10 +217,6 @@ const IMStatusChooserItem = new Lang.Class({
}
}));
this._userManager = AccountsService.UserManager.get_default();
this._user = this._userManager.get_user(GLib.get_user_name());
this._userLoadedId = this._user.connect('notify::is-loaded',
Lang.bind(this,
this._updateUser));
@ -227,44 +254,12 @@ const IMStatusChooserItem = new Lang.Class({
},
_updateUser: function() {
let iconFile = null;
if (this._user.is_loaded) {
if (this._user.is_loaded)
this._name.label.set_text(this._user.get_real_name());
iconFile = this._user.get_icon_file();
if (!GLib.file_test(iconFile, GLib.FileTest.EXISTS))
iconFile = null;
} else {
this._name.label.set_text("");
}
if (iconFile)
this._setIconFromFile(iconFile);
else
this._setIconFromName('avatar-default');
},
this._name.label.set_text("");
_setIconFromFile: function(iconFile) {
this._iconBin.set_style('background-image: url("' + iconFile + '");' +
'background-size: contain;');
this._iconBin.child = null;
},
_setIconFromName: function(iconName) {
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();
}
this._avatar.update();
},
_statusForPresence: function(presence) {
@ -455,7 +450,6 @@ const UserMenuButton = new Lang.Class({
this._accountMgr = Tp.AccountManager.dup();
this._upClient = new UPowerGlib.Client();
this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy();
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._iconBox = new St.Bin();
@ -481,10 +475,8 @@ const UserMenuButton = new Lang.Class({
Lang.bind(this, this._updatePresenceIcon));
this._accountMgr.connect('account-enabled',
Lang.bind(this, this._onAccountEnabled));
this._accountMgr.connect('account-disabled',
Lang.bind(this, this._onAccountDisabled));
this._accountMgr.connect('account-removed',
Lang.bind(this, this._onAccountDisabled));
Lang.bind(this, this._onAccountRemoved));
this._accountMgr.prepare_async(null, Lang.bind(this,
function(mgr) {
let [presence, s, msg] = mgr.get_most_available_presence();
@ -525,6 +517,10 @@ const UserMenuButton = new Lang.Class({
this._updateLogout();
this._updateLockScreen();
this._updatesFile = Gio.File.new_for_path('/var/lib/PackageKit/prepared-update');
this._updatesMonitor = this._updatesFile.monitor(Gio.FileMonitorFlags.NONE, null);
this._updatesMonitor.connect('changed', Lang.bind(this, this._updateInstallUpdates));
// Whether shutdown is available or not depends on both lockdown
// settings (disable-log-out) and Polkit policy - the latter doesn't
// notify, so we update the menu item each time the menu opens or
@ -540,6 +536,12 @@ const UserMenuButton = new Lang.Class({
this._upClient.connect('notify::can-suspend', Lang.bind(this, this._updateSuspendOrPowerOff));
},
setLockedState: function(locked) {
if (locked)
this.menu.close();
this.actor.reactive = !locked;
},
_onDestroy: function() {
this._user.disconnect(this._userLoadedId);
this._user.disconnect(this._userChangedId);
@ -560,7 +562,7 @@ const UserMenuButton = new Lang.Class({
_updateSwitchUser: function() {
let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY);
let multiUser = this._userManager.can_switch() && this._userManager.has_multiple_users;
let multiSession = GdmGreeter.get_session_ids().length > 1;
let multiSession = Gdm.get_session_ids().length > 1;
this._loginScreenItem.label.set_text(multiUser ? _("Switch User")
: _("Switch Session"));
@ -570,7 +572,7 @@ const UserMenuButton = new Lang.Class({
_updateLogout: function() {
let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY);
let multiUser = this._userManager.has_multiple_users;
let multiSession = GdmGreeter.get_session_ids().length > 1;
let multiSession = Gdm.get_session_ids().length > 1;
this._logoutItem.actor.visible = allowLogout && (multiUser || multiSession);
},
@ -580,11 +582,17 @@ const UserMenuButton = new Lang.Class({
this._lockScreenItem.actor.visible = allowLockScreen;
},
_updateInstallUpdates: function() {
let haveUpdates = this._updatesFile.query_exists(null);
this._installUpdatesItem.actor.visible = haveUpdates && this._haveShutdown;
},
_updateHaveShutdown: function() {
this._session.CanShutdownRemote(Lang.bind(this,
function(result, error) {
if (!error) {
this._haveShutdown = result;
this._haveShutdown = result[0];
this._updateInstallUpdates();
this._updateSuspendOrPowerOff();
}
}));
@ -645,9 +653,11 @@ const UserMenuButton = new Lang.Class({
this._updateChangingPresence();
},
_onAccountDisabled: function(accountMgr, account) {
account.disconnect(account._changingId);
account._changingId = 0;
_onAccountRemoved: function(accountMgr, account) {
if (account._changingId) {
account.disconnect(account._changingId);
account._changingId = 0;
}
this._updateChangingPresence();
},
@ -692,13 +702,6 @@ const UserMenuButton = new Lang.Class({
this.menu.addMenuItem(item);
}
item = new PopupMenu.PopupAlternatingMenuItem(_("Power Off"),
_("Suspend"));
this.menu.addMenuItem(item);
item.connect('activate', Lang.bind(this, this._onSuspendOrPowerOffActivate));
this._suspendOrPowerOffItem = item;
this._updateSuspendOrPowerOff();
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
@ -712,13 +715,25 @@ const UserMenuButton = new Lang.Class({
this.menu.addMenuItem(item);
this._logoutItem = item;
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("Lock"));
item.connect('activate', Lang.bind(this, this._onLockScreenActivate));
this.menu.addMenuItem(item);
this._lockScreenItem = item;
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
item = new PopupMenu.PopupAlternatingMenuItem(_("Power Off"),
_("Suspend"));
this.menu.addMenuItem(item);
item.connect('activate', Lang.bind(this, this._onSuspendOrPowerOffActivate));
this._suspendOrPowerOffItem = item;
this._updateSuspendOrPowerOff();
item = new PopupMenu.PopupMenuItem(_("Install Updates & Restart"));
item.connect('activate', Lang.bind(this, this._onInstallUpdatesActivate));
this.menu.addMenuItem(item);
this._installUpdatesItem = item;
},
_updatePresenceStatus: function(item, event) {
@ -754,16 +769,13 @@ const UserMenuButton = new Lang.Class({
_onLockScreenActivate: function() {
Main.overview.hide();
this._screenSaverProxy.LockRemote();
Main.screenShield.lock(true);
},
_onLoginScreenActivate: function() {
Main.overview.hide();
// Ensure we only move to GDM after the screensaver has activated; in some
// OS configurations, the X server may block event processing on VT switch
this._screenSaverProxy.SetActiveRemote(true, Lang.bind(this, function() {
this._userManager.goto_login_session();
}));
Main.screenShield.lock(false);
this._userManager.goto_login_session();
},
_onQuitSessionActivate: function() {
@ -771,6 +783,13 @@ const UserMenuButton = new Lang.Class({
this._session.LogoutRemote(0);
},
_onInstallUpdatesActivate: function() {
Main.overview.hide();
Util.spawn(['pkexec', '/usr/libexec/pk-trigger-offline-update']);
this._session.RebootRemote();
},
_onSuspendOrPowerOffActivate: function() {
Main.overview.hide();
@ -778,10 +797,13 @@ const UserMenuButton = new Lang.Class({
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
this._session.ShutdownRemote();
} else {
// Ensure we only suspend after locking the screen
this._screenSaverProxy.LockRemote(Lang.bind(this, function() {
let tmpId = Main.screenShield.connect('lock-screen-shown', Lang.bind(this, function() {
Main.screenShield.disconnect(tmpId);
this._upClient.suspend_sync(null);
}));
Main.screenShield.lock(true);
}
}
});

View File

@ -73,8 +73,8 @@ const Source = new Lang.Class({
this.signalIDs = [];
},
createNotificationIcon : function() {
return this._app.create_icon_texture(this.ICON_SIZE);
createIcon : function(size) {
return this._app.create_icon_texture(size);
},
open : function(notification) {

View File

@ -15,65 +15,38 @@ const Tweener = imports.ui.tweener;
const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings';
const WINDOW_ANIMATION_TIME = 0.25;
const DIM_DESATURATION = 0.6;
const DIM_BRIGHTNESS = -0.1;
const DIM_TIME = 0.500;
const UNDIM_TIME = 0.250;
var dimShader = undefined;
function getDimShaderSource() {
if (!dimShader)
dimShader = Shell.get_file_contents_utf8_sync(global.datadir + '/shaders/dim-window.glsl');
return dimShader;
}
function getTopInvisibleBorder(metaWindow) {
let outerRect = metaWindow.get_outer_rect();
let inputRect = metaWindow.get_input_rect();
return outerRect.y - inputRect.y;
}
const WindowDimmer = new Lang.Class({
Name: 'WindowDimmer',
_init: function(actor) {
if (Clutter.feature_available(Clutter.FeatureFlags.SHADERS_GLSL)) {
this._effect = new Clutter.ShaderEffect({ shader_type: Clutter.ShaderType.FRAGMENT_SHADER });
this._effect.set_shader_source(getDimShaderSource());
} else {
this._effect = null;
}
this._desaturateEffect = new Clutter.DesaturateEffect();
this._brightnessEffect = new Clutter.BrightnessContrastEffect();
actor.add_effect(this._desaturateEffect);
actor.add_effect(this._brightnessEffect);
this.actor = actor;
this._dimFactor = 0.0;
},
set dimFraction(fraction) {
this._dimFraction = fraction;
if (this._effect == null)
return;
if (!Meta.prefs_get_attach_modal_dialogs()) {
this._effect.enabled = false;
return;
}
if (fraction > 0.01) {
Shell.shader_effect_set_double_uniform(this._effect, 'height', this.actor.get_height());
Shell.shader_effect_set_double_uniform(this._effect, 'fraction', fraction);
if (!this._effect.actor)
this.actor.add_effect(this._effect);
} else {
if (this._effect.actor)
this.actor.remove_effect(this._effect);
}
setEnabled: function(enabled) {
this._desaturateEffect.enabled = enabled;
this._brightnessEffect.enabled = enabled;
},
get dimFraction() {
return this._dimFraction;
set dimFactor(factor) {
this._dimFactor = factor;
this._desaturateEffect.set_factor(factor * DIM_DESATURATION);
this._brightnessEffect.set_brightness(factor * DIM_BRIGHTNESS);
},
_dimFraction: 0.0
get dimFactor() {
return this._dimFactor;
}
});
function getWindowDimmer(actor) {
@ -94,6 +67,7 @@ const WindowManager = new Lang.Class({
this._unmaximizing = [];
this._mapping = [];
this._destroying = [];
this._movingWindow = null;
this._dimmedWindows = [];
@ -125,6 +99,14 @@ const WindowManager = new Lang.Class({
Lang.bind(this, this._showWorkspaceSwitcher));
Meta.keybindings_set_custom_handler('switch-to-workspace-down',
Lang.bind(this, this._showWorkspaceSwitcher));
Meta.keybindings_set_custom_handler('move-to-workspace-left',
Lang.bind(this, this._showWorkspaceSwitcher));
Meta.keybindings_set_custom_handler('move-to-workspace-right',
Lang.bind(this, this._showWorkspaceSwitcher));
Meta.keybindings_set_custom_handler('move-to-workspace-up',
Lang.bind(this, this._showWorkspaceSwitcher));
Meta.keybindings_set_custom_handler('move-to-workspace-down',
Lang.bind(this, this._showWorkspaceSwitcher));
Meta.keybindings_set_custom_handler('switch-windows',
Lang.bind(this, this._startAppSwitcher));
Meta.keybindings_set_custom_handler('switch-group',
@ -142,11 +124,11 @@ const WindowManager = new Lang.Class({
Main.overview.connect('showing', Lang.bind(this, function() {
for (let i = 0; i < this._dimmedWindows.length; i++)
this._undimWindow(this._dimmedWindows[i], true);
this._undimWindow(this._dimmedWindows[i]);
}));
Main.overview.connect('hiding', Lang.bind(this, function() {
for (let i = 0; i < this._dimmedWindows.length; i++)
this._dimWindow(this._dimmedWindows[i], true);
this._dimWindow(this._dimmedWindows[i]);
}));
},
@ -158,12 +140,14 @@ const WindowManager = new Lang.Class({
this._animationBlockCount = Math.max(0, this._animationBlockCount - 1);
},
_shouldAnimate : function(actor) {
if (Main.overview.visible || this._animationBlockCount > 0)
_shouldAnimate: function() {
return !(Main.overview.visible || this._animationBlockCount > 0);
},
_shouldAnimateActor: function(actor) {
if (!this._shouldAnimate())
return false;
if (actor && (actor.meta_window.get_window_type() != Meta.WindowType.NORMAL))
return false;
return true;
return actor.meta_window.get_window_type() == Meta.WindowType.NORMAL;
},
_removeEffect : function(list, actor) {
@ -176,7 +160,7 @@ const WindowManager = new Lang.Class({
},
_minimizeWindow : function(shellwm, actor) {
if (!this._shouldAnimate(actor)) {
if (!this._shouldAnimateActor(actor)) {
shellwm.completed_minimize(actor);
return;
}
@ -260,43 +244,47 @@ const WindowManager = new Lang.Class({
window._dimmed = true;
this._dimmedWindows.push(window);
if (!Main.overview.visible)
this._dimWindow(window, true);
this._dimWindow(window);
} else if (!shouldDim && window._dimmed) {
window._dimmed = false;
this._dimmedWindows = this._dimmedWindows.filter(function(win) {
return win != window;
});
if (!Main.overview.visible)
this._undimWindow(window, true);
this._undimWindow(window);
}
},
_dimWindow: function(window, animate) {
_dimWindow: function(window) {
let actor = window.get_compositor_private();
if (!actor)
return;
if (animate)
Tweener.addTween(getWindowDimmer(actor),
{ dimFraction: 1.0,
time: DIM_TIME,
transition: 'linear'
});
else
getWindowDimmer(actor).dimFraction = 1.0;
let dimmer = getWindowDimmer(actor);
let enabled = Meta.prefs_get_attach_modal_dialogs();
dimmer.setEnabled(enabled);
if (!enabled)
return;
Tweener.addTween(dimmer,
{ dimFactor: 1.0,
time: DIM_TIME,
transition: 'linear'
});
},
_undimWindow: function(window, animate) {
_undimWindow: function(window) {
let actor = window.get_compositor_private();
if (!actor)
return;
if (animate)
Tweener.addTween(getWindowDimmer(actor),
{ dimFraction: 0.0,
time: UNDIM_TIME,
transition: 'linear'
});
else
getWindowDimmer(actor).dimFraction = 0.0;
let dimmer = getWindowDimmer(actor);
let enabled = Meta.prefs_get_attach_modal_dialogs();
dimmer.setEnabled(enabled);
if (!enabled)
return;
Tweener.addTween(dimmer,
{ dimFactor: 0.0,
time: UNDIM_TIME,
transition: 'linear'
});
},
_mapWindow : function(shellwm, actor) {
@ -316,28 +304,12 @@ const WindowManager = new Lang.Class({
}));
if (actor.meta_window.is_attached_dialog()) {
this._checkDimming(actor.get_meta_window().get_transient_for());
if (this._shouldAnimate()) {
actor.set_scale(1.0, 0.0);
actor.show();
this._mapping.push(actor);
Tweener.addTween(actor,
{ scale_y: 1,
time: WINDOW_ANIMATION_TIME,
transition: "easeOutQuad",
onComplete: this._mapWindowDone,
onCompleteScope: this,
onCompleteParams: [shellwm, actor],
onOverwrite: this._mapWindowOverwrite,
onOverwriteScope: this,
onOverwriteParams: [shellwm, actor]
});
if (!this._shouldAnimate()) {
shellwm.completed_map(actor);
return;
}
shellwm.completed_map(actor);
return;
}
if (!this._shouldAnimate(actor)) {
} else if (!this._shouldAnimateActor(actor)) {
shellwm.completed_map(actor);
return;
}
@ -393,7 +365,7 @@ const WindowManager = new Lang.Class({
return;
}
actor.set_scale(1.0, 1.0);
actor.opacity = 255;
actor.show();
this._destroying.push(actor);
@ -403,7 +375,7 @@ const WindowManager = new Lang.Class({
}));
Tweener.addTween(actor,
{ scale_y: 0,
{ opacity: 0,
time: WINDOW_ANIMATION_TIME,
transition: "easeOutQuad",
onComplete: this._destroyWindowDone,
@ -465,11 +437,13 @@ const WindowManager = new Lang.Class({
this._switchData = switchData;
switchData.inGroup = new Clutter.Group();
switchData.outGroup = new Clutter.Group();
switchData.movingWindowBin = new Clutter.Group();
switchData.windows = [];
let wgroup = global.window_group;
wgroup.add_actor(switchData.inGroup);
wgroup.add_actor(switchData.outGroup);
wgroup.add_actor(switchData.movingWindowBin);
for (let i = 0; i < windows.length; i++) {
let window = windows[i];
@ -477,7 +451,12 @@ const WindowManager = new Lang.Class({
if (!window.meta_window.showing_on_its_workspace())
continue;
if (window.get_workspace() == from) {
if (this._movingWindow && window.meta_window == this._movingWindow) {
switchData.movingWindow = { window: window,
parent: window.get_parent() };
switchData.windows.push(switchData.movingWindow);
window.reparent(switchData.movingWindowBin);
} else if (window.get_workspace() == from) {
switchData.windows.push({ window: window,
parent: window.get_parent() });
window.reparent(switchData.outGroup);
@ -492,6 +471,8 @@ const WindowManager = new Lang.Class({
switchData.inGroup.set_position(-xDest, -yDest);
switchData.inGroup.raise_top();
switchData.movingWindowBin.raise_top();
Tweener.addTween(switchData.outGroup,
{ x: xDest,
y: yDest,
@ -529,6 +510,10 @@ const WindowManager = new Lang.Class({
Tweener.removeTweens(switchData.outGroup);
switchData.inGroup.destroy();
switchData.outGroup.destroy();
switchData.movingWindowBin.destroy();
if (this._movingWindow)
this._movingWindow = null;
shellwm.completed_switch_workspace();
},
@ -536,7 +521,7 @@ const WindowManager = new Lang.Class({
_startAppSwitcher : function(display, screen, window, binding) {
/* prevent a corner case where both popups show up at once */
if (this._workspaceSwitcherPopup != null)
this._workspaceSwitcherPopup.actor.hide();
this._workspaceSwitcherPopup.destroy();
let tabPopup = new AltTab.AltTabPopup();
@ -560,76 +545,56 @@ const WindowManager = new Lang.Class({
if (screen.n_workspaces == 1)
return;
if (this._workspaceSwitcherPopup == null)
this._workspaceSwitcherPopup = new WorkspaceSwitcherPopup.WorkspaceSwitcherPopup();
let [action,,,direction] = binding.get_name().split('-');
let direction = Meta.MotionDirection[direction.toUpperCase()];
let newWs;
if (binding.get_name() == 'switch-to-workspace-up')
this.actionMoveWorkspaceUp();
else if (binding.get_name() == 'switch-to-workspace-down')
this.actionMoveWorkspaceDown();
// left/right would effectively act as synonyms for up/down if we enabled them;
// but that could be considered confusing.
// else if (binding.get_name() == 'switch-to-workspace-left')
// this.actionMoveWorkspaceLeft();
// else if (binding.get_name() == 'switch-to-workspace-right')
// this.actionMoveWorkspaceRight();
if (direction != Meta.MotionDirection.UP &&
direction != Meta.MotionDirection.DOWN)
return;
if (action == 'switch')
newWs = this.actionMoveWorkspace(direction);
else
newWs = this.actionMoveWindow(window, direction);
if (!Main.overview.visible) {
if (this._workspaceSwitcherPopup == null) {
this._workspaceSwitcherPopup = new WorkspaceSwitcherPopup.WorkspaceSwitcherPopup();
this._workspaceSwitcherPopup.connect('destroy', Lang.bind(this, function() {
this._workspaceSwitcherPopup = null;
}));
}
this._workspaceSwitcherPopup.display(direction, newWs.index());
}
},
actionMoveWorkspaceLeft: function() {
let rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL);
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
let indexToActivate = activeWorkspaceIndex;
if (rtl && activeWorkspaceIndex < global.screen.n_workspaces - 1)
indexToActivate++;
else if (!rtl && activeWorkspaceIndex > 0)
indexToActivate--;
actionMoveWorkspace: function(direction) {
let activeWorkspace = global.screen.get_active_workspace();
let toActivate = activeWorkspace.get_neighbor(direction);
if (indexToActivate != activeWorkspaceIndex)
global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
if (activeWorkspace != toActivate)
toActivate.activate(global.get_current_time());
if (!Main.overview.visible)
this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.UP, indexToActivate);
return toActivate;
},
actionMoveWorkspaceRight: function() {
let rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL);
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
let indexToActivate = activeWorkspaceIndex;
if (rtl && activeWorkspaceIndex > 0)
indexToActivate--;
else if (!rtl && activeWorkspaceIndex < global.screen.n_workspaces - 1)
indexToActivate++;
actionMoveWindow: function(window, direction) {
let activeWorkspace = global.screen.get_active_workspace();
let toActivate = activeWorkspace.get_neighbor(direction);
if (indexToActivate != activeWorkspaceIndex)
global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
if (activeWorkspace != toActivate) {
// This won't have any effect for "always sticky" windows
// (like desktop windows or docks)
if (!Main.overview.visible)
this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.DOWN, indexToActivate);
this._movingWindow = window;
window.change_workspace(toActivate);
global.display.clear_mouse_mode();
toActivate.activate_with_focus (window, global.get_current_time());
}
return toActivate;
},
actionMoveWorkspaceUp: function() {
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
let indexToActivate = activeWorkspaceIndex;
if (activeWorkspaceIndex > 0)
indexToActivate--;
if (indexToActivate != activeWorkspaceIndex)
global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
if (!Main.overview.visible)
this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.UP, indexToActivate);
},
actionMoveWorkspaceDown: function() {
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
let indexToActivate = activeWorkspaceIndex;
if (activeWorkspaceIndex < global.screen.n_workspaces - 1)
indexToActivate++;
if (indexToActivate != activeWorkspaceIndex)
global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
if (!Main.overview.visible)
this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.DOWN, indexToActivate);
}
});

View File

@ -301,7 +301,8 @@ const WindowClone = new Lang.Class({
if (!this._zoomLightbox)
this._zoomLightbox = new Lightbox.Lightbox(Main.uiGroup,
{ fadeTime: LIGHTBOX_FADE_TIME });
{ fadeInTime: LIGHTBOX_FADE_TIME,
fadeOutTime: LIGHTBOX_FADE_TIME });
this._zoomLightbox.show();
this._zoomLocalOrig = new ScaledPoint(this.actor.x, this.actor.y, this.actor.scale_x, this.actor.scale_y);
@ -1121,25 +1122,6 @@ const Workspace = new Lang.Class({
}
},
_showAllOverlays: function() {
let currentWorkspace = global.screen.get_active_workspace();
for (let i = 0; i < this._windows.length; i++) {
let clone = this._windows[i];
let overlay = this._windowOverlays[i];
this._showWindowOverlay(clone, overlay,
this.metaWorkspace == null || this.metaWorkspace == currentWorkspace);
}
},
_hideAllOverlays: function() {
for (let i = 0; i < this._windows.length; i++) {
let clone = this._windows[i];
let overlay = this._windowOverlays[i];
if (overlay)
overlay.hide();
}
},
_delayedWindowRepositioning: function() {
if (this._windowIsZooming)
return true;
@ -1339,6 +1321,10 @@ const Workspace = new Lang.Class({
// Position and scale the windows.
for (let i = 0; i < this._windows.length; i++) {
let clone = this._windows[i];
let overlay = this._windowOverlays[i];
if (overlay)
overlay.hide();
clone.zoomFromOverview();
@ -1363,8 +1349,6 @@ const Workspace = new Lang.Class({
});
}
}
this._hideAllOverlays();
},
destroy : function() {

View File

@ -3,18 +3,17 @@
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
const Main = imports.ui.main;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const ANIMATION_TIME = 0.1;
const DISPLAY_TIMEOUT = 600;
const UP = -1;
const DOWN = 1;
const WorkspaceSwitcherPopup = new Lang.Class({
Name: 'WorkspaceSwitcherPopup',
@ -43,12 +42,14 @@ const WorkspaceSwitcherPopup = new Lang.Class({
this.actor.add_actor(this._container);
this._redraw();
this._position();
this._redisplay();
this.actor.hide();
this._globalSignals = [];
this._globalSignals.push(global.screen.connect('workspace-added', Lang.bind(this, this._redisplay)));
this._globalSignals.push(global.screen.connect('workspace-removed', Lang.bind(this, this._redisplay)));
this._timeoutId = Mainloop.timeout_add(DISPLAY_TIMEOUT, Lang.bind(this, this._onTimeout));
},
@ -104,15 +105,15 @@ const WorkspaceSwitcherPopup = new Lang.Class({
}
},
_redraw : function(direction, activeWorkspaceIndex) {
_redisplay: function() {
this._list.destroy_all_children();
for (let i = 0; i < global.screen.n_workspaces; i++) {
let indicator = null;
if (i == activeWorkspaceIndex && direction == UP)
if (i == this._activeWorkspaceIndex && this._direction == Meta.MotionDirection.UP)
indicator = new St.Bin({ style_class: 'ws-switcher-active-up' });
else if(i == activeWorkspaceIndex && direction == DOWN)
else if(i == this._activeWorkspaceIndex && this._direction == Meta.MotionDirection.DOWN)
indicator = new St.Bin({ style_class: 'ws-switcher-active-down' });
else
indicator = new St.Bin({ style_class: 'ws-switcher-box' });
@ -120,13 +121,13 @@ const WorkspaceSwitcherPopup = new Lang.Class({
this._list.add_actor(indicator);
}
},
_position: function() {
let primary = Main.layoutManager.primaryMonitor;
this._container.x = primary.x + Math.floor((primary.width - this._container.width) / 2);
let [containerMinHeight, containerNatHeight] = this._container.get_preferred_height(global.screen_width);
let [containerMinWidth, containerNatWidth] = this._container.get_preferred_width(containerNatHeight);
this._container.x = primary.x + Math.floor((primary.width - containerNatWidth) / 2);
this._container.y = primary.y + Main.panel.actor.height +
Math.floor(((primary.height - Main.panel.actor.height) - this._container.height) / 2);
Math.floor(((primary.height - Main.panel.actor.height) - containerNatHeight) / 2);
},
_show : function() {
@ -134,12 +135,14 @@ const WorkspaceSwitcherPopup = new Lang.Class({
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
this._position();
this.actor.show();
},
display : function(direction, activeWorkspaceIndex) {
this._redraw(direction, activeWorkspaceIndex);
this._direction = direction;
this._activeWorkspaceIndex = activeWorkspaceIndex;
this._redisplay();
if (this._timeoutId != 0)
Mainloop.source_remove(this._timeoutId);
this._timeoutId = Mainloop.timeout_add(DISPLAY_TIMEOUT, Lang.bind(this, this._onTimeout));
@ -152,8 +155,22 @@ const WorkspaceSwitcherPopup = new Lang.Class({
Tweener.addTween(this._container, { opacity: 0.0,
time: ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: function() { this.actor.hide(); },
onComplete: function() { this.destroy(); },
onCompleteScope: this
});
},
destroy: function() {
if (this._timeoutId)
Mainloop.source_remove(this._timeoutId);
this._timeoutId = 0;
for (let i = 0; i < this._globalSignals.length; i++)
global.screen.disconnect(this._globalSignals[i]);
this.actor.destroy();
this.emit('destroy');
}
});
Signals.addSignalMethods(WorkspaceSwitcherPopup.prototype);

View File

@ -187,7 +187,7 @@ const WorkspacesView = new Lang.Class({
activeWorkspace.actor.raise_top();
this.actor.remove_clip(this._x, this._y, this._width, this._height);
this.actor.remove_clip();
for (let w = 0; w < this._workspaces.length; w++)
this._workspaces[w].zoomFromOverview();
@ -1048,10 +1048,10 @@ const WorkspacesDisplay = new Lang.Class({
_onScrollEvent: function (actor, event) {
switch ( event.get_scroll_direction() ) {
case Clutter.ScrollDirection.UP:
Main.wm.actionMoveWorkspaceUp();
Main.wm.actionMoveWorkspace(Meta.MotionDirection.UP);
break;
case Clutter.ScrollDirection.DOWN:
Main.wm.actionMoveWorkspaceDown();
Main.wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
break;
}
}

View File

@ -1,6 +1,6 @@
data/gnome-shell.desktop.in.in
data/gnome-shell-extension-prefs.desktop.in.in
data/org.gnome.shell.gschema.xml.in
data/org.gnome.shell.gschema.xml.in.in
js/extensionPrefs/main.js
js/gdm/loginDialog.js
js/gdm/powerMenu.js
@ -9,10 +9,10 @@ js/ui/appDisplay.js
js/ui/appFavorites.js
js/ui/autorunManager.js
js/ui/calendar.js
js/ui/contactDisplay.js
js/ui/dash.js
js/ui/dateMenu.js
js/ui/endSessionDialog.js
js/ui/extensionDownloader.js
js/ui/extensionSystem.js
js/ui/keyboard.js
js/ui/keyringPrompt.js
@ -37,10 +37,12 @@ js/ui/status/network.js
js/ui/status/power.js
js/ui/status/volume.js
js/ui/telepathyClient.js
js/ui/unlockDialog.js
js/ui/userMenu.js
js/ui/viewSelector.js
js/ui/wanda.js
js/ui/windowAttentionHandler.js
src/calendar-server/evolution-calendar.desktop.in.in
src/gvc/gvc-mixer-control.c
src/main.c
src/shell-app.c

View File

@ -1,3 +1,2 @@
data/gnome-shell.desktop.in
data/gnome-shell-extension-prefs.desktop.in
data/org.gnome.shell.evolution.calendar.gschema.xml.in
src/calendar-server/evolution-calendar.desktop.in

876
po/ar.po

File diff suppressed because it is too large Load Diff

1037
po/as.po

File diff suppressed because it is too large Load Diff

692
po/be.po

File diff suppressed because it is too large Load Diff

678
po/bg.po

File diff suppressed because it is too large Load Diff

1212
po/el.po

File diff suppressed because it is too large Load Diff

844
po/es.po

File diff suppressed because it is too large Load Diff

941
po/fa.po

File diff suppressed because it is too large Load Diff

799
po/gl.po

File diff suppressed because it is too large Load Diff

929
po/gu.po

File diff suppressed because it is too large Load Diff

857
po/he.po

File diff suppressed because it is too large Load Diff

1021
po/hu.po

File diff suppressed because it is too large Load Diff

478
po/it.po

File diff suppressed because it is too large Load Diff

968
po/kk.po

File diff suppressed because it is too large Load Diff

View File

@ -1705,11 +1705,11 @@ msgstr ""
#: ../js/ui/wanda.js:128
#, c-format
msgid "%s the Oracle says"
msgstr "Orakulė sako %s"
msgstr "Orakulė %s sako"
#: ../js/ui/wanda.js:168
msgid "Your favorite Easter Egg"
msgstr "Jūsų mėgstamiausias Velykinis kiaušinis"
msgstr "Jūsų mėgstamiausias velykinis kiaušinis"
#: ../js/ui/windowAttentionHandler.js:19
#, c-format

731
po/nb.po

File diff suppressed because it is too large Load Diff

1391
po/nn.po

File diff suppressed because it is too large Load Diff

717
po/pa.po

File diff suppressed because it is too large Load Diff

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