Compare commits

...

488 Commits

Author SHA1 Message Date
555d45f06c wip center layout garbage 2012-11-01 12:58:12 -04:00
0a8713770b overview, viewSelector: Don't use two signals for checking the app button
Just one will do.
2012-11-01 12:58:12 -04:00
7d3ea1ac68 overview, viewSelector: show/hide workspaces and message tray on apps button 2012-11-01 12:58:12 -04:00
7785710964 messageTray: Always show message tray upon entering overview
We hide the message tray while searching, but it should always be visible when
not searching, including when initially entering overview.
2012-11-01 12:58:12 -04:00
3730dc01cf viewSelector: remove switching from search results on app-drag-begin
https://bugzilla.gnome.org/show_bug.cgi?id=682050
2012-11-01 12:58:12 -04:00
66bf0df737 viewSelector: this.active --> this.entryNonEmpty
'active' isn't terribly clear about just what is active, this variable is
true/false depending on whether or not the search entry has text.

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2012-11-01 12:58:12 -04:00
4590b33f2e messageTray, viewSelector: show/hide message tray on search signals
https://bugzilla.gnome.org/show_bug.cgi?id=682050
2012-11-01 12:58:12 -04:00
6538f60322 overview: Connect app-drag signals to show/hide overview elements
Anytime we begin dragging an app launcher, ensure the overview elements are
showing. While searching, if an app-drag ends or is cancelled, hide the
overview elements, but don't switch back to windows (which was the old
behavior).

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2012-11-01 12:58:12 -04:00
4b0ba8b7b8 viewSelector, overview: Add search signals and connect them to hide/show
Hide the elements when the search entry is non-empty. Show them if the search
is cancelled.

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2012-11-01 12:58:11 -04:00
bc8965fe63 overview: Center the main overview group accordingly
Using the same logic as the panel which smartly centers everything,
smartly center the overview contents if we have enough space to do so.
2012-11-01 12:58:11 -04:00
e7fcce3484 overview, workspacesView: Use ThumbnailsBox for independent workspace thumbnails
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=682050
2012-11-01 12:58:11 -04:00
b1e4d3335c workspaceThumbnail: Add keyboard nav to ThumbnailsBox
https://bugzilla.gnome.org/show_bug.cgi?id=682050
2012-11-01 12:58:11 -04:00
a3a3f24ed3 workspaceThumbnail: Rename show(), hide() and add new methods
show() and hide() now slide the thumbnails in and out, respectively. The old
show and hide are now _createThumbnails and _destroyThumbnails. We only care
about these thumbnails while in the overview, so create them when entering and
destroy them on exit.

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2012-11-01 12:58:11 -04:00
96191a9c96 workspaceThumbnail: React to scroll event
https://bugzilla.gnome.org/show_bug.cgi?id=682050
2012-11-01 12:58:11 -04:00
f4814d200b workspaceThumbnail: Make ThumbnailsBox track workspace changes itself
https://bugzilla.gnome.org/show_bug.cgi?id=682050
2012-11-01 12:58:11 -04:00
52156930d3 overview: Respect multi-monitor situations 2012-11-01 12:58:11 -04:00
cbaa999ced overview: overview as boxlayouts
https://bugzilla.gnome.org/show_bug.cgi?id=682286
2012-11-01 12:58:10 -04:00
9491f6bd23 dash: fix for shrinking dash while animating
The this.actor.height > this._maxHeight check is needed because animating the
dash out causes constant notify::height signals and so after each one the max
height would shrink to the actor height causing the icons to shrink until
eventually we get to 16px icons. This way, only increase maxHeight if the
actor can be drawn bigger than it is currently set.

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2012-11-01 12:58:10 -04:00
afe8198d4b dash: Add show/hide methods
https://bugzilla.gnome.org/show_bug.cgi?id=682050
2012-11-01 12:58:10 -04:00
6aa8f14285 overview and others: Rename item-drag signals to app-drag
Since results other than apps should not be draggable, be more descriptive and
rename item-drag signals to app-drag signals.

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2012-11-01 12:58:09 -04:00
e073670c4d workspace: Don't recalculate window positions when leaving the overview
https://bugzilla.gnome.org/show_bug.cgi?id=682286
2012-11-01 12:58:09 -04:00
17a3d2c63f searchDisplay: Put the grid search results in the middle
This is still "off-balance" due to the dash on the left.
2012-11-01 12:58:09 -04:00
ca38e05ed4 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-11-01 12:58:09 -04:00
9841e56ebf 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-11-01 12:58:09 -04:00
c0d3a14ac2 popupMenu: Break separator drawing code out of PopupSeparatorMenuItem
https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-11-01 12:58:09 -04:00
d485fcf9ec appDisplay: Add provider icons for the search system
Only temporary, to test with, unless a remote provider for Settings doesn't
materialize. this.icon should be a GIcon since that is what remote providers
will return.

https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-11-01 12:58:09 -04:00
ce2c5106f8 theme: Update for search redesign
https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-11-01 12:58:09 -04:00
5e96c3dfb4 searchDisplay: Set can_focus on provider icon only if its results have focus
This way, the search results take priority, but while the user is in the
search section, they can navigate to that provider icon to launch a search.

https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-11-01 12:58:09 -04:00
a72b642f3e searchDisplay: Add ListSearchResult and ListSearchResults
These are for all search results except apps.

https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-11-01 12:58:09 -04:00
8507d3c4e4 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-11-01 12:58:09 -04:00
c985fdccba iconGrid: Handle preferred height requests for infinite widths
Request enough height to fit all children in a single line instead of
requesting 0.

https://bugzilla.gnome.org/show_bug.cgi?id=679168
2012-11-01 12:58:08 -04:00
1d7c2b1c26 centerLayout: Add some utilities to connect us up to CSS
This allows us to automatically adjust the "spacing" property of the
layout manager whenever the CSS changes.
2012-11-01 12:58:07 -04:00
ab60c628e7 panel: Abstract the centered panel logic out into a ClutterLayoutManager
Since we want to use this in the overview as well, put it into centerLayout.js

Panel corners are disabled for now. They'll be added back eventually.
2012-11-01 12:58:07 -04:00
2c34c8e20f Overview search box is too subtle
When it isn't focused, the search box can be quite hard to see.

The text/icon/border color is changed to be brighter in order to increase
contrast with the dark background and this works well with various
wallpapers.

https://bugzilla.gnome.org/show_bug.cgi?id=686479
2012-11-01 11:32:40 +00:00
d19fa731d6 Updated Polish translation 2012-10-31 19:15:34 +01:00
59bb1cc387 Add a setting to force the 'Log out' menuitem
I've heard quite a bit of feedback from people who want to log out,
even if they are the sole user on their system. It doesn't seem worth
alienating them over this; so add a setting to make the 'Log out' item
always show up.
https://bugzilla.gnome.org/show_bug.cgi?id=686057
2012-10-31 12:46:49 -04:00
56909d0646 Show 'Log out' in more situations
When the current user is a remote account, or a we are logged in
as root, we should always show 'Log out'.

https://bugzilla.gnome.org/show_bug.cgi?id=686736
2012-10-31 12:45:08 -04:00
04da29c939 appMenu: Update on icon theme changes
While we recreate icons on style changes elsewhere, the faded
icon in the application menu will stick around after icon theme
changes until another application is focused.

https://bugzilla.gnome.org/show_bug.cgi?id=687224
2012-10-31 00:06:46 +01:00
307f7a3024 IM status menu: adjust the combo popup background
The transparent background for available/unavailable IM status menu
makes the text difficult to read.

Simplifies that by using the default combo-popup background instead of a
custom one.

https://bugzilla.gnome.org/show_bug.cgi?id=658091
2012-10-30 22:51:50 +00:00
e80bfa39f5 userMenu: Use "offline" instead of "unavailable"
https://bugzilla.gnome.org/show_bug.cgi?id=687226
2012-10-30 23:01:22 +01:00
29714922ea calendar: Drop unnecessary libedataserverui dependency
The libedataserverui dependency is a relic of the old E-D-S API.
As of 3.6.0, E-D-S now centralizes authentication prompts so clients
don't have to display their own.  This also allows trading the GTK+
main loop for a plain GMainLoop in gnome-shell-calendar-server.c.

https://bugzilla.gnome.org/show_bug.cgi?id=687189
2012-10-30 14:18:39 -04:00
88192114ac NetworkAgent: cancel requests when disabling component
When the NetworkAgent is disabled (for example because the lock screen
is being activated), cancel all modal dialogs.

https://bugzilla.gnome.org/show_bug.cgi?id=685239
2012-10-30 16:34:13 +01:00
9d78208b76 NetworkMenu: don't use a global switch for all VPN connections
Stop pretending that VPN is a NMDevice, and split the useful bits into
a NMConnectionBased interface.
Make each connection have its own switch menu item and handle its own
status, and remove the VPN section title, which is no longer needed.

https://bugzilla.gnome.org/show_bug.cgi?id=682929
2012-10-30 16:08:33 +01:00
d817bf0395 theme: standardize the run dialog text styles a bit
Use the standard color for dialog headings, and use the standard
text style for the entry field. These tweaks make the dialog easier
to read.

https://bugzilla.gnome.org/show_bug.cgi?id=687127
2012-10-30 15:50:05 +01:00
4d51056226 runDialog: Remove "Run" button again
While not in the mockups, it was introduced during review of commit
0c807bddaf after discussion on IRC, but the designers disagree;
remove it again.

https://bugzilla.gnome.org/show_bug.cgi?id=687127
2012-10-30 15:50:05 +01:00
a607174a25 runDialog: Add entry to focus chain
Currently the entry takes the intial key focus, but is not actually
part of the focus chain. Fix that, even though keynav does not work
too well for the dialog anyway, due to the entry consuming tab for
command completion.

https://bugzilla.gnome.org/show_bug.cgi?id=687127
2012-10-30 15:50:05 +01:00
37d6a624b7 Improve the button insensitive style
The current insensitive style for buttons needs to be improved. Right
now we just change the text color: this doesn't make it clear that the
button is actually insensitive.

Instead of just changing the text color, we make the button background
almost transparent. We also make the text color the same as the border
color, use a thinner border.

This patch also simplifies some border rule overwrites to emphasis
only the border width is changed on focus, and makes the button look
closer to the mockups.

https://bugzilla.gnome.org/show_bug.cgi?id=687110
2012-10-30 12:35:48 +00:00
0c807bddaf runDialog: Better match style of other modal dialogs
Update the run dialog to
 - use a proper title
 - use dialog buttons
 - use the standard entry style

https://bugzilla.gnome.org/show_bug.cgi?id=687127
2012-10-29 19:12:12 +01:00
a0470bfc66 UnlockDialog: clear the password on failure
It is wrong, and the user can't correct it because it's obfuscated.
Just let him type it again.

https://bugzilla.gnome.org/show_bug.cgi?id=687132
2012-10-29 17:54:12 +01:00
b9463d23e8 ShellUserVerifier: fix fail counter
If it is updated after checking, it counts the number of failures
not including the current one, so it allows one extra attempt. Instead,
by updating it before checking, we get the expected result of dropping the
curtain at the third password.

https://bugzilla.gnome.org/show_bug.cgi?id=687132
2012-10-29 17:54:12 +01:00
04debd1623 LoginDialog: clear previous auth failed messages when trying again
When the user has the entered the password for the second time
and clicked OK, clear messages from the previous attempt, so any
new failure is shown clearly.

https://bugzilla.gnome.org/show_bug.cgi?id=687132
2012-10-29 17:54:11 +01:00
9d31576cf5 App search: Match GenericName too
This is making shell search results more useful in many cases,
such as 'web', 'browser', spreadsheet'.
https://bugzilla.gnome.org/show_bug.cgi?id=687121
2012-10-29 11:24:36 -04:00
8daca28a90 Updated Slovak translation 2012-10-28 19:50:05 +00:00
9899604261 Updated Slovak translation 2012-10-28 18:27:51 +00:00
687e1eabed Overview: Resize the window title labels on content change
Reposition the window overlay when the title changes, using the current
transformed size of the window clone.
Includes a test that changes title to a string of random length every 3 seconds.

Based on a patch by Alex Hultman <alexhultman@gmail.com>

https://bugzilla.gnome.org/show_bug.cgi?id=620874
2012-10-27 18:18:48 +02:00
eb09f34114 recorder: save recorded video as recent item
Often the first thing a user wants to do after making a recording
is post it somewhere.

This commit adds the video to recently used items, so that it shows
up prominently in open file choosers.

https://bugzilla.gnome.org/show_bug.cgi?id=680647
2012-10-26 13:29:30 -04:00
fbeb446ed7 recorder: rename "filename" property to "file-template"
The filename property is actually a template string with
substitution variables, not a filename.

This commit renames for clarity.

https://bugzilla.gnome.org/show_bug.cgi?id=680647
2012-10-26 13:29:30 -04:00
92033ce0f5 recorder: keep test-recorder alive until done recording
Recording continues for some time after the recorder object
is closed, since closing isn't a synchronous operation.

This commit defers quiting the test-recorder application until
the recording is finished.

https://bugzilla.gnome.org/show_bug.cgi?id=680647
2012-10-26 13:29:30 -04:00
9171bab5e5 recorder: keep recorder object alive until pipeline finishes
We want to make sure the recorder isn't finalized until the
saved recording hits disk.  This means the pipeline object needs
a hard reference on the recorder.

https://bugzilla.gnome.org/show_bug.cgi?id=680647
2012-10-26 13:29:30 -04:00
f9819eb7b0 recorder: Clean up stage lifetime handling
The stage is a floating object. We don't own a reference
to it, so we shouldn't unref it.

This commit removes the erroneous unref call and makes sure
we call clutter_actor_destroy on the stage when we're done
with it.

https://bugzilla.gnome.org/show_bug.cgi?id=680647
2012-10-26 13:29:30 -04:00
85728f0d15 layout: Use a MetaBackgroundActor, not a custom ClutterX11TexturePixmap
While looking at how the plymouth implementation was built, I was so
short-sighted and focused on the string "_XROOTPMAP_ID" that I didn't
realize it was the name of the standard background on the root window.
Remove our own implementation, and switch to using a standard mutter
MetaBackgroundActor.

https://bugzilla.gnome.org/show_bug.cgi?id=682428
2012-10-26 11:54:25 -04:00
9396849d56 message-tray: Restore Fittsability of summary items
The reactive area of tray items should extend to the screen edge. This
regressed when implementing the new tray design, make it work again.

https://bugzilla.gnome.org/show_bug.cgi?id=686474
2012-10-26 16:32:07 +02:00
6f5e5672bb panelMenu: Fix exception when destroying menuless button
There's explicit API to create PanelMenu.Buttons with no menu, so
guard against this case in destroy().

https://bugzilla.gnome.org/show_bug.cgi?id=686763
2012-10-26 15:49:26 +02:00
b936e60876 screenShield: Tweak curtain animation timings
Rationale:
 - Getting something out of the way should be quick;

 - Very few things in the real world move linearly so, linear
   animations, especially for something as big and visible as this,
   felt too artificial;

 - Moving the curtain out should start slower to make it feel like
   having weight (it fills the whole screen after all) but quickly
   accelerate towards the end to make it snappy too.

https://bugzilla.gnome.org/show_bug.cgi?id=686745
2012-10-26 12:56:17 +02:00
fa4bd91213 build: Stop linking gnome-desktop
Since commit 80eac7370e removed the last build-time dependency ages
ago, we only use GnomeDesktop via introspection.
2012-10-26 12:37:33 +02:00
81eeef7d3c messageTray: Hide summary notification immediately when closing the tray
When the summary notification is open when the tray is closed, we end
up with two concurrent animations: the notification fading out, and the
tray moving away from underneath it. Sliding out the tray should be the
primary transition here, so hide the notification immediately to not
draw the user's attention away from it.

https://bugzilla.gnome.org/show_bug.cgi?id=686888
2012-10-25 22:45:36 +02:00
a7b5134820 messageTray: Hide notification close button immediately on click
Having the close button move away from under the pointer after
clicking it is confusing and distracts from the main transition,
which is hiding the notification. Just hide it immediately.

https://bugzilla.gnome.org/show_bug.cgi?id=682237
2012-10-25 22:41:32 +02:00
92a01c67ba messageTray: Don't destroy the notification when clicking on the close button
Clicking on the close button should simply hide the notification.

https://bugzilla.gnome.org/show_bug.cgi?id=682237
2012-10-25 22:41:32 +02:00
71c23613b5 messageTray: Only hide the notification stack on clicking close
Rather than destroying the entire source, which is unintuitive, simply
close the notification. Removing the entire source is still possible
by right-clicking on the summary item and choosing "Remove".

https://bugzilla.gnome.org/show_bug.cgi?id=682237
2012-10-25 22:41:05 +02:00
4f876995de messageTray: make SummaryItem._closeButton public
Use this to show/hide the close button instead of closeButtonVisible.

https://bugzilla.gnome.org/show_bug.cgi?id=682237
2012-10-25 21:45:18 +02:00
9cf4a76196 po: Enforce RTL in fa/ug for messages that might end up as LTR
See commit 8b796d80a7.

https://bugzilla.gnome.org/show_bug.cgi?id=686630
2012-10-25 20:12:25 +02:00
9efe5287e4 gdm: Move logo into the panel
GDM has a 'logo' key in its schema to allow distributors to add
some branding. It is currently placed above the user list, which
no longer works too well since the login screen lost its dialog
window. Display the logo in the top-left corner instead of the
Activities button instead.

https://bugzilla.gnome.org/show_bug.cgi?id=685852
2012-10-25 18:31:16 +02:00
8c4b34de4e messageTray: Fix close button position in RTL locales 2012-10-25 18:31:16 +02:00
93e3559dc3 style: Adjust close button overlap
After changing the button size, we need to adjust the x-offset by
1px (apparently the y-offset already assumed the correct size).
2012-10-25 18:31:16 +02:00
caa0f63e1f style: Fix close button size
The image is actually 32px, so we end up with a slightly fuzzy
button when scaling up to 34px. Don't do that.

Spotted by lamefun.xOr<at>gmail.com

https://bugzilla.gnome.org/show_bug.cgi?id=686574
2012-10-25 18:27:59 +02:00
a4e29e1244 calendar: Handle calendar-server errors
The current code assumes that the GetEvents call will always
receive, causing an exception in the error case.

https://bugzilla.gnome.org/show_bug.cgi?id=686805
2012-10-24 18:26:03 +02:00
599f2f43e3 Revert "screenShield: Connect to the actor's show signal instead of using BEFORE_REDRAW"
This reverts commit bdeb7d86b6.

git bz PEBKAC
2012-10-24 10:17:43 -04:00
3a453c5f73 messageTray: Fix lightbox
Commit 448517032e accidentally reverted the condition for showing
the lightbox. Fix that.

https://bugzilla.gnome.org/show_bug.cgi?id=686728
2012-10-24 09:03:12 +02:00
98b313c75f popupMenu: Overwrite ongoing animations when calling close repeatedly
Currently close() is a no-op when the menu has already been closed.
However, repeated calls could pass different animation parameters.
For instance in the user menu, we try to hide the menu immediately
before locking the screen, to avoid the popup jumping across the
screen while fading out - as we do this from the corresponding
item's activate handler, the closing is still animated if the menu's
own handler (which requests a full animation) is run first.
Fix this by changing close() to overwrite ongoing animations before
bailing out early.

https://bugzilla.gnome.org/show_bug.cgi?id=686484
2012-10-23 22:21:45 +02:00
8b796d80a7 po: Enforce RTL in he for messages that might end up as LTR
As the messages start with a string placeholder that might be
untranslated, we need an explicit mark to ensure that the string
does not end up as LTR.

https://bugzilla.gnome.org/show_bug.cgi?id=686630
2012-10-23 22:21:45 +02:00
719a8908a6 Release 3.7.1
Update NEWS
2012-10-23 21:14:10 +02:00
fed007ecae userMenu: Use LoginManager for suspend
https://bugzilla.gnome.org/show_bug.cgi?id=686482
2012-10-23 21:14:10 +02:00
990443465f powerMenu: Use LoginManager for suspend
https://bugzilla.gnome.org/show_bug.cgi?id=686482
2012-10-23 21:14:10 +02:00
1f183b8a4e loginManager: Add support for suspend()
Logind provides a Suspend method, which we should use instead of
the UPower API when available. Expose this in loginManager, using
the UPower API for the ConsoleKit implementation.

https://bugzilla.gnome.org/show_bug.cgi?id=686482
2012-10-23 21:14:10 +02:00
96556eb959 workspacesView: Add some more spacing between window and workspace thumbs
https://bugzilla.gnome.org/show_bug.cgi?id=582650
2012-10-23 15:12:45 -04:00
3ffeeac577 overview: Reduce space between window picker and dash
Do this in a hacky way by hardcoding this, for now. When we land
search rework, we can fix this.

https://bugzilla.gnome.org/show_bug.cgi?id=582650
2012-10-23 15:12:45 -04:00
d7929a2340 workspacesView: Don't conform to aspect ratio
We want the window picker to use the full height of the area it's given.

https://bugzilla.gnome.org/show_bug.cgi?id=582650
2012-10-23 15:12:45 -04:00
667019a8c1 workspace: Don't relayout windows when zooming workspace thumbnails
It looks ugly and busy to have windows shuffle around when I just wanted
to look at my workspaces.

https://bugzilla.gnome.org/show_bug.cgi?id=582650
2012-10-23 15:12:45 -04:00
d9b46b4782 workspace: Use all available space for windows in window selector
Change the layout strategy to be more like the mockups. With less than
two rows of windows, we try to fit every window in a non-aligned situation;
with more than three rows of windows, we try to fit every window in an
aligned situation.

Based heavily on a patch from Pierre-Eric Pelloux-Prayer <pelloux@gmail.com>

https://bugzilla.gnome.org/show_bug.cgi?id=582650
2012-10-23 15:12:45 -04:00
e249218a9d Allow testing GDM login dialog from the session
Check an environment variable, GDM_GREETER_TEST. If 1, LoginDialog will
skip anything that fails outside a GDM session.
It is therefore possible to test the GDM greeter without installing it
system-wide, by attempting login as the already logged in user (uses the
same code path as the unlock dialog).

https://bugzilla.gnome.org/show_bug.cgi?id=683725
2012-10-23 19:09:00 +02:00
a2b1946b01 workspacesView: Fix updating when number of workspaces changes
Ouch. This went unnoticed for a long time as by default (using
dynamic workspaces) only one workspace is added at a time, which
happens to work fine.

https://bugzilla.gnome.org/show_bug.cgi?id=686487
2012-10-23 18:27:07 +02:00
96a80f7ba0 workspacesView: Fix typo
We want to check for a setting, not the existence of a function.

https://bugzilla.gnome.org/show_bug.cgi?id=686487
2012-10-23 18:27:07 +02:00
9955fbf4f8 Updated Greek translation 2012-10-23 15:41:32 +03:00
14966b0cd0 Telepathy: Set empathy-chat as prefered handler when delegating channels
https://bugzilla.gnome.org/show_bug.cgi?id=686296
2012-10-23 12:37:05 +02:00
6e1a8f16a8 build: Bump gnome-desktop required version for GnomeIdleMonitor 2012-10-23 09:19:03 +02:00
d106191e6a Port to GnomeIdleMonitor
https://bugzilla.gnome.org/show_bug.cgi?id=682224
2012-10-22 12:06:45 -04:00
418cf6281e screenShield: explicitly load gnome-screensaver in fallback mode.
When running gnome-shell from lightDM, gnome-screensaver is no
longer auto-loaded. As a result the dbus calls for Lock user etc
will fail.

https://bugzilla.gnome.org/show_bug.cgi?id=683060
Bug-Ubuntu: https://launchpad.net/bugs/1064354
2012-10-22 11:21:18 -04:00
bdeb7d86b6 screenShield: Connect to the actor's show signal instead of using BEFORE_REDRAW
This should make sure that we grab at the right time while at the same time
not break focus handling.

https://bugzilla.gnome.org/show_bug.cgi?id=684650
2012-10-22 11:20:30 -04:00
a0e0cc1038 workspacesView: Simplify extraWorkspaces handling
https://bugzilla.gnome.org/show_bug.cgi?id=686002
2012-10-21 18:53:48 -04:00
78f6dec73b workspacesView: Don't show workspace switcher when it doesn't make sense
Hide workspace switcher if dynamic workspaces is disabled and number of
workspaces is set to one only, since the user is bound to only one workspace
and showing the switcher is redundant.

Signed-off-by: Seif Lotfy <seif@lotfy.com>
2012-10-20 01:39:52 +02:00
f6458f215f userMenu: Hide menu immediately before suspending
The same logic as for commit 1f30670c1d applies to the case
where we lock the screen before suspending - we don't want the
menu to jump to the opposite screen side to fade out, so remove
the animation altogether.

https://bugzilla.gnome.org/show_bug.cgi?id=686484
2012-10-19 19:09:41 +02:00
c7e0d547c4 Add missing translations for GSetting schema
These are showing in the UI of gnome-tweak-tool.

https://bugzilla.gnome.org/show_bug.cgi?id=686413
2012-10-18 22:58:51 +02:00
fa54cfa0c3 Updated slovak translation 2012-10-18 20:41:36 +02:00
31ea3f737c messageTray: Remove hack for the lack of negative units in libcroco
libcroco now has native support for negative units.

https://bugzilla.gnome.org/show_bug.cgi?id=686240
2012-10-18 12:50:49 -04:00
2a8a8065a8 ScreenShield: implement o.g.ScreenSaver.GetActiveTime
Part of the old gnome-screensaver interface, returns the number of seconds
that the screensaver has been active for.

https://bugzilla.gnome.org/show_bug.cgi?id=686064
2012-10-18 15:30:40 +02:00
d3ba002313 st: Remove unused methods
This reverts commits cd024e21f0 and dc9ad8df80.

https://bugzilla.gnome.org/show_bug.cgi?id=679268
2012-10-17 23:25:56 +02:00
f18fd8d959 userMenu: Rely on automatic texture changes
This reverts commit 6f3cf0ae50.

https://bugzilla.gnome.org/show_bug.cgi?id=679268
2012-10-17 23:25:56 +02:00
d54f7b13fb st-widget: Keep background-image and border-image updated
Currently we miss changes to a file referenced in background-image
or border-image.
Connect to the StTextureCache::texture-file-changed signal to keep
up with file changes and update the drawing state if necessary.

https://bugzilla.gnome.org/show_bug.cgi?id=679268
2012-10-17 23:25:56 +02:00
9c8b75206c st-texture-cache: Add texture-file-changed signal
For textures loaded from files, the cache might hide image changes
by keeping the data of a previous version around indefinitely. For
instance AccountsService will notify of avatar changes, but as new
image is copied over the old one, we will continue to use the old
image data.
Install a file monitor for each file resource we load and clear
the corresponding data from the cache on changes, emitting the
new StTextureCache::texture-file-changed signal.

https://bugzilla.gnome.org/show_bug.cgi?id=679268
2012-10-17 23:25:56 +02:00
15273c7f22 st: Canonicalize URLs in stylesheets
Make _st_theme_resolve_url() a bit smarter by canonicalizing the
resulting path (e.g. resolving references to /./ and /../).

https://bugzilla.gnome.org/show_bug.cgi?id=679268
2012-10-17 23:25:56 +02:00
0ea8217c55 st: Fix handling of file:// URIs in _st_theme_resolve_uri()
https://bugzilla.gnome.org/show_bug.cgi?id=679268
2012-10-17 23:03:07 +02:00
1735f28f5a Remove use of deprecated g_type_init () ...
... and bump GObject requirement accordingly.

https://bugzilla.gnome.org/show_bug.cgi?id=686318
2012-10-17 22:49:32 +02:00
8de4070f7f autorunManager: Stop erring on the side of caution for network mounts
Every mount that we care about that's local should have a GVolume
associated with it.

https://bugzilla.gnome.org/show_bug.cgi?id=686241
2012-10-17 12:24:45 -04:00
45e64f453f runDialog: Use a symbolic icon
Another fallout from the St.IconType changes

https://bugzilla.gnome.org/show_bug.cgi?id=686233
2012-10-17 12:04:28 -04:00
62dc5f2ac6 Updated Serbian translation 2012-10-17 10:09:46 +02:00
86c85a752e loginScreen: Add support for 'disable-restart-buttons'
GDM's GSettings schema contains a 'disable-restart-buttons' key
that currently is only supported by the fallback greeter.
Implement support in the shell greeter as well.

https://bugzilla.gnome.org/show_bug.cgi?id=686247
2012-10-16 22:57:37 +02:00
3de0ebf7fd messageTray: Change timestamp string formats
The timestamps before contained unnecessary information.
Additionally, align the timestamps to be in the middle of the
bubble for design reasons.

https://bugzilla.gnome.org/show_bug.cgi?id=680989
2012-10-16 22:35:10 +02:00
1496ba0bbd messageTray: Clean up "TODO" code
The code here says to remove it after the GNOME3 release. Better late
than never.
2012-10-16 12:20:46 -04:00
f5974f6793 messageTray: Primarily use a GIcon to drive the source's icon
This is a bit of a cleanup since we ported notification icons/secondary
icons to be in the same situation.

https://bugzilla.gnome.org/show_bug.cgi?id=680426
2012-10-16 12:14:16 -04:00
928ea3bb01 messageTray: Use a GIcon for a notification's icon/secondary icon
Using a GIcon instead of an actor means that we can always create
a new icon with the right size from an old icon.

https://bugzilla.gnome.org/show_bug.cgi?id=680426
2012-10-16 12:14:16 -04:00
b7acb1d488 st-texture-cache: Remove load_icon_name
With the St.Icon bug fixed, we have removed the last use of load_icon_name.
Celebration time!
2012-10-16 11:01:25 -04:00
360d94dd67 modalDialog: Remove the fade in buttons code
Due to a typo, it never worked correctly. After a fix-up to the appropriate
method, the behavior is suboptimal, as the buttons only fade in the first time
the modal dialog is constructed. Just remove the fade-in behavior, rather than
keeping this non-working code around.

https://bugzilla.gnome.org/show_bug.cgi?id=677426
2012-10-16 10:49:20 -04:00
671a813135 Updated Japanese translation 2012-10-16 23:30:05 +09:00
c005417f9b Updated Galician translations 2012-10-16 14:22:21 +02:00
b5ec68bda3 Updated Spanish translation 2012-10-16 11:11:40 +02:00
b4ed29094d Updated Belarusian translation. 2012-10-16 12:05:56 +03:00
284ab031b8 Updated Slovenian translation 2012-10-16 08:34:57 +02:00
6ad5064c09 Updated Indonesian translation 2012-10-16 07:15:32 +07:00
3a8e723a36 data: Expose '<Super>F10' shortcut in System Settings
https://bugzilla.gnome.org/show_bug.cgi?id=672909
2012-10-16 01:38:12 +02:00
dd3484b93f dash: Open 'Show Apps' with Super+A
This is a workaround for power users for the "Show Apps" button
placement being too inconvenient to press at the bottom of the
dash favorites list.

Unity also uses Super+A to show the Apps lens.

https://bugzilla.gnome.org/show_bug.cgi?id=685738
2012-10-16 01:38:12 +02:00
205773b700 Bump version to 3.6.1
Update NEWS
2012-10-16 01:34:30 +02:00
f2039070e6 build: Add missing files 2012-10-16 01:34:30 +02:00
cf58a7eafd dash: Reset show-apps label on drag-end
Currently the label for the show-apps button is only updated during
drag operations, so after an item is successfully dropped on the
button, the label will still read "Remove from Favorites".
Fix this by resetting the label on drag-end.

https://bugzilla.gnome.org/show_bug.cgi?id=684627
2012-10-15 23:22:34 +02:00
487749c25b st-entry: Force a relayout if necessary
ClutterText will only queue a relayout after font changes if it has
any contents other than the empty string. As a result, its height
request may change after the first character has been entered. To
avoid this visual glitch, force a relayout on actual font changes.

https://bugzilla.gnome.org/show_bug.cgi?id=685534
2012-10-15 23:01:42 +02:00
48fb16b570 ScreenShield: show the unlock dialog on the primary monitor when using the keyboard
Make ModalDialog.open() accept an optional onPrimary argument, and
pass it when the dialog is activated using ESC or Return.

https://bugzilla.gnome.org/show_bug.cgi?id=685855
2012-10-15 22:45:19 +02:00
f94369dd6e Updated German translation 2012-10-15 20:46:38 +02:00
fc0bd3b9e8 Updated Latvian translation. 2012-10-15 20:15:59 +03:00
19946f1d19 Updated Danish translation 2012-10-15 06:36:57 +02:00
562f56130a Updated Kannada Translation 2012-10-14 23:24:53 +05:30
99f97adfc6 UnlockDialog: reset UI on verification failure
When failing verification, reset the UI to the default pre-password
request state, waiting for the next prompt.

https://bugzilla.gnome.org/show_bug.cgi?id=685441
2012-10-14 18:48:13 +02:00
5ad7db722d ScreenShield: don't allow cancelling the curtain by pressing esc twice in the dialog
If esc is pressed twice in succession in the unlock dialog, the curtain
is cancelled, but the dialog is cleared after the first esc cancels it,
and it's not destroyed and recreated.

https://bugzilla.gnome.org/show_bug.cgi?id=685441
2012-10-14 18:48:13 +02:00
a7d344d287 Make org.gnome.ScreenSaver.SetActive work
The interface was declared to take an unsigned integer instead
of a boolean, as gnome-screensaver does. Due to this,
gnome-screensaver-command --activate or --deactivate does not
work when used with gnome-shell.

https://bugzilla.gnome.org/show_bug.cgi?id=686063
2012-10-13 21:07:08 -04:00
0ac215f9de autorunManager: Fix another StIconType removal regression
https://bugzilla.gnome.org/show_bug.cgi?id=686079
2012-10-13 14:40:51 +02:00
4342155748 dateMenu: Hide "Open Calendar" item if calendar unavailable
The configured calendar application might not actually be installed.
Instead of failing with an error message, hide the menu item altogether
in this case.

https://bugzilla.gnome.org/show_bug.cgi?id=686050
2012-10-13 14:40:51 +02:00
b561694bf0 theme: Remove left-over style 2012-10-13 14:40:51 +02:00
cae9a8d608 Updated Korean translation 2012-10-13 18:56:02 +09:00
88fbdba018 panel: Fix a copy/paste type when dealing with panel corners
Since panel corners are currently square, this doesn't really affect much,
but it's very clear what the code was supposed to be. At the same time,
also fix up a redeclaration with 'let', which technically isnt' kosher.
2012-10-12 17:47:21 -04:00
046067565a workspace: fix typo in removeThumbnails.
https://bugzilla.gnome.org/show_bug.cgi?id=684869
2012-10-12 17:44:10 -04:00
11299d9913 Updated British English translation 2012-10-12 18:57:03 +01:00
42ab233b08 L10N: Updated Persian translation 2012-10-12 20:38:12 +03:30
4a92d7d1b2 st-im-text: Chain up to parent first in dispose()
The actor's GtkIMContext is freed in dispose and reset in unrealize - as
ClutterActor's dispose will unrealize the actor if necessary, chaining
up to the parent after clearing the im context will result in warnings
if the actor is still realized, so chain up first.

https://bugzilla.gnome.org/show_bug.cgi?id=686016
2012-10-12 14:56:34 +02:00
2502ca6ccc tests: Actually call test() in box-layout 2012-10-11 19:27:56 -03:00
651030ba93 layout: Fix an accidental undefined variable error
The layout code was using actorData without defining it first.

https://bugzilla.gnome.org/show_bug.cgi?id=673189
2012-10-11 19:23:40 -03:00
8dfea9566e Updated Norwegian bokmål translation. 2012-10-11 20:25:20 +02:00
8cc54ce2a2 loginDialog: Add focus indication
While keynav works on login screen and unlock dialog, most elements
currently miss a focus indication.

https://bugzilla.gnome.org/show_bug.cgi?id=684730
2012-10-11 19:04:43 +02:00
3abfcda8b5 loginDialog: Use the same focus root for dialog and ctrl-alt-tab
Adding a group to the Ctrl-Alt-Tab popup will also add it to the
focus manager. Due to that, we currently end up with two focus
groups added for the login dialog - an explicit one for the entire
dialog, and an implicit one for the main content group.
When doing keynav, we ascend in the widget hierarchy from the
currently focused actor until we find a valid focus root, so
adding a children of the dialog as focus root breaks keynav to
any actors that are not inside the main content group.
The simple fix is to use the same group in both cases.

https://bugzilla.gnome.org/show_bug.cgi?id=684730
2012-10-11 19:04:43 +02:00
cf0ae8f182 screenShield: Remove erroneous top/bottom padding that may appear
If there are either no resident or persistent notifications, we'll
add some spacing for those boxes that may contain nothing. Make them
invisible to remove the spacing for those elements. It's possible
that we may want to be smarter about this in StBoxLayout to remove
spacing for zero-sized actors, but today is not the day.

https://bugzilla.gnome.org/show_bug.cgi?id=685919
2012-10-11 12:26:00 -03:00
d90bf5c6dc theme: Make the notifications box match mockups better
Adjust font sizes, spacing and padding, and add a box shadow
to the notifications box.

https://bugzilla.gnome.org/show_bug.cgi?id=685919
2012-10-11 12:26:00 -03:00
955b550e95 screenShield: Add some padding between the clock and notifications box
https://bugzilla.gnome.org/show_bug.cgi?id=685919
2012-10-11 12:26:00 -03:00
b3cd46a5c8 screenShield: Align notifications to the left, not the middle
With different lengths of text in notifications, icons aren't
aligned and things look ugly. Put notifications on the left
for now.

https://bugzilla.gnome.org/show_bug.cgi?id=685919
2012-10-11 12:26:00 -03:00
3fdc8bfa3d main: Override focus-change-on-pointer-rest preference
The application menu is currently unusable with non-maximized
windows when using focus-follows-mouse mode. Override mutter's
focus-change-on-pointer-rest preference, so that the actual
focus change is delayed until the pointer stops moving.

https://bugzilla.gnome.org/show_bug.cgi?id=678169
2012-10-11 16:30:33 +02:00
e8f96a6e16 loginDialog: Sync :expanded better with user list visibility
Now that we use a different text style for the username depending on
whether the user list is expanded or not, changing the :expanded style
before the actual transition looks disruptive. Adding the style right
before fading in other items and removing it right after fading them
out gives a better result.

https://bugzilla.gnome.org/show_bug.cgi?id=685201
2012-10-11 16:14:38 +02:00
dc15df1aa7 loginDialog: Remove now unused functions
https://bugzilla.gnome.org/show_bug.cgi?id=685201
2012-10-11 16:14:38 +02:00
05f5fac35b loginDialog: Use the same prompt layout as the unlock dialog
Currently the layout of the password prompt differs slightly between
login dialog and unlock screen - for the former, the prompt is
displayed next to the user avatar, replacing the user name, for
the latter, it is diplayed below both avatar and name.

https://bugzilla.gnome.org/show_bug.cgi?id=685201
2012-10-11 16:14:38 +02:00
5bfcc5392d messageTray: Reset summary after losing focus to outside actor
Currently when the summary boxpointer is ungrabbed automatically
because the keyboard focus was moved outside the message tray
(for instance by selecting the overview search entry or opening
the right-click menu of a dash item), after the popup is hidden
_updateState() will grab focus and show the popup again.
Work around this by unsetting the clicked summary item when losing
focus to an actor outside the message tray.

https://bugzilla.gnome.org/show_bug.cgi?id=685156
2012-10-11 14:28:42 +02:00
d2e830cce3 messageTray: Do not add the tray unconditionally to ctrl-alt-tab
Commit 448517032e added the message tray unconditionally to
the Ctrl-Alt-Tab popup, but while this makes sense for a normal
session, we do not want it in the login screen.
Be a bit more careful where we make the tray available.

https://bugzilla.gnome.org/show_bug.cgi?id=685914
2012-10-11 08:00:55 +02:00
1e942be639 Add a new code style/hacking guide
Written in Markdown style.

Somewhat based off of the gjs code style guide found at
http://git.gnome.org/browse/gjs/plain/doc/Style_Guide.txt.

https://bugzilla.gnome.org/show_bug.cgi?id=661241
2012-10-10 19:11:36 -03:00
576009bad0 theme: Make the selected text color white for all entries
This was accidentally changed when Allan cleaned up the styles for
all entries
2012-10-10 18:21:03 -03:00
f71108a214 extensionPrefs: Remove an extra parameter to set_cell_data_func 2012-10-10 18:21:03 -03:00
7109bd52f2 Updated Thai translation 2012-10-09 18:34:05 +07:00
0f6effa263 Calendar: hide all actions when on the login screen
No events on the login screen, and no opening calendars or
settings either.

https://bugzilla.gnome.org/show_bug.cgi?id=685142
2012-10-08 19:48:44 +02:00
8b0301ed00 Updated Irish translation 2012-10-07 16:21:57 -06:00
4f56fb125e overview: Make sure that we put the desktop clone at the right place
This ensures that the desktop window's smooth fadeout when going to
the overview is in the same spot as the desktop window, which may not
always be at 0, 0.

https://bugzilla.gnome.org/show_bug.cgi?id=681159
2012-10-06 16:07:55 -03:00
6487cd8c6f dash: Only set the label's text when initially showing the label
Having the tooltip change when it's visible looks strange and glitchy.
This also makes sure that "Remove from Favorites" doesn't change, even
when the user removes their mouse cursor from it.

https://bugzilla.gnome.org/show_bug.cgi?id=685313
2012-10-06 16:07:55 -03:00
06e5c25383 dash: Make the dash items accessible
https://bugzilla.gnome.org/show_bug.cgi?id=685313
2012-10-06 16:07:54 -03:00
36e5ae4a25 dash: Construct the label at init
Nothing doesn't use a label, so it doesn't make sense to
lazily initialize it.

https://bugzilla.gnome.org/show_bug.cgi?id=685313
2012-10-06 16:07:54 -03:00
245e43ea8c dash: Make the tooltip and hover effect sync up to the real insensitivity
We shouldn't display "Remove from Favorites" when dropping on the button
will do nothing.

https://bugzilla.gnome.org/show_bug.cgi?id=685313
2012-10-06 16:07:54 -03:00
52871c781a dash: Make the show apps button insensitive when we're dragging a non-favorite
This doesn't handle the tooltip case, unfortunately. That will come in a bit.

https://bugzilla.gnome.org/show_bug.cgi?id=685313
2012-10-06 16:07:54 -03:00
f994ada576 dash: Clean up unused code
https://bugzilla.gnome.org/show_bug.cgi?id=685313
2012-10-06 16:07:53 -03:00
147a6e49dc dash: Refactor some common code
Take the code that gets an app from a "source" that's duplicated
in lots of spots and refactor it out.

https://bugzilla.gnome.org/show_bug.cgi?id=685313
2012-10-06 16:07:53 -03:00
be24ee435c messageTray: Clean up and consolidate code for tween completion
https://bugzilla.gnome.org/show_bug.cgi?id=685341
2012-10-06 16:07:52 -03:00
9fac285b69 messageTray: Fix some artifacts when tweening back from the tray
The math to calculate the clip isn't quite right here -- it overcompensates
in the Y value.

https://bugzilla.gnome.org/show_bug.cgi?id=685341
2012-10-06 16:07:42 -03:00
3ed5f9cd15 gdm: Try harder to move focus to the first user
_moveFocusToItems seems to be called to early causing
clutter_actor_grab_key_focus not to be called.

So queue another attempt with BEFORE_REDRAW priority when
this happens to make sure we actually move the focus sucessfully.

https://bugzilla.gnome.org/show_bug.cgi?id=684650
2012-10-06 18:15:02 +02:00
17e70ff8ec notificationDaemon: Store tray-icon-added/removed ids
This allows extensions to reuse the trayManager.
2012-10-06 16:11:54 +02:00
6dab119650 extensionUtils: Don't warn about missing url
This did not have the desired effect and just produces noise.
2012-10-05 20:35:07 +02:00
f80eb89d57 jhbuildrc-gnome-shell: switch to the GNOME 3.8 moduleset
Now that GNOME 3.6 is out, the default development platform
should be the 3.8 moduleset.
2012-10-05 13:59:57 -04:00
f6c2902fe4 gnome-shell-build-setup.sh: Fix warning about git-bz symlink
The warning when changing the git-bz symlink triggered on
initial install. Suppress that.
2012-10-05 13:57:31 -04:00
1e890a8a0a gnome-shell-build-setup.sh: install libdb on Fedora 18.
For F18, db4-devel doesn't install the main -ldb and db.h any more,
so we need the libdb-devel package and Berkeley DB 5.
2012-10-05 13:52:41 -04:00
aa1a84e677 gnome-shell-build-setup.sh: add a note about removing install directory
If the user relocates from ~/gnome-shell to ~/gnome, they need to
remove the install directory or left-over paths will cause problems.
2012-10-05 13:35:58 -04:00
9395f310d6 extensionUtils: don't log verbosely on missing extension directory
A missing extension directory isn't worth debug spew, so check
if the error when reading the extension directory is NOT_FOUND,
and if so, suppress output.

https://bugzilla.gnome.org/show_bug.cgi?id=685466
2012-10-05 13:08:02 -04:00
b99bb3d4bb gnome-shell-build-setup.sh: Move default directory to ~/gnome
gnome-shell-build-setup.sh is generally useful for working on GNOME.
If moving on from hacking on gnome-shell to some other module,
having the checkout location be ~/gnome-shell is a little odd and
cumbersome, so start out checking things out into ~/gnome/source
and installing them into ~/gnome/install.

Add a warning if the old ~/gnome-shell exists to avoid unnecessary
checking out of every module again.

https://bugzilla.gnome.org/show_bug.cgi?id=685355
2012-10-05 13:08:02 -04:00
4f66f096ff userMenu: Ignore 'lock-enabled' setting for user switching
The preference controls whether the screen should be locked when
the screensaver is activated, not whether the screen should be
locked at all. In particular after having switched to a different
user, log out should not automatically switch back to the unlocked
session, so always activate the lock when user switching.

https://bugzilla.gnome.org/show_bug.cgi?id=685536
2012-10-05 16:15:47 +02:00
7f5f2284f3 Updated Hebrew tranlation. 2012-10-05 11:02:25 +02:00
59f9f4fca1 Updated Hebrew translation. 2012-10-05 11:02:25 +02:00
55284e418c modalDialog: Add alternative keys to activate default
Currently Return is used to activate the default button of a modal
dialog if no key is specified. It makes sense to allow alternatives
as the keypad's Enter key as well in this case.

https://bugzilla.gnome.org/show_bug.cgi?id=685511
2012-10-04 21:18:54 +02:00
20d4ffde6e loginDialog: Rely on default button for activation
Currently the default action is performed twice when pressing Return
in the login dialog, once in response to the entry's 'activate' signal,
and again by activating the default button. Usually this is not a
problem, as the second invocation is simply ignored, however it breaks
the case where multiple consecutive questions are asked (e.g. username
and password in the 'Not listed' case).
Fix the problem by not handling the 'activate' signal at all.

https://bugzilla.gnome.org/show_bug.cgi?id=685511
2012-10-04 21:18:54 +02:00
78e5d4df9d Update French translation 2012-10-04 15:34:55 +02:00
1118ec9653 GActionMuxer: disconnect group signals on finalize
The signals for the action group were being disconnected when the action
group was explicitly removed from the GActionMuxer but the same was not
being done when it was finalized.

This means that a change in the state of an action group that used to be
associated with a finalized GActionMuxer would result in a crash.  This
would happen for stateful application actions after closing a window.

https://bugzilla.gnome.org/show_bug.cgi?id=681399
2012-10-04 12:03:23 +02:00
7d6c85be42 messageTray: Don't animate the desktop clone for a failed tray grab
If we fail to grab for the message tray, we shouldn't be animating
the desktop clone. This is a regression from commit fe124e6.

https://bugzilla.gnome.org/show_bug.cgi?id=685342
2012-10-04 04:02:16 -03:00
0192a6cb12 unlockDialog: Make the prompt entry insensitive while logging in
This ensures that the user can't confusingly edit the entry while
waiting for a response.

https://bugzilla.gnome.org/show_bug.cgi?id=685444
2012-10-04 04:02:16 -03:00
0e01a81219 userMenu: Remove 'Switch Session' item
GDM does not allow concurrent logins of a single user, so making
'Switch Session' a user switch operation does not work - in order
to choose a different session, users have to log out.
Rather than making 'Switch Session' an alias of 'Log out' (which
is available anyway when multiple sessions are defined), remove
the item altogether - 'Switch Session' suggests an operation that
does not loose state, and we currently favor 'Switch Session' over
'Switch User', so on systems that have both multiple users and
multiple sessions, the latter would become unavailable.

https://bugzilla.gnome.org/show_bug.cgi?id=685062
2012-10-04 08:57:55 +02:00
36edf20273 gnome-shell-build-setup.sh: Add additional system packages
Add system packages for asn1Parser, cracklib, ruby, wireless-tools,
libsystemd-login.

https://bugzilla.gnome.org/show_bug.cgi?id=685352
2012-10-03 16:16:52 -04:00
a1bf19dbdf ShellUserVerifier: fix typo in function name, caught on auth error
https://bugzilla.gnome.org/show_bug.cgi?id=685434
2012-10-03 22:07:27 +02:00
0ad739e78b autorunManager: Don't try to scan remote filesystems
Content-Type scanning can be super expensive. The autorun manager is meant
for local filesystems that are plugged into a USB port or similar, not
remote NFS or sshfs mounts.

https://bugzilla.gnome.org/show_bug.cgi?id=684093
2012-10-03 14:36:37 -03:00
ff9509b901 autorunManager: Don't scan the filesystem if autorun is disabled
Content-Type scanning can be expensive, and the user disabled autorun, so...

https://bugzilla.gnome.org/show_bug.cgi?id=684093
2012-10-03 14:34:52 -03:00
7e496b1979 autorunManager: Clean up mount operations
Add a processMount function that's shared between mount scanning
and hotplug.

https://bugzilla.gnome.org/show_bug.cgi?id=684093
2012-10-03 14:34:52 -03:00
448517032e messageTray: Show the message tray in Ctrl+Alt+Tab outside of the overview
Since the message tray is accessible outside of the overview, it doesn't
make sense to show it only there.

https://bugzilla.gnome.org/show_bug.cgi?id=684633
2012-10-03 13:04:47 -03:00
a171e92e6c ctrlAltTab: Only set the stage focus mode when we're focusing a widget
This ensures that we don't accidentally lose focus when switching to
the message tray.

https://bugzilla.gnome.org/show_bug.cgi?id=684633
2012-10-03 13:04:46 -03:00
06e9bf9b0a gnome-shell-build-setup.sh: install git-bz
Someone starting gnome-shell development work needs git-bz set up.
Install it into the user account much like we install jhbuild.

(Getting git-bz packaged into distributions would be better, but
this improves thing easily.)

https://bugzilla.gnome.org/show_bug.cgi?id=685354
2012-10-03 09:49:39 -04:00
7d9ec8cea0 jhbuildrc-gnome-shell: add ignore_suggests = True
Adding ignore_suggests = True avoids building some large modules like
evolution speeding the build. But more importantly, it removes
gnome-disk-utility from the build. gnome-disk-utility has a hard
dependency on udisks-1.99, which is not found on anything but the
latest systems.

https://bugzilla.gnome.org/show_bug.cgi?id=685353
2012-10-03 09:49:39 -04:00
5b4553ff0c Revert "st-texture-cache: Remove load_icon_name"
This reverts commit 8b6df2e23f.

I shouldn't be going around removing API in minor releases.
Sorry about that.
2012-10-02 21:40:40 -03:00
d4ce7aef59 dnd: Fix regression from e6fd2be
We need to fetch the actor's parent before we reparent, otherwise
we won't put it back properly at the end.
2012-10-02 21:16:28 -03:00
8b6df2e23f st-texture-cache: Remove load_icon_name
With the St.Icon bug fixed, we have removed the last use of load_icon_name.
Celebration time!
2012-10-02 18:42:42 -03:00
e2ff5846df wanda: Use an StIcon again for createIcon
With the DND bug fixed, this is no longer necessary.
2012-10-02 18:42:41 -03:00
24efeff788 Really hide 'Show Keyboard Layout' on the lock screen
We have to set _showLayoutItem.actor.visible,
not _showLayoutItem.visible.
2012-10-02 15:34:10 -04:00
4f359e62df workspaceThumbnail: Fix the dynamic workspaces check for dragging
Be able to move windows between workspaces with static workspaces
enabled; the incorrect check here simply disabled all dragging
operations.

https://bugzilla.gnome.org/show_bug.cgi?id=684641
2012-10-02 16:15:23 -03:00
e6fd2bed4d dnd: Add drag actors to the stage before querying their sizes
Drag actors may be St widgets, which require that they're added
to the stage before they have a size.

https://bugzilla.gnome.org/show_bug.cgi?id=684888
2012-10-02 16:15:23 -03:00
9fb6510135 Updated Bulgarian translation 2012-10-02 05:38:07 +03:00
7206b61838 dash: Make padding even on the top/bottom of the dash
When Florian landed the new dash container to show the all apps button
always, he got the math wrong -- he forgot to add padding around the
container, and used the height of the box to calculate a y2 position,
rather than the y2 position of the box.

https://bugzilla.gnome.org/show_bug.cgi?id=684619
2012-10-01 06:49:13 -03:00
e8ab0b3e8f Updated Lithuanian translation 2012-09-30 12:31:59 +03:00
843788580e [l10n] Updated Italian translation. 2012-09-29 18:09:10 +02:00
4ceb3d890d Updated Norwegian bokmål translation 2012-09-28 16:10:46 +02:00
3f8995b25e Updated Danish translation 2012-09-27 22:54:39 +02:00
37e0cefc79 Updated Lithuanian translation 2012-09-27 22:26:37 +03:00
f026741dbb Updated Russian translation 2012-09-27 16:38:39 +04:00
044b121e01 Updated Serbian translation 2012-09-27 05:28:17 +02:00
d57c3b4f89 Updated Czech translation 2012-09-26 22:40:49 +02:00
960f7d5f2e Updated Hungarian translation 2012-09-26 16:12:46 +02:00
2f61381651 Updated Belarusian translation. 2012-09-26 15:06:04 +03:00
ba6e931e21 Updated Traditional Chinese translation(Hong Kong and Taiwan) 2012-09-26 11:53:23 +08:00
440aa0d369 Update Simplified Chinese Translations 2012-09-26 08:48:31 +08:00
f42d4b5fa2 [l10n] Updated Catalan (Valencian) translation 2012-09-26 01:29:34 +02:00
5d9fa2c484 [l10n] Updated Catalan translation 2012-09-26 01:29:28 +02:00
f5c86fa171 Added uk translation 2012-09-25 21:18:28 +03:00
f9019ce62d Update French translation 2012-09-25 17:28:22 +02:00
ee485e1728 Updated Polish translation 2012-09-25 15:14:11 +02:00
8f2a6f8387 Bump version to 3.6.0
Update NEWS
2012-09-25 12:59:31 +02:00
908bf3b117 volume: Fix initial visibility of input
Currently the visibility of input volume is only updated when a stream
is added/removed - apparently no one noticed until now, as in the normal
user session we get away with this as long as we have some startup sound,
but this is not the case in the lock screen, so we may end up showing
input volume incorrectly.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

https://bugzilla.gnome.org/show_bug.cgi?id=682544
2012-09-04 23:38:46 +02:00
ec4f6b7f91 Updated Hungarian translation 2012-09-04 15:54:39 +02:00
164 changed files with 46350 additions and 40121 deletions

1
.gitignore vendored
View File

@ -16,6 +16,7 @@ config.log
config.status
config
configure
data/50-gnome-shell-*.xml
data/gnome-shell.desktop
data/gnome-shell.desktop.in
data/gnome-shell-extension-prefs.desktop

331
HACKING Normal file
View File

@ -0,0 +1,331 @@
Coding guide
============
Our goal is to have all JavaScript code in GNOME follow a consistent style. In
a dynamic language like JavaScript, it is essential to be rigorous about style
(and unit tests), or you rapidly end up with a spaghetti-code mess.
A quick note
------------
Life isn't fun if you can't break the rules. If a rule seems unnecessarily
restrictive while you're coding, ignore it, and let the patch reviewer decide
what to do.
Indentation and whitespace
--------------------------
Use four-space indents. Braces are on the same line as their associated
statements. You should only omit braces if *both* sides of the statement are
on one line.
* One space after the `function` keyword. No space between the function name
* in a declaration or a call. One space before the parens in the `if`
* statements, or `while`, or `for` loops.
function foo(a, b) {
let bar;
if (a > b)
bar = do_thing(a);
else
bar = do_thing(b);
if (var == 5) {
for (let i = 0; i < 10; i++) {
print(i);
}
} else {
print(20);
}
}
Semicolons
----------
JavaScript allows omitting semicolons at the end of lines, but don't. Always
end statements with a semicolon.
js2-mode
--------
If using Emacs, do not use js2-mode. It is outdated and hasn't worked for a
while. emacs now has a built-in JavaScript mode, js-mode, based on
espresso-mode. It is the de facto emacs mode for JavaScript.
File naming and creation
------------------------
For JavaScript files, use lowerCamelCase-style names, with a `.js` extension.
We only use C where gjs/gobject-introspection is not available for the task, or
where C would be cleaner. To work around limitations in
gjs/gobject-introspection itself, add a new method in `shell-util.[ch]`.
Like many other GNOME projects, we prefix our C source filenames with the
library name followed by a dash, e.g. `shell-app-system.c`. Create a
`-private.h` header when you want to share code internally in the
library. These headers are not installed, distributed or introspected.
Imports
-------
Use UpperCamelCase when importing modules to distinguish them from ordinary
variables, e.g.
const GLib = imports.gi.GLib;
Imports should be categorized into one of two places. The top-most import block
should contain only "environment imports". These are either modules from
gobject-introspection or modules added by gjs itself.
The second block of imports should contain only "application imports". These
are the JS code that is in the gnome-shell codebase,
e.g. `imports.ui.popupMenu`.
Each import block should be sorted alphabetically. Don't import modules you
don't use.
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const St = imports.gi.St;
const Main = imports.ui.main;
const Params = imports.misc.params;
const Tweener = imports.ui.tweener;
const Util = imports.misc.util;
The alphabetical ordering should be done independently of the location of the
location. Never reference `imports` in actual code.
Constants
---------
We use CONSTANTS_CASE to define constants. All constants should be directly
under the imports:
const MY_DBUS_INTERFACE = 'org.my.Interface';
Variable declaration
--------------------
Always use either `const` or `let` when defining a variable.
// Iterating over an array
for (let i = 0; i < arr.length; ++i) {
let item = arr[i];
}
// Iterating over an object's properties
for (let prop in someobj) {
...
}
If you use "var" then the variable is added to function scope, not block scope.
See [What's new in JavaScript 1.7](https://developer.mozilla.org/en/JavaScript/New_in_JavaScript/1.7#Block_scope_with_let_%28Merge_into_let_Statement%29)
Classes
-------
There are many approaches to classes in JavaScript. We use our own class framework
(sigh), which is built in gjs. The advantage is that it supports inheriting from
GObjects, although this feature isn't used very often in the Shell itself.
const IconLabelMenuItem = new Lang.Class({
Name: 'IconLabelMenuItem',
Extends: PopupMenu.PopupMenuBaseItem,
_init: function(icon, label) {
this.parent({ reactive: false });
this.addActor(icon);
this.addActor(label);
},
open: function() {
log("menu opened!");
}
});
* 'Name' is required. 'Extends' is optional. If you leave it out, you will
automatically inherit from Object.
* Leave a blank line between the "class header" (Name, Extends, and other
things) and the "class body" (methods). Leave a blank line between each
method.
* No space before the colon, one space after.
* No trailing comma after the last item.
* Make sure to use a semicolon after the closing paren to the class. It's
still a giant function call, even though it may resemble a more
conventional syntax.
GObject Introspection
---------------------
GObject Introspection is a powerful feature that allows us to have native
bindings for almost any library built around GObject. If a library requires
you to inherit from a type to use it, you can do so:
const MyClutterActor = new Lang.Class({
Name: 'MyClutterActor',
Extends: Clutter.Actor,
vfunc_get_preferred_width: function(actor, forHeight) {
return [100, 100];
},
vfunc_get_preferred_height: function(actor, forWidth) {
return [100, 100];
},
vfunc_paint: function(actor) {
let alloc = this.get_allocation_box();
Cogl.set_source_color4ub(255, 0, 0, 255);
Cogl.rectangle(alloc.x1, alloc.y1,
alloc.x2, alloc.y2);
}
});
Translatable strings, `environment.js`
--------------------------------------
We use gettext to translate the GNOME Shell into all the languages that GNOME
supports. The `gettext` function is aliased globally as `_`, you do not need to
explicitly import it. This is done through some magic in the
[environment.js](http://git.gnome.org/browse/gnome-shell/tree/js/ui/environment.js)
file. If you can't find a method that's used, it's probably either in gjs itself
or installed on the global object from the Environment.
Use 'single quotes' for programming strings that should not be translated
and "double quotes" for strings that the user may see. This allows us to
quickly find untranslated or mistranslated strings by grepping through the
sources for double quotes without a gettext call around them.
`actor` and `_delegate`
-----------------------
gjs allows us to set so-called "expando properties" on introspected objects,
allowing us to treat them like any other. Because the Shell was built before
you could inherit from GTypes natively in JS, we usually have a wrapper class
that has a property called `actor`. We call this wrapper class the "delegate".
We sometimes use expando properties to set a property called `_delegate` on
the actor itself:
const MyClass = new Lang.Class({
Name: 'MyClass',
_init: function() {
this.actor = new St.Button({ text: "This is a button" });
this.actor._delegate = this;
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
},
_onClicked: function(actor) {
actor.set_label("You clicked the button!");
}
});
The 'delegate' property is important for anything which trying to get the
delegate object from an associated actor. For instance, the drag and drop
system calls the `handleDragOver` function on the delegate of a "drop target"
when the user drags an item over it. If you do not set the `_delegate`
property, your actor will not be able to be dropped onto.
Functional style
----------------
JavaScript Array objects offer a lot of common functional programming
capabilities such as forEach, map, filter and so on. You can use these when
they make sense, but please don't have a spaghetti mess of function programming
messed in a procedural style. Use your best judgment.
Closures
--------
`this` will not be captured in a closure, it is relative to how the closure is
invoked, not to the value of this where the closure is created, because "this"
is a keyword with a value passed in at function invocation time, it is not a
variable that can be captured in closures.
All closures should be wrapped with a Lang.bind.
const Lang = imports.lang;
let closure = Lang.bind(this, function() { this._fnorbate(); });
A more realistic example would be connecting to a signal on a method of a
prototype:
const Lang = imports.lang;
const FnorbLib = imports.fborbLib;
const MyClass = new Lang.Class({
_init: function() {
let fnorb = new FnorbLib.Fnorb();
fnorb.connect('frobate', Lang.bind(this, this._onFnorbFrobate));
},
_onFnorbFrobate: function(fnorb) {
this._updateFnorb();
}
});
Object literal syntax
---------------------
In JavaScript, these are equivalent:
foo = { 'bar': 42 };
foo = { bar: 42 };
and so are these:
var b = foo['bar'];
var b = foo.bar;
If your usage of an object is like an object, then you're defining "member
variables." For member variables, use the no-quotes no-brackets syntax: `{ bar:
42 }` `foo.bar`.
If your usage of an object is like a hash table (and thus conceptually the keys
can have special chars in them), don't use quotes, but use brackets: `{ bar: 42
}`, `foo['bar']`.
Getters, setters, and Tweener
-----------------------------
Getters and setters should be used when you are dealing with an API that is
designed around setting properties, like Tweener. If you want to animate an
arbitrary property, create a getter and setter, and use Tweener to animate the
property.
const ANIMATION_TIME = 2000;
const MyClass = new Lang.Class({
Name: 'MyClass',
_init: function() {
this.actor = new St.BoxLayout();
this._position = 0;
},
get position() {
return this._position;
},
set position(value) {
this._position = value;
this.actor.set_position(value, value);
}
});
let myThing = new MyClass();
Tweener.addTween(myThing,
{ position: 100,
time: ANIMATION_TIME,
transition: 'easeOutQuad' });

View File

@ -13,6 +13,7 @@ EXTRA_DIST = \
DIST_EXCLUDE = \
.gitignore \
gnome-shell.doap \
HACKING \
MAINTAINERS \
tools/build/*

184
NEWS
View File

@ -1,3 +1,187 @@
3.7.1
=====
* Add shortcut to open application view directly [Jeremy; #685738]
* Expose '<Super>F10' shortcut in System Settings [Florian; #672909]
* Clean up timestamp format in chat notifications [Carlos; #680989]
* loginScreen: Add support for 'disable-restart-buttons' [Florian; #686247]
* Update textures automatically on file changes [Florian; #679268]
* Implement org.gnome.ScreenSaver.GetActiveTime [Giovanni; #686064]
* Add missing translations for GSetting schema [Giovanni; #686413]
* Hide workspace switcher completely when it's not necessary [Seif; #686483]
* Explicitly load gnome-screensaver when not running GDM [Tim; #683060]
* Port to GnomeIdleMonitor [Jasper; #682224]
* Set Empathy as preferred handler when delegating channels [Xavier; #686296]
* Allow testing GDM login dialog from the session [Giovanni; #683725]
* Use all available space for windows in window picker [Jasper, Pierre-Eric;
#582650]
* Use logind for suspend if available [Florian; #686482]
* Misc. fixes and cleanups [Jasper, Florian, Adel, Rui; #677426, #680426,
#686233, #686241, #686318, #686240, #686484, #686002, #684650, #686487]
Contributors:
Jeremy Bicha, Giovanni Campagna, Xavier Claessens, Adel Gadllah, Seif Lotfy,
Tim Lunn, Rui Matos, Florian Müllner, Pierre-Eric Pelloux-Prayer,
Carlos Soriano, Jasper St. Pierre
Translations:
Andika Triwidada [id], Matej Urbančič [sl], Ihar Hrachyshka [be],
Daniel Mustieles [es], Fran Diéguez [gl], Takayuki KUSANO [ja],
Мирослав Николић [sr, sr@latin], Dušan Kazik [sk], Tom Tryfonidis [el]
3.6.1
=====
* dash: Make padding even on the top/bottom of the dash [Jasper; #684619]
* Fix a crash when dragging search results [Jasper; #684888]
* workspaceThumbnail: Fix dragging with static workspaces [Jasper; #684641]
* Really hide 'Show Keyboard Layout' on the lock screen [Matthias]
* Misc. improvements to jhbuild setup [Owen; #685352, #685353, #685354, #685355]
* Show message tray in Ctrl+Alt+Tab outside of the overview [Jasper, Florian;
#684633, #685914]
* Disable hotplug sniffer on remote filesystems [Jasper; #684093]
* userMenu: Remove 'Switch Session' item [Florian; #685062]
* unlockDialog: Make prompt entry insensitive while logging in [Jasper; #685444]
* messageTray: Don't animate desktop clone for failed grabs [Jasper; #685342]
* Fix crash on dragging windows between workspaces [Ryan; #681399]
* userMenu: Ignore 'lock-enabled' setting for user switching [Florian; #685536]
* gdm: Fix key-focus on first user [Adel; #684650]
* Make grid button insensitive when dragging non-favorites [Jasper; #685313]
* Calendar: hide all actions when on the login screen [Matthias; #685142]
* Adapt unlock dialog layout for the login screen [Florian; #685201]
* Make focus-follows-mouse work better with Shell UI [Florian; #678169]
* Improve look of screen shield [Jasper; #685919]
* Fix keynav in the login screen [Florian; #684730]
* dateMenu: Hide "Open Calendar" item if calendar unavailable [Florian; #686050]
* unlockDialog: Reset UI on verification failure [Giovanni; #685441]
* Show unlock dialog on primary monitor when using keynav [Giovanni; #685855]
* Fix height changes of entries when entering text [Florian; #685534]
* Fix show-apps label after successful drags [Florian; #684627]
* Misc. bugfixes and cleanups [Jasper, Olivier, Florian, Owen, Adel, Tanner, Tim, Matthias; #685434, #685511, #685466, #685341, #685156, #681159, #673189, #686016, 684869, #686079, #686063
Contributors:
Jasper St. Pierre
Matthias Clasen
Owen Taylor
Olivier Blin
Florian Müllner
Ryan Lortie
Adel Gadllah
Tanner Doshier
Tim Lunn
Giovanni Campagna
Translations:
Tobias Endrigkeit [de], Rudolfs Mazurs [lv], Ask H. Larsen [da],
Shankar Prasad [kn], Changwoo Ryu [ko], Chris Leonard [en_GB],
Arash Mousavi [fa], Theppitak Karoonboonyanan [th], Seán de Búrca [ga],
Yaron Shahrabani [he], Alexander Shopov [bg], Žygimantas Beručka [lt],
Milo Casagrande [it], Kjartan Maraas [nb], Kris Thomsen [da],
Aurimas Černius [lt], Yuri Myasoedov [ru], Мирослав Николић [sr],
Marek Černocký [cs], Gabor Kelemen [hu], Ihar Hrachyshka [be],
Chao-Hsiung Liao [zh_HK, zh_TW], Eleanor Chen [zh_CN],
Carles Ferrando [ca@valencia], Vicent Cubells [ca], Daniel Korostil [uk],
Alexandre Franke [fr], Piotr Drąg [pl]
3.6.0
=====
* keyboard: Make input source items accessible [Florian; #684462]
* Don't show network dialogs in the lock screen [Giovanni; #684384]
* popupMenu: Fix initial visibility of settings items [Florian; #684473]
* userMenu: Close menu immediately on user/session switch [Florian; #684459]
* Fix alignment of search section headers in RTL locales [Florian; #684379]
* screenShield: Fix unlock animation [Florian; #684591]
* Don't open the tray from a dwell while in a modal grab [Jasper; #684458]
* userMenu: Fix texture updates on icon changes [Florian; #679268]
* Fix a11y support in the login screen [Florian, Ray; #684727, #684728, #684748]
* Make On-Screen-Keyboard usable with new message tray [Giovanni, Florian;
#683546]
* Fix initial visibility of input volume in lock-screen [Florian; #684611]
Contributors:
Giovanni Campagna, Florian Müllner, Jasper St. Pierre, Ray Strode
Translations:
Matej Urbančič [sl], Dr.T.Vasudevan [ta], Piotr Drąg [pl], A S Alam [pa],
Alexander Shopov [bg], Nilamdyuti Goswami [as], Chandan Kumar [hi],
Khaled Hosny [ar], Ibrahim Saed [ar], Sandeep Sheshrao Shedmake [mr],
Tom Tryfonidis [el], Theppitak Karoonboonyanan [th], Alexandre Franke [fr],
Fran Diéguez [gl], Gabor Kelemen [hu], Ani Peter [ml], Daniel Mustieles [es],
Мирослав Николић [sr, sr@latin], Duarte Loreto [pt], ManojKumar Giri [or],
Ihar Hrachyshka [be], Aurimas Černius [lt], Djavan Fagundes [pt_BR],
Changwoo Ryu [ko], Bruce Cowan [en_GB], Kris Thomsen [da], Gil Forcada [ca],
Yaron Shahrabani [he], Milo Casagrande [it], Ville-Pekka Vainio [fi],
YunQiang Su [zh_CN], Carles Ferrando [ca@valencia], Mario Blättermann [de],
Rajesh Ranjan [hi], Yuri Myasoedov [ru], Rūdolfs Mazurs [lv],
Jiro Matsuzawa [ja], Mattias Põldaru [et], Timur Zhamakeev [ky],
Petr Kovar [cs], Chao-Hsiung Liao [zh_HK,zh_TW], Andika Triwidada [id]
3.5.92
======
* Login/UnlockDialog: Don't reset immediately if auth fails [Giovanni; #682544]
* Allow changing session mode at runtime [Jasper, Giovanni; #683156]
* Add zoom out animation on login [Jasper; #683170]
* Bluetooth: don't restrict the length of non numeric PINs [Giovanni; #683356]
* Force chat notification to stay open when focusing entry [Debarshi; #682236]
* Make sure the screen is fully locked before suspending [Giovanni; #683448]
* st-texture-cache: Fix a case of distorted textures [Florian; #683483]
* popupSubMenu: Fix padding for non-scrolled submenus [Florian; #683009]
* popupMenu: Fix width changes on submenu open/close [Florian; #683485]
* boxpointer: Avoid malformed boxpointer arrow [Debarshi; #680077]
* Change stage background color to grey [Adel; #683514]
* messageTray: Update style of summary counters [Debarshi; #682891]
* Don't fail if a legacy tray icon has no WM_CLASS [Giovanni; #683724]
* PolkitAgent: Fix a crash if there is no avatar [Giovanni; #683707]
* Hide the a11y menu in the lock screen, but show it in the login screen
[Giovanni; #682542]
* Fix show-apps button dropping off the dash [Florian; #683340]
* Fix committing strings to shell entries from input method [Florian; #658325]
* Make IBus display strings consistent with control-center [Rui; #683124]
* Fix missing short codes for some input sources [Rui; #683613]
* Remove support for long-press from entry context menus [Jasper; #683509]
* screenShield: Add box-shadow to the shield [Florian]
* Don't show a right-click menu for the hotplug source [Jasper; #683438]
* Fix extension styling [Giovanni; #682128]
* Fix on-screen keyboard not working with system-modal dialogs
[Giovanni; #664309]
* Fix insensitive styling for popup menu items [Giovanni; #683988]
* Disable the message tray dwell when the user is interacting [Owen; #683811]
* Animate going from the unlock dialog to the lock screen [Giovanni; #681143]
* Autostart fprintd when necessary [Ray; #683131]
* UnlockDialog: Allow typing before the first PAM question [Giovanni; #681576]
* Make Return key dismiss screenshield [Ray; #683889]
* Fix keyboard navigation in the message tray [Florian; #682243]
* Remove the places & devices search provider [Giovanni; #683506]
* Enable hot corner while the message tray is up [Florian; #682255]
* Port screen recorder to new GStreamer vp8enc API [Adel; #684206]
* Fix fish flickering [Giovanni; #684154]
* Fix extension ordering with !important [Jasper; #684163]
* Allow the shell to run without the screenshield [Giovanni; #683060]
* Add menu items for IBus Anthy's InputMode, TypingMode [Rui; #682314]
* Improve transition to the login dialog [Jasper; #682428]
* Keep unlock dialog around until shield animation ends [Florian; #684342]
* Expose shell keybindings in System Settings [Florian; #671010]
* Misc. bugfixes and cleanups [Debarshi, Florian, Giovanni, Jasper, Rico, Rui;
#672790, #677434, #683305, #683357, #683369, #683377, #683378, #683400,
#683449, #683472, #683482, #683487, #683488, #683526, #683529, #683546,
#683583, #683628, #683705, #683982, #683989, #684035, #684036, #684040,
#684162, #684214, #684343]
Contributors:
Giovanni Campagna, Adel Gadllah, Rui Matos, Florian Müllner, Debarshi Ray,
Jasper St. Pierre, Ray Strode, Owen Taylor, Rico Tzschichholz
Translations:
Gabor Kelemen [hu], Piotr Drąg [pl], Khaled Hosny [ar],
Мирослав Николић [sr, sr@latin], Chao-Hsiung Liao [zh_HK, zh_TW],
Bruce Cowan [en_GB], Dirgita [id], Tom Tryfonidis [el], Timo Jyrinki [fi],
Adorilson Bezerra [pt_BR], Arash Mousavi [fa], Matej Urbančič [sl],
Christian Kirbach [de], Yaron Shahrabani [he], Ihar Hrachyshka [be],
Changwoo Ryu [ko], Duarte Loreto [pt], Theppitak Karoonboonyanan [th],
Nilamdyuti Goswami [as], Sandeep Sheshrao Shedmake [mr],
Alexandre Franke [fr], Ivaylo Valkov [bg], tuhaihe [zh_CN],
Yuri Myasoedov [ru], Aurimas Černius [lt], Andika Triwidada [id],
Rajesh Ranjan [hi], Sweta Kothari [gu], Daniel Mustieles [es],
Fran Diéguez [gl], Praveen Illa [te]
3.5.91
======
* Improve modal dialog styling of network secret prompts [Jasper; #682412]

View File

@ -153,8 +153,6 @@ NP_Initialize(NPNetscapeFuncs *pfuncs, NPPluginFuncs *plugin)
/* global initialization routine, called once when plugin
is loaded */
g_type_init ();
g_debug ("plugin loaded");
memcpy (&funcs, pfuncs, sizeof (funcs));

View File

@ -1,5 +1,5 @@
AC_PREREQ(2.63)
AC_INIT([gnome-shell],[3.5.91],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_INIT([gnome-shell],[3.7.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_SRCDIR([src/shell-global.c])
@ -52,7 +52,7 @@ AC_MSG_CHECKING([for GStreamer (needed for recording functionality)])
if $PKG_CONFIG --exists gstreamer-1.0 '>=' $GSTREAMER_MIN_VERSION ; then
AC_MSG_RESULT(yes)
build_recorder=true
recorder_modules="gstreamer-1.0 gstreamer-base-1.0 x11"
recorder_modules="gstreamer-1.0 gstreamer-base-1.0 x11 gtk+-3.0"
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes gl)
else
AC_MSG_RESULT(no)
@ -63,9 +63,9 @@ AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
CLUTTER_MIN_VERSION=1.11.11
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
GJS_MIN_VERSION=1.33.2
MUTTER_MIN_VERSION=3.5.91
MUTTER_MIN_VERSION=3.7.1
GTK_MIN_VERSION=3.3.9
GIO_MIN_VERSION=2.31.6
GIO_MIN_VERSION=2.35.0
LIBECAL_MIN_VERSION=3.5.3
LIBEDATASERVER_MIN_VERSION=3.5.3
LIBEDATASERVERUI_MIN_VERSION=3.5.3
@ -74,7 +74,7 @@ TELEPATHY_LOGGER_MIN_VERSION=0.2.4
POLKIT_MIN_VERSION=0.100
STARTUP_NOTIFICATION_MIN_VERSION=0.11
GCR_MIN_VERSION=3.3.90
GNOME_DESKTOP_REQUIRED_VERSION=3.5.1
GNOME_DESKTOP_REQUIRED_VERSION=3.7.1
GNOME_MENUS_REQUIRED_VERSION=3.5.3
# Collect more than 20 libraries for a prize!
@ -97,8 +97,7 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
libnm-glib libnm-util gnome-keyring-1
gcr-3 >= $GCR_MIN_VERSION
gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED_VERSION)
gcr-3 >= $GCR_MIN_VERSION)
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
@ -106,6 +105,9 @@ 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
@ -117,7 +119,7 @@ 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.2 x11)
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11)
PKG_CHECK_MODULES(TRAY, gtk+-3.0)
PKG_CHECK_MODULES(GVC, libpulse libpulse-mainloop-glib gobject-2.0)
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.5.4)
@ -136,7 +138,7 @@ PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
AC_SUBST([HAVE_BLUETOOTH],[0])
AC_MSG_RESULT([no])])
PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION libedataserverui-3.0 >= $LIBEDATASERVERUI_MIN_VERSION gio-2.0)
PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION gio-2.0)
AC_SUBST(CALENDAR_SERVER_CFLAGS)
AC_SUBST(CALENDAR_SERVER_LIBS)

View File

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

View File

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

View File

@ -39,6 +39,7 @@ dist_theme_DATA = \
theme/process-working.svg \
theme/running-indicator.svg \
theme/source-button-border.svg \
theme/summary-counter.svg \
theme/toggle-off-us.svg \
theme/toggle-off-intl.svg \
theme/toggle-on-us.svg \
@ -46,6 +47,13 @@ dist_theme_DATA = \
theme/ws-switch-arrow-up.png \
theme/ws-switch-arrow-down.png
keysdir = @GNOME_KEYBINDINGS_KEYSDIR@
keys_in_files = \
50-gnome-shell-screenshot.xml.in \
50-gnome-shell-system.xml.in \
$(NULL)
keys_DATA = $(keys_in_files:.xml.in=.xml)
gsettings_SCHEMAS = org.gnome.shell.gschema.xml
@INTLTOOL_XML_NOMERGE_RULE@
@ -72,12 +80,14 @@ EXTRA_DIST = \
$(introspection_DATA) \
$(menu_DATA) \
$(convert_DATA) \
$(keys_in_files) \
org.gnome.shell.gschema.xml.in.in
CLEANFILES = \
gnome-shell.desktop.in \
gnome-shell-extension-prefs.in \
$(desktop_DATA) \
$(keys_DATA) \
$(gsettings_SCHEMAS) \
gschemas.compiled \
org.gnome.shell.gschema.valid \

View File

@ -108,14 +108,18 @@
</doc:summary>
</doc:doc>
</arg>
<arg type="a{sv}" direction="out">
<arg type="aa{sv}" direction="out">
<doc:doc>
<doc:summary>
<doc:para>
A dictionary describing the given search result, containing
'id', 'name' (both strings) and either 'icon' (a serialized
GIcon) or 'icon-data' (raw image data as (iiibiiay) - width,
height, rowstride, has-alpha, bits per sample, channels, data)
'id' and 'name' (both strings). Optionally, either 'gicon' (a
serialized GIcon) or 'icon-data' (raw image data as (iiibiiay)
- width, height, rowstride, has-alpha, bits per sample,
channels, data) can be specified if the result can be better
served with a thumbnail of the content (such as with images).
A 'description' field (string) may also be specified if more
context would help the user find the desired result.
</doc:para>
</doc:summary>
</doc:doc>
@ -143,5 +147,25 @@
</doc:doc>
</arg>
</method>
<method name="LaunchSearch">
<doc:doc>
<doc:description>
<doc:para>
Called when the user clicks on the provider icon. The provider
application should open and run the active search term itself.
</doc:para>
</doc:description>
</doc:doc>
<arg type="as" direction="in">
<doc:doc>
<doc:summary>
<doc:para>
The current search term(s).
</doc:para>
</doc:summary>
</doc:doc>
</arg>
</method>
</interface>
</node>

View File

@ -39,10 +39,6 @@
will be displayed in the favorites area.
</_description>
</key>
<key name="disabled-open-search-providers" type="as">
<default>[]</default>
<_summary>disabled OpenSearch providers</_summary>
</key>
<key name="command-history" type="as">
<default>[]</default>
<_summary>History for command (Alt-F2) dialog</_summary>
@ -61,6 +57,14 @@ value here is from the TpConnectionPresenceType enumeration.</_summary>
<_summary>Internally used to store the last session presence status for the user. The
value here is from the GsmPresenceStatus enumeration.</_summary>
</key>
<key name="always-show-log-out" type="b">
<default>false</default>
<_summary>Always show the 'Log out' menuitem in the user menu.</_summary>
<_description>
This key overrides the automatic hiding of the 'Log out'
menuitem in single-user, single-session situations.
</_description>
</key>
<child name="calendar" schema="org.gnome.shell.calendar"/>
<child name="recorder" schema="org.gnome.shell.recorder"/>
<child name="keybindings" schema="org.gnome.shell.keybindings"/>
@ -87,6 +91,14 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
Keybinding to open the application menu.
</_description>
</key>
<key name="toggle-application-view" type="as">
<default>["&lt;Super&gt;a"]</default>
<_summary>Keybinding to open the "Show Applications" view</_summary>
<_description>
Keybinding to open the "Show Applications" view of the Activities
Overview.
</_description>
</key>
<key name="toggle-message-tray" type="as">
<default>["&lt;Super&gt;m"]</default>
<_summary>Keybinding to toggle the visibility of the message tray</_summary>
@ -136,7 +148,7 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
take care of its own output - this might be used to send the output
to an icecast server via shout2send or similar. When unset or set
to an empty value, the default pipeline will be used. This is currently
'vp8enc quality=8 speed=6 threads=%T ! queue ! webmmux'
'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux'
and records to WEBM using the VP8 codec. %T is used as a placeholder
for a guess at the optimal thread count on the system.
</_description>
@ -152,44 +164,53 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
</key>
</schema>
<schema id="org.gnome.shell.overrides" path="/org/gnome/shell/overrides/">
<schema id="org.gnome.shell.overrides" path="/org/gnome/shell/overrides/"
gettext-domain="@GETTEXT_PACKAGE@">
<key name="attach-modal-dialogs" type="b">
<default>true</default>
<summary>Attach modal dialog to the parent window</summary>
<description>
<_summary>Attach modal dialog to the parent window</_summary>
<_description>
This key overrides the key in org.gnome.mutter when running
GNOME Shell.
</description>
</_description>
</key>
<key name="button-layout" type="s">
<default>":close"</default>
<summary>Arrangement of buttons on the titlebar</summary>
<description>
<_summary>Arrangement of buttons on the titlebar</_summary>
<_description>
This key overrides the key in org.gnome.desktop.wm.preferences when
running GNOME Shell.
</description>
</_description>
</key>
<key name="edge-tiling" type="b">
<default>true</default>
<summary>Enable edge tiling when dropping windows on screen edges</summary>
<description>
<_summary>Enable edge tiling when dropping windows on screen edges</_summary>
<_description>
This key overrides the key in org.gnome.mutter when running GNOME Shell.
</description>
</_description>
</key>
<key name="dynamic-workspaces" type="b">
<default>true</default>
<summary>Workspaces are managed dynamically</summary>
<description>
<_summary>Workspaces are managed dynamically</_summary>
<_description>
This key overrides the key in org.gnome.mutter when running GNOME Shell.
</description>
</_description>
</key>
<key name="workspaces-only-on-primary" type="b">
<default>true</default>
<summary>Workspaces only on primary monitor</summary>
<_summary>Workspaces only on primary monitor</_summary>
<_description>
This key overrides the key in org.gnome.mutter when running GNOME Shell.
</_description>
</key>
<key name="focus-change-on-pointer-rest" type="b">
<default>true</default>
<summary>Delay focus changes in mouse mode until the pointer stops moving</summary>
<description>
This key overrides the key in org.gnome.mutter when running GNOME Shell.
</description>

View File

@ -39,7 +39,6 @@ stage {
/* small */
.app-well-menu,
.contact-details-status,
.run-dialog-label,
.run-dialog-error-label {
font-size: 9pt;
}
@ -151,11 +150,11 @@ StScrollBar StButton#vhandle:active {
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.9);
}
.popup-sub-menu .popup-menu-item:ltr {
.popup-sub-menu:scrolled .popup-menu-item:ltr {
padding-right: 0em;
}
.popup-sub-menu .popup-menu-item:rtl {
.popup-sub-menu:scrolled .popup-menu-item:rtl {
padding-left: 0em;
}
@ -195,7 +194,7 @@ StScrollBar StButton#vhandle:active {
background-color: #4c4c4c;
}
StButton.popup-menu-item:insensitive {
.popup-menu-item:insensitive {
color: #9f9f9f;
}
@ -234,13 +233,18 @@ StButton.popup-menu-item:insensitive {
spacing: .5em;
}
.popup-inactive-menu-item {
.popup-status-menu-item {
font-weight: normal;
color: #999;
}
.popup-subtitle-menu-item {
.popup-inactive-menu-item, .popup-inactive-menu-item:insensitive {
color: white;
}
.popup-subtitle-menu-item, .popup-subtitle-menu-item:insensitive {
font-weight: bold;
color: white;
}
.popup-menu-icon {
@ -306,7 +310,7 @@ StButton.popup-menu-item:insensitive {
.notification-icon-button:focus,
.hotplug-notification-item:focus,
.modal-dialog-button:focus {
border: 2px solid #8b8b8b;
border-width: 2px;
}
.dash-search-button:active,
@ -321,26 +325,30 @@ StButton.popup-menu-item:insensitive {
background-gradient-end: rgba(255, 255, 255, 0.2);
}
.notification-button:insensitive,
.notification-icon-button:insensitive,
.notification-button:insensitive {
.modal-dialog-button:insensitive {
border-color: #666666;
color: #9f9f9f;
background-gradient-direction: none;
background-color: rgba(102, 102, 102, 0.15);
}
/* Entries */
#searchEntry,
.notification StEntry,
.login-dialog-prompt-entry,
.prompt-dialog-password-entry {
.modal-dialog StEntry {
color: rgb(64, 64, 64);
caret-color: rgb(64, 64, 64);
font-size: 12pt;
caret-size: 1px;
selected-color: black;
selected-color: white;
padding: 4px 12px;
}
#searchEntry,
.run-dialog-entry,
.notification StEntry {
border: 2px solid rgba(245,245,245,0.2);
background-gradient-start: rgba(5,5,6,0.1);
@ -353,8 +361,7 @@ StButton.popup-menu-item:insensitive {
#searchEntry:focus,
#searchEntry:hover,
.notification StEntry:focus,
.login-dialog-prompt-entry,
.prompt-dialog-password-entry {
.modal-dialog StEntry {
border: 2px solid rgb(136,138,133);
background-gradient-start: rgb(200,200,200);
background-gradient-end: white;
@ -363,12 +370,17 @@ StButton.popup-menu-item:insensitive {
}
.notification StEntry:focus,
.prompt-dialog-password-entry:focus,
.login-dialog-prompt-entry:focus {
.modal-dialog StEntry:focus {
border: 2px solid #3465a4;
}
#searchEntry {
border-color: rgba(245,245,245,0.3);
color: rgb(192, 192, 192);
caret-color: rgb(192, 192, 192);
}
#searchEntry:hover {
color: rgb(128, 128, 128);
caret-color: rgb(128, 128, 128);
}
@ -381,8 +393,7 @@ StButton.popup-menu-item:insensitive {
}
.notification StEntry,
.prompt-dialog-password-entry,
.login-dialog-prompt-entry {
.modal-dialog StEntry {
border-radius: 5px;
padding: 4px 4px;
}
@ -397,6 +408,8 @@ StButton.popup-menu-item:insensitive {
.login-dialog-prompt-entry:insensitive {
color: rgba(0,0,0,0.7);
border: 2px solid #565656;
background-gradient-start: rgb(200,200,200);
background-gradient-end: rgb(210,210,210);
}
/* Panel */
@ -544,7 +557,6 @@ StButton.popup-menu-item:insensitive {
}
.status-chooser-combo.popup-combo-menu {
background-color: rgba(0,0,0,0.7);
padding: .4em 0em;
border-radius: 4px;
border: 1px solid #5f5f5f;
@ -562,6 +574,10 @@ StButton.popup-menu-item:insensitive {
/* Overview */
#overview {
spacing: 40px;
}
#overview-group {
spacing: 12px;
}
@ -607,9 +623,9 @@ StButton.popup-menu-item:insensitive {
.window-close, .notification-close {
background-image: url("close-window.svg");
background-size: 34px;
height: 34px;
width: 34px;
background-size: 32px;
height: 32px;
width: 32px;
}
.window-close {
@ -620,19 +636,31 @@ StButton.popup-menu-item:insensitive {
/* we start out in the top right of the
* notification, inset.
*
* center is 32px/2 = 17px
* center is 32px/2 = 16px
*
* adjust left 2px
* adjust down 8px */
-shell-close-overlap-x: 15px;
-shell-close-overlap-y: 12px;
-shell-close-overlap-x: 14px;
-shell-close-overlap-y: -12px;
}
.notification-close:rtl {
/* as above, but starting out in the top left of the
* notification. */
-shell-close-overlap-x: -14px;
}
.window-close:rtl {
-st-background-image-shadow: 2px 2px 6px rgba(0,0,0,0.5);
}
.window-picker {
-horizontal-spacing: 40px;
-vertical-spacing: 40px;
}
/* Dash */
#dash {
@ -651,11 +679,6 @@ StButton.popup-menu-item:insensitive {
border-radius: 9px 0px 0px 9px;
}
#dash:empty {
height: 100px;
width: 60px;
}
.placeholder {
background-image: url("dash-placeholder.svg");
background-size: contain;
@ -675,6 +698,11 @@ StButton.popup-menu-item:insensitive {
.search-entry-icon {
icon-size: 1em;
color: #c0c0c0;
}
#searchEntry:hover .search-entry-icon,
#searchEntry:focus .search-entry-icon {
color: #8d8f8a;
}
@ -687,7 +715,7 @@ StButton.popup-menu-item:insensitive {
#searchResultsContent {
padding-right: 20px;
spacing: 36px;
spacing: 16px;
}
#searchResultsContent:rtl {
@ -695,6 +723,25 @@ StButton.popup-menu-item:insensitive {
padding-left: 20px;
}
.search-section {
/* This should be equal to #searchResultsContent spacing */
spacing: 16px;
}
.search-section-separator {
-gradient-height: 1px;
-gradient-start: rgba(255,255,255,0);
-gradient-end: rgba(255,255,255,0.5);
-margin-horizontal: 1.5em;
height: 1px;
}
.search-section-content {
/* This is the space between the provider icon and the results container */
spacing: 25px;
}
.search-statustext,
.search-section-header {
padding: 4px 12px;
spacing: 4px;
@ -724,6 +771,10 @@ StButton.popup-menu-item:insensitive {
spacing: 12px;
}
.results-list {
spacing: 5px;
}
/* Text labels are an odd number of pixels tall. The uneven top and bottom
* padding compensates for this and ensures that the label is vertically
* centered */
@ -804,7 +855,10 @@ StButton.popup-menu-item:insensitive {
.app-well-app > .overview-icon,
.show-apps > .overview-icon,
.search-result-content > .overview-icon {
.remove-favorite > .overview-icon,
.search-section-icon-bin,
.search-result,
.grid-search-result-content > .overview-icon {
border-radius: 4px;
padding: 3px;
border: 1px rgba(0,0,0,0);
@ -820,7 +874,10 @@ StButton.popup-menu-item:insensitive {
.app-well-app:hover > .overview-icon,
.show-apps:hover > .overview-icon,
.search-result-content:hover > .overview-icon {
.remove-favorite:hover > .overview-icon,
.search-section-icon-bin:hover,
.search-result:hover,
.grid-search-result-content:hover > .overview-icon {
background-color: rgba(255,255,255,0.1);
text-shadow: black 0px 2px 2px;
transition-duration: 100;
@ -855,13 +912,39 @@ StButton.popup-menu-item:insensitive {
}
.app-well-app:focus > .overview-icon,
.search-result-content:focus > .overview-icon,
.show-apps:focus > .overview-icon,
.search-section-icon-bin:focus,
.search-result:focus,
.grid-search-result-content:focus > .overview-icon,
.app-well-app:selected > .overview-icon,
.search-result-content:selected > .overview-icon {
.search-section-icon-bin:selected,
.search-result:selected,
.grid-search-result-content:selected > .overview-icon {
background-color: rgba(255,255,255,0.33);
}
/* List Results */
.search-result {
padding: 15px;
}
.search-result-content {
spacing: 12px;
}
.search-result-details {
font-weight: bold;
}
.search-result-details-title {
font-size: 16pt;
}
.search-result-details-description {
font-size: 14pt;
}
/* LookingGlass */
#LookingGlassDialog {
@ -1190,12 +1273,16 @@ StButton.popup-menu-item:insensitive {
background: #2e3436 url(message-tray-background.png);
background-repeat: repeat;
transition-duration: 250;
height: 72px;
}
#message-tray:keyboard {
/* Same as the OSK */
background: rgba(0, 0, 0, 0.8);
}
#message-tray:overview {
background: rgba(0, 0, 0, 0.1);
border-top: 1px solid rgba(128, 128, 128, 0.3);
outline: 1px solid rgba(128, 128, 128, 0.3);
}
.notification {
@ -1417,38 +1504,45 @@ StButton.popup-menu-item:insensitive {
}
#summary-mode {
padding: 0px 6px 0px 6px; /* same as the values in .summary-source */
height: 60px;
spacing: 10px;
height: 72px;
}
.summary-source-button {
border-radius: 4px;
transition-duration: 100;
padding: 6px 3px 6px 3px;
}
.summary-source-button:hover {
.summary-source-button:last-child:ltr {
padding-right: 6px;
}
.summary-source-button:last-child:rtl {
padding-left: 6px;
}
.summary-source-button:hover .summary-source {
background-color: rgba(255,255,255,0.1);
}
.summary-source-button:focus,
.summary-source-button:selected {
.summary-source-button:focus .summary-source,
.summary-source-button:selected .summary-source {
background-color: rgba(255,255,255,0.33);
}
.summary-source {
padding-right: 6px;
padding-left: 6px;
border-radius: 4px;
padding: 0 6px 0 6px;
transition-duration: 100;
}
.summary-source-counter {
color: white;
background-color: #3465A4;
text-shadow: black 1px 1px 0;
font-size: 9pt;
border-radius: 1em;
min-height: 1em;
min-width: 1em;
background-image: url("summary-counter.svg");
background-size: 2.5em;
font-size: 10pt;
font-weight: bold;
height: 2.5em;
width: 2.5em;
-shell-counter-overlap-x: 4px;
-shell-counter-overlap-y: 4px;
}
/* App Switcher */
@ -1615,35 +1709,26 @@ StButton.popup-menu-item:insensitive {
padding: 4px 32px 5px;
}
.modal-dialog-button:insensitive {
color: rgb(60, 60, 60);
}
.modal-dialog-button:focus {
padding: 3px 31px 4px;
}
/* Run Dialog */
.run-dialog-label {
font-size: 12pt;
font-weight: bold;
color: #999999;
padding-bottom: .4em;
}
.run-dialog-error-box {
padding-top: 15px;
spacing: 5px;
}
.run-dialog-entry {
font-weight: bold;
width: 23em;
selection-background-color: white;
selected-color: black;
}
.run-dialog {
border-radius: 16px;
padding-right: 21px;
padding-left: 21px;
padding-bottom: 15px;
padding-top: 15px;
.modal-dialog .run-dialog-entry {
width: 20em;
}
.lightbox {
@ -2024,7 +2109,6 @@ StButton.popup-menu-item:insensitive {
}
.login-dialog-user-list-item {
color: #666666;
border-radius: 10px;
padding: .2em;
}
@ -2042,6 +2126,11 @@ StButton.popup-menu-item:insensitive {
padding-left: 1em;
}
.login-dialog-user-list:expanded .login-dialog-user-list-item {
color: #666666;
}
.login-dialog-user-list-item,
.login-dialog-user-list-item:hover .login-dialog-user-list-item-name,
.login-dialog-user-list:expanded .login-dialog-user-list-item:focus .login-dialog-user-list-item-name,
.login-dialog-user-list:expanded .login-dialog-user-list-item:logged-in {
@ -2089,6 +2178,7 @@ StButton.popup-menu-item:insensitive {
padding-top: 1em;
}
.login-dialog-not-listed-button:focus .login-dialog-not-listed-label,
.login-dialog-not-listed-button:hover .login-dialog-not-listed-label {
color: #E8E8E8;
}
@ -2164,18 +2254,24 @@ StButton.popup-menu-item:insensitive {
height: .75em;
}
.login-dialog .modal-dialog-button {
border: 1px solid #666666;
border-radius: 5px;
padding: 3px 18px;
}
.login-dialog .modal-dialog-button:focus {
padding: 2px 17px;
}
.login-dialog .modal-dialog-button:default {
background-gradient-start: #6793c4;
background-gradient-end: #335d8f;
background-gradient-direction: vertical;
border: 2px solid #16335d;
border-color: #16335d;
}
.login-dialog .modal-dialog-button:default:focus {
border: 2px solid #377fe7;
}
.login-dialog .modal-dialog-button:default:hover {
@ -2189,12 +2285,28 @@ StButton.popup-menu-item:insensitive {
background-gradient-end: #74a0d0;
}
.login-dialog .modal-dialog-button:default:insensitive {
border-color: #666666;
color: #9f9f9f;
background-gradient-direction: none;
background-color: rgba(102, 102, 102, 0.15);
}
.login-dialog-message-warning {
color: orange;
}
.unlock-dialog-user-name-container {
spacing: .4em;
}
/* Screen shield */
.screen-shield-background {
background: black;
box-shadow: 0px 4px 8px rgba(0,0,0,0.9);
}
#lockDialogGroup {
background: #2e3436 url(noise-texture.png);
background-repeat: repeat;
@ -2212,6 +2324,10 @@ StButton.popup-menu-item:insensitive {
-arrow-shadow: 0 1px 1px rgba(0,0,0,0.4);
}
.screen-shield-contents-box {
spacing: 48px;
}
.screen-shield-clock {
color: white;
text-shadow: 0px 1px 2px rgba(0,0,0,0.6);
@ -2221,12 +2337,12 @@ StButton.popup-menu-item:insensitive {
}
.screen-shield-clock-time {
font-size: 86px;
font-size: 72pt;
text-shadow: 0px 2px 2px rgba(0,0,0,0.4);
}
.screen-shield-clock-date {
font-size: 48px;
font-size: 28pt;
}
#screenShieldNotifications {
@ -2234,11 +2350,12 @@ StButton.popup-menu-item:insensitive {
background-color: rgba(0.0, 0.0, 0.0, 0.9);
border: 2px solid #868686;
max-height: 500px;
padding: 12px 0;
padding: 18px 0;
box-shadow: .5em .5em 20px rgba(0, 0, 0, 0.5);
}
.screen-shield-notifications-box {
spacing: 12px;
spacing: 18px;
}
.screen-shield-notification-source {
@ -2249,6 +2366,7 @@ StButton.popup-menu-item:insensitive {
.screen-shield-notification-label {
font-size: 1.2em;
font-weight: bold;
color: #babdb6;
}
/* Remove background from notifications, otherwise

View File

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

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

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

View File

@ -1,3 +1,4 @@
NULL =
EXTRA_DIST = misc/config.js.in
CLEANFILES = misc/config.js
@ -37,25 +38,23 @@ nobase_dist_js_DATA = \
ui/altTab.js \
ui/appDisplay.js \
ui/appFavorites.js \
ui/automountManager.js \
ui/autorunManager.js \
ui/boxpointer.js \
ui/calendar.js \
ui/checkBox.js \
ui/centerLayout.js \
ui/ctrlAltTab.js \
ui/dash.js \
ui/dateMenu.js \
ui/dnd.js \
ui/endSessionDialog.js \
ui/environment.js \
ui/extensionSystem.js \
ui/extensionDownloader.js \
ui/environment.js \
ui/flashspot.js \
ui/ibusCandidatePopup.js\
ui/grabHelper.js \
ui/iconGrid.js \
ui/keyboard.js \
ui/keyringPrompt.js \
ui/layout.js \
ui/lightbox.js \
ui/lookingGlass.js \
@ -64,7 +63,6 @@ nobase_dist_js_DATA = \
ui/main.js \
ui/messageTray.js \
ui/modalDialog.js \
ui/networkAgent.js \
ui/sessionMode.js \
ui/shellEntry.js \
ui/shellMountOperation.js \
@ -72,9 +70,7 @@ nobase_dist_js_DATA = \
ui/overview.js \
ui/panel.js \
ui/panelMenu.js \
ui/placeDisplay.js \
ui/pointerWatcher.js \
ui/polkitAuthenticationAgent.js \
ui/popupMenu.js \
ui/remoteSearch.js \
ui/runDialog.js \
@ -90,7 +86,6 @@ nobase_dist_js_DATA = \
ui/status/power.js \
ui/status/volume.js \
ui/status/bluetooth.js \
ui/telepathyClient.js \
ui/tweener.js \
ui/unlockDialog.js \
ui/userMenu.js \
@ -102,4 +97,13 @@ nobase_dist_js_DATA = \
ui/workspaceThumbnail.js \
ui/workspacesView.js \
ui/workspaceSwitcherPopup.js \
ui/xdndHandler.js
ui/xdndHandler.js \
ui/components/__init__.js \
ui/components/autorunManager.js \
ui/components/automountManager.js \
ui/components/networkAgent.js \
ui/components/polkitAgent.js \
ui/components/recorder.js \
ui/components/telepathyClient.js \
ui/components/keyring.js \
$(NULL)

View File

@ -174,7 +174,7 @@ const Application = new Lang.Class({
let renderer = new Gtk.CellRendererText();
this._extensionSelector.pack_start(renderer, true);
this._extensionSelector.add_attribute(renderer, 'text', 1);
this._extensionSelector.set_cell_data_func(renderer, Lang.bind(this, this._setExtensionInsensitive), null);
this._extensionSelector.set_cell_data_func(renderer, Lang.bind(this, this._setExtensionInsensitive));
this._extensionSelector.connect('changed', Lang.bind(this, this._extensionSelected));
toolitem = new Gtk.ToolItem({ child: this._extensionSelector });

View File

@ -19,8 +19,7 @@ function FprintManager() {
g_interface_info: FprintManagerInfo,
g_name: 'net.reactivated.Fprint',
g_object_path: '/net/reactivated/Fprint/Manager',
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
g_flags: (Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
self.init(null);
return self;

View File

@ -25,6 +25,7 @@ const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Lang = imports.lang;
const Pango = imports.gi.Pango;
const Signals = imports.signals;
@ -38,13 +39,14 @@ const GdmUtil = imports.gdm.util;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog;
const PanelMenu = imports.ui.panelMenu;
const Tweener = imports.ui.tweener;
const UserMenu = imports.ui.userMenu;
const _RESIZE_ANIMATION_TIME = 0.25;
const _SCROLL_ANIMATION_TIME = 0.5;
const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
const _LOGO_ICON_NAME_SIZE = 48;
const _LOGO_ICON_HEIGHT = 16;
let _loginDialog = null;
@ -81,6 +83,36 @@ function _smoothlyResizeActor(actor, width, height) {
return hold;
}
const LogoMenuButton = new Lang.Class({
Name: 'LogoMenuButton',
Extends: PanelMenu.Button,
_init: function() {
this.parent(0.0, null, true);
this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
this._settings.connect('changed::' + GdmUtil.LOGO_KEY,
Lang.bind(this, this._updateLogo));
this._iconBin = new St.Bin();
this.actor.add_actor(this._iconBin);
this._updateLogo();
},
_updateLogo: function() {
let path = this._settings.get_string(GdmUtil.LOGO_KEY);
let icon = null;
if (path) {
let file = Gio.file_new_for_path(path);
let cache = St.TextureCache.get_default();
icon = cache.load_uri_async(file.get_uri(), -1, _LOGO_ICON_HEIGHT);
}
this._iconBin.set_child(icon);
}
});
const UserListItem = new Lang.Class({
Name: 'UserListItem',
@ -105,6 +137,7 @@ const UserListItem = new Lang.Class({
layout.add(textLayout, { expand: true });
this._nameLabel = new St.Label({ style_class: 'login-dialog-user-list-item-name' });
this.actor.label_actor = this._nameLabel;
textLayout.add(this._nameLabel,
{ y_fill: false,
y_align: St.Align.MIDDLE,
@ -148,14 +181,6 @@ const UserListItem = new Lang.Class({
this.emit('activate');
},
fadeOutName: function() {
return GdmUtil.fadeOutActor(this._nameLabel);
},
fadeInName: function() {
return GdmUtil.fadeInActor(this._nameLabel);
},
showTimedLoginIndicator: function(time) {
let hold = new Batch.Hold();
@ -206,16 +231,18 @@ const UserList = new Lang.Class({
if (global.stage.get_key_focus() != this.actor)
return;
this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
let focusSet = this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
if (!focusSet) {
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
this._moveFocusToItems();
return false;
}));
}
},
_showItem: function(item) {
let tasks = [function() {
return GdmUtil.fadeInActor(item.actor);
},
function() {
return item.fadeInName();
}];
let batch = new Batch.ConsecutiveBatch(this, tasks);
@ -279,13 +306,16 @@ const UserList = new Lang.Class({
});
}
this._box.remove_style_pseudo_class('expanded');
let batch = new Batch.ConsecutiveBatch(this,
[function() {
return GdmUtil.fadeOutActor(this.actor.vscroll);
},
new Batch.ConcurrentBatch(this, tasks)
new Batch.ConcurrentBatch(this, tasks),
function() {
this._box.remove_style_pseudo_class('expanded');
}
]);
return batch.run();
@ -335,7 +365,6 @@ const UserList = new Lang.Class({
});
}
this._box.add_style_pseudo_class('expanded');
let batch = new Batch.ConsecutiveBatch(this,
[function() {
this.takeOverWhitespace();
@ -346,6 +375,10 @@ const UserList = new Lang.Class({
return _smoothlyResizeActor(this._box, -1, fullHeight);
},
function() {
this._box.add_style_pseudo_class('expanded');
},
new Batch.ConcurrentBatch(this, tasks),
function() {
@ -471,6 +504,7 @@ const SessionListItem = new Lang.Class({
let label = new St.Label({ style_class: 'login-dialog-session-list-item-label',
text: name });
this.actor.label_actor = label;
this._box.add_actor(label);
},
@ -625,8 +659,8 @@ const LoginDialog = new Lang.Class({
_init: function(parentActor) {
this.parent({ shellReactive: true,
styleClass: 'login-dialog',
parentActor: parentActor
});
parentActor: parentActor,
shouldFadeIn: false });
this.connect('destroy',
Lang.bind(this, this._onDestroy));
this.connect('opened',
@ -635,37 +669,32 @@ const LoginDialog = new Lang.Class({
this._userManager = AccountsService.UserManager.get_default()
this._greeterClient = new Gdm.Client();
this._greeter = this._greeterClient.get_greeter_sync(null);
if (GLib.getenv('GDM_GREETER_TEST') != '1') {
this._greeter = this._greeterClient.get_greeter_sync(null);
this._greeter.connect('default-session-name-changed',
Lang.bind(this, this._onDefaultSessionChanged));
this._greeter.connect('default-session-name-changed',
Lang.bind(this, this._onDefaultSessionChanged));
this._greeter.connect('session-opened',
Lang.bind(this, this._onSessionOpened));
this._greeter.connect('timed-login-requested',
Lang.bind(this, this._onTimedLoginRequested));
this._greeter.connect('session-opened',
Lang.bind(this, this._onSessionOpened));
this._greeter.connect('timed-login-requested',
Lang.bind(this, this._onTimedLoginRequested));
}
this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient);
this._userVerifier.connect('ask-question', Lang.bind(this, this._askQuestion));
this._userVerifier.connect('verification-failed', Lang.bind(this, this._onVerificationFailed));
this._userVerifier.connect('show-message', Lang.bind(this, this._showMessage));
this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
this._userVerifier.connect('show-login-hint', Lang.bind(this, this._showLoginHint));
this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._hideLoginHint));
this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
this._settings.connect('changed::' + GdmUtil.LOGO_KEY,
Lang.bind(this, this._updateLogo));
this._settings.connect('changed::' + GdmUtil.BANNER_MESSAGE_KEY,
Lang.bind(this, this._updateBanner));
this._settings.connect('changed::' + GdmUtil.BANNER_MESSAGE_TEXT_KEY,
Lang.bind(this, this._updateBanner));
this._logoBox = new St.Bin({ style_class: 'login-dialog-logo-box' });
this.contentLayout.add(this._logoBox);
this._updateLogo();
this._bannerLabel = new St.Label({ style_class: 'login-dialog-banner',
text: '' });
this.contentLayout.add(this._bannerLabel);
@ -678,31 +707,23 @@ const LoginDialog = new Lang.Class({
{ y_fill: false,
y_align: St.Align.START });
let mainContentBox = new St.BoxLayout({ vertical: false });
this.contentLayout.add(mainContentBox,
this._userList = new UserList();
this.contentLayout.add(this._userList.actor,
{ expand: true,
x_fill: true,
y_fill: false });
this._userList = new UserList();
mainContentBox.add(this._userList.actor,
{ expand: true,
x_fill: true,
y_fill: true });
y_fill: true });
this.setInitialKeyFocus(this._userList.actor);
this._promptBox = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
vertical: true });
mainContentBox.add(this._promptBox,
{ expand: true,
x_fill: true,
y_fill: true,
x_align: St.Align.START });
this.contentLayout.add(this._promptBox,
{ expand: true,
x_fill: true,
y_fill: true,
x_align: St.Align.START });
this._promptLabel = new St.Label({ style_class: 'login-dialog-prompt-label' });
this._mainContentBox = mainContentBox;
this._promptBox.add(this._promptLabel,
{ expand: true,
x_fill: true,
@ -715,6 +736,10 @@ const LoginDialog = new Lang.Class({
x_fill: true,
y_fill: false,
x_align: St.Align.START });
this._promptMessage = new St.Label({ visible: false });
this._promptBox.add(this._promptMessage, { x_fill: true });
this._promptLoginHint = new St.Label({ style_class: 'login-dialog-prompt-login-hint-message' });
this._promptLoginHint.hide();
this._promptBox.add(this._promptLoginHint);
@ -770,20 +795,6 @@ const LoginDialog = new Lang.Class({
},
_updateLogo: function() {
this._logoBox.child = null;
let path = this._settings.get_string(GdmUtil.LOGO_KEY);
if (path) {
let file = Gio.file_new_for_path(path);
let uri = file.get_uri();
let textureCache = St.TextureCache.get_default();
this._logoBox.child = textureCache.load_uri_async(uri, -1, _LOGO_ICON_NAME_SIZE);
}
},
_updateBanner: function() {
let enabled = this._settings.get_boolean(GdmUtil.BANNER_MESSAGE_KEY);
let text = this._settings.get_string(GdmUtil.BANNER_MESSAGE_TEXT_KEY);
@ -797,11 +808,12 @@ const LoginDialog = new Lang.Class({
},
_onReset: function(client, serviceName) {
this._promptMessage.hide();
let tasks = [this._hidePrompt,
new Batch.ConcurrentBatch(this, [this._fadeInTitleLabel,
this._fadeInNotListedButton,
this._fadeInLogo]),
this._fadeInNotListedButton]),
function() {
this._sessionList.close();
@ -826,6 +838,16 @@ const LoginDialog = new Lang.Class({
this._sessionList.setActiveSession(sessionId);
},
_showMessage: function(userVerifier, message, styleClass) {
if (message) {
this._promptMessage.text = message;
this._promptMessage.styleClass = styleClass;
GdmUtil.fadeInActor(this._promptMessage);
} else {
GdmUtil.fadeOutActor(this._promptMessage);
}
},
_showLoginHint: function(verifier, message) {
this._promptLoginHint.set_text(message)
GdmUtil.fadeInActor(this._promptLoginHint);
@ -888,15 +910,6 @@ const LoginDialog = new Lang.Class({
label: C_("button", "Sign In"),
default: true }];
this._promptEntryActivateCallbackId = this._promptEntry.clutter_text.connect('activate',
Lang.bind(this, function() {
hold.release();
}));
hold.connect('release', Lang.bind(this, function() {
this._promptEntry.clutter_text.disconnect(this._promptEntryActivateCallbackId);
this._promptEntryActivateCallbackId = null;
}));
let tasks = [function() {
return this._fadeInPrompt();
},
@ -913,11 +926,6 @@ const LoginDialog = new Lang.Class({
},
_hidePrompt: function() {
if (this._promptEntryActivateCallbackId) {
this._promptEntry.clutter_text.disconnect(this._promptEntryActivateCallbackId);
this._promptEntryActivateCallbackId = null;
}
this.setButtons([]);
let tasks = [function() {
@ -1081,10 +1089,6 @@ const LoginDialog = new Lang.Class({
}));
},
_onVerificationFailed: function() {
this._userVerifier.cancel();
},
_onNotListedClicked: function(user) {
let tasks = [function() {
return this._userList.hideItems();
@ -1099,8 +1103,7 @@ const LoginDialog = new Lang.Class({
},
new Batch.ConcurrentBatch(this, [this._fadeOutTitleLabel,
this._fadeOutNotListedButton,
this._fadeOutLogo]),
this._fadeOutNotListedButton]),
function() {
let hold = new Batch.Hold();
@ -1113,14 +1116,6 @@ const LoginDialog = new Lang.Class({
batch.run();
},
_fadeInLogo: function() {
return GdmUtil.fadeInActor(this._logoBox);
},
_fadeOutLogo: function() {
return GdmUtil.fadeOutActor(this._logoBox);
},
_fadeInBanner: function() {
return GdmUtil.fadeInActor(this._bannerLabel);
},
@ -1168,13 +1163,8 @@ const LoginDialog = new Lang.Class({
return this._userList.giveUpWhitespace();
},
function() {
return activatedItem.fadeOutName();
},
new Batch.ConcurrentBatch(this, [this._fadeOutTitleLabel,
this._fadeOutNotListedButton,
this._fadeOutLogo]),
this._fadeOutNotListedButton]),
function() {
return this._userList.shrinkToNaturalHeight();
@ -1227,7 +1217,7 @@ const LoginDialog = new Lang.Class({
},
_onOpened: function() {
Main.ctrlAltTabManager.addGroup(this._mainContentBox,
Main.ctrlAltTabManager.addGroup(this.dialogLayout,
_("Login Window"),
'dialog-password',
{ sortGroup: CtrlAltTab.SortGroup.MIDDLE });

View File

@ -18,11 +18,12 @@
* 02111-1307, USA.
*/
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const UPowerGlib = imports.gi.UPowerGlib;
const LoginManager = imports.misc.loginManager;
const GdmUtil = imports.gdm.util;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
@ -31,16 +32,16 @@ const PowerMenuButton = new Lang.Class({
Extends: PanelMenu.SystemStatusButton,
_init: function() {
this.parent('system-shutdown-symbolic', null);
this._upClient = new UPowerGlib.Client();
/* Translators: accessible name of the power menu in the login screen */
this.parent('system-shutdown-symbolic', _("Power"));
this._loginManager = LoginManager.getLoginManager();
this._createSubMenu();
this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
this._settings.connect('changed::disable-restart-buttons',
Lang.bind(this, this._updateVisibility));
this._upClient.connect('notify::can-suspend',
Lang.bind(this, this._updateHaveSuspend));
this._updateHaveSuspend();
this._createSubMenu();
// ConsoleKit doesn't send notifications when shutdown/reboot
// are disabled, so we update the menu item each time the menu opens
@ -49,15 +50,17 @@ const PowerMenuButton = new Lang.Class({
if (open) {
this._updateHaveShutdown();
this._updateHaveRestart();
this._updateHaveSuspend();
}
}));
this._updateHaveShutdown();
this._updateHaveRestart();
this._updateHaveSuspend();
},
_updateVisibility: function() {
let shouldBeVisible = (this._haveSuspend || this._haveShutdown || this._haveRestart);
this.actor.visible = shouldBeVisible;
this.actor.visible = shouldBeVisible && !this._settings.get_boolean('disable-restart-buttons');
},
_updateHaveShutdown: function() {
@ -77,9 +80,11 @@ const PowerMenuButton = new Lang.Class({
},
_updateHaveSuspend: function() {
this._haveSuspend = this._upClient.get_can_suspend();
this._suspendItem.actor.visible = this._haveSuspend;
this._updateVisibility();
this._loginManager.canSuspend(Lang.bind(this, function(result) {
this._haveSuspend = result;
this._suspendItem.actor.visible = this._haveSuspend;
this._updateVisibility();
}));
},
_createSubMenu: function() {
@ -102,8 +107,10 @@ const PowerMenuButton = new Lang.Class({
},
_onActivateSuspend: function() {
if (this._haveSuspend)
this._upClient.suspend_sync(null);
if (!this._haveSuspend)
return;
this._loginManager.suspend();
},
_onActivateRestart: function() {

View File

@ -2,6 +2,7 @@
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Signals = imports.signals;
const Batch = imports.gdm.batch;
@ -19,6 +20,7 @@ const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
const BANNER_MESSAGE_KEY = 'banner-message-enable';
const BANNER_MESSAGE_TEXT_KEY = 'banner-message-text';
const ALLOWED_FAILURES_KEY = 'allowed-failures';
const LOGO_KEY = 'logo';
@ -81,6 +83,8 @@ const ShellUserVerifier = new Lang.Class({
this._fprintManager = new Fprint.FprintManager();
this._realmManager = new Realmd.Manager();
this._failCounter = 0;
},
begin: function(userName, hold) {
@ -121,6 +125,9 @@ const ShellUserVerifier = new Lang.Class({
},
answerQuery: function(serviceName, answer) {
// Clear any previous message
this.emit('show-message', null, null);
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
},
@ -137,34 +144,43 @@ const ShellUserVerifier = new Lang.Class({
}));
},
_reportInitError: function(where, error) {
logError(error, where);
this._hold.release();
this.emit('show-message', _("Authentication error"), 'login-dialog-message-warning');
this._verificationFailed(false);
},
_reauthenticationChannelOpened: function(client, result) {
try {
this._userVerifier = client.open_reauthentication_channel_finish(result);
this._connectSignals();
this._beginVerification();
this._hold.release();
} catch (e) {
if (this._reauthOnly) {
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
return;
logError(e, 'Failed to open reauthentication channel');
this.emit('verification-failed');
this._hold.release();
return;
}
// If there's no session running, or it otherwise fails, then fall back
// to performing verification from this login session
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
return;
} catch(e if e.matches(Gio.DBusError, Gio.DBusError.ACCESS_DENIED) &&
!this._reauthOnly) {
// Gdm emits org.freedesktop.DBus.Error.AccessDenied when there is
// no session to reauthenticate. Fall back to performing verification
// from this login session
client.get_user_verifier(this._cancellable, Lang.bind(this, this._userVerifierGot));
return;
} catch(e) {
this._reportInitError('Failed to open reauthentication channel', e);
return;
}
this._connectSignals();
this._beginVerification();
this._hold.release();
},
_userVerifierGot: function(client, result) {
try {
this._userVerifier = client.get_user_verifier_finish(result);
} catch(e if e.matches(Gio.IOErrorEnum, Gio.ErrorEnum.CANCELLED)) {
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
return;
} catch(e) {
this._reportInitError('Failed to obtain user verifier', e);
return;
}
@ -195,6 +211,9 @@ const ShellUserVerifier = new Lang.Class({
obj.call_begin_verification_for_user_finish(result);
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
return;
} catch(e) {
this._reportInitError('Failed to start verification for user', e);
return;
}
this._hold.release();
@ -211,6 +230,9 @@ const ShellUserVerifier = new Lang.Class({
obj.call_begin_verification_for_user_finish(result);
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
return;
} catch(e) {
this._reportInitError('Failed to start fingerprint verification for user', e);
return;
}
this._hold.release();
@ -224,6 +246,9 @@ const ShellUserVerifier = new Lang.Class({
obj.call_begin_verification_finish(result);
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
return;
} catch(e) {
this._reportInitError('Failed to start verification', e);
return;
}
this._hold.release();
@ -242,7 +267,7 @@ const ShellUserVerifier = new Lang.Class({
// to indicate the user can swipe their finger instead
this.emit('show-login-hint', _("(or swipe finger)"));
} else if (serviceName == PASSWORD_SERVICE_NAME) {
Main.notifyError(info);
this.emit('show-message', info, 'login-dialog-message-info');
}
},
@ -251,7 +276,7 @@ const ShellUserVerifier = new Lang.Class({
// users who haven't enrolled their fingerprint.
if (serviceName != PASSWORD_SERVICE_NAME)
return;
Main.notifyError(problem);
this.emit('show-message', problem, 'login-dialog-message-warning');
},
_showRealmLoginHint: function() {
@ -290,8 +315,10 @@ const ShellUserVerifier = new Lang.Class({
},
_onReset: function() {
this._userVerifier.run_dispose();
this._userVerifier = null;
this.clear();
// Clear previous attempts to authenticate
this._failCounter = 0;
this.emit('reset');
},
@ -300,12 +327,37 @@ const ShellUserVerifier = new Lang.Class({
this.emit('verification-complete');
},
_verificationFailed: function(retry) {
// For Not Listed / enterprise logins, immediately reset
// the dialog
// Otherwise, we allow ALLOWED_FAILURES attempts. After that, we
// go back to the welcome screen.
this._failCounter++;
let canRetry = retry && this._userName &&
this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY);
if (canRetry) {
this.clear();
this.begin(this._userName, new Batch.Hold());
} else {
// Allow some time to see the message, then reset everything
Mainloop.timeout_add(3000, Lang.bind(this, function() {
this.cancel();
this._onReset();
}));
}
this.emit('verification-failed');
},
_onConversationStopped: function(client, serviceName) {
// if the password service fails, then cancel everything.
// But if, e.g., fingerprint fails, still give
// password authentication a chance to succeed
if (serviceName == PASSWORD_SERVICE_NAME) {
this.emit('verification-failed');
this._verificationFailed(true);
}
this.emit('hide-login-hint');

View File

@ -120,11 +120,6 @@ function createExtensionObject(uuid, dir, type) {
}
}
// Encourage people to add this
if (!meta.url) {
log('Warning: Missing "url" property in %s/metadata.json'.format(uuid));
}
if (uuid != meta.uuid) {
throw new Error('uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + uuid + '"');
}
@ -161,7 +156,8 @@ const ExtensionFinder = new Lang.Class({
try {
fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
} catch(e) {
logError(e, 'Could not enumerate extensions directory');
if (e.domain != Gio.io_error_quark() || e.code != Gio.IOErrorEnum.NOT_FOUND)
logError(e, 'Could not enumerate extensions directory');
return;
}

View File

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

View File

@ -3,7 +3,9 @@
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
const UPowerGlib = imports.gi.UPowerGlib;
const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager'>
<method name='PowerOff'>
@ -12,12 +14,18 @@ const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager
<method name='Reboot'>
<arg type='b' direction='in'/>
</method>
<method name='Suspend'>
<arg type='b' direction='in'/>
</method>
<method name='CanPowerOff'>
<arg type='s' direction='out'/>
</method>
<method name='CanReboot'>
<arg type='s' direction='out'/>
</method>
<method name='CanSuspend'>
<arg type='s' direction='out'/>
</method>
</interface>;
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
@ -123,12 +131,25 @@ const LoginManagerSystemd = new Lang.Class({
});
},
canSuspend: function(asyncCallback) {
this._proxy.CanSuspendRemote(function(result, error) {
if (error)
asyncCallback(false);
else
asyncCallback(result[0] != 'no');
});
},
powerOff: function() {
this._proxy.PowerOffRemote(true);
},
reboot: function() {
this._proxy.RebootRemote(true);
},
suspend: function() {
this._proxy.SuspendRemote(true);
}
});
@ -139,6 +160,7 @@ 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
@ -186,12 +208,22 @@ const LoginManagerConsoleKit = new Lang.Class({
});
},
canSuspend: function(asyncCallback) {
Mainloop.idle_add(Lang.bind(this, function() {
asyncCallback(this._upClient.get_can_suspend());
return false;
}));
},
powerOff: function() {
this._proxy.StopRemote();
},
reboot: function() {
this._proxy.RestartRemote();
},
suspend: function() {
this._upClient.suspend_sync(null);
}
});

View File

@ -1,6 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const GMenu = imports.gi.GMenu;
@ -371,6 +372,8 @@ const SettingsSearchProvider = new Lang.Class({
this._appSys = Shell.AppSystem.get_default();
this._gnomecc = this._appSys.lookup_app('gnome-control-center.desktop');
let appInfo = Gio.DesktopAppInfo.new('gnome-control-center.desktop');
this.icon = appInfo.get_icon();
},
getResultMetas: function(prefs, callback) {
@ -379,9 +382,7 @@ const SettingsSearchProvider = new Lang.Class({
let pref = prefs[i];
metas.push({ 'id': pref,
'name': pref.get_name(),
'createIcon': function(size) {
return pref.create_icon_texture(size);
}
'createIcon': function(size) { return; }
});
}
callback(metas);
@ -461,15 +462,15 @@ const AppWellIcon = new Lang.Class({
this._draggable.connect('drag-begin', Lang.bind(this,
function () {
this._removeMenuTimeout();
Main.overview.beginItemDrag(this);
Main.overview.beginAppDrag(this);
}));
this._draggable.connect('drag-cancelled', Lang.bind(this,
function () {
Main.overview.cancelledItemDrag(this);
Main.overview.cancelledAppDrag(this);
}));
this._draggable.connect('drag-end', Lang.bind(this,
function () {
Main.overview.endItemDrag(this);
Main.overview.endAppDrag(this);
}));
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));

View File

@ -141,6 +141,7 @@ const BoxPointer = new Lang.Class({
this._muteInput();
Tweener.removeTweens(this);
Tweener.addTween(this, { opacity: fade ? 0 : 255,
xOffset: xOffset,
yOffset: yOffset,
@ -274,14 +275,51 @@ const BoxPointer = new Lang.Class({
let [x1, y1] = [halfBorder, halfBorder];
let [x2, y2] = [boxWidth - halfBorder, boxHeight - halfBorder];
let skipTopLeft = false;
let skipTopRight = false;
let skipBottomLeft = false;
let skipBottomRight = false;
switch (this._arrowSide) {
case St.Side.TOP:
if (this._arrowOrigin == x1)
skipTopLeft = true;
else if (this._arrowOrigin == x2)
skipTopRight = true;
break;
case St.Side.RIGHT:
if (this._arrowOrigin == y1)
skipTopRight = true;
else if (this._arrowOrigin == y2)
skipBottomRight = true;
break;
case St.Side.BOTTOM:
if (this._arrowOrigin == x1)
skipBottomLeft = true;
else if (this._arrowOrigin == x2)
skipBottomRight = true;
break;
case St.Side.LEFT:
if (this._arrowOrigin == y1)
skipTopLeft = true;
else if (this._arrowOrigin == y2)
skipBottomLeft = true;
break;
}
cr.moveTo(x1 + borderRadius, y1);
if (this._arrowSide == St.Side.TOP) {
if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) {
cr.lineTo(this._arrowOrigin, y1 - rise);
cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y1);
} else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y1);
cr.lineTo(this._arrowOrigin, y1 - rise);
if (skipTopLeft) {
cr.moveTo(x1, y2 - borderRadius);
cr.lineTo(x1, y1 - rise);
cr.lineTo(x1 + halfBase, y1);
} else if (skipTopRight) {
cr.lineTo(x2 - halfBase, y1);
cr.lineTo(x2, y1 - rise);
cr.lineTo(x2, y1 + borderRadius);
} else {
cr.lineTo(this._arrowOrigin - halfBase, y1);
cr.lineTo(this._arrowOrigin, y1 - rise);
@ -289,19 +327,20 @@ const BoxPointer = new Lang.Class({
}
}
cr.lineTo(x2 - borderRadius, y1);
// top-right corner
cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius,
3*Math.PI/2, Math.PI*2);
if (!skipTopRight) {
cr.lineTo(x2 - borderRadius, y1);
cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius,
3*Math.PI/2, Math.PI*2);
}
if (this._arrowSide == St.Side.RIGHT) {
if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
cr.lineTo(x2 + rise, this._arrowOrigin);
cr.lineTo(x2, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase);
} else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
cr.lineTo(x2, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase);
cr.lineTo(x2 + rise, this._arrowOrigin);
if (skipTopRight) {
cr.lineTo(x2 + rise, y1);
cr.lineTo(x2 + rise, y1 + halfBase);
} else if (skipBottomRight) {
cr.lineTo(x2, y2 - halfBase);
cr.lineTo(x2 + rise, y2);
cr.lineTo(x2 - borderRadius, y2);
} else {
cr.lineTo(x2, this._arrowOrigin - halfBase);
cr.lineTo(x2 + rise, this._arrowOrigin);
@ -309,19 +348,20 @@ const BoxPointer = new Lang.Class({
}
}
cr.lineTo(x2, y2 - borderRadius);
// bottom-right corner
cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius,
0, Math.PI/2);
if (!skipBottomRight) {
cr.lineTo(x2, y2 - borderRadius);
cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius,
0, Math.PI/2);
}
if (this._arrowSide == St.Side.BOTTOM) {
if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) {
cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y2);
cr.lineTo(this._arrowOrigin, y2 + rise);
} else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
cr.lineTo(this._arrowOrigin, y2 + rise);
cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y2);
if (skipBottomLeft) {
cr.lineTo(x1 + halfBase, y2);
cr.lineTo(x1, y2 + rise);
cr.lineTo(x1, y2 - borderRadius);
} else if (skipBottomRight) {
cr.lineTo(x2, y2 + rise);
cr.lineTo(x2 - halfBase, y2);
} else {
cr.lineTo(this._arrowOrigin + halfBase, y2);
cr.lineTo(this._arrowOrigin, y2 + rise);
@ -329,19 +369,20 @@ const BoxPointer = new Lang.Class({
}
}
cr.lineTo(x1 + borderRadius, y2);
// bottom-left corner
cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius,
Math.PI/2, Math.PI);
if (!skipBottomLeft) {
cr.lineTo(x1 + borderRadius, y2);
cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius,
Math.PI/2, Math.PI);
}
if (this._arrowSide == St.Side.LEFT) {
if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
cr.lineTo(x1, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase);
cr.lineTo(x1 - rise, this._arrowOrigin);
} else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
cr.lineTo(x1 - rise, this._arrowOrigin);
cr.lineTo(x1, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase);
if (skipTopLeft) {
cr.lineTo(x1, y1 + halfBase);
cr.lineTo(x1 - rise, y1);
cr.lineTo(x1 + borderRadius, y1);
} else if (skipBottomLeft) {
cr.lineTo(x1 - rise, y2)
cr.lineTo(x1 - rise, y2 - halfBase);
} else {
cr.lineTo(x1, this._arrowOrigin + halfBase);
cr.lineTo(x1 - rise, this._arrowOrigin);
@ -349,11 +390,11 @@ const BoxPointer = new Lang.Class({
}
}
cr.lineTo(x1, y1 + borderRadius);
// top-left corner
cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius,
Math.PI, 3*Math.PI/2);
if (!skipTopLeft) {
cr.lineTo(x1, y1 + borderRadius);
cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius,
Math.PI, 3*Math.PI/2);
}
Clutter.cairo_set_source_color(cr, backgroundColor);
cr.fillPreserve();
@ -406,9 +447,7 @@ const BoxPointer = new Lang.Class({
let arrowBase = themeNode.get_length('-arrow-base');
let borderRadius = themeNode.get_length('-arrow-border-radius');
let margin = (4 * borderRadius + borderWidth + arrowBase);
let halfMargin = margin / 2;
let themeNode = this.actor.get_theme_node();
let gap = themeNode.get_length('-boxpointer-gap');
let padding = themeNode.get_length('-arrow-rise');
@ -429,8 +468,24 @@ const BoxPointer = new Lang.Class({
break;
}
// Now align and position the pointing axis, making sure
// it fits on screen
// Now align and position the pointing axis, making sure it fits on
// screen. If the arrowOrigin is so close to the edge that the arrow
// will not be isosceles, we try to compensate as follows:
// - We skip the rounded corner and settle for a right angled arrow
// as shown below. See _drawBorder for further details.
// |\_____
// |
// |
// - If the arrow was going to be acute angled, we move the position
// of the box to maintain the arrow's accuracy.
let arrowOrigin;
let halfBase = Math.floor(arrowBase/2);
let halfBorder = borderWidth / 2;
let halfMargin = margin / 2;
let [x1, y1] = [halfBorder, halfBorder];
let [x2, y2] = [natWidth - halfBorder, natHeight - halfBorder];
switch (this._arrowSide) {
case St.Side.TOP:
case St.Side.BOTTOM:
@ -438,7 +493,17 @@ const BoxPointer = new Lang.Class({
resX = Math.max(resX, monitor.x + padding);
resX = Math.min(resX, monitor.x + monitor.width - (padding + natWidth));
this.setArrowOrigin(sourceCenterX - resX);
arrowOrigin = sourceCenterX - resX;
if (arrowOrigin <= (x1 + (borderRadius + halfBase))) {
if (arrowOrigin > x1)
resX += (arrowOrigin - x1);
arrowOrigin = x1;
} else if (arrowOrigin >= (x2 - (borderRadius + halfBase))) {
if (arrowOrigin < x2)
resX -= (x2 - arrowOrigin);
arrowOrigin = x2;
}
break;
case St.Side.LEFT:
@ -448,10 +513,21 @@ const BoxPointer = new Lang.Class({
resY = Math.max(resY, monitor.y + padding);
resY = Math.min(resY, monitor.y + monitor.height - (padding + natHeight));
this.setArrowOrigin(sourceCenterY - resY);
arrowOrigin = sourceCenterY - resY;
if (arrowOrigin <= (y1 + (borderRadius + halfBase))) {
if (arrowOrigin > y1)
resY += (arrowOrigin - y1);
arrowOrigin = y1;
} else if (arrowOrigin >= (y2 - (borderRadius + halfBase))) {
if (arrowOrigin < y2)
resX -= (y2 - arrowOrigin);
arrowOrigin = y2;
}
break;
}
this.setArrowOrigin(arrowOrigin);
let parent = this.actor.get_parent();
let success, x, y;
while (!success) {

View File

@ -270,8 +270,9 @@ const DBusEventSource = new Lang.Class({
this._loadEvents(false);
},
_onEventsReceived: function([appointments]) {
_onEventsReceived: function(results, error) {
let newEvents = [];
let appointments = results ? results[0] : null;
if (appointments != null) {
for (let n = 0; n < appointments.length; n++) {
let a = appointments[n];
@ -339,22 +340,10 @@ const DBusEventSource = new Lang.Class({
});
Signals.addSignalMethods(DBusEventSource.prototype);
// Calendar:
// @eventSource: is an object implementing the EventSource API, e.g. the
// requestRange(), getEvents(), hasEvents() methods and the ::changed signal.
const Calendar = new Lang.Class({
Name: 'Calendar',
_init: function(eventSource) {
if (eventSource) {
this._eventSource = eventSource;
this._eventSource.connect('changed', Lang.bind(this,
function() {
this._update(false);
}));
}
_init: function() {
this._weekStart = Shell.util_get_week_start();
this._weekdate = NaN;
this._digitWidth = NaN;
@ -391,6 +380,24 @@ const Calendar = new Lang.Class({
this._buildHeader ();
},
// @eventSource: is an object implementing the EventSource API, e.g. the
// requestRange(), getEvents(), hasEvents() methods and the ::changed signal.
setEventSource: function(eventSource) {
if (this._eventSource) {
this._eventSource.disconnect(this._eventSourceChangedId);
this._eventSource = null;
}
this._eventSource = eventSource;
if (this._eventSource) {
this._eventSourceChangedId = this._eventSource.connect('changed', Lang.bind(this, function() {
this._update(false);
}));
this._update(true);
}
},
// Sets the calendar to show a specific date
setDate: function(date, forceReload) {
if (!_sameDay(date, this._selectedDate)) {
@ -621,16 +628,25 @@ Signals.addSignalMethods(Calendar.prototype);
const EventsList = new Lang.Class({
Name: 'EventsList',
_init: function(eventSource) {
_init: function() {
this.actor = new St.BoxLayout({ vertical: true, style_class: 'events-header-vbox'});
this._date = new Date();
this._eventSource = eventSource;
this._eventSource.connect('changed', Lang.bind(this, this._update));
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
this._desktopSettings.connect('changed', Lang.bind(this, this._update));
this._weekStart = Shell.util_get_week_start();
},
this._update();
setEventSource: function(eventSource) {
if (this._eventSource) {
this._eventSource.disconnect(this._eventSourceChangedId);
this._eventSource = null;
}
this._eventSource = eventSource;
if (this._eventSource) {
this._eventSourceChangedId = this._eventSource.connect('changed', Lang.bind(this, this._update));
}
},
_addEvent: function(dayNameBox, timeBox, eventTitleBox, includeDayName, day, time, desc) {

93
js/ui/centerLayout.js Normal file
View File

@ -0,0 +1,93 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Clutter = imports.gi.Clutter;
const St = imports.gi.St;
function connectLayoutManager(layoutManager, styleChanged) {
let widget, styleChangedId;
function _styleChanged() {
let themeNode = widget.get_theme_node();
styleChanged(themeNode, widget);
}
function actorChanged() {
if (widget) {
widget.disconnect(styleChangedId);
styleChangedId = 0;
}
let actor = layoutManager.get_actor();
if (actor && actor instanceof St.Widget) {
widget = actor;
styleChangedId = widget.connect('style-changed', _styleChanged);
_styleChanged();
}
}
layoutManager.connect('notify::actor', actorChanged);
return layoutManager;
}
function connectSpacing(layoutManager) {
return connectLayoutManager(layoutManager, function(themeNode, widget) {
layoutManager.spacing = themeNode.get_length('spacing');
});
}
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 [centerMinWidth, centerNaturalWidth] = center.get_preferred_width(availWidth);
let sideWidth = (availWidth - centerNaturalWidth) / 2;
let childBox = new Clutter.ActorBox();
childBox.y1 = box.y1;
childBox.y2 = box.y1 + availHeight;
if (left) {
let [leftMinWidth, leftNaturalWidth] = left.get_preferred_width(availWidth);
let leftSide = Math.min(Math.floor(sideWidth), leftNaturalWidth);
if (rtl) {
childBox.x1 = availWidth - leftSide;
childBox.x2 = availWidth;
} else {
childBox.x1 = 0;
childBox.x2 = leftSide;
}
childBox.x1 += box.x1;
left.allocate(childBox, flags);
}
childBox.x1 = box.x1 + Math.ceil(sideWidth);
childBox.x2 = childBox.x1 + centerNaturalWidth;
center.allocate(childBox, flags);
if (right) {
let [rightMinWidth, rightNaturalWidth] = right.get_preferred_width(availWidth);
let rightSide = Math.min(Math.floor(sideWidth), rightNaturalWidth);
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

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

View File

@ -34,28 +34,30 @@ const AutomountManager = new Lang.Class({
this._inhibited = false;
this._loginManager = LoginManager.getLoginManager();
Main.screenShield.connect('lock-status-changed', Lang.bind(this, this._lockStatusChanged));
this._volumeMonitor = Gio.VolumeMonitor.get();
},
this._volumeMonitor.connect('volume-added',
Lang.bind(this,
this._onVolumeAdded));
this._volumeMonitor.connect('volume-removed',
Lang.bind(this,
this._onVolumeRemoved));
this._volumeMonitor.connect('drive-connected',
Lang.bind(this,
this._onDriveConnected));
this._volumeMonitor.connect('drive-disconnected',
Lang.bind(this,
this._onDriveDisconnected));
this._volumeMonitor.connect('drive-eject-button',
Lang.bind(this,
this._onDriveEjectButton));
enable: function() {
this._volumeAddedId = this._volumeMonitor.connect('volume-added', Lang.bind(this, this._onVolumeAdded));
this._volumeRemovedId = this._volumeMonitor.connect('volume-removed', Lang.bind(this, this._onVolumeRemoved));
this._driveConnectedId = this._volumeMonitor.connect('drive-connected', Lang.bind(this, this._onDriveConnected));
this._driveDisconnectedId = this._volumeMonitor.connect('drive-disconnected', Lang.bind(this, this._onDriveDisconnected));
this._driveEjectButtonId = this._volumeMonitor.connect('drive-eject-button', Lang.bind(this, this._onDriveEjectButton));
Mainloop.idle_add(Lang.bind(this, this._startupMountAll));
this._mountAllId = Mainloop.idle_add(Lang.bind(this, this._startupMountAll));
},
disable: function() {
this._volumeMonitor.disconnect(this._volumeAddedId);
this._volumeMonitor.disconnect(this._volumeRemovedId);
this._volumeMonitor.disconnect(this._driveConnectedId);
this._volumeMonitor.disconnect(this._driveDisconnectedId);
this._volumeMonitor.disconnect(this._driveEjectButtonId);
if (this._mountAllId > 0) {
Mainloop.source_remove(this._mountAllId);
this._mountAllId = 0;
}
},
_InhibitorsChanged: function(object, senderName, [inhibtor]) {
@ -68,17 +70,6 @@ const AutomountManager = new Lang.Class({
}));
},
_lockStatusChanged: function(shield, locked) {
if (!locked) {
this._volumeQueue.forEach(Lang.bind(this, function(volume) {
this._checkAndMountVolume(volume);
}));
}
// clear the queue anyway
this._volumeQueue = [];
},
_startupMountAll: function() {
let volumes = this._volumeMonitor.get_volumes();
volumes.forEach(Lang.bind(this, function(volume) {
@ -87,6 +78,7 @@ const AutomountManager = new Lang.Class({
allowAutorun: false });
}));
this._mountAllId = 0;
return false;
},
@ -96,9 +88,6 @@ const AutomountManager = new Lang.Class({
if (!this._loginManager.sessionActive)
return;
if (Main.screenShield.locked)
return;
global.play_theme_sound(0, 'device-added-media');
},
@ -108,9 +97,6 @@ const AutomountManager = new Lang.Class({
if (!this._loginManager.sessionActive)
return;
if (Main.screenShield.locked)
return;
global.play_theme_sound(0, 'device-removed-media');
},
@ -159,13 +145,6 @@ const AutomountManager = new Lang.Class({
// don't attempt automount
if (!this._loginManager.sessionActive)
return;
if (Main.screenShield.locked) {
if (this._volumeQueue.indexOf(volume) == -1)
this._volumeQueue.push(volume);
return;
}
}
if (this._inhibited)
@ -259,3 +238,4 @@ const AutomountManager = new Lang.Class({
});
}
});
const Component = AutomountManager;

View File

@ -44,6 +44,17 @@ function isMountRootHidden(root) {
return (path.indexOf('/.') != -1);
}
function isMountNonLocal(mount) {
// If the mount doesn't have an associated volume, that means it's
// an uninteresting filesystem. Most devices that we care about will
// have a mount, like media players and USB sticks.
let volume = mount.get_volume();
if (volume == null)
return true;
return (volume.get_identifier("class") == "network");
}
function startAppForMount(app, mount) {
let files = [];
let root = mount.get_root();
@ -83,13 +94,21 @@ const ContentTypeDiscoverer = new Lang.Class({
_init: function(callback) {
this._callback = callback;
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
},
guessContentTypes: function(mount) {
// guess mount's content types using GIO
mount.guess_content_type(false, null,
Lang.bind(this,
this._onContentTypeGuessed));
let autorunEnabled = !this._settings.get_boolean(SETTING_DISABLE_AUTORUN);
let shouldScan = autorunEnabled && !isMountNonLocal(mount);
if (shouldScan) {
// guess mount's content types using GIO
mount.guess_content_type(false, null,
Lang.bind(this,
this._onContentTypeGuessed));
} else {
this._emitCallback(mount, []);
}
},
_onContentTypeGuessed: function(mount, res) {
@ -147,33 +166,50 @@ const AutorunManager = new Lang.Class({
this._volumeMonitor = Gio.VolumeMonitor.get();
this._volumeMonitor.connect('mount-added',
Lang.bind(this,
this._onMountAdded));
this._volumeMonitor.connect('mount-removed',
Lang.bind(this,
this._onMountRemoved));
this._transDispatcher = new AutorunTransientDispatcher(this);
},
this._transDispatcher = new AutorunTransientDispatcher();
this._createResidentSource();
_ensureResidentSource: function() {
if (this._residentSource)
return;
let mounts = this._volumeMonitor.get_mounts();
mounts.forEach(Lang.bind(this, function (mount) {
let discoverer = new ContentTypeDiscoverer(Lang.bind (this,
function (mount, apps) {
this._residentSource.addMount(mount, apps);
}));
discoverer.guessContentTypes(mount);
this._residentSource = new AutorunResidentSource(this);
let destroyId = this._residentSource.connect('destroy', Lang.bind(this, function() {
this._residentSource.disconnect(destroyId);
this._residentSource = null;
}));
},
_createResidentSource: function() {
this._residentSource = new AutorunResidentSource();
this._residentSource.connect('destroy',
Lang.bind(this,
this._createResidentSource));
enable: function() {
this._scanMounts();
this._mountAddedId = this._volumeMonitor.connect('mount-added', Lang.bind(this, this._onMountAdded));
this._mountRemovedId = this._volumeMonitor.connect('mount-removed', Lang.bind(this, this._onMountRemoved));
},
disable: function() {
if (this._residentSource)
this._residentSource.destroy();
this._volumeMonitor.disconnect(this._mountAddedId);
this._volumeMonitor.disconnect(this._mountRemovedId);
},
_processMount: function(mount, hotplug) {
let discoverer = new ContentTypeDiscoverer(Lang.bind(this, function(mount, apps, contentTypes) {
this._ensureResidentSource();
this._residentSource.addMount(mount, apps);
if (hotplug)
this._transDispatcher.addMount(mount, apps, contentTypes);
}));
discoverer.guessContentTypes(mount);
},
_scanMounts: function() {
let mounts = this._volumeMonitor.get_mounts();
mounts.forEach(Lang.bind(this, function(mount) {
this._processMount(mount, false);
}));
},
_onMountAdded: function(monitor, mount) {
@ -182,18 +218,13 @@ const AutorunManager = new Lang.Class({
if (!this._loginManager.sessionActive)
return;
let discoverer = new ContentTypeDiscoverer(Lang.bind (this,
function (mount, apps, contentTypes) {
this._transDispatcher.addMount(mount, apps, contentTypes);
this._residentSource.addMount(mount, apps);
}));
discoverer.guessContentTypes(mount);
this._processMount(mount, true);
},
_onMountRemoved: function(monitor, mount) {
this._transDispatcher.removeMount(mount);
this._residentSource.removeMount(mount);
if (this._residentSource)
this._residentSource.removeMount(mount);
},
ejectMount: function(mount) {
@ -260,13 +291,18 @@ const AutorunResidentSource = new Lang.Class({
Name: 'AutorunResidentSource',
Extends: MessageTray.Source,
_init: function() {
_init: function(manager) {
this.parent(_("Removable Devices"), 'media-removable');
this.showInLockScreen = false;
this._mounts = [];
this._notification = new AutorunResidentNotification(this);
this._manager = manager;
this._notification = new AutorunResidentNotification(this._manager, this);
},
buildRightClickMenu: function() {
return null;
},
addMount: function(mount, apps) {
@ -316,7 +352,7 @@ const AutorunResidentNotification = new Lang.Class({
Name: 'AutorunResidentNotification',
Extends: MessageTray.Notification,
_init: function(source) {
_init: function(manager, source) {
this.parent(source, source.title, null, { customContent: true });
// set the notification as resident
@ -324,6 +360,7 @@ const AutorunResidentNotification = new Lang.Class({
this._layout = new St.BoxLayout ({ style_class: 'hotplug-resident-box',
vertical: true });
this._manager = manager;
this.addActor(this._layout,
{ x_expand: true,
@ -371,7 +408,7 @@ const AutorunResidentNotification = new Lang.Class({
expand: true });
let ejectIcon =
new St.Icon({ icon_name: 'media-eject',
new St.Icon({ icon_name: 'media-eject-symbolic',
style_class: 'hotplug-resident-eject-icon' });
let ejectButton =
@ -386,7 +423,7 @@ const AutorunResidentNotification = new Lang.Class({
}));
ejectButton.connect('clicked', Lang.bind(this, function() {
Main.autorunManager.ejectMount(mount);
this._manager.ejectMount(mount);
}));
return item;
@ -396,7 +433,8 @@ const AutorunResidentNotification = new Lang.Class({
const AutorunTransientDispatcher = new Lang.Class({
Name: 'AutorunTransientDispatcher',
_init: function() {
_init: function(manager) {
this._manager = manager;
this._sources = [];
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
},
@ -439,7 +477,7 @@ const AutorunTransientDispatcher = new Lang.Class({
return;
// add a new source
this._sources.push(new AutorunTransientSource(mount, apps));
this._sources.push(new AutorunTransientSource(this._manager, mount, apps));
},
addMount: function(mount, apps, contentTypes) {
@ -492,22 +530,22 @@ const AutorunTransientSource = new Lang.Class({
Name: 'AutorunTransientSource',
Extends: MessageTray.Source,
_init: function(mount, apps) {
_init: function(manager, mount, apps) {
this._manager = manager;
this.mount = mount;
this.apps = apps;
this.parent(mount.get_name());
this._notification = new AutorunTransientNotification(this);
this._notification = new AutorunTransientNotification(this._manager, this);
// add ourselves as a source, and popup the notification
Main.messageTray.add(this);
this.notify(this._notification);
},
createIcon: function(size) {
return new St.Icon({ gicon: this.mount.get_icon(),
icon_size: size });
getIcon: function() {
return this.mount.get_icon();
}
});
@ -515,9 +553,10 @@ const AutorunTransientNotification = new Lang.Class({
Name: 'AutorunTransientNotification',
Extends: MessageTray.Notification,
_init: function(source) {
_init: function(manager, source) {
this.parent(source, source.title, null, { customContent: true });
this._manager = manager;
this._box = new St.BoxLayout({ style_class: 'hotplug-transient-box',
vertical: true });
this.addActor(this._box);
@ -569,7 +608,7 @@ const AutorunTransientNotification = new Lang.Class({
_buttonForEject: function() {
let box = new St.BoxLayout();
let icon = new St.Icon({ icon_name: 'media-eject',
let icon = new St.Icon({ icon_name: 'media-eject-symbolic',
style_class: 'hotplug-notification-item-icon' });
box.add(icon);
@ -586,10 +625,11 @@ const AutorunTransientNotification = new Lang.Class({
style_class: 'hotplug-notification-item' });
button.connect('clicked', Lang.bind(this, function() {
Main.autorunManager.ejectMount(this._mount);
this._manager.ejectMount(this._mount);
}));
return button;
}
});
const Component = AutorunManager;

View File

@ -192,23 +192,36 @@ const KeyringDialog = new Lang.Class({
},
_onContinueButton: function() {
this.prompt.complete()
this.prompt.complete();
},
_onCancelButton: function() {
this.prompt.cancel()
this.prompt.cancel();
},
});
function init() {
prompter = new Gcr.SystemPrompter();
prompter.connect('new-prompt', function(prompter) {
let dialog = new KeyringDialog();
return dialog.prompt;
});
const KeyringPrompter = new Lang.Class({
Name: 'KeyringPrompter',
let connection = Gio.DBus.session;
prompter.register(connection);
Gio.bus_own_name_on_connection (connection, 'org.gnome.keyring.SystemPrompter',
Gio.BusNameOwnerFlags.REPLACE, null, null);
}
_init: function() {
this._prompter = new Gcr.SystemPrompter();
this._prompter.connect('new-prompt', function(prompter) {
let dialog = new KeyringDialog();
return dialog.prompt;
});
this._dbusId = null;
},
enable: function() {
this._prompter.register(Gio.DBus.session);
this._dbusId = Gio.DBus.session.own_name('org.gnome.keyring.SystemPrompter',
Gio.BusNameOwnerFlags.REPLACE, null, null);
},
disable: function() {
this._prompter.unregister(false);
Gio.DBus.session.unown_name(this._dbusId);
}
});
const Component = KeyringPrompter;

View File

@ -1,23 +1,4 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/*
* Copyright 2011 Giovanni Campagna <scampa.giovanni@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
*/
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
@ -427,7 +408,10 @@ const VPNRequestHandler = new Lang.Class({
}
},
cancel: function() {
cancel: function(respond) {
if (respond)
this._agent.respond(this._requestId, Shell.NetworkAgentResponse.USER_CANCELED);
if (this._newStylePlugin && this._shellDialog) {
this._shellDialog.close(global.get_current_time());
this._shellDialog.destroy();
@ -603,7 +587,7 @@ const NetworkAgent = new Lang.Class({
Name: 'NetworkAgent',
_init: function() {
this._native = new Shell.NetworkAgent({ auto_register: true,
this._native = new Shell.NetworkAgent({ auto_register: false,
identifier: 'org.gnome.Shell.NetworkAgent' });
this._dialogs = { };
@ -613,6 +597,24 @@ const NetworkAgent = new Lang.Class({
this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest));
},
enable: function() {
this._native.register();
},
disable: function() {
let requestId;
for (requestId in this._dialogs)
this._dialogs[requestId].cancel();
this._dialogs = { };
for (requestId in this._vpnRequests)
this._vpnRequests[requestId].cancel(true);
this._vpnRequests = { };
this._native.unregister();
},
_newRequest: function(agent, requestId, connection, settingName, hints, flags) {
if (settingName == 'vpn') {
this._vpnRequest(requestId, connection, hints, flags);
@ -633,7 +635,7 @@ const NetworkAgent = new Lang.Class({
this._dialogs[requestId].destroy();
delete this._dialogs[requestId];
} else if (this._vpnRequests[requestId]) {
this._vpnRequests[requestId].cancel();
this._vpnRequests[requestId].cancel(false);
delete this._vpnRequests[requestId];
}
},
@ -702,3 +704,4 @@ const NetworkAgent = new Lang.Class({
}
}
});
const Component = NetworkAgent;

View File

@ -1,24 +1,4 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/*
* Copyright 2010 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
const Lang = imports.lang;
const Signals = imports.signals;
@ -33,6 +13,7 @@ const Mainloop = imports.mainloop;
const Polkit = imports.gi.Polkit;
const PolkitAgent = imports.gi.PolkitAgent;
const Components = imports.ui.components;
const ModalDialog = imports.ui.modalDialog;
const ShellEntry = imports.ui.shellEntry;
const UserMenu = imports.ui.userMenu;
@ -318,7 +299,7 @@ const AuthenticationDialog = new Lang.Class({
},
_onUserChanged: function() {
if (this._user.is_loaded) {
if (this._user.is_loaded && this._userAvatar) {
this._userAvatar.update();
this._userAvatar.actor.show();
}
@ -336,11 +317,20 @@ const AuthenticationAgent = new Lang.Class({
Name: 'AuthenticationAgent',
_init: function() {
this._currentDialog = null;
this._isCompleting = false;
this._handle = null;
this._native = new Shell.PolkitAuthenticationAgent();
this._native.connect('initiate', Lang.bind(this, this._onInitiate));
this._native.connect('cancel', Lang.bind(this, this._onCancel));
this._currentDialog = null;
this._isCompleting = false;
},
enable: function() {
this._native.register();
},
disable: function() {
this._native.unregister();
},
_onInitiate: function(nativeAgent, actionId, message, iconName, cookie, userNames) {
@ -398,6 +388,4 @@ const AuthenticationAgent = new Lang.Class({
}
});
function init() {
let agent = new AuthenticationAgent();
}
const Component = AuthenticationAgent;

View File

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

View File

@ -11,10 +11,10 @@ const Tpl = imports.gi.TelepathyLogger;
const Tp = imports.gi.TelepathyGLib;
const History = imports.misc.history;
const Params = imports.misc.params;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const Params = imports.misc.params;
const PopupMenu = imports.ui.popupMenu;
// See Notification.appendMessage
const SCROLLBACK_IMMEDIATE_TIME = 60; // 1 minute
@ -63,10 +63,10 @@ function makeMessageFromTplEvent(event) {
};
}
const Client = new Lang.Class({
Name: 'Client',
const TelepathyClient = new Lang.Class({
Name: 'TelepathyClient',
_init : function() {
_init: function() {
// channel path -> ChatSource
this._chatSources = {};
this._chatState = Tp.ChannelChatState.ACTIVE;
@ -89,9 +89,9 @@ const Client = new Lang.Class({
// channel matching its filters is detected.
// The second argument, recover, means _observeChannels will be run
// for any existing channel as well.
this._tpClient = new Shell.TpClient({ 'account-manager': this._accountManager,
'name': 'GnomeShell',
'uniquify-name': true })
this._tpClient = new Shell.TpClient({ name: 'GnomeShell',
account_manager: this._accountManager,
uniquify_name: true });
this._tpClient.set_observe_channels_func(
Lang.bind(this, this._observeChannels));
this._tpClient.set_approve_channels_func(
@ -99,6 +99,10 @@ const Client = new Lang.Class({
this._tpClient.set_handle_channels_func(
Lang.bind(this, this._handleChannels));
// Watch subscription requests and connection errors
this._subscriptionSource = null;
this._accountSource = null;
// Workaround for gjs not supporting GPtrArray in signals.
// See BGO bug #653941 for context.
this._tpClient.set_contact_list_changed_func(
@ -108,21 +112,26 @@ const Client = new Lang.Class({
// needed
this._tpClient.set_delegated_channels_callback(
Lang.bind(this, this._delegatedChannelsCb));
},
enable: function() {
try {
this._tpClient.register();
} catch (e) {
throw new Error('Couldn\'t register Telepathy client. Error: \n' + e);
}
// Watch subscription requests and connection errors
this._subscriptionSource = null;
this._accountSource = null;
this._accountManagerValidityChangedId = this._accountManager.connect('account-validity-changed',
Lang.bind(this, this._accountValidityChanged));
this._accountManager.connect('account-validity-changed',
Lang.bind(this, this._accountValidityChanged));
if (!this._accountManager.is_prepared(Tp.AccountManager.get_feature_quark_core()))
this._accountManager.prepare_async(null, Lang.bind(this, this._accountManagerPrepared));
},
this._accountManager.prepare_async(null, Lang.bind(this, this._accountManagerPrepared));
disable: function() {
this._tpClient.unregister();
this._accountManager.disconnect(this._accountManagerValidityChangedId);
this._accountManagerValidityChangedId = 0;
},
_observeChannels: function(observer, account, conn, channels,
@ -473,6 +482,22 @@ const ChatSource = new Lang.Class({
this._getLogMessages();
},
buildRightClickMenu: function() {
let item;
let rightClickMenu = this.parent();
item = new PopupMenu.PopupMenuItem('');
item.actor.connect('notify::mapped', Lang.bind(this, function() {
item.label.set_text(this.isMuted ? _("Unmute") : _("Mute"));
}));
item.connect('activate', Lang.bind(this, function() {
this.setMuted(!this.isMuted);
this.emit('done-displaying-content');
}));
rightClickMenu.add(item.actor);
return rightClickMenu;
},
_updateAlias: function() {
let oldAlias = this.title;
let newAlias = this._contact.get_alias();
@ -484,51 +509,42 @@ const ChatSource = new Lang.Class({
this._notification.appendAliasChange(oldAlias, newAlias);
},
createIcon: function(size) {
this._iconBox = new St.Bin({ style_class: 'avatar-box' });
this._iconBox._size = size;
let textureCache = St.TextureCache.get_default();
getIcon: function() {
let file = this._contact.get_avatar_file();
if (file) {
let uri = file.get_uri();
this._iconBox.child = textureCache.load_uri_async(uri, this._iconBox._size, this._iconBox._size);
return new Gio.FileIcon({ file: file });
} else {
this._iconBox.child = new St.Icon({ icon_name: 'avatar-default',
icon_size: this._iconBox._size });
return new Gio.ThemedIcon({ name: 'avatar-default' });
}
return this._iconBox;
},
createSecondaryIcon: function() {
let iconBox = new St.Bin();
iconBox.child = new St.Icon({ style_class: 'secondary-icon' });
getSecondaryIcon: function() {
let iconName;
let presenceType = this._contact.get_presence_type();
switch (presenceType) {
case Tp.ConnectionPresenceType.AVAILABLE:
iconBox.child.icon_name = 'user-available';
iconName = 'user-available';
break;
case Tp.ConnectionPresenceType.BUSY:
iconBox.child.icon_name = 'user-busy';
iconName = 'user-busy';
break;
case Tp.ConnectionPresenceType.OFFLINE:
iconBox.child.icon_name = 'user-offline';
iconName = 'user-offline';
break;
case Tp.ConnectionPresenceType.HIDDEN:
iconBox.child.icon_name = 'user-invisible';
iconName = 'user-invisible';
break;
case Tp.ConnectionPresenceType.AWAY:
iconBox.child.icon_name = 'user-away';
iconName = 'user-away';
break;
case Tp.ConnectionPresenceType.EXTENDED_AWAY:
iconBox.child.icon_name = 'user-idle';
iconName = 'user-idle';
break;
default:
iconBox.child.icon_name = 'user-offline';
iconName = 'user-offline';
}
return iconBox;
return new Gio.ThemedIcon({ name: iconName });
},
_updateAvatarIcon: function() {
@ -539,7 +555,9 @@ const ChatSource = new Lang.Class({
open: function(notification) {
if (this._client.is_handling_channel(this._channel)) {
// We are handling the channel, try to pass it to Empathy
this._client.delegate_channels_async([this._channel], global.get_current_time(), '', null);
this._client.delegate_channels_async([this._channel],
global.get_current_time(),
'org.freedesktop.Telepathy.Client.Empathy.Chat', null);
}
else {
// We are not the handler, just ask to present the channel
@ -708,7 +726,7 @@ const ChatSource = new Lang.Class({
title = GLib.markup_escape_text(this.title, -1);
this._notification.update(this._notification.title, null, { customContent: true, secondaryIcon: this.createSecondaryIcon() });
this._notification.update(this._notification.title, null, { customContent: true, secondaryGIcon: this.getSecondaryIcon() });
if (message)
msg += ' <i>(' + GLib.markup_escape_text(message, -1) + ')</i>';
@ -721,8 +739,6 @@ const ChatSource = new Lang.Class({
this._pendingMessages.splice(idx, 1);
this.countUpdated();
}
else
throw new Error('Message not in our pending list: ' + message);
},
_ackMessages: function() {
@ -738,7 +754,7 @@ const ChatNotification = new Lang.Class({
Extends: MessageTray.Notification,
_init: function(source) {
this.parent(source, source.title, null, { customContent: true, secondaryIcon: source.createSecondaryIcon() });
this.parent(source, source.title, null, { customContent: true, secondaryGIcon: source.getSecondaryIcon() });
this.setResident(true);
this._responseEntry = new St.Entry({ style_class: 'chat-response',
@ -747,6 +763,14 @@ const ChatNotification = new Lang.Class({
this._responseEntry.clutter_text.connect('text-changed', Lang.bind(this, this._onEntryChanged));
this.setActionArea(this._responseEntry);
this._responseEntry.clutter_text.connect('key-focus-in', Lang.bind(this, function() {
this.focused = true;
}));
this._responseEntry.clutter_text.connect('key-focus-out', Lang.bind(this, function() {
this.focused = false;
this.emit('unfocused');
}));
this._oldMaxScrollAdjustment = 0;
this._createScrollArea();
this._lastGroup = null;
@ -908,24 +932,32 @@ const ChatNotification = new Lang.Class({
let format;
// Show a week day and time if date is in the last week
if (daysAgo < 1 || (daysAgo < 7 && now.getDay() != date.getDay())) {
/* Translators: this is a time format string followed by a date.
If applicable, replace %X with a strftime format valid for your
locale, without seconds. */
// Show only the hour if date is on today
if(daysAgo < 1){
format = "<b>%H:%M</b>";
}
// Show the word "Yesterday" and time if date is on yesterday
else if(daysAgo <2){
/* Translators: this is a time format string followed by the word "Yesterday". i.e. "14:30 on Yesterday"*/
// xgettext:no-c-format
format = _("Sent at <b>%X</b> on <b>%A</b>");
format = _("<b>%H:%M</b> on Yesterday");
}
// Show a week day and time if date is in the last week
else if (daysAgo < 7) {
/* Translators: this is a time format string followed by a week day name. i.e. "14:30 on Monday*/
// xgettext:no-c-format
format = _("<b>%H:%M</b> on <b>%A</b>");
} else if (date.getYear() == now.getYear()) {
/* Translators: this is a time format in the style of "Wednesday, May 25",
shown when you get a chat message in the same year. */
/* 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 */
// xgettext:no-c-format
format = _("Sent on <b>%A</b>, <b>%B %d</b>");
format = _("<b>%H:%M</b> on <b>%A</b>, <b>%B</b> <b>%d</b>");
} else {
/* Translators: this is a time format in the style of "Wednesday, May 25, 2012",
shown when you get a chat message in a different year. */
/* 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 */
// xgettext:no-c-format
format = _("Sent on <b>%A</b>, <b>%B %d</b>, %Y");
format = _("<b>%H:%M</b> on <b>%A</b>, <b>%B</b> <b>%d</b>, %Y");
}
return date.toLocaleFormat(format);
@ -938,8 +970,7 @@ const ChatNotification = new Lang.Class({
let timeLabel = this._append({ body: this._formatTimestamp(lastMessageDate),
group: 'meta',
styles: ['chat-meta-message'],
childProps: { expand: true, x_fill: false,
x_align: St.Align.END },
childProps: { expand: true, x_fill: false },
noTimestamp: true,
timestamp: lastMessageTime });
@ -1040,9 +1071,8 @@ const ApproverSource = new Lang.Class({
this.parent();
},
createIcon: function(size) {
return new St.Icon({ gicon: this._gicon,
icon_size: size });
getIcon: function() {
return this._gicon;
}
});
@ -1399,3 +1429,4 @@ const AccountNotification = new Lang.Class({
this.parent();
}
});
const Component = TelepathyClient;

View File

@ -54,16 +54,17 @@ const CtrlAltTabManager = new Lang.Class({
},
focusGroup: function(item) {
if (global.stage_input_mode == Shell.StageInputMode.NONREACTIVE ||
global.stage_input_mode == Shell.StageInputMode.NORMAL)
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
if (item.window)
if (item.window) {
Main.activateWindow(item.window);
else if (item.focusCallback)
} else if (item.focusCallback) {
item.focusCallback();
else
} else {
if (global.stage_input_mode == Shell.StageInputMode.NONREACTIVE ||
global.stage_input_mode == Shell.StageInputMode.NORMAL)
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
item.root.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
}
},
// Sort the items into a consistent order; panel first, tray last,

View File

@ -21,6 +21,18 @@ const DASH_ITEM_LABEL_SHOW_TIME = 0.15;
const DASH_ITEM_LABEL_HIDE_TIME = 0.1;
const DASH_ITEM_HOVER_TIMEOUT = 300;
function getAppFromSource(source) {
if (source instanceof AppDisplay.AppWellIcon) {
let appSystem = Shell.AppSystem.get_default();
return appSystem.lookup_app(source.getId());
} else if (source.metaWindow) {
let tracker = Shell.WindowTracker.get_default();
return tracker.get_window_app(source.metaWindow);
} else {
return null;
}
}
// A container like StBin, but taking the child's scale into account
// when requesting a size
const DashItemContainer = new Lang.Class({
@ -36,7 +48,11 @@ const DashItemContainer = new Lang.Class({
Lang.bind(this, this._allocate));
this.actor._delegate = this;
this.label = null;
this._labelText = "";
this.label = new St.Label({ style_class: 'dash-label'});
this.label.hide();
Main.layoutManager.addChrome(this.label);
this.actor.label_actor = this.label;
this.child = null;
this._childScale = 1;
@ -91,9 +107,10 @@ const DashItemContainer = new Lang.Class({
},
showLabel: function() {
if (this.label == null)
if (!this._labelText)
return;
this.label.set_text(this._labelText);
this.label.opacity = 0;
this.label.show();
@ -124,19 +141,10 @@ const DashItemContainer = new Lang.Class({
},
setLabelText: function(text) {
if (this.label == null) {
this.label = new St.Label({ style_class: 'dash-label'});
Main.layoutManager.addChrome(this.label);
this.label.hide();
}
this.label.set_text(text);
this._labelText = text;
},
hideLabel: function () {
if (this.label == null)
return;
Tweener.addTween(this.label,
{ opacity: 0,
time: DASH_ITEM_LABEL_HIDE_TIME,
@ -250,7 +258,7 @@ const ShowAppsIcon = new Lang.Class({
this.toggleButton._delegate = this;
this.setChild(this.toggleButton);
this.setHover(false);
this.setDragApp(null);
this.toggleButton.label_actor = this.label;
},
@ -262,31 +270,45 @@ const ShowAppsIcon = new Lang.Class({
return this._iconActor;
},
setHover: function(hovered) {
this.toggleButton.set_hover(hovered);
if (this._iconActor)
this._iconActor.set_hover(hovered);
_canRemoveApp: function(app) {
if (app == null)
return false;
if (hovered)
let id = app.get_id();
let isFavorite = AppFavorites.getAppFavorites().isFavorite(id);
return isFavorite;
},
setDragApp: function(app) {
let canRemove = this._canRemoveApp(app);
this.toggleButton.set_hover(canRemove);
if (this._iconActor)
this._iconActor.set_hover(canRemove);
if (canRemove)
this.setLabelText(_("Remove from Favorites"));
else
this.setLabelText(_("Show Applications"));
},
// Rely on the dragged item being a favorite
handleDragOver: function(source, actor, x, y, time) {
let app = getAppFromSource(source);
if (app == null)
return DND.DragMotionResult.NO_DROP;
let id = app.get_id();
let isFavorite = AppFavorites.getAppFavorites().isFavorite(id);
if (!isFavorite)
return DND.DragMotionResult.NO_DROP;
return DND.DragMotionResult.MOVE_DROP;
},
acceptDrop: function(source, actor, x, y, time) {
let app = null;
if (source instanceof AppDisplay.AppWellIcon) {
let appSystem = Shell.AppSystem.get_default();
app = appSystem.lookup_app(source.getId());
} else if (source.metaWindow) {
let tracker = Shell.WindowTracker.get_default();
app = tracker.get_window_app(source.metaWindow);
}
let app = getAppFromSource(source);
if (app == null)
return false;
let id = app.get_id();
@ -310,6 +332,39 @@ const DragPlaceholderItem = new Lang.Class({
}
});
const DashActor = new Lang.Class({
Name: 'DashActor',
Extends: St.Widget,
_init: function() {
let layout = new Clutter.BoxLayout({ orientation: Clutter.Orientation.VERTICAL });
this.parent({ name: 'dash',
layout_manager: layout,
clip_to_allocation: true });
},
vfunc_allocate: function(box, flags) {
let contentBox = this.get_theme_node().get_content_box(box);
let availWidth = contentBox.x2 - contentBox.x1;
this.set_allocation(box, flags);
let [appIcons, showAppsButton] = this.get_children();
let [showAppsMinHeight, showAppsNatHeight] = showAppsButton.get_preferred_height(availWidth);
let childBox = new Clutter.ActorBox();
childBox.x1 = contentBox.x1;
childBox.y1 = contentBox.y1;
childBox.x2 = contentBox.x2;
childBox.y2 = contentBox.y2 - showAppsNatHeight;
appIcons.allocate(childBox, flags);
childBox.y1 = contentBox.y2 - showAppsNatHeight;
childBox.y2 = contentBox.y2;
showAppsButton.allocate(childBox, flags);
}
});
const Dash = new Lang.Class({
Name: 'Dash',
@ -317,6 +372,9 @@ const Dash = new Lang.Class({
this._maxHeight = -1;
this.iconSize = 64;
this._shownInitially = false;
this.visible = false;
this._hiddenX;
this._targetX;
this._dragPlaceholder = null;
this._dragPlaceholderPos = -1;
@ -325,14 +383,11 @@ const Dash = new Lang.Class({
this._resetHoverTimeoutId = 0;
this._labelShowing = false;
this._container = new St.BoxLayout({ name: 'dash',
vertical: true,
clip_to_allocation: true });
this._container = new DashActor();
this._box = new St.BoxLayout({ vertical: true,
clip_to_allocation: true });
this._box._delegate = this;
this._container.add(this._box);
this._container.add_actor(this._box);
this._showAppsIcon = new ShowAppsIcon();
this._showAppsIcon.icon.setIconSize(this.iconSize);
@ -340,30 +395,30 @@ const Dash = new Lang.Class({
this.showAppsButton = this._showAppsIcon.toggleButton;
this._container.add(this._showAppsIcon.actor);
this._container.add_actor(this._showAppsIcon.actor);
this.actor = new St.Bin({ child: this._container });
this.actor.connect('notify::height', Lang.bind(this,
function() {
if (this._maxHeight != this.actor.height)
this._queueRedisplay();
this._maxHeight = this.actor.height;
if (this.actor.height > this._maxHeight)
this._maxHeight = this.actor.height;
}));
this._workId = Main.initializeDeferredWork(this._box, Lang.bind(this, this._redisplay));
this._tracker = Shell.WindowTracker.get_default();
this._appSystem = Shell.AppSystem.get_default();
this._appSystem.connect('installed-changed', Lang.bind(this, this._queueRedisplay));
AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._queueRedisplay));
this._appSystem.connect('app-state-changed', Lang.bind(this, this._queueRedisplay));
Main.overview.connect('item-drag-begin',
Main.overview.connect('app-drag-begin',
Lang.bind(this, this._onDragBegin));
Main.overview.connect('item-drag-end',
Main.overview.connect('app-drag-end',
Lang.bind(this, this._onDragEnd));
Main.overview.connect('item-drag-cancelled',
Main.overview.connect('app-drag-cancelled',
Lang.bind(this, this._onDragCancelled));
Main.overview.connect('window-drag-begin',
Lang.bind(this, this._onDragBegin));
@ -395,31 +450,25 @@ const Dash = new Lang.Class({
_endDrag: function() {
this._clearDragPlaceholder();
this._showAppsIcon.setDragApp(null);
DND.removeDragMonitor(this._dragMonitor);
},
_onDragMotion: function(dragEvent) {
let app = null;
if (dragEvent.source instanceof AppDisplay.AppWellIcon)
app = this._appSystem.lookup_app(dragEvent.source.getId());
else if (dragEvent.source.metaWindow)
app = this._tracker.get_window_app(dragEvent.source.metaWindow);
else
let app = getAppFromSource(dragEvent.source);
if (app == null)
return DND.DragMotionResult.CONTINUE;
let id = app.get_id();
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
let srcIsFavorite = (id in favorites);
let showAppsHovered =
this._showAppsIcon.actor.contains(dragEvent.targetActor);
if (!this._box.contains(dragEvent.targetActor) || showAppsHovered)
this._clearDragPlaceholder();
this._showAppsIcon.setHover(showAppsHovered);
if (showAppsHovered)
this._showAppsIcon.setDragApp(app);
else
this._showAppsIcon.setDragApp(null);
return DND.DragMotionResult.CONTINUE;
},
@ -501,6 +550,17 @@ const Dash = new Lang.Class({
}
},
_computeDashX: function() {
this._targetX = this.actor.get_x();
let rtl = (this.actor.get_text_direction() == Clutter.TextDirection.RTL);
if (rtl)
this._hiddenX = this._targetX + this.actor.width;
else
this._hiddenX = -this.actor.width;
},
_adjustIconSize: function() {
// For the icon size, we only consider children which are "proper"
// icons (i.e. ignoring drag placeholders) and which are not
@ -596,6 +656,8 @@ const Dash = new Lang.Class({
}
});
}
this._computeDashX();
},
_redisplay: function () {
@ -712,6 +774,7 @@ const Dash = new Lang.Class({
// of items, to avoid all items zooming in at once
if (!this._shownInitially) {
this._shownInitially = true;
this.visible = true;
return;
}
@ -732,11 +795,7 @@ const Dash = new Lang.Class({
},
handleDragOver : function(source, actor, x, y, time) {
let app = null;
if (source instanceof AppDisplay.AppWellIcon)
app = this._appSystem.lookup_app(source.getId());
else if (source.metaWindow)
app = this._tracker.get_window_app(source.metaWindow);
let app = getAppFromSource(source);
// Don't allow favoriting of transient apps
if (app == null || app.is_window_backed())
@ -817,12 +876,7 @@ const Dash = new Lang.Class({
// Draggable target interface
acceptDrop : function(source, actor, x, y, time) {
let app = null;
if (source instanceof AppDisplay.AppWellIcon) {
app = this._appSystem.lookup_app(source.getId());
} else if (source.metaWindow) {
app = this._tracker.get_window_app(source.metaWindow);
}
let app = getAppFromSource(source);
// Don't allow favoriting of transient apps
if (app == null || app.is_window_backed()) {
@ -865,6 +919,34 @@ const Dash = new Lang.Class({
}));
return true;
},
show: function() {
if (this.visible)
return;
this.visible = true;
this.actor.show();
Tweener.addTween(this.actor, { translation_x: this._targetX,
transition: 'easeOutQuad',
time: DASH_ANIMATION_TIME
});
},
hide: function() {
if (!this.visible)
return;
this.visible = false;
Tweener.addTween(this.actor, { translation_x: this._hiddenX,
transition: 'easeOutQuad',
time: DASH_ANIMATION_TIME,
onComplete: Lang.bind(this, function () {
this.actor.hide();
})
});
}
});

View File

@ -70,16 +70,8 @@ const DateMenuButton = new Lang.Class({
this._date.style_class = 'datemenu-date-label';
vbox.add(this._date);
if (Main.sessionMode.showCalendarEvents) {
this._eventSource = new Calendar.DBusEventSource();
this._eventList = new Calendar.EventsList(this._eventSource);
} else {
this._eventSource = null;
this._eventList = null;
}
// Calendar
this._calendar = new Calendar.Calendar(this._eventSource);
this._eventList = new Calendar.EventsList();
this._calendar = new Calendar.Calendar();
this._calendar.connect('selected-date-changed',
Lang.bind(this, function(calendar, date) {
@ -97,31 +89,34 @@ const DateMenuButton = new Lang.Class({
separator.setColumnWidths(1);
vbox.add(separator.actor, {y_align: St.Align.END, expand: true, y_fill: false});
item.actor.show_on_set_parent = false;
item.actor.can_focus = false;
item.actor.reparent(vbox);
this._dateAndTimeSeparator = separator;
}
if (Main.sessionMode.showCalendarEvents) {
// Add vertical separator
this._separator = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
pseudo_class: 'highlighted' });
this._separator.connect('repaint', Lang.bind(this, _onVertSepRepaint));
hbox.add(this._separator);
item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
pseudo_class: 'highlighted' });
item.connect('repaint', Lang.bind(this, _onVertSepRepaint));
hbox.add(item);
// Fill up the second column
vbox = new St.BoxLayout({ name: 'calendarEventsArea',
vertical: true });
hbox.add(vbox, { expand: true });
// Fill up the second column
vbox = new St.BoxLayout({name: 'calendarEventsArea',
vertical: true});
hbox.add(vbox, { expand: true });
// Event list
vbox.add(this._eventList.actor, { expand: true });
// Event list
vbox.add(this._eventList.actor, { expand: true });
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});
item = new PopupMenu.PopupMenuItem(_("Open Calendar"));
item.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
item.actor.can_focus = false;
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
}
this._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) {
@ -151,6 +146,51 @@ const DateMenuButton = new Lang.Class({
this._clock = new GnomeDesktop.WallClock();
this._clock.connect('notify::clock', Lang.bind(this, this._updateClockAndDate));
this._updateClockAndDate();
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._sessionUpdated();
},
_calendarSettingsChanged: function() {
let exec = this._calendarSettings.get_string('exec');
let fullExec = GLib.find_program_in_path(exec);
this._openCalendarItem.actor.visible = fullExec != null;
},
_setEventsVisibility: function(visible) {
this._openCalendarItem.actor.visible = visible;
this._separator.visible = visible;
if (visible) {
let alignment = 0.25;
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
alignment = 1.0 - alignment;
this.menu._arrowAlignment = alignment;
this._eventList.actor.get_parent().show();
} else {
this.menu._arrowAlignment = 0.5;
this._eventList.actor.get_parent().hide();
}
},
_setEventSource: function(eventSource) {
this._calendar.setEventSource(eventSource);
this._eventList.setEventSource(eventSource);
},
_sessionUpdated: function() {
let eventSource;
let showEvents = Main.sessionMode.showCalendarEvents;
if (showEvents) {
eventSource = new Calendar.DBusEventSource();
} else {
eventSource = null;
}
this._setEventSource(eventSource);
this._setEventsVisibility(showEvents);
// This needs to be handled manually, as the code to
// autohide separators doesn't work across the vbox
this._dateAndTimeSeparator.actor.visible = Main.sessionMode.allowSettings;
},
_updateClockAndDate: function() {
@ -165,14 +205,13 @@ const DateMenuButton = new Lang.Class({
_onOpenCalendarActivate: function() {
this.menu.close();
let calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' });
let tool = calendarSettings.get_string('exec');
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 = calendarSettings.get_boolean('needs-term');
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');

View File

@ -235,6 +235,10 @@ const _Draggable = new Lang.Class({
if (this.actor._delegate && this.actor._delegate.getDragActor) {
this._dragActor = this.actor._delegate.getDragActor(this._dragStartX, this._dragStartY);
this._dragActor.reparent(Main.uiGroup);
this._dragActor.raise_top();
Shell.util_set_hidden_from_pick(this._dragActor, true);
// Drag actor does not always have to be the same as actor. For example drag actor
// can be an image that's part of the actor. So to perform "snap back" correctly we need
// to know what was the drag actor source.
@ -263,12 +267,17 @@ const _Draggable = new Lang.Class({
this._dragOffsetY = this._dragActor.y - this._dragStartY;
} else {
this._dragActor = this.actor;
this._dragActorSource = undefined;
this._dragOrigParent = this.actor.get_parent();
this._dragOrigX = this._dragActor.x;
this._dragOrigY = this._dragActor.y;
this._dragOrigScale = this._dragActor.scale_x;
this._dragActor.reparent(Main.uiGroup);
this._dragActor.raise_top();
Shell.util_set_hidden_from_pick(this._dragActor, true);
let [actorStageX, actorStageY] = this.actor.get_transformed_position();
this._dragOffsetX = actorStageX - this._dragStartX;
this._dragOffsetY = actorStageY - this._dragStartY;
@ -280,10 +289,6 @@ const _Draggable = new Lang.Class({
scaledHeight / this.actor.height);
}
this._dragActor.reparent(Main.uiGroup);
this._dragActor.raise_top();
Shell.util_set_hidden_from_pick(this._dragActor, true);
this._dragOrigOpacity = this._dragActor.opacity;
if (this._dragActorOpacity != undefined)
this._dragActor.opacity = this._dragActorOpacity;

View File

@ -8,6 +8,7 @@ const Gio = imports.gi.Gio;
const St = imports.gi.St;
const ExtensionUtils = imports.misc.extensionUtils;
const Main = imports.ui.main;
const ExtensionState = {
ENABLED: 1,
@ -38,6 +39,9 @@ const disconnect = Lang.bind(_signals, _signals.disconnect);
const ENABLED_EXTENSIONS_KEY = 'enabled-extensions';
var initted = false;
var enabled;
function disableExtension(uuid) {
let extension = ExtensionUtils.extensions[uuid];
if (!extension)
@ -102,8 +106,6 @@ function enableExtension(uuid) {
extensionOrder.push(uuid);
extension.stateObj.enable();
let stylesheetFile = extension.dir.get_child('stylesheet.css');
if (stylesheetFile.query_exists(null)) {
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
@ -111,6 +113,8 @@ function enableExtension(uuid) {
extension.stylesheet = stylesheetFile;
}
extension.stateObj.enable();
extension.state = ExtensionState.ENABLED;
_signals.emit('extension-state-changed', extension);
}
@ -216,6 +220,9 @@ function initExtension(uuid) {
function onEnabledExtensionsChanged() {
let newEnabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
if (!enabled)
return;
// Find and enable all the newly enabled extensions: UUIDs found in the
// new setting, but not in the old one.
newEnabledExtensions.filter(function(uuid) {
@ -243,7 +250,7 @@ function onEnabledExtensionsChanged() {
enabledExtensions = newEnabledExtensions;
}
function loadExtensions() {
function _loadExtensions() {
global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged);
enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
@ -257,3 +264,43 @@ function loadExtensions() {
});
finder.scanExtensions();
}
function enableAllExtensions() {
if (enabled)
return;
if (!initted) {
_loadExtensions();
initted = true;
} else {
enabledExtensions.forEach(function(uuid) {
enableExtension(uuid);
});
}
enabled = true;
}
function disableAllExtensions() {
if (!enabled)
return;
if (initted) {
enabledExtensions.forEach(function(uuid) {
disableExtension(uuid);
});
}
enabled = false;
}
function _sessionUpdated() {
if (Main.sessionMode.allowExtensions)
enableAllExtensions();
else
disableAllExtensions();
}
function init() {
Main.sessionMode.connect('updated', _sessionUpdated);
_sessionUpdated();
}

View File

@ -10,11 +10,12 @@ const St = imports.gi.St;
const Main = imports.ui.main;
const Params = imports.misc.params;
function _navigateActor(actor, needsGrab) {
function _navigateActor(actor) {
if (!actor)
return;
if (needsGrab && actor instanceof St.Widget)
let needsGrab = true;
if (actor instanceof St.Widget)
needsGrab = !actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
if (needsGrab)
actor.grab_key_focus();
@ -84,11 +85,7 @@ const GrabHelper = new Lang.Class({
},
get currentGrab() {
let idx = this._grabStack.length - 1;
while (idx >= 0 && this._grabStack[idx].untracked)
idx--;
return this._grabStack[idx] || {};
return this._grabStack[this._grabStack.length - 1] || {};
},
_findStackIndex: function(actor) {
@ -137,14 +134,9 @@ const GrabHelper = new Lang.Class({
// input mode to %Shell.StageInputMode.FOCUSED, and ungrab() will
// revert it back, and re-focus the previously-focused window (if
// another window hasn't been explicitly focused before then).
//
// If @params contains { untracked: true }, then it will be skipped
// when the grab helper ungrabs for you, or when calculating
// currentGrab.
grab: function(params) {
params = Params.parse(params, { actor: null,
modal: false,
untracked: false,
grabFocus: false,
onUngrab: null });
@ -155,55 +147,109 @@ const GrabHelper = new Lang.Class({
if (this.isActorGrabbed(params.actor))
return true;
if (this._grabFocusCount == 0 && this._modalCount == 0) {
if (!this._fullGrab(hadFocus, params.modal, params.grabFocus))
return false;
}
params.savedFocus = focus;
if (params.modal && !this._takeModalGrab())
return false;
if (params.grabFocus && !this._takeFocusGrab(hadFocus))
return false;
if (hadFocus || params.grabFocus)
_navigateActor(newFocus);
this._grabStack.push(params);
if (params.modal)
this._modalCount++;
if (params.grabFocus)
this._grabFocusCount++;
_navigateActor(newFocus, hadFocus);
return true;
},
_fullGrab: function(hadFocus, modal, grabFocus) {
let metaDisplay = global.screen.get_display();
if (modal) {
_takeModalGrab: function() {
let firstGrab = (this._modalCount == 0);
if (firstGrab) {
if (!Main.pushModal(this._owner))
return false;
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
this._eventId = global.stage.connect('event', Lang.bind(this, this._onEvent));
}
this._grabbedFromKeynav = hadFocus;
this._preGrabInputMode = global.stage_input_mode;
this._prevFocusedWindow = null;
this._modalCount++;
return true;
},
if (grabFocus) {
_releaseModalGrab: function() {
this._modalCount--;
if (this._modalCount > 0)
return;
if (this._capturedEventId > 0) {
global.stage.disconnect(this._capturedEventId);
this._capturedEventId = 0;
}
if (this._eventId > 0) {
global.stage.disconnect(this._eventId);
this._eventId = 0;
}
Main.popModal(this._owner);
global.sync_pointer();
},
_takeFocusGrab: function(hadFocus) {
let firstGrab = (this._grabFocusCount == 0);
if (firstGrab) {
let metaDisplay = global.screen.get_display();
this._grabbedFromKeynav = hadFocus;
this._preGrabInputMode = global.stage_input_mode;
this._prevFocusedWindow = metaDisplay.focus_window;
if (this._preGrabInputMode == Shell.StageInputMode.NONREACTIVE ||
this._preGrabInputMode == Shell.StageInputMode.NORMAL) {
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
}
}
if (modal || grabFocus) {
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
this._eventId = global.stage.connect('event', Lang.bind(this, this._onEvent));
this._keyFocusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
this._focusWindowChangedId = metaDisplay.connect('notify::focus-window', Lang.bind(this, this._focusWindowChanged));
}
this._grabFocusCount++;
return true;
},
_releaseFocusGrab: function() {
this._grabFocusCount--;
if (this._grabFocusCount > 0)
return;
if (this._keyFocusNotifyId > 0) {
global.stage.disconnect(this._keyFocusNotifyId);
this._keyFocusNotifyId = 0;
}
if (!this._focusWindowChanged > 0) {
let metaDisplay = global.screen.get_display();
metaDisplay.disconnect(this._focusWindowChangedId);
this._focusWindowChangedId = 0;
}
let prePopInputMode = global.stage_input_mode;
if (this._grabbedFromKeynav) {
if (this._preGrabInputMode == Shell.StageInputMode.FOCUSED &&
prePopInputMode != Shell.StageInputMode.FULLSCREEN)
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
}
if (this._prevFocusedWindow) {
let metaDisplay = global.screen.get_display();
if (!metaDisplay.focus_window) {
metaDisplay.set_input_focus_window(this._prevFocusedWindow,
false, global.get_current_time());
}
}
},
// ignoreRelease:
//
// Make sure that the next button release event evaluated by the
@ -228,12 +274,14 @@ const GrabHelper = new Lang.Class({
if (grabStackIndex < 0)
return;
let focus = global.stage.key_focus;
let hadFocus = focus && this._isWithinGrabbedActor(focus);
let poppedGrabs = this._grabStack.slice(grabStackIndex);
// "Pop" all newly ungrabbed actors off the grab stack
// by truncating the array.
this._grabStack.length = grabStackIndex;
let wasModal = this._modalCount > 0;
for (let i = poppedGrabs.length - 1; i >= 0; i--) {
let poppedGrab = poppedGrabs[i];
@ -241,63 +289,15 @@ const GrabHelper = new Lang.Class({
poppedGrab.onUngrab();
if (poppedGrab.modal)
this._modalCount--;
this._releaseModalGrab();
if (poppedGrab.grabFocus)
this._grabFocusCount--;
this._releaseFocusGrab();
}
let focus = global.stage.key_focus;
let hadFocus = focus && this._isWithinGrabbedActor(focus);
if (this._grabFocusCount == 0 && this._modalCount == 0)
this._fullUngrab(wasModal);
let poppedGrab = poppedGrabs[0];
_navigateActor(poppedGrab.savedFocus, hadFocus);
},
_fullUngrab: function(wasModal) {
if (this._capturedEventId > 0) {
global.stage.disconnect(this._capturedEventId);
this._capturedEventId = 0;
}
if (this._eventId > 0) {
global.stage.disconnect(this._eventId);
this._eventId = 0;
}
if (this._keyFocusNotifyId > 0) {
global.stage.disconnect(this._keyFocusNotifyId);
this._keyFocusNotifyId = 0;
}
if (!this._focusWindowChanged > 0) {
let metaDisplay = global.screen.get_display();
metaDisplay.disconnect(this._focusWindowChangedId);
this._focusWindowChangedId = 0;
}
let prePopInputMode = global.stage_input_mode;
if (wasModal) {
Main.popModal(this._owner);
global.sync_pointer();
}
if (this._grabbedFromKeynav) {
if (this._preGrabInputMode == Shell.StageInputMode.FOCUSED &&
prePopInputMode != Shell.StageInputMode.FULLSCREEN)
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
}
if (this._prevFocusedWindow) {
let metaDisplay = global.screen.get_display();
if (!metaDisplay.focus_window) {
metaDisplay.set_input_focus_window(this._prevFocusedWindow,
false, global.get_current_time());
}
if (hadFocus) {
let poppedGrab = poppedGrabs[0];
_navigateActor(poppedGrab.savedFocus);
}
},
@ -318,12 +318,15 @@ const GrabHelper = new Lang.Class({
if (this._isWithinGrabbedActor(event.get_source()))
return false;
if (Main.keyboard.shouldTakeEvent(event))
return false;
if (button) {
// If we have a press event, ignore the next event,
// which should be a release event.
if (press)
this._ignoreRelease = true;
this.ungrab();
this.ungrab({ actor: this._grabStack[0].actor });
}
return this._modalCount > 0;

View File

@ -198,7 +198,11 @@ const IconGrid = new Lang.Class({
_getPreferredHeight: function (grid, forWidth, alloc) {
let children = this._getVisibleChildren();
let [nColumns, usedWidth] = this._computeLayout(forWidth);
let nColumns;
if (forWidth < 0)
nColumns = children.length;
else
nColumns = this._computeLayout(forWidth)[0];
let nRows;
if (nColumns > 0)
nRows = Math.ceil(children.length / nColumns);

View File

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

View File

@ -17,6 +17,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 PLYMOUTH_TRANSITION_TIME = 1;
const MonitorConstraint = new Lang.Class({
Name: 'MonitorConstraint',
@ -42,6 +43,8 @@ const MonitorConstraint = new Lang.Class({
},
set primary(v) {
if (v)
this._index = -1;
this._primary = v;
if (this.actor)
this.actor.queue_relayout();
@ -53,6 +56,7 @@ const MonitorConstraint = new Lang.Class({
},
set index(v) {
this._primary = false;
this._index = v;
if (this.actor)
this.actor.queue_relayout();
@ -100,6 +104,7 @@ const LayoutManager = new Lang.Class({
this.primaryMonitor = null;
this.primaryIndex = -1;
this._hotCorners = [];
this._background = null;
this._leftPanelBarrier = 0;
this._rightPanelBarrier = 0;
this._trayBarrier = 0;
@ -120,7 +125,8 @@ const LayoutManager = new Lang.Class({
this.panelBox.connect('allocation-changed',
Lang.bind(this, this._updatePanelBarriers));
this.trayBox = new St.BoxLayout({ name: 'trayBox' });
this.trayBox = new St.Widget({ name: 'trayBox',
layout_manager: new Clutter.BinLayout() });
this.addChrome(this.trayBox);
this.trayBox.connect('allocation-changed',
Lang.bind(this, this._updateTrayBarrier));
@ -318,10 +324,41 @@ const LayoutManager = new Lang.Class({
},
_startupAnimation: function() {
this.panelBox.anchor_y = this.panelBox.height;
let plymouthTransitionRunning = false;
// If we're the greeter, put up the xrootpmap actor
// and fade it out to have a nice transition from plymouth
// to the greeter. Otherwise, we'll just animate the panel,
// as usual.
if (Main.sessionMode.isGreeter) {
this._background = Meta.BackgroundActor.new_for_screen(global.screen);
if (this._background != null) {
Main.uiGroup.add_actor(this._background);
Tweener.addTween(this._background,
{ opacity: 0,
time: PLYMOUTH_TRANSITION_TIME,
transition: 'linear',
onComplete: this._fadeBackgroundComplete,
onCompleteScope: this });
plymouthTransitionRunning = true;
}
}
if (!plymouthTransitionRunning)
this._fadeBackgroundComplete();
},
_fadeBackgroundComplete: function() {
// Don't animate the strut
this._chrome.freezeUpdateRegions();
this.panelBox.anchor_y = this.panelBox.height;
if (this._background != null) {
this._background.destroy();
this._background = null;
}
Tweener.addTween(this.panelBox,
{ anchor_y: 0,
time: STARTUP_ANIMATION_TIME,
@ -336,7 +373,6 @@ const LayoutManager = new Lang.Class({
},
showKeyboard: function () {
Main.messageTray.hide();
this.keyboardBox.raise_top();
Tweener.addTween(this.keyboardBox,
{ anchor_y: this.keyboardBox.height,
@ -350,6 +386,8 @@ const LayoutManager = new Lang.Class({
time: KEYBOARD_ANIMATION_TIME,
transition: 'easeOutQuad'
});
this.emit('keyboard-visible-changed', true);
},
_showKeyboardComplete: function() {
@ -364,7 +402,6 @@ const LayoutManager = new Lang.Class({
},
hideKeyboard: function (immediate) {
Main.messageTray.hide();
if (this._keyboardHeightNotifyId) {
this.keyboardBox.disconnect(this._keyboardHeightNotifyId);
this._keyboardHeightNotifyId = 0;
@ -381,6 +418,8 @@ const LayoutManager = new Lang.Class({
time: immediate ? 0 : KEYBOARD_ANIMATION_TIME,
transition: 'easeOutQuad'
});
this.emit('keyboard-visible-changed', false);
},
_hideKeyboardComplete: function() {
@ -639,7 +678,6 @@ const Chrome = new Lang.Class({
this._monitors = [];
this._inOverview = false;
this._isLocked = false;
this._updateRegionIdle = 0;
this._freezeUpdateCount = 0;
@ -658,12 +696,9 @@ const Chrome = new Lang.Class({
},
init: function() {
Main.overview.connect('showing',
Lang.bind(this, this._overviewShowing));
Main.overview.connect('hidden',
Lang.bind(this, this._overviewHidden));
Main.screenShield.connect('lock-status-changed',
Lang.bind(this, this._lockStatusChanged));
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) {
@ -749,10 +784,13 @@ const Chrome = new Lang.Class({
_actorReparented: function(actor, oldParent) {
let newParent = actor.get_parent();
if (!newParent)
if (!newParent) {
this._untrackActor(actor);
else
} else {
let i = this._findActor(actor);
let actorData = this._trackedActors[i];
actorData.isToplevel = (newParent == Main.uiGroup);
}
},
_updateVisibility: function() {
@ -763,7 +801,7 @@ const Chrome = new Lang.Class({
if (!actorData.isToplevel)
continue;
if (this._inOverview || this._isLocked)
if (this._inOverview || !Main.sessionMode.hasWindows)
visible = true;
else if (this.findMonitorForActor(actorData.actor).inFullscreen)
visible = false;
@ -785,8 +823,7 @@ const Chrome = new Lang.Class({
this._queueUpdateRegions();
},
_lockStatusChanged: function(shield, locked) {
this._isLocked = locked;
_sessionUpdated: function() {
this._updateVisibility();
this._queueUpdateRegions();
},

View File

@ -371,7 +371,8 @@ const ObjInspector = new Lang.Class({
this._parentList = [];
this.actor = new St.ScrollView({ x_fill: true, y_fill: true });
this.actor = new St.ScrollView({ pivot_point: new Clutter.Point({ x: 0.5, y: 0.5 }),
x_fill: true, y_fill: true });
this.actor.get_hscroll_bar().hide();
this._container = new St.BoxLayout({ name: 'LookingGlassPropertyInspector',
style_class: 'lg-dialog',
@ -443,10 +444,6 @@ const ObjInspector = new Lang.Class({
this.actor.show();
if (sourceActor) {
this.actor.set_scale(0, 0);
let [sourceX, sourceY] = sourceActor.get_transformed_position();
let [sourceWidth, sourceHeight] = sourceActor.get_transformed_size();
this.actor.move_anchor_point(Math.floor(sourceX + sourceWidth / 2),
Math.floor(sourceY + sourceHeight / 2));
Tweener.addTween(this.actor, { scale_x: 1, scale_y: 1,
transition: 'easeOutQuad',
time: 0.2 });
@ -1092,8 +1089,8 @@ const LookingGlass = new Lang.Class({
this.actor.width = myWidth;
this.actor.height = myHeight;
this._objInspector.actor.set_size(Math.floor(myWidth * 0.8), Math.floor(myHeight * 0.8));
this._objInspector.actor.set_position(this.actor.x + Math.floor(myWidth * 0.1),
this._targetY + Math.floor(myHeight * 0.1));
this._objInspector.actor.set_position(primary.x + this.actor.x + Math.floor(myWidth * 0.1),
primary.y + this._targetY + Math.floor(myHeight * 0.1));
},
insertObject: function(obj) {

View File

@ -10,12 +10,9 @@ const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const AutomountManager = imports.ui.automountManager;
const AutorunManager = imports.ui.autorunManager;
const Components = imports.ui.components;
const CtrlAltTab = imports.ui.ctrlAltTab;
const EndSessionDialog = imports.ui.endSessionDialog;
const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent;
const KeyringPrompt = imports.ui.keyringPrompt;
const Environment = imports.ui.environment;
const ExtensionSystem = imports.ui.extensionSystem;
const ExtensionDownloader = imports.ui.extensionDownloader;
@ -23,11 +20,9 @@ const Keyboard = imports.ui.keyboard;
const MessageTray = imports.ui.messageTray;
const Overview = imports.ui.overview;
const Panel = imports.ui.panel;
const PlaceDisplay = imports.ui.placeDisplay;
const RunDialog = imports.ui.runDialog;
const Layout = imports.ui.layout;
const LookingGlass = imports.ui.lookingGlass;
const NetworkAgent = imports.ui.networkAgent;
const NotificationDaemon = imports.ui.notificationDaemon;
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
const ScreenShield = imports.ui.screenShield;
@ -35,7 +30,6 @@ const Scripting = imports.ui.scripting;
const SessionMode = imports.ui.sessionMode;
const ShellDBus = imports.ui.shellDBus;
const ShellMountOperation = imports.ui.shellMountOperation;
const TelepathyClient = imports.ui.telepathyClient;
const UnlockDialog = imports.ui.unlockDialog;
const WindowManager = imports.ui.windowManager;
const Magnifier = imports.ui.magnifier;
@ -43,10 +37,9 @@ const XdndHandler = imports.ui.xdndHandler;
const Util = imports.misc.util;
const OVERRIDES_SCHEMA = 'org.gnome.shell.overrides';
const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2266bbff);
const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
let automountManager = null;
let autorunManager = null;
let componentManager = null;
let panel = null;
let overview = null;
let runDialog = null;
@ -56,9 +49,7 @@ let messageTray = null;
let screenShield = null;
let notificationDaemon = null;
let windowAttentionHandler = null;
let telepathyClient = null;
let ctrlAltTabManager = null;
let recorder = null;
let sessionMode = null;
let shellDBusService = null;
let shellMountOpDBusService = null;
@ -70,7 +61,6 @@ let magnifier = null;
let xdndHandler = null;
let keyboard = null;
let layoutManager = null;
let networkAgent = null;
let _startDate;
let _defaultCssStylesheet = null;
let _cssStylesheet = null;
@ -78,69 +68,10 @@ let _overridesSettings = null;
let background = null;
function createUserSession() {
telepathyClient = new TelepathyClient.Client();
automountManager = new AutomountManager.AutomountManager();
autorunManager = new AutorunManager.AutorunManager();
networkAgent = new NetworkAgent.NetworkAgent();
_initRecorder();
}
function createGDMSession() {
screenShield.showDialog();
}
function createGDMLoginDialog(parentActor) {
// We do this this here instead of at the top to prevent GDM
// related code from getting loaded in normal user sessions
const LoginDialog = imports.gdm.loginDialog;
let loginDialog = new LoginDialog.LoginDialog(parentActor);
return [loginDialog, true];
}
function createSessionUnlockDialog(parentActor) {
let dialog = new UnlockDialog.UnlockDialog(parentActor);
return [dialog, false];
}
function createInitialSetupSession() {
networkAgent = new NetworkAgent.NetworkAgent();
}
function _initRecorder() {
let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
let desktopLockdownSettings = new Gio.Settings({ schema: 'org.gnome.desktop.lockdown' });
let bindingSettings = new Gio.Settings({ schema: 'org.gnome.shell.keybindings' });
global.display.add_keybinding('toggle-recording',
bindingSettings,
Meta.KeyBindingFlags.NONE, function() {
if (recorder == null) {
recorder = new Shell.Recorder({ stage: global.stage });
}
if (recorder.is_recording()) {
recorder.close();
Meta.enable_unredirect_for_screen(global.screen);
} else if (!desktopLockdownSettings.get_boolean('disable-save-to-disk')) {
// read the parameters from GSettings always in case they have changed
recorder.set_framerate(recorderSettings.get_int('framerate'));
/* Translators: this is a filename used for screencast recording */
// xgettext:no-c-format
recorder.set_filename(_("Screencast from %d %t") + '.' + recorderSettings.get_string('file-extension'));
let pipeline = recorderSettings.get_string('pipeline');
if (!pipeline.match(/^\s*$/))
recorder.set_pipeline(pipeline);
else
recorder.set_pipeline(null);
Meta.disable_unredirect_for_screen(global.screen);
recorder.record();
}
});
function _sessionUpdated() {
Meta.keybindings_set_custom_handler('panel-run-dialog', sessionMode.hasRunDialog ? openRunDialog : null);
if (sessionMode.isGreeter)
screenShield.showDialog();
}
function start() {
@ -206,51 +137,34 @@ function start() {
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
overview = new Overview.Overview();
magnifier = new Magnifier.Magnifier();
screenShield = new ScreenShield.ScreenShield();
screenSaverDBus = new ShellDBus.ScreenSaverDBus();
if (UnlockDialog.isSupported())
screenShield = new ScreenShield.ScreenShield();
else
screenShield = new ScreenShield.ScreenShieldFallback();
panel = new Panel.Panel();
wm = new WindowManager.WindowManager();
messageTray = new MessageTray.MessageTray();
keyboard = new Keyboard.Keyboard();
notificationDaemon = new NotificationDaemon.NotificationDaemon();
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
componentManager = new Components.ComponentManager();
sessionMode.createSession();
panel.init();
layoutManager.init();
keyboard.init();
overview.init();
if (sessionMode.hasWorkspaces)
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
false, -1, 1);
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
false, -1, 1);
Meta.keybindings_set_custom_handler('panel-main-menu', Lang.bind(overview, overview.toggle));
global.display.connect('overlay-key', Lang.bind(overview, overview.toggle));
if (sessionMode.hasRunDialog) {
Meta.keybindings_set_custom_handler('panel-run-dialog', function() {
getRunDialog().open();
});
}
if (sessionMode.hasOverview) {
Meta.keybindings_set_custom_handler('panel-main-menu', function () {
overview.toggle();
});
global.display.connect('overlay-key',
Lang.bind(overview, overview.toggle));
}
sessionMode.connect('update', _sessionUpdated);
_sessionUpdated();
// Provide the bus object for gnome-session to
// initiate logouts.
EndSessionDialog.init();
// Attempt to become a PolicyKit authentication agent
PolkitAuthenticationAgent.init()
// Become a prompter for gnome keyring
KeyringPrompt.init();
_startDate = new Date();
global.stage.connect('captured-event', _globalKeyPressHandler);
@ -275,10 +189,8 @@ function start() {
_nWorkspacesChanged();
if (sessionMode.allowExtensions) {
ExtensionDownloader.init();
ExtensionSystem.loadExtensions();
}
ExtensionDownloader.init();
ExtensionSystem.init();
}
let _workspaces = [];
@ -522,7 +434,6 @@ function notify(msg, details) {
messageTray.add(source);
let notification = new MessageTray.Notification(source, msg, details);
notification.setTransient(true);
notification.setShowWhenLocked(true);
source.notify(notification);
}
@ -617,7 +528,7 @@ function _globalKeyPressHandler(actor, event) {
if (!sessionMode.hasRunDialog)
return false;
getRunDialog().open();
openRunDialog();
return true;
case Meta.KeyBindingAction.PANEL_MAIN_MENU:
case Meta.KeyBindingAction.OVERLAY_KEY:
@ -763,11 +674,11 @@ function createLookingGlass() {
return lookingGlass;
}
function getRunDialog() {
function openRunDialog() {
if (runDialog == null) {
runDialog = new RunDialog.RunDialog();
}
return runDialog;
runDialog.open();
}
/**

View File

@ -4,6 +4,7 @@ const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const GnomeDesktop = imports.gi.GnomeDesktop;
const Atk = imports.gi.Atk;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
@ -35,8 +36,6 @@ const LONGER_SUMMARY_TIMEOUT = 4;
const HIDE_TIMEOUT = 0.2;
const LONGER_HIDE_TIMEOUT = 0.6;
const NOTIFICATION_ICON_SIZE = 24;
// We delay hiding of the tray if the mouse is within MOUSE_LEFT_ACTOR_THRESHOLD
// range from the point where it left the tray.
const MOUSE_LEFT_ACTOR_THRESHOLD = 20;
@ -143,14 +142,9 @@ const URLHighlighter = new Lang.Class({
let url = this._urls[urlId].url;
if (url.indexOf(':') == -1)
url = 'http://' + url;
try {
Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context());
return true;
} catch (e) {
// TODO: remove this after gnome 3 release
Util.spawn(['gvfs-open', url]);
return true;
}
Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context());
return true;
}
return false;
}));
@ -243,9 +237,7 @@ function makeCloseButton() {
closeButton.connect('style-changed', function() {
let themeNode = closeButton.get_theme_node();
closeButton.translation_x = themeNode.get_length('-shell-close-overlap-x');
// libcroco doesn't support negative units
closeButton.translation_y = -themeNode.get_length('-shell-close-overlap-y');
closeButton.translation_y = themeNode.get_length('-shell-close-overlap-y');
});
return closeButton;
@ -291,9 +283,12 @@ function makeCloseButton() {
// If @params contains a 'body' parameter, then that text will be added to
// the content area (as with addBody()).
//
// By default, the icon shown is created by calling
// source.createIcon(). However, if @params contains an 'icon'
// parameter, the passed in icon will be used.
// By default, the icon shown is the same as the source's.
// However, if @params contains a 'gicon' parameter, the passed in gicon
// will be used.
//
// You can add a secondary icon to the banner with 'secondaryGIcon'. There
// is no fallback for this icon.
//
// If @params contains a 'titleMarkup', 'bannerMarkup', or
// 'bodyMarkup' parameter with the value %true, then the corresponding
@ -308,6 +303,8 @@ function makeCloseButton() {
const Notification = new Lang.Class({
Name: 'Notification',
ICON_SIZE: 24,
IMAGE_SIZE: 125,
_init: function(source, title, banner, params) {
@ -318,7 +315,7 @@ const Notification = new Lang.Class({
// 'transient' is a reserved keyword in JS, so we have to use an alternate variable name
this.isTransient = false;
this.expanded = false;
this.showWhenLocked = false;
this.focused = false;
this.acknowledged = false;
this._destroyed = false;
this._useActionIcons = false;
@ -391,8 +388,8 @@ const Notification = new Lang.Class({
update: function(title, banner, params) {
params = Params.parse(params, { customContent: false,
body: null,
icon: null,
secondaryIcon: null,
gicon: null,
secondaryGIcon: null,
titleMarkup: false,
bannerMarkup: false,
bodyMarkup: false,
@ -402,12 +399,12 @@ const Notification = new Lang.Class({
let oldFocus = global.stage.key_focus;
if (this._icon && (params.icon || params.clear)) {
if (this._icon && (params.gicon || params.clear)) {
this._icon.destroy();
this._icon = null;
}
if (this._secondaryIcon && (params.secondaryIcon || params.clear)) {
if (this._secondaryIcon && (params.secondaryGIcon || params.clear)) {
this._secondaryIcon.destroy();
this._secondaryIcon = null;
}
@ -437,8 +434,14 @@ const Notification = new Lang.Class({
if (!this._scrollArea && !this._actionArea && !this._imageBin)
this._table.remove_style_class_name('multi-line-notification');
if (!this._icon) {
this._icon = params.icon || this.source.createIcon(NOTIFICATION_ICON_SIZE);
if (params.gicon) {
this._icon = new St.Icon({ gicon: params.gicon,
icon_size: this.ICON_SIZE });
} else {
this._icon = this.source.createIcon(this.ICON_SIZE);
}
if (this._icon) {
this._table.add(this._icon, { row: 0,
col: 0,
x_expand: false,
@ -447,11 +450,10 @@ const Notification = new Lang.Class({
y_align: St.Align.START });
}
if (!this._secondaryIcon) {
this._secondaryIcon = params.secondaryIcon;
if (this._secondaryIcon)
this._bannerBox.add_actor(this._secondaryIcon);
if (params.secondaryGIcon) {
this._secondaryIcon = new St.Icon({ gicon: params.secondaryGIcon,
style_class: 'secondary-icon' });
this._bannerBox.add_actor(this._secondaryIcon);
}
this.title = title;
@ -711,14 +713,6 @@ const Notification = new Lang.Class({
this.isTransient = isTransient;
},
setShowWhenLocked: function(show) {
if (show && !this.isTransient) {
throw new Error('ShowWhenLocked can only be set on a transient notification');
}
this.showWhenLocked = show;
},
setUseActionIcons: function(useIcons) {
this._useActionIcons = useIcons;
},
@ -952,11 +946,22 @@ const SourceActor = new Lang.Class({
}));
this._actorDestroyed = false;
this._counterLabel = new St.Label();
this._counterLabel = new St.Label( {x_align: Clutter.ActorAlign.CENTER,
x_expand: true,
y_align: Clutter.ActorAlign.CENTER,
y_expand: true });
this._counterBin = new St.Bin({ style_class: 'summary-source-counter',
child: this._counterLabel });
child: this._counterLabel,
layout_manager: new Clutter.BinLayout() });
this._counterBin.hide();
this._counterBin.connect('style-changed', Lang.bind(this, function() {
let themeNode = this._counterBin.get_theme_node();
this._counterBin.translation_x = themeNode.get_length('-shell-counter-overlap-x');
this._counterBin.translation_y = themeNode.get_length('-shell-counter-overlap-y');
}));
this._iconBin = new St.Bin({ width: size,
height: size,
x_fill: true,
@ -1025,7 +1030,14 @@ const SourceActor = new Lang.Class({
return;
this._counterBin.visible = this._source.countVisible;
this._counterLabel.set_text(this._source.count.toString());
let text;
if (this._source.count < 100)
text = this._source.count.toString();
else
text = String.fromCharCode(0x22EF); // midline horizontal ellipsis
this._counterLabel.set_text(text);
}
});
@ -1042,6 +1054,7 @@ const Source = new Lang.Class({
this.isChat = false;
this.isMuted = false;
this.showInLockScreen = true;
this.keepTrayOnSummaryClick = false;
this.notifications = [];
},
@ -1062,6 +1075,27 @@ const Source = new Lang.Class({
this.emit('count-updated');
},
buildRightClickMenu: function() {
let item;
let rightClickMenu = new St.BoxLayout({ name: 'summary-right-click-menu',
vertical: true });
item = new PopupMenu.PopupMenuItem(_("Open"));
item.connect('activate', Lang.bind(this, function() {
this.open();
this.emit('done-displaying-content');
}));
rightClickMenu.add(item.actor);
item = new PopupMenu.PopupMenuItem(_("Remove"));
item.connect('activate', Lang.bind(this, function() {
this.destroy();
this.emit('done-displaying-content');
}));
rightClickMenu.add(item.actor);
return rightClickMenu;
},
setTransient: function(isTransient) {
this.isTransient = isTransient;
},
@ -1082,10 +1116,14 @@ const Source = new Lang.Class({
// Provides a sane default implementation, override if you need
// something more fancy.
createIcon: function(size) {
return new St.Icon({ icon_name: this.iconName,
return new St.Icon({ gicon: this.getIcon(),
icon_size: size });
},
getIcon: function() {
return new Gio.ThemedIcon({ name: this.iconName });
},
_ensureMainIcon: function() {
if (this._mainIcon)
return;
@ -1190,6 +1228,7 @@ const SummaryItem = new Lang.Class({
can_focus: true,
track_hover: true });
this.actor.label_actor = new St.Label({ text: source.title });
this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPress));
this._sourceBox = new St.BoxLayout({ style_class: 'summary-source' });
this._sourceIcon = source.getSummaryIcon();
@ -1198,13 +1237,6 @@ const SummaryItem = new Lang.Class({
this.notificationStackWidget = new St.Widget({ layout_manager: new Clutter.BinLayout() });
this._closeButton = makeCloseButton();
this._closeButton.connect('clicked', Lang.bind(this, function() {
source.destroy();
this.emit('done-displaying-content');
}));
this.notificationStackWidget.add_actor(this._closeButton);
this.notificationStackView = new St.ScrollView({ style_class: source.isChat ? '' : 'summary-notification-stack-scrollview',
vscrollbar_policy: source.isChat ? Gtk.PolicyType.NEVER : Gtk.PolicyType.AUTOMATIC,
hscrollbar_policy: Gtk.PolicyType.NEVER });
@ -1213,6 +1245,9 @@ const SummaryItem = new Lang.Class({
vertical: true });
this.notificationStackView.add_actor(this.notificationStack);
this.notificationStackWidget.add_actor(this.notificationStackView);
this.closeButton = makeCloseButton();
this.notificationStackWidget.add_actor(this.closeButton);
this._stackedNotifications = [];
this._oldMaxScrollAdjustment = 0;
@ -1224,46 +1259,17 @@ const SummaryItem = new Lang.Class({
this._oldMaxScrollAdjustment = adjustment.upper;
}));
this.rightClickMenu = new St.BoxLayout({ name: 'summary-right-click-menu',
vertical: true });
let item;
item = new PopupMenu.PopupMenuItem(_("Open"));
item.connect('activate', Lang.bind(this, function() {
source.open();
this.emit('done-displaying-content');
}));
this.rightClickMenu.add(item.actor);
item = new PopupMenu.PopupMenuItem(_("Remove"));
item.connect('activate', Lang.bind(this, function() {
source.destroy();
this.emit('done-displaying-content');
}));
this.rightClickMenu.add(item.actor);
if (source.isChat) {
item = new PopupMenu.PopupMenuItem('');
item.actor.connect('notify::mapped', Lang.bind(this, function() {
item.label.set_text(source.isMuted ? _("Unmute") : _("Mute"));
}));
item.connect('activate', Lang.bind(this, function() {
source.setMuted(!source.isMuted);
this.emit('done-displaying-content');
}));
this.rightClickMenu.add(item.actor);
}
global.focus_manager.add_group(this.rightClickMenu);
this.rightClickMenu = source.buildRightClickMenu();
if (this.rightClickMenu)
global.focus_manager.add_group(this.rightClickMenu);
},
get closeButtonVisible() {
return this._closeButton.visible;
},
set closeButtonVisible(v) {
this._closeButton.visible = v;
_onKeyPress: function(actor, event) {
if (event.get_key_symbol() == Clutter.KEY_Up) {
actor.emit('clicked', 1);
return true;
}
return false;
},
prepareNotificationStackForShowing: function() {
@ -1330,7 +1336,7 @@ const SummaryItem = new Lang.Class({
},
_notificationDoneDisplaying: function() {
this.emit('done-displaying-content');
this.source.emit('done-displaying-content');
},
_notificationDestroyed: function(notification) {
@ -1368,39 +1374,47 @@ const MessageTray = new Lang.Class({
}));
this.actor = new St.Widget({ name: 'message-tray',
layout_manager: new Clutter.BinLayout(),
reactive: true,
track_hover: true });
this.actor.connect('style-changed', Lang.bind(this, this._onStyleChanged));
track_hover: true,
layout_manager: new Clutter.BinLayout(),
x_expand: true,
y_expand: true,
y_align: Clutter.ActorAlign.START,
});
this.actor.connect('notify::hover', Lang.bind(this, this._onTrayHoverChanged));
this._notificationWidget = new St.Widget({ name: 'notification-container',
y_align: Clutter.ActorAlign.START,
x_align: Clutter.ActorAlign.CENTER,
y_expand: true,
x_expand: true,
layout_manager: new Clutter.BinLayout() });
this.actor.add_actor(this._notificationWidget);
this._notificationBin = new St.Bin();
this._notificationBin = new St.Bin({ y_expand: true });
this._notificationBin.set_y_align(Clutter.ActorAlign.START);
this._notificationWidget.add_actor(this._notificationBin);
this._notificationWidget.hide();
this._notificationQueue = [];
this._notification = null;
this._notificationClickedId = 0;
this._summaryBin = new St.Bin({ x_align: St.Align.END,
y_expand: true, // this is the Clutter property
reactive: true });
this._summaryBin.connect('button-release-event', Lang.bind(this, function(actor, event) {
this.actor.connect('button-release-event', Lang.bind(this, function(actor, event) {
this._setClickedSummaryItem(null);
this._updateState();
actor.grab_key_focus();
}));
this.actor.add_actor(this._summaryBin);
global.focus_manager.add_group(this.actor);
this._summary = new St.BoxLayout({ name: 'summary-mode',
reactive: true,
track_hover: true });
global.focus_manager.add_group(this._summary);
track_hover: true,
x_align: Clutter.ActorAlign.END,
x_expand: true,
y_align: Clutter.ActorAlign.CENTER,
y_expand: true });
this._summary.connect('notify::hover', Lang.bind(this, this._onSummaryHoverChanged));
this._summaryBin.child = this._summary;
this._summaryBin.opacity = 0;
this.actor.add_actor(this._summary);
this._summary.opacity = 0;
this._summaryMotionId = 0;
this._trayMotionId = 0;
@ -1408,6 +1422,8 @@ const MessageTray = new Lang.Class({
this._summaryBoxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
{ reactive: true,
track_hover: true });
this._summaryBoxPointer.actor.connect('key-press-event',
Lang.bind(this, this._onSummaryBoxPointerKeyPress));
this._summaryBoxPointer.actor.style_class = 'summary-boxpointer';
this._summaryBoxPointer.actor.hide();
Main.layoutManager.addChrome(this._summaryBoxPointer.actor);
@ -1425,16 +1441,19 @@ const MessageTray = new Lang.Class({
this._closeButton.connect('clicked', Lang.bind(this, this._onCloseClicked));
this._notificationWidget.add_actor(this._closeButton);
this._idleMonitorWatchId = 0;
this._idleMonitorBecameActiveId = 0;
this._userActiveWhileNotificationShown = false;
this.idleMonitor = Shell.IdleMonitor.get();
this.idleMonitor = new GnomeDesktop.IdleMonitor();
this._grabHelper = new GrabHelper.GrabHelper(this.actor);
this._grabHelper.addActor(this._summaryBoxPointer.actor);
this._grabHelper.addActor(this.actor);
if (Main.panel.statusArea.activities)
this._grabHelper.addActor(Main.panel.statusArea.activities.hotCorner.actor);
Main.layoutManager.keyboardBox.connect('notify::hover', Lang.bind(this, this._onKeyboardHoverChanged));
Main.layoutManager.connect('keyboard-visible-changed', Lang.bind(this, this._onKeyboardVisibleChanged));
this._trayState = State.HIDDEN;
this._locked = false;
@ -1443,13 +1462,16 @@ const MessageTray = new Lang.Class({
this._trayLeftTimeoutId = 0;
this._pointerInTray = false;
this._pointerInKeyboard = false;
this._keyboardVisible = false;
this._summaryState = State.HIDDEN;
this._pointerInSummary = false;
this._notificationClosed = false;
this._notificationState = State.HIDDEN;
this._notificationTimeoutId = 0;
this._notificationExpandedId = 0;
this._summaryBoxPointerState = State.HIDDEN;
this._summaryBoxPointerTimeoutId = 0;
this._desktopCloneState = State.HIDDEN;
this._overviewVisible = Main.overview.visible;
this._notificationRemoved = false;
this._reNotifyAfterHideNotification = null;
@ -1469,16 +1491,14 @@ const MessageTray = new Lang.Class({
Main.layoutManager.trackChrome(this._notificationWidget);
Main.layoutManager.trackChrome(this._closeButton);
Main.layoutManager.connect('monitors-changed', Lang.bind(this, function() {
this.actor.style_changed();
}));
Main.layoutManager.connect('primary-fullscreen-changed', Lang.bind(this, this._onFullscreenChanged));
Main.overview.connect('showing', Lang.bind(this,
function() {
this._overviewVisible = true;
this._grabHelper.ungrab(); // drop modal grab if necessary
this.actor.add_style_pseudo_class('overview');
this._updateState();
this.show();
}));
Main.overview.connect('hiding', Lang.bind(this,
function() {
@ -1488,8 +1508,7 @@ const MessageTray = new Lang.Class({
this._updateState();
}));
this._isScreenLocked = false;
Main.screenShield.connect('lock-status-changed', Lang.bind(this, this._onScreenLockStatusChanged));
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
global.display.add_keybinding('toggle-message-tray',
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
@ -1503,6 +1522,19 @@ const MessageTray = new Lang.Class({
pointerWatcher.addWatch(TRAY_DWELL_CHECK_INTERVAL, Lang.bind(this, this._checkTrayDwell));
this._trayDwellTimeoutId = 0;
this._trayDwelling = false;
this._trayDwellUserTime = 0;
this._sessionUpdated();
},
_sessionUpdated: function() {
if (Main.sessionMode.isLocked || Main.sessionMode.isGreeter)
Main.ctrlAltTabManager.removeGroup(this._summary);
else
Main.ctrlAltTabManager.addGroup(this._summary, _("Message Tray"), 'start-here-symbolic',
{ focusCallback: Lang.bind(this, this.toggleAndNavigate),
sortGroup: CtrlAltTab.SortGroup.BOTTOM });
this._updateState();
},
_checkTrayDwell: function(x, y) {
@ -1516,9 +1548,14 @@ const MessageTray = new Lang.Class({
// of the monitor. The _trayDwelling variable is used so that we only try to
// fire off one tray dwell - if it fails (because, say, the user has the mouse down),
// we don't try again until the user moves the mouse up and down again.
if (!this._trayDwelling && !this.actor.hover && this._trayDwellTimeoutId == 0)
if (!this._trayDwelling && !this.actor.hover && this._trayDwellTimeoutId == 0) {
// Save the interaction timestamp so we can detect user input
let focusWindow = global.display.focus_window;
this._trayDwellUserTime = focusWindow ? focusWindow.user_time : 0;
this._trayDwellTimeoutId = Mainloop.timeout_add(TRAY_DWELL_TIME,
Lang.bind(this, this._trayDwellTimeout));
}
this._trayDwelling = true;
} else {
this._cancelTrayDwell();
@ -1534,6 +1571,16 @@ const MessageTray = new Lang.Class({
},
_trayDwellTimeout: function() {
if (Main.modalCount > 0)
return false;
// If the user interacted with the focus window since we started the tray
// dwell (by clicking or typing), don't activate the message tray
let focusWindow = global.display.focus_window;
let currentUserTime = focusWindow ? focusWindow.user_time : 0;
if (currentUserTime != this._trayDwellUserTime)
return false;
this._trayDwellTimeoutId = 0;
this._traySummoned = true;
@ -1544,21 +1591,13 @@ const MessageTray = new Lang.Class({
_onCloseClicked: function() {
if (this._notificationState == State.SHOWN) {
this._notification.emit('done-displaying');
if (!this._notification.resident)
this._notification.destroy();
this._closeButton.hide();
this._notificationClosed = true;
this._updateState();
this._notificationClosed = false;
}
},
_onStyleChanged: function() {
let monitor = Main.layoutManager.bottomMonitor;
let width = this._notificationWidget.get_width();
this._notificationWidget.x = Math.floor((monitor.width - width) / 2);
this._summaryBin.x = 0;
this._summaryBin.width = monitor.width;
},
contains: function(source) {
return this._getIndexOfSummaryItemForSource(source) >= 0;
},
@ -1668,11 +1707,6 @@ const MessageTray = new Lang.Class({
this._notificationQueue.splice(index, 1);
},
_onScreenLockStatusChanged: function(screenShield, locked) {
this._isScreenLocked = locked;
this._updateState();
},
_lock: function() {
this._locked = true;
},
@ -1691,10 +1725,8 @@ const MessageTray = new Lang.Class({
},
toggleAndNavigate: function() {
// Grab the key focus so that GrabHelper knows
// that we have the key grab.
this._summary.grab_key_focus();
this.toggle();
this._summary.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
},
hide: function() {
@ -1704,6 +1736,11 @@ 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) {
@ -1747,7 +1784,10 @@ const MessageTray = new Lang.Class({
_onSummaryItemClicked: function(summaryItem, button) {
if (summaryItem.source.handleSummaryClick()) {
this._escapeTray();
if (summaryItem.source.keepTrayOnSummaryClick)
this._setClickedSummaryItem(null);
else
this._escapeTray();
} else {
if (!this._setClickedSummaryItem(summaryItem, button))
this._setClickedSummaryItem(null);
@ -1838,6 +1878,20 @@ const MessageTray = new Lang.Class({
this._updateState();
},
_onKeyboardVisibleChanged: function(layoutManager, keyboardVisible) {
if (this._keyboardVisible == keyboardVisible)
return;
this._keyboardVisible = keyboardVisible;
if (keyboardVisible)
this.actor.add_style_pseudo_class('keyboard');
else
this.actor.remove_style_pseudo_class('keyboard');
this._updateState();
},
_onFullscreenChanged: function(obj, state) {
this._inFullscreen = state;
this._updateState();
@ -1898,34 +1952,26 @@ const MessageTray = new Lang.Class({
// at the present time.
_updateState: function() {
// Notifications
let notificationQueue = this._notificationQueue.filter(Lang.bind(this, function(notification) {
if (this._isScreenLocked)
return notification.showWhenLocked;
else
return true;
}));
let notificationQueue = this._notificationQueue;
let notificationUrgent = notificationQueue.length > 0 && notificationQueue[0].urgency == Urgency.CRITICAL;
// notificationsLimited is false when the screen is locked, because they go through
// different filtering, and we want to show non urgent messages at times
let notificationsLimited = (this._busy || this._inFullscreen) && !this._isScreenLocked;
let notificationsPending = notificationQueue.length > 0 && (!notificationsLimited || notificationUrgent);
let notificationsLimited = this._busy || this._inFullscreen;
let notificationsPending = notificationQueue.length > 0 && (!notificationsLimited || notificationUrgent) && Main.sessionMode.hasNotifications;
let nextNotification = notificationQueue.length > 0 ? notificationQueue[0] : null;
let notificationPinned = this._pointerInTray && !this._pointerInSummary && !this._notificationRemoved;
let notificationExpanded = this._notification && this._notification.expanded;
let notificationExpired = this._notificationTimeoutId == 0 &&
!(this._notification && this._notification.urgency == Urgency.CRITICAL) &&
!(this._notification && this._notification.focused) &&
!this._pointerInTray &&
!this._locked &&
!(this._pointerInKeyboard && notificationExpanded);
let notificationLockedOut = this._isScreenLocked && (this._notification && !this._notification.showWhenLocked);
// TODO: how to deal with locked out notiifcations if want to keep showing notifications?!
let notificationMustClose = this._notificationRemoved || notificationLockedOut || (notificationExpired && this._userActiveWhileNotificationShown);
let notificationLockedOut = !Main.sessionMode.hasNotifications && this._notification;
let notificationMustClose = this._notificationRemoved || notificationLockedOut || (notificationExpired && this._userActiveWhileNotificationShown) || this._notificationClosed;
let canShowNotification = notificationsPending && this._summaryState == State.HIDDEN;
if (this._notificationState == State.HIDDEN) {
if (canShowNotification) {
this._showNotification(nextNotification);
this._notificationQueue.splice(this._notificationQueue.indexOf(nextNotification), 1);
this._showNotification();
}
} else if (this._notificationState == State.SHOWN) {
if (notificationMustClose)
@ -1937,7 +1983,7 @@ const MessageTray = new Lang.Class({
}
// Summary
let summarySummoned = this._pointerInSummary || this._overviewVisible || this._traySummoned;
let summarySummoned = this._pointerInSummary || this._traySummoned;
let summaryPinned = this._pointerInTray || summarySummoned || this._locked;
let summaryHovered = this._pointerInTray || this._pointerInSummary;
@ -1945,9 +1991,9 @@ const MessageTray = new Lang.Class({
this._notificationState == State.SHOWN);
let notificationsDone = !notificationsVisible && !notificationsPending;
let summaryOptionalInOverview = this._overviewVisible && !this._locked && !summaryHovered;
let summaryOptionalInOverview = !this._locked && !summaryHovered;
let mustHideSummary = (notificationsPending && (notificationUrgent || summaryOptionalInOverview))
|| notificationsVisible || this._isScreenLocked;
|| notificationsVisible || !Main.sessionMode.hasNotifications;
if (this._summaryState == State.HIDDEN && !mustHideSummary && summarySummoned)
this._showSummary();
@ -1986,9 +2032,22 @@ const MessageTray = new Lang.Class({
let trayShouldBeVisible = (this._summaryState == State.SHOWING ||
this._summaryState == State.SHOWN);
if (!trayIsVisible && trayShouldBeVisible)
this._showTray();
trayShouldBeVisible = this._showTray();
else if (trayIsVisible && !trayShouldBeVisible)
this._hideTray();
// Desktop clone
let desktopCloneIsVisible = (this._desktopCloneState == State.SHOWING ||
this._desktopCloneState == State.SHOWN);
let desktopCloneShouldBeVisible = (trayShouldBeVisible &&
!this._overviewVisible &&
!this._keyboardVisible);
if (!desktopCloneIsVisible && desktopCloneShouldBeVisible) {
this._showDesktopClone();
} else if (desktopCloneIsVisible && !desktopCloneShouldBeVisible) {
this._hideDesktopClone (this._keyboardVisible);
}
},
_tween: function(actor, statevar, value, params) {
@ -2017,11 +2076,13 @@ const MessageTray = new Lang.Class({
// Don't actually take a modal grab in the overview.
// Just add something to the grab stack that we can
// pop later.
let modal = !this._overviewVisible;
if (!this._grabHelper.grab({ actor: this.actor,
modal: !this._overviewVisible,
modal: modal,
onUngrab: Lang.bind(this, this._escapeTray) })) {
this._traySummoned = false;
return;
return false;
}
this._tween(this.actor, '_trayState', State.SHOWN,
@ -2030,49 +2091,48 @@ const MessageTray = new Lang.Class({
transition: 'easeOutQuad'
});
// Don't move the windows up if we are in the overview,
// but show the tray in the ctrl+alt+tab list.
if (this._overviewVisible) {
Main.ctrlAltTabManager.addGroup(this._summary, _("Message Tray"), 'start-here-symbolic',
{ sortGroup: CtrlAltTab.SortGroup.BOTTOM });
return;
}
if (!this._overviewVisible)
this._lightbox.show();
return true;
},
_updateDesktopCloneClip: function() {
let geometry = this._bottomMonitorGeometry;
let progress = -Math.round(this._desktopClone.y);
this._desktopClone.set_clip(geometry.x,
geometry.y + progress,
geometry.width,
geometry.height - progress);
},
_showDesktopClone: function() {
let bottomMonitor = Main.layoutManager.bottomMonitor;
let geometry = new Clutter.Geometry({ x: bottomMonitor.x,
y: bottomMonitor.y,
width: bottomMonitor.width,
height: bottomMonitor.height
});
this._desktopClone = new Clutter.Clone({ source: global.window_group, clip: geometry });
this._bottomMonitorGeometry = { x: bottomMonitor.x,
y: bottomMonitor.y,
width: bottomMonitor.width,
height: bottomMonitor.height };
if (this._desktopClone)
this._desktopClone.destroy();
this._desktopClone = new Clutter.Clone({ source: global.window_group, clip: new Clutter.Geometry(this._bottomMonitorGeometry) });
Main.uiGroup.insert_child_above(this._desktopClone, global.window_group);
this._desktopClone.x = 0;
this._desktopClone.y = 0;
this._desktopClone.show();
this._lightbox.show();
this._desktopClone._progress = 0;
Tweener.addTween(this._desktopClone,
{ _progress: this.actor.height,
time: ANIMATION_TIME,
transition: 'easeOutQuad',
onUpdate: Lang.bind(this, function() {
let progress = Math.round(this._desktopClone._progress);
this._desktopClone.y = - progress;
this._desktopClone.set_clip(geometry.x,
geometry.y + progress,
geometry.width,
geometry.height - progress);
})
});
this._tween(this._desktopClone, '_desktopCloneState', State.SHOWN,
{ y: -this.actor.height,
time: ANIMATION_TIME,
transition: 'easeOutQuad',
onUpdate: Lang.bind(this, this._updateDesktopCloneClip)
});
},
_hideTray: function() {
// Note that we might have entered here without a grab,
// which would happen if GrabHelper ungrabbed for us.
// This is a no-op in that case.
this._grabHelper.ungrab({ actor: this.actor });
// Having the summary item animate out while sliding down the tray
// is distracting, so hide it immediately in case it was visible.
this._summaryBoxPointer.actor.hide();
this._tween(this.actor, '_trayState', State.HIDDEN,
{ y: 0,
@ -2080,52 +2140,52 @@ const MessageTray = new Lang.Class({
transition: 'easeOutQuad'
});
// If we are coming back from the overview, there are no windows
// to be moved. Just remove the tray from the ctrl+alt+tab list.
if (!this._desktopClone) {
Main.ctrlAltTabManager.removeGroup(this._summary);
return;
}
let geometry = this._desktopClone.clip;
this._desktopClone._progress = 0;
Tweener.addTween(this._desktopClone,
{ _progress: this.actor.height,
time: ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this._desktopClone.destroy();
this._desktopClone = null;
}),
onUpdate: Lang.bind(this, function() {
let progress = Math.round(this._desktopClone._progress);
this._desktopClone.y = progress - this.actor.height;
this._desktopClone.set_clip(geometry.x,
geometry.y - progress,
geometry.width,
geometry.height + progress);
})
});
// Note that we might have entered here without a grab,
// which would happen if GrabHelper ungrabbed for us.
// This is a no-op in that case.
this._grabHelper.ungrab({ actor: this.actor });
this._lightbox.hide();
},
_onIdleMonitorWatch: function(monitor, id, userBecameIdle) {
this.idleMonitor.remove_watch(this._idleMonitorWatchId);
this._idleMonitorWatchId = 0;
if (!userBecameIdle)
this._updateNotificationTimeout(2000);
_hideDesktopClone: function(now) {
this._tween(this._desktopClone, '_desktopCloneState', State.HIDDEN,
{ y: 0,
time: now ? 0 : ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this._desktopClone.destroy();
this._desktopClone = null;
this._bottomMonitorGeometry = null;
}),
onUpdate: Lang.bind(this, this._updateDesktopCloneClip)
});
},
_onIdleMonitorBecameActive: function() {
this.idleMonitor.disconnect(this._idleMonitorBecameActiveId);
this._idleMonitorBecameActiveId = 0;
this._userActiveWhileNotificationShown = true;
this._updateNotificationTimeout(2000);
this._updateState();
},
_showNotification: function(notification) {
this._notification = notification;
this._userActiveWhileNotificationShown = this.idleMonitor.get_idletime() <= IDLE_TIME;
this._idleMonitorWatchId = this.idleMonitor.add_watch(IDLE_TIME,
Lang.bind(this, this._onIdleMonitorWatch));
_showNotification: function() {
this._notification = this._notificationQueue.shift();
let userIdle = this.idleMonitor.get_idletime() > IDLE_TIME;
if (userIdle) {
this._userActiveWhileNotificationShown = false;
this._idleMonitorBecameActiveId = this.idleMonitor.connect('became-active', Lang.bind(this, this._onIdleMonitorBecameActive));
} else {
this._userActiveWhileNotificationShown = true;
}
this._notificationClickedId = this._notification.connect('done-displaying',
Lang.bind(this, this._escapeTray));
this._notification.connect('unfocused', Lang.bind(this, function() {
this._updateState();
}));
this._notificationBin.child = this._notification.actor;
this._notificationWidget.opacity = 0;
@ -2233,6 +2293,11 @@ const MessageTray = new Lang.Class({
_hideNotification: function() {
this._grabHelper.ungrab({ actor: this._notification.actor });
if (this._idleMonitorBecameActiveId) {
this.idleMonitor.disconnect(this._idleMonitorBecameActiveId);
this._idleMonitorBecameActiveId = 0;
}
if (this._notificationExpandedId) {
this._notification.disconnect(this._notificationExpandedId);
this._notificationExpandedId = 0;
@ -2311,18 +2376,16 @@ const MessageTray = new Lang.Class({
},
_showSummary: function() {
this._summaryBin.opacity = 0;
this._summaryBin.y = this.actor.height;
this._tween(this._summaryBin, '_summaryState', State.SHOWN,
{ y: 0,
opacity: 255,
this._summary.opacity = 0;
this._tween(this._summary, '_summaryState', State.SHOWN,
{ opacity: 255,
time: ANIMATION_TIME,
transition: 'easeOutQuad',
});
},
_hideSummary: function() {
this._tween(this._summaryBin, '_summaryState', State.HIDDEN,
this._tween(this._summary, '_summaryState', State.HIDDEN,
{ opacity: 0,
time: ANIMATION_TIME,
transition: 'easeOutQuad',
@ -2333,9 +2396,11 @@ const MessageTray = new Lang.Class({
this._summaryBoxPointerItem = this._clickedSummaryItem;
this._summaryBoxPointerContentUpdatedId = this._summaryBoxPointerItem.connect('content-updated',
Lang.bind(this, this._onSummaryBoxPointerContentUpdated));
this._summaryBoxPointerDoneDisplayingId = this._summaryBoxPointerItem.connect('done-displaying-content',
Lang.bind(this, this._escapeTray));
if (this._clickedSummaryItemMouseButton == 1) {
this._sourceDoneDisplayingId = this._summaryBoxPointerItem.source.connect('done-displaying-content',
Lang.bind(this, this._escapeTray));
let hasRightClickMenu = this._summaryBoxPointerItem.rightClickMenu != null;
if (this._clickedSummaryItemMouseButton == 1 || !hasRightClickMenu) {
let newQueue = [];
for (let i = 0; i < this._notificationQueue.length; i++) {
let notification = this._notificationQueue[i];
@ -2348,14 +2413,16 @@ const MessageTray = new Lang.Class({
this._notificationQueue = newQueue;
this._summaryBoxPointer.bin.child = this._summaryBoxPointerItem.notificationStackWidget;
this._summaryBoxPointerItem.closeButtonVisible = true;
let closeButton = this._summaryBoxPointerItem.closeButton;
closeButton.show();
this._summaryBoxPointerCloseClickedId = closeButton.connect('clicked', Lang.bind(this, this._hideSummaryBoxPointer));
this._summaryBoxPointerItem.prepareNotificationStackForShowing();
} else if (this._clickedSummaryItemMouseButton == 3) {
this._summaryBoxPointer.bin.child = this._clickedSummaryItem.rightClickMenu;
}
this._grabHelper.grab({ actor: this._summaryBoxPointer.bin.child,
untracked: true,
grabFocus: true,
onUngrab: Lang.bind(this, this._onSummaryBoxPointerUngrabbed) });
this._lock();
@ -2418,6 +2485,21 @@ const MessageTray = new Lang.Class({
return true;
},
_onSummaryBoxPointerKeyPress: function(actor, event) {
switch (event.get_key_symbol()) {
case Clutter.KEY_Down:
case Clutter.KEY_Escape:
this._setClickedSummaryItem(null);
this._updateState();
return true;
case Clutter.KEY_Delete:
this._clickedSummaryItem.source.destroy();
this._escapeTray();
return true;
}
return false;
},
_onSummaryBoxPointerUngrabbed: function() {
this._summaryBoxPointerState = State.HIDING;
this._unlock();
@ -2426,19 +2508,14 @@ const MessageTray = new Lang.Class({
this._summaryBoxPointer.actor.hide();
this._hideSummaryBoxPointerCompleted();
} else {
if (global.stage.key_focus &&
!this.actor.contains(global.stage.key_focus))
this._setClickedSummaryItem(null);
this._summaryBoxPointer.hide(BoxPointer.PopupAnimation.FULL, Lang.bind(this, this._hideSummaryBoxPointerCompleted));
}
},
_hideSummaryBoxPointer: function() {
// We should be sure to hide the box pointer if all notifications in it are destroyed while
// it is hiding, so that we don't show an animation of an empty blob being hidden.
if (this._summaryBoxPointerState == State.HIDING &&
this._summaryBoxPointerItem.notificationStack.get_n_children() == 0) {
this._summaryBoxPointer.actor.hide();
return;
}
this._grabHelper.ungrab({ actor: this._summaryBoxPointer.bin.child });
},
@ -2449,7 +2526,9 @@ const MessageTray = new Lang.Class({
this._summaryBoxPointer.bin.child = null;
this._summaryBoxPointerItem.disconnect(this._summaryBoxPointerContentUpdatedId);
this._summaryBoxPointerContentUpdatedId = 0;
this._summaryBoxPointerItem.disconnect(this._summaryBoxPointerDoneDisplayingId);
this._summaryBoxPointerItem.closeButton.disconnect(this._summaryBoxPointerCloseClickedId);
this._summaryBoxPointerCloseClickedId = 0;
this._summaryBoxPointerItem.source.disconnect(this._sourceDoneDisplayingId);
this._summaryBoxPointerDoneDisplayingId = 0;
let sourceNotificationStackDoneShowing = null;

View File

@ -20,7 +20,6 @@ const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const OPEN_AND_CLOSE_TIME = 0.1;
const FADE_IN_BUTTONS_TIME = 0.33;
const FADE_OUT_DIALOG_TIME = 1.0;
const State = {
@ -37,12 +36,13 @@ const ModalDialog = new Lang.Class({
_init: function(params) {
params = Params.parse(params, { shellReactive: false,
styleClass: null,
parentActor: Main.uiGroup
});
parentActor: Main.uiGroup,
shouldFadeIn: true });
this.state = State.CLOSED;
this._hasModal = false;
this._shellReactive = params.shellReactive;
this._shouldFadeIn = params.shouldFadeIn;
this._group = new St.Widget({ visible: false,
x: 0,
@ -116,8 +116,6 @@ const ModalDialog = new Lang.Class({
},
setButtons: function(buttons) {
let hadChildren = this._buttonLayout.get_children() > 0;
this._buttonLayout.destroy_all_children();
this._actionKeys = {};
@ -130,8 +128,11 @@ const ModalDialog = new Lang.Class({
let key = buttonInfo['key'];
let isDefault = buttonInfo['default'];
if (isDefault && !key)
if (isDefault && !key) {
this._actionKeys[Clutter.KEY_KP_Enter] = action;
this._actionKeys[Clutter.KEY_ISO_Enter] = action;
key = Clutter.KEY_Return;
}
buttonInfo.button = new St.Button({ style_class: 'modal-dialog-button',
reactive: true,
@ -165,21 +166,7 @@ const ModalDialog = new Lang.Class({
this._actionKeys[key] = action;
}
// Fade in buttons if there weren't any before
if (!hadChildren && buttons.length > 0) {
this._buttonLayout.opacity = 0;
Tweener.addTween(this._buttonLayout,
{ opacity: 255,
time: FADE_IN_BUTTONS_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this.emit('buttons-set');
})
});
} else {
this.emit('buttons-set');
}
this.emit('buttons-set');
},
_onKeyReleaseEvent: function(object, event) {
@ -198,8 +185,11 @@ const ModalDialog = new Lang.Class({
this.emit('destroy');
},
_fadeOpen: function() {
this._monitorConstraint.index = global.screen.get_current_monitor();
_fadeOpen: function(onPrimary) {
if (onPrimary)
this._monitorConstraint.primary = true;
else
this._monitorConstraint.index = global.screen.get_current_monitor();
this.state = State.OPENING;
@ -210,7 +200,7 @@ const ModalDialog = new Lang.Class({
this._group.show();
Tweener.addTween(this._group,
{ opacity: 255,
time: OPEN_AND_CLOSE_TIME,
time: this._shouldFadeIn ? OPEN_AND_CLOSE_TIME : 0,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
@ -232,14 +222,14 @@ const ModalDialog = new Lang.Class({
}));
},
open: function(timestamp) {
open: function(timestamp, onPrimary) {
if (this.state == State.OPENED || this.state == State.OPENING)
return true;
if (!this.pushModal(timestamp))
return false;
this._fadeOpen();
this._fadeOpen(onPrimary);
return true;
},

View File

@ -116,8 +116,8 @@ const NotificationDaemon = new Lang.Class({
this._busProxy = new Bus();
this._trayManager = new Shell.TrayManager();
this._trayManager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded));
this._trayManager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
this._trayIconAddedId = this._trayManager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded));
this._trayIconRemovedId = this._trayManager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
Shell.WindowTracker.get_default().connect('notify::focus-app',
Lang.bind(this, this._onFocusAppChanged));
@ -356,12 +356,10 @@ const NotificationDaemon = new Lang.Class({
ndata.actions, ndata.hints, ndata.notification];
let gicon = this._iconForNotificationData(icon, hints);
let iconActor = new St.Icon({ gicon: gicon,
icon_size: MessageTray.NOTIFICATION_ICON_SIZE });
if (notification == null) {
notification = new MessageTray.Notification(source, summary, body,
{ icon: iconActor,
{ gicon: gicon,
bannerMarkup: true });
ndata.notification = notification;
notification.connect('destroy', Lang.bind(this,
@ -386,7 +384,7 @@ const NotificationDaemon = new Lang.Class({
this._emitActionInvoked(ndata.id, actionId);
}));
} else {
notification.update(summary, body, { icon: iconActor,
notification.update(summary, body, { gicon: gicon,
bannerMarkup: true,
clear: true });
}
@ -499,7 +497,7 @@ const NotificationDaemon = new Lang.Class({
},
_onTrayIconAdded: function(o, icon) {
let wmClass = icon.wm_class.toLowerCase();
let wmClass = icon.wm_class ? icon.wm_class.toLowerCase() : '';
if (STANDARD_TRAY_ICON_IMPLEMENTATIONS[wmClass] !== undefined)
return;

View File

@ -10,6 +10,7 @@ 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;
@ -23,9 +24,6 @@ const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
// Time for initial animation going into Overview mode
const ANIMATION_TIME = 0.25;
// We split the screen vertically between the dash and the view selector.
const DASH_SPLIT_FRACTION = 0.1;
const DND_WINDOW_SWITCH_TIMEOUT = 1250;
const SwipeScrollDirection = {
@ -71,7 +69,6 @@ const ShellInfo = new Lang.Class({
if (this._source.notifications.length == 0) {
notification = new MessageTray.Notification(this._source, text, null);
notification.setTransient(true);
notification.setShowWhenLocked(true);
} else {
notification = this._source.notifications[0];
notification.update(text, null, { clear: true });
@ -92,16 +89,21 @@ const ShellInfo = new Lang.Class({
const Overview = new Lang.Class({
Name: 'Overview',
_init : function() {
this.isDummy = !Main.sessionMode.hasOverview;
_init: function() {
this._overviewCreated = false;
// We only have an overview in user sessions, so
// create a dummy overview in other cases
if (this.isDummy) {
this.animationInProgress = false;
this.visible = false;
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._sessionUpdated();
},
_createOverview: function() {
if (this._overviewCreated)
return;
}
if (this.isDummy)
return;
this._overviewCreated = true;
// The main BackgroundActor is inside global.window_group which is
// hidden when displaying the overview, so we create a new
@ -115,23 +117,18 @@ const Overview = new Lang.Class({
this._desktopFade = new St.Bin();
global.overlay_group.add_actor(this._desktopFade);
this._spacing = 0;
/* Translators: This is the main view to select
activities. See also note for "Activities" string. */
this._group = new St.Widget({ name: 'overview',
accessible_name: _("Overview"),
reactive: true });
this._group._delegate = this;
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._relayout();
}
}));
this._overview = new St.BoxLayout({ name: 'overview',
accessible_name: _("Overview"),
reactive: true,
vertical: true });
this._overview._delegate = this;
let layout = new CenterLayout.CenterLayout();
CenterLayout.connectSpacing(layout);
this._group = new St.Widget({ name: 'overview-group',
layout_manager: layout });
this._scrollDirection = SwipeScrollDirection.NONE;
this._scrollAdjustment = null;
@ -144,18 +141,19 @@ const Overview = new Lang.Class({
this._modal = false; // have a modal grab
this.animationInProgress = false;
this._hideInProgress = false;
this.searchActive = false;
this.appsActive = false;
// During transitions, we raise this to the top to avoid having the overview
// area be reactive; it causes too many issues such as double clicks on
// Dash elements, or mouseover handlers in the workspaces.
this._coverPane = new Clutter.Rectangle({ opacity: 0,
reactive: true });
this._group.add_actor(this._coverPane);
this._overview.add_actor(this._coverPane);
this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; }));
this._group.hide();
global.overlay_group.add_actor(this._group);
this._overview.hide();
global.overlay_group.add_actor(this._overview);
this._coverPane.hide();
@ -167,6 +165,8 @@ const Overview = new Lang.Class({
Main.xdndHandler.connect('drag-begin', Lang.bind(this, this._onDragBegin));
Main.xdndHandler.connect('drag-end', Lang.bind(this, this._onDragEnd));
global.screen.connect('restacked', Lang.bind(this, this._onRestacked));
this._windowSwitchTimeoutId = 0;
this._windowSwitchTimestamp = 0;
this._lastActiveWorkspaceIndex = -1;
@ -174,6 +174,11 @@ const Overview = new Lang.Class({
this._needsFakePointerEvent = false;
},
_sessionUpdated: function() {
this.isDummy = !Main.sessionMode.hasOverview;
this._createOverview();
},
// The members we construct that are implemented in JS might
// want to access the overview as Main.overview to connect
// signal handlers and so forth. So we create them after
@ -184,6 +189,13 @@ const Overview = new Lang.Class({
this._shellInfo = new ShellInfo();
// Add a clone of the panel to the overview so spacing and such is
// automatic
this._panelGhost = new St.Bin({ child: new Clutter.Clone({ source: Main.panel.actor }),
reactive: false,
opacity: 0 });
this._overview.add_actor(this._panelGhost);
this._searchEntry = new St.Entry({ name: 'searchEntry',
/* Translators: this is the text displayed
in the search entry when no search is
@ -192,16 +204,13 @@ const Overview = new Lang.Class({
hint_text: _("Type to search..."),
track_hover: true,
can_focus: true });
this._group.add_actor(this._searchEntry);
this._dash = new Dash.Dash();
this._viewSelector = new ViewSelector.ViewSelector(this._searchEntry,
this._dash.showAppsButton);
this._group.add_actor(this._viewSelector.actor);
this._group.add_actor(this._dash.actor);
this._searchEntryBin = new St.Bin({ child: this._searchEntry,
x_align: St.Align.MIDDLE });
this._overview.add_actor(this._searchEntryBin);
// TODO - recalculate everything when desktop size changes
this._dash.actor.add_constraint(this._viewSelector.constrainHeight);
this._dash = new Dash.Dash();
this._group.add_actor(this._dash.actor);
this.dashIconSize = this._dash.iconSize;
this._dash.connect('icon-size-changed',
Lang.bind(this, function() {
@ -212,6 +221,82 @@ const Overview = new Lang.Class({
// the left of the overview
Main.ctrlAltTabManager.addGroup(this._dash.actor, _("Dash"), 'user-bookmarks-symbolic');
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);
// TODO: unique icon
Main.ctrlAltTabManager.addGroup(this._thumbnailsBox.actor, _("Workspaces"), 'user-bookmarks-symbolic');
// Add our same-line elements after the search entry
this._overview.add_actor(this._group);
// Then account for message tray
this._messageTrayGhost = new St.Bin({ child: new Clutter.Clone({ source: Main.messageTray.actor }),
reactive: false,
opacity: 0 });
this._overview.add_actor(this._messageTrayGhost);
this._viewSelector.connect('search-begin', Lang.bind(this,
function() {
this.searchActive = true;
this._dash.hide();
this._thumbnailsBox.hide();
Main.messageTray.hide();
}));
this._viewSelector.connect('search-cancelled', Lang.bind(this,
function() {
this.searchActive = false;
this._dash.show();
this._thumbnailsBox.show();
// search-cancelled is emitted if we leave the overview
// directly from searching. That means the message tray will
// be shown when we reach the workspace. Don't do this, only
// show the message tray on search-cancelled if we are still
// in the overview/not transitioning out of it.
if (this.visible && !this.animationInProgress)
Main.messageTray.show();
}));
this._viewSelector.connect('apps-button-checked', Lang.bind(this,
function(vs, checked) {
this.appsActive = checked;
if (checked) {
this._thumbnailsBox.hide();
Main.messageTray.hide();
} else {
this._thumbnailsBox.show();
if (this.visible && !this.animationInProgress)
Main.messageTray.show();
}
}));
this.connect('app-drag-begin',
Lang.bind(this, function () {
this._dash.show();
this._thumbnailsBox.show();
}));
this.connect('app-drag-cancelled',
Lang.bind(this, function () {
if (this.searchActive) {
this._dash.hide();
this._thumbnailsBox.hide();
} else if (this.appsActive) {
this._thumbnailsBox.hide();
}
}));
this.connect('app-drag-end',
Lang.bind(this, function () {
if (this.searchActive) {
this._dash.hide();
this._thumbnailsBox.hide();
} else if (this.appsActive) {
this._thumbnailsBox.hide();
}
}));
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
this._relayout();
},
@ -461,7 +546,9 @@ const Overview = new Lang.Class({
if (windows.length == 0)
return null;
let clone = new Clutter.Clone({ source: windows[0].get_texture() });
let window = windows[0];
let clone = new Clutter.Clone({ source: window.get_texture(),
x: window.x, y: window.y });
clone.source.connect('destroy', Lang.bind(this, function() {
clone.destroy();
}));
@ -475,55 +562,41 @@ const Overview = new Lang.Class({
this.hide();
let primary = Main.layoutManager.primaryMonitor;
let rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL);
let contentY = Main.panel.actor.height;
let contentHeight = primary.height - contentY - Main.messageTray.actor.height;
this._group.set_position(primary.x, primary.y);
this._group.set_size(primary.width, primary.height);
this._overview.set_position(primary.x, primary.y);
this._overview.set_size(primary.width, primary.height);
this._coverPane.set_position(0, contentY);
this._coverPane.set_size(primary.width, contentHeight);
},
let searchWidth = this._searchEntry.get_width();
let searchHeight = this._searchEntry.get_height();
let searchX = (primary.width - searchWidth) / 2;
let searchY = contentY + this._spacing;
_onRestacked: function() {
let stack = global.get_window_actors();
let stackIndices = {};
let dashWidth = Math.round(DASH_SPLIT_FRACTION * primary.width);
let dashY = searchY + searchHeight + this._spacing;
let dashX;
if (rtl) {
this._dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
dashX = primary.width;
} else {
dashX = 0;
for (let i = 0; i < stack.length; i++) {
// Use the stable sequence for an integer to use as a hash key
stackIndices[stack[i].get_meta_window().get_stable_sequence()] = i;
}
let viewX = rtl ? 0 : dashWidth + this._spacing;
let viewY = searchY + searchHeight + this._spacing;
let viewWidth = primary.width - dashWidth - this._spacing;
let viewHeight = contentHeight - this._spacing - viewY;
this._searchEntry.set_position(searchX, searchY);
this._dash.actor.set_position(dashX, dashY);
this._viewSelector.actor.set_position(viewX, viewY);
this._viewSelector.actor.set_size(viewWidth, viewHeight);
this.emit('sync-window-stacking', stackIndices);
},
//// Public methods ////
beginItemDrag: function(source) {
this.emit('item-drag-begin');
beginAppDrag: function(source) {
this.emit('app-drag-begin');
},
cancelledItemDrag: function(source) {
this.emit('item-drag-cancelled');
cancelledAppDrag: function(source) {
this.emit('app-drag-cancelled');
},
endItemDrag: function(source) {
this.emit('item-drag-end');
endAppDrag: function(source) {
this.emit('app-drag-end');
},
beginWindowDrag: function(source) {
@ -547,13 +620,13 @@ const Overview = new Lang.Class({
if (this._shown)
return;
// Do this manually instead of using _syncInputMode, to handle failure
if (!Main.pushModal(this._group))
if (!Main.pushModal(this._overview))
return;
this._modal = true;
this._animateVisible();
this._shown = true;
this._buttonPressId = this._group.connect('button-press-event',
this._buttonPressId = this._overview.connect('button-press-event',
Lang.bind(this, this._onButtonPress));
},
@ -597,12 +670,12 @@ const Overview = new Lang.Class({
// Disable unredirection while in the overview
Meta.disable_unredirect_for_screen(global.screen);
global.window_group.hide();
this._group.show();
this._overview.show();
this._background.show();
this._viewSelector.show();
this._group.opacity = 0;
Tweener.addTween(this._group,
this._overview.opacity = 0;
Tweener.addTween(this._overview,
{ opacity: 255,
transition: 'easeOutQuad',
time: ANIMATION_TIME,
@ -656,7 +729,7 @@ const Overview = new Lang.Class({
this._syncInputMode();
if (this._buttonPressId > 0)
this._group.disconnect(this._buttonPressId);
this._overview.disconnect(this._buttonPressId);
this._buttonPressId = 0;
},
@ -698,20 +771,20 @@ const Overview = new Lang.Class({
if (this._shown) {
if (!this._modal) {
if (Main.pushModal(this._group))
if (Main.pushModal(this._overview))
this._modal = true;
else
this.hide();
}
} else if (this._shownTemporarily) {
if (this._modal) {
Main.popModal(this._group);
Main.popModal(this._overview);
this._modal = false;
}
global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
} else {
if (this._modal) {
Main.popModal(this._group);
Main.popModal(this._overview);
this._modal = false;
}
else if (global.stage_input_mode == Shell.StageInputMode.FULLSCREEN)
@ -729,7 +802,7 @@ const Overview = new Lang.Class({
this._viewSelector.zoomFromOverview();
// Make other elements fade out.
Tweener.addTween(this._group,
Tweener.addTween(this._overview,
{ opacity: 0,
transition: 'easeOutQuad',
time: ANIMATION_TIME,
@ -771,7 +844,7 @@ const Overview = new Lang.Class({
this._viewSelector.hide();
this._desktopFade.hide();
this._background.hide();
this._group.hide();
this._overview.hide();
this.visible = false;
this.animationInProgress = false;

View File

@ -15,6 +15,7 @@ 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;
@ -247,6 +248,10 @@ const AppMenuButton = new Lang.Class({
this._container.connect('get-preferred-height', Lang.bind(this, this._getContentPreferredHeight));
this._container.connect('allocate', Lang.bind(this, this._contentAllocate));
let textureCache = St.TextureCache.get_default();
textureCache.connect('icon-theme-changed',
Lang.bind(this, this._onIconThemeChanged));
this._iconBox = new Shell.Slicer({ name: 'appMenuIcon' });
this._iconBox.connect('style-changed',
Lang.bind(this, this._onIconBoxStyleChanged));
@ -332,6 +337,15 @@ const AppMenuButton = new Lang.Class({
this._updateIconBoxClip();
},
_onIconThemeChanged: function() {
if (this._iconBox.child == null)
return;
this._iconBox.child.destroy();
let icon = this._targetApp.get_faded_icon(2 * PANEL_ICON_SIZE);
this._iconBox.set_child(icon);
},
_updateIconBoxClip: function() {
let allocation = this._iconBox.allocation;
if (this._iconBottomClip > 0)
@ -468,15 +482,6 @@ const AppMenuButton = new Lang.Class({
this._sync();
},
setLockedState: function(locked) {
if (locked) {
this.hide();
} else {
this.show();
this._sync();
}
},
_sync: function() {
let tracker = Shell.WindowTracker.get_default();
let focusedApp = tracker.focus_app;
@ -610,8 +615,8 @@ const ActivitiesButton = new Lang.Class({
this.actor.label_actor = this._label;
this._hotCorner = new Layout.HotCorner();
container.add_actor(this._hotCorner.actor);
this.hotCorner = new Layout.HotCorner();
container.add_actor(this.hotCorner.actor);
// Hack up our menu...
this.menu.open = Lang.bind(this, this._onMenuOpenRequest);
@ -661,10 +666,10 @@ const ActivitiesButton = new Lang.Class({
}
hotBox.x1 = Math.round(x);
hotBox.x2 = hotBox.x1 + this._hotCorner.actor.width;
hotBox.x2 = hotBox.x1 + this.hotCorner.actor.width;
hotBox.y1 = Math.round(y);
hotBox.y2 = hotBox.y1 + this._hotCorner.actor.height;
this._hotCorner.actor.allocate(hotBox, flags);
hotBox.y2 = hotBox.y1 + this.hotCorner.actor.height;
this.hotCorner.actor.allocate(hotBox, flags);
},
handleDragOver: function(source, actor, x, y, time) {
@ -686,7 +691,7 @@ const ActivitiesButton = new Lang.Class({
_onCapturedEvent: function(actor, event) {
if (event.type() == Clutter.EventType.BUTTON_PRESS) {
if (!this._hotCorner.shouldToggleOverviewOnClick())
if (!this.hotCorner.shouldToggleOverviewOnClick())
return true;
}
return false;
@ -910,6 +915,7 @@ const PANEL_ITEM_IMPLEMENTATIONS = {
'volume': imports.ui.status.volume.Indicator,
'battery': imports.ui.status.power.Indicator,
'lockScreen': imports.ui.status.lockScreenMenu.Indicator,
'logo': imports.gdm.loginDialog.LogoMenuButton,
'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
'powerMenu': imports.gdm.powerMenu.PowerMenuButton,
'userMenu': imports.ui.userMenu.UserMenuButton
@ -926,12 +932,47 @@ 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 Shell.GenericContainer({ name: 'panel',
reactive: true });
this.actor = new St.Widget({ name: 'panel',
reactive: true,
layoutManager: new PanelLayout() });
this.actor._delegate = this;
this.statusArea = {};
@ -943,8 +984,6 @@ const Panel = new Lang.Class({
this.actor.remove_style_class_name('in-overview');
}));
Main.screenShield.connect('lock-status-changed', Lang.bind(this, this._onLockStateChanged));
this.menuManager = new PopupMenu.PopupMenuManager(this);
this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
@ -958,7 +997,6 @@ const Panel = new Lang.Class({
this._leftCorner = new PanelCorner(this._rightBox, St.Side.LEFT);
else
this._leftCorner = new PanelCorner(this._leftBox, St.Side.LEFT);
this.actor.add_actor(this._leftCorner.actor);
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL)
@ -967,88 +1005,14 @@ const Panel = new Lang.Class({
this._rightCorner = new PanelCorner(this._rightBox, 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.layoutManager.panelBox.add(this.actor);
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'start-here-symbolic',
{ sortGroup: CtrlAltTab.SortGroup.TOP });
},
_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, cornerWidth] = this._leftCorner.actor.get_preferred_width(-1);
let [cornerMinHeight, cornerHeight] = this._leftCorner.actor.get_preferred_width(-1);
childBox.x1 = 0;
childBox.x2 = cornerWidth;
childBox.y1 = allocHeight;
childBox.y2 = allocHeight + cornerHeight;
this._leftCorner.actor.allocate(childBox, flags);
let [cornerMinWidth, cornerWidth] = this._rightCorner.actor.get_preferred_width(-1);
let [cornerMinHeight, cornerHeight] = this._rightCorner.actor.get_preferred_width(-1);
childBox.x1 = allocWidth - cornerWidth;
childBox.x2 = allocWidth;
childBox.y1 = allocHeight;
childBox.y2 = allocHeight + cornerHeight;
this._rightCorner.actor.allocate(childBox, flags);
Main.sessionMode.connect('updated', Lang.bind(this, this._updatePanel));
this._updatePanel();
},
_onButtonPress: function(actor, event) {
@ -1103,11 +1067,27 @@ const Panel = new Lang.Class({
menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
},
init: function() {
set boxOpacity(value) {
let isReactive = value > 0;
this._leftBox.opacity = value;
this._leftBox.reactive = isReactive;
this._centerBox.opacity = value;
this._centerBox.reactive = isReactive;
this._rightBox.opacity = value;
this._rightBox.reactive = isReactive;
},
get boxOpacity() {
return this._leftBox.opacity;
},
_updatePanel: function() {
let panel = Main.sessionMode.panel;
this._initBox(panel.left, this._leftBox);
this._initBox(panel.center, this._centerBox);
this._initBox(panel.right, this._rightBox);
this._hideIndicators();
this._updateBox(panel.left, this._leftBox);
this._updateBox(panel.center, this._centerBox);
this._updateBox(panel.right, this._rightBox);
},
_initBox: function(elements, box) {
@ -1119,20 +1099,63 @@ const Panel = new Lang.Class({
// bluetooth or network)
continue;
}
}
},
let indicator = new constructor(this);
this._addToPanelBox(role, indicator, i, box);
_hideIndicators: function() {
for (let role in PANEL_ITEM_IMPLEMENTATIONS) {
let indicator = this.statusArea[role];
if (!indicator)
continue;
if (indicator.menu)
indicator.menu.close();
indicator.container.hide();
}
},
_ensureIndicator: function(role) {
let indicator = this.statusArea[role];
if (!indicator) {
let constructor = PANEL_ITEM_IMPLEMENTATIONS[role];
if (!constructor) {
// This icon is not implemented (this is a bug)
return null;
}
indicator = new constructor(this);
this.statusArea[role] = indicator;
}
return indicator;
},
_updateBox: function(elements, box) {
let nChildren = box.get_n_children();
for (let i = 0; i < elements.length; i++) {
let role = elements[i];
let indicator = this._ensureIndicator(role);
if (indicator == null)
continue;
this._addToPanelBox(role, indicator, i + nChildren, box);
}
},
_addToPanelBox: function(role, indicator, position, box) {
box.insert_child_at_index(indicator.actor, position);
let container = indicator.container;
container.show();
let parent = container.get_parent();
if (parent)
parent.remove_actor(container);
box.insert_child_at_index(container, position);
if (indicator.menu)
this.menuManager.addMenu(indicator.menu);
this.statusArea[role] = indicator;
let destroyId = indicator.connect('destroy', Lang.bind(this, function(emitter) {
delete this.statusArea[role];
emitter.disconnect(destroyId);
container.destroy();
}));
},
@ -1150,12 +1173,8 @@ const Panel = new Lang.Class({
right: this._rightBox
};
let boxContainer = boxes[box] || this._rightBox;
this.statusArea[role] = indicator;
this._addToPanelBox(role, indicator, position, boxContainer);
return indicator;
},
_onLockStateChanged: function(shield, locked) {
for (let id in this.statusArea)
this.statusArea[id].setLockedState(locked);
},
}
});

View File

@ -21,6 +21,10 @@ const ButtonBox = new Lang.Class({
this.actor = new Shell.GenericContainer(params);
this.actor._delegate = this;
this.container = new St.Bin({ y_fill: true,
x_fill: true,
child: this.actor });
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this.actor.connect('allocate', Lang.bind(this, this._allocate));
@ -115,6 +119,12 @@ const Button = new Lang.Class({
this.setName(nameText);
},
setSensitive: function(sensitive) {
this.actor.reactive = sensitive;
this.actor.can_focus = sensitive;
this.actor.track_hover = sensitive;
},
setName: function(text) {
if (text != null) {
// This is the easiest way to provide a accessible name to
@ -146,13 +156,6 @@ const Button = new Lang.Class({
}
},
setLockedState: function(locked) {
// default behaviour is to hide completely
if (locked)
this.menu.close();
this.actor.visible = !locked;
},
_onButtonPress: function(actor, event) {
if (!this.menu)
return;
@ -211,7 +214,8 @@ const Button = new Lang.Class({
destroy: function() {
this.actor._delegate = null;
this.menu.destroy();
if (this.menu)
this.menu.destroy();
this.actor.destroy();
this.emit('destroy');

View File

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

View File

@ -2,6 +2,7 @@
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const GnomeDesktop = imports.gi.GnomeDesktop;
const Shell = imports.gi.Shell;
// We stop polling if the user is idle for more than this amount of time
@ -40,9 +41,9 @@ const PointerWatcher = new Lang.Class({
Name: 'PointerWatcher',
_init: function() {
let idleMonitor = Shell.IdleMonitor.get();
idleMonitor.add_watch(IDLE_TIME,
Lang.bind(this, this._onIdleMonitorWatch));
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._watches = [];
this.pointerX = null;
@ -78,11 +79,14 @@ const PointerWatcher = new Lang.Class({
}
},
_onIdleMonitorWatch: function(monitor, id, userBecameIdle) {
this._idle = userBecameIdle;
if (!userBecameIdle)
this._updatePointer();
_onIdleMonitorBecameActive: function(monitor) {
this._idle = false;
this._updatePointer();
this._updateTimeout();
},
_onIdleMonitorBecameIdle: function(monitor) {
this._idle = true;
this._updateTimeout();
},

View File

@ -61,6 +61,9 @@ const PopupBaseMenuItem = new Lang.Class({
this.setSensitive(this.sensitive);
if (!this._activatable)
this.actor.add_style_class_name('popup-inactive-menu-item');
if (params.style_class)
this.actor.add_style_class_name(params.style_class);
@ -401,9 +404,17 @@ const PopupSeparatorMenuItem = new Lang.Class({
this.parent({ reactive: false,
can_focus: false});
this._drawingArea = new St.DrawingArea({ style_class: 'popup-separator-menu-item' });
this.addActor(this._drawingArea, { span: -1, expand: true });
this._drawingArea.connect('repaint', Lang.bind(this, this._onRepaint));
this._separator = new HorzSeparator({ style_class: 'popup-separator-menu-item' });
this.addActor(this._separator.actor, { span: -1, expand: true });
}
});
const HorzSeparator = new Lang.Class({
Name: 'HorzSeparator',
_init: function (params) {
this.actor = new St.DrawingArea(params);
this.actor.connect('repaint', Lang.bind(this, this._onRepaint));
},
_onRepaint: function(area) {
@ -763,7 +774,7 @@ const PopupSwitchMenuItem = new Lang.Class({
{ expand: true, span: -1, align: St.Align.END });
this._statusLabel = new St.Label({ text: '',
style_class: 'popup-inactive-menu-item'
style_class: 'popup-status-menu-item'
});
this._statusBin.child = this._switch.actor;
},
@ -870,6 +881,12 @@ const PopupMenuBase = new Lang.Class({
this._activeMenuItem = null;
this._childMenus = [];
this._settingsActions = { };
this._sessionUpdatedId = Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
},
_sessionUpdated: function() {
this._setSettingsVisibility(Main.sessionMode.allowSettings);
},
addAction: function(title, callback) {
@ -883,9 +900,6 @@ const PopupMenuBase = new Lang.Class({
},
addSettingsAction: function(title, desktopFile) {
if (!Main.sessionMode.allowSettings)
return null;
let menuItem = this.addAction(title, function() {
let app = Shell.AppSystem.get_default().lookup_setting(desktopFile);
@ -898,12 +912,13 @@ const PopupMenuBase = new Lang.Class({
app.activate();
});
menuItem.actor.visible = Main.sessionMode.allowSettings;
this._settingsActions[desktopFile] = menuItem;
return menuItem;
},
setSettingsVisibility: function(visible) {
_setSettingsVisibility: function(visible) {
for (let id in this._settingsActions) {
let item = this._settingsActions[id];
item.actor.visible = visible;
@ -1097,7 +1112,8 @@ const PopupMenuBase = new Lang.Class({
let columnWidths = [];
let items = this.box.get_children();
for (let i = 0; i < items.length; i++) {
if (!items[i].visible)
if (!items[i].visible &&
!(items[i]._delegate instanceof PopupSubMenu && items[i-1].visible))
continue;
if (items[i]._delegate instanceof PopupBaseMenuItem || items[i]._delegate instanceof PopupMenuBase) {
let itemColumnWidths = items[i]._delegate.getColumnWidths();
@ -1172,6 +1188,9 @@ const PopupMenuBase = new Lang.Class({
this.actor.destroy();
this.emit('destroy');
Main.sessionMode.disconnect(this._sessionUpdatedId);
this._sessionUpdatedId = 0;
}
});
Signals.addSignalMethods(PopupMenuBase.prototype);
@ -1258,13 +1277,14 @@ const PopupMenu = new Lang.Class({
},
close: function(animate) {
if (!this.isOpen)
return;
if (this._activeMenuItem)
this._activeMenuItem.setActive(false);
this._boxPointer.hide(animate);
if (this._boxPointer.actor.visible)
this._boxPointer.hide(animate);
if (!this.isOpen)
return;
this.isOpen = false;
this.emit('open-state-changed', false);
@ -1337,6 +1357,11 @@ const PopupSubMenu = new Lang.Class({
this.actor.vscrollbar_policy =
needsScrollbar ? Gtk.PolicyType.AUTOMATIC : Gtk.PolicyType.NEVER;
if (needsScrollbar)
this.actor.add_style_pseudo_class('scrolled');
else
this.actor.remove_style_pseudo_class('scrolled');
// It looks funny if we animate with a scrollbar (at what point is
// the scrollbar added?) so just skip that case
if (animate && needsScrollbar)
@ -2041,6 +2066,9 @@ const PopupMenuManager = new Lang.Class({
},
addMenu: function(menu, position) {
if (this._findMenu(menu) > -1)
return;
let menudata = {
menu: menu,
openStateChangeId: menu.connect('open-state-changed', Lang.bind(this, this._onMenuOpenState)),

View File

@ -27,6 +27,9 @@ const SearchProviderIface = <interface name="org.gnome.Shell.SearchProvider">
<method name="ActivateResult">
<arg type="s" direction="in" />
</method>
<method name="LaunchSearch">
<arg type="as" direction="in" />
</method>
</interface>;
var SearchProviderProxy = Gio.DBusProxy.makeProxyWrapper(SearchProviderIface);
@ -112,6 +115,7 @@ const RemoteSearchProvider = new Lang.Class({
this.parent(title.toUpperCase());
this._cancellable = new Gio.Cancellable();
this.icon = icon;
},
createIcon: function(size, meta) {
@ -126,9 +130,7 @@ const RemoteSearchProvider = new Lang.Class({
width, height, rowStride, size);
}
// Ugh, but we want to fall back to something ...
return new St.Icon({ icon_name: 'text-x-generic',
icon_size: size });
return null;
},
_getResultsFinished: function(results, error) {
@ -196,6 +198,10 @@ const RemoteSearchProvider = new Lang.Class({
activateResult: function(id) {
this._proxy.ActivateResultRemote(id);
},
launchSearch: function(terms) {
this._proxy.LaunchSearchRemote(terms);
}
});

View File

@ -202,11 +202,12 @@ const RunDialog = new Lang.Class({
let label = new St.Label({ style_class: 'run-dialog-label',
text: _("Please enter a command:") });
text: _("Enter a Command") });
this.contentLayout.add(label, { y_align: St.Align.START });
let entry = new St.Entry({ style_class: 'run-dialog-entry' });
let entry = new St.Entry({ style_class: 'run-dialog-entry',
can_focus: true });
ShellEntry.addContextMenu(entry);
entry.label_actor = label;
@ -219,7 +220,9 @@ const RunDialog = new Lang.Class({
this.contentLayout.add(this._errorBox, { expand: true });
let errorIcon = new St.Icon({ icon_name: 'dialog-error', icon_size: 24, style_class: 'run-dialog-error-icon' });
let errorIcon = new St.Icon({ icon_name: 'dialog-error-symbolic',
icon_size: 24,
style_class: 'run-dialog-error-icon' });
this._errorBox.add(errorIcon, { y_align: St.Align.MIDDLE });
@ -234,6 +237,10 @@ const RunDialog = new Lang.Class({
this._errorBox.hide();
this.setButtons([{ action: Lang.bind(this, this.close),
label: _("Close"),
key: Clutter.Escape }]);
this._pathCompleter = new Gio.FilenameCompleter();
this._commandCompleter = new CommandCompleter();
this._group.connect('notify::visible', Lang.bind(this._commandCompleter, this._commandCompleter.update));
@ -244,20 +251,12 @@ const RunDialog = new Lang.Class({
let symbol = e.get_key_symbol();
if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) {
this.popModal();
if (e.get_state() & Clutter.ModifierType.CONTROL_MASK)
this._run(o.get_text(), true);
else
this._run(o.get_text(), false);
if (!this._commandError)
this._run(o.get_text(),
e.get_state() & Clutter.ModifierType.CONTROL_MASK);
if (!this._commandError ||
!this.pushModal())
this.close();
else {
if (!this.pushModal())
this.close();
}
return true;
}
if (symbol == Clutter.Escape) {
this.close();
return true;
}
if (symbol == Clutter.slash) {

View File

@ -17,16 +17,19 @@ const Layout = imports.ui.layout;
const LoginManager = imports.misc.loginManager;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const Overview = imports.ui.overview;
const MessageTray = imports.ui.messageTray;
const ShellDBus = imports.ui.shellDBus;
const Tweener = imports.ui.tweener;
const Util = imports.misc.util;
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
const LOCK_ENABLED_KEY = 'lock-enabled';
const CURTAIN_SLIDE_TIME = 0.5;
const CURTAIN_SLIDE_TIME = 0.3;
// fraction of screen height the arrow must reach before completing
// the slide up automatically
const ARROW_DRAG_TRESHOLD = 0.1;
const ARROW_DRAG_THRESHOLD = 0.1;
// Parameters for the arrow animation
const N_ARROWS = 3;
@ -44,7 +47,7 @@ const SUMMARY_ICON_SIZE = 48;
// STANDARD_FADE_TIME is used when the session goes idle, while
// SHORT_FADE_TIME is used when requesting lock explicitly from the user menu
const STANDARD_FADE_TIME = 10;
const SHORT_FADE_TIME = 0.8;
const SHORT_FADE_TIME = 0.3;
const Clock = new Lang.Class({
Name: 'ScreenShieldClock',
@ -93,13 +96,13 @@ const NotificationsBox = new Lang.Class({
this._residentNotificationBox = new St.BoxLayout({ vertical: true,
style_class: 'screen-shield-notifications-box' });
let scrollView = new St.ScrollView({ x_fill: false, x_align: St.Align.MIDDLE });
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.actor.add(this._residentNotificationBox, { x_fill: true });
this.actor.add(scrollView, { x_fill: true, x_align: St.Align.MIDDLE });
this.actor.add(scrollView, { x_fill: true, x_align: St.Align.START });
this._items = [];
Main.messageTray.getSummaryItems().forEach(Lang.bind(this, function(item) {
@ -124,13 +127,12 @@ const NotificationsBox = new Lang.Class({
},
_updateVisibility: function() {
if (this._residentNotificationBox.get_n_children() > 0) {
this.actor.show();
return;
}
this._residentNotificationBox.visible = this._residentNotificationBox.get_n_children() > 0;
this._persistentNotificationBox.visible = this._persistentNotificationBox.get_children().some(function(a) {
return a.visible;
});
let children = this._persistentNotificationBox.get_children();
this.actor.visible = children.some(function(a) { return a.visible; });
this.actor.visible = this._residentNotificationBox.visible || this._persistentNotificationBox.visible;
},
_sourceIsResident: function(source) {
@ -151,7 +153,7 @@ const NotificationsBox = new Lang.Class({
box.add(sourceActor.actor, { y_fill: true });
let textBox = new St.BoxLayout({ vertical: true });
box.add(textBox);
box.add(textBox, { y_fill: false, y_align: St.Align.START });
let label = new St.Label({ text: source.title,
style_class: 'screen-shield-notification-label' });
@ -184,11 +186,11 @@ const NotificationsBox = new Lang.Class({
if (obj.resident) {
this._residentNotificationBox.add(item.notificationStackWidget);
item.closeButtonVisible = false;
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.MIDDLE });
this._persistentNotificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.START });
}
obj.contentUpdatedId = item.connect('content-updated', Lang.bind(this, this._onItemContentUpdated));
@ -234,7 +236,7 @@ const NotificationsBox = new Lang.Class({
this._residentNotificationBox.remove_actor(obj.item.notificationStackWidget);
[obj.sourceBox, obj.countLabel] = this._makeNotificationSource(obj.source);
this._persistentNotificationBox.add(obj.sourceBox);
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();
@ -242,7 +244,7 @@ const NotificationsBox = new Lang.Class({
obj.resident = true;
this._residentNotificationBox.add(obj.item.notificationStackWidget);
obj.item.closeButtonVisible = false;
obj.item.closeButton.hide();
obj.item.prepareNotificationStackForShowing();
} else {
// just update the counter
@ -365,7 +367,8 @@ const ScreenShield = new Lang.Class({
name: 'lockScreenContents' });
this._lockScreenContents.add_constraint(new Layout.MonitorConstraint({ primary: true }));
this._background = Meta.BackgroundActor.new_for_screen(global.screen);
this._background = new St.Bin({ style_class: 'screen-shield-background',
child: Meta.BackgroundActor.new_for_screen(global.screen) });
this._lockScreenGroup.add_actor(this._background);
this._lockScreenGroup.add_actor(this._lockScreenContents);
@ -395,6 +398,7 @@ const ScreenShield = new Lang.Class({
this._lockDialogGroup = new St.Widget({ x_expand: true,
y_expand: true,
pivot_point: new Clutter.Point({ x: 0.5, y: 0.5 }),
name: 'lockDialogGroup' });
this.actor.add_actor(this._lockDialogGroup);
@ -412,6 +416,8 @@ const ScreenShield = new Lang.Class({
this._onStatusChanged(status);
}));
this._screenSaverDBus = new ShellDBus.ScreenSaverDBus(this);
this._loginManager = LoginManager.getLoginManager();
this._loginSession = this._loginManager.getCurrentSessionProxy();
this._loginSession.connectSignal('Lock', Lang.bind(this, function() { this.lock(false); }));
@ -420,8 +426,11 @@ const ScreenShield = new Lang.Class({
this._settings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
this._isModal = false;
this._isLocked = false;
this._hasLockScreen = false;
this._isGreeter = false;
this._isActive = false;
this._inUnlockAnimation = false;
this._activationTime = 0;
this._lightbox = new Lightbox.Lightbox(Main.uiGroup,
{ inhibitEvents: true,
@ -430,17 +439,28 @@ const ScreenShield = new Lang.Class({
},
_onLockScreenKeyRelease: function(actor, event) {
if (event.get_key_symbol() == Clutter.KEY_Escape) {
this._ensureUnlockDialog();
let symbol = event.get_key_symbol();
// Do nothing if the lock screen is not fully shown.
// This avoids reusing the previous (and stale) unlock
// dialog if esc is pressed while the curtain is going
// down after cancel.
// Similarly, don't bump if the lock screen is not showing or is
// animating, as the bump overrides the animation and would
// remove any onComplete handler.
if (this._lockScreenState != MessageTray.State.SHOWN)
return false;
if (symbol == Clutter.KEY_Escape ||
symbol == Clutter.KEY_Return ||
symbol == Clutter.KEY_KP_Enter) {
this._ensureUnlockDialog(true);
this._hideLockScreen(true);
return true;
}
// Don't bump if the lock screen is not showing or is
// animating, as the bump overrides the animation and would
// remove any onComplete handler
if (this._lockScreenState == MessageTray.State.SHOWN)
this._bumpLockScreen();
this._bumpLockScreen();
return true;
},
@ -458,8 +478,8 @@ const ScreenShield = new Lang.Class({
// 7 standard scrolls to lift up
if (this._lockScreenScrollCounter > 35) {
this._ensureUnlockDialog();
this._hideLockScreen(0);
this._ensureUnlockDialog(false);
this._hideLockScreen(true);
}
return true;
@ -490,11 +510,11 @@ const ScreenShield = new Lang.Class({
_onDragBegin: function() {
Tweener.removeTweens(this._lockScreenGroup);
this._lockScreenState = MessageTray.State.HIDING;
this._ensureUnlockDialog();
this._ensureUnlockDialog(false);
},
_onDragEnd: function(action, actor, eventX, eventY, modifiers) {
if (this._lockScreenGroup.y < -(ARROW_DRAG_TRESHOLD * global.stage.height)) {
if (this._lockScreenGroup.y < -(ARROW_DRAG_THRESHOLD * global.stage.height)) {
// Complete motion automatically
this._hideLockScreen(true);
} else {
@ -506,7 +526,7 @@ const ScreenShield = new Lang.Class({
Tweener.addTween(this._lockScreenGroup,
{ y: 0,
time: time,
transition: 'linear',
transition: 'easeInQuad',
onComplete: function() {
this._lockScreenGroup.fixed_position_set = false;
this._lockScreenState = MessageTray.State.SHOWN;
@ -517,7 +537,7 @@ const ScreenShield = new Lang.Class({
// If we have a unlock dialog, cancel it
if (this._dialog) {
this._dialog.cancel();
if (!this._keepDialog) {
if (!this._isGreeter) {
this._dialog = null;
}
}
@ -528,7 +548,7 @@ const ScreenShield = new Lang.Class({
if (status == GnomeSession.PresenceStatus.IDLE) {
if (this._dialog) {
this._dialog.cancel();
if (!this._keepDialog) {
if (!this._isGreeter) {
this._dialog = null;
}
}
@ -538,14 +558,18 @@ const ScreenShield = new Lang.Class({
this._isModal = true;
}
if (!this._isLocked)
if (!this._isActive) {
this._lightbox.show();
if (this._activationTime == 0)
this._activationTime = GLib.get_monotonic_time();
}
} else {
let lightboxWasShown = this._lightbox.shown;
this._lightbox.hide();
let shouldLock = lightboxWasShown && this._settings.get_boolean(LOCK_ENABLED_KEY);
if (shouldLock || this._isLocked) {
if (shouldLock || this._isActive) {
this.lock(false);
} else if (this._isModal) {
this.unlock();
@ -554,8 +578,20 @@ const ScreenShield = new Lang.Class({
},
showDialog: function() {
this.lock(true);
this._ensureUnlockDialog();
// Ensure that the stage window is mapped, before taking a grab
// otherwise X errors out
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
if (!this._isModal) {
Main.pushModal(this.actor);
this._isModal = true;
}
return false;
}));
this.actor.show();
this._isGreeter = Main.sessionMode.isGreeter;
this._ensureUnlockDialog(true);
this._hideLockScreen(false);
},
@ -586,33 +622,35 @@ const ScreenShield = new Lang.Class({
Tweener.addTween(this._lockScreenGroup,
{ y: -h,
time: time,
transition: 'linear',
transition: 'easeInQuad',
onComplete: function() {
this._lockScreenHidden();
this._lockScreenState = MessageTray.State.HIDDEN;
this._lockScreenGroup.hide();
},
onCompleteScope: this,
});
} else {
this._lockScreenHidden();
this._lockScreenState = MessageTray.State.HIDDEN;
this._lockScreenGroup.hide();
}
if (Main.sessionMode.currentMode == 'lock-screen')
Main.sessionMode.popMode('lock-screen');
},
_lockScreenHidden: function() {
this._lockScreenState = MessageTray.State.HIDDEN;
this._lockScreenGroup.hide();
},
_ensureUnlockDialog: function() {
_ensureUnlockDialog: function(onPrimary) {
if (!this._dialog) {
[this._dialog, this._keepDialog] = Main.sessionMode.createUnlockDialog(this._lockDialogGroup);
let constructor = Main.sessionMode.unlockDialog;
this._dialog = new constructor(this._lockDialogGroup);
if (!this._dialog) {
// This session mode has no locking capabilities
this.unlock();
return;
}
let time = global.get_current_time();
this._dialog.connect('loaded', Lang.bind(this, function() {
if (!this._dialog.open()) {
if (!this._dialog.open(time, onPrimary)) {
log('Could not open login dialog: failed to acquire grab');
this.unlock();
}
@ -621,45 +659,42 @@ const ScreenShield = new Lang.Class({
this._dialog.connect('failed', Lang.bind(this, this._onUnlockFailed));
this._dialog.connect('unlocked', Lang.bind(this, this._onUnlockSucceded));
}
if (this._keepDialog) {
// Notify the other components that even though we are showing the
// screenshield, we're not in a locked state
// (this happens for the gdm greeter)
this._isLocked = false;
this.emit('lock-status-changed', false);
}
},
_onUnlockFailed: function() {
this._dialog.destroy();
this._dialog = null;
this._resetLockScreen(false);
this._resetLockScreen(true, false);
},
_onUnlockSucceded: function() {
this.unlock();
this._tweenUnlocked();
},
_resetLockScreen: function(animate) {
_resetLockScreen: function(animateLockScreen, animateLockDialog) {
this._ensureLockScreen();
this._lockDialogGroup.scale_x = 1;
this._lockDialogGroup.scale_y = 1;
this._lockScreenGroup.show();
this._lockScreenState = MessageTray.State.SHOWING;
if (animate) {
if (animateLockScreen) {
this._lockScreenGroup.y = -global.screen_height;
Tweener.removeTweens(this._lockScreenGroup);
Tweener.addTween(this._lockScreenGroup,
{ y: 0,
time: SHORT_FADE_TIME,
transition: 'linear',
transition: 'easeOutQuad',
onComplete: function() {
this._lockScreenShown();
},
onCompleteScope: this
});
} else {
this._lockScreenGroup.fixed_position_set = false;
this._lockScreenShown();
}
if (animateLockDialog) {
this._lockDialogGroup.opacity = 0;
Tweener.removeTweens(this._lockDialogGroup);
Tweener.addTween(this._lockDialogGroup,
@ -668,13 +703,20 @@ const ScreenShield = new Lang.Class({
transition: 'easeOutQuad' });
} else {
this._lockDialogGroup.opacity = 255;
this._lockScreenShown();
}
this._lockScreenGroup.grab_key_focus();
if (Main.sessionMode.currentMode != 'lock-screen')
Main.sessionMode.pushMode('lock-screen');
},
_lockScreenShown: function() {
if (this._dialog && !this._isGreeter) {
this._dialog.destroy();
this._dialog = null;
}
if (this._arrowAnimationId)
Mainloop.source_remove(this._arrowAnimationId);
this._arrowAnimationId = Mainloop.timeout_add(6000, Lang.bind(this, this._animateArrows));
@ -689,12 +731,16 @@ const ScreenShield = new Lang.Class({
// Some of the actors in the lock screen are heavy in
// resources, so we only create them when needed
_prepareLockScreen: function() {
_ensureLockScreen: function() {
if (this._hasLockScreen)
return;
this._lockScreenContentsBox = new St.BoxLayout({ x_align: Clutter.ActorAlign.CENTER,
y_align: Clutter.ActorAlign.CENTER,
x_expand: true,
y_expand: true,
vertical: true });
vertical: true,
style_class: 'screen-shield-contents-box' });
this._clock = new Clock();
this._lockScreenContentsBox.add(this._clock.actor, { x_fill: true,
y_fill: true });
@ -731,22 +777,38 @@ const ScreenShield = new Lang.Class({
},
get locked() {
return this._isLocked;
return this._isActive;
},
get activationTime() {
return this._activationTime;
},
_tweenUnlocked: function() {
this._inUnlockAnimation = true;
this.unlock();
Tweener.addTween(this._lockDialogGroup, {
scale_x: 0,
scale_y: 0,
time: Overview.ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: function() {
if (this._dialog) {
this._dialog.destroy();
this._dialog = null;
}
this.actor.hide();
this._inUnlockAnimation = false;
},
onCompleteScope: this
});
},
unlock: function() {
if (this._hasLockScreen)
this._clearLockScreen();
if (this._keepDialog) {
// The dialog must be kept alive,
// so immediately go back to it
// This will also reset _isLocked
this._ensureUnlockDialog();
return;
}
if (this._dialog) {
if (this._dialog && !this._isGreeter) {
this._dialog.destroy();
this._dialog = null;
}
@ -758,26 +820,100 @@ const ScreenShield = new Lang.Class({
this._isModal = false;
}
this._isLocked = false;
this.actor.hide();
if (!this._inUnlockAnimation)
this.actor.hide();
this.emit('lock-status-changed', false);
if (Main.sessionMode.currentMode == 'lock-screen')
Main.sessionMode.popMode('lock-screen');
if (Main.sessionMode.currentMode == 'unlock-dialog')
Main.sessionMode.popMode('unlock-dialog');
this._activationTime = 0;
this._isActive = false;
this.emit('lock-status-changed');
},
lock: function(animate) {
if (!this._hasLockScreen)
this._prepareLockScreen();
if (!this._isModal) {
Main.pushModal(this.actor);
this._isModal = true;
}
this._isLocked = true;
this.actor.show();
this._resetLockScreen(animate);
if (this._activationTime == 0)
this._activationTime = GLib.get_monotonic_time();
this.emit('lock-status-changed', true);
this.actor.show();
if (Main.sessionMode.currentMode != 'unlock-dialog' &&
Main.sessionMode.currentMode != 'lock-screen') {
this._isGreeter = Main.sessionMode.isGreeter;
if (!this._isGreeter)
Main.sessionMode.pushMode('unlock-dialog');
}
this._resetLockScreen(animate, animate);
this._isActive = true;
this.emit('lock-status-changed');
},
});
Signals.addSignalMethods(ScreenShield.prototype);
/* Fallback code to handle session locking using gnome-screensaver,
in case the required GDM dependency is not there
*/
const ScreenShieldFallback = new Lang.Class({
Name: 'ScreenShieldFallback',
_init: function() {
Util.spawn(['gnome-screensaver']);
this._proxy = new Gio.DBusProxy({ g_connection: Gio.DBus.session,
g_name: 'org.gnome.ScreenSaver',
g_object_path: '/org/gnome/ScreenSaver',
g_interface_name: 'org.gnome.ScreenSaver',
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES),
});
this._proxy.init(null);
this._proxy.connect('g-signal', Lang.bind(this, this._onSignal));
this._proxy.connect('notify::g-name-owner', Lang.bind(this, this._onNameOwnerChanged));
},
_onNameOwnerChanged: function(object, pspec) {
if (this._proxy.g_name_owner)
[this._locked] = this._proxy.call_sync('GetActive', null,
Gio.DBusCallFlags.NONE, -1, null).deep_unpack();
else
this._locked = false;
this.emit('lock-status-changed', this._locked);
},
_onSignal: function(proxy, senderName, signalName, params) {
if (signalName == 'ActiveChanged') {
[this._locked] = params.deep_unpack();
this.emit('lock-status-changed', this._locked);
}
},
get locked() {
return this._locked;
},
lock: function() {
this._proxy.call('Lock', null, Gio.DBusCallFlags.NONE, -1, null,
Lang.bind(this, function(proxy, result) {
proxy.call_finish(result);
this.emit('lock-screen-shown');
}));
},
unlock: function() {
this._proxy.call('SetActive', GLib.Variant.new('(b)', false),
Gio.DBusCallFlags.NONE, -1, null, null);
},
});
Signals.addSignalMethods(ScreenShieldFallback.prototype);

View File

@ -163,6 +163,16 @@ const SearchProvider = new Lang.Class({
*/
activateResult: function(id) {
throw new Error('Not implemented');
},
/**
* launchSearch:
* @terms: Current search terms
*
* Called when the user clicks the provider icon.
*/
launchSearch: function(terms) {
throw new Error('Not implemented');
}
});
Signals.addSignalMethods(SearchProvider.prototype);
@ -261,5 +271,11 @@ const SearchSystem = new Lang.Class({
}
}
},
launchSearch: function(provider) {
let terms = this.getTerms();
provider.launchSearch(terms);
Main.overview.toggle();
}
});
Signals.addSignalMethods(SearchSystem.prototype);

View File

@ -4,25 +4,191 @@ const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Gtk = imports.gi.Gtk;
const Meta = imports.gi.Meta;
const Signals = imports.signals;
const St = imports.gi.St;
const Atk = imports.gi.Atk;
const AppDisplay = imports.ui.appDisplay;
const DND = imports.ui.dnd;
const IconGrid = imports.ui.iconGrid;
const Main = imports.ui.main;
const Overview = imports.ui.overview;
const PopupMenu = imports.ui.popupMenu;
const Search = imports.ui.search;
const MAX_SEARCH_RESULTS_ROWS = 1;
const MAX_SEARCH_RESULTS_ROWS = 3;
const MAX_GRID_SEARCH_RESULTS_ROWS = 1;
const SearchResult = new Lang.Class({
Name: 'SearchResult',
const ListSearchResult = new Lang.Class({
Name: 'ListSearchResult',
ICON_SIZE: 64,
_init: function(provider, metaInfo, terms) {
this.provider = provider;
this.metaInfo = metaInfo;
this.actor = new St.Button({ style_class: 'search-result',
reactive: true,
can_focus: true,
track_hover: true,
x_align: St.Align.START,
x_fill: true,
y_fill: true });
this.actor._delegate = this;
let content = new St.BoxLayout({ style_class: 'search-result-content',
vertical: false });
this._content = content;
this.actor.set_child(content);
// An icon for, or thumbnail of, content
let icon = this.metaInfo['createIcon'](this.ICON_SIZE);
if (icon) {
let iconBin = new St.Bin({ style_class: 'search-result-icon',
child: icon });
content.add(iconBin);
}
let details = new St.BoxLayout({ style_class: 'search-result-details',
vertical: true });
content.add(details, { x_fill: true,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
let title = new St.Label({ style_class: 'search-result-details-title',
text: this.metaInfo['name'] })
details.add(title, { x_fill: true,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
// TODO: could highlight terms in the description here, or should
// providers provide what should be highlighted?
if (this.metaInfo['description']) {
let description = new St.Label({ style_class: 'search-result-details-description',
text: this.metaInfo['description'] });
details.add(description, { x_fill: false,
y_fill: true,
x_align: St.Align.START,
y_align: St.Align.END });
}
this.actor.connect('clicked', Lang.bind(this, this._onResultClicked));
},
setSelected: function(selected) {
if (selected)
this.actor.add_style_pseudo_class('selected');
else
this.actor.remove_style_pseudo_class('selected');
},
activate: function() {
this.provider.activateResult(this.metaInfo.id);
Main.overview.toggle();
},
_onResultClicked: function(actor) {
this.activate();
}
});
const ListSearchResults = new Lang.Class({
Name: 'ListSearchResults',
Extends: Search.SearchResultDisplay,
_init: function(provider) {
this.parent(provider);
this.actor = new St.BoxLayout({ style_class: 'results-list',
vertical: true });
this.actor.connect('notify::width', Lang.bind(this, function() {
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
let results = this.getResultsForDisplay();
if (results.length == 0)
return;
provider.getResultMetas(results, Lang.bind(this, this.renderResults));
}));
}));
this._notDisplayedResult = [];
this._terms = [];
this._pendingClear = false;
},
getResultsForDisplay: function() {
let alreadyVisible = this._pendingClear ? 0 : this.getVisibleResultCount();
let canDisplay = MAX_SEARCH_RESULTS_ROWS - alreadyVisible;
let numResults = Math.min(this._notDisplayedResult.length, canDisplay);
return this._notDisplayedResult.splice(0, numResults);
},
getVisibleResultCount: function() {
return this.actor.get_n_children();
},
hasMoreResults: function() {
return this._notDisplayedResult.length > 0;
},
setResults: function(results, terms) {
// copy the lists
this._notDisplayedResult = results.slice(0);
this._terms = terms.slice(0);
this._pendingClear = true;
},
renderResults: function(metas) {
for (let i = 0; i < metas.length; i++) {
let display = new ListSearchResult(this.provider, metas[i], this._terms);
this.addItem(display.actor);
display.actor.connect('key-focus-in', Lang.bind(this, this._onFocusedProviderChanged));
}
},
clear: function () {
this.actor.destroy_all_children();
this._pendingClear = false;
},
getFirstResult: function() {
if (this.getVisibleResultCount() > 0)
return this.getItemAtIndex(0)._delegate;
else
return null;
},
addItem: function(actor, index) {
if (index !== undefined)
this.actor.insert_child_at_index(actor, index);
else
this.actor.add_actor(actor);
},
getItemAtIndex: function(index) {
return this.actor.get_child_at_index(index);
},
_onFocusedProviderChanged: function() {
this.emit('focused-provider-changed');
}
});
Signals.addSignalMethods(ListSearchResults.prototype);
const GridSearchResult = new Lang.Class({
Name: 'GridSearchResult',
_init: function(provider, metaInfo, terms) {
this.provider = provider;
this.metaInfo = metaInfo;
this.actor = new St.Button({ style_class: 'grid-search-result',
reactive: true,
x_align: St.Align.START,
y_fill: true });
@ -31,7 +197,7 @@ const SearchResult = new Lang.Class({
let content = provider.createResultActor(metaInfo, terms);
if (content == null) {
content = new St.Bin({ style_class: 'search-result-content',
content = new St.Bin({ style_class: 'grid-search-result-content',
reactive: true,
can_focus: true,
track_hover: true,
@ -45,7 +211,7 @@ const SearchResult = new Lang.Class({
if (content._delegate && content._delegate.getDragActorSource)
this._dragActorSource = content._delegate.getDragActorSource();
}
this._content = content;
this.content = content;
this.actor.set_child(content);
this.actor.connect('clicked', Lang.bind(this, this._onResultClicked));
@ -53,23 +219,23 @@ const SearchResult = new Lang.Class({
let draggable = DND.makeDraggable(this.actor);
draggable.connect('drag-begin',
Lang.bind(this, function() {
Main.overview.beginItemDrag(this);
Main.overview.beginAppDrag(this);
}));
draggable.connect('drag-cancelled',
Lang.bind(this, function() {
Main.overview.cancelledItemDrag(this);
Main.overview.cancelledAppDrag(this);
}));
draggable.connect('drag-end',
Lang.bind(this, function() {
Main.overview.endItemDrag(this);
Main.overview.endAppDrag(this);
}));
},
setSelected: function(selected) {
if (selected)
this._content.add_style_pseudo_class('selected');
this.content.add_style_pseudo_class('selected');
else
this._content.remove_style_pseudo_class('selected');
this.content.remove_style_pseudo_class('selected');
},
activate: function() {
@ -85,7 +251,7 @@ const SearchResult = new Lang.Class({
if (this._dragActorSource)
return this._dragActorSource;
// not exactly right, but alignment problems are hard to notice
return this._content;
return this.content;
},
getDragActor: function(stageX, stageY) {
@ -108,11 +274,11 @@ const GridSearchResults = new Lang.Class({
_init: function(provider, grid) {
this.parent(provider);
this._grid = grid || new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
this._grid = grid || new IconGrid.IconGrid({ rowLimit: MAX_GRID_SEARCH_RESULTS_ROWS,
xAlign: St.Align.START });
this.actor = new St.Bin({ x_align: St.Align.START });
this.actor = new St.Bin({ x_align: St.Align.MIDDLE });
this.actor.set_child(this._grid.actor);
this._width = 0;
this.actor.connect('notify::width', Lang.bind(this, function() {
this._width = this.actor.width;
@ -130,6 +296,7 @@ const GridSearchResults = new Lang.Class({
},
getResultsForDisplay: function() {
this._grid.actor.ensure_style();
let alreadyVisible = this._pendingClear ? 0 : this._grid.visibleItemsCount();
let canDisplay = this._grid.childrenInRow(this._width) * this._grid.getRowLimit()
- alreadyVisible;
@ -143,6 +310,10 @@ const GridSearchResults = new Lang.Class({
return this._grid.visibleItemsCount();
},
hasMoreResults: function() {
return this._notDisplayedResult.length > 0;
},
setResults: function(results, terms) {
// copy the lists
this._notDisplayedResult = results.slice(0);
@ -152,8 +323,10 @@ const GridSearchResults = new Lang.Class({
renderResults: function(metas) {
for (let i = 0; i < metas.length; i++) {
let display = new SearchResult(this.provider, metas[i], this._terms);
let display = new GridSearchResult(this.provider, metas[i], this._terms);
this._grid.addItem(display.actor);
display.content.connect('key-focus-in', Lang.bind(this, this._onFocusedProviderChanged));
}
},
@ -167,11 +340,17 @@ const GridSearchResults = new Lang.Class({
return this._grid.getItemAtIndex(0)._delegate;
else
return null;
},
_onFocusedProviderChanged: function() {
this.emit('focused-provider-changed');
}
});
Signals.addSignalMethods(GridSearchResults.prototype);
const SearchResults = new Lang.Class({
Name: 'SearchResults',
const SearchDisplay = new Lang.Class({
Name: 'SearchDisplay',
_init: function(searchSystem) {
this._searchSystem = searchSystem;
@ -214,8 +393,6 @@ const SearchResults = new Lang.Class({
for (let i = 0; i < this._providers.length; i++) {
this.createProviderMeta(this._providers[i]);
}
this._searchProvidersBox = new St.BoxLayout({ style_class: 'search-providers-box' });
this.actor.add(this._searchProvidersBox);
this._highlightDefault = false;
this._defaultResult = null;
@ -224,19 +401,43 @@ const SearchResults = new Lang.Class({
createProviderMeta: function(provider) {
let providerBox = new St.BoxLayout({ style_class: 'search-section',
vertical: true });
let title = new St.Label({ style_class: 'search-section-header',
text: provider.title });
providerBox.add(title);
let providerBoxContent = new St.BoxLayout({ style_class: 'search-section-content' });
let isAppsProvider = (provider instanceof AppDisplay.AppSearchProvider);
let providerIcon;
if (!isAppsProvider) {
let separator = new PopupMenu.HorzSeparator({ style_class: 'search-section-separator' });
providerBox.add(separator.actor, { expand: true });
providerIcon = new ProviderIcon(provider);
providerIcon.connect('launch-search', Lang.bind(this, function(providerIcon) {
this._searchSystem.launchSearch(providerIcon.provider);
}));
providerBoxContent.add(providerIcon.actor, { x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
}
let resultDisplayBin = new St.Bin({ style_class: 'search-section-results',
x_fill: true,
y_fill: true });
providerBox.add(resultDisplayBin, { expand: true });
let resultDisplay = new GridSearchResults(provider);
providerBoxContent.add(resultDisplayBin, { expand: true });
providerBox.add(providerBoxContent);
let resultDisplay;
if (isAppsProvider)
resultDisplay = new GridSearchResults(provider);
else
resultDisplay = new ListSearchResults(provider);
resultDisplay.connect('focused-provider-changed', Lang.bind(this, this._updateProviderIconCanFocus));
resultDisplayBin.set_child(resultDisplay.actor);
this._providerMeta.push({ provider: provider,
actor: providerBox,
icon: providerIcon,
resultDisplay: resultDisplay });
this._content.add(providerBox);
},
@ -257,6 +458,7 @@ const SearchResults = new Lang.Class({
let meta = this._providerMeta[i];
meta.resultDisplay.clear();
meta.actor.hide();
if (meta.icon) meta.icon.actor.can_focus = false;
}
},
@ -264,6 +466,7 @@ const SearchResults = new Lang.Class({
let meta = this._metaForProvider(provider);
meta.resultDisplay.clear();
meta.actor.hide();
if (meta.icon) meta.icon.actor.can_focus = false;
},
reset: function() {
@ -286,6 +489,11 @@ const SearchResults = new Lang.Class({
return this._providerMeta[this._providers.indexOf(provider)];
},
_metaForProvidersExcept: function(provider) {
let skip = this._providers.indexOf(provider);
return this._providerMeta.filter(function(meta, index) { return index != skip }, this);
},
_maybeSetInitialSelection: function() {
let newDefaultResult = null;
@ -302,9 +510,6 @@ const SearchResults = new Lang.Class({
}
}
if (!newDefaultResult)
newDefaultResult = this._searchProvidersBox.get_first_child();
if (newDefaultResult != this._defaultResult) {
if (this._defaultResult)
this._defaultResult.setSelected(false);
@ -346,6 +551,9 @@ const SearchResults = new Lang.Class({
meta.resultDisplay.setResults(providerResults, terms);
let results = meta.resultDisplay.getResultsForDisplay();
if (meta.icon)
meta.icon.moreIcon.visible = meta.resultDisplay.hasMoreResults();
provider.getResultMetas(results, Lang.bind(this, function(metas) {
this._clearDisplayForProvider(provider);
meta.actor.show();
@ -379,6 +587,15 @@ const SearchResults = new Lang.Class({
this._defaultResult.setSelected(highlight);
},
_updateProviderIconCanFocus: function(resultDisplay) {
let meta = this._metaForProvider(resultDisplay.provider);
if (meta.icon)
meta.icon.actor.can_focus = true;
let others = this._metaForProvidersExcept(resultDisplay.provider);
others.forEach(function(meta) { if (meta.icon) meta.icon.actor.can_focus = false; });
},
navigateFocus: function(direction) {
let rtl = this.actor.get_text_direction() == Clutter.TextDirection.RTL;
if (direction == Gtk.DirectionType.TAB_BACKWARD ||
@ -398,3 +615,65 @@ const SearchResults = new Lang.Class({
}
}
});
const ProviderIcon = new Lang.Class({
Name: 'ProviderIcon',
PROVIDER_ICON_SIZE: 48,
MORE_ICON_SIZE: 16,
_init: function(provider) {
this.provider = provider;
this.actor = new St.Button({ style_class: 'search-section-icon-bin',
reactive: true,
track_hover: true });
this.actor.connect('clicked', Lang.bind(this, this._onIconClicked));
this._content = new St.Widget({ layout_manager: new Clutter.BinLayout() });
this.actor.set_child(this._content);
let rtl = (this.actor.get_text_direction() == Clutter.TextDirection.RTL);
this.moreIcon = new St.Icon({ style_class: 'search-section-icon-more',
icon_size: this.MORE_ICON_SIZE,
icon_name: 'list-add-symbolic',
visible: false,
x_align: rtl ? Clutter.ActorAlign.START : Clutter.ActorAlign.END,
y_align: Clutter.ActorAlign.END,
// HACK: without these, ClutterBinLayout
// ignores alignment properties on the actor
x_expand: true,
y_expand: true });
this._iconBin = new St.Bin({ style_class: 'search-section-icon',
width: this.PROVIDER_ICON_SIZE,
height: this.PROVIDER_ICON_SIZE });
this._content.add_actor(this._iconBin);
this._content.add_actor(this.moreIcon);
this._createProviderIcon();
},
_createProviderIcon: function() {
let icon = new St.Icon({ icon_size: this.PROVIDER_ICON_SIZE });
if (this.provider.icon)
icon.gicon = this.provider.icon;
else
icon.icon_name = 'application-x-executable';
this._iconBin.set_child(icon);
},
activate: function() {
this.emit('launch-search');
},
_onIconClicked: function(actor) {
this.activate();
}
});
Signals.addSignalMethods(ProviderIcon.prototype);

View File

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

View File

@ -57,7 +57,10 @@ const ScreenSaverIface = <interface name="org.gnome.ScreenSaver">
<arg name="active" direction="out" type="b" />
</method>
<method name="SetActive">
<arg name="value" direction="in" type="u" />
<arg name="value" direction="in" type="b" />
</method>
<method name="GetActiveTime">
<arg name="value" direction="out" type="u" />
</method>
<signal name="ActiveChanged">
<arg name="new_value" type="b" />
@ -340,29 +343,46 @@ const GnomeShellExtensions = new Lang.Class({
const ScreenSaverDBus = new Lang.Class({
Name: 'ScreenSaverDBus',
_init: function() {
_init: function(screenShield) {
this.parent();
Main.screenShield.connect('lock-status-changed', Lang.bind(this, function(shield, locked) {
this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [locked]));
this._screenShield = screenShield;
screenShield.connect('lock-status-changed', Lang.bind(this, function(shield) {
this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [shield.locked]));
}));
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenSaverIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/ScreenSaver');
Gio.DBus.session.own_name('org.gnome.ScreenSaver', Gio.BusNameOwnerFlags.REPLACE, null, null);
},
Lock: function() {
Main.screenShield.lock(true);
LockAsync: function(parameters, invocation) {
let tmpId = this._screenShield.connect('lock-screen-shown', Lang.bind(this, function() {
this._screenShield.disconnect(tmpId);
invocation.return_value(null);
}));
this._screenShield.lock(true);
},
SetActive: function(active) {
if (active)
Main.screenShield.lock(true);
this._screenShield.lock(true);
else
Main.screenShield.unlock();
this._screenShield.unlock();
},
GetActive: function() {
return Main.screenShield.locked;
}
return this._screenShield.locked;
},
GetActiveTime: function() {
let started = this._screenShield.activationTime;
if (started > 0)
return Math.floor((GLib.get_monotonic_time() - started) / 1000000);
else
return 0;
},
});

View File

@ -76,6 +76,12 @@ const EntryMenu = new Lang.Class({
this.actor.grab_key_focus();
this.parent();
this._entry.add_style_pseudo_class('focus');
},
close: function() {
this._entry.grab_key_focus();
this.parent();
},
_updateCopyItem: function() {
@ -126,34 +132,20 @@ function _setMenuAlignment(entry, stageX) {
entry.menu.setSourceAlignment(entryX / entry.width);
};
function _onClicked(action, actor) {
let entry = actor.menu ? actor : actor.get_parent();
function _onButtonPressEvent(actor, event, entry) {
if (entry.menu.isOpen) {
entry.menu.close();
} else if (action.get_button() == 3) {
let [stageX, stageY] = action.get_coords();
_setMenuAlignment(entry, stageX);
entry.menu.open();
}
};
function _onLongPress(action, actor, state) {
let entry = actor.menu ? actor : actor.get_parent();
if (state == Clutter.LongPressState.QUERY)
return action.get_button() == 1 && !entry.menu.isOpen;
if (state == Clutter.LongPressState.ACTIVATE) {
let [stageX, stageY] = action.get_coords();
return true;
} else if (event.get_button() == 3) {
let [stageX, stageY] = event.get_coords();
_setMenuAlignment(entry, stageX);
entry.menu.open();
return true;
}
return false;
};
function _onPopup(actor) {
let entry = actor.menu ? actor : actor.get_parent();
function _onPopup(actor, entry) {
let [success, textX, textY, lineHeight] = entry.clutter_text.position_to_coords(-1);
if (success)
entry.menu.setSourceAlignment(textX / entry.width);
@ -168,20 +160,11 @@ function addContextMenu(entry, params) {
entry._menuManager = new PopupMenu.PopupMenuManager({ actor: entry });
entry._menuManager.addMenu(entry.menu);
let clickAction;
// Add a click action to both the entry and its clutter_text; the former
// Add an event handler to both the entry and its clutter_text; the former
// so padding is included in the clickable area, the latter because the
// event processing of ClutterText prevents event-bubbling.
clickAction = new Clutter.ClickAction();
clickAction.connect('clicked', _onClicked);
clickAction.connect('long-press', _onLongPress);
entry.clutter_text.add_action(clickAction);
entry.clutter_text.connect('button-press-event', Lang.bind(null, _onButtonPressEvent, entry));
entry.connect('button-press-event', Lang.bind(null, _onButtonPressEvent, entry));
clickAction = new Clutter.ClickAction();
clickAction.connect('clicked', _onClicked);
clickAction.connect('long-press', _onLongPress);
entry.add_action(clickAction);
entry.connect('popup-menu', _onPopup);
entry.connect('popup-menu', Lang.bind(null, _onPopup, entry));
}

View File

@ -75,10 +75,6 @@ const ATIndicator = new Lang.Class({
this.menu.addSettingsAction(_("Universal Access Settings"), 'gnome-universal-access-panel.desktop');
},
setLockedState: function(locked) {
this.actor.visible = !locked;
},
_buildItemExtended: function(string, initial_value, writable, on_set) {
let widget = new PopupMenu.PopupSwitchMenuItem(string, initial_value);
if (!writable)

View File

@ -88,11 +88,6 @@ const Indicator = new Lang.Class({
this._applet.connect('cancel-request', Lang.bind(this, this._cancelRequest));
},
setLockedState: function(locked) {
this._isLocked = locked;
this._updateKillswitch();
},
_updateKillswitch: function() {
let current_state = this._applet.killswitch_state;
let on = current_state == GnomeBluetooth.KillswitchState.UNBLOCKED;
@ -107,7 +102,7 @@ const Indicator = new Lang.Class({
/* TRANSLATORS: this means that bluetooth was disabled by hardware rfkill */
this._killswitch.setStatus(_("hardware disabled"));
this.actor.visible = !this._isLocked && has_adapter;
this.actor.visible = has_adapter;
if (on) {
this._discoverable.actor.show();
@ -461,7 +456,10 @@ const PinNotification = new Lang.Class({
_canActivateOkButton: function() {
// PINs have a fixed length of 6
return this._entry.clutter_text.text.length == 6;
if (this._numeric)
return this._entry.clutter_text.text.length == 6;
else
return true;
},
grabFocus: function(lockTray) {

View File

@ -5,6 +5,7 @@ const GLib = imports.gi.GLib;
const GnomeDesktop = imports.gi.GnomeDesktop;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
try {
@ -45,6 +46,7 @@ const IBusManager = new Lang.Class({
this._panelService = null;
this._engines = {};
this._ready = false;
this._registerPropertiesId = 0;
this._nameWatcherId = Gio.DBus.session.watch_name(IBus.SERVICE_IBUS,
Gio.BusNameWatcherFlags.NONE,
@ -63,6 +65,7 @@ const IBusManager = new Lang.Class({
this._candidatePopup.setPanelService(null);
this._engines = {};
this._ready = false;
this._registerPropertiesId = 0;
},
_onNameAppeared: function() {
@ -100,6 +103,11 @@ const IBusManager = new Lang.Class({
this._panelService = new IBus.PanelService({ connection: this._ibus.get_connection(),
object_path: IBus.PATH_PANEL });
this._candidatePopup.setPanelService(this._panelService);
// Need to set this to get 'global-engine-changed' emitions
this._ibus.set_watch_ibus_signal(true);
this._ibus.connect('global-engine-changed', Lang.bind(this, this._resetProperties));
this._panelService.connect('update-property', Lang.bind(this, this._updateProperty));
this._resetProperties();
} else {
this._clear();
return;
@ -116,6 +124,39 @@ const IBusManager = new Lang.Class({
this._readyCallback();
},
_resetProperties: function() {
this.emit('properties-registered', null);
if (this._registerPropertiesId != 0)
return;
this._registerPropertiesId =
this._panelService.connect('register-properties', Lang.bind(this, function(p, props) {
if (!props.get(0))
return;
this._panelService.disconnect(this._registerPropertiesId);
this._registerPropertiesId = 0;
this.emit('properties-registered', props);
}));
},
_updateProperty: function(panel, prop) {
this.emit('property-updated', prop);
},
activateProperty: function(key, state) {
this._panelService.property_activate(key, state);
},
hasProperties: function(id) {
if (id == 'anthy')
return true;
return false;
},
getEngineDesc: function(id) {
if (!IBus || !this._ready)
return null;
@ -123,6 +164,7 @@ const IBusManager = new Lang.Class({
return this._engines[id];
}
});
Signals.addSignalMethods(IBusManager.prototype);
const LayoutMenuItem = new Lang.Class({
Name: 'LayoutMenuItem',
@ -135,6 +177,7 @@ const LayoutMenuItem = new Lang.Class({
this.indicator = new St.Label({ text: shortName });
this.addActor(this.label);
this.addActor(this.indicator);
this.actor.label_actor = this.label;
}
});
@ -142,6 +185,12 @@ const InputSourceIndicator = new Lang.Class({
Name: 'InputSourceIndicator',
Extends: PanelMenu.Button,
_propertiesWhitelist: [
'InputMode',
'TypingMode',
'DictMode'
],
_init: function() {
this.parent(0.0, _("Keyboard"));
@ -162,56 +211,73 @@ const InputSourceIndicator = new Lang.Class({
this._currentSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
this._xkbInfo = new GnomeDesktop.XkbInfo();
this._ibusManager = new IBusManager(Lang.bind(this, this._inputSourcesChanged));
this._propSeparator = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(this._propSeparator);
this._propSection = new PopupMenu.PopupMenuSection();
this.menu.addMenuItem(this._propSection);
this._propSection.actor.hide();
this._properties = null;
this._ibusManager = new IBusManager(Lang.bind(this, this._inputSourcesChanged));
this._ibusManager.connect('properties-registered', Lang.bind(this, this._ibusPropertiesRegistered));
this._ibusManager.connect('property-updated', Lang.bind(this, this._ibusPropertyUpdated));
this._inputSourcesChanged();
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._showLayoutItem = this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, this._showLayout));
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._sessionUpdated();
this.menu.addSettingsAction(_("Region and Language Settings"), 'gnome-region-panel.desktop');
},
_sessionUpdated: function() {
// re-using "allowSettings" for the keyboard layout is a bit shady,
// but at least for now it is used as "allow popping up windows
// from shell menus"; we can always add a separate sessionMode
// option if need arises.
if (Main.sessionMode.allowSettings) {
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._showLayoutItem = this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, this._showLayout));
}
this.menu.addSettingsAction(_("Region and Language Settings"), 'gnome-region-panel.desktop');
},
setLockedState: function(locked) {
if (Main.sessionMode.allowSettings) {
this._showLayoutItem.actor.visible = !locked;
}
this.menu.setSettingsVisibility(!locked);
this._showLayoutItem.actor.visible = Main.sessionMode.allowSettings;
},
_currentInputSourceChanged: function() {
let nVisibleSources = Object.keys(this._layoutItems).length;
if (nVisibleSources < 2)
return;
let nSources = this._settings.get_value(KEY_INPUT_SOURCES).n_children();
let newCurrentSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
if (newCurrentSourceIndex >= nSources)
return;
let newLayoutItem = this._layoutItems[newCurrentSourceIndex];
let hasProperties;
if (!this._layoutItems[newCurrentSourceIndex]) {
// This source index is invalid as we weren't able to
// build a menu item for it, so we hide ourselves since we
// can't fix it here. *shrug*
if (newLayoutItem)
hasProperties = this._ibusManager.hasProperties(newLayoutItem.ibusEngineId);
else
hasProperties = false;
if (!newLayoutItem || (nVisibleSources < 2 && !hasProperties)) {
// This source index might be invalid if we weren't able
// to build a menu item for it, so we hide ourselves since
// we can't fix it here. *shrug*
// We also hide if we have only one visible source unless
// it's an IBus source with properties.
this.menu.close();
this.actor.hide();
return;
} else {
this.actor.show();
}
this.actor.show();
if (this._layoutItems[this._currentSourceIndex]) {
this._layoutItems[this._currentSourceIndex].setShowDot(false);
this._container.set_skip_paint(this._labelActors[this._currentSourceIndex], true);
}
this._layoutItems[newCurrentSourceIndex].setShowDot(true);
this._container.set_skip_paint(this._labelActors[newCurrentSourceIndex], false);
newLayoutItem.setShowDot(true);
let newLabelActor = this._labelActors[newCurrentSourceIndex];
this._container.set_skip_paint(newLabelActor, false);
if (hasProperties)
newLabelActor.set_text(newLayoutItem.indicator.get_text());
this._currentSourceIndex = newCurrentSourceIndex;
},
@ -242,9 +308,12 @@ const InputSourceIndicator = new Lang.Class({
} 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 = engineDesc.get_longname();
info.shortName = engineDesc.get_symbol();
info.displayName = language + ' (' + engineDesc.get_longname() + ')';
info.shortName = this._makeEngineShortName(engineDesc);
info.ibusEngineId = id;
}
}
@ -259,13 +328,6 @@ const InputSourceIndicator = new Lang.Class({
infos.push(info);
}
if (infos.length > 1) {
this.actor.show();
} else {
this.menu.close();
this.actor.hide();
}
for (let i = 0; i < infos.length; i++) {
let info = infos[i];
if (infosByShortName[info.shortName].length > 1) {
@ -274,6 +336,7 @@ const InputSourceIndicator = new Lang.Class({
}
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() {
@ -319,6 +382,146 @@ const InputSourceIndicator = new Lang.Class({
Util.spawn(['gkbd-keyboard-display', '-l', description]);
},
_makeEngineShortName: function(engineDesc) {
let symbol = engineDesc.get_symbol();
if (symbol && symbol[0])
return symbol;
let langCode = engineDesc.get_language().split('_', 1)[0];
if (langCode.length == 2 || langCode.length == 3)
return langCode.toLowerCase();
return String.fromCharCode(0x2328); // keyboard glyph
},
_propertyWhitelisted: function(prop) {
for (let i = 0; i < this._propertiesWhitelist.length; ++i) {
let key = prop.get_key();
if (key.substr(0, this._propertiesWhitelist[i].length) == this._propertiesWhitelist[i])
return true;
}
return false;
},
_ibusPropertiesRegistered: function(im, props) {
this._properties = props;
this._buildPropSection();
},
_ibusPropertyUpdated: function(im, prop) {
if (!this._propertyWhitelisted(prop))
return;
if (this._updateSubProperty(this._properties, prop))
this._buildPropSection();
},
_updateSubProperty: function(props, prop) {
if (!props)
return false;
let p;
for (let i = 0; (p = props.get(i)) != null; ++i) {
if (p.get_key() == prop.get_key() && p.get_prop_type() == prop.get_prop_type()) {
p.update(prop);
return true;
} else if (p.get_prop_type() == IBus.PropType.MENU) {
if (this._updateSubProperty(p.get_sub_props(), prop))
return true;
}
}
return false;
},
_updateIndicatorLabel: function(text) {
let layoutItem = this._layoutItems[this._currentSourceIndex];
let hasProperties;
if (layoutItem)
hasProperties = this._ibusManager.hasProperties(layoutItem.ibusEngineId);
else
hasProperties = false;
if (hasProperties)
this._labelActors[this._currentSourceIndex].set_text(text);
},
_buildPropSection: function() {
this._propSeparator.actor.hide();
this._propSection.actor.hide();
this._propSection.removeAll();
this._buildPropSubMenu(this._propSection, this._properties);
if (!this._propSection.isEmpty()) {
this._propSection.actor.show();
this._propSeparator.actor.show();
}
},
_buildPropSubMenu: function(menu, props) {
if (!props)
return;
let radioGroup = [];
let p;
for (let i = 0; (p = props.get(i)) != null; ++i) {
let prop = p;
if (!this._propertyWhitelisted(prop) ||
!prop.get_visible())
continue;
if (prop.get_key() == 'InputMode') {
let text;
if (prop.get_symbol)
text = prop.get_symbol().get_text();
else
text = prop.get_label().get_text();
if (text && text.length > 0 && text.length < 3)
this._updateIndicatorLabel(text);
}
let item;
let type = prop.get_prop_type();
if (type == IBus.PropType.MENU) {
item = new PopupMenu.PopupSubMenuMenuItem(prop.get_label().get_text());
this._buildPropSubMenu(item.menu, prop.get_sub_props());
} else if (type == IBus.PropType.RADIO) {
item = new PopupMenu.PopupMenuItem(prop.get_label().get_text());
item.prop = prop;
radioGroup.push(item);
item.radioGroup = radioGroup;
item.setShowDot(prop.get_state() == IBus.PropState.CHECKED);
item.connect('activate', Lang.bind(this, function() {
if (item.prop.get_state() == IBus.PropState.CHECKED)
return;
let group = item.radioGroup;
for (let i = 0; i < group.length; ++i) {
if (group[i] == item) {
item.setShowDot(true);
item.prop.set_state(IBus.PropState.CHECKED);
this._ibusManager.activateProperty(item.prop.get_key(),
IBus.PropState.CHECKED);
} else {
group[i].setShowDot(false);
group[i].prop.set_state(IBus.PropState.UNCHECKED);
this._ibusManager.activateProperty(group[i].prop.get_key(),
IBus.PropState.UNCHECKED);
}
}
}));
} else {
continue;
}
item.setSensitive(prop.get_sensitive());
menu.addMenuItem(item);
}
},
_containerGetPreferredWidth: function(container, for_height, alloc) {
// Here, and in _containerGetPreferredHeight, we need to query
// for the height of all children, but we ignore the results

View File

@ -16,7 +16,6 @@ const Indicator = new Lang.Class({
_init: function() {
this.parent(null, _("Volume, network, battery"));
this.actor.hide();
this._volume = Main.panel.statusArea.volume;
if (this._volume) {
@ -54,9 +53,5 @@ const Indicator = new Lang.Class({
this._battery.mainIcon.bind_property('visible', this._batteryIcon, 'visible',
GObject.BindingFlags.SYNC_CREATE);
}
},
setLockedState: function(locked) {
this.actor.visible = locked;
}
});

View File

@ -233,20 +233,11 @@ const NMWirelessSectionTitleMenuItem = new Lang.Class({
}
});
const NMDevice = new Lang.Class({
Name: 'NMDevice',
const NMConnectionBased = new Lang.Class({
Name: 'NMConnectionBased',
Abstract: true,
_init: function(client, device, connections) {
this.device = device;
if (device) {
this.device._delegate = this;
this._stateChangedId = this.device.connect('state-changed', Lang.bind(this, this._deviceStateChanged));
} else
this._stateChangedId = 0;
// protected
this._client = client;
_init: function(connections) {
this._connections = [ ];
for (let i = 0; i < connections.length; i++) {
if (!connections[i].get_uuid())
@ -264,26 +255,115 @@ const NMDevice = new Lang.Class({
this._connections.push(obj);
}
this._connections.sort(this._connectionSortFunction);
},
checkConnection: function(connection) {
let pos = this._findConnection(connection.get_uuid());
let exists = pos != -1;
let valid = this.connectionValid(connection);
let similar = false;
if (exists) {
let existing = this._connections[pos];
// Check if connection changed name or id
similar = existing.name == connection.get_id() &&
existing.timestamp == connection._timestamp;
}
if (exists && valid && similar) {
// Nothing to do
return;
}
if (exists)
this.removeConnection(connection);
if (valid)
this.addConnection(connection);
},
addConnection: function(connection) {
// record the connection
let obj = {
connection: connection,
name: connection.get_id(),
uuid: connection.get_uuid(),
timestamp: connection._timestamp,
item: null,
};
Util.insertSorted(this._connections, obj, this._connectionSortFunction);
this._queueCreateSection();
},
removeConnection: function(connection) {
let pos = this._findConnection(connection.get_uuid());
if (pos == -1) {
// this connection was never added, nothing to do here
return;
}
let obj = this._connections[pos];
if (obj.item)
obj.item.destroy();
this._connections.splice(pos, 1);
if (this._connections.length <= 1) {
// We need to show the automatic connection again
// (or in the case of NMDeviceWired, we want to hide
// the only explicit connection)
this._queueCreateSection();
}
},
_findConnection: function(uuid) {
for (let i = 0; i < this._connections.length; i++) {
let obj = this._connections[i];
if (obj.uuid == uuid)
return i;
}
return -1;
},
_connectionSortFunction: function(one, two) {
if (one.timestamp == two.timestamp)
return GLib.utf8_collate(one.name, two.name);
return two.timestamp - one.timestamp;
},
});
Signals.addSignalMethods(NMConnectionBased.prototype);
const NMDevice = new Lang.Class({
Name: 'NMDevice',
Abstract: true,
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.parent(connections);
this._activeConnection = null;
this._activeConnectionItem = null;
this._autoConnectionItem = null;
this._overflowItem = null;
if (this.device) {
this.statusItem = new PopupMenu.PopupSwitchMenuItem(this._getDescription(), this.connected, { style_class: 'popup-subtitle-menu-item' });
this._statusChanged = this.statusItem.connect('toggled', Lang.bind(this, function(item, state) {
let ok;
if (state)
ok = this.activate();
else
ok = this.deactivate();
this.statusItem = new PopupMenu.PopupSwitchMenuItem(this._getDescription(), this.connected, { style_class: 'popup-subtitle-menu-item' });
this._statusChanged = this.statusItem.connect('toggled', Lang.bind(this, function(item, state) {
let ok;
if (state)
ok = this.activate();
else
ok = this.deactivate();
if (!ok)
item.setToggleState(!state);
}));
if (!ok)
item.setToggleState(!state);
}));
this._updateStatusItem();
}
this._updateStatusItem();
this.section = new PopupMenu.PopupMenuSection();
this._deferredWorkId = Main.initializeDeferredWork(this.section.actor, Lang.bind(this, this._createSection));
@ -352,6 +432,10 @@ const NMDevice = new Lang.Class({
return this.device.state == NetworkManager.DeviceState.ACTIVATED;
},
clearActiveConnection: function(activeConnection) {
this.setActiveConnection(null);
},
setActiveConnection: function(activeConnection) {
if (activeConnection == this._activeConnection)
// nothing to do
@ -369,80 +453,13 @@ const NMDevice = new Lang.Class({
this._queueCreateSection();
},
checkConnection: function(connection) {
let pos = this._findConnection(connection.get_uuid());
let exists = pos != -1;
let valid = this.connectionValid(connection);
let similar = false;
if (exists) {
let existing = this._connections[pos];
// Check if connection changed name or id
similar = existing.name == connection.get_id() &&
existing.timestamp == connection._timestamp;
}
if (exists && valid && similar) {
// Nothing to do
return;
}
if (exists)
this.removeConnection(connection);
if (valid)
this.addConnection(connection);
},
addConnection: function(connection) {
// record the connection
let obj = {
connection: connection,
name: connection.get_id(),
uuid: connection.get_uuid(),
timestamp: connection._timestamp,
item: null,
};
Util.insertSorted(this._connections, obj, this._connectionSortFunction);
this._clearSection();
this._queueCreateSection();
},
removeConnection: function(connection) {
let pos = this._findConnection(connection.get_uuid());
if (pos == -1) {
// this connection was never added, nothing to do here
return;
}
let obj = this._connections[pos];
if (obj.item)
obj.item.destroy();
this._connections.splice(pos, 1);
if (this._connections.length <= 1) {
// We need to show the automatic connection again
// (or in the case of NMDeviceWired, we want to hide
// the only explicit connection)
this._clearSection();
this._queueCreateSection();
}
},
connectionValid: function(connection) {
return this.device.connection_valid(connection);
},
_connectionSortFunction: function(one, two) {
if (one.timestamp == two.timestamp)
return GLib.utf8_collate(one.name, two.name);
return two.timestamp - one.timestamp;
},
setEnabled: function(enabled) {
// do nothing by default, we want to keep the conneciton list visible
// in the majority of cases (wired, wwan, vpn)
// in the majority of cases (wired, wwan)
},
getStatusLabel: function() {
@ -500,15 +517,6 @@ const NMDevice = new Lang.Class({
throw new TypeError('Invoking pure virtual function NMDevice.createAutomaticConnection');
},
_findConnection: function(uuid) {
for (let i = 0; i < this._connections.length; i++) {
let obj = this._connections[i];
if (obj.uuid == uuid)
return i;
}
return -1;
},
_queueCreateSection: function() {
this._clearSection();
Main.queueDeferredWork(this._deferredWorkId);
@ -652,8 +660,6 @@ const NMDevice = new Lang.Class({
return out;
}
});
Signals.addSignalMethods(NMDevice.prototype);
const NMDeviceWired = new Lang.Class({
Name: 'NMDeviceWired',
@ -868,95 +874,6 @@ const NMDeviceBluetooth = new Lang.Class({
}
});
// Not a real device, but I save a lot code this way
const NMDeviceVPN = new Lang.Class({
Name: 'NMDeviceVPN',
Extends: NMDevice,
_init: function(client, device, connections) {
// Disable autoconnections
this._autoConnectionName = null;
this.category = NMConnectionCategory.VPN;
this.parent(client, null, connections);
},
connectionValid: function(connection) {
return connection._type == NetworkManager.SETTING_VPN_SETTING_NAME;
},
get empty() {
return this._connections.length == 0;
},
get connected() {
if (!this._activeConnection)
return false;
return this._activeConnection.vpn_state == NetworkManager.VPNConnectionState.ACTIVATED;
},
setActiveConnection: function(activeConnection) {
if (this._stateChangeId)
this._activeConnection.disconnect(this._stateChangeId);
this._stateChangeId = 0;
this.parent(activeConnection);
if (this._activeConnection)
this._stateChangeId = this._activeConnection.connect('vpn-state-changed',
Lang.bind(this, this._connectionStateChanged));
this.emit('state-changed');
},
_shouldShowConnectionList: function() {
return true;
},
deactivate: function() {
if (this._activeConnection)
this._client.deactivate_connection(this._activeConnection);
},
getStatusLabel: function() {
if (!this._activeConnection) // Same as DISCONNECTED
return null;
switch(this._activeConnection.vpn_state) {
case NetworkManager.VPNConnectionState.DISCONNECTED:
case NetworkManager.VPNConnectionState.ACTIVATED:
return null;
case NetworkManager.VPNConnectionState.PREPARE:
case NetworkManager.VPNConnectionState.CONNECT:
case NetworkManager.VPNConnectionState.IP_CONFIG_GET:
return _("connecting...");
case NetworkManager.VPNConnectionState.NEED_AUTH:
/* Translators: this is for network connections that require some kind of key or password */
return _("authentication required");
case NetworkManager.VPNConnectionState.FAILED:
return _("connection failed");
default:
log('VPN connection state invalid, is %d'.format(this.device.state));
return 'invalid';
}
},
_connectionStateChanged: function(connection, newstate, reason) {
if (newstate == NetworkManager.VPNConnectionState.FAILED) {
// FIXME: if we ever want to show something based on reason,
// we need to convert from NetworkManager.VPNConnectionStateReason
// to NetworkManager.DeviceStateReason
this.emit('activation-failed', reason);
}
// Differently from real NMDevices, there is no need to queue
// an update of the menu section, contents wouldn't change anyway
this.emit('state-changed');
}
});
const NMDeviceWireless = new Lang.Class({
Name: 'NMDeviceWireless',
Extends: NMDevice,
@ -1557,21 +1474,165 @@ const NMDeviceWireless = new Lang.Class({
},
});
const NMVPNSection = new Lang.Class({
Name: 'NMVPNSection',
Extends: NMConnectionBased,
category: NMConnectionCategory.VPN,
_init: function(client, connections) {
this.parent(connections);
this._client = client;
this.section = new PopupMenu.PopupMenuSection();
this._deferredWorkId = Main.initializeDeferredWork(this.section.actor, Lang.bind(this, this._createSection));
},
get empty() {
return this._connections.length == 0;
},
connectionValid: function(connection) {
// filtering is done by NMApplet code
return true;
},
getStatusLabel: function(activeConnection) {
switch(activeConnection.vpn_state) {
case NetworkManager.VPNConnectionState.DISCONNECTED:
case NetworkManager.VPNConnectionState.ACTIVATED:
return null;
case NetworkManager.VPNConnectionState.PREPARE:
case NetworkManager.VPNConnectionState.CONNECT:
case NetworkManager.VPNConnectionState.IP_CONFIG_GET:
return _("connecting...");
case NetworkManager.VPNConnectionState.NEED_AUTH:
/* Translators: this is for network connections that require some kind of key or password */
return _("authentication required");
case NetworkManager.VPNConnectionState.FAILED:
return _("connection failed");
default:
log('VPN connection state invalid, is %d'.format(this.device.state));
return 'invalid';
}
},
clearActiveConnection: function(activeConnection) {
let pos = this._findConnection(activeConnection.uuid);
if (pos < 0)
return;
let obj = this._connections[pos];
obj.active.disconnect(obj.stateChangedId);
obj.active = null;
if (obj.item) {
obj.item.setToggleState(false);
obj.item.setStatus(null);
}
},
setActiveConnection: function(activeConnection) {
let pos = this._findConnection(activeConnection.uuid);
if (pos < 0)
return;
let obj = this._connections[pos];
obj.active = activeConnection;
obj.stateChangedId = obj.active.connect('vpn-state-changed',
Lang.bind(this, this._connectionStateChanged));
if (obj.item) {
obj.item.setToggleState(obj.active.vpn_state ==
NetworkManager.VPNConnectionState.ACTIVATED);
obj.item.setStatus(this.getStatusLabel(obj.active));
}
},
_queueCreateSection: function() {
this.section.removeAll();
Main.queueDeferredWork(this._deferredWorkId);
},
_createConnectionItem: function(obj) {
let menuItem = new PopupMenu.PopupSwitchMenuItem(obj.name, false,
{ style_class: 'popup-subtitle-menu-item' });
menuItem.connect('toggled', Lang.bind(this, function(menuItem) {
if (menuItem.state) {
this._client.activate_connection(obj.connection, null, null, null);
// Immediately go back to disconnected, until NM tells us to change
menuItem.setToggleState(false);
} else if (obj.active) {
this._client.deactivate_connection(obj.active);
}
}));
if (obj.active) {
menuItem.setToggleState(obj.active.vpn_state ==
NetworkManager.VPNConnectionState.ACTIVATED);
menuItem.setStatus(this.getStatusLabel(obj.active));
}
return menuItem;
},
_createSection: function() {
if (this._connections.length > 0) {
this.section.actor.show();
for(let j = 0; j < this._connections.length; ++j) {
let obj = this._connections[j];
obj.item = this._createConnectionItem(obj);
if (j >= NUM_VISIBLE_NETWORKS) {
if (!this._overflowItem) {
this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More..."));
this.section.addMenuItem(this._overflowItem);
}
this._overflowItem.menu.addMenuItem(obj.item);
} else
this.section.addMenuItem(obj.item);
}
} else {
this.section.actor.hide()
}
},
_connectionStateChanged: function(vpnConnection, newstate, reason) {
if (newstate == NetworkManager.VPNConnectionState.FAILED) {
// FIXME: if we ever want to show something based on reason,
// we need to convert from NetworkManager.VPNConnectionStateReason
// to NetworkManager.DeviceStateReason
this.emit('activation-failed', reason);
}
let pos = this._findConnection(vpnConnection.uuid);
if (pos >= 0) {
let obj = this._connections[pos];
if (obj.item) {
obj.item.setToggleState(vpnConnection.vpn_state ==
NetworkManager.VPNConnectionState.ACTIVATED);
obj.item.setStatus(this.getStatusLabel(vpnConnection));
}
} else {
log('Could not find connection for vpn-state-changed handler');
}
},
});
const NMApplet = new Lang.Class({
Name: 'NMApplet',
Extends: PanelMenu.SystemStatusButton,
_init: function() {
this.parent('network-offline', _('Network'));
this.parent('network-offline-symbolic', _('Network'));
this.secondaryIcon = this.addIcon(new Gio.ThemedIcon({ name: 'network-vpn-symbolic' }));
this.secondaryIcon.hide();
this._isLocked = false;
this._client = NMClient.Client.new();
this._statusSection = new PopupMenu.PopupMenuSection();
this._statusItem = new PopupMenu.PopupMenuItem('', { style_class: 'popup-inactive-menu-item', reactive: false });
this._statusItem = new PopupMenu.PopupMenuItem('', { reactive: false });
this._statusSection.addMenuItem(this._statusItem);
this._statusSection.addAction(_("Enable networking"), Lang.bind(this, function() {
this._client.networking_enabled = true;
@ -1623,15 +1684,9 @@ const NMApplet = new Lang.Class({
this.menu.addMenuItem(this._devices.wwan.section);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.vpn = {
section: new PopupMenu.PopupMenuSection(),
device: this._makeWrapperDevice(NMDeviceVPN, null),
item: new NMWiredSectionTitleMenuItem(_("VPN Connections"))
};
this._devices.vpn.section.addMenuItem(this._devices.vpn.item);
this._devices.vpn.section.addMenuItem(this._devices.vpn.device.section);
this._devices.vpn.section.actor.hide();
this.menu.addMenuItem(this._devices.vpn.section);
this._vpnSection = new NMVPNSection(this._client, this._connections);
this._vpnSection.connect('activation-failed', Lang.bind(this, this._onActivationFailed));
this.menu.addMenuItem(this._vpnSection.section);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addSettingsAction(_("Network Settings"), 'gnome-network-panel.desktop');
@ -1675,11 +1730,6 @@ const NMApplet = new Lang.Class({
}));
},
setLockedState: function(locked) {
this._isLocked = locked;
this._syncNMState();
},
_ensureSource: function() {
if (!this._source) {
this._source = new MessageTray.Source(_("Network Manager"),
@ -1705,18 +1755,6 @@ const NMApplet = new Lang.Class({
},
_syncSectionTitle: function(category) {
if (category == NMConnectionCategory.VPN) {
// Special case VPN: it's only one device (and a fake one
// actually), and we don't show it if empty
let device = this._devices.vpn.device;
let section = this._devices.vpn.section;
let item = this._devices.vpn.item;
section.actor.visible = !device.empty;
item.updateForDevice(device);
return;
}
let devices = this._devices[category].devices;
let item = this._devices[category].item;
let section = this._devices[category].section;
@ -1753,10 +1791,9 @@ const NMApplet = new Lang.Class({
or this._source will be cleared */
this._ensureSource();
let icon = new St.Icon({ icon_name: iconName,
icon_size: MessageTray.NOTIFICATION_ICON_SIZE });
let gicon = new Gio.ThemedIcon({ name: iconName });
device._notification = new MessageTray.Notification(this._source, title, text,
{ icon: icon });
{ gicon: gicon });
device._notification.setUrgency(urgency);
device._notification.setTransient(true);
device._notification.connect('destroy', function() {
@ -1765,17 +1802,20 @@ const NMApplet = new Lang.Class({
this._source.notify(device._notification);
},
_onActivationFailed: function(device, reason) {
// XXX: nm-applet has no special text depending on reason
// but I'm not sure of this generic message
this._notifyForDevice(device, 'network-error-symbolic',
_("Connection failed"),
_("Activation of network connection failed"),
MessageTray.Urgency.HIGH);
},
_makeWrapperDevice: function(wrapperClass, device) {
let wrapper = new wrapperClass(this._client, device, this._connections);
wrapper._activationFailedId = wrapper.connect('activation-failed', Lang.bind(this, function(device, reason) {
// XXX: nm-applet has no special text depending on reason
// but I'm not sure of this generic message
this._notifyForDevice(device, 'network-error-symbolic',
_("Connection failed"),
_("Activation of network connection failed"),
MessageTray.Urgency.HIGH);
}));
wrapper._activationFailedId = wrapper.connect('activation-failed',
Lang.bind(this, this._onActivationFailed));
wrapper._deviceStateChangedId = wrapper.connect('state-changed', Lang.bind(this, function(dev) {
this._syncSectionTitle(dev.category);
}));
@ -1858,7 +1898,7 @@ const NMApplet = new Lang.Class({
for (let i = 0; i < closedConnections.length; i++) {
let active = closedConnections[i];
if (active._primaryDevice) {
active._primaryDevice.setActiveConnection(null);
active._primaryDevice.clearActiveConnection(active);
active._primaryDevice = null;
}
if (active._inited) {
@ -1925,7 +1965,7 @@ const NMApplet = new Lang.Class({
}
}
} else
a._primaryDevice = this._devices.vpn.device
a._primaryDevice = this._vpnSection;
if (a._primaryDevice)
a._primaryDevice.setActiveConnection(a);
@ -2007,8 +2047,7 @@ const NMApplet = new Lang.Class({
let section = connection._section;
if (section == NMConnectionCategory.VPN) {
this._devices.vpn.device.removeConnection(connection);
this._syncSectionTitle(section);
this._vpnSection.removeConnection(connection);
} else if (section != NMConnectionCategory.INVALID) {
let devices = this._devices[section].devices;
for (let i = 0; i < devices.length; i++)
@ -2031,8 +2070,7 @@ const NMApplet = new Lang.Class({
if (section == NMConnectionCategory.INVALID)
return;
if (section == NMConnectionCategory.VPN) {
this._devices.vpn.device.checkConnection(connection);
this._syncSectionTitle(section);
this._vpnSection.checkConnection(connection);
} else {
let devices = this._devices[section].devices;
for (let i = 0; i < devices.length; i++) {
@ -2058,12 +2096,11 @@ const NMApplet = new Lang.Class({
this._syncSectionTitle(NMConnectionCategory.WIRED);
this._syncSectionTitle(NMConnectionCategory.WIRELESS);
this._syncSectionTitle(NMConnectionCategory.WWAN);
this._syncSectionTitle(NMConnectionCategory.VPN);
},
_syncNMState: function() {
this.mainIcon.visible = this._client.manager_running;
this.actor.visible = this.mainIcon.visible && !this._isLocked;
this.actor.visible = this.mainIcon.visible;
if (!this._client.networking_enabled) {
this.setIcon('network-offline-symbolic');

View File

@ -56,7 +56,6 @@ const Indicator = new Lang.Class({
this._proxy = new PowerManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH);
this._isLocked = false;
this._deviceItems = [ ];
this._hasPrimary = false;
this._primaryDeviceId = null;
@ -77,11 +76,6 @@ const Indicator = new Lang.Class({
this._devicesChanged();
},
setLockedState: function(locked) {
this._isLocked = locked;
this._syncIcon();
},
_readPrimaryDevice: function() {
this._proxy.GetPrimaryDeviceRemote(Lang.bind(this, function(result, error) {
if (error) {
@ -160,7 +154,7 @@ const Indicator = new Lang.Class({
hasIcon = true;
}
this.mainIcon.visible = hasIcon;
this.actor.visible = hasIcon && !this._isLocked;
this.actor.visible = hasIcon;
},
_devicesChanged: function() {

View File

@ -62,6 +62,8 @@ const VolumeMenu = new Lang.Class({
this._inputSlider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
this.addMenuItem(this._inputTitle);
this.addMenuItem(this._inputSlider);
this._onControlStateChanged();
},
scroll: function(direction) {
@ -90,6 +92,7 @@ const VolumeMenu = new Lang.Class({
if (this._control.get_state() == Gvc.MixerControlState.READY) {
this._readOutput();
this._readInput();
this._maybeShowInput();
} else {
this.emit('icon-changed', null);
}
@ -217,8 +220,6 @@ const Indicator = new Lang.Class({
_init: function() {
this.parent('audio-volume-muted-symbolic', _("Volume"));
this._isLocked = false;
this._control = getMixerControl();
this._volumeMenu = new VolumeMenu(this._control);
this._volumeMenu.connect('icon-changed', Lang.bind(this, function(menu, icon) {
@ -235,13 +236,8 @@ const Indicator = new Lang.Class({
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
},
setLockedState: function(locked) {
this._isLocked = locked;
this._syncVisibility();
},
_syncVisibility: function() {
this.actor.visible = this._hasPulseAudio && !this._isLocked;
this.actor.visible = this._hasPulseAudio;
this.mainIcon.visible = this._hasPulseAudio;
},

View File

@ -2,9 +2,10 @@
const AccountsService = imports.gi.AccountsService;
const Clutter = imports.gi.Clutter;
const Gdm = imports.gi.Gdm;
const Gdm = imports.gi.Gdm;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const GnomeDesktop = imports.gi.GnomeDesktop;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Signals = imports.signals;
@ -23,6 +24,35 @@ const GdmUtil = imports.gdm.util;
// The timeout before going back automatically to the lock screen (in seconds)
const IDLE_TIMEOUT = 2 * 60;
function versionCompare(required, reference) {
required = required.split('.');
reference = reference.split('.');
for (let i = 0; i < required.length; i++) {
if (required[i] != reference[i])
return required[i] < reference[i];
}
return true;
}
function isSupported() {
try {
let params = GLib.Variant.new('(ss)', ['org.gnome.DisplayManager.Manager', 'Version']);
let result = Gio.DBus.system.call_sync('org.gnome.DisplayManager',
'/org/gnome/DisplayManager/Manager',
'org.freedesktop.DBus.Properties',
'Get', params, null,
Gio.DBusCallFlags.NONE,
-1, null);
let version = result.deep_unpack()[0].deep_unpack();
return versionCompare('3.5.91', version);
} catch(e) {
return false;
}
}
// A widget showing the user avatar and name
const UserWidget = new Lang.Class({
Name: 'UserWidget',
@ -41,6 +71,7 @@ const UserWidget = new Lang.Class({
this.actor.add(this._label,
{ expand: true,
x_fill: true,
y_fill: false,
y_align: St.Align.MIDDLE });
this._userLoadedId = this._user.connect('notify::is-loaded',
@ -89,13 +120,17 @@ const UnlockDialog = new Lang.Class({
this._userName = GLib.get_user_name();
this._user = this._userManager.get_user(this._userName);
this._failCounter = 0;
this._firstQuestion = true;
this._greeterClient = new Gdm.Client();
this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient, { reauthenticationOnly: true });
this._userVerifier.connect('reset', Lang.bind(this, this._reset));
this._userVerifier.connect('ask-question', Lang.bind(this, this._onAskQuestion));
this._userVerifier.connect('show-message', Lang.bind(this, this._showMessage));
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
this._userVerifier.connect('verification-failed', Lang.bind(this, this._onVerificationFailed));
this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
this._userVerifier.connect('show-login-hint', Lang.bind(this, this._showLoginHint));
this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._hideLoginHint));
@ -112,7 +147,8 @@ const UnlockDialog = new Lang.Class({
this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
can_focus: true });
ShellEntry.addContextMenu(this._promptEntry);
this._promptEntry.clutter_text.set_password_char('\u25cf');
ShellEntry.addContextMenu(this._promptEntry, { isPassword: true });
this.setInitialKeyFocus(this._promptEntry);
this._promptEntry.clutter_text.connect('activate', Lang.bind(this, this._doUnlock));
@ -122,6 +158,9 @@ const UnlockDialog = new Lang.Class({
this.contentLayout.add_actor(this._promptLayout);
this._promptMessage = new St.Label({ visible: false });
this.contentLayout.add(this._promptMessage, { x_fill: true });
this._promptLoginHint = new St.Label({ style_class: 'login-dialog-prompt-login-hint' });
this._promptLoginHint.hide();
this.contentLayout.add_actor(this._promptLoginHint);
@ -134,8 +173,7 @@ const UnlockDialog = new Lang.Class({
default: true };
this.setButtons([cancelButton, this._okButton]);
this._updateOkButton(false);
this._reset();
this._updateSensitivity(true);
let otherUserLabel = new St.Label({ text: _("Log in as another user"),
style_class: 'login-dialog-not-listed-label' });
@ -150,35 +188,55 @@ const UnlockDialog = new Lang.Class({
{ x_align: St.Align.START,
x_fill: false });
let batch = new Batch.Hold();
this._userVerifier.begin(this._userName, batch);
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
this.emit('loaded');
return false;
}));
this._idleMonitor = Shell.IdleMonitor.get();
// this dialog is only created after user activity (curtain drag or
// escape key press), so the timeout will fire after IDLE_TIMEOUT seconds of inactivity
this._idleMonitor = new GnomeDesktop.IdleMonitor();
this._idleWatchId = this._idleMonitor.add_watch(IDLE_TIMEOUT * 1000, Lang.bind(this, this._escape));
},
_updateOkButton: function(sensitive) {
_updateSensitivity: function(sensitive) {
this._promptEntry.reactive = sensitive;
this._promptEntry.clutter_text.editable = sensitive;
this._okButton.button.reactive = sensitive;
this._okButton.button.can_focus = sensitive;
},
_reset: function() {
this._userVerifier.clear();
this._userVerifier.begin(this._userName, new Batch.Hold());
_showMessage: function(userVerifier, message, styleClass) {
if (message) {
this._promptMessage.text = message;
this._promptMessage.styleClass = styleClass;
GdmUtil.fadeInActor(this._promptMessage);
} else {
GdmUtil.fadeOutActor(this._promptMessage);
}
},
_onAskQuestion: function(verifier, serviceName, question, passwordChar) {
if (this._firstQuestion && this._firstQuestionAnswer) {
this._userVerifier.answerQuery(serviceName, this._firstQuestionAnswer);
this._firstQuestionAnswer = null;
this._firstQuestion = false;
return;
}
this._promptLabel.text = question;
this._promptEntry.text = '';
if (!this._firstQuestion)
this._promptEntry.text = '';
else
this._firstQuestion = false;
this._promptEntry.clutter_text.set_password_char(passwordChar);
this._promptEntry.menu.isPassword = passwordChar != '';
this._currentQuery = serviceName;
this._updateOkButton(true);
this._updateSensitivity(true);
},
_showLoginHint: function(verifier, message) {
@ -191,13 +249,22 @@ const UnlockDialog = new Lang.Class({
},
_doUnlock: function() {
if (this._firstQuestion) {
// we haven't received a query yet, so stash the answer
// and make ourself non-reactive
// the actual reply to GDM will be sent as soon as asked
this._firstQuestionAnswer = this._promptEntry.text;
this._updateSensitivity(false);
return;
}
if (!this._currentQuery)
return;
let query = this._currentQuery;
this._currentQuery = null;
this._updateOkButton(false);
this._updateSensitivity(false);
this._userVerifier.answerQuery(query, this._promptEntry.text);
},
@ -207,13 +274,24 @@ const UnlockDialog = new Lang.Class({
this.emit('unlocked');
},
_onVerificationFailed: function() {
this._userVerifier.cancel();
_onReset: function() {
this.emit('failed');
},
_onVerificationFailed: function() {
this._currentQuery = null;
this._firstQuestion = true;
this._promptEntry.text = '';
this._promptEntry.clutter_text.set_password_char('\u25cf');
this._promptEntry.menu.isPassword = true;
this._updateSensitivity(false);
},
_escape: function() {
this._onVerificationFailed();
this._userVerifier.cancel();
this.emit('failed');
},
_otherUserClicked: function(button, event) {

View File

@ -9,10 +9,11 @@ const Pango = imports.gi.Pango;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Tp = imports.gi.TelepathyGLib;
const UPowerGlib = imports.gi.UPowerGlib;
const Atk = imports.gi.Atk;
const BoxPointer = imports.ui.boxpointer;
const GnomeSession = imports.misc.gnomeSession;
const LoginManager = imports.misc.loginManager;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
@ -25,6 +26,7 @@ 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 DIALOG_ICON_SIZE = 64;
@ -58,6 +60,11 @@ const UserAvatarWidget = new Lang.Class({
reactive: params.reactive });
},
setSensitive: function(sensitive) {
this.actor.can_focus = sensitive;
this.actor.reactive = sensitive;
},
update: function() {
let iconFile = this._user.get_icon_file();
if (!GLib.file_test(iconFile, GLib.FileTest.EXISTS))
@ -181,7 +188,7 @@ const IMStatusChooserItem = new Lang.Class({
item = new IMStatusItem(_("Idle"), 'user-idle-symbolic');
this._combo.addMenuItem(item, IMStatus.IDLE);
item = new IMStatusItem(_("Unavailable"), 'user-offline-symbolic');
item = new IMStatusItem(_("Offline"), 'user-offline-symbolic');
this._combo.addMenuItem(item, IMStatus.OFFLINE);
this._combo.connect('active-item-changed',
@ -236,6 +243,10 @@ const IMStatusChooserItem = new Lang.Class({
if (this.actor.mapped)
this._updateUser();
}));
this.connect('sensitive-changed', function(sensitive) {
this._avatar.setSensitive(sensitive);
});
},
_restorePresence: function() {
@ -472,10 +483,11 @@ const UserMenuButton = new Lang.Class({
this._presence = new GnomeSession.Presence();
this._session = new GnomeSession.SessionManager();
this._haveShutdown = true;
this._haveSuspend = true;
this._accountMgr = Tp.AccountManager.dup();
this._upClient = new UPowerGlib.Client();
this._loginManager = LoginManager.getLoginManager();
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._iconBox = new St.Bin();
@ -496,6 +508,8 @@ const UserMenuButton = new Lang.Class({
style_class: 'popup-menu-icon' });
this._pendingIcon = new St.Icon({ icon_name: 'user-status-pending-symbolic',
style_class: 'popup-menu-icon' });
this._lockedIcon = new St.Icon({ icon_name: 'changes-prevent-symbolic',
style_class: 'popup-menu-icon' });
this._accountMgr.connect('most-available-presence-changed',
Lang.bind(this, this._updatePresenceIcon));
@ -553,17 +567,28 @@ const UserMenuButton = new Lang.Class({
// the lockdown setting changes, which should be close enough.
this.menu.connect('open-state-changed', Lang.bind(this,
function(menu, open) {
if (open)
this._updateHaveShutdown();
if (!open)
return;
this._updateHaveShutdown();
this._updateHaveSuspend();
}));
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
Lang.bind(this, this._updateHaveShutdown));
this._upClient.connect('notify::can-suspend', Lang.bind(this, this._updateSuspendOrPowerOff));
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._sessionUpdated();
},
setLockedState: function(locked) {
this.actor.visible = !locked;
_sessionUpdated: function() {
this.actor.visible = !Main.sessionMode.isGreeter;
let allowSettings = Main.sessionMode.allowSettings;
this._statusChooser.setSensitive(allowSettings);
this._systemSettings.visible = allowSettings;
this.setSensitive(!Main.sessionMode.isLocked);
this._updatePresenceIcon();
},
_onDestroy: function() {
@ -586,19 +611,19 @@ const UserMenuButton = new Lang.Class({
_updateSwitchUser: function() {
let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY);
let multiUser = this._userManager.can_switch() && this._userManager.has_multiple_users;
let multiSession = Gdm.get_session_ids().length > 1;
this._loginScreenItem.label.set_text(multiUser ? _("Switch User")
: _("Switch Session"));
this._loginScreenItem.actor.visible = allowSwitch && (multiUser || multiSession);
this._loginScreenItem.actor.visible = allowSwitch && multiUser;
},
_updateLogout: function() {
let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY);
let alwaysShow = global.settings.get_boolean(ALWAYS_SHOW_LOG_OUT_KEY);
let systemAccount = this._user.system_account;
let localAccount = this._user.local_account;
let multiUser = this._userManager.has_multiple_users;
let multiSession = Gdm.get_session_ids().length > 1;
this._logoutItem.actor.visible = allowLogout && (multiUser || multiSession);
this._logoutItem.actor.visible = allowLogout && (alwaysShow || multiUser || multiSession || systemAccount || !localAccount);
},
_updateLockScreen: function() {
@ -622,9 +647,15 @@ const UserMenuButton = new Lang.Class({
}));
},
_updateSuspendOrPowerOff: function() {
this._haveSuspend = this._upClient.get_can_suspend();
_updateHaveSuspend: function() {
this._loginManager.canSuspend(Lang.bind(this,
function(result) {
this._haveSuspend = result;
this._updateSuspendOrPowerOff();
}));
},
_updateSuspendOrPowerOff: function() {
if (!this._suspendOrPowerOffItem)
return;
@ -647,7 +678,9 @@ const UserMenuButton = new Lang.Class({
},
_updatePresenceIcon: function(accountMgr, presence, status, message) {
if (presence == Tp.ConnectionPresenceType.AVAILABLE)
if (Main.sessionMode.isLocked)
this._iconBox.child = this._lockedIcon;
else if (presence == Tp.ConnectionPresenceType.AVAILABLE)
this._iconBox.child = this._availableIcon;
else if (presence == Tp.ConnectionPresenceType.BUSY)
this._iconBox.child = this._busyIcon;
@ -707,8 +740,7 @@ const UserMenuButton = new Lang.Class({
let item;
item = new IMStatusChooserItem();
if (Main.sessionMode.allowSettings)
item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
this.menu.addMenuItem(item);
this._statusChooser = item;
@ -720,11 +752,10 @@ const UserMenuButton = new Lang.Class({
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
if (Main.sessionMode.allowSettings) {
item = new PopupMenu.PopupMenuItem(_("System Settings"));
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
this.menu.addMenuItem(item);
}
item = new PopupMenu.PopupMenuItem(_("System Settings"));
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
this.menu.addMenuItem(item);
this._systemSettings = item;
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
@ -792,14 +823,15 @@ const UserMenuButton = new Lang.Class({
},
_onLockScreenActivate: function() {
this.menu.close(BoxPointer.PopupAnimation.NONE);
Main.overview.hide();
Main.screenShield.lock(true);
},
_onLoginScreenActivate: function() {
this.menu.close(BoxPointer.PopupAnimation.NONE);
Main.overview.hide();
if (this._screenSaverSettings.get_boolean(LOCK_ENABLED_KEY))
Main.screenShield.lock(false);
Main.screenShield.lock(false);
Gdm.goto_login_session_sync(null);
},
@ -826,12 +858,13 @@ const UserMenuButton = new Lang.Class({
let tmpId = Main.screenShield.connect('lock-screen-shown', Lang.bind(this, function() {
Main.screenShield.disconnect(tmpId);
this._upClient.suspend_sync(null);
this._loginManager.suspend();
}));
this.menu.close(BoxPointer.PopupAnimation.NONE);
Main.screenShield.lock(true);
} else {
this._upClient.suspend_sync(null);
this._loginManager.suspend();
}
}
}

View File

@ -1,6 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
@ -11,7 +12,6 @@ const St = imports.gi.St;
const AppDisplay = imports.ui.appDisplay;
const Main = imports.ui.main;
const PlaceDisplay = imports.ui.placeDisplay;
const RemoteSearch = imports.ui.remoteSearch;
const Search = imports.ui.search;
const SearchDisplay = imports.ui.searchDisplay;
@ -20,6 +20,7 @@ const Tweener = imports.ui.tweener;
const Wanda = imports.ui.wanda;
const WorkspacesView = imports.ui.workspacesView;
const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings';
const FocusTrap = new Lang.Class({
Name: 'FocusTrap',
@ -51,7 +52,7 @@ const ViewSelector = new Lang.Class({
this._activePage = null;
this.active = false;
this.entryNonEmpty = false;
this._searchPending = false;
this._searchTimeoutId = 0;
@ -89,7 +90,7 @@ const ViewSelector = new Lang.Class({
this._appsPage = this._addPage(this._appDisplay.actor, null,
_("Applications"), 'system-run-symbolic');
this._searchResults = new SearchDisplay.SearchResults(this._searchSystem);
this._searchResults = new SearchDisplay.SearchDisplay(this._searchSystem);
this._searchPage = this._addPage(this._searchResults.actor, this._entry,
_("Search"), 'edit-find-symbolic');
@ -98,7 +99,6 @@ const ViewSelector = new Lang.Class({
this.addSearchProvider(new Wanda.WandaSearchProvider());
this.addSearchProvider(new AppDisplay.AppSearchProvider());
this.addSearchProvider(new AppDisplay.SettingsSearchProvider());
this.addSearchProvider(new PlaceDisplay.PlaceSearchProvider());
// Load remote search providers provided by applications
RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider));
@ -114,9 +114,6 @@ 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 () {
@ -133,16 +130,15 @@ const ViewSelector = new Lang.Class({
}
}));
// Public constraints which may be used to tie actors' height or
// vertical position to the current tab's content; as the content's
// height and position depend on the view selector's style properties
// (e.g. font size, padding, spacing, ...) it would be extremely hard
// and ugly to get these from the outside. While it would be possible
// to use position and height properties directly, outside code would
// need to ensure that the content is properly allocated before
// accessing the properties.
this.constrainHeight = new Clutter.BindConstraint({ source: this._pageArea,
coordinate: Clutter.BindCoordinate.HEIGHT });
global.display.add_keybinding('toggle-application-view',
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
Meta.KeyBindingFlags.NONE,
Lang.bind(this, this._showWithAppsPage));
},
_showWithAppsPage: function() {
Main.overview.show();
this._showAppsButton.set_checked(true);
},
show: function() {
@ -220,7 +216,9 @@ const ViewSelector = new Lang.Class({
},
_onShowAppsButtonToggled: function() {
if (this.active)
this.emit('apps-button-checked', this._showAppsButton.checked);
if (this.entryNonEmpty)
this.reset();
else
this._showPage(this._showAppsButton.checked ? this._appsPage
@ -236,7 +234,7 @@ const ViewSelector = new Lang.Class({
let symbol = event.get_key_symbol();
if (symbol == Clutter.Escape) {
if (this.active)
if (this.entryNonEmpty)
this.reset();
else if (this._showAppsButton.checked)
this._resetShowAppsButton();
@ -244,9 +242,9 @@ const ViewSelector = new Lang.Class({
Main.overview.hide();
return true;
} else if (Clutter.keysym_to_unicode(symbol) ||
(symbol == Clutter.BackSpace && this.active)) {
(symbol == Clutter.BackSpace && this.entryNonEmpty)) {
this.startSearch(event);
} else if (!this.active) {
} else if (!this.entryNonEmpty) {
if (symbol == Clutter.Tab || symbol == Clutter.Down) {
this._activePage.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
return true;
@ -259,6 +257,7 @@ const ViewSelector = new Lang.Class({
},
_searchCancelled: function() {
this.emit('search-cancelled');
this._showPage(this._showAppsButton.checked ? this._appsPage
: this._workspacesPage);
@ -311,7 +310,7 @@ const ViewSelector = new Lang.Class({
startSearch: function(event) {
global.stage.set_key_focus(this._text);
this._text.event(event, false);
this._text.event(event, true);
},
// the entry does not show the hint
@ -320,13 +319,14 @@ const ViewSelector = new Lang.Class({
},
_onTextChanged: function (se, prop) {
let searchPreviouslyActive = this.active;
this.active = this._entry.get_text() != '';
this._searchPending = this.active && !searchPreviouslyActive;
let searchPreviouslyActive = this.entryNonEmpty;
this.entryNonEmpty = this._entry.get_text() != '';
this._searchPending = this.entryNonEmpty && !searchPreviouslyActive;
if (this._searchPending) {
this._searchResults.startingSearch();
}
if (this.active) {
if (this.entryNonEmpty) {
this.emit('search-begin');
this._entry.set_secondary_icon(this._activeIcon);
if (this._iconClickedId == 0) {
@ -343,7 +343,7 @@ const ViewSelector = new Lang.Class({
this._entry.set_secondary_icon(this._inactiveIcon);
this._searchCancelled();
}
if (!this.active) {
if (!this.entryNonEmpty) {
if (this._searchTimeoutId > 0) {
Mainloop.source_remove(this._searchTimeoutId);
this._searchTimeoutId = 0;
@ -371,7 +371,7 @@ const ViewSelector = new Lang.Class({
}
this._searchResults.activateDefault();
return true;
} else if (this.active) {
} else if (this.entryNonEmpty) {
let arrowNext, nextDirection;
if (entry.get_text_direction() == Clutter.TextDirection.RTL) {
arrowNext = Clutter.Left;

View File

@ -1,11 +1,9 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GdkPixbuf = imports.gi.GdkPixbuf;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
const IconGrid = imports.ui.iconGrid;
@ -50,21 +48,12 @@ const WandaIcon = new Lang.Class({
},
createIcon: function(iconSize) {
if (this._animations)
this._animations.destroy();
if (!this._imageFile) {
return new St.Icon({ icon_name: 'face-smile',
icon_size: iconSize });
}
this._animations = St.TextureCache.get_default().load_sliced_image(this._imageFile, this._imgWidth, this._imgHeight);
this._animations.connect('destroy', Lang.bind(this, function() {
if (this._timeoutId)
GLib.source_remove(this._timeoutId);
this._timeoutId = 0;
this._animations = null;
}));
this._animations.connect('notify::mapped', Lang.bind(this, function() {
if (this._animations.mapped && !this._timeoutId) {
this._timeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, FISH_SPEED, Lang.bind(this, this._update));
@ -77,11 +66,16 @@ const WandaIcon = new Lang.Class({
}
}));
this._i = 0;
return this._animations;
},
_createIconTexture: function(size) {
if (size == this.iconSize)
return;
this.parent(size);
},
_update: function() {
let n = this._animations.get_n_children();
if (n == 0) {
@ -173,15 +167,8 @@ const WandaSearchProvider = new Lang.Class({
// only one which speaks the truth!
'name': capitalize(fish[0]),
'createIcon': function(iconSize) {
// for DND only (maybe could be improved)
// DON'T use St.Icon here, it crashes the shell
// (dnd.js code assumes it can query the actor size
// without parenting it, while StWidget accesses
// StThemeNode in get_preferred_width/height, which
// triggers an assertion failure)
return St.TextureCache.get_default().load_icon_name(null,
'face-smile',
iconSize);
return new St.Icon({ gicon: Gio.icon_new_for_string('face-smile'),
icon_size: iconSize });
}
}]);
},

View File

@ -33,21 +33,13 @@ const DRAGGING_WINDOW_OPACITY = 100;
const BUTTON_LAYOUT_SCHEMA = 'org.gnome.shell.overrides';
const BUTTON_LAYOUT_KEY = 'button-layout';
// Define a layout scheme for small window counts. For larger
// counts we fall back to an algorithm. We need more schemes here
// unless we have a really good algorithm.
// Each triplet is [xCenter, yCenter, scale] where the scale
// is relative to the width of the workspace.
const POSITIONS = {
1: [[0.5, 0.5, 0.95]],
2: [[0.25, 0.5, 0.48], [0.75, 0.5, 0.48]],
3: [[0.25, 0.25, 0.48], [0.75, 0.25, 0.48], [0.5, 0.75, 0.48]],
4: [[0.25, 0.25, 0.47], [0.75, 0.25, 0.47], [0.75, 0.75, 0.47], [0.25, 0.75, 0.47]],
5: [[0.165, 0.25, 0.32], [0.495, 0.25, 0.32], [0.825, 0.25, 0.32], [0.25, 0.75, 0.32], [0.75, 0.75, 0.32]]
};
// Used in _orderWindowsPermutations, 5! = 120 which is probably the highest we can go
const POSITIONING_PERMUTATIONS_MAX = 5;
// 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,
// we stop looking for a new layout and use the previous layout.
// Otherwise, we keep looking for a new layout.
const LAYOUT_SCALE_WEIGHT = 1;
const LAYOUT_SPACE_WEIGHT = 0.1;
function _interpolate(start, end, step) {
return start + (end - start) * step;
@ -167,6 +159,24 @@ const WindowClone = new Lang.Class({
this._selected = false;
},
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];
},
setStackAbove: function (actor) {
this._stackAbove = actor;
if (this.inDrag || this._zooming)
@ -443,6 +453,11 @@ 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();
}));
let button = new St.Button({ style_class: 'window-close' });
@ -504,15 +519,16 @@ const WindowOverlay = new Lang.Class({
transition: 'easeOutQuad' });
},
chromeWidth: function () {
return this.closeButton.width - this.closeButton._overlap;
},
chromeHeights: function () {
return [this.closeButton.height - this.closeButton._overlap,
this.title.height + this.title._spacing];
},
_repositionSelf: function() {
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
@ -550,9 +566,8 @@ const WindowOverlay = new Lang.Class({
else
button.set_position(Math.floor(buttonX), Math.floor(buttonY));
if (!title.fullWidth)
title.fullWidth = title.width;
let titleWidth = Math.min(title.fullWidth, cloneWidth);
let [titleMinWidth, titleNatWidth] = title.get_preferred_width(-1);
let titleWidth = Math.max(titleMinWidth, Math.min(titleNatWidth, cloneWidth));
let titleX = cloneX + (cloneWidth - titleWidth) / 2;
let titleY = cloneY + cloneHeight + title._spacing;
@ -668,6 +683,280 @@ const WindowPositionFlags = {
ANIMATE: 1 << 1
};
const LayoutStrategy = new Lang.Class({
Name: 'LayoutStrategy',
Abstract: true,
_init: function(monitor, rowSpacing, columnSpacing, bottomPadding) {
this._monitor = monitor;
this._rowSpacing = rowSpacing;
this._columnSpacing = columnSpacing;
this._bottomPadding = bottomPadding;
},
_newRow: function() {
// Row properties:
//
// * x, y are the position of row, relative to area
//
// * width, height are the scaled versions of fullWidth, fullHeight
//
// * width also has the spacing in between windows. It's not in
// fullWidth, as the spacing is constant, whereas fullWidth is
// meant to be scaled
//
// * neither height/fullHeight have any sort of spacing or padding
//
// * if cellWidth is present, all windows in the row will occupy
// the space of cellWidth, centered.
return { x: 0, y: 0,
width: 0, height: 0,
fullWidth: 0, fullHeight: 0,
cellWidth: 0,
windows: [] };
},
// Compute the size and 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;
if (width > height)
ratio = width / this._monitor.width;
else
ratio = height / this._monitor.height;
let fancyScale = (2 / (1 + ratio)) * scale;
return [width * fancyScale, height * fancyScale, fancyScale];
},
// Compute the size of each row, by assigning to the properties
// row.width, row.height, row.fullWidth, row.fullHeight, and
// (optionally) row.cellWidth, for each row in @layout.rows.
// This method is intended to be called by subclasses.
_computeRowSizes: function(layout) {
throw new Error('_computeRowSizes not implemented');
},
// Compute strategy-specific window slots for each window in
// @windows, given the @layout. The strategy may also use @layout
// as strategy-specific storage.
//
// This must calculate:
// * maxColumns - The maximum number of columns used by the layout.
// * gridWidth - The total width used by the grid, unscaled, unspaced.
// * gridHeight - The totial height used by the grid, unscaled, unspaced.
// * rows - A list of rows, which should be instantiated by _newRow.
computeLayout: function(windows, layout) {
throw new Error('computeLayout not implemented');
},
// Given @layout, compute the overall scale and space of the layout.
// The scale is the individual, non-fancy scale of each window, and
// the space is the percentage of the available area eventually
// used by the layout.
// This method does not return anything, but instead installs
// the properties "scale" and "space" on @layout directly.
//
// Make sure to call this methods before calling computeWindowSlots(),
// as it depends on the scale property installed in @layout here.
computeScaleAndSpace: function(layout) {
let area = layout.area;
let hspacing = (layout.maxColumns - 1) * this._columnSpacing;
let vspacing = (layout.numRows - 1) * this._rowSpacing + this._bottomPadding;
let spacedWidth = area.width - hspacing;
let spacedHeight = area.height - vspacing;
let horizontalScale = spacedWidth / layout.gridWidth;
let verticalScale = spacedHeight / layout.gridHeight;
// Thumbnails should be less than 70% of the original size
let scale = Math.min(horizontalScale, verticalScale, WINDOW_CLONE_MAXIMUM_SCALE);
let scaledLayoutWidth = layout.gridWidth * scale + hspacing;
let scaledLayoutHeight = layout.gridHeight * scale + vspacing;
let space = (scaledLayoutWidth * scaledLayoutHeight) / (area.width * area.height);
layout.scale = scale;
layout.space = space;
},
computeWindowSlots: function(layout, area) {
this._computeRowSizes(layout);
let { rows: rows, scale: scale, state: state } = layout;
let slots = [];
let y = 0;
for (let i = 0; i < rows.length; i++) {
let row = rows[i];
row.x = area.x + (area.width - row.width) / 2;
row.y = area.y + y;
y += row.height + this._rowSpacing;
}
let height = y - this._rowSpacing + this._bottomPadding;
let baseY = (area.height - height) / 2;
for (let i = 0; i < rows.length; i++) {
let row = rows[i];
row.y += baseY;
let baseX = row.x;
for (let j = 0; j < row.windows.length; j++) {
let window = row.windows[j];
let [width, height, s] = this._computeWindowSizeAndScale(window, scale);
let y = row.y + row.height - height;
let x = baseX;
if (row.cellWidth) {
x += (row.cellWidth - width) / 2;
width = row.cellWidth;
}
slots.push([x, y, s]);
baseX += width + this._columnSpacing;
}
}
return slots;
}
});
const UnalignedLayoutStrategy = new Lang.Class({
Name: 'UnalignedLayoutStrategy',
Extends: LayoutStrategy,
_computeRowSizes: function(layout) {
let { rows: rows, scale: scale } = layout;
for (let i = 0; i < rows.length; i++) {
let row = rows[i];
row.width = row.fullWidth * scale + (row.windows.length - 1) * this._columnSpacing;
row.height = row.fullHeight * scale;
}
},
_keepSameRow: function(row, window, width, idealRowWidth) {
if (row.fullWidth + width <= idealRowWidth)
return true;
let oldRatio = row.fullWidth / idealRowWidth;
let newRatio = (row.fullWidth + width) / idealRowWidth;
if (Math.abs(1 - newRatio) < Math.abs(1 - oldRatio))
return true;
return false;
},
computeLayout: function(windows, layout) {
let numRows = layout.numRows;
let rows = [];
let totalWidth = 0;
for (let i = 0; i < windows.length; i++) {
totalWidth += windows[i].actor.width;
}
let idealRowWidth = totalWidth / numRows;
let windowIdx = 0;
for (let i = 0; i < numRows; i++) {
let col = 0;
let row = this._newRow();
rows.push(row);
for (; windowIdx < windows.length; windowIdx++) {
let window = windows[windowIdx];
let [width, height] = this._computeWindowSizeAndScale(window, 1);
row.fullHeight = Math.max(row.fullHeight, height);
// either new width is < idealWidth or new width is nearer from idealWidth then oldWidth
if (this._keepSameRow(row, window, width, idealRowWidth) || (i == numRows - 1)) {
row.windows.push(window);
row.fullWidth += width;
} else {
break;
}
}
}
let gridHeight = 0;
let maxRow;
for (let i = 0; i < numRows; i++) {
let row = rows[i];
if (!maxRow || row.fullWidth > maxRow.fullWidth)
maxRow = row;
gridHeight += row.fullHeight;
}
layout.rows = rows;
layout.maxColumns = maxRow.windows.length;
layout.gridWidth = maxRow.fullWidth;
layout.gridHeight = gridHeight;
}
});
const GridLayoutStrategy = new Lang.Class({
Name: 'GridLayoutStrategy',
Extends: LayoutStrategy,
_computeRowSizes: function(layout) {
let { rows: rows, scale: scale } = layout;
let gridWidth = layout.numColumns * layout.maxWindowWidth;
let hspacing = (layout.numColumns - 1) * this._columnSpacing;
for (let i = 0; i < rows.length; i++) {
let row = rows[i];
row.fullWidth = layout.gridWidth;
row.fullHeight = layout.maxWindowHeight;
row.width = row.fullWidth * scale + hspacing;
row.height = row.fullHeight * scale;
row.cellWidth = layout.maxWindowWidth * scale;
}
},
computeLayout: function(windows, layout) {
let { numRows: numRows, numColumns: numColumns } = layout;
let rows = [];
let windowIdx = 0;
let maxWindowWidth = 0;
let maxWindowHeight = 0;
for (let i = 0; i < numRows; i++) {
let row = this._newRow();
rows.push(row);
for (; windowIdx < windows.length; windowIdx++) {
if (row.windows.length >= numColumns)
break;
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);
}
}
layout.rows = rows;
layout.maxColumns = numColumns;
layout.gridWidth = numColumns * maxWindowWidth;
layout.gridHeight = numRows * maxWindowHeight;
layout.maxWindowWidth = maxWindowWidth;
layout.maxWindowHeight = maxWindowHeight;
}
});
/**
* @metaWorkspace: a #Meta.Workspace, or null
*/
@ -689,7 +978,7 @@ const Workspace = new Lang.Class({
// Without this the drop area will be overlapped.
this._windowOverlaysGroup.set_size(0, 0);
this.actor = new Clutter.Group();
this.actor = new St.Widget({ style_class: 'window-picker' });
this.actor.set_size(0, 0);
this._dropRect = new Clutter.Rectangle({ opacity: 0 });
@ -729,6 +1018,8 @@ const Workspace = new Lang.Class({
this._positionWindowsFlags = 0;
this._positionWindowsId = 0;
this._currentLayout = null;
},
setGeometry: function(x, y, width, height) {
@ -763,225 +1054,6 @@ const Workspace = new Lang.Class({
return this._windows.length == 0;
},
// Only use this for n <= 20 say
_factorial: function(n) {
let result = 1;
for (let i = 2; i <= n; i++)
result *= i;
return result;
},
/**
* _permutation:
* @permutationIndex: An integer from [0, list.length!)
* @list: (inout): Array of objects to permute; will be modified in place
*
* Given an integer between 0 and length of array, re-order the array in-place
* into a permutation denoted by the index.
*/
_permutation: function(permutationIndex, list) {
for (let j = 2; j <= list.length; j++) {
let firstIndex = (permutationIndex % j);
let secondIndex = j - 1;
// Swap
let tmp = list[firstIndex];
list[firstIndex] = list[secondIndex];
list[secondIndex] = tmp;
permutationIndex = Math.floor(permutationIndex / j);
}
},
/**
* _forEachPermutations:
* @list: Array
* @func: Function which takes a single array argument
*
* Call @func with each permutation of @list as an argument.
*/
_forEachPermutations: function(list, func) {
let nCombinations = this._factorial(list.length);
for (let i = 0; i < nCombinations; i++) {
let listCopy = list.concat();
this._permutation(i, listCopy);
func(listCopy);
}
},
/**
* _computeWindowMotion:
* @actor: A #WindowClone's #ClutterActor
* @slot: An element of #POSITIONS
* @slotGeometry: Layout of @slot
*
* Returns a number corresponding to how much perceived motion
* would be involved in moving the window to the given slot.
* Currently this is the square of the distance between the
* centers.
*/
_computeWindowMotion: function (actor, slot) {
let [xCenter, yCenter, fraction] = slot;
let xDelta, yDelta, distanceSquared;
let actorWidth, actorHeight;
let x = actor.x;
let y = actor.y;
let scale = actor.scale_x;
if (actor._delegate.inDrag) {
x = actor._delegate.dragOrigX;
y = actor._delegate.dragOrigY;
scale = actor._delegate.dragOrigScale;
}
actorWidth = actor.width * scale;
actorHeight = actor.height * scale;
xDelta = x + actorWidth / 2.0 - xCenter * this._width - this._x;
yDelta = y + actorHeight / 2.0 - yCenter * this._height - this._y;
distanceSquared = xDelta * xDelta + yDelta * yDelta;
return distanceSquared;
},
/**
* _orderWindowsPermutations:
*
* Iterate over all permutations of the windows, and determine the
* permutation which has the least total motion.
*/
_orderWindowsPermutations: function (clones, slots) {
let minimumMotionPermutation = null;
let minimumMotion = -1;
let permIndex = 0;
this._forEachPermutations(clones, Lang.bind(this, function (permutation) {
let motion = 0;
for (let i = 0; i < permutation.length; i++) {
let cloneActor = permutation[i].actor;
let slot = slots[i];
let delta = this._computeWindowMotion(cloneActor, slot);
motion += delta;
// Bail out early if we're already larger than the
// previous best
if (minimumMotionPermutation != null &&
motion > minimumMotion)
continue;
}
if (minimumMotionPermutation == null || motion < minimumMotion) {
minimumMotionPermutation = permutation;
minimumMotion = motion;
}
permIndex++;
}));
return minimumMotionPermutation;
},
/**
* _orderWindowsGreedy:
*
* Iterate over available slots in order, placing into each one the window
* we find with the smallest motion to that slot.
*/
_orderWindowsGreedy: function(clones, slots) {
let result = [];
let slotIndex = 0;
// Copy since we mutate below
let clonesCopy = clones.concat();
for (let i = 0; i < slots.length; i++) {
let slot = slots[i];
let minimumMotionIndex = -1;
let minimumMotion = -1;
for (let j = 0; j < clonesCopy.length; j++) {
let cloneActor = clonesCopy[j].actor;
let delta = this._computeWindowMotion(cloneActor, slot);
if (minimumMotionIndex == -1 || delta < minimumMotion) {
minimumMotionIndex = j;
minimumMotion = delta;
}
}
result.push(clonesCopy[minimumMotionIndex]);
clonesCopy.splice(minimumMotionIndex, 1);
}
return result;
},
/**
* _orderWindowsByMotionAndStartup:
* @windows: Array of #MetaWindow
* @slots: Array of slots
*
* Returns a copy of @windows, ordered in such a way that they require least motion
* to move to the final screen coordinates of @slots. Ties are broken in a stable
* fashion by the order in which the windows were created.
*/
_orderWindowsByMotionAndStartup: function(clones, slots) {
clones.sort(function(w1, w2) {
return w2.metaWindow.get_stable_sequence() - w1.metaWindow.get_stable_sequence();
});
if (clones.length <= POSITIONING_PERMUTATIONS_MAX)
return this._orderWindowsPermutations(clones, slots);
else
return this._orderWindowsGreedy(clones, slots);
},
/**
* _getSlotGeometry:
* @slot: A layout slot
*
* Returns: the screen-relative [x, y, width, height]
* of a given window layout slot.
*/
_getSlotGeometry: function(slot) {
let [xCenter, yCenter, fraction] = slot;
let width = this._width * fraction;
let height = this._height * fraction;
let x = this._x + xCenter * this._width - width / 2 ;
let y = this._y + yCenter * this._height - height / 2;
return [x, y, width, height];
},
/**
* _computeWindowLayout:
* @metaWindow: A #MetaWindow
* @slot: A layout slot
*
* Given a window and slot to fit it in, compute its
* screen-relative [x, y, scale] where scale applies
* to both X and Y directions.
*/
_computeWindowLayout: function(metaWindow, slot) {
let [x, y, width, height] = this._getSlotGeometry(slot);
let rect = metaWindow.get_outer_rect();
let buttonOuterHeight, captionHeight;
let buttonOuterWidth = 0;
if (this._windowOverlays[0]) {
[buttonOuterHeight, captionHeight] = this._windowOverlays[0].chromeHeights();
buttonOuterWidth = this._windowOverlays[0].chromeWidth();
} else
[buttonOuterHeight, captionHeight] = [0, 0];
let scale = Math.min((width - buttonOuterWidth) / rect.width,
(height - buttonOuterHeight - captionHeight) / rect.height,
WINDOW_CLONE_MAXIMUM_SCALE);
x = Math.floor(x + (width - scale * rect.width) / 2);
// We want to center the window in case we have just one
if (metaWindow.get_workspace().n_windows == 1)
y = Math.floor(y + (height - scale * rect.height) / 2);
else
y = Math.floor(y + height - rect.height * scale - captionHeight);
return [x, y, scale];
},
setReservedSlot: function(clone) {
if (this._reservedSlot == clone)
return;
@ -990,6 +1062,7 @@ const Workspace = new Lang.Class({
clone = null;
this._reservedSlot = clone;
this._currentLayout = null;
this.positionWindows(WindowPositionFlags.ANIMATE);
},
@ -1000,6 +1073,9 @@ const Workspace = new Lang.Class({
* ANIMATE - Indicates that we need animate changing position.
*/
positionWindows: function(flags) {
if (this.leavingOverview)
return;
this._positionWindowsFlags |= flags;
if (this._positionWindowsId > 0)
@ -1020,6 +1096,11 @@ const Workspace = new Lang.Class({
}
let clones = this._windows.slice();
clones.sort(function(a, b) {
return a.metaWindow.get_stable_sequence() - b.metaWindow.get_stable_sequence();
});
if (this._reservedSlot)
clones.push(this._reservedSlot);
@ -1027,8 +1108,7 @@ const Workspace = new Lang.Class({
let animate = flags & WindowPositionFlags.ANIMATE;
// Start the animations
let slots = this._computeAllWindowSlots(clones.length);
clones = this._orderWindowsByMotionAndStartup(clones, slots);
let slots = this._computeAllWindowSlots(clones);
let currentWorkspace = global.screen.get_active_workspace();
let isOnCurrentWorkspace = this.metaWorkspace == null || this.metaWorkspace == currentWorkspace;
@ -1045,10 +1125,11 @@ const Workspace = new Lang.Class({
if (clone.inDrag)
continue;
let [x, y, scale] = this._computeWindowLayout(metaWindow, slot);
let [x, y, scale] = slot;
if (overlay && initialPositioning)
overlay.hide();
if (animate && isOnCurrentWorkspace) {
if (!metaWindow.showing_on_its_workspace()) {
/* Hidden windows should fade in and grow
@ -1072,6 +1153,8 @@ const Workspace = new Lang.Class({
this._animateClone(clone, overlay, x, y, scale, initialPositioning);
} else {
// cancel any active tweens (otherwise they might override our changes)
Tweener.removeTweens(clone.actor);
clone.actor.set_position(x, y);
clone.actor.set_scale(scale, scale);
this._updateWindowOverlayPositions(clone, overlay, x, y, scale, false);
@ -1113,11 +1196,11 @@ const Workspace = new Lang.Class({
},
_updateWindowOverlayPositions: function(clone, overlay, x, y, scale, animate) {
if (!overlay)
return;
let [cloneWidth, cloneHeight] = clone.actor.get_size();
cloneWidth = scale * cloneWidth;
cloneHeight = scale * cloneHeight;
if (overlay)
overlay.updatePositions(x, y, cloneWidth, cloneHeight, animate);
overlay.updatePositions(x, y, cloneWidth * scale, cloneHeight * scale, animate);
},
_showWindowOverlay: function(clone, overlay, fade) {
@ -1211,6 +1294,7 @@ const Workspace = new Lang.Class({
this._cursorX = x;
this._cursorY = y;
this._currentLayout = null;
this._repositionWindowsId = Mainloop.timeout_add(750,
Lang.bind(this, this._delayedWindowRepositioning));
},
@ -1263,6 +1347,7 @@ const Workspace = new Lang.Class({
clone.actor.set_position (this._x, this._y);
}
this._currentLayout = null;
this.positionWindows(WindowPositionFlags.ANIMATE);
},
@ -1300,6 +1385,8 @@ const Workspace = new Lang.Class({
// Animate the full-screen to Overview transition.
zoomToOverview : function() {
this._currentLayout = null;
// Position and scale the windows.
if (Main.overview.animationInProgress)
this.positionWindows(WindowPositionFlags.ANIMATE | WindowPositionFlags.INITIAL);
@ -1464,29 +1551,108 @@ const Workspace = new Lang.Class({
}
},
_computeWindowSlot : function(windowIndex, numberOfWindows) {
if (numberOfWindows in POSITIONS)
return POSITIONS[numberOfWindows][windowIndex];
_isBetterLayout: function(oldLayout, newLayout) {
if (oldLayout.scale === undefined)
return true;
// If we don't have a predefined scheme for this window count,
// arrange the windows in a grid pattern.
let gridWidth = Math.ceil(Math.sqrt(numberOfWindows));
let gridHeight = Math.ceil(numberOfWindows / gridWidth);
let spacePower = (newLayout.space - oldLayout.space) * LAYOUT_SPACE_WEIGHT;
let scalePower = (newLayout.scale - oldLayout.scale) * LAYOUT_SCALE_WEIGHT;
let fraction = 0.95 * (1. / gridWidth);
let xCenter = (.5 / gridWidth) + ((windowIndex) % gridWidth) / gridWidth;
let yCenter = (.5 / gridHeight) + Math.floor((windowIndex / gridWidth)) / gridHeight;
return [xCenter, yCenter, fraction];
if (newLayout.scale > oldLayout.scale && newLayout.space > oldLayout.space) {
// Win win -- better scale and better space
return true;
} else if (newLayout.scale > oldLayout.scale && newLayout.space <= oldLayout.space) {
// Keep new layout only if scale gain outweights aspect space loss
return scalePower > spacePower;
} else if (newLayout.scale <= oldLayout.scale && newLayout.space > oldLayout.space) {
// Keep new layout only if aspect space gain outweights scale loss
return spacePower > scalePower;
} else {
// Lose -- worse scale and space
return false;
}
},
_computeAllWindowSlots: function(totalWindows) {
let slots = [];
for (let i = 0; i < totalWindows; i++) {
slots.push(this._computeWindowSlot(i, totalWindows));
_computeLayout: function(windows, area, rowSpacing, columnSpacing, bottomPadding) {
// We look for the largest scale that allows us to fit the
// largest row/tallest column on the workspace.
let lastLayout = {};
for (let numRows = 1; ; numRows++) {
let numColumns = Math.ceil(windows.length / numRows);
// If adding a new row does not change column count just stop
// (for instance: 9 windows, with 3 rows -> 3 columns, 4 rows ->
// 3 columns as well => just use 3 rows then)
if (numColumns == lastLayout.numColumns)
break;
let strategyClass = numRows > 2 ? GridLayoutStrategy : UnalignedLayoutStrategy;
let strategy = new strategyClass(this._monitor, rowSpacing, columnSpacing, bottomPadding);
let layout = { area: area, strategy: strategy, numRows: numRows, numColumns: numColumns };
strategy.computeLayout(windows, layout);
strategy.computeScaleAndSpace(layout);
if (!this._isBetterLayout(lastLayout, layout))
break;
lastLayout = layout;
}
return slots;
return lastLayout;
},
_rectEqual: function(one, two) {
if (one == two)
return true;
return (one.x == two.x &&
one.y == two.y &&
one.width == two.width &&
one.height == two.height);
},
_computeAllWindowSlots: function(windows) {
let totalWindows = windows.length;
let node = this.actor.get_theme_node();
// Window grid spacing
let columnSpacing = node.get_length('-horizontal-spacing');
let rowSpacing = node.get_length('-vertical-spacing');
if (!totalWindows)
return [];
let closeButtonHeight, captionHeight;
if (this._windowOverlays.length) {
// All of the overlays have the same chrome sizes,
// so just pick the first one.
let overlay = this._windowOverlays[0];
[closeButtonHeight, captionHeight] = overlay.chromeHeights();
} else {
[closeButtonHeight, captionHeight] = [0, 0];
}
rowSpacing += captionHeight;
let area = { x: this._x, y: this._y, width: this._width, height: this._height };
area.y += closeButtonHeight;
area.height -= closeButtonHeight;
if (!this._currentLayout)
this._currentLayout = this._computeLayout(windows, area, rowSpacing, columnSpacing, captionHeight);
let layout = this._currentLayout;
let strategy = layout.strategy;
if (!this._rectEqual(area, layout.area)) {
layout.area = area;
strategy.computeScaleAndSpace(layout);
}
return strategy.computeWindowSlots(layout, area);
},
_onCloneSelected : function (clone, time) {

View File

@ -493,6 +493,7 @@ const ThumbnailsBox = new Lang.Class({
_init: function() {
this.actor = new Shell.GenericContainer({ reactive: true,
style_class: 'workspace-thumbnails',
can_focus: 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,6 +527,8 @@ const ThumbnailsBox = new Lang.Class({
this._dropPlaceholder = new St.Bin({ style_class: 'placeholder' });
this.actor.add_actor(this._dropPlaceholder);
this.visible = true;
this._targetScale = 0;
this._scale = 0;
this._pendingScaleUpdate = false;
@ -541,12 +544,21 @@ 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.overview.connect('item-drag-begin',
Main.overview.connect('showing',
Lang.bind(this, this._createThumbnails));
Main.overview.connect('hidden',
Lang.bind(this, this._destroyThumbnails));
Main.overview.connect('app-drag-begin',
Lang.bind(this, this._onDragBegin));
Main.overview.connect('item-drag-end',
Main.overview.connect('app-drag-end',
Lang.bind(this, this._onDragEnd));
Main.overview.connect('item-drag-cancelled',
Main.overview.connect('app-drag-cancelled',
Lang.bind(this, this._onDragCancelled));
Main.overview.connect('window-drag-begin',
Lang.bind(this, this._onDragBegin));
@ -620,9 +632,7 @@ const ThumbnailsBox = new Lang.Class({
if (!source.realWindow && !source.shellWorkspaceLaunch && source != Main.xdndHandler)
return DND.DragMotionResult.CONTINUE;
if (!Meta.prefs_get_dynamic_workspaces())
return DND.DragMotionResult.CONTINUE;
let canCreateWorkspaces = Meta.prefs_get_dynamic_workspaces();
let spacing = this.actor.get_theme_node().get_length('spacing');
this._dropWorkspace = -1;
@ -647,7 +657,7 @@ const ThumbnailsBox = new Lang.Class({
if (i == this._dropPlaceholderPos)
targetBottom += this._dropPlaceholder.get_height();
if (y > targetTop && y <= targetBottom && source != Main.xdndHandler) {
if (y > targetTop && y <= targetBottom && source != Main.xdndHandler && canCreateWorkspaces) {
placeholderPos = i;
break;
} else if (y > targetBottom && y <= nextTargetTop) {
@ -721,10 +731,16 @@ const ThumbnailsBox = new Lang.Class({
}
},
show: function() {
_createThumbnails: function() {
this._switchWorkspaceNotifyId =
global.window_manager.connect('switch-workspace',
Lang.bind(this, this._activeWorkspaceChanged));
this._nWorkspacesNotifyId =
global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._workspacesChanged));
this._syncStackingId =
Main.overview.connect('sync-window-stacking',
Lang.bind(this, this.syncStacking));
this._targetScale = 0;
this._scale = 0;
@ -748,17 +764,86 @@ const ThumbnailsBox = new Lang.Class({
this.addThumbnails(0, global.screen.n_workspaces);
},
hide: function() {
_destroyThumbnails: function() {
if (this._switchWorkspaceNotifyId > 0) {
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
this._switchWorkspaceNotifyId = 0;
}
if (this._nWorkspacesNotifyId > 0) {
global.screen.disconnect(this._nWorkspacesNotifyId);
this._nWorkspacesNotifyId = 0;
}
if (this._syncStackingId > 0) {
Main.overview.disconnect(this._syncStackingId);
this._syncStackingId = 0;
}
for (let w = 0; w < this._thumbnails.length; w++)
this._thumbnails[w].destroy();
this._thumbnails = [];
},
_computeThumbnailsX: function() {
this._targetX = this.actor.get_x();
let rtl = (this.actor.get_text_direction() == Clutter.TextDirection.RTL);
if (rtl)
this._hiddenX = -this.actor.width;
else
this._hiddenX = this._targetX + this.actor.width;
},
show: function() {
if (this.visible)
return;
this.visible = true;
this.actor.show();
Tweener.addTween(this.actor, { translation_x: this._targetX,
transition: 'easeOutQuad',
time: SLIDE_ANIMATION_TIME
});
},
hide: function() {
if (!this.visible)
return;
this.visible = false;
Tweener.addTween(this.actor, { translation_x: this._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;
let active = global.screen.get_active_workspace_index();
if (newNumWorkspaces > oldNumWorkspaces) {
this.addThumbnails(oldNumWorkspaces, newNumWorkspaces - oldNumWorkspaces);
} else {
let removedIndex;
let removedNum = oldNumWorkspaces - newNumWorkspaces;
for (let w = 0; w < oldNumWorkspaces; w++) {
let metaWorkspace = global.screen.get_workspace_by_index(w);
if (this._thumbnails[w].metaWorkspace != metaWorkspace) {
removedIndex = w;
break;
}
}
this.removeThumbnails(removedIndex, removedNum);
}
},
addThumbnails: function(start, count) {
for (let k = start; k < start + count; k++) {
let metaWorkspace = global.screen.get_workspace_by_index(k);
@ -785,7 +870,7 @@ const ThumbnailsBox = new Lang.Class({
this._indicator.raise_top();
},
removeThumbmails: function(start, count) {
removeThumbnails: function(start, count) {
let currentPos = 0;
for (let k = 0; k < this._thumbnails.length; k++) {
let thumbnail = this._thumbnails[k];
@ -804,7 +889,7 @@ const ThumbnailsBox = new Lang.Class({
this._queueUpdateStates();
},
syncStacking: function(stackIndices) {
syncStacking: function(actor, stackIndices) {
for (let i = 0; i < this._thumbnails.length; i++)
this._thumbnails[i].syncStacking(stackIndices);
},
@ -926,6 +1011,8 @@ const ThumbnailsBox = new Lang.Class({
onCompleteScope: this
});
});
this._computeThumbnailsX();
},
_queueUpdateStates: function() {
@ -1159,5 +1246,30 @@ const ThumbnailsBox = new Lang.Class({
},
onCompleteScope: this
});
},
_onScrollEvent: function (actor, event) {
switch (event.get_scroll_direction()) {
case Clutter.ScrollDirection.UP:
Main.wm.actionMoveWorkspace(Meta.MotionDirection.UP);
break;
case Clutter.ScrollDirection.DOWN:
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

@ -22,8 +22,6 @@ 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',
@ -51,7 +49,6 @@ const WorkspacesView = new Lang.Class({
this._clipY = 0;
this._clipWidth = 0;
this._clipHeight = 0;
this._workspaceRatioSpacing = 0;
this._spacing = 0;
this._animating = false; // tweening
this._scrolling = false; // swipe-scrolling
@ -72,6 +69,7 @@ const WorkspacesView = new Lang.Class({
this._workspaces[w].actor.reparent(this.actor);
this._workspaces[activeWorkspaceIndex].actor.raise_top();
this._extraWorkspaces = [];
this._updateExtraWorkspaces();
// Position/scale the desktop windows and their children after the
@ -83,8 +81,6 @@ const WorkspacesView = new Lang.Class({
Lang.bind(this, function() {
for (let w = 0; w < this._workspaces.length; w++)
this._workspaces[w].zoomToOverview();
if (!this._extraWorkspaces)
return;
for (let w = 0; w < this._extraWorkspaces.length; w++)
this._extraWorkspaces[w].zoomToOverview();
}));
@ -108,9 +104,9 @@ const WorkspacesView = new Lang.Class({
global.window_manager.connect('switch-workspace',
Lang.bind(this, this._activeWorkspaceChanged));
this._itemDragBeginId = Main.overview.connect('item-drag-begin',
this._appDragBeginId = Main.overview.connect('app-drag-begin',
Lang.bind(this, this._dragBegin));
this._itemDragEndId = Main.overview.connect('item-drag-end',
this._appDragEndId = Main.overview.connect('app-drag-end',
Lang.bind(this, this._dragEnd));
this._windowDragBeginId = Main.overview.connect('window-drag-begin',
Lang.bind(this, this._dragBegin));
@ -124,7 +120,6 @@ const WorkspacesView = new Lang.Class({
if (!this._settings.get_boolean('workspaces-only-on-primary'))
return;
this._extraWorkspaces = [];
let monitors = Main.layoutManager.monitors;
for (let i = 0; i < monitors.length; i++) {
if (i == Main.layoutManager.primaryIndex)
@ -139,23 +134,20 @@ const WorkspacesView = new Lang.Class({
},
_destroyExtraWorkspaces: function() {
if (!this._extraWorkspaces)
return;
for (let m = 0; m < this._extraWorkspaces.length; m++)
this._extraWorkspaces[m].destroy();
this._extraWorkspaces = null;
this._extraWorkspaces = [];
},
setGeometry: function(x, y, width, height, spacing) {
setGeometry: function(x, y, width, height) {
if (this._x == x && this._y == y &&
this._width == width && this._height == height)
return;
this._width = width;
this._height = height;
this._x = x;
this._y = y;
this._workspaceRatioSpacing = spacing;
for (let i = 0; i < this._workspaces.length; i++)
this._workspaces[i].setGeometry(x, y, width, height);
@ -191,8 +183,6 @@ const WorkspacesView = new Lang.Class({
for (let w = 0; w < this._workspaces.length; w++)
this._workspaces[w].zoomFromOverview();
if (!this._extraWorkspaces)
return;
for (let w = 0; w < this._extraWorkspaces.length; w++)
this._extraWorkspaces[w].zoomFromOverview();
},
@ -204,8 +194,6 @@ const WorkspacesView = new Lang.Class({
syncStacking: function(stackIndices) {
for (let i = 0; i < this._workspaces.length; i++)
this._workspaces[i].syncStacking(stackIndices);
if (!this._extraWorkspaces)
return;
for (let i = 0; i < this._extraWorkspaces.length; i++)
this._extraWorkspaces[i].syncStacking(stackIndices);
},
@ -234,7 +222,7 @@ const WorkspacesView = new Lang.Class({
Tweener.removeTweens(workspace.actor);
let y = (w - active) * (this._height + this._spacing + this._workspaceRatioSpacing);
let y = (w - active) * (this._height + this._spacing);
if (showAnimation) {
let params = { y: y,
@ -337,13 +325,13 @@ const WorkspacesView = new Lang.Class({
if (this._inDrag)
this._dragEnd();
if (this._itemDragBeginId > 0) {
Main.overview.disconnect(this._itemDragBeginId);
this._itemDragBeginId = 0;
if (this._appDragBeginId > 0) {
Main.overview.disconnect(this._appDragBeginId);
this._appDragBeginId = 0;
}
if (this._itemDragEndId > 0) {
Main.overview.disconnect(this._itemDragEndId);
this._itemDragEndId = 0;
if (this._appDragEndId > 0) {
Main.overview.disconnect(this._appDragEndId);
this._appDragEndId = 0;
}
if (this._windowDragBeginId > 0) {
Main.overview.disconnect(this._windowDragBeginId);
@ -376,9 +364,6 @@ const WorkspacesView = new Lang.Class({
this._firstDragMotion = false;
for (let i = 0; i < this._workspaces.length; i++)
this._workspaces[i].setReservedSlot(dragEvent.dragActor._delegate);
if (!this._extraWorkspaces)
return DND.DragMotionResult.CONTINUE;
for (let i = 0; i < this._extraWorkspaces.length; i++)
this._extraWorkspaces[i].setReservedSlot(dragEvent.dragActor._delegate);
}
@ -392,9 +377,6 @@ const WorkspacesView = new Lang.Class({
for (let i = 0; i < this._workspaces.length; i++)
this._workspaces[i].setReservedSlot(null);
if (!this._extraWorkspaces)
return;
for (let i = 0; i < this._extraWorkspaces.length; i++)
this._extraWorkspaces[i].setReservedSlot(null);
},
@ -476,25 +458,8 @@ const WorkspacesDisplay = new Lang.Class({
this.actor.connect('parent-set', Lang.bind(this, this._parentSet));
this.actor.set_clip_to_allocation(true);
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));
controls.connect('scroll-event',
Lang.bind(this, this._onScrollEvent));
this._primaryIndex = Main.layoutManager.primaryIndex;
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
controls.add_actor(this._thumbnailsBox.actor);
this._workspacesViews = null;
this._primaryScrollAdjustment = null;
@ -507,77 +472,47 @@ 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));
this._switchWorkspaceNotifyId = 0;
this._itemDragBeginId = 0;
this._itemDragCancelledId = 0;
this._itemDragEndId = 0;
this._appDragBeginId = 0;
this._appDragCancelledId = 0;
this._appDragEndId = 0;
this._windowDragBeginId = 0;
this._windowDragCancelledId = 0;
this._windowDragEndId = 0;
this._notifyOpacityId = 0;
this._swipeScrollBeginId = 0;
this._swipeScrollEndId = 0;
this._settings = new Gio.Settings({ schema: OVERRIDE_SCHEMA });
this._settings.connect('changed::dynamic-workspaces',
Lang.bind(this, this._updateSwitcherVisibility));
},
_updateSwitcherVisibility: function() {
this._thumbnailsBox.actor.visible =
this._settings.get_boolean('dynamic-workspaces') ||
global.screen.n_workspaces > 1;
},
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._thumbnailsBox.show();
this._updateWorkspacesViews();
this._restackedNotifyId =
global.screen.connect('restacked',
Main.overview.connect('sync-window-stacking',
Lang.bind(this, this._onRestacked));
if (this._itemDragBeginId == 0)
this._itemDragBeginId = Main.overview.connect('item-drag-begin',
if (this._appDragBeginId == 0)
this._appDragBeginId = Main.overview.connect('app-drag-begin',
Lang.bind(this, this._dragBegin));
if (this._itemDragCancelledId == 0)
this._itemDragCancelledId = Main.overview.connect('item-drag-cancelled',
if (this._appDragCancelledId == 0)
this._appDragCancelledId = Main.overview.connect('app-drag-cancelled',
Lang.bind(this, this._dragCancelled));
if (this._itemDragEndId == 0)
this._itemDragEndId = Main.overview.connect('item-drag-end',
if (this._appDragEndId == 0)
this._appDragEndId = Main.overview.connect('app-drag-end',
Lang.bind(this, this._dragEnd));
if (this._windowDragBeginId == 0)
this._windowDragBeginId = Main.overview.connect('window-drag-begin',
@ -588,8 +523,6 @@ const WorkspacesDisplay = new Lang.Class({
if (this._windowDragEndId == 0)
this._windowDragEndId = Main.overview.connect('window-drag-end',
Lang.bind(this, this._dragEnd));
this._onRestacked();
},
zoomFromOverview: function() {
@ -599,27 +532,21 @@ const WorkspacesDisplay = new Lang.Class({
},
hide: function() {
this._controls.hide();
this._thumbnailsBox.hide();
if (!this._alwaysZoomOut)
this.zoomFraction = 0;
if (this._restackedNotifyId > 0){
global.screen.disconnect(this._restackedNotifyId);
Main.overview.disconnect(this._restackedNotifyId);
this._restackedNotifyId = 0;
}
if (this._itemDragBeginId > 0) {
Main.overview.disconnect(this._itemDragBeginId);
this._itemDragBeginId = 0;
if (this._appDragBeginId > 0) {
Main.overview.disconnect(this._appDragBeginId);
this._appDragBeginId = 0;
}
if (this._itemDragCancelledId > 0) {
Main.overview.disconnect(this._itemDragCancelledId);
this._itemDragCancelledId = 0;
if (this._appDragCancelledId > 0) {
Main.overview.disconnect(this._appDragCancelledId);
this._appDragCancelledId = 0;
}
if (this._itemDragEndId > 0) {
Main.overview.disconnect(this._itemDragEndId);
this._itemDragEndId = 0;
if (this._appDragEndId > 0) {
Main.overview.disconnect(this._appDragEndId);
this._appDragEndId = 0;
}
if (this._windowDragBeginId > 0) {
Main.overview.disconnect(this._windowDragBeginId);
@ -750,73 +677,15 @@ 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();
},
@ -856,36 +725,22 @@ 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 - controlsVisible;
let clipHeight = (fullHeight / fullWidth) * clipWidth;
let clipX = rtl ? x + controlsVisible : x;
let clipWidth = width;
let clipHeight = fullHeight;
let clipX = x;
let clipY = y + (fullHeight - clipHeight) / 2;
let widthAdjust = this._zoomOut ? controlsNatural : controlsVisible;
width -= widthAdjust;
if (rtl)
x += widthAdjust;
height = (fullHeight / fullWidth) * width;
let difference = fullHeight - height;
y += difference / 2;
let monitors = Main.layoutManager.monitors;
let m = 0;
for (let i = 0; i < monitors.length; i++) {
if (i == this._primaryIndex) {
this._workspacesViews[m].setClipRect(clipX, clipY,
clipWidth, clipHeight);
this._workspacesViews[m].setGeometry(x, y, width, height,
difference);
this._workspacesViews[m].setGeometry(x, y, width, height);
m++;
} else if (!this._workspacesOnlyOnPrimary) {
this._workspacesViews[m].setClipRect(monitors[i].x,
@ -895,31 +750,18 @@ const WorkspacesDisplay = new Lang.Class({
this._workspacesViews[m].setGeometry(monitors[i].x,
monitors[i].y,
monitors[i].width,
monitors[i].height, 0);
monitors[i].height);
m++;
}
}
},
_onRestacked: function() {
let stack = global.get_window_actors();
let stackIndices = {};
for (let i = 0; i < stack.length; i++) {
// Use the stable sequence for an integer to use as a hash key
stackIndices[stack[i].get_meta_window().get_stable_sequence()] = i;
}
_onRestacked: function(actor, stackIndices) {
for (let i = 0; i < this._workspacesViews.length; i++)
this._workspacesViews[i].syncStacking(stackIndices);
this._thumbnailsBox.syncStacking(stackIndices);
},
_workspacesChanged: function() {
this._updateAlwaysZoom();
this._updateZoom();
if (this._workspacesViews == null)
return;
@ -932,19 +774,18 @@ const WorkspacesDisplay = new Lang.Class({
let monitors = Main.layoutManager.monitors;
let m = 0;
for (let i = 0; i < monitors.length; i++) {
if (this._workspacesOnlyOnPrimaryChanged &&
if (this._workspacesOnlyOnPrimary &&
i != this._primaryIndex)
continue;
// Assume workspaces are only added at the end
for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
let metaWorkspace = global.screen.get_workspace_by_index(w);
this._workspaces[m++][w] =
this._workspaces[m][w] =
new Workspace.Workspace(metaWorkspace, i);
}
m++;
}
this._thumbnailsBox.addThumbnails(oldNumWorkspaces, newNumWorkspaces - oldNumWorkspaces);
} else {
// Assume workspaces are only removed sequentially
// (e.g. 2,3,4 - not 2,4,7)
@ -967,42 +808,12 @@ const WorkspacesDisplay = new Lang.Class({
lostWorkspaces[l].destroy();
}
}
this._thumbnailsBox.removeThumbmails(removedIndex, removedNum);
}
for (let i = 0; i < this._workspacesViews.length; i++)
this._workspacesViews[i].updateWorkspaces(oldNumWorkspaces,
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)
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();
this._updateSwitcherVisibility();
},
_dragBegin: function() {
@ -1020,33 +831,11 @@ 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));
},
_onScrollEvent: function (actor, event) {
switch ( event.get_scroll_direction() ) {
case Clutter.ScrollDirection.UP:
Main.wm.actionMoveWorkspace(Meta.MotionDirection.UP);
break;
case Clutter.ScrollDirection.DOWN:
Main.wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
break;
}
}
});
Signals.addSignalMethods(WorkspacesDisplay.prototype);

View File

@ -34,6 +34,7 @@ kk
kn
ko
ku
ky
lt
lv
ml

View File

@ -1,3 +1,5 @@
data/50-gnome-shell-screenshot.xml.in
data/50-gnome-shell-system.xml.in
data/gnome-shell.desktop.in.in
data/gnome-shell-extension-prefs.desktop.in.in
data/org.gnome.shell.gschema.xml.in.in
@ -8,24 +10,25 @@ js/gdm/util.js
js/misc/util.js
js/ui/appDisplay.js
js/ui/appFavorites.js
js/ui/autorunManager.js
js/ui/calendar.js
js/ui/components/autorunManager.js
js/ui/components/keyring.js
js/ui/components/networkAgent.js
js/ui/components/polkitAgent.js
js/ui/components/recorder.js
js/ui/components/telepathyClient.js
js/ui/dash.js
js/ui/dateMenu.js
js/ui/endSessionDialog.js
js/ui/extensionDownloader.js
js/ui/extensionSystem.js
js/ui/keyboard.js
js/ui/keyringPrompt.js
js/ui/lookingGlass.js
js/ui/main.js
js/ui/messageTray.js
js/ui/networkAgent.js
js/ui/notificationDaemon.js
js/ui/overview.js
js/ui/panel.js
js/ui/placeDisplay.js
js/ui/polkitAuthenticationAgent.js
js/ui/popupMenu.js
js/ui/runDialog.js
js/ui/screenShield.js
@ -39,7 +42,6 @@ js/ui/status/lockScreenMenu.js
js/ui/status/network.js
js/ui/status/power.js
js/ui/status/volume.js
js/ui/telepathyClient.js
js/ui/unlockDialog.js
js/ui/userMenu.js
js/ui/viewSelector.js

1244
po/ar.po

File diff suppressed because it is too large Load Diff

1295
po/as.po

File diff suppressed because it is too large Load Diff

1585
po/be.po

File diff suppressed because it is too large Load Diff

1558
po/bg.po

File diff suppressed because it is too large Load Diff

1657
po/ca.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1586
po/cs.po

File diff suppressed because it is too large Load Diff

1637
po/da.po

File diff suppressed because it is too large Load Diff

1334
po/de.po

File diff suppressed because it is too large Load Diff

1347
po/el.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1292
po/es.po

File diff suppressed because it is too large Load Diff

1431
po/et.po

File diff suppressed because it is too large Load Diff

1561
po/fa.po

File diff suppressed because it is too large Load Diff

1640
po/fi.po

File diff suppressed because it is too large Load Diff

1577
po/fr.po

File diff suppressed because it is too large Load Diff

1556
po/ga.po

File diff suppressed because it is too large Load Diff

1228
po/gl.po

File diff suppressed because it is too large Load Diff

1282
po/gu.po

File diff suppressed because it is too large Load Diff

1447
po/he.po

File diff suppressed because it is too large Load Diff

2228
po/hi.po

File diff suppressed because it is too large Load Diff

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