Compare commits

..

309 Commits

Author SHA1 Message Date
b1051365fa Bump version to 3.7.5
Update NEWS.
2013-02-06 01:12:32 +01:00
a935cd0c5d build: Update POTFILES.in 2013-02-06 01:06:16 +01:00
62ce90795f userMenu: Remove explicit screen lock on suspend
This is now handled by the screen shield itself.

https://bugzilla.gnome.org/show_bug.cgi?id=686482
2013-02-06 01:01:52 +01:00
6bcad45392 screenShield: Use inhibitors to lock screen on suspend
If the screen lock is enabled, lock the screen before suspension.
When using systemd, this will cover both explicitly suspending from
the user menu and suspension initiated by g-s-d (lid close, power
button).

https://bugzilla.gnome.org/show_bug.cgi?id=686482
2013-02-06 01:01:52 +01:00
8747c0c58d loginManager: Add inhibitor API
If screen locking is enabled, the screen shield should drop down
on suspend. Currently this is achieved by either explicitly locking
the screen (when selecting "Suspend" from the user menu) or by
relying on g-s-d delaying the suspension enough time for the shield
to get into place (lid close, power button).
Systemd inhibitors offer a safer way to ensure that the screen is
locked before going to sleep, so add a small abstraction for them
in the loginManager - with inhibitors being a systemd-only feature,
the ConsoleKit path only has a dummy implementation.

https://bugzilla.gnome.org/show_bug.cgi?id=686482
2013-02-06 01:01:52 +01:00
b91d9c2867 loginManager: Make suspend() a NOP in the ConsoleKit path
UPower will remove its suspend support eventually, and g-s-d already
depends on logind for power management.

https://bugzilla.gnome.org/show_bug.cgi?id=693162
2013-02-06 01:01:52 +01:00
140935bca3 shell-docs: remove leftover mobile-providers include 2013-02-06 01:33:51 +02:00
67c7abaac3 main: Fix typo
This fixes a regression from commit fcc32fca.
2013-02-05 20:42:27 +01:00
11bd7dc7de scroll-view-fade: Remove explicit GLSL version
Cogl sets this for us since commit 2701b93f159bf2d3387cedf2d06fe921ad5641f3.

Setting it twice is illegal and causes compile failures:
error C0204: version directive must be first statement and may not be repeated.
2013-02-05 20:32:08 +01:00
406f65cb23 [l10n] Updated German translation 2013-02-05 20:31:22 +01:00
2344706462 extensionSystem: Allow extensions to provide mode-specific styling
Allow extensions to integrate with modes that provide a distinct
style by providing a mode-specific stylesheet.

https://bugzilla.gnome.org/show_bug.cgi?id=693219
2013-02-05 19:54:17 +01:00
fcc32fca7e sessionMode: Add stylesheetName property
This will allow to visually differentiate modes by providing
separate styling.

https://bugzilla.gnome.org/show_bug.cgi?id=693219
2013-02-05 19:54:17 +01:00
7f587fd4da messageTray: Add 'source-removed' signal
While it is possible to keep track of removed sources by tracking
their summary items' actor:.destroy signal, a dedicated signal
mirroring the existing 'source-added' one is more convenient.

https://bugzilla.gnome.org/show_bug.cgi?id=693220
2013-02-05 19:54:17 +01:00
f47c0601ce panel: Add :overview pseudo class while in overview
The panel used to provide an .in-overview class which was removed after
the theme stopped using it. Classic mode should use a different top bar
style in the overview, so bring it back (but use a pseudo class this
time for consistency with MessageTray and ActivitiesButton).

https://bugzilla.gnome.org/show_bug.cgi?id=693218
2013-02-05 19:54:17 +01:00
3d638c692e bluetooth: use "Set Up" instead of "Set up"
Capitalize prepositions if they are part of a verb phrase

https://bugzilla.gnome.org/show_bug.cgi?id=689589
2013-02-05 13:21:13 -05:00
e6ef3ea24f power: consistently use Title Case
https://bugzilla.gnome.org/show_bug.cgi?id=689589
2013-02-05 13:20:55 -05:00
8dd880d0c8 mobile-providers: use libnm-gtk to work with mobile providers
This commit removes all the code in charge of playing with the database of
mobile providers, which was originally included in order to perform
MCCMNC->OperatorName and SID->OperatorName conversions.

This logic is now exposed by libnm-gtk.

https://bugzilla.gnome.org/show_bug.cgi?id=688943
2013-02-05 17:59:39 +01:00
393a3a079f Updated Spanish translation 2013-02-05 17:13:00 +01:00
ceb035e9b3 network: treat LTE-only modems as GSM ones
LTE-only modems need to be treated as GSM/HSPA modems, as they all are 3GPP
modems and they all need the same kind of configuration (APN, user, password,
PIN...).

https://bugzilla.gnome.org/show_bug.cgi?id=688144
2013-02-05 15:49:39 +01:00
e38570437e network: add support for the new 'ModemManager1' interface
ModemManager >= 0.7 comes with a new DBus interface. This patch makes the shell
work with the new interface if the modem is detected as being exposed by the new
ModemManager (based on the device.udi string reported by NM).

https://bugzilla.gnome.org/show_bug.cgi?id=687359.
2013-02-05 15:39:38 +01:00
f0203d1f19 general: Use & instead of 'and' for Settings panels
UI consistency follow up from bug 676562

https://bugzilla.gnome.org/show_bug.cgi?id=689590
2013-02-05 08:54:16 -05:00
e0c5a61be5 loginManager: Remove sessionActive property
It is unused after switching to GnomeSession:SessionIsActive, so
remove it together with shell_session_is_active_for_systemd().

https://bugzilla.gnome.org/show_bug.cgi?id=693161
2013-02-05 00:01:33 +01:00
526a16298c Replace LoginManager:sessionActive with GnomeSession:SessionIsActive
Gnome session started to track the session's active state a while
ago, so use that instead of our own ConsoleKit/logind abstraction
in LoginManager.

https://bugzilla.gnome.org/show_bug.cgi?id=693161
2013-02-05 00:01:33 +01:00
443f02cd92 [l10n] Updated Italian translation. 2013-02-04 18:14:28 +01:00
b682c8e052 main: Move KeybindingMode into Shell
Having the definition in C instead of Javascript allows sharing
the corresponding header with gnome-settings-daemon.

https://bugzilla.gnome.org/show_bug.cgi?id=643111
2013-02-04 16:48:40 +01:00
d07c8dcd9c volume: Suppress a critical warning
gvc_mixer_stream_get_port() emits a warning in the no-ports case,
which we can avoid by rearranging the check for that case.

https://bugzilla.gnome.org/show_bug.cgi?id=693049
2013-02-04 12:42:36 +01:00
9bcce4f271 Fix typo in gnome-shell-extension-prefs
Environment variables should be checked for zero length,
instead of non-zero. This prevented the script to run
correctly in a jhbuild shell for example.

https://bugzilla.gnome.org/show_bug.cgi?id=693031
2013-02-04 12:00:42 +01:00
eab497a814 layout: Call _startupAnimation() on init
Commit 8e231cb2ec inadvertently removed
it.

https://bugzilla.gnome.org/show_bug.cgi?id=693067
2013-02-04 11:23:38 +01:00
a8c8df6ee8 StImText: don't translate keyboard state again
Clutter translates keyboard state internally, and clears the lock bits
from modifier state, so translating again results in the wrong keysym.
Given that Clutter already gives us a fine keysym, we don't need this.

https://bugzilla.gnome.org/show_bug.cgi?id=692586
2013-02-03 16:45:28 +01:00
b3549f421d UserMenu: don't show a lock icon when we're not actually locked
The screen shield can now be active (forcing the lock-screen session mode)
without being locked.

https://bugzilla.gnome.org/show_bug.cgi?id=693007
2013-02-03 14:59:58 +01:00
ae0accb5a4 ScreenShield: separate notifications for active and locked
Now that we have an explicit active-but-not-locked state, we should
use different signals to notify changes. lock-status-changed is
renamed to active-changed, and a new locked-changed is introduced.

https://bugzilla.gnome.org/show_bug.cgi?id=693007
2013-02-03 14:59:58 +01:00
d367566db9 Updated Traditional Chinese translation(Hong Kong and Taiwan) 2013-02-02 18:39:58 +08:00
35812208e7 Updated Hebrew translation. 2013-02-02 12:06:39 +02:00
afaff724f6 Updated Uyghur translation
Signed-off-by: Gheyret Kenji <gheyret@gmail.com>
2013-02-02 13:19:42 +09:00
ec8dfb0cad doap: add myself to doap file
This is so hopefully darkxst's mango request will show
up for me to approve.
2013-02-01 17:30:35 -05:00
7798da8dff layout: Remove an unused variable
The tray barrier was removed when the hot corner to activate the message
tray was removed.

https://bugzilla.gnome.org/show_bug.cgi?id=677215
2013-02-01 12:55:01 -05:00
89a49ce72e windowManager: Respect icon geometry when minimizing
When using a dock or window-list with the shell, it makes sense for
us to minimize to the location requested rather than the activities
button.

https://bugzilla.gnome.org/show_bug.cgi?id=692997
2013-02-01 17:38:33 +01:00
655dce6a4b st-theme-node: Recalculate properties on stylesheet changes
As theme nodes keep a cache of matched properties, we need to make
sure to update it when the list of stylesheets changes. In particular
this fixes a regression from commit dc2ec0a8f9, which caused
extensions with stylesheets to crash the shell when re-enabled (for
instances when coming back from the lock screen).

https://bugzilla.gnome.org/show_bug.cgi?id=692994
2013-02-01 17:28:40 +01:00
2cf403a3e9 st-theme: Add "custom-stylesheets-changed" signal
StThemeNodes cache matched properties from stylesheets, so when the
list of custom stylesheets changes, the node may miss better matches
(when a stylesheet was added) or have pointers to invalid memory in
the list (when a stylesheet was removed).
In order to allow theme nodes to listen for stylesheet changes, add
an appropriate signal to StTheme.

https://bugzilla.gnome.org/show_bug.cgi?id=692994
2013-02-01 17:28:39 +01:00
4351ad2414 Updated Dutch translation 2013-02-01 17:00:13 +01:00
fd2944fa8d userMenu: Update logout item when always-show-log-out changes
https://bugzilla.gnome.org/show_bug.cgi?id=692996
2013-02-01 14:56:28 +01:00
f4626cf8ad workspace: Make previews of minimized windows translucent
Because the comment says so! Seriously, Jakub suggested this after
bug 682887 brought back a rebranded default shortcut for minimize.

https://bugzilla.gnome.org/show_bug.cgi?id=692999
2013-02-01 14:54:38 +01:00
65bf0d20e7 keyboard: Fix a warning
While we handle the case where ibus_bus_get_global_engine() returns
NULL, this case actually generates an exception we have to catch to
avoid some (harmless) console spam.

https://bugzilla.gnome.org/show_bug.cgi?id=692995
2013-02-01 13:06:00 +01:00
2f496de98a Updated Polish translation 2013-01-31 20:03:14 +01:00
644b8304ab ScreenShield: fix wrong signal value in ActiveChanged
ActiveChanged should be about active, not locked.

https://bugzilla.gnome.org/show_bug.cgi?id=691964
2013-01-31 18:25:31 +01:00
5c4570fcaa ScreenShield: fix regression in GDM greeter
The initial state of the lock screen is HIDDEN, and hideLockScreen
enforces that now, so reflect it in Clutter state too.

https://bugzilla.gnome.org/show_bug.cgi?id=692948
2013-01-31 16:57:38 +01:00
ac0bd3b116 Fix bad rebase and regression in 3f6f597 2013-01-31 16:07:16 +01:00
7ad881d0ec ScreenShield: emit lock-status-changed at the end of animation for manual locking too
gnome-settings-daemon uses lock-status-changed/ActiveChanged to drive
screen blanking, so must wait for the animation end before emitting it.

https://bugzilla.gnome.org/show_bug.cgi?id=691964
2013-01-31 14:10:21 +01:00
9a25224890 ScreenShield: lower the shield when the user is idle but before locking
In time span between idle and lock the shield should behave like autologin,
but should prevent accidental reactivation (for example when using a touch
screen) by showing the curtain.

https://bugzilla.gnome.org/show_bug.cgi?id=692560
2013-01-31 14:10:20 +01:00
3f6f597093 ScreenShield: only emit ActiveChanged at the end of the idle fading
gnome-settings-daemon wants to use ActiveChanged to drive screen
blanking policies.
I also added two big comments that should cover all cases, to clear
up what's happening when the idle timers fire.

https://bugzilla.gnome.org/show_bug.cgi?id=691964
2013-01-31 14:07:26 +01:00
9ab22fe551 ScreenShield: fix fallout from 8cb3884
We must remove music notifications before we're destroyed, otherwise
they get destroyed with us.
Also, integrate a review comment I previously forgot.

https://bugzilla.gnome.org/show_bug.cgi?id=685926
2013-01-31 13:45:57 +01:00
9794e71a86 Add policy for builtin message sources
Some notifications, despite being emitted by shell code, should appear
to be from application or "separable" system components. Do that by
associating them with a notification-daemon policy.

Note that for this to look really good, empathy should rename itself
to Chat.

https://bugzilla.gnome.org/show_bug.cgi?id=685926
2013-01-31 13:20:08 +01:00
8cb3884fae ScreenShield: decouple detailed notifications from resident notifications
The designs says that only music notifications should be shown in full
in the screenshield, the others should be either shown as a summary or
with very light details.

https://bugzilla.gnome.org/show_bug.cgi?id=685926
2013-01-31 13:20:08 +01:00
098bd4509b MessageTray: introduce configurable per-source notification policy
Allow message tray sources to provide a NotificationPolicy object,
that will configure how and if the source is displayed. For notification
daemon sources, this object is hooked to GSettings.

https://bugzilla.gnome.org/show_bug.cgi?id=685926
2013-01-31 13:20:08 +01:00
4dc5bac72f NotificationDaemon: improve associating sources with applications
Try harder to find a ShellApp for a notification source, by looking
at the PID, tray icon wm_class and desktop-entry libnotify hint.

https://bugzilla.gnome.org/show_bug.cgi?id=685926
2013-01-31 13:20:08 +01:00
b150869b51 MessageTray: clean up source tracking
Use the new Hash.Map class, and store signal connections along with
the source and summaryItem. This allows to remove sources without destroying
them.

https://bugzilla.gnome.org/show_bug.cgi?id=685926
2013-01-31 13:20:08 +01:00
daf8be9f78 Add a new Hash module
A simple implementation of the ES6 Map proposal, internally
done as a hash table, using System.addressOf() to support keys that
are arbitrary objects.
Should help replacing linear searches in various places around the shell.

https://bugzilla.gnome.org/show_bug.cgi?id=685926
2013-01-31 13:20:08 +01:00
d14cf1e80b Updated Basque language 2013-01-30 22:02:44 +01:00
605d34008e Updated Basque language 2013-01-30 22:00:50 +01:00
d3135414cc Updated Norwegian bokmål translation 2013-01-29 18:29:45 +01:00
b3f29b46cf NetworkMenu: defer calling syncSectionTitle until after pushing the device
syncSectionTitle looks at device list for the section, to understand if
the section should be visible or not, so obviously it needs to see the
new device.

I wonder when this broke.

https://bugzilla.gnome.org/show_bug.cgi?id=692749
2013-01-29 15:28:02 +01:00
4545719e18 MessageTray: disconnect signal to notification early
Similar to the previous patch, this adds robustness and prevents more
stacktraces and confusion in the state machine.

https://bugzilla.gnome.org/show_bug.cgi?id=692693
2013-01-28 21:02:46 +01:00
cc60afa31a Disconnect content-updated signal early
During the box pointer animation, other events can trigger an updateState,
losing the information that the summary is hiding and thus never disconnecting
the signals. Then, this stale connections can cause stacktraces, as they
fire when summaryBoxPointerItem is null.

https://bugzilla.gnome.org/show_bug.cgi?id=692693
2013-01-28 21:02:45 +01:00
85743ede7e MessageTray: fix reentrancy problem in hideNotificationCompleted
Hiding notificationWidget with a telepathy notification causes
unfocused to be emitted, which causes a reentrant updateState.
If another notification is queued, it is shown before the old
one is cleared.

https://bugzilla.gnome.org/show_bug.cgi?id=683986
2013-01-28 21:02:45 +01:00
8e231cb2ec layout: Merge Chrome and LayoutManager
The two classes have been gaining each other's functionality for a little
while, adding the new code wherever it was more convenient. Rather than
have a clear delineation between "This Manages Shell Chrome" and "This
Manages Shell Layout", I think it's better off if we just accept that
the responsibilities are pretty much the same.

https://bugzilla.gnome.org/show_bug.cgi?id=692677
2013-01-28 12:41:06 -05:00
72405cd43f layout: Shuffle code around
This will make the next diff much cleaner.

https://bugzilla.gnome.org/show_bug.cgi?id=692677
2013-01-28 12:41:06 -05:00
a9ad9d5e6d remoteSearch: fix fallout from RemoteSearchProvider refactor
RemoteSearchProvider.title was removed, but we were still using it in a
few places.

https://bugzilla.gnome.org/show_bug.cgi?id=692723
2013-01-28 12:34:19 -05:00
2d79c7333f main: Fix a bad rebase
This wasn't meant to be here...
2013-01-28 12:14:41 -05:00
7a79cfd76b keyboard: Do the initial redraw in _init
It doesn't seem like there's anything preventing us from doing this.

https://bugzilla.gnome.org/show_bug.cgi?id=692678
2013-01-28 12:10:32 -05:00
6600d6b6d9 status/power: Init the proxy asynchronously
Blocking the compositor is evil!

https://bugzilla.gnome.org/show_bug.cgi?id=692715
2013-01-28 17:35:02 +01:00
8ca25aed8b viewSelector: stop launching app from empty search
In the overview, when there is no text in the search entry, this._searchActive
will be set to false. Moving the Clutter.Return code block ensures that
pressing enter in the search field after deleting the characters of a search
will no longer launch the #1 application for the previous search.

https://bugzilla.gnome.org/show_bug.cgi?id=692391
2013-01-27 20:29:26 +01:00
1db353a0fc Updated Norwegian bokmål translation 2013-01-27 12:54:02 +01:00
2d384eb546 PA Update (alam): Punjabi Translation completed 2013-01-27 09:27:42 +00:00
5d1de33026 Use the standard apps tree for gnome-control-center panels desktop files
gnome-control-center is planning on removing its own tree in the
future. Since it already installs these applications into
/usr/share/applications, just use this for now.

https://bugzilla.gnome.org/show_bug.cgi?id=692483
2013-01-26 19:56:01 -05:00
f2ba49fa35 app-system: Remove unused get_settings_tree method
The last use for this is long-gone.

https://bugzilla.gnome.org/show_bug.cgi?id=692483
2013-01-26 19:56:00 -05:00
772ae1cae4 Updated Slovenian translation 2013-01-26 20:08:46 +01:00
1d267f2642 Added Friulian translation 2013-01-26 04:09:31 +01:00
cba299160a Added Friulian language 2013-01-26 04:09:22 +01:00
ea33193e73 Assamese translation updated 2013-01-25 17:43:05 +05:30
ef69c228fd workspace: Use a better algorithm for computing individual thumbnail scale
The one we had before could make unmaximized windows appear to be bigger
than maximized ones, for a few reasons. Ensure that this doesn't happen
again, and add some comments to explain the whys and needs for twiddling
the individual thumbnail size.

https://bugzilla.gnome.org/show_bug.cgi?id=686944
2013-01-24 15:14:03 -05:00
7ba0f07732 workspace: Clamp individual maximized scales to WINDOW_CLONE_MAXIMUM_SCALE
We clamp the overall layout's scale to WINDOW_CLONE_MAXIMUM_SCALE, but since
we do a bit of tweaking to try and make super small windows a tad larger, it's
theoretically possible that windows may become larger than the proper maximum
scale. Fix this issue.

https://bugzilla.gnome.org/show_bug.cgi?id=686944
2013-01-24 15:13:54 -05:00
07696086a2 appDisplay: set hscrollbar_policy = NEVER on the category view actor
https://bugzilla.gnome.org/show_bug.cgi?id=686881
2013-01-24 15:10:02 -05:00
426581eb1e scroll-view: reserve scrollbar allocation when policy is automatic
This is useful to avoid reflowing the layout when there's a
center-aligned block and a scrollbar appears.

https://bugzilla.gnome.org/show_bug.cgi?id=686881
2013-01-24 15:10:02 -05:00
fe88811a40 [l10n] Updated German translation 2013-01-24 20:09:42 +01:00
30aaa6e26c searchDisplay: set a max width on the search results scrolled child
On large displays, we don't want the search results list to expand
across the whole screen; set a maximum width of 1000px.
Unfortunately, since in St max-width only affects size requisition, we
need a little custom layout manager to have it applied to the allocation
too.

https://bugzilla.gnome.org/show_bug.cgi?id=692453
2013-01-24 12:06:46 -05:00
4bd071bf3c viewSelector: clean up active search state code
'active' isn't terribly clear about just what is active; also, make it
private, remove an useless extra object state we were saving, and
refactor some messy code.
Based on a patch by Tanner Doshier.

https://bugzilla.gnome.org/show_bug.cgi?id=692454
2013-01-24 12:04:20 -05:00
309ac65447 Updated Spanish translation 2013-01-24 16:42:18 +01:00
fe246470ce Make Show Applications button depress when held down
The way it currently exists is awkward and not how most virtual buttons
work. This patch causes the "clicked" look to occur when the button is held.

https://bugzilla.gnome.org/show_bug.cgi?id=692319
2013-01-24 12:41:15 +00:00
660cfe707c searchDisplay: Make list results span all available horizontal space
https://bugzilla.gnome.org/show_bug.cgi?id=691967
2013-01-22 23:44:21 -05:00
a0d7d7bc4b loginDialog: Allow right-clicking on button items
In a gdm session, we may not know what mouse orientation the user
may be in, so it makes sense to support both the left and right
mouse buttons to activate login or other items.

Additionally, add the behavior to all modal dialog items, even in
a user session, because it's unlikely that the user will right-click
on buttons, and it makes for an easier implementation.

https://bugzilla.gnome.org/show_bug.cgi?id=688748
2013-01-22 23:42:53 -05:00
758764ea75 Updated Serbian translation 2013-01-22 21:10:34 +01:00
6935c62a61 Updated POTFILES.in 2013-01-22 16:41:03 +01:00
a34d4d9124 network: more NM 0.9.6 compatibility
Virtual device support requires
NMGtk.utils_get_connection_device_name(), so don't try to support
virtual devices with older NM.
2013-01-21 16:01:08 -05:00
c49bb5aa03 network: add support for virtual device types (vlan, bond, bridge)
Virtual devices may not actually exist until their corresponding
connections are brought up. So we need new code to create fake device
wrapper objects for them based on the corresponding NMConnections.

https://bugzilla.gnome.org/show_bug.cgi?id=677144
https://bugzilla.gnome.org/show_bug.cgi?id=677146
https://bugzilla.gnome.org/show_bug.cgi?id=677148
2013-01-21 16:01:08 -05:00
427750d6af ShellGlobal: improve code to emit sound events
Use libcanberra-gtk3 and improve the set of context properties to correctly
associate the sounds with the shell.

https://bugzilla.gnome.org/show_bug.cgi?id=642831
2013-01-21 18:03:23 +01:00
b9ad5f8727 Assamese translation updated 2013-01-21 19:42:56 +05:30
c776826425 Updated Norwegian bokmål translation 2013-01-20 21:46:55 +01:00
a8ede18db5 Updated Spanish translation 2013-01-20 12:05:39 +01:00
9aa84ec54a MessageTray: restore symbolic icons to notification buttons
These were lost when we moved away with StIconType. The idea was that
apps needed to include -symbolic in their action IDs, but apps were
not updated, and it never makes sense to have non symbolic icons there,
so let's restore the previous behavior.

https://bugzilla.gnome.org/show_bug.cgi?id=692091
2013-01-19 19:41:24 +01:00
91a6520c3b MessageTray: don't show expanded content when the notification is collapsed
This fixes the image and scrollbars peeking through in banner mode,
because StTable wasn't able to allocate them at the restricted height
imposed by CSS.

https://bugzilla.gnome.org/show_bug.cgi?id=692091
2013-01-19 19:41:23 +01:00
45fde4888e Notification: fix regression in notification image handling
We must set the image after calling .update(), because we're passing
clear: true and thus we're removing the image too.
Also, we need to specify an explicit icon size, or St.Icon will use
the default (48px)

https://bugzilla.gnome.org/show_bug.cgi?id=692091
2013-01-19 19:41:23 +01:00
484ef5f2f6 MessageTray: simplify image handling
Remove duplicate checks before calling unsetImage, and set properties
directly in the constructor.

https://bugzilla.gnome.org/show_bug.cgi?id=692091
2013-01-19 19:41:23 +01:00
cdd354739a configure.ac: Remove deprecated macro
AM_PROG_CC_C_O has been deprecated upstream for some time now.

As a side effect, this makes "git grep taskpanel" turn up blank!

https://bugzilla.gnome.org/show_bug.cgi?id=692052
2013-01-19 13:38:04 -05:00
420f544bf3 configure.ac: Rearrange pkg-config checks
https://bugzilla.gnome.org/show_bug.cgi?id=692052
2013-01-19 13:38:03 -05:00
15b33189ae configure.ac: Remove old introspection infrastructure
This hasn't been needed in quite some time.

https://bugzilla.gnome.org/show_bug.cgi?id=692052
2013-01-19 13:37:18 -05:00
685a9f36f6 configure.ac: Remove checks for JS_NewGlobalObject
The need for this is long gone.

https://bugzilla.gnome.org/show_bug.cgi?id=692052
2013-01-19 13:37:18 -05:00
3b57812e29 configure.ac: Use GNOME_COMPILE_WARNINGS
This is now implemented as standard in gnome-common.

https://bugzilla.gnome.org/show_bug.cgi?id=692052
2013-01-19 13:37:18 -05:00
d583c48992 NetworkMenu: don't queue menu updates for destroyed devices
It will cause an exception as the work ID is now invalid, and may block
new devices from appearing for example when NM is restarted.

https://bugzilla.gnome.org/show_bug.cgi?id=684279
2013-01-19 03:07:03 +01:00
0e636ea67e NetworkMenu: don't clear the section when queuing an update
It's the very first thing that queueing does.

https://bugzilla.gnome.org/show_bug.cgi?id=684279
2013-01-19 03:07:03 +01:00
c9ce1bd30f Updated Portuguese translation and converted to New Spelling (Novo AO) 2013-01-19 00:37:51 +00:00
f2cbc3192c switcherPopup: Hide the top level actor on _init
destroy() checks if we are visible to decide whether to destroy the
actor immediately or after a fade out animation. Since actors are
visible by default it would always end up destroying only after the
animation time.

https://bugzilla.gnome.org/show_bug.cgi?id=691963
2013-01-19 00:27:36 +01:00
56d76791f1 PointerWatcher: keep a reference to the idle monitor
If nothing references the idle monitor, it can be finalized at GC,
causing it to have no effect. Worse, if the finalization happens at
wrong time, the pointer watcher can be permanently off, disabling the
message tray.

https://bugzilla.gnome.org/show_bug.cgi?id=691976
2013-01-18 21:14:09 +01:00
df2cab877f MessageTray: wait until the tray is hidden to switch styles
Switching style on Overview::hiding creates a weird effect, as the noise
texture is shown while the overview is still visibile. Instead, wait for
the tray to be fully hidden, then apply the new style.
As now the switch is invisible, there is no need for the transition
(which introduced the same problem on overview showing)

https://bugzilla.gnome.org/show_bug.cgi?id=689091
2013-01-18 21:07:12 +01:00
27ad8305e5 Overview: apply extra padding to windows in external monitor
Add an style class targetting workspaces located outside the overview,
and use it for extra padding around the window clones. Padding is passed
down and applied inside LayoutStrategy, consolidating code that previously
handled the bottom side only.

https://bugzilla.gnome.org/show_bug.cgi?id=690171
2013-01-18 20:58:07 +01:00
c97b4dd48e ViewSelector: use Params to pass optional parameters to addPage
This allows to introduce new parameters without making the signature
overly complex.

https://bugzilla.gnome.org/show_bug.cgi?id=690171
2013-01-18 20:53:20 +01:00
59ecd610b1 MessageTray: pass keyboard events to tray icons
Synthetize XKeyEvents for clicks emulated by StButton using Return or
Space.

https://bugzilla.gnome.org/show_bug.cgi?id=687425
2013-01-18 20:53:20 +01:00
42c1285ead messageTray: Remove code for ignoring num lock / scroll lock
Clutter now ignores these masks, so we can remove code that depends
on this.

https://bugzilla.gnome.org/show_bug.cgi?id=691731
2013-01-18 14:25:32 -05:00
622c1c9236 shell-global: Move shell_fonts_init() back to main
We do not need any stage-specific code, so let's move
the initialization back to where other components are
initialized.

https://bugzilla.gnome.org/show_bug.cgi?id=691745
2013-01-18 00:25:42 -05:00
2d88508f9d Updated Portuguese translation and converted to New Spelling (Novo AO) 2013-01-18 00:02:32 +00:00
86efba4ecc Bump version to 3.7.4.1
Update NEWS.
2013-01-17 13:56:38 +01:00
90fae00aa6 screenshot: Immediately show the flash spot
The "flash" effect looks awkward when it fades in and out. Real
camera have an immediate flash of light, which then seems to fade
out as our eyes readjust the rapid change in lighting.

https://bugzilla.gnome.org/show_bug.cgi?id=691875
2013-01-16 17:19:16 -05:00
d497ed4040 build: Drop obsololete variable from configure.ac
LIBEDATASERVERUI_MIN_VERSION is not used
2013-01-16 23:13:20 +01:00
c4a21ae5ad Updated Belarusian translation. 2013-01-16 16:09:54 +03:00
5e5798bee9 dateMenu: Add "Open Clocks" entry
This is similar to how the dateMenu already allows opening the calendar
application. However, the new entry only appears if GNOME Clocks is
installed, as it is not a core GNOME application.

https://bugzilla.gnome.org/show_bug.cgi?id=644390
2013-01-16 05:06:55 +02:00
d793077b91 dateMenu: Move the "Open Calendar" entry
The design calls for it to be in the left column rather than the right
one.

https://bugzilla.gnome.org/show_bug.cgi?id=644390
2013-01-16 05:06:52 +02:00
3e826f9249 build: Bump gsettings-desktop-schemas requirement
Needed for f7212cf80c
2013-01-15 12:18:53 -05:00
2adfb60dc4 data: Remove unused show-full-name in schema
It's now in gsettings-desktop-schemas.

https://bugzilla.gnome.org/show_bug.cgi?id=689561
2013-01-15 09:53:08 +01:00
f7212cf80c userMenu: Use show-full-name-in-top-bar setting
For the non-locked case, from gsettings-desktop-schemas' privacy
schema. For the locked case, from the privacy schema.

https://bugzilla.gnome.org/show_bug.cgi?id=689561
2013-01-15 09:53:08 +01:00
69556643bb Depend on gjs 1.35.4
Needed for 9548cd8341
2013-01-15 09:25:36 +01:00
f0ebc84840 Bump version to 3.7.4
Update NEWS.
2013-01-15 02:48:12 +01:00
490eb59ea3 build: Add missing file 2013-01-15 02:48:12 +01:00
16a9391726 remoteSearch: fix a typo in createIcon
4288761235 changed the method to always
create a GIcon, but didn't pass it down to the StIcon constructor.

https://bugzilla.gnome.org/show_bug.cgi?id=691750
2013-01-14 19:43:45 -05:00
adf8ba67d2 run-test: Properly set the path for the GVC library
We really should get a better testrunner rather than this hacked
together thing.

https://bugzilla.gnome.org/show_bug.cgi?id=691743
2013-01-14 17:53:23 -05:00
c37b222cbf network: support NM 0.9.6 again
NM 0.9.7 is still not released even as a tarball, so fix this to work
with 0.9.6 again for now (although it doesn't do any device name
disambiguation in this case now).

https://bugzilla.gnome.org/show_bug.cgi?id=691720
2013-01-14 11:25:40 -05:00
72f8f2beb1 Updated Norwegian bokmål translation 2013-01-14 11:21:19 +01:00
16bb9c17f5 status/keyboard: Add input source switching per window
If the setting is enabled, we record the last activated input source
for the currently focused window and switch to it when focusing back
that window. The Overview is considered a window for this purpose.

https://bugzilla.gnome.org/show_bug.cgi?id=691414
2013-01-14 11:11:00 +01:00
ca44977d92 status/keyboard: Only change the current source setting if it changed
This avoids all the work that goes on in various processes when
switching input sources if the activated source is the currently
configured one.

https://bugzilla.gnome.org/show_bug.cgi?id=691414
2013-01-14 11:11:00 +01:00
bcf294475f Updated Slovenian translation 2013-01-12 23:03:41 +01:00
3b31774dd3 Fix remaining uses of transition-duration without time units
Commit 8be3c5ed21 changed the CSS parser
to only accept values with explicit time units.
2013-01-12 17:39:08 +01:00
c106347c59 Updated Uyghur translation
Signed-off-by: Gheyret Kenji <gheyret@gmail.com>
2013-01-12 14:30:08 +09:00
2679be9d97 Telepathy: lookup PATH when launching empathy-accounts
It might be installed only in the jhbuild path.

https://bugzilla.gnome.org/show_bug.cgi?id=691553
2013-01-11 16:27:53 +01:00
4d59368c7d Updated Slovenian translation 2013-01-10 21:37:09 +01:00
3f29680fb6 workspace: Use MetaButtonLayout to get side of close button
The current code parses the button-layout setting because MetaButtonLayout
was not usable from introspection. With that fixed, we can switch to
using meta_prefs_get_button_layout().

https://bugzilla.gnome.org/show_bug.cgi?id=689263
2013-01-10 00:08:55 +01:00
8f1df14ae7 Revert "Remove non-existing file to make intltool happy"
This reverts commit 8410fc38c9.

It very much exists. Do a 'git submodule init && git submodule update'.
2013-01-09 21:02:06 +01:00
7159e360d9 Update Simplified Chinese translation 2013-01-09 06:40:20 +08:00
65723bcac5 PowerMenu: increase padding to the left of the percentage
This avoids having the percentage and device label in close succession.

https://bugzilla.gnome.org/show_bug.cgi?id=689297
2013-01-08 23:30:02 +01:00
73ae451cb4 Updated slovak translation 2013-01-08 21:41:08 +01:00
dc0ea3a248 main: Remove unused 'background' property 2013-01-08 13:24:09 -05:00
9548cd8341 js: Explicitly dispose all cairo contexts
Due to limitations and bugs in SpiderMonkey's GC, wrapper objects
for cairo contexts and similar may not get cleaned up immediately
after repainting, leading to leaking memory. Explicitly disposing
of such objects after they're not needed can clean up large portions
of memory for cairo surfaces.

https://bugzilla.gnome.org/show_bug.cgi?id=685513
2013-01-08 13:07:51 -05:00
8410fc38c9 Remove non-existing file to make intltool happy 2013-01-08 14:40:18 +01:00
0c9c2168b3 Updated minor fixes in Brazilian Portuguese translation contributed by F. Neves <florencioneves@gmail.com> 2013-01-08 10:32:34 -02:00
2690f54743 Updated Brazilian Portuguese translation 2013-01-08 10:17:45 -02:00
22783813c5 Update German translation 2013-01-07 15:38:44 +01:00
b5ed46a5af Updated Norwegian bokmål translation 2013-01-07 11:28:29 +01:00
1a3f17e296 Updated Kazakh translation 2013-01-06 20:22:44 +06:00
9281129f07 Really use the right getter
Doh, it is actually a uint, not an int.
2013-01-05 14:21:30 -05:00
0e9ddd3b99 Use the right getter
org.gnome.desktop.screensaver lock-delay is an integer, not
a boolean, so don't use get_bool() on it.

https://bugzilla.gnome.org/show_bug.cgi?id=691170
2013-01-04 22:01:25 -05:00
d87db04e55 panel: Update corners' nearest button on session mode change
With panel buttons changing dynamically on session mode changes we can
no longer rely on a corner's respective box style-changed signal to
find the nearest button.

Instead, make the panel take care of telling the corners to look for a
new button when buttons are changed.

https://bugzilla.gnome.org/show_bug.cgi?id=690180
2013-01-04 13:35:53 +00:00
906ec3c8a9 panel: Remove unused method
https://bugzilla.gnome.org/show_bug.cgi?id=690180
2013-01-04 13:35:47 +00:00
449575ceae keyboard: Don't hide or show the keyboard immediately
Acting on each Show/Hide DBus call immediately may cause a lot of
jittery movement when Alt+Tabbing or even just switching tabs in
e.g. gnome-terminal.

To make the OSK feel sturdier, we wait a bit before actually showing
or hiding it so that we can coalesce tight sequences of Show/Hide
calls. I.e. the last call wins which means that we might end up not
doing anything.

https://bugzilla.gnome.org/show_bug.cgi?id=688646
2013-01-04 13:13:15 +00:00
6255c77eba keyboard: Don't set keys as checked
There's no reason to do it and it actually breaks when showing subkeys
since then we won't get a 'key-released' signal for the main key.

https://bugzilla.gnome.org/show_bug.cgi?id=674955
2013-01-04 13:13:08 +00:00
c33622f2b3 keyboard: Fix subkeys handling
We can't pushModal() when showing the subkeys popup because that will
cause the application to lose focus and thus we get a Hide() call for
the whole OSK.

Instead, capture events on the main OSK actor while the subkeys popup
is shown so that we can both prevent events from reaching the main
keys but also cancel the subkeys if the user clicks away in the OSK.

https://bugzilla.gnome.org/show_bug.cgi?id=674955
2013-01-04 13:13:00 +00:00
e4860acb58 screenshot: use g_file_replace() when using an absolute path
Instead of g_file_create(), which always fails if the file exists
already.
2013-01-03 15:02:00 +01:00
e96867f757 screenshot: document the Screenshot interface
Use gdbus-codegen to generate a docbook for the screenshot interface,
and add it to our gtk-doc.

https://bugzilla.gnome.org/show_bug.cgi?id=688004
2013-01-03 12:56:46 +01:00
57bd43baf3 screenshot: move to a separate interface
Since we're breaking API already, take this as an occasion to use a
separate interface for all the screenshot-related methods. The interface
name is org.gnome.Shell.Screenshot.
Internally, move all the related code to screenshot.js.

https://bugzilla.gnome.org/show_bug.cgi?id=688004
2013-01-03 12:56:45 +01:00
3a4e595d32 screenshot: save to an unique path when using a basename
When a basename is passed, avoid filename conflicts. The unique path is
returned by the API.

https://bugzilla.gnome.org/show_bug.cgi?id=688004
2013-01-03 12:56:45 +01:00
dcad22bfa8 screenshot: change API to return the filename used for saving
Since we also support passing a basename now, clients might be
interested in knowing the path used to save the file.
Add an out argument to the interface for that.

https://bugzilla.gnome.org/show_bug.cgi?id=688004
2013-01-03 12:56:45 +01:00
acba0e47d8 screenshot: support non-absolute paths when saving screenshots
If a non-absolute path is passed to the screenshot methods, treat it as
a basename for the output image, and automatically try to save it in
$XDG_PICTURES_DIR, falling back to $HOME if it doesn't exist.

https://bugzilla.gnome.org/show_bug.cgi?id=688004
2013-01-03 12:56:45 +01:00
dd19459e18 Add a SelectArea() DBus method
This will be useful for e.g. selecting an area for a screenshot.

https://bugzilla.gnome.org/show_bug.cgi?id=687954
2013-01-03 12:56:45 +01:00
8be3c5ed21 St: fix parsing of transition-duration values
According to css3-transition, transition-duration is expressed
as a time, that is, in seconds or milliseconds. Fix that by
recognizing numbers with units and implicitly converting to
milliseconds after parsing.

https://bugzilla.gnome.org/show_bug.cgi?id=681376
2013-01-03 03:47:58 +01:00
51726d8de7 Fix regression from 4288761235 2013-01-03 02:48:22 +01:00
8f41c6bad8 UnlockDialog: honor org.gnome.desktop.screensaver.user-switch-enabled
The screensaver schema has a key that it is meant for locking down
the ability to switch user when the screen is locked, but support
for it was not implemented in the new screenshield.
Fix that by checking the key before creating the button.

https://bugzilla.gnome.org/show_bug.cgi?id=691042
2013-01-03 01:43:27 +01:00
e294abc567 keyboard: Remove leftover DBus import
This isn't there in newer versions of gjs.
2013-01-02 13:59:33 -05:00
9f9518c872 gnome-shell-plugin: Port event filter to XInput2
Otherwise, we'll see issues with tray icons.

https://bugzilla.gnome.org/show_bug.cgi?id=690590
2013-01-02 13:43:06 -05:00
5faf7cb59e gnome-shell-plugin: Extract crossing event ignores
This is a preliminary patch to make the diff in the next patch
cleaner. This just shuffles code around without changing anything.

https://bugzilla.gnome.org/show_bug.cgi?id=690590
2013-01-02 13:43:06 -05:00
4b095d532c st-texture-cache: Remove st_texture_cache_load_from_raw
This is now unused.

https://bugzilla.gnome.org/show_bug.cgi?id=691019
2013-01-02 12:32:29 -05:00
4288761235 remoteSearch: Use GIcon for loading icon data
This removes us from caching the pixbuf data in the icon cache,
and allows us to remove St.TextureCache.load_from_raw().

https://bugzilla.gnome.org/show_bug.cgi?id=691019
2013-01-02 12:32:29 -05:00
73388f30fd main: Add a better comment about shifting the modal stack
It took me a few minutes to realize why, so let's just add this
in for future reference.

https://bugzilla.gnome.org/show_bug.cgi?id=690965
2013-01-02 12:32:29 -05:00
2d9ddd4bc8 main: Rename curFocus to prevFocus
This better describes what we're tracking here: the previous
keyboard focus before pushing the modal.

https://bugzilla.gnome.org/show_bug.cgi?id=690965
2013-01-02 12:32:29 -05:00
55aa0cf303 main: Don't mess up the modal stack when the focus actor is destroyed
This seems to be an incorrect conversion when we moved from an array
to an object of keys in 3a6b4f3.

https://bugzilla.gnome.org/show_bug.cgi?id=690965
2013-01-02 12:32:29 -05:00
52ca15b514 grabHelper: Allow pressing escape on grab focus grabs
We didn't install the captured event handler on grab focus grabs,
leading to the case where we didn't ungrab correctly.

https://bugzilla.gnome.org/show_bug.cgi?id=690897
2013-01-02 12:32:29 -05:00
bd383888de notificationDaemon: Clean up icon/image handling
Make the logic for this clearer and easier to see.

https://bugzilla.gnome.org/show_bug.cgi?id=680414
2013-01-02 12:24:10 -05:00
1bd349485f notificationDaemon: Merge two pieces of similar code
Use the same code for parsing notification data to handle both
icons and images.

https://bugzilla.gnome.org/show_bug.cgi?id=680414
2012-12-30 14:09:39 -05:00
155f9dc1b1 messageTray: Support setImage(null) to mean unsetImage()
This is a quick API change that should clean up some conditionals.

https://bugzilla.gnome.org/show_bug.cgi?id=680414
2012-12-30 14:09:39 -05:00
fe7ee1edc3 notificationDaemon: Clean up code paths
While we really need to clean up the bad MessageTray API, this is
a quick step to allow for a cleaner codebase for the future.

https://bugzilla.gnome.org/show_bug.cgi?id=680414
2012-12-30 14:09:39 -05:00
c11cbff605 notificationDaemon: Prevent doing redundant work
We already calculated and created a gicon based on the icon and hints.

https://bugzilla.gnome.org/show_bug.cgi?id=680414
2012-12-30 14:09:39 -05:00
fbc629266f notificationDaemon: Fix style
https://bugzilla.gnome.org/show_bug.cgi?id=680414
2012-12-30 14:09:39 -05:00
75d44dca6b keyring: Actually ensure sensitivity when the keyring dialog opens
I misunderstood how the control flow for the keyring worked.

https://bugzilla.gnome.org/show_bug.cgi?id=690895
2012-12-30 14:09:39 -05:00
3e6b794a33 keyring: Fix copy/paste error
This was clearly meant to be continueButton.

https://bugzilla.gnome.org/show_bug.cgi?id=690895
2012-12-30 14:09:39 -05:00
a757ce48a1 ScreenShield: don't reset the lock screen animation if it's already happening
When you click Suspend from the user menu, the following things happen:
- we lock the screen internally by calling Main.screenShield.lock() and waiting
  for lock-screen-shown
- logind emits a Lock signal, which causes us to lock again
- gnome-settings-daemon notices PrepareForSleep, and calls org.gnome.ScreenSaver.Lock,
  just in case, so we lock once more
This means that, if you're lucky, you can see the curtain fall down multiple times,
as each .lock() call resets the animation.

https://bugzilla.gnome.org/show_bug.cgi?id=690858
2012-12-30 01:39:24 +01:00
507f29a7bd layout: Make OSK animation quicker, snappier
https://bugzilla.gnome.org/show_bug.cgi?id=688642
2012-12-30 00:12:27 +01:00
e5f4f77073 Updated Hungarian translation 2012-12-29 16:14:51 +01:00
f07fee538d keyboard: Show in an idle on clutter key focus changes
It's common to do actor.grab_key_focus() before the actor is mapped
which means that we can't reliably determine where the actor is at
notify::key-focus time and thus might end up showing the keyboard on
the wrong monitor.

This is happening, in particular, with the run dialog. Delaying until
we hit the main loop allows us to know where the actor finally is
before showing the OSK.

https://bugzilla.gnome.org/show_bug.cgi?id=685856
2012-12-28 13:00:15 -05:00
fa1420b384 lookingGlass: Move to an inspect() function
Rather than add invalid results to the place where you enter JavaScript
commands when you use the eyedropped, add an inspect() function and add
a fake call to it.

https://bugzilla.gnome.org/show_bug.cgi?id=690726
2012-12-28 03:20:37 -05:00
724a2bd72f Calendar: use text/calendar preferred app as the calendar app
That's what the info panel in g-c-c defines as the preferred calendar
application.

https://bugzilla.gnome.org/show_bug.cgi?id=690767
2012-12-27 19:05:52 +01:00
d525d02348 ScreenShield: honor lock-delay GSettings key
org.gnome.desktop.screensaver.lock-delay contains the grace period
of the screensaver: if deactivated within that many seconds from the
start of the idle period, the shell should not prompt for a password.
This setting correspond to the "Lock screen after" combo in screen
and privacy panels.

https://bugzilla.gnome.org/show_bug.cgi?id=690766
2012-12-27 19:05:44 +01:00
4920cf2b98 Updated Polish translation 2012-12-27 00:42:13 +01:00
f41d0938a4 shell-global: Drop unused variable
Leftover from 5566aa4588
2012-12-26 16:23:38 +01:00
924a405829 Updated Spanish translation 2012-12-26 11:59:17 +01:00
fc9225e24a keyring: Make sure the entries are insensitive after submission
https://bugzilla.gnome.org/show_bug.cgi?id=690594
2012-12-25 22:18:42 -05:00
ac202cbdd3 keyring: Don't use setButtons
https://bugzilla.gnome.org/show_bug.cgi?id=690594
2012-12-25 22:18:42 -05:00
cedd68c942 polkitAgent: Make sure the entries are insensitive after submission
It feels weird and strange to be able to type in the polkit agent
dialog after hitting enter.

https://bugzilla.gnome.org/show_bug.cgi?id=690594
2012-12-25 22:18:42 -05:00
4231c879ef polkitAgent: Don't use setButtons
https://bugzilla.gnome.org/show_bug.cgi?id=690594
2012-12-25 22:18:42 -05:00
42e58a4b72 modalDialog: Make the button layout visible always
With the last button-less modal dialog gone, there's no reason it
should ever be hidden.

https://bugzilla.gnome.org/show_bug.cgi?id=690594
2012-12-25 22:18:42 -05:00
ebd1bc83c9 Ping the active window when using the app menu
Use the new meta_window_check_alive() to verify if the application is
responding after the user activates an action from the app menu.
This in particular restores the ability to force quit applications
from the menu, even if the use a custom GMenu.

https://bugzilla.gnome.org/show_bug.cgi?id=684340
2012-12-25 18:41:22 +01:00
0bc9d7455f Updated Galician translations 2012-12-25 00:37:18 +01:00
5274166f8c layout: Fix strut heuristics for multi-monitor
Spotted while going through the code.

https://bugzilla.gnome.org/show_bug.cgi?id=690666
2012-12-23 21:17:40 -05:00
5566aa4588 shell-global: Remove hacks around clutter_event_get_current_time()
clutter_event_get_current_time() has been fixed upstream as part
of reentrant bug fixes, so there's no need for these shenanigans.

https://bugzilla.gnome.org/show_bug.cgi?id=690665
2012-12-23 21:17:40 -05:00
ab638a4f54 gnome-shell-jhbuild: Remove old envvar
This isn't used anymore.

https://bugzilla.gnome.org/show_bug.cgi?id=690667
2012-12-23 21:17:40 -05:00
86a8452ab7 run-test: Load shell files from the srcdir
https://bugzilla.gnome.org/show_bug.cgi?id=690667
2012-12-23 21:17:26 -05:00
8c96c16eac Update Arabic translation 2012-12-23 18:35:16 +02:00
45c2e6575d Bluetooth: remove dead code
Notification.grabFocus doesn't exist anymore.

https://bugzilla.gnome.org/show_bug.cgi?id=687081
2012-12-23 13:32:13 +01:00
a786a7dc55 Revert "Remove a non-existing file"
This reverts commit 685262fc5e.

Please don't remove this file from POTFILES.in.

You need to run 'git submodule init && git submodule update' in your
git checkout. Damned Lies currently lacks support for submodules,
thus this error.
2012-12-21 20:43:42 +01:00
6adf5cbee5 Reverting "volume: Put canberra sound events on output streams only"
This reverts commit 1964b54627.

This was committed accidentally.
2012-12-21 13:28:46 -05:00
1964b54627 volume: Put canberra sound events on output streams only
This seems to be an oversight in the original volume menu code
that slipped in by accident.
2012-12-21 13:24:02 -05:00
c9d0e82c52 volume: Clean up stream/slider handling code
Rather than using naming schemes and dynamic property lookups as
a kind of namespace, use what was designed to be used as a namespace:
a class.

https://bugzilla.gnome.org/show_bug.cgi?id=690539
2012-12-21 13:23:48 -05:00
1a4948f0f2 volume: Pick up hasHeadphones from the signal handler
A preliminary patch for big cleanups happening soon.

https://bugzilla.gnome.org/show_bug.cgi?id=690539
2012-12-21 12:30:16 -05:00
66da3f5668 volume: Don't have a separate syncVisibility method
With it doing less now, we can simply do it inline.

https://bugzilla.gnome.org/show_bug.cgi?id=690539
2012-12-21 12:30:03 -05:00
9ebeb64570 volume: Don't set the visibility of the main icon
As the main icon is inside the actor, this is needless calculation.

https://bugzilla.gnome.org/show_bug.cgi?id=690539
2012-12-21 12:30:03 -05:00
571aaece2e volume: Merge the two update handlers
With our mess of callbacks gone, we can update stream volume
in a single place only.

https://bugzilla.gnome.org/show_bug.cgi?id=690539
2012-12-21 12:30:03 -05:00
f60fb954a2 volume: Make icon calculation stable
Calculate an icon based on our current state, not a mess of
signal emissions and callbacks. This is a preliminary basic
cleanup patch in preparation for the next one.

https://bugzilla.gnome.org/show_bug.cgi?id=690539
2012-12-21 12:30:03 -05:00
1d136cacfb lockScreenMenu: Construct actual panel icons much more accurately
Instead of faking it by adding a bunch of main icons and secondary
icons to our own box, try and recreate the original button box
with the original icons.

https://bugzilla.gnome.org/show_bug.cgi?id=690589
2012-12-21 12:28:00 -05:00
994021d77f panelMenu: Remove inaccurate comment
This has been out of date ever since the -symbolic fallbacks
have gone away.

https://bugzilla.gnome.org/show_bug.cgi?id=690589
2012-12-21 12:26:32 -05:00
8899325a0e boxpointer: Fix a small typo
This was showing up in my auto-complete results.
2012-12-21 12:24:48 -05:00
43876a9357 boxpointer: Defer re-allocation after a flip
As we may be flipping the box pointer in response to re-allocation,
like the addition of a new actor to the boxpointer, we can't queue
a re-layout while in a re-layout, so defer.

https://bugzilla.gnome.org/show_bug.cgi?id=690608
2012-12-21 12:24:48 -05:00
d6cace32f5 boxpointer: Don't use the box allocation when calculating the arrow side
Depending on the current state of arrowSide, the box allocation may be
wrong; e.g. if the user requested a TOP, but we flipped to a BOTTOM, the
next request would look to the y2 value of the flipped BOTTOM, which is
wrong.

Instead, use the origin, plus the calculated preferred size of the box.

https://bugzilla.gnome.org/show_bug.cgi?id=690608
2012-12-21 12:24:48 -05:00
2388de455b boxpointer: Rework how flipping works
Make sure we re-allocate after we flip sides, to ensure that
padding around the child actor is updated correctly. Additionally,
ensure that we flip after we setPosition, as we won't get re-allocated
auotmatically by just changing the position.

https://bugzilla.gnome.org/show_bug.cgi?id=690608
2012-12-21 12:24:48 -05:00
b14b3ab276 boxpointer: Clean up reposition
Given that it's modifying state, there's no reason it can't be
using it instead. This makes it easier to use, so we aren't passing
in a bunch of instance variables every time.

https://bugzilla.gnome.org/show_bug.cgi?id=690608
2012-12-21 12:24:48 -05:00
32ccb779c6 theme: notification improvements
Tweak the layout to give more space to action buttons, associate
the heading with the body text, and give button labels a regular
font weight.

https://bugzilla.gnome.org/show_bug.cgi?id=688506
2012-12-21 11:37:48 +00:00
52536e94e5 st-scroll-view: Fix build with -Werror=maybe-uninitialized 2012-12-20 23:01:13 +01:00
5d6b3abd26 st-adjustment: Add missing math.h include 2012-12-20 23:01:13 +01:00
79682bbcb0 Updated Lithuanian translation 2012-12-20 23:55:18 +02:00
770ff19313 workspace: Add smooth scrolling support to zoom windows
https://bugzilla.gnome.org/show_bug.cgi?id=687573
2012-12-20 08:40:05 -05:00
8d4855f100 volume: Add smooth scrolling to adjust output volume
Allow users to smoothly scroll on the volume indicator icon
to adjust the volume. Do this by simply passing the scroll
event to the slider inside the menu.

https://bugzilla.gnome.org/show_bug.cgi?id=687573
2012-12-20 08:40:04 -05:00
7d4e14f384 popupMenu: Add smooth scrolling support for sliders
Allowing smooth scrolling on the Y axis to accurately adjust the value
of the slider.

https://bugzilla.gnome.org/show_bug.cgi?id=687573
2012-12-20 08:40:04 -05:00
f162dd7e87 scroll-bar: Add smooth scrolling support
Do the same for StScrollBar.

https://bugzilla.gnome.org/show_bug.cgi?id=687573
2012-12-20 08:40:04 -05:00
06dc12e217 scroll-view: Add proper smooth scrolling
The code here before was added as dummy code to satisfy an error
in the missing switch, and wasn't ever tested due to the lack of XI2
in mutter. Use the same math as GtkRange does to calculate scroll bar
positions from raw XI2 deltas to allow for proper smooth scrolling.

https://bugzilla.gnome.org/show_bug.cgi?id=687573
2012-12-20 08:40:04 -05:00
36edff9e15 Updated Hebrew translation. 2012-12-20 13:37:03 +02:00
34a0c079de power: Update for new D-Bus name
https://bugzilla.gnome.org/show_bug.cgi?id=690506
2012-12-19 17:18:32 +01:00
84b581ae13 Crisp menu separators
Instead of being fuzzy, the menu separators should be a clear
line with a horizontal gradient. This looks better and is
consistent with the mockups.

https://bugzilla.gnome.org/show_bug.cgi?id=641745
2012-12-19 15:45:18 +00:00
587c93eadf StDrawingArea: fix texture drawing
cogl_rectangle() wants the four rectangle corners, not the size.

https://bugzilla.gnome.org/show_bug.cgi?id=641745
2012-12-19 15:45:18 +00:00
b5aa549e0c [l10n] Updated Estonian translation 2012-12-19 17:14:03 +02:00
5fb6738612 Updated Norwegian bokmål translation 2012-12-19 15:45:24 +01:00
685262fc5e Remove a non-existing file 2012-12-19 15:45:21 +01:00
8802f6cf23 Updated Slovenian translation 2012-12-19 11:38:43 +01:00
2698d0db29 Bump version to 3.7.3
Update NEWS.
2012-12-18 18:28:42 +01:00
2fa7230133 gvc: Update git submodule
This fixes make distcheck.
2012-12-18 18:28:42 +01:00
658243eae4 build: Exclude .gitmodules from distribution 2012-12-18 18:28:42 +01:00
7282bc8859 build: Bump gsettings-desktop-schemas requirement 2012-12-18 18:28:42 +01:00
07511cb21a Handle 'switch-input-source' keybinding move
Moving this to gsettings-desktop-schemas instead so that
gnome-control-center doesn't have to grow a dependency on gnome-shell.

https://bugzilla.gnome.org/show_bug.cgi?id=690427
2012-12-18 16:13:13 +00:00
f71f767a3c [l10n] Updated Estonian translation 2012-12-18 17:34:09 +02:00
176daa1469 WorkspacesDisplay: clean up workspacesView lists
Rather than sometimes having a list and sometimes null, keep an array
always and check for its length.

https://bugzilla.gnome.org/show_bug.cgi?id=688234
2012-12-18 16:00:54 +01:00
5487f8cf93 WorkspacesView: don't use clutter_actor_reparent()
It's deprecated and in this case is not needed because at that point
the actor hasn't been parented yet.

https://bugzilla.gnome.org/show_bug.cgi?id=688234
2012-12-18 16:00:54 +01:00
944762ac83 WindowOverlay: fix title sizing
After the first time the title was placed, we were setting its width,
thus forcing get_preferred_width() to return that as the minimum and
natural width.
To workaround that, explicitly reset the width to -1, -1, causing
StLabel->get_preferred_width() to be called, which would give us a meaningful
value for minimum and natural width.

https://bugzilla.gnome.org/show_bug.cgi?id=688234
2012-12-18 16:00:54 +01:00
c66068c435 Workspace: fix window clone slot reporting
WindowOverlay was at times seeing bogus values reported as WindowClone
sizes. Fix that by storing and passing the value from the authoritative
source, that is, the LayoutStrategy.

https://bugzilla.gnome.org/show_bug.cgi?id=688234
2012-12-18 16:00:54 +01:00
d479c936b8 Workspace: miscellaneuous cleanups
Use a single GSettings object for all layout changes of window overlays,
and avoid quadratic behavior for window positioning.

https://bugzilla.gnome.org/show_bug.cgi?id=650843
2012-12-18 16:00:54 +01:00
c5a3f784dd theme: make the 'more results' emblem more legible on light icons 2012-12-18 15:40:23 +01:00
529d012865 LockScreenMenu: double the space between icons
Using the same spacing as secondary status icons causes the
three indicators to be too close to each other.

https://bugzilla.gnome.org/show_bug.cgi?id=687955
2012-12-17 17:25:29 +01:00
8a1b83417a network: add minimal support for InfiniBand
If present, InfiniBand devices show up with the ethernet devices (and
the presence of an InfiniBand connection will cause "Wired" to be
renamed to "Ethernet").

https://bugzilla.gnome.org/show_bug.cgi?id=677150
2012-12-17 08:11:36 -05:00
6c64c0873c workspaceThumbnail: unify restack handling
Both WorkspacesDisplay and ThumbnailsBox need to know when windows have been
restacked. Instead of each tracking changes on their own or trying to call
each other, have the overview keep track and do the calculations, emitting
a signal with the result.

https://bugzilla.gnome.org/show_bug.cgi?id=690175
2012-12-17 13:24:25 +01:00
d2c6149923 workspaceThumbnail: handle show/hide when entering/leaving the overview
Handle this internally, instead of having WorkspacesDisplay show/hide
us.

https://bugzilla.gnome.org/show_bug.cgi?id=690175
2012-12-17 13:24:25 +01:00
9f98f6f02d workspaceThumbnail: handle scroll-event internally
Instead of having WorkspacesDisplay calling us.

https://bugzilla.gnome.org/show_bug.cgi?id=690175
2012-12-17 13:24:25 +01:00
2f7c0b1985 workspaceThumbnail: make ThumbnailsBox track workspace changes itself
Instead of having WorkspacesDisplay calling us.

https://bugzilla.gnome.org/show_bug.cgi?id=690175
2012-12-17 13:24:25 +01:00
f61548234a status/keyboard: Add an Alt+Tab like input source switcher
https://bugzilla.gnome.org/show_bug.cgi?id=682315
2012-12-17 11:43:02 +00:00
6eef830ba4 status/keyboard: Keep a list of input sources in MRU order
This will allow us to provide an Alt+Tab like input source switcher.

https://bugzilla.gnome.org/show_bug.cgi?id=682315
2012-12-17 11:43:02 +00:00
88a9b7648c Add the new switch-input-source keybinding
https://bugzilla.gnome.org/show_bug.cgi?id=682315
2012-12-17 11:43:02 +00:00
b7678493f9 windowManager: Return the KeyBindingAction value from addKeybinding()
meta_display_add_keybinding() returns a keybinding action ID for
dynamically registered keybindings which can be used to match a
keycode/mask pair to the action it is bound to.

https://bugzilla.gnome.org/show_bug.cgi?id=682315
2012-12-17 11:43:01 +00:00
a9ec8a354a main: Initialize WindowManager earlier
This allows us to register keybindings from most other places that
might need to do so like the panel or any status indicator.

https://bugzilla.gnome.org/show_bug.cgi?id=682315
2012-12-17 11:43:01 +00:00
a42d35deab main: Add an ALL value to KeybindingMode
https://bugzilla.gnome.org/show_bug.cgi?id=682315
2012-12-17 11:43:01 +00:00
2008feb7da status/keyboard: Store IBus engine properties per InputSource object
Instead of storing them globally and having an hardcoded list of
engines which are allowed to change their indicator symbol when the
InputMode property changes.

https://bugzilla.gnome.org/show_bug.cgi?id=682318
2012-12-17 11:43:01 +00:00
c41424b57b status/keyboard: Make input sources be proper objects
Introduce an InputSource class which makes it easier to keep track of
all the data we have about them.

https://bugzilla.gnome.org/show_bug.cgi?id=682318
2012-12-17 11:43:01 +00:00
3bd5563a7e ctrlAltTab: Remove a "window" parameter leftover
Commit 1f46a0d removed support for the items "window" parameter but
there was an occurence left.

https://bugzilla.gnome.org/show_bug.cgi?id=690312
2012-12-16 23:03:34 +01:00
eff8ec00c4 Updated slovak translation 2012-12-16 22:50:06 +01:00
1256af7b9a WindowManager: scale windows to their monitor's corner on minimize
We previously scaled windows to (primary.x, 0), which is wrong if the
primary monitor is not at y == 0. At the same time, change the policy
to pick the hot corner on the monitor the window is on, because that's
closest to where it will appear in the overview.

https://bugzilla.gnome.org/show_bug.cgi?id=690241
2012-12-16 22:40:33 +01:00
b38ecaf925 LayoutManager: fix findMonitorForWindow to take a metaWindow
This makes the method usable in places where the associated window actor
might not have the right size (such as from window manager animations).
Also, make the method public from LayoutManager.

https://bugzilla.gnome.org/show_bug.cgi?id=690241
2012-12-16 22:40:33 +01:00
bd5d945fb2 Updated Spanish translation 2012-12-16 20:48:08 +01:00
15063ef3d5 WindowManager: clean up scale_y when overwrite the map animation
If we overwrite a map animation (for example because the actor is now destroyed),
we need to complete it first, otherwise it starts off from a random
middle point. This is the same treatment opacity gets for normal windows.

https://bugzilla.gnome.org/show_bug.cgi?id=690241
2012-12-16 20:45:43 +01:00
3b8a125732 WindowManager: clean up effects code
Use consistently shouldAnimateActor(), and try to have a uniform code flow
between the modal dialog and the normal case.

https://bugzilla.gnome.org/show_bug.cgi?id=690241
2012-12-16 20:45:43 +01:00
9860b1c677 St: fix regression from f7af96dbb2
A bad rebase caused the wrong patch to be pushed.

https://bugzilla.gnome.org/show_bug.cgi?id=687881
2012-12-16 20:45:43 +01:00
a0e340f06e workspaces-view: make sure to add spacing between view and controls
The thumbnail controls are not a separate actor in the overview group
yet, so we need to ensure a spacing between them and the workspaces
view.
Instead of exporting the overview spacing, just add a temporary style
class to the workspaces-view actor for it. It will be removed in the
future when we change the layout of overview elements.

https://bugzilla.gnome.org/show_bug.cgi?id=690174
2012-12-15 14:13:36 -05:00
57d3bec95c MessageTray: change notification and boxpointer background opacity to
make the text in it more clear.
2012-12-15 16:34:58 +01:00
be10f3c2b5 ViewSelector: remove unnecessary StBoxLayout
It was a remnant from the tabbed interface.

https://bugzilla.gnome.org/show_bug.cgi?id=690171
2012-12-15 15:23:21 +01:00
f7af96dbb2 St: avoid blocking IO to resolve relative urls
realpath() does a series of lstat() on each path component to resolve
symbolic links, but we just want to get an absolute path, and we don't
really care if it is physical or not. Going through a GFile does the
canonicalization we need, and is a lot faster.

https://bugzilla.gnome.org/show_bug.cgi?id=687881
2012-12-15 03:40:13 +01:00
468a855f04 make the window thumbnails bigger
Adjust the layout of the overview and window thumbnails to make them
bigger. Also, make the background shade darker to compensate for the
increased thumbnail density.

https://bugzilla.gnome.org/show_bug.cgi?id=689876
2012-12-14 12:58:43 +00:00
429f9e1d15 iconGrid: Only recreate icons when needed
Recreating icons on every style change -- like hover, can have
disasterous effects. Not only is the quick creation/destruction of
the actors bad, but adding/removing actors at runtime queues many
relayouts, which makes the whole system slower as a lot of unnecessary
reallocations are figured out.

While an optimization was here before, it was broken because it
broke high-contrast themes. Connect explicitly to the texture cache
to know when the icon theme has changed, instead of removing a valuable
optimization.

https://bugzilla.gnome.org/show_bug.cgi?id=672941
2012-12-13 21:35:05 -05:00
5de91197ae status/keyboard: remove the property whitelist
It was just a stopgap solution for 3.6, as the necessary UI bits
were not implemented.

https://bugzilla.gnome.org/show_bug.cgi?id=682318
2012-12-14 00:47:19 +01:00
bc6b4b01ea status/keyboard: implement remaining property types
Input methods other than Anthy need different property types, so we
should support all those exposed by the IBus protocol.

https://bugzilla.gnome.org/show_bug.cgi?id=682318
2012-12-14 00:47:19 +01:00
e89ce7358a overview: don't clone the whole message tray when layouting
The message tray actor also includes notifications themselves. We want
our ghost to be sized as the base part of the tray instead.
Just make sure to use the same style class as the base actor then, as
its height is specified by the CSS.

https://bugzilla.gnome.org/show_bug.cgi?id=690174
2012-12-13 17:56:23 -05:00
bdf0a6fd0b message-tray: use a style class for summary-mode actor
We want to use the same style class for the message tray ghost.

https://bugzilla.gnome.org/show_bug.cgi?id=690174
2012-12-13 17:56:22 -05:00
783abd4f5f Reverting "extensionUtils: Remove unused property"
This reverts commit 28aa9201f0.

This broke the extension-state-changed signal.

https://bugzilla.gnome.org/show_bug.cgi?id=690173
2012-12-13 14:16:44 -05:00
28aa9201f0 extensionUtils: Remove unused property
https://bugzilla.gnome.org/show_bug.cgi?id=690173
2012-12-13 19:55:34 +01:00
378df515d0 extensionSystem: Fix error reporting in lg / ListExtensionErrors()
Commit c99e8eb29 removed this line probably accidentally, as no
effort has been made to replace consumers in lookingGlass and
shellDBus.

https://bugzilla.gnome.org/show_bug.cgi?id=690173
2012-12-13 19:55:33 +01:00
04d68c6e36 overview: overview as boxlayouts
https://bugzilla.gnome.org/show_bug.cgi?id=682286
2012-12-13 09:38:45 -05:00
5308d12239 search: skip combining diacritical marks in search operations
https://bugzilla.gnome.org/show_bug.cgi?id=648587
2012-12-12 17:41:45 +01:00
15cac0157c networkAgent: Only unregister if we've registered
It's possible to turn auto_register back on, but not immediately be
registered. Don't cause a (harmless) critical in this case.

https://bugzilla.gnome.org/show_bug.cgi?id=689884
2012-12-12 06:35:46 -05:00
fc696bc054 userMenu: Update user name on session mode changes
The new privacy panel offers separate settings for the user name
visibility in lock screen and normal mode. The code to support
either setting is already in place, but we need to make sure to
update the user name on session mode changes to pick up the correct
one.

https://bugzilla.gnome.org/show_bug.cgi?id=690049
2012-12-11 20:31:46 +01:00
5a7b7b7389 bluetooth: Update settings desktop file
For the one now used in gnome-control-center

https://bugzilla.gnome.org/show_bug.cgi?id=690046
2012-12-11 17:12:00 +01:00
9c9cf6f5ab Depend on libpulse 2.0
Commit 3af9f636af introduced a dependency on
libpulse 2.0 so best to check for it.

https://bugzilla.gnome.org/show_bug.cgi?id=689965
2012-12-11 11:42:50 +01:00
0155739bef search: remove more dead code
https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-12-10 16:50:54 -05:00
d0902fa28b search: remove SearchProvider base class
This is causing more confusion than anything else these days; the DBus
API is properly documented now and that's what people are expected to
use, the rest are implementation details we're not interested in
exposing.

https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-12-10 16:50:54 -05:00
3aa0d455c9 search: remove SearchResultDisplay base class
It's unused, and the clear() method is just wrong. Remove it.

https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-12-10 16:50:54 -05:00
835d4e4b58 appDisplay: Don't use icons in results for settings
The search results are not necessarily improved by including icons, so don't.

https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-12-10 16:50:54 -05:00
6075332f2f remoteSearch: We do not need a fallback for createIcon
Remote providers no longer have access to a grid layout, where an icon is
a requirement. If they don't specify an icon, don't create one.

https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-12-10 16:50:53 -05:00
c5d8484e19 searchDisplay: Add ListSearchResult and ListSearchResults
These are for all search results except apps (and Wanda).
We also simplify a bit the packing of search results, which removes some
ugly code in navigateFocus() where we needed to call
st_widget_navigate_focus() twice, since the grid icon was composed by
two nested boxes, both focusable.

https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-12-10 16:50:53 -05:00
575ab0d609 searchDisplay: simplify drag actor source code
Make sure this._dragActorSource is always set from _init() to siplify
code.

https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-12-10 16:50:53 -05:00
b48a7d5cfd popupMenu: Break separator drawing code out of PopupSeparatorMenuItem
And into a separate HorizontalSeparatorClass.

https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-12-10 16:50:53 -05:00
9af107feff searchDisplay, and others: Switch from provider title to provider icon
Display a '+' icon on the provider icon if there are more results that are
hidden. If the provider icon is clicked, ask the provider to launch itself and
perform a search with the current terms.

https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-12-10 16:50:53 -05:00
70b5db16d3 search: make sure to pass a timestamp to LaunchSearch()
Do not repeat past mistakes while we're still in time.

https://bugzilla.gnome.org/show_bug.cgi?id=690009
2012-12-10 16:50:22 -05:00
5072ea7e47 Calendar: remove dead code
this._weekdateHeader is never assigned, so _setWeekdateHeaderWidth is
useless. Everything else falls from that.

Spotted by mathematical.coffee@gmail.com, our human coverity :)

https://bugzilla.gnome.org/show_bug.cgi?id=687250
2012-12-10 22:34:10 +01:00
138 changed files with 21450 additions and 15685 deletions

View File

@ -16,6 +16,7 @@ EXTRA_DIST = \
# These are files checked into Git that we don't want to distribute
DIST_EXCLUDE = \
.gitignore \
.gitmodules \
gnome-shell.doap \
HACKING \
MAINTAINERS \

154
NEWS
View File

@ -1,3 +1,157 @@
3.7.5
=====
* MessageTray: pass keyboard events to tray icons [Giovanni; #687425]
* network: add support for virtual devices (vlan, bond, bridge) [Dan; #677144]
* gdm: Allow right-clicking buttons for left-handed users [Jasper; #688748]
* Make list search results span all available horizontal space [Tanner; #691967]
* Make Show-Applications button depress when held down [Hashem; #692319]
* Set a max width on search results [Cosimo; #692453]
* Reserve scrollbar allocation for automatic policy [Cosimo; #686881]
* Improve scaling algorithm for window thumbnails [Jasper; #686944]
* Fix launching settings panels after g-c-c changes [Jasper; #692483]
* Stop launching applications from empty searches [Hashem; #692391]
* Implement per-source notification filtering [Giovanni; #685926]
* ScreenShield: Omit ActiveChanged() signal at end of fade [Giovanni; #691964]
* ScreenShield: Lower the shield on idle before locking [Giovanni; #692560]
* Make previews of minimized windows translucent in overview [Florian; #692999]
* windowManager: Respect icon geometry when minimizing [Florian; #692997]
* ScreenShield: Only show lock icon when actually locked [Giovanni; #693007]
* general: Use & instead of 'and' for Settings panels [Jeremy; #689590]
* network: Add support for new ModemManager1 interface [Aleksander; #687359]
* network: Handle LTE-only modems as GSM ones [Aleksander; #688144]
* mobile-providers: Port to libnm-gtk [Aleksander; #688943]
* general: Consistently use Title Case in top bar [Jeremy; #689589]
* panel: Add :overview pseudo class while in overview [Florian; #693218]
* sessionMode: Add support for mode-specific styling [Florian; #693219]
* loginManager: Make suspend a NOP in the ConsoleKit patch [Florian; #693162]
* screenShield: Inhibit suspend until the screen is locked [Florian; #686482]
* Misc bug fixes and cleanups [Jasper, Giovanni, Rui, Cosimo, Florian, Stefano,
Adel, Yanko; #691745, #691731, #690171, #689091, #691976, #691963, #684279,
#692052, #692091, #642831, #692454, #692715, #692678, #692723, #692677,
#683986, #692693, #692749, #692948, #692995, #692996, #692994, #677215,
#692586, #693067, #693031, #693049, #643111, #693161, #693220]
Contributors:
Jeremy Bicha, Giovanni Campagna, Cosimo Cecchi, Tanner Doshier,
Stefano Facchini, Adel Gadllah, Yanko Kaneti, Rui Matos, Aleksander Morgado,
Florian Müllner, Hashem Nasarat, Jasper St. Pierre, Dan Winship
Translations:
Duarte Loreto [pt], Daniel Mustieles [es], Kjartan Maraas [nb],
Nilamdyuti Goswami [as], Мирослав Николић [sr,sr@latin],
Tobias Endrigkeit [de], Fabio Tomat [fur], Matej Urbančič [sl], A S Alam [pa],
Inaki Larranaga Murgoitio [eu], Piotr Drąg [pl], Wouter Bolsterlee [nl],
Gheyret Kenji [ug], Yaron Shahrabani [he], Chao-Hsiung Liao [zh_HK,zh_TW],
Milo Casagrande [it], Benjamin Steinwender [de]
3.7.4.1
=======
* userMenu: Use show-full-name-in-top-bar setting [Bastien; #689561]
* dateMenu: Add "Open Clocks" entry [Mathieu; #644390]
* screenshot: Immediately show the flash spot [Jasper; #691875]
* Misc. bug fixes [Rico, Jeremy]
Contributors:
Jeremy Bicha, Mathieu Bridon, Bastien Nocera, Jasper St. Pierre,
Rico Tzschichholz
Translations:
Ihar Hrachyshka [be]
3.7.4
=====
* Make menu separators crisp [Giovanni, Allan; #641745]
* power: Update for new D-Bus name [Bastien; #690506]
* Add smooth scrolling support [Jasper; #687573]
* Tweak notification layout [Allan; #688506]
* Ping the active window when using the app menu [Giovanni; #684340]
* Make password entries insensitive after submission [Jasper; #690594, #690895]
* Honor lock-delay GSettings key [Giovanni, Matthias; #690766, #691170]
* Use text/calendar preferred app as the calendar app [Giovanni; #690767]
* lookingGlass: Move to an inspect() function [Jasper; #690726]
* Make OSK animation quicker, snappier [Rui; #688642]
* Allow to close chat notifications with Escape [Jasper; #690897]
* Honor org.gnome.desktop.screensaver.user-switch-enabled [Giovanni; #691042]
* Add a SelectArea() DBus method [Cosimo; #687954]
* Support non-absolute paths when saving screenshots [Cosimo; #688004]
* OSK: Fix extended keys popups [Rui; #674955]
* Don't hide or show the keyboard immediately [Rui; #688646]
* Improve padding in power menu [Giovanni; #689297]
* Add per-window input source switching [Rui; #691414]
* Misc bug fixes and cleanups [Rico, Jasper, Giovanni, Rui, Florian, Dan;
#690608, #690589, #690539, #687081, #690667, #690665, #690666, #685856,
#690858, #690895, #680414, #690965, #691019, #690590, #681376, #690180,
#685513, #689263, #691553, #691720, #691743, #691750]
Contributors:
Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Allan Day, Rui Matos,
Florian Müllner, Bastien Nocera, Jasper St. Pierre, Rico Tzschichholz,
Dan Winship
Translations:
Matej Urbančič [sl], Kjartan Maraas [nb], Mattias Põldaru [et],
Yaron Shahrabani [he], Aurimas Černius [lt], Khaled Hosny [ar],
Fran Diéguez [gl], Daniel Mustieles [es], Piotr Drąg [pl], Balázs Úr [hu],
Baurzhan Muftakhidinov [kk], Tobias Endrigkeit [de], Dušan Kazik [sk],
Aron Xu [zh_CN], Gheyret Kenji [ug]
3.7.3
=====
* Add 'No Messages' label when message tray is empty [Victoria; #686738]
* Use better icons in Ctrl-Alt-Tab popup [Stéphane; #641303]
* Show the OSK on the monitor where the focused window lives [Giovanni; #685856]
* Highlight window clone and caption when hovered [Giovanni, Marc; #665310]
* Improve login process indication [Stéphane; #687113]
* Omit empty categories in apps view [Stéphane; #687970]
* Style panel differently according to mode [Florian; #684573]
* Make it possible to hide the user name [Matthias; #688577]
* Consolidate and improve chat connection notifications [Giovanni; #687213]
* Improve notification scrollbar appearance [Carlos; #688393]
* Fade scroll view fade near scrolling edges [Jasper; #689249]
* Add a read-only org.gnome.Shell.Mode property [Debarshi; #689300]
* Don't close message tray after using context menus [Giovanni; #689296]
* Port swipe-scrolling to ClutterPanAction [Jasper, Florian; #689062, #689552]
* Remember state of 'Remember Password' checkbox [Ron; #688039]
* Improve timestamp format in chat notifications [Carlos; #680989]
* Improve style of missed-messages counter [Carlos; #686472]
* Omit connection failure notifications if cancelled by user [Giovanni; #684823]
* Add window-based Alt-Tab popup [Florian; #688913]
* Support external session mode definitions [Florian; #689304]
* Support session-mode-specific extensions [Florian; #689305]
* Support 'parentMode' property in session modes [Florian; #689308]
* Support a new org.gnome.ShellSearchProvider2 DBus interface
[Cosimo; #689735, #690009]
* Add "windows" to Ctrl-Alt-Tab popup [Jasper; #689653]
* Port PopupMenu to GrabHelper [Jasper; #689109, #689954]
* Show headphone icon when headphones are plugged in [Giovanni; #675902]
* Display (non-app) search results as list [Tanner, Cosimo; #681797]
* Skip diacritical marks in search terms [Aleksander; #648587]
* Expose all engine options in input sources [Giovanni, Rui; #682318]
* Add input source switcher popup [Rui; #682315]
* Add minimal support for InfiniBand in network menu [Dan; #677150]
* Misc bug fixes and cleanups [Sebastian, Aleksander, Giovanni, Tim, Cosimo,
Florian, Matthias, Rui, Lionel, Colin, Piotr, Guillaume, Bastien, Tanner,
Carlos, Stéphane, Jakub; #688422, #688379, #688750, #688771, #686800,
#688133, #688895, #688966, #683986, #688004, #689108, #689029, #683449,
#688196, #689304, #689243, #689295, #689325, #689400, #679168, #689568,
#689537, #689528, #689749, #689789, #689353, #689820, #689868, #689778,
#689959, #688589, #688589, #689955, #687250, #689965, #690046, #690049,
#689884, #682286, #690173, #690174, #672941, #689876, #687881, #690171,
#690241, #690312, #690175, #687955, #650843, #688234, #690427
Contributors:
Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Stéphane Démurget,
Guillaume Desmottes, Tanner Doshier, Piotr Drąg, Sebastian Keller,
Lionel Landwerlin, Tim Lunn, Victoria Martínez de la Cruz, Aleksander Morgado,
Florian Müllner, Bastien Nocera, Marc Plano-Lesay, Carlos Soriano Sánchez,
Jakub Steiner, Jasper St. Pierre, Colin Walters, Dan Winship, Ron Yorston
Translations:
Yuri Myasoedov [ru], Wouter Bolsterlee [nl], Yaron Shahrabani [he],
Nilamdyuti Goswami [as], Ani Peter [ml], Kjartan Maraas [nb],
Dr.T.Vasudevan [ta], A S Alam [pa], Shankar Prasad [kn], Khaled Hosny [ar],
Daniel Mustieles [es], Dušan Kazik [sk]
3.7.2
=====
* Enforce RTL in he for messages that might end up as LTR [Florian; #686630]

View File

@ -1,5 +1,5 @@
AC_PREREQ(2.63)
AC_INIT([gnome-shell],[3.7.2],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_INIT([gnome-shell],[3.7.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])
@ -16,8 +16,6 @@ m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
# Checks for programs.
AC_PROG_CC
# Needed for per-target cflags, like in gnomeshell-taskpanel
AM_PROG_CC_C_O
# Initialize libtool
LT_PREREQ([2.2.6])
@ -62,15 +60,14 @@ fi
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
CLUTTER_MIN_VERSION=1.11.11
CLUTTER_MIN_VERSION=1.13.4
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
GJS_MIN_VERSION=1.33.2
MUTTER_MIN_VERSION=3.7.2
GJS_MIN_VERSION=1.35.4
MUTTER_MIN_VERSION=3.7.5
GTK_MIN_VERSION=3.3.9
GIO_MIN_VERSION=2.35.0
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
@ -78,7 +75,8 @@ STARTUP_NOTIFICATION_MIN_VERSION=0.11
GCR_MIN_VERSION=3.3.90
GNOME_DESKTOP_REQUIRED_VERSION=3.7.1
GNOME_MENUS_REQUIRED_VERSION=3.5.3
NETWORKMANAGER_MIN_VERSION=0.9.7
NETWORKMANAGER_MIN_VERSION=0.9.6
PULSE_MIN_VERS=2.0
# Collect more than 20 libraries for a prize!
PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
@ -95,7 +93,7 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION
gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
libcanberra
libcanberra libcanberra-gtk3
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
@ -103,30 +101,14 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
gnome-keyring-1 gcr-3 >= $GCR_MIN_VERSION)
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
GNOME_KEYBINDINGS_KEYSDIR=`$PKG_CONFIG --variable keysdir gnome-keybindings`
AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
saved_CFLAGS=$CFLAGS
saved_LIBS=$LIBS
CFLAGS=$GNOME_SHELL_CFLAGS
LIBS=$GNOME_SHELL_LIBS
AC_CHECK_FUNCS(JS_NewGlobalObject XFixesCreatePointerBarrier)
CFLAGS=$saved_CFLAGS
LIBS=$saved_LIBS
PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11)
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
PKG_CHECK_MODULES(TRAY, gtk+-3.0)
PKG_CHECK_MODULES(GVC, libpulse libpulse-mainloop-glib gobject-2.0)
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.7.2)
PKG_CHECK_MODULES(GVC, libpulse >= $PULSE_MIN_VERS libpulse-mainloop-glib gobject-2.0)
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.7.4)
AC_MSG_CHECKING([for bluetooth support])
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
@ -146,32 +128,18 @@ PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedatas
AC_SUBST(CALENDAR_SERVER_CFLAGS)
AC_SUBST(CALENDAR_SERVER_LIBS)
AC_ARG_WITH(systemd,
AS_HELP_STRING([--with-systemd],
[Add systemd support]),
[with_systemd=$withval], [with_systemd=auto])
GNOME_KEYBINDINGS_KEYSDIR=`$PKG_CONFIG --variable keysdir gnome-keybindings`
AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
PKG_CHECK_MODULES(SYSTEMD,
[libsystemd-login libsystemd-daemon],
[have_systemd=yes], [have_systemd=no])
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
if test "x$with_systemd" = "xauto" ; then
if test x$have_systemd = xno ; then
use_systemd=no
else
use_systemd=yes
fi
else
use_systemd=$with_systemd
fi
if test "x$use_systemd" = "xyes"; then
if test "x$have_systemd" = "xno"; then
AC_MSG_ERROR([Systemd support explicitly required, but systemd not found])
fi
AC_DEFINE(WITH_SYSTEMD, 1, [systemd support])
fi
saved_CFLAGS=$CFLAGS
saved_LIBS=$LIBS
CFLAGS=$GNOME_SHELL_CFLAGS
LIBS=$GNOME_SHELL_LIBS
AC_CHECK_FUNCS(XFixesCreatePointerBarrier)
CFLAGS=$saved_CFLAGS
LIBS=$saved_LIBS
MUTTER_GIR_DIR=`$PKG_CONFIG --variable=girdir libmutter`
MUTTER_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter`
@ -198,16 +166,6 @@ fi
# Sets GLIB_GENMARSHAL and GLIB_MKENUMS
AM_PATH_GLIB_2_0()
G_IR_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0`
AC_SUBST(G_IR_SCANNER)
G_IR_COMPILER=`$PKG_CONFIG --variable=g_ir_compiler gobject-introspection-1.0`
AC_SUBST(G_IR_COMPILER)
G_IR_GENERATE=`$PKG_CONFIG --variable=g_ir_generate gobject-introspection-1.0`
AC_SUBST(G_IR_GENERATE)
GIRDIR=`$PKG_CONFIG --variable=girdir gobject-introspection-1.0`
AC_SUBST(GIRDIR)
TYPELIBDIR="$($PKG_CONFIG --variable=typelibdir gobject-introspection-1.0)"
AC_SUBST(TYPELIBDIR)
GTK_DOC_CHECK([1.15], [--flavour no-tmpl])
@ -223,32 +181,7 @@ if test "$enable_man" != no; then
fi
AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no)
# Stay command-line compatible with the gnome-common configure option. Here
# minimum/yes/maximum are the same, however.
AC_ARG_ENABLE(compile_warnings,
AS_HELP_STRING([--enable-compile-warnings=@<:@no/minimum/yes/maximum/error@:>@],[Turn on compiler warnings]),,
enable_compile_warnings=error)
changequote(,)dnl
if test "$enable_compile_warnings" != no ; then
if test "x$GCC" = "xyes"; then
case " $CFLAGS " in
*[\ \ ]-Wall[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Wall" ;;
esac
case " $CFLAGS " in
*[\ \ ]-Wmissing-prototypes[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Wmissing-prototypes" ;;
esac
if test "$enable_compile_warnings" = error ; then
case " $CFLAGS " in
*[\ \ ]-Werror[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Werror -Wno-error=deprecated-declarations" ;;
esac
fi
fi
fi
changequote([,])dnl
GNOME_COMPILE_WARNINGS([error])
AC_ARG_ENABLE(jhbuild-wrapper-script,
AS_HELP_STRING([--enable-jhbuild-wrapper-script],[Make "gnome-shell" script work for jhbuild]),,enable_jhbuild_wrapper_script=no)

View File

@ -12,6 +12,7 @@ desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
introspectiondir = $(datadir)/dbus-1/interfaces
introspection_DATA = \
org.gnome.Shell.Screenshot.xml \
org.gnome.ShellSearchProvider.xml \
org.gnome.ShellSearchProvider2.xml

View File

@ -0,0 +1,128 @@
<!DOCTYPE node PUBLIC
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
<node>
<!--
org.gnome.Shell.Screenshot:
@short_description: Screenshot interface
The interface used to capture pictures of the screen contents.
-->
<interface name="org.gnome.Shell.Screenshot">
<!--
Screenshot:
@filename: The filename for the screenshot
@include_cursor: Whether to include the cursor image or not
@flash: Whether to flash the screen or not
@success: whether the screenshot was captured
@filename_used: the file where the screenshot was saved
Takes a screenshot of the whole screen and saves it
in @filename as png image, it returns a boolean
indicating whether the operation was successful or not.
@filename can either be an absolute path or a basename, in
which case the screenshot will be saved in the $XDG_PICTURES_DIR
or the home directory if it doesn't exist. The filename used
to save the screenshot will be returned in @filename_used.
-->
<method name="Screenshot">
<arg type="b" direction="in" name="include_cursor"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<!--
ScreenshotWindow:
@include_frame: Whether to include the frame or not
@include_cursor: Whether to include the cursor image or not
@flash: Whether to flash the window area or not
@filename: The filename for the screenshot
@success: whether the screenshot was captured
@filename_used: the file where the screenshot was saved
Takes a screenshot of the focused window (optionally omitting the frame)
and saves it in @filename as png image, it returns a boolean
indicating whether the operation was successful or not.
@filename can either be an absolute path or a basename, in
which case the screenshot will be saved in the $XDG_PICTURES_DIR
or the home directory if it doesn't exist. The filename used
to save the screenshot will be returned in @filename_used.
-->
<method name="ScreenshotWindow">
<arg type="b" direction="in" name="include_frame"/>
<arg type="b" direction="in" name="include_cursor"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<!--
ScreenshotArea:
@x: the X coordinate of the area to capture
@y: the Y coordinate of the area to capture
@width: the width of the area to capture
@height: the height of the area to capture
@flash: whether to flash the area or not
@filename: the filename for the screenshot
@success: whether the screenshot was captured
@filename_used: the file where the screenshot was saved
Takes a screenshot of the passed in area and saves it
in @filename as png image, it returns a boolean
indicating whether the operation was successful or not.
@filename can either be an absolute path or a basename, in
which case the screenshot will be saved in the $XDG_PICTURES_DIR
or the home directory if it doesn't exist. The filename used
to save the screenshot will be returned in @filename_used.
-->
<method name="ScreenshotArea">
<arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="y"/>
<arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<!--
FlashArea:
@x: the X coordinate of the area to flash
@y: the Y coordinate of the area to flash
@width: the width of the area to flash
@height: the height of the area to flash
Renders a flash spot effect in the specified rectangle of the screen.
-->
<method name="FlashArea">
<arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="y"/>
<arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/>
</method>
<!--
SelectArea:
@x: the X coordinate of the selected area
@y: the Y coordinate of the selected area
@width: the width of the selected area
@height: the height of the selected area
Interactively allows the user to select a rectangular area of
the screen, and returns its coordinates.
-->
<method name="SelectArea">
<arg type="i" direction="out" name="x"/>
<arg type="i" direction="out" name="y"/>
<arg type="i" direction="out" name="width"/>
<arg type="i" direction="out" name="height"/>
</method>
</interface>
</node>

View File

@ -65,11 +65,6 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
menuitem in single-user, single-session situations.
</_description>
</key>
<key name="show-full-name" type="b">
<default>true</default>
<_summary>Show full name in the user menu</_summary>
<_description>Whether the users full name is shown in the user menu or not.</_description>
</key>
<key name="remember-mount-password" type="b">
<default>false</default>
<_summary>Whether to remember password for mounting encrypted or remote filesystems</_summary>

View File

@ -206,11 +206,12 @@ StScrollBar StButton#vhandle:active {
}
.popup-separator-menu-item {
-gradient-height: 2px;
-gradient-start: rgba(8,8,8,0);
-gradient-end: #333333;
-margin-horizontal: 1.5em;
height: 1em;
-gradient-height: 1px;
-gradient-start: rgba(255,255,255,0.0);
-gradient-end: rgba(255,255,255,0.3);
-margin-horizontal: 24px;
height: 1px;
padding: 8px 0px;
}
.popup-alternating-menu-item:alternate {
@ -251,6 +252,10 @@ StScrollBar StButton#vhandle:active {
icon-size: 1.09em;
}
.popup-battery-percentage {
padding-left: 24px;
}
/* Switches */
.toggle-switch {
width: 65px;
@ -287,13 +292,17 @@ StScrollBar StButton#vhandle:active {
.hotplug-notification-item,
.hotplug-resident-eject-button,
.modal-dialog-button {
font-weight: bold;
border: 1px solid #8b8b8b;
background-gradient-direction: vertical;
background-gradient-start: rgba(255, 255, 255, 0.2);
background-gradient-end: rgba(255, 255, 255, 0);
}
.dash-search-button,
.modal-dialog-button {
font-weight: bold;
}
.dash-search-button:hover,
.notification-button:hover,
.notification-icon-button:hover,
@ -354,7 +363,7 @@ StScrollBar StButton#vhandle:active {
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;
transition-duration: 300ms;
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.6);
}
@ -389,7 +398,7 @@ StScrollBar StButton#vhandle:active {
color: rgb(64, 64, 64);
caret-color: rgb(64, 64, 64);
font-weight: bold;
transition-duration: 0;
transition-duration: 0ms;
}
.notification StEntry,
@ -420,7 +429,7 @@ StScrollBar StButton#vhandle:active {
background-color: black;
font-weight: bold;
height: 1.86em;
transition-duration: 250;
transition-duration: 250ms;
}
#panel.lock-screen {
@ -503,7 +512,7 @@ StScrollBar StButton#vhandle:active {
-minimum-hpadding: 6px;
font-weight: bold;
color: #ccc;
transition-duration: 100;
transition-duration: 100ms;
}
#panel.unlock-screen .panel-button,
@ -558,6 +567,10 @@ StScrollBar StButton#vhandle:active {
spacing: 4px;
}
.lock-screen-status-button-box {
spacing: 8px;
}
/* User Menu */
#panelUserMenu {
@ -613,17 +626,25 @@ StScrollBar StButton#vhandle:active {
/* Overview */
#overview {
spacing: 40px;
spacing: 24px;
}
#overview-group {
spacing: 32px;
}
.workspaces-display {
spacing: 32px; /* needs to be the same value as #overview-group */
}
.window-caption {
spacing: 25px;
}
.workspace-controls {
visible-width: 32px; /* Amount visible before hovering */
}
.workspace-thumbnails-background {
border: 1px solid rgba(128, 128, 128, 0.4);
border-right: 0px;
@ -641,7 +662,6 @@ StScrollBar StButton#vhandle:active {
.workspace-thumbnails {
spacing: 11px;
visible-width: 96px;
}
.workspace-thumbnail-indicator {
@ -703,8 +723,12 @@ StScrollBar StButton#vhandle:active {
}
.window-picker {
-horizontal-spacing: 40px;
-vertical-spacing: 40px;
-horizontal-spacing: 32px;
-vertical-spacing: 32px;
}
.window-picker.external-monitor {
padding: 32px;
}
/* Dash */
@ -731,10 +755,6 @@ StScrollBar StButton#vhandle:active {
height: 24px;
}
#viewSelector {
spacing: 1em;
}
/* Search Box */
#searchEntry {
@ -755,15 +775,22 @@ StScrollBar StButton#vhandle:active {
/* Search Results */
#searchResults {
padding: 20px 10px 10px 10px;
spacing: 18px;
}
#searchResultsContent {
spacing: 16px;
#searchResultsBin {
max-width: 1000px;
}
/* for scrollbars */
padding-left: 20px;
#searchResultsContent {
padding-right: 20px;
spacing: 16px;
}
#searchResultsContent:rtl {
padding-right: 0px;
padding-left: 20px;
}
.search-section {
@ -833,10 +860,15 @@ StScrollBar StButton#vhandle:active {
}
.all-app {
padding: 16px;
padding: 16px 25px 16px 16px;
spacing: 20px;
}
.all-app:rtl {
padding-right: 16px;
padding-left: 25px;
}
.app-filter {
font-weight: bold;
height: 2.85em;
@ -898,7 +930,7 @@ StScrollBar StButton#vhandle:active {
border-radius: 4px;
padding: 3px;
border: 1px rgba(0,0,0,0);
transition-duration: 100;
transition-duration: 100ms;
text-align: center;
}
@ -915,7 +947,7 @@ StScrollBar StButton#vhandle:active {
.grid-search-result:hover .overview-icon {
background-color: rgba(255,255,255,0.1);
text-shadow: black 0px 2px 2px;
transition-duration: 100;
transition-duration: 100ms;
color:white;
}
@ -931,19 +963,20 @@ StScrollBar StButton#vhandle:active {
color: white;
}
.show-apps:checked > .overview-icon {
.show-apps:checked > .overview-icon,
.show-apps:active > .overview-icon {
background-gradient-start: rgba(255, 255, 255, .05);
background-gradient-end: rgba(255, 255, 255, .15);
background-gradient-direction: vertical;
border-radius: 4px;
box-shadow: inset 0px 1px 2px 0px rgba(0, 0, 0, 1);
transition-duration: 100;
transition-duration: 100ms;
}
.show-apps:checked .show-apps-icon,
.show-apps:focus .show-apps-icon {
color: white;
transition-duration: 100;
transition-duration: 100ms;
}
.app-well-app:focus > .overview-icon,
@ -982,7 +1015,7 @@ StScrollBar StButton#vhandle:active {
-minimum-hpadding: 6px;
font-weight: bold;
color: #ccc;
transition-duration: 100;
transition-duration: 100ms;
padding-left: .3em;
padding-right: .3em;
}
@ -1285,7 +1318,7 @@ StScrollBar StButton#vhandle:active {
#message-tray {
background: #2e3436 url(message-tray-background.png);
background-repeat: repeat;
transition-duration: 250;
height: 72px;
}
#message-tray:keyboard {
@ -1298,6 +1331,10 @@ StScrollBar StButton#vhandle:active {
outline: 1px solid rgba(128, 128, 128, 0.3);
}
.message-tray-summary {
height: 72px;
}
.no-messages-label {
font-family: cantarell, sans-serif;
font-size: 11pt;
@ -1306,9 +1343,9 @@ StScrollBar StButton#vhandle:active {
.notification {
border-radius: 10px 10px 0px 0px;
background: rgba(0,0,0,0.8);
background: rgba(0,0,0,0.9);
padding: 8px 8px 4px 8px;
spacing-rows: 10px;
spacing-rows: 4px;
spacing-columns: 10px;
}
@ -1338,7 +1375,7 @@ StScrollBar StButton#vhandle:active {
.summary-boxpointer {
-arrow-border-radius: 15px;
-arrow-background-color: rgba(0,0,0,0.8);
-arrow-background-color: rgba(0,0,0,0.9);
-arrow-base: 36px;
-arrow-rise: 18px;
color: white;
@ -1388,6 +1425,7 @@ StScrollBar StButton#vhandle:active {
}
.notification-actions {
padding-top: 18px;
spacing: 10px;
}
@ -1526,10 +1564,6 @@ StScrollBar StButton#vhandle:active {
border-radius: 4px;
}
#summary-mode {
height: 72px;
}
.summary-source-button {
padding: 6px 3px 6px 3px;
}
@ -1554,7 +1588,7 @@ StScrollBar StButton#vhandle:active {
.summary-source {
border-radius: 4px;
padding: 0 6px 0 6px;
transition-duration: 100;
transition-duration: 100ms;
}
.summary-source-counter {
@ -2383,6 +2417,7 @@ StScrollBar StButton#vhandle:active {
.screen-shield-notifications-box {
spacing: 18px;
max-width: 34em;
}
.screen-shield-notification-source {
@ -2416,3 +2451,8 @@ StScrollBar StButton#vhandle:active {
padding-bottom: 0px;
}
.input-source-switcher-symbol {
font-size: 42pt;
width: 96px;
height: 96px;
}

View File

@ -14,7 +14,7 @@
height="16"
id="svg12430"
version="1.1"
inkscape:version="0.48+devel r11908 custom"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="more-results.svg">
<defs
id="defs12432" />
@ -26,8 +26,8 @@
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="7.4498765"
inkscape:cy="9.9072581"
inkscape:cx="8.3155237"
inkscape:cy="0.89548874"
inkscape:document-units="px"
inkscape:current-layer="g14642-3-0"
showgrid="false"
@ -35,8 +35,8 @@
inkscape:showpageshadow="false"
inkscape:window-width="2560"
inkscape:window-height="1376"
inkscape:window-x="1600"
inkscape:window-y="27"
inkscape:window-x="1200"
inkscape:window-y="187"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
@ -50,7 +50,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
@ -69,11 +69,27 @@
inkscape:radius="0"
inkscape:original="M 145.1875 400 C 144.5248 400 144 400.54899 144 401.21875 L 144 410.78125 C 144 411.45101 144.5248 412 145.1875 412 L 154.8125 412 C 155.4752 412 156 411.45101 156 410.78125 L 156 401.21875 C 156 400.54899 155.4752 400 154.8125 400 L 145.1875 400 z M 149 403 L 151 403 L 151 405 L 153 405 L 153 407 L 151 407 L 151 409 L 149 409 L 149 407 L 147 407 L 147 405 L 149 405 L 149 403 z "
xlink:href="#rect11749-5-0-1-8"
style="color:#bebebe;fill:#000000;fill-opacity:0.38207546;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible"
style="color:#bebebe;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;opacity:0.8"
id="path13004"
inkscape:href="#rect11749-5-0-1-8"
d="M 145.1875,400 C 144.5248,400 144,400.54899 144,401.21875 l 0,9.5625 c 0,0.66976 0.5248,1.21875 1.1875,1.21875 l 9.625,0 c 0.6627,0 1.1875,-0.54899 1.1875,-1.21875 l 0,-9.5625 C 156,400.54899 155.4752,400 154.8125,400 L 145.1875,400 z m 3.8125,3 2,0 0,2 2,0 0,2 -2,0 0,2 -2,0 0,-2 -2,0 0,-2 2,0 L 149,403 Z"
transform="translate(0,1)" />
<use
x="0"
y="0"
xlink:href="#path13004"
id="use11960"
transform="translate(1,-1)"
width="16"
height="16" />
<use
x="0"
y="0"
xlink:href="#use11960"
id="use11962"
transform="translate(-2,0)"
width="16"
height="16" />
<path
inkscape:connector-curvature="0"
style="color:#bebebe;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible"

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -88,11 +88,18 @@ doc-gen-org.gnome.Shell.SearchProvider2.xml: $(top_srcdir)/data/org.gnome.ShellS
--generate-docbook doc-gen \
$(top_srcdir)/data/org.gnome.ShellSearchProvider2.xml
doc-gen-org.gnome.Shell.Screenshot.xml: $(top_srcdir)/data/org.gnome.Shell.Screenshot.xml
gdbus-codegen \
--interface-prefix org.gnome.Shell.Screenshot. \
--generate-docbook doc-gen \
$(top_srcdir)/data/org.gnome.Shell.Screenshot.xml
# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
content_files= \
doc-gen-org.gnome.Shell.SearchProvider.xml \
doc-gen-org.gnome.Shell.SearchProvider2.xml
doc-gen-org.gnome.Shell.SearchProvider2.xml \
doc-gen-org.gnome.Shell.Screenshot.xml
# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
# These files must be listed here *and* in content_files

View File

@ -50,7 +50,6 @@
<xi:include href="xml/shell-xfixes-cursor.xml"/>
<xi:include href="xml/shell-util.xml"/>
<xi:include href="xml/shell-mount-operation.xml"/>
<xi:include href="xml/shell-mobile-providers.xml"/>
<xi:include href="xml/shell-network-agent.xml"/>
<xi:include href="xml/shell-polkit-authentication-agent.xml"/>
<xi:include href="xml/shell-tp-client.xml"/>

View File

@ -66,4 +66,11 @@ its dependencies to build from tarballs.</description>
<gnome:userid>fmuellner</gnome:userid>
</foaf:Person>
</maintainer>
<maintainer>
<foaf:Person>
<foaf:name>Ray Strode</foaf:name>
<foaf:mbox rdf:resource="mailto:halfline@gmail.com" />
<gnome:userid>halfline</gnome:userid>
</foaf:Person>
</maintainer>
</Project>

View File

@ -28,6 +28,7 @@ nobase_dist_js_DATA = \
misc/extensionUtils.js \
misc/fileUtils.js \
misc/gnomeSession.js \
misc/hash.js \
misc/history.js \
misc/jsParse.js \
misc/loginManager.js \
@ -41,7 +42,6 @@ nobase_dist_js_DATA = \
ui/boxpointer.js \
ui/calendar.js \
ui/checkBox.js \
ui/centerLayout.js \
ui/ctrlAltTab.js \
ui/dash.js \
ui/dateMenu.js \
@ -50,7 +50,6 @@ nobase_dist_js_DATA = \
ui/extensionSystem.js \
ui/extensionDownloader.js \
ui/environment.js \
ui/flashspot.js \
ui/ibusCandidatePopup.js\
ui/grabHelper.js \
ui/iconGrid.js \
@ -75,6 +74,7 @@ nobase_dist_js_DATA = \
ui/popupMenu.js \
ui/remoteSearch.js \
ui/runDialog.js \
ui/screenshot.js \
ui/screenShield.js \
ui/scripting.js \
ui/search.js \

View File

@ -128,6 +128,7 @@ const UserListItem = new Lang.Class({
let layout = new St.BoxLayout({ vertical: false });
this.actor = new St.Button({ style_class: 'login-dialog-user-list-item',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
can_focus: true,
child: layout,
reactive: true,
@ -492,6 +493,7 @@ const SessionListItem = new Lang.Class({
this.id = id;
this.actor = new St.Button({ style_class: 'login-dialog-session-list-item',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
can_focus: true,
reactive: true,
x_fill: true,
@ -532,6 +534,7 @@ const SessionListItem = new Lang.Class({
color.alpha / 255);
cr.arc(width / 2, height / 2, width / 3, 0, 2 * Math.PI);
cr.fill();
cr.$dispose();
},
_onClicked: function() {
@ -551,6 +554,7 @@ const SessionList = new Lang.Class({
this.actor.child = this._box;
this._button = new St.Button({ style_class: 'login-dialog-session-list-button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
can_focus: true,
x_fill: true,
y_fill: true });
@ -673,7 +677,7 @@ const LoginDialog = new Lang.Class({
this.parent({ shellReactive: true,
styleClass: 'login-dialog',
parentActor: parentActor,
keybindingMode: Main.KeybindingMode.LOGIN_SCREEN,
keybindingMode: Shell.KeyBindingMode.LOGIN_SCREEN,
shouldFadeIn: false });
this.connect('destroy',
Lang.bind(this, this._onDestroy));
@ -786,6 +790,7 @@ const LoginDialog = new Lang.Class({
let notListedLabel = new St.Label({ text: _("Not listed?"),
style_class: 'login-dialog-not-listed-label' });
this._notListedButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
can_focus: true,
child: notListedLabel,
reactive: true,

View File

@ -58,6 +58,7 @@ const SessionManagerIface = <interface name="org.gnome.SessionManager">
<arg type="u" direction="in" />
<arg type="b" direction="out" />
</method>
<property name="SessionIsActive" type="b" access="read"/>
<signal name="InhibitorAdded">
<arg type="o" direction="out"/>
</signal>

141
js/misc/hash.js Normal file
View File

@ -0,0 +1,141 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const System = imports.system;
const Params = imports.misc.params;
// This is an implementation of EcmaScript SameValue algorithm,
// which returns true if two values are not observably distinguishable.
// It was taken from http://wiki.ecmascript.org/doku.php?id=harmony:egal
//
// In the future, we may want to use the 'is' operator instead.
function _sameValue(x, y) {
if (x === y) {
// 0 === -0, but they are not identical
return x !== 0 || 1 / x === 1 / y;
}
// NaN !== NaN, but they are identical.
// NaNs are the only non-reflexive value, i.e., if x !== x,
// then x is a NaN.
// isNaN is broken: it converts its argument to number, so
// isNaN("foo") => true
return x !== x && y !== y;
}
const _hashers = {
object: function(o) { return o ? System.addressOf(o) : 'null'; },
function: function(f) { return System.addressOf(f); },
string: function(s) { return s; },
number: function(n) { return String(n); },
undefined: function() { return 'undefined'; },
};
/* Map is meant to be similar in usage to ES6 Map, which is
described at http://wiki.ecmascript.org/doku.php?id=harmony:simple_maps_and_sets,
without requiring more than ES5 + Gjs extensions.
Known differences from other implementations:
Polyfills around the web usually implement HashMaps for
primitive values and reversed WeakMaps for object keys,
but we want real maps with real O(1) semantics in all cases,
and the easiest way is to have different hashers for different
types.
Known differences from the ES6 specification:
- Map is a Lang.Class, not a ES6 class, so inheritance,
prototype, sealing, etc. work differently.
- items(), keys() and values() don't return iterators,
they return actual arrays, so they incur a full copy everytime
they're called, and they don't see changes if you mutate
the table while iterating
(admittedly, the ES6 spec is a bit unclear on this, and
the reference code would just blow up)
*/
const Map = new Lang.Class({
Name: 'Map',
_init: function(iterable) {
this._pool = { };
if (iterable) {
for (let i = 0; i < iterable.length; i++) {
let [key, value] = iterable[i];
this.set(key, value);
}
}
},
_hashKey: function(key) {
let type = typeof(key);
return type + ':' + _hashers[type](key);
},
_internalGet: function(key) {
let hash = this._hashKey(key);
let node = this._pool[hash];
if (node && _sameValue(node.key, key))
return [true, node.value];
else
return [false, null];
},
get: function(key) {
return this._internalGet(key)[1];
},
has: function(key) {
return this._internalGet(key)[0];
},
set: function(key, value) {
let hash = this._hashKey(key);
let node = this._pool[hash];
if (node) {
node.key = key;
node.value = value;
} else {
this._pool[hash] = { key: key, value: value };
}
},
delete: function(key) {
let hash = this._hashKey(key);
let node = this._pool[hash];
if (node && _sameValue(node.key, key)) {
delete this._pool[hash];
return [node.key, node.value];
} else {
return [null, null];
}
},
keys: function() {
let pool = this._pool;
return Object.getOwnPropertyNames(pool).map(function(hash) {
return pool[hash].key;
});
},
values: function() {
let pool = this._pool;
return Object.getOwnPropertyNames(pool).map(function(hash) {
return pool[hash].value;
});
},
items: function() {
let pool = this._pool;
return Object.getOwnPropertyNames(pool).map(function(hash) {
return [pool[hash].key, pool[hash].value];
});
},
size: function() {
return Object.getOwnPropertyNames(this._pool).length;
},
});

View File

@ -5,7 +5,7 @@ const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
const UPowerGlib = imports.gi.UPowerGlib;
const Signals = imports.signals;
const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager'>
<method name='PowerOff'>
@ -26,6 +26,16 @@ const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager
<method name='CanSuspend'>
<arg type='s' direction='out'/>
</method>
<method name='Inhibit'>
<arg type='s' direction='in'/>
<arg type='s' direction='in'/>
<arg type='s' direction='in'/>
<arg type='s' direction='in'/>
<arg type='h' direction='out'/>
</method>
<signal name='PrepareForSleep'>
<arg type='b' direction='out'/>
</signal>
</interface>;
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
@ -51,12 +61,6 @@ const ConsoleKitManagerIface = <interface name='org.freedesktop.ConsoleKit.Manag
</interface>;
const ConsoleKitSessionIface = <interface name='org.freedesktop.ConsoleKit.Session'>
<method name='IsActive'>
<arg type='b' direction='out' />
</method>
<signal name='ActiveChanged'>
<arg type='b' direction='out' />
</signal>
<signal name='Lock' />
<signal name='Unlock' />
</interface>;
@ -93,6 +97,8 @@ const LoginManagerSystemd = new Lang.Class({
this._proxy = new SystemdLoginManager(Gio.DBus.system,
'org.freedesktop.login1',
'/org/freedesktop/login1');
this._proxy.connectSignal('PrepareForSleep',
Lang.bind(this, this._prepareForSleep));
},
// Having this function is a bit of a hack since the Systemd and ConsoleKit
@ -109,10 +115,6 @@ const LoginManagerSystemd = new Lang.Class({
return this._currentSession;
},
get sessionActive() {
return Shell.session_is_active_for_systemd();
},
canPowerOff: function(asyncCallback) {
this._proxy.CanPowerOffRemote(function(result, error) {
if (error)
@ -150,8 +152,33 @@ const LoginManagerSystemd = new Lang.Class({
suspend: function() {
this._proxy.SuspendRemote(true);
},
inhibit: function(reason, callback) {
let inVariant = GLib.Variant.new('(ssss)',
['sleep',
'GNOME Shell',
reason,
'delay']);
this._proxy.call_with_unix_fd_list('Inhibit', inVariant, 0, -1, null, null,
Lang.bind(this, function(proxy, result) {
let fd = -1;
try {
let [outVariant, fdList] = proxy.call_with_unix_fd_list_finish(result);
fd = fdList.steal_fds(outVariant.deep_unpack())[0];
callback(new Gio.UnixInputStream({ fd: fd }));
} catch(e) {
logError(e, "Error getting systemd inhibitor");
callback(null);
}
}));
},
_prepareForSleep: function(proxy, sender, [aboutToSuspend]) {
this.emit('prepare-for-sleep', aboutToSuspend);
}
});
Signals.addSignalMethods(LoginManagerSystemd.prototype);
const LoginManagerConsoleKit = new Lang.Class({
Name: 'LoginManagerConsoleKit',
@ -160,7 +187,6 @@ const LoginManagerConsoleKit = new Lang.Class({
this._proxy = new ConsoleKitManager(Gio.DBus.system,
'org.freedesktop.ConsoleKit',
'/org/freedesktop/ConsoleKit/Manager');
this._upClient = new UPowerGlib.Client();
},
// Having this function is a bit of a hack since the Systemd and ConsoleKit
@ -177,19 +203,6 @@ const LoginManagerConsoleKit = new Lang.Class({
return this._currentSession;
},
get sessionActive() {
if (this._sessionActive !== undefined)
return this._sessionActive;
let session = this.getCurrentSessionProxy();
session.connectSignal('ActiveChanged', Lang.bind(this, function(object, senderName, [isActive]) {
this._sessionActive = isActive;
}));
[this._sessionActive] = session.IsActiveSync();
return this._sessionActive;
},
canPowerOff: function(asyncCallback) {
this._proxy.CanStopRemote(function(result, error) {
if (error)
@ -209,10 +222,7 @@ const LoginManagerConsoleKit = new Lang.Class({
},
canSuspend: function(asyncCallback) {
Mainloop.idle_add(Lang.bind(this, function() {
asyncCallback(this._upClient.get_can_suspend());
return false;
}));
return false;
},
powerOff: function() {
@ -224,6 +234,12 @@ const LoginManagerConsoleKit = new Lang.Class({
},
suspend: function() {
this._upClient.suspend_sync(null);
this.emit('prepare-for-sleep', true);
this.emit('prepare-for-sleep', false);
},
inhibit: function(reason, callback) {
callback(null);
}
});
Signals.addSignalMethods(LoginManagerConsoleKit.prototype);

View File

@ -2,9 +2,93 @@
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const NMGtk = imports.gi.NMGtk;
const Signals = imports.signals;
// _getMobileProvidersDatabase:
//
// Gets the database of mobile providers, with references between MCCMNC/SID and
// operator name
//
let _mpd;
function _getMobileProvidersDatabase() {
if (_mpd == null) {
try {
_mpd = new NMGtk.MobileProvidersDatabase();
_mpd.init(null);
} catch (e) {
log(e.message);
_mpd = null;
}
}
return _mpd;
}
// _findProviderForMccMnc:
// @operator_name: operator name
// @operator_code: operator code
//
// Given an operator name string (which may not be a real operator name) and an
// operator code string, tries to find a proper operator name to display.
//
function _findProviderForMccMnc(operator_name, operator_code) {
if (operator_name) {
if (operator_name.length != 0 &&
(operator_name.length > 6 || operator_name.length < 5)) {
// this looks like a valid name, i.e. not an MCCMNC (that some
// devices return when not yet connected
return operator_name;
}
if (isNaN(parseInt(operator_name))) {
// name is definitely not a MCCMNC, so it may be a name
// after all; return that
return operator_name;
}
}
let needle;
if ((!operator_name || operator_name.length == 0) && operator_code)
needle = operator_code;
else if (operator_name && (operator_name.length == 6 || operator_name.length == 5))
needle = operator_name;
else // nothing to search
return null;
let mpd = _getMobileProvidersDatabase();
if (mpd) {
let provider = mpd.lookup_3gpp_mcc_mnc(needle);
if (provider)
return provider.get_name();
}
return null;
}
// _findProviderForSid:
// @sid: System Identifier of the serving CDMA network
//
// Tries to find the operator name corresponding to the given SID
//
function _findProviderForSid(sid) {
if (sid == 0)
return null;
let mpd = _getMobileProvidersDatabase();
if (mpd) {
let provider = mpd.lookup_cdma_sid(sid);
if (provider)
return provider.get_name();
}
return null;
}
//------------------------------------------------------------------------------
// Support for the old ModemManager interface (MM < 0.7)
//------------------------------------------------------------------------------
// The following are not the complete interfaces, just the methods we need
// (or may need in the future)
@ -42,76 +126,6 @@ const ModemCdmaInterface = <interface name="org.freedesktop.ModemManager.Modem.C
const ModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(ModemCdmaInterface);
let _providersTable;
function _getProvidersTable() {
if (_providersTable)
return _providersTable;
return _providersTable = Shell.mobile_providers_parse(null,null);
}
function findProviderForMCCMNC(table, needle) {
let needlemcc = needle.substring(0, 3);
let needlemnc = needle.substring(3, needle.length);
let name2, name3;
for (let iter in table) {
let country = table[iter];
let providers = country.get_providers();
// Search through each country's providers
for (let i = 0; i < providers.length; i++) {
let provider = providers[i];
// Search through MCC/MNC list
let list = provider.get_gsm_mcc_mnc();
for (let j = 0; j < list.length; j++) {
let mccmnc = list[j];
// Match both 2-digit and 3-digit MNC; prefer a
// 3-digit match if found, otherwise a 2-digit one.
if (mccmnc.mcc != needlemcc)
continue; // MCC was wrong
if (!name3 && needle.length == 6 && needlemnc == mccmnc.mnc)
name3 = provider.name;
if (!name2 && needlemnc.substring(0, 2) == mccmnc.mnc.substring(0, 2))
name2 = provider.name;
if (name2 && name3)
break;
}
}
}
return name3 || name2 || null;
}
function findProviderForSid(table, sid) {
if (sid == 0)
return null;
// Search through each country
for (let iter in table) {
let country = table[iter];
let providers = country.get_providers();
// Search through each country's providers
for (let i = 0; i < providers.length; i++) {
let provider = providers[i];
let cdma_sid = provider.get_cdma_sid();
// Search through CDMA SID list
for (let j = 0; j < cdma_sid.length; j++) {
if (cdma_sid[j] == sid)
return provider.name;
}
}
}
return null;
}
const ModemGsm = new Lang.Class({
Name: 'ModemGsm',
@ -127,7 +141,7 @@ const ModemGsm = new Lang.Class({
this.emit('notify::signal-quality');
}));
this._proxy.connectSignal('RegistrationInfo', Lang.bind(this, function(proxy, sender, [status, code, name]) {
this.operator_name = this._findOperatorName(name, code);
this.operator_name = _findProviderForMccMnc(name, code);
this.emit('notify::operator-name');
}));
this._proxy.GetRegistrationInfoRemote(Lang.bind(this, function([result], err) {
@ -137,7 +151,7 @@ const ModemGsm = new Lang.Class({
}
let [status, code, name] = result;
this.operator_name = this._findOperatorName(name, code);
this.operator_name = _findProviderForMccMnc(name, code);
this.emit('notify::operator-name');
}));
this._proxy.GetSignalQualityRemote(Lang.bind(this, function(result, err) {
@ -150,32 +164,6 @@ const ModemGsm = new Lang.Class({
}
this.emit('notify::signal-quality');
}));
},
_findOperatorName: function(name, opCode) {
if (name) {
if (name && name.length != 0 && (name.length > 6 || name.length < 5)) {
// this looks like a valid name, i.e. not an MCCMNC (that some
// devices return when not yet connected
return name;
}
if (isNaN(parseInt(name))) {
// name is definitely not a MCCMNC, so it may be a name
// after all; return that
return name;
}
}
let needle;
if ((name == null || name.length == 0) && opCode)
needle = opCode;
else if (name.length == 6 || name.length == 5)
needle = name;
else // nothing to search
return null;
let table = _getProvidersTable();
return findProviderForMCCMNC(table, needle);
}
});
Signals.addSignalMethods(ModemGsm.prototype);
@ -215,15 +203,99 @@ const ModemCdma = new Lang.Class({
// it will return an error if the device is not connected
this.operator_name = null;
} else {
let [bandClass, band, id] = result;
if (name.length > 0) {
let table = _getProvidersTable();
this.operator_name = findProviderForSid(table, id);
} else
this.operator_name = null;
let [bandClass, band, sid] = result;
this.operator_name = _findProviderForSid(sid)
}
this.emit('notify::operator-name');
}));
}
});
Signals.addSignalMethods(ModemCdma.prototype);
//------------------------------------------------------------------------------
// Support for the new ModemManager1 interface (MM >= 0.7)
//------------------------------------------------------------------------------
const BroadbandModemInterface = <interface name="org.freedesktop.ModemManager1.Modem">
<property name="SignalQuality" type="(ub)" access="read" />
</interface>;
const BroadbandModemProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemInterface);
const BroadbandModem3gppInterface = <interface name="org.freedesktop.ModemManager1.Modem.Modem3gpp">
<property name="OperatorCode" type="s" access="read" />
<property name="OperatorName" type="s" access="read" />
</interface>;
const BroadbandModem3gppProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModem3gppInterface);
const BroadbandModemCdmaInterface = <interface name="org.freedesktop.ModemManager1.Modem.ModemCdma">
<property name="Sid" type="u" access="read" />
</interface>;
const BroadbandModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemCdmaInterface);
const BroadbandModem = new Lang.Class({
Name: 'BroadbandModem',
_init: function(path, capabilities) {
this._proxy = new BroadbandModemProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
this._proxy_3gpp = new BroadbandModem3gppProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
this._proxy_cdma = new BroadbandModemCdmaProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
this._capabilities = capabilities;
this._proxy.connect('g-properties-changed', Lang.bind(this, function(proxy, properties) {
if ('SignalQuality' in properties.deep_unpack())
this._reloadSignalQuality();
}));
this._reloadSignalQuality();
this._proxy_3gpp.connect('g-properties-changed', Lang.bind(this, function(proxy, properties) {
let unpacked = properties.deep_unpack();
if ('OperatorName' in unpacked || 'OperatorCode' in unpacked)
this._reload3gppOperatorName();
}));
this._reload3gppOperatorName();
this._proxy_cdma.connect('g-properties-changed', Lang.bind(this, function(proxy, properties) {
let unpacked = properties.deep_unpack();
if ('Nid' in unpacked || 'Sid' in unpacked)
this._reloadCdmaOperatorName();
}));
this._reloadCdmaOperatorName();
},
_reloadSignalQuality: function() {
let [quality, recent] = this._proxy.SignalQuality;
this.signal_quality = quality;
this.emit('notify::signal-quality');
},
_reloadOperatorName: function() {
let new_name = "";
if (this.operator_name_3gpp && this.operator_name_3gpp.length > 0)
new_name += this.operator_name_3gpp;
if (this.operator_name_cdma && this.operator_name_cdma.length > 0) {
if (new_name != "")
new_name += ", ";
new_name += this.operator_name_cdma;
}
this.operator_name = new_name;
this.emit('notify::operator-name');
},
_reload3gppOperatorName: function() {
let name = this._proxy_3gpp.OperatorName;
let code = this._proxy_3gpp.OperatorCode;
this.operator_name_3gpp = _findProviderForMccMnc(name, code);
this._reloadOperatorName();
},
_reloadCdmaOperatorName: function() {
let sid = this._proxy_cdma.Sid;
this.operator_name_cdma = _findProviderForSid(sid);
this._reloadOperatorName();
}
});
Signals.addSignalMethods(BroadbandModem.prototype);

View File

@ -153,6 +153,7 @@ const ViewByCategories = new Lang.Class({
y_fill: false,
style_class: 'vfade' });
this._categoryScroll.add_actor(this._categoryBox);
this._categoryScroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
this.actor.add(this._view.actor, { expand: true, x_fill: true, y_fill: true });
this.actor.add(this._categoryScroll, { expand: false, y_fill: false, y_align: St.Align.START });

View File

@ -220,25 +220,8 @@ const BoxPointer = new Lang.Class({
this.bin.allocate(childBox, flags);
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);
}
this._reposition();
this._updateFlip();
}
},
@ -405,11 +388,11 @@ const BoxPointer = new Lang.Class({
cr.setLineWidth(borderWidth);
cr.stroke();
}
cr.$dispose();
},
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();
@ -417,7 +400,8 @@ const BoxPointer = new Lang.Class({
this._sourceActor = sourceActor;
this._arrowAlignment = alignment;
this._reposition(sourceActor, alignment);
this._reposition();
this._updateFlip();
},
setSourceAlignment: function(alignment) {
@ -429,7 +413,10 @@ const BoxPointer = new Lang.Class({
this.setPosition(this._sourceActor, this._arrowAlignment);
},
_reposition: function(sourceActor, alignment) {
_reposition: function() {
let sourceActor = this._sourceActor;
let alignment = this._arrowAlignment;
// Position correctly relative to the sourceActor
let sourceNode = sourceActor.get_theme_node();
let sourceContentBox = sourceNode.get_content_box(sourceActor.get_allocation_box());
@ -553,7 +540,7 @@ const BoxPointer = new Lang.Class({
_shiftActor : function() {
// Since the position of the BoxPointer depends on the allocated size
// of the BoxPointer and the position of the source actor, trying
// to position the BoxPoiner via the x/y properties will result in
// to position the BoxPointer via the x/y properties will result in
// allocation loops and warnings. Instead we do the positioning via
// the anchor point, which is independent of allocation, and leave
// x == y == 0.
@ -561,37 +548,47 @@ const BoxPointer = new Lang.Class({
-(this._yPosition + this._yOffset));
},
_shouldFlip: function() {
_calculateArrowSide: function(arrowSide) {
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 [minWidth, minHeight, boxWidth, boxHeight] = this._container.get_preferred_size();
let monitor = Main.layoutManager.findMonitorForActor(this.actor);
switch (this._arrowSide) {
switch (arrowSide) {
case St.Side.TOP:
if (boxAllocation.y2 > monitor.y + monitor.height &&
if (sourceAllocation.y2 + boxHeight > monitor.y + monitor.height &&
boxHeight < sourceAllocation.y1 - monitor.y)
return true;
return St.Side.BOTTOM;
break;
case St.Side.BOTTOM:
if (boxAllocation.y1 < monitor.y &&
if (sourceAllocation.y1 - boxHeight < monitor.y &&
boxHeight < monitor.y + monitor.height - sourceAllocation.y2)
return true;
return St.Side.TOP;
break;
case St.Side.LEFT:
if (boxAllocation.x2 > monitor.x + monitor.width &&
if (sourceAllocation.y2 + boxWidth > monitor.x + monitor.width &&
boxWidth < sourceAllocation.x1 - monitor.x)
return true;
return St.Side.RIGHT;
break;
case St.Side.RIGHT:
if (boxAllocation.x1 < monitor.x &&
if (sourceAllocation.y1 - boxWidth < monitor.x &&
boxWidth < monitor.x + monitor.width - sourceAllocation.x2)
return true;
return St.Side.LEFT;
break;
}
return false;
return arrowSide;
},
_updateFlip: function() {
let arrowSide = this._calculateArrowSide(this._userArrowSide);
if (this._arrowSide != arrowSide) {
this._arrowSide = arrowSide;
this._reposition();
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
this._container.queue_relayout();
return false;
}));
}
},
set xOffset(offset) {

View File

@ -11,7 +11,6 @@ const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
const MSECS_IN_DAY = 24 * 60 * 60 * 1000;
const WEEKDATE_HEADER_WIDTH_DIGITS = 3;
const SHOW_WEEKDATE_KEY = 'show-weekdate';
// in org.gnome.desktop.interface
@ -95,15 +94,6 @@ function _getCalendarWeekForDate(date) {
return weekNumber;
}
function _getDigitWidth(actor){
let context = actor.get_pango_context();
let themeNode = actor.get_theme_node();
let font = themeNode.get_font();
let metrics = context.get_metrics(font, context.get_language());
let width = metrics.get_approximate_digit_width();
return width;
}
function _getCalendarDayAbbreviation(dayNumber) {
let abbreviations = [
/* Translators: Calendar grid abbreviation for Sunday.
@ -345,8 +335,6 @@ const Calendar = new Lang.Class({
_init: function() {
this._weekStart = Shell.util_get_week_start();
this._weekdate = NaN;
this._digitWidth = NaN;
this._settings = new Gio.Settings({ schema: 'org.gnome.shell.calendar' });
this._settings.connect('changed::' + SHOW_WEEKDATE_KEY, Lang.bind(this, this._onSettingsChange));
@ -419,8 +407,6 @@ const Calendar = new Lang.Class({
this.actor.add(this._topBox,
{ row: 0, col: 0, col_span: offsetCols + 7 });
this.actor.connect('style-changed', Lang.bind(this, this._onStyleChange));
let back = new St.Button({ style_class: 'calendar-change-month-back' });
this._topBox.add(back);
back.connect('clicked', Lang.bind(this, this._onPrevMonthButtonClicked));
@ -457,18 +443,6 @@ const Calendar = new Lang.Class({
this._firstDayIndex = this.actor.get_n_children();
},
_onStyleChange: function(actor, event) {
// width of a digit in pango units
this._digitWidth = _getDigitWidth(this.actor) / Pango.SCALE;
this._setWeekdateHeaderWidth();
},
_setWeekdateHeaderWidth: function() {
if (this.digitWidth != NaN && this._useWeekdate && this._weekdateHeader) {
this._weekdateHeader.set_width (this._digitWidth * WEEKDATE_HEADER_WIDTH_DIGITS);
}
},
_onScroll : function(actor, event) {
switch (event.get_scroll_direction()) {
case Clutter.ScrollDirection.UP:

View File

@ -1,60 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Clutter = imports.gi.Clutter;
const CenterLayout = new Lang.Class({
Name: 'CenterLayout',
Extends: Clutter.BoxLayout,
vfunc_allocate: function(container, box, flags) {
let rtl = container.get_text_direction() == Clutter.TextDirection.RTL;
let availWidth = box.x2 - box.x1;
let availHeight = box.y2 - box.y1;
// Assume that these are the first three widgets and they are all visible.
let [left, center, right] = container.get_children();
// Only support horizontal layouts for now.
let [leftMinWidth, leftNaturalWidth] = left.get_preferred_width(availHeight);
let [centerMinWidth, centerNaturalWidth] = center.get_preferred_width(availHeight);
let [rightMinWidth, rightNaturalWidth] = right.get_preferred_width(availHeight);
let sideWidth = (availWidth - centerMinWidth) / 2;
let childBox = new Clutter.ActorBox();
childBox.y1 = box.y1;
childBox.y2 = box.y1 + availHeight;
let leftSide = Math.min(Math.floor(sideWidth), leftNaturalWidth);
let rightSide = Math.min(Math.floor(sideWidth), rightNaturalWidth);
if (rtl) {
childBox.x1 = availWidth - leftSide;
childBox.x2 = availWidth;
} else {
childBox.x1 = 0;
childBox.x2 = leftSide;
}
childBox.x1 += box.x1;
left.allocate(childBox, flags);
let maxSide = Math.max(leftSide, rightSide);
let sideWidth = Math.max((availWidth - centerNaturalWidth) / 2, maxSide);
childBox.x1 = box.x1 + Math.ceil(sideWidth);
childBox.x2 = box.x2 - Math.ceil(sideWidth);
center.allocate(childBox, flags);
if (rtl) {
childBox.x1 = 0;
childBox.x2 = rightSide;
} else {
childBox.x1 = availWidth - rightSide;
childBox.x2 = availWidth;
}
childBox.x1 += box.x1;
right.allocate(childBox, flags);
}
});

View File

@ -8,7 +8,6 @@ const Params = imports.misc.params;
const Shell = imports.gi.Shell;
const GnomeSession = imports.misc.gnomeSession;
const LoginManager = imports.misc.loginManager;
const Main = imports.ui.main;
const ShellMountOperation = imports.ui.shellMountOperation;
@ -33,7 +32,6 @@ const AutomountManager = new Lang.Class({
Lang.bind(this, this._InhibitorsChanged));
this._inhibited = false;
this._loginManager = LoginManager.getLoginManager();
this._volumeMonitor = Gio.VolumeMonitor.get();
},
@ -85,25 +83,29 @@ const AutomountManager = new Lang.Class({
_onDriveConnected: function() {
// if we're not in the current ConsoleKit session,
// or screensaver is active, don't play sounds
if (!this._loginManager.sessionActive)
if (!this._session.SessionIsActive)
return;
global.play_theme_sound(0, 'device-added-media');
global.play_theme_sound(0, 'device-added-media',
_("External drive connected"),
null);
},
_onDriveDisconnected: function() {
// if we're not in the current ConsoleKit session,
// or screensaver is active, don't play sounds
if (!this._loginManager.sessionActive)
if (!this._session.SessionIsActive)
return;
global.play_theme_sound(0, 'device-removed-media');
global.play_theme_sound(0, 'device-removed-media',
_("External drive disconnected"),
null);
},
_onDriveEjectButton: function(monitor, drive) {
// TODO: this code path is not tested, as the GVfs volume monitor
// doesn't emit this signal just yet.
if (!this._loginManager.sessionActive)
if (!this._session.SessionIsActive)
return;
// we force stop/eject in this case, so we don't have to pass a
@ -143,7 +145,7 @@ const AutomountManager = new Lang.Class({
if (params.checkSession) {
// if we're not in the current ConsoleKit session,
// don't attempt automount
if (!this._loginManager.sessionActive)
if (!this._session.SessionIsActive)
return;
}

View File

@ -4,7 +4,6 @@ const Lang = imports.lang;
const Gio = imports.gi.Gio;
const St = imports.gi.St;
const LoginManager = imports.misc.loginManager;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const ShellMountOperation = imports.ui.shellMountOperation;
@ -162,8 +161,6 @@ const AutorunManager = new Lang.Class({
Name: 'AutorunManager',
_init: function() {
this._loginManager = LoginManager.getLoginManager();
this._volumeMonitor = Gio.VolumeMonitor.get();
this._transDispatcher = new AutorunTransientDispatcher(this);
@ -215,7 +212,7 @@ const AutorunManager = new Lang.Class({
_onMountAdded: function(monitor, mount) {
// don't do anything if our session is not the currently
// active one
if (!this._loginManager.sessionActive)
if (!this._session.SessionIsActive)
return;
this._processMount(mount, true);
@ -293,7 +290,6 @@ const AutorunResidentSource = new Lang.Class({
_init: function(manager) {
this.parent(_("Removable Devices"), 'media-removable');
this.showInLockScreen = false;
this._mounts = [];
@ -301,6 +297,10 @@ const AutorunResidentSource = new Lang.Class({
this._notification = new AutorunResidentNotification(this._manager, this);
},
_createPolicy: function() {
return new MessageTray.NotificationPolicy({ showInLockScreen: false });
},
buildRightClickMenu: function() {
return null;
},

View File

@ -60,18 +60,14 @@ const KeyringDialog = new Lang.Class({
this._controlTable = null;
let buttons = [{ label: '',
action: Lang.bind(this, this._onCancelButton),
key: Clutter.Escape
},
{ label: '',
action: Lang.bind(this, this._onContinueButton),
default: true
}]
this.setButtons(buttons);
this._cancelButton = buttons[0].button;
this._continueButton = buttons[1].button;
this._cancelButton = this.addButton({ label: '',
action: Lang.bind(this, this._onCancelButton),
key: Clutter.Escape });
this._continueButton = this.addButton({ label: '',
action: Lang.bind(this, this._onContinueButton),
default: true },
{ expand: true, x_fill: false, x_align: St.Align.END });
this.prompt.bind_property('cancel-label', this._cancelButton, 'label', GObject.BindingFlags.SYNC_CREATE);
this.prompt.bind_property('continue-label', this._continueButton, 'label', GObject.BindingFlags.SYNC_CREATE);
@ -146,6 +142,14 @@ const KeyringDialog = new Lang.Class({
this._messageBox.add(table, { x_fill: true, y_fill: true });
},
_updateSensitivity: function(sensitive) {
this._passwordEntry.reactive = sensitive;
this._passwordEntry.clutter_text.editable = sensitive;
this._continueButton.can_focus = sensitive;
this._continueButton.reactive = sensitive;
},
_ensureOpen: function() {
// NOTE: ModalDialog.open() is safe to call if the dialog is
// already open - it just returns true without side-effects
@ -167,12 +171,14 @@ const KeyringDialog = new Lang.Class({
_onShowPassword: function(prompt) {
this._buildControlTable();
this._ensureOpen();
this._updateSensitivity(true);
this._passwordEntry.grab_key_focus();
},
_onShowConfirm: function(prompt) {
this._buildControlTable();
this._ensureOpen();
this._updateSensitivity(true);
this._continueButton.grab_key_focus();
},
@ -192,6 +198,7 @@ const KeyringDialog = new Lang.Class({
},
_onContinueButton: function() {
this._updateSensitivity(false);
this.prompt.complete();
},

View File

@ -613,7 +613,8 @@ const NetworkAgent = new Lang.Class({
this._vpnRequests = { };
this._native.auto_register = false;
this._native.unregister();
if (this._native.registered)
this._native.unregister();
},
_newRequest: function(agent, requestId, connection, settingName, hints, flags) {

View File

@ -159,14 +159,13 @@ const AuthenticationDialog = new Lang.Class({
messageBox.add(this._nullMessageLabel);
this._nullMessageLabel.show();
this.setButtons([{ label: _("Cancel"),
action: Lang.bind(this, this.cancel),
key: Clutter.Escape
},
{ label: _("Authenticate"),
action: Lang.bind(this, this._onAuthenticateButtonPressed),
default: true
}]);
this._cancelButton = this.addButton({ label: _("Cancel"),
action: Lang.bind(this, this.cancel),
key: Clutter.Escape });
this._okButton = this.addButton({ label: _("Authenticate"),
action: Lang.bind(this, this._onAuthenticateButtonPressed),
default: true },
{ expand: true, x_fill: false, x_align: St.Align.END });
this._doneEmitted = false;
@ -214,8 +213,17 @@ const AuthenticationDialog = new Lang.Class({
}
},
_updateSensitivity: function(sensitive) {
this._passwordEntry.reactive = sensitive;
this._passwordEntry.clutter_text.editable = sensitive;
this._okButton.can_focus = sensitive;
this._okButton.reactive = sensitive;
},
_onEntryActivate: function() {
let response = this._passwordEntry.get_text();
this._updateSensitivity(false);
this._session.response(response);
// When the user responds, dismiss already shown info and
// error texts (if any)
@ -269,6 +277,7 @@ const AuthenticationDialog = new Lang.Class({
this._passwordBox.show();
this._passwordEntry.set_text('');
this._passwordEntry.grab_key_focus();
this._updateSensitivity(true);
this._ensureOpen();
},

View File

@ -20,8 +20,8 @@ const Recorder = new Lang.Class({
Main.wm.addKeybinding('toggle-recording',
this._bindingSettings,
Meta.KeyBindingFlags.NONE,
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._toggleRecorder));
},

View File

@ -13,6 +13,7 @@ const Tp = imports.gi.TelepathyGLib;
const History = imports.misc.history;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const NotificationDaemon = imports.ui.notificationDaemon;
const Params = imports.misc.params;
const PopupMenu = imports.ui.popupMenu;
@ -415,6 +416,8 @@ const TelepathyClient = new Lang.Class({
_ensureAppSource: function() {
if (this._appSource == null) {
this._appSource = new MessageTray.Source(_("Chat"), 'empathy');
this._appSource.policy = new NotificationDaemon.NotificationApplicationPolicy('empathy');
Main.messageTray.add(this._appSource);
this._appSource.connect('destroy', Lang.bind(this, function () {
this._appSource = null;
@ -484,6 +487,10 @@ const ChatSource = new Lang.Class({
return rightClickMenu;
},
_createPolicy: function() {
return new NotificationDaemon.NotificationApplicationPolicy('empathy');
},
_updateAlias: function() {
let oldAlias = this.title;
let newAlias = this._contact.get_alias();
@ -1048,6 +1055,10 @@ const ApproverSource = new Lang.Class({
}));
},
_createPolicy: function() {
return new NotificationDaemon.NotificationApplicationPolicy('empathy');
},
destroy: function() {
if (this._invalidId != 0) {
this._dispatchOp.disconnect(this._invalidId);
@ -1346,9 +1357,8 @@ const AccountNotification = new Lang.Class({
this.connect('action-invoked', Lang.bind(this, function(self, action) {
switch (action) {
case 'view':
let cmd = '/usr/bin/empathy-accounts'
+ ' --select-account=%s'
.format(account.get_path_suffix());
let cmd = 'empathy-accounts --select-account=' +
account.get_path_suffix();
let app_info = Gio.app_info_create_from_commandline(cmd, null, 0);
app_info.launch([], global.create_app_launch_context());
break;

View File

@ -102,8 +102,7 @@ const CtrlAltTabManager = new Lang.Class({
icon = app.create_icon_texture(POPUP_APPICON_SIZE);
else
icon = textureCache.bind_pixbuf_property(windows[i], 'icon');
items.push({ window: windows[i],
name: windows[i].title,
items.push({ name: windows[i].title,
iconActor: icon,
sortGroup: SortGroup.MIDDLE });
}

View File

@ -371,7 +371,6 @@ const Dash = new Lang.Class({
this._maxHeight = -1;
this.iconSize = 64;
this._shownInitially = false;
this._ignoreHeight = false;
this._dragPlaceholder = null;
this._dragPlaceholderPos = -1;
@ -397,10 +396,7 @@ const Dash = new Lang.Class({
this.actor = new St.Bin({ child: this._container });
this.actor.connect('notify::height', Lang.bind(this,
function() {
if (this._ignoreHeight)
return;
if (this._maxHeight != this.actor.height);
if (this._maxHeight != this.actor.height)
this._queueRedisplay();
this._maxHeight = this.actor.height;
}));
@ -425,8 +421,6 @@ const Dash = new Lang.Class({
Lang.bind(this, this._onDragCancelled));
Main.overview.connect('window-drag-end',
Lang.bind(this, this._onDragEnd));
Main.overview.connect('showing',
Lang.bind(this, this._onOverviewShowing));
},
_onDragBegin: function() {
@ -929,65 +923,6 @@ const Dash = new Lang.Class({
}));
return true;
},
_computeTranslation: function() {
let rtl = (this.actor.get_text_direction() == Clutter.TextDirection.RTL);
if (rtl)
return this.actor.width;
else
return - this.actor.width;
},
_onOverviewShowing: function() {
// reset any translation and make sure the actor is visible when
// entering the overview
this.slideX = 0;
this.actor.show();
},
get slideX() {
return this._slideX;
},
set slideX(value) {
this._slideX = value;
this.actor.translation_x = this._slideX;
if (this._slideX > 0) {
let rect = new Clutter.Rect();
rect.size.width = this.actor.width - this._slideX;
rect.size.height = this.actor.height;
this.actor.clip_rect = rect;
} else {
this.actor.clip_rect = null;
}
},
show: function() {
this.actor.show();
Tweener.addTween(this, { slideX: 0,
transition: 'easeOutQuad',
time: DASH_ANIMATION_TIME,
onComplete: Lang.bind(this,
function() {
this._ignoreHeight = false;
})
});
},
hide: function() {
this._ignoreHeight = true;
let hiddenX = this._computeTranslation();
Tweener.addTween(this, { slideX: hiddenX,
transition: 'easeOutQuad',
time: DASH_ANIMATION_TIME,
onComplete: Lang.bind(this,
function() {
this.actor.hide();
})
});
}
});

View File

@ -32,6 +32,7 @@ function _onVertSepRepaint (area)
cr.setDash([1, 3], 1); // Hard-code for now
cr.setLineWidth(stippleWidth);
cr.stroke();
cr.$dispose();
};
const DateMenuButton = new Lang.Class({
@ -83,12 +84,26 @@ const DateMenuButton = new Lang.Class({
}));
vbox.add(this._calendar.actor);
item = this.menu.addSettingsAction(_("Date and Time Settings"), 'gnome-datetime-panel.desktop');
if (item) {
let separator = new PopupMenu.PopupSeparatorMenuItem();
separator.setColumnWidths(1);
vbox.add(separator.actor, {y_align: St.Align.END, expand: true, y_fill: false});
let separator = new PopupMenu.PopupSeparatorMenuItem();
separator.setColumnWidths(1);
vbox.add(separator.actor, {y_align: St.Align.END, expand: true, y_fill: false});
this._openCalendarItem = new PopupMenu.PopupMenuItem(_("Open Calendar"));
this._openCalendarItem.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
this._openCalendarItem.actor.can_focus = false;
vbox.add(this._openCalendarItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
this._openClocksItem = new PopupMenu.PopupMenuItem(_("Open Clocks"));
this._openClocksItem.connect('activate', Lang.bind(this, this._onOpenClocksActivate));
this._openClocksItem.actor.can_focus = false;
vbox.add(this._openClocksItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
Shell.AppSystem.get_default().connect('installed-changed',
Lang.bind(this, this._appInstalledChanged));
this._appInstalledChanged();
item = this.menu.addSettingsAction(_("Date & Time Settings"), 'gnome-datetime-panel.desktop');
if (item) {
item.actor.show_on_set_parent = false;
item.actor.can_focus = false;
item.actor.reparent(vbox);
@ -108,16 +123,6 @@ const DateMenuButton = new Lang.Class({
// Event list
vbox.add(this._eventList.actor, { expand: true });
this._openCalendarItem = new PopupMenu.PopupMenuItem(_("Open Calendar"));
this._openCalendarItem.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
this._openCalendarItem.actor.can_focus = false;
vbox.add(this._openCalendarItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
this._calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' });
this._calendarSettings.connect('changed::exec',
Lang.bind(this, this._calendarSettingsChanged));
this._calendarSettingsChanged();
// Whenever the menu is opened, select today
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
if (isOpen) {
@ -151,10 +156,9 @@ const DateMenuButton = new Lang.Class({
this._sessionUpdated();
},
_calendarSettingsChanged: function() {
let exec = this._calendarSettings.get_string('exec');
let fullExec = GLib.find_program_in_path(exec);
this._openCalendarItem.actor.visible = fullExec != null;
_appInstalledChanged: function() {
let app = Shell.AppSystem.get_default().lookup_app('gnome-clocks.desktop');
this._openClocksItem.actor.visible = app !== null;
},
_setEventsVisibility: function(visible) {
@ -205,24 +209,14 @@ const DateMenuButton = new Lang.Class({
_onOpenCalendarActivate: function() {
this.menu.close();
let tool = this._calendarSettings.get_string('exec');
if (tool.length == 0 || tool.substr(0, 9) == 'evolution') {
// TODO: pass the selected day
let app = Shell.AppSystem.get_default().lookup_app('evolution-calendar.desktop');
app.activate();
} else {
let needTerm = this._calendarSettings.get_boolean('needs-term');
if (needTerm) {
let terminalSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.terminal' });
let term = terminalSettings.get_string('exec');
let arg = terminalSettings.get_string('exec-arg');
if (arg != '')
Util.spawn([term, arg, tool]);
else
Util.spawn([term, tool]);
} else {
Util.spawnCommandLine(tool)
}
}
let app = Gio.AppInfo.get_default_for_type('text/calendar', false);
app.launch([], global.create_app_launch_context());
},
_onOpenClocksActivate: function() {
this.menu.close();
let app = Shell.AppSystem.get_default().lookup_app('gnome-clocks.desktop');
app.activate();
}
});

View File

@ -106,11 +106,15 @@ function enableExtension(uuid) {
extensionOrder.push(uuid);
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;
let stylesheetNames = [global.session_mode + '.css', 'stylesheet.css'];
for (let i = 0; i < stylesheetNames.length; i++) {
let stylesheetFile = extension.dir.get_child(stylesheetNames[i]);
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;
break;
}
}
extension.stateObj.enable();
@ -129,6 +133,7 @@ function logExtensionError(uuid, error) {
extension.state = ExtensionState.ERROR;
if (!extension.errors)
extension.errors = [];
extension.errors.push(message);
log('Extension "%s" had error: %s'.format(uuid, message));
_signals.emit('extension-state-changed', { uuid: uuid,

View File

@ -1,45 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const FLASHSPOT_ANIMATION_TIME = 0.25; // seconds
const Flashspot = new Lang.Class({
Name: 'Flashspot',
Extends: Lightbox.Lightbox,
_init: function(area) {
this.parent(Main.uiGroup, { inhibitEvents: true,
width: area.width,
height: area.height });
this.actor.style_class = 'flashspot';
this.actor.set_position(area.x, area.y);
},
fire: function() {
this.actor.opacity = 0;
Tweener.addTween(this.actor,
{ opacity: 255,
time: FLASHSPOT_ANIMATION_TIME,
transition: 'linear',
onComplete: Lang.bind(this, this._onFireShowComplete)
});
this.actor.show();
},
_onFireShowComplete: function() {
Tweener.addTween(this.actor,
{ opacity: 0,
time: FLASHSPOT_ANIMATION_TIME,
transition: 'linear',
onComplete: Lang.bind(this, function() {
this.destroy();
})
});
}
});

View File

@ -183,6 +183,9 @@ const GrabHelper = new Lang.Class({
else if (hadFocus || params.grabFocus)
_navigateActor(newFocus);
if ((params.grabFocus || params.modal) && !this._capturedEventId)
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
this._grabStack.push(params);
return true;
},
@ -192,8 +195,6 @@ const GrabHelper = new Lang.Class({
if (firstGrab) {
if (!Main.pushModal(this._owner, this._modalParams))
return false;
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
}
this._modalCount++;
@ -205,11 +206,6 @@ const GrabHelper = new Lang.Class({
if (this._modalCount > 0)
return;
if (this._capturedEventId > 0) {
global.stage.disconnect(this._capturedEventId);
this._capturedEventId = 0;
}
Main.popModal(this._owner);
global.sync_pointer();
},
@ -308,6 +304,11 @@ const GrabHelper = new Lang.Class({
this._releaseFocusGrab();
}
if (!this.grabbed && this._capturedEventId > 0) {
global.stage.disconnect(this._capturedEventId);
this._capturedEventId = 0;
}
if (hadFocus) {
let poppedGrab = poppedGrabs[0];
_navigateActor(poppedGrab.savedFocus);
@ -316,6 +317,13 @@ const GrabHelper = new Lang.Class({
_onCapturedEvent: function(actor, event) {
let type = event.type();
if (type == Clutter.EventType.KEY_PRESS &&
event.get_key_symbol() == Clutter.KEY_Escape) {
this.ungrab({ isUser: true });
return true;
}
let press = type == Clutter.EventType.BUTTON_PRESS;
let release = type == Clutter.EventType.BUTTON_RELEASE;
let button = press || release;
@ -328,12 +336,6 @@ const GrabHelper = new Lang.Class({
if (!button && this._modalCount == 0)
return false;
if (type == Clutter.EventType.KEY_PRESS &&
event.get_key_symbol() == Clutter.KEY_Escape) {
this.ungrab({ isUser: true });
return true;
}
if (this._isWithinGrabbedActor(event.get_source()))
return false;

View File

@ -23,6 +23,8 @@ const BaseIcon = new Lang.Class({
this.actor._delegate = this;
this.actor.connect('style-changed',
Lang.bind(this, this._onStyleChanged));
this.actor.connect('destroy',
Lang.bind(this, this._onDestroy));
this._spacing = 0;
@ -52,6 +54,9 @@ const BaseIcon = new Lang.Class({
this._setSizeManually = params.setSizeManually;
this.icon = null;
let cache = St.TextureCache.get_default();
this._iconThemeChangedId = cache.connect('icon-theme-changed', Lang.bind(this, this._onIconThemeChanged));
},
_allocate: function(actor, box, flags) {
@ -146,7 +151,22 @@ const BaseIcon = new Lang.Class({
size = found ? len : ICON_SIZE;
}
if (this.iconSize == size && this._iconBin.child)
return;
this._createIconTexture(size);
},
_onDestroy: function() {
if (this._iconThemeChangedId > 0) {
let cache = St.TextureCache.get_default();
cache.disconnect(this._iconThemeChangedId);
this._iconThemeChangedId = 0;
}
},
_onIconThemeChanged: function() {
this._createIconTexture(this.iconSize);
}
});

View File

@ -2,18 +2,21 @@
const Caribou = imports.gi.Caribou;
const Clutter = imports.gi.Clutter;
const DBus = imports.dbus;
const Gdk = imports.gi.Gdk;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
const BoxPointer = imports.ui.boxpointer;
const Layout = imports.ui.layout;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const KEYBOARD_REST_TIME = Layout.KEYBOARD_ANIMATION_TIME * 2 * 1000;
const KEYBOARD_SCHEMA = 'org.gnome.shell.keyboard';
const KEYBOARD_TYPE = 'keyboard-type';
@ -56,14 +59,7 @@ const Key = new Lang.Class({
if (this._key.name == 'Control_L' || this._key.name == 'Alt_L')
this._key.latch = true;
this._key.connect('key-pressed', Lang.bind(this, function ()
{ this.actor.checked = true }));
this._key.connect('key-released', Lang.bind(this, function ()
{ this.actor.checked = false; }));
if (this._extended_keys.length > 0) {
this._grabbed = false;
this._eventCaptureId = 0;
this._key.connect('notify::show-subkeys', Lang.bind(this, this._onShowSubkeysChanged));
this._boxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
{ x_fill: true,
@ -115,52 +111,23 @@ const Key = new Lang.Class({
this._boxPointer.bin.add_actor(this._extended_keyboard);
},
_onEventCapture: function (actor, event) {
let source = event.get_source();
let type = event.type();
if ((type == Clutter.EventType.BUTTON_PRESS ||
type == Clutter.EventType.BUTTON_RELEASE) &&
this._extended_keyboard.contains(source)) {
source.extended_key.press();
source.extended_key.release();
return false;
}
if (type == Clutter.EventType.BUTTON_PRESS) {
this._boxPointer.actor.hide();
this._ungrab();
return true;
}
return false;
},
_ungrab: function () {
global.stage.disconnect(this._eventCaptureId);
this._eventCaptureId = 0;
this._grabbed = false;
Main.popModal(this.actor);
get subkeys() {
return this._boxPointer;
},
_onShowSubkeysChanged: function () {
if (this._key.show_subkeys) {
this.actor.fake_release();
this._boxPointer.actor.raise_top();
this._boxPointer.setPosition(this.actor, 0.5);
this._boxPointer.show(BoxPointer.PopupAnimation.FULL);
this.emit('show-subkeys');
this.actor.fake_release();
this.actor.set_hover(false);
if (!this._grabbed) {
Main.pushModal(this.actor);
this._eventCaptureId = global.stage.connect('captured-event', Lang.bind(this, this._onEventCapture));
this._grabbed = true;
}
this._key.release();
} else {
if (this._grabbed)
this._ungrab();
this._boxPointer.hide(BoxPointer.PopupAnimation.FULL);
this.emit('hide-subkeys');
}
}
});
Signals.addSignalMethods(Key.prototype);
const Keyboard = new Lang.Class({
// HACK: we can't set Name, because it collides with Name dbus property
@ -175,16 +142,26 @@ const Keyboard = new Lang.Class({
this._focusInExtendedKeys = false;
this._timestamp = global.display.get_current_time_roundtrip();
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
this._keyboardSettings = new Gio.Settings({ schema: KEYBOARD_SCHEMA });
this._keyboardSettings.connect('changed', Lang.bind(this, this._settingsChanged));
this._a11yApplicationsSettings = new Gio.Settings({ schema: A11Y_APPLICATIONS_SCHEMA });
this._a11yApplicationsSettings.connect('changed', Lang.bind(this, this._settingsChanged));
this._settingsChanged();
},
init: function () {
this._showIdleId = 0;
this._subkeysBoxPointer = null;
this._capturedEventId = 0;
this._capturedPress = false;
this._keyboardVisible = false;
Main.layoutManager.connect('keyboard-visible-changed', Lang.bind(this, function(o, visible) {
this._keyboardVisible = visible;
}));
this._keyboardRequested = false;
this._keyboardRestingId = 0;
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
this._redraw();
},
@ -273,10 +250,14 @@ const Keyboard = new Lang.Class({
return;
let time = global.get_current_time();
if (focus instanceof Clutter.Text)
this.Show(time);
else
if (!(focus instanceof Clutter.Text)) {
this.Hide(time);
return;
}
if (!this._showIdleId)
this._showIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE,
Lang.bind(this, function() { this.Show(time); }));
},
_addKeys: function () {
@ -331,6 +312,19 @@ const Keyboard = new Lang.Class({
return trayButton;
},
_onCapturedEvent: function(actor, event) {
let type = event.type();
let press = type == Clutter.EventType.BUTTON_PRESS;
let release = type == Clutter.EventType.BUTTON_RELEASE;
if (press)
this._capturedPress = true;
else if (release && this._capturedPress)
this._hideSubkeys();
return true;
},
_addRows : function (keys, layout) {
let keyboard_row = new St.BoxLayout();
for (let i = 0; i < keys.length; ++i) {
@ -353,6 +347,19 @@ const Keyboard = new Lang.Class({
// Add new key for hiding message tray
right_box.add(this._getTrayIcon());
}
button.connect('show-subkeys', Lang.bind(this, function() {
if (this._subkeysBoxPointer)
this._subkeysBoxPointer.hide(BoxPointer.PopupAnimation.FULL);
this._subkeysBoxPointer = button.subkeys;
this._subkeysBoxPointer.show(BoxPointer.PopupAnimation.FULL);
if (!this._capturedEventId)
this._capturedEventId = this.actor.connect('captured-event',
Lang.bind(this, this._onCapturedEvent));
}));
button.connect('hide-subkeys', Lang.bind(this, function() {
this._hideSubkeys();
}));
}
keyboard_row.add(left_box, { expand: true, x_fill: false, x_align: St.Align.START });
keyboard_row.add(right_box, { expand: true, x_fill: false, x_align: St.Align.END });
@ -462,7 +469,37 @@ const Keyboard = new Lang.Class({
actor._extended_keys || actor.extended_key;
},
_clearKeyboardRestTimer: function() {
if (!this._keyboardRestingId)
return;
GLib.source_remove(this._keyboardRestingId);
this._keyboardRestingId = 0;
},
show: function (monitor) {
this._keyboardRequested = true;
if (this._keyboardVisible) {
if (monitor != Main.layoutManager.keyboardIndex) {
Main.layoutManager.keyboardIndex = monitor;
this._redraw();
}
return;
}
this._clearKeyboardRestTimer();
this._keyboardRestingId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
KEYBOARD_REST_TIME,
Lang.bind(this, function() {
this._clearKeyboardRestTimer();
this._show(monitor);
}));
},
_show: function(monitor) {
if (!this._keyboardRequested)
return;
Main.layoutManager.keyboardIndex = monitor;
this._redraw();
Main.layoutManager.showKeyboard();
@ -470,10 +507,41 @@ const Keyboard = new Lang.Class({
},
hide: function () {
this._keyboardRequested = false;
if (!this._keyboardVisible)
return;
this._clearKeyboardRestTimer();
this._keyboardRestingId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
KEYBOARD_REST_TIME,
Lang.bind(this, function() {
this._clearKeyboardRestTimer();
this._hide();
}));
},
_hide: function() {
if (this._keyboardRequested)
return;
this._hideSubkeys();
Main.layoutManager.hideKeyboard();
this._createSource();
},
_hideSubkeys: function() {
if (this._subkeysBoxPointer) {
this._subkeysBoxPointer.hide(BoxPointer.PopupAnimation.FULL);
this._subkeysBoxPointer = null;
}
if (this._capturedEventId) {
this.actor.disconnect(this._capturedEventId);
this._capturedEventId = 0;
}
this._capturedPress = false;
},
_moveTemporarily: function () {
let currentWindow = global.screen.get_display().focus_window;
let rect = currentWindow.get_outer_rect();
@ -502,6 +570,13 @@ const Keyboard = new Lang.Class({
return one - two;
},
_clearShowIdle: function() {
if (!this._showIdleId)
return;
GLib.source_remove(this._showIdleId);
this._showIdleId = 0;
},
// D-Bus methods
Show: function(timestamp) {
if (!this._enableKeyboard)
@ -510,6 +585,8 @@ const Keyboard = new Lang.Class({
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
return;
this._clearShowIdle();
if (timestamp != Clutter.CURRENT_TIME)
this._timestamp = timestamp;
this.show(Main.layoutManager.focusIndex);
@ -522,6 +599,8 @@ const Keyboard = new Lang.Class({
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
return;
this._clearShowIdle();
if (timestamp != Clutter.CURRENT_TIME)
this._timestamp = timestamp;
this.hide();
@ -556,11 +635,7 @@ const KeyboardSource = new Lang.Class({
this.keepTrayOnSummaryClick = true;
},
handleSummaryClick: function() {
let event = Clutter.get_current_event();
if (event.type() != Clutter.EventType.BUTTON_RELEASE)
return false;
handleSummaryClick: function(button) {
this.open();
return true;
},

View File

@ -16,7 +16,7 @@ const Tweener = imports.ui.tweener;
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
const STARTUP_ANIMATION_TIME = 0.2;
const KEYBOARD_ANIMATION_TIME = 0.5;
const KEYBOARD_ANIMATION_TIME = 0.15;
const PLYMOUTH_TRANSITION_TIME = 1;
const MonitorConstraint = new Lang.Class({
@ -95,6 +95,12 @@ const MonitorConstraint = new Lang.Class({
}
});
const defaultParams = {
trackFullscreen: false,
affectsStruts: false,
affectsInputRegion: true
};
const LayoutManager = new Lang.Class({
Name: 'LayoutManager',
@ -108,9 +114,11 @@ const LayoutManager = new Lang.Class({
this._background = null;
this._leftPanelBarrier = 0;
this._rightPanelBarrier = 0;
this._trayBarrier = 0;
this._chrome = new Chrome(this);
this._inOverview = false;
this._updateRegionIdle = 0;
this._trackedActors = [];
this.screenShieldGroup = new St.Widget({ name: 'screenShieldGroup',
visible: false,
@ -136,24 +144,43 @@ const LayoutManager = new Lang.Class({
this.addChrome(this.keyboardBox);
this._keyboardHeightNotifyId = 0;
// Need to update struts on new workspaces when they are added
global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._queueUpdateRegions));
global.screen.connect('restacked',
Lang.bind(this, this._windowsRestacked));
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;
// Chrome.init() needs access to Main.overview, which didn't exist
// it needs access to Main.overview, which didn't exist
// yet when the LayoutManager was constructed.
init: function() {
this._chrome.init();
Main.overview.connect('showing', Lang.bind(this, this._overviewShowing));
Main.overview.connect('hidden', Lang.bind(this, this._overviewHidden));
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._startupAnimation();
},
_overviewShowing: function() {
this._inOverview = true;
this._updateVisibility();
this._queueUpdateRegions();
},
_overviewHidden: function() {
this._inOverview = false;
this._updateVisibility();
this._queueUpdateRegions();
},
_sessionUpdated: function() {
this._updateVisibility();
this._queueUpdateRegions();
},
_updateMonitors: function() {
let screen = global.screen;
@ -230,7 +257,7 @@ const LayoutManager = new Lang.Class({
let corner = new HotCorner();
this._hotCorners.push(corner);
corner.actor.set_position(cornerX, cornerY);
this._chrome.addActor(corner.actor);
this.addChrome(corner.actor);
}
},
@ -287,6 +314,9 @@ const LayoutManager = new Lang.Class({
this._updateMonitors();
this._updateBoxes();
this._updateHotCorners();
this._updateFullscreen();
this._updateVisibility();
this._queueUpdateRegions();
this.emit('monitors-changed');
},
@ -321,11 +351,11 @@ const LayoutManager = new Lang.Class({
global.stage_input_mode == Shell.StageInputMode.FULLSCREEN) {
let focusActor = global.stage.key_focus;
if (focusActor)
i = this._chrome.findIndexForActor(focusActor);
i = this.findIndexForActor(focusActor);
} else {
let focusWindow = global.display.focus_window;
if (focusWindow)
i = this._chrome.findIndexForWindow(focusWindow.get_compositor_private());
i = this.findIndexForWindow(focusWindow);
}
return i;
@ -374,8 +404,7 @@ const LayoutManager = new Lang.Class({
},
_fadeBackgroundComplete: function() {
// Don't animate the strut
this._chrome.freezeUpdateRegions();
this._freezeUpdateRegions();
if (this._background != null) {
this._background.destroy();
@ -393,7 +422,7 @@ const LayoutManager = new Lang.Class({
_startupAnimationComplete: function() {
this.emit('panel-box-changed');
this._chrome.thawUpdateRegions();
this._thawUpdateRegions();
},
showKeyboard: function () {
@ -420,7 +449,7 @@ const LayoutManager = new Lang.Class({
_showKeyboardComplete: function() {
// Poke Chrome to update the input shape; it doesn't notice
// anchor point changes
this._chrome.updateRegions();
this._updateRegions();
this._keyboardHeightNotifyId = this.keyboardBox.connect('notify::height', Lang.bind(this, function () {
this.keyboardBox.anchor_y = this.keyboardBox.height;
@ -437,7 +466,7 @@ const LayoutManager = new Lang.Class({
Tweener.addTween(this.keyboardBox,
{ anchor_y: 0,
time: immediate ? 0 : KEYBOARD_ANIMATION_TIME,
transition: 'easeOutQuad',
transition: 'easeInQuad',
onComplete: this._hideKeyboardComplete,
onCompleteScope: this
});
@ -454,7 +483,7 @@ const LayoutManager = new Lang.Class({
},
_hideKeyboardComplete: function() {
this._chrome.updateRegions();
this._updateRegions();
},
// addChrome:
@ -477,7 +506,8 @@ const LayoutManager = new Lang.Class({
// monitor (it will be hidden whenever a fullscreen window is visible,
// and shown otherwise)
addChrome: function(actor, params) {
this._chrome.addActor(actor, params);
Main.uiGroup.add_actor(actor);
this._trackActor(actor, params);
},
// trackChrome:
@ -492,7 +522,26 @@ const LayoutManager = new Lang.Class({
// 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);
let ancestor = actor.get_parent();
let index = this._findActor(ancestor);
while (ancestor && index == -1) {
ancestor = ancestor.get_parent();
index = this._findActor(ancestor);
}
if (!ancestor)
throw new Error('actor is not a descendent of a chrome actor');
let ancestorData = this._trackedActors[index];
if (!params)
params = {};
// We can't use Params.parse here because we want to drop
// the extra values like ancestorData.actor
for (let prop in defaultParams) {
if (!params.hasOwnProperty(prop))
params[prop] = ancestorData[prop];
}
this._trackActor(actor, params);
},
// untrackChrome:
@ -500,7 +549,7 @@ const LayoutManager = new Lang.Class({
//
// Undoes the effect of trackChrome()
untrackChrome: function(actor) {
this._chrome.untrackActor(actor);
this._untrackActor(actor);
},
// removeChrome:
@ -508,11 +557,348 @@ const LayoutManager = new Lang.Class({
//
// Removes @actor from the chrome
removeChrome: function(actor) {
this._chrome.removeActor(actor);
Main.uiGroup.remove_actor(actor);
this._untrackActor(actor);
},
_findActor: function(actor) {
for (let i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i];
if (actorData.actor == actor)
return i;
}
return -1;
},
_trackActor: function(actor, params) {
if (this._findActor(actor) != -1)
throw new Error('trying to re-track existing chrome actor');
let actorData = Params.parse(params, defaultParams);
actorData.actor = actor;
actorData.isToplevel = actor.get_parent() == Main.uiGroup;
actorData.visibleId = actor.connect('notify::visible',
Lang.bind(this, this._queueUpdateRegions));
actorData.allocationId = actor.connect('notify::allocation',
Lang.bind(this, this._queueUpdateRegions));
actorData.parentSetId = actor.connect('parent-set',
Lang.bind(this, this._actorReparented));
// Note that destroying actor will unset its parent, so we don't
// need to connect to 'destroy' too.
this._trackedActors.push(actorData);
this._queueUpdateRegions();
},
_untrackActor: function(actor) {
let i = this._findActor(actor);
if (i == -1)
return;
let actorData = this._trackedActors[i];
this._trackedActors.splice(i, 1);
actor.disconnect(actorData.visibleId);
actor.disconnect(actorData.allocationId);
actor.disconnect(actorData.parentSetId);
this._queueUpdateRegions();
},
_actorReparented: function(actor, oldParent) {
let newParent = actor.get_parent();
if (!newParent) {
this._untrackActor(actor);
} else {
let i = this._findActor(actor);
let actorData = this._trackedActors[i];
actorData.isToplevel = (newParent == Main.uiGroup);
}
},
_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._inOverview || !Main.sessionMode.hasWindows)
visible = true;
else if (this.findMonitorForActor(actorData.actor).inFullscreen)
visible = false;
else
visible = true;
actorData.actor.visible = visible;
}
},
_findMonitorForRect: function(x, y, w, h) {
// First look at what monitor the center of the rectangle is at
let cx = x + w/2;
let cy = y + h/2;
for (let i = 0; i < this.monitors.length; i++) {
let monitor = this.monitors[i];
if (cx >= monitor.x && cx < monitor.x + monitor.width &&
cy >= monitor.y && cy < monitor.y + monitor.height)
return i;
}
// If the center is not on a monitor, return the first overlapping monitor
for (let i = 0; i < this.monitors.length; i++) {
let monitor = this.monitors[i];
if (x + w > monitor.x && x < monitor.x + monitor.width &&
y + h > monitor.y && y < monitor.y + monitor.height)
return i;
}
// otherwise on no monitor
return -1;
},
findIndexForWindow: function(window) {
let rect = window.get_input_rect();
let i = this._findMonitorForRect(rect.x, rect.y, rect.width, rect.height);
if (i >= 0)
return i;
return this.primaryIndex; // Not on any monitor, pretend its on the primary
},
// This call guarantees that we return some monitor to simplify usage of it
// In practice all tracked actors should be visible on some monitor anyway
findIndexForActor: function(actor) {
let [x, y] = actor.get_transformed_position();
let [w, h] = actor.get_transformed_size();
let i = this._findMonitorForRect(x, y, w, h);
if (i >= 0)
return i;
return this.primaryIndex; // Not on any monitor, pretend its on the primary
},
findMonitorForWindow: function(window) {
let rect = window.get_input_rect();
let i = this._findMonitorForRect(rect.x, rect.y, rect.width, rect.height);
if (i >= 0)
return this.monitors[i];
else
return null;
},
findMonitorForActor: function(actor) {
return this.monitors[this._chrome.findIndexForActor(actor)];
return this.monitors[this.findIndexForActor(actor)];
},
_queueUpdateRegions: function() {
if (!this._updateRegionIdle && !this._freezeUpdateCount)
this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this._updateRegions),
Meta.PRIORITY_BEFORE_REDRAW);
},
_freezeUpdateRegions: function() {
if (this._updateRegionIdle)
this._updateRegions();
this._freezeUpdateCount++;
},
_thawUpdateRegions: function() {
this._freezeUpdateCount--;
this._queueUpdateRegions();
},
_updateFullscreen: function() {
let windows = Main.getWindowActorsForWorkspace(global.screen.get_active_workspace_index());
// Reset all monitors to not fullscreen
for (let i = 0; i < this.monitors.length; i++)
this.monitors[i].inFullscreen = false;
// Ordinary chrome should be visible unless there is a window
// with layer FULLSCREEN, or a window with layer
// OVERRIDE_REDIRECT that covers the whole screen.
// ('override_redirect' is not actually a layer above all
// other windows, but this seems to be how mutter treats it
// currently...) If we wanted to be extra clever, we could
// figure out when an OVERRIDE_REDIRECT window was trying to
// partially overlap us, and then adjust the input region and
// our clip region accordingly...
// @windows is sorted bottom to top.
for (let i = windows.length - 1; i > -1; i--) {
let window = windows[i];
let metaWindow = window.meta_window;
let layer = metaWindow.get_layer();
// Skip minimized windows
if (!window.showing_on_its_workspace())
continue;
if (layer == Meta.StackLayer.FULLSCREEN) {
let monitor = this.findMonitorForWindow(metaWindow);
if (monitor)
monitor.inFullscreen = true;
}
if (layer == Meta.StackLayer.OVERRIDE_REDIRECT) {
// Check whether the window is screen sized
let isScreenSized =
(window.x == 0 && window.y == 0 &&
window.width == global.screen_width &&
window.height == global.screen_height);
if (isScreenSized) {
for (let i = 0; i < this.monitors.length; i++)
this.monitors[i].inFullscreen = true;
}
// Or whether it is monitor sized
let monitor = this.findMonitorForWindow(metaWindow);
if (monitor &&
window.x <= monitor.x &&
window.x + window.width >= monitor.x + monitor.width &&
window.y <= monitor.y &&
window.y + window.height >= monitor.y + monitor.height)
monitor.inFullscreen = true;
} else
break;
}
},
_windowsRestacked: function() {
let wasInFullscreen = [];
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;
for (let i = 0; i < wasInFullscreen.length; i++) {
if (wasInFullscreen[i] != this.monitors[i].inFullscreen) {
changed = true;
break;
}
}
if (changed) {
this._updateVisibility();
this._queueUpdateRegions();
}
if (primaryWasInFullscreen != this.primaryMonitor.inFullscreen) {
this.emit('primary-fullscreen-changed', this.primaryMonitor.inFullscreen);
}
},
_updateRegions: function() {
let rects = [], struts = [], i;
if (this._updateRegionIdle) {
Mainloop.source_remove(this._updateRegionIdle);
delete this._updateRegionIdle;
}
for (i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i];
if (!actorData.affectsInputRegion && !actorData.affectsStruts)
continue;
let [x, y] = actorData.actor.get_transformed_position();
let [w, h] = actorData.actor.get_transformed_size();
x = Math.round(x);
y = Math.round(y);
w = Math.round(w);
h = Math.round(h);
let rect = new Meta.Rectangle({ x: x, y: y, width: w, height: h});
if (actorData.affectsInputRegion &&
actorData.actor.get_paint_visibility() &&
!Main.uiGroup.get_skip_paint(actorData.actor))
rects.push(rect);
if (!actorData.affectsStruts)
continue;
// Limit struts to the size of the screen
let x1 = Math.max(x, 0);
let x2 = Math.min(x + w, global.screen_width);
let y1 = Math.max(y, 0);
let y2 = Math.min(y + h, global.screen_height);
// NetWM struts are not really powerful enought to handle
// a multi-monitor scenario, they only describe what happens
// around the outer sides of the full display region. However
// it can describe a partial region along each side, so
// we can support having the struts only affect the
// primary monitor. This should be enough as we only have
// chrome affecting the struts on the primary monitor so
// far.
//
// Metacity wants to know what side of the screen the
// strut is considered to be attached to. If the actor is
// only touching one edge, or is touching the entire
// border of the primary monitor, then it's obvious which
// side to call it. If it's in a corner, we pick a side
// arbitrarily. If it doesn't touch any edges, or it spans
// the width/height across the middle of the screen, then
// we don't create a strut for it at all.
let side;
let primary = this.primaryMonitor;
if (x1 <= primary.x && x2 >= primary.x + primary.width) {
if (y1 <= primary.y)
side = Meta.Side.TOP;
else if (y2 >= primary.y + primary.height)
side = Meta.Side.BOTTOM;
else
continue;
} else if (y1 <= primary.y && y2 >= primary.y + primary.height) {
if (x1 <= 0)
side = Meta.Side.LEFT;
else if (x2 >= primary.x + primary.width)
side = Meta.Side.RIGHT;
else
continue;
} else if (x1 <= 0)
side = Meta.Side.LEFT;
else if (y1 <= 0)
side = Meta.Side.TOP;
else if (x2 >= global.screen_width)
side = Meta.Side.RIGHT;
else if (y2 >= global.screen_height)
side = Meta.Side.BOTTOM;
else
continue;
// Ensure that the strut rects goes all the way to the screen edge,
// as this really what mutter expects.
switch (side) {
case Meta.Side.TOP:
y1 = 0;
break;
case Meta.Side.BOTTOM:
y2 = global.screen_height;
break;
case Meta.Side.LEFT:
x1 = 0;
break;
case Meta.Side.RIGHT:
x2 = global.screen_width;
break;
}
let strutRect = new Meta.Rectangle({ x: x1, y: y1, width: x2 - x1, height: y2 - y1});
let strut = new Meta.Strut({ rect: strutRect, side: side });
struts.push(strut);
}
global.set_stage_input_region(rects);
let screen = global.screen;
for (let w = 0; w < screen.n_workspaces; w++) {
let workspace = screen.get_workspace_by_index(w);
workspace.set_builtin_struts(struts);
}
return false;
}
});
Signals.addSignalMethods(LayoutManager.prototype);
@ -689,449 +1075,3 @@ const HotCorner = new Lang.Class({
return false;
}
});
// This manages the shell "chrome"; the UI that's visible in the
// normal mode (ie, outside the Overview), that surrounds the main
// workspace content.
const defaultParams = {
trackFullscreen: false,
affectsStruts: false,
affectsInputRegion: true
};
const Chrome = new Lang.Class({
Name: 'Chrome',
_init: function(layoutManager) {
this._layoutManager = layoutManager;
this._monitors = [];
this._inOverview = false;
this._updateRegionIdle = 0;
this._freezeUpdateCount = 0;
this._trackedActors = [];
this._layoutManager.connect('monitors-changed',
Lang.bind(this, this._relayout));
global.screen.connect('restacked',
Lang.bind(this, this._windowsRestacked));
// Need to update struts on new workspaces when they are added
global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._queueUpdateRegions));
this._relayout();
},
init: function() {
Main.overview.connect('showing', Lang.bind(this, this._overviewShowing));
Main.overview.connect('hidden', Lang.bind(this, this._overviewHidden));
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
},
addActor: function(actor, params) {
Main.uiGroup.add_actor(actor);
this._trackActor(actor, params);
},
trackActor: function(actor, params) {
let ancestor = actor.get_parent();
let index = this._findActor(ancestor);
while (ancestor && index == -1) {
ancestor = ancestor.get_parent();
index = this._findActor(ancestor);
}
if (!ancestor)
throw new Error('actor is not a descendent of a chrome actor');
let ancestorData = this._trackedActors[index];
if (!params)
params = {};
// We can't use Params.parse here because we want to drop
// the extra values like ancestorData.actor
for (let prop in defaultParams) {
if (!params.hasOwnProperty(prop))
params[prop] = ancestorData[prop];
}
this._trackActor(actor, params);
},
untrackActor: function(actor) {
this._untrackActor(actor);
},
removeActor: function(actor) {
Main.uiGroup.remove_actor(actor);
this._untrackActor(actor);
},
_findActor: function(actor) {
for (let i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i];
if (actorData.actor == actor)
return i;
}
return -1;
},
_trackActor: function(actor, params) {
if (this._findActor(actor) != -1)
throw new Error('trying to re-track existing chrome actor');
let actorData = Params.parse(params, defaultParams);
actorData.actor = actor;
actorData.isToplevel = actor.get_parent() == Main.uiGroup;
actorData.visibleId = actor.connect('notify::visible',
Lang.bind(this, this._queueUpdateRegions));
actorData.allocationId = actor.connect('notify::allocation',
Lang.bind(this, this._queueUpdateRegions));
actorData.parentSetId = actor.connect('parent-set',
Lang.bind(this, this._actorReparented));
// Note that destroying actor will unset its parent, so we don't
// need to connect to 'destroy' too.
this._trackedActors.push(actorData);
this._queueUpdateRegions();
},
_untrackActor: function(actor) {
let i = this._findActor(actor);
if (i == -1)
return;
let actorData = this._trackedActors[i];
this._trackedActors.splice(i, 1);
actor.disconnect(actorData.visibleId);
actor.disconnect(actorData.allocationId);
actor.disconnect(actorData.parentSetId);
this._queueUpdateRegions();
},
_actorReparented: function(actor, oldParent) {
let newParent = actor.get_parent();
if (!newParent) {
this._untrackActor(actor);
} else {
let i = this._findActor(actor);
let actorData = this._trackedActors[i];
actorData.isToplevel = (newParent == Main.uiGroup);
}
},
_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._inOverview || !Main.sessionMode.hasWindows)
visible = true;
else if (this.findMonitorForActor(actorData.actor).inFullscreen)
visible = false;
else
visible = true;
actorData.actor.visible = visible;
}
},
_overviewShowing: function() {
this._inOverview = true;
this._updateVisibility();
this._queueUpdateRegions();
},
_overviewHidden: function() {
this._inOverview = false;
this._updateVisibility();
this._queueUpdateRegions();
},
_sessionUpdated: function() {
this._updateVisibility();
this._queueUpdateRegions();
},
_relayout: function() {
this._monitors = this._layoutManager.monitors;
this._primaryIndex = this._layoutManager.primaryIndex;
this._primaryMonitor = this._layoutManager.primaryMonitor;
this._updateFullscreen();
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;
let cy = y + h/2;
for (let i = 0; i < this._monitors.length; i++) {
let monitor = this._monitors[i];
if (cx >= monitor.x && cx < monitor.x + monitor.width &&
cy >= monitor.y && cy < monitor.y + monitor.height)
return i;
}
// If the center is not on a monitor, return the first overlapping monitor
for (let i = 0; i < this._monitors.length; i++) {
let monitor = this._monitors[i];
if (x + w > monitor.x && x < monitor.x + monitor.width &&
y + h > monitor.y && y < monitor.y + monitor.height)
return i;
}
// otherwise on no monitor
return -1;
},
findIndexForWindow: function(window) {
let i = this._findMonitorForRect(window.x, window.y, window.width, window.height);
if (i >= 0)
return i;
return this._primaryIndex; // Not on any monitor, pretend its on the primary
},
// This call guarantees that we return some monitor to simplify usage of it
// In practice all tracked actors should be visible on some monitor anyway
findIndexForActor: function(actor) {
let [x, y] = actor.get_transformed_position();
let [w, h] = actor.get_transformed_size();
let i = this._findMonitorForRect(x, y, w, h);
if (i >= 0)
return i;
return this._primaryIndex; // Not on any monitor, pretend its on the primary
},
findMonitorForWindow: function(window) {
let i = this._findMonitorForRect(window.x, window.y, window.width, window.height);
if (i >= 0)
return this._monitors[i];
else
return null;
},
findMonitorForActor: function(actor) {
return this._monitors[this.findIndexForActor(actor)];
},
_queueUpdateRegions: function() {
if (!this._updateRegionIdle && !this._freezeUpdateCount)
this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this.updateRegions),
Meta.PRIORITY_BEFORE_REDRAW);
},
freezeUpdateRegions: function() {
if (this._updateRegionIdle)
this.updateRegions();
this._freezeUpdateCount++;
},
thawUpdateRegions: function() {
this._freezeUpdateCount--;
this._queueUpdateRegions();
},
_updateFullscreen: function() {
let windows = Main.getWindowActorsForWorkspace(global.screen.get_active_workspace_index());
// Reset all monitors to not fullscreen
for (let i = 0; i < this._monitors.length; i++)
this._monitors[i].inFullscreen = false;
// Ordinary chrome should be visible unless there is a window
// with layer FULLSCREEN, or a window with layer
// OVERRIDE_REDIRECT that covers the whole screen.
// ('override_redirect' is not actually a layer above all
// other windows, but this seems to be how mutter treats it
// currently...) If we wanted to be extra clever, we could
// figure out when an OVERRIDE_REDIRECT window was trying to
// partially overlap us, and then adjust the input region and
// our clip region accordingly...
// @windows is sorted bottom to top.
for (let i = windows.length - 1; i > -1; i--) {
let window = windows[i];
let layer = window.get_meta_window().get_layer();
// Skip minimized windows
if (!window.showing_on_its_workspace())
continue;
if (layer == Meta.StackLayer.FULLSCREEN) {
let monitor = this.findMonitorForWindow(window);
if (monitor)
monitor.inFullscreen = true;
}
if (layer == Meta.StackLayer.OVERRIDE_REDIRECT) {
// Check whether the window is screen sized
let isScreenSized =
(window.x == 0 && window.y == 0 &&
window.width == global.screen_width &&
window.height == global.screen_height);
if (isScreenSized) {
for (let i = 0; i < this._monitors.length; i++)
this._monitors[i].inFullscreen = true;
}
// Or whether it is monitor sized
let monitor = this.findMonitorForWindow(window);
if (monitor &&
window.x <= monitor.x &&
window.x + window.width >= monitor.x + monitor.width &&
window.y <= monitor.y &&
window.y + window.height >= monitor.y + monitor.height)
monitor.inFullscreen = true;
} else
break;
}
},
_windowsRestacked: function() {
let wasInFullscreen = [];
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;
for (let i = 0; i < wasInFullscreen.length; i++) {
if (wasInFullscreen[i] != this._monitors[i].inFullscreen) {
changed = true;
break;
}
}
if (changed) {
this._updateVisibility();
this._queueUpdateRegions();
}
if (primaryWasInFullscreen != this._primaryMonitor.inFullscreen) {
this.emit('primary-fullscreen-changed', this._primaryMonitor.inFullscreen);
}
},
updateRegions: function() {
let rects = [], struts = [], i;
if (this._updateRegionIdle) {
Mainloop.source_remove(this._updateRegionIdle);
delete this._updateRegionIdle;
}
for (i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i];
if (!actorData.affectsInputRegion && !actorData.affectsStruts)
continue;
let [x, y] = actorData.actor.get_transformed_position();
let [w, h] = actorData.actor.get_transformed_size();
x = Math.round(x);
y = Math.round(y);
w = Math.round(w);
h = Math.round(h);
let rect = new Meta.Rectangle({ x: x, y: y, width: w, height: h});
if (actorData.affectsInputRegion &&
actorData.actor.get_paint_visibility() &&
!Main.uiGroup.get_skip_paint(actorData.actor))
rects.push(rect);
if (!actorData.affectsStruts)
continue;
// Limit struts to the size of the screen
let x1 = Math.max(x, 0);
let x2 = Math.min(x + w, global.screen_width);
let y1 = Math.max(y, 0);
let y2 = Math.min(y + h, global.screen_height);
// NetWM struts are not really powerful enought to handle
// a multi-monitor scenario, they only describe what happens
// around the outer sides of the full display region. However
// it can describe a partial region along each side, so
// we can support having the struts only affect the
// primary monitor. This should be enough as we only have
// chrome affecting the struts on the primary monitor so
// far.
//
// Metacity wants to know what side of the screen the
// strut is considered to be attached to. If the actor is
// only touching one edge, or is touching the entire
// border of the primary monitor, then it's obvious which
// side to call it. If it's in a corner, we pick a side
// arbitrarily. If it doesn't touch any edges, or it spans
// the width/height across the middle of the screen, then
// we don't create a strut for it at all.
let side;
let primary = this._primaryMonitor;
if (x1 <= primary.x && x2 >= primary.x + primary.width) {
if (y1 <= primary.y)
side = Meta.Side.TOP;
else if (y2 >= primary.y + primary.height)
side = Meta.Side.BOTTOM;
else
continue;
} else if (y1 <= primary.y && y2 >= primary.y + primary.height) {
if (x1 <= 0)
side = Meta.Side.LEFT;
else if (x2 >= global.screen_width)
side = Meta.Side.RIGHT;
else
continue;
} else if (x1 <= 0)
side = Meta.Side.LEFT;
else if (y1 <= 0)
side = Meta.Side.TOP;
else if (x2 >= global.screen_width)
side = Meta.Side.RIGHT;
else if (y2 >= global.screen_height)
side = Meta.Side.BOTTOM;
else
continue;
// Ensure that the strut rects goes all the way to the screen edge,
// as this really what mutter expects.
switch (side) {
case Meta.Side.TOP:
y1 = 0;
break;
case Meta.Side.BOTTOM:
y2 = global.screen_height;
break;
case Meta.Side.LEFT:
x1 = 0;
break;
case Meta.Side.RIGHT:
x2 = global.screen_width;
break;
}
let strutRect = new Meta.Rectangle({ x: x1, y: y1, width: x2 - x1, height: y2 - y1});
let strut = new Meta.Strut({ rect: strutRect, side: side });
struts.push(strut);
}
global.set_stage_input_region(rects);
let screen = global.screen;
for (let w = 0; w < screen.n_workspaces; w++) {
let workspace = screen.get_workspace_by_index(w);
workspace.set_builtin_struts(struts);
}
return false;
}
});
Signals.addSignalMethods(Chrome.prototype);

View File

@ -3,6 +3,7 @@
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Signals = imports.signals;
const St = imports.gi.St;
const Params = imports.misc.params;
@ -101,6 +102,7 @@ const Lightbox = new Lang.Class({
},
show: function() {
Tweener.removeTweens(this.actor);
if (this._fadeInTime) {
this.shown = false;
this.actor.opacity = 0;
@ -110,17 +112,20 @@ const Lightbox = new Lang.Class({
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this.shown = true;
this.emit('shown');
})
});
} else {
this.actor.opacity = 255 * this._fadeFactor;
this.shown = true;
this.emit('shown');
}
this.actor.show();
},
hide: function() {
this.shown = false;
Tweener.removeTweens(this.actor);
if (this._fadeOutTime) {
Tweener.addTween(this.actor,
{ opacity: 0,
@ -197,3 +202,4 @@ const Lightbox = new Lang.Class({
this.highlight(null);
}
});
Signals.addSignalMethods(Lightbox.prototype);

View File

@ -39,6 +39,7 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
* in the shell core code too. */
'const stage = global.stage; ' +
/* Special lookingGlass functions */
'const inspect = Lang.bind(Main.lookingGlass, Main.lookingGlass.inspect); ' +
'const it = Main.lookingGlass.getIt(); ' +
'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ';
@ -871,8 +872,7 @@ const LookingGlass = new Lang.Class({
inspectIcon.connect('button-press-event', Lang.bind(this, function () {
let inspector = new Inspector(this);
inspector.connect('target', Lang.bind(this, function(i, target, stageX, stageY) {
this._pushResult('<inspect x:' + stageX + ' y:' + stageY + '>',
target);
this._pushResult('inspect(' + Math.round(stageX) + ', ' + Math.round(stageY) + ')', target);
}));
inspector.connect('closed', Lang.bind(this, function() {
this.actor.show();
@ -1057,6 +1057,10 @@ const LookingGlass = new Lang.Class({
this._entry.text = '';
},
inspect: function(x, y) {
return global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
},
getIt: function () {
return this._it;
},
@ -1129,7 +1133,7 @@ const LookingGlass = new Lang.Class({
if (this._open)
return;
if (!Main.pushModal(this._entry, { keybindingMode: Main.KeybindingMode.LOOKING_GLASS }))
if (!Main.pushModal(this._entry, { keybindingMode: Shell.KeyBindingMode.LOOKING_GLASS }))
return;
this._notebook.selectIndex(0);

View File

@ -40,18 +40,6 @@ const Util = imports.misc.util;
const OVERRIDES_SCHEMA = 'org.gnome.shell.overrides';
const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
const KeybindingMode = {
NONE: 0, // block all keybindings
NORMAL: 1 << 0, // window mode
OVERVIEW: 1 << 1,
LOCK_SCREEN: 1 << 2,
UNLOCK_SCREEN: 1 << 3,
LOGIN_SCREEN: 1 << 4,
MESSAGE_TRAY: 1 << 5,
SYSTEM_MODAL: 1 << 6,
LOOKING_GLASS: 1 << 7
};
let componentManager = null;
let panel = null;
let overview = null;
@ -68,7 +56,7 @@ let shellDBusService = null;
let shellMountOpDBusService = null;
let screenSaverDBus = null;
let modalCount = 0;
let keybindingMode = KeybindingMode.NORMAL;
let keybindingMode = Shell.KeyBindingMode.NORMAL;
let modalActorFocusStack = [];
let uiGroup = null;
let magnifier = null;
@ -80,19 +68,17 @@ let _defaultCssStylesheet = null;
let _cssStylesheet = null;
let _overridesSettings = null;
let background = null;
function _sessionUpdated() {
wm.setCustomKeybindingHandler('panel-main-menu',
KeybindingMode.NORMAL |
KeybindingMode.OVERVIEW,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
sessionMode.hasOverview ? Lang.bind(overview, overview.toggle) : null);
wm.allowKeybinding('overlay-key', KeybindingMode.NORMAL |
KeybindingMode.OVERVIEW);
wm.allowKeybinding('overlay-key', Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW);
wm.setCustomKeybindingHandler('panel-run-dialog',
KeybindingMode.NORMAL |
KeybindingMode.OVERVIEW,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
sessionMode.hasRunDialog ? openRunDialog : null);
if (sessionMode.isGreeter)
screenShield.showDialog();
@ -109,6 +95,8 @@ function start() {
Gio.DesktopAppInfo.set_desktop_env('GNOME');
sessionMode = new SessionMode.SessionMode();
sessionMode.connect('updated', _loadDefaultStylesheet);
shellDBusService = new ShellDBus.GnomeShell();
shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
@ -131,8 +119,7 @@ function start() {
global.stage.color = DEFAULT_BACKGROUND_COLOR;
global.stage.no_clear_hint = true;
_defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
loadTheme();
_loadDefaultStylesheet();
// Set up stage hierarchy to group all UI actors under one container.
uiGroup = new Shell.GenericContainer({ name: 'uiGroup' });
@ -160,13 +147,13 @@ function start() {
xdndHandler = new XdndHandler.XdndHandler();
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
overview = new Overview.Overview();
wm = new WindowManager.WindowManager();
magnifier = new Magnifier.Magnifier();
if (UnlockDialog.isSupported())
screenShield = new ScreenShield.ScreenShield();
else
screenShield = new ScreenShield.ScreenShieldFallback();
panel = new Panel.Panel();
wm = new WindowManager.WindowManager();
messageTray = new MessageTray.MessageTray();
keyboard = new Keyboard.Keyboard();
notificationDaemon = new NotificationDaemon.NotificationDaemon();
@ -174,7 +161,6 @@ function start() {
componentManager = new Components.ComponentManager();
layoutManager.init();
keyboard.init();
overview.init();
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
@ -394,6 +380,18 @@ function _nWorkspacesChanged() {
return false;
}
function _loadDefaultStylesheet() {
if (!sessionMode.isPrimary)
return;
let stylesheet = global.datadir + '/theme/' + sessionMode.stylesheetName;
if (_defaultCssStylesheet == stylesheet)
return;
_defaultCssStylesheet = stylesheet;
loadTheme();
}
/**
* getThemeStylesheet:
*
@ -519,7 +517,7 @@ function isInModalStack(actor) {
* - options: Meta.ModalOptions flags to indicate that the pointer is
* already grabbed
*
* - keybindingMode: used to set the current Main.KeybindingMode to filter
* - keybindingMode: used to set the current Shell.KeyBindingMode to filter
* global keybindings; the default of NONE will filter
* out all keybindings
*
@ -528,7 +526,7 @@ function isInModalStack(actor) {
function pushModal(actor, params) {
params = Params.parse(params, { timestamp: global.get_current_time(),
options: 0,
keybindingMode: KeybindingMode.NONE });
keybindingMode: Shell.KeyBindingMode.NONE });
if (modalCount == 0) {
if (!global.begin_modal(params.timestamp, params.options)) {
@ -546,19 +544,20 @@ function pushModal(actor, params) {
if (index >= 0)
popModal(actor);
});
let curFocus = global.stage.get_key_focus();
let curFocusDestroyId;
if (curFocus != null) {
curFocusDestroyId = curFocus.connect('destroy', function() {
let prevFocus = global.stage.get_key_focus();
let prevFocusDestroyId;
if (prevFocus != null) {
prevFocusDestroyId = prevFocus.connect('destroy', function() {
let index = _findModal(actor);
if (index >= 0)
modalActorFocusStack[index].actor = null;
modalActorFocusStack[index].prevFocus = null;
});
}
modalActorFocusStack.push({ actor: actor,
focus: curFocus,
destroyId: actorDestroyId,
focusDestroyId: curFocusDestroyId,
prevFocus: prevFocus,
prevFocusDestroyId: prevFocusDestroyId,
keybindingMode: keybindingMode });
keybindingMode = params.keybindingMode;
@ -588,7 +587,7 @@ function popModal(actor, timestamp) {
global.stage.set_key_focus(null);
global.end_modal(timestamp);
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
keybindingMode = KeybindingMode.NORMAL;
keybindingMode = Shell.KeyBindingMode.NORMAL;
throw new Error('incorrect pop');
}
@ -599,18 +598,33 @@ function popModal(actor, timestamp) {
record.actor.disconnect(record.destroyId);
if (focusIndex == modalActorFocusStack.length - 1) {
if (record.focus)
record.focus.disconnect(record.focusDestroyId);
if (record.prevFocus)
record.prevFocus.disconnect(record.prevFocusDestroyId);
keybindingMode = record.keybindingMode;
global.stage.set_key_focus(record.focus);
global.stage.set_key_focus(record.prevFocus);
} else {
// If we have:
// global.stage.set_focus(a);
// Main.pushModal(b);
// Main.pushModal(c);
// Main.pushModal(d);
//
// then we have the stack:
// [{ prevFocus: a, actor: b },
// { prevFocus: b, actor: c },
// { prevFocus: c, actor: d }]
//
// When actor c is destroyed/popped, if we only simply remove the
// record, then the focus stack will be [a, c], rather than the correct
// [a, b]. Shift the focus stack up before removing the record to ensure
// that we get the correct result.
let t = modalActorFocusStack[modalActorFocusStack.length - 1];
if (t.focus)
t.focus.disconnect(t.focusDestroyId);
if (t.prevFocus)
t.prevFocus.disconnect(t.prevFocusDestroyId);
// Remove from the middle, shift the focus chain up
for (let i = modalActorFocusStack.length - 1; i > focusIndex; i--) {
modalActorFocusStack[i].focus = modalActorFocusStack[i - 1].focus;
modalActorFocusStack[i].focusDestroyId = modalActorFocusStack[i - 1].focusDestroyId;
modalActorFocusStack[i].prevFocus = modalActorFocusStack[i - 1].prevFocus;
modalActorFocusStack[i].prevFocusDestroyId = modalActorFocusStack[i - 1].prevFocusDestroyId;
modalActorFocusStack[i].keybindingMode = modalActorFocusStack[i - 1].keybindingMode;
}
}
@ -622,7 +636,7 @@ function popModal(actor, timestamp) {
global.end_modal(timestamp);
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
Meta.enable_unredirect_for_screen(global.screen);
keybindingMode = KeybindingMode.NORMAL;
keybindingMode = Shell.KeyBindingMode.NORMAL;
}
function createLookingGlass() {

View File

@ -18,6 +18,7 @@ const BoxPointer = imports.ui.boxpointer;
const CtrlAltTab = imports.ui.ctrlAltTab;
const GnomeSession = imports.misc.gnomeSession;
const GrabHelper = imports.ui.grabHelper;
const Hash = imports.misc.hash;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const PointerWatcher = imports.ui.pointerWatcher;
@ -243,6 +244,36 @@ function makeCloseButton() {
return closeButton;
}
function strHasSuffix(string, suffix) {
return string.substr(-suffix.length) == suffix;
}
// NotificationPolicy:
// An object that holds all bits of configurable policy related to a notification
// source, such as whether to play sound or honour the critical bit.
//
// A notification without a policy object will inherit the default one.
const NotificationPolicy = new Lang.Class({
Name: 'NotificationPolicy',
_init: function(params) {
params = Params.parse(params, { enable: true,
enableSound: true,
showBanners: true,
forceExpanded: false,
showInLockScreen: true,
detailsInLockScreen: false
});
Lang.copyProperties(params, this);
},
// Do nothing for the default policy. These methods are only useful for the
// GSettings policy.
store: function() { },
destroy: function() { }
});
Signals.addSignalMethods(NotificationPolicy.prototype);
// Notification:
// @source: the notification's Source
// @title: the title
@ -314,6 +345,7 @@ const Notification = new Lang.Class({
this.resident = false;
// 'transient' is a reserved keyword in JS, so we have to use an alternate variable name
this.isTransient = false;
this.isMusic = false;
this.forFeedback = false;
this.expanded = false;
this.focused = false;
@ -321,8 +353,8 @@ const Notification = new Lang.Class({
this._destroyed = false;
this._useActionIcons = false;
this._customContent = false;
this._bannerBodyText = null;
this._bannerBodyMarkup = false;
this.bannerBodyText = null;
this.bannerBodyMarkup = false;
this._titleFitsInBannerMode = true;
this._titleDirection = Clutter.TextDirection.DEFAULT;
this._spacing = 0;
@ -429,7 +461,7 @@ const Notification = new Lang.Class({
this._actionArea = null;
this._buttonBox = null;
}
if (this._imageBin && params.clear)
if (params.clear)
this.unsetImage();
if (!this._scrollArea && !this._actionArea && !this._imageBin)
@ -474,12 +506,12 @@ const Notification = new Lang.Class({
// is done correctly automatically.
this._table.set_text_direction(this._titleDirection);
// Unless the notification has custom content, we save this._bannerBodyText
// Unless the notification has custom content, we save this.bannerBodyText
// to add it to the content of the notification if the notification is
// expandable due to other elements in its content area or due to the banner
// not fitting fully in the single-line mode.
this._bannerBodyText = this._customContent ? null : banner;
this._bannerBodyMarkup = params.bannerMarkup;
this.bannerBodyText = this._customContent ? null : banner;
this.bannerBodyMarkup = params.bannerMarkup;
banner = banner ? banner.replace(/\n/g, ' ') : '';
@ -487,7 +519,7 @@ const Notification = new Lang.Class({
this._bannerLabel.queue_relayout();
// Add the bannerBody now if we know for sure we'll need it
if (this._bannerBodyText && this._bannerBodyText.indexOf('\n') > -1)
if (this.bannerBodyText && this.bannerBodyText.indexOf('\n') > -1)
this._addBannerBody();
if (params.body)
@ -511,7 +543,8 @@ const Notification = new Lang.Class({
this._table.add_style_class_name('multi-line-notification');
this._scrollArea = new St.ScrollView({ style_class: 'notification-scrollview',
vscrollbar_policy: this._scrollPolicy,
hscrollbar_policy: Gtk.PolicyType.NEVER });
hscrollbar_policy: Gtk.PolicyType.NEVER,
visible: this.expanded });
this._table.add(this._scrollArea, { row: 1,
col: 2 });
this._updateLastColumnSettings();
@ -552,10 +585,10 @@ const Notification = new Lang.Class({
},
_addBannerBody: function() {
if (this._bannerBodyText) {
let text = this._bannerBodyText;
this._bannerBodyText = null;
this.addBody(text, this._bannerBodyMarkup);
if (this.bannerBodyText) {
let text = this.bannerBodyText;
this.bannerBodyText = null;
this.addBody(text, this.bannerBodyMarkup);
}
},
@ -587,6 +620,7 @@ const Notification = new Lang.Class({
this._addBannerBody();
}
this._actionArea = actor;
this._actionArea.visible = this.expanded;
if (!props)
props = {};
@ -609,11 +643,15 @@ const Notification = new Lang.Class({
},
setImage: function(image) {
if (this._imageBin)
this.unsetImage();
this._imageBin = new St.Bin();
this._imageBin.child = image;
this._imageBin.opacity = 230;
this.unsetImage();
if (!image)
return;
this._imageBin = new St.Bin({ opacity: 230,
child: image,
visible: this.expanded });
this._table.add_style_class_name('multi-line-notification');
this._table.add_style_class_name('notification-with-image');
this._addBannerBody();
@ -663,9 +701,10 @@ const Notification = new Lang.Class({
let button = new St.Button({ can_focus: true });
button._actionId = id;
if (this._useActionIcons && Gtk.IconTheme.get_default().has_icon(id)) {
let iconName = strHasSuffix(id, '-symbolic') ? id : id + '-symbolic';
if (this._useActionIcons && Gtk.IconTheme.get_default().has_icon(iconName)) {
button.add_style_class_name('notification-icon-button');
button.child = new St.Icon({ icon_name: id });
button.child = new St.Icon({ icon_name: iconName });
} else {
button.add_style_class_name('notification-button');
button.label = label;
@ -841,7 +880,7 @@ const Notification = new Lang.Class({
},
_canExpandContent: function() {
return this._bannerBodyText ||
return this.bannerBodyText ||
(!this._titleFitsInBannerMode && !this._table.has_style_class_name('multi-line-notification'));
},
@ -854,6 +893,14 @@ const Notification = new Lang.Class({
this.expanded = true;
this.actor.remove_style_class_name('notification-unexpanded');
// Show additional content that we keep hidden in banner mode
if (this._imageBin)
this._imageBin.show();
if (this._actionArea)
this._actionArea.show();
if (this._scrollArea)
this._scrollArea.show();
// The banner is never shown when the title did not fit, so this
// can be an if-else statement.
if (!this._titleFitsInBannerMode) {
@ -885,12 +932,23 @@ const Notification = new Lang.Class({
if (this._destroyed)
return;
this.expanded = false;
// Hide additional content that we keep hidden in banner mode
if (this._imageBin)
this._imageBin.hide();
if (this._actionArea)
this._actionArea.hide();
if (this._scrollArea)
this._scrollArea.hide();
// Make sure we don't line wrap the title, and ellipsize it instead.
this._titleLabel.clutter_text.line_wrap = false;
this._titleLabel.clutter_text.ellipsize = Pango.EllipsizeMode.END;
// Restore banner opacity in case the notification is shown in the
// banner mode again on update.
this._bannerLabel.opacity = 255;
// Restore height requisition
this.actor.add_style_class_name('notification-unexpanded');
this.emit('collapsed');
@ -1058,10 +1116,11 @@ const Source = new Lang.Class({
this.isTransient = false;
this.isChat = false;
this.isMuted = false;
this.showInLockScreen = true;
this.keepTrayOnSummaryClick = false;
this.notifications = [];
this.policy = this._createPolicy();
},
get count() {
@ -1080,6 +1139,10 @@ const Source = new Lang.Class({
this.emit('count-updated');
},
_createPolicy: function() {
return new NotificationPolicy();
},
buildRightClickMenu: function() {
let item;
let rightClickMenu = new St.BoxLayout({ name: 'summary-right-click-menu',
@ -1169,11 +1232,13 @@ const Source = new Lang.Class({
notify: function(notification) {
notification.acknowledged = false;
this.pushNotification(notification);
if (!this.isMuted)
this.emit('notify', notification);
if (!this.isMuted && this.policy.showBanners)
this.emit('notify', notification);
},
destroy: function(reason) {
this.policy.destroy();
this.emit('destroy', reason);
},
@ -1215,7 +1280,16 @@ const Source = new Lang.Class({
hasResidentNotification: function() {
return this.notifications.some(function(n) { return n.resident; });
}
},
getMusicNotification: function() {
for (let i = 0; i < this.notifications.length; i++) {
if (this.notifications[i].isMusic)
return this.notifications[i];
}
return null;
},
});
Signals.addSignalMethods(Source.prototype);
@ -1269,6 +1343,14 @@ const SummaryItem = new Lang.Class({
global.focus_manager.add_group(this.rightClickMenu);
},
destroy: function() {
// remove the actor from the summary item so it doesn't get destroyed
// with us
this._sourceBox.remove_actor(this._sourceIcon);
this.actor.destroy();
},
_onKeyPress: function(actor, event) {
if (event.get_key_symbol() == Clutter.KEY_Up) {
actor.emit('clicked', 1);
@ -1412,7 +1494,7 @@ const MessageTray = new Lang.Class({
actor.grab_key_focus();
}));
global.focus_manager.add_group(this.actor);
this._summary = new St.BoxLayout({ name: 'summary-mode',
this._summary = new St.BoxLayout({ style_class: 'message-tray-summary',
reactive: true,
track_hover: true,
x_align: Clutter.ActorAlign.END,
@ -1453,7 +1535,7 @@ const MessageTray = new Lang.Class({
this.idleMonitor = new GnomeDesktop.IdleMonitor();
this._grabHelper = new GrabHelper.GrabHelper(this.actor,
{ keybindingMode: Main.KeybindingMode.MESSAGE_TRAY });
{ keybindingMode: Shell.KeyBindingMode.MESSAGE_TRAY });
this._grabHelper.addActor(this._summaryBoxPointer.actor);
this._grabHelper.addActor(this.actor);
if (Main.panel.statusArea.activities)
@ -1507,13 +1589,12 @@ const MessageTray = new Lang.Class({
this._overviewVisible = true;
this._grabHelper.ungrab(); // drop modal grab if necessary
this.actor.add_style_pseudo_class('overview');
this.show();
this._updateState();
}));
Main.overview.connect('hiding', Lang.bind(this,
function() {
this._overviewVisible = false;
this._escapeTray();
this.actor.remove_style_pseudo_class('overview');
this._updateState();
}));
@ -1522,19 +1603,19 @@ const MessageTray = new Lang.Class({
Main.wm.addKeybinding('toggle-message-tray',
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
Meta.KeyBindingFlags.NONE,
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.MESSAGE_TRAY |
Main.KeybindingMode.OVERVIEW,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.MESSAGE_TRAY |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this.toggleAndNavigate));
Main.wm.addKeybinding('focus-active-notification',
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
Meta.KeyBindingFlags.NONE,
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.MESSAGE_TRAY |
Main.KeybindingMode.OVERVIEW,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.MESSAGE_TRAY |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._expandActiveNotification));
this._summaryItems = [];
this._sources = new Hash.Map();
this._chatSummaryItemsCount = 0;
let pointerWatcher = PointerWatcher.getPointerWatcher();
@ -1544,22 +1625,19 @@ const MessageTray = new Lang.Class({
this._trayDwellUserTime = 0;
this._sessionUpdated();
this._noMessages = new St.Label({ text: _("No Messages"),
style_class: 'no-messages-label',
x_align: Clutter.ActorAlign.CENTER,
x_expand: true,
y_align: Clutter.ActorAlign.CENTER,
y_expand: true });
this.actor.add_actor(this._noMessages);
this._updateNoMessagesLabel();
},
_updateNoMessagesLabel: function() {
if (this._summaryItems.length == 0 && !this._noMessages) {
this._noMessages = new St.Label({ text: _("No Messages"),
style_class: 'no-messages-label',
x_align: Clutter.ActorAlign.CENTER,
x_expand: true,
y_align: Clutter.ActorAlign.CENTER,
y_expand: true });
this.actor.add_actor(this._noMessages);
} else if (this._summaryItems.length > 0 && this._noMessages) {
this._noMessages.destroy();
this._noMessages = null;
}
this._noMessages.visible = this._sources.size() == 0;
},
_sessionUpdated: function() {
@ -1628,10 +1706,7 @@ const MessageTray = new Lang.Class({
},
_onNotificationKeyRelease: function(actor, event) {
let ignoredModifiers = global.display.get_ignored_modifier_mask();
let modifierState = event.get_state() & ~ignoredModifiers;
if (event.get_key_symbol() == Clutter.KEY_Escape && modifierState == 0) {
if (event.get_key_symbol() == Clutter.KEY_Escape && event.get_state() == 0) {
this._closeNotification();
return true;
}
@ -1649,15 +1724,7 @@ const MessageTray = new Lang.Class({
},
contains: function(source) {
return this._getIndexOfSummaryItemForSource(source) >= 0;
},
_getIndexOfSummaryItemForSource: function(source) {
for (let i = 0; i < this._summaryItems.length; i++) {
if (this._summaryItems[i].source == source)
return i;
}
return -1;
return this._sources.has(source);
},
add: function(source) {
@ -1666,7 +1733,23 @@ const MessageTray = new Lang.Class({
return;
}
let summaryItem = new SummaryItem(source);
// Register that we got a notification for this source
source.policy.store();
source.policy.connect('enable-changed', Lang.bind(this, this._onSourceEnableChanged, source));
source.policy.connect('policy-changed', Lang.bind(this, this._updateState));
this._onSourceEnableChanged(source.policy, source);
},
_addSource: function(source) {
let obj = {
source: source,
summaryItem: new SummaryItem(source),
notifyId: 0,
destroyId: 0,
mutedChangedId: 0
};
let summaryItem = obj.summaryItem;
if (source.isChat) {
this._summary.insert_child_at_index(summaryItem.actor, 0);
@ -1675,11 +1758,11 @@ const MessageTray = new Lang.Class({
this._summary.insert_child_at_index(summaryItem.actor, this._chatSummaryItemsCount);
}
this._summaryItems.push(summaryItem);
this._sources.set(source, obj);
source.connect('notify', Lang.bind(this, this._onNotify));
source.connect('muted-changed', Lang.bind(this,
obj.notifyId = source.connect('notify', Lang.bind(this, this._onNotify));
obj.destroyId = source.connect('destroy', Lang.bind(this, this._onSourceDestroy));
obj.mutedChangedId = source.connect('muted-changed', Lang.bind(this,
function () {
if (source.isMuted)
this._notificationQueue = this._notificationQueue.filter(function(notification) {
@ -1698,35 +1781,28 @@ const MessageTray = new Lang.Class({
this._onSummaryItemClicked(summaryItem, 3);
}));
source.connect('destroy', Lang.bind(this, this._onSourceDestroy));
// We need to display the newly-added summary item, but if the
// caller is about to post a notification, we want to show that
// *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);
this.emit('source-added', source);
this._updateNoMessagesLabel();
},
getSummaryItems: function() {
return this._summaryItems;
},
_onSourceDestroy: function(source) {
let index = this._getIndexOfSummaryItemForSource(source);
if (index == -1)
return;
let summaryItemToRemove = this._summaryItems[index];
this._summaryItems.splice(index, 1);
_removeSource: function(source) {
let [, obj] = this._sources.delete(source);
let summaryItem = obj.summaryItem;
if (source.isChat)
this._chatSummaryItemsCount--;
source.disconnect(obj.notifyId);
source.disconnect(obj.destroyId);
source.disconnect(obj.mutedChangedId);
let needUpdate = false;
if (this._notification && this._notification.source == source) {
@ -1734,12 +1810,14 @@ const MessageTray = new Lang.Class({
this._notificationRemoved = true;
needUpdate = true;
}
if (this._clickedSummaryItem == summaryItemToRemove) {
if (this._clickedSummaryItem == summaryItem) {
this._setClickedSummaryItem(null);
needUpdate = true;
}
summaryItemToRemove.actor.destroy();
summaryItem.destroy();
this.emit('source-removed', source);
this._updateNoMessagesLabel();
@ -1747,6 +1825,26 @@ const MessageTray = new Lang.Class({
this._updateState();
},
getSources: function() {
return this._sources.keys();
},
_onSourceEnableChanged: function(policy, source) {
let wasEnabled = this.contains(source);
let shouldBeEnabled = policy.enable;
if (wasEnabled != shouldBeEnabled) {
if (shouldBeEnabled)
this._addSource(source);
else
this._removeSource(source);
}
},
_onSourceDestroy: function(source) {
this._removeSource(source);
},
_onNotificationDestroy: function(notification) {
if (this._notification == notification && (this._notificationState == State.SHOWN || this._notificationState == State.SHOWING)) {
this._updateNotificationTimeout(0);
@ -1790,11 +1888,6 @@ const MessageTray = new Lang.Class({
this._updateState();
},
show: function() {
this._traySummoned = true;
this._updateState();
},
_onNotify: function(source, notification) {
if (this._summaryBoxPointerItem && this._summaryBoxPointerItem.source == source) {
if (this._summaryBoxPointerState == State.HIDING) {
@ -1837,7 +1930,7 @@ const MessageTray = new Lang.Class({
},
_onSummaryItemClicked: function(summaryItem, button) {
if (summaryItem.source.handleSummaryClick()) {
if (summaryItem.source.handleSummaryClick(button)) {
if (summaryItem.source.keepTrayOnSummaryClick)
this._setClickedSummaryItem(null);
else
@ -2041,14 +2134,14 @@ const MessageTray = new Lang.Class({
}
// Summary
let summarySummoned = this._pointerInSummary || this._traySummoned;
let summarySummoned = this._pointerInSummary || this._overviewVisible || this._traySummoned;
let summaryPinned = this._pointerInTray || summarySummoned || this._locked;
let summaryHovered = this._pointerInTray || this._pointerInSummary;
let notificationsVisible = this._notificationState != State.HIDDEN;
let notificationsDone = !notificationsVisible && !notificationsPending;
let summaryOptionalInOverview = !this._locked && !summaryHovered;
let summaryOptionalInOverview = this._overviewVisible && !this._locked && !summaryHovered;
let mustHideSummary = (notificationsPending && (notificationUrgent || summaryOptionalInOverview))
|| notificationsVisible || !Main.sessionMode.hasNotifications;
@ -2196,7 +2289,9 @@ const MessageTray = new Lang.Class({
this._tween(this.actor, '_trayState', State.HIDDEN,
{ y: 0,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
transition: 'easeOutQuad',
onComplete: this._onTrayHidden,
onCompleteScope: this
});
// Note that we might have entered here without a grab,
@ -2206,6 +2301,11 @@ const MessageTray = new Lang.Class({
this._lightbox.hide();
},
_onTrayHidden: function() {
if (!this._overviewVisible)
this.actor.remove_style_pseudo_class('overview');
},
_hideDesktopClone: function(now) {
this._tween(this._desktopClone, '_desktopCloneState', State.HIDDEN,
{ y: 0,
@ -2242,7 +2342,7 @@ const MessageTray = new Lang.Class({
this._notificationClickedId = this._notification.connect('done-displaying',
Lang.bind(this, this._escapeTray));
this._notification.connect('unfocused', Lang.bind(this, function() {
this._notificationUnfocusedId = this._notification.connect('unfocused', Lang.bind(this, function() {
this._updateState();
}));
this._notificationBin.child = this._notification.actor;
@ -2271,8 +2371,10 @@ const MessageTray = new Lang.Class({
_updateShowingNotification: function() {
this._notification.acknowledged = true;
// We auto-expand notifications with CRITICAL urgency.
if (this._notification.urgency == Urgency.CRITICAL)
// We auto-expand notifications with CRITICAL urgency, or for which the relevant setting
// is on in the control center.
if (this._notification.urgency == Urgency.CRITICAL ||
this._notification.source.policy.forceExpanded)
this._expandNotification(true);
// We tween all notifications to full opacity. This ensures that both new notifications and
@ -2356,11 +2458,18 @@ const MessageTray = new Lang.Class({
this.idleMonitor.disconnect(this._idleMonitorBecameActiveId);
this._idleMonitorBecameActiveId = 0;
}
if (this._notificationExpandedId) {
this._notification.disconnect(this._notificationExpandedId);
this._notificationExpandedId = 0;
}
if (this._notificationClickedId) {
this._notification.disconnect(this._notificationClickedId);
this._notificationClickedId = 0;
}
if (this._notificationUnfocusedId) {
this._notification.disconnect(this._notificationUnfocusedId);
this._notificationUnfocusedId = 0;
}
if (this._notificationRemoved) {
Tweener.removeTweens(this._notificationWidget);
@ -2382,19 +2491,19 @@ const MessageTray = new Lang.Class({
},
_hideNotificationCompleted: function() {
this._notificationRemoved = false;
this._notificationWidget.hide();
this._closeButton.hide();
this._pointerInTray = false;
this.actor.hover = false; // Clutter doesn't emit notify::hover when actors move
this._notificationBin.child = null;
this._notification.collapseCompleted();
this._notification.disconnect(this._notificationClickedId);
this._notificationClickedId = 0;
let notification = this._notification;
this._notification = null;
if (notification.isTransient)
notification.destroy(NotificationDestroyedReason.EXPIRED);
this._notificationRemoved = false;
this._closeButton.hide();
this._pointerInTray = false;
this.actor.hover = false; // Clutter doesn't emit notify::hover when actors move
this._notificationBin.child = null;
this._notificationWidget.hide();
},
_expandActiveNotification: function() {
@ -2582,6 +2691,20 @@ const MessageTray = new Lang.Class({
_onSummaryBoxPointerUngrabbed: function() {
this._summaryBoxPointerState = State.HIDING;
if (this._summaryBoxPointerContentUpdatedId) {
this._summaryBoxPointerItem.disconnect(this._summaryBoxPointerContentUpdatedId);
this._summaryBoxPointerContentUpdatedId = 0;
}
if (this._summaryBoxPointerCloseClickedId != 0) {
this._summaryBoxPointerItem.closeButton.disconnect(this._summaryBoxPointerCloseClickedId);
this._summaryBoxPointerCloseClickedId = 0;
}
if (this._sourceDoneDisplayingId) {
this._summaryBoxPointerItem.source.disconnect(this._sourceDoneDisplayingId);
this._sourceDoneDisplayingId = 0;
}
this._unlock();
if (this._summaryBoxPointerItem.source.notifications.length == 0) {
@ -2604,14 +2727,6 @@ const MessageTray = new Lang.Class({
this._summaryBoxPointerState = State.HIDDEN;
this._summaryBoxPointer.bin.child = null;
this._summaryBoxPointerItem.disconnect(this._summaryBoxPointerContentUpdatedId);
this._summaryBoxPointerContentUpdatedId = 0;
if (this._summaryBoxPointerCloseClickedId != 0) {
this._summaryBoxPointerItem.closeButton.disconnect(this._summaryBoxPointerCloseClickedId);
this._summaryBoxPointerCloseClickedId = 0;
}
this._summaryBoxPointerItem.source.disconnect(this._sourceDoneDisplayingId);
this._summaryBoxPointerDoneDisplayingId = 0;
let sourceNotificationStackDoneShowing = null;
if (doneShowingNotificationStack) {

View File

@ -37,7 +37,7 @@ const ModalDialog = new Lang.Class({
params = Params.parse(params, { shellReactive: false,
styleClass: null,
parentActor: Main.uiGroup,
keybindingMode: Main.KeybindingMode.SYSTEM_MODAL,
keybindingMode: Shell.KeyBindingMode.SYSTEM_MODAL,
shouldFadeIn: true });
this.state = State.CLOSED;
@ -96,8 +96,7 @@ const ModalDialog = new Lang.Class({
y_align: St.Align.START });
this.buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
visible: false,
vertical: false });
vertical: false });
this.dialogLayout.add(this.buttonLayout,
{ expand: true,
x_align: St.Align.MIDDLE,
@ -120,7 +119,6 @@ const ModalDialog = new Lang.Class({
setButtons: function(buttons) {
this.clearButtons();
this.buttonLayout.visible = (buttons.length > 0);
for (let i = 0; i < buttons.length; i++) {
let buttonInfo = buttons[i];
@ -159,6 +157,7 @@ const ModalDialog = new Lang.Class({
keys = [];
let button = new St.Button({ style_class: 'modal-dialog-button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
reactive: true,
can_focus: true,
label: label });

View File

@ -103,6 +103,126 @@ const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
'ibus-ui-gtk': 'keyboard'
};
const NotificationGenericPolicy = new Lang.Class({
Name: 'NotificationGenericPolicy',
Extends: MessageTray.NotificationPolicy,
_init: function() {
// Don't chain to parent, it would try setting
// our properties to the defaults
this.id = 'generic';
this._masterSettings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications' });
this._masterSettings.connect('changed', Lang.bind(this, this._changed));
},
store: function() { },
destroy: function() {
this._masterSettings.run_dispose();
},
_changed: function(settings, key) {
this.emit('policy-changed', key);
},
get enable() {
return true;
},
get enableSound() {
return true;
},
get showBanners() {
return this._masterSettings.get_boolean('show-banners');
},
get forceExpanded() {
return false;
},
get showInLockScreen() {
return this._masterSettings.get_boolean('show-in-lock-screen');
},
get detailsInLockScreen() {
return false;
}
});
const NotificationApplicationPolicy = new Lang.Class({
Name: 'NotificationApplicationPolicy',
Extends: MessageTray.NotificationPolicy,
_init: function(id) {
// Don't chain to parent, it would try setting
// our properties to the defaults
this.id = id;
this._canonicalId = this._canonicalizeId(id)
this._masterSettings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications' });
this._settings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications.application',
path: '/org/gnome/desktop/notifications/application/' + this._canonicalId + '/' });
this._masterSettings.connect('changed', Lang.bind(this, this._changed));
this._settings.connect('changed', Lang.bind(this, this._changed));
},
store: function() {
this._settings.set_string('application-id', this.id + '.desktop');
let apps = this._masterSettings.get_strv('application-children');
if (apps.indexOf(this._canonicalId) < 0) {
apps.push(this._canonicalId);
this._masterSettings.set_strv('application-children', apps);
}
},
destroy: function() {
this._masterSettings.run_dispose();
this._settings.run_dispose();
},
_changed: function(settings, key) {
this.emit('policy-changed', key);
},
_canonicalizeId: function(id) {
// Keys are restricted to lowercase alphanumeric characters and dash,
// and two dashes cannot be in succession
return id.toLowerCase().replace(/[^a-z0-9\-]/g, '-').replace(/--+/g, '-');
},
get enable() {
return this._settings.get_boolean('enable');
},
get enableSound() {
return this._settings.get_boolean('enable-sound-alerts');
},
get showBanners() {
return this._masterSettings.get_boolean('show-banners') &&
this._settings.get_boolean('show-banners');
},
get forceExpanded() {
return this._settings.get_boolean('force-expanded');
},
get showInLockScreen() {
return this._masterSettings.get_boolean('show-in-lock-screen') &&
this._settings.get_boolean('show-in-lock-screen');
},
get detailsInLockScreen() {
return this._settings.get_boolean('details-in-lock-screen');
}
});
const NotificationDaemon = new Lang.Class({
Name: 'NotificationDaemon',
@ -127,42 +247,42 @@ const NotificationDaemon = new Lang.Class({
this._trayManager.manage_stage(global.stage, Main.messageTray.actor);
},
_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
// the 'image-data' hint. These applications don't typically pass in 'app_icon'
// argument to Notify() and actually expect the pixbuf to be shown as an icon.
// So the logic here does the right thing for this case. If both an icon and either
// one of 'image-data' or 'image-path' are specified, we show both an icon and
// a large image.
if (icon) {
if (icon.substr(0, 7) == 'file://')
return new Gio.FileIcon({ file: Gio.File.new_for_uri(icon) });
else if (icon[0] == '/') {
return new Gio.FileIcon({ file: Gio.File.new_for_path(icon) });
} else
return new Gio.ThemedIcon({ name: icon });
} else if (hints['image-data']) {
_imageForNotificationData: function(hints) {
if (hints['image-data']) {
let [width, height, rowStride, hasAlpha,
bitsPerSample, nChannels, data] = hints['image-data'];
return Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha,
bitsPerSample, width, height, rowStride);
} else if (hints['image-path']) {
return new Gio.FileIcon({ file: Gio.File.new_for_path(hints['image-path']) });
} else {
let stockIcon;
switch (hints.urgency) {
case Urgency.LOW:
case Urgency.NORMAL:
stockIcon = 'gtk-dialog-info';
break;
case Urgency.CRITICAL:
stockIcon = 'gtk-dialog-error';
break;
}
return new Gio.ThemedIcon({ name: stockIcon });
}
return null;
},
_fallbackIconForNotificationData: function(hints) {
let stockIcon;
switch (hints.urgency) {
case Urgency.LOW:
case Urgency.NORMAL:
stockIcon = 'gtk-dialog-info';
break;
case Urgency.CRITICAL:
stockIcon = 'gtk-dialog-error';
break;
}
return new Gio.ThemedIcon({ name: stockIcon });
},
_iconForNotificationData: function(icon) {
if (icon) {
if (icon.substr(0, 7) == 'file://')
return new Gio.FileIcon({ file: Gio.File.new_for_uri(icon) });
else if (icon[0] == '/')
return new Gio.FileIcon({ file: Gio.File.new_for_path(icon) });
else
return new Gio.ThemedIcon({ name: icon });
}
return null;
},
_lookupSource: function(title, pid, trayIcon) {
@ -213,7 +333,7 @@ const NotificationDaemon = new Lang.Class({
}
}
let source = new Source(title, pid, sender, trayIcon);
let source = new Source(title, pid, sender, trayIcon, ndata ? ndata.hints['desktop-entry'] : null);
source.setTransient(isForTransientNotification);
if (!isForTransientNotification) {
@ -355,12 +475,8 @@ const NotificationDaemon = new Lang.Class({
[ndata.id, ndata.icon, ndata.summary, ndata.body,
ndata.actions, ndata.hints, ndata.notification];
let gicon = this._iconForNotificationData(icon, hints);
if (notification == null) {
notification = new MessageTray.Notification(source, summary, body,
{ gicon: gicon,
bannerMarkup: true });
notification = new MessageTray.Notification(source);
ndata.notification = notification;
notification.connect('destroy', Lang.bind(this,
function(n, reason) {
@ -383,29 +499,36 @@ const NotificationDaemon = new Lang.Class({
function(n, actionId) {
this._emitActionInvoked(ndata.id, actionId);
}));
} else {
notification.update(summary, body, { gicon: gicon,
bannerMarkup: true,
clear: true });
}
// We only display a large image if an icon is also specified.
if (icon && (hints['image-data'] || hints['image-path'])) {
let image = null;
if (hints['image-data']) {
let [width, height, rowStride, hasAlpha,
bitsPerSample, nChannels, data] = hints['image-data'];
image = St.TextureCache.get_default().load_from_raw(data, hasAlpha,
width, height, rowStride, notification.IMAGE_SIZE);
} else if (hints['image-path']) {
image = St.TextureCache.get_default().load_uri_async(GLib.filename_to_uri(hints['image-path'], null),
notification.IMAGE_SIZE,
notification.IMAGE_SIZE);
}
notification.setImage(image);
} else {
notification.unsetImage();
}
// Mark music notifications so they can be shown in the screen shield
notification.isMusic = (ndata.hints['category'] == 'x-gnome.music');
let gicon = this._iconForNotificationData(icon, hints);
let gimage = this._imageForNotificationData(hints);
let image = null;
// 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
// the 'image-data' hint. These applications don't typically pass in 'app_icon'
// argument to Notify() and actually expect the pixbuf to be shown as an icon.
// So the logic here does the right thing for this case. If both an icon and either
// one of 'image-data' or 'image-path' are specified, we show both an icon and
// a large image.
if (gicon && gimage)
image = new St.Icon({ gicon: gimage,
icon_size: notification.IMAGE_SIZE });
else if (!gicon && gimage)
gicon = gimage;
else if (!gicon)
gicon = this._fallbackIconForNotificationData(hints);
notification.update(summary, body, { gicon: gicon,
bannerMarkup: true,
clear: true });
notification.setImage(image);
if (actions.length) {
notification.setUseActionIcons(hints['action-icons'] == true);
@ -435,7 +558,7 @@ const NotificationDaemon = new Lang.Class({
// of the 'transient' hint with hints['transient'] rather than hints.transient
notification.setTransient(hints['transient'] == true);
let sourceGIcon = source.useNotificationIcon ? this._iconForNotificationData(icon, hints) : null;
let sourceGIcon = source.useNotificationIcon ? gicon : null;
source.processNotification(notification, sourceGIcon);
},
@ -515,12 +638,22 @@ const Source = new Lang.Class({
Name: 'NotificationDaemonSource',
Extends: MessageTray.Source,
_init: function(title, pid, sender, trayIcon) {
_init: function(title, pid, sender, trayIcon, appId) {
// Need to set the app before chaining up, so
// methods called from the parent constructor can find it
this.trayIcon = trayIcon;
this.pid = pid;
this.app = this._getApp(appId);
this.parent(title);
this.initialTitle = title;
this.pid = pid;
if (this.app)
this.title = this.app.get_name();
else
this.useNotificationIcon = true;
if (sender)
this._nameWatcherId = Gio.DBus.session.watch_name(sender,
Gio.BusNameWatcherFlags.NONE,
@ -529,16 +662,19 @@ const Source = new Lang.Class({
else
this._nameWatcherId = 0;
this._setApp();
if (this.app)
this.title = this.app.get_name();
else
this.useNotificationIcon = true;
this.trayIcon = trayIcon;
if (this.trayIcon) {
this._setSummaryIcon(this.trayIcon);
this.useNotificationIcon = false;
// Try again finding the app, using the WM_CLASS from the tray icon
this._setSummaryIcon(this.trayIcon);
this.useNotificationIcon = false;
}
},
_createPolicy: function() {
if (this.app) {
let id = this.app.get_id().replace(/\.desktop$/,'');
return new NotificationApplicationPolicy(id);
} else {
return new NotificationGenericPolicy();
}
},
@ -565,19 +701,17 @@ const Source = new Lang.Class({
this.notify(notification);
},
handleSummaryClick: function() {
handleSummaryClick: function(button) {
if (!this.trayIcon)
return false;
let event = Clutter.get_current_event();
if (event.type() != Clutter.EventType.BUTTON_RELEASE)
return false;
// Left clicks are passed through only where there aren't unacknowledged
// notifications, so it possible to open them in summary mode; right
// clicks are always forwarded, as the right click menu is not useful for
// tray icons
if (event.get_button() == 1 &&
if (button == 1 &&
this.notifications.length > 0)
return false;
@ -590,7 +724,7 @@ const Source = new Lang.Class({
return true;
},
_getApp: function() {
_getApp: function(appId) {
let app;
app = Shell.WindowTracker.get_default().get_app_from_pid(this.pid);
@ -598,7 +732,13 @@ const Source = new Lang.Class({
return app;
if (this.trayIcon) {
app = Shell.AppSystem.get_default().lookup_wmclass(this.trayIcon.wmclass);
app = Shell.AppSystem.get_default().lookup_wmclass(this.trayIcon.wm_class);
if (app != null)
return app;
}
if (appId) {
app = Shell.AppSystem.get_default().lookup_app(appId + '.desktop');
if (app != null)
return app;
}
@ -606,11 +746,11 @@ const Source = new Lang.Class({
return null;
},
_setApp: function() {
_setApp: function(appId) {
if (this.app)
return;
this.app = this._getApp();
this.app = this._getApp(appId);
if (!this.app)
return;

View File

@ -10,7 +10,6 @@ const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Gdk = imports.gi.Gdk;
const CenterLayout = imports.ui.centerLayout;
const Dash = imports.ui.dash;
const DND = imports.ui.dnd;
const Main = imports.ui.main;
@ -31,9 +30,9 @@ const GLSL_DIM_EFFECT_CODE = '\
vec2 dist = cogl_tex_coord_in[0].xy - vec2(0.5, 0.5); \
float elipse_radius = 0.5; \
/* from https://bugzilla.gnome.org/show_bug.cgi?id=669798: \
the alpha on the gradient goes from 165 at its darkest to 98 at its most transparent. */ \
float y = 165.0 / 255.0; \
float x = 98.0 / 255.0; \
the alpha on the gradient goes from 250 at its darkest to 180 at its most transparent. */ \
float y = 250.0 / 255.0; \
float x = 180.0 / 255.0; \
/* interpolate darkening value, based on distance from screen center */ \
float val = min(length(dist), elipse_radius); \
float a = mix(x, y, val / elipse_radius); \
@ -98,13 +97,6 @@ const ShellInfo = new Lang.Class({
}
});
const ControlsChange = {
BEFORE_PAGE: 1,
AFTER_PAGE: 2,
DND_START: 3,
DND_END: 4
};
const Overview = new Lang.Class({
Name: 'Overview',
@ -149,19 +141,7 @@ const Overview = new Lang.Class({
vertical: true });
this._overview._delegate = this;
let layout = new CenterLayout.CenterLayout();
this._group = new St.Widget({ name: 'overview-group',
layout_manager: layout });
this._spacing = 0;
this._group.connect('style-changed',
Lang.bind(this, function() {
let node = this._group.get_theme_node();
let spacing = node.get_length('spacing');
if (spacing != this._spacing) {
this._spacing = spacing;
}
}));
this._group = new St.BoxLayout({ name: 'overview-group' });
this._capturedEventId = 0;
this._buttonPressId = 0;
@ -257,97 +237,25 @@ const Overview = new Lang.Class({
this._viewSelector = new ViewSelector.ViewSelector(this._searchEntry,
this._dash.showAppsButton);
this._group.add_actor(this._viewSelector.actor);
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
this._group.add_actor(this._thumbnailsBox.actor);
Main.ctrlAltTabManager.addGroup(this._thumbnailsBox.actor, _("Workspaces"), 'view-list-symbolic');
this._group.add(this._viewSelector.actor, { x_fill: true,
expand: true });
// Add our same-line elements after the search entry
this._overview.add_actor(this._group);
this._overview.add(this._group, { y_fill: true,
expand: true });
// Then account for message tray
this._messageTrayGhost = new St.Bin({ child: new Clutter.Clone({ source: Main.messageTray.actor }),
this._messageTrayGhost = new St.Bin({ style_class: 'message-tray-summary',
reactive: false,
opacity: 0,
x_fill: true,
y_fill: true });
this._overview.add_actor(this._messageTrayGhost);
this._viewSelector.connect('after-page-change', Lang.bind(this,
function() {
this._setSideControlsVisibility(ControlsChange.AFTER_PAGE);
}));
this._viewSelector.connect('before-page-change', Lang.bind(this,
function() {
this._setSideControlsVisibility(ControlsChange.BEFORE_PAGE);
}));
this.connect('item-drag-begin', Lang.bind(this,
function() {
this._setSideControlsVisibility(ControlsChange.DND_START);
}));
this.connect('item-drag-cancelled', Lang.bind(this,
function() {
this._setSideControlsVisibility(ControlsChange.DND_END);
}));
this.connect('item-drag-end', Lang.bind(this,
function() {
this._setSideControlsVisibility(ControlsChange.DND_END);
}));
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
this._relayout();
},
_setSideControlsVisibility: function(changeType) {
// Ignore the case when we're leaving the overview, since
// actors will be made visible again when entering the overview
// next time, and animating them while doing so is just
// unnecesary noise
if (!this.visible || this.animationInProgress)
return;
let appsActive = this._viewSelector.getAppsActive();
let searchActive = this._viewSelector.getSearchActive();
let dashVisible = !searchActive || (changeType == ControlsChange.DND_START);
let thumbnailsVisible = (!searchActive && !appsActive) || (changeType == ControlsChange.DND_START);
let trayVisible = !searchActive;
let trayGhostVisible = trayVisible || dashVisible;
if ((changeType == ControlsChange.BEFORE_PAGE) ||
(changeType == ControlsChange.DND_START)) {
if (dashVisible)
this._dash.show();
if (thumbnailsVisible)
this._thumbnailsBox.show();
}
if ((changeType == ControlsChange.BEFORE_PAGE) ||
(changeType == ControlsChange.DND_END)) {
if (!dashVisible) {
this._dash.hide();
}
if (!thumbnailsVisible)
this._thumbnailsBox.hide();
}
if (changeType == ControlsChange.BEFORE_PAGE ||
changeType == ControlsChange.DND_START) {
if (trayGhostVisible)
this._messageTrayGhost.show();
if (trayVisible)
Main.messageTray.show();
else
Main.messageTray.hide();
} else if (changeType == ControlsChange.AFTER_PAGE ||
changeType == ControlsChange.DND_END) {
if (!trayGhostVisible)
this._messageTrayGhost.hide();
}
},
addSearchProvider: function(provider) {
this._viewSelector.addSearchProvider(provider);
},
@ -486,7 +394,7 @@ const Overview = new Lang.Class({
stackIndices[stack[i].get_meta_window().get_stable_sequence()] = i;
}
this.emit('sync-window-stacking', stackIndices);
this.emit('windows-restacked', stackIndices);
},
//// Public methods ////
@ -570,7 +478,6 @@ const Overview = new Lang.Class({
// Disable unredirection while in the overview
Meta.disable_unredirect_for_screen(global.screen);
global.window_group.hide();
this._messageTrayGhost.show();
this._overview.show();
this._background.show();
this._viewSelector.show();
@ -669,7 +576,7 @@ const Overview = new Lang.Class({
if (this._shown) {
if (!this._modal) {
if (Main.pushModal(this._overview,
{ keybindingMode: Main.KeybindingMode.OVERVIEW }))
{ keybindingMode: Shell.KeyBindingMode.OVERVIEW }))
this._modal = true;
else
this.hide();

View File

@ -15,7 +15,6 @@ const Signals = imports.signals;
const Atk = imports.gi.Atk;
const CenterLayout = imports.ui.centerLayout;
const Config = imports.misc.config;
const CtrlAltTab = imports.ui.ctrlAltTab;
const DND = imports.ui.dnd;
@ -316,7 +315,7 @@ const AppMenuButton = new Lang.Class({
},
show: function() {
if (this._visible || Main.screenShield.locked)
if (this._visible)
return;
this._visible = true;
@ -599,6 +598,11 @@ const AppMenuButton = new Lang.Class({
return;
menu = new PopupMenu.RemoteMenu(this.actor, this._targetApp.menu, this._targetApp.action_group);
menu.connect('activate', Lang.bind(this, function() {
let win = this._targetApp.get_windows()[0];
win.check_alive(global.get_current_time());
}));
} else {
if (this.menu.isDummyQuitMenu)
return;
@ -747,12 +751,9 @@ const ActivitiesButton = new Lang.Class({
const PanelCorner = new Lang.Class({
Name: 'PanelCorner',
_init: function(box, side) {
_init: function(side) {
this._side = side;
this._box = box;
this._box.connect('style-changed', Lang.bind(this, this._boxStyleChanged));
this.actor = new St.DrawingArea({ style_class: 'panel-corner' });
this.actor.connect('style-changed', Lang.bind(this, this._styleChanged));
this.actor.connect('repaint', Lang.bind(this, this._repaint));
@ -808,12 +809,12 @@ const PanelCorner = new Lang.Class({
return children[index];
},
_boxStyleChanged: function() {
setStyleParent: function(box) {
let side = this._side;
let rtlAwareContainer = this._box instanceof St.BoxLayout;
let rtlAwareContainer = box instanceof St.BoxLayout;
if (rtlAwareContainer &&
this._box.get_text_direction() == Clutter.TextDirection.RTL) {
box.get_text_direction() == Clutter.TextDirection.RTL) {
if (this._side == St.Side.LEFT)
side = St.Side.RIGHT;
else if (this._side == St.Side.RIGHT)
@ -822,9 +823,9 @@ const PanelCorner = new Lang.Class({
let button;
if (side == St.Side.LEFT)
button = this._findLeftmostButton(this._box);
button = this._findLeftmostButton(box);
else if (side == St.Side.RIGHT)
button = this._findRightmostButton(this._box);
button = this._findRightmostButton(box);
if (button) {
if (this._button && this._buttonStyleChangedSignalId) {
@ -851,7 +852,7 @@ const PanelCorner = new Lang.Class({
// The corner doesn't support theme transitions, so override
// the .panel-button default
button.style = 'transition-duration: 0';
button.style = 'transition-duration: 0ms';
}
},
@ -864,8 +865,8 @@ const PanelCorner = new Lang.Class({
let backgroundColor = node.get_color('-panel-corner-background-color');
let borderColor = node.get_color('-panel-corner-border-color');
let noOverlap = borderColor.alpha == 0;
let offsetY = noOverlap ? borderWidth : 0;
let overlap = borderColor.alpha != 0;
let offsetY = overlap ? 0 : borderWidth;
let cr = this.actor.get_context();
cr.setOperator(Cairo.Operator.SOURCE);
@ -889,17 +890,18 @@ const PanelCorner = new Lang.Class({
Clutter.cairo_set_source_color(cr, over);
cr.fill();
if (noOverlap)
return;
if (overlap) {
let offset = borderWidth;
Clutter.cairo_set_source_color(cr, backgroundColor);
let offset = borderWidth;
Clutter.cairo_set_source_color(cr, backgroundColor);
cr.save();
cr.translate(xOffsetDirection * offset, - offset);
cr.appendPath(savedPath);
cr.fill();
cr.restore();
}
cr.save();
cr.translate(xOffsetDirection * offset, - offset);
cr.appendPath(savedPath);
cr.fill();
cr.restore();
cr.$dispose();
},
_styleChanged: function() {
@ -938,47 +940,12 @@ try {
log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
}
const PanelLayout = new Lang.Class({
Name: 'PanelLayout',
Extends: CenterLayout.CenterLayout,
vfunc_allocate: function(container, box, flags) {
this.parent(container, box, flags);
let availWidth = box.x2 - box.x1;
let availHeight = box.y2 - box.y1;
let [left, center, right, leftCorner, rightCorner] = container.get_children();
let childBox = new Clutter.ActorBox();
let cornerMinWidth, cornerMinHeight;
let cornerWidth, cornerHeight;
[cornerMinWidth, cornerWidth] = leftCorner.get_preferred_width(-1);
[cornerMinHeight, cornerHeight] = leftCorner.get_preferred_height(-1);
childBox.x1 = 0;
childBox.x2 = cornerWidth;
childBox.y1 = availHeight;
childBox.y2 = availHeight + cornerHeight;
leftCorner.allocate(childBox, flags);
[cornerMinWidth, cornerWidth] = rightCorner.get_preferred_width(-1);
[cornerMinHeight, cornerHeight] = rightCorner.get_preferred_height(-1);
childBox.x1 = availWidth - cornerWidth;
childBox.x2 = availWidth;
childBox.y1 = availHeight;
childBox.y2 = availHeight + cornerHeight;
rightCorner.allocate(childBox, flags);
}
});
const Panel = new Lang.Class({
Name: 'Panel',
_init : function() {
this.actor = new St.Widget({ name: 'panel',
reactive: true,
layoutManager: new PanelLayout() });
this.actor = new Shell.GenericContainer({ name: 'panel',
reactive: true });
this.actor._delegate = this;
this._sessionStyle = null;
@ -994,20 +961,24 @@ const Panel = new Lang.Class({
this._rightBox = new St.BoxLayout({ name: 'panelRight' });
this.actor.add_actor(this._rightBox);
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL)
this._leftCorner = new PanelCorner(this._rightBox, St.Side.LEFT);
else
this._leftCorner = new PanelCorner(this._leftBox, St.Side.LEFT);
this._leftCorner = new PanelCorner(St.Side.LEFT);
this.actor.add_actor(this._leftCorner.actor);
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL)
this._rightCorner = new PanelCorner(this._leftBox, St.Side.RIGHT);
else
this._rightCorner = new PanelCorner(this._rightBox, St.Side.RIGHT);
this._rightCorner = new PanelCorner(St.Side.RIGHT);
this.actor.add_actor(this._rightCorner.actor);
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this.actor.connect('allocate', Lang.bind(this, this._allocate));
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
Main.overview.connect('showing', Lang.bind(this, function () {
this.actor.add_style_pseudo_class('overview');
}));
Main.overview.connect('hiding', Lang.bind(this, function () {
this.actor.remove_style_pseudo_class('overview');
}));
Main.layoutManager.panelBox.add(this.actor);
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'emblem-system-symbolic',
{ sortGroup: CtrlAltTab.SortGroup.TOP });
@ -1016,6 +987,83 @@ const Panel = new Lang.Class({
this._updatePanel();
},
_getPreferredWidth: function(actor, forHeight, alloc) {
alloc.min_size = -1;
alloc.natural_size = Main.layoutManager.primaryMonitor.width;
},
_getPreferredHeight: function(actor, forWidth, alloc) {
// We don't need to implement this; it's forced by the CSS
alloc.min_size = -1;
alloc.natural_size = -1;
},
_allocate: function(actor, box, flags) {
let allocWidth = box.x2 - box.x1;
let allocHeight = box.y2 - box.y1;
let [leftMinWidth, leftNaturalWidth] = this._leftBox.get_preferred_width(-1);
let [centerMinWidth, centerNaturalWidth] = this._centerBox.get_preferred_width(-1);
let [rightMinWidth, rightNaturalWidth] = this._rightBox.get_preferred_width(-1);
let sideWidth, centerWidth;
centerWidth = centerNaturalWidth;
sideWidth = (allocWidth - centerWidth) / 2;
let childBox = new Clutter.ActorBox();
childBox.y1 = 0;
childBox.y2 = allocHeight;
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL) {
childBox.x1 = allocWidth - Math.min(Math.floor(sideWidth),
leftNaturalWidth);
childBox.x2 = allocWidth;
} else {
childBox.x1 = 0;
childBox.x2 = Math.min(Math.floor(sideWidth),
leftNaturalWidth);
}
this._leftBox.allocate(childBox, flags);
childBox.x1 = Math.ceil(sideWidth);
childBox.y1 = 0;
childBox.x2 = childBox.x1 + centerWidth;
childBox.y2 = allocHeight;
this._centerBox.allocate(childBox, flags);
childBox.y1 = 0;
childBox.y2 = allocHeight;
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL) {
childBox.x1 = 0;
childBox.x2 = Math.min(Math.floor(sideWidth),
rightNaturalWidth);
} else {
childBox.x1 = allocWidth - Math.min(Math.floor(sideWidth),
rightNaturalWidth);
childBox.x2 = allocWidth;
}
this._rightBox.allocate(childBox, flags);
let cornerMinWidth, cornerMinHeight;
let cornerWidth, cornerHeight;
[cornerMinWidth, cornerWidth] = this._leftCorner.actor.get_preferred_width(-1);
[cornerMinHeight, cornerHeight] = this._leftCorner.actor.get_preferred_height(-1);
childBox.x1 = 0;
childBox.x2 = cornerWidth;
childBox.y1 = allocHeight;
childBox.y2 = allocHeight + cornerHeight;
this._leftCorner.actor.allocate(childBox, flags);
[cornerMinWidth, cornerWidth] = this._rightCorner.actor.get_preferred_width(-1);
[cornerMinHeight, cornerHeight] = this._rightCorner.actor.get_preferred_height(-1);
childBox.x1 = allocWidth - cornerWidth;
childBox.x2 = allocWidth;
childBox.y1 = allocHeight;
childBox.y2 = allocHeight + cornerHeight;
this._rightCorner.actor.allocate(childBox, flags);
},
_onButtonPress: function(actor, event) {
if (event.get_source() != actor)
return false;
@ -1096,17 +1144,13 @@ const Panel = new Lang.Class({
this._sessionStyle = Main.sessionMode.panelStyle;
if (this._sessionStyle)
this._addStyleClassName(this._sessionStyle);
},
_initBox: function(elements, box) {
for (let i = 0; i < elements.length; i++) {
let role = elements[i];
let constructor = PANEL_ITEM_IMPLEMENTATIONS[role];
if (!constructor) {
// panel icon is not supported (can happen for
// bluetooth or network)
continue;
}
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL) {
this._leftCorner.setStyleParent(this._rightBox);
this._rightCorner.setStyleParent(this._leftBox);
} else {
this._leftCorner.setStyleParent(this._leftBox);
this._rightCorner.setStyleParent(this._rightBox);
}
},

View File

@ -244,18 +244,21 @@ const SystemStatusButton = new Lang.Class({
this.setIcon(iconName);
},
get icons() {
return this._box.get_children();
},
addIcon: function(gicon) {
let icon = new St.Icon({ gicon: gicon,
style_class: 'system-status-icon' });
this._box.add_actor(icon);
this.emit('icons-changed');
return icon;
},
setIcon: function(iconName) {
// Need to first add a NULL GIcon and then set icon_name, to ensure
// compatibility with -symbolic fallbacks
if (!this.mainIcon)
this.mainIcon = this.addIcon(null);
this.mainIcon.icon_name = iconName;

View File

@ -41,10 +41,10 @@ const PointerWatcher = new Lang.Class({
Name: 'PointerWatcher',
_init: function() {
let idleMonitor = new GnomeDesktop.IdleMonitor();
idleMonitor.connect('became-active', Lang.bind(this, this._onIdleMonitorBecameActive));
idleMonitor.add_watch(IDLE_TIME, Lang.bind(this, this._onIdleMonitorBecameIdle));
this._idle = idleMonitor.get_idletime() > IDLE_TIME;
this._idleMonitor = new GnomeDesktop.IdleMonitor();
this._idleMonitor.connect('became-active', Lang.bind(this, this._onIdleMonitorBecameActive));
this._idleMonitor.add_watch(IDLE_TIME, Lang.bind(this, this._onIdleMonitorBecameIdle));
this._idle = this._idleMonitor.get_idletime() > IDLE_TIME;
this._watches = [];
this.pointerX = null;
this.pointerY = null;

View File

@ -207,6 +207,7 @@ const PopupBaseMenuItem = new Lang.Class({
color.alpha / 255);
cr.arc(width / 2, height / 2, width / 3, 0, 2 * Math.PI);
cr.fill();
cr.$dispose();
},
// This returns column widths in logical order (i.e. from the dot
@ -604,6 +605,7 @@ const PopupSliderMenuItem = new Lang.Class({
color.alpha / 255);
cr.arc(handleX, handleY, handleRadius, 0, 2 * Math.PI);
cr.fill();
cr.$dispose();
},
_startDragging: function(actor, event) {
@ -636,20 +638,34 @@ const PopupSliderMenuItem = new Lang.Class({
return true;
},
_onScrollEvent: function (actor, event) {
scroll: function(event) {
let direction = event.get_scroll_direction();
let delta;
if (event.is_pointer_emulated())
return;
if (direction == Clutter.ScrollDirection.DOWN) {
this._value = Math.max(0, this._value - SLIDER_SCROLL_STEP);
}
else if (direction == Clutter.ScrollDirection.UP) {
this._value = Math.min(1, this._value + SLIDER_SCROLL_STEP);
delta = -SLIDER_SCROLL_STEP;
} else if (direction == Clutter.ScrollDirection.UP) {
delta = +SLIDER_SCROLL_STEP;
} else if (direction == Clutter.ScrollDirection.SMOOTH) {
let [dx, dy] = event.get_scroll_delta();
// Even though the slider is horizontal, use dy to match
// the UP/DOWN above.
delta = -dy / 10;
}
this._value = Math.min(Math.max(0, this._value + delta), 1);
this._slider.queue_repaint();
this.emit('value-changed', this._value);
},
_onScrollEvent: function(actor, event) {
this.scroll(event);
},
_motionEvent: function(actor, event) {
let absX, absY;
[absX, absY] = event.get_coords();
@ -873,7 +889,7 @@ const PopupMenuBase = new Lang.Class({
addSettingsAction: function(title, desktopFile) {
let menuItem = this.addAction(title, function() {
let app = Shell.AppSystem.get_default().lookup_setting(desktopFile);
let app = Shell.AppSystem.get_default().lookup_app(desktopFile);
if (!app) {
log('Settings panel for desktop file ' + desktopFile + ' could not be loaded!');

View File

@ -1,9 +1,11 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const GdkPixbuf = imports.gi.GdkPixbuf;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const FileUtils = imports.misc.fileUtils;
const Search = imports.ui.search;
@ -135,8 +137,12 @@ function remoteProvidersLoaded(loadState) {
idxB = sortOrder.indexOf(appIdB);
// if no provider is found in the order, use alphabetical order
if ((idxA == -1) && (idxB == -1))
return GLib.utf8_collate(providerA.title, providerB.title);
if ((idxA == -1) && (idxB == -1)) {
let nameA = providerA.appInfo.get_name();
let nameB = providerB.appInfo.get_name();
return GLib.utf8_collate(nameA, nameB);
}
if (numSorted > 1) {
// if providerA is the last, it goes after everything
@ -187,18 +193,18 @@ const RemoteSearchProvider = new Lang.Class({
},
createIcon: function(size, meta) {
let gicon;
if (meta['gicon']) {
return new St.Icon({ gicon: Gio.icon_new_for_string(meta['gicon']),
icon_size: size });
gicon = Gio.icon_new_for_string(meta['gicon']);
} else if (meta['icon-data']) {
let [width, height, rowStride, hasAlpha,
bitsPerSample, nChannels, data] = meta['icon-data'];
let textureCache = St.TextureCache.get_default();
return textureCache.load_from_raw(data, hasAlpha,
width, height, rowStride, size);
gicon = Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha,
bitsPerSample, width, height, rowStride);
}
return null;
return new St.Icon({ gicon: gicon,
icon_size: size });
},
_getResultsFinished: function(results, error) {
@ -215,7 +221,7 @@ const RemoteSearchProvider = new Lang.Class({
Lang.bind(this, this._getResultsFinished),
this._cancellable);
} catch(e) {
log('Error calling GetInitialResultSet for provider %s: %s'.format( this.title, e.toString()));
log('Error calling GetInitialResultSet for provider %s: %s'.format(this.id, e.toString()));
this.searchSystem.pushResults(this, []);
}
},
@ -228,7 +234,7 @@ const RemoteSearchProvider = new Lang.Class({
Lang.bind(this, this._getResultsFinished),
this._cancellable);
} catch(e) {
log('Error calling GetSubsearchResultSet for provider %s: %s'.format(this.title, e.toString()));
log('Error calling GetSubsearchResultSet for provider %s: %s'.format(this.id, e.toString()));
this.searchSystem.pushResults(this, []);
}
},
@ -259,7 +265,7 @@ const RemoteSearchProvider = new Lang.Class({
Lang.bind(this, this._getResultMetasFinished, callback),
this._cancellable);
} catch(e) {
log('Error calling GetResultMetas for provider %s: %s'.format(this.title, e.toString()));
log('Error calling GetResultMetas for provider %s: %s'.format(this.id, e.toString()));
callback([]);
}
},

View File

@ -8,11 +8,13 @@ const GnomeDesktop = imports.gi.GnomeDesktop;
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 TweenerEquations = imports.tweener.equations;
const GnomeSession = imports.misc.gnomeSession;
const Hash = imports.misc.hash;
const Layout = imports.ui.layout;
const LoginManager = imports.misc.loginManager;
const Lightbox = imports.ui.lightbox;
@ -25,6 +27,7 @@ const Util = imports.misc.util;
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
const LOCK_ENABLED_KEY = 'lock-enabled';
const LOCK_DELAY_KEY = 'lock-delay';
const CURTAIN_SLIDE_TIME = 0.3;
// fraction of screen height the arrow must reach before completing
@ -126,49 +129,48 @@ const NotificationsBox = new Lang.Class({
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.START });
this._persistentNotificationBox = new St.BoxLayout({ vertical: true,
style_class: 'screen-shield-notifications-box' });
scrollView.add_actor(this._persistentNotificationBox);
this._musicBin = new St.Bin({ style_class: 'screen-shield-notifications-box',
visible: false });
this.actor.add(this._residentNotificationBox, { x_fill: true });
let scrollView = new St.ScrollView({ x_fill: false, x_align: St.Align.START });
this._notificationBox = new St.BoxLayout({ vertical: true,
style_class: 'screen-shield-notifications-box' });
scrollView.add_actor(this._notificationBox);
this.actor.add(this._musicBin);
this.actor.add(scrollView, { x_fill: true, x_align: St.Align.START });
this._items = [];
Main.messageTray.getSummaryItems().forEach(Lang.bind(this, function(item) {
this._summaryItemAdded(Main.messageTray, item, true);
this._sources = new Hash.Map();
Main.messageTray.getSources().forEach(Lang.bind(this, function(source) {
this._sourceAdded(Main.messageTray, source, true);
}));
this._updateVisibility();
this._summaryAddedId = Main.messageTray.connect('summary-item-added', Lang.bind(this, this._summaryItemAdded));
this._sourceAddedId = Main.messageTray.connect('source-added', Lang.bind(this, this._sourceAdded));
},
destroy: function() {
if (this._summaryAddedId) {
Main.messageTray.disconnect(this._summaryAddedId);
this._summaryAddedId = 0;
if (this._sourceAddedId) {
Main.messageTray.disconnect(this._sourceAddedId);
this._sourceAddedId = 0;
}
for (let i = 0; i < this._items.length; i++)
this._removeItem(this._items[i]);
this._items = [];
let items = this._sources.items();
for (let i = 0; i < items.length; i++) {
let [source, obj] = items[i];
this._removeSource(source, obj);
}
this.actor.destroy();
},
_updateVisibility: function() {
this._residentNotificationBox.visible = this._residentNotificationBox.get_n_children() > 0;
this._persistentNotificationBox.visible = this._persistentNotificationBox.get_children().some(function(a) {
this._musicBin.visible = this._musicBin.child != null && this._musicBin.child.visible;
this._notificationBox.visible = this._notificationBox.get_children().some(function(a) {
return a.visible;
});
this.actor.visible = this._residentNotificationBox.visible || this._persistentNotificationBox.visible;
},
_sourceIsResident: function(source) {
return source.hasResidentNotification() && !source.isChat;
this.actor.visible = this._musicBin.visible || this._notificationBox.visible;
},
_makeNotificationCountText: function(count, isChat) {
@ -178,18 +180,16 @@ const NotificationsBox = new Lang.Class({
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' });
_makeNotificationSource: function(source, box) {
let sourceActor = new MessageTray.SourceActor(source, SUMMARY_ICON_SIZE);
box.add(sourceActor.actor, { y_fill: true });
let textBox = new St.BoxLayout({ vertical: true });
box.add(textBox, { y_fill: false, y_align: St.Align.START });
let label = new St.Label({ text: source.title,
let title = new St.Label({ text: source.title,
style_class: 'screen-shield-notification-label' });
textBox.add(label);
textBox.add(title);
let count = source.unseenCount;
let countLabel = new St.Label({ text: this._makeNotificationCountText(count, source.isChat),
@ -197,118 +197,179 @@ const NotificationsBox = new Lang.Class({
textBox.add(countLabel);
box.visible = count != 0;
return [box, countLabel];
return [title, countLabel];
},
_summaryItemAdded: function(tray, item, dontUpdateVisibility) {
// Ignore transient sources, or sources explicitly marked not to show
// in the lock screen
if (item.source.isTransient || !item.source.showInLockScreen)
_makeNotificationDetailedSource: function(source, box) {
let sourceActor = new MessageTray.SourceActor(source, SUMMARY_ICON_SIZE);
box.add(sourceActor.actor, { y_fill: true });
let textBox = new St.BoxLayout({ vertical: true });
box.add(textBox, { y_fill: false, y_align: St.Align.START });
let title = new St.Label({ text: source.title,
style_class: 'screen-shield-notification-label' });
textBox.add(title);
let visible = false;
for (let i = 0; i < source.notifications.length; i++) {
let n = source.notifications[i];
if (n.acknowledged || n.isMusic)
continue;
let body = '';
if (n.bannerBodyText) {
body = n.bannerBodyMarkup ? n.bannerBodyText :
GLib.markup_escape_text(n.bannerBodyMarkup, -1);
}
let label = new St.Label({ style_class: 'screen-shield-notification-count-text' });
label.clutter_text.set_markup('<b>' + n.title + '</b> ' + body);
textBox.add(label);
visible = true;
}
box.visible = visible;
return [title, null];
},
_showSource: function(source, obj, box) {
let musicNotification = source.getMusicNotification();
if (musicNotification != null &&
this._musicBin.child == null) {
if (musicNotification.actor.get_parent() != null)
musicNotification.actor.get_parent().remove_actor(musicNotification.actor);
this._musicBin.child = musicNotification.actor;
this._musicBin.child.visible = obj.visible;
musicNotification.expand(false /* animate */);
obj.musicNotification = musicNotification;
}
if (obj.detailed) {
[obj.titleLabel, obj.countLabel] = this._makeNotificationDetailedSource(source, box);
} else {
[obj.titleLabel, obj.countLabel] = this._makeNotificationSource(source, box);
}
box.visible = obj.visible &&
(source.unseenCount > (musicNotification ? 1 : 0));
},
_sourceAdded: function(tray, source, dontUpdateVisibility) {
// Ignore transient sources
if (source.isTransient)
return;
let obj = {
item: item,
source: item.source,
resident: this._sourceIsResident(item.source),
contentUpdatedId: 0,
visible: source.policy.showInLockScreen,
detailed: source.policy.detailsInLockScreen,
sourceDestroyId: 0,
sourceCountChangedId: 0,
sourceTitleChangedId: 0,
sourceUpdatedId: 0,
musicNotification: null,
sourceBox: null,
titleLabel: null,
countLabel: null,
};
if (obj.resident) {
this._residentNotificationBox.add(item.notificationStackWidget);
item.closeButton.hide();
item.prepareNotificationStackForShowing();
} else {
[obj.sourceBox, obj.countLabel] = this._makeNotificationSource(item.source);
this._persistentNotificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.START });
}
obj.sourceBox = new St.BoxLayout({ style_class: 'screen-shield-notification-source' });
this._showSource(source, obj, obj.sourceBox);
this._notificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.START });
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);
obj.sourceCountChangedId = source.connect('count-updated', Lang.bind(this, function(source) {
this._countChanged(source, obj);
}));
obj.sourceTitleChangedId = source.connect('title-changed', Lang.bind(this, function(source) {
this._titleChanged(source, obj);
}));
obj.policyChangedId = source.policy.connect('policy-changed', Lang.bind(this, function(policy, key) {
if (key == 'show-in-lock-screen')
this._visibleChanged(source, obj);
else
this._detailedChanged(source, obj);
}));
obj.sourceDestroyId = source.connect('destroy', Lang.bind(this, function(source) {
this._onSourceDestroy(source, obj);
}));
this._sources.set(source, obj);
if (!dontUpdateVisibility)
this._updateVisibility();
},
_findSource: function(source) {
for (let i = 0; i < this._items.length; i++) {
if (this._items[i].source == source)
return i;
_titleChanged: function(source, obj) {
obj.titleLabel.text = source.title;
},
_countChanged: function(source, obj) {
if (obj.detailed) {
// A new notification was pushed, or a previous notification was destroyed.
// Give up, and build the list again.
obj.sourceBox.destroy_all_children();
obj.titleLabel = obj.countLabel = null;
this._showSource(source, obj, obj.sourceBox);
} else {
let count = source.unseenCount;
obj.countLabel.text = this._makeNotificationCountText(count, source.isChat);
}
return -1;
obj.sourceBox.visible = obj.visible &&
(source.unseenCount > (obj.musicNotification ? 1 : 0));
this._updateVisibility();
},
_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
_visibleChanged: function(source, obj) {
if (obj.visible == source.policy.showInLockScreen)
return;
}
if (obj.resident && !itemShouldBeResident) {
// make into a regular item
obj.item.doneShowingNotificationStack();
this._residentNotificationBox.remove_actor(obj.item.notificationStackWidget);
[obj.sourceBox, obj.countLabel] = this._makeNotificationSource(obj.source);
this._persistentNotificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.START });
} else if (itemShouldBeResident && !obj.resident) {
// make into a resident item
obj.sourceBox.destroy();
obj.sourceBox = obj.countLabel = null;
obj.resident = true;
this._residentNotificationBox.add(obj.item.notificationStackWidget);
obj.item.closeButton.hide();
obj.item.prepareNotificationStackForShowing();
} else {
// just update the counter
let count = obj.source.unseenCount;
obj.countLabel.text = this._makeNotificationCountText(count, obj.source.isChat);
obj.sourceBox.visible = count != 0;
}
obj.visible = source.policy.showInLockScreen;
if (obj.musicNotification)
obj.musicNotification.actor.visible = obj.visible;
obj.sourceBox.visible = obj.visible &&
source.unseenCount > (obj.musicNotification ? 1 : 0);
this._updateVisibility();
},
_onSourceDestroy: function(source) {
let idx = this._findSource(source);
_detailedChanged: function(source, obj) {
if (obj.detailed == source.policy.detailsInLockScreen)
return;
this._removeItem(this._items[idx]);
this._items.splice(idx, 1);
obj.detailed = source.policy.detailsInLockScreen;
obj.sourceBox.destroy_all_children();
obj.titleLabel = obj.countLabel = null;
this._showSource(source, obj, obj.sourceBox);
},
_onSourceDestroy: function(source, obj) {
this._removeSource(source, obj);
this._updateVisibility();
},
_removeItem: function(obj) {
if (obj.resident) {
obj.item.doneShowingNotificationStack();
this._residentNotificationBox.remove_actor(obj.item.notificationStackWidget);
} else {
obj.sourceBox.destroy();
_removeSource: function(source, obj) {
obj.sourceBox.destroy();
obj.sourceBox = obj.titleLabel = obj.countLabel = null;
if (obj.musicNotification) {
this._musicBin.child = null;
obj.musicNotification = null;
}
obj.item.disconnect(obj.contentUpdatedId);
obj.source.disconnect(obj.sourceDestroyId);
obj.source.disconnect(obj.sourceCountChangedId);
obj.source.disconnect(obj.sourceTitleChangedId);
source.disconnect(obj.sourceDestroyId);
source.disconnect(obj.sourceCountChangedId);
source.disconnect(obj.sourceTitleChangedId);
source.policy.disconnect(obj.policyChangedId);
this._sources.delete(source);
},
});
@ -394,6 +455,7 @@ const ScreenShield = new Lang.Class({
reactive: true,
can_focus: true,
name: 'lockScreenGroup',
visible: false,
});
this._lockScreenGroup.connect('key-release-event',
Lang.bind(this, this._onLockScreenKeyRelease));
@ -464,10 +526,15 @@ const ScreenShield = new Lang.Class({
this._screenSaverDBus = new ShellDBus.ScreenSaverDBus(this);
this._inhibitor = null;
this._loginManager = LoginManager.getLoginManager();
this._loginManager.connect('prepare-for-sleep',
Lang.bind(this, this._prepareForSleep));
this._inhibitSuspend();
this._loginSession = this._loginManager.getCurrentSessionProxy();
this._loginSession.connectSignal('Lock', Lang.bind(this, function() { this.lock(false); }));
this._loginSession.connectSignal('Unlock', Lang.bind(this, function() { this.unlock(); }));
this._loginSession.connectSignal('Unlock', Lang.bind(this, function() { this.deactivate(false); }));
this._settings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
@ -475,17 +542,30 @@ const ScreenShield = new Lang.Class({
this._hasLockScreen = false;
this._isGreeter = false;
this._isActive = false;
this._isLocked = false;
this._inUnlockAnimation = false;
this._activationTime = 0;
this._becameActiveId = 0;
this._lockTimeoutId = 0;
this._lightbox = new Lightbox.Lightbox(Main.uiGroup,
{ inhibitEvents: true,
fadeInTime: STANDARD_FADE_TIME,
fadeFactor: 1 });
this._lightbox.connect('shown', Lang.bind(this, this._onLightboxShown));
this.idleMonitor = new GnomeDesktop.IdleMonitor();
},
_liftShield: function(onPrimary, velocity) {
if (this._isLocked) {
this._ensureUnlockDialog(onPrimary, true /* allowCancel */);
this._hideLockScreen(true /* animate */, velocity);
} else {
this.deactivate(true /* animate */);
}
},
_onLockScreenKeyRelease: function(actor, event) {
let symbol = event.get_key_symbol();
@ -503,8 +583,7 @@ const ScreenShield = new Lang.Class({
if (symbol == Clutter.KEY_Escape ||
symbol == Clutter.KEY_Return ||
symbol == Clutter.KEY_KP_Enter) {
this._ensureUnlockDialog(true, true);
this._hideLockScreen(true, 0);
this._liftShield(false, 0);
return true;
}
@ -526,13 +605,37 @@ const ScreenShield = new Lang.Class({
// 7 standard scrolls to lift up
if (this._lockScreenScrollCounter > 35) {
this._ensureUnlockDialog(false, true);
this._hideLockScreen(true, 0);
this._liftShield(true, 0);
}
return true;
},
_inhibitSuspend: function() {
this._loginManager.inhibit(_("GNOME needs to lock the screen"),
Lang.bind(this, function(inhibitor) {
this._inhibitor = inhibitor;
}));
},
_uninhibitSuspend: function() {
if (this._inhibitor)
this._inhibitor.close(null);
this._inhibitor = null;
},
_prepareForSleep: function(loginManager, aboutToSuspend) {
if (aboutToSuspend) {
if (!this._settings.get_boolean(LOCK_ENABLED_KEY)) {
this._uninhibitSuspend();
return;
}
this.lock(true);
} else {
this._inhibitSuspend();
}
},
_animateArrows: function() {
let arrows = this._arrowContainer.get_children();
let unitaryDelay = ARROW_ANIMATION_TIME / (arrows.length + 1);
@ -558,7 +661,9 @@ const ScreenShield = new Lang.Class({
_onDragBegin: function() {
Tweener.removeTweens(this._lockScreenGroup);
this._lockScreenState = MessageTray.State.HIDING;
this._ensureUnlockDialog(false, false);
if (this._isLocked)
this._ensureUnlockDialog(false, false);
return true;
},
@ -579,8 +684,7 @@ const ScreenShield = new Lang.Class({
if (this._lockScreenGroup.y < -(ARROW_DRAG_THRESHOLD * global.stage.height)) {
// Complete motion automatically
let [velocity, velocityX, velocityY] = this._dragAction.get_velocity(0);
this._hideLockScreen(true, -velocityY);
this._ensureUnlockDialog(false, true);
this._liftShield(true, -velocityY);
} else {
// restore the lock screen to its original place
// try to use the same speed as the normal animation
@ -620,35 +724,81 @@ const ScreenShield = new Lang.Class({
}
if (!this._isModal) {
Main.pushModal(this.actor, { keybindingMode: Main.KeybindingMode.LOCK_SCREEN });
Main.pushModal(this.actor, { keybindingMode: Shell.KeyBindingMode.LOCK_SCREEN });
this._isModal = true;
}
if (this._lightbox.actor.visible ||
this._isActive) {
// We're either shown and active, or in the process of
// showing.
// The latter is a very unlikely condition (it requires
// idle-delay < 20), but in any case we have nothing
// to do at this point: either isActive is true, or
// it will soon be.
// isActive can also be true if the lightbox is hidden,
// in case the shield is down and the user hasn't unlocked yet
return;
}
if (!this._isActive) {
this._lightbox.show();
this._lightbox.show();
if (this._activationTime == 0)
this._activationTime = GLib.get_monotonic_time();
if (this._activationTime == 0)
this._activationTime = GLib.get_monotonic_time();
this._becameActiveId = this.idleMonitor.connect('became-active', Lang.bind(this, function() {
this.idleMonitor.disconnect(this._becameActiveId);
if (this._becameActiveId == 0)
this._becameActiveId = this.idleMonitor.connect('became-active',
Lang.bind(this, this._onUserBecameActive));
let lightboxWasShown = this._lightbox.shown;
this._lightbox.hide();
let shouldLock = this._settings.get_boolean(LOCK_ENABLED_KEY) && !this._isLocked;
let shouldLock = lightboxWasShown && this._settings.get_boolean(LOCK_ENABLED_KEY);
if (shouldLock || this._isLocked) {
this.lock(false);
} else if (this._isActive) {
this.unlock();
}
}));
this._isActive = true;
this.emit('lock-status-changed');
if (shouldLock) {
let lockTimeout = Math.max(STANDARD_FADE_TIME, this._settings.get_uint(LOCK_DELAY_KEY));
this._lockTimeoutId = Mainloop.timeout_add(lockTimeout * 1000,
Lang.bind(this, function() {
this._lockTimeoutId = 0;
this.lock(true);
return false;
}));
}
},
_onUserBecameActive: function() {
// This function gets called here when the user becomes active
// after gnome-session changed the status to IDLE
// There are four possibilities here:
// - we're called when already locked; isActive and isLocked are true,
// we just go back to the lock screen curtain
// - we're called before the lightbox is fully shown; at this point
// isActive is false, so we just hide the ligthbox, reset the activationTime
// and go back to the unlocked desktop
// - we're called after showing the lightbox, but before the lock
// delay; this is mostly like the case above, but isActive is true now
// so we need to notify gnome-settings-daemon to go back to the normal
// policies for blanking
// (they're handled by the same code, and we emit one extra ActiveChanged
// signal in the case above)
// - we're called after showing the lightbox and after lock-delay; the
// session is effectivelly locked now, it's time to build and show
// the lock screen
this.idleMonitor.disconnect(this._becameActiveId);
this._becameActiveId = 0;
let lightboxWasShown = this._lightbox.shown;
this._lightbox.hide();
// Shortcircuit in case the mouse was moved before the fade completed
if (!lightboxWasShown) {
this.deactivate(false);
return;
}
},
_onLightboxShown: function() {
this.activate(false);
},
showDialog: function() {
// Ensure that the stage window is mapped, before taking a grab
// otherwise X errors out
@ -663,6 +813,7 @@ const ScreenShield = new Lang.Class({
this.actor.show();
this._isGreeter = Main.sessionMode.isGreeter;
this._isLocked = true;
this._ensureUnlockDialog(true, true);
this._hideLockScreen(false, 0);
},
@ -683,6 +834,9 @@ const ScreenShield = new Lang.Class({
},
_hideLockScreen: function(animate, velocity) {
if (this._lockScreenState == MessageTray.State.HIDDEN)
return;
this._lockScreenState = MessageTray.State.HIDING;
if (animate) {
@ -724,7 +878,7 @@ const ScreenShield = new Lang.Class({
let constructor = Main.sessionMode.unlockDialog;
if (!constructor) {
// This session mode has no locking capabilities
this.unlock();
this.deactivate(true);
return;
}
@ -734,8 +888,10 @@ const ScreenShield = new Lang.Class({
let time = global.get_current_time();
this._dialog.connect('loaded', Lang.bind(this, function() {
if (!this._dialog.open(time, onPrimary)) {
// This is kind of an impossible error: we're already modal
// by the time we reach this...
log('Could not open login dialog: failed to acquire grab');
this.unlock();
this.deactivate(true);
}
}));
@ -751,10 +907,17 @@ const ScreenShield = new Lang.Class({
},
_onUnlockSucceded: function() {
this._tweenUnlocked();
this.deactivate(true);
},
_resetLockScreen: function(animateLockScreen, animateLockDialog) {
// Don't reset the lock screen unless it is completely hidden
// This prevents the shield going down if the lock-delay timeout
// fires while the user is dragging (which has the potential
// to confuse our state)
if (this._lockScreenState != MessageTray.State.HIDDEN)
return;
this._ensureLockScreen();
this._lockDialogGroup.scale_x = 1;
this._lockDialogGroup.scale_y = 1;
@ -821,6 +984,14 @@ const ScreenShield = new Lang.Class({
this._lockScreenGroup.fixed_position_set = false;
this._lockScreenScrollCounter = 0;
let prevIsActive = this._isActive;
this._isActive = true;
if (prevIsActive != this._isActive)
this.emit('active-changed');
this._uninhibitSuspend();
this.emit('lock-screen-shown');
},
@ -842,12 +1013,10 @@ const ScreenShield = new Lang.Class({
this._lockScreenContents.add_actor(this._lockScreenContentsBox);
if (this._settings.get_boolean('show-notifications')) {
this._notificationsBox = new NotificationsBox();
this._lockScreenContentsBox.add(this._notificationsBox.actor, { x_fill: true,
y_fill: true,
expand: true });
}
this._notificationsBox = new NotificationsBox();
this._lockScreenContentsBox.add(this._notificationsBox.actor, { x_fill: true,
y_fill: true,
expand: true });
this._hasLockScreen = true;
},
@ -872,6 +1041,10 @@ const ScreenShield = new Lang.Class({
},
get locked() {
return this._isLocked;
},
get active() {
return this._isActive;
},
@ -879,27 +1052,20 @@ const ScreenShield = new Lang.Class({
return this._activationTime;
},
_tweenUnlocked: function() {
this._inUnlockAnimation = true;
this.unlock();
deactivate: function(animate) {
this._hideLockScreen(animate, 0);
Tweener.addTween(this._lockDialogGroup, {
scale_x: 0,
scale_y: 0,
time: Overview.ANIMATION_TIME,
time: animate ? Overview.ANIMATION_TIME : 0,
transition: 'easeOutQuad',
onComplete: function() {
if (this._dialog) {
this._dialog.destroy();
this._dialog = null;
}
this.actor.hide();
this._inUnlockAnimation = false;
},
onComplete: Lang.bind(this, this._completeDeactivate),
onCompleteScope: this
});
},
unlock: function() {
_completeDeactivate: function() {
if (this._hasLockScreen)
this._clearLockScreen();
@ -915,26 +1081,31 @@ const ScreenShield = new Lang.Class({
this._isModal = false;
}
if (!this._inUnlockAnimation)
this.actor.hide();
this.actor.hide();
if (Main.sessionMode.currentMode == 'lock-screen')
Main.sessionMode.popMode('lock-screen');
if (Main.sessionMode.currentMode == 'unlock-dialog')
Main.sessionMode.popMode('unlock-dialog');
if (this._becameActiveId != 0) {
this.idleMonitor.disconnect(this._becameActiveId);
this._becameActiveId = 0;
}
if (this._lockTimeoutId != 0) {
Mainloop.source_remove(this._lockTimeoutId);
this._lockTimeoutId = 0;
}
this._activationTime = 0;
this._isActive = false;
this._isLocked = false;
this.emit('lock-status-changed');
this.emit('active-changed');
this.emit('locked-changed');
},
lock: function(animate) {
if (!this._isModal) {
Main.pushModal(this.actor, { keybindingMode: Main.KeybindingMode.LOCK_SCREEN });
this._isModal = true;
}
activate: function(animate) {
if (this._activationTime == 0)
this._activationTime = GLib.get_monotonic_time();
@ -949,9 +1120,28 @@ const ScreenShield = new Lang.Class({
this._resetLockScreen(animate, animate);
this._isActive = true;
// We used to set isActive and emit active-changed here,
// but now we do that from lockScreenShown, which means
// there is a 0.3 seconds window during which the lock
// screen is effectively visible and the screen is locked, but
// the DBus interface reports the screensaver is off.
// This is because when we emit ActiveChanged(true),
// gnome-settings-daemon blanks the screen, and we don't want
// blank during the animation.
// This is not a problem for the idle fade case, because we
// activate without animation in that case.
},
lock: function(animate) {
if (!this._isModal) {
Main.pushModal(this.actor, { keybindingMode: Shell.KeyBindingMode.LOCK_SCREEN });
this._isModal = true;
}
this._isLocked = true;
this.emit('lock-status-changed');
this.activate(animate);
this.emit('locked-changed');
},
});
Signals.addSignalMethods(ScreenShield.prototype);
@ -985,13 +1175,13 @@ const ScreenShieldFallback = new Lang.Class({
else
this._locked = false;
this.emit('lock-status-changed', this._locked);
this.emit('active-changed', this._locked);
},
_onSignal: function(proxy, senderName, signalName, params) {
if (signalName == 'ActiveChanged') {
[this._locked] = params.deep_unpack();
this.emit('lock-status-changed', this._locked);
this.emit('active-changed', this._locked);
}
},

272
js/ui/screenshot.js Normal file
View File

@ -0,0 +1,272 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gdk = imports.gi.Gdk;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const ScreenshotIface = <interface name="org.gnome.Shell.Screenshot">
<method name="ScreenshotArea">
<arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="y"/>
<arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<method name="ScreenshotWindow">
<arg type="b" direction="in" name="include_frame"/>
<arg type="b" direction="in" name="include_cursor"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<method name="Screenshot">
<arg type="b" direction="in" name="include_cursor"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<method name="SelectArea">
<arg type="i" direction="out" name="x"/>
<arg type="i" direction="out" name="y"/>
<arg type="i" direction="out" name="width"/>
<arg type="i" direction="out" name="height"/>
</method>
<method name="FlashArea">
<arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="y"/>
<arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/>
</method>
</interface>;
const ScreenshotService = new Lang.Class({
Name: 'ScreenshotService',
_init: function() {
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenshotIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/Screenshot');
Gio.DBus.session.own_name('org.gnome.Shell.Screenshot', Gio.BusNameOwnerFlags.REPLACE, null, null);
},
_onScreenshotComplete: function(obj, result, area, filenameUsed, flash, invocation) {
if (flash && result) {
let flashspot = new Flashspot(area);
flashspot.fire();
}
let retval = GLib.Variant.new('(bs)', [result, filenameUsed]);
invocation.return_value(retval);
},
ScreenshotAreaAsync : function (params, invocation) {
let [x, y, width, height, flash, filename, callback] = params;
let screenshot = new Shell.Screenshot();
screenshot.screenshot_area (x, y, width, height, filename,
Lang.bind(this, this._onScreenshotComplete,
flash, invocation));
},
ScreenshotWindowAsync : function (params, invocation) {
let [include_frame, include_cursor, flash, filename] = params;
let screenshot = new Shell.Screenshot();
screenshot.screenshot_window (include_frame, include_cursor, filename,
Lang.bind(this, this._onScreenshotComplete,
flash, invocation));
},
ScreenshotAsync : function (params, invocation) {
let [include_cursor, flash, filename] = params;
let screenshot = new Shell.Screenshot();
screenshot.screenshot(include_cursor, filename,
Lang.bind(this, this._onScreenshotComplete,
flash, invocation));
},
SelectAreaAsync: function (params, invocation) {
let selectArea = new SelectArea();
selectArea.show();
selectArea.connect('finished', Lang.bind(this,
function(selectArea, areaRectangle) {
if (areaRectangle) {
let retval = GLib.Variant.new('(iiii)',
[areaRectangle.x, areaRectangle.y,
areaRectangle.width, areaRectangle.height]);
invocation.return_value(retval);
} else {
invocation.return_error_literal(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
"Operation was cancelled");
}
}));
},
FlashArea: function(x, y, width, height) {
let flashspot = new Flashspot({ x : x, y : y, width: width, height: height});
flashspot.fire();
}
});
const SelectArea = new Lang.Class({
Name: 'SelectArea',
_init: function() {
this._startX = -1;
this._startY = -1;
this._lastX = 0;
this._lastY = 0;
this._initRubberbandColors();
this._group = new St.Widget({ visible: false,
reactive: true,
x: 0,
y: 0 });
Main.uiGroup.add_actor(this._group);
this._group.connect('button-press-event',
Lang.bind(this, this._onButtonPress));
this._group.connect('button-release-event',
Lang.bind(this, this._onButtonRelease));
this._group.connect('key-press-event',
Lang.bind(this, this._onKeyPress));
this._group.connect('motion-event',
Lang.bind(this, this._onMotionEvent));
let constraint = new Clutter.BindConstraint({ source: global.stage,
coordinate: Clutter.BindCoordinate.ALL });
this._group.add_constraint(constraint);
this._rubberband = new Clutter.Rectangle({ color: this._background,
has_border: true,
border_width: 1,
border_color: this._border });
this._group.add_actor(this._rubberband);
},
show: function() {
if (!Main.pushModal(this._group) || this._group.visible)
return;
global.set_cursor(Shell.Cursor.CROSSHAIR);
this._group.visible = true;
},
_initRubberbandColors: function() {
function colorFromRGBA(rgba) {
return new Clutter.Color({ red: rgba.red * 255,
green: rgba.green * 255,
blue: rgba.blue * 255,
alpha: rgba.alpha * 255 });
}
let path = new Gtk.WidgetPath();
path.append_type(Gtk.IconView);
let context = new Gtk.StyleContext();
context.set_path(path);
context.add_class('rubberband');
this._background = colorFromRGBA(context.get_background_color(Gtk.StateFlags.NORMAL));
this._border = colorFromRGBA(context.get_border_color(Gtk.StateFlags.NORMAL));
},
_getGeometry: function() {
return { x: Math.min(this._startX, this._lastX),
y: Math.min(this._startY, this._lastY),
width: Math.abs(this._startX - this._lastX),
height: Math.abs(this._startY - this._lastY) };
},
_onKeyPress: function(actor, event) {
if (event.get_key_symbol() == Clutter.Escape)
this._destroy(null, false);
return;
},
_onMotionEvent: function(actor, event) {
if (this._startX == -1 || this._startY == -1)
return false;
[this._lastX, this._lastY] = event.get_coords();
let geometry = this._getGeometry();
this._rubberband.set_position(geometry.x, geometry.y);
this._rubberband.set_size(geometry.width, geometry.height);
return false;
},
_onButtonPress: function(actor, event) {
[this._startX, this._startY] = event.get_coords();
this._rubberband.set_position(this._startX, this._startY);
return false;
},
_onButtonRelease: function(actor, event) {
this._destroy(this._getGeometry(), true);
return false;
},
_destroy: function(geometry, fade) {
Tweener.addTween(this._group,
{ opacity: 0,
time: fade ? 0.2 : 0,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
Main.popModal(this._group);
this._group.destroy();
global.unset_cursor();
this.emit('finished', geometry);
})
});
}
});
Signals.addSignalMethods(SelectArea.prototype);
const FLASHSPOT_ANIMATION_OUT_TIME = 0.5; // seconds
const Flashspot = new Lang.Class({
Name: 'Flashspot',
Extends: Lightbox.Lightbox,
_init: function(area) {
this.parent(Main.uiGroup, { inhibitEvents: true,
width: area.width,
height: area.height });
this.actor.style_class = 'flashspot';
this.actor.set_position(area.x, area.y);
},
fire: function() {
this.actor.show();
this.actor.opacity = 255;
Tweener.addTween(this.actor,
{ opacity: 0,
time: FLASHSPOT_ANIMATION_OUT_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this.destroy();
})
});
}
});

View File

@ -17,6 +17,26 @@ const Search = imports.ui.search;
const MAX_LIST_SEARCH_RESULTS_ROWS = 3;
const MAX_GRID_SEARCH_RESULTS_ROWS = 1;
const MaxWidthBin = new Lang.Class({
Name: 'MaxWidthBin',
Extends: St.Bin,
vfunc_allocate: function(box, flags) {
let themeNode = this.get_theme_node();
let maxWidth = themeNode.get_max_width();
let availWidth = box.x2 - box.x1;
let adjustedBox = box;
if (availWidth > maxWidth) {
let excessWidth = availWidth - maxWidth;
adjustedBox.x1 += Math.floor(excessWidth / 2);
adjustedBox.x2 -= Math.floor(excessWidth / 2);
}
this.parent(adjustedBox, flags);
}
});
const SearchResult = new Lang.Class({
Name: 'SearchResult',
@ -178,7 +198,7 @@ const ListSearchResults = new Lang.Class({
this._content = new St.BoxLayout({ style_class: 'list-search-results',
vertical: true });
this.actor.add_actor(this._content);
this.actor.add(this._content, { expand: true });
this._notDisplayedResult = [];
this._terms = [];
@ -301,12 +321,19 @@ const SearchResults = new Lang.Class({
this._content = new St.BoxLayout({ name: 'searchResultsContent',
vertical: true });
this._contentBin = new MaxWidthBin({ name: 'searchResultsBin',
x_fill: true,
y_fill: true,
child: this._content });
let scrollChild = new St.BoxLayout();
scrollChild.add(this._contentBin, { expand: true });
this._scrollView = new St.ScrollView({ x_fill: true,
y_fill: false,
style_class: 'vfade' });
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
this._scrollView.add_actor(this._content);
this._scrollView.add_actor(scrollChild);
let action = new Clutter.PanAction({ interpolate: true });
action.connect('pan', Lang.bind(this, this._onPan));
this._scrollView.add_action(action);

View File

@ -30,5 +30,6 @@ const HorizontalSeparator = new Lang.Class({
cr.setSource(pattern);
cr.rectangle(margin, gradientOffset, gradientWidth, gradientHeight);
cr.fill();
cr.$dispose();
}
});

View File

@ -15,6 +15,7 @@ const DEFAULT_MODE = 'restrictive';
const _modes = {
'restrictive': {
parentMode: null,
stylesheetName: 'gnome-shell.css',
hasOverview: false,
showCalendarEvents: false,
allowSettings: false,

View File

@ -9,8 +9,8 @@ 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;
const Screenshot = imports.ui.screenshot;
const GnomeShellIface = <interface name="org.gnome.Shell">
<method name="Eval">
@ -18,34 +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="ScreenshotArea">
<arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="y"/>
<arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
</method>
<method name="ScreenshotWindow">
<arg type="b" direction="in" name="include_frame"/>
<arg type="b" direction="in" name="include_cursor"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
</method>
<method name="Screenshot">
<arg type="b" direction="in" name="include_cursor"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
</method>
<method name="FlashArea">
<arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="y"/>
<arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/>
</method>
<property name="Mode" type="s" access="read" />
<property name="OverviewActive" type="b" access="readwrite" />
<property name="ShellVersion" type="s" access="read" />
@ -76,6 +48,7 @@ const GnomeShell = new Lang.Class({
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
this._extensionsSerivce = new GnomeShellExtensions();
this._screenshotService = new Screenshot.ScreenshotService();
},
/**
@ -111,82 +84,6 @@ const GnomeShell = new Lang.Class({
return [success, returnValue];
},
_onScreenshotComplete: function(obj, result, area, flash, invocation) {
if (flash && result) {
let flashspot = new Flashspot.Flashspot(area);
flashspot.fire();
}
let retval = GLib.Variant.new('(b)', [result]);
invocation.return_value(retval);
},
/**
* ScreenshotArea:
* @x: The X coordinate of the area
* @y: The Y coordinate of the area
* @width: The width of the area
* @height: The height of the area
* @flash: Whether to flash the area or not
* @filename: The filename for the screenshot
*
* Takes a screenshot of the passed in area and saves it
* in @filename as png image, it returns a boolean
* indicating whether the operation was successful or not.
*
*/
ScreenshotAreaAsync : function (params, invocation) {
let [x, y, width, height, flash, filename, callback] = params;
let screenshot = new Shell.Screenshot();
screenshot.screenshot_area (x, y, width, height, filename,
Lang.bind(this, this._onScreenshotComplete,
flash, invocation));
},
/**
* ScreenshotWindow:
* @include_frame: Whether to include the frame or not
* @include_cursor: Whether to include the cursor image or not
* @flash: Whether to flash the window area or not
* @filename: The filename for the screenshot
*
* Takes a screenshot of the focused window (optionally omitting the frame)
* and saves it in @filename as png image, it returns a boolean
* indicating whether the operation was successful or not.
*
*/
ScreenshotWindowAsync : function (params, invocation) {
let [include_frame, include_cursor, flash, filename] = params;
let screenshot = new Shell.Screenshot();
screenshot.screenshot_window (include_frame, include_cursor, filename,
Lang.bind(this, this._onScreenshotComplete,
flash, invocation));
},
/**
* Screenshot:
* @filename: The filename for the screenshot
* @include_cursor: Whether to include the cursor image or not
* @flash: Whether to flash the screen or not
*
* Takes a screenshot of the whole screen and saves it
* in @filename as png image, it returns a boolean
* indicating whether the operation was successful or not.
*
*/
ScreenshotAsync : function (params, invocation) {
let [include_cursor, flash, filename] = params;
let screenshot = new Shell.Screenshot();
screenshot.screenshot(include_cursor, filename,
Lang.bind(this, this._onScreenshotComplete,
flash, invocation));
},
FlashArea: function(x, y, width, height) {
let flashspot = new Flashspot.Flashspot({ x : x, y : y, width: width, height: height});
flashspot.fire();
},
Mode: global.session_mode,
get OverviewActive() {
@ -350,8 +247,8 @@ const ScreenSaverDBus = new Lang.Class({
this.parent();
this._screenShield = screenShield;
screenShield.connect('lock-status-changed', Lang.bind(this, function(shield) {
this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [shield.locked]));
screenShield.connect('active-changed', Lang.bind(this, function(shield) {
this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [shield.active]));
}));
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenSaverIface, this);
@ -372,13 +269,13 @@ const ScreenSaverDBus = new Lang.Class({
SetActive: function(active) {
if (active)
this._screenShield.lock(true);
this._screenShield.activate(true);
else
this._screenShield.unlock();
this._screenShield.unlock(false);
},
GetActive: function() {
return this._screenShield.locked;
return this._screenShield.active;
},
GetActiveTime: function() {

View File

@ -9,6 +9,7 @@ const St = imports.gi.St;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const NotificationDaemon = imports.ui.notificationDaemon;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
@ -56,7 +57,7 @@ const Indicator = new Lang.Class({
this._fullMenuItems = [new PopupMenu.PopupSeparatorMenuItem(),
new PopupMenu.PopupMenuItem(_("Send Files to Device...")),
new PopupMenu.PopupMenuItem(_("Set up a New Device...")),
new PopupMenu.PopupMenuItem(_("Set Up a New Device...")),
new PopupMenu.PopupSeparatorMenuItem()];
this._hasDevices = false;
@ -80,7 +81,7 @@ const Indicator = new Lang.Class({
this._applet.connect('notify::show-full-menu', Lang.bind(this, this._updateFullMenu));
this._updateFullMenu();
this.menu.addSettingsAction(_("Bluetooth Settings"), 'bluetooth-properties.desktop');
this.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop');
this._applet.connect('pincode-request', Lang.bind(this, this._pinRequest));
this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest));
@ -286,6 +287,7 @@ const Indicator = new Lang.Class({
_ensureSource: function() {
if (!this._source) {
this._source = new MessageTray.Source(_("Bluetooth"), 'bluetooth-active');
this._source.policy = new NotificationDaemon.NotificationApplicationPolicy('gnome-bluetooth-panel');
Main.messageTray.add(this._source);
}
},
@ -446,10 +448,5 @@ const PinNotification = new Lang.Class({
return this._entry.clutter_text.text.length == 6;
else
return true;
},
grabFocus: function(lockTray) {
this.parent(lockTray);
global.stage.set_key_focus(this._entry);
}
});

View File

@ -1,9 +1,11 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const GnomeDesktop = imports.gi.GnomeDesktop;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
@ -21,6 +23,7 @@ try {
const Main = imports.ui.main;
const PopupMenu = imports.ui.popupMenu;
const PanelMenu = imports.ui.panelMenu;
const SwitcherPopup = imports.ui.switcherPopup;
const Util = imports.misc.util;
const DESKTOP_INPUT_SOURCES_SCHEMA = 'org.gnome.desktop.input-sources';
@ -47,6 +50,7 @@ const IBusManager = new Lang.Class({
this._engines = {};
this._ready = false;
this._registerPropertiesId = 0;
this._currentEngineName = null;
this._nameWatcherId = Gio.DBus.session.watch_name(IBus.SERVICE_IBUS,
Gio.BusNameWatcherFlags.NONE,
@ -66,6 +70,10 @@ const IBusManager = new Lang.Class({
this._engines = {};
this._ready = false;
this._registerPropertiesId = 0;
this._currentEngineName = null;
if (this._readyCallback)
this._readyCallback(false);
},
_onNameAppeared: function() {
@ -89,12 +97,10 @@ const IBusManager = new Lang.Class({
let name = enginesList[i].get_name();
this._engines[name] = enginesList[i];
}
this._updateReadiness();
} else {
this._clear();
return;
}
this._updateReadiness();
},
_initPanelService: function(ibus, result) {
@ -105,27 +111,36 @@ const IBusManager = new Lang.Class({
this._candidatePopup.setPanelService(this._panelService);
// Need to set this to get 'global-engine-changed' emitions
this._ibus.set_watch_ibus_signal(true);
this._ibus.connect('global-engine-changed', Lang.bind(this, this._resetProperties));
this._ibus.connect('global-engine-changed', Lang.bind(this, this._engineChanged));
this._panelService.connect('update-property', Lang.bind(this, this._updateProperty));
this._resetProperties();
// If an engine is already active we need to get its properties
this._ibus.get_global_engine_async(-1, null, Lang.bind(this, function(i, result) {
let engine;
try {
engine = this._ibus.get_global_engine_async_finish(result);
if (!engine)
return;
} catch(e) {
return;
}
this._engineChanged(this._ibus, engine.get_name());
}));
this._updateReadiness();
} 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();
if (this._readyCallback)
this._readyCallback(this._ready);
},
_resetProperties: function() {
this.emit('properties-registered', null);
_engineChanged: function(bus, engineName) {
this._currentEngineName = engineName;
if (this._registerPropertiesId != 0)
return;
@ -138,25 +153,18 @@ const IBusManager = new Lang.Class({
this._panelService.disconnect(this._registerPropertiesId);
this._registerPropertiesId = 0;
this.emit('properties-registered', props);
this.emit('properties-registered', this._currentEngineName, props);
}));
},
_updateProperty: function(panel, prop) {
this.emit('property-updated', prop);
this.emit('property-updated', this._currentEngineName, prop);
},
activateProperty: function(key, state) {
this._panelService.property_activate(key, state);
},
hasProperties: function(id) {
if (id == 'anthy')
return true;
return false;
},
getEngineDesc: function(id) {
if (!IBus || !this._ready)
return null;
@ -181,16 +189,127 @@ const LayoutMenuItem = new Lang.Class({
}
});
const InputSource = new Lang.Class({
Name: 'InputSource',
_init: function(type, id, displayName, shortName, index) {
this.type = type;
this.id = id;
this.displayName = displayName;
this._shortName = shortName;
this.index = index;
this._menuItem = new LayoutMenuItem(this.displayName, this._shortName);
this._menuItem.connect('activate', Lang.bind(this, this.activate));
this._indicatorLabel = new St.Label({ text: this._shortName });
this.properties = null;
},
destroy: function() {
this._menuItem.destroy();
this._indicatorLabel.destroy();
},
get shortName() {
return this._shortName;
},
set shortName(v) {
this._shortName = v;
this._menuItem.indicator.set_text(v);
this._indicatorLabel.set_text(v);
},
get menuItem() {
return this._menuItem;
},
get indicatorLabel() {
return this._indicatorLabel;
},
activate: function() {
this.emit('activate');
},
});
Signals.addSignalMethods(InputSource.prototype);
const InputSourcePopup = new Lang.Class({
Name: 'InputSourcePopup',
Extends: SwitcherPopup.SwitcherPopup,
_init: function(items, action, actionBackward) {
this.parent(items);
this._action = action;
this._actionBackward = actionBackward;
},
_createSwitcher: function() {
this._switcherList = new InputSourceSwitcher(this._items);
return true;
},
_initialSelection: function(backward, binding) {
if (binding == 'switch-input-source') {
if (backward)
this._selectedIndex = this._items.length - 1;
} else if (binding == 'switch-input-source-backward') {
if (!backward)
this._selectedIndex = this._items.length - 1;
}
this._select(this._selectedIndex);
},
_keyPressHandler: function(keysym, backwards, action) {
if (action == this._action)
this._select(backwards ? this._previous() : this._next());
else if (action == this._actionBackward)
this._select(backwards ? this._next() : this._previous());
else if (keysym == Clutter.Left)
this._select(this._previous());
else if (keysym == Clutter.Right)
this._select(this._next());
},
_finish : function() {
this.parent();
this._items[this._selectedIndex].activate();
},
});
const InputSourceSwitcher = new Lang.Class({
Name: 'InputSourceSwitcher',
Extends: SwitcherPopup.SwitcherList,
_init: function(items) {
this.parent(true);
for (let i = 0; i < items.length; i++)
this._addIcon(items[i]);
},
_addIcon: function(item) {
let box = new St.BoxLayout({ vertical: true });
let bin = new St.Bin({ style_class: 'input-source-switcher-symbol' });
let symbol = new St.Label({ text: item.shortName });
bin.set_child(symbol);
box.add(bin, { x_fill: false, y_fill: false } );
let text = new St.Label({ text: item.displayName });
box.add(text, { x_fill: false });
this.addItem(box, text);
}
});
const InputSourceIndicator = new Lang.Class({
Name: 'InputSourceIndicator',
Extends: PanelMenu.Button,
_propertiesWhitelist: [
'InputMode',
'TypingMode',
'DictMode'
],
_init: function() {
this.parent(0.0, _("Keyboard"));
@ -201,14 +320,36 @@ const InputSourceIndicator = new Lang.Class({
this.actor.add_actor(this._container);
this.actor.add_style_class_name('panel-status-button');
this._labelActors = {};
this._layoutItems = {};
// All valid input sources currently in the gsettings
// KEY_INPUT_SOURCES list indexed by their index there
this._inputSources = {};
// All valid input sources currently in the gsettings
// KEY_INPUT_SOURCES list of type INPUT_SOURCE_TYPE_IBUS
// indexed by the IBus ID
this._ibusSources = {};
this._currentSource = null;
// All valid input sources currently in the gsettings
// KEY_INPUT_SOURCES list ordered by most recently used
this._mruSources = [];
this._keybindingAction =
Main.wm.addKeybinding('switch-input-source',
new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }),
Meta.KeyBindingFlags.REVERSES,
Shell.KeyBindingMode.ALL,
Lang.bind(this, this._switchInputSource));
this._keybindingActionBackward =
Main.wm.addKeybinding('switch-input-source-backward',
new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }),
Meta.KeyBindingFlags.REVERSES |
Meta.KeyBindingFlags.REVERSED,
Shell.KeyBindingMode.ALL,
Lang.bind(this, this._switchInputSource));
this._settings = new Gio.Settings({ schema: DESKTOP_INPUT_SOURCES_SCHEMA });
this._settings.connect('changed::' + KEY_CURRENT_INPUT_SOURCE, Lang.bind(this, this._currentInputSourceChanged));
this._settings.connect('changed::' + KEY_INPUT_SOURCES, Lang.bind(this, this._inputSourcesChanged));
this._currentSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
this._xkbInfo = new GnomeDesktop.XkbInfo();
this._propSeparator = new PopupMenu.PopupSeparatorMenuItem();
@ -217,9 +358,8 @@ const InputSourceIndicator = new Lang.Class({
this.menu.addMenuItem(this._propSection);
this._propSection.actor.hide();
this._properties = null;
this._ibusManager = new IBusManager(Lang.bind(this, this._inputSourcesChanged));
this._ibusReady = false;
this._ibusManager = new IBusManager(Lang.bind(this, this._ibusReadyCallback));
this._ibusManager.connect('properties-registered', Lang.bind(this, this._ibusPropertiesRegistered));
this._ibusManager.connect('property-updated', Lang.bind(this, this._ibusPropertyUpdated));
this._inputSourcesChanged();
@ -230,7 +370,14 @@ const InputSourceIndicator = new Lang.Class({
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._sessionUpdated();
this.menu.addSettingsAction(_("Region and Language Settings"), 'gnome-region-panel.desktop');
this.menu.addSettingsAction(_("Region & Language Settings"), 'gnome-region-panel.desktop');
this._sourcesPerWindow = false;
this._focusWindowNotifyId = 0;
this._overviewShowingId = 0;
this._overviewHiddenId = 0;
this._settings.connect('changed::per-window', Lang.bind(this, this._sourcesPerWindowChanged));
this._sourcesPerWindowChanged();
},
_sessionUpdated: function() {
@ -241,18 +388,29 @@ const InputSourceIndicator = new Lang.Class({
this._showLayoutItem.actor.visible = Main.sessionMode.allowSettings;
},
_ibusReadyCallback: function(ready) {
if (this._ibusReady == ready)
return;
this._ibusReady = ready;
this._mruSources = [];
this._inputSourcesChanged();
},
_switchInputSource: function(display, screen, window, binding) {
let popup = new InputSourcePopup(this._mruSources, this._keybindingAction, this._keybindingActionBackward);
let modifiers = binding.get_modifiers();
let backwards = modifiers & Meta.VirtualModifier.SHIFT_MASK;
if (!popup.show(backwards, binding.get_name(), binding.get_mask()))
popup.destroy();
},
_currentInputSourceChanged: function() {
let nVisibleSources = Object.keys(this._layoutItems).length;
let newCurrentSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
let newLayoutItem = this._layoutItems[newCurrentSourceIndex];
let hasProperties;
let nVisibleSources = Object.keys(this._inputSources).length;
let newSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
let newSource = this._inputSources[newSourceIndex];
if (newLayoutItem)
hasProperties = this._ibusManager.hasProperties(newLayoutItem.ibusEngineId);
else
hasProperties = false;
if (!newLayoutItem || (nVisibleSources < 2 && !hasProperties)) {
if (!newSource || (nVisibleSources < 2 && !newSource.properties)) {
// This source index might be invalid if we weren't able
// to build a menu item for it, so we hide ourselves since
// we can't fix it here. *shrug*
@ -266,106 +424,125 @@ const InputSourceIndicator = new Lang.Class({
this.actor.show();
if (this._layoutItems[this._currentSourceIndex]) {
this._layoutItems[this._currentSourceIndex].setShowDot(false);
this._container.set_skip_paint(this._labelActors[this._currentSourceIndex], true);
let oldSource;
[oldSource, this._currentSource] = [this._currentSource, newSource];
if (oldSource) {
oldSource.menuItem.setShowDot(false);
this._container.set_skip_paint(oldSource.indicatorLabel, true);
}
newLayoutItem.setShowDot(true);
newSource.menuItem.setShowDot(true);
this._container.set_skip_paint(newSource.indicatorLabel, false);
let newLabelActor = this._labelActors[newCurrentSourceIndex];
this._container.set_skip_paint(newLabelActor, false);
this._buildPropSection(newSource.properties);
if (hasProperties)
newLabelActor.set_text(newLayoutItem.indicator.get_text());
for (let i = 1; i < this._mruSources.length; ++i)
if (this._mruSources[i] == newSource) {
let currentSource = this._mruSources.splice(i, 1);
this._mruSources = currentSource.concat(this._mruSources);
break;
}
this._currentSourceIndex = newCurrentSourceIndex;
this._changePerWindowSource();
},
_inputSourcesChanged: function() {
let sources = this._settings.get_value(KEY_INPUT_SOURCES);
let nSources = sources.n_children();
for (let i in this._layoutItems)
this._layoutItems[i].destroy();
for (let i in this._inputSources)
this._inputSources[i].destroy();
for (let i in this._labelActors)
this._labelActors[i].destroy();
this._inputSources = {};
this._ibusSources = {};
this._layoutItems = {};
this._labelActors = {};
let infos = [];
let infosByShortName = {};
let inputSourcesByShortName = {};
for (let i = 0; i < nSources; i++) {
let info = { exists: false };
let displayName;
let shortName;
let [type, id] = sources.get_child_value(i).deep_unpack();
let exists = false;
if (type == INPUT_SOURCE_TYPE_XKB) {
[info.exists, info.displayName, info.shortName, , ] =
[exists, displayName, shortName, , ] =
this._xkbInfo.get_layout_info(id);
} else if (type == INPUT_SOURCE_TYPE_IBUS) {
let engineDesc = this._ibusManager.getEngineDesc(id);
if (engineDesc) {
let language = IBus.get_language_name(engineDesc.get_language());
info.exists = true;
info.displayName = language + ' (' + engineDesc.get_longname() + ')';
info.shortName = this._makeEngineShortName(engineDesc);
info.ibusEngineId = id;
exists = true;
displayName = language + ' (' + engineDesc.get_longname() + ')';
shortName = this._makeEngineShortName(engineDesc);
}
}
if (!info.exists)
if (!exists)
continue;
info.sourceIndex = i;
let is = new InputSource(type, id, displayName, shortName, i);
if (!(info.shortName in infosByShortName))
infosByShortName[info.shortName] = [];
infosByShortName[info.shortName].push(info);
infos.push(info);
}
for (let i = 0; i < infos.length; i++) {
let info = infos[i];
if (infosByShortName[info.shortName].length > 1) {
let sub = infosByShortName[info.shortName].indexOf(info) + 1;
info.shortName += String.fromCharCode(0x2080 + sub);
}
let item = new LayoutMenuItem(info.displayName, info.shortName);
item.ibusEngineId = info.ibusEngineId;
this._layoutItems[info.sourceIndex] = item;
this.menu.addMenuItem(item, i);
item.connect('activate', Lang.bind(this, function() {
is.connect('activate', Lang.bind(this, function() {
if (this._currentSource.index == is.index)
return;
this._settings.set_value(KEY_CURRENT_INPUT_SOURCE,
GLib.Variant.new_uint32(info.sourceIndex));
GLib.Variant.new_uint32(is.index));
}));
let shortLabel = new St.Label({ text: info.shortName });
this._labelActors[info.sourceIndex] = shortLabel;
this._container.add_actor(shortLabel);
this._container.set_skip_paint(shortLabel, true);
if (!(is.shortName in inputSourcesByShortName))
inputSourcesByShortName[is.shortName] = [];
inputSourcesByShortName[is.shortName].push(is);
this._inputSources[is.index] = is;
if (is.type == INPUT_SOURCE_TYPE_IBUS)
this._ibusSources[is.id] = is;
}
let menuIndex = 0;
for (let i in this._inputSources) {
let is = this._inputSources[i];
if (inputSourcesByShortName[is.shortName].length > 1) {
let sub = inputSourcesByShortName[is.shortName].indexOf(is) + 1;
is.shortName += String.fromCharCode(0x2080 + sub);
}
this.menu.addMenuItem(is.menuItem, menuIndex++);
this._container.add_actor(is.indicatorLabel);
this._container.set_skip_paint(is.indicatorLabel, true);
}
let sourcesList = [];
for (let i in this._inputSources)
sourcesList.push(this._inputSources[i]);
let mruSources = [];
for (let i = 0; i < this._mruSources.length; i++) {
for (let j = 0; j < sourcesList.length; j++)
if (this._mruSources[i].type == sourcesList[j].type &&
this._mruSources[i].id == sourcesList[j].id) {
mruSources = mruSources.concat(sourcesList.splice(j, 1));
break;
}
}
this._mruSources = mruSources.concat(sourcesList);
this._currentInputSourceChanged();
},
_showLayout: function() {
Main.overview.hide();
let sources = this._settings.get_value(KEY_INPUT_SOURCES);
let current = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
let [type, id] = sources.get_child_value(current).deep_unpack();
let source = this._currentSource;
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 (source.type == INPUT_SOURCE_TYPE_XKB) {
[, , , xkbLayout, xkbVariant] = this._xkbInfo.get_layout_info(source.id);
} else if (source.type == INPUT_SOURCE_TYPE_IBUS) {
let engineDesc = this._ibusManager.getEngineDesc(source.id);
if (engineDesc) {
xkbLayout = engineDesc.get_layout();
xkbVariant = '';
@ -394,26 +571,25 @@ const InputSourceIndicator = new Lang.Class({
return String.fromCharCode(0x2328); // keyboard glyph
},
_propertyWhitelisted: function(prop) {
for (let i = 0; i < this._propertiesWhitelist.length; ++i) {
let key = prop.get_key();
if (key.substr(0, this._propertiesWhitelist[i].length) == this._propertiesWhitelist[i])
return true;
}
return false;
},
_ibusPropertiesRegistered: function(im, props) {
this._properties = props;
this._buildPropSection();
},
_ibusPropertyUpdated: function(im, prop) {
if (!this._propertyWhitelisted(prop))
_ibusPropertiesRegistered: function(im, engineName, props) {
let source = this._ibusSources[engineName];
if (!source)
return;
if (this._updateSubProperty(this._properties, prop))
this._buildPropSection();
source.properties = props;
if (source == this._currentSource)
this._currentInputSourceChanged();
},
_ibusPropertyUpdated: function(im, engineName, prop) {
let source = this._ibusSources[engineName];
if (!source)
return;
if (this._updateSubProperty(source.properties, prop) &&
source == this._currentSource)
this._currentInputSourceChanged();
},
_updateSubProperty: function(props, prop) {
@ -433,25 +609,12 @@ const InputSourceIndicator = new Lang.Class({
return false;
},
_updateIndicatorLabel: function(text) {
let layoutItem = this._layoutItems[this._currentSourceIndex];
let hasProperties;
if (layoutItem)
hasProperties = this._ibusManager.hasProperties(layoutItem.ibusEngineId);
else
hasProperties = false;
if (hasProperties)
this._labelActors[this._currentSourceIndex].set_text(text);
},
_buildPropSection: function() {
_buildPropSection: function(properties) {
this._propSeparator.actor.hide();
this._propSection.actor.hide();
this._propSection.removeAll();
this._buildPropSubMenu(this._propSection, this._properties);
this._buildPropSubMenu(this._propSection, properties);
if (!this._propSection.isEmpty()) {
this._propSection.actor.show();
@ -468,8 +631,7 @@ const InputSourceIndicator = new Lang.Class({
for (let i = 0; (p = props.get(i)) != null; ++i) {
let prop = p;
if (!this._propertyWhitelisted(prop) ||
!prop.get_visible())
if (!prop.get_visible())
continue;
if (prop.get_key() == 'InputMode') {
@ -480,15 +642,17 @@ const InputSourceIndicator = new Lang.Class({
text = prop.get_label().get_text();
if (text && text.length > 0 && text.length < 3)
this._updateIndicatorLabel(text);
this._currentSource.indicatorLabel.set_text(text);
}
let item;
let type = prop.get_prop_type();
if (type == IBus.PropType.MENU) {
switch (prop.get_prop_type()) {
case IBus.PropType.MENU:
item = new PopupMenu.PopupSubMenuMenuItem(prop.get_label().get_text());
this._buildPropSubMenu(item.menu, prop.get_sub_props());
} else if (type == IBus.PropType.RADIO) {
break;
case IBus.PropType.RADIO:
item = new PopupMenu.PopupMenuItem(prop.get_label().get_text());
item.prop = prop;
radioGroup.push(item);
@ -513,7 +677,39 @@ const InputSourceIndicator = new Lang.Class({
}
}
}));
} else {
break;
case IBus.PropType.TOGGLE:
item = new PopupMenu.PopupSwitchMenuItem(prop.get_label().get_text(), prop.get_state() == IBus.PropState.CHECKED);
item.prop = prop;
item.connect('toggled', Lang.bind(this, function() {
if (item.state) {
item.prop.set_state(IBus.PropState.CHECKED);
this._ibusManager.activateProperty(item.prop.get_key(),
IBus.PropState.CHECKED);
} else {
item.prop.set_state(IBus.PropState.UNCHECKED);
this._ibusManager.activateProperty(item.prop.get_key(),
IBus.PropState.UNCHECKED);
}
}));
break;
case IBus.PropType.NORMAL:
item = new PopupMenu.PopupMenuItem(prop.get_label().get_text());
item.prop = prop;
item.connect('activate', Lang.bind(this, function() {
this._ibusManager.activateProperty(item.prop.get_key(),
IBus.PropState.CHECKED);
}));
break;
case IBus.PropType.SEPARATOR:
item = new PopupMenu.PopupSeparatorMenuItem();
break;
default:
log ('IBus property %s has invalid type %d'.format(prop.get_key(), type));
continue;
}
@ -522,14 +718,91 @@ const InputSourceIndicator = new Lang.Class({
}
},
_getNewInputSource: function(current) {
for (let i in this._inputSources) {
let is = this._inputSources[i];
if (is.type == current.type &&
is.id == current.id)
return is;
}
return this._currentSource;
},
_getCurrentWindow: function() {
if (Main.overview.visible)
return Main.overview;
else
return global.display.focus_window;
},
_setPerWindowInputSource: function() {
let window = this._getCurrentWindow();
if (!window)
return;
if (!window._inputSources) {
window._inputSources = this._inputSources;
window._currentSource = this._currentSource;
} else if (window._inputSources == this._inputSources) {
window._currentSource.activate();
} else {
window._inputSources = this._inputSources;
window._currentSource = this._getNewInputSource(window._currentSource);
window._currentSource.activate();
}
},
_sourcesPerWindowChanged: function() {
this._sourcesPerWindow = this._settings.get_boolean('per-window');
if (this._sourcesPerWindow && this._focusWindowNotifyId == 0) {
this._focusWindowNotifyId = global.display.connect('notify::focus-window',
Lang.bind(this, this._setPerWindowInputSource));
this._overviewShowingId = Main.overview.connect('showing',
Lang.bind(this, this._setPerWindowInputSource));
this._overviewHiddenId = Main.overview.connect('hidden',
Lang.bind(this, this._setPerWindowInputSource));
} else if (!this._sourcesPerWindow && this._focusWindowNotifyId != 0) {
global.display.disconnect(this._focusWindowNotifyId);
this._focusWindowNotifyId = 0;
Main.overview.disconnect(this._overviewShowingId);
this._overviewShowingId = 0;
Main.overview.disconnect(this._overviewHiddenId);
this._overviewHiddenId = 0;
let windows = global.get_window_actors().map(function(w) {
return w.meta_window;
});
for (let i = 0; i < windows.length; ++i) {
delete windows[i]._inputSources;
delete windows[i]._currentSource;
}
delete Main.overview._inputSources;
delete Main.overview._currentSource;
}
},
_changePerWindowSource: function() {
if (!this._sourcesPerWindow)
return;
let window = this._getCurrentWindow();
if (!window)
return;
window._inputSources = this._inputSources;
window._currentSource = this._currentSource;
},
_containerGetPreferredWidth: function(container, for_height, alloc) {
// Here, and in _containerGetPreferredHeight, we need to query
// for the height of all children, but we ignore the results
// for those we don't actually display.
let max_min_width = 0, max_natural_width = 0;
for (let i in this._labelActors) {
let [min_width, natural_width] = this._labelActors[i].get_preferred_width(for_height);
for (let i in this._inputSources) {
let is = this._inputSources[i];
let [min_width, natural_width] = is.indicatorLabel.get_preferred_width(for_height);
max_min_width = Math.max(max_min_width, min_width);
max_natural_width = Math.max(max_natural_width, natural_width);
}
@ -541,8 +814,9 @@ const InputSourceIndicator = new Lang.Class({
_containerGetPreferredHeight: function(container, for_width, alloc) {
let max_min_height = 0, max_natural_height = 0;
for (let i in this._labelActors) {
let [min_height, natural_height] = this._labelActors[i].get_preferred_height(for_width);
for (let i in this._inputSources) {
let is = this._inputSources[i];
let [min_height, natural_height] = is.indicatorLabel.get_preferred_height(for_width);
max_min_height = Math.max(max_min_height, min_height);
max_natural_height = Math.max(max_natural_height, natural_height);
}
@ -558,7 +832,9 @@ const InputSourceIndicator = new Lang.Class({
box.y2 -= box.y1;
box.y1 = 0;
for (let i in this._labelActors)
this._labelActors[i].allocate_align_fill(box, 0.5, 0, false, false, flags);
for (let i in this._inputSources) {
let is = this._inputSources[i];
is.indicatorLabel.allocate_align_fill(box, 0.5, 0, false, false, flags);
}
}
});

View File

@ -10,48 +10,53 @@ const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const VolumeMenu = imports.ui.status.volume;
const FakeStatusIcon = new Lang.Class({
Name: 'FakeStatusIcon',
_init: function(button) {
this.actor = new St.BoxLayout({ style_class: 'panel-status-button-box' });
this._button = button;
this._button.connect('icons-updated', Lang.bind(this, this._reconstructIcons));
this._button.actor.bind_property('visible', this.actor, 'visible',
GObject.BindingFlags.SYNC_CREATE);
this._reconstructIcons();
},
_reconstructIcons: function() {
this.actor.destroy_all_children();
this._button.icons.forEach(Lang.bind(this, function(icon) {
let newIcon = new St.Icon({ style_class: 'system-status-icon' });
icon.bind_property('gicon', newIcon, 'gicon',
GObject.BindingFlags.SYNC_CREATE);
icon.bind_property('visible', newIcon, 'visible',
GObject.BindingFlags.SYNC_CREATE);
this.actor.add_actor(newIcon);
}));
}
});
const Indicator = new Lang.Class({
Name: 'LockScreenMenuIndicator',
Extends: PanelMenu.SystemStatusButton,
_init: function() {
this.parent(null, _("Volume, network, battery"));
this._box.style_class = 'lock-screen-status-button-box';
this._volume = Main.panel.statusArea.volume;
if (this._volume) {
this._volumeIcon = this.addIcon(null);
this._volume.mainIcon.bind_property('gicon', this._volumeIcon, 'gicon',
GObject.BindingFlags.SYNC_CREATE);
this._volume.mainIcon.bind_property('visible', this._volumeIcon, 'visible',
GObject.BindingFlags.SYNC_CREATE);
this._volumeControl = VolumeMenu.getMixerControl();
this._volumeMenu = new VolumeMenu.VolumeMenu(this._volumeControl);
this.menu.addMenuItem(this._volumeMenu);
this._volumeControl = VolumeMenu.getMixerControl();
this._volumeMenu = new VolumeMenu.VolumeMenu(this._volumeControl);
this.menu.addMenuItem(this._volumeMenu);
this._volume = new FakeStatusIcon(Main.panel.statusArea.volume);
this._box.add_child(this._volume.actor);
// Network may not exist if the user doesn't have NetworkManager
if (Main.panel.statusArea.network) {
this._network = new FakeStatusIcon(Main.panel.statusArea.network);
this._box.add_child(this._network.actor);
}
this._network = Main.panel.statusArea.network;
if (this._network) {
this._networkIcon = this.addIcon(null);
this._network.mainIcon.bind_property('gicon', this._networkIcon, 'gicon',
GObject.BindingFlags.SYNC_CREATE);
this._network.mainIcon.bind_property('visible', this._networkIcon, 'visible',
GObject.BindingFlags.SYNC_CREATE);
this._networkSecondaryIcon = this.addIcon(null);
this._network.secondaryIcon.bind_property('gicon', this._networkSecondaryIcon, 'gicon',
GObject.BindingFlags.SYNC_CREATE);
this._network.secondaryIcon.bind_property('visible', this._networkSecondaryIcon, 'visible',
GObject.BindingFlags.SYNC_CREATE);
}
this._battery = Main.panel.statusArea.battery;
if (this._battery) {
this._batteryIcon = this.addIcon(null);
this._battery.mainIcon.bind_property('gicon', this._batteryIcon, 'gicon',
GObject.BindingFlags.SYNC_CREATE);
this._battery.mainIcon.bind_property('visible', this._batteryIcon, 'visible',
GObject.BindingFlags.SYNC_CREATE);
}
this._battery = new FakeStatusIcon(Main.panel.statusArea.battery);
this._box.add_child(this._battery.actor);
}
});

View File

@ -5,20 +5,29 @@ const Gio = imports.gi.Gio;
const Lang = imports.lang;
const NetworkManager = imports.gi.NetworkManager;
const NMClient = imports.gi.NMClient;
const NMGtk = imports.gi.NMGtk;
const Signals = imports.signals;
const St = imports.gi.St;
// Some of the new code depends on as-yet-unreleased NM
var NMGtk;
try {
NMGtk = imports.gi.NMGtk;
} catch(e) {
NMGtk = null;
}
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const MessageTray = imports.ui.messageTray;
const NotificationDaemon = imports.ui.notificationDaemon;
const ModemManager = imports.misc.modemManager;
const Util = imports.misc.util;
const NMConnectionCategory = {
INVALID: 'invalid',
WIRED: 'wired',
VIRTUAL: 'virtual',
WIRELESS: 'wireless',
WWAN: 'wwan',
VPN: 'vpn'
@ -300,13 +309,10 @@ const NMDevice = new Lang.Class({
Extends: NMConnectionBased,
_init: function(client, device, connections) {
this.device = device;
this.device._delegate = this;
this._stateChangedId = this.device.connect('state-changed', Lang.bind(this, this._deviceStateChanged));
// protected
this._client = client;
this._setDevice(device);
this.parent(connections);
this._activeConnection = null;
this._activeConnectionItem = null;
this._autoConnectionItem = null;
@ -331,23 +337,12 @@ const NMDevice = new Lang.Class({
},
destroy: function() {
if (this.device)
this.device._delegate = null;
this._setDevice(null);
if (this._stateChangedId) {
// Need to go through GObject.Object.prototype because
// nm_device_disconnect conflicts with g_signal_disconnect
GObject.Object.prototype.disconnect.call(this.device, this._stateChangedId);
this._stateChangedId = 0;
}
if (this._carrierChangedId) {
// see above for why this is needed
GObject.Object.prototype.disconnect.call(this.device, this._carrierChangedId);
this._carrierChangedId = 0;
}
if (this._firmwareChangedId) {
GObject.Object.prototype.disconnect.call(this.device, this._firmwareChangedId);
this._firmwareChangedId = 0;
if (this._deferredWorkId) {
// Just clear out, the actual removal is handled when the
// actor is destroyed
this._deferredWorkId = 0;
}
this._clearSection();
@ -356,6 +351,33 @@ const NMDevice = new Lang.Class({
this.section.destroy();
},
_setDevice: function(device) {
if (device) {
this.device = device;
this.device._delegate = this;
this._stateChangedId = this.device.connect('state-changed', Lang.bind(this, this._deviceStateChanged));
} else if (this.device) {
this.device._delegate = null;
if (this._stateChangedId) {
// Need to go through GObject.Object.prototype because
// nm_device_disconnect conflicts with g_signal_disconnect
GObject.Object.prototype.disconnect.call(this.device, this._stateChangedId);
this._stateChangedId = 0;
}
if (this._carrierChangedId) {
GObject.Object.prototype.disconnect.call(this.device, this._carrierChangedId);
this._carrierChangedId = 0;
}
if (this._firmwareChangedId) {
GObject.Object.prototype.disconnect.call(this.device, this._firmwareChangedId);
this._firmwareChangedId = 0;
}
this.device = null;
}
},
deactivate: function() {
this.device.disconnect(null);
return true;
@ -370,7 +392,7 @@ const NMDevice = new Lang.Class({
// Otherwise, if no connection is currently configured,
// try automatic configuration (or summon the config dialog)
if (this._connections.length == 1) {
this._client.activate_connection(this._connections[0].connection, this.device, null, null);
this._client.activate_connection(this._connections[0].connection, this.device || null, null, null);
return true;
} else if (this._connections.length == 0) {
return this._activateAutomaticConnection();
@ -390,7 +412,7 @@ const NMDevice = new Lang.Class({
},
get connected() {
return this.device.state == NetworkManager.DeviceState.ACTIVATED;
return this.device && this.device.state == NetworkManager.DeviceState.ACTIVATED;
},
clearActiveConnection: function(activeConnection) {
@ -410,7 +432,6 @@ const NMDevice = new Lang.Class({
this._activeConnection = activeConnection;
this._clearSection();
this._queueCreateSection();
},
@ -424,6 +445,9 @@ const NMDevice = new Lang.Class({
},
getStatusLabel: function() {
if (!this.device)
return null;
switch(this.device.state) {
case NetworkManager.DeviceState.DISCONNECTED:
case NetworkManager.DeviceState.ACTIVATED:
@ -474,7 +498,8 @@ const NMDevice = new Lang.Class({
},
syncDescription: function() {
this.statusItem.label.text = this.device._description;
if (this.device && this.device._description)
this.statusItem.label.text = this.device._description;
},
// protected
@ -483,8 +508,10 @@ const NMDevice = new Lang.Class({
},
_queueCreateSection: function() {
this._clearSection();
Main.queueDeferredWork(this._deferredWorkId);
if (this._deferredWorkId) {
this._clearSection();
Main.queueDeferredWork(this._deferredWorkId);
}
},
_clearSection: function() {
@ -584,7 +611,6 @@ const NMDevice = new Lang.Class({
this._updateStatusItem();
this._clearSection();
this._queueCreateSection();
this.emit('state-changed');
},
@ -611,12 +637,11 @@ const NMDevice = new Lang.Class({
}
});
const NMDeviceWired = new Lang.Class({
Name: 'NMDeviceWired',
const NMDeviceSimple = new Lang.Class({
Name: 'NMDeviceSimple',
Extends: NMDevice,
_init: function(client, device, connections) {
this._autoConnectionName = _("Auto Ethernet");
this.category = NMConnectionCategory.WIRED;
this.parent(client, device, connections);
@ -631,6 +656,19 @@ const NMDeviceWired = new Lang.Class({
// we can do it here because addConnection and removeConnection
// both call _createSection at some point
this.section.actor.visible = this._connections.length > 1;
}
});
const NMDeviceWired = new Lang.Class({
Name: 'NMDeviceWired',
Extends: NMDeviceSimple,
_init: function(client, device, connections) {
device._description = _("Wired");
this._autoConnectionName = _("Auto Ethernet");
this.category = NMConnectionCategory.WIRED;
this.parent(client, device, connections);
},
_createAutomaticConnection: function() {
@ -654,12 +692,24 @@ const NMDeviceModem = new Lang.Class({
_init: function(client, device, connections) {
let is_wwan = false;
device._description = _("Mobile broadband");
this._enabled = true;
this.mobileDevice = null;
this._connectionType = 'ppp';
this._capabilities = device.current_capabilities;
if (this._capabilities & NetworkManager.DeviceModemCapabilities.GSM_UMTS) {
// Support new ModemManager1 devices
if (device.udi.indexOf('/org/freedesktop/ModemManager1/Modem') == 0) {
is_wwan = true;
this.mobileDevice = new ModemManager.BroadbandModem(device.udi, device.current_capabilities);
if (this._capabilities & NetworkManager.DeviceModemCapabilities.GSM_UMTS) {
this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME;
} else if (this._capabilities & NetworkManager.DeviceModemCapabilities.LTE) {
this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME;
} else if (this._capabilities & NetworkManager.DeviceModemCapabilities.CDMA_EVDO) {
this._connectionType = NetworkManager.SETTING_CDMA_SETTING_NAME;
}
} else if (this._capabilities & NetworkManager.DeviceModemCapabilities.GSM_UMTS) {
is_wwan = true;
this.mobileDevice = new ModemManager.ModemGsm(device.udi);
this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME;
@ -669,7 +719,8 @@ const NMDeviceModem = new Lang.Class({
this._connectionType = NetworkManager.SETTING_CDMA_SETTING_NAME;
} else if (this._capabilities & NetworkManager.DeviceModemCapabilities.LTE) {
is_wwan = true;
// FIXME: support signal quality
this.mobileDevice = new ModemManager.ModemGsm(device.udi);
this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME;
}
if (is_wwan) {
@ -772,6 +823,7 @@ const NMDeviceBluetooth = new Lang.Class({
Extends: NMDevice,
_init: function(client, device, connections) {
device._description = _("Bluetooth");
this._autoConnectionName = this._makeConnectionName(device);
device.connect('notify::name', Lang.bind(this, this._updateAutoConnectionName));
@ -814,7 +866,6 @@ const NMDeviceBluetooth = new Lang.Class({
_updateAutoConnectionName: function() {
this._autoConnectionName = this._makeConnectionName(this.device);
this._clearSection();
this._queueCreateSection();
this._updateStatusItem();
}
@ -1076,10 +1127,8 @@ const NMDeviceWireless = new Lang.Class({
this._networks.splice(res.network, 1);
let newPos = Util.insertSorted(this._networks, network, Lang.bind(this, this._networkSortFunction));
if (newPos != res.network) {
this._clearSection();
if (newPos != res.network)
this._queueCreateSection();
}
},
_accessPointAdded: function(device, accessPoint) {
@ -1132,10 +1181,8 @@ const NMDeviceWireless = new Lang.Class({
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();
if (newPos != pos)
this._queueCreateSection();
}
},
_accessPointRemoved: function(device, accessPoint) {
@ -1195,12 +1242,10 @@ const NMDeviceWireless = new Lang.Class({
if (res.network < this._networks.length-1)
okNext = this._networkSortFunction(this._networks[res.network + 1], apObj) <= 0;
if (!okPrev || !okNext) {
this._clearSection();
if (!okPrev || !okNext)
this._queueCreateSection();
} else if (apObj.item) {
else if (apObj.item)
apObj.item.updateBestAP(apObj.accessPoints[0]);
}
}
},
@ -1276,7 +1321,6 @@ const NMDeviceWireless = new Lang.Class({
if (forceupdate) {
this._networks.sort(this._networkSortFunction);
this._clearSection();
this._queueCreateSection();
}
},
@ -1309,7 +1353,6 @@ const NMDeviceWireless = new Lang.Class({
if (forceupdate) {
this._networks.sort(this._networkSortFunction);
this._clearSection();
this._queueCreateSection();
}
},
@ -1420,6 +1463,56 @@ const NMDeviceWireless = new Lang.Class({
},
});
const NMDeviceVirtual = new Lang.Class({
Name: 'NMDeviceVirtual',
Extends: NMDeviceSimple,
_init: function(client, iface, connections) {
this.iface = iface;
this.parent(client, null, connections);
this.category = NMConnectionCategory.VIRTUAL;
},
_shouldShowConnectionList: function() {
return this.hasConnections();
},
connectionValid: function(connection) {
return connection.get_virtual_iface_name() == this.iface;
},
addConnection: function(connection) {
if (!this.device && !this.hasConnections())
this.statusItem.label.text = NMGtk.utils_get_connection_device_name(connection);
this.parent(connection);
},
adoptDevice: function(device) {
if (device.get_iface() == this.iface) {
this._setDevice(device);
if (device._description)
this.syncDescription();
this._updateStatusItem();
this.emit('state-changed');
return true;
} else
return false;
},
removeDevice: function(device) {
if (device == this.device) {
this._setDevice(null);
this._updateStatusItem();
this.emit('state-changed');
}
},
hasConnections: function() {
return this._connections.length != 0;
}
});
const NMVPNSection = new Lang.Class({
Name: 'NMVPNSection',
Extends: NMConnectionBased,
@ -1600,6 +1693,7 @@ const NMApplet = new Lang.Class({
this._nmDevices = [];
this._devices = { };
this._virtualDevices = [ ];
this._devices.wired = {
section: new PopupMenu.PopupMenuSection(),
@ -1610,6 +1704,15 @@ const NMApplet = new Lang.Class({
this.menu.addMenuItem(this._devices.wired.section);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.virtual = {
section: new PopupMenu.PopupMenuSection(),
devices: [ ],
};
this._devices.virtual.section.actor.hide();
this.menu.addMenuItem(this._devices.virtual.section);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.wireless = {
section: new PopupMenu.PopupMenuSection(),
devices: [ ],
@ -1640,8 +1743,17 @@ const NMApplet = new Lang.Class({
this._dtypes[NetworkManager.DeviceType.WIFI] = NMDeviceWireless;
this._dtypes[NetworkManager.DeviceType.MODEM] = NMDeviceModem;
this._dtypes[NetworkManager.DeviceType.BT] = NMDeviceBluetooth;
this._dtypes[NetworkManager.DeviceType.INFINIBAND] = NMDeviceSimple;
// TODO: WiMax support
// Virtual device types
this._vtypes = { };
if (NMGtk) {
this._vtypes[NetworkManager.SETTING_VLAN_SETTING_NAME] = NMDeviceVirtual;
this._vtypes[NetworkManager.SETTING_BOND_SETTING_NAME] = NMDeviceVirtual;
this._vtypes[NetworkManager.SETTING_BRIDGE_SETTING_NAME] = NMDeviceVirtual;
}
// Connection types
this._ctypes = { };
this._ctypes[NetworkManager.SETTING_WIRELESS_SETTING_NAME] = NMConnectionCategory.WIRELESS;
@ -1651,6 +1763,12 @@ const NMApplet = new Lang.Class({
this._ctypes[NetworkManager.SETTING_BLUETOOTH_SETTING_NAME] = NMConnectionCategory.WWAN;
this._ctypes[NetworkManager.SETTING_CDMA_SETTING_NAME] = NMConnectionCategory.WWAN;
this._ctypes[NetworkManager.SETTING_GSM_SETTING_NAME] = NMConnectionCategory.WWAN;
this._ctypes[NetworkManager.SETTING_INFINIBAND_SETTING_NAME] = NMConnectionCategory.WIRED;
if (NMGtk) {
this._ctypes[NetworkManager.SETTING_VLAN_SETTING_NAME] = NMConnectionCategory.VIRTUAL;
this._ctypes[NetworkManager.SETTING_BOND_SETTING_NAME] = NMConnectionCategory.VIRTUAL;
this._ctypes[NetworkManager.SETTING_BRIDGE_SETTING_NAME] = NMConnectionCategory.VIRTUAL;
}
this._ctypes[NetworkManager.SETTING_VPN_SETTING_NAME] = NMConnectionCategory.VPN;
this._settings = NMClient.RemoteSettings.new(null);
@ -1678,6 +1796,7 @@ const NMApplet = new Lang.Class({
if (!this._source) {
this._source = new MessageTray.Source(_("Network Manager"),
'network-transmit-receive');
this._source.policy = new NotificationDaemon.NotificationApplicationPolicy('gnome-network-panel');
this._source.connect('destroy', Lang.bind(this, function() {
this._source = null;
@ -1702,7 +1821,14 @@ const NMApplet = new Lang.Class({
let devices = this._devices[category].devices;
let item = this._devices[category].item;
let section = this._devices[category].section;
if (devices.length == 0)
let visible;
if (category == NMConnectionCategory.VIRTUAL)
visible = !section.isEmpty();
else
visible = devices.length > 0;
if (!visible)
section.actor.hide();
else {
section.actor.show();
@ -1763,9 +1889,50 @@ const NMApplet = new Lang.Class({
MessageTray.Urgency.HIGH);
},
_makeWrapperDevice: function(wrapperClass, device) {
let wrapper = new wrapperClass(this._client, device, this._connections);
_syncDeviceNames: function() {
if (NMGtk) {
let names = NMGtk.utils_disambiguate_device_names(this._nmDevices);
for (let i = 0; i < this._nmDevices.length; i++) {
let device = this._nmDevices[i];
device._description = names[i];
if (device._delegate)
device._delegate.syncDescription();
}
} else {
for (let i = 0; i < this._nmDevices.length; i++) {
let device = this._nmDevices[i];
device._delegate.syncDescription();
}
}
},
_deviceAdded: function(client, device, skipSyncDeviceNames) {
if (device._delegate) {
// already seen, not adding again
return;
}
for (let i = 0; i < this._virtualDevices.length; i++) {
if (this._virtualDevices[i].adoptDevice(device)) {
this._nmDevices.push(device);
if (!skipSyncDeviceNames)
this._syncDeviceNames();
return;
}
}
let wrapperClass = this._dtypes[device.get_device_type()];
if (wrapperClass) {
let wrapper = new wrapperClass(this._client, device, this._connections);
this._addDeviceWrapper(wrapper);
this._nmDevices.push(device);
if (!skipSyncDeviceNames)
this._syncDeviceNames();
}
},
_addDeviceWrapper: function(wrapper) {
wrapper._activationFailedId = wrapper.connect('activation-failed',
Lang.bind(this, this._onActivationFailed));
wrapper._deviceStateChangedId = wrapper.connect('state-changed', Lang.bind(this, function(dev) {
@ -1777,60 +1944,42 @@ const NMApplet = new Lang.Class({
wrapper.disconnect(wrapper._destroyId);
});
return wrapper;
},
let section = this._devices[wrapper.category].section;
section.addMenuItem(wrapper.statusItem);
section.addMenuItem(wrapper.section);
_syncDeviceNames: function() {
let names = NMGtk.utils_disambiguate_device_names(this._nmDevices);
for (let i = 0; i < this._nmDevices.length; i++) {
let device = this._nmDevices[i];
if (device._description != names[i]) {
device._description = names[i];
device._delegate.syncDescription();
}
}
},
let devices = this._devices[wrapper.category].devices;
devices.push(wrapper);
_deviceAdded: function(client, device, skipSyncDeviceNames) {
if (device._delegate) {
// already seen, not adding again
return;
}
let wrapperClass = this._dtypes[device.get_device_type()];
if (wrapperClass) {
let wrapper = this._makeWrapperDevice(wrapperClass, device);
let section = this._devices[wrapper.category].section;
let devices = this._devices[wrapper.category].devices;
section.addMenuItem(wrapper.statusItem);
section.addMenuItem(wrapper.section);
devices.push(wrapper);
this._nmDevices.push(device);
if (!skipSyncDeviceNames)
this._syncDeviceNames();
this._syncSectionTitle(wrapper.category);
}
this._syncSectionTitle(wrapper.category);
},
_deviceRemoved: function(client, device) {
if (!device._delegate) {
let pos = this._nmDevices.indexOf(device);
if (pos != -1) {
this._nmDevices.splice(pos, 1);
this._syncDeviceNames();
}
let wrapper = device._delegate;
if (!wrapper) {
log('Removing a network device that was not added');
return;
}
let wrapper = device._delegate;
if (wrapper instanceof NMDeviceVirtual)
wrapper.removeDevice(device);
else
this._removeDeviceWrapper(wrapper);
},
_removeDeviceWrapper: function(wrapper) {
wrapper.destroy();
let devices = this._devices[wrapper.category].devices;
let pos = devices.indexOf(wrapper);
devices.splice(pos, 1);
pos = this._nmDevices.indexOf(device);
this._nmDevices.splice(pos, 1);
this._syncDeviceNames();
this._syncSectionTitle(wrapper.category)
},
@ -2025,6 +2174,13 @@ const NMApplet = new Lang.Class({
devices[i].removeConnection(connection);
}
if (section == NMConnectionCategory.VIRTUAL) {
let iface = connection.get_virtual_iface_name();
let wrapper = this._findVirtualDevice(iface);
if (wrapper && !wrapper.hasConnections())
this._removeDeviceWrapper(wrapper);
}
connection.disconnect(connection._removedId);
connection.disconnect(connection._updatedId);
connection._removedId = connection._updatedId = 0;
@ -2038,6 +2194,27 @@ const NMApplet = new Lang.Class({
let section = connection._section;
if (section == NMConnectionCategory.VIRTUAL) {
let wrapperClass = this._vtypes[connection._type];
if (!wrapperClass)
return;
let iface = connection.get_virtual_iface_name();
let wrapper = this._findVirtualDevice(iface);
if (!wrapper) {
wrapper = new wrapperClass(this._client, iface, this._connections);
this._addDeviceWrapper(wrapper);
this._virtualDevices.push(wrapper);
// We might already have a device for this connection
for (let i = 0; i < this._nmDevices.length; i++) {
let device = this._nmDevices[i];
if (wrapper.adoptDevice(device))
break;
}
}
}
if (section == NMConnectionCategory.INVALID)
return;
if (section == NMConnectionCategory.VPN) {
@ -2050,6 +2227,15 @@ const NMApplet = new Lang.Class({
}
},
_findVirtualDevice: function(iface) {
for (let i = 0; i < this._virtualDevices.length; i++) {
if (this._virtualDevices[i].iface == iface)
return this._virtualDevices[i];
}
return null;
},
_hideDevices: function() {
this._devicesHidden = true;
@ -2065,6 +2251,7 @@ const NMApplet = new Lang.Class({
this._statusSection.actor.hide();
this._syncSectionTitle(NMConnectionCategory.WIRED);
this._syncSectionTitle(NMConnectionCategory.VIRTUAL);
this._syncSectionTitle(NMConnectionCategory.WIRELESS);
this._syncSectionTitle(NMConnectionCategory.WWAN);
},
@ -2102,6 +2289,7 @@ const NMApplet = new Lang.Class({
this.setIcon('network-wireless-acquiring-symbolic');
break;
case NMConnectionCategory.WIRED:
case NMConnectionCategory.VIRTUAL:
this.setIcon('network-wired-acquiring-symbolic');
break;
default:
@ -2141,6 +2329,7 @@ const NMApplet = new Lang.Class({
break;
}
case NMConnectionCategory.WIRED:
case NMConnectionCategory.VIRTUAL:
this.setIcon('network-wired-symbolic');
break;
case NMConnectionCategory.WWAN:

View File

@ -7,7 +7,7 @@ const St = imports.gi.St;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const BUS_NAME = 'org.gnome.SettingsDaemon';
const BUS_NAME = 'org.gnome.SettingsDaemon.Power';
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
const UPDeviceType = {
@ -54,14 +54,23 @@ const Indicator = new Lang.Class({
_init: function() {
this.parent('battery-missing-symbolic', _("Battery"));
this._proxy = new PowerManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH);
this._proxy = new PowerManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
Lang.bind(this, function(proxy, error) {
if (error) {
log(error.message);
return;
}
this._proxy.connect('g-properties-changed',
Lang.bind(this, this._devicesChanged));
this._devicesChanged();
}));
this._deviceItems = [ ];
this._hasPrimary = false;
this._primaryDeviceId = null;
this._batteryItem = new PopupMenu.PopupMenuItem('', { reactive: false });
this._primaryPercentage = new St.Label();
this._primaryPercentage = new St.Label({ style_class: 'popup-battery-percentage' });
this._batteryItem.addActor(this._primaryPercentage, { align: St.Align.END });
this.menu.addMenuItem(this._batteryItem);
@ -70,10 +79,6 @@ const Indicator = new Lang.Class({
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop');
this._proxy.connect('g-properties-changed',
Lang.bind(this, this._devicesChanged));
this._devicesChanged();
},
_readPrimaryDevice: function() {
@ -183,7 +188,8 @@ const DeviceItem = new Lang.Class({
this._box.add_actor(this._label);
this.addActor(this._box);
let percentLabel = new St.Label({ text: C_("percent of battery remaining", "%d%%").format(Math.round(percentage)) });
let percentLabel = new St.Label({ text: C_("percent of battery remaining", "%d%%").format(Math.round(percentage)),
style_class: 'popup-battery-percentage' });
this.addActor(percentLabel, { align: St.Align.END });
//FIXME: ideally we would like to expose this._label and percentLabel
this.actor.label_actor = percentLabel;
@ -192,9 +198,9 @@ const DeviceItem = new Lang.Class({
_deviceTypeToString: function(type) {
switch (type) {
case UPDeviceType.AC_POWER:
return _("AC adapter");
return _("AC Adapter");
case UPDeviceType.BATTERY:
return _("Laptop battery");
return _("Laptop Battery");
case UPDeviceType.UPS:
return _("UPS");
case UPDeviceType.MONITOR:
@ -206,9 +212,9 @@ const DeviceItem = new Lang.Class({
case UPDeviceType.PDA:
return _("PDA");
case UPDeviceType.PHONE:
return _("Cell phone");
return _("Cell Phone");
case UPDeviceType.MEDIA_PLAYER:
return _("Media player");
return _("Media Player");
case UPDeviceType.TABLET:
return _("Tablet");
case UPDeviceType.COMPUTER:

View File

@ -5,6 +5,7 @@ const Lang = imports.lang;
const Gio = imports.gi.Gio;
const Gvc = imports.gi.Gvc;
const St = imports.gi.St;
const Signals = imports.signals;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
@ -26,6 +27,210 @@ function getMixerControl() {
return _mixerControl;
}
const StreamSlider = new Lang.Class({
Name: 'StreamSlider',
_init: function(control, title) {
this._control = control;
this.item = new PopupMenu.PopupMenuSection();
this._title = new PopupMenu.PopupMenuItem(title, { reactive: false });
this._slider = new PopupMenu.PopupSliderMenuItem(0);
this._slider.connect('value-changed', Lang.bind(this, this._sliderChanged));
this._slider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
this.item.addMenuItem(this._title);
this.item.addMenuItem(this._slider);
this._stream = null;
this._shouldShow = true;
},
get stream() {
return this._stream;
},
set stream(stream) {
if (this._stream) {
this._disconnectStream(this._stream);
}
this._stream = stream;
if (this._stream) {
this._connectStream(this._stream);
this._updateVolume();
} else {
this.emit('stream-updated');
}
this._updateVisibility();
},
_disconnectStream: function(stream) {
stream.disconnect(this._mutedChangedId);
this._mutedChangedId = 0;
stream.disconnect(this._volumeChangedId);
this._volumeChangedId = 0;
},
_connectStream: function(stream) {
this._mutedChangedId = stream.connect('notify::is-muted', Lang.bind(this, this._updateVolume));
this._volumeChangedId = stream.connect('notify::volume', Lang.bind(this, this._updateVolume));
},
_shouldBeVisible: function() {
return this._stream != null;
},
_updateVisibility: function() {
let visible = this._shouldBeVisible();
this._title.actor.visible = visible;
this._slider.actor.visible = visible;
},
scroll: function(event) {
this._slider.scroll(event);
},
setValue: function(value) {
// piggy-back off of sliderChanged
this._slider.setValue(value);
},
_sliderChanged: function(slider, value, property) {
if (!this._stream)
return;
let volume = value * this._control.get_vol_max_norm();
let prevMuted = this._stream.is_muted;
if (volume < 1) {
this._stream.volume = 0;
if (!prevMuted)
this._stream.change_is_muted(true);
} else {
this._stream.volume = volume;
if (prevMuted)
this._stream.change_is_muted(false);
}
this._stream.push_volume();
},
_notifyVolumeChange: function() {
global.cancel_theme_sound(VOLUME_NOTIFY_ID);
global.play_theme_sound(VOLUME_NOTIFY_ID,
'audio-volume-change',
_("Volume changed"),
Clutter.get_current_event ());
},
_updateVolume: function() {
let muted = this._stream.is_muted;
this._slider.setValue(muted ? 0 : (this._stream.volume / this._control.get_vol_max_norm()));
this.emit('stream-updated');
},
getIcon: function() {
if (!this._stream)
return null;
let volume = this._stream.volume;
if (this._stream.is_muted || volume <= 0) {
return 'audio-volume-muted-symbolic';
} else {
let n = Math.floor(3 * volume / this._control.get_vol_max_norm()) + 1;
if (n < 2)
return 'audio-volume-low-symbolic';
if (n >= 3)
return 'audio-volume-high-symbolic';
return 'audio-volume-medium-symbolic';
}
}
});
Signals.addSignalMethods(StreamSlider.prototype);
const OutputStreamSlider = new Lang.Class({
Name: 'OutputStreamSlider',
Extends: StreamSlider,
_connectStream: function(stream) {
this.parent(stream);
this._portChangedId = stream.connect('notify::port', Lang.bind(this, this._portChanged));
this._portChanged();
},
_findHeadphones: function(sink) {
// This only works for external headphones (e.g. bluetooth)
if (sink.get_form_factor() == 'headset' ||
sink.get_form_factor() == 'headphone')
return true;
// a bit hackish, but ALSA/PulseAudio have a number
// of different identifiers for headphones, and I could
// not find the complete list
if (sink.get_ports().length > 0)
return sink.get_port().port.indexOf('headphone') >= 0;
return false;
},
_disconnectStream: function(stream) {
this.parent(stream);
stream.disconnect(this._portChangedId);
this._portChangedId = 0;
},
_portChanged: function() {
let hasHeadphones = this._findHeadphones(this._stream);
if (hasHeadphones != this._hasHeadphones) {
this._hasHeadphones = hasHeadphones;
this.emit('headphones-changed', this._hasHeadphones);
}
}
});
const InputStreamSlider = new Lang.Class({
Name: 'InputStreamSlider',
Extends: StreamSlider,
_init: function(control, title) {
this.parent(control, title);
this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput));
this._control.connect('stream-removed', Lang.bind(this, this._maybeShowInput));
},
_connectStream: function(stream) {
this.parent(stream);
this._maybeShowInput();
},
_maybeShowInput: function() {
// only show input widgets if any application is recording audio
let showInput = false;
let recordingApps = this._control.get_source_outputs();
if (this._stream && recordingApps) {
for (let i = 0; i < recordingApps.length; i++) {
let outputStream = recordingApps[i];
let id = outputStream.get_application_id();
// but skip gnome-volume-control and pavucontrol
// (that appear as recording because they show the input level)
if (!id || (id != 'org.gnome.VolumeControl' && id != 'org.PulseAudio.pavucontrol')) {
showInput = true;
break;
}
}
}
this._showInput = showInput;
this._updateVisibility();
},
_shouldBeVisible: function() {
return this.parent() && this._showInput;
}
});
const VolumeMenu = new Lang.Class({
Name: 'VolumeMenu',
Extends: PopupMenu.PopupMenuSection,
@ -39,207 +244,48 @@ const VolumeMenu = new Lang.Class({
this._control.connect('state-changed', Lang.bind(this, this._onControlStateChanged));
this._control.connect('default-sink-changed', Lang.bind(this, this._readOutput));
this._control.connect('default-source-changed', Lang.bind(this, this._readInput));
this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput));
this._control.connect('stream-removed', Lang.bind(this, this._maybeShowInput));
this._volumeMax = this._control.get_vol_max_norm();
this._output = null;
this._outputVolumeId = 0;
this._outputMutedId = 0;
/* Translators: This is the label for audio volume */
this._outputTitle = new PopupMenu.PopupMenuItem(_("Volume"), { reactive: false });
this._outputSlider = new PopupMenu.PopupSliderMenuItem(0);
this._outputSlider.connect('value-changed', Lang.bind(this, this._sliderChanged, '_output'));
this._outputSlider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
this.addMenuItem(this._outputTitle);
this.addMenuItem(this._outputSlider);
this._output = new OutputStreamSlider(this._control, _("Volume"));
this._output.connect('stream-updated', Lang.bind(this, function() {
this.emit('icon-changed');
}));
this._output.connect('headphones-changed', Lang.bind(this, function(stream, value) {
this.emit('headphones-changed', value);
}));
this.addMenuItem(this._output.item);
this._input = new InputStreamSlider(this._control, _("Microphone"));
this.addMenuItem(this._input.item);
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._input = null;
this._inputVolumeId = 0;
this._inputMutedId = 0;
this._inputTitle = new PopupMenu.PopupMenuItem(_("Microphone"), { reactive: false });
this._inputSlider = new PopupMenu.PopupSliderMenuItem(0);
this._inputSlider.connect('value-changed', Lang.bind(this, this._sliderChanged, '_input'));
this._inputSlider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
this.addMenuItem(this._inputTitle);
this.addMenuItem(this._inputSlider);
this._onControlStateChanged();
},
scroll: function(direction) {
let currentVolume = this._output.volume;
if (direction == Clutter.ScrollDirection.DOWN) {
let prev_muted = this._output.is_muted;
this._output.volume = Math.max(0, currentVolume - this._volumeMax * VOLUME_ADJUSTMENT_STEP);
if (this._output.volume < 1) {
this._output.volume = 0;
if (!prev_muted)
this._output.change_is_muted(true);
}
this._output.push_volume();
}
else if (direction == Clutter.ScrollDirection.UP) {
this._output.volume = Math.min(this._volumeMax, currentVolume + this._volumeMax * VOLUME_ADJUSTMENT_STEP);
this._output.change_is_muted(false);
this._output.push_volume();
}
this._notifyVolumeChange();
scroll: function(event) {
this._output.scroll(event);
},
_onControlStateChanged: function() {
if (this._control.get_state() == Gvc.MixerControlState.READY) {
this._readOutput();
this._readInput();
this._maybeShowInput();
this._readOutput();
} else {
this.emit('icon-changed', null);
this.emit('icon-changed');
}
},
_findHeadphones: function(sink) {
// This only works for external headphones (e.g. bluetooth)
if (sink.get_form_factor() == 'headset' ||
sink.get_form_factor() == 'headphone')
return true;
// a bit hackish, but ALSA/PulseAudio have a number
// of different identifiers for headphones, and I could
// not find the complete list
let port = sink.get_port();
if (port)
return port.port.indexOf('headphone') >= 0;
return false;
},
_portChanged: function() {
this.hasHeadphones = this._findHeadphones(this._output);
this.emit('headphones-changed');
},
_readOutput: function() {
if (this._outputVolumeId) {
this._output.disconnect(this._outputVolumeId);
this._output.disconnect(this._outputMutedId);
this._output.disconnect(this._outputPortId);
this._outputVolumeId = 0;
this._outputMutedId = 0;
this._outputPortId = 0;
}
this._output = this._control.get_default_sink();
if (this._output) {
this._outputMutedId = this._output.connect('notify::is-muted', Lang.bind(this, this._mutedChanged, '_output'));
this._outputVolumeId = this._output.connect('notify::volume', Lang.bind(this, this._volumeChanged, '_output'));
this._outputPortId = this._output.connect('notify::port', Lang.bind(this, this._portChanged));
this._mutedChanged(null, null, '_output');
this._volumeChanged(null, null, '_output');
this._portChanged();
} else {
this.hasHeadphones = false;
this._outputSlider.setValue(0);
this.emit('icon-changed', 'audio-volume-muted-symbolic');
}
this._output.stream = this._control.get_default_sink();
},
_readInput: function() {
if (this._inputVolumeId) {
this._input.disconnect(this._inputVolumeId);
this._input.disconnect(this._inputMutedId);
this._inputVolumeId = 0;
this._inputMutedId = 0;
}
this._input = this._control.get_default_source();
if (this._input) {
this._inputMutedId = this._input.connect('notify::is-muted', Lang.bind(this, this._mutedChanged, '_input'));
this._inputVolumeId = this._input.connect('notify::volume', Lang.bind(this, this._volumeChanged, '_input'));
this._mutedChanged (null, null, '_input');
this._volumeChanged (null, null, '_input');
} else {
this._inputTitle.actor.hide();
this._inputSlider.actor.hide();
}
this._input.stream = this._control.get_default_source();
},
_maybeShowInput: function() {
// only show input widgets if any application is recording audio
let showInput = false;
let recordingApps = this._control.get_source_outputs();
if (this._input && recordingApps) {
for (let i = 0; i < recordingApps.length; i++) {
let outputStream = recordingApps[i];
let id = outputStream.get_application_id();
// but skip gnome-volume-control and pavucontrol
// (that appear as recording because they show the input level)
if (!id || (id != 'org.gnome.VolumeControl' && id != 'org.PulseAudio.pavucontrol')) {
showInput = true;
break;
}
}
}
this._inputTitle.actor.visible = showInput;
this._inputSlider.actor.visible = showInput;
},
_volumeToIcon: function(volume) {
if (volume <= 0) {
return 'audio-volume-muted-symbolic';
} else {
let n = Math.floor(3 * volume / this._volumeMax) + 1;
if (n < 2)
return 'audio-volume-low-symbolic';
if (n >= 3)
return 'audio-volume-high-symbolic';
return 'audio-volume-medium-symbolic';
}
},
_sliderChanged: function(slider, value, property) {
if (this[property] == null) {
log ('Volume slider changed for %s, but %s does not exist'.format(property, property));
return;
}
let volume = value * this._volumeMax;
let prev_muted = this[property].is_muted;
if (volume < 1) {
this[property].volume = 0;
if (!prev_muted)
this[property].change_is_muted(true);
} else {
this[property].volume = volume;
if (prev_muted)
this[property].change_is_muted(false);
}
this[property].push_volume();
},
_notifyVolumeChange: function() {
global.cancel_theme_sound(VOLUME_NOTIFY_ID);
global.play_theme_sound(VOLUME_NOTIFY_ID, 'audio-volume-change');
},
_mutedChanged: function(object, param_spec, property) {
let muted = this[property].is_muted;
let slider = this[property+'Slider'];
slider.setValue(muted ? 0 : (this[property].volume / this._volumeMax));
if (property == '_output') {
if (muted)
this.emit('icon-changed', 'audio-volume-muted-symbolic');
else
this.emit('icon-changed', this._volumeToIcon(this._output.volume));
}
},
_volumeChanged: function(object, param_spec, property) {
this[property+'Slider'].setValue(this[property].volume / this._volumeMax);
if (property == '_output' && !this._output.is_muted)
this.emit('icon-changed', this._volumeToIcon(this._output.volume));
getIcon: function() {
return this._output.getIcon();
}
});
@ -252,13 +298,13 @@ const Indicator = new Lang.Class({
this._control = getMixerControl();
this._volumeMenu = new VolumeMenu(this._control);
this._volumeMenu.connect('icon-changed', Lang.bind(this, function(menu, icon) {
this._hasPulseAudio = (icon != null);
this._volumeMenu.connect('icon-changed', Lang.bind(this, function(menu) {
let icon = this._volumeMenu.getIcon();
this.actor.visible = (icon != null);
this.setIcon(icon);
this._syncVisibility();
}));
this._volumeMenu.connect('headphones-changed', Lang.bind(this, function() {
this._syncVisibility();
this._volumeMenu.connect('headphones-changed', Lang.bind(this, function(menu, value) {
this._headphoneIcon.visible = value;
}));
this._headphoneIcon = this.addIcon(new Gio.ThemedIcon({ name: 'headphones-symbolic' }));
@ -272,13 +318,7 @@ const Indicator = new Lang.Class({
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
},
_syncVisibility: function() {
this.actor.visible = this._hasPulseAudio;
this.mainIcon.visible = this._hasPulseAudio;
this._headphoneIcon.visible = this._hasPulseAudio && this._volumeMenu.hasHeadphones;
},
_onScrollEvent: function(actor, event) {
this._volumeMenu.scroll(event.get_scroll_direction());
this._volumeMenu.scroll(event);
}
});

View File

@ -46,7 +46,8 @@ const SwitcherPopup = new Lang.Class({
this._selectedIndex = 0;
this.actor = new Shell.GenericContainer({ style_class: 'switcher-popup',
reactive: true });
reactive: true,
visible: false });
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));
@ -637,5 +638,6 @@ function drawArrow(area, side) {
Clutter.cairo_set_source_color(cr, bodyColor);
cr.fill();
cr.$dispose();
}

View File

@ -115,7 +115,7 @@ const UnlockDialog = new Lang.Class({
_init: function(parentActor) {
this.parent({ shellReactive: true,
styleClass: 'login-dialog',
keybindingMode: Main.KeybindingMode.UNLOCK_SCREEN,
keybindingMode: Shell.KeyBindingMode.UNLOCK_SCREEN,
parentActor: parentActor
});
@ -198,18 +198,23 @@ const UnlockDialog = new Lang.Class({
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
let otherUserLabel = new St.Label({ text: _("Log in 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 });
let screenSaverSettings = new Gio.Settings({ schema: 'org.gnome.desktop.screensaver' });
if (screenSaverSettings.get_boolean('user-switch-enabled')) {
let otherUserLabel = new St.Label({ text: _("Log in 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 });
} else {
this._otherUserButton = null;
}
this._updateSensitivity(true);
@ -231,8 +236,10 @@ const UnlockDialog = new Lang.Class({
this._promptEntry.reactive = sensitive;
this._promptEntry.clutter_text.editable = sensitive;
this._updateOkButtonSensitivity(sensitive && this._promptEntry.text.length > 0);
this._otherUserButton.reactive = sensitive;
this._otherUserButton.can_focus = sensitive;
if (this._otherUserButton) {
this._otherUserButton.reactive = sensitive;
this._otherUserButton.can_focus = sensitive;
}
},
_updateOkButtonSensitivity: function(sensitive) {

View File

@ -22,12 +22,12 @@ const Util = imports.misc.util;
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
const PRIVACY_SCHEMA = 'org.gnome.desktop.privacy'
const DISABLE_USER_SWITCH_KEY = 'disable-user-switching';
const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen';
const DISABLE_LOG_OUT_KEY = 'disable-log-out';
const LOCK_ENABLED_KEY = 'lock-enabled';
const ALWAYS_SHOW_LOG_OUT_KEY = 'always-show-log-out';
const SHOW_FULL_NAME_KEY = 'show-full-name';
const SHOW_FULL_NAME_IN_TOP_BAR_KEY = 'show-full-name-in-top-bar';
const DIALOG_ICON_SIZE = 64;
@ -477,6 +477,7 @@ const UserMenuButton = new Lang.Class({
this._screenSaverSettings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
this._privacySettings = new Gio.Settings({ schema: PRIVACY_SCHEMA });
this._userManager = AccountsService.UserManager.get_default();
@ -553,10 +554,12 @@ const UserMenuButton = new Lang.Class({
Lang.bind(this, this._updateLogout));
this._lockdownSettings.connect('changed::' + DISABLE_LOCK_SCREEN_KEY,
Lang.bind(this, this._updateLockScreen));
this._screenSaverSettings.connect('changed::' + SHOW_FULL_NAME_KEY,
global.settings.connect('changed::' + ALWAYS_SHOW_LOG_OUT_KEY,
Lang.bind(this, this._updateLogout));
this._screenSaverSettings.connect('changed::' + SHOW_FULL_NAME_IN_TOP_BAR_KEY,
Lang.bind(this, this._updateUserName));
global.settings.connect('changed::' + SHOW_FULL_NAME_KEY,
Lang.bind(this, this._updateUserName));
this._privacySettings.connect('changed::' + SHOW_FULL_NAME_IN_TOP_BAR_KEY,
Lang.bind(this, this._updateUserName));
this._updateSwitchUser();
this._updateLogout();
this._updateLockScreen();
@ -581,6 +584,7 @@ const UserMenuButton = new Lang.Class({
Lang.bind(this, this._updateHaveShutdown));
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
Main.screenShield.connect('locked-changed', Lang.bind(this, this._updatePresenceIcon));
this._sessionUpdated();
},
@ -593,6 +597,7 @@ const UserMenuButton = new Lang.Class({
this.setSensitive(!Main.sessionMode.isLocked);
this._updatePresenceIcon();
this._updateUserName();
},
_onDestroy: function() {
@ -601,10 +606,10 @@ const UserMenuButton = new Lang.Class({
},
_updateUserName: function() {
let settings = global.settings;
let settings = this._privacySettings;
if (Main.sessionMode.isLocked)
settings = this._screenSaverSettings;
if (this._user.is_loaded && settings.get_boolean(SHOW_FULL_NAME_KEY))
if (this._user.is_loaded && settings.get_boolean(SHOW_FULL_NAME_IN_TOP_BAR_KEY))
this._name.set_text(this._user.get_real_name());
else
this._name.set_text("");
@ -699,6 +704,11 @@ const UserMenuButton = new Lang.Class({
this._iconBox.child = this._idleIcon;
else
this._iconBox.child = this._offlineIcon;
if (Main.sessionMode.isLocked)
this._iconBox.visible = Main.screenShield.locked;
else
this._iconBox.visible = true;
},
_setupAccounts: function() {
@ -819,7 +829,7 @@ const UserMenuButton = new Lang.Class({
_onMyAccountActivate: function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().lookup_setting('gnome-user-accounts-panel.desktop');
let app = Shell.AppSystem.get_default().lookup_app('gnome-user-accounts-panel.desktop');
app.activate();
},
@ -861,18 +871,8 @@ const UserMenuButton = new Lang.Class({
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
this._session.ShutdownRemote();
} else {
if (this._screenSaverSettings.get_boolean(LOCK_ENABLED_KEY)) {
let tmpId = Main.screenShield.connect('lock-screen-shown', Lang.bind(this, function() {
Main.screenShield.disconnect(tmpId);
this._loginManager.suspend();
}));
this.menu.close(BoxPointer.PopupAnimation.NONE);
Main.screenShield.lock(true);
} else {
this._loginManager.suspend();
}
this.menu.close(BoxPointer.PopupAnimation.NONE);
this._loginManager.suspend();
}
}
});

View File

@ -12,6 +12,7 @@ const St = imports.gi.St;
const AppDisplay = imports.ui.appDisplay;
const Main = imports.ui.main;
const Params = imports.misc.params;
const RemoteSearch = imports.ui.remoteSearch;
const Search = imports.ui.search;
const SearchDisplay = imports.ui.searchDisplay;
@ -39,17 +40,11 @@ const ViewSelector = new Lang.Class({
Name: 'ViewSelector',
_init : function(searchEntry, showAppsButton) {
this.actor = new St.BoxLayout({ name: 'viewSelector',
vertical: true });
this.actor = new Shell.Stack({ name: 'viewSelector' });
this._showAppsButton = showAppsButton;
this._showAppsButton.connect('notify::checked', Lang.bind(this, this._onShowAppsButtonToggled));
this._pageArea = new Shell.Stack();
this.actor.add(this._pageArea, { x_fill: true,
y_fill: true,
expand: true });
this._activePage = null;
this._searchActive = false;
@ -82,16 +77,17 @@ const ViewSelector = new Lang.Class({
this._capturedEventId = 0;
this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay();
this._workspacesPage = this._addPage(this._workspacesDisplay.actor, null,
this._workspacesPage = this._addPage(this._workspacesDisplay.actor,
_("Windows"), 'emblem-documents-symbolic');
this._appDisplay = new AppDisplay.AllAppDisplay();
this._appsPage = this._addPage(this._appDisplay.actor, null,
this._appsPage = this._addPage(this._appDisplay.actor,
_("Applications"), 'view-grid-symbolic');
this._searchResults = new SearchDisplay.SearchResults(this._searchSystem);
this._searchPage = this._addPage(this._searchResults.actor, this._entry,
_("Search"), 'edit-find-symbolic');
this._searchPage = this._addPage(this._searchResults.actor,
_("Search"), 'edit-find-symbolic',
{ a11yFocus: this._entry });
this._searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
this._searchSettings.connect('changed::disabled', Lang.bind(this, this._reloadRemoteProviders));
@ -118,6 +114,9 @@ const ViewSelector = new Lang.Class({
global.focus_manager.add_group(this._searchResults.actor);
Main.overview.connect('item-drag-begin',
Lang.bind(this, this._resetShowAppsButton));
this._stageKeyPressId = 0;
Main.overview.connect('showing', Lang.bind(this,
function () {
@ -137,8 +136,8 @@ const ViewSelector = new Lang.Class({
Main.wm.addKeybinding('toggle-application-view',
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
Meta.KeyBindingFlags.NONE,
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._toggleAppsPage));
},
@ -171,14 +170,16 @@ const ViewSelector = new Lang.Class({
this._workspacesDisplay.hide();
},
_addPage: function(actor, a11yFocus, name, a11yIcon) {
_addPage: function(actor, name, a11yIcon, params) {
params = Params.parse(params, { a11yFocus: null });
let page = new St.Bin({ child: actor,
x_align: St.Align.START,
y_align: St.Align.START,
x_fill: true,
y_fill: true });
if (a11yFocus)
Main.ctrlAltTabManager.addGroup(a11yFocus, name, a11yIcon);
if (params.a11yFocus)
Main.ctrlAltTabManager.addGroup(params.a11yFocus, name, a11yIcon);
else
Main.ctrlAltTabManager.addGroup(actor, name, a11yIcon,
{ proxy: this.actor,
@ -187,7 +188,7 @@ const ViewSelector = new Lang.Class({
this._a11yFocusPage(page);
})
});;
this._pageArea.add_actor(page);
this.actor.add_actor(page);
return page;
},
@ -208,16 +209,11 @@ const ViewSelector = new Lang.Class({
});
}
this.emit('before-page-change');
page.show();
Tweener.addTween(page,
{ opacity: 255,
time: 0.1,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
this.emit('after-page-change');
})
transition: 'easeOutQuad'
});
},
@ -336,37 +332,33 @@ const ViewSelector = new Lang.Class({
this._searchActive = this._entry.get_text() != '';
let startSearch = this._searchActive && !searchPreviouslyActive;
if (startSearch) {
if (startSearch)
this._searchResults.startingSearch();
}
if (this._searchActive) {
this._entry.set_secondary_icon(this._activeIcon);
if (this._iconClickedId == 0) {
if (this._iconClickedId == 0)
this._iconClickedId = this._entry.connect('secondary-icon-clicked',
Lang.bind(this, function() {
this.reset();
}));
}
} else {
if (this._iconClickedId > 0)
this._entry.disconnect(this._iconClickedId);
this._iconClickedId = 0;
Lang.bind(this, this.reset));
this._entry.set_secondary_icon(this._inactiveIcon);
this._searchCancelled();
if (this._searchTimeoutId == 0)
this._searchTimeoutId = Mainloop.timeout_add(150,
Lang.bind(this, this._doSearch));
} else {
if (this._iconClickedId > 0) {
this._entry.disconnect(this._iconClickedId);
this._iconClickedId = 0;
}
if (this._searchTimeoutId > 0) {
Mainloop.source_remove(this._searchTimeoutId);
this._searchTimeoutId = 0;
}
return;
}
if (this._searchTimeoutId > 0)
return;
this._searchTimeoutId = Mainloop.timeout_add(150, Lang.bind(this, this._doSearch));
this._entry.set_secondary_icon(this._inactiveIcon);
this._searchCancelled();
}
},
_onKeyPress: function(entry, event) {
@ -376,15 +368,6 @@ const ViewSelector = new Lang.Class({
this.reset();
return true;
}
} else if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) {
// We can't connect to 'activate' here because search providers
// might want to do something with the modifiers in activateDefault.
if (this._searchTimeoutId > 0) {
Mainloop.source_remove(this._searchTimeoutId);
this._doSearch();
}
this._searchResults.activateDefault();
return true;
} else if (this._searchActive) {
let arrowNext, nextDirection;
if (entry.get_text_direction() == Clutter.TextDirection.RTL) {
@ -409,6 +392,15 @@ const ViewSelector = new Lang.Class({
} else if (symbol == arrowNext && this._text.position == -1) {
this._searchResults.navigateFocus(nextDirection);
return true;
} else if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) {
// We can't connect to 'activate' here because search providers
// might want to do something with the modifiers in activateDefault.
if (this._searchTimeoutId > 0) {
Mainloop.source_remove(this._searchTimeoutId);
this._doSearch();
}
this._searchResults.activateDefault();
return true;
}
}
return false;
@ -459,10 +451,6 @@ const ViewSelector = new Lang.Class({
RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider));
},
getAppsActive: function() {
return this._showAppsButton.checked;
},
addSearchProvider: function(provider) {
if (!this._shouldUseSearchProvider(provider))
return;
@ -474,10 +462,6 @@ const ViewSelector = new Lang.Class({
removeSearchProvider: function(provider) {
this._searchSystem.unregisterProvider(provider);
this._searchResults.destroyProviderMeta(provider);
},
getSearchActive: function() {
return this._searchActive;
}
});
Signals.addSignalMethods(ViewSelector.prototype);

View File

@ -105,74 +105,74 @@ const WindowManager = new Lang.Class({
this._workspaceSwitcherPopup = null;
this.setCustomKeybindingHandler('switch-to-workspace-left',
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-right',
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-up',
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-down',
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-left',
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-right',
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-up',
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-down',
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-applications',
Main.KeybindingMode.NORMAL,
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._startAppSwitcher));
this.setCustomKeybindingHandler('switch-group',
Main.KeybindingMode.NORMAL,
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._startAppSwitcher));
this.setCustomKeybindingHandler('switch-applications-backward',
Main.KeybindingMode.NORMAL,
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._startAppSwitcher));
this.setCustomKeybindingHandler('switch-group-backward',
Main.KeybindingMode.NORMAL,
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._startAppSwitcher));
this.setCustomKeybindingHandler('switch-windows',
Main.KeybindingMode.NORMAL,
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._startWindowSwitcher));
this.setCustomKeybindingHandler('switch-windows-backward',
Main.KeybindingMode.NORMAL,
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._startWindowSwitcher));
this.setCustomKeybindingHandler('switch-panels',
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW |
Main.KeybindingMode.LOCK_SCREEN |
Main.KeybindingMode.UNLOCK_SCREEN |
Main.KeybindingMode.LOGIN_SCREEN,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW |
Shell.KeyBindingMode.LOCK_SCREEN |
Shell.KeyBindingMode.UNLOCK_SCREEN |
Shell.KeyBindingMode.LOGIN_SCREEN,
Lang.bind(this, this._startA11ySwitcher));
this.setCustomKeybindingHandler('switch-panels-backward',
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW |
Main.KeybindingMode.LOCK_SCREEN |
Main.KeybindingMode.UNLOCK_SCREEN |
Main.KeybindingMode.LOGIN_SCREEN,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW |
Shell.KeyBindingMode.LOCK_SCREEN |
Shell.KeyBindingMode.UNLOCK_SCREEN |
Shell.KeyBindingMode.LOGIN_SCREEN,
Lang.bind(this, this._startA11ySwitcher));
this.addKeybinding('open-application-menu',
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
Meta.KeyBindingFlags.NONE,
Main.KeybindingMode.NORMAL,
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._openAppMenu));
Main.overview.connect('showing', Lang.bind(this, function() {
@ -191,13 +191,15 @@ const WindowManager = new Lang.Class({
},
addKeybinding: function(name, settings, flags, modes, handler) {
if (global.display.add_keybinding(name, settings, flags, handler))
let action = global.display.add_keybinding(name, settings, flags, handler);
if (action != Meta.KeyBindingAction.NONE)
this.allowKeybinding(name, modes);
return action;
},
removeKeybinding: function(name) {
if (global.display.remove_keybinding(name))
this.allowKeybinding(name, Main.KeybindingMode.NONE);
this.allowKeybinding(name, Shell.KeyBindingMode.NONE);
},
allowKeybinding: function(name, modes) {
@ -219,7 +221,9 @@ const WindowManager = new Lang.Class({
_shouldAnimateActor: function(actor) {
if (!this._shouldAnimate())
return false;
return actor.meta_window.get_window_type() == Meta.WindowType.NORMAL;
let windowType = actor.meta_window.get_window_type();
return windowType == Meta.WindowType.NORMAL ||
windowType == Meta.WindowType.MODAL_DIALOG;
},
_removeEffect : function(list, actor) {
@ -238,23 +242,32 @@ const WindowManager = new Lang.Class({
}
actor.set_scale(1.0, 1.0);
actor.move_anchor_point_from_gravity(Clutter.Gravity.CENTER);
/* scale window down to 0x0.
* maybe TODO: get icon geometry passed through and move the window towards it?
*/
this._minimizing.push(actor);
let primary = Main.layoutManager.primaryMonitor;
let xDest = primary.x;
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
xDest += primary.width;
let xDest, yDest, xScale, yScale;
let [success, geom] = actor.meta_window.get_icon_geometry();
if (success) {
xDest = geom.x;
yDest = geom.y;
xScale = geom.width / actor.width;
yScale = geom.height / actor.height;
} else {
/* scale window down to 0x0. */
let monitor = Main.layoutManager.findMonitorForWindow(actor.meta_window);
xDest = monitor.x;
yDest = monitor.y;
xScale = 0.0;
yScale = 0.0;
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
xDest += monitor.width;
}
Tweener.addTween(actor,
{ scale_x: 0.0,
scale_y: 0.0,
{ scale_x: xScale,
scale_y: yScale,
x: xDest,
y: 0,
y: yDest,
time: WINDOW_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: this._minimizeWindowDone,
@ -369,57 +382,56 @@ const WindowManager = new Lang.Class({
actor._windowType = type;
}));
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.scale_gravity = Clutter.Gravity.CENTER;
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]
});
return;
}
shellwm.completed_map(actor);
return;
}
if (!this._shouldAnimateActor(actor)) {
shellwm.completed_map(actor);
return;
}
actor.opacity = 0;
actor.show();
if (actor.meta_window.is_attached_dialog()) {
/* Scale the window from the center of the parent */
this._checkDimming(actor.get_meta_window().get_transient_for());
actor.set_scale(1.0, 0.0);
actor.scale_gravity = Clutter.Gravity.CENTER;
actor.show();
this._mapping.push(actor);
/* Fade window in */
this._mapping.push(actor);
Tweener.addTween(actor,
{ opacity: 255,
time: WINDOW_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: this._mapWindowDone,
onCompleteScope: this,
onCompleteParams: [shellwm, actor],
onOverwrite: this._mapWindowOverwrite,
onOverwriteScope: this,
onOverwriteParams: [shellwm, 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]
});
} else {
/* Fade window in */
actor.opacity = 0;
actor.show();
this._mapping.push(actor);
Tweener.addTween(actor,
{ opacity: 255,
time: WINDOW_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: this._mapWindowDone,
onCompleteScope: this,
onCompleteParams: [shellwm, actor],
onOverwrite: this._mapWindowOverwrite,
onOverwriteScope: this,
onOverwriteParams: [shellwm, actor]
});
}
},
_mapWindowDone : function(shellwm, actor) {
if (this._removeEffect(this._mapping, actor)) {
Tweener.removeTweens(actor);
actor.opacity = 255;
actor.scale_y = 1;
shellwm.completed_map(actor);
}
},
@ -441,18 +453,21 @@ const WindowManager = new Lang.Class({
return win != window;
});
}
if (!this._shouldAnimateActor(actor)) {
shellwm.completed_destroy(actor);
return;
}
this._destroying.push(actor);
if (window.is_attached_dialog()) {
let parent = window.get_transient_for();
this._checkDimming(parent, window);
if (!this._shouldAnimate()) {
shellwm.completed_destroy(actor);
return;
}
actor.set_scale(1.0, 1.0);
actor.scale_gravity = Clutter.Gravity.CENTER;
actor.show();
this._destroying.push(actor);
actor._parentDestroyId = parent.connect('unmanaged', Lang.bind(this, function () {
Tweener.removeTweens(actor);
@ -487,14 +502,14 @@ const WindowManager = new Lang.Class({
},
_filterKeybinding: function(shellwm, binding) {
if (Main.keybindingMode == Main.KeybindingMode.NONE)
if (Main.keybindingMode == Shell.KeyBindingMode.NONE)
return true;
// There's little sense in implementing a keybinding in mutter and
// not having it work in NORMAL mode; handle this case generically
// so we don't have to explicitly allow all builtin keybindings in
// NORMAL mode.
if (Main.keybindingMode == Main.KeybindingMode.NORMAL &&
if (Main.keybindingMode == Shell.KeyBindingMode.NORMAL &&
binding.is_builtin())
return false;

View File

@ -1,7 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
@ -30,9 +29,6 @@ const CLOSE_BUTTON_FADE_TIME = 0.1;
const DRAGGING_WINDOW_OPACITY = 100;
const BUTTON_LAYOUT_SCHEMA = 'org.gnome.shell.overrides';
const BUTTON_LAYOUT_KEY = 'button-layout';
// When calculating a layout, we calculate the scale of windows and the percent
// of the available area the new layout uses. If the values for the new layout,
// when weighted with the values as below, are worse than the previous layout's,
@ -124,6 +120,8 @@ const WindowClone = new Lang.Class({
this.actor._delegate = this;
this._slot = [0, 0, 0, 0];
this._dragSlot = [0, 0, 0, 0];
this._stackAbove = null;
this._sizeChangedId = this.realWindow.connect('size-changed',
@ -159,22 +157,15 @@ const WindowClone = new Lang.Class({
this._selected = false;
},
set slot(slot) {
this._slot = slot;
},
get slot() {
let x, y, w, h;
if (this.inDrag) {
x = this.dragOrigX;
y = this.dragOrigY;
w = this.actor.width * this.dragOrigScale;
h = this.actor.height * this.dragOrigScale;
} else {
x = this.actor.x;
y = this.actor.y;
w = this.actor.width * this.actor.scale_x;
h = this.actor.height * this.actor.scale_y;
}
return [x, y, w, h];
if (this.inDrag)
return this._dragSlot;
else
return this._slot;
},
setStackAbove: function (actor) {
@ -265,23 +256,37 @@ const WindowClone = new Lang.Class({
_onScroll : function (actor, event) {
let direction = event.get_scroll_direction();
if (direction == Clutter.ScrollDirection.UP) {
let delta;
if (event.is_pointer_emulated())
return;
if (direction == Clutter.ScrollDirection.DOWN) {
delta = -SCROLL_SCALE_AMOUNT;
} else if (direction == Clutter.ScrollDirection.UP) {
delta = +SCROLL_SCALE_AMOUNT;
} else if (direction == Clutter.ScrollDirection.SMOOTH) {
let [dx, dy] = event.get_scroll_delta();
delta = -dy * 10;
}
if (delta > 0) {
if (this._zoomStep == undefined)
this._zoomStart();
if (this._zoomStep < 100) {
this._zoomStep += SCROLL_SCALE_AMOUNT;
this._zoomStep += delta;
this._zoomStep = Math.min(100, this._zoomStep);
this._zoomUpdate();
}
} else if (direction == Clutter.ScrollDirection.DOWN) {
} else if (delta < 0) {
if (this._zoomStep > 0) {
this._zoomStep -= SCROLL_SCALE_AMOUNT;
this._zoomStep += delta;
this._zoomStep = Math.max(0, this._zoomStep);
this._zoomUpdate();
}
if (this._zoomStep <= 0.0)
this._zoomEnd();
}
},
_zoomUpdate : function () {
@ -392,6 +397,7 @@ const WindowClone = new Lang.Class({
if (this._zooming)
this._zoomEnd();
this._dragSlot = this._slot;
[this.dragOrigX, this.dragOrigY] = this.actor.get_position();
this.dragOrigScale = this.actor.scale_x;
this.inDrag = true;
@ -456,11 +462,7 @@ const WindowOverlay = new Lang.Class({
this._updateCaptionId = metaWindow.connect('notify::title',
Lang.bind(this, function(w) {
this.title.text = w.title;
// we need this for the next call to get_preferred_width
// to return useful results
this.title.set_size(-1, -1);
this._repositionSelf();
this.relayout(false);
}));
let button = new St.Button({ style_class: 'window-close' });
@ -535,35 +537,14 @@ const WindowOverlay = new Lang.Class({
return [this.borderSize, this.borderSize];
},
_repositionSelf: function() {
relayout: function(animate) {
let [cloneX, cloneY, cloneWidth, cloneHeight] = this._windowClone.slot;
this.updatePositions(cloneX, cloneY, cloneWidth, cloneHeight, false);
},
/**
* @cloneX: x position of windowClone
* @cloneY: y position of windowClone
* @cloneWidth: width of windowClone
* @cloneHeight height of windowClone
*/
// These parameters are not the values retrieved with
// get_transformed_position() and get_transformed_size(),
// as windowClone might be moving.
// See Workspace._showWindowOverlay
updatePositions: function(cloneX, cloneY, cloneWidth, cloneHeight, animate) {
let button = this.closeButton;
let title = this.title;
let settings = new Gio.Settings({ schema: BUTTON_LAYOUT_SCHEMA });
let layout = settings.get_string(BUTTON_LAYOUT_KEY);
let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL;
let split = layout.split(":");
let side;
if (split[0].indexOf("close") > -1)
side = rtl ? St.Side.RIGHT : St.Side.LEFT;
else
side = rtl ? St.Side.LEFT : St.Side.RIGHT;
let layout = Meta.prefs_get_button_layout();
let side = layout.left_buttons.indexOf(Meta.ButtonFunction.CLOSE) > -1 ? St.Side.LEFT : St.Side.RIGHT;
let buttonX;
let buttonY = cloneY - (button.height - button._overlap);
@ -577,8 +558,16 @@ const WindowOverlay = new Lang.Class({
else
button.set_position(Math.floor(buttonX), Math.floor(buttonY));
// Clutter.Actor.get_preferred_width() will return the fixed width if one
// is set, so we need to reset the width by calling set_width(-1), to forward
// the call down to StLabel.
// We also need to save and restore the current width, otherwise the animation
// starts from the wrong point.
let prevTitleWidth = title.width;
title.set_width(-1);
let [titleMinWidth, titleNatWidth] = title.get_preferred_width(-1);
let titleWidth = Math.max(titleMinWidth, Math.min(titleNatWidth, cloneWidth));
title.width = prevTitleWidth;
let titleX = cloneX + (cloneWidth - titleWidth) / 2;
let titleY = cloneY + cloneHeight + title._spacing;
@ -757,11 +746,10 @@ const LayoutStrategy = new Lang.Class({
Name: 'LayoutStrategy',
Abstract: true,
_init: function(monitor, rowSpacing, columnSpacing, bottomPadding) {
_init: function(monitor, rowSpacing, columnSpacing) {
this._monitor = monitor;
this._rowSpacing = rowSpacing;
this._columnSpacing = columnSpacing;
this._bottomPadding = bottomPadding;
},
_newRow: function() {
@ -786,24 +774,23 @@ const LayoutStrategy = new Lang.Class({
windows: [] };
},
// Compute the size and fancy scale for @window using the
// Computes and returns a fancy scale for @window using the
// base scale, @scale.
//
// Returns a list structure: [ scaledWidth, scaledHeight, fancyScale ]
// where scaledWidth and scaledHeight are the window's
// width and height, scaled by fancyScale for convenience.
_computeWindowSizeAndScale: function(window, scale) {
let width = window.actor.width;
let height = window.actor.height;
let ratio;
_computeWindowScale: function(window, scale) {
// Since we align windows next to each other, the height of the
// thumbnails is much more important to preserve than the width of
// them, so two windows with equal height, but maybe differering
// widths line up.
let ratio = window.actor.height / this._monitor.height;
if (width > height)
ratio = width / this._monitor.width;
else
ratio = height / this._monitor.height;
// The purpose of this manipulation here is to prevent windows
// from getting too small. For something like a calculator window,
// we need to bump up the size just a bit to make sure it looks
// good. We'll use a multiplier of 1.5 for this.
let fancyScale = (2 / (1 + ratio)) * scale;
return [width * fancyScale, height * fancyScale, fancyScale];
// Map from [0, 1] to [1.5, 1]
let fancyScale = _interpolate(1.5, 1, ratio) * scale;
return fancyScale;
},
// Compute the size of each row, by assigning to the properties
@ -841,7 +828,7 @@ const LayoutStrategy = new Lang.Class({
let area = layout.area;
let hspacing = (layout.maxColumns - 1) * this._columnSpacing;
let vspacing = (layout.numRows - 1) * this._rowSpacing + this._bottomPadding;
let vspacing = (layout.numRows - 1) * this._rowSpacing;
let spacedWidth = area.width - hspacing;
let spacedHeight = area.height - vspacing;
@ -875,7 +862,7 @@ const LayoutStrategy = new Lang.Class({
y += row.height + this._rowSpacing;
}
let height = y - this._rowSpacing + this._bottomPadding;
let height = y - this._rowSpacing;
let baseY = (area.height - height) / 2;
for (let i = 0; i < rows.length; i++) {
@ -885,7 +872,10 @@ const LayoutStrategy = new Lang.Class({
for (let j = 0; j < row.windows.length; j++) {
let window = row.windows[j];
let [width, height, s] = this._computeWindowSizeAndScale(window, scale);
let s = this._computeWindowScale(window, scale);
s = Math.min(s, WINDOW_CLONE_MAXIMUM_SCALE);
let width = window.actor.width * s;
let height = window.actor.height * s;
let y = row.y + row.height - height;
let x = baseX;
@ -946,7 +936,9 @@ const UnalignedLayoutStrategy = new Lang.Class({
for (; windowIdx < windows.length; windowIdx++) {
let window = windows[windowIdx];
let [width, height] = this._computeWindowSizeAndScale(window, 1);
let s = this._computeWindowScale(window, 1);
let width = window.actor.width * s;
let height = window.actor.height * s;
row.fullHeight = Math.max(row.fullHeight, height);
// either new width is < idealWidth or new width is nearer from idealWidth then oldWidth
@ -1012,9 +1004,9 @@ const GridLayoutStrategy = new Lang.Class({
let window = windows[windowIdx];
row.windows.push(window);
let [width, height] = this._computeWindowSizeAndScale(window, 1);
maxWindowWidth = Math.max(maxWindowWidth, width);
maxWindowHeight = Math.max(maxWindowHeight, height);
let s = this._computeWindowScale(window, 1);
maxWindowWidth = Math.max(maxWindowWidth, window.actor.width * s);
maxWindowHeight = Math.max(maxWindowHeight, window.actor.height * s);
}
}
@ -1049,6 +1041,8 @@ const Workspace = new Lang.Class({
this._windowOverlaysGroup.set_size(0, 0);
this.actor = new St.Widget({ style_class: 'window-picker' });
if (monitorIndex != Main.layoutManager.primaryIndex)
this.actor.add_style_class_name('external-monitor');
this.actor.set_size(0, 0);
this._dropRect = new Clutter.Rectangle({ opacity: 0 });
@ -1184,8 +1178,8 @@ const Workspace = new Lang.Class({
let slot = slots[i];
let clone = clones[i];
let metaWindow = clone.metaWindow;
let mainIndex = this._lookupIndex(metaWindow);
let overlay = this._windowOverlays[mainIndex];
let overlay = clone.overlay;
clone.slotId = i;
// Positioning a window currently being dragged must be avoided;
// we'll just leave a blank spot in the layout for it.
@ -1193,6 +1187,7 @@ const Workspace = new Lang.Class({
continue;
let [x, y, scale] = slot;
clone.slot = [x, y, clone.actor.width * scale, clone.actor.height * scale];
if (overlay && initialPositioning)
overlay.hide();
@ -1212,7 +1207,7 @@ const Workspace = new Lang.Class({
// Make the window slightly transparent to indicate it's hidden
Tweener.addTween(clone.actor,
{ opacity: 255,
{ opacity: 128,
time: Overview.ANIMATION_TIME,
transition: 'easeInQuad'
});
@ -1224,7 +1219,7 @@ const Workspace = new Lang.Class({
Tweener.removeTweens(clone.actor);
clone.actor.set_position(x, y);
clone.actor.set_scale(scale, scale);
this._updateWindowOverlayPositions(clone, overlay, x, y, scale, false);
clone.overlay.relayout(false);
this._showWindowOverlay(clone, overlay, isOnCurrentWorkspace);
}
}
@ -1259,15 +1254,7 @@ const Workspace = new Lang.Class({
})
});
this._updateWindowOverlayPositions(clone, overlay, x, y, scale, true);
},
_updateWindowOverlayPositions: function(clone, overlay, x, y, scale, animate) {
if (!overlay)
return;
let [cloneWidth, cloneHeight] = clone.actor.get_size();
overlay.updatePositions(x, y, cloneWidth * scale, cloneHeight * scale, animate);
clone.overlay.relayout(true);
},
_showWindowOverlay: function(clone, overlay, fade) {
@ -1402,9 +1389,10 @@ const Workspace = new Lang.Class({
let scale = win._overviewHint.scale;
delete win._overviewHint;
clone.slot = [x, y, clone.actor.width * scale, clone.actor.height * scale];
clone.actor.set_position (x, y);
clone.actor.set_scale (scale, scale);
this._updateWindowOverlayPositions(clone, overlay, x, y, scale, false);
clone.overlay.relayout(false);
} else {
// Position new windows at the top corner of the workspace rather
// than where they were placed for real to avoid the window
@ -1569,6 +1557,7 @@ const Workspace = new Lang.Class({
_addWindowClone : function(win) {
let clone = new WindowClone(win, this);
let overlay = new WindowOverlay(clone, this._windowOverlaysGroup);
clone.overlay = overlay;
clone.connect('selected',
Lang.bind(this, this._onCloneSelected));
@ -1640,7 +1629,7 @@ const Workspace = new Lang.Class({
}
},
_computeLayout: function(windows, area, rowSpacing, columnSpacing, bottomPadding) {
_computeLayout: function(windows, area, rowSpacing, columnSpacing) {
// We look for the largest scale that allows us to fit the
// largest row/tallest column on the workspace.
@ -1656,7 +1645,7 @@ const Workspace = new Lang.Class({
break;
let strategyClass = numRows > 2 ? GridLayoutStrategy : UnalignedLayoutStrategy;
let strategy = new strategyClass(this._monitor, rowSpacing, columnSpacing, bottomPadding);
let strategy = new strategyClass(this._monitor, rowSpacing, columnSpacing);
let layout = { area: area, strategy: strategy, numRows: numRows, numColumns: numColumns };
strategy.computeLayout(windows, layout);
@ -1688,6 +1677,12 @@ const Workspace = new Lang.Class({
// Window grid spacing
let columnSpacing = node.get_length('-horizontal-spacing');
let rowSpacing = node.get_length('-vertical-spacing');
let padding = {
left: node.get_padding(St.Side.LEFT),
top: node.get_padding(St.Side.TOP),
bottom: node.get_padding(St.Side.BOTTOM),
right: node.get_padding(St.Side.RIGHT),
};
if (!totalWindows)
return [];
@ -1702,19 +1697,25 @@ const Workspace = new Lang.Class({
[leftBorder, rightBorder] = overlay.chromeWidths();
} else {
[closeButtonHeight, captionHeight] = [0, 0];
[leftBorder, rightBorder] = [0, 0];
}
rowSpacing += captionHeight;
columnSpacing += rightBorder;
columnSpacing += (rightBorder + leftBorder) / 2;
padding.top += closeButtonHeight;
padding.bottom += captionHeight;
padding.left += leftBorder;
padding.right += rightBorder;
let area = { x: this._x, y: this._y, width: this._width, height: this._height };
area.y += closeButtonHeight;
area.height -= closeButtonHeight;
area.x += leftBorder;
area.width -= leftBorder;
let area = {
x: this._x + padding.left,
y: this._y + padding.top,
width: this._width - padding.left - padding.right,
height: this._height - padding.top - padding.bottom,
};
if (!this._currentLayout)
this._currentLayout = this._computeLayout(windows, area, rowSpacing, columnSpacing, captionHeight);
this._currentLayout = this._computeLayout(windows, area, rowSpacing, columnSpacing);
let layout = this._currentLayout;
let strategy = layout.strategy;

View File

@ -496,8 +496,6 @@ const ThumbnailsBox = new Lang.Class({
_init: function() {
this.actor = new Shell.GenericContainer({ reactive: true,
style_class: 'workspace-thumbnails',
can_focus: true,
track_hover: true,
request_mode: Clutter.RequestMode.WIDTH_FOR_HEIGHT });
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
@ -526,7 +524,6 @@ const ThumbnailsBox = new Lang.Class({
this._indicator = indicator;
this.actor.add_actor(indicator);
this._inDrag = false;
this._dropWorkspace = -1;
this._dropPlaceholderPos = -1;
this._dropPlaceholder = new St.Bin({ style_class: 'placeholder' });
@ -547,13 +544,7 @@ const ThumbnailsBox = new Lang.Class({
this.actor.connect('button-press-event', function() { return true; });
this.actor.connect('button-release-event', Lang.bind(this, this._onButtonRelease));
this.actor.connect('scroll-event',
Lang.bind(this, this._onScrollEvent));
this.actor.connect('key-release-event',
Lang.bind(this, this._onKeyRelease));
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._updateTranslation));
this.actor.connect('notify::hover', Lang.bind(this, this._updateTranslation));
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
Main.overview.connect('showing',
Lang.bind(this, this._createThumbnails));
@ -578,40 +569,6 @@ const ThumbnailsBox = new Lang.Class({
Lang.bind(this, this._updateSwitcherVisibility));
},
_getInitialTranslation: function() {
// Always show the pager when hover, during a drag, or if workspaces are
// actually used, e.g. there are windows on more than one
let alwaysZoomOut = this.actor.hover || this._inDrag || global.screen.n_workspaces > 2;
if (!alwaysZoomOut) {
let monitors = Main.layoutManager.monitors;
let primary = Main.layoutManager.primaryMonitor;
/* Look for any monitor to the right of the primary, if there is
* one, we always keep zoom out, otherwise its hard to reach
* the thumbnail area without passing into the next monitor. */
for (let i = 0; i < monitors.length; i++) {
if (monitors[i].x >= primary.x + primary.width) {
alwaysZoomOut = true;
break;
}
}
}
if (alwaysZoomOut)
return 0;
let visibleWidth = this.actor.get_theme_node().get_length('visible-width');
let rtl = (this.actor.get_text_direction() == Clutter.TextDirection.RTL);
return rtl ? (visibleWidth - this.actor.width) : (this.actor.width - visibleWidth);
},
_updateTranslation: function() {
Tweener.addTween(this, { slideX: this._getInitialTranslation(),
time: SLIDE_ANIMATION_TIME,
transition: 'easeOutQuad' });
},
_updateSwitcherVisibility: function() {
this.actor.visible =
this._settings.get_boolean('dynamic-workspaces') ||
@ -635,9 +592,6 @@ const ThumbnailsBox = new Lang.Class({
},
_onDragBegin: function() {
this._inDrag = true;
this._updateTranslation();
this._dragCancelled = false;
this._dragMonitor = {
dragMotion: Lang.bind(this, this._onDragMotion)
@ -660,8 +614,6 @@ const ThumbnailsBox = new Lang.Class({
_endDrag: function() {
this._clearDragPlaceholder();
DND.removeDragMonitor(this._dragMonitor);
this._inDrag = false;
this._updateTranslation();
},
_onDragMotion: function(dragEvent) {
@ -794,7 +746,7 @@ const ThumbnailsBox = new Lang.Class({
global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._workspacesChanged));
this._syncStackingId =
Main.overview.connect('sync-window-stacking',
Main.overview.connect('windows-restacked',
Lang.bind(this, this._syncStacking));
this._targetScale = 0;
@ -818,11 +770,6 @@ const ThumbnailsBox = new Lang.Class({
this.addThumbnails(0, global.screen.n_workspaces);
// reset any translation and make sure the actor is visible when
// entering the overview
this.slideX = this._getInitialTranslation();
this.actor.show();
this._updateSwitcherVisibility();
},
@ -835,6 +782,7 @@ const ThumbnailsBox = new Lang.Class({
global.screen.disconnect(this._nWorkspacesNotifyId);
this._nWorkspacesNotifyId = 0;
}
if (this._syncStackingId > 0) {
Main.overview.disconnect(this._syncStackingId);
this._syncStackingId = 0;
@ -845,49 +793,6 @@ const ThumbnailsBox = new Lang.Class({
this._thumbnails = [];
},
_computeTranslation: function() {
let rtl = (this.actor.get_text_direction() == Clutter.TextDirection.RTL);
if (rtl)
return - this.actor.width;
else
return this.actor.width;
},
get slideX() {
return this._slideX;
},
set slideX(value) {
this._slideX = value;
this.actor.translation_x = this._slideX;
if (this._slideX > 0) {
let rect = new Clutter.Rect();
rect.size.width = this._background.width - this._slideX;
rect.size.height = this._background.height;
this.actor.clip_rect = rect;
} else {
this.actor.clip_rect = null;
}
},
show: function() {
this.actor.show();
this._updateTranslation();
},
hide: function() {
let hiddenX = this._computeTranslation();
Tweener.addTween(this, { slideX: hiddenX,
transition: 'easeOutQuad',
time: SLIDE_ANIMATION_TIME,
onComplete: Lang.bind(this, function () {
this.actor.hide();
})
});
},
_workspacesChanged: function() {
let oldNumWorkspaces = this._thumbnails.length;
let newNumWorkspaces = global.screen.n_workspaces;
@ -957,7 +862,7 @@ const ThumbnailsBox = new Lang.Class({
this._queueUpdateStates();
},
_syncStacking: function(actor, stackIndices) {
_syncStacking: function(overview, stackIndices) {
for (let i = 0; i < this._thumbnails.length; i++)
this._thumbnails[i].syncStacking(stackIndices);
},
@ -1323,19 +1228,5 @@ const ThumbnailsBox = new Lang.Class({
Main.wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
break;
}
},
_onKeyRelease: function (actor, event) {
switch (event.get_key_symbol()) {
case Clutter.KEY_Up:
Main.wm.actionMoveWorkspace(Meta.MotionDirection.UP);
break;
case Clutter.KEY_Down:
Main.wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
break;
case Clutter.KEY_Return:
Main.overview.toggle();
break;
}
}
});

View File

@ -23,6 +23,8 @@ const MAX_WORKSPACES = 16;
const OVERRIDE_SCHEMA = 'org.gnome.shell.overrides';
const CONTROLS_POP_IN_TIME = 0.1;
const WorkspacesView = new Lang.Class({
Name: 'WorkspacesView',
@ -67,7 +69,7 @@ const WorkspacesView = new Lang.Class({
// Add workspace actors
for (let w = 0; w < global.screen.n_workspaces; w++)
this._workspaces[w].actor.reparent(this.actor);
this.actor.add_actor(this._workspaces[w].actor);
this._workspaces[activeWorkspaceIndex].actor.raise_top();
this._extraWorkspaces = [];
@ -127,11 +129,10 @@ const WorkspacesView = new Lang.Class({
continue;
let ws = new Workspace.Workspace(null, i);
let overviewSpacing = Main.overview._spacing;
ws.setGeometry(monitors[i].x + overviewSpacing/2,
monitors[i].y + overviewSpacing/2,
monitors[i].width - overviewSpacing,
monitors[i].height - overviewSpacing);
ws.setGeometry(monitors[i].x,
monitors[i].y,
monitors[i].width,
monitors[i].height);
global.overlay_group.add_actor(ws.actor);
this._extraWorkspaces.push(ws);
}
@ -436,11 +437,24 @@ const WorkspacesDisplay = new Lang.Class({
Name: 'WorkspacesDisplay',
_init: function() {
this.actor = new Shell.GenericContainer();
this.actor.connect('allocate', Lang.bind(this, this._updateWorkspacesGeometry));
this.actor = new Shell.GenericContainer({ style_class: 'workspaces-display' });
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('parent-set', Lang.bind(this, this._parentSet));
this.actor.set_clip_to_allocation(true);
this._spacing = 0;
this.actor.connect('style-changed', Lang.bind(this,
function() {
let node = this.actor.get_theme_node();
let spacing = node.get_length('spacing');
if (spacing != this._spacing) {
this._spacing = spacing;
this._updateWorkspacesGeometry();
}
}));
let clickAction = new Clutter.ClickAction()
clickAction.connect('clicked', Lang.bind(this, function(action) {
// Only switch to the workspace when there's no application
@ -468,9 +482,25 @@ const WorkspacesDisplay = new Lang.Class({
Main.overview.addAction(panAction);
this.actor.bind_property('mapped', panAction, 'enabled', GObject.BindingFlags.SYNC_CREATE);
let controls = new St.Bin({ style_class: 'workspace-controls',
request_mode: Clutter.RequestMode.WIDTH_FOR_HEIGHT,
y_align: St.Align.START,
y_fill: true });
this._controls = controls;
this.actor.add_actor(controls);
controls.reactive = true;
controls.track_hover = true;
controls.connect('notify::hover',
Lang.bind(this, this._onControlsHoverChanged));
this._primaryIndex = Main.layoutManager.primaryIndex;
this._workspacesViews = null;
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
controls.add_actor(this._thumbnailsBox.actor);
this._workspacesViews = [];
this._workspaces = [];
this._primaryScrollAdjustment = null;
this._settings = new Gio.Settings({ schema: OVERRIDE_SCHEMA });
@ -482,6 +512,26 @@ const WorkspacesDisplay = new Lang.Class({
this._inDrag = false;
this._cancelledDrag = false;
this._controlsInitiallyHovered = false;
this._alwaysZoomOut = false;
this._zoomOut = false;
this._zoomFraction = 0;
this._updateAlwaysZoom();
// If we stop hiding the overview on layout changes, we will need to
// update the _workspacesViews here
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._updateAlwaysZoom));
Main.xdndHandler.connect('drag-begin', Lang.bind(this, function(){
this._alwaysZoomOut = true;
}));
Main.xdndHandler.connect('drag-end', Lang.bind(this, function(){
this._alwaysZoomOut = false;
this._updateAlwaysZoom();
}));
global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._workspacesChanged));
@ -506,10 +556,29 @@ const WorkspacesDisplay = new Lang.Class({
},
show: function() {
if(!this._alwaysZoomOut) {
let [mouseX, mouseY] = global.get_pointer();
let [x, y] = this._controls.get_transformed_position();
let [width, height] = this._controls.get_transformed_size();
let visibleWidth = this._controls.get_theme_node().get_length('visible-width');
let rtl = (Clutter.get_default_text_direction () == Clutter.TextDirection.RTL);
if(rtl)
x = x + width - visibleWidth;
if(mouseX > x - 0.5 && mouseX < x + visibleWidth + 0.5 &&
mouseY > y - 0.5 && mouseY < y + height + 0.5)
this._controlsInitiallyHovered = true;
}
this._zoomOut = this._alwaysZoomOut;
this._zoomFraction = this._alwaysZoomOut ? 1 : 0;
this._updateZoom();
this._controls.show();
this._updateWorkspacesViews();
this._restackedNotifyId =
Main.overview.connect('sync-window-stacking',
Main.overview.connect('windows-restacked',
Lang.bind(this, this._onRestacked));
if (this._itemDragBeginId == 0)
@ -539,6 +608,11 @@ const WorkspacesDisplay = new Lang.Class({
},
hide: function() {
this._controls.hide();
if (!this._alwaysZoomOut)
this.zoomFraction = 0;
if (this._restackedNotifyId > 0){
Main.overview.disconnect(this._restackedNotifyId);
this._restackedNotifyId = 0;
@ -570,7 +644,7 @@ const WorkspacesDisplay = new Lang.Class({
for (let i = 0; i < this._workspacesViews.length; i++)
this._workspacesViews[i].destroy();
this._workspacesViews = null;
this._workspacesViews = [];
for (let i = 0; i < this._workspaces.length; i++)
for (let w = 0; w < this._workspaces[i].length; w++) {
@ -589,14 +663,12 @@ const WorkspacesDisplay = new Lang.Class({
},
_updateWorkspacesViews: function() {
if (this._workspacesViews)
for (let i = 0; i < this._workspacesViews.length; i++)
this._workspacesViews[i].destroy();
for (let i = 0; i < this._workspacesViews.length; i++)
this._workspacesViews[i].destroy();
if (this._workspaces)
for (let i = 0; i < this._workspaces.length; i++)
for (let w = 0; w < this._workspaces[i].length; w++)
this._workspaces[i][w].destroy();
for (let i = 0; i < this._workspaces.length; i++)
for (let w = 0; w < this._workspaces[i].length; w++)
this._workspaces[i][w].destroy();
this._workspacesViews = [];
this._workspaces = [];
@ -644,7 +716,7 @@ const WorkspacesDisplay = new Lang.Class({
},
_getPrimaryView: function() {
if (!this._workspacesViews)
if (!this._workspacesViews.length)
return null;
if (this._workspacesOnlyOnPrimary)
return this._workspacesViews[0];
@ -656,6 +728,76 @@ const WorkspacesDisplay = new Lang.Class({
return this._getPrimaryView().getActiveWorkspace().hasMaximizedWindows();
},
// zoomFraction property allows us to tween the controls sliding in and out
set zoomFraction(fraction) {
this._zoomFraction = fraction;
this.actor.queue_relayout();
},
get zoomFraction() {
return this._zoomFraction;
},
_updateAlwaysZoom: function() {
// Always show the pager if workspaces are actually used,
// e.g. there are windows on more than one
this._alwaysZoomOut = global.screen.n_workspaces > 2;
if (this._alwaysZoomOut)
return;
let monitors = Main.layoutManager.monitors;
let primary = Main.layoutManager.primaryMonitor;
/* Look for any monitor to the right of the primary, if there is
* one, we always keep zoom out, otherwise its hard to reach
* the thumbnail area without passing into the next monitor. */
for (let i = 0; i < monitors.length; i++) {
if (monitors[i].x >= primary.x + primary.width) {
this._alwaysZoomOut = true;
break;
}
}
},
_getPreferredWidth: function (actor, forHeight, alloc) {
// pass through the call in case the child needs it, but report 0x0
this._controls.get_preferred_width(forHeight);
},
_getPreferredHeight: function (actor, forWidth, alloc) {
// pass through the call in case the child needs it, but report 0x0
this._controls.get_preferred_height(forWidth);
},
_allocate: function (actor, box, flags) {
let childBox = new Clutter.ActorBox();
let totalWidth = box.x2 - box.x1;
// width of the controls
let [controlsMin, controlsNatural] = this._controls.get_preferred_width(box.y2 - box.y1);
// Amount of space on the screen we reserve for the visible control
let controlsVisible = this._controls.get_theme_node().get_length('visible-width');
let controlsReserved = controlsVisible * (1 - this._zoomFraction) + controlsNatural * this._zoomFraction;
let rtl = (Clutter.get_default_text_direction () == Clutter.TextDirection.RTL);
if (rtl) {
childBox.x2 = controlsReserved;
childBox.x1 = childBox.x2 - controlsNatural;
} else {
childBox.x1 = totalWidth - controlsReserved;
childBox.x2 = childBox.x1 + controlsNatural;
}
childBox.y1 = 0;
childBox.y2 = box.y2- box.y1;
this._controls.allocate(childBox, flags);
this._updateWorkspacesGeometry();
},
_parentSet: function(actor, oldParent) {
if (oldParent && this._notifyOpacityId)
oldParent.disconnect(this._notifyOpacityId);
@ -683,7 +825,7 @@ const WorkspacesDisplay = new Lang.Class({
},
_updateWorkspacesGeometry: function() {
if (!this._workspacesViews)
if (!this._workspacesViews.length)
return;
let fullWidth = this.actor.allocation.x2 - this.actor.allocation.x1;
@ -692,19 +834,23 @@ const WorkspacesDisplay = new Lang.Class({
let width = fullWidth;
let height = fullHeight;
let [controlsMin, controlsNatural] = this._controls.get_preferred_width(height);
let controlsVisible = this._controls.get_theme_node().get_length('visible-width');
let [x, y] = this.actor.get_transformed_position();
let rtl = (Clutter.get_default_text_direction () == Clutter.TextDirection.RTL);
let clipWidth = width;
let clipWidth = width - controlsVisible;
let clipHeight = fullHeight;
let clipX = x;
let clipX = rtl ? x + controlsVisible : x;
let clipY = y + (fullHeight - clipHeight) / 2;
let overviewSpacing = Main.overview._spacing;
width -= overviewSpacing;
let widthAdjust = this._zoomOut ? controlsNatural : controlsVisible;
widthAdjust += this._spacing;
width -= widthAdjust;
if (rtl)
x += overviewSpacing;
x += widthAdjust;
let monitors = Main.layoutManager.monitors;
let m = 0;
@ -719,22 +865,25 @@ const WorkspacesDisplay = new Lang.Class({
monitors[i].y,
monitors[i].width,
monitors[i].height);
this._workspacesViews[m].setGeometry(monitors[i].x + overviewSpacing/2,
monitors[i].y + overviewSpacing/2,
monitors[i].width - overviewSpacing,
monitors[i].height - overviewSpacing);
this._workspacesViews[m].setGeometry(monitors[i].x,
monitors[i].y,
monitors[i].width,
monitors[i].height);
m++;
}
}
},
_onRestacked: function(actor, stackIndices) {
_onRestacked: function(overview, stackIndices) {
for (let i = 0; i < this._workspacesViews.length; i++)
this._workspacesViews[i].syncStacking(stackIndices);
},
_workspacesChanged: function() {
if (this._workspacesViews == null)
this._updateAlwaysZoom();
this._updateZoom();
if (!this._workspacesViews.length)
return;
let oldNumWorkspaces = this._workspaces[0].length;
@ -787,6 +936,35 @@ const WorkspacesDisplay = new Lang.Class({
newNumWorkspaces);
},
_updateZoom : function() {
if (Main.overview.animationInProgress)
return;
let shouldZoom = this._alwaysZoomOut || this._controls.hover;
if (shouldZoom != this._zoomOut) {
this._zoomOut = shouldZoom;
this._updateWorkspacesGeometry();
if (!this._workspacesViews.length)
return;
Tweener.addTween(this,
{ zoomFraction: this._zoomOut ? 1 : 0,
time: WORKSPACE_SWITCH_TIME,
transition: 'easeOutQuad' });
for (let i = 0; i < this._workspacesViews.length; i++)
this._workspacesViews[i].updateWindowPositions();
}
},
_onControlsHoverChanged: function() {
if(!this._controls.hover)
this._controlsInitiallyHovered = false;
if(!this._controlsInitiallyHovered)
this._updateZoom();
},
_dragBegin: function() {
this._inDrag = true;
this._cancelledDrag = false;
@ -802,11 +980,22 @@ const WorkspacesDisplay = new Lang.Class({
},
_onDragMotion: function(dragEvent) {
let controlsHovered = this._controls.contains(dragEvent.targetActor);
this._controls.set_hover(controlsHovered);
return DND.DragMotionResult.CONTINUE;
},
_dragEnd: function() {
this._inDrag = false;
// We do this deferred because drag-end is emitted before dnd.js emits
// event/leave events that were suppressed during the drag. If we didn't
// defer this, we'd zoom out then immediately zoom in because of the
// enter event we received. That would normally be invisible but we
// might as well avoid it.
Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
Lang.bind(this, this._updateZoom));
}
});
Signals.addSignalMethods(WorkspacesDisplay.prototype);

View File

@ -21,6 +21,7 @@ eu
fa
fi
fr
fur
ga
gl
gu

View File

@ -11,6 +11,7 @@ js/misc/util.js
js/ui/appDisplay.js
js/ui/appFavorites.js
js/ui/calendar.js
js/ui/components/automountManager.js
js/ui/components/autorunManager.js
js/ui/components/keyring.js
js/ui/components/networkAgent.js
@ -55,6 +56,5 @@ src/shell-app.c
src/shell-app-system.c
src/shell-global.c
src/shell-keyring-prompt.c
src/shell-mobile-providers.c
src/shell-polkit-authentication-agent.c
src/shell-util.c

237
po/ar.po
View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: HEAD\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-12-09 04:45+0200\n"
"POT-Creation-Date: 2012-12-23 18:34+0200\n"
"PO-Revision-Date: 2012-12-09 04:52+0200\n"
"Last-Translator: Khaled Hosny <khaledhosny@eglug.org>\n"
"Language-Team: Arabic <doc@arabeyes.org>\n"
@ -365,8 +365,8 @@ msgstr "نافذة الولوج"
msgid "Power"
msgstr "الطاقة"
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:674 ../js/ui/userMenu.js:678
#: ../js/ui/userMenu.js:789
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:675 ../js/ui/userMenu.js:679
#: ../js/ui/userMenu.js:790
msgid "Suspend"
msgstr "علّق"
@ -374,8 +374,8 @@ msgstr "علّق"
msgid "Restart"
msgstr "أعِد التشغيل"
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:676
#: ../js/ui/userMenu.js:678 ../js/ui/userMenu.js:788
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:677
#: ../js/ui/userMenu.js:679 ../js/ui/userMenu.js:789
msgid "Power Off"
msgstr "أطفئ الحاسوب"
@ -410,27 +410,19 @@ msgid "Execution of '%s' failed:"
msgstr "فشل تنفيذ '%s':"
#. Translators: Filter to display all applications
#: ../js/ui/appDisplay.js:259
#: ../js/ui/appDisplay.js:258
msgid "All"
msgstr "الكل"
#: ../js/ui/appDisplay.js:318
msgid "APPLICATIONS"
msgstr "التطبيقات"
#: ../js/ui/appDisplay.js:375
msgid "SETTINGS"
msgstr "الإعدادات"
#: ../js/ui/appDisplay.js:679
#: ../js/ui/appDisplay.js:666
msgid "New Window"
msgstr "نافذة جديدة"
#: ../js/ui/appDisplay.js:682 ../js/ui/dash.js:289
#: ../js/ui/appDisplay.js:669 ../js/ui/dash.js:289
msgid "Remove from Favorites"
msgstr "أزِل من المفضّلة"
#: ../js/ui/appDisplay.js:683
#: ../js/ui/appDisplay.js:670
msgid "Add to Favorites"
msgstr "أضِف إلى المفضّلة"
@ -447,19 +439,19 @@ msgstr "أُزيل %s من مفضّلتك."
#. Translators: Shown in calendar event list for all day events
#. * Keep it short, best if you can use less then 10 characters
#.
#: ../js/ui/calendar.js:62
#: ../js/ui/calendar.js:61
msgctxt "event list time"
msgid "All Day"
msgstr "طوال اليوم"
#. Translators: Shown in calendar event list, if 24h format
#: ../js/ui/calendar.js:67
#: ../js/ui/calendar.js:66
msgctxt "event list time"
msgid "%H:%M"
msgstr "%H:%M"
#. Transators: Shown in calendar event list, if 12h format
#: ../js/ui/calendar.js:74
#: ../js/ui/calendar.js:73
msgctxt "event list time"
msgid "%l:%M %p"
msgstr "%l:%M %p"
@ -469,43 +461,43 @@ msgstr "%l:%M %p"
#. * NOTE: These grid abbreviations are always shown together
#. * and in order, e.g. "S M T W T F S".
#.
#: ../js/ui/calendar.js:114
#: ../js/ui/calendar.js:104
msgctxt "grid sunday"
msgid "S"
msgstr "ح"
#. Translators: Calendar grid abbreviation for Monday
#: ../js/ui/calendar.js:116
#: ../js/ui/calendar.js:106
msgctxt "grid monday"
msgid "M"
msgstr "ن"
#. Translators: Calendar grid abbreviation for Tuesday
#: ../js/ui/calendar.js:118
#: ../js/ui/calendar.js:108
msgctxt "grid tuesday"
msgid "T"
msgstr "ث"
#. Translators: Calendar grid abbreviation for Wednesday
#: ../js/ui/calendar.js:120
#: ../js/ui/calendar.js:110
msgctxt "grid wednesday"
msgid "W"
msgstr "ر"
#. Translators: Calendar grid abbreviation for Thursday
#: ../js/ui/calendar.js:122
#: ../js/ui/calendar.js:112
msgctxt "grid thursday"
msgid "T"
msgstr "خ"
#. Translators: Calendar grid abbreviation for Friday
#: ../js/ui/calendar.js:124
#: ../js/ui/calendar.js:114
msgctxt "grid friday"
msgid "F"
msgstr "ج"
#. Translators: Calendar grid abbreviation for Saturday
#: ../js/ui/calendar.js:126
#: ../js/ui/calendar.js:116
msgctxt "grid saturday"
msgid "S"
msgstr "س"
@ -516,77 +508,77 @@ msgstr "س"
#. * so they need to be unique (e.g. Tuesday and Thursday cannot
#. * both be 'T').
#.
#: ../js/ui/calendar.js:139
#: ../js/ui/calendar.js:129
msgctxt "list sunday"
msgid "Su"
msgstr "الأحد"
#. Translators: Event list abbreviation for Monday
#: ../js/ui/calendar.js:141
#: ../js/ui/calendar.js:131
msgctxt "list monday"
msgid "M"
msgstr "الاثنين"
#. Translators: Event list abbreviation for Tuesday
#: ../js/ui/calendar.js:143
#: ../js/ui/calendar.js:133
msgctxt "list tuesday"
msgid "T"
msgstr "الثلاثاء"
#. Translators: Event list abbreviation for Wednesday
#: ../js/ui/calendar.js:145
#: ../js/ui/calendar.js:135
msgctxt "list wednesday"
msgid "W"
msgstr "الأربعاء"
#. Translators: Event list abbreviation for Thursday
#: ../js/ui/calendar.js:147
#: ../js/ui/calendar.js:137
msgctxt "list thursday"
msgid "Th"
msgstr "الخميس"
#. Translators: Event list abbreviation for Friday
#: ../js/ui/calendar.js:149
#: ../js/ui/calendar.js:139
msgctxt "list friday"
msgid "F"
msgstr "الجمعة"
#. Translators: Event list abbreviation for Saturday
#: ../js/ui/calendar.js:151
#: ../js/ui/calendar.js:141
msgctxt "list saturday"
msgid "S"
msgstr "السبت"
#. Translators: Text to show if there are no events
#: ../js/ui/calendar.js:700
#: ../js/ui/calendar.js:674
msgid "Nothing Scheduled"
msgstr "الجدول خال"
#. Translators: Shown on calendar heading when selected day occurs on current year
#: ../js/ui/calendar.js:716
#: ../js/ui/calendar.js:690
msgctxt "calendar heading"
msgid "%A, %B %d"
msgstr "%A %d %B"
#. Translators: Shown on calendar heading when selected day occurs on different year
#: ../js/ui/calendar.js:719
#: ../js/ui/calendar.js:693
msgctxt "calendar heading"
msgid "%A, %B %d, %Y"
msgstr "%A %d %B %Y"
#: ../js/ui/calendar.js:729
#: ../js/ui/calendar.js:703
msgid "Today"
msgstr "اليوم"
#: ../js/ui/calendar.js:733
#: ../js/ui/calendar.js:707
msgid "Tomorrow"
msgstr "غدا"
#: ../js/ui/calendar.js:744
#: ../js/ui/calendar.js:718
msgid "This week"
msgstr "هذا الأسبوع"
#: ../js/ui/calendar.js:752
#: ../js/ui/calendar.js:726
msgid "Next week"
msgstr "الأسبوع القادم"
@ -948,7 +940,7 @@ msgstr "أظهر الحساب"
msgid "Unknown reason"
msgstr "السبب غير معروف"
#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:87
#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:81
msgid "Windows"
msgstr "النوافذ"
@ -1088,7 +1080,7 @@ msgstr "هل تريد تنزيل وتثبيت '%s' من extensions.gnome.org؟"
msgid "tray"
msgstr "لوحة النظام"
#: ../js/ui/keyboard.js:555 ../js/ui/status/keyboard.js:195
#: ../js/ui/keyboard.js:555 ../js/ui/status/keyboard.js:309
#: ../js/ui/status/power.js:205
msgid "Keyboard"
msgstr "لوحة المفاتيح"
@ -1115,7 +1107,9 @@ msgstr "اظهر الأخطاء"
msgid "Enabled"
msgstr "مفعّل"
#: ../js/ui/lookingGlass.js:767
#. translators:
#. * The device has been disabled
#: ../js/ui/lookingGlass.js:767 ../src/gvc/gvc-mixer-control.c:1830
msgid "Disabled"
msgstr "معطّل"
@ -1147,15 +1141,15 @@ msgstr "افتح"
msgid "Remove"
msgstr "أزِل"
#: ../js/ui/messageTray.js:1551
#: ../js/ui/messageTray.js:1552
msgid "No Messages"
msgstr "لا رسائل"
#: ../js/ui/messageTray.js:1568
#: ../js/ui/messageTray.js:1570
msgid "Message Tray"
msgstr "لوحة الرسائل"
#: ../js/ui/messageTray.js:2635
#: ../js/ui/messageTray.js:2639
msgid "System Information"
msgstr "معلومات النظام"
@ -1164,11 +1158,11 @@ msgctxt "program"
msgid "Unknown"
msgstr "غير معروف"
#: ../js/ui/overview.js:95
#: ../js/ui/overview.js:92
msgid "Undo"
msgstr "تراجع"
#: ../js/ui/overview.js:144
#: ../js/ui/overview.js:139
msgid "Overview"
msgstr "نظرة عامة"
@ -1176,13 +1170,13 @@ msgstr "نظرة عامة"
#. in the search entry when no search is
#. active; it should not exceed ~30
#. characters.
#: ../js/ui/overview.js:221
#: ../js/ui/overview.js:218
msgid "Type to search..."
msgstr "اكتب نصا للبحث عنه..."
#. Translators: this is the name of the dock/favorites area on
#. the left of the overview
#: ../js/ui/overview.js:242
#: ../js/ui/overview.js:236
msgid "Dash"
msgstr "الشريط"
@ -1205,7 +1199,7 @@ msgstr "الشريط العلوي"
#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
#. switches containing "◯" and "|"). Other values will
#. simply result in invisible toggle switches.
#: ../js/ui/popupMenu.js:732
#: ../js/ui/popupMenu.js:725
msgid "toggle-switch-us"
msgstr "toggle-switch-intl"
@ -1245,15 +1239,15 @@ msgstr[3] "%d تنبيهات جديدة"
msgstr[4] "%d تنبيها جديدا"
msgstr[5] "%d تنبيه جديد"
#: ../js/ui/screenShield.js:402 ../js/ui/userMenu.js:780
#: ../js/ui/screenShield.js:402 ../js/ui/userMenu.js:781
msgid "Lock"
msgstr "أوصِد"
#: ../js/ui/searchDisplay.js:277
#: ../js/ui/searchDisplay.js:403
msgid "Searching..."
msgstr "يبحث..."
#: ../js/ui/searchDisplay.js:325
#: ../js/ui/searchDisplay.js:451
msgid "No results."
msgstr "لا نتائج."
@ -1369,7 +1363,7 @@ msgid "disconnecting..."
msgstr "يقطع الاتّصال..."
#: ../js/ui/status/bluetooth.js:220 ../js/ui/status/network.js:442
#: ../js/ui/status/network.js:1453
#: ../js/ui/status/network.js:1464
msgid "connecting..."
msgstr "يتّصل..."
@ -1385,7 +1379,7 @@ msgstr "إعدادات لوحة المفاتيح"
msgid "Mouse Settings"
msgstr "إعدادات الفأرة"
#: ../js/ui/status/bluetooth.js:253 ../js/ui/status/volume.js:270
#: ../js/ui/status/bluetooth.js:253 ../js/ui/status/volume.js:314
msgid "Sound Settings"
msgstr "إعدادات الصوت"
@ -1449,15 +1443,15 @@ msgstr "من فضلك أدخل الرقم المذكور على الجهاز."
msgid "OK"
msgstr "حسنا"
#: ../js/ui/status/keyboard.js:228
#: ../js/ui/status/keyboard.js:363
msgid "Show Keyboard Layout"
msgstr "أظهر تخطيط لوحة المفاتيح"
#: ../js/ui/status/keyboard.js:233
#: ../js/ui/status/keyboard.js:368
msgid "Region and Language Settings"
msgstr "إعدادات الإقليم واللغة"
#: ../js/ui/status/lockScreenMenu.js:18
#: ../js/ui/status/lockScreenMenu.js:43
msgid "Volume, network, battery"
msgstr "الصوت، الشبكة، البطارية"
@ -1477,7 +1471,7 @@ msgid "unmanaged"
msgstr "غير مُدار"
#. Translators: this is for network connections that require some kind of key or password
#: ../js/ui/status/network.js:445 ../js/ui/status/network.js:1456
#: ../js/ui/status/network.js:445 ../js/ui/status/network.js:1467
msgid "authentication required"
msgstr "الاستيثاق مطلوب"
@ -1498,72 +1492,72 @@ msgstr "الكبل مفصول"
msgid "unavailable"
msgstr "غير متاح"
#: ../js/ui/status/network.js:469 ../js/ui/status/network.js:1458
#: ../js/ui/status/network.js:469 ../js/ui/status/network.js:1469
msgid "connection failed"
msgstr "فشل الاتصال"
#: ../js/ui/status/network.js:525 ../js/ui/status/network.js:1392
#: ../js/ui/status/network.js:1534
#: ../js/ui/status/network.js:525 ../js/ui/status/network.js:1403
#: ../js/ui/status/network.js:1545
msgid "More..."
msgstr "المزيد..."
#. TRANSLATORS: this is the indication that a connection for another logged in user is active,
#. and we cannot access its settings (including the name)
#: ../js/ui/status/network.js:561 ../js/ui/status/network.js:1322
#: ../js/ui/status/network.js:561 ../js/ui/status/network.js:1333
msgid "Connected (private)"
msgstr "متّصل (شخصي)"
#: ../js/ui/status/network.js:619
#: ../js/ui/status/network.js:641
msgid "Auto Ethernet"
msgstr "إيثرنت تلقائي"
#: ../js/ui/status/network.js:677
#: ../js/ui/status/network.js:688
msgid "Auto broadband"
msgstr "شبكة هاتف محمول تلقائية"
#: ../js/ui/status/network.js:680
#: ../js/ui/status/network.js:691
msgid "Auto dial-up"
msgstr "اتصال هاتفي تلقائي"
#. TRANSLATORS: this the automatic wireless connection name (including the network name)
#: ../js/ui/status/network.js:809 ../js/ui/status/network.js:1339
#: ../js/ui/status/network.js:820 ../js/ui/status/network.js:1350
#, c-format
msgid "Auto %s"
msgstr "%s تلقائي"
#: ../js/ui/status/network.js:811
#: ../js/ui/status/network.js:822
msgid "Auto bluetooth"
msgstr "بلوتوث تلقائي"
#: ../js/ui/status/network.js:1341
#: ../js/ui/status/network.js:1352
msgid "Auto wireless"
msgstr "لاسلكي تلقائي"
#: ../js/ui/status/network.js:1584
#: ../js/ui/status/network.js:1595
msgid "Enable networking"
msgstr "فعّل الشبكات"
#: ../js/ui/status/network.js:1616
#: ../js/ui/status/network.js:1627
msgid "Wi-Fi"
msgstr "واي فاي"
#: ../js/ui/status/network.js:1635
#: ../js/ui/status/network.js:1646
msgid "Network Settings"
msgstr "إعدادات الشّبكة"
#: ../js/ui/status/network.js:1679
#: ../js/ui/status/network.js:1692
msgid "Network Manager"
msgstr "مدير الشبكة"
#: ../js/ui/status/network.js:1761
#: ../js/ui/status/network.js:1774
msgid "Connection failed"
msgstr "فشل الاتصال"
#: ../js/ui/status/network.js:1762
#: ../js/ui/status/network.js:1775
msgid "Activation of network connection failed"
msgstr "فشل تفعيل اتصال الشبكة"
#: ../js/ui/status/network.js:2079
#: ../js/ui/status/network.js:2092
msgid "Networking is disabled"
msgstr "عُطّلت الشبكات"
@ -1681,11 +1675,11 @@ msgid "Unknown"
msgstr "غير معروف"
#. Translators: This is the label for audio volume
#: ../js/ui/status/volume.js:50 ../js/ui/status/volume.js:251
#: ../js/ui/status/volume.js:247 ../js/ui/status/volume.js:295
msgid "Volume"
msgstr "شدة الصوت"
#: ../js/ui/status/volume.js:62
#: ../js/ui/status/volume.js:256
msgid "Microphone"
msgstr "ميكروفون"
@ -1721,31 +1715,31 @@ msgstr "ساكن"
msgid "Offline"
msgstr "غير متصل"
#: ../js/ui/userMenu.js:754
#: ../js/ui/userMenu.js:755
msgid "Notifications"
msgstr "التنبيهات"
#: ../js/ui/userMenu.js:762
#: ../js/ui/userMenu.js:763
msgid "Settings"
msgstr "الإعدادات"
#: ../js/ui/userMenu.js:770
#: ../js/ui/userMenu.js:771
msgid "Switch User"
msgstr "بدّل المستخدم"
#: ../js/ui/userMenu.js:775
#: ../js/ui/userMenu.js:776
msgid "Log Out"
msgstr "اخرج"
#: ../js/ui/userMenu.js:795
#: ../js/ui/userMenu.js:796
msgid "Install Updates & Restart"
msgstr "ثبّت التحديثات وأعد التشغيل"
#: ../js/ui/userMenu.js:813
#: ../js/ui/userMenu.js:814
msgid "Your chat status will be set to busy"
msgstr "ستُجعل حالة اتصالك ”مشغول“"
#: ../js/ui/userMenu.js:814
#: ../js/ui/userMenu.js:815
msgid ""
"Notifications are now disabled, including chat messages. Your online status "
"has been adjusted to let others know that you might not see their messages."
@ -1753,15 +1747,15 @@ msgstr ""
"التنبيهات معطلة الآن، بما فيها رسائل المحادثة. حالة اتصالك تغيرت حتى يعلم "
"الآخرون أنك قد لا ترى رسائلهم."
#: ../js/ui/viewSelector.js:91
#: ../js/ui/viewSelector.js:85
msgid "Applications"
msgstr "التطبيقات"
#: ../js/ui/viewSelector.js:95
#: ../js/ui/viewSelector.js:89
msgid "Search"
msgstr "ابحث"
#: ../js/ui/wanda.js:94
#: ../js/ui/wanda.js:92
#, c-format
msgid ""
"Sorry, no wisdom for you today:\n"
@ -1770,15 +1764,11 @@ msgstr ""
"عذرًا, لا حكمة لك اليوم:\n"
"%s"
#: ../js/ui/wanda.js:98
#: ../js/ui/wanda.js:96
#, c-format
msgid "%s the Oracle says"
msgstr "يقول الحكيم %s"
#: ../js/ui/wanda.js:139
msgid "Your favorite Easter Egg"
msgstr ""
#: ../js/ui/windowAttentionHandler.js:19
#, c-format
msgid "'%s' is ready"
@ -1788,6 +1778,36 @@ msgstr "'%s' جاهز"
msgid "Evolution Calendar"
msgstr "تقويم إيفُليوشِن"
#. translators:
#. * The number of sound outputs on a particular device
#: ../src/gvc/gvc-mixer-control.c:1837
#, c-format
msgid "%u Output"
msgid_plural "%u Outputs"
msgstr[0] "لا مخرَج"
msgstr[1] "مخرَج واحد"
msgstr[2] "مخرَجين"
msgstr[3] "%u مخارج"
msgstr[4] "%u مخرجا"
msgstr[5] "%u مخرج"
#. translators:
#. * The number of sound inputs on a particular device
#: ../src/gvc/gvc-mixer-control.c:1847
#, c-format
msgid "%u Input"
msgid_plural "%u Inputs"
msgstr[0] "لا مدخل"
msgstr[1] "مدخل واحد"
msgstr[2] "مدخلين"
msgstr[3] "%u مداخل"
msgstr[4] "%u مدخلا"
msgstr[5] "%u مدخل"
#: ../src/gvc/gvc-mixer-control.c:2371
msgid "System Sounds"
msgstr "أصوات النظام"
#: ../src/main.c:332
msgid "Print version"
msgstr "اطبع الإصدارة"
@ -1829,6 +1849,12 @@ msgstr "المبدئي"
msgid "Authentication dialog was dismissed by the user"
msgstr "أغلق المستخدم مربع الاستيثاق الحِواري"
#~ msgid "APPLICATIONS"
#~ msgstr "التطبيقات"
#~ msgid "SETTINGS"
#~ msgstr "الإعدادات"
#~ msgid "Subscription request"
#~ msgstr "طلب اشتراك"
@ -1877,27 +1903,6 @@ msgstr "أغلق المستخدم مربع الاستيثاق الحِواري"
#~ msgid "System Settings"
#~ msgstr "إعدادات النظام"
#~ msgid "%u Output"
#~ msgid_plural "%u Outputs"
#~ msgstr[0] "لا مخرَج"
#~ msgstr[1] "مخرَج واحد"
#~ msgstr[2] "مخرَجين"
#~ msgstr[3] "%u مخارج"
#~ msgstr[4] "%u مخرجا"
#~ msgstr[5] "%u مخرج"
#~ msgid "%u Input"
#~ msgid_plural "%u Inputs"
#~ msgstr[0] "لا مدخل"
#~ msgstr[1] "مدخل واحد"
#~ msgstr[2] "مدخلين"
#~ msgstr[3] "%u مداخل"
#~ msgstr[4] "%u مدخلا"
#~ msgstr[5] "%u مدخل"
#~ msgid "System Sounds"
#~ msgstr "أصوات النظام"
#~ msgid "Failed to unmount '%s'"
#~ msgstr "فشل فصْل '%s'"

475
po/as.po

File diff suppressed because it is too large Load Diff

737
po/be.po

File diff suppressed because it is too large Load Diff

988
po/de.po

File diff suppressed because it is too large Load Diff

915
po/es.po

File diff suppressed because it is too large Load Diff

173
po/et.po
View File

@ -13,8 +13,8 @@ msgstr ""
"Project-Id-Version: gnome-shell MASTER\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&keywords=I18N+L10N&component=general\n"
"POT-Creation-Date: 2012-11-16 18:27+0000\n"
"PO-Revision-Date: 2012-11-16 20:40+0300\n"
"POT-Creation-Date: 2012-12-09 13:03+0000\n"
"PO-Revision-Date: 2012-12-19 16:47+0300\n"
"Last-Translator: Mattias Põldaru <mahfiaz@gmail.com>\n"
"Language-Team: Estonian <>\n"
"Language: et\n"
@ -135,6 +135,28 @@ msgstr ""
"See võti keelab automaatse 'Logi välja' menüükirje peitmise, kui arvutis on "
"üks kasutaja ning avatud üks seanss."
msgid "Show full name in the user menu"
msgstr "Kasutajamenüüs näidatakse kasutaja tervet nime"
msgid "Whether the users full name is shown in the user menu or not."
msgstr "Kas kasutajamenüüs näidatakse kasutaja kogu nime või mitte."
msgid ""
"Whether to remember password for mounting encrypted or remote filesystems"
msgstr ""
"Kas pidada meeles krüpteeritud või kaugfailisüsteemide haakimise paroole."
msgid ""
"The shell will request a password when an encrypted device or a remote "
"filesystem is mounted. If the password can be saved for future use a "
"'Remember Password' checkbox will be present. This key sets the default "
"state of the checkbox."
msgstr ""
"Shell küsib parooli, kui haagitakse krüpteeritud seade või kaugfailisüsteem. "
"Kui parooli on võimalik salvestada edaspidiseks kasutuseks, näidatakse "
"'Salvesta parool' märkeruutu. See võti määrab selle märkeruudu vaikimisi "
"oleku."
msgid "Show the week date in the calendar"
msgstr "Kalendris näidatakse kuupäeva nädalavormingus"
@ -227,6 +249,18 @@ msgstr ""
"konteinervormingusse salvestades tuleks ka sellele vormingule vastav laiend "
"määrata."
msgid "The application icon mode."
msgstr "Rakenduste ikooni režiim."
msgid ""
"Configures how the windows are shown in the switcher. Valid possibilities "
"are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-"
"only' (shows only the application icon) or 'both'."
msgstr ""
"Seadistab, kuidas aknaid aknavahetajas kuvatakse. Sobivad väärtused on "
"'thumbnail-only' (näidatakse ainult pisipilti aknast), 'app-icon-"
"only' (näidatakse ainult akna ikooni) või 'both' (mõlemad)."
msgid "Attach modal dialog to the parent window"
msgstr "Modaaldialoog kuulub vanemakna juurde"
@ -588,11 +622,8 @@ msgstr "Helista"
msgid "File Transfer"
msgstr "Failiülekanne"
msgid "Subscription request"
msgstr "Tellimuse päring"
msgid "Connection error"
msgstr "Ühenduse viga"
msgid "Chat"
msgstr "Vestlus"
msgid "Unmute"
msgstr "Heli peale"
@ -600,27 +631,25 @@ msgstr "Heli peale"
msgid "Mute"
msgstr "Heli maha"
#. Translators: this is a time format string followed by the word "Yesterday". i.e. "14:30 on Yesterday"
#. Translators: this is the word "Yesterday" followed by a time string. i.e. "Yesterday, 14:30"
#, no-c-format
msgid "<b>%H:%M</b> on Yesterday"
msgstr "Eile <b>%H:%M</b>"
msgid "<b>Yesterday</b>, <b>%H:%M</b>"
msgstr "<b>Eile</b> <b>%H:%M</b>"
#. Translators: this is a time format string followed by a week day name. i.e. "14:30 on Monday
#. Translators: this is the week day name followed by a time string. i.e. "Monday, 14:30
#, no-c-format
msgid "<b>%H:%M</b> on <b>%A</b>"
msgstr "<b>%H:%M</b>, <b>%A</b>"
msgid "<b>%A</b>, <b>%H:%M</b>"
msgstr "<b>%A</b>, <b>%H:%M</b>"
#. Translators: this is a time format in the style of "14:30 on Wednesday, May 25",
#. shown when you get a chat message in the same year
#. Translators: this is the month name and day number followed by a time string. i.e. "May 25, 14:30"
#, no-c-format
msgid "<b>%H:%M</b> on <b>%A</b>, <b>%B</b> <b>%d</b>"
msgstr "<b>%H:%M</b>, <b>%A</b>, <b>%d. %B</b>"
msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
msgstr "<b>%d. %B</b>, <b>%H:%M</b>"
#. Translators: this is a time format in the style of "14:30 on Wednesday, May 25, 2012",
#. shown when you get a chat message in a different year
#. Translators: this is the month name, day number, year number followed by a time string. i.e. "May 25 2012, 14:30"
#, no-c-format
msgid "<b>%H:%M</b> on <b>%A</b>, <b>%B</b> <b>%d</b>, %Y"
msgstr "<b>%H:%M</b>, <b>%A</b>, <b>%d. %B</b> %Y"
msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
msgstr "<b>%d. %B %Y</b>, <b>%H:%M</b>"
#. Translators: this is the other person changing their old IM name to their new
#. IM name.
@ -757,18 +786,18 @@ msgstr "Sisemine viga"
#. translators: argument is the account name, like
#. * name@jabber.org for example.
#, c-format
msgid "Connection to %s failed"
msgstr "Ühendus %s nurjus"
msgid "Unable to connect to %s"
msgstr "Pole võimalik ühenduda võrguga %s"
msgid "Reconnect"
msgstr "Ühendu uuesti"
msgid "Edit account"
msgstr "Konto muutmine"
msgid "View account"
msgstr "Konto kuvamine"
msgid "Unknown reason"
msgstr "Põhjus teadmata"
msgid "Windows"
msgstr "Aknad"
msgid "Show Applications"
msgstr "Rakenduste kuvamine"
@ -891,8 +920,6 @@ msgstr "Näita vigu"
msgid "Enabled"
msgstr "Lubatud"
#. translators:
#. * The device has been disabled
msgid "Disabled"
msgstr "Keelatud"
@ -917,6 +944,9 @@ msgstr "Ava"
msgid "Remove"
msgstr "Eemalda"
msgid "No Messages"
msgstr "Teateid pole"
msgid "Message Tray"
msgstr "Teateala"
@ -987,6 +1017,9 @@ msgid_plural "%d new notifications"
msgstr[0] "%d uus märguanne"
msgstr[1] "%d uut märguannet"
msgid "Lock"
msgstr "Lukusta"
msgid "Searching..."
msgstr "Otsimine..."
@ -1107,6 +1140,7 @@ msgstr "Luba ainult seekord"
msgid "Reject"
msgstr "Lükka tagasi"
#. Translators: argument is the device short name
#, c-format
msgid "Pairing confirmation for %s"
msgstr "Paardumise kinnitus seadmele %s"
@ -1119,6 +1153,7 @@ msgstr "Seade '%s' tahab selle arvutiga paarduda"
msgid "Please confirm whether the PIN '%06d' matches the one on the device."
msgstr "Palun kontrolli, kas PIN-kood '%06d' kattub seadme parooliga."
#. Translators: this is the verb, not the noun
msgid "Matches"
msgstr "Kattub"
@ -1208,14 +1243,8 @@ msgstr "Automaatne juhtmeta ühendus"
msgid "Enable networking"
msgstr "Luba võrguühendused"
msgid "Wired"
msgstr "Juhtmega"
msgid "Wireless"
msgstr "Juhtmeta"
msgid "Mobile broadband"
msgstr "Mobiiliühendus"
msgid "Wi-Fi"
msgstr "Wi-Fi"
msgid "Network Settings"
msgstr "Võrgusätted"
@ -1319,6 +1348,9 @@ msgstr "Mikrofon"
msgid "Log in as another user"
msgstr "Logi sisse teise kasutajana"
msgid "Unlock Window"
msgstr "Võta aken lukust lahti"
msgid "Available"
msgstr "Saadaval"
@ -1349,9 +1381,6 @@ msgstr "Vaheta kasutajat"
msgid "Log Out"
msgstr "Logi välja"
msgid "Lock"
msgstr "Lukusta"
msgid "Install Updates & Restart"
msgstr "Paigalda uuendused ja taaskäivita"
@ -1365,9 +1394,6 @@ msgstr ""
"Märguanded on nüüd keelatud, sealhulgas vestlusteated. Sinu netiolekut "
"muudeti, et teised teaksid, et sa ei pruugi nende teateid näha."
msgid "Windows"
msgstr "Aknad"
msgid "Applications"
msgstr "Rakendused"
@ -1396,25 +1422,6 @@ msgstr "'%s' on valmis"
msgid "Evolution Calendar"
msgstr "Evolutioni kalender"
#. translators:
#. * The number of sound outputs on a particular device
#, c-format
msgid "%u Output"
msgid_plural "%u Outputs"
msgstr[0] "%u väljund"
msgstr[1] "%u väljundit"
#. translators:
#. * The number of sound inputs on a particular device
#, c-format
msgid "%u Input"
msgid_plural "%u Inputs"
msgstr[0] "%u sisend"
msgstr[1] "%u sisendit"
msgid "System Sounds"
msgstr "Süsteemi helid"
msgid "Print version"
msgstr "Printimise versioon"
@ -1446,6 +1453,46 @@ msgstr "Vaikimisi"
msgid "Authentication dialog was dismissed by the user"
msgstr "Kasutaja katkestas autentimisdialoogi"
#~ msgid "Subscription request"
#~ msgstr "Tellimuse päring"
#~ msgid "Connection error"
#~ msgstr "Ühenduse viga"
#~ msgid "<b>%H:%M</b> on Yesterday"
#~ msgstr "Eile <b>%H:%M</b>"
#~ msgid "<b>%H:%M</b> on <b>%A</b>, <b>%B</b> <b>%d</b>, %Y"
#~ msgstr "<b>%H:%M</b>, <b>%A</b>, <b>%d. %B</b> %Y"
#~ msgid "Connection to %s failed"
#~ msgstr "Ühendus %s nurjus"
#~ msgid "Reconnect"
#~ msgstr "Ühendu uuesti"
#~ msgid "Wired"
#~ msgstr "Juhtmega"
#~ msgid "Wireless"
#~ msgstr "Juhtmeta"
#~ msgid "Mobile broadband"
#~ msgstr "Mobiiliühendus"
#~ msgid "%u Output"
#~ msgid_plural "%u Outputs"
#~ msgstr[0] "%u väljund"
#~ msgstr[1] "%u väljundit"
#~ msgid "%u Input"
#~ msgid_plural "%u Inputs"
#~ msgstr[0] "%u sisend"
#~ msgstr[1] "%u sisendit"
#~ msgid "System Sounds"
#~ msgstr "Süsteemi helid"
#~ msgid "disabled OpenSearch providers"
#~ msgstr "keelatud OpenSearch pakkujad"

2097
po/eu.po

File diff suppressed because it is too large Load Diff

1874
po/fur.po Normal file

File diff suppressed because it is too large Load Diff

620
po/gl.po

File diff suppressed because it is too large Load Diff

779
po/he.po

File diff suppressed because it is too large Load Diff

871
po/hu.po

File diff suppressed because it is too large Load Diff

886
po/it.po

File diff suppressed because it is too large Load Diff

1830
po/kk.po

File diff suppressed because it is too large Load Diff

960
po/lt.po

File diff suppressed because it is too large Load Diff

471
po/nb.po

File diff suppressed because it is too large Load Diff

705
po/nl.po

File diff suppressed because it is too large Load Diff

477
po/pa.po

File diff suppressed because it is too large Load Diff

839
po/pl.po

File diff suppressed because it is too large Load Diff

940
po/pt.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

817
po/sk.po

File diff suppressed because it is too large Load Diff

1073
po/sl.po

File diff suppressed because it is too large Load Diff

846
po/sr.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

3525
po/ug.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -86,7 +86,6 @@ include Makefile-hotplug-sniffer.am
gnome_shell_cflags = \
$(GNOME_SHELL_CFLAGS) \
$(SYSTEMD_CFLAGS) \
-I$(srcdir)/tray \
-DVERSION=\"$(VERSION)\" \
-DLOCALEDIR=\"$(datadir)/locale\" \
@ -115,7 +114,7 @@ shell_public_headers_h = \
shell-gtk-embed.h \
shell-global.h \
shell-invert-lightness-effect.h \
shell-mobile-providers.h \
shell-keybinding-modes.h \
shell-mount-operation.h \
shell-network-agent.h \
shell-perf-log.h \
@ -160,7 +159,6 @@ libgnome_shell_la_SOURCES = \
shell-invert-lightness-effect.c \
shell-keyring-prompt.h \
shell-keyring-prompt.c \
shell-mobile-providers.c \
shell-mount-operation.c \
shell-network-agent.c \
shell-perf-log.c \
@ -281,7 +279,6 @@ libgnome_shell_la_LDFLAGS = -avoid-version
libgnome_shell_la_LIBADD = \
-lm \
$(GNOME_SHELL_LIBS) \
$(SYSTEMD_LIBS) \
$(BLUETOOTH_LIBS) \
libst-1.0.la \
libtray.la \

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